Make it easier to test SAM-2 client code
authorGreg Hudson <ghudson@mit.edu>
Thu, 26 Apr 2012 21:47:05 +0000 (21:47 +0000)
committerGreg Hudson <ghudson@mit.edu>
Thu, 26 Apr 2012 21:47:05 +0000 (21:47 +0000)
Add a method to the securid_sam2 plugin, built with alternate
compile-time flags, which supplies a plain-text challenge to the
client to be used as the OTP value.  This lets us manually exercise
the SAM-2 client code and a little bit of the KDC code.

securid_make_sam_challenge_2_and_cksum is moved into the method-
independent code and renamed.  get_securid_edata_2 has its sc2b
parameter removed as it was not used by the caller.

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

src/plugins/preauth/securid_sam2/Makefile.in
src/plugins/preauth/securid_sam2/extern.h
src/plugins/preauth/securid_sam2/grail.c [new file with mode: 0644]
src/plugins/preauth/securid_sam2/securid2.c
src/plugins/preauth/securid_sam2/securid_sam2.exports
src/plugins/preauth/securid_sam2/securid_sam2_main.c

index 860b473eb4acf288ff336377c9e03a54cceb4869..fff52c7291cd75acd783ab0fedffd583e610693f 100644 (file)
@@ -5,7 +5,8 @@ KRB5_CONFIG_SETUP = KRB5_CONFIG=$(top_srcdir)/config-files/krb5.conf ; export KR
 PROG_LIBPATH=-L$(TOPLIBD)
 PROG_RPATH=$(KRB5_LIBDIR)
 MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR)
-DEFS=@DEFS@ -DARL_SECURID_PREAUTH
+DEFS=@DEFS@
+DEFINES=-DARL_SECURID_PREAUTH
 
 LOCALINCLUDES = -I../../../include/krb5 -I.
 
@@ -18,15 +19,16 @@ RELDIR=../plugins/preauth/securid_sam2
 SHLIB_EXPDEPS = \
        $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
        $(TOPLIBD)/libkrb5$(SHLIBEXT) $(KADMSRV_DEPLIBS)
+ACELIB= -laceclnt
 SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto $(SUPPORT_LIB) $(KADMSRV_LIBS) \
-       $(LIBS) -laceclnt
+       $(LIBS) $(ACELIB)
 
 SHLIB_DIRS=-L$(TOPLIBD)
 SHLIB_RDIRS=$(KRB5_LIBDIR)
 STOBJLISTS=OBJS.ST
-STLIBOBJS=securid_sam2_main.o securid2.o
+STLIBOBJS=securid_sam2_main.o securid2.o grail.o
 
-SRCS= $(srcdir)/securid_sam2_main.c $(srcdir)/securid2.c 2
+SRCS= $(srcdir)/securid_sam2_main.c $(srcdir)/securid2.c $(srcdir)/grail.c
 
 all-unix:: all-libs
 install-unix:: install-libs
index 5a76b7eb60d323f892fe8adb1b2bbfff24c5f045..c8c76dd0789fa8317e5d947451e6b04d65c3b0b1 100644 (file)
 krb5_error_code sam_get_db_entry(krb5_context , krb5_principal,
                                  int *, krb5_db_entry **);
 
