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