rd_req.c revision 178825
1/*
2 * Copyright (c) 1997 - 2007 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <krb5_locl.h>
35
36RCSID("$Id: rd_req.c 22235 2007-12-08 21:52:07Z lha $");
37
38static krb5_error_code
39decrypt_tkt_enc_part (krb5_context context,
40		      krb5_keyblock *key,
41		      EncryptedData *enc_part,
42		      EncTicketPart *decr_part)
43{
44    krb5_error_code ret;
45    krb5_data plain;
46    size_t len;
47    krb5_crypto crypto;
48
49    ret = krb5_crypto_init(context, key, 0, &crypto);
50    if (ret)
51	return ret;
52    ret = krb5_decrypt_EncryptedData (context,
53				      crypto,
54				      KRB5_KU_TICKET,
55				      enc_part,
56				      &plain);
57    krb5_crypto_destroy(context, crypto);
58    if (ret)
59	return ret;
60
61    ret = krb5_decode_EncTicketPart(context, plain.data, plain.length,
62				    decr_part, &len);
63    krb5_data_free (&plain);
64    return ret;
65}
66
67static krb5_error_code
68decrypt_authenticator (krb5_context context,
69		       EncryptionKey *key,
70		       EncryptedData *enc_part,
71		       Authenticator *authenticator,
72		       krb5_key_usage usage)
73{
74    krb5_error_code ret;
75    krb5_data plain;
76    size_t len;
77    krb5_crypto crypto;
78
79    ret = krb5_crypto_init(context, key, 0, &crypto);
80    if (ret)
81	return ret;
82    ret = krb5_decrypt_EncryptedData (context,
83				      crypto,
84				      usage /* KRB5_KU_AP_REQ_AUTH */,
85				      enc_part,
86				      &plain);
87    /* for backwards compatibility, also try the old usage */
88    if (ret && usage == KRB5_KU_TGS_REQ_AUTH)
89	ret = krb5_decrypt_EncryptedData (context,
90					  crypto,
91					  KRB5_KU_AP_REQ_AUTH,
92					  enc_part,
93					  &plain);
94    krb5_crypto_destroy(context, crypto);
95    if (ret)
96	return ret;
97
98    ret = krb5_decode_Authenticator(context, plain.data, plain.length,
99				    authenticator, &len);
100    krb5_data_free (&plain);
101    return ret;
102}
103
104krb5_error_code KRB5_LIB_FUNCTION
105krb5_decode_ap_req(krb5_context context,
106		   const krb5_data *inbuf,
107		   krb5_ap_req *ap_req)
108{
109    krb5_error_code ret;
110    size_t len;
111    ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len);
112    if (ret)
113	return ret;
114    if (ap_req->pvno != 5){
115	free_AP_REQ(ap_req);
116	krb5_clear_error_string (context);
117	return KRB5KRB_AP_ERR_BADVERSION;
118    }
119    if (ap_req->msg_type != krb_ap_req){
120	free_AP_REQ(ap_req);
121	krb5_clear_error_string (context);
122	return KRB5KRB_AP_ERR_MSG_TYPE;
123    }
124    if (ap_req->ticket.tkt_vno != 5){
125	free_AP_REQ(ap_req);
126	krb5_clear_error_string (context);
127	return KRB5KRB_AP_ERR_BADVERSION;
128    }
129    return 0;
130}
131
132static krb5_error_code
133check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc)
134{
135    char **realms;
136    int num_realms;
137    krb5_error_code ret;
138
139    /*
140     * Windows 2000 and 2003 uses this inside their TGT so it's normaly
141     * not seen by others, however, samba4 joined with a Windows AD as
142     * a Domain Controller gets exposed to this.
143     */
144    if(enc->transited.tr_type == 0 && enc->transited.contents.length == 0)
145	return 0;
146
147    if(enc->transited.tr_type != DOMAIN_X500_COMPRESS)
148	return KRB5KDC_ERR_TRTYPE_NOSUPP;
149
150    if(enc->transited.contents.length == 0)
151	return 0;
152
153    ret = krb5_domain_x500_decode(context, enc->transited.contents,
154				  &realms, &num_realms,
155				  enc->crealm,
156				  ticket->realm);
157    if(ret)
158	return ret;
159    ret = krb5_check_transited(context, enc->crealm,
160			       ticket->realm,
161			       realms, num_realms, NULL);
162    free(realms);
163    return ret;
164}
165
166static krb5_error_code
167find_etypelist(krb5_context context,
168	       krb5_auth_context auth_context,
169	       EtypeList *etypes)
170{
171    krb5_error_code ret;
172    krb5_authdata *ad;
173    krb5_authdata adIfRelevant;
174    unsigned i;
175
176    adIfRelevant.len = 0;
177
178    etypes->len = 0;
179    etypes->val = NULL;
180
181    ad = auth_context->authenticator->authorization_data;
182    if (ad == NULL)
183	return 0;
184
185    for (i = 0; i < ad->len; i++) {
186	if (ad->val[i].ad_type == KRB5_AUTHDATA_IF_RELEVANT) {
187	    ret = decode_AD_IF_RELEVANT(ad->val[i].ad_data.data,
188					ad->val[i].ad_data.length,
189					&adIfRelevant,
190					NULL);
191	    if (ret)
192		return ret;
193
194	    if (adIfRelevant.len == 1 &&
195		adIfRelevant.val[0].ad_type ==
196			KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION) {
197		break;
198	    }
199	    free_AD_IF_RELEVANT(&adIfRelevant);
200	    adIfRelevant.len = 0;
201	}
202    }
203
204    if (adIfRelevant.len == 0)
205	return 0;
206
207    ret = decode_EtypeList(adIfRelevant.val[0].ad_data.data,
208			   adIfRelevant.val[0].ad_data.length,
209			   etypes,
210			   NULL);
211    if (ret)
212	krb5_clear_error_string(context);
213
214    free_AD_IF_RELEVANT(&adIfRelevant);
215
216    return ret;
217}
218
219krb5_error_code KRB5_LIB_FUNCTION
220krb5_decrypt_ticket(krb5_context context,
221		    Ticket *ticket,
222		    krb5_keyblock *key,
223		    EncTicketPart *out,
224		    krb5_flags flags)
225{
226    EncTicketPart t;
227    krb5_error_code ret;
228    ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t);
229    if (ret)
230	return ret;
231
232    {
233	krb5_timestamp now;
234	time_t start = t.authtime;
235
236	krb5_timeofday (context, &now);
237	if(t.starttime)
238	    start = *t.starttime;
239	if(start - now > context->max_skew
240	   || (t.flags.invalid
241	       && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) {
242	    free_EncTicketPart(&t);
243	    krb5_clear_error_string (context);
244	    return KRB5KRB_AP_ERR_TKT_NYV;
245	}
246	if(now - t.endtime > context->max_skew) {
247	    free_EncTicketPart(&t);
248	    krb5_clear_error_string (context);
249	    return KRB5KRB_AP_ERR_TKT_EXPIRED;
250	}
251
252	if(!t.flags.transited_policy_checked) {
253	    ret = check_transited(context, ticket, &t);
254	    if(ret) {
255		free_EncTicketPart(&t);
256		return ret;
257	    }
258	}
259    }
260
261    if(out)
262	*out = t;
263    else
264	free_EncTicketPart(&t);
265    return 0;
266}
267
268krb5_error_code KRB5_LIB_FUNCTION
269krb5_verify_authenticator_checksum(krb5_context context,
270				   krb5_auth_context ac,
271				   void *data,
272				   size_t len)
273{
274    krb5_error_code ret;
275    krb5_keyblock *key;
276    krb5_authenticator authenticator;
277    krb5_crypto crypto;
278
279    ret = krb5_auth_con_getauthenticator (context,
280				      ac,
281				      &authenticator);
282    if(ret)
283	return ret;
284    if(authenticator->cksum == NULL) {
285	krb5_free_authenticator(context, &authenticator);
286	return -17;
287    }
288    ret = krb5_auth_con_getkey(context, ac, &key);
289    if(ret) {
290	krb5_free_authenticator(context, &authenticator);
291	return ret;
292    }
293    ret = krb5_crypto_init(context, key, 0, &crypto);
294    if(ret)
295	goto out;
296    ret = krb5_verify_checksum (context,
297				crypto,
298				KRB5_KU_AP_REQ_AUTH_CKSUM,
299				data,
300				len,
301				authenticator->cksum);
302    krb5_crypto_destroy(context, crypto);
303out:
304    krb5_free_authenticator(context, &authenticator);
305    krb5_free_keyblock(context, key);
306    return ret;
307}
308
309
310krb5_error_code KRB5_LIB_FUNCTION
311krb5_verify_ap_req(krb5_context context,
312		   krb5_auth_context *auth_context,
313		   krb5_ap_req *ap_req,
314		   krb5_const_principal server,
315		   krb5_keyblock *keyblock,
316		   krb5_flags flags,
317		   krb5_flags *ap_req_options,
318		   krb5_ticket **ticket)
319{
320    return krb5_verify_ap_req2 (context,
321				auth_context,
322				ap_req,
323				server,
324				keyblock,
325				flags,
326				ap_req_options,
327				ticket,
328				KRB5_KU_AP_REQ_AUTH);
329}
330
331krb5_error_code KRB5_LIB_FUNCTION
332krb5_verify_ap_req2(krb5_context context,
333		    krb5_auth_context *auth_context,
334		    krb5_ap_req *ap_req,
335		    krb5_const_principal server,
336		    krb5_keyblock *keyblock,
337		    krb5_flags flags,
338		    krb5_flags *ap_req_options,
339		    krb5_ticket **ticket,
340		    krb5_key_usage usage)
341{
342    krb5_ticket *t;
343    krb5_auth_context ac;
344    krb5_error_code ret;
345    EtypeList etypes;
346
347    if (ticket)
348	*ticket = NULL;
349
350    if (auth_context && *auth_context) {
351	ac = *auth_context;
352    } else {
353	ret = krb5_auth_con_init (context, &ac);
354	if (ret)
355	    return ret;
356    }
357
358    t = calloc(1, sizeof(*t));
359    if (t == NULL) {
360	ret = ENOMEM;
361	krb5_clear_error_string (context);
362	goto out;
363    }
364
365    if (ap_req->ap_options.use_session_key && ac->keyblock){
366	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
367				  ac->keyblock,
368				  &t->ticket,
369				  flags);
370	krb5_free_keyblock(context, ac->keyblock);
371	ac->keyblock = NULL;
372    }else
373	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
374				  keyblock,
375				  &t->ticket,
376				  flags);
377
378    if(ret)
379	goto out;
380
381    ret = _krb5_principalname2krb5_principal(context,
382					     &t->server,
383					     ap_req->ticket.sname,
384					     ap_req->ticket.realm);
385    if (ret) goto out;
386    ret = _krb5_principalname2krb5_principal(context,
387					     &t->client,
388					     t->ticket.cname,
389					     t->ticket.crealm);
390    if (ret) goto out;
391
392    /* save key */
393
394    ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock);
395    if (ret) goto out;
396
397    ret = decrypt_authenticator (context,
398				 &t->ticket.key,
399				 &ap_req->authenticator,
400				 ac->authenticator,
401				 usage);
402    if (ret)
403	goto out;
404
405    {
406	krb5_principal p1, p2;
407	krb5_boolean res;
408
409	_krb5_principalname2krb5_principal(context,
410					   &p1,
411					   ac->authenticator->cname,
412					   ac->authenticator->crealm);
413	_krb5_principalname2krb5_principal(context,
414					   &p2,
415					   t->ticket.cname,
416					   t->ticket.crealm);
417	res = krb5_principal_compare (context, p1, p2);
418	krb5_free_principal (context, p1);
419	krb5_free_principal (context, p2);
420	if (!res) {
421	    ret = KRB5KRB_AP_ERR_BADMATCH;
422	    krb5_clear_error_string (context);
423	    goto out;
424	}
425    }
426
427    /* check addresses */
428
429    if (t->ticket.caddr
430	&& ac->remote_address
431	&& !krb5_address_search (context,
432				 ac->remote_address,
433				 t->ticket.caddr)) {
434	ret = KRB5KRB_AP_ERR_BADADDR;
435	krb5_clear_error_string (context);
436	goto out;
437    }
438
439    /* check timestamp in authenticator */
440    {
441	krb5_timestamp now;
442
443	krb5_timeofday (context, &now);
444
445	if (abs(ac->authenticator->ctime - now) > context->max_skew) {
446	    ret = KRB5KRB_AP_ERR_SKEW;
447	    krb5_clear_error_string (context);
448	    goto out;
449	}
450    }
451
452    if (ac->authenticator->seq_number)
453	krb5_auth_con_setremoteseqnumber(context, ac,
454					 *ac->authenticator->seq_number);
455
456    /* XXX - Xor sequence numbers */
457
458    if (ac->authenticator->subkey) {
459	ret = krb5_auth_con_setremotesubkey(context, ac,
460					    ac->authenticator->subkey);
461	if (ret)
462	    goto out;
463    }
464
465    ret = find_etypelist(context, ac, &etypes);
466    if (ret)
467	goto out;
468
469    ac->keytype = ETYPE_NULL;
470
471    if (etypes.val) {
472	int i;
473
474	for (i = 0; i < etypes.len; i++) {
475	    if (krb5_enctype_valid(context, etypes.val[i]) == 0) {
476		ac->keytype = etypes.val[i];
477		break;
478	    }
479	}
480    }
481
482    if (ap_req_options) {
483	*ap_req_options = 0;
484	if (ac->keytype != ETYPE_NULL)
485	    *ap_req_options |= AP_OPTS_USE_SUBKEY;
486	if (ap_req->ap_options.use_session_key)
487	    *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
488	if (ap_req->ap_options.mutual_required)
489	    *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
490    }
491
492    if(ticket)
493	*ticket = t;
494    else
495	krb5_free_ticket (context, t);
496    if (auth_context) {
497	if (*auth_context == NULL)
498	    *auth_context = ac;
499    } else
500	krb5_auth_con_free (context, ac);
501    free_EtypeList(&etypes);
502    return 0;
503 out:
504    if (t)
505	krb5_free_ticket (context, t);
506    if (auth_context == NULL || *auth_context == NULL)
507	krb5_auth_con_free (context, ac);
508    return ret;
509}
510
511/*
512 *
513 */
514
515struct krb5_rd_req_in_ctx_data {
516    krb5_keytab keytab;
517    krb5_keyblock *keyblock;
518    krb5_boolean check_pac;
519};
520
521struct krb5_rd_req_out_ctx_data {
522    krb5_keyblock *keyblock;
523    krb5_flags ap_req_options;
524    krb5_ticket *ticket;
525};
526
527/*
528 *
529 */
530
531krb5_error_code KRB5_LIB_FUNCTION
532krb5_rd_req_in_ctx_alloc(krb5_context context, krb5_rd_req_in_ctx *ctx)
533{
534    *ctx = calloc(1, sizeof(**ctx));
535    if (*ctx == NULL) {
536	krb5_set_error_string(context, "out of memory");
537	return ENOMEM;
538    }
539    (*ctx)->check_pac = (context->flags & KRB5_CTX_F_CHECK_PAC) ? 1 : 0;
540    return 0;
541}
542
543krb5_error_code KRB5_LIB_FUNCTION
544krb5_rd_req_in_set_keytab(krb5_context context,
545			  krb5_rd_req_in_ctx in,
546			  krb5_keytab keytab)
547{
548    in->keytab = keytab; /* XXX should make copy */
549    return 0;
550}
551
552/**
553 * Set if krb5_rq_red() is going to check the Windows PAC or not
554 *
555 * @param context Keberos 5 context.
556 * @param in krb5_rd_req_in_ctx to check the option on.
557 * @param flag flag to select if to check the pac (TRUE) or not (FALSE).
558 *
559 * @return Kerberos 5 error code, see krb5_get_error_message().
560 *
561 * @ingroup krb5
562 */
563
564krb5_error_code KRB5_LIB_FUNCTION
565krb5_rd_req_in_set_pac_check(krb5_context context,
566			     krb5_rd_req_in_ctx in,
567			     krb5_boolean flag)
568{
569    in->check_pac = flag;
570    return 0;
571}
572
573
574krb5_error_code KRB5_LIB_FUNCTION
575krb5_rd_req_in_set_keyblock(krb5_context context,
576			    krb5_rd_req_in_ctx in,
577			    krb5_keyblock *keyblock)
578{
579    in->keyblock = keyblock; /* XXX should make copy */
580    return 0;
581}
582
583krb5_error_code KRB5_LIB_FUNCTION
584krb5_rd_req_out_get_ap_req_options(krb5_context context,
585				   krb5_rd_req_out_ctx out,
586				   krb5_flags *ap_req_options)
587{
588    *ap_req_options = out->ap_req_options;
589    return 0;
590}
591
592krb5_error_code KRB5_LIB_FUNCTION
593krb5_rd_req_out_get_ticket(krb5_context context,
594			    krb5_rd_req_out_ctx out,
595			    krb5_ticket **ticket)
596{
597    return krb5_copy_ticket(context, out->ticket, ticket);
598}
599
600krb5_error_code KRB5_LIB_FUNCTION
601krb5_rd_req_out_get_keyblock(krb5_context context,
602			    krb5_rd_req_out_ctx out,
603			    krb5_keyblock **keyblock)
604{
605    return krb5_copy_keyblock(context, out->keyblock, keyblock);
606}
607
608void  KRB5_LIB_FUNCTION
609krb5_rd_req_in_ctx_free(krb5_context context, krb5_rd_req_in_ctx ctx)
610{
611    free(ctx);
612}
613
614krb5_error_code KRB5_LIB_FUNCTION
615_krb5_rd_req_out_ctx_alloc(krb5_context context, krb5_rd_req_out_ctx *ctx)
616{
617    *ctx = calloc(1, sizeof(**ctx));
618    if (*ctx == NULL) {
619	krb5_set_error_string(context, "out of memory");
620	return ENOMEM;
621    }
622    return 0;
623}
624
625void  KRB5_LIB_FUNCTION
626krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx)
627{
628    krb5_free_keyblock(context, ctx->keyblock);
629    free(ctx);
630}
631
632/*
633 *
634 */
635
636krb5_error_code KRB5_LIB_FUNCTION
637krb5_rd_req(krb5_context context,
638	    krb5_auth_context *auth_context,
639	    const krb5_data *inbuf,
640	    krb5_const_principal server,
641	    krb5_keytab keytab,
642	    krb5_flags *ap_req_options,
643	    krb5_ticket **ticket)
644{
645    krb5_error_code ret;
646    krb5_rd_req_in_ctx in;
647    krb5_rd_req_out_ctx out;
648
649    ret = krb5_rd_req_in_ctx_alloc(context, &in);
650    if (ret)
651	return ret;
652
653    ret = krb5_rd_req_in_set_keytab(context, in, keytab);
654    if (ret) {
655	krb5_rd_req_in_ctx_free(context, in);
656	return ret;
657    }
658
659    ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
660    krb5_rd_req_in_ctx_free(context, in);
661    if (ret)
662	return ret;
663
664    if (ap_req_options)
665	*ap_req_options = out->ap_req_options;
666    if (ticket) {
667	ret = krb5_copy_ticket(context, out->ticket, ticket);
668	if (ret)
669	    goto out;
670    }
671
672out:
673    krb5_rd_req_out_ctx_free(context, out);
674    return ret;
675}
676
677/*
678 *
679 */
680
681krb5_error_code KRB5_LIB_FUNCTION
682krb5_rd_req_with_keyblock(krb5_context context,
683			  krb5_auth_context *auth_context,
684			  const krb5_data *inbuf,
685			  krb5_const_principal server,
686			  krb5_keyblock *keyblock,
687			  krb5_flags *ap_req_options,
688			  krb5_ticket **ticket)
689{
690    krb5_error_code ret;
691    krb5_rd_req_in_ctx in;
692    krb5_rd_req_out_ctx out;
693
694    ret = krb5_rd_req_in_ctx_alloc(context, &in);
695    if (ret)
696	return ret;
697
698    ret = krb5_rd_req_in_set_keyblock(context, in, keyblock);
699    if (ret) {
700	krb5_rd_req_in_ctx_free(context, in);
701	return ret;
702    }
703
704    ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
705    krb5_rd_req_in_ctx_free(context, in);
706    if (ret)
707	return ret;
708
709    if (ap_req_options)
710	*ap_req_options = out->ap_req_options;
711    if (ticket) {
712	ret = krb5_copy_ticket(context, out->ticket, ticket);
713	if (ret)
714	    goto out;
715    }
716
717out:
718    krb5_rd_req_out_ctx_free(context, out);
719    return ret;
720}
721
722/*
723 *
724 */
725
726static krb5_error_code
727get_key_from_keytab(krb5_context context,
728		    krb5_auth_context *auth_context,
729		    krb5_ap_req *ap_req,
730		    krb5_const_principal server,
731		    krb5_keytab keytab,
732		    krb5_keyblock **out_key)
733{
734    krb5_keytab_entry entry;
735    krb5_error_code ret;
736    int kvno;
737    krb5_keytab real_keytab;
738
739    if(keytab == NULL)
740	krb5_kt_default(context, &real_keytab);
741    else
742	real_keytab = keytab;
743
744    if (ap_req->ticket.enc_part.kvno)
745	kvno = *ap_req->ticket.enc_part.kvno;
746    else
747	kvno = 0;
748
749    ret = krb5_kt_get_entry (context,
750			     real_keytab,
751			     server,
752			     kvno,
753			     ap_req->ticket.enc_part.etype,
754			     &entry);
755    if(ret)
756	goto out;
757    ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
758    krb5_kt_free_entry (context, &entry);
759out:
760    if(keytab == NULL)
761	krb5_kt_close(context, real_keytab);
762
763    return ret;
764}
765
766/*
767 *
768 */
769
770krb5_error_code KRB5_LIB_FUNCTION
771krb5_rd_req_ctx(krb5_context context,
772		krb5_auth_context *auth_context,
773		const krb5_data *inbuf,
774		krb5_const_principal server,
775		krb5_rd_req_in_ctx inctx,
776		krb5_rd_req_out_ctx *outctx)
777{
778    krb5_error_code ret;
779    krb5_ap_req ap_req;
780    krb5_principal service = NULL;
781    krb5_rd_req_out_ctx o = NULL;
782
783    ret = _krb5_rd_req_out_ctx_alloc(context, &o);
784    if (ret)
785	goto out;
786
787    if (*auth_context == NULL) {
788	ret = krb5_auth_con_init(context, auth_context);
789	if (ret)
790	    goto out;
791    }
792
793    ret = krb5_decode_ap_req(context, inbuf, &ap_req);
794    if(ret)
795	goto out;
796
797    if(server == NULL){
798	ret = _krb5_principalname2krb5_principal(context,
799						 &service,
800						 ap_req.ticket.sname,
801						 ap_req.ticket.realm);
802	if (ret)
803	    goto out;
804	server = service;
805    }
806    if (ap_req.ap_options.use_session_key &&
807	(*auth_context)->keyblock == NULL) {
808	krb5_set_error_string(context, "krb5_rd_req: user to user auth "
809			      "without session key given");
810	ret = KRB5KRB_AP_ERR_NOKEY;
811	goto out;
812    }
813
814    if((*auth_context)->keyblock){
815	ret = krb5_copy_keyblock(context,
816				 (*auth_context)->keyblock,
817				 &o->keyblock);
818	if (ret)
819	    goto out;
820    } else if(inctx->keyblock){
821	ret = krb5_copy_keyblock(context,
822				 inctx->keyblock,
823				 &o->keyblock);
824	if (ret)
825	    goto out;
826    } else {
827	krb5_keytab keytab = NULL;
828
829	if (inctx && inctx->keytab)
830	    keytab = inctx->keytab;
831
832	ret = get_key_from_keytab(context,
833				  auth_context,
834				  &ap_req,
835				  server,
836				  keytab,
837				  &o->keyblock);
838	if(ret)
839	    goto out;
840    }
841
842    ret = krb5_verify_ap_req2(context,
843			      auth_context,
844			      &ap_req,
845			      server,
846			      o->keyblock,
847			      0,
848			      &o->ap_req_options,
849			      &o->ticket,
850			      KRB5_KU_AP_REQ_AUTH);
851
852    if (ret)
853	goto out;
854
855    /* If there is a PAC, verify its server signature */
856    if (inctx->check_pac) {
857	krb5_pac pac;
858	krb5_data data;
859
860	ret = krb5_ticket_get_authorization_data_type(context,
861						      o->ticket,
862						      KRB5_AUTHDATA_WIN2K_PAC,
863						      &data);
864	if (ret == 0) {
865	    ret = krb5_pac_parse(context, data.data, data.length, &pac);
866	    krb5_data_free(&data);
867	    if (ret)
868		goto out;
869
870	    ret = krb5_pac_verify(context,
871				  pac,
872				  o->ticket->ticket.authtime,
873				  o->ticket->client,
874				  o->keyblock,
875				  NULL);
876	    krb5_pac_free(context, pac);
877	    if (ret)
878		goto out;
879	}
880	ret = 0;
881    }
882out:
883    if (ret || outctx == NULL) {
884	krb5_rd_req_out_ctx_free(context, o);
885    } else
886	*outctx = o;
887
888    free_AP_REQ(&ap_req);
889    if(service)
890	krb5_free_principal(context, service);
891    return ret;
892}
893