-krb5_error_code
-securid_make_sam_challenge_2_and_cksum(krb5_context context,
-                                       krb5_sam_challenge_2 *sc2,
-                                       krb5_sam_challenge_2_body *sc2b,
-                                       krb5_keyblock *cksum_key);
+krb5_error_code sam_make_challenge(krb5_context context,
+                                   krb5_sam_challenge_2_body *sc2b,
+                                   krb5_keyblock *cksum_key,
+                                   krb5_sam_challenge_2 *sc2_out);
 
 krb5_error_code get_securid_edata_2(krb5_context context,
                                     krb5_db_entry *client,
                                     krb5_keyblock *client_key,
-                                    krb5_sam_challenge_2_body *sc2b,
                                     krb5_sam_challenge_2 *sc2);
 
 krb5_error_code verify_securid_data_2(krb5_context context,
@@ -50,3 +48,13 @@ krb5_error_code verify_securid_data_2(krb5_context context,
                                       krb5_enc_tkt_part *enc_tkt_reply,
                                       krb5_pa_data *pa,
                                       krb5_sam_challenge_2 **sc2_out);
+
+krb5_error_code get_grail_edata(krb5_context context, krb5_db_entry *client,
+                                krb5_keyblock *client_key,
+                                krb5_sam_challenge_2 *sc2_out);
+
+krb5_error_code verify_grail_data(krb5_context context, krb5_db_entry *client,
+                                  krb5_sam_response_2 *sr2,
+                                  krb5_enc_tkt_part *enc_tkt_reply,
+                                  krb5_pa_data *pa,
+                                  krb5_sam_challenge_2 **sc2_out);
diff --git a/src/plugins/preauth/securid_sam2/grail.c b/src/plugins/preauth/securid_sam2/grail.c
new file mode 100644 (file)
index 0000000..18d48f9
--- /dev/null
@@ -0,0 +1,273 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* plugins/preauth/securid_sam2/grail.c - Test method for SAM-2 preauth */
+/*
+ * Copyright (C) 2012 by the Massachusetts Institute of Technology.
+ * 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.
+ *
+ * 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 HOLDER 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.
+ */
+
+/*
+ * This test method exists to exercise the client SAM-2 code and some of the
+ * KDC SAM-2 code.  We make up a weakly random number and presents it to the
+ * client in the prompt (in plain text), as well as encrypted in the track ID.
+ * To verify, we compare the decrypted track ID to the entered value.
+ *
+ * Do not use this method in production; it is not secure.
+ */
+
+#ifdef GRAIL_PREAUTH
+
+#include "k5-int.h"
+#include <kdb.h>
+#include <adm_proto.h>
+#include <ctype.h>
+#include "extern.h"
+
+static krb5_error_code
+get_grail_key(krb5_context context, krb5_db_entry *client,
+              krb5_keyblock *key_out)
+{
+    krb5_db_entry *grail_entry = NULL;
+    krb5_key_data *kd;
+    int sam_type = PA_SAM_TYPE_GRAIL;
+    krb5_error_code ret = 0;
+
+    ret = sam_get_db_entry(context, client->princ, &sam_type, &grail_entry);
+    if (ret)
+        return KRB5_PREAUTH_NO_KEY;
+    ret = krb5_dbe_find_enctype(context, grail_entry, -1, -1, -1, &kd);
+    if (ret)
+        goto cleanup;
+    ret = krb5_dbe_decrypt_key_data(context, NULL, kd, key_out, NULL);
+    if (ret)
+        goto cleanup;
+
+cleanup:
+    if (grail_entry)
+        krb5_db_free_principal(context, grail_entry);
+    return ret;
+}
+
+static krb5_error_code
+decrypt_track_data(krb5_context context, krb5_db_entry *client,
+                   krb5_data *enc_track_data, krb5_data *output)
+{
+    krb5_error_code ret;
+    krb5_keyblock sam_key;
+    krb5_enc_data enc;
+    krb5_data result = empty_data();
+
+    sam_key.contents = NULL;
+    *output = empty_data();
+
+    ret = get_grail_key(context, client, &sam_key);
+    if (ret != 0)
+        return ret;
+    enc.ciphertext = *enc_track_data;
+    enc.enctype = ENCTYPE_UNKNOWN;
+    enc.kvno = 0;
+    ret = alloc_data(&result, enc_track_data->length);
+    if (ret)
+        goto cleanup;
+    ret = krb5_c_decrypt(context, &sam_key,
+                         KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0, &enc,
+                         &result);
+    if (ret)
+        goto cleanup;
+
+    *output = result;
+    result = empty_data();
+
+cleanup:
+    krb5_free_keyblock_contents(context, &sam_key);
+    krb5_free_data_contents(context, &result);
+    return ret;
+}
+
+static krb5_error_code
+encrypt_track_data(krb5_context context, krb5_db_entry *client,
+                   krb5_data *track_data, krb5_data *output)
+{
+    krb5_error_code ret;
+    size_t olen;
+    krb5_keyblock sam_key;
+    krb5_enc_data enc;
+
+    *output = empty_data();
+    enc.ciphertext = empty_data();
+    sam_key.contents = NULL;
+
+    ret = get_grail_key(context, client, &sam_key);
+    if (ret != 0)
+        return ret;
+
+    ret = krb5_c_encrypt_length(context, sam_key.enctype,
+                                   track_data->length, &olen);
+    if (ret != 0)
+        goto cleanup;
+    assert(olen <= 65536);
+    ret = alloc_data(&enc.ciphertext, olen);
+    if (ret)
+        goto cleanup;
+    enc.enctype = sam_key.enctype;
+    enc.kvno = 0;
+
+    ret = krb5_c_encrypt(context, &sam_key,
+                         KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0,
+                         track_data, &enc);
+    if (ret)
+        goto cleanup;
+
+    *output = enc.ciphertext;
+    enc.ciphertext = empty_data();
+
+cleanup:
+    krb5_free_keyblock_contents(context, &sam_key);
+    krb5_free_data_contents(context, &enc.ciphertext);
+    return ret;
+}
+
+krb5_error_code
+get_grail_edata(krb5_context context, krb5_db_entry *client,
+                krb5_keyblock *client_key, krb5_sam_challenge_2 *sc2_out)
+{
+    krb5_error_code ret;
+    krb5_data tmp_data, track_id = empty_data();
+    int tval = time(NULL) % 77777;
+    krb5_sam_challenge_2_body sc2b;
+    char tval_string[256], prompt[256];
+
+    snprintf(tval_string, sizeof(tval_string), "%d", tval);
+    snprintf(prompt, sizeof(prompt), "Enter %d", tval);
+
+    memset(&sc2b, 0, sizeof(sc2b));
+    sc2b.magic = KV5M_SAM_CHALLENGE_2;
+    sc2b.sam_track_id = empty_data();
+    sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;
+    sc2b.sam_type_name = empty_data();
+    sc2b.sam_challenge_label = empty_data();
+    sc2b.sam_challenge = empty_data();
+    sc2b.sam_response_prompt = string2data(prompt);
+    sc2b.sam_pk_for_sad = empty_data();
+    sc2b.sam_type = PA_SAM_TYPE_GRAIL;
+    sc2b.sam_etype = client_key->enctype;
+
+    tmp_data = string2data(tval_string);
+    ret = encrypt_track_data(context, client, &tmp_data, &track_id);
+    if (ret)
+        goto cleanup;
+    sc2b.sam_track_id = track_id;
+
+    tmp_data = make_data(&sc2b.sam_nonce, sizeof(sc2b.sam_nonce));
+    ret = krb5_c_random_make_octets(context, &tmp_data);
+    if (ret)
+        goto cleanup;
+
+    ret = sam_make_challenge(context, &sc2b, client_key, sc2_out);
+
+cleanup:
+    krb5_free_data_contents(context, &track_id);
+    return ret;
+}
+
+krb5_error_code
+verify_grail_data(krb5_context context, krb5_db_entry *client,
+                  krb5_sam_response_2 *sr2, krb5_enc_tkt_part *enc_tkt_reply,
+                  krb5_pa_data *pa, krb5_sam_challenge_2 **sc2_out)
+{
+    krb5_error_code ret;
+    krb5_key_data *client_key_data = NULL;
+    krb5_keyblock client_key;
+    krb5_data scratch = empty_data(), track_id_data = empty_data();
+    krb5_enc_sam_response_enc_2 *esre2 = NULL;
+
+    *sc2_out = NULL;
+    memset(&client_key, 0, sizeof(client_key));
+
+    if ((sr2->sam_enc_nonce_or_sad.ciphertext.data == NULL) ||
+        (sr2->sam_enc_nonce_or_sad.ciphertext.length <= 0))
+        return KRB5KDC_ERR_PREAUTH_FAILED;
+
+    ret = krb5_dbe_find_enctype(context, client,
+                                sr2->sam_enc_nonce_or_sad.enctype,
+                                KRB5_KDB_SALTTYPE_NORMAL,
+                                sr2->sam_enc_nonce_or_sad.kvno,
+                                &client_key_data);
+    if (ret)
+        goto cleanup;
+
+    ret = krb5_dbe_decrypt_key_data(context, NULL, client_key_data,
+                                    &client_key, NULL);
+    if (ret)
+        goto cleanup;
+    ret = alloc_data(&scratch, sr2->sam_enc_nonce_or_sad.ciphertext.length);
+    if (ret)
+        goto cleanup;
+    ret = krb5_c_decrypt(context, &client_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE,
+                         NULL, &sr2->sam_enc_nonce_or_sad, &scratch);
+    if (ret)
+        goto cleanup;
+
+    ret = decode_krb5_enc_sam_response_enc_2(&scratch, &esre2);
+    if (ret)
+        goto cleanup;
+
+    if (sr2->sam_nonce != esre2->sam_nonce) {
+        ret = KRB5KDC_ERR_PREAUTH_FAILED;
+        goto cleanup;
+    }
+
+    if (esre2->sam_sad.length == 0 || esre2->sam_sad.data == NULL) {
+        ret = KRB5KDC_ERR_PREAUTH_FAILED;
+        goto cleanup;
+    }
+
+    ret = decrypt_track_data(context, client, &sr2->sam_track_id,
+                             &track_id_data);
+    if (ret)
+        goto cleanup;
+
+    /* Some enctypes aren't length-preserving; try to work anyway. */
+    while (track_id_data.length > 0 &&
+           !isdigit(track_id_data.data[track_id_data.length - 1]))
+        track_id_data.length--;
+
+    if (!data_eq(track_id_data, esre2->sam_sad)) {
+        ret = KRB5KDC_ERR_PREAUTH_FAILED;
+        goto cleanup;
+    }
+
+    enc_tkt_reply->flags |= (TKT_FLG_HW_AUTH | TKT_FLG_PRE_AUTH);
+
+cleanup:
+    krb5_free_keyblock_contents(context, &client_key);
+    krb5_free_data_contents(context, &scratch);
+    krb5_free_enc_sam_response_enc_2(context, esre2);
+    return ret;
+}
+
+#endif /* GRAIL_PREAUTH */
index 816946eeb376bf850b30f0429bac6e2b6ca29e2d..57d4e37d208de990f838bb9533ea9c4b4f75b340 100644 (file)
@@ -36,6 +36,8 @@
  * RESULTING FROM THE USE OF THIS SOFTWARE.
  */
 
+#ifdef ARL_SECURID_PREAUTH
+
 #include "k5-int.h"
 #include <kdb.h>
 #include <stdio.h>
@@ -120,64 +122,6 @@ cleanup:
     return retval;
 }
 
