xref: /freebsd/crypto/krb5/src/plugins/preauth/pkinit/pkinit_srv.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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