xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/rd_req_dec.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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/rd_req_dec.c
9  *
10  * Copyright (c) 1994 CyberSAFE Corporation.
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  * Neither M.I.T., the Open Computing Security Group, nor
30  * CyberSAFE Corporation make any representations about the suitability of
31  * this software for any purpose.  It is provided "as is" without express
32  * or implied warranty.
33  *
34  *
35  * krb5_rd_req_decoded()
36  */
37 
38 #include "k5-int.h"
39 #include "auth_con.h"
40 
41 /*
42  * essentially the same as krb_rd_req, but uses a decoded AP_REQ as
43  * the input rather than an encoded input.
44  */
45 /*
46  *  Parses a KRB_AP_REQ message, returning its contents.
47  *
48  *  server specifies the expected server's name for the ticket; if NULL, then
49  *  any server will be accepted if the key can be found, and the caller should
50  *  verify that the principal is something it trusts.
51  *
52  *  rcache specifies a replay detection cache used to store authenticators and
53  *  server names
54  *
55  *  keyproc specifies a procedure to generate a decryption key for the
56  *  ticket.  If keyproc is non-NULL, keyprocarg is passed to it, and the result
57  *  used as a decryption key. If keyproc is NULL, then fetchfrom is checked;
58  *  if it is non-NULL, it specifies a parameter name from which to retrieve the
59  *  decryption key.  If fetchfrom is NULL, then the default key store is
60  *  consulted.
61  *
62  *  authdat is set to point at allocated storage structures; the caller
63  *  should free them when finished.
64  *
65  *  returns system errors, encryption errors, replay errors
66  */
67 
68 static krb5_error_code decrypt_authenticator
69 	(krb5_context, const krb5_ap_req *, krb5_authenticator **,
70 		   int);
71 
72 #define in_clock_skew(date) (labs((date)-currenttime) < context->clockskew)
73 
74 static krb5_error_code
75 krb5_rd_req_decrypt_tkt_part(krb5_context context, const krb5_ap_req *req, krb5_keytab keytab)
76 {
77     krb5_error_code 	  retval;
78     krb5_enctype 	  enctype;
79     krb5_keytab_entry 	  ktent;
80 
81     enctype = req->ticket->enc_part.enctype;
82 
83     /* Solaris Kerberos: */
84     memset(&ktent, 0, sizeof(krb5_keytab_entry));
85     if ((retval = krb5_kt_get_entry(context, keytab, req->ticket->server,
86 				    req->ticket->enc_part.kvno,
87 				    enctype, &ktent)))
88 	return retval;
89 
90 
91     /*
92      * Solaris Kerberos:
93      * If we get this far then we know that the enc types are similar,
94      * therefore we should change the enc type to match that of what
95      * we are decrypting.
96      */
97     ktent.key.enctype = enctype;
98 
99     retval = krb5_decrypt_tkt_part(context, &ktent.key, req->ticket);
100     /* Upon error, Free keytab entry first, then return */
101 
102     (void) krb5_kt_free_entry(context, &ktent);
103     return retval;
104 }
105 
106 static krb5_error_code
107 krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
108 			const krb5_ap_req *req, krb5_const_principal server,
109 			krb5_keytab keytab, krb5_flags *ap_req_options,
110 			krb5_ticket **ticket, int check_valid_flag)
111 {
112     krb5_error_code 	  retval = 0;
113     krb5_timestamp 	  currenttime;
114     krb5_principal_data princ_data;
115 
116     req->ticket->enc_part2 == NULL;
117     if (server && krb5_is_referral_realm(&server->realm)) {
118 	char *realm;
119 	princ_data = *server;
120 	server = &princ_data;
121 	retval = krb5_get_default_realm(context, &realm);
122 	if (retval)
123 	    return retval;
124 	princ_data.realm.data = realm;
125 	princ_data.realm.length = strlen(realm);
126     }
127     if (server && !krb5_principal_compare(context, server, req->ticket->server)) {
128 	char *found_name = 0, *wanted_name = 0;
129 	if (krb5_unparse_name(context, server, &wanted_name) == 0
130 	    && krb5_unparse_name(context, req->ticket->server, &found_name) == 0)
131 	    krb5_set_error_message(context, KRB5KRB_AP_WRONG_PRINC,
132 				   "Wrong principal in request (found %s, wanted %s)",
133 				   found_name, wanted_name);
134 	krb5_free_unparsed_name(context, wanted_name);
135 	krb5_free_unparsed_name(context, found_name);
136 	retval =  KRB5KRB_AP_WRONG_PRINC;
137 	goto cleanup;
138     }
139 
140     /* if (req->ap_options & AP_OPTS_USE_SESSION_KEY)
141        do we need special processing here ?	*/
142 
143     /* decrypt the ticket */
144     if ((*auth_context)->keyblock) { /* User to User authentication */
145     	if ((retval = krb5_decrypt_tkt_part(context, (*auth_context)->keyblock,
146 					    req->ticket)))
147 goto cleanup;
148 	krb5_free_keyblock(context, (*auth_context)->keyblock);
149 	(*auth_context)->keyblock = NULL;
150     } else {
151     	if ((retval = krb5_rd_req_decrypt_tkt_part(context, req, keytab)))
152 	    goto cleanup;
153     }
154 
155     /* XXX this is an evil hack.  check_valid_flag is set iff the call
156        is not from inside the kdc.  we can use this to determine which
157        key usage to use */
158     if ((retval = decrypt_authenticator(context, req,
159 					&((*auth_context)->authentp),
160 					check_valid_flag)))
161 	goto cleanup;
162 
163     if (!krb5_principal_compare(context, (*auth_context)->authentp->client,
164 				req->ticket->enc_part2->client)) {
165 	retval = KRB5KRB_AP_ERR_BADMATCH;
166 	goto cleanup;
167     }
168 
169     if ((*auth_context)->remote_addr &&
170       !krb5_address_search(context, (*auth_context)->remote_addr,
171 			   req->ticket->enc_part2->caddrs)) {
172 	retval = KRB5KRB_AP_ERR_BADADDR;
173 	goto cleanup;
174     }
175 
176     /* okay, now check cross-realm policy */
177 
178 #if defined(_SINGLE_HOP_ONLY)
179 
180     /* Single hop cross-realm tickets only */
181 
182     {
183 	krb5_transited *trans = &(req->ticket->enc_part2->transited);
184 
185       	/* If the transited list is empty, then we have at most one hop */
186       	if (trans->tr_contents.data && trans->tr_contents.data[0])
187             retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
188     }
189 
190 #elif defined(_NO_CROSS_REALM)
191 
192     /* No cross-realm tickets */
193 
194     {
195 	char		* lrealm;
196       	krb5_data      	* realm;
197       	krb5_transited 	* trans;
198 
199 	realm = krb5_princ_realm(context, req->ticket->enc_part2->client);
200 	trans = &(req->ticket->enc_part2->transited);
201 
202 	/*
203       	 * If the transited list is empty, then we have at most one hop
204       	 * So we also have to check that the client's realm is the local one
205 	 */
206       	krb5_get_default_realm(context, &lrealm);
207       	if ((trans->tr_contents.data && trans->tr_contents.data[0]) ||
208           strlen(lrealm) != realm->length ||
209           memcmp(lrealm, realm->data, strlen(lrealm))) {
210             retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
211       	}
212       	free(lrealm);
213     }
214 
215 #else
216 
217     /* Hierarchical Cross-Realm */
218 
219     {
220       	krb5_data      * realm;
221       	krb5_transited * trans;
222 
223 	realm = krb5_princ_realm(context, req->ticket->enc_part2->client);
224 	trans = &(req->ticket->enc_part2->transited);
225 
226 	/*
227       	 * If the transited list is not empty, then check that all realms
228       	 * transited are within the hierarchy between the client's realm
229       	 * and the local realm.
230   	 */
231 	if (trans->tr_contents.data && trans->tr_contents.data[0]) {
232 	    retval = krb5_check_transited_list(context, &(trans->tr_contents),
233 					       realm,
234 					       krb5_princ_realm (context,
235 								 server));
236       	}
237     }
238 
239 #endif
240 
241     if (retval)  goto cleanup;
242 
243     /* only check rcache if sender has provided one---some services
244        may not be able to use replay caches (such as datagram servers) */
245 
246     if ((*auth_context)->rcache) {
247 	krb5_donot_replay  rep;
248         krb5_tkt_authent   tktauthent;
249 
250 	tktauthent.ticket = req->ticket;
251 	tktauthent.authenticator = (*auth_context)->authentp;
252 	if (!(retval = krb5_auth_to_rep(context, &tktauthent, &rep))) {
253 	    retval = krb5_rc_store(context, (*auth_context)->rcache, &rep);
254 	    krb5_xfree(rep.server);
255 	    krb5_xfree(rep.client);
256 	}
257 
258 	if (retval)
259 	    goto cleanup;
260     }
261 
262     retval = krb5_validate_times(context, &req->ticket->enc_part2->times);
263     if (retval != 0)
264 	    goto cleanup;
265 
266     if ((retval = krb5_timeofday(context, &currenttime)))
267 	goto cleanup;
268 
269     if (!in_clock_skew((*auth_context)->authentp->ctime)) {
270 	retval = KRB5KRB_AP_ERR_SKEW;
271 	goto cleanup;
272     }
273 
274     if (check_valid_flag) {
275       if (req->ticket->enc_part2->flags & TKT_FLG_INVALID) {
276 	retval = KRB5KRB_AP_ERR_TKT_INVALID;
277 	goto cleanup;
278       }
279     }
280 
281     /* check if the various etypes are permitted */
282 
283     if ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_PERMIT_ALL) {
284 	/* no etype check needed */
285 	/*EMPTY*/
286 	;
287     } else if ((*auth_context)->permitted_etypes == NULL) {
288 	int etype;
289 	/* check against the default set */
290 	if ((!krb5_is_permitted_enctype(context,
291 					etype = req->ticket->enc_part.enctype)) ||
292 	    (!krb5_is_permitted_enctype(context,
293 					etype = req->ticket->enc_part2->session->enctype)) ||
294 	    (((*auth_context)->authentp->subkey) &&
295 	     !krb5_is_permitted_enctype(context,
296 					etype = (*auth_context)->authentp->subkey->enctype))) {
297 	    char enctype_name[30];
298 	    retval = KRB5_NOPERM_ETYPE;
299 	    if (krb5_enctype_to_string(etype, enctype_name, sizeof(enctype_name)) == 0)
300 		krb5_set_error_message(context, retval,
301 				       "Encryption type %s not permitted",
302 				       enctype_name);
303 	    goto cleanup;
304 	}
305     } else {
306 	/* check against the set in the auth_context */
307 	int i;
308 
309 	for (i=0; (*auth_context)->permitted_etypes[i]; i++)
310 	    if ((*auth_context)->permitted_etypes[i] ==
311 		req->ticket->enc_part.enctype)
312 		break;
313 	if (!(*auth_context)->permitted_etypes[i]) {
314 	    char enctype_name[30];
315 	    retval = KRB5_NOPERM_ETYPE;
316 	    if (krb5_enctype_to_string(req->ticket->enc_part.enctype,
317 				       enctype_name, sizeof(enctype_name)) == 0)
318 		krb5_set_error_message(context, retval,
319 				       "Encryption type %s not permitted",
320 				       enctype_name);
321 	    goto cleanup;
322 	}
323 
324 	for (i=0; (*auth_context)->permitted_etypes[i]; i++)
325 	    if ((*auth_context)->permitted_etypes[i] ==
326 		req->ticket->enc_part2->session->enctype)
327 		break;
328 	if (!(*auth_context)->permitted_etypes[i]) {
329 	    char enctype_name[30];
330 	    retval = KRB5_NOPERM_ETYPE;
331 	    if (krb5_enctype_to_string(req->ticket->enc_part2->session->enctype,
332 				       enctype_name, sizeof(enctype_name)) == 0)
333 		krb5_set_error_message(context, retval,
334 				       "Encryption type %s not permitted",
335 				       enctype_name);
336 	    goto cleanup;
337 	}
338 
339 	if ((*auth_context)->authentp->subkey) {
340 	    for (i=0; (*auth_context)->permitted_etypes[i]; i++)
341 		if ((*auth_context)->permitted_etypes[i] ==
342 		    (*auth_context)->authentp->subkey->enctype)
343 		    break;
344 	    if (!(*auth_context)->permitted_etypes[i]) {
345 		char enctype_name[30];
346 		retval = KRB5_NOPERM_ETYPE;
347 		if (krb5_enctype_to_string((*auth_context)->authentp->subkey->enctype,
348 					   enctype_name,
349 					   sizeof(enctype_name)) == 0)
350 		    krb5_set_error_message(context, retval,
351 					   "Encryption type %s not permitted",
352 					   enctype_name);
353 		goto cleanup;
354 	    }
355 	}
356     }
357 
358     (*auth_context)->remote_seq_number = (*auth_context)->authentp->seq_number;
359     if ((*auth_context)->authentp->subkey) {
360 	/* Solaris Kerberos */
361 	if ((*auth_context)->recv_subkey != NULL) {
362 	    krb5_free_keyblock(context, (*auth_context)->recv_subkey);
363 	    (*auth_context)->recv_subkey = NULL;
364 	}
365 
366 	if ((retval = krb5_copy_keyblock(context,
367 					 (*auth_context)->authentp->subkey,
368 					 &((*auth_context)->recv_subkey))))
369 	    goto cleanup;
370 	/* Solaris Kerberos */
371 	if ((*auth_context)->send_subkey != NULL) {
372 	    krb5_free_keyblock(context, (*auth_context)->send_subkey);
373 	    (*auth_context)->send_subkey = NULL;
374 	}
375 
376 	retval = krb5_copy_keyblock(context, (*auth_context)->authentp->subkey,
377 				    &((*auth_context)->send_subkey));
378 	if (retval) {
379 	    krb5_free_keyblock(context, (*auth_context)->recv_subkey);
380 	    (*auth_context)->recv_subkey = NULL;
381 	    goto cleanup;
382 	}
383     } else {
384 	(*auth_context)->recv_subkey = 0;
385 	(*auth_context)->send_subkey = 0;
386     }
387     /* Solaris Kerberos */
388     if ((*auth_context)->keyblock != NULL) {
389 	krb5_free_keyblock(context, (*auth_context)->keyblock);
390 	(*auth_context)->keyblock = NULL;
391     }
392     if ((retval = krb5_copy_keyblock(context, req->ticket->enc_part2->session,
393 				     &((*auth_context)->keyblock))))
394 	goto cleanup;
395 
396     /*
397      * If not AP_OPTS_MUTUAL_REQUIRED then and sequence numbers are used
398      * then the default sequence number is the one's complement of the
399      * sequence number sent ot us.
400      */
401     if ((!(req->ap_options & AP_OPTS_MUTUAL_REQUIRED)) &&
402       (*auth_context)->remote_seq_number) {
403 	(*auth_context)->local_seq_number ^=
404 	  (*auth_context)->remote_seq_number;
405     }
406 
407     if (ticket)
408    	if ((retval = krb5_copy_ticket(context, req->ticket, ticket)))
409 	    goto cleanup;
410     if (ap_req_options)
411     	*ap_req_options = req->ap_options;
412     retval = 0;
413 
414 cleanup:
415     if (server == &princ_data)
416 	krb5_free_default_realm(context, princ_data.realm.data);
417     if (retval) {
418 	/* only free if we're erroring out...otherwise some
419 	   applications will need the output. */
420 	if (req->ticket->enc_part2)
421 	    krb5_free_enc_tkt_part(context, req->ticket->enc_part2);
422 	req->ticket->enc_part2 = NULL;
423     }
424     return retval;
425 }
426 
427 krb5_error_code
428 krb5_rd_req_decoded(krb5_context context, krb5_auth_context *auth_context,
429 		    const krb5_ap_req *req, krb5_const_principal server,
430 		    krb5_keytab keytab, krb5_flags *ap_req_options,
431 		    krb5_ticket **ticket)
432 {
433   krb5_error_code retval;
434   retval = krb5_rd_req_decoded_opt(context, auth_context,
435 				   req, server, keytab,
436 				   ap_req_options, ticket,
437 				   1); /* check_valid_flag */
438   return retval;
439 }
440 
441 krb5_error_code
442 krb5_rd_req_decoded_anyflag(krb5_context context,
443 			    krb5_auth_context *auth_context,
444 			    const krb5_ap_req *req,
445 			    krb5_const_principal server, krb5_keytab keytab,
446 			    krb5_flags *ap_req_options, krb5_ticket **ticket)
447 {
448   krb5_error_code retval;
449   retval = krb5_rd_req_decoded_opt(context, auth_context,
450 				   req, server, keytab,
451 				   ap_req_options, ticket,
452 				   0); /* don't check_valid_flag */
453   return retval;
454 }
455 
456 /*ARGSUSED*/
457 static krb5_error_code
458 decrypt_authenticator(krb5_context context, const krb5_ap_req *request,
459 		      krb5_authenticator **authpp, int is_ap_req)
460 {
461     krb5_authenticator *local_auth;
462     krb5_error_code retval;
463     krb5_data scratch;
464     krb5_keyblock *sesskey;
465 
466     sesskey = request->ticket->enc_part2->session;
467 
468     scratch.length = request->authenticator.ciphertext.length;
469     if (!(scratch.data = malloc(scratch.length)))
470 	return(ENOMEM);
471 
472     if ((retval = krb5_c_decrypt(context, sesskey,
473 				 is_ap_req?KRB5_KEYUSAGE_AP_REQ_AUTH:
474 				 KRB5_KEYUSAGE_TGS_REQ_AUTH, 0,
475 				 &request->authenticator, &scratch))) {
476 	free(scratch.data);
477 	return(retval);
478     }
479 
480 #define clean_scratch() {memset(scratch.data, 0, scratch.length); \
481 free(scratch.data);}
482 
483     /*  now decode the decrypted stuff */
484     if (!(retval = decode_krb5_authenticator(&scratch, &local_auth))) {
485 	*authpp = local_auth;
486     }
487     clean_scratch();
488     return retval;
489 }
490