-krb5_error_code
-securid_make_sam_challenge_2_and_cksum(krb5_context context,
-                                       krb5_sam_challenge_2 *sc2,
-                                       krb5_sam_challenge_2_body *sc2b,
-                                       krb5_keyblock *cksum_key)
-{
-    krb5_error_code retval;
-    krb5_checksum **cksum_array = NULL;
-    krb5_checksum *cksum = NULL;
-    krb5_cksumtype cksumtype;
-    krb5_data *encoded_challenge_body = NULL;
-
-    if (!cksum_key)
-        return KRB5_PREAUTH_NO_KEY;
-    if (!sc2 || !sc2b)
-        return KRB5KDC_ERR_PREAUTH_FAILED;
-
-    retval = encode_krb5_sam_challenge_2_body(sc2b, &encoded_challenge_body);
-    if (retval || !encoded_challenge_body) {
-        encoded_challenge_body = NULL;
-        goto cksum_cleanup;
-    }
-
-    cksum_array = calloc(2, sizeof(krb5_checksum *));
-    if (!cksum_array) {
-        retval = ENOMEM;
-        goto cksum_cleanup;
-    }
-
-    cksum = (krb5_checksum *)k5alloc(sizeof(krb5_checksum), &retval);
-    if (retval)
-        goto cksum_cleanup;
-    cksum_array[0] = cksum;
-    cksum_array[1] = NULL;
-
-    retval = krb5int_c_mandatory_cksumtype(context, cksum_key->enctype,
-                                           &cksumtype);
-    if (retval)
-        goto cksum_cleanup;
-
-    retval = krb5_c_make_checksum(context, cksumtype, cksum_key,
-                                  KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM,
-                                  encoded_challenge_body, cksum);
-    if (retval)
-        goto cksum_cleanup;
-
-    sc2->sam_cksum = cksum_array;
-    sc2->sam_challenge_2_body = *encoded_challenge_body;
-    return 0;
-
-cksum_cleanup:
-    krb5_free_data(context, encoded_challenge_body);
-    free(cksum_array);
-    free(cksum);
-    return retval;
-}
-
-
 static krb5_error_code
 securid_decrypt_track_data_2(krb5_context context, krb5_db_entry *client,
                              krb5_data *enc_track_data, krb5_data *output)
