summaryrefslogtreecommitdiffstats
path: root/src/plugins/preauth/wpse/wpse_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/preauth/wpse/wpse_main.c')
-rw-r--r--src/plugins/preauth/wpse/wpse_main.c374
1 files changed, 374 insertions, 0 deletions
diff --git a/src/plugins/preauth/wpse/wpse_main.c b/src/plugins/preauth/wpse/wpse_main.c
new file mode 100644
index 000000000..e7d7b6d55
--- /dev/null
+++ b/src/plugins/preauth/wpse/wpse_main.c
@@ -0,0 +1,374 @@
+/*
+ * 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, void **ctx)
+{
+ int *pctx;
+
+ pctx = malloc(sizeof(int));
+ if (pctx == NULL)
+ return ENOMEM;
+ *pctx = 0;
+ *ctx = pctx;
+ return 0;
+}
+
+static void
+client_fini(krb5_context kcontext, void *ctx)
+{
+ int *pctx;
+
+ pctx = ctx;
+ if (pctx) {
+#ifdef DEBUG
+ fprintf(stderr, "wpse module called total of %d times\n", *pctx);
+#endif
+ free(pctx);
+ }
+}
+
+static krb5_error_code
+client_process(krb5_context kcontext,
+ void *plugin_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,
+ void *gak_data,
+ krb5_data *salt, krb5_data *s2kparams,
+ 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 *pctx;
+
+#ifdef DEBUG
+ fprintf(stderr, "%d bytes of preauthentication data (type %d)\n",
+ pa_data->length, pa_data->pa_type);
+#endif
+
+ pctx = plugin_context;
+ if (pctx) {
+ (*pctx)++;
+ }
+
+ 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;
+ } 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;
+}
+
+#define WPSE_MAGIC 0x77707365
+typedef struct _wpse_req_ctx
+{
+ int magic;
+ int value;
+} wpse_req_ctx;
+
+static void
+client_req_init(krb5_context kcontext, void *plugin_context, void **req_context_p)
+{
+ wpse_req_ctx *ctx;
+
+ *req_context_p = NULL;
+
+ /* Allocate a request context. Useful for verifying that we do in fact
+ * do per-request cleanup. */
+ ctx = (wpse_req_ctx *) malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return;
+ ctx->magic = WPSE_MAGIC;
+ ctx->value = 0xc0dec0de;
+
+ *req_context_p = ctx;
+}
+
+static void
+client_req_cleanup(krb5_context kcontext, void *plugin_context, void *req_context)
+{
+ wpse_req_ctx *ctx = (wpse_req_ctx *)req_context;
+
+ if (ctx) {
+#ifdef DEBUG
+ fprintf(stderr, "client_req_cleanup: req_ctx at %p has magic %x and value %x\n",
+ ctx, ctx->magic, ctx->value);
+#endif
+ if (ctx->magic != WPSE_MAGIC) {
+#ifdef DEBUG
+ fprintf(stderr, "client_req_cleanup: req_context at %p has bad magic value %x\n",
+ ctx, ctx->magic);
+#endif
+ return;
+ }
+ free(ctx);
+ }
+ return;
+}
+
+/* Free state. */
+static krb5_error_code
+server_free_pa_request_context(krb5_context kcontext, void *plugin_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_client_ftable_v0 preauthentication_client_0 = {
+ "wpse", /* name */
+ &supported_client_pa_types[0], /* pa_type_list */
+ NULL, /* enctype_list */
+ client_init, /* plugin init function */
+ client_fini, /* plugin fini function */
+ client_get_flags, /* get flags function */
+ client_req_init, /* request init function */
+ client_req_cleanup, /* request fini function */
+ client_process, /* process function */
+ NULL, /* try_again function */
+};
+
+struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = {
+ "wpse",
+ &supported_server_pa_types[0],
+ NULL,
+ NULL,
+ server_get_flags,
+ server_get_edata,
+ server_verify,
+ server_return,
+ server_free_pa_request_context,
+};