1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * COPYRIGHT (C) 2006,2007
4 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
5 * ALL RIGHTS RESERVED
6 *
7 * Permission is granted to use, copy, create derivative works
8 * and redistribute this software and such derivative works
9 * for any purpose, so long as the name of The University of
10 * Michigan is not used in any advertising or publicity
11 * pertaining to the use of distribution of this software
12 * without specific, written prior authorization. If the
13 * above copyright notice or any other identification of the
14 * University of Michigan is included in any copy of any
15 * portion of this software, then the disclaimer below must
16 * also be included.
17 *
18 * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
19 * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
20 * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
21 * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
22 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
24 * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
25 * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
26 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
27 * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
28 * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGES.
30 */
31
32 #include "k5-int.h"
33 #include "pkinit.h"
34 #include "k5-json.h"
35
36 #include <sys/stat.h>
37
38 /**
39 * Return true if we should use ContentInfo rather than SignedData. This
40 * happens if we are talking to what might be an old (pre-6112) MIT KDC and
41 * we're using anonymous.
42 */
43 static int
use_content_info(krb5_context context,pkinit_req_context req,krb5_principal client)44 use_content_info(krb5_context context, pkinit_req_context req,
45 krb5_principal client)
46 {
47 if (req->rfc6112_kdc)
48 return 0;
49 if (krb5_principal_compare_any_realm(context, client,
50 krb5_anonymous_principal()))
51 return 1;
52 return 0;
53 }
54
55 static krb5_error_code
56 pkinit_as_req_create(krb5_context context, pkinit_context plgctx,
57 pkinit_req_context reqctx, krb5_timestamp ctsec,
58 krb5_int32 cusec, krb5_ui_4 nonce, const krb5_data *cksum,
59 const krb5_pachecksum2 *cksum2, krb5_principal client,
60 krb5_principal server, krb5_data **as_req);
61
62 static krb5_error_code
63 pkinit_as_rep_parse(krb5_context context, pkinit_context plgctx,
64 pkinit_req_context reqctx, krb5_preauthtype pa_type,
65 krb5_kdc_req *request, const krb5_data *as_rep,
66 krb5_keyblock *key_block, krb5_enctype etype, krb5_data *);
67
68 static void pkinit_client_plugin_fini(krb5_context context,
69 krb5_clpreauth_moddata moddata);
70
71 static krb5_error_code
pa_pkinit_gen_req(krb5_context context,pkinit_context plgctx,pkinit_req_context reqctx,krb5_clpreauth_callbacks cb,krb5_clpreauth_rock rock,krb5_kdc_req * request,krb5_preauthtype pa_type,krb5_pa_data *** out_padata,krb5_prompter_fct prompter,void * prompter_data,krb5_get_init_creds_opt * gic_opt)72 pa_pkinit_gen_req(krb5_context context,
73 pkinit_context plgctx,
74 pkinit_req_context reqctx,
75 krb5_clpreauth_callbacks cb,
76 krb5_clpreauth_rock rock,
77 krb5_kdc_req * request,
78 krb5_preauthtype pa_type,
79 krb5_pa_data *** out_padata,
80 krb5_prompter_fct prompter,
81 void *prompter_data,
82 krb5_get_init_creds_opt *gic_opt)
83 {
84
85 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
86 krb5_data *out_data = NULL;
87 krb5_timestamp ctsec = 0;
88 krb5_int32 cusec = 0;
89 krb5_ui_4 nonce = 0;
90 krb5_data cksum = empty_data();
91 krb5_pachecksum2 *cksum2 = NULL;
92 krb5_data *der_req = NULL;
93 krb5_pa_data **return_pa_data = NULL;
94
95 memset(&cksum, 0, sizeof(cksum));
96 reqctx->pa_type = pa_type;
97
98 pkiDebug("kdc_options = 0x%x till = %d\n",
99 request->kdc_options, request->till);
100 /* If we don't have a client, we're done */
101 if (request->client == NULL) {
102 pkiDebug("No request->client; aborting PKINIT\n");
103 return KRB5KDC_ERR_PREAUTH_FAILED;
104 }
105
106 retval = pkinit_get_kdc_cert(context, plgctx->cryptoctx, reqctx->cryptoctx,
107 reqctx->idctx, request->server);
108 if (retval) {
109 pkiDebug("pkinit_get_kdc_cert returned %d\n", retval);
110 goto cleanup;
111 }
112
113 /* checksum of the encoded KDC-REQ-BODY */
114 retval = k5int_encode_krb5_kdc_req_body(request, &der_req);
115 if (retval) {
116 pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval);
117 goto cleanup;
118 }
119
120 retval = crypto_generate_checksums(context, der_req, &cksum, &cksum2);
121 if (retval)
122 goto cleanup;
123 TRACE_PKINIT_CLIENT_REQ_CHECKSUMS(context, &cksum, cksum2);
124
125 retval = cb->get_preauth_time(context, rock, TRUE, &ctsec, &cusec);
126 if (retval)
127 goto cleanup;
128
129 /* XXX PKINIT RFC says that nonce in PKAuthenticator doesn't have be the
130 * same as in the AS_REQ. However, if we pick a different nonce, then we
131 * need to remember that info when AS_REP is returned. I'm choosing to
132 * reuse the AS_REQ nonce.
133 */
134 nonce = request->nonce;
135
136 retval = pkinit_as_req_create(context, plgctx, reqctx, ctsec, cusec,
137 nonce, &cksum, cksum2, request->client,
138 request->server, &out_data);
139 if (retval) {
140 pkiDebug("error %d on pkinit_as_req_create; aborting PKINIT\n",
141 (int) retval);
142 goto cleanup;
143 }
144
145 return_pa_data = k5calloc(2, sizeof(*return_pa_data), &retval);
146 if (return_pa_data == NULL)
147 goto cleanup;
148
149 return_pa_data[0] = k5alloc(sizeof(*return_pa_data[0]), &retval);
150 if (return_pa_data[0] == NULL)
151 goto cleanup;
152
153 return_pa_data[0]->magic = KV5M_PA_DATA;
154
155 return_pa_data[0]->pa_type = pa_type;
156 return_pa_data[0]->length = out_data->length;
157 return_pa_data[0]->contents = (krb5_octet *) out_data->data;
158 *out_data = empty_data();
159
160 *out_padata = return_pa_data;
161 return_pa_data = NULL;
162 cb->disable_fallback(context, rock);
163
164 cleanup:
165 krb5_free_data(context, der_req);
166 krb5_free_data_contents(context, &cksum);
167 free_pachecksum2(context, &cksum2);
168 krb5_free_data(context, out_data);
169 krb5_free_pa_data(context, return_pa_data);
170 return retval;
171 }
172
173 static krb5_error_code
pkinit_as_req_create(krb5_context context,pkinit_context plgctx,pkinit_req_context reqctx,krb5_timestamp ctsec,krb5_int32 cusec,krb5_ui_4 nonce,const krb5_data * cksum,const krb5_pachecksum2 * cksum2,krb5_principal client,krb5_principal server,krb5_data ** as_req)174 pkinit_as_req_create(krb5_context context, pkinit_context plgctx,
175 pkinit_req_context reqctx, krb5_timestamp ctsec,
176 krb5_int32 cusec, krb5_ui_4 nonce, const krb5_data *cksum,
177 const krb5_pachecksum2 *cksum2, krb5_principal client,
178 krb5_principal server, krb5_data **as_req)
179 {
180 krb5_error_code retval = ENOMEM;
181 krb5_data spki = empty_data(), *coded_auth_pack = NULL;
182 krb5_auth_pack auth_pack;
183 krb5_pa_pk_as_req *req = NULL;
184 krb5_algorithm_identifier **cmstypes = NULL;
185
186 pkiDebug("pkinit_as_req_create pa_type = %d\n", reqctx->pa_type);
187
188 /* Create the authpack */
189 memset(&auth_pack, 0, sizeof(auth_pack));
190 auth_pack.pkAuthenticator.ctime = ctsec;
191 auth_pack.pkAuthenticator.cusec = cusec;
192 auth_pack.pkAuthenticator.nonce = nonce;
193 auth_pack.pkAuthenticator.paChecksum = *cksum;
194 if (!reqctx->opts->disable_freshness)
195 auth_pack.pkAuthenticator.freshnessToken = reqctx->freshness_token;
196 auth_pack.pkAuthenticator.paChecksum2 = (krb5_pachecksum2 *)cksum2;
197 auth_pack.clientDHNonce.length = 0;
198 auth_pack.supportedKDFs = (krb5_data **)supported_kdf_alg_ids;
199
200 /* add List of CMS algorithms */
201 retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx,
202 reqctx->cryptoctx,
203 reqctx->idctx, &cmstypes);
204 auth_pack.supportedCMSTypes = cmstypes;
205 if (retval)
206 goto cleanup;
207
208 TRACE_PKINIT_CLIENT_REQ_DH(context);
209
210 /* create client-side DH keys */
211 retval = client_create_dh(context, plgctx->cryptoctx, reqctx->cryptoctx,
212 reqctx->idctx, reqctx->opts->dh_size, &spki);
213 auth_pack.clientPublicValue = spki;
214 if (retval != 0) {
215 pkiDebug("failed to create dh parameters\n");
216 goto cleanup;
217 }
218
219 retval = k5int_encode_krb5_auth_pack(&auth_pack, &coded_auth_pack);
220 if (retval) {
221 pkiDebug("failed to encode the AuthPack %d\n", retval);
222 goto cleanup;
223 }
224 #ifdef DEBUG_ASN1
225 print_buffer_bin((unsigned char *)coded_auth_pack->data,
226 coded_auth_pack->length,
227 "/tmp/client_auth_pack");
228 #endif
229
230 /* create PKCS7 object from authpack */
231 init_krb5_pa_pk_as_req(&req);
232 if (req == NULL) {
233 retval = ENOMEM;
234 goto cleanup;
235 }
236 if (use_content_info(context, reqctx, client)) {
237 retval = cms_contentinfo_create(context, plgctx->cryptoctx,
238 reqctx->cryptoctx, reqctx->idctx,
239 CMS_SIGN_CLIENT,
240 (unsigned char *)
241 coded_auth_pack->data,
242 coded_auth_pack->length,
243 (unsigned char **)
244 &req->signedAuthPack.data,
245 &req->signedAuthPack.length);
246 } else {
247 retval = cms_signeddata_create(context, plgctx->cryptoctx,
248 reqctx->cryptoctx, reqctx->idctx,
249 CMS_SIGN_CLIENT,
250 (unsigned char *)
251 coded_auth_pack->data,
252 coded_auth_pack->length,
253 (unsigned char **)
254 &req->signedAuthPack.data,
255 &req->signedAuthPack.length);
256 }
257
258 #ifdef DEBUG_ASN1
259 print_buffer_bin((unsigned char *)req->signedAuthPack.data,
260 req->signedAuthPack.length,
261 "/tmp/client_signed_data");
262 #endif
263
264 krb5_free_data(context, coded_auth_pack);
265 if (retval) {
266 pkiDebug("failed to create pkcs7 signed data\n");
267 goto cleanup;
268 }
269
270 /* create a list of trusted CAs */
271 retval = create_krb5_trustedCertifiers(context, plgctx->cryptoctx,
272 reqctx->cryptoctx, reqctx->idctx,
273 &req->trustedCertifiers);
274 if (retval)
275 goto cleanup;
276 retval = create_issuerAndSerial(context, plgctx->cryptoctx,
277 reqctx->cryptoctx, reqctx->idctx,
278 (unsigned char **)&req->kdcPkId.data,
279 &req->kdcPkId.length);
280 if (retval)
281 goto cleanup;
282
283 /* Encode the as-req */
284 retval = k5int_encode_krb5_pa_pk_as_req(req, as_req);
285
286 #ifdef DEBUG_ASN1
287 if (!retval)
288 print_buffer_bin((unsigned char *)(*as_req)->data, (*as_req)->length,
289 "/tmp/client_as_req");
290 #endif
291
292 cleanup:
293 free_krb5_algorithm_identifiers(&cmstypes);
294 free_krb5_pa_pk_as_req(&req);
295 krb5_free_data_contents(context, &spki);
296
297 pkiDebug("pkinit_as_req_create retval=%d\n", (int) retval);
298
299 return retval;
300 }
301
302 static krb5_error_code
pa_pkinit_parse_rep(krb5_context context,pkinit_context plgctx,pkinit_req_context reqctx,krb5_kdc_req * request,krb5_pa_data * in_padata,krb5_enctype etype,krb5_keyblock * as_key,krb5_data * encoded_request)303 pa_pkinit_parse_rep(krb5_context context,
304 pkinit_context plgctx,
305 pkinit_req_context reqctx,
306 krb5_kdc_req * request,
307 krb5_pa_data * in_padata,
308 krb5_enctype etype,
309 krb5_keyblock * as_key,
310 krb5_data *encoded_request)
311 {
312 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
313 krb5_data asRep = { 0, 0, NULL};
314
315 /*
316 * One way or the other - success or failure - no other PA systems can
317 * work if the server sent us a PKINIT reply, since only we know how to
318 * decrypt the key.
319 */
320 if ((in_padata == NULL) || (in_padata->length == 0)) {
321 pkiDebug("pa_pkinit_parse_rep: no in_padata\n");
322 return KRB5KDC_ERR_PREAUTH_FAILED;
323 }
324
325 asRep.data = (char *) in_padata->contents;
326 asRep.length = in_padata->length;
327
328 retval =
329 pkinit_as_rep_parse(context, plgctx, reqctx, in_padata->pa_type,
330 request, &asRep, as_key, etype, encoded_request);
331 if (retval) {
332 pkiDebug("pkinit_as_rep_parse returned %d (%s)\n",
333 retval, error_message(retval));
334 goto cleanup;
335 }
336
337 retval = 0;
338
339 cleanup:
340
341 return retval;
342 }
343
344 static krb5_error_code
verify_kdc_san(krb5_context context,pkinit_context plgctx,pkinit_req_context reqctx,krb5_principal kdcprinc,int * valid_san,int * need_eku_checking)345 verify_kdc_san(krb5_context context,
346 pkinit_context plgctx,
347 pkinit_req_context reqctx,
348 krb5_principal kdcprinc,
349 int *valid_san,
350 int *need_eku_checking)
351 {
352 krb5_error_code retval;
353 char **certhosts = NULL, **cfghosts = NULL, **hostptr;
354 krb5_principal *princs = NULL;
355 unsigned char ***get_dns;
356 size_t i, j;
357
358 *valid_san = 0;
359 *need_eku_checking = 1;
360
361 retval = pkinit_libdefault_strings(context,
362 krb5_princ_realm(context, kdcprinc),
363 KRB5_CONF_PKINIT_KDC_HOSTNAME,
364 &cfghosts);
365 if (retval || cfghosts == NULL) {
366 pkiDebug("%s: No pkinit_kdc_hostname values found in config file\n",
367 __FUNCTION__);
368 get_dns = NULL;
369 } else {
370 pkiDebug("%s: pkinit_kdc_hostname values found in config file\n",
371 __FUNCTION__);
372 for (hostptr = cfghosts; *hostptr != NULL; hostptr++)
373 TRACE_PKINIT_CLIENT_SAN_CONFIG_DNSNAME(context, *hostptr);
374 get_dns = (unsigned char ***)&certhosts;
375 }
376
377 retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,
378 reqctx->cryptoctx, reqctx->idctx,
379 &princs, NULL, get_dns);
380 if (retval) {
381 pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
382 TRACE_PKINIT_CLIENT_SAN_ERR(context);
383 retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
384 goto out;
385 }
386 for (i = 0; princs != NULL && princs[i] != NULL; i++)
387 TRACE_PKINIT_CLIENT_SAN_KDCCERT_PRINC(context, princs[i]);
388 if (certhosts != NULL) {
389 for (hostptr = certhosts; *hostptr != NULL; hostptr++)
390 TRACE_PKINIT_CLIENT_SAN_KDCCERT_DNSNAME(context, *hostptr);
391 }
392
393 pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);
394 for (i = 0; princs != NULL && princs[i] != NULL; i++) {
395 if (krb5_principal_compare(context, princs[i], kdcprinc)) {
396 TRACE_PKINIT_CLIENT_SAN_MATCH_PRINC(context, kdcprinc);
397 pkiDebug("%s: pkinit san match found\n", __FUNCTION__);
398 *valid_san = 1;
399 *need_eku_checking = 0;
400 retval = 0;
401 goto out;
402 }
403 }
404 pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);
405
406 if (certhosts == NULL) {
407 pkiDebug("%s: no certhosts (or we wouldn't accept them anyway)\n",
408 __FUNCTION__);
409 retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
410 goto out;
411 }
412
413 for (i = 0; certhosts[i] != NULL; i++) {
414 for (j = 0; cfghosts != NULL && cfghosts[j] != NULL; j++) {
415 pkiDebug("%s: comparing cert name '%s' with config name '%s'\n",
416 __FUNCTION__, certhosts[i], cfghosts[j]);
417 if (strcasecmp(certhosts[i], cfghosts[j]) == 0) {
418 TRACE_PKINIT_CLIENT_SAN_MATCH_DNSNAME(context, certhosts[i]);
419 pkiDebug("%s: we have a dnsName match\n", __FUNCTION__);
420 *valid_san = 1;
421 retval = 0;
422 goto out;
423 }
424 }
425 }
426 TRACE_PKINIT_CLIENT_SAN_MATCH_NONE(context);
427 pkiDebug("%s: no dnsName san match found\n", __FUNCTION__);
428
429 /* We found no match */
430 retval = 0;
431
432 out:
433 if (princs != NULL) {
434 for (i = 0; princs[i] != NULL; i++)
435 krb5_free_principal(context, princs[i]);
436 free(princs);
437 }
438 if (certhosts != NULL) {
439 for (i = 0; certhosts[i] != NULL; i++)
440 free(certhosts[i]);
441 free(certhosts);
442 }
443 if (cfghosts != NULL)
444 profile_free_list(cfghosts);
445
446 pkiDebug("%s: returning retval %d, valid_san %d, need_eku_checking %d\n",
447 __FUNCTION__, retval, *valid_san, *need_eku_checking);
448 return retval;
449 }
450
451 static krb5_error_code
verify_kdc_eku(krb5_context context,pkinit_context plgctx,pkinit_req_context reqctx,int * eku_accepted)452 verify_kdc_eku(krb5_context context,
453 pkinit_context plgctx,
454 pkinit_req_context reqctx,
455 int *eku_accepted)
456 {
457 krb5_error_code retval;
458
459 *eku_accepted = 0;
460
461 if (reqctx->opts->require_eku == 0) {
462 TRACE_PKINIT_CLIENT_EKU_SKIP(context);
463 pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
464 *eku_accepted = 1;
465 retval = 0;
466 goto out;
467 }
468 retval = crypto_check_cert_eku(context, plgctx->cryptoctx,
469 reqctx->cryptoctx, reqctx->idctx,
470 1, /* kdc cert */
471 reqctx->opts->accept_secondary_eku,
472 eku_accepted);
473 if (retval) {
474 pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
475 __FUNCTION__, retval, error_message(retval));
476 goto out;
477 }
478
479 out:
480 if (*eku_accepted)
481 TRACE_PKINIT_CLIENT_EKU_ACCEPT(context);
482 else
483 TRACE_PKINIT_CLIENT_EKU_REJECT(context);
484 pkiDebug("%s: returning retval %d, eku_accepted %d\n",
485 __FUNCTION__, retval, *eku_accepted);
486 return retval;
487 }
488
489 /*
490 * Parse PA-PK-AS-REP message. Optionally evaluates the message's
491 * certificate chain.
492 * Optionally returns various components.
493 */
494 static krb5_error_code
pkinit_as_rep_parse(krb5_context context,pkinit_context plgctx,pkinit_req_context reqctx,krb5_preauthtype pa_type,krb5_kdc_req * request,const krb5_data * as_rep,krb5_keyblock * key_block,krb5_enctype etype,krb5_data * encoded_request)495 pkinit_as_rep_parse(krb5_context context,
496 pkinit_context plgctx,
497 pkinit_req_context reqctx,
498 krb5_preauthtype pa_type,
499 krb5_kdc_req *request,
500 const krb5_data *as_rep,
501 krb5_keyblock *key_block,
502 krb5_enctype etype,
503 krb5_data *encoded_request)
504 {
505 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
506 krb5_principal kdc_princ = NULL;
507 krb5_pa_pk_as_rep *kdc_reply = NULL;
508 krb5_kdc_dh_key_info *kdc_dh = NULL;
509 krb5_reply_key_pack *key_pack = NULL;
510 krb5_data dh_data = { 0, 0, NULL };
511 unsigned char *client_key = NULL, *kdc_hostname = NULL;
512 unsigned int client_key_len = 0;
513 krb5_checksum cksum = {0, 0, 0, NULL};
514 krb5_data k5data;
515 krb5_data secret;
516 int valid_san = 0;
517 int valid_eku = 0;
518 int need_eku_checking = 1;
519
520 assert((as_rep != NULL) && (key_block != NULL));
521
522 #ifdef DEBUG_ASN1
523 print_buffer_bin((unsigned char *)as_rep->data, as_rep->length,
524 "/tmp/client_as_rep");
525 #endif
526
527 if ((retval = k5int_decode_krb5_pa_pk_as_rep(as_rep, &kdc_reply))) {
528 pkiDebug("decode_krb5_as_rep failed %d\n", retval);
529 return retval;
530 }
531
532 if (kdc_reply->choice != choice_pa_pk_as_rep_dhInfo) {
533 pkiDebug("unknown as_rep type %d\n", kdc_reply->choice);
534 retval = KRB5KDC_ERR_PREAUTH_FAILED;
535 goto cleanup;
536 }
537
538 #ifdef DEBUG_ASN1
539 print_buffer_bin(kdc_reply->u.dh_Info.dhSignedData.data,
540 kdc_reply->u.dh_Info.dhSignedData.length,
541 "/tmp/client_kdc_signeddata");
542 #endif
543 retval = cms_signeddata_verify(context, plgctx->cryptoctx,
544 reqctx->cryptoctx, reqctx->idctx,
545 CMS_SIGN_SERVER,
546 reqctx->opts->require_crl_checking,
547 (unsigned char *)
548 kdc_reply->u.dh_Info.dhSignedData.data,
549 kdc_reply->u.dh_Info.dhSignedData.length,
550 (unsigned char **)&dh_data.data,
551 &dh_data.length,
552 NULL, NULL, NULL);
553 if (retval) {
554 pkiDebug("failed to verify pkcs7 signed data\n");
555 TRACE_PKINIT_CLIENT_REP_DH_FAIL(context);
556 goto cleanup;
557 }
558 TRACE_PKINIT_CLIENT_REP_DH(context);
559
560 retval = krb5_build_principal_ext(context, &kdc_princ,
561 request->server->realm.length,
562 request->server->realm.data,
563 strlen(KRB5_TGS_NAME), KRB5_TGS_NAME,
564 request->server->realm.length,
565 request->server->realm.data,
566 0);
567 if (retval)
568 goto cleanup;
569 retval = verify_kdc_san(context, plgctx, reqctx, kdc_princ,
570 &valid_san, &need_eku_checking);
571 if (retval)
572 goto cleanup;
573 if (!valid_san) {
574 pkiDebug("%s: did not find an acceptable SAN in KDC certificate\n",
575 __FUNCTION__);
576 retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
577 goto cleanup;
578 }
579
580 if (need_eku_checking) {
581 retval = verify_kdc_eku(context, plgctx, reqctx,
582 &valid_eku);
583 if (retval)
584 goto cleanup;
585 if (!valid_eku) {
586 pkiDebug("%s: did not find an acceptable EKU in KDC certificate\n",
587 __FUNCTION__);
588 retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
589 goto cleanup;
590 }
591 } else
592 pkiDebug("%s: skipping EKU check\n", __FUNCTION__);
593
594 OCTETDATA_TO_KRB5DATA(&dh_data, &k5data);
595
596 #ifdef DEBUG_ASN1
597 print_buffer_bin(dh_data.data, dh_data.length, "/tmp/client_dh_key");
598 #endif
599 retval = k5int_decode_krb5_kdc_dh_key_info(&k5data, &kdc_dh);
600 if (retval) {
601 pkiDebug("failed to decode kdc_dh_key_info\n");
602 goto cleanup;
603 }
604
605 /* client after KDC reply */
606 retval = client_process_dh(context, plgctx->cryptoctx, reqctx->cryptoctx,
607 reqctx->idctx,
608 (unsigned char *)kdc_dh->subjectPublicKey.data,
609 kdc_dh->subjectPublicKey.length, &client_key,
610 &client_key_len);
611 if (retval) {
612 pkiDebug("failed to process dh params\n");
613 goto cleanup;
614 }
615
616 secret = make_data(client_key, client_key_len);
617 retval = pkinit_kdf(context, &secret, kdc_reply->u.dh_Info.kdfID,
618 request->client, request->server, etype,
619 encoded_request, as_rep, key_block);
620 if (retval) {
621 pkiDebug("pkinit_kdf failed: %s\n", error_message(retval));
622 goto cleanup;
623 }
624
625 retval = 0;
626
627 cleanup:
628 free(dh_data.data);
629 krb5_free_principal(context, kdc_princ);
630 free(client_key);
631 free_krb5_kdc_dh_key_info(&kdc_dh);
632 free_krb5_pa_pk_as_rep(&kdc_reply);
633
634 if (key_pack != NULL) {
635 free_krb5_reply_key_pack(&key_pack);
636 free(cksum.contents);
637 }
638
639 free(kdc_hostname);
640
641 pkiDebug("pkinit_as_rep_parse returning %d (%s)\n",
642 retval, error_message(retval));
643 return retval;
644 }
645
646 static void
pkinit_client_profile(krb5_context context,pkinit_context plgctx,pkinit_req_context reqctx,krb5_clpreauth_callbacks cb,krb5_clpreauth_rock rock,const krb5_data * realm)647 pkinit_client_profile(krb5_context context,
648 pkinit_context plgctx,
649 pkinit_req_context reqctx,
650 krb5_clpreauth_callbacks cb,
651 krb5_clpreauth_rock rock,
652 const krb5_data *realm)
653 {
654 const char *configured_identity;
655 char *eku_string = NULL, *minbits = NULL;
656
657 pkiDebug("pkinit_client_profile %p %p %p %p\n",
658 context, plgctx, reqctx, realm);
659
660 pkinit_libdefault_boolean(context, realm,
661 KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING,
662 reqctx->opts->require_crl_checking,
663 &reqctx->opts->require_crl_checking);
664 pkinit_libdefault_string(context, realm, KRB5_CONF_PKINIT_DH_MIN_BITS,
665 &minbits);
666 reqctx->opts->dh_size = parse_dh_min_bits(context, minbits);
667 free(minbits);
668 pkinit_libdefault_string(context, realm,
669 KRB5_CONF_PKINIT_EKU_CHECKING,
670 &eku_string);
671 if (eku_string != NULL) {
672 if (strcasecmp(eku_string, "kpKDC") == 0) {
673 reqctx->opts->require_eku = 1;
674 reqctx->opts->accept_secondary_eku = 0;
675 } else if (strcasecmp(eku_string, "kpServerAuth") == 0) {
676 reqctx->opts->require_eku = 1;
677 reqctx->opts->accept_secondary_eku = 1;
678 } else if (strcasecmp(eku_string, "none") == 0) {
679 reqctx->opts->require_eku = 0;
680 reqctx->opts->accept_secondary_eku = 0;
681 } else {
682 pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
683 __FUNCTION__, eku_string);
684 }
685 free(eku_string);
686 }
687
688 /* Only process anchors here if they were not specified on command line */
689 if (reqctx->idopts->anchors == NULL)
690 pkinit_libdefault_strings(context, realm,
691 KRB5_CONF_PKINIT_ANCHORS,
692 &reqctx->idopts->anchors);
693 pkinit_libdefault_strings(context, realm,
694 KRB5_CONF_PKINIT_POOL,
695 &reqctx->idopts->intermediates);
696 pkinit_libdefault_strings(context, realm,
697 KRB5_CONF_PKINIT_REVOKE,
698 &reqctx->idopts->crls);
699 pkinit_libdefault_strings(context, realm,
700 KRB5_CONF_PKINIT_IDENTITIES,
701 &reqctx->idopts->identity_alt);
702 reqctx->do_identity_matching = TRUE;
703
704 /* If we had a primary identity in the stored configuration, pick it up. */
705 configured_identity = cb->get_cc_config(context, rock,
706 "X509_user_identity");
707 if (configured_identity != NULL) {
708 free(reqctx->idopts->identity);
709 reqctx->idopts->identity = strdup(configured_identity);
710 reqctx->do_identity_matching = FALSE;
711 }
712 }
713
714 /*
715 * Convert a PKCS11 token flags value to the subset that we're interested in
716 * passing along to our API callers.
717 */
718 static long long
pkinit_client_get_token_flags(unsigned long pkcs11_token_flags)719 pkinit_client_get_token_flags(unsigned long pkcs11_token_flags)
720 {
721 long long flags = 0;
722
723 if (pkcs11_token_flags & CKF_USER_PIN_COUNT_LOW)
724 flags |= KRB5_RESPONDER_PKINIT_FLAGS_TOKEN_USER_PIN_COUNT_LOW;
725 if (pkcs11_token_flags & CKF_USER_PIN_FINAL_TRY)
726 flags |= KRB5_RESPONDER_PKINIT_FLAGS_TOKEN_USER_PIN_FINAL_TRY;
727 if (pkcs11_token_flags & CKF_USER_PIN_LOCKED)
728 flags |= KRB5_RESPONDER_PKINIT_FLAGS_TOKEN_USER_PIN_LOCKED;
729 return flags;
730 }
731
732 /*
733 * Phase one of loading client identity information - call
734 * identity_initialize() to load any identities which we can without requiring
735 * help from the calling user, and use their names of those which we can't load
736 * to construct the challenge for the responder callback.
737 */
738 static krb5_error_code
pkinit_client_prep_questions(krb5_context context,krb5_clpreauth_moddata moddata,krb5_clpreauth_modreq modreq,krb5_get_init_creds_opt * opt,krb5_clpreauth_callbacks cb,krb5_clpreauth_rock rock,krb5_kdc_req * request,krb5_data * encoded_request_body,krb5_data * encoded_previous_request,krb5_pa_data * pa_data)739 pkinit_client_prep_questions(krb5_context context,
740 krb5_clpreauth_moddata moddata,
741 krb5_clpreauth_modreq modreq,
742 krb5_get_init_creds_opt *opt,
743 krb5_clpreauth_callbacks cb,
744 krb5_clpreauth_rock rock,
745 krb5_kdc_req *request,
746 krb5_data *encoded_request_body,
747 krb5_data *encoded_previous_request,
748 krb5_pa_data *pa_data)
749 {
750 krb5_error_code retval;
751 pkinit_context plgctx = (pkinit_context)moddata;
752 pkinit_req_context reqctx = (pkinit_req_context)modreq;
753 size_t i, n;
754 const pkinit_deferred_id *deferred_ids;
755 const char *identity;
756 unsigned long ck_flags;
757 char *encoded;
758 k5_json_object jval = NULL;
759 k5_json_number jflag = NULL;
760
761 /* Don't ask questions for the informational padata items or when the
762 * ticket is issued. */
763 if (pa_data->pa_type != KRB5_PADATA_PK_AS_REQ)
764 return 0;
765
766 if (!reqctx->identity_initialized) {
767 pkinit_client_profile(context, plgctx, reqctx, cb, rock,
768 &request->server->realm);
769 retval = pkinit_identity_initialize(context, plgctx->cryptoctx,
770 reqctx->cryptoctx, reqctx->idopts,
771 reqctx->idctx, cb, rock,
772 request->client);
773 if (retval != 0) {
774 TRACE_PKINIT_CLIENT_NO_IDENTITY(context);
775 pkiDebug("pkinit_identity_initialize returned %d (%s)\n",
776 retval, error_message(retval));
777 }
778
779 reqctx->identity_initialized = TRUE;
780 if (retval != 0) {
781 pkiDebug("%s: not asking responder question\n", __FUNCTION__);
782 retval = 0;
783 goto cleanup;
784 }
785 }
786
787 deferred_ids = crypto_get_deferred_ids(context, reqctx->idctx);
788 for (i = 0; deferred_ids != NULL && deferred_ids[i] != NULL; i++)
789 continue;
790 n = i;
791
792 /* Make sure we don't just return an empty challenge. */
793 if (n == 0) {
794 pkiDebug("%s: no questions to ask\n", __FUNCTION__);
795 retval = 0;
796 goto cleanup;
797 }
798
799 /* Create the top-level object. */
800 retval = k5_json_object_create(&jval);
801 if (retval != 0)
802 goto cleanup;
803
804 for (i = 0; i < n; i++) {
805 /* Add an entry to the top-level object for the identity. */
806 identity = deferred_ids[i]->identity;
807 ck_flags = deferred_ids[i]->ck_flags;
808 /* Calculate the flags value for the bits that that we care about. */
809 retval = k5_json_number_create(pkinit_client_get_token_flags(ck_flags),
810 &jflag);
811 if (retval != 0)
812 goto cleanup;
813 retval = k5_json_object_set(jval, identity, jflag);
814 if (retval != 0)
815 goto cleanup;
816 k5_json_release(jflag);
817 jflag = NULL;
818 }
819
820 /* Encode and done. */
821 retval = k5_json_encode(jval, &encoded);
822 if (retval == 0) {
823 cb->ask_responder_question(context, rock,
824 KRB5_RESPONDER_QUESTION_PKINIT,
825 encoded);
826 pkiDebug("%s: asking question '%s'\n", __FUNCTION__, encoded);
827 free(encoded);
828 }
829
830 cleanup:
831 k5_json_release(jval);
832 k5_json_release(jflag);
833
834 pkiDebug("%s returning %d\n", __FUNCTION__, retval);
835
836 return retval;
837 }
838
839 /*
840 * Parse data supplied by the application's responder callback, saving off any
841 * PINs and passwords for identities which we noted needed them.
842 */
843 struct save_one_password_data {
844 krb5_context context;
845 krb5_clpreauth_modreq modreq;
846 const char *caller;
847 };
848
849 static void
save_one_password(void * arg,const char * key,k5_json_value val)850 save_one_password(void *arg, const char *key, k5_json_value val)
851 {
852 struct save_one_password_data *data = arg;
853 pkinit_req_context reqctx = (pkinit_req_context)data->modreq;
854 const char *password;
855
856 if (k5_json_get_tid(val) == K5_JSON_TID_STRING) {
857 password = k5_json_string_utf8(val);
858 pkiDebug("%s: \"%s\": %p\n", data->caller, key, password);
859 crypto_set_deferred_id(data->context, reqctx->idctx, key, password);
860 }
861 }
862
863 static krb5_error_code
pkinit_client_parse_answers(krb5_context context,krb5_clpreauth_moddata moddata,krb5_clpreauth_modreq modreq,krb5_clpreauth_callbacks cb,krb5_clpreauth_rock rock)864 pkinit_client_parse_answers(krb5_context context,
865 krb5_clpreauth_moddata moddata,
866 krb5_clpreauth_modreq modreq,
867 krb5_clpreauth_callbacks cb,
868 krb5_clpreauth_rock rock)
869 {
870 krb5_error_code retval;
871 const char *encoded;
872 k5_json_value jval;
873 struct save_one_password_data data;
874
875 data.context = context;
876 data.modreq = modreq;
877 data.caller = __FUNCTION__;
878
879 encoded = cb->get_responder_answer(context, rock,
880 KRB5_RESPONDER_QUESTION_PKINIT);
881 if (encoded == NULL)
882 return 0;
883
884 pkiDebug("pkinit_client_parse_answers: %s\n", encoded);
885
886 retval = k5_json_decode(encoded, &jval);
887 if (retval != 0)
888 goto cleanup;
889
890 /* Expect that the top-level answer is an object. */
891 if (k5_json_get_tid(jval) != K5_JSON_TID_OBJECT) {
892 retval = EINVAL;
893 goto cleanup;
894 }
895
896 /* Store the passed-in per-identity passwords. */
897 k5_json_object_iterate(jval, &save_one_password, &data);
898 retval = 0;
899
900 cleanup:
901 if (jval != NULL)
902 k5_json_release(jval);
903 return retval;
904 }
905
906 static krb5_error_code
pkinit_client_process(krb5_context context,krb5_clpreauth_moddata moddata,krb5_clpreauth_modreq modreq,krb5_get_init_creds_opt * gic_opt,krb5_clpreauth_callbacks cb,krb5_clpreauth_rock rock,krb5_kdc_req * request,krb5_data * encoded_request_body,krb5_data * encoded_previous_request,krb5_pa_data * in_padata,krb5_prompter_fct prompter,void * prompter_data,krb5_pa_data *** out_padata)907 pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
908 krb5_clpreauth_modreq modreq,
909 krb5_get_init_creds_opt *gic_opt,
910 krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
911 krb5_kdc_req *request, krb5_data *encoded_request_body,
912 krb5_data *encoded_previous_request,
913 krb5_pa_data *in_padata,
914 krb5_prompter_fct prompter, void *prompter_data,
915 krb5_pa_data ***out_padata)
916 {
917 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
918 krb5_enctype enctype = -1;
919 int processing_request = 0;
920 pkinit_context plgctx = (pkinit_context)moddata;
921 pkinit_req_context reqctx = (pkinit_req_context)modreq;
922 krb5_keyblock as_key;
923 krb5_data d;
924
925 pkiDebug("pkinit_client_process %p %p %p %p\n",
926 context, plgctx, reqctx, request);
927
928
929 if (plgctx == NULL || reqctx == NULL)
930 return EINVAL;
931
932 switch ((int) in_padata->pa_type) {
933 case KRB5_PADATA_PKINIT_KX:
934 reqctx->rfc6112_kdc = 1;
935 return 0;
936 case KRB5_PADATA_AS_FRESHNESS:
937 TRACE_PKINIT_CLIENT_FRESHNESS_TOKEN(context);
938 krb5_free_data(context, reqctx->freshness_token);
939 reqctx->freshness_token = NULL;
940 d = make_data(in_padata->contents, in_padata->length);
941 return krb5_copy_data(context, &d, &reqctx->freshness_token);
942 case KRB5_PADATA_PK_AS_REQ:
943 pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
944 processing_request = 1;
945 break;
946
947 case KRB5_PADATA_PK_AS_REP:
948 pkiDebug("processing KRB5_PADATA_PK_AS_REP\n");
949 break;
950 default:
951 pkiDebug("unrecognized patype = %d for PKINIT\n",
952 in_padata->pa_type);
953 return EINVAL;
954 }
955
956 if (processing_request) {
957 if (reqctx->idopts->anchors == NULL) {
958 krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
959 _("No pkinit_anchors supplied"));
960 return KRB5_PREAUTH_FAILED;
961 }
962 /* Pull in PINs and passwords for identities which we deferred
963 * loading earlier. */
964 retval = pkinit_client_parse_answers(context, moddata, modreq,
965 cb, rock);
966 if (retval) {
967 if (retval == KRB5KRB_ERR_GENERIC)
968 pkiDebug("pkinit responder answers were invalid\n");
969 return retval;
970 }
971 if (!reqctx->identity_prompted) {
972 reqctx->identity_prompted = TRUE;
973 /*
974 * Load identities (again, potentially), prompting, if we can, for
975 * anything for which we didn't get an answer from the responder
976 * callback.
977 */
978 pkinit_identity_set_prompter(reqctx->idctx, prompter,
979 prompter_data);
980 retval = pkinit_identity_prompt(context, plgctx->cryptoctx,
981 reqctx->cryptoctx, reqctx->idopts,
982 reqctx->idctx, cb, rock,
983 reqctx->do_identity_matching,
984 request->client);
985 pkinit_identity_set_prompter(reqctx->idctx, NULL, NULL);
986 reqctx->identity_prompt_retval = retval;
987 if (retval) {
988 TRACE_PKINIT_CLIENT_NO_IDENTITY(context);
989 pkiDebug("pkinit_identity_prompt returned %d (%s)\n",
990 retval, error_message(retval));
991 return retval;
992 }
993 } else if (reqctx->identity_prompt_retval) {
994 retval = reqctx->identity_prompt_retval;
995 TRACE_PKINIT_CLIENT_NO_IDENTITY(context);
996 pkiDebug("pkinit_identity_prompt previously returned %d (%s)\n",
997 retval, error_message(retval));
998 return retval;
999 }
1000 retval = pa_pkinit_gen_req(context, plgctx, reqctx, cb, rock, request,
1001 in_padata->pa_type, out_padata, prompter,
1002 prompter_data, gic_opt);
1003 } else {
1004 /*
1005 * Get the enctype of the reply.
1006 */
1007 enctype = cb->get_etype(context, rock);
1008 retval = pa_pkinit_parse_rep(context, plgctx, reqctx, request,
1009 in_padata, enctype, &as_key,
1010 encoded_previous_request);
1011 if (retval == 0) {
1012 retval = cb->set_as_key(context, rock, &as_key);
1013 krb5_free_keyblock_contents(context, &as_key);
1014 }
1015 }
1016
1017 pkiDebug("pkinit_client_process: returning %d (%s)\n",
1018 retval, error_message(retval));
1019 return retval;
1020 }
1021
1022 static krb5_error_code
pkinit_client_tryagain(krb5_context context,krb5_clpreauth_moddata moddata,krb5_clpreauth_modreq modreq,krb5_get_init_creds_opt * gic_opt,krb5_clpreauth_callbacks cb,krb5_clpreauth_rock rock,krb5_kdc_req * request,krb5_data * encoded_request_body,krb5_data * encoded_previous_request,krb5_preauthtype pa_type,krb5_error * err_reply,krb5_pa_data ** err_padata,krb5_prompter_fct prompter,void * prompter_data,krb5_pa_data *** out_padata)1023 pkinit_client_tryagain(krb5_context context, krb5_clpreauth_moddata moddata,
1024 krb5_clpreauth_modreq modreq,
1025 krb5_get_init_creds_opt *gic_opt,
1026 krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
1027 krb5_kdc_req *request, krb5_data *encoded_request_body,
1028 krb5_data *encoded_previous_request,
1029 krb5_preauthtype pa_type, krb5_error *err_reply,
1030 krb5_pa_data **err_padata, krb5_prompter_fct prompter,
1031 void *prompter_data, krb5_pa_data ***out_padata)
1032 {
1033 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
1034 pkinit_context plgctx = (pkinit_context)moddata;
1035 pkinit_req_context reqctx = (pkinit_req_context)modreq;
1036 krb5_pa_data *pa;
1037 krb5_data scratch;
1038 krb5_external_principal_identifier **certifiers = NULL;
1039 krb5_algorithm_identifier **algId = NULL;
1040 int do_again = 0;
1041
1042 pkiDebug("pkinit_client_tryagain %p %p %p %p\n",
1043 context, plgctx, reqctx, request);
1044
1045 if (reqctx->pa_type != pa_type || err_padata == NULL)
1046 return retval;
1047
1048 for (; *err_padata != NULL && !do_again; err_padata++) {
1049 pa = *err_padata;
1050 PADATA_TO_KRB5DATA(pa, &scratch);
1051 switch (pa->pa_type) {
1052 case TD_TRUSTED_CERTIFIERS:
1053 case TD_INVALID_CERTIFICATES:
1054 retval = k5int_decode_krb5_td_trusted_certifiers(&scratch,
1055 &certifiers);
1056 if (retval) {
1057 pkiDebug("failed to decode sequence of trusted certifiers\n");
1058 goto cleanup;
1059 }
1060 retval = pkinit_process_td_trusted_certifiers(context,
1061 plgctx->cryptoctx,
1062 reqctx->cryptoctx,
1063 reqctx->idctx,
1064 certifiers,
1065 pa->pa_type);
1066 if (!retval)
1067 do_again = 1;
1068 break;
1069 case TD_DH_PARAMETERS:
1070 retval = k5int_decode_krb5_td_dh_parameters(&scratch, &algId);
1071 if (retval) {
1072 pkiDebug("failed to decode td_dh_parameters\n");
1073 goto cleanup;
1074 }
1075 retval = pkinit_process_td_dh_params(context, plgctx->cryptoctx,
1076 reqctx->cryptoctx,
1077 reqctx->idctx, algId,
1078 &reqctx->opts->dh_size);
1079 if (!retval)
1080 do_again = 1;
1081 break;
1082 default:
1083 break;
1084 }
1085 }
1086
1087 if (do_again) {
1088 TRACE_PKINIT_CLIENT_TRYAGAIN(context);
1089 retval = pa_pkinit_gen_req(context, plgctx, reqctx, cb, rock, request,
1090 pa_type, out_padata, prompter,
1091 prompter_data, gic_opt);
1092 if (retval)
1093 goto cleanup;
1094 }
1095
1096 retval = 0;
1097 cleanup:
1098 if (certifiers != NULL)
1099 free_krb5_external_principal_identifier(&certifiers);
1100
1101 if (algId != NULL)
1102 free_krb5_algorithm_identifiers(&algId);
1103
1104 pkiDebug("pkinit_client_tryagain: returning %d (%s)\n",
1105 retval, error_message(retval));
1106 return retval;
1107 }
1108
1109 static int
pkinit_client_get_flags(krb5_context kcontext,krb5_preauthtype patype)1110 pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype)
1111 {
1112 if (patype == KRB5_PADATA_PKINIT_KX || patype == KRB5_PADATA_AS_FRESHNESS)
1113 return PA_INFO;
1114 return PA_REAL;
1115 }
1116
1117 /*
1118 * We want to be notified about KRB5_PADATA_PKINIT_KX in addition to the actual
1119 * pkinit patypes because RFC 6112 requires anonymous KDCs to send it. We use
1120 * that to determine whether to use the broken MIT 1.9 behavior of sending
1121 * ContentInfo rather than SignedData or the RFC 6112 behavior
1122 */
1123 static krb5_preauthtype supported_client_pa_types[] = {
1124 KRB5_PADATA_PK_AS_REP,
1125 KRB5_PADATA_PK_AS_REQ,
1126 KRB5_PADATA_PKINIT_KX,
1127 KRB5_PADATA_AS_FRESHNESS,
1128 0
1129 };
1130
1131 static void
pkinit_client_req_init(krb5_context context,krb5_clpreauth_moddata moddata,krb5_clpreauth_modreq * modreq_out)1132 pkinit_client_req_init(krb5_context context,
1133 krb5_clpreauth_moddata moddata,
1134 krb5_clpreauth_modreq *modreq_out)
1135 {
1136 krb5_error_code retval = ENOMEM;
1137 pkinit_req_context reqctx = NULL;
1138 pkinit_context plgctx = (pkinit_context)moddata;
1139
1140 *modreq_out = NULL;
1141
1142 reqctx = malloc(sizeof(*reqctx));
1143 if (reqctx == NULL)
1144 return;
1145 memset(reqctx, 0, sizeof(*reqctx));
1146
1147 reqctx->magic = PKINIT_REQ_CTX_MAGIC;
1148 reqctx->cryptoctx = NULL;
1149 reqctx->opts = NULL;
1150 reqctx->idctx = NULL;
1151 reqctx->idopts = NULL;
1152 reqctx->freshness_token = NULL;
1153
1154 retval = pkinit_init_req_opts(&reqctx->opts);
1155 if (retval)
1156 goto cleanup;
1157
1158 reqctx->opts->require_eku = plgctx->opts->require_eku;
1159 reqctx->opts->accept_secondary_eku = plgctx->opts->accept_secondary_eku;
1160 reqctx->opts->allow_upn = plgctx->opts->allow_upn;
1161 reqctx->opts->require_crl_checking = plgctx->opts->require_crl_checking;
1162 reqctx->opts->disable_freshness = plgctx->opts->disable_freshness;
1163
1164 retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
1165 if (retval)
1166 goto cleanup;
1167
1168 retval = pkinit_init_identity_crypto(&reqctx->idctx);
1169 if (retval)
1170 goto cleanup;
1171
1172 retval = pkinit_dup_identity_opts(plgctx->idopts, &reqctx->idopts);
1173 if (retval)
1174 goto cleanup;
1175
1176 *modreq_out = (krb5_clpreauth_modreq)reqctx;
1177 pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
1178
1179 cleanup:
1180 if (retval) {
1181 if (reqctx->idctx != NULL)
1182 pkinit_fini_identity_crypto(reqctx->idctx);
1183 if (reqctx->cryptoctx != NULL)
1184 pkinit_fini_req_crypto(reqctx->cryptoctx);
1185 if (reqctx->opts != NULL)
1186 pkinit_fini_req_opts(reqctx->opts);
1187 if (reqctx->idopts != NULL)
1188 pkinit_fini_identity_opts(reqctx->idopts);
1189 free(reqctx);
1190 }
1191
1192 return;
1193 }
1194
1195 static void
pkinit_client_req_fini(krb5_context context,krb5_clpreauth_moddata moddata,krb5_clpreauth_modreq modreq)1196 pkinit_client_req_fini(krb5_context context, krb5_clpreauth_moddata moddata,
1197 krb5_clpreauth_modreq modreq)
1198 {
1199 pkinit_req_context reqctx = (pkinit_req_context)modreq;
1200
1201 pkiDebug("%s: received reqctx at %p\n", __FUNCTION__, reqctx);
1202 if (reqctx == NULL)
1203 return;
1204 if (reqctx->magic != PKINIT_REQ_CTX_MAGIC) {
1205 pkiDebug("%s: Bad magic value (%x) in req ctx\n",
1206 __FUNCTION__, reqctx->magic);
1207 return;
1208 }
1209 if (reqctx->opts != NULL)
1210 pkinit_fini_req_opts(reqctx->opts);
1211
1212 if (reqctx->cryptoctx != NULL)
1213 pkinit_fini_req_crypto(reqctx->cryptoctx);
1214
1215 if (reqctx->idctx != NULL)
1216 pkinit_fini_identity_crypto(reqctx->idctx);
1217
1218 if (reqctx->idopts != NULL)
1219 pkinit_fini_identity_opts(reqctx->idopts);
1220
1221 krb5_free_data(context, reqctx->freshness_token);
1222
1223 free(reqctx);
1224 return;
1225 }
1226
1227 static int
pkinit_client_plugin_init(krb5_context context,krb5_clpreauth_moddata * moddata_out)1228 pkinit_client_plugin_init(krb5_context context,
1229 krb5_clpreauth_moddata *moddata_out)
1230 {
1231 krb5_error_code retval = ENOMEM;
1232 pkinit_context ctx = NULL;
1233
1234 ctx = calloc(1, sizeof(*ctx));
1235 if (ctx == NULL)
1236 return ENOMEM;
1237 memset(ctx, 0, sizeof(*ctx));
1238 ctx->magic = PKINIT_CTX_MAGIC;
1239 ctx->opts = NULL;
1240 ctx->cryptoctx = NULL;
1241 ctx->idopts = NULL;
1242
1243 retval = pkinit_accessor_init();
1244 if (retval)
1245 goto errout;
1246
1247 retval = pkinit_init_plg_opts(&ctx->opts);
1248 if (retval)
1249 goto errout;
1250
1251 retval = pkinit_init_plg_crypto(context, &ctx->cryptoctx);
1252 if (retval)
1253 goto errout;
1254
1255 retval = pkinit_init_identity_opts(&ctx->idopts);
1256 if (retval)
1257 goto errout;
1258
1259 *moddata_out = (krb5_clpreauth_moddata)ctx;
1260
1261 pkiDebug("%s: returning plgctx at %p\n", __FUNCTION__, ctx);
1262
1263 errout:
1264 if (retval)
1265 pkinit_client_plugin_fini(context, (krb5_clpreauth_moddata)ctx);
1266
1267 return retval;
1268 }
1269
1270 static void
pkinit_client_plugin_fini(krb5_context context,krb5_clpreauth_moddata moddata)1271 pkinit_client_plugin_fini(krb5_context context, krb5_clpreauth_moddata moddata)
1272 {
1273 pkinit_context ctx = (pkinit_context)moddata;
1274
1275 if (ctx == NULL || ctx->magic != PKINIT_CTX_MAGIC) {
1276 pkiDebug("pkinit_lib_fini: got bad plgctx (%p)!\n", ctx);
1277 return;
1278 }
1279 pkiDebug("%s: got plgctx at %p\n", __FUNCTION__, ctx);
1280
1281 pkinit_fini_identity_opts(ctx->idopts);
1282 pkinit_fini_plg_crypto(ctx->cryptoctx);
1283 pkinit_fini_plg_opts(ctx->opts);
1284 free(ctx);
1285
1286 }
1287
1288 static krb5_error_code
add_string_to_array(krb5_context context,char *** array,const char * addition)1289 add_string_to_array(krb5_context context, char ***array, const char *addition)
1290 {
1291 char **a = *array;
1292 size_t len;
1293
1294 for (len = 0; a != NULL && a[len] != NULL; len++);
1295 a = realloc(a, (len + 2) * sizeof(char *));
1296 if (a == NULL)
1297 return ENOMEM;
1298 *array = a;
1299 a[len] = strdup(addition);
1300 if (a[len] == NULL)
1301 return ENOMEM;
1302 a[len + 1] = NULL;
1303 return 0;
1304 }
1305
1306 static krb5_error_code
handle_gic_opt(krb5_context context,pkinit_context plgctx,const char * attr,const char * value)1307 handle_gic_opt(krb5_context context,
1308 pkinit_context plgctx,
1309 const char *attr,
1310 const char *value)
1311 {
1312 krb5_error_code retval;
1313
1314 if (strcmp(attr, "X509_user_identity") == 0) {
1315 if (plgctx->idopts->identity != NULL) {
1316 krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
1317 "X509_user_identity can not be given twice\n");
1318 return KRB5_PREAUTH_FAILED;
1319 }
1320 plgctx->idopts->identity = strdup(value);
1321 if (plgctx->idopts->identity == NULL) {
1322 krb5_set_error_message(context, ENOMEM,
1323 "Could not duplicate X509_user_identity value\n");
1324 return ENOMEM;
1325 }
1326 } else if (strcmp(attr, "X509_anchors") == 0) {
1327 retval = add_string_to_array(context, &plgctx->idopts->anchors, value);
1328 if (retval)
1329 return retval;
1330 } else if (strcmp(attr, "disable_freshness") == 0) {
1331 if (strcmp(value, "yes") == 0)
1332 plgctx->opts->disable_freshness = 1;
1333 }
1334 return 0;
1335 }
1336
1337 static krb5_error_code
pkinit_client_gic_opt(krb5_context context,krb5_clpreauth_moddata moddata,krb5_get_init_creds_opt * gic_opt,const char * attr,const char * value)1338 pkinit_client_gic_opt(krb5_context context, krb5_clpreauth_moddata moddata,
1339 krb5_get_init_creds_opt *gic_opt,
1340 const char *attr,
1341 const char *value)
1342 {
1343 krb5_error_code retval;
1344 pkinit_context plgctx = (pkinit_context)moddata;
1345
1346 pkiDebug("(pkinit) received '%s' = '%s'\n", attr, value);
1347 retval = handle_gic_opt(context, plgctx, attr, value);
1348 if (retval)
1349 return retval;
1350
1351 return 0;
1352 }
1353
1354 krb5_error_code
1355 clpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
1356 krb5_plugin_vtable vtable);
1357
1358 krb5_error_code
clpreauth_pkinit_initvt(krb5_context context,int maj_ver,int min_ver,krb5_plugin_vtable vtable)1359 clpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
1360 krb5_plugin_vtable vtable)
1361 {
1362 krb5_clpreauth_vtable vt;
1363
1364 if (maj_ver != 1)
1365 return KRB5_PLUGIN_VER_NOTSUPP;
1366 vt = (krb5_clpreauth_vtable)vtable;
1367 vt->name = "pkinit";
1368 vt->pa_type_list = supported_client_pa_types;
1369 vt->init = pkinit_client_plugin_init;
1370 vt->fini = pkinit_client_plugin_fini;
1371 vt->flags = pkinit_client_get_flags;
1372 vt->request_init = pkinit_client_req_init;
1373 vt->prep_questions = pkinit_client_prep_questions;
1374 vt->request_fini = pkinit_client_req_fini;
1375 vt->process = pkinit_client_process;
1376 vt->tryagain = pkinit_client_tryagain;
1377 vt->gic_opts = pkinit_client_gic_opt;
1378 return 0;
1379 }
1380