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 
73 static krb5_error_code
74 krb5_generate_authenticator (krb5_context,
75 				       krb5_authenticator *, krb5_principal,
76 				       krb5_checksum *, krb5_keyblock *,
77 				       krb5_ui_4, krb5_authdata ** );
78 
79 krb5_error_code
krb5int_generate_and_save_subkey(krb5_context context,krb5_auth_context auth_context,krb5_keyblock * keyblock)80 krb5int_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 
129 krb5_error_code KRB5_CALLCONV
krb5_mk_req_extended(krb5_context context,krb5_auth_context * auth_context,krb5_flags ap_req_options,krb5_data * in_data,krb5_creds * in_creds,krb5_data * outbuf)130 krb5_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 
259 cleanup_cksum:
260     if (checksump && checksump->checksum_type != 0x8003)
261       free(checksump->contents);
262 
263 cleanup:
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 
279 static krb5_error_code
krb5_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)280 krb5_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