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_srv.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 "krb5/certauth_plugin.h"
35
36
/* Aliases used by the built-in certauth modules */
37
struct certauth_req_opts {
38
krb5_kdcpreauth_callbacks cb;
39
krb5_kdcpreauth_rock rock;
40
pkinit_kdc_context plgctx;
41
pkinit_kdc_req_context reqctx;
42
};
43
44
typedef struct certauth_module_handle_st {
45
struct krb5_certauth_vtable_st vt;
46
krb5_certauth_moddata moddata;
47
} *certauth_handle;
48
49
struct krb5_kdcpreauth_moddata_st {
50
pkinit_kdc_context *realm_contexts;
51
certauth_handle *certauth_modules;
52
};
53
54
static krb5_error_code
55
pkinit_init_kdc_req_context(krb5_context, pkinit_kdc_req_context *blob);
56
57
static void
58
pkinit_fini_kdc_req_context(krb5_context context, void *blob);
59
60
static void
61
pkinit_server_plugin_fini_realm(krb5_context context,
62
pkinit_kdc_context plgctx);
63
64
static void
65
pkinit_server_plugin_fini(krb5_context context,
66
krb5_kdcpreauth_moddata moddata);
67
68
static pkinit_kdc_context
69
pkinit_find_realm_context(krb5_context context,
70
krb5_kdcpreauth_moddata moddata,
71
krb5_principal princ);
72
73
static void
74
free_realm_contexts(krb5_context context, pkinit_kdc_context *realm_contexts)
75
{
76
size_t i;
77
78
if (realm_contexts == NULL)
79
return;
80
for (i = 0; realm_contexts[i] != NULL; i++)
81
pkinit_server_plugin_fini_realm(context, realm_contexts[i]);
82
pkiDebug("%s: freeing context at %p\n", __FUNCTION__, realm_contexts);
83
free(realm_contexts);
84
}
85
86
static void
87
free_certauth_handles(krb5_context context, certauth_handle *list)
88
{
89
size_t i;
90
91
if (list == NULL)
92
return;
93
for (i = 0; list[i] != NULL; i++) {
94
if (list[i]->vt.fini != NULL)
95
list[i]->vt.fini(context, list[i]->moddata);
96
free(list[i]);
97
}
98
free(list);
99
}
100
101
static krb5_error_code
102
pkinit_create_edata(krb5_context context,
103
pkinit_plg_crypto_context plg_cryptoctx,
104
pkinit_req_crypto_context req_cryptoctx,
105
pkinit_identity_crypto_context id_cryptoctx,
106
pkinit_plg_opts *opts,
107
krb5_error_code err_code,
108
krb5_pa_data ***e_data_out)
109
{
110
krb5_error_code retval = KRB5KRB_ERR_GENERIC;
111
112
pkiDebug("pkinit_create_edata: creating edata for error %d (%s)\n",
113
err_code, error_message(err_code));
114
switch(err_code) {
115
case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
116
retval = pkinit_create_td_trusted_certifiers(context,
117
plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data_out);
118
break;
119
case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
120
retval = pkinit_create_td_dh_parameters(context, plg_cryptoctx,
121
req_cryptoctx, id_cryptoctx, opts, e_data_out);
122
break;
123
case KRB5KDC_ERR_INVALID_CERTIFICATE:
124
case KRB5KDC_ERR_REVOKED_CERTIFICATE:
125
retval = pkinit_create_td_invalid_certificate(context,
126
plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data_out);
127
break;
128
default:
129
pkiDebug("no edata needed for error %d (%s)\n",
130
err_code, error_message(err_code));
131
retval = 0;
132
goto cleanup;
133
}
134
135
cleanup:
136
137
return retval;
138
}
139
140
static void
141
pkinit_server_get_edata(krb5_context context,
142
krb5_kdc_req *request,
143
krb5_kdcpreauth_callbacks cb,
144
krb5_kdcpreauth_rock rock,
145
krb5_kdcpreauth_moddata moddata,
146
krb5_preauthtype pa_type,
147
krb5_kdcpreauth_edata_respond_fn respond,
148
void *arg)
149
{
150
krb5_error_code retval = 0;
151
pkinit_kdc_context plgctx = NULL;
152
153
pkiDebug("pkinit_server_get_edata: entered!\n");
154
155
156
/*
157
* If we don't have a realm context for the given realm,
158
* don't tell the client that we support pkinit!
159
*/
160
plgctx = pkinit_find_realm_context(context, moddata, request->server);
161
if (plgctx == NULL)
162
retval = EINVAL;
163
164
/* Send a freshness token if the client requested one. */
165
if (!retval)
166
cb->send_freshness_token(context, rock);
167
168
(*respond)(arg, retval, NULL);
169
}
170
171
static krb5_error_code
172
verify_client_san(krb5_context context,
173
pkinit_kdc_context plgctx,
174
pkinit_kdc_req_context reqctx,
175
krb5_kdcpreauth_callbacks cb,
176
krb5_kdcpreauth_rock rock,
177
krb5_const_principal client,
178
int *valid_san)
179
{
180
krb5_error_code retval;
181
krb5_principal *princs = NULL, upn;
182
krb5_boolean match;
183
char **upns = NULL;
184
size_t i;
185
#ifdef DEBUG_SAN_INFO
186
char *client_string = NULL, *san_string;
187
#endif
188
189
*valid_san = 0;
190
retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,
191
reqctx->cryptoctx, plgctx->idctx,
192
&princs,
193
plgctx->opts->allow_upn ? &upns : NULL,
194
NULL);
195
if (retval) {
196
pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
197
retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
198
goto out;
199
}
200
201
if (princs == NULL && upns == NULL) {
202
TRACE_PKINIT_SERVER_NO_SAN(context);
203
retval = ENOENT;
204
goto out;
205
}
206
207
#ifdef DEBUG_SAN_INFO
208
krb5_unparse_name(context, client, &client_string);
209
#endif
210
pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);
211
for (i = 0; princs != NULL && princs[i] != NULL; i++) {
212
#ifdef DEBUG_SAN_INFO
213
krb5_unparse_name(context, princs[i], &san_string);
214
pkiDebug("%s: Comparing client '%s' to pkinit san value '%s'\n",
215
__FUNCTION__, client_string, san_string);
216
krb5_free_unparsed_name(context, san_string);
217
#endif
218
if (cb->match_client(context, rock, princs[i])) {
219
TRACE_PKINIT_SERVER_MATCHING_SAN_FOUND(context);
220
*valid_san = 1;
221
retval = 0;
222
goto out;
223
}
224
}
225
pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);
226
/*
227
* XXX if cert has names but none match, should we
228
* be returning KRB5KDC_ERR_CLIENT_NAME_MISMATCH here?
229
*/
230
231
if (upns == NULL) {
232
pkiDebug("%s: no upn sans (or we wouldn't accept them anyway)\n",
233
__FUNCTION__);
234
retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
235
goto out;
236
}
237
238
pkiDebug("%s: Checking upn sans\n", __FUNCTION__);
239
for (i = 0; upns[i] != NULL; i++) {
240
#ifdef DEBUG_SAN_INFO
241
pkiDebug("%s: Comparing client '%s' to upn san value '%s'\n",
242
__FUNCTION__, client_string, upns[i]);
243
#endif
244
retval = krb5_parse_name_flags(context, upns[i],
245
KRB5_PRINCIPAL_PARSE_ENTERPRISE, &upn);
246
if (retval) {
247
TRACE_PKINIT_SERVER_UPN_PARSE_FAIL(context, upns[i], retval);
248
continue;
249
}
250
match = cb->match_client(context, rock, upn);
251
krb5_free_principal(context, upn);
252
if (match) {
253
TRACE_PKINIT_SERVER_MATCHING_UPN_FOUND(context);
254
*valid_san = 1;
255
retval = 0;
256
goto out;
257
}
258
}
259
pkiDebug("%s: no upn san match found\n", __FUNCTION__);
260
261
retval = 0;
262
out:
263
if (princs != NULL) {
264
for (i = 0; princs[i] != NULL; i++)
265
krb5_free_principal(context, princs[i]);
266
free(princs);
267
}
268
if (upns != NULL) {
269
for (i = 0; upns[i] != NULL; i++)
270
free(upns[i]);
271
free(upns);
272
}
273
#ifdef DEBUG_SAN_INFO
274
if (client_string != NULL)
275
krb5_free_unparsed_name(context, client_string);
276
#endif
277
pkiDebug("%s: returning retval %d, valid_san %d\n",
278
__FUNCTION__, retval, *valid_san);
279
return retval;
280
}
281
282
static krb5_error_code
283
verify_client_eku(krb5_context context,
284
pkinit_kdc_context plgctx,
285
pkinit_kdc_req_context reqctx,
286
int *eku_accepted)
287
{
288
krb5_error_code retval;
289
290
*eku_accepted = 0;
291
292
if (plgctx->opts->require_eku == 0) {
293
TRACE_PKINIT_SERVER_EKU_SKIP(context);
294
*eku_accepted = 1;
295
retval = 0;
296
goto out;
297
}
298
299
retval = crypto_check_cert_eku(context, plgctx->cryptoctx,
300
reqctx->cryptoctx, plgctx->idctx,
301
0, /* kdc cert */
302
plgctx->opts->accept_secondary_eku,
303
eku_accepted);
304
if (retval) {
305
pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
306
__FUNCTION__, retval, error_message(retval));
307
goto out;
308
}
309
310
out:
311
pkiDebug("%s: returning retval %d, eku_accepted %d\n",
312
__FUNCTION__, retval, *eku_accepted);
313
return retval;
314
}
315
316
317
/* Run the received, verified certificate through certauth modules, to verify
318
* that it is authorized to authenticate as client. */
319
static krb5_error_code
320
authorize_cert(krb5_context context, certauth_handle *certauth_modules,
321
pkinit_kdc_context plgctx, pkinit_kdc_req_context reqctx,
322
krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
323
krb5_principal client, krb5_boolean *hwauth_out)
324
{
325
krb5_error_code ret;
326
certauth_handle h;
327
struct certauth_req_opts opts;
328
krb5_boolean accepted = FALSE, hwauth = FALSE;
329
uint8_t *cert;
330
size_t i, cert_len;
331
void *db_ent = NULL;
332
char **ais = NULL, **ai = NULL;
333
334
/* Re-encode the received certificate into DER, which is extra work, but
335
* avoids creating an X.509 library dependency in the interface. */
336
ret = crypto_encode_der_cert(context, reqctx->cryptoctx, &cert, &cert_len);
337
if (ret)
338
goto cleanup;
339
340
/* Set options for the builtin module. */
341
opts.plgctx = plgctx;
342
opts.reqctx = reqctx;
343
opts.cb = cb;
344
opts.rock = rock;
345
346
db_ent = cb->client_entry(context, rock);
347
348
/*
349
* Check the certificate against each certauth module. For the certificate
350
* to be authorized at least one module must return 0 or
351
* KRB5_CERTAUTH_HWAUTH, and no module can return an error code other than
352
* KRB5_PLUGIN_NO_HANDLE (pass) or KRB5_CERTAUTH_HWAUTH_PASS (pass but
353
* set hw-authent). Add indicators from all modules.
354
*/
355
ret = KRB5_PLUGIN_NO_HANDLE;
356
for (i = 0; certauth_modules != NULL && certauth_modules[i] != NULL; i++) {
357
h = certauth_modules[i];
358
TRACE_PKINIT_SERVER_CERT_AUTH(context, h->vt.name);
359
ret = h->vt.authorize(context, h->moddata, cert, cert_len, client,
360
&opts, db_ent, &ais);
361
if (ret == 0)
362
accepted = TRUE;
363
else if (ret == KRB5_CERTAUTH_HWAUTH)
364
accepted = hwauth = TRUE;
365
else if (ret == KRB5_CERTAUTH_HWAUTH_PASS)
366
hwauth = TRUE;
367
else if (ret != KRB5_PLUGIN_NO_HANDLE)
368
goto cleanup;
369
370
if (ais != NULL) {
371
/* Assert authentication indicators from the module. */
372
for (ai = ais; *ai != NULL; ai++) {
373
ret = cb->add_auth_indicator(context, rock, *ai);
374
if (ret)
375
goto cleanup;
376
}
377
h->vt.free_ind(context, h->moddata, ais);
378
ais = NULL;
379
}
380
}
381
382
*hwauth_out = hwauth;
383
ret = accepted ? 0 : KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
384
385
cleanup:
386
free(cert);
387
return ret;
388
}
389
390
/* Return an error if freshness tokens are required and one was not received.
391
* Log an appropriate message indicating whether a valid token was received. */
392
static krb5_error_code
393
check_log_freshness(krb5_context context, pkinit_kdc_context plgctx,
394
krb5_kdc_req *request, krb5_boolean valid_freshness_token)
395
{
396
krb5_error_code ret;
397
char *name = NULL;
398
399
ret = krb5_unparse_name(context, request->client, &name);
400
if (ret)
401
return ret;
402
if (plgctx->opts->require_freshness && !valid_freshness_token) {
403
com_err("", 0, _("PKINIT: no freshness token, rejecting auth from %s"),
404
name);
405
ret = KRB5KDC_ERR_PREAUTH_FAILED;
406
} else if (valid_freshness_token) {
407
com_err("", 0, _("PKINIT: freshness token received from %s"), name);
408
} else {
409
com_err("", 0, _("PKINIT: no freshness token received from %s"), name);
410
}
411
krb5_free_unparsed_name(context, name);
412
return ret;
413
}
414
415
static void
416
pkinit_server_verify_padata(krb5_context context,
417
krb5_data *req_pkt,
418
krb5_kdc_req * request,
419
krb5_enc_tkt_part * enc_tkt_reply,
420
krb5_pa_data * data,
421
krb5_kdcpreauth_callbacks cb,
422
krb5_kdcpreauth_rock rock,
423
krb5_kdcpreauth_moddata moddata,
424
krb5_kdcpreauth_verify_respond_fn respond,
425
void *arg)
426
{
427
krb5_error_code retval = 0;
428
krb5_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL};
429
krb5_pa_pk_as_req *reqp = NULL;
430
krb5_auth_pack *auth_pack = NULL;
431
krb5_pk_authenticator *pka;
432
pkinit_kdc_context plgctx = NULL;
433
pkinit_kdc_req_context reqctx = NULL;
434
krb5_checksum cksum = {0, 0, 0, NULL};
435
krb5_data *der_req = NULL;
436
krb5_data k5data;
437
int is_signed = 1;
438
krb5_pa_data **e_data = NULL;
439
krb5_kdcpreauth_modreq modreq = NULL;
440
krb5_boolean valid_freshness_token = FALSE, hwauth = FALSE;
441
char **sp;
442
443
pkiDebug("pkinit_verify_padata: entered!\n");
444
if (data == NULL || data->length <= 0 || data->contents == NULL) {
445
(*respond)(arg, EINVAL, NULL, NULL, NULL);
446
return;
447
}
448
449
450
if (moddata == NULL) {
451
(*respond)(arg, EINVAL, NULL, NULL, NULL);
452
return;
453
}
454
455
plgctx = pkinit_find_realm_context(context, moddata, request->server);
456
if (plgctx == NULL) {
457
(*respond)(arg, EINVAL, NULL, NULL, NULL);
458
return;
459
}
460
461
#ifdef DEBUG_ASN1
462
print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req");
463
#endif
464
/* create a per-request context */
465
retval = pkinit_init_kdc_req_context(context, &reqctx);
466
if (retval)
467
goto cleanup;
468
reqctx->pa_type = data->pa_type;
469
470
PADATA_TO_KRB5DATA(data, &k5data);
471
472
if (data->pa_type != KRB5_PADATA_PK_AS_REQ) {
473
pkiDebug("unrecognized pa_type = %d\n", data->pa_type);
474
retval = EINVAL;
475
goto cleanup;
476
}
477
478
TRACE_PKINIT_SERVER_PADATA_VERIFY(context);
479
retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp);
480
if (retval) {
481
pkiDebug("decode_krb5_pa_pk_as_req failed\n");
482
goto cleanup;
483
}
484
#ifdef DEBUG_ASN1
485
print_buffer_bin(reqp->signedAuthPack.data, reqp->signedAuthPack.length,
486
"/tmp/kdc_signed_data");
487
#endif
488
retval = cms_signeddata_verify(context, plgctx->cryptoctx,
489
reqctx->cryptoctx, plgctx->idctx,
490
CMS_SIGN_CLIENT,
491
plgctx->opts->require_crl_checking,
492
(unsigned char *)reqp->signedAuthPack.data,
493
reqp->signedAuthPack.length,
494
(unsigned char **)&authp_data.data,
495
&authp_data.length,
496
(unsigned char **)&krb5_authz.data,
497
&krb5_authz.length, &is_signed);
498
if (retval) {
499
TRACE_PKINIT_SERVER_PADATA_VERIFY_FAIL(context);
500
goto cleanup;
501
}
502
if (is_signed) {
503
retval = authorize_cert(context, moddata->certauth_modules, plgctx,
504
reqctx, cb, rock, request->client, &hwauth);
505
if (retval)
506
goto cleanup;
507
508
} else { /* !is_signed */
509
if (!krb5_principal_compare(context, request->client,
510
krb5_anonymous_principal())) {
511
retval = KRB5KDC_ERR_PREAUTH_FAILED;
512
krb5_set_error_message(context, retval,
513
_("Pkinit request not signed, but client "
514
"not anonymous."));
515
goto cleanup;
516
}
517
}
518
#ifdef DEBUG_ASN1
519
print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack");
520
#endif
521
522
OCTETDATA_TO_KRB5DATA(&authp_data, &k5data);
523
retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack);
524
if (retval) {
525
pkiDebug("failed to decode krb5_auth_pack\n");
526
goto cleanup;
527
}
528
pka = &auth_pack->pkAuthenticator;
529
530
retval = krb5_check_clockskew(context, pka->ctime);
531
if (retval)
532
goto cleanup;
533
534
/* check dh parameters */
535
if (auth_pack->clientPublicValue.length > 0) {
536
retval = server_check_dh(context, plgctx->cryptoctx,
537
reqctx->cryptoctx, plgctx->idctx,
538
&auth_pack->clientPublicValue,
539
plgctx->opts->dh_min_bits);
540
if (retval) {
541
pkiDebug("bad dh parameters\n");
542
goto cleanup;
543
}
544
} else if (!is_signed) {
545
/*Anonymous pkinit requires DH*/
546
retval = KRB5KDC_ERR_PREAUTH_FAILED;
547
krb5_set_error_message(context, retval,
548
_("Anonymous pkinit without DH public "
549
"value not supported."));
550
goto cleanup;
551
}
552
der_req = cb->request_body(context, rock);
553
554
retval = crypto_verify_checksums(context, der_req, &pka->paChecksum,
555
pka->paChecksum2);
556
if (retval)
557
goto cleanup;
558
559
if (pka->freshnessToken != NULL) {
560
retval = cb->check_freshness_token(context, rock, pka->freshnessToken);
561
if (retval)
562
goto cleanup;
563
valid_freshness_token = TRUE;
564
}
565
566
/* check if kdcPkId present and match KDC's subjectIdentifier */
567
if (reqp->kdcPkId.data != NULL) {
568
int valid_kdcPkId = 0;
569
retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx,
570
reqctx->cryptoctx, plgctx->idctx,
571
(unsigned char *)reqp->kdcPkId.data,
572
reqp->kdcPkId.length, &valid_kdcPkId);
573
if (retval)
574
goto cleanup;
575
if (!valid_kdcPkId) {
576
pkiDebug("kdcPkId in AS_REQ does not match KDC's cert; "
577
"RFC says to ignore and proceed\n");
578
}
579
}
580
/* remember the decoded auth_pack for verify_padata routine */
581
reqctx->rcv_auth_pack = auth_pack;
582
auth_pack = NULL;
583
584
if (is_signed) {
585
retval = check_log_freshness(context, plgctx, request,
586
valid_freshness_token);
587
if (retval)
588
goto cleanup;
589
}
590
591
if (is_signed && plgctx->auth_indicators != NULL) {
592
/* Assert configured authentication indicators. */
593
for (sp = plgctx->auth_indicators; *sp != NULL; sp++) {
594
retval = cb->add_auth_indicator(context, rock, *sp);
595
if (retval)
596
goto cleanup;
597
}
598
}
599
600
/* remember to set the PREAUTH flag in the reply */
601
enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
602
if (hwauth)
603
enc_tkt_reply->flags |= TKT_FLG_HW_AUTH;
604
modreq = (krb5_kdcpreauth_modreq)reqctx;
605
reqctx = NULL;
606
607
cleanup:
608
if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) {
609
pkiDebug("pkinit_verify_padata failed: creating e-data\n");
610
if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx,
611
plgctx->idctx, plgctx->opts, retval, &e_data))
612
pkiDebug("pkinit_create_edata failed\n");
613
}
614
615
free_krb5_pa_pk_as_req(&reqp);
616
free(cksum.contents);
617
free(authp_data.data);
618
free(krb5_authz.data);
619
if (reqctx != NULL)
620
pkinit_fini_kdc_req_context(context, reqctx);
621
free_krb5_auth_pack(&auth_pack);
622
623
(*respond)(arg, retval, modreq, e_data, NULL);
624
}
625
static krb5_error_code
626
return_pkinit_kx(krb5_context context, krb5_kdc_req *request,
627
krb5_kdc_rep *reply, krb5_keyblock *encrypting_key,
628
krb5_pa_data **out_padata)
629
{
630
krb5_error_code ret = 0;
631
krb5_keyblock *session = reply->ticket->enc_part2->session;
632
krb5_keyblock *new_session = NULL;
633
krb5_pa_data *pa = NULL;
634
krb5_enc_data enc;
635
krb5_data *scratch = NULL;
636
637
*out_padata = NULL;
638
enc.ciphertext.data = NULL;
639
if (!krb5_principal_compare(context, request->client,
640
krb5_anonymous_principal()))
641
return 0;
642
/*
643
* The KDC contribution key needs to be a fresh key of an enctype supported
644
* by the client and server. The existing session key meets these
645
* requirements so we use it.
646
*/
647
ret = krb5_c_fx_cf2_simple(context, session, "PKINIT",
648
encrypting_key, "KEYEXCHANGE",
649
&new_session);
650
if (ret)
651
goto cleanup;
652
ret = encode_krb5_encryption_key( session, &scratch);
653
if (ret)
654
goto cleanup;
655
ret = krb5_encrypt_helper(context, encrypting_key,
656
KRB5_KEYUSAGE_PA_PKINIT_KX, scratch, &enc);
657
if (ret)
658
goto cleanup;
659
memset(scratch->data, 0, scratch->length);
660
krb5_free_data(context, scratch);
661
scratch = NULL;
662
ret = encode_krb5_enc_data(&enc, &scratch);
663
if (ret)
664
goto cleanup;
665
pa = malloc(sizeof(krb5_pa_data));
666
if (pa == NULL) {
667
ret = ENOMEM;
668
goto cleanup;
669
}
670
pa->pa_type = KRB5_PADATA_PKINIT_KX;
671
pa->length = scratch->length;
672
pa->contents = (krb5_octet *) scratch->data;
673
*out_padata = pa;
674
scratch->data = NULL;
675
memset(session->contents, 0, session->length);
676
krb5_free_keyblock_contents(context, session);
677
*session = *new_session;
678
new_session->contents = NULL;
679
cleanup:
680
krb5_free_data_contents(context, &enc.ciphertext);
681
krb5_free_keyblock(context, new_session);
682
krb5_free_data(context, scratch);
683
return ret;
684
}
685
686
static krb5_error_code
687
pkinit_pick_kdf_alg(krb5_context context, krb5_data **kdf_list,
688
krb5_data **alg_oid)
689
{
690
krb5_error_code retval = 0;
691
krb5_data *req_oid = NULL;
692
const krb5_data *supp_oid = NULL;
693
krb5_data *tmp_oid = NULL;
694
size_t i, j = 0;
695
696
/* if we don't find a match, return NULL value */
697
*alg_oid = NULL;
698
699
/* for each of the OIDs that the server supports... */
700
for (i = 0; NULL != (supp_oid = supported_kdf_alg_ids[i]); i++) {
701
/* if the requested OID is in the client's list, use it. */
702
for (j = 0; NULL != (req_oid = kdf_list[j]); j++) {
703
if ((req_oid->length == supp_oid->length) &&
704
(0 == memcmp(req_oid->data, supp_oid->data, req_oid->length))) {
705
tmp_oid = k5alloc(sizeof(krb5_data), &retval);
706
if (retval)
707
goto cleanup;
708
tmp_oid->data = k5memdup(supp_oid->data, supp_oid->length,
709
&retval);
710
if (retval)
711
goto cleanup;
712
tmp_oid->length = supp_oid->length;
713
*alg_oid = tmp_oid;
714
/* don't free the OID in clean-up if we are returning it */
715
tmp_oid = NULL;
716
goto cleanup;
717
}
718
}
719
}
720
cleanup:
721
if (tmp_oid)
722
krb5_free_data(context, tmp_oid);
723
return retval;
724
}
725
726
static krb5_error_code
727
pkinit_server_return_padata(krb5_context context,
728
krb5_pa_data * padata,
729
krb5_data *req_pkt,
730
krb5_kdc_req * request,
731
krb5_kdc_rep * reply,
732
krb5_keyblock * encrypting_key,
733
krb5_pa_data ** send_pa,
734
krb5_kdcpreauth_callbacks cb,
735
krb5_kdcpreauth_rock rock,
736
krb5_kdcpreauth_moddata moddata,
737
krb5_kdcpreauth_modreq modreq)
738
{
739
krb5_error_code retval = 0;
740
krb5_data scratch = {0, 0, NULL};
741
krb5_pa_pk_as_req *reqp = NULL;
742
int i = 0;
743
744
unsigned char *dh_pubkey = NULL, *server_key = NULL;
745
unsigned int server_key_len = 0, dh_pubkey_len = 0;
746
krb5_keyblock reply_key = { 0 };
747
748
krb5_kdc_dh_key_info dhkey_info;
749
krb5_data *encoded_dhkey_info = NULL;
750
krb5_pa_pk_as_rep *rep = NULL;
751
krb5_data *out_data = NULL;
752
krb5_data secret;
753
754
krb5_enctype enctype = -1;
755
756
krb5_reply_key_pack *key_pack = NULL;
757
krb5_data *encoded_key_pack = NULL;
758
759
pkinit_kdc_context plgctx;
760
pkinit_kdc_req_context reqctx;
761
762
*send_pa = NULL;
763
if (padata->pa_type == KRB5_PADATA_PKINIT_KX) {
764
return return_pkinit_kx(context, request, reply,
765
encrypting_key, send_pa);
766
}
767
if (padata->length <= 0 || padata->contents == NULL)
768
return 0;
769
770
if (modreq == NULL) {
771
pkiDebug("missing request context \n");
772
return EINVAL;
773
}
774
775
plgctx = pkinit_find_realm_context(context, moddata, request->server);
776
if (plgctx == NULL) {
777
pkiDebug("Unable to locate correct realm context\n");
778
return ENOENT;
779
}
780
781
TRACE_PKINIT_SERVER_RETURN_PADATA(context);
782
reqctx = (pkinit_kdc_req_context)modreq;
783
784
for(i = 0; i < request->nktypes; i++) {
785
enctype = request->ktype[i];
786
if (!krb5_c_valid_enctype(enctype))
787
continue;
788
else {
789
pkiDebug("KDC picked etype = %d\n", enctype);
790
break;
791
}
792
}
793
794
if (i == request->nktypes) {
795
retval = KRB5KDC_ERR_ETYPE_NOSUPP;
796
goto cleanup;
797
}
798
799
init_krb5_pa_pk_as_rep(&rep);
800
if (rep == NULL) {
801
retval = ENOMEM;
802
goto cleanup;
803
}
804
805
if (reqctx->rcv_auth_pack == NULL ||
806
reqctx->rcv_auth_pack->clientPublicValue.length == 0) {
807
retval = KRB5KDC_ERR_PREAUTH_FAILED;
808
k5_setmsg(context, retval, _("Unsupported PKINIT RSA request"));
809
goto cleanup;
810
}
811
812
rep->choice = choice_pa_pk_as_rep_dhInfo;
813
814
retval = server_process_dh(context, plgctx->cryptoctx, reqctx->cryptoctx,
815
plgctx->idctx, &dh_pubkey, &dh_pubkey_len,
816
&server_key, &server_key_len);
817
if (retval) {
818
pkiDebug("failed to process/create dh parameters\n");
819
goto cleanup;
820
}
821
822
dhkey_info.subjectPublicKey.length = dh_pubkey_len;
823
dhkey_info.subjectPublicKey.data = (char *)dh_pubkey;
824
dhkey_info.nonce = request->nonce;
825
dhkey_info.dhKeyExpiration = 0;
826
827
retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info,
828
&encoded_dhkey_info);
829
if (retval) {
830
pkiDebug("encode_krb5_kdc_dh_key_info failed\n");
831
goto cleanup;
832
}
833
#ifdef DEBUG_ASN1
834
print_buffer_bin((unsigned char *)encoded_dhkey_info->data,
835
encoded_dhkey_info->length, "/tmp/kdc_dh_key_info");
836
#endif
837
838
retval = cms_signeddata_create(context, plgctx->cryptoctx,
839
reqctx->cryptoctx, plgctx->idctx,
840
CMS_SIGN_SERVER,
841
(unsigned char *)encoded_dhkey_info->data,
842
encoded_dhkey_info->length,
843
(unsigned char **)
844
&rep->u.dh_Info.dhSignedData.data,
845
&rep->u.dh_Info.dhSignedData.length);
846
if (retval) {
847
pkiDebug("failed to create pkcs7 signed data\n");
848
goto cleanup;
849
}
850
851
if (reqctx->rcv_auth_pack != NULL &&
852
reqctx->rcv_auth_pack->supportedKDFs != NULL) {
853
/* If using the alg-agility KDF, put the algorithm in the reply
854
* before encoding it.
855
*/
856
if (reqctx->rcv_auth_pack != NULL &&
857
reqctx->rcv_auth_pack->supportedKDFs != NULL) {
858
retval = pkinit_pick_kdf_alg(context, reqctx->rcv_auth_pack->supportedKDFs,
859
&(rep->u.dh_Info.kdfID));
860
if (retval) {
861
pkiDebug("pkinit_pick_kdf_alg failed: %s\n",
862
error_message(retval));
863
goto cleanup;
864
}
865
}
866
}
867
868
retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data);
869
if (retval) {
870
pkiDebug("failed to encode AS_REP\n");
871
goto cleanup;
872
}
873
#ifdef DEBUG_ASN1
874
if (out_data != NULL)
875
print_buffer_bin((unsigned char *)out_data->data, out_data->length,
876
"/tmp/kdc_as_rep");
877
#endif
878
879
secret = make_data(server_key, server_key_len);
880
retval = pkinit_kdf(context, &secret, rep->u.dh_Info.kdfID,
881
request->client, request->server, enctype, req_pkt,
882
out_data, &reply_key);
883
if (retval) {
884
pkiDebug("pkinit_kdf failed: %s\n", error_message(retval));
885
goto cleanup;
886
}
887
888
retval = cb->replace_reply_key(context, rock, &reply_key, FALSE);
889
if (retval)
890
goto cleanup;
891
892
*send_pa = malloc(sizeof(krb5_pa_data));
893
if (*send_pa == NULL) {
894
retval = ENOMEM;
895
free(out_data->data);
896
free(out_data);
897
out_data = NULL;
898
goto cleanup;
899
}
900
(*send_pa)->magic = KV5M_PA_DATA;
901
(*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP;
902
(*send_pa)->length = out_data->length;
903
(*send_pa)->contents = (krb5_octet *) out_data->data;
904
905
cleanup:
906
free(scratch.data);
907
free(out_data);
908
if (encoded_dhkey_info != NULL)
909
krb5_free_data(context, encoded_dhkey_info);
910
if (encoded_key_pack != NULL)
911
krb5_free_data(context, encoded_key_pack);
912
free(dh_pubkey);
913
free(server_key);
914
free_krb5_pa_pk_as_req(&reqp);
915
free_krb5_pa_pk_as_rep(&rep);
916
free_krb5_reply_key_pack(&key_pack);
917
krb5_free_keyblock_contents(context, &reply_key);
918
919
if (retval)
920
pkiDebug("pkinit_verify_padata failure");
921
922
return retval;
923
}
924
925
static int
926
pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype)
927
{
928
if (patype == KRB5_PADATA_PKINIT_KX)
929
return PA_INFO;
930
/* PKINIT does not normally set the hw-authent ticket flag, but a
931
* certauth module can cause it to do so. */
932
return PA_SUFFICIENT | PA_REPLACES_KEY | PA_TYPED_E_DATA | PA_HARDWARE;
933
}
934
935
static krb5_preauthtype supported_server_pa_types[] = {
936
KRB5_PADATA_PK_AS_REQ,
937
KRB5_PADATA_PKINIT_KX,
938
0
939
};
940
941
static void
942
pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
943
{
944
/*
945
* There is nothing currently allocated by pkinit_init_kdc_profile()
946
* which needs to be freed here.
947
*/
948
}
949
950
static krb5_error_code
951
pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
952
{
953
krb5_error_code retval;
954
char *eku_string = NULL, *ocsp_check = NULL, *minbits = NULL;
955
956
pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname);
957
retval = pkinit_kdcdefault_string(context, plgctx->realmname,
958
KRB5_CONF_PKINIT_IDENTITY,
959
&plgctx->idopts->identity);
960
if (retval != 0 || NULL == plgctx->idopts->identity) {
961
retval = EINVAL;
962
krb5_set_error_message(context, retval,
963
_("No pkinit_identity supplied for realm %s"),
964
plgctx->realmname);
965
goto errout;
966
}
967
968
retval = pkinit_kdcdefault_strings(context, plgctx->realmname,
969
KRB5_CONF_PKINIT_ANCHORS,
970
&plgctx->idopts->anchors);
971
if (retval != 0 || NULL == plgctx->idopts->anchors) {
972
retval = EINVAL;
973
krb5_set_error_message(context, retval,
974
_("No pkinit_anchors supplied for realm %s"),
975
plgctx->realmname);
976
goto errout;
977
}
978
979
pkinit_kdcdefault_strings(context, plgctx->realmname,
980
KRB5_CONF_PKINIT_POOL,
981
&plgctx->idopts->intermediates);
982
983
pkinit_kdcdefault_strings(context, plgctx->realmname,
984
KRB5_CONF_PKINIT_REVOKE,
985
&plgctx->idopts->crls);
986
987
pkinit_kdcdefault_string(context, plgctx->realmname,
988
KRB5_CONF_PKINIT_KDC_OCSP,
989
&ocsp_check);
990
if (ocsp_check != NULL) {
991
free(ocsp_check);
992
retval = ENOTSUP;
993
krb5_set_error_message(context, retval,
994
_("OCSP is not supported: (realm: %s)"),
995
plgctx->realmname);
996
goto errout;
997
}
998
999
pkinit_kdcdefault_string(context, plgctx->realmname,
1000
KRB5_CONF_PKINIT_DH_MIN_BITS, &minbits);
1001
plgctx->opts->dh_min_bits = parse_dh_min_bits(context, minbits);
1002
free(minbits);
1003
1004
pkinit_kdcdefault_boolean(context, plgctx->realmname,
1005
KRB5_CONF_PKINIT_ALLOW_UPN,
1006
0, &plgctx->opts->allow_upn);
1007
1008
pkinit_kdcdefault_boolean(context, plgctx->realmname,
1009
KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING,
1010
0, &plgctx->opts->require_crl_checking);
1011
1012
pkinit_kdcdefault_boolean(context, plgctx->realmname,
1013
KRB5_CONF_PKINIT_REQUIRE_FRESHNESS,
1014
0, &plgctx->opts->require_freshness);
1015
1016
pkinit_kdcdefault_string(context, plgctx->realmname,
1017
KRB5_CONF_PKINIT_EKU_CHECKING,
1018
&eku_string);
1019
if (eku_string != NULL) {
1020
if (strcasecmp(eku_string, "kpClientAuth") == 0) {
1021
plgctx->opts->require_eku = 1;
1022
plgctx->opts->accept_secondary_eku = 0;
1023
} else if (strcasecmp(eku_string, "scLogin") == 0) {
1024
plgctx->opts->require_eku = 1;
1025
plgctx->opts->accept_secondary_eku = 1;
1026
} else if (strcasecmp(eku_string, "none") == 0) {
1027
plgctx->opts->require_eku = 0;
1028
plgctx->opts->accept_secondary_eku = 0;
1029
} else {
1030
pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
1031
__FUNCTION__, eku_string);
1032
}
1033
free(eku_string);
1034
}
1035
1036
pkinit_kdcdefault_strings(context, plgctx->realmname,
1037
KRB5_CONF_PKINIT_INDICATOR,
1038
&plgctx->auth_indicators);
1039
1040
return 0;
1041
errout:
1042
pkinit_fini_kdc_profile(context, plgctx);
1043
return retval;
1044
}
1045
1046
static pkinit_kdc_context
1047
pkinit_find_realm_context(krb5_context context,
1048
krb5_kdcpreauth_moddata moddata,
1049
krb5_principal princ)
1050
{
1051
size_t i;
1052
pkinit_kdc_context *realm_contexts;
1053
1054
if (moddata == NULL)
1055
return NULL;
1056
1057
realm_contexts = moddata->realm_contexts;
1058
if (realm_contexts == NULL)
1059
return NULL;
1060
1061
for (i = 0; realm_contexts[i] != NULL; i++) {
1062
pkinit_kdc_context p = realm_contexts[i];
1063
1064
if ((p->realmname_len == princ->realm.length) &&
1065
(strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) {
1066
pkiDebug("%s: returning context at %p for realm '%s'\n",
1067
__FUNCTION__, p, p->realmname);
1068
return p;
1069
}
1070
}
1071
pkiDebug("%s: unable to find realm context for realm '%.*s'\n",
1072
__FUNCTION__, princ->realm.length, princ->realm.data);
1073
return NULL;
1074
}
1075
1076
static int
1077
pkinit_server_plugin_init_realm(krb5_context context, const char *realmname,
1078
pkinit_kdc_context *pplgctx)
1079
{
1080
krb5_error_code retval = ENOMEM;
1081
pkinit_kdc_context plgctx = NULL;
1082
1083
*pplgctx = NULL;
1084
1085
plgctx = calloc(1, sizeof(*plgctx));
1086
if (plgctx == NULL)
1087
goto errout;
1088
1089
pkiDebug("%s: initializing context at %p for realm '%s'\n",
1090
__FUNCTION__, plgctx, realmname);
1091
memset(plgctx, 0, sizeof(*plgctx));
1092
plgctx->magic = PKINIT_CTX_MAGIC;
1093
1094
plgctx->realmname = strdup(realmname);
1095
if (plgctx->realmname == NULL)
1096
goto errout;
1097
plgctx->realmname_len = strlen(plgctx->realmname);
1098
1099
retval = pkinit_init_plg_crypto(context, &plgctx->cryptoctx);
1100
if (retval)
1101
goto errout;
1102
1103
retval = pkinit_init_plg_opts(&plgctx->opts);
1104
if (retval)
1105
goto errout;
1106
1107
retval = pkinit_init_identity_crypto(&plgctx->idctx);
1108
if (retval)
1109
goto errout;
1110
1111
retval = pkinit_init_identity_opts(&plgctx->idopts);
1112
if (retval)
1113
goto errout;
1114
1115
retval = pkinit_init_kdc_profile(context, plgctx);
1116
if (retval)
1117
goto errout;
1118
1119
retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL,
1120
plgctx->idopts, plgctx->idctx,
1121
NULL, NULL, NULL);
1122
if (retval)
1123
goto errout;
1124
retval = pkinit_identity_prompt(context, plgctx->cryptoctx, NULL,
1125
plgctx->idopts, plgctx->idctx,
1126
NULL, NULL, 0, NULL);
1127
if (retval)
1128
goto errout;
1129
1130
pkiDebug("%s: returning context at %p for realm '%s'\n",
1131
__FUNCTION__, plgctx, realmname);
1132
*pplgctx = plgctx;
1133
retval = 0;
1134
1135
errout:
1136
if (retval)
1137
pkinit_server_plugin_fini_realm(context, plgctx);
1138
1139
return retval;
1140
}
1141
1142
static krb5_error_code
1143
pkinit_san_authorize(krb5_context context, krb5_certauth_moddata moddata,
1144
const uint8_t *cert, size_t cert_len,
1145
krb5_const_principal princ, const void *opts,
1146
const struct _krb5_db_entry_new *db_entry,
1147
char ***authinds_out)
1148
{
1149
krb5_error_code ret;
1150
int valid_san;
1151
const struct certauth_req_opts *req_opts = opts;
1152
1153
*authinds_out = NULL;
1154
1155
ret = verify_client_san(context, req_opts->plgctx, req_opts->reqctx,
1156
req_opts->cb, req_opts->rock, princ, &valid_san);
1157
if (ret == ENOENT)
1158
return KRB5_PLUGIN_NO_HANDLE;
1159
else if (ret)
1160
return ret;
1161
1162
if (!valid_san) {
1163
TRACE_PKINIT_SERVER_SAN_REJECT(context);
1164
return KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
1165
}
1166
1167
return 0;
1168
}
1169
1170
static krb5_error_code
1171
pkinit_eku_authorize(krb5_context context, krb5_certauth_moddata moddata,
1172
const uint8_t *cert, size_t cert_len,
1173
krb5_const_principal princ, const void *opts,
1174
const struct _krb5_db_entry_new *db_entry,
1175
char ***authinds_out)
1176
{
1177
krb5_error_code ret;
1178
int valid_eku;
1179
const struct certauth_req_opts *req_opts = opts;
1180
1181
*authinds_out = NULL;
1182
1183
/* Verify the client EKU. */
1184
ret = verify_client_eku(context, req_opts->plgctx, req_opts->reqctx,
1185
&valid_eku);
1186
if (ret)
1187
return ret;
1188
1189
if (!valid_eku) {
1190
TRACE_PKINIT_SERVER_EKU_REJECT(context);
1191
return KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
1192
}
1193
1194
return KRB5_PLUGIN_NO_HANDLE;
1195
}
1196
1197
static krb5_error_code
1198
certauth_pkinit_san_initvt(krb5_context context, int maj_ver, int min_ver,
1199
krb5_plugin_vtable vtable)
1200
{
1201
krb5_certauth_vtable vt;
1202
1203
if (maj_ver != 1)
1204
return KRB5_PLUGIN_VER_NOTSUPP;
1205
vt = (krb5_certauth_vtable)vtable;
1206
vt->name = "pkinit_san";
1207
vt->authorize = pkinit_san_authorize;
1208
return 0;
1209
}
1210
1211
static krb5_error_code
1212
certauth_pkinit_eku_initvt(krb5_context context, int maj_ver, int min_ver,
1213
krb5_plugin_vtable vtable)
1214
{
1215
krb5_certauth_vtable vt;
1216
1217
if (maj_ver != 1)
1218
return KRB5_PLUGIN_VER_NOTSUPP;
1219
vt = (krb5_certauth_vtable)vtable;
1220
vt->name = "pkinit_eku";
1221
vt->authorize = pkinit_eku_authorize;
1222
return 0;
1223
}
1224
1225
/*
1226
* Do certificate auth based on a match expression in the pkinit_cert_match
1227
* attribute string. An expression should be in the same form as those used
1228
* for the pkinit_cert_match configuration option.
1229
*/
1230
static krb5_error_code
1231
dbmatch_authorize(krb5_context context, krb5_certauth_moddata moddata,
1232
const uint8_t *cert, size_t cert_len,
1233
krb5_const_principal princ, const void *opts,
1234
const struct _krb5_db_entry_new *db_entry,
1235
char ***authinds_out)
1236
{
1237
krb5_error_code ret;
1238
const struct certauth_req_opts *req_opts = opts;
1239
char *pattern;
1240
krb5_boolean matched;
1241
1242
*authinds_out = NULL;
1243
1244
/* Fetch the matching pattern. Pass if it isn't specified. */
1245
ret = req_opts->cb->get_string(context, req_opts->rock,
1246
"pkinit_cert_match", &pattern);
1247
if (ret)
1248
return ret;
1249
if (pattern == NULL)
1250
return KRB5_PLUGIN_NO_HANDLE;
1251
1252
/* Check the certificate against the match expression. */
1253
ret = pkinit_client_cert_match(context, req_opts->plgctx->cryptoctx,
1254
req_opts->reqctx->cryptoctx, pattern,
1255
&matched);
1256
req_opts->cb->free_string(context, req_opts->rock, pattern);
1257
if (ret)
1258
return ret;
1259
return matched ? 0 : KRB5KDC_ERR_CERTIFICATE_MISMATCH;
1260
}
1261
1262
static krb5_error_code
1263
certauth_dbmatch_initvt(krb5_context context, int maj_ver, int min_ver,
1264
krb5_plugin_vtable vtable)
1265
{
1266
krb5_certauth_vtable vt;
1267
1268
if (maj_ver != 1)
1269
return KRB5_PLUGIN_VER_NOTSUPP;
1270
vt = (krb5_certauth_vtable)vtable;
1271
vt->name = "dbmatch";
1272
vt->authorize = dbmatch_authorize;
1273
return 0;
1274
}
1275
1276
static krb5_error_code
1277
load_certauth_plugins(krb5_context context, const char *const *realmnames,
1278
certauth_handle **handle_out)
1279
{
1280
krb5_error_code ret;
1281
krb5_plugin_initvt_fn *modules = NULL, *mod;
1282
certauth_handle *list = NULL, h;
1283
size_t count;
1284
1285
/* Register the builtin modules. */
1286
ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH,
1287
"pkinit_san", certauth_pkinit_san_initvt);
1288
if (ret)
1289
goto cleanup;
1290
1291
ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH,
1292
"pkinit_eku", certauth_pkinit_eku_initvt);
1293
if (ret)
1294
goto cleanup;
1295
1296
ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH, "dbmatch",
1297
certauth_dbmatch_initvt);
1298
if (ret)
1299
goto cleanup;
1300
1301
ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_CERTAUTH, &modules);
1302
if (ret)
1303
goto cleanup;
1304
1305
/* Allocate handle list. */
1306
for (count = 0; modules[count]; count++);
1307
list = k5calloc(count + 1, sizeof(*list), &ret);
1308
if (list == NULL)
1309
goto cleanup;
1310
1311
/* Initialize each module, ignoring ones that fail. */
1312
count = 0;
1313
for (mod = modules; *mod != NULL; mod++) {
1314
h = k5calloc(1, sizeof(*h), &ret);
1315
if (h == NULL)
1316
goto cleanup;
1317
1318
ret = (*mod)(context, 1, 2, (krb5_plugin_vtable)&h->vt);
1319
if (ret) {
1320
TRACE_CERTAUTH_VTINIT_FAIL(context, ret);
1321
free(h);
1322
continue;
1323
}
1324
h->moddata = NULL;
1325
if (h->vt.init_ex != NULL)
1326
ret = h->vt.init_ex(context, realmnames, &h->moddata);
1327
else if (h->vt.init != NULL)
1328
ret = h->vt.init(context, &h->moddata);
1329
if (ret) {
1330
TRACE_CERTAUTH_INIT_FAIL(context, h->vt.name, ret);
1331
free(h);
1332
continue;
1333
}
1334
list[count++] = h;
1335
list[count] = NULL;
1336
}
1337
list[count] = NULL;
1338
1339
ret = 0;
1340
*handle_out = list;
1341
list = NULL;
1342
1343
cleanup:
1344
k5_plugin_free_modules(context, modules);
1345
free_certauth_handles(context, list);
1346
return ret;
1347
}
1348
1349
static int
1350
pkinit_server_plugin_init(krb5_context context,
1351
krb5_kdcpreauth_moddata *moddata_out,
1352
const char **realmnames)
1353
{
1354
krb5_error_code retval = ENOMEM;
1355
pkinit_kdc_context plgctx, *realm_contexts = NULL;
1356
certauth_handle *certauth_modules = NULL;
1357
krb5_kdcpreauth_moddata moddata;
1358
size_t i, j;
1359
size_t numrealms;
1360
1361
retval = pkinit_accessor_init();
1362
if (retval)
1363
return retval;
1364
1365
/* Determine how many realms we may need to support */
1366
for (i = 0; realmnames[i] != NULL; i++) {};
1367
numrealms = i;
1368
1369
realm_contexts = calloc(numrealms+1, sizeof(pkinit_kdc_context));
1370
if (realm_contexts == NULL)
1371
return ENOMEM;
1372
1373
for (i = 0, j = 0; i < numrealms; i++) {
1374
TRACE_PKINIT_SERVER_INIT_REALM(context, realmnames[i]);
1375
krb5_clear_error_message(context);
1376
retval = pkinit_server_plugin_init_realm(context, realmnames[i],
1377
&plgctx);
1378
if (retval)
1379
TRACE_PKINIT_SERVER_INIT_FAIL(context, realmnames[i], retval);
1380
else
1381
realm_contexts[j++] = plgctx;
1382
}
1383
1384
if (j == 0) {
1385
if (numrealms == 1) {
1386
k5_prependmsg(context, retval, "PKINIT initialization failed");
1387
} else {
1388
retval = EINVAL;
1389
k5_setmsg(context, retval,
1390
_("No realms configured correctly for pkinit support"));
1391
}
1392
goto errout;
1393
}
1394
1395
retval = load_certauth_plugins(context, realmnames, &certauth_modules);
1396
if (retval)
1397
goto errout;
1398
1399
moddata = k5calloc(1, sizeof(*moddata), &retval);
1400
if (moddata == NULL)
1401
goto errout;
1402
moddata->realm_contexts = realm_contexts;
1403
moddata->certauth_modules = certauth_modules;
1404
*moddata_out = moddata;
1405
pkiDebug("%s: returning context at %p\n", __FUNCTION__, moddata);
1406
return 0;
1407
1408
errout:
1409
free_realm_contexts(context, realm_contexts);
1410
free_certauth_handles(context, certauth_modules);
1411
return retval;
1412
}
1413
1414
static void
1415
pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx)
1416
{
1417
char **sp;
1418
1419
if (plgctx == NULL)
1420
return;
1421
1422
pkinit_fini_kdc_profile(context, plgctx);
1423
pkinit_fini_identity_opts(plgctx->idopts);
1424
pkinit_fini_identity_crypto(plgctx->idctx);
1425
pkinit_fini_plg_crypto(plgctx->cryptoctx);
1426
pkinit_fini_plg_opts(plgctx->opts);
1427
for (sp = plgctx->auth_indicators; sp != NULL && *sp != NULL; sp++)
1428
free(*sp);
1429
free(plgctx->auth_indicators);
1430
free(plgctx->realmname);
1431
free(plgctx);
1432
}
1433
1434
static void
1435
pkinit_server_plugin_fini(krb5_context context,
1436
krb5_kdcpreauth_moddata moddata)
1437
{
1438
if (moddata == NULL)
1439
return;
1440
free_realm_contexts(context, moddata->realm_contexts);
1441
free_certauth_handles(context, moddata->certauth_modules);
1442
free(moddata);
1443
}
1444
1445
static krb5_error_code
1446
pkinit_init_kdc_req_context(krb5_context context, pkinit_kdc_req_context *ctx)
1447
{
1448
krb5_error_code retval = ENOMEM;
1449
pkinit_kdc_req_context reqctx = NULL;
1450
1451
reqctx = malloc(sizeof(*reqctx));
1452
if (reqctx == NULL)
1453
return retval;
1454
memset(reqctx, 0, sizeof(*reqctx));
1455
reqctx->magic = PKINIT_CTX_MAGIC;
1456
1457
retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
1458
if (retval)
1459
goto cleanup;
1460
reqctx->rcv_auth_pack = NULL;
1461
1462
pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
1463
*ctx = reqctx;
1464
retval = 0;
1465
cleanup:
1466
if (retval)
1467
pkinit_fini_kdc_req_context(context, reqctx);
1468
1469
return retval;
1470
}
1471
1472
static void
1473
pkinit_fini_kdc_req_context(krb5_context context, void *ctx)
1474
{
1475
pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx;
1476
1477
if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) {
1478
pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx);
1479
return;
1480
}
1481
pkiDebug("%s: freeing reqctx at %p\n", __FUNCTION__, reqctx);
1482
1483
pkinit_fini_req_crypto(reqctx->cryptoctx);
1484
if (reqctx->rcv_auth_pack != NULL)
1485
free_krb5_auth_pack(&reqctx->rcv_auth_pack);
1486
1487
free(reqctx);
1488
}
1489
1490
static void
1491
pkinit_free_modreq(krb5_context context, krb5_kdcpreauth_moddata moddata,
1492
krb5_kdcpreauth_modreq modreq)
1493
{
1494
pkinit_fini_kdc_req_context(context, modreq);
1495
}
1496
1497
krb5_error_code
1498
kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
1499
krb5_plugin_vtable vtable);
1500
1501
krb5_error_code
1502
kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
1503
krb5_plugin_vtable vtable)
1504
{
1505
krb5_kdcpreauth_vtable vt;
1506
1507
if (maj_ver != 1)
1508
return KRB5_PLUGIN_VER_NOTSUPP;
1509
vt = (krb5_kdcpreauth_vtable)vtable;
1510
vt->name = "pkinit";
1511
vt->pa_type_list = supported_server_pa_types;
1512
vt->init = pkinit_server_plugin_init;
1513
vt->fini = pkinit_server_plugin_fini;
1514
vt->flags = pkinit_server_get_flags;
1515
vt->edata = pkinit_server_get_edata;
1516
vt->verify = pkinit_server_verify_padata;
1517
vt->return_padata = pkinit_server_return_padata;
1518
vt->free_modreq = pkinit_free_modreq;
1519
return 0;
1520
}
1521
1522