Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/plugins/preauth/pkinit/pkinit_clnt.c
34923 views
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1381