Make kdcpreauth edata method respond via callback
authorGreg Hudson <ghudson@mit.edu>
Sat, 15 Oct 2011 15:06:37 +0000 (15:06 +0000)
committerGreg Hudson <ghudson@mit.edu>
Sat, 15 Oct 2011 15:06:37 +0000 (15:06 +0000)
From npmccallum@redhat.com with changes.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25348 dc483132-0cff-0310-8789-dd5450dbe970

src/include/krb5/preauth_plugin.h
src/kdc/kdc_preauth.c
src/kdc/kdc_preauth_ec.c
src/kdc/kdc_preauth_encts.c
src/plugins/preauth/cksum_body/cksum_body_main.c
src/plugins/preauth/pkinit/pkinit_srv.c
src/plugins/preauth/wpse/wpse_main.c

index e618edd97a14db6fc8ee1c437049d2dd89a165c4..7d5dc99b1b724400153b55bb847e84fcea17999d 100644 (file)
@@ -418,21 +418,31 @@ typedef int
 (*krb5_kdcpreauth_flags_fn)(krb5_context context, krb5_preauthtype pa_type);
 
 /*
- * Optional: fill in pa_out->length and pa_out->contents with data to send to
- * the client as part of the "you need to use preauthentication" error.  If
- * this function returns non-zero, the padata type will not be included in the
- * list; if this function is not provided or returns zero without changing
- * pa_out, the padata type will be included in the list with an empty value.
- * This function not allowed to create a context because we have no guarantee
- * that the client will ever call again (or that it will hit this server if it
- * does), in which case a context might otherwise hang around forever.
+ * Responder for krb5_kdcpreauth_edata_fn.  If invoked with a non-zero code, pa
+ * will be ignored and the padata type will not be included in the hint list.
+ * If invoked with a zero code and a null pa value, the padata type will be
+ * included in the list with an empty value.  If invoked with a zero code and a
+ * non-null pa value, pa will be included in the hint list and will later be
+ * freed by the KDC.
  */
