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
67static krb5_error_code decrypt_authenticator
68	(krb5_context, const krb5_ap_req *, krb5_authenticator **,
69		   int);
70
71
72static krb5_error_code
73krb5_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 */
124static krb5_error_code
125krb5int_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
145static krb5_error_code
146krb5_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)))
187goto 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
309err_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
488cleanup:
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
501krb5_error_code
502krb5_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
515krb5_error_code
516krb5_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*/
531static krb5_error_code
532decrypt_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); \
555free(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
565krb5_error_code
566krb5int_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