xref: /illumos-gate/usr/src/lib/krb5/plugins/preauth/pkinit/pkinit_srv.c (revision e00bdde3c6d406f40f53f3025defadc22f7ec31a)
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 <string.h>
35 
36 #include "pkinit.h"
37 
38 static krb5_error_code
39 pkinit_server_get_edata(krb5_context context,
40 			krb5_kdc_req * request,
41 			struct _krb5_db_entry_new * client,
42 			struct _krb5_db_entry_new * server,
43 			preauth_get_entry_data_proc server_get_entry_data,
44 			void *pa_plugin_context,
45 			krb5_pa_data * data);
46 
47 static krb5_error_code
48 pkinit_server_verify_padata(krb5_context context,
49 			    struct _krb5_db_entry_new * client,
50 			    krb5_data *req_pkt,
51 			    krb5_kdc_req * request,
52 			    krb5_enc_tkt_part * enc_tkt_reply,
53 			    krb5_pa_data * data,
54 			    preauth_get_entry_data_proc server_get_entry_data,
55 			    void *pa_plugin_context,
56 			    void **pa_request_context,
57 			    krb5_data **e_data,
58 			    krb5_authdata ***authz_data);
59 
60 static krb5_error_code
61 pkinit_server_return_padata(krb5_context context,
62 			    krb5_pa_data * padata,
63 			    struct _krb5_db_entry_new * client,
64 			    krb5_data *req_pkt,
65 			    krb5_kdc_req * request,
66 			    krb5_kdc_rep * reply,
67 			    struct _krb5_key_data * client_key,
68 			    krb5_keyblock * encrypting_key,
69 			    krb5_pa_data ** send_pa,
70 			    preauth_get_entry_data_proc server_get_entry_data,
71 			    void *pa_plugin_context,
72 			    void **pa_request_context);
73 
74 static int pkinit_server_get_flags
75 	(krb5_context kcontext, krb5_preauthtype patype);
76 
77 static krb5_error_code pkinit_init_kdc_req_context
78 	(krb5_context, void **blob);
79 
80 static void pkinit_fini_kdc_req_context
81 	(krb5_context context, void *blob);
82 
83 static int pkinit_server_plugin_init_realm
84 	(krb5_context context, const char *realmname,
85 	 pkinit_kdc_context *pplgctx);
86 
87 static void pkinit_server_plugin_fini_realm
88 	(krb5_context context, pkinit_kdc_context plgctx);
89 
90 static int pkinit_server_plugin_init
91 	(krb5_context context, void **blob, const char **realmnames);
92 
93 static void pkinit_server_plugin_fini
94 	(krb5_context context, void *blob);
95 
96 static pkinit_kdc_context pkinit_find_realm_context
97 	(krb5_context context, void *pa_plugin_context, krb5_principal princ);
98 
99 static krb5_error_code
100 pkinit_create_edata(krb5_context context,
101 		    pkinit_plg_crypto_context plg_cryptoctx,
102 		    pkinit_req_crypto_context req_cryptoctx,
103 		    pkinit_identity_crypto_context id_cryptoctx,
104 		    pkinit_plg_opts *opts,
105 		    krb5_error_code err_code,
106 		    krb5_data **e_data)
107 {
108     krb5_error_code retval = KRB5KRB_ERR_GENERIC;
109 
110     pkiDebug("pkinit_create_edata: creating edata for error %d (%s)\n",
111 	     err_code, error_message(err_code));
112     switch(err_code) {
113 	case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
114 	    retval = pkinit_create_td_trusted_certifiers(context,
115 		plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data);
116 	    break;
117 	case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
118 	    retval = pkinit_create_td_dh_parameters(context, plg_cryptoctx,
119 		req_cryptoctx, id_cryptoctx, opts, e_data);
120 	    break;
121 	case KRB5KDC_ERR_INVALID_CERTIFICATE:
122 	case KRB5KDC_ERR_REVOKED_CERTIFICATE:
123 	    retval = pkinit_create_td_invalid_certificate(context,
124 		plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data);
125 	    break;
126 	default:
127 	    pkiDebug("no edata needed for error %d (%s)\n",
128 		     err_code, error_message(err_code));
129 	    retval = 0;
130 	    goto cleanup;
131     }
132 
133 cleanup:
134 
135     return retval;
136 }
137 
138 /* ARGSUSED */
139 static krb5_error_code
140 pkinit_server_get_edata(krb5_context context,
141 			krb5_kdc_req * request,
142 			struct _krb5_db_entry_new * client,
143 			struct _krb5_db_entry_new * server,
144 			preauth_get_entry_data_proc server_get_entry_data,
145 			void *pa_plugin_context,
146 			krb5_pa_data * data)
147 {
148     krb5_error_code retval = 0;
149     pkinit_kdc_context plgctx = NULL;
150 
151     pkiDebug("pkinit_server_get_edata: entered!\n");
152 
153     /*
154      * If we don't have a realm context for the given realm,
155      * don't tell the client that we support pkinit!
156      */
157     plgctx = pkinit_find_realm_context(context, pa_plugin_context,
158 				       request->server);
159     if (plgctx == NULL)
160 	retval = EINVAL;
161 
162     return retval;
163 }
164 
165 static krb5_error_code
166 verify_client_san(krb5_context context,
167 		  pkinit_kdc_context plgctx,
168 		  pkinit_kdc_req_context reqctx,
169 		  krb5_principal client,
170 		  int *valid_san)
171 {
172     krb5_error_code retval;
173     krb5_principal *princs = NULL;
174     krb5_principal *upns = NULL;
175     int i;
176 #ifdef DEBUG_SAN_INFO
177     char *client_string = NULL, *san_string;
178 #endif
179 
180     retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,
181 				       reqctx->cryptoctx, plgctx->idctx,
182 				       &princs,
183 				       plgctx->opts->allow_upn ? &upns : NULL,
184 				       NULL);
185     if (retval) {
186 	pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
187 	retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
188 	goto out;
189     }
190     /* XXX Verify this is consistent with client side XXX */
191 #if 0
192     retval = call_san_checking_plugins(context, plgctx, reqctx, princs,
193 				       upns, NULL, &plugin_decision, &ignore);
194     pkiDebug("%s: call_san_checking_plugins() returned retval %d\n",
195 	     __FUNCTION__);
196     if (retval) {
197 	retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
198 	goto cleanup;
199     }
200     pkiDebug("%s: call_san_checking_plugins() returned decision %d\n",
201 	     __FUNCTION__, plugin_decision);
202     if (plugin_decision != NO_DECISION) {
203 	retval = plugin_decision;
204 	goto out;
205     }
206 #endif
207 
208 #ifdef DEBUG_SAN_INFO
209     krb5_unparse_name(context, client, &client_string);
210 #endif
211     pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);
212     for (i = 0; princs != NULL && princs[i] != NULL; i++) {
213 #ifdef DEBUG_SAN_INFO
214 	krb5_unparse_name(context, princs[i], &san_string);
215 	pkiDebug("%s: Comparing client '%s' to pkinit san value '%s'\n",
216 		 __FUNCTION__, client_string, san_string);
217 	krb5_free_unparsed_name(context, san_string);
218 #endif
219 	if (krb5_principal_compare(context, princs[i], client)) {
220 	    pkiDebug("%s: pkinit san match found\n", __FUNCTION__);
221 	    *valid_san = 1;
222 	    retval = 0;
223 	    goto out;
224 	}
225     }
226     pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);
227     /*
228      * XXX if cert has names but none match, should we
229      * be returning KRB5KDC_ERR_CLIENT_NAME_MISMATCH here?
230      */
231 
232     if (upns == NULL) {
233 	pkiDebug("%s: no upn sans (or we wouldn't accept them anyway)\n",
234 		 __FUNCTION__);
235 	retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
236 	goto out;
237     }
238 
239     pkiDebug("%s: Checking upn sans\n", __FUNCTION__);
240     for (i = 0; upns[i] != NULL; i++) {
241 #ifdef DEBUG_SAN_INFO
242 	krb5_unparse_name(context, upns[i], &san_string);
243 	pkiDebug("%s: Comparing client '%s' to upn san value '%s'\n",
244 		 __FUNCTION__, client_string, san_string);
245 	krb5_free_unparsed_name(context, san_string);
246 #endif
247 	if (krb5_principal_compare(context, upns[i], client)) {
248 	    pkiDebug("%s: upn san match found\n", __FUNCTION__);
249 	    *valid_san = 1;
250 	    retval = 0;
251 	    goto out;
252 	}
253     }
254     pkiDebug("%s: no upn san match found\n", __FUNCTION__);
255 
256     /* We found no match */
257     if (princs != NULL || upns != NULL) {
258 	*valid_san = 0;
259 	/* XXX ??? If there was one or more name in the cert, but
260 	 * none matched the client name, then return mismatch? */
261 	retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
262     }
263     retval = 0;
264 
265 out:
266     if (princs != NULL) {
267 	for (i = 0; princs[i] != NULL; i++)
268 	    krb5_free_principal(context, princs[i]);
269 	free(princs);
270     }
271     if (upns != NULL) {
272 	for (i = 0; upns[i] != NULL; i++)
273 	    krb5_free_principal(context, upns[i]);
274 	free(upns);
275     }
276 #ifdef DEBUG_SAN_INFO
277     if (client_string != NULL)
278 	krb5_free_unparsed_name(context, client_string);
279 #endif
280     pkiDebug("%s: returning retval %d, valid_san %d\n",
281 	     __FUNCTION__, retval, *valid_san);
282     return retval;
283 }
284 
285 static krb5_error_code
286 verify_client_eku(krb5_context context,
287 		  pkinit_kdc_context plgctx,
288 		  pkinit_kdc_req_context reqctx,
289 		  int *eku_accepted)
290 {
291     krb5_error_code retval;
292 
293     *eku_accepted = 0;
294 
295     if (plgctx->opts->require_eku == 0) {
296 	pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
297 	*eku_accepted = 1;
298 	retval = 0;
299 	goto out;
300     }
301 
302     retval = crypto_check_cert_eku(context, plgctx->cryptoctx,
303 				   reqctx->cryptoctx, plgctx->idctx,
304 				   0, /* kdc cert */
305 				   plgctx->opts->accept_secondary_eku,
306 				   eku_accepted);
307     if (retval) {
308 	pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
309 		 __FUNCTION__, retval, error_message(retval));
310 	goto out;
311     }
312 
313 out:
314     pkiDebug("%s: returning retval %d, eku_accepted %d\n",
315 	     __FUNCTION__, retval, *eku_accepted);
316     return retval;
317 }
318 
319 /* ARGSUSED */
320 static krb5_error_code
321 pkinit_server_verify_padata(krb5_context context,
322 			    struct _krb5_db_entry_new * client,
323 			    krb5_data *req_pkt,
324 			    krb5_kdc_req * request,
325 			    krb5_enc_tkt_part * enc_tkt_reply,
326 			    krb5_pa_data * data,
327 			    preauth_get_entry_data_proc server_get_entry_data,
328 			    void *pa_plugin_context,
329 			    void **pa_request_context,
330 			    krb5_data **e_data,
331 			    krb5_authdata ***authz_data)
332 {
333     krb5_error_code retval = 0;
334     krb5_octet_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL};
335     krb5_data *encoded_pkinit_authz_data = NULL;
336     krb5_pa_pk_as_req *reqp = NULL;
337     krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
338     krb5_auth_pack *auth_pack = NULL;
339     krb5_auth_pack_draft9 *auth_pack9 = NULL;
340     pkinit_kdc_context plgctx = NULL;
341     pkinit_kdc_req_context reqctx;
342 /* Solaris Kerberos: set but not used */
343 #if 0
344     krb5_preauthtype pa_type;
345 #endif
346     krb5_checksum cksum = {0, 0, 0, NULL};
347     krb5_data *der_req = NULL;
348     int valid_eku = 0, valid_san = 0;
349     krb5_authdata **my_authz_data = NULL, *pkinit_authz_data = NULL;
350     krb5_kdc_req *tmp_as_req = NULL;
351     krb5_data k5data;
352 
353     pkiDebug("pkinit_verify_padata: entered!\n");
354     /* Solaris Kerberos */
355     if (data == NULL || data->length == 0 || data->contents == NULL)
356 	return 0;
357 
358     if (pa_plugin_context == NULL || e_data == NULL)
359 	return EINVAL;
360 
361     plgctx = pkinit_find_realm_context(context, pa_plugin_context,
362 				       request->server);
363     if (plgctx == NULL)
364 	return 0;
365 
366 #ifdef DEBUG_ASN1
367     print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req");
368 #endif
369     /* create a per-request context */
370     retval = pkinit_init_kdc_req_context(context, (void **)&reqctx);
371     if (retval)
372 	goto cleanup;
373     reqctx->pa_type = data->pa_type;
374 
375     PADATA_TO_KRB5DATA(data, &k5data);
376 
377     switch ((int)data->pa_type) {
378 	case KRB5_PADATA_PK_AS_REQ:
379 	    pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
380 	    retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp);
381 	    if (retval) {
382 		pkiDebug("decode_krb5_pa_pk_as_req failed\n");
383 		goto cleanup;
384 	    }
385 #ifdef DEBUG_ASN1
386 	    print_buffer_bin(reqp->signedAuthPack.data,
387 			     reqp->signedAuthPack.length,
388 			     "/tmp/kdc_signed_data");
389 #endif
390 	    retval = cms_signeddata_verify(context, plgctx->cryptoctx,
391 		reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT,
392 		plgctx->opts->require_crl_checking,
393 		reqp->signedAuthPack.data, reqp->signedAuthPack.length,
394 		&authp_data.data, &authp_data.length, &krb5_authz.data,
395 		&krb5_authz.length);
396 	    break;
397 	case KRB5_PADATA_PK_AS_REP_OLD:
398 	case KRB5_PADATA_PK_AS_REQ_OLD:
399 	    pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
400 	    retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9);
401 	    if (retval) {
402 		pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n");
403 		goto cleanup;
404 	    }
405 #ifdef DEBUG_ASN1
406 	    print_buffer_bin(reqp9->signedAuthPack.data,
407 			     reqp9->signedAuthPack.length,
408 			     "/tmp/kdc_signed_data_draft9");
409 #endif
410 
411 	    retval = cms_signeddata_verify(context, plgctx->cryptoctx,
412 		reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9,
413 		plgctx->opts->require_crl_checking,
414 		reqp9->signedAuthPack.data, reqp9->signedAuthPack.length,
415 		&authp_data.data, &authp_data.length, &krb5_authz.data,
416 		&krb5_authz.length);
417 	    break;
418 	default:
419 	    pkiDebug("unrecognized pa_type = %d\n", data->pa_type);
420 	    retval = EINVAL;
421 	    goto cleanup;
422     }
423     if (retval) {
424 	pkiDebug("pkcs7_signeddata_verify failed\n");
425 	goto cleanup;
426     }
427 
428     retval = verify_client_san(context, plgctx, reqctx, request->client,
429 			       &valid_san);
430     if (retval)
431 	goto cleanup;
432     if (!valid_san) {
433 	pkiDebug("%s: did not find an acceptable SAN in user certificate\n",
434 		 __FUNCTION__);
435 	retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
436 	goto cleanup;
437     }
438     retval = verify_client_eku(context, plgctx, reqctx, &valid_eku);
439     if (retval)
440 	goto cleanup;
441 
442     if (!valid_eku) {
443 	pkiDebug("%s: did not find an acceptable EKU in user certificate\n",
444 		 __FUNCTION__);
445 	retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
446 	goto cleanup;
447     }
448 
449 #ifdef DEBUG_ASN1
450     print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack");
451 #endif
452 
453     OCTETDATA_TO_KRB5DATA(&authp_data, &k5data);
454     switch ((int)data->pa_type) {
455 	case KRB5_PADATA_PK_AS_REQ:
456 	    retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack);
457 	    if (retval) {
458 		pkiDebug("failed to decode krb5_auth_pack\n");
459 		goto cleanup;
460 	    }
461 
462 	    /* check dh parameters */
463 	    if (auth_pack->clientPublicValue != NULL) {
464 		retval = server_check_dh(context, plgctx->cryptoctx,
465 		    reqctx->cryptoctx, plgctx->idctx,
466 		    &auth_pack->clientPublicValue->algorithm.parameters,
467 		    plgctx->opts->dh_min_bits);
468 
469 		if (retval) {
470 		    pkiDebug("bad dh parameters\n");
471 		    goto cleanup;
472 		}
473 	    }
474 	    /*
475 	     * The KDC may have modified the request after decoding it.
476 	     * We need to compute the checksum on the data that
477 	     * came from the client.  Therefore, we use the original
478 	     * packet contents.
479 	     */
480 	    retval = k5int_decode_krb5_as_req(req_pkt, &tmp_as_req);
481 	    if (retval) {
482 		pkiDebug("decode_krb5_as_req returned %d\n", (int)retval);
483 		goto cleanup;
484 	    }
485 
486 	    retval = k5int_encode_krb5_kdc_req_body(tmp_as_req, &der_req);
487 	    if (retval) {
488 		pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval);
489 		goto cleanup;
490 	    }
491 	    retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL,
492 					  0, der_req, &cksum);
493 	    if (retval) {
494 		pkiDebug("unable to calculate AS REQ checksum\n");
495 		goto cleanup;
496 	    }
497 	    if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length ||
498 		memcmp(cksum.contents,
499 		       auth_pack->pkAuthenticator.paChecksum.contents,
500 		       cksum.length)) {
501 		pkiDebug("failed to match the checksum\n");
502 #ifdef DEBUG_CKSUM
503 		pkiDebug("calculating checksum on buf size (%d)\n",
504 			 req_pkt->length);
505 		print_buffer(req_pkt->data, req_pkt->length);
506 		pkiDebug("received checksum type=%d size=%d ",
507 			auth_pack->pkAuthenticator.paChecksum.checksum_type,
508 			auth_pack->pkAuthenticator.paChecksum.length);
509 		print_buffer(auth_pack->pkAuthenticator.paChecksum.contents,
510 			     auth_pack->pkAuthenticator.paChecksum.length);
511 		pkiDebug("expected checksum type=%d size=%d ",
512 			 cksum.checksum_type, cksum.length);
513 		print_buffer(cksum.contents, cksum.length);
514 #endif
515 
516 		retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
517 		goto cleanup;
518 	    }
519 
520 	    /* check if kdcPkId present and match KDC's subjectIdentifier */
521 	    if (reqp->kdcPkId.data != NULL) {
522 		int valid_kdcPkId = 0;
523 		retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx,
524 		    reqctx->cryptoctx, plgctx->idctx,
525 		    reqp->kdcPkId.data, reqp->kdcPkId.length, &valid_kdcPkId);
526 		if (retval)
527 		    goto cleanup;
528 		if (!valid_kdcPkId)
529 		    pkiDebug("kdcPkId in AS_REQ does not match KDC's cert"
530 			     "RFC says to ignore and proceed\n");
531 
532 	    }
533 	    /* remember the decoded auth_pack for verify_padata routine */
534 	    reqctx->rcv_auth_pack = auth_pack;
535 	    auth_pack = NULL;
536 	    break;
537 	case KRB5_PADATA_PK_AS_REP_OLD:
538 	case KRB5_PADATA_PK_AS_REQ_OLD:
539 	    retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9);
540 	    if (retval) {
541 		pkiDebug("failed to decode krb5_auth_pack_draft9\n");
542 		goto cleanup;
543 	    }
544 	    if (auth_pack9->clientPublicValue != NULL) {
545 		retval = server_check_dh(context, plgctx->cryptoctx,
546 		    reqctx->cryptoctx, plgctx->idctx,
547 		    &auth_pack9->clientPublicValue->algorithm.parameters,
548 		    plgctx->opts->dh_min_bits);
549 
550 		if (retval) {
551 		    pkiDebug("bad dh parameters\n");
552 		    goto cleanup;
553 		}
554 	    }
555 	    /* remember the decoded auth_pack for verify_padata routine */
556 	    reqctx->rcv_auth_pack9 = auth_pack9;
557 	    auth_pack9 = NULL;
558 	    break;
559     }
560 
561     /* return authorization data to be included in the ticket */
562     switch ((int)data->pa_type) {
563 	case KRB5_PADATA_PK_AS_REQ:
564 	    my_authz_data = malloc(2 * sizeof(*my_authz_data));
565 	    if (my_authz_data == NULL) {
566 		retval = ENOMEM;
567 		pkiDebug("Couldn't allocate krb5_authdata ptr array\n");
568 		goto cleanup;
569 	    }
570 	    my_authz_data[1] = NULL;
571 	    my_authz_data[0] = malloc(sizeof(krb5_authdata));
572 	    if (my_authz_data[0] == NULL) {
573 		retval = ENOMEM;
574 		pkiDebug("Couldn't allocate krb5_authdata\n");
575 		free(my_authz_data);
576 		goto cleanup;
577 	    }
578 	    /* AD-INITIAL-VERIFIED-CAS must be wrapped in AD-IF-RELEVANT */
579 	    my_authz_data[0]->magic = KV5M_AUTHDATA;
580 	    my_authz_data[0]->ad_type = KRB5_AUTHDATA_IF_RELEVANT;
581 
582 	    /* create an internal AD-INITIAL-VERIFIED-CAS data */
583 	    pkinit_authz_data = malloc(sizeof(krb5_authdata));
584 	    if (pkinit_authz_data == NULL) {
585 		retval = ENOMEM;
586 		pkiDebug("Couldn't allocate krb5_authdata\n");
587 		free(my_authz_data[0]);
588 		free(my_authz_data);
589 		goto cleanup;
590 	    }
591 	    pkinit_authz_data->ad_type = KRB5_AUTHDATA_INITIAL_VERIFIED_CAS;
592 	    /* content of this ad-type contains the certification
593 	       path with which the client certificate was validated
594 	     */
595 	    pkinit_authz_data->contents = krb5_authz.data;
596 	    pkinit_authz_data->length = krb5_authz.length;
597 	    retval = k5int_encode_krb5_authdata_elt(pkinit_authz_data,
598 			    &encoded_pkinit_authz_data);
599 #ifdef DEBUG_ASN1
600 	    print_buffer_bin((unsigned char *)encoded_pkinit_authz_data->data,
601 			     encoded_pkinit_authz_data->length,
602 			     "/tmp/kdc_pkinit_authz_data");
603 #endif
604 	    free(pkinit_authz_data);
605 	    if (retval) {
606 		pkiDebug("k5int_encode_krb5_authdata_elt failed\n");
607 		free(my_authz_data[0]);
608 		free(my_authz_data);
609 		goto cleanup;
610 	    }
611 
612 	    my_authz_data[0]->contents =
613 			    (krb5_octet *) encoded_pkinit_authz_data->data;
614 	    my_authz_data[0]->length = encoded_pkinit_authz_data->length;
615 	    *authz_data = my_authz_data;
616 	    pkiDebug("Returning %d bytes of authorization data\n",
617 		     krb5_authz.length);
618 	    encoded_pkinit_authz_data->data = NULL; /* Don't free during cleanup*/
619 	    free(encoded_pkinit_authz_data);
620 	    break;
621 	default:
622 	    *authz_data = NULL;
623     }
624     /* remember to set the PREAUTH flag in the reply */
625     enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
626     *pa_request_context = reqctx;
627     reqctx = NULL;
628 
629   cleanup:
630     if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) {
631 	pkiDebug("pkinit_verify_padata failed: creating e-data\n");
632 	if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx,
633 		plgctx->idctx, plgctx->opts, retval, e_data))
634 	    pkiDebug("pkinit_create_edata failed\n");
635     }
636 
637     switch ((int)data->pa_type) {
638 	case KRB5_PADATA_PK_AS_REQ:
639 	    free_krb5_pa_pk_as_req(&reqp);
640 	    if (cksum.contents != NULL)
641 		free(cksum.contents);
642 	    if (der_req != NULL)
643 		 krb5_free_data(context, der_req);
644 	    break;
645 	case KRB5_PADATA_PK_AS_REP_OLD:
646 	case KRB5_PADATA_PK_AS_REQ_OLD:
647 	    free_krb5_pa_pk_as_req_draft9(&reqp9);
648     }
649     if (tmp_as_req != NULL)
650 	k5int_krb5_free_kdc_req(context, tmp_as_req);
651     if (authp_data.data != NULL)
652 	free(authp_data.data);
653     if (krb5_authz.data != NULL)
654 	free(krb5_authz.data);
655     if (reqctx != NULL)
656 	pkinit_fini_kdc_req_context(context, reqctx);
657     if (auth_pack != NULL)
658 	free_krb5_auth_pack(&auth_pack);
659     if (auth_pack9 != NULL)
660 	free_krb5_auth_pack_draft9(context, &auth_pack9);
661 
662     return retval;
663 }
664 
665 /* ARGSUSED */
666 static krb5_error_code
667 pkinit_server_return_padata(krb5_context context,
668 			    krb5_pa_data * padata,
669 			    struct _krb5_db_entry_new * client,
670 			    krb5_data *req_pkt,
671 			    krb5_kdc_req * request,
672 			    krb5_kdc_rep * reply,
673 			    struct _krb5_key_data * client_key,
674 			    krb5_keyblock * encrypting_key,
675 			    krb5_pa_data ** send_pa,
676 			    preauth_get_entry_data_proc server_get_entry_data,
677 			    void *pa_plugin_context,
678 			    void **pa_request_context)
679 {
680     krb5_error_code retval = 0;
681     krb5_data scratch = {0, 0, NULL};
682     krb5_pa_pk_as_req *reqp = NULL;
683     krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
684     int i = 0;
685 
686     unsigned char *subjectPublicKey = NULL;
687     unsigned char *dh_pubkey = NULL, *server_key = NULL;
688     unsigned int subjectPublicKey_len = 0;
689     unsigned int server_key_len = 0, dh_pubkey_len = 0;
690 
691     krb5_kdc_dh_key_info dhkey_info;
692     krb5_data *encoded_dhkey_info = NULL;
693     krb5_pa_pk_as_rep *rep = NULL;
694     krb5_pa_pk_as_rep_draft9 *rep9 = NULL;
695     krb5_data *out_data = NULL;
696 
697     krb5_enctype enctype = -1;
698 
699     krb5_reply_key_pack *key_pack = NULL;
700     krb5_reply_key_pack_draft9 *key_pack9 = NULL;
701     krb5_data *encoded_key_pack = NULL;
702     unsigned int num_types;
703     krb5_cksumtype *cksum_types = NULL;
704 
705     pkinit_kdc_context plgctx;
706     pkinit_kdc_req_context reqctx;
707 
708     int fixed_keypack = 0;
709 
710     *send_pa = NULL;
711     /* Solaris Kerberos */
712     if (padata == NULL || padata->length == 0 || padata->contents == NULL)
713 	return 0;
714 
715     if (pa_request_context == NULL || *pa_request_context == NULL) {
716 	pkiDebug("missing request context \n");
717 	return EINVAL;
718     }
719 
720     plgctx = pkinit_find_realm_context(context, pa_plugin_context,
721 				       request->server);
722     if (plgctx == NULL) {
723 	pkiDebug("Unable to locate correct realm context\n");
724 	return ENOENT;
725     }
726 
727     pkiDebug("pkinit_return_padata: entered!\n");
728     reqctx = (pkinit_kdc_req_context)*pa_request_context;
729 
730     if (encrypting_key->contents) {
731 	free(encrypting_key->contents);
732 	encrypting_key->length = 0;
733 	encrypting_key->contents = NULL;
734     }
735 
736     for(i = 0; i < request->nktypes; i++) {
737 	enctype = request->ktype[i];
738 	if (!krb5_c_valid_enctype(enctype))
739 	    continue;
740 	else {
741 	    pkiDebug("KDC picked etype = %d\n", enctype);
742 	    break;
743 	}
744     }
745 
746     if (i == request->nktypes) {
747 	retval = KRB5KDC_ERR_ETYPE_NOSUPP;
748 	goto cleanup;
749     }
750 
751     switch((int)reqctx->pa_type) {
752 	case KRB5_PADATA_PK_AS_REQ:
753 	    init_krb5_pa_pk_as_rep(&rep);
754 	    if (rep == NULL) {
755 		retval = ENOMEM;
756 		goto cleanup;
757 	    }
758 	    /* let's assume it's RSA. we'll reset it to DH if needed */
759 	    rep->choice = choice_pa_pk_as_rep_encKeyPack;
760 	    break;
761 	case KRB5_PADATA_PK_AS_REP_OLD:
762 	case KRB5_PADATA_PK_AS_REQ_OLD:
763 	    init_krb5_pa_pk_as_rep_draft9(&rep9);
764 	    if (rep9 == NULL) {
765 		retval = ENOMEM;
766 		goto cleanup;
767 	    }
768 	    rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
769 	    break;
770 	default:
771 	    retval = KRB5KDC_ERR_PREAUTH_FAILED;
772 	    goto cleanup;
773     }
774 
775     if (reqctx->rcv_auth_pack != NULL &&
776 	    reqctx->rcv_auth_pack->clientPublicValue != NULL) {
777 	subjectPublicKey =
778 	    reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.data;
779 	subjectPublicKey_len =
780 	    reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.length;
781 	rep->choice = choice_pa_pk_as_rep_dhInfo;
782     } else if (reqctx->rcv_auth_pack9 != NULL &&
783 		reqctx->rcv_auth_pack9->clientPublicValue != NULL) {
784 	subjectPublicKey =
785 	    reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.data;
786 	subjectPublicKey_len =
787 	    reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.length;
788 	rep9->choice = choice_pa_pk_as_rep_draft9_dhSignedData;
789     }
790 
791     /* if this DH, then process finish computing DH key */
792     if (((rep != NULL) && (rep->choice == choice_pa_pk_as_rep_dhInfo)) ||
793 	((rep9 != NULL) && rep9->choice ==
794 	    choice_pa_pk_as_rep_draft9_dhSignedData)) {
795 	pkiDebug("received DH key delivery AS REQ\n");
796 	retval = server_process_dh(context, plgctx->cryptoctx,
797 	    reqctx->cryptoctx, plgctx->idctx, subjectPublicKey,
798 	    subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len,
799 	    &server_key, &server_key_len);
800 	if (retval) {
801 	    pkiDebug("failed to process/create dh paramters\n");
802 	    goto cleanup;
803 	}
804     }
805 
806     if ((rep9 != NULL &&
807 	    rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) ||
808 	(rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) {
809 	retval = pkinit_octetstring2key(context, enctype, server_key,
810 					server_key_len, encrypting_key);
811 	if (retval) {
812 	    pkiDebug("pkinit_octetstring2key failed: %s\n",
813 		     error_message(retval));
814 	    goto cleanup;
815 	}
816 
817 	dhkey_info.subjectPublicKey.length = dh_pubkey_len;
818 	dhkey_info.subjectPublicKey.data = dh_pubkey;
819 	dhkey_info.nonce = request->nonce;
820 	dhkey_info.dhKeyExpiration = 0;
821 
822 	retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info,
823 						   &encoded_dhkey_info);
824 	if (retval) {
825 	    pkiDebug("encode_krb5_kdc_dh_key_info failed\n");
826 	    goto cleanup;
827 	}
828 #ifdef DEBUG_ASN1
829 	print_buffer_bin((unsigned char *)encoded_dhkey_info->data,
830 			 encoded_dhkey_info->length,
831 			 "/tmp/kdc_dh_key_info");
832 #endif
833 
834 	switch ((int)padata->pa_type) {
835 	    case KRB5_PADATA_PK_AS_REQ:
836 		retval = cms_signeddata_create(context, plgctx->cryptoctx,
837 		    reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1,
838 		    (unsigned char *)encoded_dhkey_info->data,
839 		    encoded_dhkey_info->length,
840 		    &rep->u.dh_Info.dhSignedData.data,
841 		    &rep->u.dh_Info.dhSignedData.length);
842 		if (retval) {
843 		    pkiDebug("failed to create pkcs7 signed data\n");
844 		    goto cleanup;
845 		}
846 		break;
847 	    case KRB5_PADATA_PK_AS_REP_OLD:
848 	    case KRB5_PADATA_PK_AS_REQ_OLD:
849 		retval = cms_signeddata_create(context, plgctx->cryptoctx,
850 		    reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1,
851 		    (unsigned char *)encoded_dhkey_info->data,
852 		    encoded_dhkey_info->length,
853 		    &rep9->u.dhSignedData.data,
854 		    &rep9->u.dhSignedData.length);
855 		if (retval) {
856 		    pkiDebug("failed to create pkcs7 signed data\n");
857 		    goto cleanup;
858 		}
859 		break;
860 	}
861     } else {
862 	pkiDebug("received RSA key delivery AS REQ\n");
863 
864 	retval = krb5_c_make_random_key(context, enctype, encrypting_key);
865 	if (retval) {
866 	    pkiDebug("unable to make a session key\n");
867 	    goto cleanup;
868 	}
869 
870 	/* check if PA_TYPE of 132 is present which means the client is
871 	 * requesting that a checksum is send back instead of the nonce
872 	 */
873 	for (i = 0; request->padata[i] != NULL; i++) {
874 	    pkiDebug("%s: Checking pa_type 0x%08x\n",
875 		     __FUNCTION__, request->padata[i]->pa_type);
876 	    if (request->padata[i]->pa_type == 132)
877 		fixed_keypack = 1;
878 	}
879 	pkiDebug("%s: return checksum instead of nonce = %d\n",
880 		 __FUNCTION__, fixed_keypack);
881 
882 	/* if this is an RFC reply or draft9 client requested a checksum
883 	 * in the reply instead of the nonce, create an RFC-style keypack
884 	 */
885 	if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) {
886 	    init_krb5_reply_key_pack(&key_pack);
887 	    if (key_pack == NULL) {
888 		retval = ENOMEM;
889 		goto cleanup;
890 	    }
891 	    /* retrieve checksums for a given enctype of the reply key */
892 	    retval = krb5_c_keyed_checksum_types(context,
893 		encrypting_key->enctype, &num_types, &cksum_types);
894 	    if (retval)
895 		goto cleanup;
896 
897 	    /* pick the first of acceptable enctypes for the checksum */
898 	    retval = krb5_c_make_checksum(context, cksum_types[0],
899 		    encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
900 		    req_pkt, &key_pack->asChecksum);
901 	    if (retval) {
902 		pkiDebug("unable to calculate AS REQ checksum\n");
903 		goto cleanup;
904 	    }
905 #ifdef DEBUG_CKSUM
906 	    pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length);
907 	    print_buffer(req_pkt->data, req_pkt->length);
908 	    pkiDebug("checksum size = %d\n", key_pack->asChecksum.length);
909 	    print_buffer(key_pack->asChecksum.contents,
910 			 key_pack->asChecksum.length);
911 	    pkiDebug("encrypting key (%d)\n", encrypting_key->length);
912 	    print_buffer(encrypting_key->contents, encrypting_key->length);
913 #endif
914 
915 	    krb5_copy_keyblock_contents(context, encrypting_key,
916 					&key_pack->replyKey);
917 
918 	    retval = k5int_encode_krb5_reply_key_pack(key_pack,
919 						      &encoded_key_pack);
920 	    if (retval) {
921 		pkiDebug("failed to encode reply_key_pack\n");
922 		goto cleanup;
923 	    }
924 	}
925 
926 	switch ((int)padata->pa_type) {
927 	    case KRB5_PADATA_PK_AS_REQ:
928 		rep->choice = choice_pa_pk_as_rep_encKeyPack;
929 		retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
930 		    reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1,
931 		    (unsigned char *)encoded_key_pack->data,
932 		    encoded_key_pack->length,
933 		    &rep->u.encKeyPack.data, &rep->u.encKeyPack.length);
934 		break;
935 	    case KRB5_PADATA_PK_AS_REP_OLD:
936 	    case KRB5_PADATA_PK_AS_REQ_OLD:
937 		/* if the request is from the broken draft9 client that
938 		 * expects back a nonce, create it now
939 		 */
940 		if (!fixed_keypack) {
941 		    init_krb5_reply_key_pack_draft9(&key_pack9);
942 		    if (key_pack9 == NULL) {
943 			retval = ENOMEM;
944 			goto cleanup;
945 		    }
946 		    key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce;
947 		    krb5_copy_keyblock_contents(context, encrypting_key,
948 						&key_pack9->replyKey);
949 
950 		    retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9,
951 							   &encoded_key_pack);
952 		    if (retval) {
953 			pkiDebug("failed to encode reply_key_pack\n");
954 			goto cleanup;
955 		    }
956 		}
957 
958 		rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
959 		retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
960 		    reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1,
961 		    (unsigned char *)encoded_key_pack->data,
962 		    encoded_key_pack->length,
963 		    &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length);
964 		break;
965 	}
966 	if (retval) {
967 	    pkiDebug("failed to create pkcs7 enveloped data: %s\n",
968 		     error_message(retval));
969 	    goto cleanup;
970 	}
971 #ifdef DEBUG_ASN1
972 	print_buffer_bin((unsigned char *)encoded_key_pack->data,
973 			 encoded_key_pack->length,
974 			 "/tmp/kdc_key_pack");
975 	switch ((int)padata->pa_type) {
976 	    case KRB5_PADATA_PK_AS_REQ:
977 		print_buffer_bin(rep->u.encKeyPack.data,
978 				 rep->u.encKeyPack.length,
979 				 "/tmp/kdc_enc_key_pack");
980 		break;
981 	    case KRB5_PADATA_PK_AS_REP_OLD:
982 	    case KRB5_PADATA_PK_AS_REQ_OLD:
983 		print_buffer_bin(rep9->u.encKeyPack.data,
984 				 rep9->u.encKeyPack.length,
985 				 "/tmp/kdc_enc_key_pack");
986 		break;
987 	}
988 #endif
989     }
990 
991     switch ((int)padata->pa_type) {
992 	case KRB5_PADATA_PK_AS_REQ:
993 	    retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data);
994 	    break;
995 	case KRB5_PADATA_PK_AS_REP_OLD:
996 	case KRB5_PADATA_PK_AS_REQ_OLD:
997 	    retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data);
998 	    break;
999     }
1000     if (retval) {
1001 	pkiDebug("failed to encode AS_REP\n");
1002 	goto cleanup;
1003     }
1004 #ifdef DEBUG_ASN1
1005     if (out_data != NULL)
1006 	print_buffer_bin((unsigned char *)out_data->data, out_data->length,
1007 			 "/tmp/kdc_as_rep");
1008 #endif
1009 
1010     *send_pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
1011     if (*send_pa == NULL) {
1012 	retval = ENOMEM;
1013 	free(out_data->data);
1014 	free(out_data);
1015 	out_data = NULL;
1016 	goto cleanup;
1017     }
1018     (*send_pa)->magic = KV5M_PA_DATA;
1019     switch ((int)padata->pa_type) {
1020 	case KRB5_PADATA_PK_AS_REQ:
1021 	    (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP;
1022 	    break;
1023 	case KRB5_PADATA_PK_AS_REQ_OLD:
1024 	case KRB5_PADATA_PK_AS_REP_OLD:
1025 	    (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
1026 	    break;
1027     }
1028     (*send_pa)->length = out_data->length;
1029     (*send_pa)->contents = (krb5_octet *) out_data->data;
1030 
1031 
1032   cleanup:
1033     pkinit_fini_kdc_req_context(context, reqctx);
1034     if (scratch.data != NULL)
1035 	free(scratch.data);
1036     if (out_data != NULL)
1037 	free(out_data);
1038     if (encoded_dhkey_info != NULL)
1039 	krb5_free_data(context, encoded_dhkey_info);
1040     if (encoded_key_pack != NULL)
1041 	krb5_free_data(context, encoded_key_pack);
1042     if (dh_pubkey != NULL)
1043 	free(dh_pubkey);
1044     if (server_key != NULL)
1045 	free(server_key);
1046     if (cksum_types != NULL)
1047 	free(cksum_types);
1048 
1049     switch ((int)padata->pa_type) {
1050 	case KRB5_PADATA_PK_AS_REQ:
1051 	    free_krb5_pa_pk_as_req(&reqp);
1052 	    free_krb5_pa_pk_as_rep(&rep);
1053 	    free_krb5_reply_key_pack(&key_pack);
1054 	    break;
1055 	case KRB5_PADATA_PK_AS_REP_OLD:
1056 	case KRB5_PADATA_PK_AS_REQ_OLD:
1057 	    free_krb5_pa_pk_as_req_draft9(&reqp9);
1058 	    free_krb5_pa_pk_as_rep_draft9(&rep9);
1059 	    if (!fixed_keypack)
1060 		free_krb5_reply_key_pack_draft9(&key_pack9);
1061 	    else
1062 		free_krb5_reply_key_pack(&key_pack);
1063 	    break;
1064     }
1065 
1066     if (retval)
1067 	pkiDebug("pkinit_verify_padata failure");
1068 
1069     return retval;
1070 }
1071 
1072 /* ARGSUSED */
1073 static int
1074 pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype)
1075 {
1076     return PA_SUFFICIENT | PA_REPLACES_KEY;
1077 }
1078 
1079 static krb5_preauthtype supported_server_pa_types[] = {
1080     KRB5_PADATA_PK_AS_REQ,
1081     KRB5_PADATA_PK_AS_REQ_OLD,
1082     KRB5_PADATA_PK_AS_REP_OLD,
1083     0
1084 };
1085 
1086 /* ARGSUSED */
1087 static void
1088 pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
1089 {
1090     /*
1091      * There is nothing currently allocated by pkinit_init_kdc_profile()
1092      * which needs to be freed here.
1093      */
1094 }
1095 
1096 static krb5_error_code
1097 pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
1098 {
1099     krb5_error_code retval;
1100     char *eku_string = NULL;
1101 
1102     pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname);
1103     retval = pkinit_kdcdefault_string(context, plgctx->realmname,
1104 				      "pkinit_identity",
1105 				      &plgctx->idopts->identity);
1106     if (retval != 0 || NULL == plgctx->idopts->identity) {
1107 	retval = EINVAL;
1108 	krb5_set_error_message(context, retval,
1109 			       "No pkinit_identity supplied for realm %s",
1110 			       plgctx->realmname);
1111 	goto errout;
1112     }
1113 
1114     retval = pkinit_kdcdefault_strings(context, plgctx->realmname,
1115 				       "pkinit_anchors",
1116 				       &plgctx->idopts->anchors);
1117     if (retval != 0 || NULL == plgctx->idopts->anchors) {
1118 	retval = EINVAL;
1119 	krb5_set_error_message(context, retval,
1120 			       "No pkinit_anchors supplied for realm %s",
1121 			       plgctx->realmname);
1122 	goto errout;
1123     }
1124 
1125     /* Solaris Kerberos */
1126     (void) pkinit_kdcdefault_strings(context, plgctx->realmname,
1127 			      "pkinit_pool",
1128 			      &plgctx->idopts->intermediates);
1129 
1130     (void) pkinit_kdcdefault_strings(context, plgctx->realmname,
1131 			      "pkinit_revoke",
1132 			      &plgctx->idopts->crls);
1133 
1134     (void) pkinit_kdcdefault_string(context, plgctx->realmname,
1135 			     "pkinit_kdc_ocsp",
1136 			     &plgctx->idopts->ocsp);
1137 
1138     (void) pkinit_kdcdefault_string(context, plgctx->realmname,
1139 			     "pkinit_mappings_file",
1140 			     &plgctx->idopts->dn_mapping_file);
1141 
1142     (void) pkinit_kdcdefault_integer(context, plgctx->realmname,
1143 			      "pkinit_dh_min_bits",
1144 			      PKINIT_DEFAULT_DH_MIN_BITS,
1145 			      &plgctx->opts->dh_min_bits);
1146     if (plgctx->opts->dh_min_bits < 1024) {
1147 	pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, "
1148 		 "using default value (%d) instead\n", __FUNCTION__,
1149 		 plgctx->opts->dh_min_bits, PKINIT_DEFAULT_DH_MIN_BITS);
1150 	plgctx->opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS;
1151     }
1152 
1153     (void) pkinit_kdcdefault_boolean(context, plgctx->realmname,
1154 			      "pkinit_allow_upn",
1155 			      0, &plgctx->opts->allow_upn);
1156 
1157     (void) pkinit_kdcdefault_boolean(context, plgctx->realmname,
1158 			      "pkinit_require_crl_checking",
1159 			      0, &plgctx->opts->require_crl_checking);
1160 
1161     (void) pkinit_kdcdefault_string(context, plgctx->realmname,
1162 			     "pkinit_eku_checking",
1163 			     &eku_string);
1164     if (eku_string != NULL) {
1165 	if (strcasecmp(eku_string, "kpClientAuth") == 0) {
1166 	    plgctx->opts->require_eku = 1;
1167 	    plgctx->opts->accept_secondary_eku = 0;
1168 	} else if (strcasecmp(eku_string, "scLogin") == 0) {
1169 	    plgctx->opts->require_eku = 1;
1170 	    plgctx->opts->accept_secondary_eku = 1;
1171 	} else if (strcasecmp(eku_string, "none") == 0) {
1172 	    plgctx->opts->require_eku = 0;
1173 	    plgctx->opts->accept_secondary_eku = 0;
1174 	} else {
1175 	    pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
1176 		     __FUNCTION__, eku_string);
1177 	}
1178 	free(eku_string);
1179     }
1180 
1181 
1182     return 0;
1183 errout:
1184     pkinit_fini_kdc_profile(context, plgctx);
1185     return retval;
1186 }
1187 
1188 /* ARGSUSED */
1189 static pkinit_kdc_context
1190 pkinit_find_realm_context(krb5_context context, void *pa_plugin_context,
1191 			  krb5_principal princ)
1192 {
1193     int i;
1194     pkinit_kdc_context *realm_contexts = pa_plugin_context;
1195 
1196     if (pa_plugin_context == NULL)
1197 	return NULL;
1198 
1199     for (i = 0; realm_contexts[i] != NULL; i++) {
1200 	pkinit_kdc_context p = realm_contexts[i];
1201 
1202 	if ((p->realmname_len == princ->realm.length) &&
1203 	    (strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) {
1204 	    pkiDebug("%s: returning context at %p for realm '%s'\n",
1205 		     __FUNCTION__, p, p->realmname);
1206 	    return p;
1207 	}
1208     }
1209     pkiDebug("%s: unable to find realm context for realm '%.*s'\n",
1210 	     __FUNCTION__, princ->realm.length, princ->realm.data);
1211     return NULL;
1212 }
1213 
1214 static int
1215 pkinit_server_plugin_init_realm(krb5_context context, const char *realmname,
1216 				pkinit_kdc_context *pplgctx)
1217 {
1218     krb5_error_code retval = ENOMEM;
1219     pkinit_kdc_context plgctx = NULL;
1220 
1221     *pplgctx = NULL;
1222 
1223     plgctx = (pkinit_kdc_context) calloc(1, sizeof(*plgctx));
1224     if (plgctx == NULL)
1225 	goto errout;
1226 
1227     pkiDebug("%s: initializing context at %p for realm '%s'\n",
1228 	     __FUNCTION__, plgctx, realmname);
1229     (void) memset(plgctx, 0, sizeof(*plgctx));
1230     plgctx->magic = PKINIT_CTX_MAGIC;
1231 
1232     plgctx->realmname = strdup(realmname);
1233     if (plgctx->realmname == NULL)
1234 	goto errout;
1235     plgctx->realmname_len = strlen(plgctx->realmname);
1236 
1237     retval = pkinit_init_plg_crypto(&plgctx->cryptoctx);
1238     if (retval)
1239 	goto errout;
1240 
1241     retval = pkinit_init_plg_opts(&plgctx->opts);
1242     if (retval)
1243 	goto errout;
1244 
1245     retval = pkinit_init_identity_crypto(&plgctx->idctx);
1246     if (retval)
1247 	goto errout;
1248 
1249     retval = pkinit_init_identity_opts(&plgctx->idopts);
1250     if (retval)
1251 	goto errout;
1252 
1253     retval = pkinit_init_kdc_profile(context, plgctx);
1254     if (retval)
1255 	goto errout;
1256 
1257     /*
1258      * Solaris Kerberos:
1259      * Some methods of storing key information (PKCS11, PKCS12,...) may
1260      * require interactive prompting.
1261      */
1262     retval = pkinit_identity_set_prompter(plgctx->idctx, krb5_prompter_posix,
1263 					NULL);
1264     if (retval)
1265 	goto errout;
1266 
1267     retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL,
1268 					plgctx->idopts, plgctx->idctx, 0, NULL);
1269     if (retval)
1270 	goto errout;
1271 
1272     pkiDebug("%s: returning context at %p for realm '%s'\n",
1273 	     __FUNCTION__, plgctx, realmname);
1274     *pplgctx = plgctx;
1275     retval = 0;
1276 
1277 errout:
1278     if (retval)
1279 	pkinit_server_plugin_fini_realm(context, plgctx);
1280 
1281     return retval;
1282 }
1283 
1284 static int
1285 pkinit_server_plugin_init(krb5_context context, void **blob,
1286 			  const char **realmnames)
1287 {
1288     krb5_error_code retval = ENOMEM;
1289     pkinit_kdc_context plgctx, *realm_contexts = NULL;
1290     int i, j;
1291     size_t numrealms;
1292 
1293     retval = pkinit_accessor_init();
1294     if (retval)
1295 	return retval;
1296 
1297     /* Determine how many realms we may need to support */
1298     for (i = 0; realmnames[i] != NULL; i++) {};
1299     numrealms = i;
1300 
1301     realm_contexts = (pkinit_kdc_context *)
1302 			calloc(numrealms+1, sizeof(pkinit_kdc_context));
1303     if (realm_contexts == NULL)
1304 	return ENOMEM;
1305 
1306     for (i = 0, j = 0; i < numrealms; i++) {
1307 	pkiDebug("%s: processing realm '%s'\n", __FUNCTION__, realmnames[i]);
1308 	retval = pkinit_server_plugin_init_realm(context, realmnames[i], &plgctx);
1309 	if (retval == 0 && plgctx != NULL)
1310 	    realm_contexts[j++] = plgctx;
1311     }
1312 
1313     if (j == 0) {
1314 	/*
1315 	 * Solaris Kerberos
1316 	 * Improve error messages for the common case of a single realm
1317 	 */
1318 	if (numrealms != 1) {
1319 	    retval = EINVAL;
1320 	    krb5_set_error_message(context, retval, "No realms configured "
1321 			       "correctly for pkinit support");
1322 	}
1323 
1324 	goto errout;
1325     }
1326 
1327     *blob = realm_contexts;
1328     retval = 0;
1329     pkiDebug("%s: returning context at %p\n", __FUNCTION__, realm_contexts);
1330 
1331 errout:
1332     if (retval)
1333 	pkinit_server_plugin_fini(context, realm_contexts);
1334 
1335     return retval;
1336 }
1337 
1338 static void
1339 pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx)
1340 {
1341     if (plgctx == NULL)
1342 	return;
1343 
1344     pkinit_fini_kdc_profile(context, plgctx);
1345     pkinit_fini_identity_opts(plgctx->idopts);
1346     pkinit_fini_identity_crypto(plgctx->idctx);
1347     pkinit_fini_plg_crypto(plgctx->cryptoctx);
1348     pkinit_fini_plg_opts(plgctx->opts);
1349     free(plgctx->realmname);
1350     free(plgctx);
1351 }
1352 
1353 static void
1354 pkinit_server_plugin_fini(krb5_context context, void *blob)
1355 {
1356     pkinit_kdc_context *realm_contexts = blob;
1357     int i;
1358 
1359     if (realm_contexts == NULL)
1360 	return;
1361 
1362     for (i = 0; realm_contexts[i] != NULL; i++) {
1363 	pkinit_server_plugin_fini_realm(context, realm_contexts[i]);
1364     }
1365     pkiDebug("%s: freeing   context at %p\n", __FUNCTION__, realm_contexts);
1366     free(realm_contexts);
1367 }
1368 
1369 static krb5_error_code
1370 pkinit_init_kdc_req_context(krb5_context context, void **ctx)
1371 {
1372     krb5_error_code retval = ENOMEM;
1373     pkinit_kdc_req_context reqctx = NULL;
1374 
1375     reqctx = (pkinit_kdc_req_context)malloc(sizeof(*reqctx));
1376     if (reqctx == NULL)
1377 	return retval;
1378     (void) memset(reqctx, 0, sizeof(*reqctx));
1379     reqctx->magic = PKINIT_CTX_MAGIC;
1380 
1381     retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
1382     if (retval)
1383 	goto cleanup;
1384     reqctx->rcv_auth_pack = NULL;
1385     reqctx->rcv_auth_pack9 = NULL;
1386 
1387     pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
1388     *ctx = reqctx;
1389     retval = 0;
1390 cleanup:
1391     if (retval)
1392 	pkinit_fini_kdc_req_context(context, reqctx);
1393 
1394     return retval;
1395 }
1396 
1397 static void
1398 pkinit_fini_kdc_req_context(krb5_context context, void *ctx)
1399 {
1400     pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx;
1401 
1402     if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) {
1403 	pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx);
1404 	return;
1405     }
1406     pkiDebug("%s: freeing   reqctx at %p\n", __FUNCTION__, reqctx);
1407 
1408     pkinit_fini_req_crypto(reqctx->cryptoctx);
1409     if (reqctx->rcv_auth_pack != NULL)
1410 	free_krb5_auth_pack(&reqctx->rcv_auth_pack);
1411     if (reqctx->rcv_auth_pack9 != NULL)
1412 	free_krb5_auth_pack_draft9(context, &reqctx->rcv_auth_pack9);
1413 
1414     free(reqctx);
1415 }
1416 
1417 struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = {
1418     "pkinit",			/* name */
1419     supported_server_pa_types,	/* pa_type_list */
1420     pkinit_server_plugin_init,	/* (*init_proc) */
1421     pkinit_server_plugin_fini,	/* (*fini_proc) */
1422     pkinit_server_get_flags,	/* (*flags_proc) */
1423     pkinit_server_get_edata,	/* (*edata_proc) */
1424     pkinit_server_verify_padata,/* (*verify_proc) */
1425     pkinit_server_return_padata,/* (*return_proc) */
1426     NULL,			/* (*freepa_reqcontext_proc) */
1427 };
1428