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