1 /*
2  * COPYRIGHT (C) 2006,2007
3  * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
4  * ALL RIGHTS RESERVED
5  *
6  * Permission is granted to use, copy, create derivative works
7  * and redistribute this software and such derivative works
8  * for any purpose, so long as the name of The University of
9  * Michigan is not used in any advertising or publicity
10  * pertaining to the use of distribution of this software
11  * without specific, written prior authorization.  If the
12  * above copyright notice or any other identification of the
13  * University of Michigan is included in any copy of any
14  * portion of this software, then the disclaimer below must
15  * also be included.
16  *
17  * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
18  * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
19  * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
20  * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
21  * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
22  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
23  * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
24  * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
25  * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
26  * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
27  * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGES.
29  */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <assert.h>
38 #include <dlfcn.h>
39 #include <sys/stat.h>
40 
41 #include "pkinit.h"
42 
43 #ifdef LONGHORN_BETA_COMPAT
44 /*
45  * It is anticipated that all the special checks currently
46  * required when talking to a Longhorn server will go away
47  * by the time it is officially released and all references
48  * to the longhorn global can be removed and any code
49  * #ifdef'd with LONGHORN_BETA_COMPAT can be removed.
50  *
51  * Current testing (20070620) is against a patched Beta 3
52  * version of Longhorn.  Most, if not all, problems should
53  * be fixed in SP1 of Longhorn.
54  */
55 int longhorn = 0;	/* Talking to a Longhorn server? */
56 #endif
57 
58 krb5_error_code pkinit_client_process
59 	(krb5_context context, void *plugin_context, void *request_context,
60 		krb5_get_init_creds_opt *gic_opt,
61 		preauth_get_client_data_proc get_data_proc,
62 		struct _krb5_preauth_client_rock *rock,
63 		krb5_kdc_req * request, krb5_data *encoded_request_body,
64 		krb5_data *encoded_previous_request, krb5_pa_data *in_padata,
65 		krb5_prompter_fct prompter, void *prompter_data,
66 		preauth_get_as_key_proc gak_fct, void *gak_data,
67 		krb5_data * salt, krb5_data * s2kparams,
68 		krb5_keyblock * as_key, krb5_pa_data *** out_padata);
69 
70 krb5_error_code pkinit_client_tryagain
71 	(krb5_context context, void *plugin_context, void *request_context,
72 		krb5_get_init_creds_opt *gic_opt,
73 		preauth_get_client_data_proc get_data_proc,
74 		struct _krb5_preauth_client_rock *rock,
75 		krb5_kdc_req * request, krb5_data *encoded_request_body,
76 		krb5_data *encoded_previous_request,
77 		krb5_pa_data *in_padata, krb5_error *err_reply,
78 		krb5_prompter_fct prompter, void *prompter_data,
79 		preauth_get_as_key_proc gak_fct, void *gak_data,
80 		krb5_data * salt, krb5_data * s2kparams,
81 		krb5_keyblock * as_key, krb5_pa_data *** out_padata);
82 
83 void pkinit_client_req_init
84 	(krb5_context contex, void *plugin_context, void **request_context);
85 
86 void pkinit_client_req_fini
87 	(krb5_context context, void *plugin_context, void *request_context);
88 
89 krb5_error_code pa_pkinit_gen_req
90 	(krb5_context context, pkinit_context plgctx,
91 		pkinit_req_context reqctx, krb5_kdc_req * request,
92 		krb5_pa_data * in_padata, krb5_pa_data *** out_padata,
93 		krb5_prompter_fct prompter, void *prompter_data,
94 		krb5_get_init_creds_opt *gic_opt);
95 
96 krb5_error_code pkinit_as_req_create
97 	(krb5_context context, pkinit_context plgctx,
98 		pkinit_req_context reqctx, krb5_timestamp ctsec,
99 		krb5_int32 cusec, krb5_ui_4 nonce,
100 		const krb5_checksum * cksum, krb5_principal server,
101 		krb5_data ** as_req);
102 
103 krb5_error_code pkinit_as_rep_parse
104 	(krb5_context context, pkinit_context plgctx,
105 		pkinit_req_context reqctx, krb5_preauthtype pa_type,
106 		krb5_kdc_req * request, const krb5_data * as_rep,
107 		krb5_keyblock * key_block, krb5_enctype etype, krb5_data *);
108 
109 krb5_error_code pa_pkinit_parse_rep
110 	(krb5_context context, pkinit_context plgctx,
111 		pkinit_req_context reqcxt, krb5_kdc_req * request,
112 		krb5_pa_data * in_padata, krb5_enctype etype,
113 		krb5_keyblock * as_key, krb5_data *);
114 
115 static int pkinit_client_plugin_init(krb5_context context, void **blob);
116 static void pkinit_client_plugin_fini(krb5_context context, void *blob);
117 
118 /* ARGSUSED */
119 krb5_error_code
120 pa_pkinit_gen_req(krb5_context context,
121 		  pkinit_context plgctx,
122 		  pkinit_req_context reqctx,
123 		  krb5_kdc_req * request,
124 		  krb5_pa_data * in_padata,
125 		  krb5_pa_data *** out_padata,
126 		  krb5_prompter_fct prompter,
127 		  void *prompter_data,
128 		  krb5_get_init_creds_opt *gic_opt)
129 {
130 
131     krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
132     krb5_data *out_data = NULL;
133     krb5_timestamp ctsec = 0;
134     krb5_int32 cusec = 0;
135     krb5_ui_4 nonce = 0;
136     krb5_checksum cksum;
137     krb5_data *der_req = NULL;
138     krb5_pa_data **return_pa_data = NULL;
139 
140     cksum.contents = NULL;
141     reqctx->pa_type = in_padata->pa_type;
142 
143     pkiDebug("kdc_options = 0x%x  till = %d\n",
144 	     request->kdc_options, request->till);
145     /* If we don't have a client, we're done */
146     if (request->client == NULL) {
147 	pkiDebug("No request->client; aborting PKINIT\n");
148 	return KRB5KDC_ERR_PREAUTH_FAILED;
149     }
150 
151     retval = pkinit_get_kdc_cert(context, plgctx->cryptoctx, reqctx->cryptoctx,
152 				 reqctx->idctx, request->server);
153     if (retval) {
154 	pkiDebug("pkinit_get_kdc_cert returned %d\n", retval);
155 	goto cleanup;
156     }
157 
158     /* checksum of the encoded KDC-REQ-BODY */
159     retval = k5int_encode_krb5_kdc_req_body(request, &der_req);
160     if (retval) {
161 	pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval);
162 	goto cleanup;
163     }
164 
165     retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0,
166 				  der_req, &cksum);
167     if (retval)
168 	goto cleanup;
169 #ifdef DEBUG_CKSUM
170     pkiDebug("calculating checksum on buf size (%d)\n", der_req->length);
171     print_buffer(der_req->data, der_req->length);
172 #endif
173 
174     retval = krb5_us_timeofday(context, &ctsec, &cusec);
175     if (retval)
176 	goto cleanup;
177 
178     /* XXX PKINIT RFC says that nonce in PKAuthenticator doesn't have be the
179      * same as in the AS_REQ. However, if we pick a different nonce, then we
180      * need to remember that info when AS_REP is returned. I'm choosing to
181      * reuse the AS_REQ nonce.
182      */
183     nonce = request->nonce;
184 
185     retval = pkinit_as_req_create(context, plgctx, reqctx, ctsec, cusec,
186 				  nonce, &cksum, request->server, &out_data);
187     if (retval || !out_data->length) {
188 	pkiDebug("error %d on pkinit_as_req_create; aborting PKINIT\n",
189 		 (int) retval);
190 	goto cleanup;
191     }
192     retval = ENOMEM;
193     /*
194      * The most we'll return is two pa_data, normally just one.
195      * We need to make room for the NULL terminator.
196      */
197     return_pa_data = (krb5_pa_data **) malloc(3 * sizeof(krb5_pa_data *));
198     if (return_pa_data == NULL)
199 	goto cleanup;
200 
201     return_pa_data[1] = NULL;	/* in case of an early trip to cleanup */
202     return_pa_data[2] = NULL;	/* Terminate the list */
203 
204     return_pa_data[0] = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
205     if (return_pa_data[0] == NULL)
206 	goto cleanup;
207 
208     return_pa_data[1] = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
209     if (return_pa_data[1] == NULL)
210 	goto cleanup;
211 
212     return_pa_data[0]->magic = KV5M_PA_DATA;
213 
214     if (in_padata->pa_type == KRB5_PADATA_PK_AS_REQ_OLD)
215 	return_pa_data[0]->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
216     else
217 	return_pa_data[0]->pa_type = in_padata->pa_type;
218     return_pa_data[0]->length = out_data->length;
219     return_pa_data[0]->contents = (krb5_octet *) out_data->data;
220 
221 #ifdef LONGHORN_BETA_COMPAT
222     /*
223      * LH Beta 3 requires the extra pa-data, even for RFC requests,
224      * in order to get the Checksum rather than a Nonce in the reply.
225      * This can be removed when LH SP1 is released.
226      */
227     if ((return_pa_data[0]->pa_type == KRB5_PADATA_PK_AS_REP_OLD
228 	&& reqctx->opts->win2k_require_cksum) || (longhorn == 1)) {
229 #else
230     if ((return_pa_data[0]->pa_type == KRB5_PADATA_PK_AS_REP_OLD
231 	&& reqctx->opts->win2k_require_cksum)) {
232 #endif
233 	return_pa_data[1]->pa_type = 132;
234 	return_pa_data[1]->length = 0;
235 	return_pa_data[1]->contents = NULL;
236     } else {
237 	free(return_pa_data[1]);
238 	return_pa_data[1] = NULL;   /* Move the list terminator */
239     }
240     *out_padata = return_pa_data;
241     retval = 0;
242 
243   cleanup:
244     if (der_req != NULL)
245 	krb5_free_data(context, der_req);
246 
247     if (out_data != NULL)
248 	free(out_data);
249 
250     if (retval) {
251 	if (return_pa_data) {
252 	    if (return_pa_data[0] != NULL)
253 		free(return_pa_data[0]);
254 	    if (return_pa_data[1] != NULL)
255 		free(return_pa_data[1]);
256 	    free(return_pa_data);
257 	}
258 	if (out_data) {
259 	    free(out_data->data);
260 	    free(out_data);
261 	}
262     }
263     return retval;
264 }
265 
266 krb5_error_code
267 pkinit_as_req_create(krb5_context context,
268 		     pkinit_context plgctx,
269 		     pkinit_req_context reqctx,
270 		     krb5_timestamp ctsec,
271 		     krb5_int32 cusec,
272 		     krb5_ui_4 nonce,
273 		     const krb5_checksum * cksum,
274 		     krb5_principal server,
275 		     krb5_data ** as_req)
276 {
277     krb5_error_code retval = ENOMEM;
278     krb5_subject_pk_info *info = NULL;
279     krb5_data *coded_auth_pack = NULL;
280     krb5_auth_pack *auth_pack = NULL;
281     krb5_pa_pk_as_req *req = NULL;
282     krb5_auth_pack_draft9 *auth_pack9 = NULL;
283     krb5_pa_pk_as_req_draft9 *req9 = NULL;
284     int protocol = reqctx->opts->dh_or_rsa;
285 
286     pkiDebug("pkinit_as_req_create pa_type = %d\n", reqctx->pa_type);
287 
288     /* Create the authpack */
289     switch((int)reqctx->pa_type) {
290 	case KRB5_PADATA_PK_AS_REQ_OLD:
291 	    protocol = RSA_PROTOCOL;
292 	    init_krb5_auth_pack_draft9(&auth_pack9);
293 	    if (auth_pack9 == NULL)
294 		goto cleanup;
295 	    auth_pack9->pkAuthenticator.ctime = ctsec;
296 	    auth_pack9->pkAuthenticator.cusec = cusec;
297 	    auth_pack9->pkAuthenticator.nonce = nonce;
298 	    auth_pack9->pkAuthenticator.kdcName = server;
299 	    auth_pack9->pkAuthenticator.kdcRealm.magic = 0;
300 	    auth_pack9->pkAuthenticator.kdcRealm.data =
301 					(unsigned char *)server->realm.data;
302 	    auth_pack9->pkAuthenticator.kdcRealm.length = server->realm.length;
303 	    free(cksum->contents);
304 	    break;
305 	case KRB5_PADATA_PK_AS_REQ:
306 	    init_krb5_subject_pk_info(&info);
307 	    if (info == NULL)
308 		goto cleanup;
309 	    init_krb5_auth_pack(&auth_pack);
310 	    if (auth_pack == NULL)
311 		goto cleanup;
312 	    auth_pack->pkAuthenticator.ctime = ctsec;
313 	    auth_pack->pkAuthenticator.cusec = cusec;
314 	    auth_pack->pkAuthenticator.nonce = nonce;
315 	    auth_pack->pkAuthenticator.paChecksum = *cksum;
316 	    auth_pack->clientDHNonce.length = 0;
317 	    auth_pack->clientPublicValue = info;
318 
319 	    /* add List of CMS algorithms */
320 	    retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx,
321 			reqctx->cryptoctx, reqctx->idctx,
322 			&auth_pack->supportedCMSTypes);
323 	    if (retval)
324 		goto cleanup;
325 	    break;
326 	default:
327 	    pkiDebug("as_req: unrecognized pa_type = %d\n",
328 		    (int)reqctx->pa_type);
329 	    retval = -1;
330 	    goto cleanup;
331     }
332 
333     switch(protocol) {
334 	case DH_PROTOCOL:
335 	    pkiDebug("as_req: DH key transport algorithm\n");
336 	    retval = pkinit_copy_krb5_octet_data(&info->algorithm.algorithm, &dh_oid);
337 	    if (retval) {
338 		pkiDebug("failed to copy dh_oid\n");
339 		goto cleanup;
340 	    }
341 
342 	    /* create client-side DH keys */
343 	    if ((retval = client_create_dh(context, plgctx->cryptoctx,
344 		    reqctx->cryptoctx, reqctx->idctx, reqctx->opts->dh_size,
345 		    &info->algorithm.parameters.data,
346 		    &info->algorithm.parameters.length,
347 		    &info->subjectPublicKey.data,
348 		    &info->subjectPublicKey.length)) != 0) {
349 		pkiDebug("failed to create dh parameters\n");
350 		goto cleanup;
351 	    }
352 	    break;
353 	case RSA_PROTOCOL:
354 	    pkiDebug("as_req: RSA key transport algorithm\n");
355 	    switch((int)reqctx->pa_type) {
356 		case KRB5_PADATA_PK_AS_REQ_OLD:
357 		    auth_pack9->clientPublicValue = NULL;
358 		    break;
359 		case KRB5_PADATA_PK_AS_REQ:
360 		    free_krb5_subject_pk_info(&info);
361 		    auth_pack->clientPublicValue = NULL;
362 		    break;
363 	    }
364 	    break;
365 	default:
366 	    pkiDebug("as_req: unknown key transport protocol %d\n",
367 		    protocol);
368 	    retval = -1;
369 	    goto cleanup;
370     }
371 
372     /* Encode the authpack */
373     switch((int)reqctx->pa_type) {
374 	case KRB5_PADATA_PK_AS_REQ:
375 	    retval = k5int_encode_krb5_auth_pack(auth_pack, &coded_auth_pack);
376 	    break;
377 	case KRB5_PADATA_PK_AS_REQ_OLD:
378 	    retval = k5int_encode_krb5_auth_pack_draft9(auth_pack9,
379 							&coded_auth_pack);
380 	    break;
381     }
382     if (retval) {
383 	pkiDebug("failed to encode the AuthPack %d\n", retval);
384 	goto cleanup;
385     }
386 #ifdef DEBUG_ASN1
387     print_buffer_bin((unsigned char *)coded_auth_pack->data,
388 		     coded_auth_pack->length,
389 		     "/tmp/client_auth_pack");
390 #endif
391 
392     /* create PKCS7 object from authpack */
393     switch((int)reqctx->pa_type) {
394 	case KRB5_PADATA_PK_AS_REQ:
395 	    init_krb5_pa_pk_as_req(&req);
396 	    if (req == NULL) {
397 		retval = ENOMEM;
398 		goto cleanup;
399 	    }
400 	    retval = cms_signeddata_create(context, plgctx->cryptoctx,
401 		reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_CLIENT, 1,
402 		(unsigned char *)coded_auth_pack->data, coded_auth_pack->length,
403 		&req->signedAuthPack.data, &req->signedAuthPack.length);
404 #ifdef DEBUG_ASN1
405 	    print_buffer_bin((unsigned char *)req->signedAuthPack.data,
406 			     req->signedAuthPack.length,
407 			     "/tmp/client_signed_data");
408 #endif
409 	    break;
410 	case KRB5_PADATA_PK_AS_REQ_OLD:
411 	    init_krb5_pa_pk_as_req_draft9(&req9);
412 	    if (req9 == NULL) {
413 		retval = ENOMEM;
414 		goto cleanup;
415 	    }
416 	    retval = cms_signeddata_create(context, plgctx->cryptoctx,
417 		reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_DRAFT9, 1,
418 		(unsigned char *)coded_auth_pack->data, coded_auth_pack->length,
419 		&req9->signedAuthPack.data, &req9->signedAuthPack.length);
420 	    break;
421 #ifdef DEBUG_ASN1
422 	    print_buffer_bin((unsigned char *)req9->signedAuthPack.data,
423 			     req9->signedAuthPack.length,
424 			     "/tmp/client_signed_data_draft9");
425 #endif
426     }
427     krb5_free_data(context, coded_auth_pack);
428     if (retval) {
429 	pkiDebug("failed to create pkcs7 signed data\n");
430 	goto cleanup;
431     }
432 
433     /* create a list of trusted CAs */
434     switch((int)reqctx->pa_type) {
435 	case KRB5_PADATA_PK_AS_REQ:
436 	    retval = create_krb5_trustedCertifiers(context, plgctx->cryptoctx,
437 		reqctx->cryptoctx, reqctx->idctx, &req->trustedCertifiers);
438 	    if (retval)
439 		goto cleanup;
440 	    retval = create_issuerAndSerial(context, plgctx->cryptoctx,
441 		reqctx->cryptoctx, reqctx->idctx, &req->kdcPkId.data,
442 		&req->kdcPkId.length);
443 	    if (retval)
444 		goto cleanup;
445 
446 	    /* Encode the as-req */
447 	    retval = k5int_encode_krb5_pa_pk_as_req(req, as_req);
448 	    break;
449 	case KRB5_PADATA_PK_AS_REQ_OLD:
450 #if 0
451 	    /* W2K3 KDC doesn't like this */
452 	    retval = create_krb5_trustedCas(context, plgctx->cryptoctx,
453 		reqctx->cryptoctx, reqctx->idctx, 1, &req9->trustedCertifiers);
454 	    if (retval)
455 		goto cleanup;
456 
457 #endif
458 	    retval = create_issuerAndSerial(context, plgctx->cryptoctx,
459 		reqctx->cryptoctx, reqctx->idctx, &req9->kdcCert.data,
460 		&req9->kdcCert.length);
461 	    if (retval)
462 		goto cleanup;
463 	    /* Encode the as-req */
464 	    retval = k5int_encode_krb5_pa_pk_as_req_draft9(req9, as_req);
465 	    break;
466     }
467 #ifdef DEBUG_ASN1
468     if (!retval)
469 	print_buffer_bin((unsigned char *)(*as_req)->data, (*as_req)->length,
470 			 "/tmp/client_as_req");
471 #endif
472 
473 cleanup:
474     switch((int)reqctx->pa_type) {
475 	case KRB5_PADATA_PK_AS_REQ:
476 	    free_krb5_auth_pack(&auth_pack);
477 	    free_krb5_pa_pk_as_req(&req);
478 	    break;
479 	case KRB5_PADATA_PK_AS_REQ_OLD:
480 	    free_krb5_pa_pk_as_req_draft9(&req9);
481 	    free(auth_pack9);
482 	    break;
483     }
484 
485 
486     pkiDebug("pkinit_as_req_create retval=%d\n", (int) retval);
487 
488     return retval;
489 }
490 
491 krb5_error_code
492 pa_pkinit_parse_rep(krb5_context context,
493 		    pkinit_context plgctx,
494 		    pkinit_req_context reqctx,
495 		    krb5_kdc_req * request,
496 		    krb5_pa_data * in_padata,
497 		    krb5_enctype etype,
498 		    krb5_keyblock * as_key,
499 		    krb5_data *encoded_request)
500 {
501     krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
502     krb5_data asRep = { 0, 0, NULL};
503 
504     /*
505      * One way or the other - success or failure - no other PA systems can
506      * work if the server sent us a PKINIT reply, since only we know how to
507      * decrypt the key.
508      */
509     if ((in_padata == NULL) || (in_padata->length == 0)) {
510 	pkiDebug("pa_pkinit_parse_rep: no in_padata\n");
511 	return KRB5KDC_ERR_PREAUTH_FAILED;
512     }
513 
514     asRep.data = (char *) in_padata->contents;
515     asRep.length = in_padata->length;
516 
517     retval =
518 	pkinit_as_rep_parse(context, plgctx, reqctx, in_padata->pa_type,
519 			    request, &asRep, as_key, etype, encoded_request);
520     if (retval) {
521 	pkiDebug("pkinit_as_rep_parse returned %d (%s)\n",
522 		 retval, error_message(retval));
523 	goto cleanup;
524     }
525 
526     retval = 0;
527 
528 cleanup:
529 
530     return retval;
531 }
532 
533 static krb5_error_code
534 verify_kdc_san(krb5_context context,
535 	       pkinit_context plgctx,
536 	       pkinit_req_context reqctx,
537 	       krb5_principal kdcprinc,
538 	       int *valid_san,
539 	       int *need_eku_checking)
540 {
541     krb5_error_code retval;
542     char **certhosts = NULL, **cfghosts = NULL;
543     krb5_principal *princs = NULL;
544     unsigned char ***get_dns;
545     int i, j;
546 
547     *valid_san = 0;
548     *need_eku_checking = 1;
549 
550     retval = pkinit_libdefault_strings(context,
551 				       krb5_princ_realm(context, kdcprinc),
552 				       "pkinit_kdc_hostname",
553 				       &cfghosts);
554     if (retval || cfghosts == NULL) {
555 	pkiDebug("%s: No pkinit_kdc_hostname values found in config file\n",
556 		 __FUNCTION__);
557 	get_dns = NULL;
558     } else {
559 	pkiDebug("%s: pkinit_kdc_hostname values found in config file\n",
560 		 __FUNCTION__);
561 	get_dns = (unsigned char ***)&certhosts;
562     }
563 
564     retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,
565 				       reqctx->cryptoctx, reqctx->idctx,
566 				       &princs, NULL, get_dns);
567     if (retval) {
568 	pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
569 	retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
570 	goto out;
571     }
572 #if 0
573     retval = call_san_checking_plugins(context, plgctx, reqctx, idctx,
574 				       princs, hosts, &plugin_decision,
575 				       need_eku_checking);
576     pkiDebug("%s: call_san_checking_plugins() returned retval %d\n",
577 	     __FUNCTION__);
578     if (retval) {
579 	retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
580 	goto out;
581     }
582     pkiDebug("%s: call_san_checking_plugins() returned decision %d and "
583 	     "need_eku_checking %d\n",
584 	     __FUNCTION__, plugin_decision, *need_eku_checking);
585     if (plugin_decision != NO_DECISION) {
586 	retval = plugin_decision;
587 	goto out;
588     }
589 #endif
590 
591     pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);
592     for (i = 0; princs != NULL && princs[i] != NULL; i++) {
593 	if (krb5_principal_compare(context, princs[i], kdcprinc)) {
594 	    pkiDebug("%s: pkinit san match found\n", __FUNCTION__);
595 	    *valid_san = 1;
596 	    *need_eku_checking = 0;
597 	    retval = 0;
598 	    goto out;
599 	}
600     }
601     pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);
602 
603     if (certhosts == NULL) {
604 	pkiDebug("%s: no certhosts (or we wouldn't accept them anyway)\n",
605 		 __FUNCTION__);
606 	retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
607 	goto out;
608     }
609 
610     for (i = 0; certhosts[i] != NULL; i++) {
611 	for (j = 0; cfghosts != NULL && cfghosts[j] != NULL; j++) {
612 	    pkiDebug("%s: comparing cert name '%s' with config name '%s'\n",
613 		     __FUNCTION__, certhosts[i], cfghosts[j]);
614 	    if (strcmp(certhosts[i], cfghosts[j]) == 0) {
615 		pkiDebug("%s: we have a dnsName match\n", __FUNCTION__);
616 		*valid_san = 1;
617 		retval = 0;
618 		goto out;
619 	    }
620 	}
621     }
622     pkiDebug("%s: no dnsName san match found\n", __FUNCTION__);
623 
624     /* We found no match */
625     retval = 0;
626 
627 out:
628     if (princs != NULL) {
629 	for (i = 0; princs[i] != NULL; i++)
630 	    krb5_free_principal(context, princs[i]);
631 	free(princs);
632     }
633     if (certhosts != NULL) {
634 	for (i = 0; certhosts[i] != NULL; i++)
635 	    free(certhosts[i]);
636 	free(certhosts);
637     }
638     if (cfghosts != NULL)
639 	profile_free_list(cfghosts);
640 
641     pkiDebug("%s: returning retval %d, valid_san %d, need_eku_checking %d\n",
642 	     __FUNCTION__, retval, *valid_san, *need_eku_checking);
643     return retval;
644 }
645 
646 static krb5_error_code
647 verify_kdc_eku(krb5_context context,
648 	       pkinit_context plgctx,
649 	       pkinit_req_context reqctx,
650 	       int *eku_accepted)
651 {
652     krb5_error_code retval;
653 
654     *eku_accepted = 0;
655 
656     if (reqctx->opts->require_eku == 0) {
657 	pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
658 	*eku_accepted = 1;
659 	retval = 0;
660 	goto out;
661     }
662     retval = crypto_check_cert_eku(context, plgctx->cryptoctx,
663 				   reqctx->cryptoctx, reqctx->idctx,
664 				   1, /* kdc cert */
665 				   reqctx->opts->accept_secondary_eku,
666 				   eku_accepted);
667     if (retval) {
668 	pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
669 		 __FUNCTION__, retval, error_message(retval));
670 	goto out;
671     }
672 
673 out:
674     pkiDebug("%s: returning retval %d, eku_accepted %d\n",
675 	     __FUNCTION__, retval, *eku_accepted);
676     return retval;
677 }
678 
679 /*
680  * Parse PA-PK-AS-REP message. Optionally evaluates the message's
681  * certificate chain.
682  * Optionally returns various components.
683  */
684 krb5_error_code
685 pkinit_as_rep_parse(krb5_context context,
686 		    pkinit_context plgctx,
687   		    pkinit_req_context reqctx,
688 		    krb5_preauthtype pa_type,
689 		    krb5_kdc_req *request,
690 		    const krb5_data *as_rep,
691 		    krb5_keyblock *key_block,
692 		    krb5_enctype etype,
693 		    krb5_data *encoded_request)
694 {
695     krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
696     krb5_pa_pk_as_rep *kdc_reply = NULL;
697     krb5_kdc_dh_key_info *kdc_dh = NULL;
698     krb5_reply_key_pack *key_pack = NULL;
699     krb5_reply_key_pack_draft9 *key_pack9 = NULL;
700     krb5_octet_data dh_data = { 0, 0, NULL };
701     unsigned char *client_key = NULL, *kdc_hostname = NULL;
702     unsigned int client_key_len = 0;
703     krb5_checksum cksum = {0, 0, 0, NULL};
704     krb5_data k5data;
705     int valid_san = 0;
706     int valid_eku = 0;
707     int need_eku_checking = 1;
708 
709     assert((as_rep != NULL) && (key_block != NULL));
710 
711 #ifdef DEBUG_ASN1
712     print_buffer_bin((unsigned char *)as_rep->data, as_rep->length,
713 		     "/tmp/client_as_rep");
714 #endif
715 
716     if ((retval = k5int_decode_krb5_pa_pk_as_rep(as_rep, &kdc_reply))) {
717 	pkiDebug("decode_krb5_as_rep failed %d\n", retval);
718 	return retval;
719     }
720 
721     switch(kdc_reply->choice) {
722 	case choice_pa_pk_as_rep_dhInfo:
723 	    pkiDebug("as_rep: DH key transport algorithm\n");
724 #ifdef DEBUG_ASN1
725     print_buffer_bin(kdc_reply->u.dh_Info.dhSignedData.data,
726 	kdc_reply->u.dh_Info.dhSignedData.length, "/tmp/client_kdc_signeddata");
727 #endif
728 	    if ((retval = cms_signeddata_verify(context, plgctx->cryptoctx,
729 		    reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_SERVER,
730 		    reqctx->opts->require_crl_checking,
731 		    kdc_reply->u.dh_Info.dhSignedData.data,
732 		    kdc_reply->u.dh_Info.dhSignedData.length,
733 		    &dh_data.data, &dh_data.length, NULL, NULL)) != 0) {
734 		pkiDebug("failed to verify pkcs7 signed data\n");
735 		goto cleanup;
736 	    }
737 
738 	    break;
739 	case choice_pa_pk_as_rep_encKeyPack:
740 	    pkiDebug("as_rep: RSA key transport algorithm\n");
741 	    if ((retval = cms_envelopeddata_verify(context, plgctx->cryptoctx,
742 		    reqctx->cryptoctx, reqctx->idctx, pa_type,
743 		    reqctx->opts->require_crl_checking,
744 		    kdc_reply->u.encKeyPack.data,
745 		    kdc_reply->u.encKeyPack.length,
746 		    &dh_data.data, &dh_data.length)) != 0) {
747 		pkiDebug("failed to verify pkcs7 enveloped data\n");
748 		goto cleanup;
749 	    }
750 	    break;
751 	default:
752 	    pkiDebug("unknown as_rep type %d\n", kdc_reply->choice);
753 	    retval = -1;
754 	    goto cleanup;
755     }
756 
757     retval = verify_kdc_san(context, plgctx, reqctx, request->server,
758 			    &valid_san, &need_eku_checking);
759     if (retval)
760 	    goto cleanup;
761     if (!valid_san) {
762 	pkiDebug("%s: did not find an acceptable SAN in KDC certificate\n",
763 		 __FUNCTION__);
764 	retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
765 	goto cleanup;
766     }
767 
768     if (need_eku_checking) {
769 	retval = verify_kdc_eku(context, plgctx, reqctx,
770 				&valid_eku);
771 	if (retval)
772 	    goto cleanup;
773 	if (!valid_eku) {
774 	    pkiDebug("%s: did not find an acceptable EKU in KDC certificate\n",
775 		     __FUNCTION__);
776 	    retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
777 	    goto cleanup;
778 	}
779     } else
780 	pkiDebug("%s: skipping EKU check\n", __FUNCTION__);
781 
782     OCTETDATA_TO_KRB5DATA(&dh_data, &k5data);
783 
784     switch(kdc_reply->choice) {
785 	case choice_pa_pk_as_rep_dhInfo:
786 #ifdef DEBUG_ASN1
787 	    print_buffer_bin(dh_data.data, dh_data.length,
788 			     "/tmp/client_dh_key");
789 #endif
790 	    if ((retval = k5int_decode_krb5_kdc_dh_key_info(&k5data,
791 		    &kdc_dh)) != 0) {
792 		pkiDebug("failed to decode kdc_dh_key_info\n");
793 		goto cleanup;
794 	    }
795 
796 	    /* client after KDC reply */
797 	    if ((retval = client_process_dh(context, plgctx->cryptoctx,
798 		    reqctx->cryptoctx, reqctx->idctx,
799 		    kdc_dh->subjectPublicKey.data,
800 		    kdc_dh->subjectPublicKey.length,
801 		    &client_key, &client_key_len)) != 0) {
802 		pkiDebug("failed to process dh params\n");
803 		goto cleanup;
804 	    }
805 
806 	    retval = pkinit_octetstring2key(context, etype, client_key,
807 					  client_key_len, key_block);
808 	    if (retval) {
809 		pkiDebug("failed to create key pkinit_octetstring2key %s\n",
810 			 error_message(retval));
811 		goto cleanup;
812 	    }
813 
814 	    break;
815 	case choice_pa_pk_as_rep_encKeyPack:
816 #ifdef DEBUG_ASN1
817 	    print_buffer_bin(dh_data.data, dh_data.length,
818 			     "/tmp/client_key_pack");
819 #endif
820 	    if ((retval = k5int_decode_krb5_reply_key_pack(&k5data,
821 		    &key_pack)) != 0) {
822 		pkiDebug("failed to decode reply_key_pack\n");
823 #ifdef LONGHORN_BETA_COMPAT
824     /*
825      * LH Beta 3 requires the extra pa-data, even for RFC requests,
826      * in order to get the Checksum rather than a Nonce in the reply.
827      * This can be removed when LH SP1 is released.
828      */
829 		if (pa_type == KRB5_PADATA_PK_AS_REP && longhorn == 0)
830 #else
831 		if (pa_type == KRB5_PADATA_PK_AS_REP)
832 #endif
833 		    goto cleanup;
834 		else {
835 		    if ((retval =
836 			k5int_decode_krb5_reply_key_pack_draft9(&k5data,
837 							  &key_pack9)) != 0) {
838 			pkiDebug("failed to decode reply_key_pack_draft9\n");
839 			goto cleanup;
840 		    }
841 		    pkiDebug("decode reply_key_pack_draft9\n");
842 		    if (key_pack9->nonce != request->nonce) {
843 			pkiDebug("nonce in AS_REP=%d doesn't match AS_REQ=%d\n",				 key_pack9->nonce, request->nonce);
844 			retval = -1;
845 			goto cleanup;
846 		    }
847 		    krb5_copy_keyblock_contents(context, &key_pack9->replyKey,
848 						key_block);
849 		    break;
850 		}
851 	    }
852 	    /*
853 	     * This is hack but Windows sends back SHA1 checksum
854 	     * with checksum type of 14. There is currently no
855 	     * checksum type of 14 defined.
856 	     */
857 	    if (key_pack->asChecksum.checksum_type == 14)
858 		key_pack->asChecksum.checksum_type = CKSUMTYPE_NIST_SHA;
859 	    retval = krb5_c_make_checksum(context,
860 					  key_pack->asChecksum.checksum_type,
861 					  &key_pack->replyKey,
862 					  KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
863 					  encoded_request, &cksum);
864 	    if (retval) {
865 		pkiDebug("failed to make a checksum\n");
866 		goto cleanup;
867 	    }
868 
869 	    if ((cksum.length != key_pack->asChecksum.length) ||
870 		memcmp(cksum.contents, key_pack->asChecksum.contents,
871 			cksum.length)) {
872 		pkiDebug("failed to match the checksums\n");
873 #ifdef DEBUG_CKSUM
874 	    pkiDebug("calculating checksum on buf size (%d)\n",
875 		     encoded_request->length);
876 	    print_buffer(encoded_request->data, encoded_request->length);
877 	    pkiDebug("encrypting key (%d)\n", key_pack->replyKey.length);
878 	    print_buffer(key_pack->replyKey.contents,
879 			 key_pack->replyKey.length);
880 	    pkiDebug("received checksum type=%d size=%d ",
881 		     key_pack->asChecksum.checksum_type,
882 		     key_pack->asChecksum.length);
883 	    print_buffer(key_pack->asChecksum.contents,
884 			 key_pack->asChecksum.length);
885 	    pkiDebug("expected checksum type=%d size=%d ",
886 		     cksum.checksum_type, cksum.length);
887 	    print_buffer(cksum.contents, cksum.length);
888 #endif
889 		goto cleanup;
890 	    } else
891 		pkiDebug("checksums match\n");
892 
893 	    krb5_copy_keyblock_contents(context, &key_pack->replyKey,
894 					key_block);
895 
896 	    break;
897 	default:
898 	    pkiDebug("unknow as_rep type %d\n", kdc_reply->choice);
899 	    goto cleanup;
900     }
901 
902     retval = 0;
903 
904 cleanup:
905     if (dh_data.data != NULL)
906 	free(dh_data.data);
907     if (client_key != NULL)
908 	free(client_key);
909     free_krb5_kdc_dh_key_info(&kdc_dh);
910     free_krb5_pa_pk_as_rep(&kdc_reply);
911 
912     if (key_pack != NULL) {
913 	free_krb5_reply_key_pack(&key_pack);
914 	if (cksum.contents != NULL)
915 	    free(cksum.contents);
916     }
917     if (key_pack9 != NULL)
918 	free_krb5_reply_key_pack_draft9(&key_pack9);
919 
920     if (kdc_hostname != NULL)
921 	free(kdc_hostname);
922 
923     pkiDebug("pkinit_as_rep_parse returning %d (%s)\n",
924 	     retval, error_message(retval));
925     return retval;
926 }
927 
928 static void
929 pkinit_client_profile(krb5_context context,
930 		      pkinit_context plgctx,
931 		      pkinit_req_context reqctx,
932 		      krb5_kdc_req *request)
933 {
934     char *eku_string = NULL;
935 
936     pkiDebug("pkinit_client_profile %p %p %p %p\n",
937 	     context, plgctx, reqctx, request);
938 
939     (void) pkinit_libdefault_boolean(context, &request->server->realm,
940 			      "pkinit_win2k",
941 			      reqctx->opts->win2k_target,
942 			      &reqctx->opts->win2k_target);
943     (void) pkinit_libdefault_boolean(context, &request->server->realm,
944 			      "pkinit_win2k_require_binding",
945 			      reqctx->opts->win2k_require_cksum,
946 			      &reqctx->opts->win2k_require_cksum);
947     (void) pkinit_libdefault_boolean(context, &request->server->realm,
948 			      "pkinit_require_crl_checking",
949 			      reqctx->opts->require_crl_checking,
950 			      &reqctx->opts->require_crl_checking);
951     (void) pkinit_libdefault_integer(context, &request->server->realm,
952 			      "pkinit_dh_min_bits",
953 			      reqctx->opts->dh_size,
954 			      &reqctx->opts->dh_size);
955     if (reqctx->opts->dh_size != 1024 && reqctx->opts->dh_size != 2048
956         && reqctx->opts->dh_size != 4096) {
957 	pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, "
958 		 "using default value (%d) instead\n", __FUNCTION__,
959 		 reqctx->opts->dh_size, PKINIT_DEFAULT_DH_MIN_BITS);
960 	reqctx->opts->dh_size = PKINIT_DEFAULT_DH_MIN_BITS;
961     }
962     (void) pkinit_libdefault_string(context, &request->server->realm,
963 			     "pkinit_eku_checking",
964 			     &eku_string);
965     if (eku_string != NULL) {
966 	if (strcasecmp(eku_string, "kpKDC") == 0) {
967 	    reqctx->opts->require_eku = 1;
968 	    reqctx->opts->accept_secondary_eku = 0;
969 	} else if (strcasecmp(eku_string, "kpServerAuth") == 0) {
970 	    reqctx->opts->require_eku = 1;
971 	    reqctx->opts->accept_secondary_eku = 1;
972 	} else if (strcasecmp(eku_string, "none") == 0) {
973 	    reqctx->opts->require_eku = 0;
974 	    reqctx->opts->accept_secondary_eku = 0;
975 	} else {
976 	    pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
977 		     __FUNCTION__, eku_string);
978 	}
979 	free(eku_string);
980     }
981 #ifdef LONGHORN_BETA_COMPAT
982     /* Temporarily just set global flag from config file */
983     (void) pkinit_libdefault_boolean(context, &request->server->realm,
984 			      "pkinit_longhorn",
985 			      0,
986 			      &longhorn);
987 #endif
988 
989     /* Only process anchors here if they were not specified on command line */
990     if (reqctx->idopts->anchors == NULL)
991 	(void) pkinit_libdefault_strings(context, &request->server->realm,
992 				  "pkinit_anchors",
993 				  &reqctx->idopts->anchors);
994     /* Solaris Kerberos */
995     (void) pkinit_libdefault_strings(context, &request->server->realm,
996 			      "pkinit_pool",
997 			      &reqctx->idopts->intermediates);
998     (void) pkinit_libdefault_strings(context, &request->server->realm,
999 			      "pkinit_revoke",
1000 			      &reqctx->idopts->crls);
1001     (void) pkinit_libdefault_strings(context, &request->server->realm,
1002 			      "pkinit_identities",
1003 			      &reqctx->idopts->identity_alt);
1004 }
1005 
1006 /* ARGSUSED */
1007 krb5_error_code
1008 pkinit_client_process(krb5_context context,
1009 		      void *plugin_context,
1010 		      void *request_context,
1011 		      krb5_get_init_creds_opt *gic_opt,
1012 		      preauth_get_client_data_proc get_data_proc,
1013 		      struct _krb5_preauth_client_rock *rock,
1014 		      krb5_kdc_req *request,
1015 		      krb5_data *encoded_request_body,
1016 		      krb5_data *encoded_previous_request,
1017 		      krb5_pa_data *in_padata,
1018 		      krb5_prompter_fct prompter,
1019 		      void *prompter_data,
1020 		      preauth_get_as_key_proc gak_fct,
1021 		      void *gak_data,
1022 		      krb5_data *salt,
1023 		      krb5_data *s2kparams,
1024 		      krb5_keyblock *as_key,
1025 		      krb5_pa_data ***out_padata)
1026 {
1027     krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
1028     krb5_enctype enctype = -1;
1029     krb5_data *cdata = NULL;
1030     int processing_request = 0;
1031     pkinit_context plgctx = (pkinit_context)plugin_context;
1032     pkinit_req_context reqctx = (pkinit_req_context)request_context;
1033 
1034     pkiDebug("pkinit_client_process %p %p %p %p\n",
1035 	     context, plgctx, reqctx, request);
1036 
1037     if (plgctx == NULL || reqctx == NULL)
1038 	return EINVAL;
1039 
1040     switch ((int) in_padata->pa_type) {
1041 	case KRB5_PADATA_PK_AS_REQ:
1042 	    pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
1043 	    processing_request = 1;
1044 	    break;
1045 
1046 	case KRB5_PADATA_PK_AS_REP:
1047 	    pkiDebug("processing KRB5_PADATA_PK_AS_REP\n");
1048 	    break;
1049 	case KRB5_PADATA_PK_AS_REP_OLD:
1050 	case KRB5_PADATA_PK_AS_REQ_OLD:
1051 	    if (in_padata->length == 0) {
1052 		pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
1053 		in_padata->pa_type = KRB5_PADATA_PK_AS_REQ_OLD;
1054 		processing_request = 1;
1055 	    } else {
1056 		pkiDebug("processing KRB5_PADATA_PK_AS_REP_OLD\n");
1057 		in_padata->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
1058 	    }
1059 	    break;
1060 	default:
1061 	    pkiDebug("unrecognized patype = %d for PKINIT\n",
1062 		    in_padata->pa_type);
1063 	    return EINVAL;
1064     }
1065 
1066     if (processing_request) {
1067 	pkinit_client_profile(context, plgctx, reqctx, request);
1068 	/* Solaris Kerberos */
1069 	retval = pkinit_identity_set_prompter(reqctx->idctx, prompter, prompter_data);
1070 	if (retval) {
1071 	    pkiDebug("pkinit_identity_set_prompter returned %d (%s)\n",
1072 		     retval, error_message(retval));
1073 	    return retval;
1074 	}
1075 
1076 	retval = pkinit_identity_initialize(context, plgctx->cryptoctx,
1077 					    reqctx->cryptoctx, reqctx->idopts,
1078 					    reqctx->idctx, 1, request->client);
1079 	if (retval) {
1080 	    pkiDebug("pkinit_identity_initialize returned %d (%s)\n",
1081 		     retval, error_message(retval));
1082 	    return retval;
1083 	}
1084 	retval = pa_pkinit_gen_req(context, plgctx, reqctx, request,
1085 				   in_padata, out_padata, prompter,
1086 				   prompter_data, gic_opt);
1087     } else {
1088 	/*
1089 	 * Get the enctype of the reply.
1090 	 */
1091 	retval = (*get_data_proc)(context, rock,
1092 				krb5plugin_preauth_client_get_etype, &cdata);
1093 	if (retval) {
1094 	    pkiDebug("get_data_proc returned %d (%s)\n",
1095 		     retval, error_message(retval));
1096 	    return retval;
1097 	}
1098 	enctype = *((krb5_enctype *)cdata->data);
1099 	(*get_data_proc)(context, rock,
1100 			 krb5plugin_preauth_client_free_etype, &cdata);
1101 	retval = pa_pkinit_parse_rep(context, plgctx, reqctx, request,
1102 				     in_padata, enctype, as_key,
1103 				     encoded_previous_request);
1104     }
1105 
1106     pkiDebug("pkinit_client_process: returning %d (%s)\n",
1107 	     retval, error_message(retval));
1108     return retval;
1109 }
1110 
1111 /* ARGSUSED */
1112 krb5_error_code
1113 pkinit_client_tryagain(krb5_context context,
1114 		       void *plugin_context,
1115 		       void *request_context,
1116 		       krb5_get_init_creds_opt *gic_opt,
1117 		       preauth_get_client_data_proc get_data_proc,
1118 		       struct _krb5_preauth_client_rock *rock,
1119 		       krb5_kdc_req *request,
1120 		       krb5_data *encoded_request_body,
1121 		       krb5_data *encoded_previous_request,
1122 		       krb5_pa_data *in_padata,
1123 		       krb5_error *err_reply,
1124 		       krb5_prompter_fct prompter,
1125 		       void *prompter_data,
1126 		       preauth_get_as_key_proc gak_fct,
1127 		       void *gak_data,
1128 		       krb5_data *salt,
1129 		       krb5_data *s2kparams,
1130 		       krb5_keyblock *as_key,
1131 		       krb5_pa_data ***out_padata)
1132 {
1133     krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
1134     pkinit_context plgctx = (pkinit_context)plugin_context;
1135     pkinit_req_context reqctx = (pkinit_req_context)request_context;
1136     krb5_typed_data **typed_data = NULL;
1137     krb5_data scratch;
1138     krb5_external_principal_identifier **krb5_trusted_certifiers = NULL;
1139     krb5_algorithm_identifier **algId = NULL;
1140     int do_again = 0;
1141 
1142     pkiDebug("pkinit_client_tryagain %p %p %p %p\n",
1143 	     context, plgctx, reqctx, request);
1144 
1145     if (reqctx->pa_type != in_padata->pa_type)
1146 	return retval;
1147 
1148 #ifdef DEBUG_ASN1
1149     print_buffer_bin((unsigned char *)err_reply->e_data.data,
1150 		     err_reply->e_data.length, "/tmp/client_edata");
1151 #endif
1152     retval = k5int_decode_krb5_typed_data(&err_reply->e_data, &typed_data);
1153     if (retval) {
1154 	pkiDebug("decode_krb5_typed_data failed\n");
1155 	goto cleanup;
1156     }
1157 #ifdef DEBUG_ASN1
1158     print_buffer_bin(typed_data[0]->data, typed_data[0]->length,
1159 		     "/tmp/client_typed_data");
1160 #endif
1161     OCTETDATA_TO_KRB5DATA(typed_data[0], &scratch);
1162 
1163     switch(typed_data[0]->type) {
1164 	case TD_TRUSTED_CERTIFIERS:
1165 	case TD_INVALID_CERTIFICATES:
1166 	    retval = k5int_decode_krb5_td_trusted_certifiers(&scratch,
1167 		&krb5_trusted_certifiers);
1168 	    if (retval) {
1169 		pkiDebug("failed to decode sequence of trusted certifiers\n");
1170 		goto cleanup;
1171 	    }
1172 	    retval = pkinit_process_td_trusted_certifiers(context,
1173 		    plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx,
1174 		    krb5_trusted_certifiers, typed_data[0]->type);
1175 	    if (!retval)
1176 		do_again = 1;
1177 	    break;
1178 	case TD_DH_PARAMETERS:
1179 	    retval = k5int_decode_krb5_td_dh_parameters(&scratch, &algId);
1180 	    if (retval) {
1181 		pkiDebug("failed to decode td_dh_parameters\n");
1182 		goto cleanup;
1183 	    }
1184 	    retval = pkinit_process_td_dh_params(context, plgctx->cryptoctx,
1185 		reqctx->cryptoctx, reqctx->idctx, algId,
1186 		&reqctx->opts->dh_size);
1187 	    if (!retval)
1188 		do_again = 1;
1189 	    break;
1190 	default:
1191 	    break;
1192     }
1193 
1194     if (do_again) {
1195 	retval = pa_pkinit_gen_req(context, plgctx, reqctx, request, in_padata,
1196 				   out_padata, prompter, prompter_data, gic_opt);
1197 	if (retval)
1198 	    goto cleanup;
1199     }
1200 
1201     retval = 0;
1202 cleanup:
1203     if (krb5_trusted_certifiers != NULL)
1204 	free_krb5_external_principal_identifier(&krb5_trusted_certifiers);
1205 
1206     if (typed_data != NULL)
1207 	free_krb5_typed_data(&typed_data);
1208 
1209     if (algId != NULL)
1210 	free_krb5_algorithm_identifiers(&algId);
1211 
1212     pkiDebug("pkinit_client_tryagain: returning %d (%s)\n",
1213 	     retval, error_message(retval));
1214     return retval;
1215 }
1216 
1217 /* ARGSUSED */
1218 static int
1219 pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype)
1220 {
1221     return PA_REAL;
1222 }
1223 
1224 static krb5_preauthtype supported_client_pa_types[] = {
1225     KRB5_PADATA_PK_AS_REP,
1226     KRB5_PADATA_PK_AS_REQ,
1227     KRB5_PADATA_PK_AS_REP_OLD,
1228     KRB5_PADATA_PK_AS_REQ_OLD,
1229     0
1230 };
1231 
1232 /* ARGSUSED */
1233 void
1234 pkinit_client_req_init(krb5_context context,
1235 		       void *plugin_context,
1236 		       void **request_context)
1237 {
1238     krb5_error_code retval = ENOMEM;
1239     struct _pkinit_req_context *reqctx = NULL;
1240     struct _pkinit_context *plgctx = (struct _pkinit_context *)plugin_context;
1241 
1242     *request_context = NULL;
1243 
1244     reqctx = (struct _pkinit_req_context *) malloc(sizeof(*reqctx));
1245     if (reqctx == NULL)
1246 	return;
1247     (void) memset(reqctx, 0, sizeof(*reqctx));
1248 
1249     reqctx->magic = PKINIT_REQ_CTX_MAGIC;
1250     reqctx->cryptoctx = NULL;
1251     reqctx->opts = NULL;
1252     reqctx->idctx = NULL;
1253     reqctx->idopts = NULL;
1254 
1255     retval = pkinit_init_req_opts(&reqctx->opts);
1256     if (retval)
1257 	goto cleanup;
1258 
1259     reqctx->opts->require_eku = plgctx->opts->require_eku;
1260     reqctx->opts->accept_secondary_eku = plgctx->opts->accept_secondary_eku;
1261     reqctx->opts->dh_or_rsa = plgctx->opts->dh_or_rsa;
1262     reqctx->opts->allow_upn = plgctx->opts->allow_upn;
1263     reqctx->opts->require_crl_checking = plgctx->opts->require_crl_checking;
1264 
1265     retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
1266     if (retval)
1267 	goto cleanup;
1268 
1269     retval = pkinit_init_identity_crypto(&reqctx->idctx);
1270     if (retval)
1271 	goto cleanup;
1272 
1273     retval = pkinit_dup_identity_opts(plgctx->idopts, &reqctx->idopts);
1274     if (retval)
1275 	goto cleanup;
1276 
1277     *request_context = (void *) reqctx;
1278     pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
1279 
1280 cleanup:
1281     if (retval) {
1282 	if (reqctx->idctx != NULL)
1283 	    pkinit_fini_identity_crypto(reqctx->idctx);
1284 	if (reqctx->cryptoctx != NULL)
1285 	    pkinit_fini_req_crypto(reqctx->cryptoctx);
1286 	if (reqctx->opts != NULL)
1287 	    pkinit_fini_req_opts(reqctx->opts);
1288 	if (reqctx->idopts != NULL)
1289 	    pkinit_fini_identity_opts(reqctx->idopts);
1290 	free(reqctx);
1291     }
1292 
1293     return;
1294 }
1295 
1296 /* ARGSUSED */
1297 void
1298 pkinit_client_req_fini(krb5_context context,
1299 		      void *plugin_context,
1300 		      void *request_context)
1301 {
1302     struct _pkinit_req_context *reqctx =
1303 	(struct _pkinit_req_context *)request_context;
1304 
1305     pkiDebug("%s: received reqctx at %p\n", __FUNCTION__, reqctx);
1306     if (reqctx == NULL)
1307 	return;
1308     if (reqctx->magic != PKINIT_REQ_CTX_MAGIC) {
1309 	pkiDebug("%s: Bad magic value (%x) in req ctx\n",
1310 		 __FUNCTION__, reqctx->magic);
1311 	return;
1312     }
1313     if (reqctx->opts != NULL)
1314 	pkinit_fini_req_opts(reqctx->opts);
1315 
1316     if (reqctx->cryptoctx != NULL)
1317 	pkinit_fini_req_crypto(reqctx->cryptoctx);
1318 
1319     if (reqctx->idctx != NULL)
1320 	pkinit_fini_identity_crypto(reqctx->idctx);
1321 
1322     if (reqctx->idopts != NULL)
1323 	pkinit_fini_identity_opts(reqctx->idopts);
1324 
1325     free(reqctx);
1326     return;
1327 }
1328 
1329 /* ARGSUSED */
1330 static void
1331 pkinit_fini_client_profile(krb5_context context, pkinit_context plgctx)
1332 {
1333     /* This should clean up anything allocated in pkinit_init_client_profile */
1334 }
1335 
1336 /* ARGSUSED */
1337 static krb5_error_code
1338 pkinit_init_client_profile(krb5_context context, pkinit_context plgctx)
1339 {
1340     return 0;
1341 }
1342 
1343 static int
1344 pkinit_client_plugin_init(krb5_context context, void **blob)
1345 {
1346     krb5_error_code retval = ENOMEM;
1347     struct _pkinit_context *ctx = NULL;
1348 
1349     ctx = (struct _pkinit_context *)calloc(1, sizeof(*ctx));
1350     if (ctx == NULL)
1351 	return ENOMEM;
1352     (void) memset(ctx, 0, sizeof(*ctx));
1353     ctx->magic = PKINIT_CTX_MAGIC;
1354     ctx->opts = NULL;
1355     ctx->cryptoctx = NULL;
1356     ctx->idopts = NULL;
1357 
1358     retval = pkinit_accessor_init();
1359     if (retval)
1360 	goto errout;
1361 
1362     retval = pkinit_init_plg_opts(&ctx->opts);
1363     if (retval)
1364 	goto errout;
1365 
1366     retval = pkinit_init_plg_crypto(&ctx->cryptoctx);
1367     if (retval)
1368 	goto errout;
1369 
1370     retval = pkinit_init_identity_opts(&ctx->idopts);
1371     if (retval)
1372 	goto errout;
1373 
1374     retval = pkinit_init_client_profile(context, ctx);
1375     if (retval)
1376 	goto errout;
1377 
1378     *blob = ctx;
1379 
1380     pkiDebug("%s: returning plgctx at %p\n", __FUNCTION__, ctx);
1381 
1382 errout:
1383     if (retval)
1384 	pkinit_client_plugin_fini(context, ctx);
1385 
1386     return retval;
1387 }
1388 
1389 static void
1390 pkinit_client_plugin_fini(krb5_context context, void *blob)
1391 {
1392     struct _pkinit_context *ctx = (struct _pkinit_context *)blob;
1393 
1394     if (ctx == NULL || ctx->magic != PKINIT_CTX_MAGIC) {
1395 	pkiDebug("pkinit_lib_fini: got bad plgctx (%p)!\n", ctx);
1396 	return;
1397     }
1398     pkiDebug("%s: got plgctx at %p\n", __FUNCTION__, ctx);
1399 
1400     pkinit_fini_client_profile(context, ctx);
1401     pkinit_fini_identity_opts(ctx->idopts);
1402     pkinit_fini_plg_crypto(ctx->cryptoctx);
1403     pkinit_fini_plg_opts(ctx->opts);
1404     free(ctx);
1405 
1406 }
1407 
1408 /* ARGSUSED */
1409 static krb5_error_code
1410 add_string_to_array(krb5_context context, char ***array, const char *addition)
1411 {
1412     char **out = NULL;
1413 
1414     if (*array == NULL) {
1415 	out = malloc(2 * sizeof(char *));
1416 	if (out == NULL)
1417 	    return ENOMEM;
1418 	out[1] = NULL;
1419 	out[0] = strdup(addition);
1420 	if (out[0] == NULL) {
1421 	    free(out);
1422 	    return ENOMEM;
1423 	}
1424     } else {
1425 	int i;
1426 	char **a = *array;
1427 	for (i = 0; a[i] != NULL; i++);
1428 	out = malloc( (i + 2) * sizeof(char *));
1429 	if (out == NULL)
1430 	    return ENOMEM;
1431 	for (i = 0; a[i] != NULL; i++) {
1432 	    out[i] = a[i];
1433 	}
1434 	out[i++] = strdup(addition);
1435 	if (out == NULL) {
1436 	    free(out);
1437 	    return ENOMEM;
1438 	}
1439 	out[i] = NULL;
1440 	free(*array);
1441     }
1442     *array = out;
1443 
1444     return 0;
1445 }
1446 static krb5_error_code
1447 handle_gic_opt(krb5_context context,
1448 	       struct _pkinit_context *plgctx,
1449 	       const char *attr,
1450 	       const char *value)
1451 {
1452     krb5_error_code retval;
1453 
1454     if (strcmp(attr, "X509_user_identity") == 0) {
1455 	if (plgctx->idopts->identity != NULL) {
1456 	    krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
1457 		"X509_user_identity can not be given twice\n");
1458 	    return KRB5_PREAUTH_FAILED;
1459 	}
1460 	plgctx->idopts->identity = strdup(value);
1461 	if (plgctx->idopts->identity == NULL) {
1462 	    krb5_set_error_message(context, ENOMEM,
1463 		"Could not duplicate X509_user_identity value\n");
1464 	    return ENOMEM;
1465 	}
1466     } else if (strcmp(attr, "X509_anchors") == 0) {
1467 	retval = add_string_to_array(context, &plgctx->idopts->anchors, value);
1468 	if (retval)
1469 	    return retval;
1470     } else if (strcmp(attr, "flag_RSA_PROTOCOL") == 0) {
1471 	if (strcmp(value, "yes") == 0) {
1472 	    pkiDebug("Setting flag to use RSA_PROTOCOL\n");
1473 	    plgctx->opts->dh_or_rsa = RSA_PROTOCOL;
1474 	}
1475     }
1476     return 0;
1477 }
1478 
1479 /* ARGSUSED */
1480 static krb5_error_code
1481 pkinit_client_gic_opt(krb5_context context,
1482 		      void *plugin_context,
1483 		      krb5_get_init_creds_opt *gic_opt,
1484 		      const char *attr,
1485 		      const char *value)
1486 {
1487     krb5_error_code retval;
1488     struct _pkinit_context *plgctx = (struct _pkinit_context *)plugin_context;
1489 
1490     pkiDebug("(pkinit) received '%s' = '%s'\n", attr, value);
1491     retval = handle_gic_opt(context, plgctx, attr, value);
1492     if (retval)
1493 	return retval;
1494 
1495     return 0;
1496 }
1497 
1498 struct krb5plugin_preauth_client_ftable_v1 preauthentication_client_1 = {
1499     "pkinit",			/* name */
1500     supported_client_pa_types,	/* pa_type_list */
1501     NULL,			/* enctype_list */
1502     pkinit_client_plugin_init,	/* (*init) */
1503     pkinit_client_plugin_fini,	/* (*fini) */
1504     pkinit_client_get_flags,	/* (*flags) */
1505     pkinit_client_req_init,     /* (*client_req_init) */
1506     pkinit_client_req_fini,     /* (*client_req_fini) */
1507     pkinit_client_process,	/* (*process) */
1508     pkinit_client_tryagain,	/* (*tryagain) */
1509     pkinit_client_gic_opt	/* (*gic_opt) */
1510 };
1511