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
krb5_rd_req_decrypt_tkt_part(krb5_context context,const krb5_ap_req * req,krb5_keytab keytab)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
krb5int_check_clockskew2(krb5_context context,krb5_timestamp date,krb5_timestamp * ret_skew)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, ¤ttime);
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
krb5_rd_req_decoded_opt(krb5_context context,krb5_auth_context * auth_context,const krb5_ap_req * req,krb5_const_principal server,krb5_keytab keytab,krb5_flags * ap_req_options,krb5_ticket ** ticket,int check_valid_flag)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
krb5_rd_req_decoded(krb5_context context,krb5_auth_context * auth_context,const krb5_ap_req * req,krb5_const_principal server,krb5_keytab keytab,krb5_flags * ap_req_options,krb5_ticket ** ticket)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
krb5_rd_req_decoded_anyflag(krb5_context context,krb5_auth_context * auth_context,const krb5_ap_req * req,krb5_const_principal server,krb5_keytab keytab,krb5_flags * ap_req_options,krb5_ticket ** ticket)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
decrypt_authenticator(krb5_context context,const krb5_ap_req * request,krb5_authenticator ** authpp,int is_ap_req)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
krb5int_check_clockskew(krb5_context context,krb5_timestamp date)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, ¤ttime);
572 if (retval)
573 return retval;
574 if (!(labs((date)-currenttime) < context->clockskew))
575 return KRB5KRB_AP_ERR_SKEW;
576 return 0;
577 }
578