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