Line data Source code
1 :
2 : /*
3 : * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
4 : * (Royal Institute of Technology, Stockholm, Sweden).
5 : * All rights reserved.
6 : *
7 : * Redistribution and use in source and binary forms, with or without
8 : * modification, are permitted provided that the following conditions
9 : * are met:
10 : *
11 : * 1. Redistributions of source code must retain the above copyright
12 : * notice, this list of conditions and the following disclaimer.
13 : *
14 : * 2. Redistributions in binary form must reproduce the above copyright
15 : * notice, this list of conditions and the following disclaimer in the
16 : * documentation and/or other materials provided with the distribution.
17 : *
18 : * 3. Neither the name of the Institute nor the names of its contributors
19 : * may be used to endorse or promote products derived from this software
20 : * without specific prior written permission.
21 : *
22 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
23 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
26 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 : * SUCH DAMAGE.
33 : */
34 :
35 : #include "krb5_locl.h"
36 :
37 : static krb5_error_code
38 86966 : decrypt_tkt_enc_part (krb5_context context,
39 : krb5_keyblock *key,
40 : EncryptedData *enc_part,
41 : EncTicketPart *decr_part)
42 : {
43 : krb5_error_code ret;
44 : krb5_data plain;
45 : size_t len;
46 : krb5_crypto crypto;
47 :
48 86966 : ret = krb5_crypto_init(context, key, 0, &crypto);
49 86966 : if (ret)
50 0 : return ret;
51 86966 : ret = krb5_decrypt_EncryptedData (context,
52 : crypto,
53 : KRB5_KU_TICKET,
54 : enc_part,
55 : &plain);
56 86966 : krb5_crypto_destroy(context, crypto);
57 86966 : if (ret)
58 31 : return ret;
59 :
60 86935 : ret = decode_EncTicketPart(plain.data, plain.length, decr_part, &len);
61 86935 : if (ret)
62 0 : krb5_set_error_message(context, ret,
63 0 : N_("Failed to decode encrypted "
64 : "ticket part", ""));
65 86935 : krb5_data_free (&plain);
66 86935 : return ret;
67 : }
68 :
69 : static krb5_error_code
70 86841 : decrypt_authenticator (krb5_context context,
71 : EncryptionKey *key,
72 : EncryptedData *enc_part,
73 : Authenticator *authenticator,
74 : krb5_key_usage usage)
75 : {
76 : krb5_error_code ret;
77 : krb5_data plain;
78 : size_t len;
79 : krb5_crypto crypto;
80 :
81 86841 : ret = krb5_crypto_init(context, key, 0, &crypto);
82 86841 : if (ret)
83 0 : return ret;
84 86841 : ret = krb5_decrypt_EncryptedData (context,
85 : crypto,
86 : usage /* KRB5_KU_AP_REQ_AUTH */,
87 : enc_part,
88 : &plain);
89 : /* for backwards compatibility, also try the old usage */
90 86841 : if (ret && usage == KRB5_KU_TGS_REQ_AUTH)
91 0 : ret = krb5_decrypt_EncryptedData (context,
92 : crypto,
93 : KRB5_KU_AP_REQ_AUTH,
94 : enc_part,
95 : &plain);
96 86841 : krb5_crypto_destroy(context, crypto);
97 86841 : if (ret)
98 0 : return ret;
99 :
100 86841 : ret = decode_Authenticator(plain.data, plain.length,
101 : authenticator, &len);
102 86841 : krb5_data_free (&plain);
103 86841 : return ret;
104 : }
105 :
106 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
107 88114 : krb5_decode_ap_req(krb5_context context,
108 : const krb5_data *inbuf,
109 : krb5_ap_req *ap_req)
110 : {
111 : krb5_error_code ret;
112 : size_t len;
113 88114 : ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len);
114 88114 : if (ret)
115 0 : return ret;
116 88114 : if (ap_req->pvno != 5){
117 0 : free_AP_REQ(ap_req);
118 0 : krb5_clear_error_message (context);
119 0 : return KRB5KRB_AP_ERR_BADVERSION;
120 : }
121 88114 : if (ap_req->msg_type != krb_ap_req){
122 0 : free_AP_REQ(ap_req);
123 0 : krb5_clear_error_message (context);
124 0 : return KRB5KRB_AP_ERR_MSG_TYPE;
125 : }
126 88114 : if (ap_req->ticket.tkt_vno != 5){
127 0 : free_AP_REQ(ap_req);
128 0 : krb5_clear_error_message (context);
129 0 : return KRB5KRB_AP_ERR_BADVERSION;
130 : }
131 86287 : return 0;
132 : }
133 :
134 : static krb5_error_code
135 32926 : check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc)
136 : {
137 : char **realms;
138 : unsigned int num_realms, n;
139 : krb5_error_code ret;
140 :
141 : /*
142 : * Windows 2000 and 2003 uses this inside their TGT so it's normaly
143 : * not seen by others, however, samba4 joined with a Windows AD as
144 : * a Domain Controller gets exposed to this.
145 : */
146 32926 : if(enc->transited.tr_type == 0 && enc->transited.contents.length == 0)
147 0 : return 0;
148 :
149 32926 : if(enc->transited.tr_type != DOMAIN_X500_COMPRESS)
150 0 : return KRB5KDC_ERR_TRTYPE_NOSUPP;
151 :
152 32926 : if(enc->transited.contents.length == 0)
153 31837 : return 0;
154 :
155 0 : ret = krb5_domain_x500_decode(context, enc->transited.contents,
156 : &realms, &num_realms,
157 0 : enc->crealm,
158 0 : ticket->realm);
159 0 : if(ret)
160 0 : return ret;
161 0 : ret = krb5_check_transited(context, enc->crealm,
162 0 : ticket->realm,
163 : realms, num_realms, NULL);
164 0 : for (n = 0; n < num_realms; n++)
165 0 : free(realms[n]);
166 0 : free(realms);
167 0 : return ret;
168 : }
169 :
170 : static krb5_error_code
171 86837 : find_etypelist(krb5_context context,
172 : krb5_auth_context auth_context,
173 : EtypeList *etypes)
174 : {
175 : krb5_error_code ret;
176 : krb5_authdata *ad;
177 : krb5_authdata adIfRelevant;
178 : unsigned i;
179 :
180 86837 : memset(&adIfRelevant, 0, sizeof(adIfRelevant));
181 :
182 86837 : etypes->len = 0;
183 86837 : etypes->val = NULL;
184 :
185 86837 : ad = auth_context->authenticator->authorization_data;
186 86837 : if (ad == NULL)
187 41275 : return 0;
188 :
189 43736 : for (i = 0; i < ad->len; i++) {
190 44473 : if (ad->val[i].ad_type == KRB5_AUTHDATA_IF_RELEVANT) {
191 44472 : ret = decode_AD_IF_RELEVANT(ad->val[i].ad_data.data,
192 43734 : ad->val[i].ad_data.length,
193 : &adIfRelevant,
194 : NULL);
195 44472 : if (ret)
196 0 : return ret;
197 :
198 88944 : if (adIfRelevant.len == 1 &&
199 44472 : adIfRelevant.val[0].ad_type ==
200 : KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION) {
201 43734 : break;
202 : }
203 0 : free_AD_IF_RELEVANT(&adIfRelevant);
204 0 : adIfRelevant.len = 0;
205 : }
206 : }
207 :
208 44473 : if (adIfRelevant.len == 0)
209 1 : return 0;
210 :
211 44472 : ret = decode_EtypeList(adIfRelevant.val[0].ad_data.data,
212 44472 : adIfRelevant.val[0].ad_data.length,
213 : etypes,
214 : NULL);
215 44472 : if (ret)
216 0 : krb5_clear_error_message(context);
217 :
218 44472 : free_AD_IF_RELEVANT(&adIfRelevant);
219 :
220 43734 : return ret;
221 : }
222 :
223 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
224 86966 : krb5_decrypt_ticket(krb5_context context,
225 : Ticket *ticket,
226 : krb5_keyblock *key,
227 : EncTicketPart *out,
228 : krb5_flags flags)
229 : {
230 : EncTicketPart t;
231 : krb5_error_code ret;
232 86966 : ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t);
233 86966 : if (ret)
234 31 : return ret;
235 :
236 : {
237 : krb5_timestamp now;
238 86935 : time_t start = t.authtime;
239 :
240 86935 : krb5_timeofday (context, &now);
241 86935 : if(t.starttime)
242 54009 : start = *t.starttime;
243 86935 : if(start - now > context->max_skew
244 86935 : || (t.flags.invalid
245 0 : && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) {
246 0 : free_EncTicketPart(&t);
247 0 : krb5_clear_error_message (context);
248 0 : return KRB5KRB_AP_ERR_TKT_NYV;
249 : }
250 86935 : if(now - t.endtime > context->max_skew) {
251 0 : free_EncTicketPart(&t);
252 0 : krb5_clear_error_message (context);
253 0 : return KRB5KRB_AP_ERR_TKT_EXPIRED;
254 : }
255 :
256 86935 : if(!t.flags.transited_policy_checked) {
257 32926 : ret = check_transited(context, ticket, &t);
258 32926 : if(ret) {
259 0 : free_EncTicketPart(&t);
260 0 : return ret;
261 : }
262 : }
263 : }
264 :
265 86935 : if(out)
266 86935 : *out = t;
267 : else
268 0 : free_EncTicketPart(&t);
269 85108 : return 0;
270 : }
271 :
272 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
273 0 : krb5_verify_authenticator_checksum(krb5_context context,
274 : krb5_auth_context ac,
275 : void *data,
276 : size_t len)
277 : {
278 : krb5_error_code ret;
279 : krb5_keyblock *key;
280 : krb5_authenticator authenticator;
281 : krb5_crypto crypto;
282 :
283 0 : ret = krb5_auth_con_getauthenticator (context,
284 : ac,
285 : &authenticator);
286 0 : if(ret)
287 0 : return ret;
288 0 : if(authenticator->cksum == NULL) {
289 0 : krb5_free_authenticator(context, &authenticator);
290 0 : return -17;
291 : }
292 0 : ret = krb5_auth_con_getkey(context, ac, &key);
293 0 : if(ret) {
294 0 : krb5_free_authenticator(context, &authenticator);
295 0 : return ret;
296 : }
297 0 : ret = krb5_crypto_init(context, key, 0, &crypto);
298 0 : if(ret)
299 0 : goto out;
300 0 : ret = krb5_verify_checksum (context,
301 : crypto,
302 : KRB5_KU_AP_REQ_AUTH_CKSUM,
303 : data,
304 : len,
305 0 : authenticator->cksum);
306 0 : krb5_crypto_destroy(context, crypto);
307 0 : out:
308 0 : krb5_free_authenticator(context, &authenticator);
309 0 : krb5_free_keyblock(context, key);
310 0 : return ret;
311 : }
312 :
313 :
314 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
315 0 : krb5_verify_ap_req(krb5_context context,
316 : krb5_auth_context *auth_context,
317 : krb5_ap_req *ap_req,
318 : krb5_const_principal server,
319 : krb5_keyblock *keyblock,
320 : krb5_flags flags,
321 : krb5_flags *ap_req_options,
322 : krb5_ticket **ticket)
323 : {
324 0 : return krb5_verify_ap_req2 (context,
325 : auth_context,
326 : ap_req,
327 : server,
328 : keyblock,
329 : flags,
330 : ap_req_options,
331 : ticket,
332 : KRB5_KU_AP_REQ_AUTH);
333 : }
334 :
335 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
336 86872 : krb5_verify_ap_req2(krb5_context context,
337 : krb5_auth_context *auth_context,
338 : krb5_ap_req *ap_req,
339 : krb5_const_principal server,
340 : krb5_keyblock *keyblock,
341 : krb5_flags flags,
342 : krb5_flags *ap_req_options,
343 : krb5_ticket **ticket,
344 : krb5_key_usage usage)
345 : {
346 : krb5_ticket *t;
347 : krb5_auth_context ac;
348 : krb5_error_code ret;
349 : EtypeList etypes;
350 :
351 86872 : if (ticket)
352 86872 : *ticket = NULL;
353 :
354 86872 : if (auth_context && *auth_context) {
355 46216 : ac = *auth_context;
356 : } else {
357 40656 : ret = krb5_auth_con_init (context, &ac);
358 40656 : if (ret)
359 0 : return ret;
360 : }
361 :
362 86872 : t = calloc(1, sizeof(*t));
363 86872 : if (t == NULL) {
364 0 : ret = ENOMEM;
365 0 : krb5_clear_error_message (context);
366 0 : goto out;
367 : }
368 :
369 86872 : if (ap_req->ap_options.use_session_key && ac->keyblock){
370 0 : ret = krb5_decrypt_ticket(context, &ap_req->ticket,
371 0 : ac->keyblock,
372 : &t->ticket,
373 : flags);
374 0 : krb5_free_keyblock(context, ac->keyblock);
375 0 : ac->keyblock = NULL;
376 : }else
377 86872 : ret = krb5_decrypt_ticket(context, &ap_req->ticket,
378 : keyblock,
379 : &t->ticket,
380 : flags);
381 :
382 86872 : if(ret)
383 31 : goto out;
384 :
385 86841 : ret = _krb5_principalname2krb5_principal(context,
386 : &t->server,
387 : ap_req->ticket.sname,
388 : ap_req->ticket.realm);
389 86841 : if (ret) goto out;
390 86841 : ret = _krb5_principalname2krb5_principal(context,
391 : &t->client,
392 : t->ticket.cname,
393 : t->ticket.crealm);
394 86841 : if (ret) goto out;
395 :
396 86841 : ret = decrypt_authenticator (context,
397 : &t->ticket.key,
398 : &ap_req->authenticator,
399 86841 : ac->authenticator,
400 : usage);
401 86841 : if (ret)
402 0 : goto out;
403 :
404 : {
405 : krb5_principal p1, p2;
406 : krb5_boolean res;
407 :
408 171855 : _krb5_principalname2krb5_principal(context,
409 : &p1,
410 85014 : ac->authenticator->cname,
411 86841 : ac->authenticator->crealm);
412 86841 : _krb5_principalname2krb5_principal(context,
413 : &p2,
414 : t->ticket.cname,
415 : t->ticket.crealm);
416 86841 : res = krb5_principal_compare (context, p1, p2);
417 86841 : krb5_free_principal (context, p1);
418 86841 : krb5_free_principal (context, p2);
419 86841 : if (!res) {
420 4 : ret = KRB5KRB_AP_ERR_BADMATCH;
421 4 : krb5_clear_error_message (context);
422 4 : goto out;
423 : }
424 : }
425 :
426 : /* check addresses */
427 :
428 86837 : if (t->ticket.caddr
429 77 : && ac->remote_address
430 0 : && !krb5_address_search (context,
431 0 : ac->remote_address,
432 0 : t->ticket.caddr)) {
433 0 : ret = KRB5KRB_AP_ERR_BADADDR;
434 0 : krb5_clear_error_message (context);
435 0 : goto out;
436 : }
437 :
438 : /* check timestamp in authenticator */
439 : {
440 : krb5_timestamp now;
441 :
442 86837 : krb5_timeofday (context, &now);
443 :
444 86837 : if (abs(ac->authenticator->ctime - now) > context->max_skew) {
445 0 : ret = KRB5KRB_AP_ERR_SKEW;
446 0 : krb5_clear_error_message (context);
447 0 : goto out;
448 : }
449 : }
450 :
451 86837 : if (ac->authenticator->seq_number)
452 44814 : krb5_auth_con_setremoteseqnumber(context, ac,
453 44814 : *ac->authenticator->seq_number);
454 :
455 : /* XXX - Xor sequence numbers */
456 :
457 86837 : if (ac->authenticator->subkey) {
458 86835 : ret = krb5_auth_con_setremotesubkey(context, ac,
459 85008 : ac->authenticator->subkey);
460 86835 : if (ret)
461 0 : goto out;
462 : }
463 :
464 86837 : ret = find_etypelist(context, ac, &etypes);
465 86837 : if (ret)
466 0 : goto out;
467 :
468 86837 : ac->keytype = ETYPE_NULL;
469 :
470 86837 : if (etypes.val) {
471 : size_t i;
472 :
473 43734 : for (i = 0; i < etypes.len; i++) {
474 44472 : if (krb5_enctype_valid(context, etypes.val[i]) == 0) {
475 44472 : ac->keytype = etypes.val[i];
476 44472 : break;
477 : }
478 : }
479 : }
480 :
481 : /* save key */
482 86837 : ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock);
483 86837 : if (ret) goto out;
484 :
485 86837 : if (ap_req_options) {
486 86837 : *ap_req_options = 0;
487 86837 : if (ac->keytype != ETYPE_NULL)
488 44472 : *ap_req_options |= AP_OPTS_USE_SUBKEY;
489 86837 : if (ap_req->ap_options.use_session_key)
490 0 : *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
491 86837 : if (ap_req->ap_options.mutual_required)
492 44679 : *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
493 : }
494 :
495 86837 : if(ticket)
496 86837 : *ticket = t;
497 : else
498 0 : krb5_free_ticket (context, t);
499 86837 : if (auth_context) {
500 86837 : if (*auth_context == NULL)
501 40652 : *auth_context = ac;
502 : } else
503 0 : krb5_auth_con_free (context, ac);
504 86837 : free_EtypeList(&etypes);
505 86837 : return 0;
506 35 : out:
507 35 : if (t)
508 35 : krb5_free_ticket (context, t);
509 35 : if (auth_context == NULL || *auth_context == NULL)
510 4 : krb5_auth_con_free (context, ac);
511 35 : return ret;
512 : }
513 :
514 : /*
515 : *
516 : */
517 :
518 : struct krb5_rd_req_in_ctx_data {
519 : krb5_keytab keytab;
520 : krb5_keyblock *keyblock;
521 : krb5_boolean check_pac;
522 : };
523 :
524 : struct krb5_rd_req_out_ctx_data {
525 : krb5_keyblock *keyblock;
526 : krb5_flags ap_req_options;
527 : krb5_ticket *ticket;
528 : krb5_principal server;
529 : };
530 :
531 : /**
532 : * Allocate a krb5_rd_req_in_ctx as an input parameter to
533 : * krb5_rd_req_ctx(). The caller should free the context with
534 : * krb5_rd_req_in_ctx_free() when done with the context.
535 : *
536 : * @param context Keberos 5 context.
537 : * @param ctx in ctx to krb5_rd_req_ctx().
538 : *
539 : * @return Kerberos 5 error code, see krb5_get_error_message().
540 : *
541 : * @ingroup krb5_auth
542 : */
543 :
544 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
545 46224 : krb5_rd_req_in_ctx_alloc(krb5_context context, krb5_rd_req_in_ctx *ctx)
546 : {
547 46224 : *ctx = calloc(1, sizeof(**ctx));
548 46224 : if (*ctx == NULL) {
549 0 : krb5_set_error_message(context, ENOMEM,
550 0 : N_("malloc: out of memory", ""));
551 0 : return ENOMEM;
552 : }
553 46224 : (*ctx)->check_pac = (context->flags & KRB5_CTX_F_CHECK_PAC) ? 1 : 0;
554 46224 : return 0;
555 : }
556 :
557 : /**
558 : * Set the keytab that krb5_rd_req_ctx() will use.
559 : *
560 : * @param context Keberos 5 context.
561 : * @param in in ctx to krb5_rd_req_ctx().
562 : * @param keytab keytab that krb5_rd_req_ctx() will use, only copy the
563 : * pointer, so the caller must free they keytab after
564 : * krb5_rd_req_in_ctx_free() is called.
565 : *
566 : * @return Kerberos 5 error code, see krb5_get_error_message().
567 : *
568 : * @ingroup krb5_auth
569 : */
570 :
571 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
572 46224 : krb5_rd_req_in_set_keytab(krb5_context context,
573 : krb5_rd_req_in_ctx in,
574 : krb5_keytab keytab)
575 : {
576 46224 : in->keytab = keytab;
577 46224 : return 0;
578 : }
579 :
580 : /**
581 : * Set if krb5_rq_red() is going to check the Windows PAC or not
582 : *
583 : * @param context Keberos 5 context.
584 : * @param in krb5_rd_req_in_ctx to check the option on.
585 : * @param flag flag to select if to check the pac (TRUE) or not (FALSE).
586 : *
587 : * @return Kerberos 5 error code, see krb5_get_error_message().
588 : *
589 : * @ingroup krb5_auth
590 : */
591 :
592 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
593 0 : krb5_rd_req_in_set_pac_check(krb5_context context,
594 : krb5_rd_req_in_ctx in,
595 : krb5_boolean flag)
596 : {
597 0 : in->check_pac = flag;
598 0 : return 0;
599 : }
600 :
601 :
602 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
603 0 : krb5_rd_req_in_set_keyblock(krb5_context context,
604 : krb5_rd_req_in_ctx in,
605 : krb5_keyblock *keyblock)
606 : {
607 0 : in->keyblock = keyblock; /* XXX should make copy */
608 0 : return 0;
609 : }
610 :
611 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
612 44740 : krb5_rd_req_out_get_ap_req_options(krb5_context context,
613 : krb5_rd_req_out_ctx out,
614 : krb5_flags *ap_req_options)
615 : {
616 44740 : *ap_req_options = out->ap_req_options;
617 44740 : return 0;
618 : }
619 :
620 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
621 46185 : krb5_rd_req_out_get_ticket(krb5_context context,
622 : krb5_rd_req_out_ctx out,
623 : krb5_ticket **ticket)
624 : {
625 46185 : return krb5_copy_ticket(context, out->ticket, ticket);
626 : }
627 :
628 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
629 46185 : krb5_rd_req_out_get_keyblock(krb5_context context,
630 : krb5_rd_req_out_ctx out,
631 : krb5_keyblock **keyblock)
632 : {
633 46185 : return krb5_copy_keyblock(context, out->keyblock, keyblock);
634 : }
635 :
636 : /**
637 : * Get the principal that was used in the request from the
638 : * client. Might not match whats in the ticket if krb5_rd_req_ctx()
639 : * searched in the keytab for a matching key.
640 : *
641 : * @param context a Kerberos 5 context.
642 : * @param out a krb5_rd_req_out_ctx from krb5_rd_req_ctx().
643 : * @param principal return principal, free with krb5_free_principal().
644 : *
645 : * @ingroup krb5_auth
646 : */
647 :
648 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
649 0 : krb5_rd_req_out_get_server(krb5_context context,
650 : krb5_rd_req_out_ctx out,
651 : krb5_principal *principal)
652 : {
653 0 : return krb5_copy_principal(context, out->server, principal);
654 : }
655 :
656 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
657 46224 : krb5_rd_req_in_ctx_free(krb5_context context, krb5_rd_req_in_ctx ctx)
658 : {
659 46224 : free(ctx);
660 46224 : }
661 :
662 : /**
663 : * Free the krb5_rd_req_out_ctx.
664 : *
665 : * @param context Keberos 5 context.
666 : * @param ctx krb5_rd_req_out_ctx context to free.
667 : *
668 : * @ingroup krb5_auth
669 : */
670 :
671 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
672 46224 : krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx)
673 : {
674 46224 : if (ctx->ticket)
675 46185 : krb5_free_ticket(context, ctx->ticket);
676 46224 : if (ctx->keyblock)
677 46205 : krb5_free_keyblock(context, ctx->keyblock);
678 46224 : if (ctx->server)
679 46224 : krb5_free_principal(context, ctx->server);
680 46224 : free(ctx);
681 46224 : }
682 :
683 : /*
684 : *
685 : */
686 :
687 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
688 0 : krb5_rd_req(krb5_context context,
689 : krb5_auth_context *auth_context,
690 : const krb5_data *inbuf,
691 : krb5_const_principal server,
692 : krb5_keytab keytab,
693 : krb5_flags *ap_req_options,
694 : krb5_ticket **ticket)
695 : {
696 : krb5_error_code ret;
697 : krb5_rd_req_in_ctx in;
698 : krb5_rd_req_out_ctx out;
699 :
700 0 : ret = krb5_rd_req_in_ctx_alloc(context, &in);
701 0 : if (ret)
702 0 : return ret;
703 :
704 0 : ret = krb5_rd_req_in_set_keytab(context, in, keytab);
705 0 : if (ret) {
706 0 : krb5_rd_req_in_ctx_free(context, in);
707 0 : return ret;
708 : }
709 :
710 0 : ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
711 0 : krb5_rd_req_in_ctx_free(context, in);
712 0 : if (ret)
713 0 : return ret;
714 :
715 0 : if (ap_req_options)
716 0 : *ap_req_options = out->ap_req_options;
717 0 : if (ticket) {
718 0 : ret = krb5_copy_ticket(context, out->ticket, ticket);
719 0 : if (ret)
720 0 : goto out;
721 : }
722 :
723 0 : out:
724 0 : krb5_rd_req_out_ctx_free(context, out);
725 0 : return ret;
726 : }
727 :
728 : /*
729 : *
730 : */
731 :
732 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
733 0 : krb5_rd_req_with_keyblock(krb5_context context,
734 : krb5_auth_context *auth_context,
735 : const krb5_data *inbuf,
736 : krb5_const_principal server,
737 : krb5_keyblock *keyblock,
738 : krb5_flags *ap_req_options,
739 : krb5_ticket **ticket)
740 : {
741 : krb5_error_code ret;
742 : krb5_rd_req_in_ctx in;
743 : krb5_rd_req_out_ctx out;
744 :
745 0 : ret = krb5_rd_req_in_ctx_alloc(context, &in);
746 0 : if (ret)
747 0 : return ret;
748 :
749 0 : ret = krb5_rd_req_in_set_keyblock(context, in, keyblock);
750 0 : if (ret) {
751 0 : krb5_rd_req_in_ctx_free(context, in);
752 0 : return ret;
753 : }
754 :
755 0 : ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
756 0 : krb5_rd_req_in_ctx_free(context, in);
757 0 : if (ret)
758 0 : return ret;
759 :
760 0 : if (ap_req_options)
761 0 : *ap_req_options = out->ap_req_options;
762 0 : if (ticket) {
763 0 : ret = krb5_copy_ticket(context, out->ticket, ticket);
764 0 : if (ret)
765 0 : goto out;
766 : }
767 :
768 0 : out:
769 0 : krb5_rd_req_out_ctx_free(context, out);
770 0 : return ret;
771 : }
772 :
773 : /*
774 : *
775 : */
776 :
777 : static krb5_error_code
778 46224 : get_key_from_keytab(krb5_context context,
779 : krb5_ap_req *ap_req,
780 : krb5_const_principal server,
781 : krb5_keytab keytab,
782 : krb5_keyblock **out_key)
783 : {
784 : krb5_keytab_entry entry;
785 : krb5_error_code ret;
786 : int kvno;
787 : krb5_keytab real_keytab;
788 :
789 46224 : if(keytab == NULL)
790 0 : krb5_kt_default(context, &real_keytab);
791 : else
792 46224 : real_keytab = keytab;
793 :
794 46224 : if (ap_req->ticket.enc_part.kvno)
795 46224 : kvno = *ap_req->ticket.enc_part.kvno;
796 : else
797 0 : kvno = 0;
798 :
799 46224 : ret = krb5_kt_get_entry (context,
800 : real_keytab,
801 : server,
802 : kvno,
803 : ap_req->ticket.enc_part.etype,
804 : &entry);
805 46224 : if(ret)
806 742 : goto out;
807 45482 : ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
808 45482 : krb5_kt_free_entry (context, &entry);
809 46224 : out:
810 46224 : if(keytab == NULL)
811 0 : krb5_kt_close(context, real_keytab);
812 :
813 46224 : return ret;
814 : }
815 :
816 : /**
817 : * The core server function that verify application authentication
818 : * requests from clients.
819 : *
820 : * @param context Keberos 5 context.
821 : * @param auth_context the authentication context, can be NULL, then
822 : * default values for the authentication context will used.
823 : * @param inbuf the (AP-REQ) authentication buffer
824 : *
825 : * @param server the server with authenticate as, if NULL the function
826 : * will try to find any available credential in the keytab
827 : * that will verify the reply. The function will prefer the
828 : * server the server client specified in the AP-REQ, but if
829 : * there is no mach, it will try all keytab entries for a
830 : * match. This have serious performance issues for larger keytabs.
831 : *
832 : * @param inctx control the behavior of the function, if NULL, the
833 : * default behavior is used.
834 : * @param outctx the return outctx, free with krb5_rd_req_out_ctx_free().
835 : * @return Kerberos 5 error code, see krb5_get_error_message().
836 : *
837 : * @ingroup krb5_auth
838 : */
839 :
840 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
841 46224 : krb5_rd_req_ctx(krb5_context context,
842 : krb5_auth_context *auth_context,
843 : const krb5_data *inbuf,
844 : krb5_const_principal server,
845 : krb5_rd_req_in_ctx inctx,
846 : krb5_rd_req_out_ctx *outctx)
847 : {
848 : krb5_error_code ret;
849 : krb5_ap_req ap_req;
850 46224 : krb5_rd_req_out_ctx o = NULL;
851 46224 : krb5_keytab id = NULL, keytab = NULL;
852 46224 : krb5_principal service = NULL;
853 :
854 46224 : *outctx = NULL;
855 :
856 46224 : o = calloc(1, sizeof(*o));
857 46224 : if (o == NULL) {
858 0 : krb5_set_error_message(context, ENOMEM,
859 0 : N_("malloc: out of memory", ""));
860 0 : return ENOMEM;
861 : }
862 :
863 46224 : if (*auth_context == NULL) {
864 0 : ret = krb5_auth_con_init(context, auth_context);
865 0 : if (ret)
866 0 : goto out;
867 : }
868 :
869 46224 : ret = krb5_decode_ap_req(context, inbuf, &ap_req);
870 46224 : if(ret)
871 0 : goto out;
872 :
873 : /* Save that principal that was in the request */
874 46224 : ret = _krb5_principalname2krb5_principal(context,
875 : &o->server,
876 : ap_req.ticket.sname,
877 : ap_req.ticket.realm);
878 46224 : if (ret)
879 0 : goto out;
880 :
881 46224 : if (ap_req.ap_options.use_session_key &&
882 0 : (*auth_context)->keyblock == NULL) {
883 0 : ret = KRB5KRB_AP_ERR_NOKEY;
884 0 : krb5_set_error_message(context, ret,
885 0 : N_("krb5_rd_req: user to user auth "
886 : "without session key given", ""));
887 0 : goto out;
888 : }
889 :
890 46224 : if (inctx && inctx->keytab)
891 46224 : id = inctx->keytab;
892 :
893 46224 : if((*auth_context)->keyblock){
894 0 : ret = krb5_copy_keyblock(context,
895 0 : (*auth_context)->keyblock,
896 : &o->keyblock);
897 0 : if (ret)
898 0 : goto out;
899 46224 : } else if(inctx && inctx->keyblock){
900 0 : ret = krb5_copy_keyblock(context,
901 0 : inctx->keyblock,
902 : &o->keyblock);
903 0 : if (ret)
904 0 : goto out;
905 : } else {
906 :
907 46224 : if(id == NULL) {
908 0 : krb5_kt_default(context, &keytab);
909 0 : id = keytab;
910 : }
911 46224 : if (id == NULL)
912 0 : goto out;
913 :
914 46224 : if (server == NULL) {
915 769 : ret = _krb5_principalname2krb5_principal(context,
916 : &service,
917 : ap_req.ticket.sname,
918 : ap_req.ticket.realm);
919 769 : if (ret)
920 0 : goto out;
921 769 : server = service;
922 : }
923 :
924 46224 : ret = get_key_from_keytab(context,
925 : &ap_req,
926 : server,
927 : id,
928 : &o->keyblock);
929 46224 : if (ret) {
930 : /* If caller specified a server, fail. */
931 742 : if (service == NULL && (context->flags & KRB5_CTX_F_RD_REQ_IGNORE) == 0)
932 19 : goto out;
933 : /* Otherwise, fall back to iterating over the keytab. This
934 : * have serious performace issues for larger keytab.
935 : */
936 723 : o->keyblock = NULL;
937 : }
938 : }
939 :
940 46205 : if (o->keyblock) {
941 : /*
942 : * We got an exact keymatch, use that.
943 : */
944 :
945 45482 : ret = krb5_verify_ap_req2(context,
946 : auth_context,
947 : &ap_req,
948 : server,
949 : o->keyblock,
950 : 0,
951 : &o->ap_req_options,
952 : &o->ticket,
953 : KRB5_KU_AP_REQ_AUTH);
954 :
955 45482 : if (ret)
956 20 : goto out;
957 :
958 : } else {
959 : /*
960 : * Interate over keytab to find a key that can decrypt the request.
961 : */
962 :
963 : krb5_keytab_entry entry;
964 : krb5_kt_cursor cursor;
965 723 : int done = 0, kvno = 0;
966 :
967 723 : memset(&cursor, 0, sizeof(cursor));
968 :
969 723 : if (ap_req.ticket.enc_part.kvno)
970 723 : kvno = *ap_req.ticket.enc_part.kvno;
971 :
972 723 : ret = krb5_kt_start_seq_get(context, id, &cursor);
973 723 : if (ret)
974 0 : goto out;
975 :
976 723 : done = 0;
977 3120 : while (!done) {
978 : krb5_principal p;
979 :
980 1674 : ret = krb5_kt_next_entry(context, id, &entry, &cursor);
981 1674 : if (ret) {
982 0 : _krb5_kt_principal_not_found(context, ret, id, o->server,
983 : ap_req.ticket.enc_part.etype,
984 : kvno);
985 0 : goto out;
986 : }
987 :
988 1674 : if (entry.keyblock.keytype != ap_req.ticket.enc_part.etype) {
989 940 : krb5_kt_free_entry (context, &entry);
990 1891 : continue;
991 : }
992 :
993 734 : ret = krb5_verify_ap_req2(context,
994 : auth_context,
995 : &ap_req,
996 : server,
997 : &entry.keyblock,
998 : 0,
999 : &o->ap_req_options,
1000 : &o->ticket,
1001 : KRB5_KU_AP_REQ_AUTH);
1002 734 : if (ret) {
1003 11 : krb5_kt_free_entry (context, &entry);
1004 11 : continue;
1005 : }
1006 :
1007 : /*
1008 : * Found a match, save the keyblock for PAC processing,
1009 : * and update the service principal in the ticket to match
1010 : * whatever is in the keytab.
1011 : */
1012 :
1013 723 : ret = krb5_copy_keyblock(context,
1014 : &entry.keyblock,
1015 : &o->keyblock);
1016 723 : if (ret) {
1017 0 : krb5_kt_free_entry (context, &entry);
1018 0 : goto out;
1019 : }
1020 :
1021 723 : ret = krb5_copy_principal(context, entry.principal, &p);
1022 723 : if (ret) {
1023 0 : krb5_kt_free_entry (context, &entry);
1024 0 : goto out;
1025 : }
1026 723 : krb5_free_principal(context, o->ticket->server);
1027 723 : o->ticket->server = p;
1028 :
1029 723 : krb5_kt_free_entry (context, &entry);
1030 :
1031 723 : done = 1;
1032 : }
1033 723 : krb5_kt_end_seq_get (context, id, &cursor);
1034 : }
1035 :
1036 : /* If there is a PAC, verify its server signature */
1037 46185 : if (inctx == NULL || inctx->check_pac) {
1038 : krb5_pac pac;
1039 : krb5_data data;
1040 :
1041 46185 : ret = krb5_ticket_get_authorization_data_type(context,
1042 : o->ticket,
1043 : KRB5_AUTHDATA_WIN2K_PAC,
1044 : &data);
1045 46185 : if (ret == 0) {
1046 46185 : ret = krb5_pac_parse(context, data.data, data.length, &pac);
1047 46185 : krb5_data_free(&data);
1048 46185 : if (ret)
1049 0 : goto out;
1050 :
1051 46185 : ret = krb5_pac_verify(context,
1052 : pac,
1053 45447 : o->ticket->ticket.authtime,
1054 46185 : o->ticket->client,
1055 46185 : o->keyblock,
1056 : NULL);
1057 46185 : krb5_pac_free(context, pac);
1058 46185 : if (ret)
1059 0 : goto out;
1060 : } else
1061 0 : ret = 0;
1062 : }
1063 46224 : out:
1064 :
1065 46224 : if (ret || outctx == NULL) {
1066 39 : krb5_rd_req_out_ctx_free(context, o);
1067 : } else
1068 46185 : *outctx = o;
1069 :
1070 46224 : free_AP_REQ(&ap_req);
1071 :
1072 46224 : if (service)
1073 769 : krb5_free_principal(context, service);
1074 :
1075 46224 : if (keytab)
1076 0 : krb5_kt_close(context, keytab);
1077 :
1078 45486 : return ret;
1079 : }
|