-typedef krb5_error_code
+typedef void
+(*krb5_kdcpreauth_edata_respond_fn)(void *arg, krb5_error_code code,
+                                    krb5_pa_data *pa);
+
+/*
+ * Optional: provide pa_data to send to the client as part of the "you need to
+ * use preauthentication" error.  This function is not allowed to create a
+ * modreq object because we have no guarantee that the client will ever make a
+ * follow-up request, or that it will hit this KDC if it does.
+ */
+typedef void
 (*krb5_kdcpreauth_edata_fn)(krb5_context context, krb5_kdc_req *request,
                             krb5_kdcpreauth_callbacks cb,
                             krb5_kdcpreauth_rock rock,
                             krb5_kdcpreauth_moddata moddata,
-                            krb5_pa_data *pa_out);
+                            krb5_preauthtype pa_type,
+                            krb5_kdcpreauth_edata_respond_fn respond,
+                            void *arg);
 
 /*
  * Responder for krb5_kdcpreauth_verify_fn.  Invoke with the arg parameter
index 13b51e3299248b24c9c9f2d8a4622efe9d58601a..93fddd7753a40930b4049c3478ce3da38a7b73a3 100644 (file)
@@ -104,15 +104,17 @@ typedef struct preauth_system_st {
     krb5_kdcpreauth_free_modreq_fn free_modreq;
 } preauth_system;
 
-static krb5_error_code
+static void
 get_etype_info(krb5_context context, krb5_kdc_req *request,
                krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
-               krb5_kdcpreauth_moddata moddata, krb5_pa_data *data);
+               krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
+               krb5_kdcpreauth_edata_respond_fn respond, void *arg);
 
-static krb5_error_code
+static void
 get_etype_info2(krb5_context context, krb5_kdc_req *request,
                 krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
-                krb5_kdcpreauth_moddata moddata, krb5_pa_data *pa_data);
+                krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
+                krb5_kdcpreauth_edata_respond_fn respond, void *arg);
 
 static krb5_error_code
 etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
@@ -744,67 +746,132 @@ const char *missing_required_preauth(krb5_db_entry *client,
     return 0;
 }
 
+struct hint_state {
+    kdc_hint_respond_fn respond;
+    void *arg;
+    kdc_realm_t *realm;
+
+    krb5_kdcpreauth_rock rock;
+    krb5_kdc_req *request;
+    krb5_pa_data ***e_data_out;
+
+    int hw_only;
+    preauth_system *ap;
+    krb5_pa_data **pa_data, **pa_cur;
+    krb5_preauthtype pa_type;
+};
+
+static void
+hint_list_finish(struct hint_state *state, krb5_error_code code)
+{
+    kdc_hint_respond_fn oldrespond = state->respond;
+    void *oldarg = state->arg;
+
+    if (!code) {
+        if (state->pa_data[0] == 0) {
+            krb5_klog_syslog(LOG_INFO,
+                             _("%spreauth required but hint list is empty"),
+                             state->hw_only ? "hw" : "");
+        }
+        /* If we fail to get the cookie it is probably still reasonable to
+         * continue with the response. */
+        kdc_preauth_get_cookie(state->rock->rstate, state->pa_cur);
+
+        *state->e_data_out = state->pa_data;
+        state->pa_data = NULL;
+    }
+
+    krb5_free_pa_data(kdc_context, state->pa_data);
+    free(state);
+    (*oldrespond)(oldarg);
+}
+
+static void
+hint_list_next(struct hint_state *arg);
+
+static void
+finish_get_edata(void *arg, krb5_error_code code, krb5_pa_data *pa)
+{
+    struct hint_state *state = arg;
+
+    kdc_active_realm = state->realm;
+    if (code == 0) {
+        if (pa == NULL) {
+            /* Include an empty value of the current type. */
+            pa = calloc(1, sizeof(*pa));
+            pa->magic = KV5M_PA_DATA;
+            pa->pa_type = state->pa_type;
+        }
+        *state->pa_cur++ = pa;
+    }
+
+    state->ap++;
+    hint_list_next(state);
+}
+
+static void
+hint_list_next(struct hint_state *state)
+{
+    preauth_system *ap = state->ap;
+
+    if (ap->type == -1) {
+        hint_list_finish(state, 0);
+        return;
+    }
+
+    if (state->hw_only && !(ap->flags & PA_HARDWARE))
+        goto next;
+    if (ap->flags & PA_PSEUDO)
+        goto next;
+
+    state->pa_type = ap->type;
+    if (ap->get_edata) {
+        ap->get_edata(kdc_context, state->request, &callbacks, state->rock,
+                      ap->moddata, ap->type, finish_get_edata, state);
+    } else
+        finish_get_edata(state, ap->type, NULL);
+    return;
+
+next:
+    state->ap++;
+    hint_list_next(state);
+}
+
 void
 get_preauth_hint_list(krb5_kdc_req *request, krb5_kdcpreauth_rock rock,
                       krb5_pa_data ***e_data_out, kdc_hint_respond_fn respond,
                       void *arg)
 {
-    int hw_only;
-    preauth_system *ap;
-    krb5_pa_data **pa_data, **pa;
-    krb5_error_code retval;
+    struct hint_state *state;
 
     *e_data_out = NULL;
 
-    hw_only = isflagset(rock->client->attributes, KRB5_KDB_REQUIRES_HW_AUTH);
-    /* Allocate two extra entries for the cookie and the terminator. */
-    pa_data = calloc(n_preauth_systems + 2, sizeof(krb5_pa_data *));
-    if (pa_data == 0) {
+    /* Allocate our state. */
+    state = malloc(sizeof(*state));
+    if (!state) {
         (*respond)(arg);
         return;
     }
-    pa = pa_data;
+    state->hw_only = isflagset(rock->client->attributes,
+                                  KRB5_KDB_REQUIRES_HW_AUTH);
+    state->respond = respond;
+    state->arg = arg;
+    state->request = request;
+    state->rock = rock;
+    state->realm = kdc_active_realm;
+    state->e_data_out = e_data_out;
 
-    for (ap = preauth_systems; ap->type != -1; ap++) {
-        if (hw_only && !(ap->flags & PA_HARDWARE))
-            continue;
-        if (ap->flags & PA_PSEUDO)
-            continue;
-        *pa = malloc(sizeof(krb5_pa_data));
-        if (*pa == 0)
-            goto errout;
-        memset(*pa, 0, sizeof(krb5_pa_data));
-        (*pa)->magic = KV5M_PA_DATA;
-        (*pa)->pa_type = ap->type;
-        if (ap->get_edata) {
-            retval = ap->get_edata(kdc_context, request, &callbacks, rock,
-                                   ap->moddata, *pa);
-            if (retval) {
-                /* just failed on this type, continue */
-                free(*pa);
-                *pa = 0;
-                continue;
-            }
-        }
-        pa++;
-    }
-    if (pa_data[0] == 0) {
-        krb5_klog_syslog(LOG_INFO,
-                         _("%spreauth required but hint list is empty"),
-                         hw_only ? "hw" : "");
+    /* Allocate two extra entries for the cookie and the terminator. */
+    state->pa_data = calloc(n_preauth_systems + 2, sizeof(krb5_pa_data *));
+    if (!state->pa_data) {
+        free(state);
+        (*respond)(arg);
+        return;
     }
-    /*
-     * If we fail to get the cookie it is probably
-     * still reasonable to continue with the response
-     */
-    kdc_preauth_get_cookie(rock->rstate, pa);
 
-    *e_data_out = pa_data;
-    pa_data = NULL;
-
-errout:
-    krb5_free_pa_data(kdc_context, pa_data);
-    (*respond)(arg);
+    state->pa_cur = state->pa_data;
+    state->ap = preauth_systems;
+    hint_list_next(state);
 }
 
 /*
@@ -1306,29 +1373,26 @@ fail:
         free(salt.data);
     return retval;
 }
-/*
- * This function returns the etype information for a particular
- * client, to be passed back in the preauth list in the KRB_ERROR
- * message.  It supports generating both etype_info  and etype_info2
- *  as most of the work is the same.
- */
-static krb5_error_code
+
+/* Create etype information for a client for the preauth-required hint list,
+ * for either etype-info or etype-info2. */
+static void
 etype_info_helper(krb5_context context, krb5_kdc_req *request,
-                  krb5_db_entry *client, krb5_pa_data *pa_data,
-                  int etype_info2)
+                  krb5_db_entry *client, krb5_preauthtype pa_type,
+                  krb5_kdcpreauth_edata_respond_fn respond, void *arg)
 {
-    krb5_etype_info_entry **    entry = 0;
-    krb5_key_data               *client_key;
-    krb5_error_code             retval;
-    krb5_data *                 scratch;
-    krb5_enctype                db_etype;
-    int                         i = 0;
-    int                         start = 0;
-    int                         seen_des = 0;
+    krb5_error_code retval;
+    krb5_pa_data *pa = NULL;
+    krb5_etype_info_entry **entry = NULL;
+    krb5_data *scratch = NULL;
+    krb5_key_data *client_key;
+    krb5_enctype db_etype;
+    int i = 0, start = 0, seen_des = 0;
+    int etype_info2 = (pa_type == KRB5_PADATA_ETYPE_INFO2);
 
-    entry = malloc((client->n_key_data * 2 + 1) * sizeof(krb5_etype_info_entry *));
+    entry = k5alloc((client->n_key_data * 2 + 1) * sizeof(*entry), &retval);
     if (entry == NULL)
-        return ENOMEM;
+        goto cleanup;
     entry[0] = NULL;
 
     while (1) {
@@ -1349,7 +1413,6 @@ etype_info_helper(krb5_context context, krb5_kdc_req *request,
                                             db_etype, &entry[i], etype_info2);
             if (retval != 0)
                 goto cleanup;
-            entry[i+1] = 0;
             i++;
         }
 
@@ -1387,39 +1450,47 @@ etype_info_helper(krb5_context context, krb5_kdc_req *request,
         retval = encode_krb5_etype_info(entry, &scratch);
     if (retval)
         goto cleanup;
-    pa_data->contents = (unsigned char *)scratch->data;
-    pa_data->length = scratch->length;
-    free(scratch);
-
-    retval = 0;
+    pa = k5alloc(sizeof(*pa), &retval);
+    if (pa == NULL)
+        goto cleanup;
+    pa->magic = KV5M_PA_DATA;
+    pa->pa_type = pa_type;
+    pa->contents = (unsigned char *)scratch->data;
+    pa->length = scratch->length;
+    scratch->data = NULL;
 
 cleanup:
-    if (entry)
-        krb5_free_etype_info(context, entry);
-    return retval;
+    krb5_free_etype_info(context, entry);
+    krb5_free_data(context, scratch);
+    (*respond)(arg, retval, pa);
 }
 
-static krb5_error_code
+static void
 get_etype_info(krb5_context context, krb5_kdc_req *request,
                krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
-               krb5_kdcpreauth_moddata moddata, krb5_pa_data *pa_data)
+               krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
+               krb5_kdcpreauth_edata_respond_fn respond, void *arg)
 {
     int i;
+
     for (i=0;  i < request->nktypes; i++) {
-        if (enctype_requires_etype_info_2(request->ktype[i]))
-            return KRB5KDC_ERR_PADATA_TYPE_NOSUPP ;;;; /*Caller will
-                                                        * skip this
-                                                        * type*/
+        if (enctype_requires_etype_info_2(request->ktype[i])) {
+            /* Requestor understands etype-info2, so don't send etype-info. */
+            (*respond)(arg, KRB5KDC_ERR_PADATA_TYPE_NOSUPP, NULL);
+            return;
+        }
     }
-    return etype_info_helper(context, request, rock->client, pa_data, 0);
+
+    etype_info_helper(context, request, rock->client, pa_type, respond, arg);
 }
 
-static krb5_error_code
+static void
 get_etype_info2(krb5_context context, krb5_kdc_req *request,
                 krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
-                krb5_kdcpreauth_moddata moddata, krb5_pa_data *pa_data)
+                krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
+                krb5_kdcpreauth_edata_respond_fn respond, void *arg)
 {
-    return etype_info_helper(context, request, rock->client, pa_data, 1);
+    etype_info_helper(context, request, rock->client, pa_type, respond, arg);
 }
 
 static krb5_error_code
