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 105716 : decrypt_tkt_enc_part (krb5_context context,
39 : krb5_keyblock *key,
40 : EncryptedData *enc_part,
41 : EncTicketPart *decr_part)
42 : {
43 2539 : krb5_error_code ret;
44 2539 : krb5_data plain;
45 2539 : size_t len;
46 2539 : krb5_crypto crypto;
47 :
48 105716 : ret = krb5_crypto_init(context, key, 0, &crypto);
49 105716 : if (ret)
50 0 : return ret;
51 105716 : ret = krb5_decrypt_EncryptedData (context,
52 : crypto,
53 : KRB5_KU_TICKET,
54 : enc_part,
55 : &plain);
56 105716 : krb5_crypto_destroy(context, crypto);
57 105716 : if (ret)
58 15 : return ret;
59 :
60 105701 : ret = decode_EncTicketPart(plain.data, plain.length, decr_part, &len);
61 105701 : if (ret)
62 0 : krb5_set_error_message(context, ret,
63 0 : N_("Failed to decode encrypted "
64 : "ticket part", ""));
65 105701 : krb5_data_free (&plain);
66 105701 : return ret;
67 : }
68 :
69 : static krb5_error_code
70 105399 : decrypt_authenticator (krb5_context context,
71 : EncryptionKey *key,
72 : EncryptedData *enc_part,
73 : Authenticator *authenticator,
74 : krb5_key_usage usage)
75 : {
76 2539 : krb5_error_code ret;
77 2539 : krb5_data plain;
78 2539 : size_t len;
79 2539 : krb5_crypto crypto;
80 :
81 105399 : ret = krb5_crypto_init(context, key, 0, &crypto);
82 105399 : if (ret)
83 0 : return ret;
84 105399 : 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 105399 : 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 105399 : krb5_crypto_destroy(context, crypto);
97 105399 : if (ret)
98 0 : return ret;
99 :
100 105399 : ret = decode_Authenticator(plain.data, plain.length,
101 : authenticator, &len);
102 105399 : krb5_data_free (&plain);
103 105399 : return ret;
104 : }
105 :
106 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
107 106797 : krb5_decode_ap_req(krb5_context context,
108 : const krb5_data *inbuf,
109 : krb5_ap_req *ap_req)
110 : {
111 2539 : krb5_error_code ret;
112 2539 : size_t len;
113 106797 : ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len);
114 106797 : if (ret)
115 0 : return ret;
116 106797 : 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 106797 : 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 106797 : 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 104258 : return 0;
132 : }
133 :
134 : static krb5_error_code
135 51581 : check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc)
136 : {
137 1658 : char **realms;
138 1658 : unsigned int num_realms, n;
139 1658 : 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 51581 : if(enc->transited.tr_type == 0 && enc->transited.contents.length == 0)
147 0 : return 0;
148 :
149 51581 : if(enc->transited.tr_type != domain_X500_Compress)
150 0 : return KRB5KDC_ERR_TRTYPE_NOSUPP;
151 :
152 51581 : if(enc->transited.contents.length == 0)
153 49923 : 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 105398 : find_etypelist(krb5_context context,
172 : krb5_auth_context auth_context,
173 : EtypeList *etypes)
174 : {
175 2539 : krb5_error_code ret;
176 2539 : krb5_data data;
177 :
178 105398 : ret = _krb5_get_ad(context, auth_context->authenticator->authorization_data, NULL, KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION, &data);
179 105398 : if (ret)
180 51625 : return 0;
181 :
182 52115 : ret = decode_EtypeList(data.data, data.length, etypes, NULL);
183 52115 : krb5_data_free(&data);
184 52115 : if (ret)
185 0 : krb5_clear_error_message(context);
186 :
187 51234 : return ret;
188 : }
189 :
190 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
191 105716 : krb5_decrypt_ticket(krb5_context context,
192 : Ticket *ticket,
193 : krb5_keyblock *key,
194 : EncTicketPart *out,
195 : krb5_flags flags)
196 : {
197 2539 : EncTicketPart t;
198 2539 : krb5_error_code ret;
199 105716 : ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t);
200 105716 : if (ret)
201 15 : return ret;
202 :
203 : {
204 2539 : krb5_timestamp now;
205 105701 : time_t start = t.authtime;
206 :
207 105701 : krb5_timeofday (context, &now);
208 105701 : if(t.starttime)
209 54146 : start = *t.starttime;
210 105701 : if(start - now > context->max_skew
211 105701 : || (t.flags.invalid
212 30 : && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) {
213 4 : free_EncTicketPart(&t);
214 4 : krb5_clear_error_message (context);
215 4 : return KRB5KRB_AP_ERR_TKT_NYV;
216 : }
217 105697 : if(now - t.endtime > context->max_skew) {
218 0 : free_EncTicketPart(&t);
219 0 : krb5_clear_error_message (context);
220 0 : return KRB5KRB_AP_ERR_TKT_EXPIRED;
221 : }
222 :
223 105697 : if(!t.flags.transited_policy_checked) {
224 51581 : ret = check_transited(context, ticket, &t);
225 51581 : if(ret) {
226 0 : free_EncTicketPart(&t);
227 0 : return ret;
228 : }
229 : }
230 : }
231 :
232 105697 : if(out)
233 105697 : *out = t;
234 : else
235 0 : free_EncTicketPart(&t);
236 103158 : return 0;
237 : }
238 :
239 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
240 0 : krb5_verify_authenticator_checksum(krb5_context context,
241 : krb5_auth_context ac,
242 : void *data,
243 : size_t len)
244 : {
245 0 : krb5_error_code ret;
246 0 : krb5_keyblock *key = NULL;
247 0 : krb5_authenticator authenticator;
248 0 : krb5_crypto crypto;
249 :
250 0 : ret = krb5_auth_con_getauthenticator(context, ac, &authenticator);
251 0 : if (ret)
252 0 : return ret;
253 0 : if (authenticator->cksum == NULL) {
254 0 : ret = -17;
255 0 : goto out;
256 : }
257 0 : ret = krb5_auth_con_getkey(context, ac, &key);
258 0 : if (ret)
259 0 : goto out;
260 0 : ret = krb5_crypto_init(context, key, 0, &crypto);
261 0 : if (ret)
262 0 : goto out;
263 :
264 0 : _krb5_crypto_set_flags(context, crypto, KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM);
265 0 : ret = krb5_verify_checksum(context, crypto,
266 : KRB5_KU_AP_REQ_AUTH_CKSUM,
267 0 : data, len, authenticator->cksum);
268 0 : krb5_crypto_destroy(context, crypto);
269 0 : out:
270 0 : krb5_free_authenticator(context, &authenticator);
271 0 : krb5_free_keyblock(context, key);
272 0 : return ret;
273 : }
274 :
275 :
276 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
277 0 : krb5_verify_ap_req(krb5_context context,
278 : krb5_auth_context *auth_context,
279 : krb5_ap_req *ap_req,
280 : krb5_const_principal server,
281 : krb5_keyblock *keyblock,
282 : krb5_flags flags,
283 : krb5_flags *ap_req_options,
284 : krb5_ticket **ticket)
285 : {
286 0 : return krb5_verify_ap_req2 (context,
287 : auth_context,
288 : ap_req,
289 : server,
290 : keyblock,
291 : flags,
292 : ap_req_options,
293 : ticket,
294 : KRB5_KU_AP_REQ_AUTH);
295 : }
296 :
297 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
298 105417 : krb5_verify_ap_req2(krb5_context context,
299 : krb5_auth_context *auth_context,
300 : krb5_ap_req *ap_req,
301 : krb5_const_principal server,
302 : krb5_keyblock *keyblock,
303 : krb5_flags flags,
304 : krb5_flags *ap_req_options,
305 : krb5_ticket **ticket,
306 : krb5_key_usage usage)
307 : {
308 2539 : krb5_ticket *t;
309 2539 : krb5_auth_context ac;
310 2539 : krb5_error_code ret;
311 2539 : EtypeList etypes;
312 105417 : int badaddr = 0;
313 :
314 105417 : memset(&etypes, 0, sizeof(etypes));
315 :
316 105417 : if (ticket)
317 105417 : *ticket = NULL;
318 :
319 105417 : if (auth_context && *auth_context) {
320 53457 : ac = *auth_context;
321 : } else {
322 51960 : ret = krb5_auth_con_init (context, &ac);
323 51960 : if (ret)
324 0 : return ret;
325 : }
326 :
327 105417 : t = calloc(1, sizeof(*t));
328 105417 : if (t == NULL) {
329 0 : ret = krb5_enomem(context);
330 0 : goto out;
331 : }
332 :
333 105417 : if (ap_req->ap_options.use_session_key && ac->keyblock){
334 0 : ret = krb5_decrypt_ticket(context, &ap_req->ticket,
335 0 : ac->keyblock,
336 : &t->ticket,
337 : flags);
338 0 : krb5_free_keyblock(context, ac->keyblock);
339 0 : ac->keyblock = NULL;
340 : }else
341 105417 : ret = krb5_decrypt_ticket(context, &ap_req->ticket,
342 : keyblock,
343 : &t->ticket,
344 : flags);
345 :
346 105417 : if(ret)
347 18 : goto out;
348 :
349 105399 : ret = _krb5_principalname2krb5_principal(context,
350 : &t->server,
351 : ap_req->ticket.sname,
352 : ap_req->ticket.realm);
353 105399 : if (ret) goto out;
354 :
355 107938 : ret = decrypt_authenticator (context,
356 : &t->ticket.key,
357 : &ap_req->authenticator,
358 105399 : ac->authenticator,
359 : usage);
360 105399 : if (ret)
361 0 : goto out;
362 :
363 : {
364 2539 : krb5_principal p1, p2;
365 2539 : krb5_boolean res;
366 :
367 105399 : _krb5_principalname2krb5_principal(context,
368 : &p1,
369 102860 : ac->authenticator->cname,
370 105399 : ac->authenticator->crealm);
371 105399 : _krb5_principalname2krb5_principal(context,
372 : &p2,
373 : t->ticket.cname,
374 : t->ticket.crealm);
375 105399 : res = krb5_principal_compare (context, p1, p2);
376 105399 : krb5_free_principal (context, p1);
377 105399 : krb5_free_principal (context, p2);
378 105399 : if (!res) {
379 1 : ret = KRB5KRB_AP_ERR_BADMATCH;
380 1 : krb5_clear_error_message (context);
381 1 : goto out;
382 : }
383 : }
384 :
385 : /*
386 : * The ticket authenticates the client, and conveys naming attributes that
387 : * we want to expose in GSS using RFC6680 APIs.
388 : *
389 : * So we same the ticket enc-part in the client's krb5_principal object
390 : * (note though that the session key will be absent in that copy of the
391 : * ticket enc-part).
392 : */
393 107937 : ret = _krb5_ticket2krb5_principal(context, &t->client, &t->ticket,
394 105398 : ac->authenticator->authorization_data);
395 105398 : if (ret) goto out;
396 :
397 105398 : t->client->nameattrs->peer_realm =
398 105398 : calloc(1, sizeof(t->client->nameattrs->peer_realm[0]));
399 105398 : if (t->client->nameattrs->peer_realm == NULL) {
400 0 : ret = krb5_enomem(context);
401 0 : goto out;
402 : }
403 105398 : ret = copy_Realm(&ap_req->ticket.realm, t->client->nameattrs->peer_realm);
404 105398 : if (ret) goto out;
405 :
406 : /* check addresses */
407 :
408 105398 : if (t->ticket.caddr
409 325 : && ac->remote_address
410 0 : && !krb5_address_search (context,
411 0 : ac->remote_address,
412 0 : t->ticket.caddr)) {
413 : /*
414 : * Hack alert. If KRB5_VERIFY_AP_REQ_IGNORE_ADDRS and the client's
415 : * address didn't check out then we'll return KRB5KRB_AP_ERR_BADADDR
416 : * even on success, and we'll let the caller figure it out because
417 : * `*ticket != NULL' or `*auth_context != NULL'.
418 : */
419 0 : if ((flags & KRB5_VERIFY_AP_REQ_IGNORE_ADDRS)) {
420 0 : badaddr = 1;
421 : } else {
422 0 : ret = KRB5KRB_AP_ERR_BADADDR;
423 0 : krb5_clear_error_message(context);
424 0 : goto out;
425 : }
426 : }
427 :
428 : /* check timestamp in authenticator */
429 : {
430 2539 : krb5_timestamp now;
431 :
432 105398 : krb5_timeofday (context, &now);
433 :
434 105398 : if (krb5_time_abs(ac->authenticator->ctime, now) > context->max_skew) {
435 0 : ret = KRB5KRB_AP_ERR_SKEW;
436 0 : krb5_clear_error_message (context);
437 0 : goto out;
438 : }
439 : }
440 :
441 105398 : if (ac->authenticator->seq_number)
442 56529 : krb5_auth_con_setremoteseqnumber(context, ac,
443 56529 : *ac->authenticator->seq_number);
444 :
445 : /* XXX - Xor sequence numbers */
446 :
447 105398 : if (ac->authenticator->subkey) {
448 105394 : ret = krb5_auth_con_setremotesubkey(context, ac,
449 102855 : ac->authenticator->subkey);
450 105394 : if (ret)
451 0 : goto out;
452 : }
453 :
454 105398 : ret = find_etypelist(context, ac, &etypes);
455 105398 : if (ret)
456 0 : goto out;
457 :
458 105398 : ac->keytype = ETYPE_NULL;
459 :
460 105398 : if (etypes.val) {
461 : size_t i;
462 :
463 52115 : for (i = 0; i < etypes.len; i++) {
464 52115 : if (krb5_enctype_valid(context, etypes.val[i]) == 0) {
465 52115 : ac->keytype = etypes.val[i];
466 52115 : break;
467 : }
468 : }
469 : }
470 :
471 : /* save key */
472 105398 : ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock);
473 105398 : if (ret) goto out;
474 :
475 105398 : if (ap_req_options) {
476 105398 : *ap_req_options = 0;
477 105398 : if (ac->keytype != ETYPE_NULL)
478 52115 : *ap_req_options |= AP_OPTS_USE_SUBKEY;
479 105398 : if (ap_req->ap_options.use_session_key)
480 3 : *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
481 105398 : if (ap_req->ap_options.mutual_required)
482 52161 : *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
483 : }
484 :
485 105398 : if(ticket)
486 105398 : *ticket = t;
487 : else
488 0 : krb5_free_ticket (context, t);
489 105398 : if (auth_context) {
490 105398 : if (*auth_context == NULL)
491 51956 : *auth_context = ac;
492 : } else
493 0 : krb5_auth_con_free (context, ac);
494 105398 : free_EtypeList(&etypes);
495 :
496 105398 : if (badaddr) {
497 0 : krb5_clear_error_message(context);
498 0 : return KRB5KRB_AP_ERR_BADADDR;
499 : }
500 102859 : return 0;
501 19 : out:
502 19 : free_EtypeList(&etypes);
503 19 : if (t)
504 19 : krb5_free_ticket (context, t);
505 19 : if (auth_context == NULL || *auth_context == NULL)
506 4 : krb5_auth_con_free (context, ac);
507 19 : return ret;
508 : }
509 :
510 : /*
511 : *
512 : */
513 :
514 : struct krb5_rd_req_in_ctx_data {
515 : krb5_keytab keytab;
516 : krb5_keyblock *keyblock;
517 : krb5_boolean check_pac;
518 : };
519 :
520 : struct krb5_rd_req_out_ctx_data {
521 : krb5_keyblock *keyblock;
522 : krb5_flags ap_req_options;
523 : krb5_ticket *ticket;
524 : krb5_principal server;
525 : };
526 :
527 : /**
528 : * Allocate a krb5_rd_req_in_ctx as an input parameter to
529 : * krb5_rd_req_ctx(). The caller should free the context with
530 : * krb5_rd_req_in_ctx_free() when done with the context.
531 : *
532 : * @param context Keberos 5 context.
533 : * @param ctx in ctx to krb5_rd_req_ctx().
534 : *
535 : * @return Kerberos 5 error code, see krb5_get_error_message().
536 : *
537 : * @ingroup krb5_auth
538 : */
539 :
540 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
541 53469 : krb5_rd_req_in_ctx_alloc(krb5_context context, krb5_rd_req_in_ctx *ctx)
542 : {
543 53469 : *ctx = calloc(1, sizeof(**ctx));
544 53469 : if (*ctx == NULL)
545 0 : return krb5_enomem(context);
546 53469 : (*ctx)->check_pac = (context->flags & KRB5_CTX_F_CHECK_PAC) ? 1 : 0;
547 53469 : return 0;
548 : }
549 :
550 : /**
551 : * Set the keytab that krb5_rd_req_ctx() will use.
552 : *
553 : * @param context Keberos 5 context.
554 : * @param in in ctx to krb5_rd_req_ctx().
555 : * @param keytab keytab that krb5_rd_req_ctx() will use, only copy the
556 : * pointer, so the caller must free they keytab after
557 : * krb5_rd_req_in_ctx_free() is called.
558 : *
559 : * @return Kerberos 5 error code, see krb5_get_error_message().
560 : *
561 : * @ingroup krb5_auth
562 : */
563 :
564 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
565 53469 : krb5_rd_req_in_set_keytab(krb5_context context,
566 : krb5_rd_req_in_ctx in,
567 : krb5_keytab keytab)
568 : {
569 53469 : in->keytab = keytab;
570 53469 : return 0;
571 : }
572 :
573 : /**
574 : * Set if krb5_rq_red() is going to check the Windows PAC or not
575 : *
576 : * @param context Keberos 5 context.
577 : * @param in krb5_rd_req_in_ctx to check the option on.
578 : * @param flag flag to select if to check the pac (TRUE) or not (FALSE).
579 : *
580 : * @return Kerberos 5 error code, see krb5_get_error_message().
581 : *
582 : * @ingroup krb5_auth
583 : */
584 :
585 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
586 0 : krb5_rd_req_in_set_pac_check(krb5_context context,
587 : krb5_rd_req_in_ctx in,
588 : krb5_boolean flag)
589 : {
590 0 : in->check_pac = flag;
591 0 : return 0;
592 : }
593 :
594 :
595 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
596 0 : krb5_rd_req_in_set_keyblock(krb5_context context,
597 : krb5_rd_req_in_ctx in,
598 : krb5_keyblock *keyblock)
599 : {
600 0 : in->keyblock = keyblock; /* XXX should make copy */
601 0 : return 0;
602 : }
603 :
604 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
605 52228 : krb5_rd_req_out_get_ap_req_options(krb5_context context,
606 : krb5_rd_req_out_ctx out,
607 : krb5_flags *ap_req_options)
608 : {
609 52228 : *ap_req_options = out->ap_req_options;
610 52228 : return 0;
611 : }
612 :
613 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
614 53442 : krb5_rd_req_out_get_ticket(krb5_context context,
615 : krb5_rd_req_out_ctx out,
616 : krb5_ticket **ticket)
617 : {
618 53442 : return krb5_copy_ticket(context, out->ticket, ticket);
619 : }
620 :
621 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
622 53442 : krb5_rd_req_out_get_keyblock(krb5_context context,
623 : krb5_rd_req_out_ctx out,
624 : krb5_keyblock **keyblock)
625 : {
626 53442 : return krb5_copy_keyblock(context, out->keyblock, keyblock);
627 : }
628 :
629 : /**
630 : * Get the principal that was used in the request from the
631 : * client. Might not match whats in the ticket if krb5_rd_req_ctx()
632 : * searched in the keytab for a matching key.
633 : *
634 : * @param context a Kerberos 5 context.
635 : * @param out a krb5_rd_req_out_ctx from krb5_rd_req_ctx().
636 : * @param principal return principal, free with krb5_free_principal().
637 : *
638 : * @ingroup krb5_auth
639 : */
640 :
641 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
642 0 : krb5_rd_req_out_get_server(krb5_context context,
643 : krb5_rd_req_out_ctx out,
644 : krb5_principal *principal)
645 : {
646 0 : return krb5_copy_principal(context, out->server, principal);
647 : }
648 :
649 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
650 53469 : krb5_rd_req_in_ctx_free(krb5_context context, krb5_rd_req_in_ctx ctx)
651 : {
652 53469 : free(ctx);
653 53469 : }
654 :
655 : /**
656 : * Free the krb5_rd_req_out_ctx.
657 : *
658 : * @param context Keberos 5 context.
659 : * @param ctx krb5_rd_req_out_ctx context to free.
660 : *
661 : * @ingroup krb5_auth
662 : */
663 :
664 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
665 53469 : krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx)
666 : {
667 53469 : if (ctx->ticket)
668 53442 : krb5_free_ticket(context, ctx->ticket);
669 53469 : if (ctx->keyblock)
670 53446 : krb5_free_keyblock(context, ctx->keyblock);
671 53469 : if (ctx->server)
672 53469 : krb5_free_principal(context, ctx->server);
673 53469 : free(ctx);
674 53469 : }
675 :
676 : /**
677 : * Process an AP_REQ message.
678 : *
679 : * @param context Kerberos 5 context.
680 : * @param auth_context authentication context of the peer.
681 : * @param inbuf the AP_REQ message, obtained for example with krb5_read_message().
682 : * @param server server principal.
683 : * @param keytab server keytab.
684 : * @param ap_req_options set to the AP_REQ options. See the AP_OPTS_* defines.
685 : * @param ticket on success, set to the authenticated client credentials.
686 : * Must be deallocated with krb5_free_ticket(). If not
687 : * interested, pass a NULL value.
688 : *
689 : * @return 0 to indicate success. Otherwise a Kerberos error code is
690 : * returned, see krb5_get_error_message().
691 : */
692 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
693 0 : krb5_rd_req(krb5_context context,
694 : krb5_auth_context *auth_context,
695 : const krb5_data *inbuf,
696 : krb5_const_principal server,
697 : krb5_keytab keytab,
698 : krb5_flags *ap_req_options,
699 : krb5_ticket **ticket)
700 : {
701 0 : krb5_error_code ret;
702 0 : krb5_rd_req_in_ctx in;
703 0 : krb5_rd_req_out_ctx out;
704 :
705 0 : ret = krb5_rd_req_in_ctx_alloc(context, &in);
706 0 : if (ret)
707 0 : return ret;
708 :
709 0 : ret = krb5_rd_req_in_set_keytab(context, in, keytab);
710 0 : if (ret) {
711 0 : krb5_rd_req_in_ctx_free(context, in);
712 0 : return ret;
713 : }
714 :
715 0 : ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
716 0 : krb5_rd_req_in_ctx_free(context, in);
717 0 : if (ret)
718 0 : return ret;
719 :
720 0 : if (ap_req_options)
721 0 : *ap_req_options = out->ap_req_options;
722 0 : if (ticket) {
723 0 : ret = krb5_copy_ticket(context, out->ticket, ticket);
724 0 : if (ret)
725 0 : goto out;
726 : }
727 :
728 0 : out:
729 0 : krb5_rd_req_out_ctx_free(context, out);
730 0 : return ret;
731 : }
732 :
733 : /*
734 : *
735 : */
736 :
737 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
738 0 : krb5_rd_req_with_keyblock(krb5_context context,
739 : krb5_auth_context *auth_context,
740 : const krb5_data *inbuf,
741 : krb5_const_principal server,
742 : krb5_keyblock *keyblock,
743 : krb5_flags *ap_req_options,
744 : krb5_ticket **ticket)
745 : {
746 0 : krb5_error_code ret;
747 0 : krb5_rd_req_in_ctx in;
748 0 : krb5_rd_req_out_ctx out;
749 :
750 0 : ret = krb5_rd_req_in_ctx_alloc(context, &in);
751 0 : if (ret)
752 0 : return ret;
753 :
754 0 : ret = krb5_rd_req_in_set_keyblock(context, in, keyblock);
755 0 : if (ret) {
756 0 : krb5_rd_req_in_ctx_free(context, in);
757 0 : return ret;
758 : }
759 :
760 0 : ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
761 0 : krb5_rd_req_in_ctx_free(context, in);
762 0 : if (ret)
763 0 : return ret;
764 :
765 0 : if (ap_req_options)
766 0 : *ap_req_options = out->ap_req_options;
767 0 : if (ticket) {
768 0 : ret = krb5_copy_ticket(context, out->ticket, ticket);
769 0 : if (ret)
770 0 : goto out;
771 : }
772 :
773 0 : out:
774 0 : krb5_rd_req_out_ctx_free(context, out);
775 0 : return ret;
776 : }
777 :
778 : /*
779 : *
780 : */
781 :
782 : static krb5_error_code
783 53469 : get_key_from_keytab(krb5_context context,
784 : krb5_ap_req *ap_req,
785 : krb5_const_principal server,
786 : krb5_keytab keytab,
787 : krb5_keyblock **out_key)
788 : {
789 881 : krb5_keytab_entry entry;
790 881 : krb5_error_code ret;
791 881 : int kvno;
792 881 : krb5_keytab real_keytab;
793 :
794 53469 : if(keytab == NULL)
795 0 : krb5_kt_default(context, &real_keytab);
796 : else
797 53469 : real_keytab = keytab;
798 :
799 53469 : if (ap_req->ticket.enc_part.kvno)
800 53469 : kvno = *ap_req->ticket.enc_part.kvno;
801 : else
802 0 : kvno = 0;
803 :
804 53469 : ret = krb5_kt_get_entry (context,
805 : real_keytab,
806 : server,
807 : kvno,
808 : ap_req->ticket.enc_part.etype,
809 : &entry);
810 53469 : if(ret == 0) {
811 52562 : ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
812 52562 : krb5_kt_free_entry(context, &entry);
813 : }
814 53469 : if(keytab == NULL)
815 0 : krb5_kt_close(context, real_keytab);
816 :
817 53469 : return ret;
818 : }
819 :
820 : /**
821 : * The core server function that verify application authentication
822 : * requests from clients.
823 : *
824 : * @param context Keberos 5 context.
825 : * @param auth_context the authentication context, can be NULL, then
826 : * default values for the authentication context will used.
827 : * @param inbuf the (AP-REQ) authentication buffer
828 : *
829 : * @param server the server to authenticate to. If NULL the function
830 : * will try to find any available credential in the keytab
831 : * that will verify the reply. The function will prefer the
832 : * server specified in the AP-REQ, but if
833 : * there is no mach, it will try all keytab entries for a
834 : * match. This has serious performance issues for large keytabs.
835 : *
836 : * @param inctx control the behavior of the function, if NULL, the
837 : * default behavior is used.
838 : * @param outctx the return outctx, free with krb5_rd_req_out_ctx_free().
839 : * @return Kerberos 5 error code, see krb5_get_error_message().
840 : *
841 : * @ingroup krb5_auth
842 : */
843 :
844 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
845 53469 : krb5_rd_req_ctx(krb5_context context,
846 : krb5_auth_context *auth_context,
847 : const krb5_data *inbuf,
848 : krb5_const_principal server,
849 : krb5_rd_req_in_ctx inctx,
850 : krb5_rd_req_out_ctx *outctx)
851 : {
852 881 : krb5_error_code ret;
853 881 : krb5_ap_req ap_req;
854 53469 : krb5_rd_req_out_ctx o = NULL;
855 53469 : krb5_keytab id = NULL, keytab = NULL;
856 53469 : krb5_principal service = NULL;
857 :
858 53469 : if (outctx)
859 53469 : *outctx = NULL;
860 :
861 53469 : o = calloc(1, sizeof(*o));
862 53469 : if (o == NULL)
863 0 : return krb5_enomem(context);
864 :
865 53469 : if (*auth_context == NULL) {
866 0 : ret = krb5_auth_con_init(context, auth_context);
867 0 : if (ret)
868 0 : goto out;
869 : }
870 :
871 53469 : ret = krb5_decode_ap_req(context, inbuf, &ap_req);
872 53469 : if(ret)
873 0 : goto out;
874 :
875 : /* Save the principal that was in the request */
876 53469 : ret = _krb5_principalname2krb5_principal(context,
877 : &o->server,
878 : ap_req.ticket.sname,
879 : ap_req.ticket.realm);
880 53469 : if (ret)
881 0 : goto out;
882 :
883 53469 : if (ap_req.ap_options.use_session_key &&
884 0 : (*auth_context)->keyblock == NULL) {
885 0 : ret = KRB5KRB_AP_ERR_NOKEY;
886 0 : krb5_set_error_message(context, ret,
887 0 : N_("krb5_rd_req: user to user auth "
888 : "without session key given", ""));
889 0 : goto out;
890 : }
891 :
892 53469 : if (inctx && inctx->keytab)
893 53469 : id = inctx->keytab;
894 :
895 53469 : if((*auth_context)->keyblock){
896 0 : ret = krb5_copy_keyblock(context,
897 0 : (*auth_context)->keyblock,
898 : &o->keyblock);
899 0 : if (ret)
900 0 : goto out;
901 53469 : } else if(inctx && inctx->keyblock){
902 0 : ret = krb5_copy_keyblock(context,
903 0 : inctx->keyblock,
904 : &o->keyblock);
905 0 : if (ret)
906 0 : goto out;
907 : } else {
908 :
909 53469 : if(id == NULL) {
910 0 : krb5_kt_default(context, &keytab);
911 0 : id = keytab;
912 : }
913 53469 : if (id == NULL)
914 0 : goto out;
915 :
916 53469 : if (server == NULL) {
917 894 : ret = _krb5_principalname2krb5_principal(context,
918 : &service,
919 : ap_req.ticket.sname,
920 : ap_req.ticket.realm);
921 894 : if (ret)
922 0 : goto out;
923 894 : server = service;
924 : }
925 :
926 53469 : ret = get_key_from_keytab(context,
927 : &ap_req,
928 : server,
929 : id,
930 : &o->keyblock);
931 53469 : if (ret) {
932 : /* If caller specified a server, fail. */
933 907 : if (service == NULL && (context->flags & KRB5_CTX_F_RD_REQ_IGNORE) == 0)
934 23 : goto out;
935 : /* Otherwise, fall back to iterating over the keytab. This
936 : * have serious performace issues for larger keytab.
937 : */
938 884 : o->keyblock = NULL;
939 : }
940 : }
941 :
942 53446 : if (o->keyblock) {
943 : /*
944 : * We got an exact keymatch, use that.
945 : */
946 :
947 52562 : ret = krb5_verify_ap_req2(context,
948 : auth_context,
949 : &ap_req,
950 : server,
951 : o->keyblock,
952 : 0,
953 : &o->ap_req_options,
954 : &o->ticket,
955 : KRB5_KU_AP_REQ_AUTH);
956 :
957 52562 : if (ret)
958 4 : goto out;
959 :
960 : } else {
961 : /*
962 : * Interate over keytab to find a key that can decrypt the request.
963 : */
964 :
965 0 : krb5_keytab_entry entry;
966 0 : krb5_kt_cursor cursor;
967 884 : int done = 0, kvno = 0;
968 :
969 884 : memset(&cursor, 0, sizeof(cursor));
970 :
971 884 : if (ap_req.ticket.enc_part.kvno)
972 884 : kvno = *ap_req.ticket.enc_part.kvno;
973 :
974 884 : ret = krb5_kt_start_seq_get(context, id, &cursor);
975 884 : if (ret)
976 0 : goto out;
977 :
978 884 : done = 0;
979 2747 : while (!done) {
980 0 : krb5_principal p;
981 :
982 1863 : ret = krb5_kt_next_entry(context, id, &entry, &cursor);
983 1863 : if (ret) {
984 0 : _krb5_kt_principal_not_found(context, ret, id, o->server,
985 : ap_req.ticket.enc_part.etype,
986 : kvno);
987 0 : break;
988 : }
989 :
990 1863 : if (entry.keyblock.keytype != ap_req.ticket.enc_part.etype) {
991 968 : krb5_kt_free_entry (context, &entry);
992 979 : continue;
993 : }
994 :
995 895 : ret = krb5_verify_ap_req2(context,
996 : auth_context,
997 : &ap_req,
998 : server,
999 : &entry.keyblock,
1000 : 0,
1001 : &o->ap_req_options,
1002 : &o->ticket,
1003 : KRB5_KU_AP_REQ_AUTH);
1004 895 : if (ret) {
1005 11 : krb5_kt_free_entry (context, &entry);
1006 11 : continue;
1007 : }
1008 :
1009 : /*
1010 : * Found a match, save the keyblock for PAC processing,
1011 : * and update the service principal in the ticket to match
1012 : * whatever is in the keytab.
1013 : */
1014 :
1015 884 : ret = krb5_copy_keyblock(context,
1016 : &entry.keyblock,
1017 : &o->keyblock);
1018 884 : if (ret) {
1019 0 : krb5_kt_free_entry (context, &entry);
1020 0 : break;
1021 : }
1022 :
1023 884 : ret = krb5_copy_principal(context, entry.principal, &p);
1024 884 : if (ret) {
1025 0 : krb5_kt_free_entry (context, &entry);
1026 0 : break;
1027 : }
1028 884 : krb5_free_principal(context, o->ticket->server);
1029 884 : o->ticket->server = p;
1030 :
1031 884 : krb5_kt_free_entry (context, &entry);
1032 :
1033 884 : done = 1;
1034 : }
1035 884 : krb5_kt_end_seq_get (context, id, &cursor);
1036 884 : if (ret)
1037 0 : goto out;
1038 : }
1039 :
1040 53442 : if (krb5_ticket_get_authorization_data_type(context, o->ticket,
1041 : KRB5_AUTHDATA_KDC_ISSUED,
1042 : NULL) == 0)
1043 0 : o->ticket->client->nameattrs->kdc_issued_verified = 1;
1044 :
1045 : /* If there is a PAC, verify its server signature */
1046 53442 : if (inctx == NULL || inctx->check_pac) {
1047 881 : krb5_pac pac;
1048 881 : krb5_data data;
1049 :
1050 53442 : ret = krb5_ticket_get_authorization_data_type(context,
1051 : o->ticket,
1052 : KRB5_AUTHDATA_WIN2K_PAC,
1053 : &data);
1054 53442 : if (ret == 0) {
1055 53434 : ret = krb5_pac_parse(context, data.data, data.length, &pac);
1056 53434 : krb5_data_free(&data);
1057 53434 : if (ret)
1058 0 : goto out;
1059 :
1060 54315 : ret = krb5_pac_verify(context,
1061 : pac,
1062 52553 : o->ticket->ticket.authtime,
1063 53434 : o->ticket->client,
1064 53434 : o->keyblock,
1065 : NULL);
1066 53434 : if (ret == 0)
1067 53434 : o->ticket->client->nameattrs->pac_verified = 1;
1068 53434 : if (ret == 0 && (context->flags & KRB5_CTX_F_REPORT_CANONICAL_CLIENT_NAME)) {
1069 0 : krb5_error_code ret2;
1070 0 : krb5_principal canon_name;
1071 :
1072 0 : ret2 = _krb5_pac_get_canon_principal(context, pac, &canon_name);
1073 0 : if (ret2 == 0) {
1074 0 : free_Realm(&o->ticket->client->realm);
1075 0 : free_PrincipalName(&o->ticket->client->name);
1076 0 : ret = copy_Realm(&canon_name->realm, &o->ticket->client->realm);
1077 0 : if (ret == 0)
1078 0 : ret = copy_PrincipalName(&canon_name->name, &o->ticket->client->name);
1079 0 : krb5_free_principal(context, canon_name);
1080 0 : } else if (ret2 != ENOENT)
1081 0 : ret = ret2;
1082 : }
1083 53434 : if (ret) {
1084 0 : krb5_pac_free(context, pac);
1085 0 : goto out;
1086 : }
1087 53434 : o->ticket->client->nameattrs->pac = pac;
1088 : } else
1089 8 : ret = 0;
1090 : }
1091 0 : out:
1092 :
1093 53469 : if (ret || outctx == NULL)
1094 27 : krb5_rd_req_out_ctx_free(context, o);
1095 : else
1096 53442 : *outctx = o;
1097 :
1098 53469 : free_AP_REQ(&ap_req);
1099 :
1100 53469 : if (service)
1101 894 : krb5_free_principal(context, service);
1102 :
1103 53469 : if (keytab)
1104 0 : krb5_kt_close(context, keytab);
1105 :
1106 52588 : return ret;
1107 : }
|