xref: /titanic_52/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/rd_req_dec.c (revision 5e01956f3000408c2a2c5a08c8d0acf2c2a9d8ee)
1 /*
2  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * lib/krb5/krb/rd_req_dec.c
6  *
7  * Copyright (c) 1994 CyberSAFE Corporation.
8  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
9  * All Rights Reserved.
10  *
11  * Export of this software from the United States of America may
12  *   require a specific license from the United States Government.
13  *   It is the responsibility of any person or organization contemplating
14  *   export to obtain such a license before exporting.
15  *
16  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
17  * distribute this software and its documentation for any purpose and
18  * without fee is hereby granted, provided that the above copyright
19  * notice appear in all copies and that both that copyright notice and
20  * this permission notice appear in supporting documentation, and that
21  * the name of M.I.T. not be used in advertising or publicity pertaining
22  * to distribution of the software without specific, written prior
23  * permission.  Furthermore if you modify this software you must label
24  * your software as modified software and not distribute it in such a
25  * fashion that it might be confused with the original M.I.T. software.
26  * Neither M.I.T., the Open Computing Security Group, nor
27  * CyberSAFE Corporation make any representations about the suitability of
28  * this software for any purpose.  It is provided "as is" without express
29  * or implied warranty.
30  *
31  *
32  * krb5_rd_req_decoded()
33  */
34 
35 #include "k5-int.h"
36 #include "auth_con.h"
37 #include <locale.h>
38 #include <syslog.h>
39 
40 /*
41  * essentially the same as krb_rd_req, but uses a decoded AP_REQ as
42  * the input rather than an encoded input.
43  */
44 /*
45  *  Parses a KRB_AP_REQ message, returning its contents.
46  *
47  *  server specifies the expected server's name for the ticket; if NULL, then
48  *  any server will be accepted if the key can be found, and the caller should
49  *  verify that the principal is something it trusts.
50  *
51  *  rcache specifies a replay detection cache used to store authenticators and
52  *  server names
53  *
54  *  keyproc specifies a procedure to generate a decryption key for the
55  *  ticket.  If keyproc is non-NULL, keyprocarg is passed to it, and the result
56  *  used as a decryption key. If keyproc is NULL, then fetchfrom is checked;
57  *  if it is non-NULL, it specifies a parameter name from which to retrieve the
58  *  decryption key.  If fetchfrom is NULL, then the default key store is
59  *  consulted.
60  *
61  *  authdat is set to point at allocated storage structures; the caller
62  *  should free them when finished.
63  *
64  *  returns system errors, encryption errors, replay errors
65  */
66 
67 static krb5_error_code decrypt_authenticator
68 	(krb5_context, const krb5_ap_req *, krb5_authenticator **,
69 		   int);
70 
71 
72 static krb5_error_code
73 krb5_rd_req_decrypt_tkt_part(krb5_context context, const krb5_ap_req *req, krb5_keytab keytab)
74 {
75     krb5_error_code 	  retval;
76     krb5_enctype 	  enctype;
77     krb5_keytab_entry 	  ktent;
78 
79     enctype = req->ticket->enc_part.enctype;
80 
81     /* Solaris Kerberos: */
82     memset(&ktent, 0, sizeof(krb5_keytab_entry));
83     if ((retval = krb5_kt_get_entry(context, keytab, req->ticket->server,
84 				    req->ticket->enc_part.kvno,
85 				    enctype, &ktent)))
86 	return retval;
87 
88 
89     /*
90      * Solaris Kerberos:
91      * If we get this far then we know that the enc types are similar,
92      * therefore we should change the enc type to match that of what
93      * we are decrypting.
94      */
95     ktent.key.enctype = enctype;
96 
97     retval = krb5_decrypt_tkt_part(context, &ktent.key, req->ticket);
98     /* Upon error, Free keytab entry first, then return */
99 
100     if (retval == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
101         /* Solaris Kerberos: spruce-up the err msg */
102         krb5_principal princ = (krb5_principal) req->ticket->server;
103 	char *s_name = NULL;
104 	int kret = krb5_unparse_name(context, princ, &s_name);
105 	if (kret == 0) {
106 	    krb5_set_error_message(context, retval,
107 				dgettext(TEXT_DOMAIN,
108 					"AP Request ticket decrypt fail for principal '%s' (kvno=%d, enctype=%d)"),
109 				s_name,
110 				req->ticket->enc_part.kvno,
111 				enctype);
112 	   krb5_free_unparsed_name(context, s_name);
113 	}
114     }
115 
116     (void) krb5_kt_free_entry(context, &ktent);
117     return retval;
118 }
119 
120 /*
121  * Solaris Kerberos
122  * Same as krb5int_check_clockskew() plus return the skew in seconds.
123  */
124 static krb5_error_code
125 krb5int_check_clockskew2(krb5_context context,
126 			krb5_timestamp date,
127 			krb5_timestamp *ret_skew)
128 {
129     krb5_timestamp currenttime, skew;
130     krb5_error_code retval;
131 
132     retval = krb5_timeofday(context, &currenttime);
133     if (retval)
134         return retval;
135 
136     skew = labs((date)-currenttime);
137     if (!(skew < context->clockskew)) {
138         *ret_skew = skew;
139         return KRB5KRB_AP_ERR_SKEW;
140     }
141 
142     return 0;
143 }
144 
145 static krb5_error_code
146 krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
147 			const krb5_ap_req *req, krb5_const_principal server,
148 			krb5_keytab keytab, krb5_flags *ap_req_options,
149 			krb5_ticket **ticket, int check_valid_flag)
150 {
151     krb5_error_code 	  retval = 0;
152     krb5_principal_data princ_data;
153     krb5_timestamp	  skew = 0; /* Solaris Kerberos */
154 
155     req->ticket->enc_part2 == NULL;
156     if (server && krb5_is_referral_realm(&server->realm)) {
157 	char *realm;
158 	princ_data = *server;
159 	server = &princ_data;
160 	retval = krb5_get_default_realm(context, &realm);
161 	if (retval)
162 	    return retval;
163 	princ_data.realm.data = realm;
164 	princ_data.realm.length = strlen(realm);
165     }
166     if (server && !krb5_principal_compare(context, server, req->ticket->server)) {
167 	char *found_name = 0, *wanted_name = 0;
168 	if (krb5_unparse_name(context, server, &wanted_name) == 0
169 	    && krb5_unparse_name(context, req->ticket->server, &found_name) == 0)
170 	    krb5_set_error_message(context, KRB5KRB_AP_WRONG_PRINC,
171 				dgettext(TEXT_DOMAIN,
172 					"Wrong principal in request (found %s, wanted %s)"),
173 				   found_name, wanted_name);
174 	krb5_free_unparsed_name(context, wanted_name);
175 	krb5_free_unparsed_name(context, found_name);
176 	retval =  KRB5KRB_AP_WRONG_PRINC;
177 	goto cleanup;
178     }
179 
180     /* if (req->ap_options & AP_OPTS_USE_SESSION_KEY)
181        do we need special processing here ?	*/
182 
183     /* decrypt the ticket */
184     if ((*auth_context)->keyblock) { /* User to User authentication */
185     	if ((retval = krb5_decrypt_tkt_part(context, (*auth_context)->keyblock,
186 					    req->ticket)))
187 goto cleanup;
188 	krb5_free_keyblock(context, (*auth_context)->keyblock);
189 	(*auth_context)->keyblock = NULL;
190     } else {
191     	if ((retval = krb5_rd_req_decrypt_tkt_part(context, req, keytab)))
192 	    goto cleanup;
193     }
194 
195     /* XXX this is an evil hack.  check_valid_flag is set iff the call
196        is not from inside the kdc.  we can use this to determine which
197        key usage to use */
198     if ((retval = decrypt_authenticator(context, req,
199 					&((*auth_context)->authentp),
200 					check_valid_flag)))
201 	goto cleanup;
202 
203     if (!krb5_principal_compare(context, (*auth_context)->authentp->client,
204 				req->ticket->enc_part2->client)) {
205 	retval = KRB5KRB_AP_ERR_BADMATCH;
206 	goto cleanup;
207     }
208 
209     if ((*auth_context)->remote_addr &&
210       !krb5_address_search(context, (*auth_context)->remote_addr,
211 			   req->ticket->enc_part2->caddrs)) {
212 	retval = KRB5KRB_AP_ERR_BADADDR;
213 	goto cleanup;
214     }
215 
216     /* okay, now check cross-realm policy */
217 
218 #if defined(_SINGLE_HOP_ONLY)
219 
220     /* Single hop cross-realm tickets only */
221 
222     {
223 	krb5_transited *trans = &(req->ticket->enc_part2->transited);
224 
225       	/* If the transited list is empty, then we have at most one hop */
226       	if (trans->tr_contents.data && trans->tr_contents.data[0])
227             retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
228     }
229 
230 #elif defined(_NO_CROSS_REALM)
231 
232     /* No cross-realm tickets */
233 
234     {
235 	char		* lrealm;
236       	krb5_data      	* realm;
237       	krb5_transited 	* trans;
238 
239 	realm = krb5_princ_realm(context, req->ticket->enc_part2->client);
240 	trans = &(req->ticket->enc_part2->transited);
241 
242 	/*
243       	 * If the transited list is empty, then we have at most one hop
244       	 * So we also have to check that the client's realm is the local one
245 	 */
246       	krb5_get_default_realm(context, &lrealm);
247       	if ((trans->tr_contents.data && trans->tr_contents.data[0]) ||
248           strlen(lrealm) != realm->length ||
249           memcmp(lrealm, realm->data, strlen(lrealm))) {
250             retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
251       	}
252       	free(lrealm);
253     }
254 
255 #else
256 
257     /* Hierarchical Cross-Realm */
258 
259     {
260       	krb5_data      * realm;
261       	krb5_transited * trans;
262 
263 	realm = krb5_princ_realm(context, req->ticket->enc_part2->client);
264 	trans = &(req->ticket->enc_part2->transited);
265 
266 	/*
267       	 * If the transited list is not empty, then check that all realms
268       	 * transited are within the hierarchy between the client's realm
269       	 * and the local realm.
270   	 */
271 	if (trans->tr_contents.data && trans->tr_contents.data[0]) {
272 	    retval = krb5_check_transited_list(context, &(trans->tr_contents),
273 					       realm,
274 					       krb5_princ_realm (context,
275 								 server));
276       	}
277     }
278 
279 #endif
280 
281     if (retval)  goto cleanup;
282 
283     /* only check rcache if sender has provided one---some services
284        may not be able to use replay caches (such as datagram servers) */
285 
286     if ((*auth_context)->rcache) {
287 	krb5_donot_replay  rep;
288         krb5_tkt_authent   tktauthent;
289 
290 	tktauthent.ticket = req->ticket;
291 	tktauthent.authenticator = (*auth_context)->authentp;
292 	if (!(retval = krb5_auth_to_rep(context, &tktauthent, &rep))) {
293 	    retval = krb5_rc_store(context, (*auth_context)->rcache, &rep);
294 	    krb5_xfree(rep.server);
295 	    krb5_xfree(rep.client);
296 	}
297 
298 	if (retval == KRB5KRB_AP_ERR_SKEW)
299 	    goto err_skew;
300 
301 	if (retval)
302 	    goto cleanup;
303     }
304 
305     retval = krb5_validate_times(context, &req->ticket->enc_part2->times);
306     if (retval != 0)
307 	    goto cleanup;
308 
309 err_skew:
310     if ((retval = krb5int_check_clockskew2(context,
311 					(*auth_context)->authentp->ctime,
312 					&skew))) {
313         /* Solaris Kerberos */
314         char *s_name = NULL;
315         char *c_name = NULL;
316 	krb5_error_code serr, cerr;
317 	serr = krb5_unparse_name(context, req->ticket->server, &s_name);
318 	cerr = krb5_unparse_name(context, req->ticket->enc_part2->client,
319 				&c_name);
320 	krb5_set_error_message(context, retval,
321 			    dgettext(TEXT_DOMAIN,
322 				    "Clock skew too great: client '%s' AP request with ticket for '%s'. Skew is %dm (allowable %dm)."),
323 			    cerr == 0 ? c_name : "unknown",
324 			    serr == 0 ? s_name : "unknown",
325 			    skew > 0 ? skew/60 : 0,
326 			    context->clockskew > 0 ? context->clockskew/60 : 0);
327 	if (s_name)
328 	    krb5_free_unparsed_name(context, s_name);
329 	if (c_name)
330 	    krb5_free_unparsed_name(context, c_name);
331         goto cleanup;
332     }
333 
334     if (check_valid_flag) {
335         if (req->ticket->enc_part2->flags & TKT_FLG_INVALID) {
336 	    /* Solaris Kerberos */
337 	    char *s_name = NULL;
338 	    int err = krb5_unparse_name(context, req->ticket->server, &s_name);
339 	    retval = KRB5KRB_AP_ERR_TKT_INVALID;
340 	    if (!err) {
341 	        krb5_set_error_message(context, retval,
342 				    dgettext(TEXT_DOMAIN,
343 				    "Ticket has invalid flag set for server '%s'"),
344 				    s_name);
345 	        krb5_free_unparsed_name(context, s_name);
346 	    }
347 	    goto cleanup;
348 	}
349     }
350 
351     /* check if the various etypes are permitted */
352 
353     if ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_PERMIT_ALL) {
354 	/* no etype check needed */
355 	/*EMPTY*/
356 	;
357     } else if ((*auth_context)->permitted_etypes == NULL) {
358 	int etype;
359 	/* check against the default set */
360 	if ((!krb5_is_permitted_enctype(context,
361 					etype = req->ticket->enc_part.enctype)) ||
362 	    (!krb5_is_permitted_enctype(context,
363 					etype = req->ticket->enc_part2->session->enctype)) ||
364 	    (((*auth_context)->authentp->subkey) &&
365 	     !krb5_is_permitted_enctype(context,
366 					etype = (*auth_context)->authentp->subkey->enctype))) {
367 	    char enctype_name[30];
368 	    retval = KRB5_NOPERM_ETYPE;
369 	    if (krb5_enctype_to_string(etype, enctype_name, sizeof(enctype_name)) == 0)
370 		krb5_set_error_message(context, retval,
371 				    dgettext(TEXT_DOMAIN,
372 					    "Encryption type %s not permitted"),
373 				    enctype_name);
374 	    goto cleanup;
375 	}
376     } else {
377 	/* check against the set in the auth_context */
378 	int i;
379 
380 	for (i=0; (*auth_context)->permitted_etypes[i]; i++)
381 	    if ((*auth_context)->permitted_etypes[i] ==
382 		req->ticket->enc_part.enctype)
383 		break;
384 	if (!(*auth_context)->permitted_etypes[i]) {
385 	    char enctype_name[30];
386 	    retval = KRB5_NOPERM_ETYPE;
387 	    if (krb5_enctype_to_string(req->ticket->enc_part.enctype,
388 				       enctype_name, sizeof(enctype_name)) == 0)
389 		krb5_set_error_message(context, retval,
390 				    dgettext(TEXT_DOMAIN,
391 					    "Encryption type %s not permitted"),
392 				    enctype_name);
393 	    goto cleanup;
394 	}
395 
396 	for (i=0; (*auth_context)->permitted_etypes[i]; i++)
397 	    if ((*auth_context)->permitted_etypes[i] ==
398 		req->ticket->enc_part2->session->enctype)
399 		break;
400 	if (!(*auth_context)->permitted_etypes[i]) {
401 	    char enctype_name[30];
402 	    retval = KRB5_NOPERM_ETYPE;
403 	    if (krb5_enctype_to_string(req->ticket->enc_part2->session->enctype,
404 				       enctype_name, sizeof(enctype_name)) == 0)
405 		krb5_set_error_message(context, retval,
406 				    dgettext(TEXT_DOMAIN,
407 					    "Encryption type %s not permitted"),
408 				    enctype_name);
409 	    goto cleanup;
410 	}
411 
412 	if ((*auth_context)->authentp->subkey) {
413 	    for (i=0; (*auth_context)->permitted_etypes[i]; i++)
414 		if ((*auth_context)->permitted_etypes[i] ==
415 		    (*auth_context)->authentp->subkey->enctype)
416 		    break;
417 	    if (!(*auth_context)->permitted_etypes[i]) {
418 		char enctype_name[30];
419 		retval = KRB5_NOPERM_ETYPE;
420 		if (krb5_enctype_to_string((*auth_context)->authentp->subkey->enctype,
421 					   enctype_name,
422 					   sizeof(enctype_name)) == 0)
423 		    krb5_set_error_message(context, retval,
424 					dgettext(TEXT_DOMAIN,
425 					    "Encryption type %s not permitted"),
426 					enctype_name);
427 		goto cleanup;
428 	    }
429 	}
430     }
431 
432     (*auth_context)->remote_seq_number = (*auth_context)->authentp->seq_number;
433     if ((*auth_context)->authentp->subkey) {
434 	/* Solaris Kerberos */
435 	if ((*auth_context)->recv_subkey != NULL) {
436 	    krb5_free_keyblock(context, (*auth_context)->recv_subkey);
437 	    (*auth_context)->recv_subkey = NULL;
438 	}
439 
440 	if ((retval = krb5_copy_keyblock(context,
441 					 (*auth_context)->authentp->subkey,
442 					 &((*auth_context)->recv_subkey))))
443 	    goto cleanup;
444 	/* Solaris Kerberos */
445 	if ((*auth_context)->send_subkey != NULL) {
446 	    krb5_free_keyblock(context, (*auth_context)->send_subkey);
447 	    (*auth_context)->send_subkey = NULL;
448 	}
449 
450 	retval = krb5_copy_keyblock(context, (*auth_context)->authentp->subkey,
451 				    &((*auth_context)->send_subkey));
452 	if (retval) {
453 	    krb5_free_keyblock(context, (*auth_context)->recv_subkey);
454 	    (*auth_context)->recv_subkey = NULL;
455 	    goto cleanup;
456 	}
457     } else {
458 	(*auth_context)->recv_subkey = 0;
459 	(*auth_context)->send_subkey = 0;
460     }
461     /* Solaris Kerberos */
462     if ((*auth_context)->keyblock != NULL) {
463 	krb5_free_keyblock(context, (*auth_context)->keyblock);
464 	(*auth_context)->keyblock = NULL;
465     }
466     if ((retval = krb5_copy_keyblock(context, req->ticket->enc_part2->session,
467 				     &((*auth_context)->keyblock))))
468 	goto cleanup;
469 
470     /*
471      * If not AP_OPTS_MUTUAL_REQUIRED then and sequence numbers are used
472      * then the default sequence number is the one's complement of the
473      * sequence number sent ot us.
474      */
475     if ((!(req->ap_options & AP_OPTS_MUTUAL_REQUIRED)) &&
476       (*auth_context)->remote_seq_number) {
477 	(*auth_context)->local_seq_number ^=
478 	  (*auth_context)->remote_seq_number;
479     }
480 
481     if (ticket)
482    	if ((retval = krb5_copy_ticket(context, req->ticket, ticket)))
483 	    goto cleanup;
484     if (ap_req_options)
485     	*ap_req_options = req->ap_options;
486     retval = 0;
487 
488 cleanup:
489     if (server == &princ_data)
490 	krb5_free_default_realm(context, princ_data.realm.data);
491     if (retval) {
492 	/* only free if we're erroring out...otherwise some
493 	   applications will need the output. */
494 	if (req->ticket->enc_part2)
495 	    krb5_free_enc_tkt_part(context, req->ticket->enc_part2);
496 	req->ticket->enc_part2 = NULL;
497     }
498     return retval;
499 }
500 
501 krb5_error_code
502 krb5_rd_req_decoded(krb5_context context, krb5_auth_context *auth_context,
503 		    const krb5_ap_req *req, krb5_const_principal server,
504 		    krb5_keytab keytab, krb5_flags *ap_req_options,
505 		    krb5_ticket **ticket)
506 {
507   krb5_error_code retval;
508   retval = krb5_rd_req_decoded_opt(context, auth_context,
509 				   req, server, keytab,
510 				   ap_req_options, ticket,
511 				   1); /* check_valid_flag */
512   return retval;
513 }
514 
515 krb5_error_code
516 krb5_rd_req_decoded_anyflag(krb5_context context,
517 			    krb5_auth_context *auth_context,
518 			    const krb5_ap_req *req,
519 			    krb5_const_principal server, krb5_keytab keytab,
520 			    krb5_flags *ap_req_options, krb5_ticket **ticket)
521 {
522   krb5_error_code retval;
523   retval = krb5_rd_req_decoded_opt(context, auth_context,
524 				   req, server, keytab,
525 				   ap_req_options, ticket,
526 				   0); /* don't check_valid_flag */
527   return retval;
528 }
529 
530 /*ARGSUSED*/
531 static krb5_error_code
532 decrypt_authenticator(krb5_context context, const krb5_ap_req *request,
533 		      krb5_authenticator **authpp, int is_ap_req)
534 {
535     krb5_authenticator *local_auth;
536     krb5_error_code retval;
537     krb5_data scratch;
538     krb5_keyblock *sesskey;
539 
540     sesskey = request->ticket->enc_part2->session;
541 
542     scratch.length = request->authenticator.ciphertext.length;
543     if (!(scratch.data = malloc(scratch.length)))
544 	return(ENOMEM);
545 
546     if ((retval = krb5_c_decrypt(context, sesskey,
547 				 is_ap_req?KRB5_KEYUSAGE_AP_REQ_AUTH:
548 				 KRB5_KEYUSAGE_TGS_REQ_AUTH, 0,
549 				 &request->authenticator, &scratch))) {
550 	free(scratch.data);
551 	return(retval);
552     }
553 
554 #define clean_scratch() {memset(scratch.data, 0, scratch.length); \
555 free(scratch.data);}
556 
557     /*  now decode the decrypted stuff */
558     if (!(retval = decode_krb5_authenticator(&scratch, &local_auth))) {
559 	*authpp = local_auth;
560     }
561     clean_scratch();
562     return retval;
563 }
564 
565 krb5_error_code
566 krb5int_check_clockskew(krb5_context context, krb5_timestamp date)
567 {
568     krb5_timestamp currenttime;
569     krb5_error_code retval;
570 
571     retval = krb5_timeofday(context, &currenttime);
572     if (retval)
573         return retval;
574     if (!(labs((date)-currenttime) < context->clockskew))
575         return KRB5KRB_AP_ERR_SKEW;
576     return 0;
577 }
578