index e5f5d6e586256c0f387df636c441d78b2df82a24..9d7236c101a0a8bdd43ea1d13997b6823b069328 100644 (file)
 #include <krb5/preauth_plugin.h>
 #include "kdc_util.h"
 
-static krb5_error_code
+static void
 ec_edata(krb5_context context, krb5_kdc_req *request,
          krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
-         krb5_kdcpreauth_moddata moddata, krb5_pa_data *data)
+         krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
+         krb5_kdcpreauth_edata_respond_fn respond, void *arg)
 {
     krb5_keyblock *armor_key = cb->fast_armor(context, rock);
-
-    return (armor_key == NULL) ? ENOENT : 0;
+    (*respond)(arg, (armor_key == NULL) ? ENOENT : 0, NULL);
 }
 
 static void
index 48626d972240b5553d44244909a38e270a4c50e8..a63298a9c448d069ace4be8d57f00e80ce4af04a 100644 (file)
 #include <krb5/preauth_plugin.h>
 #include "kdc_util.h"
 
-static krb5_error_code
+static void
 enc_ts_get(krb5_context context, krb5_kdc_req *request,
           krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
-          krb5_kdcpreauth_moddata moddata, krb5_pa_data *data)
+           krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
+           krb5_kdcpreauth_edata_respond_fn respond, void *arg)
 {
     krb5_keyblock *armor_key = cb->fast_armor(context, rock);
 
-    return (armor_key != NULL) ? ENOENT : 0;
+    (*respond)(arg, (armor_key != NULL) ? ENOENT : 0, NULL);
 }
 
 static void
