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
free_realm_contexts(krb5_context context,pkinit_kdc_context * realm_contexts)74 free_realm_contexts(krb5_context context, pkinit_kdc_context *realm_contexts)
75 {
76 int 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
free_certauth_handles(krb5_context context,certauth_handle * list)87 free_certauth_handles(krb5_context context, certauth_handle *list)
88 {
89 int 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
pkinit_create_edata(krb5_context context,pkinit_plg_crypto_context plg_cryptoctx,pkinit_req_crypto_context req_cryptoctx,pkinit_identity_crypto_context id_cryptoctx,pkinit_plg_opts * opts,krb5_error_code err_code,krb5_pa_data *** e_data_out)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
pkinit_server_get_edata(krb5_context context,krb5_kdc_req * request,krb5_kdcpreauth_callbacks cb,krb5_kdcpreauth_rock rock,krb5_kdcpreauth_moddata moddata,krb5_preauthtype pa_type,krb5_kdcpreauth_edata_respond_fn respond,void * arg)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
verify_client_san(krb5_context context,pkinit_kdc_context plgctx,pkinit_kdc_req_context reqctx,krb5_kdcpreauth_callbacks cb,krb5_kdcpreauth_rock rock,krb5_const_principal client,int * valid_san)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 int 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
verify_client_eku(krb5_context context,pkinit_kdc_context plgctx,pkinit_kdc_req_context reqctx,int * eku_accepted)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
authorize_cert(krb5_context context,certauth_handle * certauth_modules,pkinit_kdc_context plgctx,pkinit_kdc_req_context reqctx,krb5_kdcpreauth_callbacks cb,krb5_kdcpreauth_rock rock,krb5_principal client,krb5_boolean * hwauth_out)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
check_log_freshness(krb5_context context,pkinit_kdc_context plgctx,krb5_kdc_req * request,krb5_boolean valid_freshness_token)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
pkinit_server_verify_padata(krb5_context context,krb5_data * req_pkt,krb5_kdc_req * request,krb5_enc_tkt_part * enc_tkt_reply,krb5_pa_data * data,krb5_kdcpreauth_callbacks cb,krb5_kdcpreauth_rock rock,krb5_kdcpreauth_moddata moddata,krb5_kdcpreauth_verify_respond_fn respond,void * arg)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 pkinit_kdc_context plgctx = NULL;
432 pkinit_kdc_req_context reqctx = NULL;
433 krb5_checksum cksum = {0, 0, 0, NULL};
434 krb5_data *der_req = NULL;
435 krb5_data k5data, *ftoken;
436 int is_signed = 1;
437 krb5_pa_data **e_data = NULL;
438 krb5_kdcpreauth_modreq modreq = NULL;
439 krb5_boolean valid_freshness_token = FALSE, hwauth = FALSE;
440 char **sp;
441
442 pkiDebug("pkinit_verify_padata: entered!\n");
443 if (data == NULL || data->length <= 0 || data->contents == NULL) {
444 (*respond)(arg, EINVAL, NULL, NULL, NULL);
445 return;
446 }
447
448
449 if (moddata == NULL) {
450 (*respond)(arg, EINVAL, NULL, NULL, NULL);
451 return;
452 }
453
454 plgctx = pkinit_find_realm_context(context, moddata, request->server);
455 if (plgctx == NULL) {
456 (*respond)(arg, EINVAL, NULL, NULL, NULL);
457 return;
458 }
459
460 #ifdef DEBUG_ASN1
461 print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req");
462 #endif
463 /* create a per-request context */
464 retval = pkinit_init_kdc_req_context(context, &reqctx);
465 if (retval)
466 goto cleanup;
467 reqctx->pa_type = data->pa_type;
468
469 PADATA_TO_KRB5DATA(data, &k5data);
470
471 if (data->pa_type != KRB5_PADATA_PK_AS_REQ) {
472 pkiDebug("unrecognized pa_type = %d\n", data->pa_type);
473 retval = EINVAL;
474 goto cleanup;
475 }
476
477 TRACE_PKINIT_SERVER_PADATA_VERIFY(context);
478 retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp);
479 if (retval) {
480 pkiDebug("decode_krb5_pa_pk_as_req failed\n");
481 goto cleanup;
482 }
483 #ifdef DEBUG_ASN1
484 print_buffer_bin(reqp->signedAuthPack.data, reqp->signedAuthPack.length,
485 "/tmp/kdc_signed_data");
486 #endif
487 retval = cms_signeddata_verify(context, plgctx->cryptoctx,
488 reqctx->cryptoctx, plgctx->idctx,
489 CMS_SIGN_CLIENT,
490 plgctx->opts->require_crl_checking,
491 (unsigned char *)reqp->signedAuthPack.data,
492 reqp->signedAuthPack.length,
493 (unsigned char **)&authp_data.data,
494 &authp_data.length,
495 (unsigned char **)&krb5_authz.data,
496 &krb5_authz.length, &is_signed);
497 if (retval) {
498 TRACE_PKINIT_SERVER_PADATA_VERIFY_FAIL(context);
499 goto cleanup;
500 }
501 if (is_signed) {
502 retval = authorize_cert(context, moddata->certauth_modules, plgctx,
503 reqctx, cb, rock, request->client, &hwauth);
504 if (retval)
505 goto cleanup;
506
507 } else { /* !is_signed */
508 if (!krb5_principal_compare(context, request->client,
509 krb5_anonymous_principal())) {
510 retval = KRB5KDC_ERR_PREAUTH_FAILED;
511 krb5_set_error_message(context, retval,
512 _("Pkinit request not signed, but client "
513 "not anonymous."));
514 goto cleanup;
515 }
516 }
517 #ifdef DEBUG_ASN1
518 print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack");
519 #endif
520
521 OCTETDATA_TO_KRB5DATA(&authp_data, &k5data);
522 retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack);
523 if (retval) {
524 pkiDebug("failed to decode krb5_auth_pack\n");
525 goto cleanup;
526 }
527
528 retval = krb5_check_clockskew(context, auth_pack->pkAuthenticator.ctime);
529 if (retval)
530 goto cleanup;
531
532 /* check dh parameters */
533 if (auth_pack->clientPublicValue.length > 0) {
534 retval = server_check_dh(context, plgctx->cryptoctx,
535 reqctx->cryptoctx, plgctx->idctx,
536 &auth_pack->clientPublicValue,
537 plgctx->opts->dh_min_bits);
538 if (retval) {
539 pkiDebug("bad dh parameters\n");
540 goto cleanup;
541 }
542 } else if (!is_signed) {
543 /*Anonymous pkinit requires DH*/
544 retval = KRB5KDC_ERR_PREAUTH_FAILED;
545 krb5_set_error_message(context, retval,
546 _("Anonymous pkinit without DH public "
547 "value not supported."));
548 goto cleanup;
549 }
550 der_req = cb->request_body(context, rock);
551 retval = krb5_c_make_checksum(context, CKSUMTYPE_SHA1, NULL, 0, der_req,
552 &cksum);
553 if (retval) {
554 pkiDebug("unable to calculate AS REQ checksum\n");
555 goto cleanup;
556 }
557 if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length ||
558 k5_bcmp(cksum.contents, auth_pack->pkAuthenticator.paChecksum.contents,
559 cksum.length) != 0) {
560 pkiDebug("failed to match the checksum\n");
561 #ifdef DEBUG_CKSUM
562 pkiDebug("calculating checksum on buf size (%d)\n", req_pkt->length);
563 print_buffer(req_pkt->data, req_pkt->length);
564 pkiDebug("received checksum type=%d size=%d ",
565 auth_pack->pkAuthenticator.paChecksum.checksum_type,
566 auth_pack->pkAuthenticator.paChecksum.length);
567 print_buffer(auth_pack->pkAuthenticator.paChecksum.contents,
568 auth_pack->pkAuthenticator.paChecksum.length);
569 pkiDebug("expected checksum type=%d size=%d ",
570 cksum.checksum_type, cksum.length);
571 print_buffer(cksum.contents, cksum.length);
572 #endif
573
574 retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
575 goto cleanup;
576 }
577
578 ftoken = auth_pack->pkAuthenticator.freshnessToken;
579 if (ftoken != NULL) {
580 retval = cb->check_freshness_token(context, rock, ftoken);
581 if (retval)
582 goto cleanup;
583 valid_freshness_token = TRUE;
584 }
585
586 /* check if kdcPkId present and match KDC's subjectIdentifier */
587 if (reqp->kdcPkId.data != NULL) {
588 int valid_kdcPkId = 0;
589 retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx,
590 reqctx->cryptoctx, plgctx->idctx,
591 (unsigned char *)reqp->kdcPkId.data,
592 reqp->kdcPkId.length, &valid_kdcPkId);
593 if (retval)
594 goto cleanup;
595 if (!valid_kdcPkId) {
596 pkiDebug("kdcPkId in AS_REQ does not match KDC's cert; "
597 "RFC says to ignore and proceed\n");
598 }
599 }
600 /* remember the decoded auth_pack for verify_padata routine */
601 reqctx->rcv_auth_pack = auth_pack;
602 auth_pack = NULL;
603
604 if (is_signed) {
605 retval = check_log_freshness(context, plgctx, request,
606 valid_freshness_token);
607 if (retval)
608 goto cleanup;
609 }
610
611 if (is_signed && plgctx->auth_indicators != NULL) {
612 /* Assert configured authentication indicators. */
613 for (sp = plgctx->auth_indicators; *sp != NULL; sp++) {
614 retval = cb->add_auth_indicator(context, rock, *sp);
615 if (retval)
616 goto cleanup;
617 }
618 }
619
620 /* remember to set the PREAUTH flag in the reply */
621 enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
622 if (hwauth)
623 enc_tkt_reply->flags |= TKT_FLG_HW_AUTH;
624 modreq = (krb5_kdcpreauth_modreq)reqctx;
625 reqctx = NULL;
626
627 cleanup:
628 if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) {
629 pkiDebug("pkinit_verify_padata failed: creating e-data\n");
630 if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx,
631 plgctx->idctx, plgctx->opts, retval, &e_data))
632 pkiDebug("pkinit_create_edata failed\n");
633 }
634
635 free_krb5_pa_pk_as_req(&reqp);
636 free(cksum.contents);
637 free(authp_data.data);
638 free(krb5_authz.data);
639 if (reqctx != NULL)
640 pkinit_fini_kdc_req_context(context, reqctx);
641 free_krb5_auth_pack(&auth_pack);
642
643 (*respond)(arg, retval, modreq, e_data, NULL);
644 }
645 static krb5_error_code
return_pkinit_kx(krb5_context context,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_keyblock * encrypting_key,krb5_pa_data ** out_padata)646 return_pkinit_kx(krb5_context context, krb5_kdc_req *request,
647 krb5_kdc_rep *reply, krb5_keyblock *encrypting_key,
648 krb5_pa_data **out_padata)
649 {
650 krb5_error_code ret = 0;
651 krb5_keyblock *session = reply->ticket->enc_part2->session;
652 krb5_keyblock *new_session = NULL;
653 krb5_pa_data *pa = NULL;
654 krb5_enc_data enc;
655 krb5_data *scratch = NULL;
656
657 *out_padata = NULL;
658 enc.ciphertext.data = NULL;
659 if (!krb5_principal_compare(context, request->client,
660 krb5_anonymous_principal()))
661 return 0;
662 /*
663 * The KDC contribution key needs to be a fresh key of an enctype supported
664 * by the client and server. The existing session key meets these
665 * requirements so we use it.
666 */
667 ret = krb5_c_fx_cf2_simple(context, session, "PKINIT",
668 encrypting_key, "KEYEXCHANGE",
669 &new_session);
670 if (ret)
671 goto cleanup;
672 ret = encode_krb5_encryption_key( session, &scratch);
673 if (ret)
674 goto cleanup;
675 ret = krb5_encrypt_helper(context, encrypting_key,
676 KRB5_KEYUSAGE_PA_PKINIT_KX, scratch, &enc);
677 if (ret)
678 goto cleanup;
679 memset(scratch->data, 0, scratch->length);
680 krb5_free_data(context, scratch);
681 scratch = NULL;
682 ret = encode_krb5_enc_data(&enc, &scratch);
683 if (ret)
684 goto cleanup;
685 pa = malloc(sizeof(krb5_pa_data));
686 if (pa == NULL) {
687 ret = ENOMEM;
688 goto cleanup;
689 }
690 pa->pa_type = KRB5_PADATA_PKINIT_KX;
691 pa->length = scratch->length;
692 pa->contents = (krb5_octet *) scratch->data;
693 *out_padata = pa;
694 scratch->data = NULL;
695 memset(session->contents, 0, session->length);
696 krb5_free_keyblock_contents(context, session);
697 *session = *new_session;
698 new_session->contents = NULL;
699 cleanup:
700 krb5_free_data_contents(context, &enc.ciphertext);
701 krb5_free_keyblock(context, new_session);
702 krb5_free_data(context, scratch);
703 return ret;
704 }
705
706 static krb5_error_code
pkinit_pick_kdf_alg(krb5_context context,krb5_data ** kdf_list,krb5_data ** alg_oid)707 pkinit_pick_kdf_alg(krb5_context context, krb5_data **kdf_list,
708 krb5_data **alg_oid)
709 {
710 krb5_error_code retval = 0;
711 krb5_data *req_oid = NULL;
712 const krb5_data *supp_oid = NULL;
713 krb5_data *tmp_oid = NULL;
714 int i, j = 0;
715
716 /* if we don't find a match, return NULL value */
717 *alg_oid = NULL;
718
719 /* for each of the OIDs that the server supports... */
720 for (i = 0; NULL != (supp_oid = supported_kdf_alg_ids[i]); i++) {
721 /* if the requested OID is in the client's list, use it. */
722 for (j = 0; NULL != (req_oid = kdf_list[j]); j++) {
723 if ((req_oid->length == supp_oid->length) &&
724 (0 == memcmp(req_oid->data, supp_oid->data, req_oid->length))) {
725 tmp_oid = k5alloc(sizeof(krb5_data), &retval);
726 if (retval)
727 goto cleanup;
728 tmp_oid->data = k5memdup(supp_oid->data, supp_oid->length,
729 &retval);
730 if (retval)
731 goto cleanup;
732 tmp_oid->length = supp_oid->length;
733 *alg_oid = tmp_oid;
734 /* don't free the OID in clean-up if we are returning it */
735 tmp_oid = NULL;
736 goto cleanup;
737 }
738 }
739 }
740 cleanup:
741 if (tmp_oid)
742 krb5_free_data(context, tmp_oid);
743 return retval;
744 }
745
746 static krb5_error_code
pkinit_server_return_padata(krb5_context context,krb5_pa_data * padata,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_keyblock * encrypting_key,krb5_pa_data ** send_pa,krb5_kdcpreauth_callbacks cb,krb5_kdcpreauth_rock rock,krb5_kdcpreauth_moddata moddata,krb5_kdcpreauth_modreq modreq)747 pkinit_server_return_padata(krb5_context context,
748 krb5_pa_data * padata,
749 krb5_data *req_pkt,
750 krb5_kdc_req * request,
751 krb5_kdc_rep * reply,
752 krb5_keyblock * encrypting_key,
753 krb5_pa_data ** send_pa,
754 krb5_kdcpreauth_callbacks cb,
755 krb5_kdcpreauth_rock rock,
756 krb5_kdcpreauth_moddata moddata,
757 krb5_kdcpreauth_modreq modreq)
758 {
759 krb5_error_code retval = 0;
760 krb5_data scratch = {0, 0, NULL};
761 krb5_pa_pk_as_req *reqp = NULL;
762 int i = 0;
763
764 unsigned char *dh_pubkey = NULL, *server_key = NULL;
765 unsigned int server_key_len = 0, dh_pubkey_len = 0;
766 krb5_keyblock reply_key = { 0 };
767
768 krb5_kdc_dh_key_info dhkey_info;
769 krb5_data *encoded_dhkey_info = NULL;
770 krb5_pa_pk_as_rep *rep = NULL;
771 krb5_data *out_data = NULL;
772 krb5_data secret;
773
774 krb5_enctype enctype = -1;
775
776 krb5_reply_key_pack *key_pack = NULL;
777 krb5_data *encoded_key_pack = NULL;
778
779 pkinit_kdc_context plgctx;
780 pkinit_kdc_req_context reqctx;
781
782 *send_pa = NULL;
783 if (padata->pa_type == KRB5_PADATA_PKINIT_KX) {
784 return return_pkinit_kx(context, request, reply,
785 encrypting_key, send_pa);
786 }
787 if (padata->length <= 0 || padata->contents == NULL)
788 return 0;
789
790 if (modreq == NULL) {
791 pkiDebug("missing request context \n");
792 return EINVAL;
793 }
794
795 plgctx = pkinit_find_realm_context(context, moddata, request->server);
796 if (plgctx == NULL) {
797 pkiDebug("Unable to locate correct realm context\n");
798 return ENOENT;
799 }
800
801 TRACE_PKINIT_SERVER_RETURN_PADATA(context);
802 reqctx = (pkinit_kdc_req_context)modreq;
803
804 for(i = 0; i < request->nktypes; i++) {
805 enctype = request->ktype[i];
806 if (!krb5_c_valid_enctype(enctype))
807 continue;
808 else {
809 pkiDebug("KDC picked etype = %d\n", enctype);
810 break;
811 }
812 }
813
814 if (i == request->nktypes) {
815 retval = KRB5KDC_ERR_ETYPE_NOSUPP;
816 goto cleanup;
817 }
818
819 init_krb5_pa_pk_as_rep(&rep);
820 if (rep == NULL) {
821 retval = ENOMEM;
822 goto cleanup;
823 }
824 /* let's assume it's RSA. we'll reset it to DH if needed */
825 rep->choice = choice_pa_pk_as_rep_encKeyPack;
826
827 if (reqctx->rcv_auth_pack != NULL &&
828 reqctx->rcv_auth_pack->clientPublicValue.length > 0) {
829 rep->choice = choice_pa_pk_as_rep_dhInfo;
830
831 pkiDebug("received DH key delivery AS REQ\n");
832 retval = server_process_dh(context, plgctx->cryptoctx,
833 reqctx->cryptoctx, plgctx->idctx,
834 &dh_pubkey, &dh_pubkey_len,
835 &server_key, &server_key_len);
836 if (retval) {
837 pkiDebug("failed to process/create dh parameters\n");
838 goto cleanup;
839 }
840
841 /*
842 * This is DH, so don't generate the key until after we
843 * encode the reply, because the encoded reply is needed
844 * to generate the key in some cases.
845 */
846
847 dhkey_info.subjectPublicKey.length = dh_pubkey_len;
848 dhkey_info.subjectPublicKey.data = (char *)dh_pubkey;
849 dhkey_info.nonce = request->nonce;
850 dhkey_info.dhKeyExpiration = 0;
851
852 retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info,
853 &encoded_dhkey_info);
854 if (retval) {
855 pkiDebug("encode_krb5_kdc_dh_key_info failed\n");
856 goto cleanup;
857 }
858 #ifdef DEBUG_ASN1
859 print_buffer_bin((unsigned char *)encoded_dhkey_info->data,
860 encoded_dhkey_info->length,
861 "/tmp/kdc_dh_key_info");
862 #endif
863
864 retval = cms_signeddata_create(context, plgctx->cryptoctx,
865 reqctx->cryptoctx, plgctx->idctx,
866 CMS_SIGN_SERVER,
867 (unsigned char *)
868 encoded_dhkey_info->data,
869 encoded_dhkey_info->length,
870 (unsigned char **)
871 &rep->u.dh_Info.dhSignedData.data,
872 &rep->u.dh_Info.dhSignedData.length);
873 if (retval) {
874 pkiDebug("failed to create pkcs7 signed data\n");
875 goto cleanup;
876 }
877
878 } else {
879 pkiDebug("received RSA key delivery AS REQ\n");
880
881 init_krb5_reply_key_pack(&key_pack);
882 if (key_pack == NULL) {
883 retval = ENOMEM;
884 goto cleanup;
885 }
886
887 retval = krb5_c_make_random_key(context, enctype, &key_pack->replyKey);
888 if (retval) {
889 pkiDebug("unable to make a session key\n");
890 goto cleanup;
891 }
892
893 retval = krb5_c_make_checksum(context, 0, &key_pack->replyKey,
894 KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
895 req_pkt, &key_pack->asChecksum);
896 if (retval) {
897 pkiDebug("unable to calculate AS REQ checksum\n");
898 goto cleanup;
899 }
900 #ifdef DEBUG_CKSUM
901 pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length);
902 print_buffer(req_pkt->data, req_pkt->length);
903 pkiDebug("checksum size = %d\n", key_pack->asChecksum.length);
904 print_buffer(key_pack->asChecksum.contents,
905 key_pack->asChecksum.length);
906 pkiDebug("encrypting key (%d)\n", key_pack->replyKey.length);
907 print_buffer(key_pack->replyKey.contents, key_pack->replyKey.length);
908 #endif
909
910 retval = k5int_encode_krb5_reply_key_pack(key_pack,
911 &encoded_key_pack);
912 if (retval) {
913 pkiDebug("failed to encode reply_key_pack\n");
914 goto cleanup;
915 }
916
917 rep->choice = choice_pa_pk_as_rep_encKeyPack;
918 retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
919 reqctx->cryptoctx, plgctx->idctx,
920 padata->pa_type,
921 (unsigned char *)
922 encoded_key_pack->data,
923 encoded_key_pack->length,
924 (unsigned char **)
925 &rep->u.encKeyPack.data,
926 &rep->u.encKeyPack.length);
927 if (retval) {
928 pkiDebug("failed to create pkcs7 enveloped data: %s\n",
929 error_message(retval));
930 goto cleanup;
931 }
932 #ifdef DEBUG_ASN1
933 print_buffer_bin((unsigned char *)encoded_key_pack->data,
934 encoded_key_pack->length,
935 "/tmp/kdc_key_pack");
936 print_buffer_bin(rep->u.encKeyPack.data, rep->u.encKeyPack.length,
937 "/tmp/kdc_enc_key_pack");
938 #endif
939
940 retval = cb->replace_reply_key(context, rock, &key_pack->replyKey,
941 FALSE);
942 if (retval)
943 goto cleanup;
944 }
945
946 if (rep->choice == choice_pa_pk_as_rep_dhInfo &&
947 ((reqctx->rcv_auth_pack != NULL &&
948 reqctx->rcv_auth_pack->supportedKDFs != NULL))) {
949
950 /* If using the alg-agility KDF, put the algorithm in the reply
951 * before encoding it.
952 */
953 if (reqctx->rcv_auth_pack != NULL &&
954 reqctx->rcv_auth_pack->supportedKDFs != NULL) {
955 retval = pkinit_pick_kdf_alg(context, reqctx->rcv_auth_pack->supportedKDFs,
956 &(rep->u.dh_Info.kdfID));
957 if (retval) {
958 pkiDebug("pkinit_pick_kdf_alg failed: %s\n",
959 error_message(retval));
960 goto cleanup;
961 }
962 }
963 }
964
965 retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data);
966 if (retval) {
967 pkiDebug("failed to encode AS_REP\n");
968 goto cleanup;
969 }
970 #ifdef DEBUG_ASN1
971 if (out_data != NULL)
972 print_buffer_bin((unsigned char *)out_data->data, out_data->length,
973 "/tmp/kdc_as_rep");
974 #endif
975
976 /* If this is DH, we haven't computed the key yet, so do it now. */
977 if (rep->choice == choice_pa_pk_as_rep_dhInfo) {
978
979 /* If mutually supported KDFs were found, use the algorithm agility
980 * KDF. */
981 if (rep->u.dh_Info.kdfID) {
982 secret.data = (char *)server_key;
983 secret.length = server_key_len;
984
985 retval = pkinit_alg_agility_kdf(context, &secret,
986 rep->u.dh_Info.kdfID,
987 request->client, request->server,
988 enctype, req_pkt, out_data,
989 &reply_key);
990 if (retval) {
991 pkiDebug("pkinit_alg_agility_kdf failed: %s\n",
992 error_message(retval));
993 goto cleanup;
994 }
995
996 /* Otherwise, use the older octetstring2key() function */
997 } else {
998 retval = pkinit_octetstring2key(context, enctype, server_key,
999 server_key_len, &reply_key);
1000 if (retval) {
1001 pkiDebug("pkinit_octetstring2key failed: %s\n",
1002 error_message(retval));
1003 goto cleanup;
1004 }
1005 }
1006 retval = cb->replace_reply_key(context, rock, &reply_key, FALSE);
1007 if (retval)
1008 goto cleanup;
1009 }
1010
1011 *send_pa = malloc(sizeof(krb5_pa_data));
1012 if (*send_pa == NULL) {
1013 retval = ENOMEM;
1014 free(out_data->data);
1015 free(out_data);
1016 out_data = NULL;
1017 goto cleanup;
1018 }
1019 (*send_pa)->magic = KV5M_PA_DATA;
1020 (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP;
1021 (*send_pa)->length = out_data->length;
1022 (*send_pa)->contents = (krb5_octet *) out_data->data;
1023
1024 cleanup:
1025 free(scratch.data);
1026 free(out_data);
1027 if (encoded_dhkey_info != NULL)
1028 krb5_free_data(context, encoded_dhkey_info);
1029 if (encoded_key_pack != NULL)
1030 krb5_free_data(context, encoded_key_pack);
1031 free(dh_pubkey);
1032 free(server_key);
1033 free_krb5_pa_pk_as_req(&reqp);
1034 free_krb5_pa_pk_as_rep(&rep);
1035 free_krb5_reply_key_pack(&key_pack);
1036 krb5_free_keyblock_contents(context, &reply_key);
1037
1038 if (retval)
1039 pkiDebug("pkinit_verify_padata failure");
1040
1041 return retval;
1042 }
1043
1044 static int
pkinit_server_get_flags(krb5_context kcontext,krb5_preauthtype patype)1045 pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype)
1046 {
1047 if (patype == KRB5_PADATA_PKINIT_KX)
1048 return PA_INFO;
1049 /* PKINIT does not normally set the hw-authent ticket flag, but a
1050 * certauth module can cause it to do so. */
1051 return PA_SUFFICIENT | PA_REPLACES_KEY | PA_TYPED_E_DATA | PA_HARDWARE;
1052 }
1053
1054 static krb5_preauthtype supported_server_pa_types[] = {
1055 KRB5_PADATA_PK_AS_REQ,
1056 KRB5_PADATA_PKINIT_KX,
1057 0
1058 };
1059
1060 static void
pkinit_fini_kdc_profile(krb5_context context,pkinit_kdc_context plgctx)1061 pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
1062 {
1063 /*
1064 * There is nothing currently allocated by pkinit_init_kdc_profile()
1065 * which needs to be freed here.
1066 */
1067 }
1068
1069 static krb5_error_code
pkinit_init_kdc_profile(krb5_context context,pkinit_kdc_context plgctx)1070 pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
1071 {
1072 krb5_error_code retval;
1073 char *eku_string = NULL, *ocsp_check = NULL;
1074
1075 pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname);
1076 retval = pkinit_kdcdefault_string(context, plgctx->realmname,
1077 KRB5_CONF_PKINIT_IDENTITY,
1078 &plgctx->idopts->identity);
1079 if (retval != 0 || NULL == plgctx->idopts->identity) {
1080 retval = EINVAL;
1081 krb5_set_error_message(context, retval,
1082 _("No pkinit_identity supplied for realm %s"),
1083 plgctx->realmname);
1084 goto errout;
1085 }
1086
1087 retval = pkinit_kdcdefault_strings(context, plgctx->realmname,
1088 KRB5_CONF_PKINIT_ANCHORS,
1089 &plgctx->idopts->anchors);
1090 if (retval != 0 || NULL == plgctx->idopts->anchors) {
1091 retval = EINVAL;
1092 krb5_set_error_message(context, retval,
1093 _("No pkinit_anchors supplied for realm %s"),
1094 plgctx->realmname);
1095 goto errout;
1096 }
1097
1098 pkinit_kdcdefault_strings(context, plgctx->realmname,
1099 KRB5_CONF_PKINIT_POOL,
1100 &plgctx->idopts->intermediates);
1101
1102 pkinit_kdcdefault_strings(context, plgctx->realmname,
1103 KRB5_CONF_PKINIT_REVOKE,
1104 &plgctx->idopts->crls);
1105
1106 pkinit_kdcdefault_string(context, plgctx->realmname,
1107 KRB5_CONF_PKINIT_KDC_OCSP,
1108 &ocsp_check);
1109 if (ocsp_check != NULL) {
1110 free(ocsp_check);
1111 retval = ENOTSUP;
1112 krb5_set_error_message(context, retval,
1113 _("OCSP is not supported: (realm: %s)"),
1114 plgctx->realmname);
1115 goto errout;
1116 }
1117
1118 pkinit_kdcdefault_integer(context, plgctx->realmname,
1119 KRB5_CONF_PKINIT_DH_MIN_BITS,
1120 PKINIT_DEFAULT_DH_MIN_BITS,
1121 &plgctx->opts->dh_min_bits);
1122 if (plgctx->opts->dh_min_bits < PKINIT_DH_MIN_CONFIG_BITS) {
1123 pkiDebug("%s: invalid value (%d < %d) for pkinit_dh_min_bits, "
1124 "using default value (%d) instead\n", __FUNCTION__,
1125 plgctx->opts->dh_min_bits, PKINIT_DH_MIN_CONFIG_BITS,
1126 PKINIT_DEFAULT_DH_MIN_BITS);
1127 plgctx->opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS;
1128 }
1129
1130 pkinit_kdcdefault_boolean(context, plgctx->realmname,
1131 KRB5_CONF_PKINIT_ALLOW_UPN,
1132 0, &plgctx->opts->allow_upn);
1133
1134 pkinit_kdcdefault_boolean(context, plgctx->realmname,
1135 KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING,
1136 0, &plgctx->opts->require_crl_checking);
1137
1138 pkinit_kdcdefault_boolean(context, plgctx->realmname,
1139 KRB5_CONF_PKINIT_REQUIRE_FRESHNESS,
1140 0, &plgctx->opts->require_freshness);
1141
1142 pkinit_kdcdefault_string(context, plgctx->realmname,
1143 KRB5_CONF_PKINIT_EKU_CHECKING,
1144 &eku_string);
1145 if (eku_string != NULL) {
1146 if (strcasecmp(eku_string, "kpClientAuth") == 0) {
1147 plgctx->opts->require_eku = 1;
1148 plgctx->opts->accept_secondary_eku = 0;
1149 } else if (strcasecmp(eku_string, "scLogin") == 0) {
1150 plgctx->opts->require_eku = 1;
1151 plgctx->opts->accept_secondary_eku = 1;
1152 } else if (strcasecmp(eku_string, "none") == 0) {
1153 plgctx->opts->require_eku = 0;
1154 plgctx->opts->accept_secondary_eku = 0;
1155 } else {
1156 pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
1157 __FUNCTION__, eku_string);
1158 }
1159 free(eku_string);
1160 }
1161
1162 pkinit_kdcdefault_strings(context, plgctx->realmname,
1163 KRB5_CONF_PKINIT_INDICATOR,
1164 &plgctx->auth_indicators);
1165
1166 return 0;
1167 errout:
1168 pkinit_fini_kdc_profile(context, plgctx);
1169 return retval;
1170 }
1171
1172 static pkinit_kdc_context
pkinit_find_realm_context(krb5_context context,krb5_kdcpreauth_moddata moddata,krb5_principal princ)1173 pkinit_find_realm_context(krb5_context context,
1174 krb5_kdcpreauth_moddata moddata,
1175 krb5_principal princ)
1176 {
1177 int i;
1178 pkinit_kdc_context *realm_contexts;
1179
1180 if (moddata == NULL)
1181 return NULL;
1182
1183 realm_contexts = moddata->realm_contexts;
1184 if (realm_contexts == NULL)
1185 return NULL;
1186
1187 for (i = 0; realm_contexts[i] != NULL; i++) {
1188 pkinit_kdc_context p = realm_contexts[i];
1189
1190 if ((p->realmname_len == princ->realm.length) &&
1191 (strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) {
1192 pkiDebug("%s: returning context at %p for realm '%s'\n",
1193 __FUNCTION__, p, p->realmname);
1194 return p;
1195 }
1196 }
1197 pkiDebug("%s: unable to find realm context for realm '%.*s'\n",
1198 __FUNCTION__, princ->realm.length, princ->realm.data);
1199 return NULL;
1200 }
1201
1202 static int
pkinit_server_plugin_init_realm(krb5_context context,const char * realmname,pkinit_kdc_context * pplgctx)1203 pkinit_server_plugin_init_realm(krb5_context context, const char *realmname,
1204 pkinit_kdc_context *pplgctx)
1205 {
1206 krb5_error_code retval = ENOMEM;
1207 pkinit_kdc_context plgctx = NULL;
1208
1209 *pplgctx = NULL;
1210
1211 plgctx = calloc(1, sizeof(*plgctx));
1212 if (plgctx == NULL)
1213 goto errout;
1214
1215 pkiDebug("%s: initializing context at %p for realm '%s'\n",
1216 __FUNCTION__, plgctx, realmname);
1217 memset(plgctx, 0, sizeof(*plgctx));
1218 plgctx->magic = PKINIT_CTX_MAGIC;
1219
1220 plgctx->realmname = strdup(realmname);
1221 if (plgctx->realmname == NULL)
1222 goto errout;
1223 plgctx->realmname_len = strlen(plgctx->realmname);
1224
1225 retval = pkinit_init_plg_crypto(&plgctx->cryptoctx);
1226 if (retval)
1227 goto errout;
1228
1229 retval = pkinit_init_plg_opts(&plgctx->opts);
1230 if (retval)
1231 goto errout;
1232
1233 retval = pkinit_init_identity_crypto(&plgctx->idctx);
1234 if (retval)
1235 goto errout;
1236
1237 retval = pkinit_init_identity_opts(&plgctx->idopts);
1238 if (retval)
1239 goto errout;
1240
1241 retval = pkinit_init_kdc_profile(context, plgctx);
1242 if (retval)
1243 goto errout;
1244
1245 retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL,
1246 plgctx->idopts, plgctx->idctx,
1247 NULL, NULL, NULL);
1248 if (retval)
1249 goto errout;
1250 retval = pkinit_identity_prompt(context, plgctx->cryptoctx, NULL,
1251 plgctx->idopts, plgctx->idctx,
1252 NULL, NULL, 0, NULL);
1253 if (retval)
1254 goto errout;
1255
1256 pkiDebug("%s: returning context at %p for realm '%s'\n",
1257 __FUNCTION__, plgctx, realmname);
1258 *pplgctx = plgctx;
1259 retval = 0;
1260
1261 errout:
1262 if (retval)
1263 pkinit_server_plugin_fini_realm(context, plgctx);
1264
1265 return retval;
1266 }
1267
1268 static krb5_error_code
pkinit_san_authorize(krb5_context context,krb5_certauth_moddata moddata,const uint8_t * cert,size_t cert_len,krb5_const_principal princ,const void * opts,const struct _krb5_db_entry_new * db_entry,char *** authinds_out)1269 pkinit_san_authorize(krb5_context context, krb5_certauth_moddata moddata,
1270 const uint8_t *cert, size_t cert_len,
1271 krb5_const_principal princ, const void *opts,
1272 const struct _krb5_db_entry_new *db_entry,
1273 char ***authinds_out)
1274 {
1275 krb5_error_code ret;
1276 int valid_san;
1277 const struct certauth_req_opts *req_opts = opts;
1278
1279 *authinds_out = NULL;
1280
1281 ret = verify_client_san(context, req_opts->plgctx, req_opts->reqctx,
1282 req_opts->cb, req_opts->rock, princ, &valid_san);
1283 if (ret == ENOENT)
1284 return KRB5_PLUGIN_NO_HANDLE;
1285 else if (ret)
1286 return ret;
1287
1288 if (!valid_san) {
1289 TRACE_PKINIT_SERVER_SAN_REJECT(context);
1290 return KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
1291 }
1292
1293 return 0;
1294 }
1295
1296 static krb5_error_code
pkinit_eku_authorize(krb5_context context,krb5_certauth_moddata moddata,const uint8_t * cert,size_t cert_len,krb5_const_principal princ,const void * opts,const struct _krb5_db_entry_new * db_entry,char *** authinds_out)1297 pkinit_eku_authorize(krb5_context context, krb5_certauth_moddata moddata,
1298 const uint8_t *cert, size_t cert_len,
1299 krb5_const_principal princ, const void *opts,
1300 const struct _krb5_db_entry_new *db_entry,
1301 char ***authinds_out)
1302 {
1303 krb5_error_code ret;
1304 int valid_eku;
1305 const struct certauth_req_opts *req_opts = opts;
1306
1307 *authinds_out = NULL;
1308
1309 /* Verify the client EKU. */
1310 ret = verify_client_eku(context, req_opts->plgctx, req_opts->reqctx,
1311 &valid_eku);
1312 if (ret)
1313 return ret;
1314
1315 if (!valid_eku) {
1316 TRACE_PKINIT_SERVER_EKU_REJECT(context);
1317 return KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
1318 }
1319
1320 return KRB5_PLUGIN_NO_HANDLE;
1321 }
1322
1323 static krb5_error_code
certauth_pkinit_san_initvt(krb5_context context,int maj_ver,int min_ver,krb5_plugin_vtable vtable)1324 certauth_pkinit_san_initvt(krb5_context context, int maj_ver, int min_ver,
1325 krb5_plugin_vtable vtable)
1326 {
1327 krb5_certauth_vtable vt;
1328
1329 if (maj_ver != 1)
1330 return KRB5_PLUGIN_VER_NOTSUPP;
1331 vt = (krb5_certauth_vtable)vtable;
1332 vt->name = "pkinit_san";
1333 vt->authorize = pkinit_san_authorize;
1334 return 0;
1335 }
1336
1337 static krb5_error_code
certauth_pkinit_eku_initvt(krb5_context context,int maj_ver,int min_ver,krb5_plugin_vtable vtable)1338 certauth_pkinit_eku_initvt(krb5_context context, int maj_ver, int min_ver,
1339 krb5_plugin_vtable vtable)
1340 {
1341 krb5_certauth_vtable vt;
1342
1343 if (maj_ver != 1)
1344 return KRB5_PLUGIN_VER_NOTSUPP;
1345 vt = (krb5_certauth_vtable)vtable;
1346 vt->name = "pkinit_eku";
1347 vt->authorize = pkinit_eku_authorize;
1348 return 0;
1349 }
1350
1351 /*
1352 * Do certificate auth based on a match expression in the pkinit_cert_match
1353 * attribute string. An expression should be in the same form as those used
1354 * for the pkinit_cert_match configuration option.
1355 */
1356 static krb5_error_code
dbmatch_authorize(krb5_context context,krb5_certauth_moddata moddata,const uint8_t * cert,size_t cert_len,krb5_const_principal princ,const void * opts,const struct _krb5_db_entry_new * db_entry,char *** authinds_out)1357 dbmatch_authorize(krb5_context context, krb5_certauth_moddata moddata,
1358 const uint8_t *cert, size_t cert_len,
1359 krb5_const_principal princ, const void *opts,
1360 const struct _krb5_db_entry_new *db_entry,
1361 char ***authinds_out)
1362 {
1363 krb5_error_code ret;
1364 const struct certauth_req_opts *req_opts = opts;
1365 char *pattern;
1366 krb5_boolean matched;
1367
1368 *authinds_out = NULL;
1369
1370 /* Fetch the matching pattern. Pass if it isn't specified. */
1371 ret = req_opts->cb->get_string(context, req_opts->rock,
1372 "pkinit_cert_match", &pattern);
1373 if (ret)
1374 return ret;
1375 if (pattern == NULL)
1376 return KRB5_PLUGIN_NO_HANDLE;
1377
1378 /* Check the certificate against the match expression. */
1379 ret = pkinit_client_cert_match(context, req_opts->plgctx->cryptoctx,
1380 req_opts->reqctx->cryptoctx, pattern,
1381 &matched);
1382 req_opts->cb->free_string(context, req_opts->rock, pattern);
1383 if (ret)
1384 return ret;
1385 return matched ? 0 : KRB5KDC_ERR_CERTIFICATE_MISMATCH;
1386 }
1387
1388 static krb5_error_code
certauth_dbmatch_initvt(krb5_context context,int maj_ver,int min_ver,krb5_plugin_vtable vtable)1389 certauth_dbmatch_initvt(krb5_context context, int maj_ver, int min_ver,
1390 krb5_plugin_vtable vtable)
1391 {
1392 krb5_certauth_vtable vt;
1393
1394 if (maj_ver != 1)
1395 return KRB5_PLUGIN_VER_NOTSUPP;
1396 vt = (krb5_certauth_vtable)vtable;
1397 vt->name = "dbmatch";
1398 vt->authorize = dbmatch_authorize;
1399 return 0;
1400 }
1401
1402 static krb5_error_code
load_certauth_plugins(krb5_context context,const char * const * realmnames,certauth_handle ** handle_out)1403 load_certauth_plugins(krb5_context context, const char *const *realmnames,
1404 certauth_handle **handle_out)
1405 {
1406 krb5_error_code ret;
1407 krb5_plugin_initvt_fn *modules = NULL, *mod;
1408 certauth_handle *list = NULL, h;
1409 size_t count;
1410
1411 /* Register the builtin modules. */
1412 ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH,
1413 "pkinit_san", certauth_pkinit_san_initvt);
1414 if (ret)
1415 goto cleanup;
1416
1417 ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH,
1418 "pkinit_eku", certauth_pkinit_eku_initvt);
1419 if (ret)
1420 goto cleanup;
1421
1422 ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH, "dbmatch",
1423 certauth_dbmatch_initvt);
1424 if (ret)
1425 goto cleanup;
1426
1427 ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_CERTAUTH, &modules);
1428 if (ret)
1429 goto cleanup;
1430
1431 /* Allocate handle list. */
1432 for (count = 0; modules[count]; count++);
1433 list = k5calloc(count + 1, sizeof(*list), &ret);
1434 if (list == NULL)
1435 goto cleanup;
1436
1437 /* Initialize each module, ignoring ones that fail. */
1438 count = 0;
1439 for (mod = modules; *mod != NULL; mod++) {
1440 h = k5calloc(1, sizeof(*h), &ret);
1441 if (h == NULL)
1442 goto cleanup;
1443
1444 ret = (*mod)(context, 1, 2, (krb5_plugin_vtable)&h->vt);
1445 if (ret) {
1446 TRACE_CERTAUTH_VTINIT_FAIL(context, ret);
1447 free(h);
1448 continue;
1449 }
1450 h->moddata = NULL;
1451 if (h->vt.init_ex != NULL)
1452 ret = h->vt.init_ex(context, realmnames, &h->moddata);
1453 else if (h->vt.init != NULL)
1454 ret = h->vt.init(context, &h->moddata);
1455 if (ret) {
1456 TRACE_CERTAUTH_INIT_FAIL(context, h->vt.name, ret);
1457 free(h);
1458 continue;
1459 }
1460 list[count++] = h;
1461 list[count] = NULL;
1462 }
1463 list[count] = NULL;
1464
1465 ret = 0;
1466 *handle_out = list;
1467 list = NULL;
1468
1469 cleanup:
1470 k5_plugin_free_modules(context, modules);
1471 free_certauth_handles(context, list);
1472 return ret;
1473 }
1474
1475 static int
pkinit_server_plugin_init(krb5_context context,krb5_kdcpreauth_moddata * moddata_out,const char ** realmnames)1476 pkinit_server_plugin_init(krb5_context context,
1477 krb5_kdcpreauth_moddata *moddata_out,
1478 const char **realmnames)
1479 {
1480 krb5_error_code retval = ENOMEM;
1481 pkinit_kdc_context plgctx, *realm_contexts = NULL;
1482 certauth_handle *certauth_modules = NULL;
1483 krb5_kdcpreauth_moddata moddata;
1484 size_t i, j;
1485 size_t numrealms;
1486
1487 retval = pkinit_accessor_init();
1488 if (retval)
1489 return retval;
1490
1491 /* Determine how many realms we may need to support */
1492 for (i = 0; realmnames[i] != NULL; i++) {};
1493 numrealms = i;
1494
1495 realm_contexts = calloc(numrealms+1, sizeof(pkinit_kdc_context));
1496 if (realm_contexts == NULL)
1497 return ENOMEM;
1498
1499 for (i = 0, j = 0; i < numrealms; i++) {
1500 TRACE_PKINIT_SERVER_INIT_REALM(context, realmnames[i]);
1501 krb5_clear_error_message(context);
1502 retval = pkinit_server_plugin_init_realm(context, realmnames[i],
1503 &plgctx);
1504 if (retval)
1505 TRACE_PKINIT_SERVER_INIT_FAIL(context, realmnames[i], retval);
1506 else
1507 realm_contexts[j++] = plgctx;
1508 }
1509
1510 if (j == 0) {
1511 if (numrealms == 1) {
1512 k5_prependmsg(context, retval, "PKINIT initialization failed");
1513 } else {
1514 retval = EINVAL;
1515 k5_setmsg(context, retval,
1516 _("No realms configured correctly for pkinit support"));
1517 }
1518 goto errout;
1519 }
1520
1521 retval = load_certauth_plugins(context, realmnames, &certauth_modules);
1522 if (retval)
1523 goto errout;
1524
1525 moddata = k5calloc(1, sizeof(*moddata), &retval);
1526 if (moddata == NULL)
1527 goto errout;
1528 moddata->realm_contexts = realm_contexts;
1529 moddata->certauth_modules = certauth_modules;
1530 *moddata_out = moddata;
1531 pkiDebug("%s: returning context at %p\n", __FUNCTION__, moddata);
1532 return 0;
1533
1534 errout:
1535 free_realm_contexts(context, realm_contexts);
1536 free_certauth_handles(context, certauth_modules);
1537 return retval;
1538 }
1539
1540 static void
pkinit_server_plugin_fini_realm(krb5_context context,pkinit_kdc_context plgctx)1541 pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx)
1542 {
1543 char **sp;
1544
1545 if (plgctx == NULL)
1546 return;
1547
1548 pkinit_fini_kdc_profile(context, plgctx);
1549 pkinit_fini_identity_opts(plgctx->idopts);
1550 pkinit_fini_identity_crypto(plgctx->idctx);
1551 pkinit_fini_plg_crypto(plgctx->cryptoctx);
1552 pkinit_fini_plg_opts(plgctx->opts);
1553 for (sp = plgctx->auth_indicators; sp != NULL && *sp != NULL; sp++)
1554 free(*sp);
1555 free(plgctx->auth_indicators);
1556 free(plgctx->realmname);
1557 free(plgctx);
1558 }
1559
1560 static void
pkinit_server_plugin_fini(krb5_context context,krb5_kdcpreauth_moddata moddata)1561 pkinit_server_plugin_fini(krb5_context context,
1562 krb5_kdcpreauth_moddata moddata)
1563 {
1564 if (moddata == NULL)
1565 return;
1566 free_realm_contexts(context, moddata->realm_contexts);
1567 free_certauth_handles(context, moddata->certauth_modules);
1568 free(moddata);
1569 }
1570
1571 static krb5_error_code
pkinit_init_kdc_req_context(krb5_context context,pkinit_kdc_req_context * ctx)1572 pkinit_init_kdc_req_context(krb5_context context, pkinit_kdc_req_context *ctx)
1573 {
1574 krb5_error_code retval = ENOMEM;
1575 pkinit_kdc_req_context reqctx = NULL;
1576
1577 reqctx = malloc(sizeof(*reqctx));
1578 if (reqctx == NULL)
1579 return retval;
1580 memset(reqctx, 0, sizeof(*reqctx));
1581 reqctx->magic = PKINIT_CTX_MAGIC;
1582
1583 retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
1584 if (retval)
1585 goto cleanup;
1586 reqctx->rcv_auth_pack = NULL;
1587
1588 pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
1589 *ctx = reqctx;
1590 retval = 0;
1591 cleanup:
1592 if (retval)
1593 pkinit_fini_kdc_req_context(context, reqctx);
1594
1595 return retval;
1596 }
1597
1598 static void
pkinit_fini_kdc_req_context(krb5_context context,void * ctx)1599 pkinit_fini_kdc_req_context(krb5_context context, void *ctx)
1600 {
1601 pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx;
1602
1603 if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) {
1604 pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx);
1605 return;
1606 }
1607 pkiDebug("%s: freeing reqctx at %p\n", __FUNCTION__, reqctx);
1608
1609 pkinit_fini_req_crypto(reqctx->cryptoctx);
1610 if (reqctx->rcv_auth_pack != NULL)
1611 free_krb5_auth_pack(&reqctx->rcv_auth_pack);
1612
1613 free(reqctx);
1614 }
1615
1616 static void
pkinit_free_modreq(krb5_context context,krb5_kdcpreauth_moddata moddata,krb5_kdcpreauth_modreq modreq)1617 pkinit_free_modreq(krb5_context context, krb5_kdcpreauth_moddata moddata,
1618 krb5_kdcpreauth_modreq modreq)
1619 {
1620 pkinit_fini_kdc_req_context(context, modreq);
1621 }
1622
1623 krb5_error_code
1624 kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
1625 krb5_plugin_vtable vtable);
1626
1627 krb5_error_code
kdcpreauth_pkinit_initvt(krb5_context context,int maj_ver,int min_ver,krb5_plugin_vtable vtable)1628 kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
1629 krb5_plugin_vtable vtable)
1630 {
1631 krb5_kdcpreauth_vtable vt;
1632
1633 if (maj_ver != 1)
1634 return KRB5_PLUGIN_VER_NOTSUPP;
1635 vt = (krb5_kdcpreauth_vtable)vtable;
1636 vt->name = "pkinit";
1637 vt->pa_type_list = supported_server_pa_types;
1638 vt->init = pkinit_server_plugin_init;
1639 vt->fini = pkinit_server_plugin_fini;
1640 vt->flags = pkinit_server_get_flags;
1641 vt->edata = pkinit_server_get_edata;
1642 vt->verify = pkinit_server_verify_padata;
1643 vt->return_padata = pkinit_server_return_padata;
1644 vt->free_modreq = pkinit_free_modreq;
1645 return 0;
1646 }
1647