Path: blob/main/crypto/krb5/src/kdc/kdc_preauth.c
105679 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* kdc/kdc_preauth.c - Preauthentication routines for the KDC */2/*3* Copyright 1995, 2003, 2007, 2009 by the Massachusetts Institute of4* Technology. All Rights Reserved.5*6* Export of this software from the United States of America may7* require a specific license from the United States Government.8* It is the responsibility of any person or organization contemplating9* export to obtain such a license before exporting.10*11* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and12* distribute this software and its documentation for any purpose and13* without fee is hereby granted, provided that the above copyright14* notice appear in all copies and that both that copyright notice and15* this permission notice appear in supporting documentation, and that16* the name of M.I.T. not be used in advertising or publicity pertaining17* to distribution of the software without specific, written prior18* permission. Furthermore if you modify this software you must label19* your software as modified software and not distribute it in such a20* fashion that it might be confused with the original M.I.T. software.21* M.I.T. makes no representations about the suitability of22* this software for any purpose. It is provided "as is" without express23* or implied warranty.24*/25/*26* Copyright (C) 1998 by the FundsXpress, INC.27*28* All rights reserved.29*30* Export of this software from the United States of America may require31* a specific license from the United States Government. It is the32* responsibility of any person or organization contemplating export to33* obtain such a license before exporting.34*35* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and36* distribute this software and its documentation for any purpose and37* without fee is hereby granted, provided that the above copyright38* notice appear in all copies and that both that copyright notice and39* this permission notice appear in supporting documentation, and that40* the name of FundsXpress. not be used in advertising or publicity pertaining41* to distribution of the software without specific, written prior42* permission. FundsXpress makes no representations about the suitability of43* this software for any purpose. It is provided "as is" without express44* or implied warranty.45*46* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR47* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED48* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.49*/50/*51* Copyright (c) 2006-2008, Novell, Inc.52* All rights reserved.53*54* Redistribution and use in source and binary forms, with or without55* modification, are permitted provided that the following conditions are met:56*57* * Redistributions of source code must retain the above copyright notice,58* this list of conditions and the following disclaimer.59* * Redistributions in binary form must reproduce the above copyright60* notice, this list of conditions and the following disclaimer in the61* documentation and/or other materials provided with the distribution.62* * The copyright holder's name is not used to endorse or promote products63* derived from this software without specific prior written permission.64*65* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"66* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE67* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE68* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE69* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR70* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF71* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS72* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN73* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)74* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE75* POSSIBILITY OF SUCH DAMAGE.76*/7778#include "k5-int.h"79#include "kdc_util.h"80#include "extern.h"81#include <stdio.h>82#include "adm_proto.h"8384#include <syslog.h>8586#include <assert.h>87#include <krb5/kdcpreauth_plugin.h>8889/* Let freshness tokens be valid for ten minutes. */90#define FRESHNESS_LIFETIME 6009192typedef struct preauth_system_st {93const char *name;94int type;95int flags;96krb5_kdcpreauth_moddata moddata;97krb5_kdcpreauth_init_fn init;98krb5_kdcpreauth_fini_fn fini;99krb5_kdcpreauth_edata_fn get_edata;100krb5_kdcpreauth_verify_fn verify_padata;101krb5_kdcpreauth_return_fn return_padata;102krb5_kdcpreauth_free_modreq_fn free_modreq;103krb5_kdcpreauth_loop_fn loop;104} preauth_system;105106static preauth_system *preauth_systems;107static size_t n_preauth_systems;108109static krb5_error_code110make_etype_info(krb5_context context, krb5_boolean etype_info2,111krb5_principal client, krb5_key_data *client_key,112krb5_enctype enctype, krb5_data **der_out);113114/* Get all available kdcpreauth vtables and a count of preauth types they115* support. Return an empty list on failure. */116static void117get_plugin_vtables(krb5_context context,118struct krb5_kdcpreauth_vtable_st **vtables_out,119size_t *n_tables_out, size_t *n_systems_out)120{121krb5_plugin_initvt_fn *plugins = NULL, *pl;122struct krb5_kdcpreauth_vtable_st *vtables;123size_t count, n_tables, n_systems, i;124125*vtables_out = NULL;126*n_tables_out = *n_systems_out = 0;127128/* Auto-register encrypted challenge and (if possible) pkinit. */129k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "pkinit",130"preauth");131k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "otp",132"preauth");133k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "spake",134"preauth");135k5_plugin_register(context, PLUGIN_INTERFACE_KDCPREAUTH,136"encrypted_challenge",137kdcpreauth_encrypted_challenge_initvt);138k5_plugin_register(context, PLUGIN_INTERFACE_KDCPREAUTH,139"encrypted_timestamp",140kdcpreauth_encrypted_timestamp_initvt);141142if (k5_plugin_load_all(context, PLUGIN_INTERFACE_KDCPREAUTH, &plugins))143return;144for (count = 0; plugins[count]; count++);145vtables = calloc(count + 1, sizeof(*vtables));146if (vtables == NULL)147goto cleanup;148for (pl = plugins, n_tables = 0; *pl != NULL; pl++) {149if ((*pl)(context, 1, 2, (krb5_plugin_vtable)&vtables[n_tables]) == 0)150n_tables++;151}152for (i = 0, n_systems = 0; i < n_tables; i++) {153for (count = 0; vtables[i].pa_type_list[count] != 0; count++);154n_systems += count;155}156*vtables_out = vtables;157*n_tables_out = n_tables;158*n_systems_out = n_systems;159160cleanup:161k5_plugin_free_modules(context, plugins);162}163164/* Make a list of realm names. The caller should free the list container but165* not the list elements (which are aliases into kdc_realmlist). */166static krb5_error_code167get_realm_names(struct server_handle *handle, const char ***list_out)168{169const char **list;170int i;171172list = calloc(handle->kdc_numrealms + 1, sizeof(*list));173if (list == NULL)174return ENOMEM;175for (i = 0; i < handle->kdc_numrealms; i++)176list[i] = handle->kdc_realmlist[i]->realm_name;177list[i] = NULL;178*list_out = list;179return 0;180}181182void183load_preauth_plugins(struct server_handle *handle, krb5_context context,184verto_ctx *ctx)185{186krb5_error_code ret;187struct krb5_kdcpreauth_vtable_st *vtables = NULL, *vt;188size_t n_systems, n_tables, i, j;189krb5_kdcpreauth_moddata moddata;190const char **realm_names = NULL, *emsg;191preauth_system *sys;192193/* Get all available kdcpreauth vtables. */194get_plugin_vtables(context, &vtables, &n_tables, &n_systems);195196/* Allocate the list of static and plugin preauth systems. */197preauth_systems = calloc(n_systems + 1, sizeof(preauth_system));198if (preauth_systems == NULL)199goto cleanup;200201if (get_realm_names(handle, &realm_names))202goto cleanup;203204/* Add the dynamically-loaded mechanisms to the list. */205n_systems = 0;206for (i = 0; i < n_tables; i++) {207/* Try to initialize this module. */208vt = &vtables[i];209moddata = NULL;210if (vt->init) {211ret = vt->init(context, &moddata, realm_names);212if (ret) {213emsg = krb5_get_error_message(context, ret);214krb5_klog_syslog(LOG_ERR, _("preauth %s failed to "215"initialize: %s"), vt->name, emsg);216krb5_free_error_message(context, emsg);217continue;218}219}220221if (vt->loop) {222ret = vt->loop(context, moddata, ctx);223if (ret) {224emsg = krb5_get_error_message(context, ret);225krb5_klog_syslog(LOG_ERR, _("preauth %s failed to setup "226"loop: %s"), vt->name, emsg);227krb5_free_error_message(context, emsg);228if (vt->fini)229vt->fini(context, moddata);230continue;231}232}233234/* Add this module to the systems list once for each pa type. */235for (j = 0; vt->pa_type_list[j] != 0; j++) {236sys = &preauth_systems[n_systems];237sys->name = vt->name;238sys->type = vt->pa_type_list[j];239sys->flags = (vt->flags) ? vt->flags(context, sys->type) : 0;240sys->moddata = moddata;241sys->init = vt->init;242/* Only call fini once for each plugin. */243sys->fini = (j == 0) ? vt->fini : NULL;244sys->get_edata = vt->edata;245sys->verify_padata = vt->verify;246sys->return_padata = vt->return_padata;247sys->free_modreq = vt->free_modreq;248sys->loop = vt->loop;249n_systems++;250}251}252n_preauth_systems = n_systems;253/* Add the end-of-list marker. */254preauth_systems[n_systems].name = "[end]";255preauth_systems[n_systems].type = -1;256257cleanup:258free(vtables);259free(realm_names);260}261262void263unload_preauth_plugins(krb5_context context)264{265size_t i;266267for (i = 0; i < n_preauth_systems; i++) {268if (preauth_systems[i].fini)269preauth_systems[i].fini(context, preauth_systems[i].moddata);270}271free(preauth_systems);272preauth_systems = NULL;273n_preauth_systems = 0;274}275276/*277* The make_padata_context() function creates a space for storing any278* request-specific module data which will be needed by return_padata() later.279* Each preauth type gets a storage location of its own.280*/281struct request_pa_context {282int n_contexts;283struct {284preauth_system *pa_system;285krb5_kdcpreauth_modreq modreq;286} *contexts;287};288289static krb5_error_code290make_padata_context(krb5_context context, void **padata_context)291{292int i;293struct request_pa_context *ret;294295ret = malloc(sizeof(*ret));296if (ret == NULL) {297return ENOMEM;298}299300ret->n_contexts = n_preauth_systems;301ret->contexts = malloc(sizeof(ret->contexts[0]) * ret->n_contexts);302if (ret->contexts == NULL) {303free(ret);304return ENOMEM;305}306307memset(ret->contexts, 0, sizeof(ret->contexts[0]) * ret->n_contexts);308309for (i = 0; i < ret->n_contexts; i++) {310ret->contexts[i].pa_system = &preauth_systems[i];311ret->contexts[i].modreq = NULL;312}313314*padata_context = ret;315316return 0;317}318319/*320* The free_padata_context function frees any context information pointers321* which the check_padata() function created but which weren't already cleaned322* up by return_padata().323*/324void325free_padata_context(krb5_context kcontext, void *padata_context)326{327struct request_pa_context *context = padata_context;328preauth_system *sys;329int i;330331if (context == NULL)332return;333for (i = 0; i < context->n_contexts; i++) {334sys = context->contexts[i].pa_system;335if (!sys->free_modreq || !context->contexts[i].modreq)336continue;337sys->free_modreq(kcontext, sys->moddata, context->contexts[i].modreq);338context->contexts[i].modreq = NULL;339}340341free(context->contexts);342free(context);343}344345static krb5_deltat346max_time_skew(krb5_context context, krb5_kdcpreauth_rock rock)347{348return context->clockskew;349}350351static krb5_error_code352client_keys(krb5_context context, krb5_kdcpreauth_rock rock,353krb5_keyblock **keys_out)354{355krb5_kdc_req *request = rock->request;356krb5_db_entry *client = rock->client;357krb5_keyblock *keys, key;358krb5_key_data *entry_key;359int i, k;360361keys = calloc(request->nktypes + 1, sizeof(krb5_keyblock));362if (keys == NULL)363return ENOMEM;364365k = 0;366for (i = 0; i < request->nktypes; i++) {367entry_key = NULL;368if (krb5_dbe_find_enctype(context, client, request->ktype[i],369-1, 0, &entry_key) != 0)370continue;371if (krb5_dbe_decrypt_key_data(context, NULL, entry_key,372&key, NULL) != 0)373continue;374keys[k++] = key;375}376if (k == 0) {377free(keys);378return ENOENT;379}380*keys_out = keys;381return 0;382}383384static void free_keys(krb5_context context, krb5_kdcpreauth_rock rock,385krb5_keyblock *keys)386{387krb5_keyblock *k;388389if (keys == NULL)390return;391for (k = keys; k->enctype != 0; k++)392krb5_free_keyblock_contents(context, k);393free(keys);394}395396static krb5_data *397request_body(krb5_context context, krb5_kdcpreauth_rock rock)398{399return rock->inner_body;400}401402static krb5_keyblock *403fast_armor(krb5_context context, krb5_kdcpreauth_rock rock)404{405return rock->rstate->armor_key;406}407408static krb5_error_code409get_string(krb5_context context, krb5_kdcpreauth_rock rock, const char *key,410char **value_out)411{412return krb5_dbe_get_string(context, rock->client, key, value_out);413}414415static void416free_string(krb5_context context, krb5_kdcpreauth_rock rock, char *string)417{418krb5_dbe_free_string(context, string);419}420421static void *422client_entry(krb5_context context, krb5_kdcpreauth_rock rock)423{424return rock->client;425}426427static verto_ctx *428event_context(krb5_context context, krb5_kdcpreauth_rock rock)429{430return rock->vctx;431}432433static krb5_boolean434have_client_keys(krb5_context context, krb5_kdcpreauth_rock rock)435{436krb5_kdc_req *request = rock->request;437krb5_key_data *kd;438int i;439440for (i = 0; i < request->nktypes; i++) {441if (krb5_dbe_find_enctype(context, rock->client, request->ktype[i],442-1, 0, &kd) == 0)443return TRUE;444}445return FALSE;446}447448static const krb5_keyblock *449client_keyblock(krb5_context context, krb5_kdcpreauth_rock rock)450{451if (rock->client_keyblock->enctype == ENCTYPE_NULL)452return NULL;453return rock->client_keyblock;454}455456static krb5_error_code457add_auth_indicator(krb5_context context, krb5_kdcpreauth_rock rock,458const char *indicator)459{460return authind_add(context, indicator, rock->auth_indicators);461}462463static krb5_boolean464get_cookie(krb5_context context, krb5_kdcpreauth_rock rock,465krb5_preauthtype pa_type, krb5_data *out)466{467return kdc_fast_search_cookie(rock->rstate, pa_type, out);468}469470static krb5_error_code471set_cookie(krb5_context context, krb5_kdcpreauth_rock rock,472krb5_preauthtype pa_type, const krb5_data *data)473{474return kdc_fast_set_cookie(rock->rstate, pa_type, data);475}476477static krb5_boolean478match_client(krb5_context context, krb5_kdcpreauth_rock rock,479krb5_principal princ)480{481krb5_db_entry *ent;482krb5_boolean match = FALSE;483krb5_principal req_client = rock->request->client;484krb5_principal client = rock->client->princ;485486/* Check for a direct match against the request principal or487* the post-canon client principal. */488if (krb5_principal_compare_flags(context, princ, req_client,489KRB5_PRINCIPAL_COMPARE_ENTERPRISE) ||490krb5_principal_compare(context, princ, client))491return TRUE;492493if (krb5_db_get_principal(context, princ, KRB5_KDB_FLAG_CLIENT, &ent))494return FALSE;495match = krb5_principal_compare(context, ent->princ, client);496krb5_db_free_principal(context, ent);497return match;498}499500static krb5_principal501client_name(krb5_context context, krb5_kdcpreauth_rock rock)502{503return rock->client->princ;504}505506static void507send_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock)508{509rock->send_freshness_token = TRUE;510}511512static krb5_error_code513check_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock,514const krb5_data *token)515{516krb5_timestamp token_ts, now;517krb5_key_data *kd;518krb5_keyblock kb;519krb5_kvno token_kvno;520krb5_checksum cksum;521krb5_data d;522uint8_t *token_cksum;523size_t token_cksum_len;524krb5_boolean valid = FALSE;525char ckbuf[4];526527memset(&kb, 0, sizeof(kb));528529if (krb5_timeofday(context, &now) != 0)530goto cleanup;531532if (token->length <= 8)533goto cleanup;534token_ts = load_32_be(token->data);535token_kvno = load_32_be(token->data + 4);536token_cksum = (uint8_t *)token->data + 8;537token_cksum_len = token->length - 8;538539/* Check if the token timestamp is too old. */540if (ts_after(now, ts_incr(token_ts, FRESHNESS_LIFETIME)))541goto cleanup;542543/* Fetch and decrypt the local krbtgt key of the token's kvno. */544if (krb5_dbe_find_enctype(context, rock->local_tgt, -1, -1, token_kvno,545&kd) != 0)546goto cleanup;547if (krb5_dbe_decrypt_key_data(context, NULL, kd, &kb, NULL) != 0)548goto cleanup;549550/* Verify the token checksum against the current KDC time. The checksum551* must use the mandatory checksum type of the krbtgt key's enctype. */552store_32_be(token_ts, ckbuf);553d = make_data(ckbuf, sizeof(ckbuf));554cksum.magic = KV5M_CHECKSUM;555cksum.checksum_type = 0;556cksum.length = token_cksum_len;557cksum.contents = token_cksum;558(void)krb5_c_verify_checksum(context, &kb, KRB5_KEYUSAGE_PA_AS_FRESHNESS,559&d, &cksum, &valid);560561cleanup:562krb5_free_keyblock_contents(context, &kb);563return valid ? 0 : KRB5KDC_ERR_PREAUTH_EXPIRED;564}565566static krb5_error_code567replace_reply_key(krb5_context context, krb5_kdcpreauth_rock rock,568const krb5_keyblock *key, krb5_boolean is_strengthen)569{570krb5_keyblock copy;571572if (krb5_copy_keyblock_contents(context, key, ©) != 0)573return ENOMEM;574krb5_free_keyblock_contents(context, rock->client_keyblock);575*rock->client_keyblock = copy;576if (!is_strengthen)577rock->replaced_reply_key = TRUE;578return 0;579}580581static struct krb5_kdcpreauth_callbacks_st callbacks = {5826,583max_time_skew,584client_keys,585free_keys,586request_body,587fast_armor,588get_string,589free_string,590client_entry,591event_context,592have_client_keys,593client_keyblock,594add_auth_indicator,595get_cookie,596set_cookie,597match_client,598client_name,599send_freshness_token,600check_freshness_token,601replace_reply_key602};603604static krb5_error_code605find_pa_system(int type, preauth_system **preauth)606{607preauth_system *ap;608609if (preauth_systems == NULL)610return KRB5_PREAUTH_BAD_TYPE;611ap = preauth_systems;612while ((ap->type != -1) && (ap->type != type))613ap++;614if (ap->type == -1)615return(KRB5_PREAUTH_BAD_TYPE);616*preauth = ap;617return 0;618}619620/* Find a pointer to the request-specific module data for pa_sys. */621static krb5_error_code622find_modreq(preauth_system *pa_sys, struct request_pa_context *context,623krb5_kdcpreauth_modreq **modreq_out)624{625int i;626627*modreq_out = NULL;628if (context == NULL)629return KRB5KRB_ERR_GENERIC;630631for (i = 0; i < context->n_contexts; i++) {632if (context->contexts[i].pa_system == pa_sys) {633*modreq_out = &context->contexts[i].modreq;634return 0;635}636}637638return KRB5KRB_ERR_GENERIC;639}640641/*642* Create a list of indices into the preauth_systems array, sorted by order of643* preference.644*/645static krb5_boolean646pa_list_includes(krb5_pa_data **pa_data, krb5_preauthtype pa_type)647{648while (*pa_data != NULL) {649if ((*pa_data)->pa_type == pa_type)650return TRUE;651pa_data++;652}653return FALSE;654}655static void656sort_pa_order(krb5_context context, krb5_kdc_req *request, int *pa_order)657{658size_t i, j, k, n_repliers, n_key_replacers;659660/* First, set up the default order. */661i = 0;662for (j = 0; j < n_preauth_systems; j++) {663if (preauth_systems[j].return_padata != NULL)664pa_order[i++] = j;665}666n_repliers = i;667pa_order[n_repliers] = -1;668669/* Reorder so that PA_REPLACES_KEY modules are listed first. */670for (i = 0; i < n_repliers; i++) {671/* If this module replaces the key, then it's okay to leave it where it672* is in the order. */673if (preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY)674continue;675/* If not, search for a module which does, and swap in the first one we676* find. */677for (j = i + 1; j < n_repliers; j++) {678if (preauth_systems[pa_order[j]].flags & PA_REPLACES_KEY) {679k = pa_order[j];680pa_order[j] = pa_order[i];681pa_order[i] = k;682break;683}684}685/* If we didn't find one, we have moved all of the key-replacing686* modules, and i is the count of those modules. */687if (j == n_repliers)688break;689}690n_key_replacers = i;691692if (request->padata != NULL) {693/* Now reorder the subset of modules which replace the key,694* bubbling those which handle pa_data types provided by the695* client ahead of the others.696*/697for (i = 0; i < n_key_replacers; i++) {698if (pa_list_includes(request->padata,699preauth_systems[pa_order[i]].type))700continue;701for (j = i + 1; j < n_key_replacers; j++) {702if (pa_list_includes(request->padata,703preauth_systems[pa_order[j]].type)) {704k = pa_order[j];705pa_order[j] = pa_order[i];706pa_order[i] = k;707break;708}709}710}711}712#ifdef DEBUG713krb5_klog_syslog(LOG_DEBUG, "original preauth mechanism list:");714for (i = 0; i < n_preauth_systems; i++) {715if (preauth_systems[i].return_padata != NULL)716krb5_klog_syslog(LOG_DEBUG, "... %s(%d)", preauth_systems[i].name,717preauth_systems[i].type);718}719krb5_klog_syslog(LOG_DEBUG, "sorted preauth mechanism list:");720for (i = 0; pa_order[i] != -1; i++) {721krb5_klog_syslog(LOG_DEBUG, "... %s(%d)",722preauth_systems[pa_order[i]].name,723preauth_systems[pa_order[i]].type);724}725#endif726}727728const char *missing_required_preauth(krb5_db_entry *client,729krb5_db_entry *server,730krb5_enc_tkt_part *enc_tkt_reply)731{732#ifdef DEBUG733krb5_klog_syslog (734LOG_DEBUG,735"client needs %spreauth, %shw preauth; request has %spreauth, %shw preauth",736isflagset (client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) ? "" : "no ",737isflagset (client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) ? "" : "no ",738isflagset (enc_tkt_reply->flags, TKT_FLG_PRE_AUTH) ? "" : "no ",739isflagset (enc_tkt_reply->flags, TKT_FLG_HW_AUTH) ? "" : "no ");740#endif741742if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&743!isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))744return "NEEDED_PREAUTH";745746if (isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&747!isflagset(enc_tkt_reply->flags, TKT_FLG_HW_AUTH))748return "NEEDED_HW_PREAUTH";749750return 0;751}752753/* Return true if request's enctypes indicate support for etype-info2. */754static krb5_boolean755requires_info2(const krb5_kdc_req *request)756{757int i;758759for (i = 0; i < request->nktypes; i++) {760if (enctype_requires_etype_info_2(request->ktype[i]))761return TRUE;762}763return FALSE;764}765766/* Add PA-ETYPE-INFO2 and possibly PA-ETYPE-INFO entries to pa_list as767* appropriate for the request and client principal. */768static krb5_error_code769add_etype_info(krb5_context context, krb5_kdcpreauth_rock rock,770krb5_pa_data ***pa_list)771{772krb5_error_code ret;773krb5_data *der;774775if (rock->client_key == NULL)776return 0;777778if (!requires_info2(rock->request)) {779/* Include PA-ETYPE-INFO only for old clients. */780ret = make_etype_info(context, FALSE, rock->client->princ,781rock->client_key, rock->client_keyblock->enctype,782&der);783if (ret)784return ret;785ret = k5_add_pa_data_from_data(pa_list, KRB5_PADATA_ETYPE_INFO, der);786krb5_free_data(context, der);787if (ret)788return ret;789}790791/* Always include PA-ETYPE-INFO2. */792ret = make_etype_info(context, TRUE, rock->client->princ, rock->client_key,793rock->client_keyblock->enctype, &der);794if (ret)795return ret;796ret = k5_add_pa_data_from_data(pa_list, KRB5_PADATA_ETYPE_INFO2, der);797krb5_free_data(context, der);798return ret;799}800801/* Add PW-SALT entries to pa_list as appropriate for the request and client802* principal. */803static krb5_error_code804add_pw_salt(krb5_context context, krb5_kdcpreauth_rock rock,805krb5_pa_data ***pa_list)806{807krb5_error_code ret;808krb5_data *salt = NULL;809krb5_int16 salttype;810811/* Only include this pa-data for old clients. */812if (rock->client_key == NULL || requires_info2(rock->request))813return 0;814815ret = krb5_dbe_compute_salt(context, rock->client_key,816rock->request->client, &salttype, &salt);817if (ret)818return 0;819820ret = k5_add_pa_data_from_data(pa_list, KRB5_PADATA_PW_SALT, salt);821krb5_free_data(context, salt);822return ret;823}824825static krb5_error_code826add_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock,827krb5_pa_data ***pa_list)828{829krb5_error_code ret;830krb5_timestamp now;831krb5_keyblock kb;832krb5_checksum cksum;833krb5_data d;834krb5_pa_data *pa = NULL;835char ckbuf[4];836837memset(&cksum, 0, sizeof(cksum));838memset(&kb, 0, sizeof(kb));839840if (!rock->send_freshness_token)841return 0;842if (krb5int_find_pa_data(context, rock->request->padata,843KRB5_PADATA_AS_FRESHNESS) == NULL)844return 0;845846/* Compute a checksum over the current KDC time. */847ret = krb5_timeofday(context, &now);848if (ret)849goto cleanup;850store_32_be(now, ckbuf);851d = make_data(ckbuf, sizeof(ckbuf));852ret = krb5_c_make_checksum(context, 0, rock->local_tgt_key,853KRB5_KEYUSAGE_PA_AS_FRESHNESS, &d, &cksum);854855/* Compose a freshness token from the time, krbtgt kvno, and checksum. */856ret = k5_alloc_pa_data(KRB5_PADATA_AS_FRESHNESS, 8 + cksum.length, &pa);857if (ret)858goto cleanup;859store_32_be(now, pa->contents);860store_32_be(current_kvno(rock->local_tgt), pa->contents + 4);861memcpy(pa->contents + 8, cksum.contents, cksum.length);862863ret = k5_add_pa_data_element(pa_list, &pa);864865cleanup:866krb5_free_keyblock_contents(context, &kb);867krb5_free_checksum_contents(context, &cksum);868k5_free_pa_data_element(pa);869return ret;870}871872struct hint_state {873kdc_hint_respond_fn respond;874void *arg;875krb5_context context;876877krb5_kdcpreauth_rock rock;878krb5_kdc_req *request;879krb5_pa_data ***e_data_out;880881int hw_only;882preauth_system *ap;883krb5_pa_data **pa_data;884krb5_preauthtype pa_type;885};886887static void888hint_list_finish(struct hint_state *state, krb5_error_code code)889{890krb5_context context = state->context;891kdc_hint_respond_fn oldrespond = state->respond;892void *oldarg = state->arg;893894/* Add a freshness token if a preauth module requested it and the client895* request indicates support for it. */896if (!code)897code = add_freshness_token(context, state->rock, &state->pa_data);898899if (!code) {900if (state->pa_data == NULL) {901krb5_klog_syslog(LOG_INFO,902_("%spreauth required but hint list is empty"),903state->hw_only ? "hw" : "");904}905906*state->e_data_out = state->pa_data;907state->pa_data = NULL;908}909910krb5_free_pa_data(context, state->pa_data);911free(state);912(*oldrespond)(oldarg);913}914915static void916hint_list_next(struct hint_state *arg);917918static void919finish_get_edata(void *arg, krb5_error_code code, krb5_pa_data *pa)920{921krb5_error_code ret;922struct hint_state *state = arg;923924if (code == 0) {925if (pa == NULL) {926ret = k5_alloc_pa_data(state->pa_type, 0, &pa);927if (ret)928goto error;929}930ret = k5_add_pa_data_element(&state->pa_data, &pa);931k5_free_pa_data_element(pa);932if (ret)933goto error;934}935936state->ap++;937hint_list_next(state);938return;939940error:941hint_list_finish(state, ret);942}943944static void945hint_list_next(struct hint_state *state)946{947krb5_context context = state->context;948preauth_system *ap = state->ap;949950if (ap->type == -1) {951hint_list_finish(state, 0);952return;953}954955if (state->hw_only && !(ap->flags & PA_HARDWARE))956goto next;957if (ap->flags & PA_PSEUDO)958goto next;959960state->pa_type = ap->type;961if (ap->get_edata) {962ap->get_edata(context, state->request, &callbacks, state->rock,963ap->moddata, ap->type, finish_get_edata, state);964} else965finish_get_edata(state, 0, NULL);966return;967968next:969state->ap++;970hint_list_next(state);971}972973void974get_preauth_hint_list(krb5_kdc_req *request, krb5_kdcpreauth_rock rock,975krb5_pa_data ***e_data_out, kdc_hint_respond_fn respond,976void *arg)977{978krb5_context context = rock->rstate->realm_data->realm_context;979struct hint_state *state;980981*e_data_out = NULL;982983/* Allocate our state. */984state = calloc(1, sizeof(*state));985if (state == NULL)986goto error;987state->hw_only = isflagset(rock->client->attributes,988KRB5_KDB_REQUIRES_HW_AUTH);989state->respond = respond;990state->arg = arg;991state->request = request;992state->rock = rock;993state->context = context;994state->e_data_out = e_data_out;995state->pa_data = NULL;996state->ap = preauth_systems;997998/* Add an empty PA-FX-FAST element to advertise FAST support. */999if (k5_add_empty_pa_data(&state->pa_data, KRB5_PADATA_FX_FAST) != 0)1000goto error;10011002if (add_etype_info(context, rock, &state->pa_data) != 0)1003goto error;10041005hint_list_next(state);1006return;10071008error:1009if (state != NULL)1010krb5_free_pa_data(context, state->pa_data);1011free(state);1012(*respond)(arg);1013}10141015/*1016* Add authorization data returned from preauth modules to the ticket1017* It is assumed that ad is a "null-terminated" array of krb5_authdata ptrs1018*/1019static krb5_error_code1020add_authorization_data(krb5_enc_tkt_part *enc_tkt_part, krb5_authdata **ad)1021{1022krb5_authdata **newad;1023int oldones, newones;1024int i;10251026if (enc_tkt_part == NULL || ad == NULL)1027return EINVAL;10281029for (newones = 0; ad[newones] != NULL; newones++);1030if (newones == 0)1031return 0; /* nothing to add */10321033if (enc_tkt_part->authorization_data == NULL)1034oldones = 0;1035else1036for (oldones = 0;1037enc_tkt_part->authorization_data[oldones] != NULL; oldones++);10381039newad = malloc((oldones + newones + 1) * sizeof(krb5_authdata *));1040if (newad == NULL)1041return ENOMEM;10421043/* Copy any existing pointers */1044for (i = 0; i < oldones; i++)1045newad[i] = enc_tkt_part->authorization_data[i];10461047/* Add the new ones */1048for (i = 0; i < newones; i++)1049newad[oldones+i] = ad[i];10501051/* Terminate the new list */1052newad[oldones+i] = NULL;10531054/* Free any existing list */1055if (enc_tkt_part->authorization_data != NULL)1056free(enc_tkt_part->authorization_data);10571058/* Install our new list */1059enc_tkt_part->authorization_data = newad;10601061return 0;1062}10631064struct padata_state {1065kdc_preauth_respond_fn respond;1066void *arg;1067kdc_realm_t *realm;10681069krb5_kdcpreauth_modreq *modreq_ptr;1070krb5_pa_data **padata;1071int pa_found;1072krb5_context context;1073krb5_kdcpreauth_rock rock;1074krb5_data *req_pkt;1075krb5_kdc_req *request;1076krb5_enc_tkt_part *enc_tkt_reply;1077void **padata_context;10781079preauth_system *pa_sys;1080krb5_pa_data **pa_e_data;1081krb5_boolean typed_e_data_flag;1082int pa_ok;1083krb5_error_code saved_code;10841085krb5_pa_data ***e_data_out;1086krb5_boolean *typed_e_data_out;1087};10881089/* Return code if it is 0 or one of the codes we pass through to the client.1090* Otherwise return KRB5KDC_ERR_PREAUTH_FAILED. */1091static krb5_error_code1092filter_preauth_error(krb5_error_code code)1093{1094/* The following switch statement allows us1095* to return some preauth system errors back to the client.1096*/1097switch(code) {1098case 0:1099case KRB5KRB_AP_ERR_BAD_INTEGRITY:1100case KRB5KRB_AP_ERR_SKEW:1101case KRB5KDC_ERR_PREAUTH_REQUIRED:1102case KRB5KDC_ERR_ETYPE_NOSUPP:1103/* rfc 4556 */1104case KRB5KDC_ERR_CLIENT_NOT_TRUSTED:1105case KRB5KDC_ERR_INVALID_SIG:1106case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:1107case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:1108case KRB5KDC_ERR_INVALID_CERTIFICATE:1109case KRB5KDC_ERR_REVOKED_CERTIFICATE:1110case KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN:1111case KRB5KDC_ERR_CLIENT_NAME_MISMATCH:1112case KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE:1113case KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED:1114case KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED:1115case KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED:1116case KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED:1117/* earlier drafts of what became rfc 4556 */1118case KRB5KDC_ERR_CERTIFICATE_MISMATCH:1119case KRB5KDC_ERR_KDC_NOT_TRUSTED:1120case KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE:1121/* This value is shared with1122* KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */1123/* case KRB5KDC_ERR_KEY_TOO_WEAK: */1124case KRB5KDC_ERR_DISCARD:1125/* pkinit alg-agility */1126case KRB5KDC_ERR_NO_ACCEPTABLE_KDF:1127/* rfc 6113 */1128case KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED:1129return code;1130default:1131return KRB5KDC_ERR_PREAUTH_FAILED;1132}1133}11341135/*1136* If the client performed optimistic pre-authentication for a multi-round-trip1137* mechanism, it may need key information to complete the exchange, so send it1138* a PA-ETYPE-INFO2 element in addition to the pa-data from the module.1139*/1140static krb5_error_code1141maybe_add_etype_info2(struct padata_state *state, krb5_error_code code)1142{1143krb5_error_code ret;1144krb5_context context = state->context;1145krb5_kdcpreauth_rock rock = state->rock;1146krb5_data *der;11471148/* Only add key information when requesting another preauth round trip. */1149if (code != KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)1150return 0;11511152/* Don't try to add key information when there is no key. */1153if (rock->client_key == NULL)1154return 0;11551156/* If the client sent a cookie, it has already seen a KDC response with key1157* information. */1158if (krb5int_find_pa_data(context, state->request->padata,1159KRB5_PADATA_FX_COOKIE) != NULL)1160return 0;11611162ret = make_etype_info(context, TRUE, rock->client->princ, rock->client_key,1163rock->client_keyblock->enctype, &der);1164if (ret)1165return ret;1166ret = k5_add_pa_data_from_data(&state->pa_e_data, KRB5_PADATA_ETYPE_INFO2,1167der);1168krb5_free_data(context, der);1169return ret;1170}11711172/* Release state and respond to the AS-REQ processing code with the result of1173* checking pre-authentication data. */1174static void1175finish_check_padata(struct padata_state *state, krb5_error_code code)1176{1177kdc_preauth_respond_fn respond;1178void *arg;11791180if (state->pa_ok || !state->pa_found) {1181/* Return successfully. If we didn't match a preauth system, we may1182* return PREAUTH_REQUIRED later, but we didn't fail to verify. */1183code = 0;1184goto cleanup;1185}11861187/* Add key information to the saved error pa-data if required. */1188if (maybe_add_etype_info2(state, code) != 0) {1189code = KRB5KDC_ERR_PREAUTH_FAILED;1190goto cleanup;1191}11921193/* Return any saved error pa-data, stealing the pointer from state. */1194*state->e_data_out = state->pa_e_data;1195*state->typed_e_data_out = state->typed_e_data_flag;1196state->pa_e_data = NULL;11971198cleanup:1199/* Discard saved error pa-data if we aren't returning it, free state, and1200* respond to the AS-REQ processing code. */1201respond = state->respond;1202arg = state->arg;1203krb5_free_pa_data(state->context, state->pa_e_data);1204free(state);1205(*respond)(arg, filter_preauth_error(code));1206}12071208static void1209next_padata(struct padata_state *state);12101211static void1212finish_verify_padata(void *arg, krb5_error_code code,1213krb5_kdcpreauth_modreq modreq, krb5_pa_data **e_data,1214krb5_authdata **authz_data)1215{1216struct padata_state *state = arg;1217const char *emsg;1218krb5_boolean typed_e_data_flag;12191220assert(state);1221*state->modreq_ptr = modreq;12221223if (code) {1224emsg = krb5_get_error_message(state->context, code);1225krb5_klog_syslog(LOG_INFO, "preauth (%s) verify failure: %s",1226state->pa_sys->name, emsg);1227krb5_free_error_message(state->context, emsg);12281229/* Ignore authorization data returned from modules that fail */1230if (authz_data != NULL) {1231krb5_free_authdata(state->context, authz_data);1232authz_data = NULL;1233}12341235typed_e_data_flag = ((state->pa_sys->flags & PA_TYPED_E_DATA) != 0);12361237/*1238* We'll return edata from either the first PA_REQUIRED module1239* that fails, or the first non-PA_REQUIRED module that fails.1240* Hang on to edata from the first non-PA_REQUIRED module.1241* If we've already got one saved, simply discard this one.1242*/1243if (state->pa_sys->flags & PA_REQUIRED) {1244/* free up any previous edata we might have been saving */1245if (state->pa_e_data != NULL)1246krb5_free_pa_data(state->context, state->pa_e_data);1247state->pa_e_data = e_data;1248state->typed_e_data_flag = typed_e_data_flag;12491250/* Make sure we use the current retval */1251state->pa_ok = 0;1252finish_check_padata(state, code);1253return;1254} else if (state->pa_e_data == NULL) {1255/* save the first error code and e-data */1256state->pa_e_data = e_data;1257state->typed_e_data_flag = typed_e_data_flag;1258state->saved_code = code;1259} else if (e_data != NULL) {1260/* discard this extra e-data from non-PA_REQUIRED module */1261krb5_free_pa_data(state->context, e_data);1262}1263} else {1264#ifdef DEBUG1265krb5_klog_syslog (LOG_DEBUG, ".. .. ok");1266#endif12671268/* Ignore any edata returned on success */1269if (e_data != NULL)1270krb5_free_pa_data(state->context, e_data);12711272/* Add any authorization data to the ticket */1273if (authz_data != NULL) {1274add_authorization_data(state->enc_tkt_reply, authz_data);1275free(authz_data);1276}12771278state->pa_ok = 1;1279if (state->pa_sys->flags & PA_SUFFICIENT) {1280finish_check_padata(state, state->saved_code);1281return;1282}1283}12841285next_padata(state);1286}12871288static void1289next_padata(struct padata_state *state)1290{1291assert(state);1292if (!state->padata)1293state->padata = state->request->padata;1294else1295state->padata++;12961297if (!*state->padata) {1298finish_check_padata(state, state->saved_code);1299return;1300}13011302#ifdef DEBUG1303krb5_klog_syslog (LOG_DEBUG, ".. pa_type 0x%x", (*state->padata)->pa_type);1304#endif1305if (find_pa_system((*state->padata)->pa_type, &state->pa_sys))1306goto next;1307if (find_modreq(state->pa_sys, *state->padata_context, &state->modreq_ptr))1308goto next;1309#ifdef DEBUG1310krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", state->pa_sys->name);1311#endif1312if (state->pa_sys->verify_padata == 0)1313goto next;13141315state->pa_found++;1316state->pa_sys->verify_padata(state->context, state->req_pkt,1317state->request, state->enc_tkt_reply,1318*state->padata, &callbacks, state->rock,1319state->pa_sys->moddata, finish_verify_padata,1320state);1321return;13221323next:1324next_padata(state);1325}13261327/*1328* This routine is called to verify the preauthentication information1329* for a V5 request.1330*1331* Returns 0 if the pre-authentication is valid, non-zero to indicate1332* an error code of some sort.1333*/13341335void1336check_padata(krb5_context context, krb5_kdcpreauth_rock rock,1337krb5_data *req_pkt, krb5_kdc_req *request,1338krb5_enc_tkt_part *enc_tkt_reply, void **padata_context,1339krb5_pa_data ***e_data, krb5_boolean *typed_e_data,1340kdc_preauth_respond_fn respond, void *arg)1341{1342struct padata_state *state;13431344if (request->padata == 0) {1345(*respond)(arg, 0);1346return;1347}13481349if (make_padata_context(context, padata_context) != 0) {1350(*respond)(arg, KRB5KRB_ERR_GENERIC);1351return;1352}13531354state = calloc(1, sizeof(*state));1355if (state == NULL) {1356(*respond)(arg, ENOMEM);1357return;1358}1359state->respond = respond;1360state->arg = arg;1361state->context = context;1362state->rock = rock;1363state->req_pkt = req_pkt;1364state->request = request;1365state->enc_tkt_reply = enc_tkt_reply;1366state->padata_context = padata_context;1367state->e_data_out = e_data;1368state->typed_e_data_out = typed_e_data;1369state->realm = rock->rstate->realm_data;13701371#ifdef DEBUG1372krb5_klog_syslog (LOG_DEBUG, "checking padata");1373#endif13741375next_padata(state);1376}13771378/* Return true if k1 and k2 have the same type and contents. */1379static krb5_boolean1380keyblock_equal(const krb5_keyblock *k1, const krb5_keyblock *k2)1381{1382if (k1->enctype != k2->enctype)1383return FALSE;1384if (k1->length != k2->length)1385return FALSE;1386return memcmp(k1->contents, k2->contents, k1->length) == 0;1387}13881389/*1390* return_padata creates any necessary preauthentication1391* structures which should be returned by the KDC to the client1392*/1393krb5_error_code1394return_padata(krb5_context context, krb5_kdcpreauth_rock rock,1395krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply,1396krb5_keyblock *encrypting_key, void **padata_context)1397{1398krb5_error_code retval;1399krb5_pa_data ** padata;1400krb5_pa_data ** send_pa_list = NULL;1401krb5_pa_data * send_pa;1402krb5_pa_data * pa = 0;1403krb5_pa_data null_item;1404preauth_system * ap;1405int * pa_order = NULL;1406int * pa_type;1407int size = 0;1408krb5_kdcpreauth_modreq *modreq_ptr;1409krb5_boolean key_modified;1410krb5_keyblock original_key;14111412memset(&original_key, 0, sizeof(original_key));14131414if ((!*padata_context) &&1415(make_padata_context(context, padata_context) != 0)) {1416return KRB5KRB_ERR_GENERIC;1417}14181419for (ap = preauth_systems; ap->type != -1; ap++) {1420if (ap->return_padata)1421size++;1422}14231424pa_order = k5calloc(size + 1, sizeof(int), &retval);1425if (pa_order == NULL)1426goto cleanup;1427sort_pa_order(context, request, pa_order);14281429retval = krb5_copy_keyblock_contents(context, encrypting_key,1430&original_key);1431if (retval)1432goto cleanup;1433key_modified = FALSE;1434null_item.contents = NULL;1435null_item.length = 0;14361437for (pa_type = pa_order; *pa_type != -1; pa_type++) {1438ap = &preauth_systems[*pa_type];1439if (key_modified && (ap->flags & PA_REPLACES_KEY))1440continue;1441if (ap->return_padata == 0)1442continue;1443if (find_modreq(ap, *padata_context, &modreq_ptr))1444continue;1445pa = &null_item;1446null_item.pa_type = ap->type;1447if (request->padata) {1448for (padata = request->padata; *padata; padata++) {1449if ((*padata)->pa_type == ap->type) {1450pa = *padata;1451break;1452}1453}1454}1455send_pa = NULL;1456retval = ap->return_padata(context, pa, req_pkt, request, reply,1457encrypting_key, &send_pa, &callbacks, rock,1458ap->moddata, *modreq_ptr);1459if (retval)1460goto cleanup;14611462if (send_pa != NULL) {1463retval = k5_add_pa_data_element(&send_pa_list, &send_pa);1464k5_free_pa_data_element(send_pa);1465if (retval)1466goto cleanup;1467}14681469if (!key_modified && !keyblock_equal(&original_key, encrypting_key))1470key_modified = TRUE;1471}14721473/*1474* Add etype-info and pw-salt pa-data as needed. If we replaced the reply1475* key, we can't send consistent etype-info; the salt from the client key1476* data doesn't correspond to the replaced reply key, and RFC 4120 section1477* 5.2.7.5 forbids us from sending etype-info describing the initial reply1478* key in an AS-REP if it doesn't have the same enctype as the replaced1479* reply key. For all current and foreseeable preauth mechs, we can assume1480* the client received etype-info2 in an earlier step and already computed1481* the initial reply key if it needed it. The client can determine the1482* enctype of the replaced reply key from the etype field of the enc-part1483* field of the AS-REP.1484*/1485if (!key_modified) {1486retval = add_etype_info(context, rock, &send_pa_list);1487if (retval)1488goto cleanup;1489retval = add_pw_salt(context, rock, &send_pa_list);1490if (retval)1491goto cleanup;1492}14931494if (send_pa_list != NULL) {1495reply->padata = send_pa_list;1496send_pa_list = 0;1497}14981499cleanup:1500krb5_free_keyblock_contents(context, &original_key);1501free(pa_order);1502krb5_free_pa_data(context, send_pa_list);15031504return (retval);1505}15061507static krb5_error_code1508_make_etype_info_entry(krb5_context context,1509krb5_principal client_princ, krb5_key_data *client_key,1510krb5_enctype etype, krb5_etype_info_entry **entry_out,1511int etype_info2)1512{1513krb5_error_code retval;1514krb5_int16 salttype;1515krb5_data *salt = NULL;1516krb5_etype_info_entry *entry = NULL;15171518*entry_out = NULL;1519entry = malloc(sizeof(*entry));1520if (entry == NULL)1521return ENOMEM;15221523entry->magic = KV5M_ETYPE_INFO_ENTRY;1524entry->etype = etype;1525entry->length = KRB5_ETYPE_NO_SALT;1526entry->salt = NULL;1527entry->s2kparams = empty_data();1528retval = krb5_dbe_compute_salt(context, client_key, client_princ,1529&salttype, &salt);1530if (retval)1531goto cleanup;15321533entry->length = salt->length;1534entry->salt = (unsigned char *)salt->data;1535salt->data = NULL;1536*entry_out = entry;1537entry = NULL;15381539cleanup:1540if (entry != NULL)1541krb5_free_data_contents(context, &entry->s2kparams);1542free(entry);1543krb5_free_data(context, salt);1544return retval;1545}15461547/* Encode an etype-info or etype-info2 message for client_key with the given1548* enctype, using client to compute the salt if necessary. */1549static krb5_error_code1550make_etype_info(krb5_context context, krb5_boolean etype_info2,1551krb5_principal client, krb5_key_data *client_key,1552krb5_enctype enctype, krb5_data **der_out)1553{1554krb5_error_code retval;1555krb5_etype_info_entry **entry = NULL;15561557*der_out = NULL;15581559entry = k5calloc(2, sizeof(*entry), &retval);1560if (entry == NULL)1561goto cleanup;1562retval = _make_etype_info_entry(context, client, client_key, enctype,1563&entry[0], etype_info2);1564if (retval != 0)1565goto cleanup;15661567if (etype_info2)1568retval = encode_krb5_etype_info2(entry, der_out);1569else1570retval = encode_krb5_etype_info(entry, der_out);15711572cleanup:1573krb5_free_etype_info(context, entry);1574return retval;1575}15761577/*1578* Returns TRUE if the PAC should be included1579*/1580krb5_boolean1581include_pac_p(krb5_context context, krb5_kdc_req *request)1582{1583krb5_error_code code;1584krb5_pa_data **padata;1585krb5_boolean retval = TRUE; /* default is to return PAC */1586krb5_data data;1587krb5_pa_pac_req *req = NULL;15881589if (request->padata == NULL) {1590return retval;1591}15921593for (padata = request->padata; *padata != NULL; padata++) {1594if ((*padata)->pa_type == KRB5_PADATA_PAC_REQUEST) {1595data.data = (char *)(*padata)->contents;1596data.length = (*padata)->length;15971598code = decode_krb5_pa_pac_req(&data, &req);1599if (code == 0) {1600retval = req->include_pac;1601krb5_free_pa_pac_req(context, req);1602req = NULL;1603}1604break;1605}1606}16071608return retval;1609}16101611static krb5_error_code1612return_referral_enc_padata( krb5_context context,1613krb5_enc_kdc_rep_part *reply,1614krb5_db_entry *server)1615{1616krb5_error_code code;1617krb5_tl_data tl_data;1618krb5_pa_data *pa;16191620tl_data.tl_data_type = KRB5_TL_SVR_REFERRAL_DATA;1621code = krb5_dbe_lookup_tl_data(context, server, &tl_data);1622if (code || tl_data.tl_data_length == 0)1623return 0;16241625code = k5_alloc_pa_data(KRB5_PADATA_SVR_REFERRAL_INFO,1626tl_data.tl_data_length, &pa);1627if (code)1628return code;1629memcpy(pa->contents, tl_data.tl_data_contents, tl_data.tl_data_length);1630code = k5_add_pa_data_element(&reply->enc_padata, &pa);1631k5_free_pa_data_element(pa);1632return code;1633}16341635krb5_error_code1636return_enc_padata(krb5_context context, krb5_data *req_pkt,1637krb5_kdc_req *request, krb5_keyblock *reply_key,1638krb5_db_entry *server, krb5_enc_kdc_rep_part *reply_encpart,1639krb5_boolean is_referral)1640{1641krb5_error_code code = 0;1642/* This should be initialized and only used for Win2K compat and other1643* specific standardized uses such as FAST negotiation. */1644if (is_referral) {1645code = return_referral_enc_padata(context, reply_encpart, server);1646if (code)1647return code;1648}1649code = kdc_handle_protected_negotiation(context, req_pkt, request, reply_key,1650&reply_encpart->enc_padata);1651if (code)1652goto cleanup;16531654code = kdc_add_pa_pac_options(context, request,1655&reply_encpart->enc_padata);1656if (code)1657goto cleanup;16581659/*Add potentially other enc_padata providers*/1660cleanup:1661return code;1662}166316641665