2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * Copyright 1995, 2003, 2008 by the Massachusetts Institute of Technology. All
2N/A * Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A */
2N/A/*
2N/A * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * This file contains routines for establishing, verifying, and any other
2N/A * necessary functions, for utilizing the pre-authentication field of the
2N/A * kerberos kdc request, with various hardware/software verification devices.
2N/A */
2N/A
2N/A#include "k5-int.h"
2N/A#if APPLE_PKINIT
2N/A#include "pkinit_client.h"
2N/A#include "pkinit_cert_store.h"
2N/A#endif /* APPLE_PKINIT */
2N/A#include "osconf.h"
2N/A#include <preauth_plugin.h> /* Solaris Kerberos removed krb5/ */
2N/A#include "int-proto.h"
2N/A#include "fast.h"
2N/A
2N/A#if !defined(_WIN32)
2N/A#include <unistd.h>
2N/A#endif
2N/A
2N/A#if TARGET_OS_MAC
2N/Astatic const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR,
2N/A LIBDIR "/krb5/plugins/preauth",
2N/A NULL };
2N/A#else
2N/Astatic const char *objdirs[] = { LIBDIR "/krb5/plugins/preauth", NULL };
2N/A#endif
2N/A
2N/Atypedef krb5_error_code (*pa_function)(krb5_context,
2N/A krb5_kdc_req *request,
2N/A krb5_pa_data *in_padata,
2N/A krb5_pa_data **out_padata,
2N/A krb5_data *salt, krb5_data *s2kparams,
2N/A krb5_enctype *etype,
2N/A krb5_keyblock *as_key,
2N/A krb5_prompter_fct prompter_fct,
2N/A void *prompter_data,
2N/A krb5_gic_get_as_key_fct gak_fct,
2N/A void *gak_data);
2N/A
2N/Atypedef struct _pa_types_t {
2N/A krb5_preauthtype type;
2N/A pa_function fct;
2N/A int flags;
2N/A} pa_types_t;
2N/A
2N/A/* Open plugin directories for preauth modules. */
2N/Astatic krb5_error_code
2N/Aopen_preauth_plugin_dirs(krb5_context ctx)
2N/A{
2N/A static const char *path[] = {
2N/A KRB5_CONF_LIBDEFAULTS, KRB5_CONF_PREAUTH_MODULE_DIR, NULL,
2N/A };
2N/A char **profpath = NULL;
2N/A const char **dirs;
2N/A krb5_error_code ret;
2N/A
2N/A ret = profile_get_values(ctx->profile, path, &profpath);
2N/A if (ret != 0 && ret != PROF_NO_RELATION)
2N/A return ret;
2N/A dirs = (profpath != NULL) ? (const char **) profpath : objdirs;
2N/A ret = krb5int_open_plugin_dirs(dirs, NULL, &ctx->preauth_plugins,
2N/A &ctx->err);
2N/A profile_free_list(profpath);
2N/A return ret;
2N/A}
2N/A
2N/A/* Create the per-krb5_context context. This means loading the modules
2N/A * if we haven't done that yet (applications which never obtain initial
2N/A * credentials should never hit this routine), breaking up the module's
2N/A * list of support pa_types so that we can iterate over the modules more
2N/A * easily, and copying over the relevant parts of the module's table. */
2N/Avoid KRB5_CALLCONV
2N/Akrb5_init_preauth_context(krb5_context kcontext)
2N/A{
2N/A int n_modules, n_tables, i, j, k;
2N/A void **tables;
2N/A struct krb5plugin_preauth_client_ftable_v1 *table;
2N/A krb5_preauth_context *context = NULL;
2N/A void *plugin_context;
2N/A krb5_preauthtype pa_type;
2N/A void **rcpp;
2N/A
2N/A /* Only do this once for each krb5_context */
2N/A if (kcontext->preauth_context != NULL)
2N/A return;
2N/A
2N/A /* load the plugins for the current context */
2N/A if (PLUGIN_DIR_OPEN(&kcontext->preauth_plugins) == 0) {
2N/A if (open_preauth_plugin_dirs(kcontext) != 0)
2N/A return;
2N/A }
2N/A
2N/A /* pull out the module function tables for all of the modules */
2N/A tables = NULL;
2N/A if (krb5int_get_plugin_dir_data(&kcontext->preauth_plugins,
2N/A "preauthentication_client_1",
2N/A &tables,
2N/A &kcontext->err) != 0) {
2N/A return;
2N/A }
2N/A if (tables == NULL) {
2N/A return;
2N/A }
2N/A
2N/A /* count how many modules we ended up loading, and how many preauth
2N/A * types we may claim to support as a result */
2N/A n_modules = 0;
2N/A for (n_tables = 0;
2N/A (tables != NULL) && (tables[n_tables] != NULL);
2N/A n_tables++) {
2N/A table = tables[n_tables];
2N/A if ((table->pa_type_list != NULL) && (table->process != NULL)) {
2N/A for (j = 0; table->pa_type_list[j] > 0; j++) {
2N/A n_modules++;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* allocate the space we need */
2N/A context = malloc(sizeof(*context));
2N/A if (context == NULL) {
2N/A krb5int_free_plugin_dir_data(tables);
2N/A return;
2N/A }
2N/A context->modules = calloc(n_modules, sizeof(context->modules[0]));
2N/A if (context->modules == NULL) {
2N/A krb5int_free_plugin_dir_data(tables);
2N/A free(context);
2N/A return;
2N/A }
2N/A context->n_modules = n_modules;
2N/A
2N/A /* fill in the structure */
2N/A k = 0;
2N/A for (i = 0; i < n_tables; i++) {
2N/A table = tables[i];
2N/A if ((table->pa_type_list != NULL) && (table->process != NULL)) {
2N/A plugin_context = NULL;
2N/A if ((table->init != NULL) &&
2N/A ((*table->init)(kcontext, &plugin_context) != 0)) {
2N/A#ifdef DEBUG
2N/A fprintf (stderr, "init err, skipping module \"%s\"\n",
2N/A table->name);
2N/A#endif
2N/A continue;
2N/A }
2N/A
2N/A rcpp = NULL;
2N/A for (j = 0; table->pa_type_list[j] > 0; j++) {
2N/A pa_type = table->pa_type_list[j];
2N/A context->modules[k].pa_type = pa_type;
2N/A context->modules[k].enctypes = table->enctype_list;
2N/A context->modules[k].plugin_context = plugin_context;
2N/A /* Only call client_fini once per plugin */
2N/A if (j == 0)
2N/A context->modules[k].client_fini = table->fini;
2N/A else
2N/A context->modules[k].client_fini = NULL;
2N/A context->modules[k].ftable = table;
2N/A context->modules[k].name = table->name;
2N/A context->modules[k].flags = (*table->flags)(kcontext, pa_type);
2N/A context->modules[k].use_count = 0;
2N/A context->modules[k].client_process = table->process;
2N/A context->modules[k].client_tryagain = table->tryagain;
2N/A if (j == 0)
2N/A context->modules[k].client_supply_gic_opts = table->gic_opts;
2N/A else
2N/A context->modules[k].client_supply_gic_opts = NULL;
2N/A context->modules[k].request_context = NULL;
2N/A /*
2N/A * Only call request_init and request_fini once per plugin.
2N/A * Only the first module within each plugin will ever
2N/A * have request_context filled in. Every module within
2N/A * the plugin will have its request_context_pp pointing
2N/A * to that entry's request_context. That way all the
2N/A * modules within the plugin share the same request_context
2N/A */
2N/A if (j == 0) {
2N/A context->modules[k].client_req_init = table->request_init;
2N/A context->modules[k].client_req_fini = table->request_fini;
2N/A rcpp = &context->modules[k].request_context;
2N/A } else {
2N/A context->modules[k].client_req_init = NULL;
2N/A context->modules[k].client_req_fini = NULL;
2N/A }
2N/A context->modules[k].request_context_pp = rcpp;
2N/A#ifdef DEBUG
2N/A fprintf (stderr, "init module \"%s\", pa_type %d, flag %d\n",
2N/A context->modules[k].name,
2N/A context->modules[k].pa_type,
2N/A context->modules[k].flags);
2N/A#endif
2N/A k++;
2N/A }
2N/A }
2N/A }
2N/A krb5int_free_plugin_dir_data(tables);
2N/A
2N/A /* return the result */
2N/A kcontext->preauth_context = context;
2N/A}
2N/A
2N/A/* Zero the use counts for the modules herein. Usually used before we
2N/A * start processing any data from the server, at which point every module
2N/A * will again be able to take a crack at whatever the server sent. */
2N/Avoid KRB5_CALLCONV
2N/Akrb5_clear_preauth_context_use_counts(krb5_context context)
2N/A{
2N/A int i;
2N/A if (context->preauth_context != NULL) {
2N/A for (i = 0; i < context->preauth_context->n_modules; i++) {
2N/A context->preauth_context->modules[i].use_count = 0;
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Give all the preauth plugins a look at the preauth option which
2N/A * has just been set
2N/A */
2N/Akrb5_error_code
2N/Akrb5_preauth_supply_preauth_data(krb5_context context,
2N/A krb5_gic_opt_ext *opte,
2N/A const char *attr,
2N/A const char *value)
2N/A{
2N/A krb5_error_code retval = 0;
2N/A int i;
2N/A void *pctx;
2N/A const char *emsg = NULL;
2N/A
2N/A if (context->preauth_context == NULL)
2N/A krb5_init_preauth_context(context);
2N/A if (context->preauth_context == NULL) {
2N/A retval = EINVAL;
2N/A krb5int_set_error(&context->err, retval,
2N/A "krb5_preauth_supply_preauth_data: "
2N/A "Unable to initialize preauth context");
2N/A return retval;
2N/A }
2N/A
2N/A /*
2N/A * Go down the list of preauth modules, and supply them with the
2N/A * attribute/value pair.
2N/A */
2N/A for (i = 0; i < context->preauth_context->n_modules; i++) {
2N/A if (context->preauth_context->modules[i].client_supply_gic_opts == NULL)
2N/A continue;
2N/A pctx = context->preauth_context->modules[i].plugin_context;
2N/A retval = (*context->preauth_context->modules[i].client_supply_gic_opts)
2N/A (context, pctx,
2N/A (krb5_get_init_creds_opt *)opte, attr, value);
2N/A if (retval) {
2N/A emsg = krb5_get_error_message(context, retval);
2N/A krb5int_set_error(&context->err, retval, "Preauth plugin %s: %s",
2N/A context->preauth_context->modules[i].name, emsg);
2N/A krb5_free_error_message(context, emsg);
2N/A break;
2N/A }
2N/A }
2N/A return retval;
2N/A}
2N/A
2N/A/* Free the per-krb5_context preauth_context. This means clearing any
2N/A * plugin-specific context which may have been created, and then
2N/A * freeing the context itself. */
2N/Avoid KRB5_CALLCONV
2N/Akrb5_free_preauth_context(krb5_context context)
2N/A{
2N/A int i;
2N/A void *pctx;
2N/A if (context && context->preauth_context != NULL) {
2N/A for (i = 0; i < context->preauth_context->n_modules; i++) {
2N/A pctx = context->preauth_context->modules[i].plugin_context;
2N/A if (context->preauth_context->modules[i].client_fini != NULL) {
2N/A (*context->preauth_context->modules[i].client_fini)(context, pctx);
2N/A }
2N/A memset(&context->preauth_context->modules[i], 0,
2N/A sizeof(context->preauth_context->modules[i]));
2N/A }
2N/A if (context->preauth_context->modules != NULL) {
2N/A free(context->preauth_context->modules);
2N/A context->preauth_context->modules = NULL;
2N/A }
2N/A free(context->preauth_context);
2N/A context->preauth_context = NULL;
2N/A }
2N/A}
2N/A
2N/A/* Initialize the per-AS-REQ context. This means calling the client_req_init
2N/A * function to give the plugin a chance to allocate a per-request context. */
2N/Avoid KRB5_CALLCONV
2N/Akrb5_preauth_request_context_init(krb5_context context)
2N/A{
2N/A int i;
2N/A void *rctx, *pctx;
2N/A
2N/A /* Limit this to only one attempt per context? */
2N/A if (context->preauth_context == NULL)
2N/A krb5_init_preauth_context(context);
2N/A if (context->preauth_context != NULL) {
2N/A for (i = 0; i < context->preauth_context->n_modules; i++) {
2N/A pctx = context->preauth_context->modules[i].plugin_context;
2N/A if (context->preauth_context->modules[i].client_req_init != NULL) {
2N/A rctx = context->preauth_context->modules[i].request_context_pp;
2N/A (*context->preauth_context->modules[i].client_req_init) (context, pctx, rctx);
2N/A }
2N/A }
2N/A }
2N/A}
2N/A
2N/A/* Free the per-AS-REQ context. This means clearing any request-specific
2N/A * context which the plugin may have created. */
2N/Avoid KRB5_CALLCONV
2N/Akrb5_preauth_request_context_fini(krb5_context context)
2N/A{
2N/A int i;
2N/A void *rctx, *pctx;
2N/A if (context->preauth_context != NULL) {
2N/A for (i = 0; i < context->preauth_context->n_modules; i++) {
2N/A pctx = context->preauth_context->modules[i].plugin_context;
2N/A rctx = context->preauth_context->modules[i].request_context;
2N/A if (rctx != NULL) {
2N/A if (context->preauth_context->modules[i].client_req_fini != NULL) {
2N/A (*context->preauth_context->modules[i].client_req_fini)(context, pctx, rctx);
2N/A }
2N/A context->preauth_context->modules[i].request_context = NULL;
2N/A }
2N/A }
2N/A }
2N/A}
2N/A
2N/A/* Add the named encryption type to the existing list of ktypes. */
2N/Astatic void
2N/Agrow_ktypes(krb5_enctype **out_ktypes, int *out_nktypes, krb5_enctype ktype)
2N/A{
2N/A int i;
2N/A krb5_enctype *ktypes;
2N/A for (i = 0; i < *out_nktypes; i++) {
2N/A if ((*out_ktypes)[i] == ktype)
2N/A return;
2N/A }
2N/A ktypes = malloc((*out_nktypes + 2) * sizeof(ktype));
2N/A if (ktypes) {
2N/A for (i = 0; i < *out_nktypes; i++)
2N/A ktypes[i] = (*out_ktypes)[i];
2N/A ktypes[i++] = ktype;
2N/A ktypes[i] = 0;
2N/A free(*out_ktypes);
2N/A *out_ktypes = ktypes;
2N/A *out_nktypes = i;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Add the given list of pa_data items to the existing list of items.
2N/A * Factored out here to make reading the do_preauth logic easier to read.
2N/A */
2N/Astatic int
2N/Agrow_pa_list(krb5_pa_data ***out_pa_list, int *out_pa_list_size,
2N/A krb5_pa_data **addition, int num_addition)
2N/A{
2N/A krb5_pa_data **pa_list;
2N/A int i, j;
2N/A
2N/A if (out_pa_list == NULL || addition == NULL) {
2N/A return EINVAL;
2N/A }
2N/A
2N/A if (*out_pa_list == NULL) {
2N/A /* Allocate room for the new additions and a NULL terminator. */
2N/A pa_list = malloc((num_addition + 1) * sizeof(krb5_pa_data *));
2N/A if (pa_list == NULL)
2N/A return ENOMEM;
2N/A for (i = 0; i < num_addition; i++)
2N/A pa_list[i] = addition[i];
2N/A pa_list[i] = NULL;
2N/A *out_pa_list = pa_list;
2N/A *out_pa_list_size = num_addition;
2N/A } else {
2N/A /*
2N/A * Allocate room for the existing entries plus
2N/A * the new additions and a NULL terminator.
2N/A */
2N/A pa_list = malloc((*out_pa_list_size + num_addition + 1)
2N/A * sizeof(krb5_pa_data *));
2N/A if (pa_list == NULL)
2N/A return ENOMEM;
2N/A for (i = 0; i < *out_pa_list_size; i++)
2N/A pa_list[i] = (*out_pa_list)[i];
2N/A for (j = 0; j < num_addition;)
2N/A pa_list[i++] = addition[j++];
2N/A pa_list[i] = NULL;
2N/A free(*out_pa_list);
2N/A *out_pa_list = pa_list;
2N/A *out_pa_list_size = i;
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Retrieve a specific piece of information required by the plugin and
2N/A * return it in a new krb5_data item. There are separate request_types
2N/A * to obtain the data and free it.
2N/A *
2N/A * This may require massaging data into a contrived format, but it will
2N/A * hopefully keep us from having to reveal library-internal functions
2N/A * or data to the plugin modules.
2N/A */
2N/A
2N/Astatic krb5_error_code
2N/Aclient_data_proc(krb5_context kcontext,
2N/A krb5_preauth_client_rock *rock,
2N/A krb5_int32 request_type,
2N/A krb5_data **retdata)
2N/A{
2N/A krb5_data *ret;
2N/A krb5_error_code retval;
2N/A char *data;
2N/A
2N/A if (rock->magic != CLIENT_ROCK_MAGIC)
2N/A return EINVAL;
2N/A if (retdata == NULL)
2N/A return EINVAL;
2N/A
2N/A switch (request_type) {
2N/A case krb5plugin_preauth_client_get_etype:
2N/A {
2N/A krb5_enctype *eptr;
2N/A ret = malloc(sizeof(krb5_data));
2N/A if (ret == NULL)
2N/A return ENOMEM;
2N/A data = malloc(sizeof(krb5_enctype));
2N/A if (data == NULL) {
2N/A free(ret);
2N/A return ENOMEM;
2N/A }
2N/A ret->data = data;
2N/A ret->length = sizeof(krb5_enctype);
2N/A eptr = (krb5_enctype *)data;
2N/A *eptr = *rock->etype;
2N/A *retdata = ret;
2N/A return 0;
2N/A }
2N/A break;
2N/A case krb5plugin_preauth_client_free_etype:
2N/A ret = *retdata;
2N/A if (ret == NULL)
2N/A return 0;
2N/A if (ret->data)
2N/A free(ret->data);
2N/A free(ret);
2N/A return 0;
2N/A break;
2N/A case krb5plugin_preauth_client_fast_armor: {
2N/A krb5_keyblock *key = NULL;
2N/A ret = calloc(1, sizeof(krb5_data));
2N/A if (ret == NULL)
2N/A return ENOMEM;
2N/A retval = 0;
2N/A if (rock->fast_state->armor_key)
2N/A retval = krb5_copy_keyblock(kcontext, rock->fast_state->armor_key,
2N/A &key);
2N/A if (retval == 0) {
2N/A ret->data = (char *) key;
2N/A ret->length = key?sizeof(krb5_keyblock):0;
2N/A key = NULL;
2N/A }
2N/A if (retval == 0) {
2N/A *retdata = ret;
2N/A ret = NULL;
2N/A }
2N/A if (ret)
2N/A free(ret);
2N/A return retval;
2N/A }
2N/A case krb5plugin_preauth_client_free_fast_armor:
2N/A ret = *retdata;
2N/A if (ret) {
2N/A if (ret->data)
2N/A krb5_free_keyblock(kcontext, (krb5_keyblock *) ret->data);
2N/A free(ret);
2N/A *retdata = NULL;
2N/A }
2N/A return 0;
2N/A default:
2N/A return EINVAL;
2N/A }
2N/A}
2N/A
2N/A/* Tweak the request body, for now adding any enctypes which the module claims
2N/A * to add support for to the list, but in the future perhaps doing more
2N/A * involved things. */
2N/Avoid KRB5_CALLCONV
2N/Akrb5_preauth_prepare_request(krb5_context kcontext,
2N/A krb5_gic_opt_ext *opte,
2N/A krb5_kdc_req *request)
2N/A{
2N/A int i, j;
2N/A
2N/A if (kcontext->preauth_context == NULL) {
2N/A return;
2N/A }
2N/A /* Add the module-specific enctype list to the request, but only if
2N/A * it's something we can safely modify. */
2N/A if (!(opte && (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))) {
2N/A for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
2N/A if (kcontext->preauth_context->modules[i].enctypes == NULL)
2N/A continue;
2N/A for (j = 0; kcontext->preauth_context->modules[i].enctypes[j] != 0; j++) {
2N/A grow_ktypes(&request->ktype, &request->nktypes,
2N/A kcontext->preauth_context->modules[i].enctypes[j]);
2N/A }
2N/A }
2N/A }
2N/A}
2N/A
2N/A/* Find the first module which provides for the named preauth type which also
2N/A * hasn't had a chance to run yet (INFO modules don't count, because as a rule
2N/A * they don't generate preauth data), and run it. */
2N/Astatic krb5_error_code
2N/Arun_preauth_plugins(krb5_context kcontext,
2N/A int module_required_flags,
2N/A krb5_kdc_req *request,
2N/A krb5_data *encoded_request_body,
2N/A krb5_data *encoded_previous_request,
2N/A krb5_pa_data *in_padata,
2N/A krb5_prompter_fct prompter,
2N/A void *prompter_data,
2N/A preauth_get_as_key_proc gak_fct,
2N/A krb5_data *salt,
2N/A krb5_data *s2kparams,
2N/A void *gak_data,
2N/A krb5_preauth_client_rock *get_data_rock,
2N/A krb5_keyblock *as_key,
2N/A krb5_pa_data ***out_pa_list,
2N/A int *out_pa_list_size,
2N/A int *module_ret,
2N/A int *module_flags,
2N/A krb5_gic_opt_ext *opte)
2N/A{
2N/A int i;
2N/A krb5_pa_data **out_pa_data;
2N/A krb5_error_code ret;
2N/A struct _krb5_preauth_context_module *module;
2N/A
2N/A if (kcontext->preauth_context == NULL) {
2N/A return ENOENT;
2N/A }
2N/A /* iterate over all loaded modules */
2N/A for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
2N/A module = &kcontext->preauth_context->modules[i];
2N/A /* skip over those which don't match the preauth type */
2N/A if (module->pa_type != in_padata->pa_type)
2N/A continue;
2N/A /* skip over those which don't match the flags (INFO vs REAL, mainly) */
2N/A if ((module->flags & module_required_flags) == 0)
2N/A continue;
2N/A /* if it's a REAL module, try to call it only once per library call */
2N/A if (module_required_flags & PA_REAL) {
2N/A if (module->use_count > 0) {
2N/A#ifdef DEBUG
2N/A fprintf(stderr, "skipping already-used module \"%s\"(%d)\n",
2N/A module->name, module->pa_type);
2N/A#endif
2N/A continue;
2N/A }
2N/A module->use_count++;
2N/A }
2N/A /* run the module's callback function */
2N/A out_pa_data = NULL;
2N/A#ifdef DEBUG
2N/A fprintf(stderr, "using module \"%s\" (%d), flags = %d\n",
2N/A module->name, module->pa_type, module->flags);
2N/A#endif
2N/A ret = module->client_process(kcontext,
2N/A module->plugin_context,
2N/A *module->request_context_pp,
2N/A (krb5_get_init_creds_opt *)opte,
2N/A client_data_proc,
2N/A get_data_rock,
2N/A request,
2N/A encoded_request_body,
2N/A encoded_previous_request,
2N/A in_padata,
2N/A prompter, prompter_data,
2N/A gak_fct, gak_data, salt, s2kparams,
2N/A as_key,
2N/A &out_pa_data);
2N/A /* Make note of the module's flags and status. */
2N/A *module_flags = module->flags;
2N/A *module_ret = ret;
2N/A /* Save the new preauth data item. */
2N/A if (out_pa_data != NULL) {
2N/A int j;
2N/A for (j = 0; out_pa_data[j] != NULL; j++);
2N/A ret = grow_pa_list(out_pa_list, out_pa_list_size, out_pa_data, j);
2N/A free(out_pa_data);
2N/A if (ret != 0)
2N/A return ret;
2N/A }
2N/A break;
2N/A }
2N/A if (i >= kcontext->preauth_context->n_modules) {
2N/A return ENOENT;
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/Astatic inline krb5_data
2N/Apadata2data(krb5_pa_data p)
2N/A{
2N/A krb5_data d;
2N/A d.magic = KV5M_DATA;
2N/A d.length = p.length;
2N/A d.data = (char *) p.contents;
2N/A return d;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Apa_salt(krb5_context context, krb5_kdc_req *request, krb5_pa_data *in_padata,
2N/A krb5_pa_data **out_padata, krb5_data *salt, krb5_data *s2kparams,
2N/A krb5_enctype *etype, krb5_keyblock *as_key, krb5_prompter_fct prompter,
2N/A void *prompter_data, krb5_gic_get_as_key_fct gak_fct, void *gak_data)
2N/A{
2N/A krb5_data tmp;
2N/A krb5_error_code retval;
2N/A
2N/A tmp = padata2data(*in_padata);
2N/A krb5_free_data_contents(context, salt);
2N/A retval = krb5int_copy_data_contents(context, &tmp, salt);
2N/A if (retval)
2N/A return retval;
2N/A
2N/A if (in_padata->pa_type == KRB5_PADATA_AFS3_SALT)
2N/A salt->length = SALT_TYPE_AFS_LENGTH;
2N/A
2N/A return(0);
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Apa_fx_cookie(krb5_context context, krb5_kdc_req *request,
2N/A krb5_pa_data *in_padata, krb5_pa_data **out_padata,
2N/A krb5_data *salt, krb5_data *s2kparams, krb5_enctype *etype,
2N/A krb5_keyblock *as_key, krb5_prompter_fct prompter,
2N/A void *prompter_data, krb5_gic_get_as_key_fct gak_fct,
2N/A void *gak_data)
2N/A{
2N/A krb5_pa_data *pa = calloc(1, sizeof(krb5_pa_data));
2N/A krb5_octet *contents;
2N/A if (pa == NULL)
2N/A return ENOMEM;
2N/A contents = malloc(in_padata->length);
2N/A if (contents == NULL) {
2N/A free(pa);
2N/A return ENOMEM;
2N/A }
2N/A *pa = *in_padata;
2N/A pa->contents = contents;
2N/A memcpy(contents, in_padata->contents, pa->length);
2N/A *out_padata = pa;
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Apa_enc_timestamp(krb5_context context, krb5_kdc_req *request,
2N/A krb5_pa_data *in_padata, krb5_pa_data **out_padata,
2N/A krb5_data *salt, krb5_data *s2kparams, krb5_enctype *etype,
2N/A krb5_keyblock *as_key, krb5_prompter_fct prompter,
2N/A void *prompter_data, krb5_gic_get_as_key_fct gak_fct,
2N/A void *gak_data)
2N/A{
2N/A krb5_error_code ret;
2N/A krb5_pa_enc_ts pa_enc;
2N/A krb5_data *tmp;
2N/A krb5_enc_data enc_data;
2N/A krb5_pa_data *pa;
2N/A
2N/A if (as_key->length == 0) {
2N/A#ifdef DEBUG
2N/A /* Solaris Kerberos */
2N/A if (salt != NULL && salt->data != NULL) {
2N/A fprintf (stderr, "%s:%d: salt len=%d", __FILE__, __LINE__,
2N/A salt->length);
2N/A if ((int) salt->length > 0)
2N/A fprintf (stderr, " '%.*s'", salt->length, salt->data);
2N/A fprintf (stderr, "; *etype=%d request->ktype[0]=%d\n",
2N/A *etype, request->ktype[0]);
2N/A }
2N/A#endif
2N/A if ((ret = ((*gak_fct)(context, request->client,
2N/A *etype ? *etype : request->ktype[0],
2N/A prompter, prompter_data,
2N/A salt, s2kparams, as_key, gak_data))))
2N/A return(ret);
2N/A }
2N/A
2N/A /* now get the time of day, and encrypt it accordingly */
2N/A
2N/A if ((ret = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec)))
2N/A return(ret);
2N/A
2N/A if ((ret = encode_krb5_pa_enc_ts(&pa_enc, &tmp)))
2N/A return(ret);
2N/A
2N/A#ifdef DEBUG
2N/A fprintf (stderr, "key type %d bytes %02x %02x ...\n",
2N/A as_key->enctype,
2N/A as_key->contents[0], as_key->contents[1]);
2N/A#endif
2N/A ret = krb5_encrypt_helper(context, as_key,
2N/A KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
2N/A tmp, &enc_data);
2N/A#ifdef DEBUG
2N/A fprintf (stderr, "enc data { type=%d kvno=%d data=%02x %02x ... }\n",
2N/A enc_data.enctype, enc_data.kvno,
2N/A 0xff & enc_data.ciphertext.data[0],
2N/A 0xff & enc_data.ciphertext.data[1]);
2N/A#endif
2N/A
2N/A krb5_free_data(context, tmp);
2N/A
2N/A if (ret) {
2N/A free(enc_data.ciphertext.data);
2N/A return(ret);
2N/A }
2N/A
2N/A ret = encode_krb5_enc_data(&enc_data, &tmp);
2N/A
2N/A free(enc_data.ciphertext.data);
2N/A
2N/A if (ret)
2N/A return(ret);
2N/A
2N/A if ((pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
2N/A krb5_free_data(context, tmp);
2N/A return(ENOMEM);
2N/A }
2N/A
2N/A pa->magic = KV5M_PA_DATA;
2N/A pa->pa_type = KRB5_PADATA_ENC_TIMESTAMP;
2N/A pa->length = tmp->length;
2N/A pa->contents = (krb5_octet *) tmp->data;
2N/A
2N/A *out_padata = pa;
2N/A
2N/A free(tmp);
2N/A
2N/A return(0);
2N/A}
2N/A
2N/Astatic char *
2N/Asam_challenge_banner(krb5_int32 sam_type)
2N/A{
2N/A char *label;
2N/A
2N/A switch (sam_type) {
2N/A case PA_SAM_TYPE_ENIGMA: /* Enigma Logic */
2N/A label = "Challenge for Enigma Logic mechanism";
2N/A break;
2N/A case PA_SAM_TYPE_DIGI_PATH: /* Digital Pathways */
2N/A case PA_SAM_TYPE_DIGI_PATH_HEX: /* Digital Pathways */
2N/A label = "Challenge for Digital Pathways mechanism";
2N/A break;
2N/A case PA_SAM_TYPE_ACTIVCARD_DEC: /* Digital Pathways */
2N/A case PA_SAM_TYPE_ACTIVCARD_HEX: /* Digital Pathways */
2N/A label = "Challenge for Activcard mechanism";
2N/A break;
2N/A case PA_SAM_TYPE_SKEY_K0: /* S/key where KDC has key 0 */
2N/A label = "Challenge for Enhanced S/Key mechanism";
2N/A break;
2N/A case PA_SAM_TYPE_SKEY: /* Traditional S/Key */
2N/A label = "Challenge for Traditional S/Key mechanism";
2N/A break;
2N/A case PA_SAM_TYPE_SECURID: /* Security Dynamics */
2N/A label = "Challenge for Security Dynamics mechanism";
2N/A break;
2N/A case PA_SAM_TYPE_SECURID_PREDICT: /* predictive Security Dynamics */
2N/A label = "Challenge for Security Dynamics mechanism";
2N/A break;
2N/A default:
2N/A label = "Challenge from authentication server";
2N/A break;
2N/A }
2N/A
2N/A return(label);
2N/A}
2N/A
2N/A/* this macro expands to the int,ptr necessary for "%.*s" in an sprintf */
2N/A
2N/A#define SAMDATA(kdata, str, maxsize) \
2N/A (int)((kdata.length)? \
2N/A ((((kdata.length)<=(maxsize))?(kdata.length):strlen(str))): \
2N/A strlen(str)), \
2N/A (kdata.length)? \
2N/A ((((kdata.length)<=(maxsize))?(kdata.data):(str))):(str)
2N/A
2N/A/* XXX Danger! This code is not in sync with the kerberos-password-02
2N/A draft. This draft cannot be implemented as written. This code is
2N/A compatible with earlier versions of mit krb5 and cygnus kerbnet. */
2N/A
2N/Astatic krb5_error_code
2N/Apa_sam(krb5_context context, krb5_kdc_req *request, krb5_pa_data *in_padata,
2N/A krb5_pa_data **out_padata, krb5_data *salt, krb5_data *s2kparams,
2N/A krb5_enctype *etype, krb5_keyblock *as_key, krb5_prompter_fct prompter,
2N/A void *prompter_data, krb5_gic_get_as_key_fct gak_fct, void *gak_data)
2N/A{
2N/A krb5_error_code ret;
2N/A krb5_data tmpsam;
2N/A char name[100], banner[100];
2N/A char prompt[100], response[100];
2N/A krb5_data response_data;
2N/A krb5_prompt kprompt;
2N/A krb5_prompt_type prompt_type;
2N/A krb5_data defsalt;
2N/A krb5_sam_challenge *sam_challenge = 0;
2N/A krb5_sam_response sam_response;
2N/A /* these two get encrypted and stuffed in to sam_response */
2N/A krb5_enc_sam_response_enc enc_sam_response_enc;
2N/A krb5_data * scratch;
2N/A krb5_pa_data * pa;
2N/A /* Solaris Kerberos */
2N/A krb5_enc_data * enc_data;
2N/A size_t enclen;
2N/A
2N/A if (prompter == NULL)
2N/A return EIO;
2N/A
2N/A tmpsam.length = in_padata->length;
2N/A tmpsam.data = (char *) in_padata->contents;
2N/A if ((ret = decode_krb5_sam_challenge(&tmpsam, &sam_challenge)))
2N/A return(ret);
2N/A
2N/A if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
2N/A krb5_free_sam_challenge(context, sam_challenge);
2N/A return(KRB5_SAM_UNSUPPORTED);
2N/A }
2N/A
2N/A /* If we need the password from the user (USE_SAD_AS_KEY not set), */
2N/A /* then get it here. Exception for "old" KDCs with CryptoCard */
2N/A /* support which uses the USE_SAD_AS_KEY flag, but still needs pwd */
2N/A
2N/A if (!(sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) ||
2N/A (sam_challenge->sam_type == PA_SAM_TYPE_CRYPTOCARD)) {
2N/A
2N/A /* etype has either been set by caller or by KRB5_PADATA_ETYPE_INFO */
2N/A /* message from the KDC. If it is not set, pick an enctype that we */
2N/A /* think the KDC will have for us. */
2N/A
2N/A if (*etype == 0)
2N/A *etype = ENCTYPE_DES_CBC_CRC;
2N/A
2N/A if ((ret = (gak_fct)(context, request->client, *etype, prompter,
2N/A prompter_data, salt, s2kparams, as_key,
2N/A gak_data))) {
2N/A krb5_free_sam_challenge(context, sam_challenge);
2N/A return(ret);
2N/A }
2N/A }
2N/A snprintf(name, sizeof(name), "%.*s",
2N/A SAMDATA(sam_challenge->sam_type_name, "SAM Authentication",
2N/A sizeof(name) - 1));
2N/A
2N/A snprintf(banner, sizeof(banner), "%.*s",
2N/A SAMDATA(sam_challenge->sam_challenge_label,
2N/A sam_challenge_banner(sam_challenge->sam_type),
2N/A sizeof(banner)-1));
2N/A
2N/A /* sprintf(prompt, "Challenge is [%s], %s: ", challenge, prompt); */
2N/A snprintf(prompt, sizeof(prompt), "%s%.*s%s%.*s",
2N/A sam_challenge->sam_challenge.length?"Challenge is [":"",
2N/A SAMDATA(sam_challenge->sam_challenge, "", 20),
2N/A sam_challenge->sam_challenge.length?"], ":"",
2N/A SAMDATA(sam_challenge->sam_response_prompt, "passcode", 55));
2N/A
2N/A response_data.data = response;
2N/A response_data.length = sizeof(response);
2N/A
2N/A kprompt.prompt = prompt;
2N/A kprompt.hidden = 1;
2N/A kprompt.reply = &response_data;
2N/A prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
2N/A
2N/A /* PROMPTER_INVOCATION */
2N/A krb5int_set_prompt_types(context, &prompt_type);
2N/A if ((ret = ((*prompter)(context, prompter_data, name,
2N/A banner, 1, &kprompt)))) {
2N/A krb5_free_sam_challenge(context, sam_challenge);
2N/A krb5int_set_prompt_types(context, 0);
2N/A return(ret);
2N/A }
2N/A krb5int_set_prompt_types(context, 0);
2N/A
2N/A enc_sam_response_enc.sam_nonce = sam_challenge->sam_nonce;
2N/A if (sam_challenge->sam_nonce == 0) {
2N/A if ((ret = krb5_us_timeofday(context,
2N/A &enc_sam_response_enc.sam_timestamp,
2N/A &enc_sam_response_enc.sam_usec))) {
2N/A krb5_free_sam_challenge(context,sam_challenge);
2N/A return(ret);
2N/A }
2N/A
2N/A sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp;
2N/A }
2N/A
2N/A /* XXX What if more than one flag is set? */
2N/A if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
2N/A
2N/A /* Most of this should be taken care of before we get here. We */
2N/A /* will need the user's password and as_key to encrypt the SAD */
2N/A /* and we want to preserve ordering of user prompts (first */
2N/A /* password, then SAM data) so that user's won't be confused. */
2N/A
2N/A if (as_key->length) {
2N/A krb5_free_keyblock_contents(context, as_key);
2N/A as_key->length = 0;
2N/A }
2N/A
2N/A /* generate a salt using the requested principal */
2N/A
2N/A if ((salt->length == -1 || salt->length == SALT_TYPE_AFS_LENGTH) && (salt->data == NULL)) {
2N/A if ((ret = krb5_principal2salt(context, request->client,
2N/A &defsalt))) {
2N/A krb5_free_sam_challenge(context, sam_challenge);
2N/A return(ret);
2N/A }
2N/A
2N/A salt = &defsalt;
2N/A } else {
2N/A defsalt.length = 0;
2N/A }
2N/A
2N/A /* generate a key using the supplied password */
2N/A
2N/A ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
2N/A (krb5_data *)gak_data, salt, as_key);
2N/A
2N/A if (defsalt.length)
2N/A free(defsalt.data);
2N/A
2N/A if (ret) {
2N/A krb5_free_sam_challenge(context, sam_challenge);
2N/A return(ret);
2N/A }
2N/A
2N/A /* encrypt the passcode with the key from above */
2N/A
2N/A enc_sam_response_enc.sam_sad = response_data;
2N/A } else if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
2N/A
2N/A /* process the key as password */
2N/A
2N/A if (as_key->length) {
2N/A krb5_free_keyblock_contents(context, as_key);
2N/A as_key->length = 0;
2N/A }
2N/A
2N/A#if 0
2N/A if ((salt->length == SALT_TYPE_AFS_LENGTH) && (salt->data == NULL)) {
2N/A if (ret = krb5_principal2salt(context, request->client,
2N/A &defsalt)) {
2N/A krb5_free_sam_challenge(context, sam_challenge);
2N/A return(ret);
2N/A }
2N/A
2N/A salt = &defsalt;
2N/A } else {
2N/A defsalt.length = 0;
2N/A }
2N/A#else
2N/A defsalt.length = 0;
2N/A salt = NULL;
2N/A#endif
2N/A
2N/A /* XXX As of the passwords-04 draft, no enctype is specified,
2N/A the server uses ENCTYPE_DES_CBC_MD5. In the future the
2N/A server should send a PA-SAM-ETYPE-INFO containing the enctype. */
2N/A
2N/A ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
2N/A &response_data, salt, as_key);
2N/A
2N/A if (defsalt.length)
2N/A free(defsalt.data);
2N/A
2N/A if (ret) {
2N/A krb5_free_sam_challenge(context, sam_challenge);
2N/A return(ret);
2N/A }
2N/A
2N/A enc_sam_response_enc.sam_sad.length = 0;
2N/A } else {
2N/A /* Eventually, combine SAD with long-term key to get
2N/A encryption key. */
2N/A krb5_free_sam_challenge(context, sam_challenge);
2N/A return KRB5_PREAUTH_BAD_TYPE;
2N/A }
2N/A
2N/A /* copy things from the challenge */
2N/A sam_response.sam_nonce = sam_challenge->sam_nonce;
2N/A sam_response.sam_flags = sam_challenge->sam_flags;
2N/A sam_response.sam_track_id = sam_challenge->sam_track_id;
2N/A sam_response.sam_type = sam_challenge->sam_type;
2N/A sam_response.magic = KV5M_SAM_RESPONSE;
2N/A
2N/A krb5_free_sam_challenge(context, sam_challenge);
2N/A
2N/A /* encode the encoded part of the response */
2N/A if ((ret = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc,
2N/A &scratch)))
2N/A return(ret);
2N/A
2N/A ret = krb5_encrypt_helper(context, as_key, 0, scratch,
2N/A &sam_response.sam_enc_nonce_or_ts);
2N/A
2N/A krb5_free_data(context, scratch);
2N/A
2N/A if (ret)
2N/A return(ret);
2N/A
2N/A /* sam_enc_key is reserved for future use */
2N/A sam_response.sam_enc_key.ciphertext.length = 0;
2N/A
2N/A if ((pa = malloc(sizeof(krb5_pa_data))) == NULL)
2N/A return(ENOMEM);
2N/A
2N/A if ((ret = encode_krb5_sam_response(&sam_response, &scratch))) {
2N/A free(pa);
2N/A return(ret);
2N/A }
2N/A
2N/A pa->magic = KV5M_PA_DATA;
2N/A pa->pa_type = KRB5_PADATA_SAM_RESPONSE;
2N/A pa->length = scratch->length;
2N/A pa->contents = (krb5_octet *) scratch->data;
2N/A
2N/A *out_padata = pa;
2N/A
2N/A free(scratch);
2N/A
2N/A return(0);
2N/A}
2N/A
2N/A#if APPLE_PKINIT
2N/A/*
2N/A * PKINIT. One function to generate AS-REQ, one to parse AS-REP
2N/A */
2N/A#define PKINIT_DEBUG 0
2N/A#if PKINIT_DEBUG
2N/A#define kdcPkinitDebug(args...) printf(args)
2N/A#else
2N/A#define kdcPkinitDebug(args...)
2N/A#endif
2N/A
2N/Astatic krb5_error_code
2N/Apa_pkinit_gen_req(krb5_context context,
2N/A krb5_kdc_req *request,
2N/A krb5_pa_data *in_padata,
2N/A krb5_pa_data **out_padata,
2N/A krb5_data *salt,
2N/A krb5_data *s2kparams,
2N/A krb5_enctype *etype,
2N/A krb5_keyblock *as_key,
2N/A krb5_prompter_fct prompter,
2N/A void *prompter_data,
2N/A krb5_gic_get_as_key_fct gak_fct,
2N/A void *gak_data)
2N/A{
2N/A krb5_error_code krtn;
2N/A krb5_data out_data = {0, 0, NULL};
2N/A krb5_timestamp kctime = 0;
2N/A krb5_int32 cusec = 0;
2N/A krb5_ui_4 nonce = 0;
2N/A krb5_checksum cksum;
2N/A krb5_pkinit_signing_cert_t client_cert;
2N/A krb5_data *der_req = NULL;
2N/A char *client_principal = NULL;
2N/A char *server_principal = NULL;
2N/A unsigned char nonce_bytes[4];
2N/A krb5_data nonce_data = {0, 4, (char *)nonce_bytes};
2N/A int dex;
2N/A
2N/A /*
2N/A * Trusted CA list and specific KC cert optionally obtained via
2N/A * krb5_pkinit_get_server_certs(). All are DER-encoded certs.
2N/A */
2N/A krb5_data *trusted_CAs = NULL;
2N/A krb5_ui_4 num_trusted_CAs;
2N/A krb5_data kdc_cert = {0};
2N/A
2N/A kdcPkinitDebug("pa_pkinit_gen_req\n");
2N/A
2N/A /* If we don't have a client cert, we're done */
2N/A if(request->client == NULL) {
2N/A kdcPkinitDebug("No request->client; aborting PKINIT\n");
2N/A return KRB5KDC_ERR_PREAUTH_FAILED;
2N/A }
2N/A krtn = krb5_unparse_name(context, request->client, &client_principal);
2N/A if(krtn) {
2N/A return krtn;
2N/A }
2N/A krtn = krb5_pkinit_get_client_cert(client_principal, &client_cert);
2N/A free(client_principal);
2N/A if(krtn) {
2N/A kdcPkinitDebug("No client cert; aborting PKINIT\n");
2N/A return krtn;
2N/A }
2N/A
2N/A /* optional platform-dependent CA list and KDC cert */
2N/A krtn = krb5_unparse_name(context, request->server, &server_principal);
2N/A if(krtn) {
2N/A goto cleanup;
2N/A }
2N/A krtn = krb5_pkinit_get_server_certs(client_principal, server_principal,
2N/A &trusted_CAs, &num_trusted_CAs, &kdc_cert);
2N/A if(krtn) {
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* checksum of the encoded KDC-REQ-BODY */
2N/A krtn = encode_krb5_kdc_req_body(request, &der_req);
2N/A if(krtn) {
2N/A kdcPkinitDebug("encode_krb5_kdc_req_body returned %d\n", (int)krtn);
2N/A goto cleanup;
2N/A }
2N/A krtn = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, der_req, &cksum);
2N/A if(krtn) {
2N/A goto cleanup;
2N/A }
2N/A
2N/A krtn = krb5_us_timeofday(context, &kctime, &cusec);
2N/A if(krtn) {
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* cook up a random 4-byte nonce */
2N/A krtn = krb5_c_random_make_octets(context, &nonce_data);
2N/A if(krtn) {
2N/A goto cleanup;
2N/A }
2N/A for(dex=0; dex<4; dex++) {
2N/A nonce <<= 8;
2N/A nonce |= nonce_bytes[dex];
2N/A }
2N/A
2N/A krtn = krb5int_pkinit_as_req_create(context,
2N/A kctime, cusec, nonce, &cksum,
2N/A client_cert,
2N/A trusted_CAs, num_trusted_CAs,
2N/A (kdc_cert.data ? &kdc_cert : NULL),
2N/A &out_data);
2N/A if(krtn) {
2N/A kdcPkinitDebug("error %d on pkinit_as_req_create; aborting PKINIT\n", (int)krtn);
2N/A goto cleanup;
2N/A }
2N/A *out_padata = (krb5_pa_data *)malloc(sizeof(krb5_pa_data));
2N/A if(*out_padata == NULL) {
2N/A krtn = ENOMEM;
2N/A free(out_data.data);
2N/A goto cleanup;
2N/A }
2N/A (*out_padata)->magic = KV5M_PA_DATA;
2N/A (*out_padata)->pa_type = KRB5_PADATA_PK_AS_REQ;
2N/A (*out_padata)->length = out_data.length;
2N/A (*out_padata)->contents = (krb5_octet *)out_data.data;
2N/A krtn = 0;
2N/Acleanup:
2N/A if(client_cert) {
2N/A krb5_pkinit_release_cert(client_cert);
2N/A }
2N/A if(cksum.contents) {
2N/A free(cksum.contents);
2N/A }
2N/A if (der_req) {
2N/A krb5_free_data(context, der_req);
2N/A }
2N/A if(server_principal) {
2N/A free(server_principal);
2N/A }
2N/A /* free data mallocd by krb5_pkinit_get_server_certs() */
2N/A if(trusted_CAs) {
2N/A unsigned udex;
2N/A for(udex=0; udex<num_trusted_CAs; udex++) {
2N/A free(trusted_CAs[udex].data);
2N/A }
2N/A free(trusted_CAs);
2N/A }
2N/A if(kdc_cert.data) {
2N/A free(kdc_cert.data);
2N/A }
2N/A return krtn;
2N/A
2N/A}
2N/A
2N/A/* If and only if the realm is that of a Local KDC, accept
2N/A * the KDC certificate as valid if its hash matches the
2N/A * realm.
2N/A */
2N/Astatic krb5_boolean
2N/Alocal_kdc_cert_match(krb5_context context,
2N/A krb5_data *signer_cert,
2N/A krb5_principal client)
2N/A{
2N/A static const char lkdcprefix[] = "LKDC:SHA1.";
2N/A krb5_boolean match = FALSE;
2N/A size_t cert_hash_len;
2N/A char *cert_hash;
2N/A const char *realm_hash;
2N/A size_t realm_hash_len;
2N/A
2N/A if (client->realm.length <= sizeof(lkdcprefix) ||
2N/A 0 != memcmp(lkdcprefix, client->realm.data, sizeof(lkdcprefix)-1))
2N/A return match;
2N/A realm_hash = &client->realm.data[sizeof(lkdcprefix)-1];
2N/A realm_hash_len = client->realm.length - sizeof(lkdcprefix) + 1;
2N/A kdcPkinitDebug("checking realm versus certificate hash\n");
2N/A if (NULL != (cert_hash = krb5_pkinit_cert_hash_str(signer_cert))) {
2N/A kdcPkinitDebug("hash = %s\n", cert_hash);
2N/A cert_hash_len = strlen(cert_hash);
2N/A if (cert_hash_len == realm_hash_len &&
2N/A 0 == memcmp(cert_hash, realm_hash, cert_hash_len))
2N/A match = TRUE;
2N/A free(cert_hash);
2N/A }
2N/A kdcPkinitDebug("result: %s\n", match ? "matches" : "does not match");
2N/A return match;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Apa_pkinit_parse_rep(krb5_context context,
2N/A krb5_kdc_req *request,
2N/A krb5_pa_data *in_padata,
2N/A krb5_pa_data **out_padata,
2N/A krb5_data *salt,
2N/A krb5_data *s2kparams,
2N/A krb5_enctype *etype,
2N/A krb5_keyblock *as_key,
2N/A krb5_prompter_fct prompter,
2N/A void *prompter_data,
2N/A krb5_gic_get_as_key_fct gak_fct,
2N/A void *gak_data)
2N/A{
2N/A krb5int_cert_sig_status sig_status = (krb5int_cert_sig_status)-999;
2N/A krb5_error_code krtn;
2N/A krb5_data asRep;
2N/A krb5_keyblock local_key = {0};
2N/A krb5_pkinit_signing_cert_t client_cert;
2N/A char *princ_name = NULL;
2N/A krb5_checksum as_req_checksum_rcd = {0}; /* received checksum */
2N/A krb5_checksum as_req_checksum_gen = {0}; /* calculated checksum */
2N/A krb5_data *encoded_as_req = NULL;
2N/A krb5_data signer_cert = {0};
2N/A
2N/A *out_padata = NULL;
2N/A kdcPkinitDebug("pa_pkinit_parse_rep\n");
2N/A if((in_padata == NULL) || (in_padata->length== 0)) {
2N/A kdcPkinitDebug("pa_pkinit_parse_rep: no in_padata\n");
2N/A return KRB5KDC_ERR_PREAUTH_FAILED;
2N/A }
2N/A
2N/A /* If we don't have a client cert, we're done */
2N/A if(request->client == NULL) {
2N/A kdcPkinitDebug("No request->client; aborting PKINIT\n");
2N/A return KRB5KDC_ERR_PREAUTH_FAILED;
2N/A }
2N/A krtn = krb5_unparse_name(context, request->client, &princ_name);
2N/A if(krtn) {
2N/A return krtn;
2N/A }
2N/A krtn = krb5_pkinit_get_client_cert(princ_name, &client_cert);
2N/A free(princ_name);
2N/A if(krtn) {
2N/A kdcPkinitDebug("No client cert; aborting PKINIT\n");
2N/A return krtn;
2N/A }
2N/A
2N/A memset(&local_key, 0, sizeof(local_key));
2N/A asRep.data = (char *)in_padata->contents;
2N/A asRep.length = in_padata->length;
2N/A krtn = krb5int_pkinit_as_rep_parse(context, &asRep, client_cert,
2N/A &local_key, &as_req_checksum_rcd, &sig_status,
2N/A &signer_cert, NULL, NULL);
2N/A if(krtn) {
2N/A kdcPkinitDebug("pkinit_as_rep_parse returned %d\n", (int)krtn);
2N/A return krtn;
2N/A }
2N/A switch(sig_status) {
2N/A case pki_cs_good:
2N/A break;
2N/A case pki_cs_unknown_root:
2N/A if (local_kdc_cert_match(context, &signer_cert, request->client))
2N/A break;
2N/A /* FALLTHROUGH */
2N/A default:
2N/A kdcPkinitDebug("pa_pkinit_parse_rep: bad cert/sig status %d\n",
2N/A (int)sig_status);
2N/A krtn = KRB5KDC_ERR_PREAUTH_FAILED;
2N/A goto error_out;
2N/A }
2N/A
2N/A /* calculate checksum of incoming AS-REQ using the decryption key
2N/A * we just got from the ReplyKeyPack */
2N/A krtn = encode_krb5_as_req(request, &encoded_as_req);
2N/A if(krtn) {
2N/A goto error_out;
2N/A }
2N/A krtn = krb5_c_make_checksum(context, context->kdc_req_sumtype,
2N/A &local_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
2N/A encoded_as_req, &as_req_checksum_gen);
2N/A if(krtn) {
2N/A goto error_out;
2N/A }
2N/A if((as_req_checksum_gen.length != as_req_checksum_rcd.length) ||
2N/A memcmp(as_req_checksum_gen.contents,
2N/A as_req_checksum_rcd.contents,
2N/A as_req_checksum_gen.length)) {
2N/A kdcPkinitDebug("pa_pkinit_parse_rep: checksum miscompare\n");
2N/A krtn = KRB5KDC_ERR_PREAUTH_FAILED;
2N/A goto error_out;
2N/A }
2N/A
2N/A /* We have the key; transfer to caller */
2N/A if (as_key->length) {
2N/A krb5_free_keyblock_contents(context, as_key);
2N/A }
2N/A *as_key = local_key;
2N/A
2N/A#if PKINIT_DEBUG
2N/A fprintf(stderr, "pa_pkinit_parse_rep: SUCCESS\n");
2N/A fprintf(stderr, "enctype %d keylen %d keydata %02x %02x %02x %02x...\n",
2N/A (int)as_key->enctype, (int)as_key->length,
2N/A as_key->contents[0], as_key->contents[1],
2N/A as_key->contents[2], as_key->contents[3]);
2N/A#endif
2N/A
2N/A krtn = 0;
2N/A
2N/Aerror_out:
2N/A if (signer_cert.data) {
2N/A free(signer_cert.data);
2N/A }
2N/A if(as_req_checksum_rcd.contents) {
2N/A free(as_req_checksum_rcd.contents);
2N/A }
2N/A if(as_req_checksum_gen.contents) {
2N/A free(as_req_checksum_gen.contents);
2N/A }
2N/A if(encoded_as_req) {
2N/A krb5_free_data(context, encoded_as_req);
2N/A }
2N/A if(krtn && (local_key.contents != NULL)) {
2N/A krb5_free_keyblock_contents(context, &local_key);
2N/A }
2N/A return krtn;
2N/A}
2N/A#endif /* APPLE_PKINIT */
2N/A
2N/Astatic krb5_error_code
2N/Apa_sam_2(krb5_context context, krb5_kdc_req *request, krb5_pa_data *in_padata,
2N/A krb5_pa_data **out_padata, krb5_data *salt, krb5_data *s2kparams,
2N/A krb5_enctype *etype, krb5_keyblock *as_key,
2N/A krb5_prompter_fct prompter, void *prompter_data,
2N/A krb5_gic_get_as_key_fct gak_fct, void *gak_data)
2N/A{
2N/A krb5_error_code retval;
2N/A krb5_sam_challenge_2 *sc2 = NULL;
2N/A krb5_sam_challenge_2_body *sc2b = NULL;
2N/A krb5_data tmp_data;
2N/A krb5_data response_data;
2N/A char name[100], banner[100], prompt[100], response[100];
2N/A krb5_prompt kprompt;
2N/A krb5_prompt_type prompt_type;
2N/A krb5_data defsalt;
2N/A krb5_checksum **cksum;
2N/A krb5_data *scratch = NULL;
2N/A krb5_boolean valid_cksum = 0;
2N/A krb5_enc_sam_response_enc_2 enc_sam_response_enc_2;
2N/A krb5_sam_response_2 sr2;
2N/A size_t ciph_len;
2N/A krb5_pa_data *sam_padata;
2N/A
2N/A if (prompter == NULL)
2N/A return KRB5_LIBOS_CANTREADPWD;
2N/A
2N/A tmp_data.length = in_padata->length;
2N/A tmp_data.data = (char *)in_padata->contents;
2N/A
2N/A if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2)))
2N/A return(retval);
2N/A
2N/A retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b);
2N/A
2N/A if (retval) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A return(retval);
2N/A }
2N/A
2N/A if (!sc2->sam_cksum || ! *sc2->sam_cksum) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A return(KRB5_SAM_NO_CHECKSUM);
2N/A }
2N/A
2N/A if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A return(KRB5_SAM_UNSUPPORTED);
2N/A }
2N/A
2N/A if (!krb5_c_valid_enctype(sc2b->sam_etype)) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A return(KRB5_SAM_INVALID_ETYPE);
2N/A }
2N/A
2N/A /* All of the above error checks are KDC-specific, that is, they */
2N/A /* assume a failure in the KDC reply. By returning anything other */
2N/A /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED, */
2N/A /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will */
2N/A /* most likely go on to try the AS_REQ against master KDC */
2N/A
2N/A if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
2N/A /* We will need the password to obtain the key used for */
2N/A /* the checksum, and encryption of the sam_response. */
2N/A /* Go ahead and get it now, preserving the ordering of */
2N/A /* prompts for the user. */
2N/A
2N/A retval = (gak_fct)(context, request->client,
2N/A sc2b->sam_etype, prompter,
2N/A prompter_data, salt, s2kparams, as_key, gak_data);
2N/A if (retval) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A return(retval);
2N/A }
2N/A }
2N/A
2N/A snprintf(name, sizeof(name), "%.*s",
2N/A SAMDATA(sc2b->sam_type_name, "SAM Authentication",
2N/A sizeof(name) - 1));
2N/A
2N/A snprintf(banner, sizeof(banner), "%.*s",
2N/A SAMDATA(sc2b->sam_challenge_label,
2N/A sam_challenge_banner(sc2b->sam_type),
2N/A sizeof(banner)-1));
2N/A
2N/A snprintf(prompt, sizeof(prompt), "%s%.*s%s%.*s",
2N/A sc2b->sam_challenge.length?"Challenge is [":"",
2N/A SAMDATA(sc2b->sam_challenge, "", 20),
2N/A sc2b->sam_challenge.length?"], ":"",
2N/A SAMDATA(sc2b->sam_response_prompt, "passcode", 55));
2N/A
2N/A response_data.data = response;
2N/A response_data.length = sizeof(response);
2N/A kprompt.prompt = prompt;
2N/A kprompt.hidden = 1;
2N/A kprompt.reply = &response_data;
2N/A
2N/A prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
2N/A krb5int_set_prompt_types(context, &prompt_type);
2N/A
2N/A if ((retval = ((*prompter)(context, prompter_data, name,
2N/A banner, 1, &kprompt)))) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A krb5int_set_prompt_types(context, 0);
2N/A return(retval);
2N/A }
2N/A
2N/A krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL);
2N/A
2N/A /* Generate salt used by string_to_key() */
2N/A if ((salt->length == -1) && (salt->data == NULL)) {
2N/A if ((retval =
2N/A krb5_principal2salt(context, request->client, &defsalt))) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A return(retval);
2N/A }
2N/A salt = &defsalt;
2N/A } else {
2N/A defsalt.length = 0;
2N/A }
2N/A
2N/A /* Get encryption key to be used for checksum and sam_response */
2N/A if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
2N/A /* as_key = string_to_key(password) */
2N/A
2N/A if (as_key->length) {
2N/A krb5_free_keyblock_contents(context, as_key);
2N/A as_key->length = 0;
2N/A }
2N/A
2N/A /* generate a key using the supplied password */
2N/A retval = krb5_c_string_to_key(context, sc2b->sam_etype,
2N/A (krb5_data *)gak_data, salt, as_key);
2N/A
2N/A if (retval) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A if (defsalt.length) free(defsalt.data);
2N/A return(retval);
2N/A }
2N/A
2N/A if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) {
2N/A /* as_key = combine_key (as_key, string_to_key(SAD)) */
2N/A krb5_keyblock tmp_kb;
2N/A
2N/A retval = krb5_c_string_to_key(context, sc2b->sam_etype,
2N/A &response_data, salt, &tmp_kb);
2N/A
2N/A if (retval) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A if (defsalt.length) free(defsalt.data);
2N/A return(retval);
2N/A }
2N/A
2N/A /* This should be a call to the crypto library some day */
2N/A /* key types should already match the sam_etype */
2N/A retval = krb5int_c_combine_keys(context, as_key, &tmp_kb, as_key);
2N/A
2N/A if (retval) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A if (defsalt.length) free(defsalt.data);
2N/A return(retval);
2N/A }
2N/A krb5_free_keyblock_contents(context, &tmp_kb);
2N/A }
2N/A
2N/A if (defsalt.length)
2N/A free(defsalt.data);
2N/A
2N/A } else {
2N/A /* as_key = string_to_key(SAD) */
2N/A
2N/A if (as_key->length) {
2N/A krb5_free_keyblock_contents(context, as_key);
2N/A as_key->length = 0;
2N/A }
2N/A
2N/A /* generate a key using the supplied password */
2N/A retval = krb5_c_string_to_key(context, sc2b->sam_etype,
2N/A &response_data, salt, as_key);
2N/A
2N/A if (defsalt.length)
2N/A free(defsalt.data);
2N/A
2N/A if (retval) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A return(retval);
2N/A }
2N/A }
2N/A
2N/A /* Now we have a key, verify the checksum on the sam_challenge */
2N/A
2N/A cksum = sc2->sam_cksum;
2N/A
2N/A /*
2N/A * Solaris Kerberos
2N/A *
2N/A * If we do not have a keyed checksum, no need to verify
2N/A */
2N/A for (; *cksum; cksum++) {
2N/A if (!krb5_c_is_keyed_cksum((*cksum)->checksum_type))
2N/A continue;
2N/A /* Check this cksum */
2N/A retval = krb5_c_verify_checksum(context, as_key,
2N/A KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM,
2N/A &sc2->sam_challenge_2_body,
2N/A *cksum, &valid_cksum);
2N/A if (retval) {
2N/A krb5_free_data(context, scratch);
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A return(retval);
2N/A }
2N/A if (valid_cksum)
2N/A break;
2N/A }
2N/A
2N/A if (!valid_cksum) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A /*
2N/A * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications
2N/A * can interpret that as "password incorrect", which is probably
2N/A * the best error we can return in this situation.
2N/A */
2N/A return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
2N/A }
2N/A
2N/A /* fill in enc_sam_response_enc_2 */
2N/A enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2;
2N/A enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce;
2N/A if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
2N/A enc_sam_response_enc_2.sam_sad = response_data;
2N/A } else {
2N/A enc_sam_response_enc_2.sam_sad.data = NULL;
2N/A enc_sam_response_enc_2.sam_sad.length = 0;
2N/A }
2N/A
2N/A /* encode and encrypt enc_sam_response_enc_2 with as_key */
2N/A retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2,
2N/A &scratch);
2N/A if (retval) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A return(retval);
2N/A }
2N/A
2N/A /* Fill in sam_response_2 */
2N/A memset(&sr2, 0, sizeof(sr2));
2N/A sr2.sam_type = sc2b->sam_type;
2N/A sr2.sam_flags = sc2b->sam_flags;
2N/A sr2.sam_track_id = sc2b->sam_track_id;
2N/A sr2.sam_nonce = sc2b->sam_nonce;
2N/A
2N/A /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded */
2N/A /* enc_sam_response_enc_2 from above */
2N/A
2N/A retval = krb5_c_encrypt_length(context, as_key->enctype, scratch->length,
2N/A &ciph_len);
2N/A if (retval) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A krb5_free_data(context, scratch);
2N/A return(retval);
2N/A }
2N/A sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len;
2N/A
2N/A sr2.sam_enc_nonce_or_sad.ciphertext.data =
2N/A (char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length);
2N/A
2N/A if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A krb5_free_data(context, scratch);
2N/A return(ENOMEM);
2N/A }
2N/A
2N/A retval = krb5_c_encrypt(context, as_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE,
2N/A NULL, scratch, &sr2.sam_enc_nonce_or_sad);
2N/A if (retval) {
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A krb5_free_data(context, scratch);
2N/A krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);
2N/A return(retval);
2N/A }
2N/A krb5_free_data(context, scratch);
2N/A scratch = NULL;
2N/A
2N/A /* Encode the sam_response_2 */
2N/A retval = encode_krb5_sam_response_2(&sr2, &scratch);
2N/A krb5_free_sam_challenge_2(context, sc2);
2N/A krb5_free_sam_challenge_2_body(context, sc2b);
2N/A krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);
2N/A
2N/A if (retval) {
2N/A return (retval);
2N/A }
2N/A
2N/A /* Almost there, just need to make padata ! */
2N/A sam_padata = malloc(sizeof(krb5_pa_data));
2N/A if (sam_padata == NULL) {
2N/A krb5_free_data(context, scratch);
2N/A return(ENOMEM);
2N/A }
2N/A
2N/A sam_padata->magic = KV5M_PA_DATA;
2N/A sam_padata->pa_type = KRB5_PADATA_SAM_RESPONSE_2;
2N/A sam_padata->length = scratch->length;
2N/A sam_padata->contents = (krb5_octet *) scratch->data;
2N/A free(scratch);
2N/A
2N/A *out_padata = sam_padata;
2N/A
2N/A return(0);
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Apa_s4u_x509_user(krb5_context context, krb5_kdc_req *request,
2N/A krb5_pa_data *in_padata, krb5_pa_data **out_padata,
2N/A krb5_data *salt, krb5_data *s2kparams, krb5_enctype *etype,
2N/A krb5_keyblock *as_key, krb5_prompter_fct prompter,
2N/A void *prompter_data, krb5_gic_get_as_key_fct gak_fct,
2N/A void *gak_data)
2N/A{
2N/A krb5_s4u_userid *userid = (krb5_s4u_userid *)gak_data; /* XXX private contract */
2N/A krb5_pa_data *s4u_padata;
2N/A krb5_error_code code;
2N/A krb5_principal client;
2N/A
2N/A *out_padata = NULL;
2N/A
2N/A if (userid == NULL)
2N/A return EINVAL;
2N/A
2N/A code = krb5_copy_principal(context, request->client, &client);
2N/A if (code != 0)
2N/A return code;
2N/A
2N/A if (userid->user != NULL)
2N/A krb5_free_principal(context, userid->user);
2N/A userid->user = client;
2N/A
2N/A if (userid->subject_cert.length != 0) {
2N/A s4u_padata = malloc(sizeof(*s4u_padata));
2N/A if (s4u_padata == NULL)
2N/A return ENOMEM;
2N/A
2N/A s4u_padata->magic = KV5M_PA_DATA;
2N/A s4u_padata->pa_type = KRB5_PADATA_S4U_X509_USER;
2N/A s4u_padata->contents = malloc(userid->subject_cert.length);
2N/A if (s4u_padata->contents == NULL) {
2N/A free(s4u_padata);
2N/A return ENOMEM;
2N/A }
2N/A memcpy(s4u_padata->contents, userid->subject_cert.data, userid->subject_cert.length);
2N/A s4u_padata->length = userid->subject_cert.length;
2N/A
2N/A *out_padata = s4u_padata;
2N/A }
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A/* FIXME - order significant? */
2N/Astatic const pa_types_t pa_types[] = {
2N/A {
2N/A KRB5_PADATA_PW_SALT,
2N/A pa_salt,
2N/A PA_INFO,
2N/A },
2N/A {
2N/A KRB5_PADATA_AFS3_SALT,
2N/A pa_salt,
2N/A PA_INFO,
2N/A },
2N/A#if APPLE_PKINIT
2N/A {
2N/A KRB5_PADATA_PK_AS_REQ,
2N/A pa_pkinit_gen_req,
2N/A PA_INFO,
2N/A },
2N/A {
2N/A KRB5_PADATA_PK_AS_REP,
2N/A pa_pkinit_parse_rep,
2N/A PA_REAL,
2N/A },
2N/A#endif /* APPLE_PKINIT */
2N/A {
2N/A KRB5_PADATA_ENC_TIMESTAMP,
2N/A pa_enc_timestamp,
2N/A PA_REAL,
2N/A },
2N/A {
2N/A KRB5_PADATA_SAM_CHALLENGE_2,
2N/A pa_sam_2,
2N/A PA_REAL,
2N/A },
2N/A {
2N/A KRB5_PADATA_SAM_CHALLENGE,
2N/A pa_sam,
2N/A PA_REAL,
2N/A },
2N/A {
2N/A KRB5_PADATA_FX_COOKIE,
2N/A pa_fx_cookie,
2N/A PA_INFO,
2N/A },
2N/A {
2N/A KRB5_PADATA_S4U_X509_USER,
2N/A pa_s4u_x509_user,
2N/A PA_INFO,
2N/A },
2N/A {
2N/A -1,
2N/A NULL,
2N/A 0,
2N/A },
2N/A};
2N/A
2N/A/*
2N/A * If one of the modules can adjust its AS_REQ data using the contents of the
2N/A * err_reply, return 0. If it's the sort of correction which requires that we
2N/A * ask the user another question, we let the calling application deal with it.
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_do_preauth_tryagain(krb5_context kcontext,
2N/A krb5_kdc_req *request,
2N/A krb5_data *encoded_request_body,
2N/A krb5_data *encoded_previous_request,
2N/A krb5_pa_data **padata,
2N/A krb5_pa_data ***return_padata,
2N/A krb5_error *err_reply,
2N/A krb5_data *salt, krb5_data *s2kparams,
2N/A krb5_enctype *etype,
2N/A krb5_keyblock *as_key,
2N/A krb5_prompter_fct prompter, void *prompter_data,
2N/A krb5_gic_get_as_key_fct gak_fct, void *gak_data,
2N/A krb5_preauth_client_rock *get_data_rock,
2N/A krb5_gic_opt_ext *opte)
2N/A{
2N/A krb5_error_code ret;
2N/A krb5_pa_data **out_padata;
2N/A krb5_preauth_context *context;
2N/A struct _krb5_preauth_context_module *module;
2N/A int i, j;
2N/A int out_pa_list_size = 0;
2N/A
2N/A ret = KRB5KRB_ERR_GENERIC;
2N/A if (kcontext->preauth_context == NULL) {
2N/A return KRB5KRB_ERR_GENERIC;
2N/A }
2N/A context = kcontext->preauth_context;
2N/A if (context == NULL) {
2N/A return KRB5KRB_ERR_GENERIC;
2N/A }
2N/A
2N/A for (i = 0; padata[i] != NULL && padata[i]->pa_type != 0; i++) {
2N/A out_padata = NULL;
2N/A for (j = 0; j < context->n_modules; j++) {
2N/A module = &context->modules[j];
2N/A if (module->pa_type != padata[i]->pa_type) {
2N/A continue;
2N/A }
2N/A if (module->client_tryagain == NULL) {
2N/A continue;
2N/A }
2N/A if ((*module->client_tryagain)(kcontext,
2N/A module->plugin_context,
2N/A *module->request_context_pp,
2N/A (krb5_get_init_creds_opt *)opte,
2N/A client_data_proc,
2N/A get_data_rock,
2N/A request,
2N/A encoded_request_body,
2N/A encoded_previous_request,
2N/A padata[i],
2N/A err_reply,
2N/A prompter, prompter_data,
2N/A gak_fct, gak_data, salt, s2kparams,
2N/A as_key,
2N/A &out_padata) == 0) {
2N/A if (out_padata != NULL) {
2N/A int k;
2N/A for (k = 0; out_padata[k] != NULL; k++);
2N/A grow_pa_list(return_padata, &out_pa_list_size,
2N/A out_padata, k);
2N/A free(out_padata);
2N/A return 0;
2N/A }
2N/A }
2N/A }
2N/A }
2N/A return ret;
2N/A}
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_do_preauth(krb5_context context,
2N/A krb5_kdc_req *request,
2N/A krb5_data *encoded_request_body,
2N/A krb5_data *encoded_previous_request,
2N/A krb5_pa_data **in_padata, krb5_pa_data ***out_padata,
2N/A krb5_data *salt, krb5_data *s2kparams,
2N/A krb5_enctype *etype,
2N/A krb5_keyblock *as_key,
2N/A krb5_prompter_fct prompter, void *prompter_data,
2N/A krb5_gic_get_as_key_fct gak_fct, void *gak_data,
2N/A krb5_preauth_client_rock *get_data_rock,
2N/A krb5_gic_opt_ext *opte)
2N/A{
2N/A unsigned int h;
2N/A int i, j, out_pa_list_size;
2N/A int seen_etype_info2 = 0;
2N/A krb5_pa_data *out_pa = NULL, **out_pa_list = NULL;
2N/A krb5_data scratch;
2N/A krb5_etype_info etype_info = NULL;
2N/A krb5_error_code ret;
2N/A static const int paorder[] = { PA_INFO, PA_REAL };
2N/A int realdone;
2N/A
2N/A if (in_padata == NULL) {
2N/A *out_padata = NULL;
2N/A return(0);
2N/A }
2N/A
2N/A#ifdef DEBUG
2N/A if (salt && salt->data && salt->length > 0) {
2N/A fprintf (stderr, "salt len=%d", (int) salt->length);
2N/A if ((int) salt->length > 0)
2N/A fprintf (stderr, " '%.*s'", salt->length, salt->data);
2N/A fprintf (stderr, "; preauth data types:");
2N/A for (i = 0; in_padata[i]; i++) {
2N/A fprintf (stderr, " %d", in_padata[i]->pa_type);
2N/A }
2N/A fprintf (stderr, "\n");
2N/A }
2N/A#endif
2N/A
2N/A out_pa_list = NULL;
2N/A out_pa_list_size = 0;
2N/A
2N/A /* first do all the informational preauths, then the first real one */
2N/A
2N/A for (h=0; h<(sizeof(paorder)/sizeof(paorder[0])); h++) {
2N/A realdone = 0;
2N/A for (i=0; in_padata[i] && !realdone; i++) {
2N/A int k, l, etype_found, valid_etype_found;
2N/A /*
2N/A * This is really gross, but is necessary to prevent
2N/A * lossage when talking to a 1.0.x KDC, which returns an
2N/A * erroneous PA-PW-SALT when it returns a KRB-ERROR
2N/A * requiring additional preauth.
2N/A */
2N/A switch (in_padata[i]->pa_type) {
2N/A case KRB5_PADATA_ETYPE_INFO:
2N/A case KRB5_PADATA_ETYPE_INFO2:
2N/A {
2N/A krb5_preauthtype pa_type = in_padata[i]->pa_type;
2N/A if (etype_info) {
2N/A if (seen_etype_info2 || pa_type != KRB5_PADATA_ETYPE_INFO2)
2N/A continue;
2N/A if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
2N/A krb5_free_etype_info( context, etype_info);
2N/A etype_info = NULL;
2N/A }
2N/A }
2N/A
2N/A scratch.length = in_padata[i]->length;
2N/A scratch.data = (char *) in_padata[i]->contents;
2N/A if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
2N/A seen_etype_info2++;
2N/A ret = decode_krb5_etype_info2(&scratch, &etype_info);
2N/A }
2N/A else ret = decode_krb5_etype_info(&scratch, &etype_info);
2N/A if (ret) {
2N/A ret = 0; /*Ignore error and etype_info element*/
2N/A if (etype_info)
2N/A krb5_free_etype_info( context, etype_info);
2N/A etype_info = NULL;
2N/A continue;
2N/A }
2N/A if (etype_info[0] == NULL) {
2N/A krb5_free_etype_info(context, etype_info);
2N/A etype_info = NULL;
2N/A break;
2N/A }
2N/A /*
2N/A * Select first etype in our request which is also in
2N/A * etype-info (preferring client request ktype order).
2N/A */
2N/A for (etype_found = 0, valid_etype_found = 0, k = 0;
2N/A !etype_found && k < request->nktypes; k++) {
2N/A for (l = 0; etype_info[l]; l++) {
2N/A if (etype_info[l]->etype == request->ktype[k]) {
2N/A etype_found++;
2N/A break;
2N/A }
2N/A /* check if program has support for this etype for more
2N/A * precise error reporting.
2N/A */
2N/A if (krb5_c_valid_enctype(etype_info[l]->etype))
2N/A valid_etype_found++;
2N/A }
2N/A }
2N/A if (!etype_found) {
2N/A if (valid_etype_found) {
2N/A /* supported enctype but not requested */
2N/A ret = KRB5_CONFIG_ETYPE_NOSUPP;
2N/A goto cleanup;
2N/A }
2N/A else {
2N/A /* unsupported enctype */
2N/A ret = KRB5_PROG_ETYPE_NOSUPP;
2N/A goto cleanup;
2N/A }
2N/A
2N/A }
2N/A scratch.data = (char *) etype_info[l]->salt;
2N/A scratch.length = etype_info[l]->length;
2N/A krb5_free_data_contents(context, salt);
2N/A if (scratch.length == KRB5_ETYPE_NO_SALT)
2N/A salt->data = NULL;
2N/A else
2N/A if ((ret = krb5int_copy_data_contents( context, &scratch, salt)) != 0)
2N/A goto cleanup;
2N/A *etype = etype_info[l]->etype;
2N/A krb5_free_data_contents(context, s2kparams);
2N/A if ((ret = krb5int_copy_data_contents(context,
2N/A &etype_info[l]->s2kparams,
2N/A s2kparams)) != 0)
2N/A goto cleanup;
2N/A#ifdef DEBUG
2N/A for (j = 0; etype_info[j]; j++) {
2N/A krb5_etype_info_entry *e = etype_info[j];
2N/A fprintf (stderr, "etype info %d: etype %d salt len=%d",
2N/A j, e->etype, e->length);
2N/A if (e->length > 0 && e->length != KRB5_ETYPE_NO_SALT)
2N/A fprintf (stderr, " '%.*s'", e->length, e->salt);
2N/A fprintf (stderr, "\n");
2N/A }
2N/A#endif
2N/A break;
2N/A }
2N/A case KRB5_PADATA_PW_SALT:
2N/A case KRB5_PADATA_AFS3_SALT:
2N/A if (etype_info)
2N/A continue;
2N/A break;
2N/A default:
2N/A ;
2N/A }
2N/A /* Try the internally-provided preauth type list. */
2N/A if (!realdone) for (j=0; pa_types[j].type >= 0; j++) {
2N/A if ((in_padata[i]->pa_type == pa_types[j].type) &&
2N/A (pa_types[j].flags & paorder[h])) {
2N/A#ifdef DEBUG
2N/A fprintf (stderr, "calling internal function for pa_type "
2N/A "%d, flag %d\n", pa_types[j].type, paorder[h]);
2N/A#endif
2N/A out_pa = NULL;
2N/A
2N/A if ((ret = ((*pa_types[j].fct)(context, request,
2N/A in_padata[i], &out_pa,
2N/A salt, s2kparams, etype, as_key,
2N/A prompter, prompter_data,
2N/A gak_fct, gak_data)))) {
2N/A if (paorder[h] == PA_INFO) {
2N/A#ifdef DEBUG
2N/A fprintf (stderr,
2N/A "internal function for type %d, flag %d "
2N/A "failed with err %d\n",
2N/A in_padata[i]->pa_type, paorder[h], ret);
2N/A#endif
2N/A ret = 0;
2N/A continue; /* PA_INFO type failed, ignore */
2N/A }
2N/A
2N/A goto cleanup;
2N/A }
2N/A
2N/A ret = grow_pa_list(&out_pa_list, &out_pa_list_size,
2N/A &out_pa, 1);
2N/A if (ret != 0) {
2N/A goto cleanup;
2N/A }
2N/A if (paorder[h] == PA_REAL)
2N/A realdone = 1;
2N/A }
2N/A }
2N/A
2N/A /* Try to use plugins now. */
2N/A if (!realdone) {
2N/A krb5_init_preauth_context(context);
2N/A if (context->preauth_context != NULL) {
2N/A int module_ret = 0, module_flags;
2N/A#ifdef DEBUG
2N/A fprintf (stderr, "trying modules for pa_type %d, flag %d\n",
2N/A in_padata[i]->pa_type, paorder[h]);
2N/A#endif
2N/A ret = run_preauth_plugins(context,
2N/A paorder[h],
2N/A request,
2N/A encoded_request_body,
2N/A encoded_previous_request,
2N/A in_padata[i],
2N/A prompter,
2N/A prompter_data,
2N/A gak_fct,
2N/A salt, s2kparams,
2N/A gak_data,
2N/A get_data_rock,
2N/A as_key,
2N/A &out_pa_list,
2N/A &out_pa_list_size,
2N/A &module_ret,
2N/A &module_flags,
2N/A opte);
2N/A if (ret == 0) {
2N/A if (module_ret == 0) {
2N/A if (paorder[h] == PA_REAL) {
2N/A realdone = 1;
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A *out_padata = out_pa_list;
2N/A if (etype_info)
2N/A krb5_free_etype_info(context, etype_info);
2N/A
2N/A return(0);
2N/Acleanup:
2N/A if (out_pa_list) {
2N/A out_pa_list[out_pa_list_size++] = NULL;
2N/A krb5_free_pa_data(context, out_pa_list);
2N/A }
2N/A if (etype_info)
2N/A krb5_free_etype_info(context, etype_info);
2N/A return (ret);
2N/A}