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 	    rep->choice == choice_pa_pk_as_rep_draft9_dhSignedData)) {
794 	pkiDebug("received DH key delivery AS REQ\n");
795 	retval = server_process_dh(context, plgctx->cryptoctx,
796 	    reqctx->cryptoctx, plgctx->idctx, subjectPublicKey,
797 	    subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len,
798 	    &server_key, &server_key_len);
799 	if (retval) {
800 	    pkiDebug("failed to process/create dh paramters\n");
801 	    goto cleanup;
802 	}
803     }
804 
805     if ((rep9 != NULL &&
806 	    rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) ||
807 	(rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) {
808 	retval = pkinit_octetstring2key(context, enctype, server_key,
809 					server_key_len, encrypting_key);
810 	if (retval) {
811 	    pkiDebug("pkinit_octetstring2key failed: %s\n",
812 		     error_message(retval));
813 	    goto cleanup;
814 	}
815 
816 	dhkey_info.subjectPublicKey.length = dh_pubkey_len;
817 	dhkey_info.subjectPublicKey.data = dh_pubkey;
818 	dhkey_info.nonce = request->nonce;
819 	dhkey_info.dhKeyExpiration = 0;
820 
821 	retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info,
822 						   &encoded_dhkey_info);
823 	if (retval) {
824 	    pkiDebug("encode_krb5_kdc_dh_key_info failed\n");
825 	    goto cleanup;
826 	}
827 #ifdef DEBUG_ASN1
828 	print_buffer_bin((unsigned char *)encoded_dhkey_info->data,
829 			 encoded_dhkey_info->length,
830 			 "/tmp/kdc_dh_key_info");
831 #endif
832 
833 	switch ((int)padata->pa_type) {
834 	    case KRB5_PADATA_PK_AS_REQ:
835 		retval = cms_signeddata_create(context, plgctx->cryptoctx,
836 		    reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1,
837 		    (unsigned char *)encoded_dhkey_info->data,
838 		    encoded_dhkey_info->length,
839 		    &rep->u.dh_Info.dhSignedData.data,
840 		    &rep->u.dh_Info.dhSignedData.length);
841 		if (retval) {
842 		    pkiDebug("failed to create pkcs7 signed data\n");
843 		    goto cleanup;
844 		}
845 		break;
846 	    case KRB5_PADATA_PK_AS_REP_OLD:
847 	    case KRB5_PADATA_PK_AS_REQ_OLD:
848 		retval = cms_signeddata_create(context, plgctx->cryptoctx,
849 		    reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1,
850 		    (unsigned char *)encoded_dhkey_info->data,
851 		    encoded_dhkey_info->length,
852 		    &rep9->u.dhSignedData.data,
853 		    &rep9->u.dhSignedData.length);
854 		if (retval) {
855 		    pkiDebug("failed to create pkcs7 signed data\n");
856 		    goto cleanup;
857 		}
858 		break;
859 	}
860     } else {
861 	pkiDebug("received RSA key delivery AS REQ\n");
862 
863 	retval = krb5_c_make_random_key(context, enctype, encrypting_key);
864 	if (retval) {
865 	    pkiDebug("unable to make a session key\n");
866 	    goto cleanup;
867 	}
868 
869 	/* check if PA_TYPE of 132 is present which means the client is
870 	 * requesting that a checksum is send back instead of the nonce
871 	 */
872 	for (i = 0; request->padata[i] != NULL; i++) {
873 	    pkiDebug("%s: Checking pa_type 0x%08x\n",
874 		     __FUNCTION__, request->padata[i]->pa_type);
875 	    if (request->padata[i]->pa_type == 132)
876 		fixed_keypack = 1;
877 	}
878 	pkiDebug("%s: return checksum instead of nonce = %d\n",
879 		 __FUNCTION__, fixed_keypack);
880 
881 	/* if this is an RFC reply or draft9 client requested a checksum
882 	 * in the reply instead of the nonce, create an RFC-style keypack
883 	 */
884 	if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) {
885 	    init_krb5_reply_key_pack(&key_pack);
886 	    if (key_pack == NULL) {
887 		retval = ENOMEM;
888 		goto cleanup;
889 	    }
890 	    /* retrieve checksums for a given enctype of the reply key */
891 	    retval = krb5_c_keyed_checksum_types(context,
892 		encrypting_key->enctype, &num_types, &cksum_types);
893 	    if (retval)
894 		goto cleanup;
895 
896 	    /* pick the first of acceptable enctypes for the checksum */
897 	    retval = krb5_c_make_checksum(context, cksum_types[0],
898 		    encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
899 		    req_pkt, &key_pack->asChecksum);
900 	    if (retval) {
901 		pkiDebug("unable to calculate AS REQ checksum\n");
902 		goto cleanup;
903 	    }
904 #ifdef DEBUG_CKSUM
905 	    pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length);
906 	    print_buffer(req_pkt->data, req_pkt->length);
907 	    pkiDebug("checksum size = %d\n", key_pack->asChecksum.length);
908 	    print_buffer(key_pack->asChecksum.contents,
909 			 key_pack->asChecksum.length);
910 	    pkiDebug("encrypting key (%d)\n", encrypting_key->length);
911 	    print_buffer(encrypting_key->contents, encrypting_key->length);
912 #endif
913 
914 	    krb5_copy_keyblock_contents(context, encrypting_key,
915 					&key_pack->replyKey);
916 
917 	    retval = k5int_encode_krb5_reply_key_pack(key_pack,
918 						      &encoded_key_pack);
919 	    if (retval) {
920 		pkiDebug("failed to encode reply_key_pack\n");
921 		goto cleanup;
922 	    }
923 	}
924 
925 	switch ((int)padata->pa_type) {
926 	    case KRB5_PADATA_PK_AS_REQ:
927 		rep->choice = choice_pa_pk_as_rep_encKeyPack;
928 		retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
929 		    reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1,
930 		    (unsigned char *)encoded_key_pack->data,
931 		    encoded_key_pack->length,
932 		    &rep->u.encKeyPack.data, &rep->u.encKeyPack.length);
933 		break;
934 	    case KRB5_PADATA_PK_AS_REP_OLD:
935 	    case KRB5_PADATA_PK_AS_REQ_OLD:
936 		/* if the request is from the broken draft9 client that
937 		 * expects back a nonce, create it now
938 		 */
939 		if (!fixed_keypack) {
940 		    init_krb5_reply_key_pack_draft9(&key_pack9);
941 		    if (key_pack9 == NULL) {
942 			retval = ENOMEM;
943 			goto cleanup;
944 		    }
945 		    key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce;
946 		    krb5_copy_keyblock_contents(context, encrypting_key,
947 						&key_pack9->replyKey);
948 
949 		    retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9,
950 							   &encoded_key_pack);
951 		    if (retval) {
952 			pkiDebug("failed to encode reply_key_pack\n");
953 			goto cleanup;
954 		    }
955 		}
956 
957 		rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
958 		retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
959 		    reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1,
960 		    (unsigned char *)encoded_key_pack->data,
961 		    encoded_key_pack->length,
962 		    &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length);
963 		break;
964 	}
965 	if (retval) {
966 	    pkiDebug("failed to create pkcs7 enveloped data: %s\n",
967 		     error_message(retval));
968 	    goto cleanup;
969 	}
970 #ifdef DEBUG_ASN1
971 	print_buffer_bin((unsigned char *)encoded_key_pack->data,
972 			 encoded_key_pack->length,
973 			 "/tmp/kdc_key_pack");
974 	switch ((int)padata->pa_type) {
975 	    case KRB5_PADATA_PK_AS_REQ:
976 		print_buffer_bin(rep->u.encKeyPack.data,
977 				 rep->u.encKeyPack.length,
978 				 "/tmp/kdc_enc_key_pack");
979 		break;
980 	    case KRB5_PADATA_PK_AS_REP_OLD:
981 	    case KRB5_PADATA_PK_AS_REQ_OLD:
982 		print_buffer_bin(rep9->u.encKeyPack.data,
983 				 rep9->u.encKeyPack.length,
984 				 "/tmp/kdc_enc_key_pack");
985 		break;
986 	}
987 #endif
988     }
989 
990     switch ((int)padata->pa_type) {
991 	case KRB5_PADATA_PK_AS_REQ:
992 	    retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data);
993 	    break;
994 	case KRB5_PADATA_PK_AS_REP_OLD:
995 	case KRB5_PADATA_PK_AS_REQ_OLD:
996 	    retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data);
997 	    break;
998     }
999     if (retval) {
1000 	pkiDebug("failed to encode AS_REP\n");
1001 	goto cleanup;
1002     }
1003 #ifdef DEBUG_ASN1
1004     if (out_data != NULL)
1005 	print_buffer_bin((unsigned char *)out_data->data, out_data->length,
1006 			 "/tmp/kdc_as_rep");
1007 #endif
1008 
1009     *send_pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
1010     if (*send_pa == NULL) {
1011 	retval = ENOMEM;
1012 	free(out_data->data);
1013 	free(out_data);
1014 	out_data = NULL;
1015 	goto cleanup;
1016     }
1017     (*send_pa)->magic = KV5M_PA_DATA;
1018     switch ((int)padata->pa_type) {
1019 	case KRB5_PADATA_PK_AS_REQ:
1020 	    (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP;
1021 	    break;
1022 	case KRB5_PADATA_PK_AS_REQ_OLD:
1023 	case KRB5_PADATA_PK_AS_REP_OLD:
1024 	    (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
1025 	    break;
1026     }
1027     (*send_pa)->length = out_data->length;
1028     (*send_pa)->contents = (krb5_octet *) out_data->data;
1029 
1030 
1031   cleanup:
1032     pkinit_fini_kdc_req_context(context, reqctx);
1033     if (scratch.data != NULL)
1034 	free(scratch.data);
1035     if (out_data != NULL)
1036 	free(out_data);
1037     if (encoded_dhkey_info != NULL)
1038 	krb5_free_data(context, encoded_dhkey_info);
1039     if (encoded_key_pack != NULL)
1040 	krb5_free_data(context, encoded_key_pack);
1041     if (dh_pubkey != NULL)
1042 	free(dh_pubkey);
1043     if (server_key != NULL)
1044 	free(server_key);
1045     if (cksum_types != NULL)
1046 	free(cksum_types);
1047 
1048     switch ((int)padata->pa_type) {
1049 	case KRB5_PADATA_PK_AS_REQ:
1050 	    free_krb5_pa_pk_as_req(&reqp);
1051 	    free_krb5_pa_pk_as_rep(&rep);
1052 	    free_krb5_reply_key_pack(&key_pack);
1053 	    break;
1054 	case KRB5_PADATA_PK_AS_REP_OLD:
1055 	case KRB5_PADATA_PK_AS_REQ_OLD:
1056 	    free_krb5_pa_pk_as_req_draft9(&reqp9);
1057 	    free_krb5_pa_pk_as_rep_draft9(&rep9);
1058 	    if (!fixed_keypack)
1059 		free_krb5_reply_key_pack_draft9(&key_pack9);
1060 	    else
1061 		free_krb5_reply_key_pack(&key_pack);
1062 	    break;
1063     }
1064 
1065     if (retval)
1066 	pkiDebug("pkinit_verify_padata failure");
1067 
1068     return retval;
1069 }
1070 
1071 /* ARGSUSED */
1072 static int
1073 pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype)
1074 {
1075     return PA_SUFFICIENT | PA_REPLACES_KEY;
1076 }
1077 
1078 static krb5_preauthtype supported_server_pa_types[] = {
1079     KRB5_PADATA_PK_AS_REQ,
1080     KRB5_PADATA_PK_AS_REQ_OLD,
1081     KRB5_PADATA_PK_AS_REP_OLD,
1082     0
1083 };
1084 
1085 /* ARGSUSED */
1086 static void
1087 pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
1088 {
1089     /*
1090      * There is nothing currently allocated by pkinit_init_kdc_profile()
1091      * which needs to be freed here.
1092      */
1093 }
1094 
1095 static krb5_error_code
1096 pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
1097 {
1098     krb5_error_code retval;
1099     char *eku_string = NULL;
1100 
1101     pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname);
1102     retval = pkinit_kdcdefault_string(context, plgctx->realmname,
1103 				      "pkinit_identity",
1104 				      &plgctx->idopts->identity);
1105     if (retval != 0 || NULL == plgctx->idopts->identity) {
1106 	retval = EINVAL;
1107 	krb5_set_error_message(context, retval,
1108 			       "No pkinit_identity supplied for realm %s",
1109 			       plgctx->realmname);
1110 	goto errout;
1111     }
1112 
1113     retval = pkinit_kdcdefault_strings(context, plgctx->realmname,
1114 				       "pkinit_anchors",
1115 				       &plgctx->idopts->anchors);
1116     if (retval != 0 || NULL == plgctx->idopts->anchors) {
1117 	retval = EINVAL;
1118 	krb5_set_error_message(context, retval,
1119 			       "No pkinit_anchors supplied for realm %s",
1120 			       plgctx->realmname);
1121 	goto errout;
1122     }
1123 
1124     /* Solaris Kerberos */
1125     (void) pkinit_kdcdefault_strings(context, plgctx->realmname,
1126 			      "pkinit_pool",
1127 			      &plgctx->idopts->intermediates);
1128 
1129     (void) pkinit_kdcdefault_strings(context, plgctx->realmname,
1130 			      "pkinit_revoke",
1131 			      &plgctx->idopts->crls);
1132 
1133     (void) pkinit_kdcdefault_string(context, plgctx->realmname,
1134 			     "pkinit_kdc_ocsp",
1135 			     &plgctx->idopts->ocsp);
1136 
1137     (void) pkinit_kdcdefault_string(context, plgctx->realmname,
1138 			     "pkinit_mappings_file",
1139 			     &plgctx->idopts->dn_mapping_file);
1140 
1141     (void) pkinit_kdcdefault_integer(context, plgctx->realmname,
1142 			      "pkinit_dh_min_bits",
1143 			      PKINIT_DEFAULT_DH_MIN_BITS,
1144 			      &plgctx->opts->dh_min_bits);
1145     if (plgctx->opts->dh_min_bits < 1024) {
1146 	pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, "
1147 		 "using default value (%d) instead\n", __FUNCTION__,
1148 		 plgctx->opts->dh_min_bits, PKINIT_DEFAULT_DH_MIN_BITS);
1149 	plgctx->opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS;
1150     }
1151 
1152     (void) pkinit_kdcdefault_boolean(context, plgctx->realmname,
1153 			      "pkinit_allow_upn",
1154 			      0, &plgctx->opts->allow_upn);
1155 
1156     (void) pkinit_kdcdefault_boolean(context, plgctx->realmname,
1157 			      "pkinit_require_crl_checking",
1158 			      0, &plgctx->opts->require_crl_checking);
1159 
1160     (void) pkinit_kdcdefault_string(context, plgctx->realmname,
1161 			     "pkinit_eku_checking",
1162 			     &eku_string);
1163     if (eku_string != NULL) {
1164 	if (strcasecmp(eku_string, "kpClientAuth") == 0) {
1165 	    plgctx->opts->require_eku = 1;
1166 	    plgctx->opts->accept_secondary_eku = 0;
1167 	} else if (strcasecmp(eku_string, "scLogin") == 0) {
1168 	    plgctx->opts->require_eku = 1;
1169 	    plgctx->opts->accept_secondary_eku = 1;
1170 	} else if (strcasecmp(eku_string, "none") == 0) {
1171 	    plgctx->opts->require_eku = 0;
1172 	    plgctx->opts->accept_secondary_eku = 0;
1173 	} else {
1174 	    pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
1175 		     __FUNCTION__, eku_string);
1176 	}
1177 	free(eku_string);
1178     }
1179 
1180 
1181     return 0;
1182 errout:
1183     pkinit_fini_kdc_profile(context, plgctx);
1184     return retval;
1185 }
1186 
1187 /* ARGSUSED */
1188 static pkinit_kdc_context
1189 pkinit_find_realm_context(krb5_context context, void *pa_plugin_context,
1190 			  krb5_principal princ)
1191 {
1192     int i;
1193     pkinit_kdc_context *realm_contexts = pa_plugin_context;
1194 
1195     if (pa_plugin_context == NULL)
1196 	return NULL;
1197 
1198     for (i = 0; realm_contexts[i] != NULL; i++) {
1199 	pkinit_kdc_context p = realm_contexts[i];
1200 
1201 	if ((p->realmname_len == princ->realm.length) &&
1202 	    (strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) {
1203 	    pkiDebug("%s: returning context at %p for realm '%s'\n",
1204 		     __FUNCTION__, p, p->realmname);
1205 	    return p;
1206 	}
1207     }
1208     pkiDebug("%s: unable to find realm context for realm '%.*s'\n",
1209 	     __FUNCTION__, princ->realm.length, princ->realm.data);
1210     return NULL;
1211 }
1212 
1213 static int
1214 pkinit_server_plugin_init_realm(krb5_context context, const char *realmname,
1215 				pkinit_kdc_context *pplgctx)
1216 {
1217     krb5_error_code retval = ENOMEM;
1218     pkinit_kdc_context plgctx = NULL;
1219 
1220     *pplgctx = NULL;
1221 
1222     plgctx = (pkinit_kdc_context) calloc(1, sizeof(*plgctx));
1223     if (plgctx == NULL)
1224 	goto errout;
1225 
1226     pkiDebug("%s: initializing context at %p for realm '%s'\n",
1227 	     __FUNCTION__, plgctx, realmname);
1228     (void) memset(plgctx, 0, sizeof(*plgctx));
1229     plgctx->magic = PKINIT_CTX_MAGIC;
1230 
1231     plgctx->realmname = strdup(realmname);
1232     if (plgctx->realmname == NULL)
1233 	goto errout;
1234     plgctx->realmname_len = strlen(plgctx->realmname);
1235 
1236     retval = pkinit_init_plg_crypto(&plgctx->cryptoctx);
1237     if (retval)
1238 	goto errout;
1239 
1240     retval = pkinit_init_plg_opts(&plgctx->opts);
1241     if (retval)
1242 	goto errout;
1243 
1244     retval = pkinit_init_identity_crypto(&plgctx->idctx);
1245     if (retval)
1246 	goto errout;
1247 
1248     retval = pkinit_init_identity_opts(&plgctx->idopts);
1249     if (retval)
1250 	goto errout;
1251 
1252     retval = pkinit_init_kdc_profile(context, plgctx);
1253     if (retval)
1254 	goto errout;
1255 
1256     /*
1257      * Solaris Kerberos:
1258      * Some methods of storing key information (PKCS11, PKCS12,...) may
1259      * require interactive prompting.
1260      */
1261     retval = pkinit_identity_set_prompter(plgctx->idctx, krb5_prompter_posix,
1262 					NULL);
1263     if (retval)
1264 	goto errout;
1265 
1266     retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL,
1267 					plgctx->idopts, plgctx->idctx, 0, NULL);
1268     if (retval)
1269 	goto errout;
1270 
1271     pkiDebug("%s: returning context at %p for realm '%s'\n",
1272 	     __FUNCTION__, plgctx, realmname);
1273     *pplgctx = plgctx;
1274     retval = 0;
1275 
1276 errout:
1277     if (retval)
1278 	pkinit_server_plugin_fini_realm(context, plgctx);
1279 
1280     return retval;
1281 }
1282 
1283 static int
1284 pkinit_server_plugin_init(krb5_context context, void **blob,
1285 			  const char **realmnames)
1286 {
1287     krb5_error_code retval = ENOMEM;
1288     pkinit_kdc_context plgctx, *realm_contexts = NULL;
1289     int i, j;
1290     size_t numrealms;
1291 
1292     retval = pkinit_accessor_init();
1293     if (retval)
1294 	return retval;
1295 
1296     /* Determine how many realms we may need to support */
1297     for (i = 0; realmnames[i] != NULL; i++) {};
1298     numrealms = i;
1299 
1300     realm_contexts = (pkinit_kdc_context *)
1301 			calloc(numrealms+1, sizeof(pkinit_kdc_context));
1302     if (realm_contexts == NULL)
1303 	return ENOMEM;
1304 
1305     for (i = 0, j = 0; i < numrealms; i++) {
1306 	pkiDebug("%s: processing realm '%s'\n", __FUNCTION__, realmnames[i]);
1307 	retval = pkinit_server_plugin_init_realm(context, realmnames[i], &plgctx);
1308 	if (retval == 0 && plgctx != NULL)
1309 	    realm_contexts[j++] = plgctx;
1310     }
1311 
1312     if (j == 0) {
1313 	/*
1314 	 * Solaris Kerberos
1315 	 * Improve error messages for the common case of a single realm
1316 	 */
1317 	if (numrealms != 1) {
1318 	    retval = EINVAL;
1319 	    krb5_set_error_message(context, retval, "No realms configured "
1320 			       "correctly for pkinit support");
1321 	}
1322 
1323 	goto errout;
1324     }
1325 
1326     *blob = realm_contexts;
1327     retval = 0;
1328     pkiDebug("%s: returning context at %p\n", __FUNCTION__, realm_contexts);
1329 
1330 errout:
1331     if (retval)
1332 	pkinit_server_plugin_fini(context, realm_contexts);
1333 
1334     return retval;
1335 }
1336 
1337 static void
1338 pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx)
1339 {
1340     if (plgctx == NULL)
1341 	return;
1342 
1343     pkinit_fini_kdc_profile(context, plgctx);
1344     pkinit_fini_identity_opts(plgctx->idopts);
1345     pkinit_fini_identity_crypto(plgctx->idctx);
1346     pkinit_fini_plg_crypto(plgctx->cryptoctx);
1347     pkinit_fini_plg_opts(plgctx->opts);
1348     free(plgctx->realmname);
1349     free(plgctx);
1350 }
1351 
1352 static void
1353 pkinit_server_plugin_fini(krb5_context context, void *blob)
1354 {
1355     pkinit_kdc_context *realm_contexts = blob;
1356     int i;
1357 
1358     if (realm_contexts == NULL)
1359 	return;
1360 
1361     for (i = 0; realm_contexts[i] != NULL; i++) {
1362 	pkinit_server_plugin_fini_realm(context, realm_contexts[i]);
1363     }
1364     pkiDebug("%s: freeing   context at %p\n", __FUNCTION__, realm_contexts);
1365     free(realm_contexts);
1366 }
1367 
1368 static krb5_error_code
1369 pkinit_init_kdc_req_context(krb5_context context, void **ctx)
1370 {
1371     krb5_error_code retval = ENOMEM;
1372     pkinit_kdc_req_context reqctx = NULL;
1373 
1374     reqctx = (pkinit_kdc_req_context)malloc(sizeof(*reqctx));
1375     if (reqctx == NULL)
1376 	return retval;
1377     (void) memset(reqctx, 0, sizeof(*reqctx));
1378     reqctx->magic = PKINIT_CTX_MAGIC;
1379 
1380     retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
1381     if (retval)
1382 	goto cleanup;
1383     reqctx->rcv_auth_pack = NULL;
1384     reqctx->rcv_auth_pack9 = NULL;
1385 
1386     pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
1387     *ctx = reqctx;
1388     retval = 0;
1389 cleanup:
1390     if (retval)
1391 	pkinit_fini_kdc_req_context(context, reqctx);
1392 
1393     return retval;
1394 }
1395 
1396 static void
1397 pkinit_fini_kdc_req_context(krb5_context context, void *ctx)
1398 {
1399     pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx;
1400 
1401     if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) {
1402 	pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx);
1403 	return;
1404     }
1405     pkiDebug("%s: freeing   reqctx at %p\n", __FUNCTION__, reqctx);
1406 
1407     pkinit_fini_req_crypto(reqctx->cryptoctx);
1408     if (reqctx->rcv_auth_pack != NULL)
1409 	free_krb5_auth_pack(&reqctx->rcv_auth_pack);
1410     if (reqctx->rcv_auth_pack9 != NULL)
1411 	free_krb5_auth_pack_draft9(context, &reqctx->rcv_auth_pack9);
1412 
1413     free(reqctx);
1414 }
1415 
1416 struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = {
1417     "pkinit",			/* name */
1418     supported_server_pa_types,	/* pa_type_list */
1419     pkinit_server_plugin_init,	/* (*init_proc) */
1420     pkinit_server_plugin_fini,	/* (*fini_proc) */
1421     pkinit_server_get_flags,	/* (*flags_proc) */
1422     pkinit_server_get_edata,	/* (*edata_proc) */
1423     pkinit_server_verify_padata,/* (*verify_proc) */
1424     pkinit_server_return_padata,/* (*return_proc) */
1425     NULL,			/* (*freepa_reqcontext_proc) */
1426 };
1427