@@ -262,45 +206,47 @@ cleanup:
 
 krb5_error_code
 get_securid_edata_2(krb5_context context, krb5_db_entry *client,
-                    krb5_keyblock *client_key,
-                    krb5_sam_challenge_2_body *sc2b, krb5_sam_challenge_2 *sc2)
+                    krb5_keyblock *client_key, krb5_sam_challenge_2 *sc2)
 {
     krb5_error_code retval;
-    krb5_data scratch;
+    krb5_data scratch, track_id = empty_data();
     char *user = NULL;
     char *def_user = "<unknown user>";
     struct securid_track_data sid_track_data;
     krb5_data tmp_data;
+    krb5_sam_challenge_2_body sc2b;
 
     scratch.data = NULL;
-    sc2b->sam_track_id.data = NULL;
 
     retval = krb5_unparse_name(context, client->princ, &user);
     if (retval)
         goto cleanup;
 
-    sc2b->sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;
-    sc2b->sam_type_name.length = 0;
-    sc2b->sam_challenge_label.length = 0;
-    sc2b->sam_challenge.length = 0;
-    sc2b->sam_response_prompt.data = PASSCODE_message;
-    sc2b->sam_response_prompt.length = strlen(sc2b->sam_response_prompt.data);
-    sc2b->sam_pk_for_sad.length = 0;
-    sc2b->sam_type = PA_SAM_TYPE_SECURID;
+    memset(&sc2b, 0, sizeof(sc2b));
+    sc2b.magic = KV5M_SAM_CHALLENGE_2;
+    sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;
+    sc2b.sam_type_name.length = 0;
+    sc2b.sam_challenge_label.length = 0;
+    sc2b.sam_challenge.length = 0;
+    sc2b.sam_response_prompt.data = PASSCODE_message;
+    sc2b.sam_response_prompt.length = strlen(sc2b.sam_response_prompt.data);
+    sc2b.sam_pk_for_sad.length = 0;
+    sc2b.sam_type = PA_SAM_TYPE_SECURID;
 
     sid_track_data.state = SECURID_STATE_INITIAL;
     sid_track_data.hostid = gethostid();
     tmp_data.data = (char *)&sid_track_data;
     tmp_data.length = sizeof(sid_track_data);
     retval = securid_encrypt_track_data_2(context, client, &tmp_data,
-                                          &sc2b->sam_track_id);
+                                          &track_id);
     if (retval != 0) {
         com_err("krb5kdc", retval, "while encrypting nonce track data");
         goto cleanup;
     }
