Path: blob/main/crypto/krb5/src/plugins/preauth/pkinit/pkinit_srv.c
34923 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/*2* COPYRIGHT (C) 2006,20073* THE REGENTS OF THE UNIVERSITY OF MICHIGAN4* ALL RIGHTS RESERVED5*6* Permission is granted to use, copy, create derivative works7* and redistribute this software and such derivative works8* for any purpose, so long as the name of The University of9* Michigan is not used in any advertising or publicity10* pertaining to the use of distribution of this software11* without specific, written prior authorization. If the12* above copyright notice or any other identification of the13* University of Michigan is included in any copy of any14* portion of this software, then the disclaimer below must15* also be included.16*17* THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION18* FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY19* PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF20* MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING21* WITHOUT LIMITATION THE IMPLIED WARRANTIES OF22* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE23* REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE24* FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR25* CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING26* OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN27* IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF28* SUCH DAMAGES.29*/3031#include <k5-int.h>32#include "pkinit.h"33#include "krb5/certauth_plugin.h"3435/* Aliases used by the built-in certauth modules */36struct certauth_req_opts {37krb5_kdcpreauth_callbacks cb;38krb5_kdcpreauth_rock rock;39pkinit_kdc_context plgctx;40pkinit_kdc_req_context reqctx;41};4243typedef struct certauth_module_handle_st {44struct krb5_certauth_vtable_st vt;45krb5_certauth_moddata moddata;46} *certauth_handle;4748struct krb5_kdcpreauth_moddata_st {49pkinit_kdc_context *realm_contexts;50certauth_handle *certauth_modules;51};5253static krb5_error_code54pkinit_init_kdc_req_context(krb5_context, pkinit_kdc_req_context *blob);5556static void57pkinit_fini_kdc_req_context(krb5_context context, void *blob);5859static void60pkinit_server_plugin_fini_realm(krb5_context context,61pkinit_kdc_context plgctx);6263static void64pkinit_server_plugin_fini(krb5_context context,65krb5_kdcpreauth_moddata moddata);6667static pkinit_kdc_context68pkinit_find_realm_context(krb5_context context,69krb5_kdcpreauth_moddata moddata,70krb5_principal princ);7172static void73free_realm_contexts(krb5_context context, pkinit_kdc_context *realm_contexts)74{75size_t i;7677if (realm_contexts == NULL)78return;79for (i = 0; realm_contexts[i] != NULL; i++)80pkinit_server_plugin_fini_realm(context, realm_contexts[i]);81pkiDebug("%s: freeing context at %p\n", __FUNCTION__, realm_contexts);82free(realm_contexts);83}8485static void86free_certauth_handles(krb5_context context, certauth_handle *list)87{88size_t i;8990if (list == NULL)91return;92for (i = 0; list[i] != NULL; i++) {93if (list[i]->vt.fini != NULL)94list[i]->vt.fini(context, list[i]->moddata);95free(list[i]);96}97free(list);98}99100static krb5_error_code101pkinit_create_edata(krb5_context context,102pkinit_plg_crypto_context plg_cryptoctx,103pkinit_req_crypto_context req_cryptoctx,104pkinit_identity_crypto_context id_cryptoctx,105pkinit_plg_opts *opts,106krb5_error_code err_code,107krb5_pa_data ***e_data_out)108{109krb5_error_code retval = KRB5KRB_ERR_GENERIC;110111pkiDebug("pkinit_create_edata: creating edata for error %d (%s)\n",112err_code, error_message(err_code));113switch(err_code) {114case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:115retval = pkinit_create_td_trusted_certifiers(context,116plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data_out);117break;118case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:119retval = pkinit_create_td_dh_parameters(context, plg_cryptoctx,120req_cryptoctx, id_cryptoctx, opts, e_data_out);121break;122case KRB5KDC_ERR_INVALID_CERTIFICATE:123case KRB5KDC_ERR_REVOKED_CERTIFICATE:124retval = pkinit_create_td_invalid_certificate(context,125plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data_out);126break;127default:128pkiDebug("no edata needed for error %d (%s)\n",129err_code, error_message(err_code));130retval = 0;131goto cleanup;132}133134cleanup:135136return retval;137}138139static void140pkinit_server_get_edata(krb5_context context,141krb5_kdc_req *request,142krb5_kdcpreauth_callbacks cb,143krb5_kdcpreauth_rock rock,144krb5_kdcpreauth_moddata moddata,145krb5_preauthtype pa_type,146krb5_kdcpreauth_edata_respond_fn respond,147void *arg)148{149krb5_error_code retval = 0;150pkinit_kdc_context plgctx = NULL;151152pkiDebug("pkinit_server_get_edata: entered!\n");153154155/*156* If we don't have a realm context for the given realm,157* don't tell the client that we support pkinit!158*/159plgctx = pkinit_find_realm_context(context, moddata, request->server);160if (plgctx == NULL)161retval = EINVAL;162163/* Send a freshness token if the client requested one. */164if (!retval)165cb->send_freshness_token(context, rock);166167(*respond)(arg, retval, NULL);168}169170static krb5_error_code171verify_client_san(krb5_context context,172pkinit_kdc_context plgctx,173pkinit_kdc_req_context reqctx,174krb5_kdcpreauth_callbacks cb,175krb5_kdcpreauth_rock rock,176krb5_const_principal client,177int *valid_san)178{179krb5_error_code retval;180krb5_principal *princs = NULL, upn;181krb5_boolean match;182char **upns = NULL;183size_t i;184#ifdef DEBUG_SAN_INFO185char *client_string = NULL, *san_string;186#endif187188*valid_san = 0;189retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,190reqctx->cryptoctx, plgctx->idctx,191&princs,192plgctx->opts->allow_upn ? &upns : NULL,193NULL);194if (retval) {195pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);196retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;197goto out;198}199200if (princs == NULL && upns == NULL) {201TRACE_PKINIT_SERVER_NO_SAN(context);202retval = ENOENT;203goto out;204}205206#ifdef DEBUG_SAN_INFO207krb5_unparse_name(context, client, &client_string);208#endif209pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);210for (i = 0; princs != NULL && princs[i] != NULL; i++) {211#ifdef DEBUG_SAN_INFO212krb5_unparse_name(context, princs[i], &san_string);213pkiDebug("%s: Comparing client '%s' to pkinit san value '%s'\n",214__FUNCTION__, client_string, san_string);215krb5_free_unparsed_name(context, san_string);216#endif217if (cb->match_client(context, rock, princs[i])) {218TRACE_PKINIT_SERVER_MATCHING_SAN_FOUND(context);219*valid_san = 1;220retval = 0;221goto out;222}223}224pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);225/*226* XXX if cert has names but none match, should we227* be returning KRB5KDC_ERR_CLIENT_NAME_MISMATCH here?228*/229230if (upns == NULL) {231pkiDebug("%s: no upn sans (or we wouldn't accept them anyway)\n",232__FUNCTION__);233retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;234goto out;235}236237pkiDebug("%s: Checking upn sans\n", __FUNCTION__);238for (i = 0; upns[i] != NULL; i++) {239#ifdef DEBUG_SAN_INFO240pkiDebug("%s: Comparing client '%s' to upn san value '%s'\n",241__FUNCTION__, client_string, upns[i]);242#endif243retval = krb5_parse_name_flags(context, upns[i],244KRB5_PRINCIPAL_PARSE_ENTERPRISE, &upn);245if (retval) {246TRACE_PKINIT_SERVER_UPN_PARSE_FAIL(context, upns[i], retval);247continue;248}249match = cb->match_client(context, rock, upn);250krb5_free_principal(context, upn);251if (match) {252TRACE_PKINIT_SERVER_MATCHING_UPN_FOUND(context);253*valid_san = 1;254retval = 0;255goto out;256}257}258pkiDebug("%s: no upn san match found\n", __FUNCTION__);259260retval = 0;261out:262if (princs != NULL) {263for (i = 0; princs[i] != NULL; i++)264krb5_free_principal(context, princs[i]);265free(princs);266}267if (upns != NULL) {268for (i = 0; upns[i] != NULL; i++)269free(upns[i]);270free(upns);271}272#ifdef DEBUG_SAN_INFO273if (client_string != NULL)274krb5_free_unparsed_name(context, client_string);275#endif276pkiDebug("%s: returning retval %d, valid_san %d\n",277__FUNCTION__, retval, *valid_san);278return retval;279}280281static krb5_error_code282verify_client_eku(krb5_context context,283pkinit_kdc_context plgctx,284pkinit_kdc_req_context reqctx,285int *eku_accepted)286{287krb5_error_code retval;288289*eku_accepted = 0;290291if (plgctx->opts->require_eku == 0) {292TRACE_PKINIT_SERVER_EKU_SKIP(context);293*eku_accepted = 1;294retval = 0;295goto out;296}297298retval = crypto_check_cert_eku(context, plgctx->cryptoctx,299reqctx->cryptoctx, plgctx->idctx,3000, /* kdc cert */301plgctx->opts->accept_secondary_eku,302eku_accepted);303if (retval) {304pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",305__FUNCTION__, retval, error_message(retval));306goto out;307}308309out:310pkiDebug("%s: returning retval %d, eku_accepted %d\n",311__FUNCTION__, retval, *eku_accepted);312return retval;313}314315316/* Run the received, verified certificate through certauth modules, to verify317* that it is authorized to authenticate as client. */318static krb5_error_code319authorize_cert(krb5_context context, certauth_handle *certauth_modules,320pkinit_kdc_context plgctx, pkinit_kdc_req_context reqctx,321krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,322krb5_principal client, krb5_boolean *hwauth_out)323{324krb5_error_code ret;325certauth_handle h;326struct certauth_req_opts opts;327krb5_boolean accepted = FALSE, hwauth = FALSE;328uint8_t *cert;329size_t i, cert_len;330void *db_ent = NULL;331char **ais = NULL, **ai = NULL;332333/* Re-encode the received certificate into DER, which is extra work, but334* avoids creating an X.509 library dependency in the interface. */335ret = crypto_encode_der_cert(context, reqctx->cryptoctx, &cert, &cert_len);336if (ret)337goto cleanup;338339/* Set options for the builtin module. */340opts.plgctx = plgctx;341opts.reqctx = reqctx;342opts.cb = cb;343opts.rock = rock;344345db_ent = cb->client_entry(context, rock);346347/*348* Check the certificate against each certauth module. For the certificate349* to be authorized at least one module must return 0 or350* KRB5_CERTAUTH_HWAUTH, and no module can return an error code other than351* KRB5_PLUGIN_NO_HANDLE (pass) or KRB5_CERTAUTH_HWAUTH_PASS (pass but352* set hw-authent). Add indicators from all modules.353*/354ret = KRB5_PLUGIN_NO_HANDLE;355for (i = 0; certauth_modules != NULL && certauth_modules[i] != NULL; i++) {356h = certauth_modules[i];357TRACE_PKINIT_SERVER_CERT_AUTH(context, h->vt.name);358ret = h->vt.authorize(context, h->moddata, cert, cert_len, client,359&opts, db_ent, &ais);360if (ret == 0)361accepted = TRUE;362else if (ret == KRB5_CERTAUTH_HWAUTH)363accepted = hwauth = TRUE;364else if (ret == KRB5_CERTAUTH_HWAUTH_PASS)365hwauth = TRUE;366else if (ret != KRB5_PLUGIN_NO_HANDLE)367goto cleanup;368369if (ais != NULL) {370/* Assert authentication indicators from the module. */371for (ai = ais; *ai != NULL; ai++) {372ret = cb->add_auth_indicator(context, rock, *ai);373if (ret)374goto cleanup;375}376h->vt.free_ind(context, h->moddata, ais);377ais = NULL;378}379}380381*hwauth_out = hwauth;382ret = accepted ? 0 : KRB5KDC_ERR_CLIENT_NAME_MISMATCH;383384cleanup:385free(cert);386return ret;387}388389/* Return an error if freshness tokens are required and one was not received.390* Log an appropriate message indicating whether a valid token was received. */391static krb5_error_code392check_log_freshness(krb5_context context, pkinit_kdc_context plgctx,393krb5_kdc_req *request, krb5_boolean valid_freshness_token)394{395krb5_error_code ret;396char *name = NULL;397398ret = krb5_unparse_name(context, request->client, &name);399if (ret)400return ret;401if (plgctx->opts->require_freshness && !valid_freshness_token) {402com_err("", 0, _("PKINIT: no freshness token, rejecting auth from %s"),403name);404ret = KRB5KDC_ERR_PREAUTH_FAILED;405} else if (valid_freshness_token) {406com_err("", 0, _("PKINIT: freshness token received from %s"), name);407} else {408com_err("", 0, _("PKINIT: no freshness token received from %s"), name);409}410krb5_free_unparsed_name(context, name);411return ret;412}413414static void415pkinit_server_verify_padata(krb5_context context,416krb5_data *req_pkt,417krb5_kdc_req * request,418krb5_enc_tkt_part * enc_tkt_reply,419krb5_pa_data * data,420krb5_kdcpreauth_callbacks cb,421krb5_kdcpreauth_rock rock,422krb5_kdcpreauth_moddata moddata,423krb5_kdcpreauth_verify_respond_fn respond,424void *arg)425{426krb5_error_code retval = 0;427krb5_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL};428krb5_pa_pk_as_req *reqp = NULL;429krb5_auth_pack *auth_pack = NULL;430krb5_pk_authenticator *pka;431pkinit_kdc_context plgctx = NULL;432pkinit_kdc_req_context reqctx = NULL;433krb5_checksum cksum = {0, 0, 0, NULL};434krb5_data *der_req = NULL;435krb5_data k5data;436int is_signed = 1;437krb5_pa_data **e_data = NULL;438krb5_kdcpreauth_modreq modreq = NULL;439krb5_boolean valid_freshness_token = FALSE, hwauth = FALSE;440char **sp;441442pkiDebug("pkinit_verify_padata: entered!\n");443if (data == NULL || data->length <= 0 || data->contents == NULL) {444(*respond)(arg, EINVAL, NULL, NULL, NULL);445return;446}447448449if (moddata == NULL) {450(*respond)(arg, EINVAL, NULL, NULL, NULL);451return;452}453454plgctx = pkinit_find_realm_context(context, moddata, request->server);455if (plgctx == NULL) {456(*respond)(arg, EINVAL, NULL, NULL, NULL);457return;458}459460#ifdef DEBUG_ASN1461print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req");462#endif463/* create a per-request context */464retval = pkinit_init_kdc_req_context(context, &reqctx);465if (retval)466goto cleanup;467reqctx->pa_type = data->pa_type;468469PADATA_TO_KRB5DATA(data, &k5data);470471if (data->pa_type != KRB5_PADATA_PK_AS_REQ) {472pkiDebug("unrecognized pa_type = %d\n", data->pa_type);473retval = EINVAL;474goto cleanup;475}476477TRACE_PKINIT_SERVER_PADATA_VERIFY(context);478retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp);479if (retval) {480pkiDebug("decode_krb5_pa_pk_as_req failed\n");481goto cleanup;482}483#ifdef DEBUG_ASN1484print_buffer_bin(reqp->signedAuthPack.data, reqp->signedAuthPack.length,485"/tmp/kdc_signed_data");486#endif487retval = cms_signeddata_verify(context, plgctx->cryptoctx,488reqctx->cryptoctx, plgctx->idctx,489CMS_SIGN_CLIENT,490plgctx->opts->require_crl_checking,491(unsigned char *)reqp->signedAuthPack.data,492reqp->signedAuthPack.length,493(unsigned char **)&authp_data.data,494&authp_data.length,495(unsigned char **)&krb5_authz.data,496&krb5_authz.length, &is_signed);497if (retval) {498TRACE_PKINIT_SERVER_PADATA_VERIFY_FAIL(context);499goto cleanup;500}501if (is_signed) {502retval = authorize_cert(context, moddata->certauth_modules, plgctx,503reqctx, cb, rock, request->client, &hwauth);504if (retval)505goto cleanup;506507} else { /* !is_signed */508if (!krb5_principal_compare(context, request->client,509krb5_anonymous_principal())) {510retval = KRB5KDC_ERR_PREAUTH_FAILED;511krb5_set_error_message(context, retval,512_("Pkinit request not signed, but client "513"not anonymous."));514goto cleanup;515}516}517#ifdef DEBUG_ASN1518print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack");519#endif520521OCTETDATA_TO_KRB5DATA(&authp_data, &k5data);522retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack);523if (retval) {524pkiDebug("failed to decode krb5_auth_pack\n");525goto cleanup;526}527pka = &auth_pack->pkAuthenticator;528529retval = krb5_check_clockskew(context, pka->ctime);530if (retval)531goto cleanup;532533/* check dh parameters */534if (auth_pack->clientPublicValue.length > 0) {535retval = server_check_dh(context, plgctx->cryptoctx,536reqctx->cryptoctx, plgctx->idctx,537&auth_pack->clientPublicValue,538plgctx->opts->dh_min_bits);539if (retval) {540pkiDebug("bad dh parameters\n");541goto cleanup;542}543} else if (!is_signed) {544/*Anonymous pkinit requires DH*/545retval = KRB5KDC_ERR_PREAUTH_FAILED;546krb5_set_error_message(context, retval,547_("Anonymous pkinit without DH public "548"value not supported."));549goto cleanup;550}551der_req = cb->request_body(context, rock);552553retval = crypto_verify_checksums(context, der_req, &pka->paChecksum,554pka->paChecksum2);555if (retval)556goto cleanup;557558if (pka->freshnessToken != NULL) {559retval = cb->check_freshness_token(context, rock, pka->freshnessToken);560if (retval)561goto cleanup;562valid_freshness_token = TRUE;563}564565/* check if kdcPkId present and match KDC's subjectIdentifier */566if (reqp->kdcPkId.data != NULL) {567int valid_kdcPkId = 0;568retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx,569reqctx->cryptoctx, plgctx->idctx,570(unsigned char *)reqp->kdcPkId.data,571reqp->kdcPkId.length, &valid_kdcPkId);572if (retval)573goto cleanup;574if (!valid_kdcPkId) {575pkiDebug("kdcPkId in AS_REQ does not match KDC's cert; "576"RFC says to ignore and proceed\n");577}578}579/* remember the decoded auth_pack for verify_padata routine */580reqctx->rcv_auth_pack = auth_pack;581auth_pack = NULL;582583if (is_signed) {584retval = check_log_freshness(context, plgctx, request,585valid_freshness_token);586if (retval)587goto cleanup;588}589590if (is_signed && plgctx->auth_indicators != NULL) {591/* Assert configured authentication indicators. */592for (sp = plgctx->auth_indicators; *sp != NULL; sp++) {593retval = cb->add_auth_indicator(context, rock, *sp);594if (retval)595goto cleanup;596}597}598599/* remember to set the PREAUTH flag in the reply */600enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;601if (hwauth)602enc_tkt_reply->flags |= TKT_FLG_HW_AUTH;603modreq = (krb5_kdcpreauth_modreq)reqctx;604reqctx = NULL;605606cleanup:607if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) {608pkiDebug("pkinit_verify_padata failed: creating e-data\n");609if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx,610plgctx->idctx, plgctx->opts, retval, &e_data))611pkiDebug("pkinit_create_edata failed\n");612}613614free_krb5_pa_pk_as_req(&reqp);615free(cksum.contents);616free(authp_data.data);617free(krb5_authz.data);618if (reqctx != NULL)619pkinit_fini_kdc_req_context(context, reqctx);620free_krb5_auth_pack(&auth_pack);621622(*respond)(arg, retval, modreq, e_data, NULL);623}624static krb5_error_code625return_pkinit_kx(krb5_context context, krb5_kdc_req *request,626krb5_kdc_rep *reply, krb5_keyblock *encrypting_key,627krb5_pa_data **out_padata)628{629krb5_error_code ret = 0;630krb5_keyblock *session = reply->ticket->enc_part2->session;631krb5_keyblock *new_session = NULL;632krb5_pa_data *pa = NULL;633krb5_enc_data enc;634krb5_data *scratch = NULL;635636*out_padata = NULL;637enc.ciphertext.data = NULL;638if (!krb5_principal_compare(context, request->client,639krb5_anonymous_principal()))640return 0;641/*642* The KDC contribution key needs to be a fresh key of an enctype supported643* by the client and server. The existing session key meets these644* requirements so we use it.645*/646ret = krb5_c_fx_cf2_simple(context, session, "PKINIT",647encrypting_key, "KEYEXCHANGE",648&new_session);649if (ret)650goto cleanup;651ret = encode_krb5_encryption_key( session, &scratch);652if (ret)653goto cleanup;654ret = krb5_encrypt_helper(context, encrypting_key,655KRB5_KEYUSAGE_PA_PKINIT_KX, scratch, &enc);656if (ret)657goto cleanup;658memset(scratch->data, 0, scratch->length);659krb5_free_data(context, scratch);660scratch = NULL;661ret = encode_krb5_enc_data(&enc, &scratch);662if (ret)663goto cleanup;664pa = malloc(sizeof(krb5_pa_data));665if (pa == NULL) {666ret = ENOMEM;667goto cleanup;668}669pa->pa_type = KRB5_PADATA_PKINIT_KX;670pa->length = scratch->length;671pa->contents = (krb5_octet *) scratch->data;672*out_padata = pa;673scratch->data = NULL;674memset(session->contents, 0, session->length);675krb5_free_keyblock_contents(context, session);676*session = *new_session;677new_session->contents = NULL;678cleanup:679krb5_free_data_contents(context, &enc.ciphertext);680krb5_free_keyblock(context, new_session);681krb5_free_data(context, scratch);682return ret;683}684685static krb5_error_code686pkinit_pick_kdf_alg(krb5_context context, krb5_data **kdf_list,687krb5_data **alg_oid)688{689krb5_error_code retval = 0;690krb5_data *req_oid = NULL;691const krb5_data *supp_oid = NULL;692krb5_data *tmp_oid = NULL;693size_t i, j = 0;694695/* if we don't find a match, return NULL value */696*alg_oid = NULL;697698/* for each of the OIDs that the server supports... */699for (i = 0; NULL != (supp_oid = supported_kdf_alg_ids[i]); i++) {700/* if the requested OID is in the client's list, use it. */701for (j = 0; NULL != (req_oid = kdf_list[j]); j++) {702if ((req_oid->length == supp_oid->length) &&703(0 == memcmp(req_oid->data, supp_oid->data, req_oid->length))) {704tmp_oid = k5alloc(sizeof(krb5_data), &retval);705if (retval)706goto cleanup;707tmp_oid->data = k5memdup(supp_oid->data, supp_oid->length,708&retval);709if (retval)710goto cleanup;711tmp_oid->length = supp_oid->length;712*alg_oid = tmp_oid;713/* don't free the OID in clean-up if we are returning it */714tmp_oid = NULL;715goto cleanup;716}717}718}719cleanup:720if (tmp_oid)721krb5_free_data(context, tmp_oid);722return retval;723}724725static krb5_error_code726pkinit_server_return_padata(krb5_context context,727krb5_pa_data * padata,728krb5_data *req_pkt,729krb5_kdc_req * request,730krb5_kdc_rep * reply,731krb5_keyblock * encrypting_key,732krb5_pa_data ** send_pa,733krb5_kdcpreauth_callbacks cb,734krb5_kdcpreauth_rock rock,735krb5_kdcpreauth_moddata moddata,736krb5_kdcpreauth_modreq modreq)737{738krb5_error_code retval = 0;739krb5_data scratch = {0, 0, NULL};740krb5_pa_pk_as_req *reqp = NULL;741int i = 0;742743unsigned char *dh_pubkey = NULL, *server_key = NULL;744unsigned int server_key_len = 0, dh_pubkey_len = 0;745krb5_keyblock reply_key = { 0 };746747krb5_kdc_dh_key_info dhkey_info;748krb5_data *encoded_dhkey_info = NULL;749krb5_pa_pk_as_rep *rep = NULL;750krb5_data *out_data = NULL;751krb5_data secret;752753krb5_enctype enctype = -1;754755krb5_reply_key_pack *key_pack = NULL;756krb5_data *encoded_key_pack = NULL;757758pkinit_kdc_context plgctx;759pkinit_kdc_req_context reqctx;760761*send_pa = NULL;762if (padata->pa_type == KRB5_PADATA_PKINIT_KX) {763return return_pkinit_kx(context, request, reply,764encrypting_key, send_pa);765}766if (padata->length <= 0 || padata->contents == NULL)767return 0;768769if (modreq == NULL) {770pkiDebug("missing request context \n");771return EINVAL;772}773774plgctx = pkinit_find_realm_context(context, moddata, request->server);775if (plgctx == NULL) {776pkiDebug("Unable to locate correct realm context\n");777return ENOENT;778}779780TRACE_PKINIT_SERVER_RETURN_PADATA(context);781reqctx = (pkinit_kdc_req_context)modreq;782783for(i = 0; i < request->nktypes; i++) {784enctype = request->ktype[i];785if (!krb5_c_valid_enctype(enctype))786continue;787else {788pkiDebug("KDC picked etype = %d\n", enctype);789break;790}791}792793if (i == request->nktypes) {794retval = KRB5KDC_ERR_ETYPE_NOSUPP;795goto cleanup;796}797798init_krb5_pa_pk_as_rep(&rep);799if (rep == NULL) {800retval = ENOMEM;801goto cleanup;802}803804if (reqctx->rcv_auth_pack == NULL ||805reqctx->rcv_auth_pack->clientPublicValue.length == 0) {806retval = KRB5KDC_ERR_PREAUTH_FAILED;807k5_setmsg(context, retval, _("Unsupported PKINIT RSA request"));808goto cleanup;809}810811rep->choice = choice_pa_pk_as_rep_dhInfo;812813retval = server_process_dh(context, plgctx->cryptoctx, reqctx->cryptoctx,814plgctx->idctx, &dh_pubkey, &dh_pubkey_len,815&server_key, &server_key_len);816if (retval) {817pkiDebug("failed to process/create dh parameters\n");818goto cleanup;819}820821dhkey_info.subjectPublicKey.length = dh_pubkey_len;822dhkey_info.subjectPublicKey.data = (char *)dh_pubkey;823dhkey_info.nonce = request->nonce;824dhkey_info.dhKeyExpiration = 0;825826retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info,827&encoded_dhkey_info);828if (retval) {829pkiDebug("encode_krb5_kdc_dh_key_info failed\n");830goto cleanup;831}832#ifdef DEBUG_ASN1833print_buffer_bin((unsigned char *)encoded_dhkey_info->data,834encoded_dhkey_info->length, "/tmp/kdc_dh_key_info");835#endif836837retval = cms_signeddata_create(context, plgctx->cryptoctx,838reqctx->cryptoctx, plgctx->idctx,839CMS_SIGN_SERVER,840(unsigned char *)encoded_dhkey_info->data,841encoded_dhkey_info->length,842(unsigned char **)843&rep->u.dh_Info.dhSignedData.data,844&rep->u.dh_Info.dhSignedData.length);845if (retval) {846pkiDebug("failed to create pkcs7 signed data\n");847goto cleanup;848}849850if (reqctx->rcv_auth_pack != NULL &&851reqctx->rcv_auth_pack->supportedKDFs != NULL) {852/* If using the alg-agility KDF, put the algorithm in the reply853* before encoding it.854*/855if (reqctx->rcv_auth_pack != NULL &&856reqctx->rcv_auth_pack->supportedKDFs != NULL) {857retval = pkinit_pick_kdf_alg(context, reqctx->rcv_auth_pack->supportedKDFs,858&(rep->u.dh_Info.kdfID));859if (retval) {860pkiDebug("pkinit_pick_kdf_alg failed: %s\n",861error_message(retval));862goto cleanup;863}864}865}866867retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data);868if (retval) {869pkiDebug("failed to encode AS_REP\n");870goto cleanup;871}872#ifdef DEBUG_ASN1873if (out_data != NULL)874print_buffer_bin((unsigned char *)out_data->data, out_data->length,875"/tmp/kdc_as_rep");876#endif877878secret = make_data(server_key, server_key_len);879retval = pkinit_kdf(context, &secret, rep->u.dh_Info.kdfID,880request->client, request->server, enctype, req_pkt,881out_data, &reply_key);882if (retval) {883pkiDebug("pkinit_kdf failed: %s\n", error_message(retval));884goto cleanup;885}886887retval = cb->replace_reply_key(context, rock, &reply_key, FALSE);888if (retval)889goto cleanup;890891*send_pa = malloc(sizeof(krb5_pa_data));892if (*send_pa == NULL) {893retval = ENOMEM;894free(out_data->data);895free(out_data);896out_data = NULL;897goto cleanup;898}899(*send_pa)->magic = KV5M_PA_DATA;900(*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP;901(*send_pa)->length = out_data->length;902(*send_pa)->contents = (krb5_octet *) out_data->data;903904cleanup:905free(scratch.data);906free(out_data);907if (encoded_dhkey_info != NULL)908krb5_free_data(context, encoded_dhkey_info);909if (encoded_key_pack != NULL)910krb5_free_data(context, encoded_key_pack);911free(dh_pubkey);912free(server_key);913free_krb5_pa_pk_as_req(&reqp);914free_krb5_pa_pk_as_rep(&rep);915free_krb5_reply_key_pack(&key_pack);916krb5_free_keyblock_contents(context, &reply_key);917918if (retval)919pkiDebug("pkinit_verify_padata failure");920921return retval;922}923924static int925pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype)926{927if (patype == KRB5_PADATA_PKINIT_KX)928return PA_INFO;929/* PKINIT does not normally set the hw-authent ticket flag, but a930* certauth module can cause it to do so. */931return PA_SUFFICIENT | PA_REPLACES_KEY | PA_TYPED_E_DATA | PA_HARDWARE;932}933934static krb5_preauthtype supported_server_pa_types[] = {935KRB5_PADATA_PK_AS_REQ,936KRB5_PADATA_PKINIT_KX,9370938};939940static void941pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)942{943/*944* There is nothing currently allocated by pkinit_init_kdc_profile()945* which needs to be freed here.946*/947}948949static krb5_error_code950pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)951{952krb5_error_code retval;953char *eku_string = NULL, *ocsp_check = NULL, *minbits = NULL;954955pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname);956retval = pkinit_kdcdefault_string(context, plgctx->realmname,957KRB5_CONF_PKINIT_IDENTITY,958&plgctx->idopts->identity);959if (retval != 0 || NULL == plgctx->idopts->identity) {960retval = EINVAL;961krb5_set_error_message(context, retval,962_("No pkinit_identity supplied for realm %s"),963plgctx->realmname);964goto errout;965}966967retval = pkinit_kdcdefault_strings(context, plgctx->realmname,968KRB5_CONF_PKINIT_ANCHORS,969&plgctx->idopts->anchors);970if (retval != 0 || NULL == plgctx->idopts->anchors) {971retval = EINVAL;972krb5_set_error_message(context, retval,973_("No pkinit_anchors supplied for realm %s"),974plgctx->realmname);975goto errout;976}977978pkinit_kdcdefault_strings(context, plgctx->realmname,979KRB5_CONF_PKINIT_POOL,980&plgctx->idopts->intermediates);981982pkinit_kdcdefault_strings(context, plgctx->realmname,983KRB5_CONF_PKINIT_REVOKE,984&plgctx->idopts->crls);985986pkinit_kdcdefault_string(context, plgctx->realmname,987KRB5_CONF_PKINIT_KDC_OCSP,988&ocsp_check);989if (ocsp_check != NULL) {990free(ocsp_check);991retval = ENOTSUP;992krb5_set_error_message(context, retval,993_("OCSP is not supported: (realm: %s)"),994plgctx->realmname);995goto errout;996}997998pkinit_kdcdefault_string(context, plgctx->realmname,999KRB5_CONF_PKINIT_DH_MIN_BITS, &minbits);1000plgctx->opts->dh_min_bits = parse_dh_min_bits(context, minbits);1001free(minbits);10021003pkinit_kdcdefault_boolean(context, plgctx->realmname,1004KRB5_CONF_PKINIT_ALLOW_UPN,10050, &plgctx->opts->allow_upn);10061007pkinit_kdcdefault_boolean(context, plgctx->realmname,1008KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING,10090, &plgctx->opts->require_crl_checking);10101011pkinit_kdcdefault_boolean(context, plgctx->realmname,1012KRB5_CONF_PKINIT_REQUIRE_FRESHNESS,10130, &plgctx->opts->require_freshness);10141015pkinit_kdcdefault_string(context, plgctx->realmname,1016KRB5_CONF_PKINIT_EKU_CHECKING,1017&eku_string);1018if (eku_string != NULL) {1019if (strcasecmp(eku_string, "kpClientAuth") == 0) {1020plgctx->opts->require_eku = 1;1021plgctx->opts->accept_secondary_eku = 0;1022} else if (strcasecmp(eku_string, "scLogin") == 0) {1023plgctx->opts->require_eku = 1;1024plgctx->opts->accept_secondary_eku = 1;1025} else if (strcasecmp(eku_string, "none") == 0) {1026plgctx->opts->require_eku = 0;1027plgctx->opts->accept_secondary_eku = 0;1028} else {1029pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",1030__FUNCTION__, eku_string);1031}1032free(eku_string);1033}10341035pkinit_kdcdefault_strings(context, plgctx->realmname,1036KRB5_CONF_PKINIT_INDICATOR,1037&plgctx->auth_indicators);10381039return 0;1040errout:1041pkinit_fini_kdc_profile(context, plgctx);1042return retval;1043}10441045static pkinit_kdc_context1046pkinit_find_realm_context(krb5_context context,1047krb5_kdcpreauth_moddata moddata,1048krb5_principal princ)1049{1050size_t i;1051pkinit_kdc_context *realm_contexts;10521053if (moddata == NULL)1054return NULL;10551056realm_contexts = moddata->realm_contexts;1057if (realm_contexts == NULL)1058return NULL;10591060for (i = 0; realm_contexts[i] != NULL; i++) {1061pkinit_kdc_context p = realm_contexts[i];10621063if ((p->realmname_len == princ->realm.length) &&1064(strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) {1065pkiDebug("%s: returning context at %p for realm '%s'\n",1066__FUNCTION__, p, p->realmname);1067return p;1068}1069}1070pkiDebug("%s: unable to find realm context for realm '%.*s'\n",1071__FUNCTION__, princ->realm.length, princ->realm.data);1072return NULL;1073}10741075static int1076pkinit_server_plugin_init_realm(krb5_context context, const char *realmname,1077pkinit_kdc_context *pplgctx)1078{1079krb5_error_code retval = ENOMEM;1080pkinit_kdc_context plgctx = NULL;10811082*pplgctx = NULL;10831084plgctx = calloc(1, sizeof(*plgctx));1085if (plgctx == NULL)1086goto errout;10871088pkiDebug("%s: initializing context at %p for realm '%s'\n",1089__FUNCTION__, plgctx, realmname);1090memset(plgctx, 0, sizeof(*plgctx));1091plgctx->magic = PKINIT_CTX_MAGIC;10921093plgctx->realmname = strdup(realmname);1094if (plgctx->realmname == NULL)1095goto errout;1096plgctx->realmname_len = strlen(plgctx->realmname);10971098retval = pkinit_init_plg_crypto(context, &plgctx->cryptoctx);1099if (retval)1100goto errout;11011102retval = pkinit_init_plg_opts(&plgctx->opts);1103if (retval)1104goto errout;11051106retval = pkinit_init_identity_crypto(&plgctx->idctx);1107if (retval)1108goto errout;11091110retval = pkinit_init_identity_opts(&plgctx->idopts);1111if (retval)1112goto errout;11131114retval = pkinit_init_kdc_profile(context, plgctx);1115if (retval)1116goto errout;11171118retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL,1119plgctx->idopts, plgctx->idctx,1120NULL, NULL, NULL);1121if (retval)1122goto errout;1123retval = pkinit_identity_prompt(context, plgctx->cryptoctx, NULL,1124plgctx->idopts, plgctx->idctx,1125NULL, NULL, 0, NULL);1126if (retval)1127goto errout;11281129pkiDebug("%s: returning context at %p for realm '%s'\n",1130__FUNCTION__, plgctx, realmname);1131*pplgctx = plgctx;1132retval = 0;11331134errout:1135if (retval)1136pkinit_server_plugin_fini_realm(context, plgctx);11371138return retval;1139}11401141static krb5_error_code1142pkinit_san_authorize(krb5_context context, krb5_certauth_moddata moddata,1143const uint8_t *cert, size_t cert_len,1144krb5_const_principal princ, const void *opts,1145const struct _krb5_db_entry_new *db_entry,1146char ***authinds_out)1147{1148krb5_error_code ret;1149int valid_san;1150const struct certauth_req_opts *req_opts = opts;11511152*authinds_out = NULL;11531154ret = verify_client_san(context, req_opts->plgctx, req_opts->reqctx,1155req_opts->cb, req_opts->rock, princ, &valid_san);1156if (ret == ENOENT)1157return KRB5_PLUGIN_NO_HANDLE;1158else if (ret)1159return ret;11601161if (!valid_san) {1162TRACE_PKINIT_SERVER_SAN_REJECT(context);1163return KRB5KDC_ERR_CLIENT_NAME_MISMATCH;1164}11651166return 0;1167}11681169static krb5_error_code1170pkinit_eku_authorize(krb5_context context, krb5_certauth_moddata moddata,1171const uint8_t *cert, size_t cert_len,1172krb5_const_principal princ, const void *opts,1173const struct _krb5_db_entry_new *db_entry,1174char ***authinds_out)1175{1176krb5_error_code ret;1177int valid_eku;1178const struct certauth_req_opts *req_opts = opts;11791180*authinds_out = NULL;11811182/* Verify the client EKU. */1183ret = verify_client_eku(context, req_opts->plgctx, req_opts->reqctx,1184&valid_eku);1185if (ret)1186return ret;11871188if (!valid_eku) {1189TRACE_PKINIT_SERVER_EKU_REJECT(context);1190return KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;1191}11921193return KRB5_PLUGIN_NO_HANDLE;1194}11951196static krb5_error_code1197certauth_pkinit_san_initvt(krb5_context context, int maj_ver, int min_ver,1198krb5_plugin_vtable vtable)1199{1200krb5_certauth_vtable vt;12011202if (maj_ver != 1)1203return KRB5_PLUGIN_VER_NOTSUPP;1204vt = (krb5_certauth_vtable)vtable;1205vt->name = "pkinit_san";1206vt->authorize = pkinit_san_authorize;1207return 0;1208}12091210static krb5_error_code1211certauth_pkinit_eku_initvt(krb5_context context, int maj_ver, int min_ver,1212krb5_plugin_vtable vtable)1213{1214krb5_certauth_vtable vt;12151216if (maj_ver != 1)1217return KRB5_PLUGIN_VER_NOTSUPP;1218vt = (krb5_certauth_vtable)vtable;1219vt->name = "pkinit_eku";1220vt->authorize = pkinit_eku_authorize;1221return 0;1222}12231224/*1225* Do certificate auth based on a match expression in the pkinit_cert_match1226* attribute string. An expression should be in the same form as those used1227* for the pkinit_cert_match configuration option.1228*/1229static krb5_error_code1230dbmatch_authorize(krb5_context context, krb5_certauth_moddata moddata,1231const uint8_t *cert, size_t cert_len,1232krb5_const_principal princ, const void *opts,1233const struct _krb5_db_entry_new *db_entry,1234char ***authinds_out)1235{1236krb5_error_code ret;1237const struct certauth_req_opts *req_opts = opts;1238char *pattern;1239krb5_boolean matched;12401241*authinds_out = NULL;12421243/* Fetch the matching pattern. Pass if it isn't specified. */1244ret = req_opts->cb->get_string(context, req_opts->rock,1245"pkinit_cert_match", &pattern);1246if (ret)1247return ret;1248if (pattern == NULL)1249return KRB5_PLUGIN_NO_HANDLE;12501251/* Check the certificate against the match expression. */1252ret = pkinit_client_cert_match(context, req_opts->plgctx->cryptoctx,1253req_opts->reqctx->cryptoctx, pattern,1254&matched);1255req_opts->cb->free_string(context, req_opts->rock, pattern);1256if (ret)1257return ret;1258return matched ? 0 : KRB5KDC_ERR_CERTIFICATE_MISMATCH;1259}12601261static krb5_error_code1262certauth_dbmatch_initvt(krb5_context context, int maj_ver, int min_ver,1263krb5_plugin_vtable vtable)1264{1265krb5_certauth_vtable vt;12661267if (maj_ver != 1)1268return KRB5_PLUGIN_VER_NOTSUPP;1269vt = (krb5_certauth_vtable)vtable;1270vt->name = "dbmatch";1271vt->authorize = dbmatch_authorize;1272return 0;1273}12741275static krb5_error_code1276load_certauth_plugins(krb5_context context, const char *const *realmnames,1277certauth_handle **handle_out)1278{1279krb5_error_code ret;1280krb5_plugin_initvt_fn *modules = NULL, *mod;1281certauth_handle *list = NULL, h;1282size_t count;12831284/* Register the builtin modules. */1285ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH,1286"pkinit_san", certauth_pkinit_san_initvt);1287if (ret)1288goto cleanup;12891290ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH,1291"pkinit_eku", certauth_pkinit_eku_initvt);1292if (ret)1293goto cleanup;12941295ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH, "dbmatch",1296certauth_dbmatch_initvt);1297if (ret)1298goto cleanup;12991300ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_CERTAUTH, &modules);1301if (ret)1302goto cleanup;13031304/* Allocate handle list. */1305for (count = 0; modules[count]; count++);1306list = k5calloc(count + 1, sizeof(*list), &ret);1307if (list == NULL)1308goto cleanup;13091310/* Initialize each module, ignoring ones that fail. */1311count = 0;1312for (mod = modules; *mod != NULL; mod++) {1313h = k5calloc(1, sizeof(*h), &ret);1314if (h == NULL)1315goto cleanup;13161317ret = (*mod)(context, 1, 2, (krb5_plugin_vtable)&h->vt);1318if (ret) {1319TRACE_CERTAUTH_VTINIT_FAIL(context, ret);1320free(h);1321continue;1322}1323h->moddata = NULL;1324if (h->vt.init_ex != NULL)1325ret = h->vt.init_ex(context, realmnames, &h->moddata);1326else if (h->vt.init != NULL)1327ret = h->vt.init(context, &h->moddata);1328if (ret) {1329TRACE_CERTAUTH_INIT_FAIL(context, h->vt.name, ret);1330free(h);1331continue;1332}1333list[count++] = h;1334list[count] = NULL;1335}1336list[count] = NULL;13371338ret = 0;1339*handle_out = list;1340list = NULL;13411342cleanup:1343k5_plugin_free_modules(context, modules);1344free_certauth_handles(context, list);1345return ret;1346}13471348static int1349pkinit_server_plugin_init(krb5_context context,1350krb5_kdcpreauth_moddata *moddata_out,1351const char **realmnames)1352{1353krb5_error_code retval = ENOMEM;1354pkinit_kdc_context plgctx, *realm_contexts = NULL;1355certauth_handle *certauth_modules = NULL;1356krb5_kdcpreauth_moddata moddata;1357size_t i, j;1358size_t numrealms;13591360retval = pkinit_accessor_init();1361if (retval)1362return retval;13631364/* Determine how many realms we may need to support */1365for (i = 0; realmnames[i] != NULL; i++) {};1366numrealms = i;13671368realm_contexts = calloc(numrealms+1, sizeof(pkinit_kdc_context));1369if (realm_contexts == NULL)1370return ENOMEM;13711372for (i = 0, j = 0; i < numrealms; i++) {1373TRACE_PKINIT_SERVER_INIT_REALM(context, realmnames[i]);1374krb5_clear_error_message(context);1375retval = pkinit_server_plugin_init_realm(context, realmnames[i],1376&plgctx);1377if (retval)1378TRACE_PKINIT_SERVER_INIT_FAIL(context, realmnames[i], retval);1379else1380realm_contexts[j++] = plgctx;1381}13821383if (j == 0) {1384if (numrealms == 1) {1385k5_prependmsg(context, retval, "PKINIT initialization failed");1386} else {1387retval = EINVAL;1388k5_setmsg(context, retval,1389_("No realms configured correctly for pkinit support"));1390}1391goto errout;1392}13931394retval = load_certauth_plugins(context, realmnames, &certauth_modules);1395if (retval)1396goto errout;13971398moddata = k5calloc(1, sizeof(*moddata), &retval);1399if (moddata == NULL)1400goto errout;1401moddata->realm_contexts = realm_contexts;1402moddata->certauth_modules = certauth_modules;1403*moddata_out = moddata;1404pkiDebug("%s: returning context at %p\n", __FUNCTION__, moddata);1405return 0;14061407errout:1408free_realm_contexts(context, realm_contexts);1409free_certauth_handles(context, certauth_modules);1410return retval;1411}14121413static void1414pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx)1415{1416char **sp;14171418if (plgctx == NULL)1419return;14201421pkinit_fini_kdc_profile(context, plgctx);1422pkinit_fini_identity_opts(plgctx->idopts);1423pkinit_fini_identity_crypto(plgctx->idctx);1424pkinit_fini_plg_crypto(plgctx->cryptoctx);1425pkinit_fini_plg_opts(plgctx->opts);1426for (sp = plgctx->auth_indicators; sp != NULL && *sp != NULL; sp++)1427free(*sp);1428free(plgctx->auth_indicators);1429free(plgctx->realmname);1430free(plgctx);1431}14321433static void1434pkinit_server_plugin_fini(krb5_context context,1435krb5_kdcpreauth_moddata moddata)1436{1437if (moddata == NULL)1438return;1439free_realm_contexts(context, moddata->realm_contexts);1440free_certauth_handles(context, moddata->certauth_modules);1441free(moddata);1442}14431444static krb5_error_code1445pkinit_init_kdc_req_context(krb5_context context, pkinit_kdc_req_context *ctx)1446{1447krb5_error_code retval = ENOMEM;1448pkinit_kdc_req_context reqctx = NULL;14491450reqctx = malloc(sizeof(*reqctx));1451if (reqctx == NULL)1452return retval;1453memset(reqctx, 0, sizeof(*reqctx));1454reqctx->magic = PKINIT_CTX_MAGIC;14551456retval = pkinit_init_req_crypto(&reqctx->cryptoctx);1457if (retval)1458goto cleanup;1459reqctx->rcv_auth_pack = NULL;14601461pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);1462*ctx = reqctx;1463retval = 0;1464cleanup:1465if (retval)1466pkinit_fini_kdc_req_context(context, reqctx);14671468return retval;1469}14701471static void1472pkinit_fini_kdc_req_context(krb5_context context, void *ctx)1473{1474pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx;14751476if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) {1477pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx);1478return;1479}1480pkiDebug("%s: freeing reqctx at %p\n", __FUNCTION__, reqctx);14811482pkinit_fini_req_crypto(reqctx->cryptoctx);1483if (reqctx->rcv_auth_pack != NULL)1484free_krb5_auth_pack(&reqctx->rcv_auth_pack);14851486free(reqctx);1487}14881489static void1490pkinit_free_modreq(krb5_context context, krb5_kdcpreauth_moddata moddata,1491krb5_kdcpreauth_modreq modreq)1492{1493pkinit_fini_kdc_req_context(context, modreq);1494}14951496krb5_error_code1497kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,1498krb5_plugin_vtable vtable);14991500krb5_error_code1501kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,1502krb5_plugin_vtable vtable)1503{1504krb5_kdcpreauth_vtable vt;15051506if (maj_ver != 1)1507return KRB5_PLUGIN_VER_NOTSUPP;1508vt = (krb5_kdcpreauth_vtable)vtable;1509vt->name = "pkinit";1510vt->pa_type_list = supported_server_pa_types;1511vt->init = pkinit_server_plugin_init;1512vt->fini = pkinit_server_plugin_fini;1513vt->flags = pkinit_server_get_flags;1514vt->edata = pkinit_server_get_edata;1515vt->verify = pkinit_server_verify_padata;1516vt->return_padata = pkinit_server_return_padata;1517vt->free_modreq = pkinit_free_modreq;1518return 0;1519}152015211522