rd_req_dec.c revision 7934:6aeeafc994de
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
68static 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
74static krb5_error_code
75krb5_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
106static krb5_error_code
107krb5_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)))
147goto 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
414cleanup:
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
427krb5_error_code
428krb5_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
441krb5_error_code
442krb5_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*/
457static krb5_error_code
458decrypt_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); \
481free(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