+    sc2b.sam_track_id = track_id;
 
-    scratch.data = (char *)&sc2b->sam_nonce;
-    scratch.length = sizeof(sc2b->sam_nonce);
+    scratch.data = (char *)&sc2b.sam_nonce;
+    scratch.length = sizeof(sc2b.sam_nonce);
     retval = krb5_c_random_make_octets(context, &scratch);
     if (retval) {
         com_err("krb5kdc", retval,
@@ -310,10 +256,9 @@ get_securid_edata_2(krb5_context context, krb5_db_entry *client,
     }
 
     /* Get the client's key */
-    sc2b->sam_etype = client_key->enctype;
+    sc2b.sam_etype = client_key->enctype;
 
-    retval = securid_make_sam_challenge_2_and_cksum(context,
-                                                    sc2, sc2b, client_key);
+    retval = sam_make_challenge(context, &sc2b, client_key, sc2);
     if (retval) {
         com_err("krb5kdc", retval,
                 "while making SAM_CHALLENGE_2 checksum (%s)",
@@ -322,10 +267,7 @@ get_securid_edata_2(krb5_context context, krb5_db_entry *client,
 
 cleanup:
     free(user);
-    if (retval) {
-        krb5_free_data_contents(context, &sc2b->sam_track_id);
-        sc2b->sam_track_id.data = NULL;
-    }
+    krb5_free_data_contents(context, &track_id);
     return retval;
 }
 
@@ -554,9 +496,7 @@ verify_securid_data_2(krb5_context context, krb5_db_entry *client,
                         securid_user);
                 goto cleanup;
             }
-            retval = securid_make_sam_challenge_2_and_cksum(context, sc2p,
-                                                            &sc2b,
-                                                            &client_key);
+            retval = sam_make_challenge(context, &sc2b, &client_key, sc2p);
             if (retval) {
                 com_err("krb5kdc", retval,
                         "while making cksum for "
@@ -688,9 +628,7 @@ verify_securid_data_2(krb5_context context, krb5_db_entry *client,
                         securid_user);
                 goto cleanup;
             }
-            retval = securid_make_sam_challenge_2_and_cksum(context, sc2p,
-                                                            &sc2b,
-                                                            &client_key);
+            retval = sam_make_challenge(context, &sc2b, &client_key, sc2p);
             if (retval) {
                 com_err("krb5kdc", retval,
                         "while making cksum for SAM_CHALLENGE_2 (%s)",
@@ -727,3 +665,5 @@ cleanup:
     krb5_free_sam_challenge_2(context, sc2p);
     return retval;
 }
+
+#endif /* ARL_SECURID_PREAUTH */
index cb26d472273731389e4430ba2e441feaa28cb30f..9abae9196b9430191f42c65a3756e1347f0a8d79 100644 (file)
@@ -1 +1 @@
-preauthentication_server_1
+kdcpreauth_securid_sam2_initvt
index b20a87d8b53c5fa70b89d08877a9ad63f9ea8b06..5dcd2ebf3e11faf71847f7cbb0f81dfe120a54a4 100644 (file)
@@ -112,6 +112,61 @@ cleanup:
     }
 }
 
+krb5_error_code
+sam_make_challenge(krb5_context context, krb5_sam_challenge_2_body *sc2b,
+                   krb5_keyblock *cksum_key, krb5_sam_challenge_2 *sc2_out)
+{
+    krb5_error_code retval;
+    krb5_checksum **cksum_array = NULL;
+    krb5_checksum *cksum = NULL;
+    krb5_cksumtype cksumtype;
+    krb5_data *encoded_challenge_body = NULL;
+
+    if (!cksum_key)
+        return KRB5_PREAUTH_NO_KEY;
+    if (!sc2_out || !sc2b)
+        return KRB5KDC_ERR_PREAUTH_FAILED;
+
+    retval = encode_krb5_sam_challenge_2_body(sc2b, &encoded_challenge_body);
+    if (retval || !encoded_challenge_body) {
+        encoded_challenge_body = NULL;
+        goto cksum_cleanup;
+    }
+
+    cksum_array = calloc(2, sizeof(krb5_checksum *));
+    if (!cksum_array) {
+        retval = ENOMEM;
+        goto cksum_cleanup;
+    }
+
+    cksum = (krb5_checksum *)k5alloc(sizeof(krb5_checksum), &retval);
+    if (retval)
+        goto cksum_cleanup;
+    cksum_array[0] = cksum;
+    cksum_array[1] = NULL;
+
+    retval = krb5int_c_mandatory_cksumtype(context, cksum_key->enctype,
+                                           &cksumtype);
+    if (retval)
+        goto cksum_cleanup;
+
+    retval = krb5_c_make_checksum(context, cksumtype, cksum_key,
+                                  KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM,
+                                  encoded_challenge_body, cksum);
+    if (retval)
+        goto cksum_cleanup;
+
+    sc2_out->sam_cksum = cksum_array;
+    sc2_out->sam_challenge_2_body = *encoded_challenge_body;
+    return 0;
+
+cksum_cleanup:
+    krb5_free_data(context, encoded_challenge_body);
+    free(cksum_array);
+    free(cksum);
+    return retval;
+}
+
 static void
 kdc_include_padata(krb5_context context, krb5_kdc_req *request,
                    krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
@@ -121,16 +176,12 @@ kdc_include_padata(krb5_context context, krb5_kdc_req *request,
     krb5_error_code retval;
     krb5_keyblock *client_key = NULL;
     krb5_sam_challenge_2 sc2;
-    krb5_sam_challenge_2_body sc2b;
     int sam_type = 0;             /* unknown */
     krb5_db_entry *sam_db_entry = NULL, *client;
     krb5_data *encoded_challenge = NULL;
     krb5_pa_data *pa_data = NULL;
 
     memset(&sc2, 0, sizeof(sc2));
-    memset(&sc2b, 0, sizeof(sc2b));
-    sc2b.magic = KV5M_SAM_CHALLENGE_2;
-    sc2b.sam_type = sam_type;
 
     client = cb->client_entry(context, rock);
     retval = sam_get_db_entry(context, client->princ, &sam_type,
@@ -161,34 +212,39 @@ kdc_include_padata(krb5_context context, krb5_kdc_req *request,
     switch (sam_type) {
 #ifdef ARL_SECURID_PREAUTH
     case PA_SAM_TYPE_SECURID:
-        retval = get_securid_edata_2(context, client, client_key, &sc2b, &sc2);
+        retval = get_securid_edata_2(context, client, client_key, &sc2);
         if (retval)
             goto cleanup;
-
-        retval = encode_krb5_sam_challenge_2(&sc2, &encoded_challenge);
-        if (retval) {
-            com_err("krb5kdc", retval,
-                    "while encoding SECURID SAM_CHALLENGE_2");
-            goto cleanup;
-        }
-
-        pa_data = k5alloc(sizeof(*pa_data), &retval);
-        if (pa_data == NULL)
-            goto cleanup;
-        pa_data->magic = KV5M_PA_DATA;
-        pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE_2;
-        pa_data->contents = (krb5_octet *) encoded_challenge->data;
-        pa_data->length = encoded_challenge->length;
-        encoded_challenge->data = NULL;
-
-        retval = 0;
         break;
 #endif  /* ARL_SECURID_PREAUTH */
+#ifdef GRAIL_PREAUTH
+    case PA_SAM_TYPE_GRAIL:
+        retval = get_grail_edata(context, client, client_key, &sc2);
+        if (retval)
+            goto cleanup;
+        break;
+#endif /* GRAIL_PREAUTH */
     default:
         retval = KRB5_PREAUTH_BAD_TYPE;
         goto cleanup;
     }
 
+    retval = encode_krb5_sam_challenge_2(&sc2, &encoded_challenge);
+    if (retval) {
+        com_err("krb5kdc", retval,
+                "while encoding SECURID SAM_CHALLENGE_2");
+        goto cleanup;
+    }
+
+    pa_data = k5alloc(sizeof(*pa_data), &retval);
+    if (pa_data == NULL)
+        goto cleanup;
+    pa_data->magic = KV5M_PA_DATA;
+    pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE_2;
+    pa_data->contents = (krb5_octet *)encoded_challenge->data;
+    pa_data->length = encoded_challenge->length;
+    encoded_challenge->data = NULL;
+
 cleanup:
     krb5_free_data(context, encoded_challenge);
     if (sam_db_entry)
@@ -235,6 +291,14 @@ kdc_verify_preauth(krb5_context context, krb5_data *req_pkt,
             goto cleanup;
         break;
 #endif  /* ARL_SECURID_PREAUTH */
+#ifdef GRAIL_PREAUTH
+    case PA_SAM_TYPE_GRAIL:
+        retval = verify_grail_data(context, client, sr2, enc_tkt_reply,
+                                   pa_data, &out_sc2);
+        if (retval)
+            goto cleanup;
+        break;
+#endif /* GRAIL_PREAUTH */
     default:
         retval = KRB5_PREAUTH_BAD_TYPE;
         com_err("krb5kdc", retval, "while verifying SAM 2 data");