1/*
2 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6
7/*
8 * lib/krb5/krb/mk_req_ext.c
9 *
10 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
11 * All Rights Reserved.
12 *
13 * Export of this software from the United States of America may
14 *   require a specific license from the United States Government.
15 *   It is the responsibility of any person or organization contemplating
16 *   export to obtain such a license before exporting.
17 *
18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19 * distribute this software and its documentation for any purpose and
20 * without fee is hereby granted, provided that the above copyright
21 * notice appear in all copies and that both that copyright notice and
22 * this permission notice appear in supporting documentation, and that
23 * the name of M.I.T. not be used in advertising or publicity pertaining
24 * to distribution of the software without specific, written prior
25 * permission.  Furthermore if you modify this software you must label
26 * your software as modified software and not distribute it in such a
27 * fashion that it might be confused with the original M.I.T. software.
28 * M.I.T. makes no representations about the suitability of
29 * this software for any purpose.  It is provided "as is" without express
30 * or implied warranty.
31 *
32 *
33 * krb5_mk_req_extended()
34 */
35
36
37#include "k5-int.h"
38#include "auth_con.h"
39
40/*
41 Formats a KRB_AP_REQ message into outbuf, with more complete options than
42 krb_mk_req.
43
44 outbuf, ap_req_options, checksum, and ccache are used in the
45 same fashion as for krb5_mk_req.
46
47 creds is used to supply the credentials (ticket and session key) needed
48 to form the request.
49
50 if creds->ticket has no data (length == 0), then a ticket is obtained
51 from either the cache or the TGS, passing creds to krb5_get_credentials().
52 kdc_options specifies the options requested for the ticket to be used.
53 If a ticket with appropriate flags is not found in the cache, then these
54 options are passed on in a request to an appropriate KDC.
55
56 ap_req_options specifies the KRB_AP_REQ options desired.
57
58 if ap_req_options specifies AP_OPTS_USE_SESSION_KEY, then creds->ticket
59 must contain the appropriate ENC-TKT-IN-SKEY ticket.
60
61 checksum specifies the checksum to be used in the authenticator.
62
63 The outbuf buffer storage is allocated, and should be freed by the
64 caller when finished.
65
66 On an error return, the credentials pointed to by creds might have been
67 augmented with additional fields from the obtained credentials; the entire
68 credentials should be released by calling krb5_free_creds().
69
70 returns system errors
71*/
72
73static krb5_error_code
74krb5_generate_authenticator (krb5_context,
75				       krb5_authenticator *, krb5_principal,
76				       krb5_checksum *, krb5_keyblock *,
77				       krb5_ui_4, krb5_authdata ** );
78
79krb5_error_code
80krb5int_generate_and_save_subkey (krb5_context context,
81				  krb5_auth_context auth_context,
82				  krb5_keyblock *keyblock)
83{
84#if 0
85    /*
86     * Solaris Kerberos:  Don't bother with this PRNG stuff,
87     * we have /dev/random and PKCS#11 to handle Random Numbers.
88     */
89    /* Provide some more fodder for random number code.
90       This isn't strong cryptographically; the point here is not
91       to guarantee randomness, but to make it less likely that multiple
92       sessions could pick the same subkey.  */
93    struct {
94	krb5_int32 sec, usec;
95    } rnd_data;
96    krb5_data d;
97
98    krb5_crypto_us_timeofday (&rnd_data.sec, &rnd_data.usec);
99    d.length = sizeof (rnd_data);
100    d.data = (char *) &rnd_data;
101    (void) krb5_c_random_add_entropy (context, KRB5_C_RANDSOURCE_TIMING, &d);
102#endif
103    krb5_error_code retval;
104
105    /* Solaris Kerberos */
106    if (auth_context->send_subkey != NULL) {
107	krb5_free_keyblock(context, auth_context->send_subkey);
108	auth_context->send_subkey = NULL;
109    }
110
111    if ((retval = krb5_generate_subkey(context, keyblock, &auth_context->send_subkey)))
112	return retval;
113
114    /* Solaris Kerberos */
115    if (auth_context->recv_subkey != NULL) {
116	krb5_free_keyblock(context, auth_context->recv_subkey);
117	auth_context->recv_subkey = NULL;
118    }
119    retval = krb5_copy_keyblock(context, auth_context->send_subkey,
120				&auth_context->recv_subkey);
121    if (retval) {
122	krb5_free_keyblock(context, auth_context->send_subkey);
123	auth_context->send_subkey = NULL;
124	return retval;
125    }
126    return 0;
127}
128
129krb5_error_code KRB5_CALLCONV
130krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
131		     krb5_flags ap_req_options, krb5_data *in_data,
132		     krb5_creds *in_creds, krb5_data *outbuf)
133{
134    krb5_error_code 	  retval;
135    krb5_checksum	  checksum;
136    krb5_checksum	  *checksump = 0;
137    krb5_auth_context	  new_auth_context;
138
139    krb5_ap_req request;
140    krb5_data *scratch = 0;
141    krb5_data *toutbuf;
142
143    request.ap_options = ap_req_options & AP_OPTS_WIRE_MASK;
144    request.authenticator.ciphertext.data = 0;
145    request.ticket = 0;
146
147    if (!in_creds->ticket.length)
148	return(KRB5_NO_TKT_SUPPLIED);
149
150    /* we need a native ticket */
151    if ((retval = decode_krb5_ticket(&(in_creds)->ticket, &request.ticket)))
152	return(retval);
153
154    /* verify that the ticket is not expired */
155    if ((retval = krb5_validate_times(context, &in_creds->times)) != 0)
156	goto cleanup;
157
158    /* generate auth_context if needed */
159    if (*auth_context == NULL) {
160	if ((retval = krb5_auth_con_init(context, &new_auth_context)))
161	    goto cleanup;
162	*auth_context = new_auth_context;
163    }
164
165    if ((*auth_context)->keyblock != NULL) {
166	krb5_free_keyblock(context, (*auth_context)->keyblock);
167	(*auth_context)->keyblock = NULL;
168    }
169
170    /* set auth context keyblock */
171    if ((retval = krb5_copy_keyblock(context, &in_creds->keyblock,
172				     &((*auth_context)->keyblock))))
173	goto cleanup;
174
175    /* generate seq number if needed */
176    if ((((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
177     || ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
178      && ((*auth_context)->local_seq_number == 0))
179	if ((retval = krb5_generate_seq_number(context, &in_creds->keyblock,
180				     &(*auth_context)->local_seq_number)))
181	    goto cleanup;
182
183
184    /* generate subkey if needed */
185    if (!in_data &&(*auth_context)->checksum_func) {
186	retval = (*auth_context)->checksum_func( context,
187						 *auth_context,
188						 (*auth_context)->checksum_func_data,
189						 &in_data);
190	if (retval)
191	    goto cleanup;
192    }
193
194    if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->send_subkey)) {
195	retval = krb5int_generate_and_save_subkey (context, *auth_context,
196						   &in_creds->keyblock);
197	if (retval)
198	    goto cleanup;
199    }
200
201
202    if (in_data) {
203
204      if ((*auth_context)->req_cksumtype == 0x8003) {
205	    /* XXX Special hack for GSSAPI */
206	    checksum.checksum_type = 0x8003;
207	    checksum.length = in_data->length;
208	    checksum.contents = (krb5_octet *) in_data->data;
209	} else {
210	    if ((retval = krb5_c_make_checksum(context,
211					       (*auth_context)->req_cksumtype,
212					       (*auth_context)->keyblock,
213					       KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM,
214					       in_data, &checksum)))
215		goto cleanup_cksum;
216	}
217	checksump = &checksum;
218    }
219
220    /* Generate authenticator */
221    if (((*auth_context)->authentp = (krb5_authenticator *)malloc(sizeof(
222					krb5_authenticator))) == NULL) {
223	retval = ENOMEM;
224	goto cleanup_cksum;
225    }
226
227    if ((retval = krb5_generate_authenticator(context,
228					      (*auth_context)->authentp,
229					      (in_creds)->client, checksump,
230					      (*auth_context)->send_subkey,
231					      (*auth_context)->local_seq_number,
232					      (in_creds)->authdata)))
233	goto cleanup_cksum;
234
235    /* encode the authenticator */
236    if ((retval = encode_krb5_authenticator((*auth_context)->authentp,
237					    &scratch)))
238	goto cleanup_cksum;
239
240    /* Null out these fields, to prevent pointer sharing problems;
241     * they were supplied by the caller
242     */
243    (*auth_context)->authentp->client = NULL;
244    (*auth_context)->authentp->checksum = NULL;
245    (*auth_context)->authentp->authorization_data = NULL;
246
247    /* call the encryption routine */
248    if ((retval = krb5_encrypt_helper(context, &in_creds->keyblock,
249				      KRB5_KEYUSAGE_AP_REQ_AUTH,
250				      scratch, &request.authenticator)))
251	goto cleanup_cksum;
252
253    if ((retval = encode_krb5_ap_req(&request, &toutbuf)))
254	goto cleanup_cksum;
255    *outbuf = *toutbuf;
256
257    krb5_xfree(toutbuf);
258
259cleanup_cksum:
260    if (checksump && checksump->checksum_type != 0x8003)
261      free(checksump->contents);
262
263cleanup:
264    if (request.ticket)
265	krb5_free_ticket(context, request.ticket);
266    if (request.authenticator.ciphertext.data) {
267    	(void) memset(request.authenticator.ciphertext.data, 0,
268		      request.authenticator.ciphertext.length);
269	free(request.authenticator.ciphertext.data);
270    }
271    if (scratch) {
272	memset(scratch->data, 0, scratch->length);
273        krb5_xfree(scratch->data);
274	krb5_xfree(scratch);
275    }
276    return retval;
277}
278
279static krb5_error_code
280krb5_generate_authenticator(krb5_context context, krb5_authenticator *authent, krb5_principal client, krb5_checksum *cksum, krb5_keyblock *key, krb5_ui_4 seq_number, krb5_authdata **authorization)
281{
282    krb5_error_code retval;
283
284    authent->client = client;
285    authent->checksum = cksum;
286    if (key) {
287	retval = krb5_copy_keyblock(context, key, &authent->subkey);
288	if (retval)
289	    return retval;
290    } else
291	authent->subkey = 0;
292    authent->seq_number = seq_number;
293    authent->authorization_data = authorization;
294
295    return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec));
296}
297