diff options
author | Sam Hartman <hartmans@mit.edu> | 2006-10-03 19:07:17 +0000 |
---|---|---|
committer | Sam Hartman <hartmans@mit.edu> | 2006-10-03 19:07:17 +0000 |
commit | 63a8ab15aa5ee116b26a50c073fe8ee33e147cbd (patch) | |
tree | 40296b89921ea7edb3117c4d38c4132494d4ce06 /src/plugins | |
parent | 7f7a4fff296db90d36c39fb01dd35b61bdd6a2b0 (diff) | |
download | krb5-63a8ab15aa5ee116b26a50c073fe8ee33e147cbd.zip krb5-63a8ab15aa5ee116b26a50c073fe8ee33e147cbd.tar.gz krb5-63a8ab15aa5ee116b26a50c073fe8ee33e147cbd.tar.bz2 |
Preauthentication Plugin Framework
Patch from Nalin Dahyabhai at Redhat to implement a preauthentication
framework based on the plugin architecture. Currently. the API is
considered internal and the header is not installed.
See src/include/krb5/preauth_plugin.h for the interface.
ticket: new
Tags: enhancement
Status: open
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@18641 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/preauth/cksum_body/Makefile.in | 42 | ||||
-rw-r--r-- | src/plugins/preauth/cksum_body/cksum_body.exports | 1 | ||||
-rw-r--r-- | src/plugins/preauth/cksum_body/configure.in | 14 | ||||
-rw-r--r-- | src/plugins/preauth/cksum_body/src/cksum_body.c | 489 | ||||
-rw-r--r-- | src/plugins/preauth/wpse/Makefile.in | 42 | ||||
-rw-r--r-- | src/plugins/preauth/wpse/configure.in | 14 | ||||
-rw-r--r-- | src/plugins/preauth/wpse/src/wpse.c | 337 | ||||
-rw-r--r-- | src/plugins/preauth/wpse/wpse.exports | 1 |
8 files changed, 940 insertions, 0 deletions
diff --git a/src/plugins/preauth/cksum_body/Makefile.in b/src/plugins/preauth/cksum_body/Makefile.in new file mode 100644 index 0000000..ddac24d --- /dev/null +++ b/src/plugins/preauth/cksum_body/Makefile.in @@ -0,0 +1,42 @@ +thisconfigdir=. +myfulldir=plugins/preauth/cksum_body +mydir=. +BUILDTOP=$(REL)..$(S)..$(S).. +KRB5_RUN_ENV = @KRB5_RUN_ENV@ +KRB5_CONFIG_SETUP = KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; export KRB5_CONFIG ; +PROG_LIBPATH=-L$(TOPLIBD) +PROG_RPATH=$(KRB5_LIBDIR) +MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR) +DEFS=@DEFS@ + +LOCALINCLUDES = -I../../../include/krb5 + +LIBBASE=cksum_body +LIBMAJOR=0 +LIBMINOR=0 +SO_EXT=.so +RELDIR=../plugins/preauth/cksum_body +# Depends on libk5crypto and libkrb5 +SHLIB_EXPDEPS = \ + $(TOPLIBD)/libk5crypto$(SHLIBEXT) \ + $(TOPLIBD)/libkrb5$(SHLIBEXT) +SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto $(SUPPORT_LIB) $(LIBS) + +SHLIB_DIRS=-L$(TOPLIBD) +SHLIB_RDIRS=$(KRB5_LIBDIR) +STOBJLISTS=OBJS.ST +STLIBOBJS=src/cksum_body.o + +SRCS= $(srcdir)/src/cksum_body.c + +all-unix:: $(LIBBASE)$(SO_EXT) +install-unix:: install-libs +clean-unix:: clean-libs clean-libobjs + +clean:: + $(RM) lib$(LIBBASE)$(SO_EXT) + +@libnover_frag@ +@libobj_frag@ + +# +++ Dependency line eater +++ diff --git a/src/plugins/preauth/cksum_body/cksum_body.exports b/src/plugins/preauth/cksum_body/cksum_body.exports new file mode 100644 index 0000000..ff5e3f1 --- /dev/null +++ b/src/plugins/preauth/cksum_body/cksum_body.exports @@ -0,0 +1 @@ +preauthentication0 diff --git a/src/plugins/preauth/cksum_body/configure.in b/src/plugins/preauth/cksum_body/configure.in new file mode 100644 index 0000000..868dfd5 --- /dev/null +++ b/src/plugins/preauth/cksum_body/configure.in @@ -0,0 +1,14 @@ +K5_AC_INIT(configure.in) +enable_shared=yes +build_dynobj=yes +CONFIG_RULES + +AC_CHECK_HEADERS(errno.h string.h) + +KRB5_RUN_FLAGS +dnl The following is for check... +KRB5_BUILD_PROGRAM +KRB5_BUILD_LIBOBJS +KRB5_BUILD_LIBRARY_WITH_DEPS +AC_CONFIG_HEADERS(config.h) +V5_AC_OUTPUT_MAKEFILE diff --git a/src/plugins/preauth/cksum_body/src/cksum_body.c b/src/plugins/preauth/cksum_body/src/cksum_body.c new file mode 100644 index 0000000..ff4cc84 --- /dev/null +++ b/src/plugins/preauth/cksum_body/src/cksum_body.c @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2006 Red Hat, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Red Hat, Inc., nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Checksum the request body with the user's long-term key. + * + * The e-data from the KDC is a list of network-byte-order 32-bit integers + * listing key types which the KDC has for the user. + * + * The client uses one of these key types to generate a checksum over the body + * of the request, and includes the checksum in the AS-REQ as preauthentication + * data. + * + * The AS-REP carries no preauthentication data for this scheme. + */ + +#ident "$Id$" + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include <arpa/inet.h> +#include <stdio.h> + +#include <krb5/krb5.h> +#include <krb5/preauth_plugin.h> + +/* This is not a standardized value. It's defined here only to make it easier + * to change in this module. */ +#define KRB5_PADATA_CKSUM_BODY_REQ 130 + +struct server_stats{ + int successes, failures; +}; + +static int +client_get_flags(krb5_context kcontext, krb5_preauthtype pa_type) +{ + return PA_REAL; +} + +static krb5_error_code +client_process(krb5_context kcontext, + void *client_module_context, + void **client_request_context, + krb5_kdc_req *request, + krb5_data *encoded_request_body, + krb5_data *encoded_previous_request, + krb5_pa_data *pa_data, + krb5_prompter_fct prompter, + void *prompter_data, + preauth_get_as_key_proc gak_fct, + krb5_data *salt, krb5_data *s2kparams, + void *gak_data, + krb5_keyblock *as_key, + krb5_pa_data **out_pa_data) +{ + krb5_pa_data *send_pa; + krb5_checksum checksum; + krb5_enctype enctype; + krb5_cksumtype *cksumtypes; + krb5_error_code status; + krb5_int32 cksumtype, *enctypes; + unsigned int i, n_enctypes, cksumtype_count; + + memset(&checksum, 0, sizeof(checksum)); + + /* Get the user's long-term key if we haven't asked for it yet. Try + * all of the encryption types which the server supports. */ + if (as_key->length == 0) { + if ((pa_data != NULL) && (pa_data->length >= 4)) { +#ifdef DEBUG + fprintf(stderr, "%d bytes of preauth data.\n", pa_data->length); +#endif + n_enctypes = pa_data->length / 4; + enctypes = (krb5_int32*) pa_data->contents; + } else { + n_enctypes = request->nktypes; + } + for (i = 0; i < n_enctypes; i++) { + if ((pa_data != NULL) && (pa_data->length >= 4)) { + memcpy(&enctype, pa_data->contents + 4 * i, 4); + enctype = ntohl(enctype); + } else { + enctype = request->ktype[i]; + } +#ifdef DEBUG + fprintf(stderr, "Asking for AS key (type = %d).\n", enctype); +#endif + status = (*gak_fct)(kcontext, request->client, enctype, + prompter, prompter_data, + salt, s2kparams, as_key, gak_data); + if (status == 0) + break; + } + if (status != 0) + return status; + } +#ifdef DEBUG + fprintf(stderr, "Got AS key (type = %d).\n", as_key->enctype); +#endif + + /* Determine an appropriate checksum type for this key. */ + cksumtype_count = 0; + cksumtypes = NULL; + status = krb5_c_keyed_checksum_types(kcontext, as_key->enctype, + &cksumtype_count, &cksumtypes); + if (status != 0) + return status; + + /* Generate the checksum. */ + for (i = 0; i < cksumtype_count; i++) { + status = krb5_c_make_checksum(kcontext, cksumtypes[i], as_key, + KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, + encoded_request_body, + &checksum); + if (status == 0) { +#ifdef DEBUG + fprintf(stderr, "Made checksum (type = %d, %d bytes).\n", + checksum.checksum_type, encoded_request_body->length); +#endif + break; + } + } + cksumtype = htonl(cksumtypes[i]); + krb5_free_cksumtypes(kcontext, cksumtypes); + if (status != 0) { + if (checksum.length > 0) + krb5_free_checksum_contents(kcontext, &checksum); + return status; + } + + /* Allocate the preauth data structure. */ + send_pa = malloc(sizeof(krb5_pa_data)); + if (send_pa == NULL) { + krb5_free_checksum_contents(kcontext, &checksum); + return ENOMEM; + } + send_pa->pa_type = KRB5_PADATA_CKSUM_BODY_REQ; + send_pa->length = 4 + checksum.length; + send_pa->contents = malloc(4 + checksum.length); + if (send_pa->contents == NULL) { + krb5_free_checksum_contents(kcontext, &checksum); + free(send_pa); + return ENOMEM; + } + + /* Store the checksum. */ + memcpy(send_pa->contents, &cksumtype, 4); + memcpy(send_pa->contents + 4, checksum.contents, checksum.length); + *out_pa_data = send_pa; + + /* Clean up. */ + krb5_free_checksum_contents(kcontext, &checksum); + + return 0; +} + +/* Initialize and tear down the server-side module, and do stat tracking. */ +static krb5_error_code +server_init(krb5_context kcontext, krb5_preauthtype pa_type, + void **module_context) +{ + struct server_stats *stats; + stats = malloc(sizeof(struct server_stats)); + if (stats == NULL) + return ENOMEM; + stats->successes = 0; + stats->failures = 0; + *module_context = stats; + return 0; +} +static void +server_fini(krb5_context kcontext, krb5_preauthtype pa_type, + void *module_context) +{ + struct server_stats *stats; + stats = module_context; + if (stats != NULL) { +#ifdef DEBUG + fprintf(stderr, "Total %d clients failed pa_type %d, %d succeeded.\n", + stats->failures, pa_type, stats->successes); +#endif + free(stats); + } +} + +/* Obtain and return any preauthentication data (which is destined for the + * client) which matches type data->pa_type. */ +static krb5_error_code +server_get_edata(krb5_context kcontext, + krb5_kdc_req *request, + struct _krb5_db_entry_new *client, + struct _krb5_db_entry_new *server, + preauth_get_entry_data_proc server_get_entry_data, + void *pa_module_context, + krb5_pa_data *data) +{ + krb5_data *key_data; + krb5_keyblock *keys, *key; + krb5_int32 *enctypes, enctype; + int i; + + /* Retrieve the client's keys. */ + key_data = NULL; + if ((*server_get_entry_data)(kcontext, request, client, + krb5plugin_preauth_keys, &key_data) != 0) { +#ifdef DEBUG + fprintf(stderr, "Error retrieving client keys.\n"); +#endif + return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; + } + + /* Count which types of keys we've got, freeing the contents, which we + * don't need at this point. */ + keys = (krb5_keyblock *) key_data->data; + key = NULL; + for (i = 0; keys[i].enctype != 0; i++) + krb5_free_keyblock_contents(kcontext, &keys[i]); + + /* Return the list of encryption types. */ + enctypes = malloc(i * 4); + if (enctypes == NULL) { + krb5_free_data(kcontext, key_data); + return ENOMEM; + } +#ifdef DEBUG + fprintf(stderr, "Supported enctypes = {"); +#endif + for (i = 0; keys[i].enctype != 0; i++) { +#ifdef DEBUG + fprintf(stderr, "%s%d", (i > 0) ? ", " : "", keys[i].enctype); +#endif + enctype = htonl(keys[i].enctype); + memcpy(&enctypes[i], &enctype, 4); + } +#ifdef DEBUG + fprintf(stderr, "}.\n"); +#endif + data->pa_type = KRB5_PADATA_CKSUM_BODY_REQ; + data->length = (i * 4); + data->contents = (unsigned char *) enctypes; + krb5_free_data(kcontext, key_data); + return 0; +} + +/* Verify a request from a client. */ +static krb5_error_code +server_verify(krb5_context kcontext, + struct _krb5_db_entry_new *client, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_enc_tkt_part *enc_tkt_reply, + krb5_pa_data *data, + preauth_get_entry_data_proc server_get_entry_data, + void *pa_module_context, + void **pa_request_context) +{ + krb5_int32 cksumtype; + krb5_checksum checksum; + krb5_boolean valid; + krb5_data *key_data, *req_body; + krb5_keyblock *keys, *key; + size_t length; + int i; + unsigned int j, cksumtypes_count; + krb5_cksumtype *cksumtypes; + krb5_error_code status; + struct server_stats *stats; + + stats = pa_module_context; + + /* Verify the preauth data. Start with the checksum type. */ + if (data->length < 4) { + stats->failures++; + return KRB5KDC_ERR_PREAUTH_FAILED; + } + memcpy(&cksumtype, data->contents, 4); + memset(&checksum, 0, sizeof(checksum)); + checksum.checksum_type = ntohl(cksumtype); + + /* Verify that the amount of data we have left is what we expect. */ + if (krb5_c_checksum_length(kcontext, checksum.checksum_type, + &length) != 0) { +#ifdef DEBUG + fprintf(stderr, "Error determining checksum size (type = %d). " + "Is it supported?\n", checksum.checksum_type); +#endif + stats->failures++; + return KRB5KDC_ERR_SUMTYPE_NOSUPP; + } + if (data->length - 4 != length) { +#ifdef DEBUG + fprintf(stderr, "Checksum size doesn't match client packet size.\n"); +#endif + stats->failures++; + return KRB5KDC_ERR_PREAUTH_FAILED; + } + checksum.length = length; + + /* Pull up the client's keys. */ + key_data = NULL; + if ((*server_get_entry_data)(kcontext, request, client, + krb5plugin_preauth_keys, &key_data) != 0) { +#ifdef DEBUG + fprintf(stderr, "Error retrieving client keys.\n"); +#endif + stats->failures++; + return KRB5KDC_ERR_PREAUTH_FAILED; + } + + /* Find the key which would have been used to generate the checksum. */ + keys = (krb5_keyblock *) key_data->data; + key = NULL; + for (i = 0; keys[i].enctype != 0; i++) { + key = &keys[i]; + cksumtypes_count = 0; + cksumtypes = NULL; + if (krb5_c_keyed_checksum_types(kcontext, key->enctype, + &cksumtypes_count, &cksumtypes) != 0) + continue; + for (j = 0; j < cksumtypes_count; j++) { + if (cksumtypes[j] == checksum.checksum_type) + break; + } + if (cksumtypes != NULL) + krb5_free_cksumtypes(kcontext, cksumtypes); + if (j < cksumtypes_count) { +#ifdef DEBUG + fprintf(stderr, "Found checksum key.\n"); +#endif + break; + } + } + if ((key == NULL) || (key->enctype == 0)) { + for (i = 0; keys[i].enctype != 0; i++) + krb5_free_keyblock_contents(kcontext, &keys[i]); + krb5_free_data(kcontext, key_data); + stats->failures++; + return KRB5KDC_ERR_SUMTYPE_NOSUPP; + } + + /* Save a copy of the key. */ + if (krb5_copy_keyblock(kcontext, &keys[i], &key) != 0) { + for (i = 0; keys[i].enctype != 0; i++) + krb5_free_keyblock_contents(kcontext, &keys[i]); + krb5_free_data(kcontext, key_data); + stats->failures++; + return KRB5KDC_ERR_SUMTYPE_NOSUPP; + } + for (i = 0; keys[i].enctype != 0; i++) + krb5_free_keyblock_contents(kcontext, &keys[i]); + krb5_free_data(kcontext, key_data); + + /* Rebuild a copy of the client's request-body. If we were serious + * about doing this with any chance of working interoperability, we'd + * extract the structure directly from the req_pkt structure. This + * will probably work if it's us on both ends, though. */ + req_body = NULL; + if ((*server_get_entry_data)(kcontext, request, client, + krb5plugin_preauth_request_body, + &req_body) != 0) { + krb5_free_keyblock(kcontext, key); + stats->failures++; + return KRB5KDC_ERR_PREAUTH_FAILED; + } + +#ifdef DEBUG + fprintf(stderr, "AS key type %d, checksum type %d, %d bytes.\n", + key->enctype, checksum.checksum_type, req_body->length); +#endif + + /* Verify the checksum itself. */ + checksum.contents = data->contents + 4; + valid = FALSE; + status = krb5_c_verify_checksum(kcontext, key, + KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, + req_body, &checksum, &valid); + + /* Clean up. */ + krb5_free_data(kcontext, req_body); + krb5_free_keyblock(kcontext, key); + + /* Evaluate our results. */ + if ((status != 0) || (!valid)) { +#ifdef DEBUG + if (status != 0) { + fprintf(stderr, "Error in checksum verification.\n"); + } else { + fprintf(stderr, "Checksum mismatch.\n"); + } +#endif + stats->failures++; + return KRB5KDC_ERR_PREAUTH_FAILED; + } + + /* Note that preauthentication succeeded. */ + enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; + stats->successes++; + return 0; +} + +/* Create the response for a client. */ +static krb5_error_code +server_return(krb5_context kcontext, + krb5_pa_data *padata, + struct _krb5_db_entry_new *client, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_kdc_rep *reply, + struct _krb5_key_data *client_key, + krb5_keyblock *encrypting_key, + krb5_pa_data **send_pa, + preauth_get_entry_data_proc server_get_entry_data, + void *pa_module_context, + void **pa_request_context) +{ + /* We don't need to send data back on the return trip. */ + *send_pa = NULL; + return 0; +} + +static int +server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type) +{ + return PA_SUFFICIENT; +} + +static krb5_preauthtype supported_client_pa_types[] = { + KRB5_PADATA_CKSUM_BODY_REQ, 0, +}; +static krb5_preauthtype supported_server_pa_types[] = { + KRB5_PADATA_CKSUM_BODY_REQ, 0, +}; + +struct krb5plugin_preauth_ftable_v0 preauthentication0 = { + "cksum_body", + &supported_client_pa_types[0], + &supported_server_pa_types[0], + NULL, + NULL, + NULL, + client_get_flags, + NULL, + client_process, + NULL, + server_init, + server_fini, + server_get_flags, + server_get_edata, + server_verify, + server_return, + NULL +}; diff --git a/src/plugins/preauth/wpse/Makefile.in b/src/plugins/preauth/wpse/Makefile.in new file mode 100644 index 0000000..6b18a7c --- /dev/null +++ b/src/plugins/preauth/wpse/Makefile.in @@ -0,0 +1,42 @@ +thisconfigdir=. +myfulldir=plugins/preauth/wpse +mydir=. +BUILDTOP=$(REL)..$(S)..$(S).. +KRB5_RUN_ENV = @KRB5_RUN_ENV@ +KRB5_CONFIG_SETUP = KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; export KRB5_CONFIG ; +PROG_LIBPATH=-L$(TOPLIBD) +PROG_RPATH=$(KRB5_LIBDIR) +MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR) +DEFS=@DEFS@ + +LOCALINCLUDES = -I../../../include/krb5 + +LIBBASE=wpse +LIBMAJOR=0 +LIBMINOR=0 +SO_EXT=.so +RELDIR=../plugins/preauth/wpse +# Depends on libk5crypto and libkrb5 +SHLIB_EXPDEPS = \ + $(TOPLIBD)/libk5crypto$(SHLIBEXT) \ + $(TOPLIBD)/libkrb5$(SHLIBEXT) +SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto $(SUPPORT_LIB) $(LIBS) + +SHLIB_DIRS=-L$(TOPLIBD) +SHLIB_RDIRS=$(KRB5_LIBDIR) +STOBJLISTS=OBJS.ST +STLIBOBJS=src/wpse.o + +SRCS= $(srcdir)/src/wpse.c + +all-unix:: $(LIBBASE)$(SO_EXT) +install-unix:: install-libs +clean-unix:: clean-libs clean-libobjs + +clean:: + $(RM) lib$(LIBBASE)$(SO_EXT) + +@libnover_frag@ +@libobj_frag@ + +# +++ Dependency line eater +++ diff --git a/src/plugins/preauth/wpse/configure.in b/src/plugins/preauth/wpse/configure.in new file mode 100644 index 0000000..868dfd5 --- /dev/null +++ b/src/plugins/preauth/wpse/configure.in @@ -0,0 +1,14 @@ +K5_AC_INIT(configure.in) +enable_shared=yes +build_dynobj=yes +CONFIG_RULES + +AC_CHECK_HEADERS(errno.h string.h) + +KRB5_RUN_FLAGS +dnl The following is for check... +KRB5_BUILD_PROGRAM +KRB5_BUILD_LIBOBJS +KRB5_BUILD_LIBRARY_WITH_DEPS +AC_CONFIG_HEADERS(config.h) +V5_AC_OUTPUT_MAKEFILE diff --git a/src/plugins/preauth/wpse/src/wpse.c b/src/plugins/preauth/wpse/src/wpse.c new file mode 100644 index 0000000..579c63b --- /dev/null +++ b/src/plugins/preauth/wpse/src/wpse.c @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2006 Red Hat, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Red Hat, Inc., nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Worst. Preauthentication. Scheme. Ever. */ + +#ident "$Id$" + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include <arpa/inet.h> +#include <stdio.h> + +#include <krb5/krb5.h> +#include <krb5/preauth_plugin.h> + +/* This is not a standardized value. It's defined here only to make it easier + * to change in this module. */ +#define KRB5_PADATA_WPSE_REQ 131 + +static int +client_get_flags(krb5_context kcontext, krb5_preauthtype pa_type) +{ + return PA_REAL; +} + +static krb5_error_code +client_init(krb5_context kcontext, krb5_preauthtype pa_type, void **ctx) +{ + int *mctx; + + mctx = malloc(sizeof(int)); + if (mctx == NULL) + return ENOMEM; + *mctx = 0; + *ctx = mctx; + return 0; +} + +static void +client_fini(krb5_context kcontext, krb5_preauthtype pa_type, void *ctx) +{ + int *mctx; + + mctx = ctx; + if (mctx) { +#ifdef DEBUG + fprintf(stderr, "wpse module called total of %d times\n", *mctx); +#endif + free(mctx); + } +} + +static krb5_error_code +client_process(krb5_context kcontext, + void *module_context, + void **request_context, + krb5_kdc_req *request, + krb5_data *encoded_request_body, + krb5_data *encoded_previous_request, + krb5_pa_data *pa_data, + krb5_prompter_fct prompter, + void *prompter_data, + preauth_get_as_key_proc gak_fct, + krb5_data *salt, krb5_data *s2kparams, + void *gak_data, + krb5_keyblock *as_key, + krb5_pa_data **out_pa_data) +{ + krb5_pa_data *send_pa; + krb5_int32 nnonce, enctype; + krb5_keyblock *kb; + krb5_error_code status; + int *mctx; + +#ifdef DEBUG + fprintf(stderr, "%d bytes of preauthentication data (type %d)\n", + pa_data->length, pa_data->pa_type); +#endif + + mctx = module_context; + if (mctx) { + (*mctx)++; + } + + if (pa_data->length == 0) { + /* Create preauth data. */ + send_pa = malloc(sizeof(krb5_pa_data)); + if (send_pa == NULL) + return ENOMEM; + send_pa->pa_type = KRB5_PADATA_WPSE_REQ; + send_pa->length = 4; + send_pa->contents = malloc(4); + if (send_pa->contents == NULL) { + free(send_pa); + return ENOMEM; + } + /* Store the preauth data. */ + nnonce = htonl(request->nonce); + memcpy(send_pa->contents, &nnonce, 4); + *out_pa_data = send_pa; + /* Allocate a context. Useful for verifying that we do in fact + * do per-request cleanup. */ + if (*request_context == NULL) + *request_context = malloc(4); + } else { + /* A reply from the KDC. Conventionally this would be + * indicated by a different preauthentication type, but this + * mechanism/implementation doesn't do that. */ + if (pa_data->length > 4) { + memcpy(&enctype, pa_data->contents, 4); + kb = NULL; + status = krb5_init_keyblock(kcontext, ntohl(enctype), + pa_data->length - 4, &kb); + if (status != 0) + return status; + memcpy(kb->contents, pa_data->contents + 4, pa_data->length - 4); +#ifdef DEBUG + fprintf(stderr, "Recovered key type=%d, length=%d.\n", + kb->enctype, kb->length); +#endif + status = krb5_copy_keyblock_contents(kcontext, kb, as_key); + krb5_free_keyblock(kcontext, kb); + return status; + } + return KRB5KRB_ERR_GENERIC; + } + return 0; +} + +static void +client_cleanup(krb5_context kcontext, void *module_context, + void **request_context) +{ + if (*request_context != NULL) { + free(*request_context); + *request_context = NULL; + } + return; +} + +/* Free state. */ +static krb5_error_code +server_free_pa_request_context(krb5_context kcontext, void *module_context, + void **request_context) +{ + if (*request_context != NULL) { + free(*request_context); + *request_context = NULL; + } + return 0; +} + +/* Obtain and return any preauthentication data (which is destined for the + * client) which matches type data->pa_type. */ +static krb5_error_code +server_get_edata(krb5_context kcontext, + krb5_kdc_req *request, + struct _krb5_db_entry_new *client, + struct _krb5_db_entry_new *server, + preauth_get_entry_data_proc server_get_entry_data, + void *pa_module_context, + krb5_pa_data *data) +{ + /* Return zero bytes of data. */ + data->length = 0; + data->contents = NULL; + return 0; +} + +/* Verify a request from a client. */ +static krb5_error_code +server_verify(krb5_context kcontext, + struct _krb5_db_entry_new *client, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_enc_tkt_part *enc_tkt_reply, + krb5_pa_data *data, + preauth_get_entry_data_proc server_get_entry_data, + void *pa_module_context, + void **pa_request_context) +{ + krb5_int32 nnonce; + /* Verify the preauth data. */ + if (data->length != 4) + return KRB5KDC_ERR_PREAUTH_FAILED; + memcpy(&nnonce, data->contents, 4); + nnonce = ntohl(nnonce); + if (memcmp(&nnonce, &request->nonce, 4) != 0) + return KRB5KDC_ERR_PREAUTH_FAILED; + /* Note that preauthentication succeeded. */ + enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; + enc_tkt_reply->flags |= TKT_FLG_HW_AUTH; + /* Allocate a context. Useful for verifying that we do in fact do + * per-request cleanup. */ + if (*pa_request_context == NULL) + *pa_request_context = malloc(4); + return 0; +} + +/* Create the response for a client. */ +static krb5_error_code +server_return(krb5_context kcontext, + krb5_pa_data *padata, + struct _krb5_db_entry_new *client, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_kdc_rep *reply, + struct _krb5_key_data *client_key, + krb5_keyblock *encrypting_key, + krb5_pa_data **send_pa, + preauth_get_entry_data_proc server_get_entry_data, + void *pa_module_context, + void **pa_request_context) +{ + /* This module does a couple of dumb things. It tags its reply with + * the same type as the initial challenge (expecting the client to sort + * out whether there's anything useful in there). Oh, and it replaces + * the AS reply key with one which is sent in the clear. */ + krb5_keyblock *kb; + krb5_int32 enctype; + int i; + + *send_pa = NULL; + + /* We'll want a key with the first supported enctype. */ + for (i = 0; i < request->nktypes; i++) { + kb = NULL; + if (krb5_init_keyblock(kcontext, request->ktype[i], 0, &kb) == 0) { + break; + } + } + if (i >= request->nktypes) { + /* No matching cipher type found. */ + return 0; + } + + /* Randomize a key and save it for the client. */ + if (krb5_c_make_random_key(kcontext, request->ktype[i], kb) != 0) { + krb5_free_keyblock(kcontext, kb); + return 0; + } +#ifdef DEBUG + fprintf(stderr, "Generated random key, type=%d, length=%d.\n", + kb->enctype, kb->length); +#endif + + *send_pa = malloc(sizeof(krb5_pa_data)); + if (*send_pa == NULL) { + krb5_free_keyblock(kcontext, kb); + return ENOMEM; + } + (*send_pa)->pa_type = KRB5_PADATA_WPSE_REQ; + (*send_pa)->length = 4 + kb->length; + (*send_pa)->contents = malloc(4 + kb->length); + if ((*send_pa)->contents == NULL) { + free(*send_pa); + *send_pa = NULL; + krb5_free_keyblock(kcontext, kb); + return ENOMEM; + } + + /* Store the preauth data. */ + enctype = htonl(kb->enctype); + memcpy((*send_pa)->contents, &enctype, 4); + memcpy((*send_pa)->contents + 4, kb->contents, kb->length); + krb5_copy_keyblock_contents(kcontext, kb, encrypting_key); + + /* Clean up. */ + krb5_free_keyblock(kcontext, kb); + + return 0; +} + +static int +server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type) +{ + return PA_HARDWARE | PA_REPLACES_KEY; +} + +static krb5_preauthtype supported_client_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0}; +static krb5_preauthtype supported_server_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0}; + +struct krb5plugin_preauth_ftable_v0 preauthentication0 = { + "wpse", + &supported_client_pa_types[0], + &supported_server_pa_types[0], + NULL, + client_init, + client_fini, + client_get_flags, + client_cleanup, + client_process, + NULL, + NULL, + NULL, + server_get_flags, + server_get_edata, + server_verify, + server_return, + server_free_pa_request_context, +}; diff --git a/src/plugins/preauth/wpse/wpse.exports b/src/plugins/preauth/wpse/wpse.exports new file mode 100644 index 0000000..ff5e3f1 --- /dev/null +++ b/src/plugins/preauth/wpse/wpse.exports @@ -0,0 +1 @@ +preauthentication0 |