index da2643fc1849ce0eec9a8551c1f33df58281b978..6643e8e0420499e3d203360037f8abaa5de348db 100644 (file)
@@ -271,13 +271,15 @@ server_fini(krb5_context kcontext, krb5_kdcpreauth_moddata moddata)
 
 /* Obtain and return any preauthentication data (which is destined for the
  * client) which matches type data->pa_type. */
-static krb5_error_code
+static void
 server_get_edata(krb5_context kcontext, krb5_kdc_req *request,
                  krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
-                 krb5_kdcpreauth_moddata moddata, krb5_pa_data *data)
+                 krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
+                 krb5_kdcpreauth_edata_respond_fn respond, void *arg)
 {
     krb5_keyblock *keys;
     krb5_int32 *enctypes, enctype;
+    krb5_pa_data *data;
     int i;
 
     /* Retrieve the client's keys. */
@@ -285,7 +287,8 @@ server_get_edata(krb5_context kcontext, krb5_kdc_req *request,
 #ifdef DEBUG
         fprintf(stderr, "Error retrieving client keys.\n");
 #endif
-        return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+        (*respond)(arg, KRB5KDC_ERR_PADATA_TYPE_NOSUPP, NULL);
+        return;
     }
 
     /* Count which types of keys we've got. */
@@ -295,7 +298,8 @@ server_get_edata(krb5_context kcontext, krb5_kdc_req *request,
     enctypes = malloc((unsigned)i * 4);
     if (enctypes == NULL) {
         cb->free_keys(kcontext, rock, keys);
-        return ENOMEM;
+        (*respond)(arg, ENOMEM, NULL);
+        return;
     }
 #ifdef DEBUG
     fprintf(stderr, "Supported enctypes = {");
@@ -310,11 +314,17 @@ server_get_edata(krb5_context kcontext, krb5_kdc_req *request,
 #ifdef DEBUG
     fprintf(stderr, "}.\n");
 #endif
+    cb->free_keys(kcontext, rock, keys);
+    data = malloc(sizeof(*data));
+    if (data == NULL) {
+        free(enctypes);
+        (*respond)(arg, ENOMEM, NULL);
+    }
+    data->magic = KV5M_PA_DATA;
     data->pa_type = KRB5_PADATA_CKSUM_BODY_REQ;
     data->length = (i * 4);
     data->contents = (unsigned char *) enctypes;
-    cb->free_keys(kcontext, rock, keys);
-    return 0;
+    (*respond)(arg, 0, data);
 }
 
 /* Verify a request from a client. */
index d209f9e76f2afe3e6c21568ee4b3686d04273019..e43fb7e816a998198794f39894889ad1c0737a03 100644 (file)
@@ -95,13 +95,15 @@ cleanup:
     return retval;
 }
 
-static krb5_error_code
+static void
 pkinit_server_get_edata(krb5_context context,
                         krb5_kdc_req *request,
                         krb5_kdcpreauth_callbacks cb,
                         krb5_kdcpreauth_rock rock,
                         krb5_kdcpreauth_moddata moddata,
-                        krb5_pa_data *data)
+                        krb5_preauthtype pa_type,
+                        krb5_kdcpreauth_edata_respond_fn respond,
+                        void *arg)
 {
     krb5_error_code retval = 0;
     pkinit_kdc_context plgctx = NULL;
@@ -111,8 +113,10 @@ pkinit_server_get_edata(krb5_context context,
 
     /* Remove (along with armor_key) when FAST PKINIT is settled. */
     /* Don't advertise PKINIT if the client used FAST. */
-    if (armor_key != NULL)
-        return EINVAL;
+    if (armor_key != NULL) {
+        (*respond)(arg, EINVAL, NULL);
+        return;
+    }
 
     /*
      * If we don't have a realm context for the given realm,
@@ -122,7 +126,7 @@ pkinit_server_get_edata(krb5_context context,
     if (plgctx == NULL)
         retval = EINVAL;
 
-    return retval;
+    (*respond)(arg, retval, NULL);
 }
 
 static krb5_error_code
index 4da2c2f48cb2e1bbc10252b0e62b8d37537a43a0..4f603474d8a6d7e3a034b08e6bae91691ff23b9e 100644 (file)
@@ -243,18 +243,17 @@ server_free_modreq(krb5_context kcontext,
 
 /* Obtain and return any preauthentication data (which is destined for the
  * client) which matches type data->pa_type. */
-static krb5_error_code
+static void
 server_get_edata(krb5_context kcontext,
                  krb5_kdc_req *request,
                  krb5_kdcpreauth_callbacks cb,
                  krb5_kdcpreauth_rock rock,
                  krb5_kdcpreauth_moddata moddata,
-                 krb5_pa_data *data)
+                 krb5_preauthtype pa_type,
+                 krb5_kdcpreauth_edata_respond_fn respond,
+                 void *arg)
 {
-    /* Return zero bytes of data. */
-    data->length = 0;
-    data->contents = NULL;
-    return 0;
+    (*respond)(arg, 0, NULL);
 }
 
 /* Verify a request from a client. */