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