1 /*
2 * COPYRIGHT (C) 2006,2007
3 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
4 * ALL RIGHTS RESERVED
5 *
6 * Permission is granted to use, copy, create derivative works
7 * and redistribute this software and such derivative works
8 * for any purpose, so long as the name of The University of
9 * Michigan is not used in any advertising or publicity
10 * pertaining to the use of distribution of this software
11 * without specific, written prior authorization. If the
12 * above copyright notice or any other identification of the
13 * University of Michigan is included in any copy of any
14 * portion of this software, then the disclaimer below must
15 * also be included.
16 *
17 * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
18 * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
19 * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
20 * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
21 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
23 * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
24 * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
25 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
26 * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
27 * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGES.
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <string.h>
35
36 #include "pkinit.h"
37
38 static krb5_error_code
39 pkinit_server_get_edata(krb5_context context,
40 krb5_kdc_req * request,
41 struct _krb5_db_entry_new * client,
42 struct _krb5_db_entry_new * server,
43 preauth_get_entry_data_proc server_get_entry_data,
44 void *pa_plugin_context,
45 krb5_pa_data * data);
46
47 static krb5_error_code
48 pkinit_server_verify_padata(krb5_context context,
49 struct _krb5_db_entry_new * client,
50 krb5_data *req_pkt,
51 krb5_kdc_req * request,
52 krb5_enc_tkt_part * enc_tkt_reply,
53 krb5_pa_data * data,
54 preauth_get_entry_data_proc server_get_entry_data,
55 void *pa_plugin_context,
56 void **pa_request_context,
57 krb5_data **e_data,
58 krb5_authdata ***authz_data);
59
60 static krb5_error_code
61 pkinit_server_return_padata(krb5_context context,
62 krb5_pa_data * padata,
63 struct _krb5_db_entry_new * client,
64 krb5_data *req_pkt,
65 krb5_kdc_req * request,
66 krb5_kdc_rep * reply,
67 struct _krb5_key_data * client_key,
68 krb5_keyblock * encrypting_key,
69 krb5_pa_data ** send_pa,
70 preauth_get_entry_data_proc server_get_entry_data,
71 void *pa_plugin_context,
72 void **pa_request_context);
73
74 static int pkinit_server_get_flags
75 (krb5_context kcontext, krb5_preauthtype patype);
76
77 static krb5_error_code pkinit_init_kdc_req_context
78 (krb5_context, void **blob);
79
80 static void pkinit_fini_kdc_req_context
81 (krb5_context context, void *blob);
82
83 static int pkinit_server_plugin_init_realm
84 (krb5_context context, const char *realmname,
85 pkinit_kdc_context *pplgctx);
86
87 static void pkinit_server_plugin_fini_realm
88 (krb5_context context, pkinit_kdc_context plgctx);
89
90 static int pkinit_server_plugin_init
91 (krb5_context context, void **blob, const char **realmnames);
92
93 static void pkinit_server_plugin_fini
94 (krb5_context context, void *blob);
95
96 static pkinit_kdc_context pkinit_find_realm_context
97 (krb5_context context, void *pa_plugin_context, krb5_principal princ);
98
99 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_data ** e_data)100 pkinit_create_edata(krb5_context context,
101 pkinit_plg_crypto_context plg_cryptoctx,
102 pkinit_req_crypto_context req_cryptoctx,
103 pkinit_identity_crypto_context id_cryptoctx,
104 pkinit_plg_opts *opts,
105 krb5_error_code err_code,
106 krb5_data **e_data)
107 {
108 krb5_error_code retval = KRB5KRB_ERR_GENERIC;
109
110 pkiDebug("pkinit_create_edata: creating edata for error %d (%s)\n",
111 err_code, error_message(err_code));
112 switch(err_code) {
113 case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
114 retval = pkinit_create_td_trusted_certifiers(context,
115 plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data);
116 break;
117 case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
118 retval = pkinit_create_td_dh_parameters(context, plg_cryptoctx,
119 req_cryptoctx, id_cryptoctx, opts, e_data);
120 break;
121 case KRB5KDC_ERR_INVALID_CERTIFICATE:
122 case KRB5KDC_ERR_REVOKED_CERTIFICATE:
123 retval = pkinit_create_td_invalid_certificate(context,
124 plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data);
125 break;
126 default:
127 pkiDebug("no edata needed for error %d (%s)\n",
128 err_code, error_message(err_code));
129 retval = 0;
130 goto cleanup;
131 }
132
133 cleanup:
134
135 return retval;
136 }
137
138 /* ARGSUSED */
139 static krb5_error_code
pkinit_server_get_edata(krb5_context context,krb5_kdc_req * request,struct _krb5_db_entry_new * client,struct _krb5_db_entry_new * server,preauth_get_entry_data_proc server_get_entry_data,void * pa_plugin_context,krb5_pa_data * data)140 pkinit_server_get_edata(krb5_context context,
141 krb5_kdc_req * request,
142 struct _krb5_db_entry_new * client,
143 struct _krb5_db_entry_new * server,
144 preauth_get_entry_data_proc server_get_entry_data,
145 void *pa_plugin_context,
146 krb5_pa_data * data)
147 {
148 krb5_error_code retval = 0;
149 pkinit_kdc_context plgctx = NULL;
150
151 pkiDebug("pkinit_server_get_edata: entered!\n");
152
153 /*
154 * If we don't have a realm context for the given realm,
155 * don't tell the client that we support pkinit!
156 */
157 plgctx = pkinit_find_realm_context(context, pa_plugin_context,
158 request->server);
159 if (plgctx == NULL)
160 retval = EINVAL;
161
162 return retval;
163 }
164
165 static krb5_error_code
verify_client_san(krb5_context context,pkinit_kdc_context plgctx,pkinit_kdc_req_context reqctx,krb5_principal client,int * valid_san)166 verify_client_san(krb5_context context,
167 pkinit_kdc_context plgctx,
168 pkinit_kdc_req_context reqctx,
169 krb5_principal client,
170 int *valid_san)
171 {
172 krb5_error_code retval;
173 krb5_principal *princs = NULL;
174 krb5_principal *upns = NULL;
175 int i;
176 #ifdef DEBUG_SAN_INFO
177 char *client_string = NULL, *san_string;
178 #endif
179
180 retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,
181 reqctx->cryptoctx, plgctx->idctx,
182 &princs,
183 plgctx->opts->allow_upn ? &upns : NULL,
184 NULL);
185 if (retval) {
186 pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
187 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
188 goto out;
189 }
190 /* XXX Verify this is consistent with client side XXX */
191 #if 0
192 retval = call_san_checking_plugins(context, plgctx, reqctx, princs,
193 upns, NULL, &plugin_decision, &ignore);
194 pkiDebug("%s: call_san_checking_plugins() returned retval %d\n",
195 __FUNCTION__);
196 if (retval) {
197 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
198 goto cleanup;
199 }
200 pkiDebug("%s: call_san_checking_plugins() returned decision %d\n",
201 __FUNCTION__, plugin_decision);
202 if (plugin_decision != NO_DECISION) {
203 retval = plugin_decision;
204 goto out;
205 }
206 #endif
207
208 #ifdef DEBUG_SAN_INFO
209 krb5_unparse_name(context, client, &client_string);
210 #endif
211 pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);
212 for (i = 0; princs != NULL && princs[i] != NULL; i++) {
213 #ifdef DEBUG_SAN_INFO
214 krb5_unparse_name(context, princs[i], &san_string);
215 pkiDebug("%s: Comparing client '%s' to pkinit san value '%s'\n",
216 __FUNCTION__, client_string, san_string);
217 krb5_free_unparsed_name(context, san_string);
218 #endif
219 if (krb5_principal_compare(context, princs[i], client)) {
220 pkiDebug("%s: pkinit san match found\n", __FUNCTION__);
221 *valid_san = 1;
222 retval = 0;
223 goto out;
224 }
225 }
226 pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);
227 /*
228 * XXX if cert has names but none match, should we
229 * be returning KRB5KDC_ERR_CLIENT_NAME_MISMATCH here?
230 */
231
232 if (upns == NULL) {
233 pkiDebug("%s: no upn sans (or we wouldn't accept them anyway)\n",
234 __FUNCTION__);
235 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
236 goto out;
237 }
238
239 pkiDebug("%s: Checking upn sans\n", __FUNCTION__);
240 for (i = 0; upns[i] != NULL; i++) {
241 #ifdef DEBUG_SAN_INFO
242 krb5_unparse_name(context, upns[i], &san_string);
243 pkiDebug("%s: Comparing client '%s' to upn san value '%s'\n",
244 __FUNCTION__, client_string, san_string);
245 krb5_free_unparsed_name(context, san_string);
246 #endif
247 if (krb5_principal_compare(context, upns[i], client)) {
248 pkiDebug("%s: upn san match found\n", __FUNCTION__);
249 *valid_san = 1;
250 retval = 0;
251 goto out;
252 }
253 }
254 pkiDebug("%s: no upn san match found\n", __FUNCTION__);
255
256 /* We found no match */
257 if (princs != NULL || upns != NULL) {
258 *valid_san = 0;
259 /* XXX ??? If there was one or more name in the cert, but
260 * none matched the client name, then return mismatch? */
261 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
262 }
263 retval = 0;
264
265 out:
266 if (princs != NULL) {
267 for (i = 0; princs[i] != NULL; i++)
268 krb5_free_principal(context, princs[i]);
269 free(princs);
270 }
271 if (upns != NULL) {
272 for (i = 0; upns[i] != NULL; i++)
273 krb5_free_principal(context, upns[i]);
274 free(upns);
275 }
276 #ifdef DEBUG_SAN_INFO
277 if (client_string != NULL)
278 krb5_free_unparsed_name(context, client_string);
279 #endif
280 pkiDebug("%s: returning retval %d, valid_san %d\n",
281 __FUNCTION__, retval, *valid_san);
282 return retval;
283 }
284
285 static krb5_error_code
verify_client_eku(krb5_context context,pkinit_kdc_context plgctx,pkinit_kdc_req_context reqctx,int * eku_accepted)286 verify_client_eku(krb5_context context,
287 pkinit_kdc_context plgctx,
288 pkinit_kdc_req_context reqctx,
289 int *eku_accepted)
290 {
291 krb5_error_code retval;
292
293 *eku_accepted = 0;
294
295 if (plgctx->opts->require_eku == 0) {
296 pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
297 *eku_accepted = 1;
298 retval = 0;
299 goto out;
300 }
301
302 retval = crypto_check_cert_eku(context, plgctx->cryptoctx,
303 reqctx->cryptoctx, plgctx->idctx,
304 0, /* kdc cert */
305 plgctx->opts->accept_secondary_eku,
306 eku_accepted);
307 if (retval) {
308 pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
309 __FUNCTION__, retval, error_message(retval));
310 goto out;
311 }
312
313 out:
314 pkiDebug("%s: returning retval %d, eku_accepted %d\n",
315 __FUNCTION__, retval, *eku_accepted);
316 return retval;
317 }
318
319 /* ARGSUSED */
320 static krb5_error_code
pkinit_server_verify_padata(krb5_context context,struct _krb5_db_entry_new * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_enc_tkt_part * enc_tkt_reply,krb5_pa_data * data,preauth_get_entry_data_proc server_get_entry_data,void * pa_plugin_context,void ** pa_request_context,krb5_data ** e_data,krb5_authdata *** authz_data)321 pkinit_server_verify_padata(krb5_context context,
322 struct _krb5_db_entry_new * client,
323 krb5_data *req_pkt,
324 krb5_kdc_req * request,
325 krb5_enc_tkt_part * enc_tkt_reply,
326 krb5_pa_data * data,
327 preauth_get_entry_data_proc server_get_entry_data,
328 void *pa_plugin_context,
329 void **pa_request_context,
330 krb5_data **e_data,
331 krb5_authdata ***authz_data)
332 {
333 krb5_error_code retval = 0;
334 krb5_octet_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL};
335 krb5_data *encoded_pkinit_authz_data = NULL;
336 krb5_pa_pk_as_req *reqp = NULL;
337 krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
338 krb5_auth_pack *auth_pack = NULL;
339 krb5_auth_pack_draft9 *auth_pack9 = NULL;
340 pkinit_kdc_context plgctx = NULL;
341 pkinit_kdc_req_context reqctx;
342 /* Solaris Kerberos: set but not used */
343 #if 0
344 krb5_preauthtype pa_type;
345 #endif
346 krb5_checksum cksum = {0, 0, 0, NULL};
347 krb5_data *der_req = NULL;
348 int valid_eku = 0, valid_san = 0;
349 krb5_authdata **my_authz_data = NULL, *pkinit_authz_data = NULL;
350 krb5_kdc_req *tmp_as_req = NULL;
351 krb5_data k5data;
352
353 pkiDebug("pkinit_verify_padata: entered!\n");
354 /* Solaris Kerberos */
355 if (data == NULL || data->length == 0 || data->contents == NULL)
356 return 0;
357
358 if (pa_plugin_context == NULL || e_data == NULL)
359 return EINVAL;
360
361 plgctx = pkinit_find_realm_context(context, pa_plugin_context,
362 request->server);
363 if (plgctx == NULL)
364 return 0;
365
366 #ifdef DEBUG_ASN1
367 print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req");
368 #endif
369 /* create a per-request context */
370 retval = pkinit_init_kdc_req_context(context, (void **)&reqctx);
371 if (retval)
372 goto cleanup;
373 reqctx->pa_type = data->pa_type;
374
375 PADATA_TO_KRB5DATA(data, &k5data);
376
377 switch ((int)data->pa_type) {
378 case KRB5_PADATA_PK_AS_REQ:
379 pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
380 retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp);
381 if (retval) {
382 pkiDebug("decode_krb5_pa_pk_as_req failed\n");
383 goto cleanup;
384 }
385 #ifdef DEBUG_ASN1
386 print_buffer_bin(reqp->signedAuthPack.data,
387 reqp->signedAuthPack.length,
388 "/tmp/kdc_signed_data");
389 #endif
390 retval = cms_signeddata_verify(context, plgctx->cryptoctx,
391 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT,
392 plgctx->opts->require_crl_checking,
393 reqp->signedAuthPack.data, reqp->signedAuthPack.length,
394 &authp_data.data, &authp_data.length, &krb5_authz.data,
395 &krb5_authz.length);
396 break;
397 case KRB5_PADATA_PK_AS_REP_OLD:
398 case KRB5_PADATA_PK_AS_REQ_OLD:
399 pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
400 retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9);
401 if (retval) {
402 pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n");
403 goto cleanup;
404 }
405 #ifdef DEBUG_ASN1
406 print_buffer_bin(reqp9->signedAuthPack.data,
407 reqp9->signedAuthPack.length,
408 "/tmp/kdc_signed_data_draft9");
409 #endif
410
411 retval = cms_signeddata_verify(context, plgctx->cryptoctx,
412 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9,
413 plgctx->opts->require_crl_checking,
414 reqp9->signedAuthPack.data, reqp9->signedAuthPack.length,
415 &authp_data.data, &authp_data.length, &krb5_authz.data,
416 &krb5_authz.length);
417 break;
418 default:
419 pkiDebug("unrecognized pa_type = %d\n", data->pa_type);
420 retval = EINVAL;
421 goto cleanup;
422 }
423 if (retval) {
424 pkiDebug("pkcs7_signeddata_verify failed\n");
425 goto cleanup;
426 }
427
428 retval = verify_client_san(context, plgctx, reqctx, request->client,
429 &valid_san);
430 if (retval)
431 goto cleanup;
432 if (!valid_san) {
433 pkiDebug("%s: did not find an acceptable SAN in user certificate\n",
434 __FUNCTION__);
435 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
436 goto cleanup;
437 }
438 retval = verify_client_eku(context, plgctx, reqctx, &valid_eku);
439 if (retval)
440 goto cleanup;
441
442 if (!valid_eku) {
443 pkiDebug("%s: did not find an acceptable EKU in user certificate\n",
444 __FUNCTION__);
445 retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
446 goto cleanup;
447 }
448
449 #ifdef DEBUG_ASN1
450 print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack");
451 #endif
452
453 OCTETDATA_TO_KRB5DATA(&authp_data, &k5data);
454 switch ((int)data->pa_type) {
455 case KRB5_PADATA_PK_AS_REQ:
456 retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack);
457 if (retval) {
458 pkiDebug("failed to decode krb5_auth_pack\n");
459 goto cleanup;
460 }
461
462 /* check dh parameters */
463 if (auth_pack->clientPublicValue != NULL) {
464 retval = server_check_dh(context, plgctx->cryptoctx,
465 reqctx->cryptoctx, plgctx->idctx,
466 &auth_pack->clientPublicValue->algorithm.parameters,
467 plgctx->opts->dh_min_bits);
468
469 if (retval) {
470 pkiDebug("bad dh parameters\n");
471 goto cleanup;
472 }
473 }
474 /*
475 * The KDC may have modified the request after decoding it.
476 * We need to compute the checksum on the data that
477 * came from the client. Therefore, we use the original
478 * packet contents.
479 */
480 retval = k5int_decode_krb5_as_req(req_pkt, &tmp_as_req);
481 if (retval) {
482 pkiDebug("decode_krb5_as_req returned %d\n", (int)retval);
483 goto cleanup;
484 }
485
486 retval = k5int_encode_krb5_kdc_req_body(tmp_as_req, &der_req);
487 if (retval) {
488 pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval);
489 goto cleanup;
490 }
491 retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL,
492 0, der_req, &cksum);
493 if (retval) {
494 pkiDebug("unable to calculate AS REQ checksum\n");
495 goto cleanup;
496 }
497 if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length ||
498 memcmp(cksum.contents,
499 auth_pack->pkAuthenticator.paChecksum.contents,
500 cksum.length)) {
501 pkiDebug("failed to match the checksum\n");
502 #ifdef DEBUG_CKSUM
503 pkiDebug("calculating checksum on buf size (%d)\n",
504 req_pkt->length);
505 print_buffer(req_pkt->data, req_pkt->length);
506 pkiDebug("received checksum type=%d size=%d ",
507 auth_pack->pkAuthenticator.paChecksum.checksum_type,
508 auth_pack->pkAuthenticator.paChecksum.length);
509 print_buffer(auth_pack->pkAuthenticator.paChecksum.contents,
510 auth_pack->pkAuthenticator.paChecksum.length);
511 pkiDebug("expected checksum type=%d size=%d ",
512 cksum.checksum_type, cksum.length);
513 print_buffer(cksum.contents, cksum.length);
514 #endif
515
516 retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
517 goto cleanup;
518 }
519
520 /* check if kdcPkId present and match KDC's subjectIdentifier */
521 if (reqp->kdcPkId.data != NULL) {
522 int valid_kdcPkId = 0;
523 retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx,
524 reqctx->cryptoctx, plgctx->idctx,
525 reqp->kdcPkId.data, reqp->kdcPkId.length, &valid_kdcPkId);
526 if (retval)
527 goto cleanup;
528 if (!valid_kdcPkId)
529 pkiDebug("kdcPkId in AS_REQ does not match KDC's cert"
530 "RFC says to ignore and proceed\n");
531
532 }
533 /* remember the decoded auth_pack for verify_padata routine */
534 reqctx->rcv_auth_pack = auth_pack;
535 auth_pack = NULL;
536 break;
537 case KRB5_PADATA_PK_AS_REP_OLD:
538 case KRB5_PADATA_PK_AS_REQ_OLD:
539 retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9);
540 if (retval) {
541 pkiDebug("failed to decode krb5_auth_pack_draft9\n");
542 goto cleanup;
543 }
544 if (auth_pack9->clientPublicValue != NULL) {
545 retval = server_check_dh(context, plgctx->cryptoctx,
546 reqctx->cryptoctx, plgctx->idctx,
547 &auth_pack9->clientPublicValue->algorithm.parameters,
548 plgctx->opts->dh_min_bits);
549
550 if (retval) {
551 pkiDebug("bad dh parameters\n");
552 goto cleanup;
553 }
554 }
555 /* remember the decoded auth_pack for verify_padata routine */
556 reqctx->rcv_auth_pack9 = auth_pack9;
557 auth_pack9 = NULL;
558 break;
559 }
560
561 /* return authorization data to be included in the ticket */
562 switch ((int)data->pa_type) {
563 case KRB5_PADATA_PK_AS_REQ:
564 my_authz_data = malloc(2 * sizeof(*my_authz_data));
565 if (my_authz_data == NULL) {
566 retval = ENOMEM;
567 pkiDebug("Couldn't allocate krb5_authdata ptr array\n");
568 goto cleanup;
569 }
570 my_authz_data[1] = NULL;
571 my_authz_data[0] = malloc(sizeof(krb5_authdata));
572 if (my_authz_data[0] == NULL) {
573 retval = ENOMEM;
574 pkiDebug("Couldn't allocate krb5_authdata\n");
575 free(my_authz_data);
576 goto cleanup;
577 }
578 /* AD-INITIAL-VERIFIED-CAS must be wrapped in AD-IF-RELEVANT */
579 my_authz_data[0]->magic = KV5M_AUTHDATA;
580 my_authz_data[0]->ad_type = KRB5_AUTHDATA_IF_RELEVANT;
581
582 /* create an internal AD-INITIAL-VERIFIED-CAS data */
583 pkinit_authz_data = malloc(sizeof(krb5_authdata));
584 if (pkinit_authz_data == NULL) {
585 retval = ENOMEM;
586 pkiDebug("Couldn't allocate krb5_authdata\n");
587 free(my_authz_data[0]);
588 free(my_authz_data);
589 goto cleanup;
590 }
591 pkinit_authz_data->ad_type = KRB5_AUTHDATA_INITIAL_VERIFIED_CAS;
592 /* content of this ad-type contains the certification
593 path with which the client certificate was validated
594 */
595 pkinit_authz_data->contents = krb5_authz.data;
596 pkinit_authz_data->length = krb5_authz.length;
597 retval = k5int_encode_krb5_authdata_elt(pkinit_authz_data,
598 &encoded_pkinit_authz_data);
599 #ifdef DEBUG_ASN1
600 print_buffer_bin((unsigned char *)encoded_pkinit_authz_data->data,
601 encoded_pkinit_authz_data->length,
602 "/tmp/kdc_pkinit_authz_data");
603 #endif
604 free(pkinit_authz_data);
605 if (retval) {
606 pkiDebug("k5int_encode_krb5_authdata_elt failed\n");
607 free(my_authz_data[0]);
608 free(my_authz_data);
609 goto cleanup;
610 }
611
612 my_authz_data[0]->contents =
613 (krb5_octet *) encoded_pkinit_authz_data->data;
614 my_authz_data[0]->length = encoded_pkinit_authz_data->length;
615 *authz_data = my_authz_data;
616 pkiDebug("Returning %d bytes of authorization data\n",
617 krb5_authz.length);
618 encoded_pkinit_authz_data->data = NULL; /* Don't free during cleanup*/
619 free(encoded_pkinit_authz_data);
620 break;
621 default:
622 *authz_data = NULL;
623 }
624 /* remember to set the PREAUTH flag in the reply */
625 enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
626 *pa_request_context = reqctx;
627 reqctx = NULL;
628
629 cleanup:
630 if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) {
631 pkiDebug("pkinit_verify_padata failed: creating e-data\n");
632 if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx,
633 plgctx->idctx, plgctx->opts, retval, e_data))
634 pkiDebug("pkinit_create_edata failed\n");
635 }
636
637 switch ((int)data->pa_type) {
638 case KRB5_PADATA_PK_AS_REQ:
639 free_krb5_pa_pk_as_req(&reqp);
640 if (cksum.contents != NULL)
641 free(cksum.contents);
642 if (der_req != NULL)
643 krb5_free_data(context, der_req);
644 break;
645 case KRB5_PADATA_PK_AS_REP_OLD:
646 case KRB5_PADATA_PK_AS_REQ_OLD:
647 free_krb5_pa_pk_as_req_draft9(&reqp9);
648 }
649 if (tmp_as_req != NULL)
650 k5int_krb5_free_kdc_req(context, tmp_as_req);
651 if (authp_data.data != NULL)
652 free(authp_data.data);
653 if (krb5_authz.data != NULL)
654 free(krb5_authz.data);
655 if (reqctx != NULL)
656 pkinit_fini_kdc_req_context(context, reqctx);
657 if (auth_pack != NULL)
658 free_krb5_auth_pack(&auth_pack);
659 if (auth_pack9 != NULL)
660 free_krb5_auth_pack_draft9(context, &auth_pack9);
661
662 return retval;
663 }
664
665 /* ARGSUSED */
666 static krb5_error_code
pkinit_server_return_padata(krb5_context context,krb5_pa_data * padata,struct _krb5_db_entry_new * client,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,struct _krb5_key_data * client_key,krb5_keyblock * encrypting_key,krb5_pa_data ** send_pa,preauth_get_entry_data_proc server_get_entry_data,void * pa_plugin_context,void ** pa_request_context)667 pkinit_server_return_padata(krb5_context context,
668 krb5_pa_data * padata,
669 struct _krb5_db_entry_new * client,
670 krb5_data *req_pkt,
671 krb5_kdc_req * request,
672 krb5_kdc_rep * reply,
673 struct _krb5_key_data * client_key,
674 krb5_keyblock * encrypting_key,
675 krb5_pa_data ** send_pa,
676 preauth_get_entry_data_proc server_get_entry_data,
677 void *pa_plugin_context,
678 void **pa_request_context)
679 {
680 krb5_error_code retval = 0;
681 krb5_data scratch = {0, 0, NULL};
682 krb5_pa_pk_as_req *reqp = NULL;
683 krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
684 int i = 0;
685
686 unsigned char *subjectPublicKey = NULL;
687 unsigned char *dh_pubkey = NULL, *server_key = NULL;
688 unsigned int subjectPublicKey_len = 0;
689 unsigned int server_key_len = 0, dh_pubkey_len = 0;
690
691 krb5_kdc_dh_key_info dhkey_info;
692 krb5_data *encoded_dhkey_info = NULL;
693 krb5_pa_pk_as_rep *rep = NULL;
694 krb5_pa_pk_as_rep_draft9 *rep9 = NULL;
695 krb5_data *out_data = NULL;
696
697 krb5_enctype enctype = -1;
698
699 krb5_reply_key_pack *key_pack = NULL;
700 krb5_reply_key_pack_draft9 *key_pack9 = NULL;
701 krb5_data *encoded_key_pack = NULL;
702 unsigned int num_types;
703 krb5_cksumtype *cksum_types = NULL;
704
705 pkinit_kdc_context plgctx;
706 pkinit_kdc_req_context reqctx;
707
708 int fixed_keypack = 0;
709
710 *send_pa = NULL;
711 /* Solaris Kerberos */
712 if (padata == NULL || padata->length == 0 || padata->contents == NULL)
713 return 0;
714
715 if (pa_request_context == NULL || *pa_request_context == NULL) {
716 pkiDebug("missing request context \n");
717 return EINVAL;
718 }
719
720 plgctx = pkinit_find_realm_context(context, pa_plugin_context,
721 request->server);
722 if (plgctx == NULL) {
723 pkiDebug("Unable to locate correct realm context\n");
724 return ENOENT;
725 }
726
727 pkiDebug("pkinit_return_padata: entered!\n");
728 reqctx = (pkinit_kdc_req_context)*pa_request_context;
729
730 if (encrypting_key->contents) {
731 free(encrypting_key->contents);
732 encrypting_key->length = 0;
733 encrypting_key->contents = NULL;
734 }
735
736 for(i = 0; i < request->nktypes; i++) {
737 enctype = request->ktype[i];
738 if (!krb5_c_valid_enctype(enctype))
739 continue;
740 else {
741 pkiDebug("KDC picked etype = %d\n", enctype);
742 break;
743 }
744 }
745
746 if (i == request->nktypes) {
747 retval = KRB5KDC_ERR_ETYPE_NOSUPP;
748 goto cleanup;
749 }
750
751 switch((int)reqctx->pa_type) {
752 case KRB5_PADATA_PK_AS_REQ:
753 init_krb5_pa_pk_as_rep(&rep);
754 if (rep == NULL) {
755 retval = ENOMEM;
756 goto cleanup;
757 }
758 /* let's assume it's RSA. we'll reset it to DH if needed */
759 rep->choice = choice_pa_pk_as_rep_encKeyPack;
760 break;
761 case KRB5_PADATA_PK_AS_REP_OLD:
762 case KRB5_PADATA_PK_AS_REQ_OLD:
763 init_krb5_pa_pk_as_rep_draft9(&rep9);
764 if (rep9 == NULL) {
765 retval = ENOMEM;
766 goto cleanup;
767 }
768 rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
769 break;
770 default:
771 retval = KRB5KDC_ERR_PREAUTH_FAILED;
772 goto cleanup;
773 }
774
775 if (reqctx->rcv_auth_pack != NULL &&
776 reqctx->rcv_auth_pack->clientPublicValue != NULL) {
777 subjectPublicKey =
778 reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.data;
779 subjectPublicKey_len =
780 reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.length;
781 rep->choice = choice_pa_pk_as_rep_dhInfo;
782 } else if (reqctx->rcv_auth_pack9 != NULL &&
783 reqctx->rcv_auth_pack9->clientPublicValue != NULL) {
784 subjectPublicKey =
785 reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.data;
786 subjectPublicKey_len =
787 reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.length;
788 rep9->choice = choice_pa_pk_as_rep_draft9_dhSignedData;
789 }
790
791 /* if this DH, then process finish computing DH key */
792 if (((rep != NULL) && (rep->choice == choice_pa_pk_as_rep_dhInfo)) ||
793 ((rep9 != NULL) && rep9->choice ==
794 choice_pa_pk_as_rep_draft9_dhSignedData)) {
795 pkiDebug("received DH key delivery AS REQ\n");
796 retval = server_process_dh(context, plgctx->cryptoctx,
797 reqctx->cryptoctx, plgctx->idctx, subjectPublicKey,
798 subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len,
799 &server_key, &server_key_len);
800 if (retval) {
801 pkiDebug("failed to process/create dh paramters\n");
802 goto cleanup;
803 }
804 }
805
806 if ((rep9 != NULL &&
807 rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) ||
808 (rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) {
809 retval = pkinit_octetstring2key(context, enctype, server_key,
810 server_key_len, encrypting_key);
811 if (retval) {
812 pkiDebug("pkinit_octetstring2key failed: %s\n",
813 error_message(retval));
814 goto cleanup;
815 }
816
817 dhkey_info.subjectPublicKey.length = dh_pubkey_len;
818 dhkey_info.subjectPublicKey.data = dh_pubkey;
819 dhkey_info.nonce = request->nonce;
820 dhkey_info.dhKeyExpiration = 0;
821
822 retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info,
823 &encoded_dhkey_info);
824 if (retval) {
825 pkiDebug("encode_krb5_kdc_dh_key_info failed\n");
826 goto cleanup;
827 }
828 #ifdef DEBUG_ASN1
829 print_buffer_bin((unsigned char *)encoded_dhkey_info->data,
830 encoded_dhkey_info->length,
831 "/tmp/kdc_dh_key_info");
832 #endif
833
834 switch ((int)padata->pa_type) {
835 case KRB5_PADATA_PK_AS_REQ:
836 retval = cms_signeddata_create(context, plgctx->cryptoctx,
837 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1,
838 (unsigned char *)encoded_dhkey_info->data,
839 encoded_dhkey_info->length,
840 &rep->u.dh_Info.dhSignedData.data,
841 &rep->u.dh_Info.dhSignedData.length);
842 if (retval) {
843 pkiDebug("failed to create pkcs7 signed data\n");
844 goto cleanup;
845 }
846 break;
847 case KRB5_PADATA_PK_AS_REP_OLD:
848 case KRB5_PADATA_PK_AS_REQ_OLD:
849 retval = cms_signeddata_create(context, plgctx->cryptoctx,
850 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1,
851 (unsigned char *)encoded_dhkey_info->data,
852 encoded_dhkey_info->length,
853 &rep9->u.dhSignedData.data,
854 &rep9->u.dhSignedData.length);
855 if (retval) {
856 pkiDebug("failed to create pkcs7 signed data\n");
857 goto cleanup;
858 }
859 break;
860 }
861 } else {
862 pkiDebug("received RSA key delivery AS REQ\n");
863
864 retval = krb5_c_make_random_key(context, enctype, encrypting_key);
865 if (retval) {
866 pkiDebug("unable to make a session key\n");
867 goto cleanup;
868 }
869
870 /* check if PA_TYPE of 132 is present which means the client is
871 * requesting that a checksum is send back instead of the nonce
872 */
873 for (i = 0; request->padata[i] != NULL; i++) {
874 pkiDebug("%s: Checking pa_type 0x%08x\n",
875 __FUNCTION__, request->padata[i]->pa_type);
876 if (request->padata[i]->pa_type == 132)
877 fixed_keypack = 1;
878 }
879 pkiDebug("%s: return checksum instead of nonce = %d\n",
880 __FUNCTION__, fixed_keypack);
881
882 /* if this is an RFC reply or draft9 client requested a checksum
883 * in the reply instead of the nonce, create an RFC-style keypack
884 */
885 if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) {
886 init_krb5_reply_key_pack(&key_pack);
887 if (key_pack == NULL) {
888 retval = ENOMEM;
889 goto cleanup;
890 }
891 /* retrieve checksums for a given enctype of the reply key */
892 retval = krb5_c_keyed_checksum_types(context,
893 encrypting_key->enctype, &num_types, &cksum_types);
894 if (retval)
895 goto cleanup;
896
897 /* pick the first of acceptable enctypes for the checksum */
898 retval = krb5_c_make_checksum(context, cksum_types[0],
899 encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
900 req_pkt, &key_pack->asChecksum);
901 if (retval) {
902 pkiDebug("unable to calculate AS REQ checksum\n");
903 goto cleanup;
904 }
905 #ifdef DEBUG_CKSUM
906 pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length);
907 print_buffer(req_pkt->data, req_pkt->length);
908 pkiDebug("checksum size = %d\n", key_pack->asChecksum.length);
909 print_buffer(key_pack->asChecksum.contents,
910 key_pack->asChecksum.length);
911 pkiDebug("encrypting key (%d)\n", encrypting_key->length);
912 print_buffer(encrypting_key->contents, encrypting_key->length);
913 #endif
914
915 krb5_copy_keyblock_contents(context, encrypting_key,
916 &key_pack->replyKey);
917
918 retval = k5int_encode_krb5_reply_key_pack(key_pack,
919 &encoded_key_pack);
920 if (retval) {
921 pkiDebug("failed to encode reply_key_pack\n");
922 goto cleanup;
923 }
924 }
925
926 switch ((int)padata->pa_type) {
927 case KRB5_PADATA_PK_AS_REQ:
928 rep->choice = choice_pa_pk_as_rep_encKeyPack;
929 retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
930 reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1,
931 (unsigned char *)encoded_key_pack->data,
932 encoded_key_pack->length,
933 &rep->u.encKeyPack.data, &rep->u.encKeyPack.length);
934 break;
935 case KRB5_PADATA_PK_AS_REP_OLD:
936 case KRB5_PADATA_PK_AS_REQ_OLD:
937 /* if the request is from the broken draft9 client that
938 * expects back a nonce, create it now
939 */
940 if (!fixed_keypack) {
941 init_krb5_reply_key_pack_draft9(&key_pack9);
942 if (key_pack9 == NULL) {
943 retval = ENOMEM;
944 goto cleanup;
945 }
946 key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce;
947 krb5_copy_keyblock_contents(context, encrypting_key,
948 &key_pack9->replyKey);
949
950 retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9,
951 &encoded_key_pack);
952 if (retval) {
953 pkiDebug("failed to encode reply_key_pack\n");
954 goto cleanup;
955 }
956 }
957
958 rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
959 retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
960 reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1,
961 (unsigned char *)encoded_key_pack->data,
962 encoded_key_pack->length,
963 &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length);
964 break;
965 }
966 if (retval) {
967 pkiDebug("failed to create pkcs7 enveloped data: %s\n",
968 error_message(retval));
969 goto cleanup;
970 }
971 #ifdef DEBUG_ASN1
972 print_buffer_bin((unsigned char *)encoded_key_pack->data,
973 encoded_key_pack->length,
974 "/tmp/kdc_key_pack");
975 switch ((int)padata->pa_type) {
976 case KRB5_PADATA_PK_AS_REQ:
977 print_buffer_bin(rep->u.encKeyPack.data,
978 rep->u.encKeyPack.length,
979 "/tmp/kdc_enc_key_pack");
980 break;
981 case KRB5_PADATA_PK_AS_REP_OLD:
982 case KRB5_PADATA_PK_AS_REQ_OLD:
983 print_buffer_bin(rep9->u.encKeyPack.data,
984 rep9->u.encKeyPack.length,
985 "/tmp/kdc_enc_key_pack");
986 break;
987 }
988 #endif
989 }
990
991 switch ((int)padata->pa_type) {
992 case KRB5_PADATA_PK_AS_REQ:
993 retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data);
994 break;
995 case KRB5_PADATA_PK_AS_REP_OLD:
996 case KRB5_PADATA_PK_AS_REQ_OLD:
997 retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data);
998 break;
999 }
1000 if (retval) {
1001 pkiDebug("failed to encode AS_REP\n");
1002 goto cleanup;
1003 }
1004 #ifdef DEBUG_ASN1
1005 if (out_data != NULL)
1006 print_buffer_bin((unsigned char *)out_data->data, out_data->length,
1007 "/tmp/kdc_as_rep");
1008 #endif
1009
1010 *send_pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
1011 if (*send_pa == NULL) {
1012 retval = ENOMEM;
1013 free(out_data->data);
1014 free(out_data);
1015 out_data = NULL;
1016 goto cleanup;
1017 }
1018 (*send_pa)->magic = KV5M_PA_DATA;
1019 switch ((int)padata->pa_type) {
1020 case KRB5_PADATA_PK_AS_REQ:
1021 (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP;
1022 break;
1023 case KRB5_PADATA_PK_AS_REQ_OLD:
1024 case KRB5_PADATA_PK_AS_REP_OLD:
1025 (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
1026 break;
1027 }
1028 (*send_pa)->length = out_data->length;
1029 (*send_pa)->contents = (krb5_octet *) out_data->data;
1030
1031
1032 cleanup:
1033 pkinit_fini_kdc_req_context(context, reqctx);
1034 if (scratch.data != NULL)
1035 free(scratch.data);
1036 if (out_data != NULL)
1037 free(out_data);
1038 if (encoded_dhkey_info != NULL)
1039 krb5_free_data(context, encoded_dhkey_info);
1040 if (encoded_key_pack != NULL)
1041 krb5_free_data(context, encoded_key_pack);
1042 if (dh_pubkey != NULL)
1043 free(dh_pubkey);
1044 if (server_key != NULL)
1045 free(server_key);
1046 if (cksum_types != NULL)
1047 free(cksum_types);
1048
1049 switch ((int)padata->pa_type) {
1050 case KRB5_PADATA_PK_AS_REQ:
1051 free_krb5_pa_pk_as_req(&reqp);
1052 free_krb5_pa_pk_as_rep(&rep);
1053 free_krb5_reply_key_pack(&key_pack);
1054 break;
1055 case KRB5_PADATA_PK_AS_REP_OLD:
1056 case KRB5_PADATA_PK_AS_REQ_OLD:
1057 free_krb5_pa_pk_as_req_draft9(&reqp9);
1058 free_krb5_pa_pk_as_rep_draft9(&rep9);
1059 if (!fixed_keypack)
1060 free_krb5_reply_key_pack_draft9(&key_pack9);
1061 else
1062 free_krb5_reply_key_pack(&key_pack);
1063 break;
1064 }
1065
1066 if (retval)
1067 pkiDebug("pkinit_verify_padata failure");
1068
1069 return retval;
1070 }
1071
1072 /* ARGSUSED */
1073 static int
pkinit_server_get_flags(krb5_context kcontext,krb5_preauthtype patype)1074 pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype)
1075 {
1076 return PA_SUFFICIENT | PA_REPLACES_KEY;
1077 }
1078
1079 static krb5_preauthtype supported_server_pa_types[] = {
1080 KRB5_PADATA_PK_AS_REQ,
1081 KRB5_PADATA_PK_AS_REQ_OLD,
1082 KRB5_PADATA_PK_AS_REP_OLD,
1083 0
1084 };
1085
1086 /* ARGSUSED */
1087 static void
pkinit_fini_kdc_profile(krb5_context context,pkinit_kdc_context plgctx)1088 pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
1089 {
1090 /*
1091 * There is nothing currently allocated by pkinit_init_kdc_profile()
1092 * which needs to be freed here.
1093 */
1094 }
1095
1096 static krb5_error_code
pkinit_init_kdc_profile(krb5_context context,pkinit_kdc_context plgctx)1097 pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
1098 {
1099 krb5_error_code retval;
1100 char *eku_string = NULL;
1101
1102 pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname);
1103 retval = pkinit_kdcdefault_string(context, plgctx->realmname,
1104 "pkinit_identity",
1105 &plgctx->idopts->identity);
1106 if (retval != 0 || NULL == plgctx->idopts->identity) {
1107 retval = EINVAL;
1108 krb5_set_error_message(context, retval,
1109 "No pkinit_identity supplied for realm %s",
1110 plgctx->realmname);
1111 goto errout;
1112 }
1113
1114 retval = pkinit_kdcdefault_strings(context, plgctx->realmname,
1115 "pkinit_anchors",
1116 &plgctx->idopts->anchors);
1117 if (retval != 0 || NULL == plgctx->idopts->anchors) {
1118 retval = EINVAL;
1119 krb5_set_error_message(context, retval,
1120 "No pkinit_anchors supplied for realm %s",
1121 plgctx->realmname);
1122 goto errout;
1123 }
1124
1125 /* Solaris Kerberos */
1126 (void) pkinit_kdcdefault_strings(context, plgctx->realmname,
1127 "pkinit_pool",
1128 &plgctx->idopts->intermediates);
1129
1130 (void) pkinit_kdcdefault_strings(context, plgctx->realmname,
1131 "pkinit_revoke",
1132 &plgctx->idopts->crls);
1133
1134 (void) pkinit_kdcdefault_string(context, plgctx->realmname,
1135 "pkinit_kdc_ocsp",
1136 &plgctx->idopts->ocsp);
1137
1138 (void) pkinit_kdcdefault_string(context, plgctx->realmname,
1139 "pkinit_mappings_file",
1140 &plgctx->idopts->dn_mapping_file);
1141
1142 (void) pkinit_kdcdefault_integer(context, plgctx->realmname,
1143 "pkinit_dh_min_bits",
1144 PKINIT_DEFAULT_DH_MIN_BITS,
1145 &plgctx->opts->dh_min_bits);
1146 if (plgctx->opts->dh_min_bits < 1024) {
1147 pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, "
1148 "using default value (%d) instead\n", __FUNCTION__,
1149 plgctx->opts->dh_min_bits, PKINIT_DEFAULT_DH_MIN_BITS);
1150 plgctx->opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS;
1151 }
1152
1153 (void) pkinit_kdcdefault_boolean(context, plgctx->realmname,
1154 "pkinit_allow_upn",
1155 0, &plgctx->opts->allow_upn);
1156
1157 (void) pkinit_kdcdefault_boolean(context, plgctx->realmname,
1158 "pkinit_require_crl_checking",
1159 0, &plgctx->opts->require_crl_checking);
1160
1161 (void) pkinit_kdcdefault_string(context, plgctx->realmname,
1162 "pkinit_eku_checking",
1163 &eku_string);
1164 if (eku_string != NULL) {
1165 if (strcasecmp(eku_string, "kpClientAuth") == 0) {
1166 plgctx->opts->require_eku = 1;
1167 plgctx->opts->accept_secondary_eku = 0;
1168 } else if (strcasecmp(eku_string, "scLogin") == 0) {
1169 plgctx->opts->require_eku = 1;
1170 plgctx->opts->accept_secondary_eku = 1;
1171 } else if (strcasecmp(eku_string, "none") == 0) {
1172 plgctx->opts->require_eku = 0;
1173 plgctx->opts->accept_secondary_eku = 0;
1174 } else {
1175 pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
1176 __FUNCTION__, eku_string);
1177 }
1178 free(eku_string);
1179 }
1180
1181
1182 return 0;
1183 errout:
1184 pkinit_fini_kdc_profile(context, plgctx);
1185 return retval;
1186 }
1187
1188 /* ARGSUSED */
1189 static pkinit_kdc_context
pkinit_find_realm_context(krb5_context context,void * pa_plugin_context,krb5_principal princ)1190 pkinit_find_realm_context(krb5_context context, void *pa_plugin_context,
1191 krb5_principal princ)
1192 {
1193 int i;
1194 pkinit_kdc_context *realm_contexts = pa_plugin_context;
1195
1196 if (pa_plugin_context == NULL)
1197 return NULL;
1198
1199 for (i = 0; realm_contexts[i] != NULL; i++) {
1200 pkinit_kdc_context p = realm_contexts[i];
1201
1202 if ((p->realmname_len == princ->realm.length) &&
1203 (strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) {
1204 pkiDebug("%s: returning context at %p for realm '%s'\n",
1205 __FUNCTION__, p, p->realmname);
1206 return p;
1207 }
1208 }
1209 pkiDebug("%s: unable to find realm context for realm '%.*s'\n",
1210 __FUNCTION__, princ->realm.length, princ->realm.data);
1211 return NULL;
1212 }
1213
1214 static int
pkinit_server_plugin_init_realm(krb5_context context,const char * realmname,pkinit_kdc_context * pplgctx)1215 pkinit_server_plugin_init_realm(krb5_context context, const char *realmname,
1216 pkinit_kdc_context *pplgctx)
1217 {
1218 krb5_error_code retval = ENOMEM;
1219 pkinit_kdc_context plgctx = NULL;
1220
1221 *pplgctx = NULL;
1222
1223 plgctx = (pkinit_kdc_context) calloc(1, sizeof(*plgctx));
1224 if (plgctx == NULL)
1225 goto errout;
1226
1227 pkiDebug("%s: initializing context at %p for realm '%s'\n",
1228 __FUNCTION__, plgctx, realmname);
1229 (void) memset(plgctx, 0, sizeof(*plgctx));
1230 plgctx->magic = PKINIT_CTX_MAGIC;
1231
1232 plgctx->realmname = strdup(realmname);
1233 if (plgctx->realmname == NULL)
1234 goto errout;
1235 plgctx->realmname_len = strlen(plgctx->realmname);
1236
1237 retval = pkinit_init_plg_crypto(&plgctx->cryptoctx);
1238 if (retval)
1239 goto errout;
1240
1241 retval = pkinit_init_plg_opts(&plgctx->opts);
1242 if (retval)
1243 goto errout;
1244
1245 retval = pkinit_init_identity_crypto(&plgctx->idctx);
1246 if (retval)
1247 goto errout;
1248
1249 retval = pkinit_init_identity_opts(&plgctx->idopts);
1250 if (retval)
1251 goto errout;
1252
1253 retval = pkinit_init_kdc_profile(context, plgctx);
1254 if (retval)
1255 goto errout;
1256
1257 /*
1258 * Solaris Kerberos:
1259 * Some methods of storing key information (PKCS11, PKCS12,...) may
1260 * require interactive prompting.
1261 */
1262 retval = pkinit_identity_set_prompter(plgctx->idctx, krb5_prompter_posix,
1263 NULL);
1264 if (retval)
1265 goto errout;
1266
1267 retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL,
1268 plgctx->idopts, plgctx->idctx, 0, NULL);
1269 if (retval)
1270 goto errout;
1271
1272 pkiDebug("%s: returning context at %p for realm '%s'\n",
1273 __FUNCTION__, plgctx, realmname);
1274 *pplgctx = plgctx;
1275 retval = 0;
1276
1277 errout:
1278 if (retval)
1279 pkinit_server_plugin_fini_realm(context, plgctx);
1280
1281 return retval;
1282 }
1283
1284 static int
pkinit_server_plugin_init(krb5_context context,void ** blob,const char ** realmnames)1285 pkinit_server_plugin_init(krb5_context context, void **blob,
1286 const char **realmnames)
1287 {
1288 krb5_error_code retval = ENOMEM;
1289 pkinit_kdc_context plgctx, *realm_contexts = NULL;
1290 int i, j;
1291 size_t numrealms;
1292
1293 retval = pkinit_accessor_init();
1294 if (retval)
1295 return retval;
1296
1297 /* Determine how many realms we may need to support */
1298 for (i = 0; realmnames[i] != NULL; i++) {};
1299 numrealms = i;
1300
1301 realm_contexts = (pkinit_kdc_context *)
1302 calloc(numrealms+1, sizeof(pkinit_kdc_context));
1303 if (realm_contexts == NULL)
1304 return ENOMEM;
1305
1306 for (i = 0, j = 0; i < numrealms; i++) {
1307 pkiDebug("%s: processing realm '%s'\n", __FUNCTION__, realmnames[i]);
1308 retval = pkinit_server_plugin_init_realm(context, realmnames[i], &plgctx);
1309 if (retval == 0 && plgctx != NULL)
1310 realm_contexts[j++] = plgctx;
1311 }
1312
1313 if (j == 0) {
1314 /*
1315 * Solaris Kerberos
1316 * Improve error messages for the common case of a single realm
1317 */
1318 if (numrealms != 1) {
1319 retval = EINVAL;
1320 krb5_set_error_message(context, retval, "No realms configured "
1321 "correctly for pkinit support");
1322 }
1323
1324 goto errout;
1325 }
1326
1327 *blob = realm_contexts;
1328 retval = 0;
1329 pkiDebug("%s: returning context at %p\n", __FUNCTION__, realm_contexts);
1330
1331 errout:
1332 if (retval)
1333 pkinit_server_plugin_fini(context, realm_contexts);
1334
1335 return retval;
1336 }
1337
1338 static void
pkinit_server_plugin_fini_realm(krb5_context context,pkinit_kdc_context plgctx)1339 pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx)
1340 {
1341 if (plgctx == NULL)
1342 return;
1343
1344 pkinit_fini_kdc_profile(context, plgctx);
1345 pkinit_fini_identity_opts(plgctx->idopts);
1346 pkinit_fini_identity_crypto(plgctx->idctx);
1347 pkinit_fini_plg_crypto(plgctx->cryptoctx);
1348 pkinit_fini_plg_opts(plgctx->opts);
1349 free(plgctx->realmname);
1350 free(plgctx);
1351 }
1352
1353 static void
pkinit_server_plugin_fini(krb5_context context,void * blob)1354 pkinit_server_plugin_fini(krb5_context context, void *blob)
1355 {
1356 pkinit_kdc_context *realm_contexts = blob;
1357 int i;
1358
1359 if (realm_contexts == NULL)
1360 return;
1361
1362 for (i = 0; realm_contexts[i] != NULL; i++) {
1363 pkinit_server_plugin_fini_realm(context, realm_contexts[i]);
1364 }
1365 pkiDebug("%s: freeing context at %p\n", __FUNCTION__, realm_contexts);
1366 free(realm_contexts);
1367 }
1368
1369 static krb5_error_code
pkinit_init_kdc_req_context(krb5_context context,void ** ctx)1370 pkinit_init_kdc_req_context(krb5_context context, void **ctx)
1371 {
1372 krb5_error_code retval = ENOMEM;
1373 pkinit_kdc_req_context reqctx = NULL;
1374
1375 reqctx = (pkinit_kdc_req_context)malloc(sizeof(*reqctx));
1376 if (reqctx == NULL)
1377 return retval;
1378 (void) memset(reqctx, 0, sizeof(*reqctx));
1379 reqctx->magic = PKINIT_CTX_MAGIC;
1380
1381 retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
1382 if (retval)
1383 goto cleanup;
1384 reqctx->rcv_auth_pack = NULL;
1385 reqctx->rcv_auth_pack9 = NULL;
1386
1387 pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
1388 *ctx = reqctx;
1389 retval = 0;
1390 cleanup:
1391 if (retval)
1392 pkinit_fini_kdc_req_context(context, reqctx);
1393
1394 return retval;
1395 }
1396
1397 static void
pkinit_fini_kdc_req_context(krb5_context context,void * ctx)1398 pkinit_fini_kdc_req_context(krb5_context context, void *ctx)
1399 {
1400 pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx;
1401
1402 if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) {
1403 pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx);
1404 return;
1405 }
1406 pkiDebug("%s: freeing reqctx at %p\n", __FUNCTION__, reqctx);
1407
1408 pkinit_fini_req_crypto(reqctx->cryptoctx);
1409 if (reqctx->rcv_auth_pack != NULL)
1410 free_krb5_auth_pack(&reqctx->rcv_auth_pack);
1411 if (reqctx->rcv_auth_pack9 != NULL)
1412 free_krb5_auth_pack_draft9(context, &reqctx->rcv_auth_pack9);
1413
1414 free(reqctx);
1415 }
1416
1417 struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = {
1418 "pkinit", /* name */
1419 supported_server_pa_types, /* pa_type_list */
1420 pkinit_server_plugin_init, /* (*init_proc) */
1421 pkinit_server_plugin_fini, /* (*fini_proc) */
1422 pkinit_server_get_flags, /* (*flags_proc) */
1423 pkinit_server_get_edata, /* (*edata_proc) */
1424 pkinit_server_verify_padata,/* (*verify_proc) */
1425 pkinit_server_return_padata,/* (*return_proc) */
1426 NULL, /* (*freepa_reqcontext_proc) */
1427 };
1428