svn rev #25191: trunk/src/plugins/preauth/pkinit/

hartmans@MIT.EDU hartmans at MIT.EDU
Sun Sep 18 20:34:57 EDT 2011


http://src.mit.edu/fisheye/changelog/krb5/?cs=25191
Commit By: hartmans
Log Message:
 implementation of new function and test program for pkinit agility.
Implementation of pkinit_alg_agility_kdf() function to implement the
key derivation function defined in draft-ietf-krb-wg-pkinit-alg-agility-04,
and implementation of pkinit_kdf_test program to test the new KDF
against the test vector in the draft.

Signed-off-by: Margaret Wasserman <mrw at painless-security.com>


Changed Files:
U   trunk/src/plugins/preauth/pkinit/Makefile.in
U   trunk/src/plugins/preauth/pkinit/pkinit_crypto.h
U   trunk/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
A   trunk/src/plugins/preauth/pkinit/pkinit_kdf_constants.c
A   trunk/src/plugins/preauth/pkinit/pkinit_kdf_test.c
Modified: trunk/src/plugins/preauth/pkinit/Makefile.in
===================================================================
--- trunk/src/plugins/preauth/pkinit/Makefile.in	2011-09-19 00:34:52 UTC (rev 25190)
+++ trunk/src/plugins/preauth/pkinit/Makefile.in	2011-09-19 00:34:57 UTC (rev 25191)
@@ -8,6 +8,7 @@
 DEFS=@DEFS@
 
 LOCALINCLUDES = -I../../../include/krb5 -I.
+RUN_SETUP = @KRB5_RUN_ENV@
 
 LIBBASE=pkinit
 LIBMAJOR=0
@@ -29,6 +30,7 @@
 	pkinit_srv.o \
 	pkinit_lib.o \
 	pkinit_clnt.o \
+        pkinit_kdf_constants.o \
 	pkinit_profile.o \
 	pkinit_identity.o \
 	pkinit_matching.o \
@@ -38,6 +40,8 @@
 	$(srcdir)/pkinit_accessor.c \
 	$(srcdir)/pkinit_srv.c \
 	$(srcdir)/pkinit_lib.c \
+	$(srcdir)/pkinit_kdf_test.c \
+	$(srcdir)/pkinit_kdf_constants.c \
 	$(srcdir)/pkinit_clnt.c \
 	$(srcdir)/pkinit_profile.c \
 	$(srcdir)/pkinit_identity.c \
@@ -50,7 +54,14 @@
 
 clean::
 	$(RM) lib$(LIBBASE)$(SO_EXT)
+	$(RM) pkinit_test_kdf
 
+check-unix:: pkinit_kdf_test
+	     $(RUN_SETUP) $(VALGRIND) ./pkinit_kdf_test
+
+pkinit_kdf_test: pkinit_kdf_test.o $(STLIBOBJS) $(SHLIB_EXPDEPS)
+		 $(CC_LINK) -o $@ pkinit_kdf_test.o $(STLIBOBJS) $(SHLIB_EXPLIBS)
+
 @libnover_frag@
 @libobj_frag@
 

Modified: trunk/src/plugins/preauth/pkinit/pkinit_crypto.h
===================================================================
--- trunk/src/plugins/preauth/pkinit/pkinit_crypto.h	2011-09-19 00:34:52 UTC (rev 25190)
+++ trunk/src/plugins/preauth/pkinit/pkinit_crypto.h	2011-09-19 00:34:57 UTC (rev 25191)
@@ -631,4 +631,23 @@
 	krb5_prompter_fct prompter,			/* IN */
 	void *prompter_data);				/* IN */
 
+krb5_error_code
+pkinit_alg_agility_kdf(krb5_context context,
+                       krb5_octet_data *secret,
+                       krb5_algorithm_identifier *alg_id,
+                       krb5_principal party_u_info,
+                       krb5_principal party_v_info,
+                       krb5_enctype enctype,
+                       krb5_octet_data *as_req,
+                       krb5_octet_data *pk_as_rep,
+                       const krb5_ticket  *ticket,
+                       krb5_keyblock *key_block);
+
+extern const krb5_octet krb5_pkinit_sha1_oid[];
+extern const size_t krb5_pkinit_sha1_oid_len;
+extern const krb5_octet krb5_pkinit_sha256_oid[];
+extern const size_t krb5_pkinit_sha256_oid_len;
+extern const krb5_octet krb5_pkinit_sha512_oid[];
+extern const size_t  krb5_pkinit_sha512_oid_len;
+
 #endif	/* _PKINIT_CRYPTO_H */

Modified: trunk/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
===================================================================
--- trunk/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c	2011-09-19 00:34:52 UTC (rev 25190)
+++ trunk/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c	2011-09-19 00:34:57 UTC (rev 25191)
@@ -2101,7 +2101,7 @@
                        krb5_enctype etype,
                        unsigned char *key,
                        unsigned int dh_key_len,
-                       krb5_keyblock * key_block)
+                       krb5_keyblock *key_block)
 {
     krb5_error_code retval;
     unsigned char *buf = NULL;
@@ -2140,7 +2140,7 @@
 
     retval = krb5_c_keylengths(context, etype, &keybytes, &keylength);
     if (retval)
-        goto cleanup;
+       goto cleanup;
 
     key_block->length = keylength;
     key_block->contents = malloc(keylength);
@@ -2156,7 +2156,7 @@
 
 cleanup:
     free(buf);
-    // If this is an error return, free the allocated keyblock, if any
+    /* If this is an error return, free the allocated keyblock, if any */
     if (retval) {
         krb5_free_keyblock_contents(context, key_block);
     }
@@ -2164,6 +2164,208 @@
     return retval;
 }
 
+
+/**
+ * Given an algorithm_identifier, this function returns the hash length
+ * and EVP function associated with that algorithm.
+ */
+static krb5_error_code
+pkinit_alg_values(krb5_context context,
+                  krb5_algorithm_identifier *alg_id,
+                  size_t *hash_bytes,
+                  const EVP_MD *(**func)(void))
+{
+    *hash_bytes = 0;
+    *func = NULL;
+    if ((alg_id->algorithm.length == krb5_pkinit_sha1_oid_len) &&
+        (0 == memcmp(alg_id->algorithm.data, &krb5_pkinit_sha1_oid,
+                     krb5_pkinit_sha1_oid_len))) {
+        *hash_bytes = 20;
+        *func = &EVP_sha1;
+        return 0;
+    }
+    else if ((alg_id->algorithm.length == krb5_pkinit_sha256_oid_len) &&
+        (0 == memcmp(alg_id->algorithm.data, krb5_pkinit_sha256_oid,
+                     krb5_pkinit_sha256_oid_len))) {
+        *hash_bytes = 32;
+        *func = &EVP_sha256;
+        return 0;
+    }
+    else if ((alg_id->algorithm.length == krb5_pkinit_sha512_oid_len) &&
+        (0 == memcmp(alg_id->algorithm.data, krb5_pkinit_sha512_oid,
+                     krb5_pkinit_sha512_oid_len))) {
+        *hash_bytes = 32;
+        *func = &EVP_sha512;
+        return 0;
+    }
+    else {
+        krb5_set_error_message(context, KRB5_ERR_BAD_S2K_PARAMS,
+                               "Bad algorithm ID passed to PK-INIT KDF.");
+        return KRB5_ERR_BAD_S2K_PARAMS;
+    }
+} /* pkinit_alg_values() */
+
+
+/* pkinit_alg_agility_kdf() --
+ * This function generates a key using the KDF described in
+ * draft_ietf_krb_wg_pkinit_alg_agility-04.txt.  The algorithm is
+ * described as follows:
+ *
+ *     1.  reps = keydatalen (K) / hash length (H)
+ *
+ *     2.  Initialize a 32-bit, big-endian bit string counter as 1.
+ *
+ *     3.  For i = 1 to reps by 1, do the following:
+ *
+ *         -  Compute Hashi = H(counter || Z || OtherInfo).
+ *
+ *         -  Increment counter (modulo 2^32)
+ *
+ *     4.  Set key = Hash1 || Hash2 || ... so that length of key is K bytes.
+ */
+krb5_error_code
+pkinit_alg_agility_kdf(krb5_context context,
+                       krb5_octet_data *secret,
+                       krb5_algorithm_identifier *alg_id,
+                       krb5_principal party_u_info,
+                       krb5_principal party_v_info,
+                       krb5_enctype enctype,
+                       krb5_octet_data *as_req,
+                       krb5_octet_data *pk_as_rep,
+                       const krb5_ticket *ticket,
+                       krb5_keyblock *key_block)
+{
+    krb5_error_code retval = 0;
+
+    unsigned int reps = 0;
+    uint32_t counter = 1;       /* Does this type work on Windows? */
+    size_t offset = 0;
+    size_t hash_len = 0;
+    unsigned char *rand_buf = NULL;
+    krb5_data random_data;
+    krb5_sp80056a_other_info other_info_fields;
+    krb5_pkinit_supp_pub_info supp_pub_info_fields;
+    krb5_data *other_info = NULL;
+    krb5_data *supp_pub_info = NULL;
+    const EVP_MD *(*EVP_func)(void);
+
+    /* initialize random_data here to make clean-up safe */
+    random_data.length = 0;
+    random_data.data = NULL;
+
+    /* allocate and initialize the key block */
+    key_block->magic = 0;
+    key_block->enctype = enctype;
+    if (0 != (retval = krb5_c_keylengths(context, enctype, (size_t *)NULL,
+                                         (size_t *)&(key_block->length))))
+        goto cleanup;
+    if (NULL == (key_block->contents = malloc(key_block->length))) {
+        retval = ENOMEM;
+        goto cleanup;
+    }
+    memset (key_block->contents, 0, key_block->length);
+
+    if (0 != (retval = pkinit_alg_values(context, alg_id, &hash_len, &EVP_func)))
+        goto cleanup;
+
+    /* 1.  reps = keydatalen (K) / hash length (H) */
+    reps = key_block->length/hash_len;
+
+    /* ... and round up, if necessary */
+    if (key_block->length > (reps * hash_len))
+        reps++;
+
+    /* Allocate enough space in the random data buffer to hash directly into
+     * it, even if the last hash will make it bigger than the key length. */
+    if (NULL == (rand_buf = malloc(reps * hash_len))) {
+        retval = ENOMEM;
+        goto cleanup;
+    }
+    memset(rand_buf, 0, reps * hash_len);
+
+    random_data.length = reps * hash_len;
+    random_data.data = (char *)rand_buf;
+
+    /* Encode the ASN.1 octet string for "SuppPubInfo" */
+    supp_pub_info_fields.enctype = enctype;
+    supp_pub_info_fields.as_req = *as_req;
+    supp_pub_info_fields.pk_as_rep = *pk_as_rep;
+    supp_pub_info_fields.ticket = (krb5_ticket *) ticket;
+    if (0 != ((retval = encode_krb5_pkinit_supp_pub_info(&supp_pub_info_fields,
+                                                         &supp_pub_info))))
+        goto cleanup;
+
+    /* Now encode the ASN.1 octet string for "OtherInfo" */
+    other_info_fields.algorithm_identifier = *alg_id;
+    other_info_fields.party_u_info = party_u_info;
+    other_info_fields.party_v_info = party_v_info;
+    other_info_fields.supp_pub_info = *supp_pub_info;
+    if (0 != (retval = encode_krb5_sp80056a_other_info(&other_info_fields, &other_info)))
+        goto cleanup;
+
+    /* 2.  Initialize a 32-bit, big-endian bit string counter as 1.
+     * 3.  For i = 1 to reps by 1, do the following:
+     *     -   Compute Hashi = H(counter || Z || OtherInfo).
+     *     -   Increment counter (modulo 2^32)
+     */
+    for (counter = 1, offset = 0; ((counter <= reps) && (offset <= key_block->length)); counter++) {
+        EVP_MD_CTX c;
+        uint s = 0;
+        uint32_t be_counter = htonl(counter);
+
+        EVP_MD_CTX_init(&c);
+
+        /* -   Compute Hashi = H(counter || Z || OtherInfo). */
+        if (0 == EVP_DigestInit(&c, EVP_func())) {
+            krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL,
+                                   "Call to OpenSSL EVP_DigestInit() returned an error.");
+            retval = KRB5_CRYPTO_INTERNAL;
+            goto cleanup;
+        }
+
+        if ((0 == EVP_DigestUpdate(&c, &be_counter, 4)) ||
+            (0 == EVP_DigestUpdate(&c, secret->data, secret->length)) ||
+            (0 == EVP_DigestUpdate(&c, other_info->data, other_info->length))) {
+            krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL,
+                               "Call to OpenSSL EVP_DigestUpdate() returned an error.");
+            retval = KRB5_CRYPTO_INTERNAL;
+            goto cleanup;
+        }
+
+        /* 4.  Set key = Hash1 || Hash2 || ... so that length of key is K bytes. */
+        if (0 == EVP_DigestFinal(&c, (rand_buf + offset), &s)) {
+        krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL,
+                                   "Call to OpenSSL EVP_DigestUpdate() returned an error.");
+            retval = KRB5_CRYPTO_INTERNAL;
+            goto cleanup;
+        }
+        offset += s;
+
+        assert(s == hash_len); /* add a message to this assert? */
+
+    EVP_MD_CTX_cleanup(&c);
+    }
+
+    /* Reduce length of random data to key_len to avoid errors. */
+    random_data.length = key_block->length;
+    retval = krb5_c_random_to_key(context, enctype, &random_data,
+                                  key_block);
+
+cleanup:
+    /* If this has been an error, free the allocated key_block, if any */
+    if (retval) {
+        krb5_free_keyblock_contents(context, key_block);
+    }
+
+    /* free other allocated resources, either way */
+    if (rand_buf)
+        free(rand_buf);
+    krb5_free_data(context, other_info);
+    krb5_free_data(context, supp_pub_info);
+
+    return retval;
+} /*pkinit_alg_agility_kdf() */
+
 /* Call DH_compute_key() and ensure that we left-pad short results instead of
  * leaving junk bytes at the end of the buffer. */
 static void

Copied: trunk/src/plugins/preauth/pkinit/pkinit_kdf_constants.c (from rev 25190, trunk/src/util/profile/test_load.c)
===================================================================
--- trunk/src/plugins/preauth/pkinit/pkinit_kdf_constants.c	                        (rev 0)
+++ trunk/src/plugins/preauth/pkinit/pkinit_kdf_constants.c	2011-09-19 00:34:57 UTC (rev 25191)
@@ -0,0 +1,57 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* prototype/prototype.c */
+/*
+ * Copyright (C) 2011 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * pkinit_kdf_test.c -- Structures and constants for implementation of
+ * pkinit algorithm agility.  Includes definitions of algorithm identifiers
+ * for SHA-1, SHA-256 and SHA-512.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "k5-platform.h"
+#include "krb5.h"
+#include "k5-int-pkinit.h"
+
+#include "pkinit.h"
+#include "pkinit_crypto.h"
+
+/* statically declare OID constants for all three algorithms */
+const krb5_octet krb5_pkinit_sha1_oid[10] =
+                    {0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x01};
+const size_t krb5_pkinit_sha1_oid_len = 8;
+const krb5_octet krb5_pkinit_sha256_oid[10] =
+                    {0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x02};
+const size_t krb5_pkinit_sha256_oid_len = 8;
+const krb5_octet krb5_pkinit_sha512_oid [10] =
+                    {0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x03};
+const size_t krb5_pkinit_sha512_oid_len = 8;

Added: trunk/src/plugins/preauth/pkinit/pkinit_kdf_test.c
===================================================================
--- trunk/src/plugins/preauth/pkinit/pkinit_kdf_test.c	                        (rev 0)
+++ trunk/src/plugins/preauth/pkinit/pkinit_kdf_test.c	2011-09-19 00:34:57 UTC (rev 25191)
@@ -0,0 +1,183 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* prototype/prototype.c */
+/*
+ * Copyright (C) 2011 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * pkinit_kdf_test.c -- Test to verify the correctness of the function
+ * pkinit_alg_agility_kdf() in pkinit_crypto_openssl, which implements
+ * the Key Derivation Function from the PKInit Algorithm Agility
+ * document, currently draft-ietf-krb-wg-pkinit-alg-agility-04.txt.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "k5-platform.h"
+
+#include "pkinit_crypto_openssl.h"
+
+/**
+ * Initialize a krb5_data from @a s, a constant string. Note @a s is evaluated
+ * multiple times; this is acceptable for constants.
+ */
+#define DATA_FROM_STRING(s) \
+    {0, sizeof(s)-1, (char *) s}
+
+
+/* values from the test vector in the pkinit-alg-agility draft */
+int secret_len = 256;
+char twenty_as[10];
+char eighteen_bs[9] ;
+char party_u_name [] = "lha at SU.SE";
+char party_v_name [] = "krbtgt/SU.SE at SU.SE";
+int enctype_value = 18;
+krb5_octet key_hex [] =
+  {0xC7, 0x62, 0x89, 0xEC, 0x4B, 0x28, 0xA6, 0x91,
+   0xFF, 0xCE, 0x80, 0xBB, 0xB7, 0xEC, 0x82, 0x41,
+   0x52, 0x3F, 0x99, 0xB1, 0x90, 0xCF, 0x2D, 0x34,
+   0x8F, 0x54, 0xA8, 0x65, 0x81, 0x2C, 0x32, 0x73};
+const krb5_data lha_data = DATA_FROM_STRING("lha");
+const krb5_principal_data ticket_server = {
+    0, /*magic*/
+    DATA_FROM_STRING("SU.SE"),
+    (krb5_data *) &lha_data,
+    1, 1};
+const krb5_ticket test_ticket = {
+    KV5M_TICKET,
+    (krb5_principal) &ticket_server,
+    {0, /*magic*/
+     18,
+     0,
+     DATA_FROM_STRING("hejhej") },
+    NULL};
+
+
+
+int
+main (int argc,
+      char  **argv)
+{
+    /* arguments for calls to pkinit_alg_agility_kdf() */
+    krb5_context context = 0;
+    krb5_octet_data secret;
+    krb5_algorithm_identifier alg_id;
+    krb5_enctype enctype = enctype_value;
+    krb5_octet_data as_req;
+    krb5_octet_data pk_as_rep;
+    krb5_keyblock key_block;
+
+    /* other local variables */
+    int retval = 0;
+    int max_keylen = 2048;
+    krb5_principal u_principal = NULL;
+    krb5_principal v_principal = NULL;
+    krb5_keyblock *key_block_ptr = &key_block;
+
+    /* initialize variables that get malloc'ed, so cleanup is safe */
+    krb5_init_context (&context);
+    memset (&alg_id, 0, sizeof(alg_id));
+    memset (&as_req, 0, sizeof(as_req));
+    memset (&pk_as_rep, 0, sizeof(pk_as_rep));
+    memset (&key_block, 0, sizeof(key_block));
+
+    /* set up algorithm id */
+    alg_id.algorithm.data = (unsigned char *) &krb5_pkinit_sha1_oid;
+    alg_id.algorithm.length = krb5_pkinit_sha1_oid_len;
+
+    /* set up a 256-byte, ALL-ZEROS secret */
+    if (NULL == (secret.data = malloc(secret_len))) {
+        printf("ERROR in pkinit_kdf_test: Memory allocation failed.");
+        retval = ENOMEM;
+        goto cleanup;
+    }
+    secret.length = secret_len;
+    memset(secret.data, 0, secret_len);
+
+    /* set-up the partyUInfo and partyVInfo principals */
+    if ((0 != (retval = krb5_parse_name(context, party_u_name,
+					&u_principal)))
+	(0 != (retval = krb5_parse_name(context, party_v_name,
+					&v_principal)))) {
+      printf("ERROR in pkinit_kdf_test: Error parsing names, retval = %d",
+	     retval);
+      goto cleanup;
+    }
+
+    /* set-up the as_req and and pk_as_rep data */
+    memset(twenty_as, 0xaa, sizeof(twenty_as));
+           memset(eighteen_bs, 0xbb, sizeof(eighteen_bs));
+    as_req.length = sizeof(twenty_as);
+    as_req.data = (unsigned char *)&twenty_as;
+
+    pk_as_rep.length = sizeof(eighteen_bs);
+    pk_as_rep.data = (unsigned char *)&eighteen_bs;
+
+    /* set-up the key_block */
+    if (0 != (retval = krb5_init_keyblock(context, enctype, max_keylen,
+                                          &key_block_ptr))) {
+	  printf("ERROR in pkinit_kdf_test: can't init keybloc, retval = %d",
+		 retval);
+	  goto cleanup;
+
+	}
+
+    /* call krb5_pkinit_alg_agility_kdf() with test vector values*/
+    if (0 != (retval = pkinit_alg_agility_kdf(context, &secret, &alg_id,
+					      u_principal, v_principal,
+					      enctype, &as_req, &pk_as_rep,
+					      &test_ticket, &key_block))) {
+        printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d",
+	       retval);
+	goto cleanup;
+    }
+
+    /* compare key to expected key value */
+
+    if ((key_block.length == sizeof(key_hex)) &&
+            (0 == memcmp(key_block.contents, key_hex, key_block.length))) {
+            printf("SUCCESS: Correct key value generated!");
+            retval = 0;
+        }
+        else {
+            printf("FAILURE: Incorrect key value generated!");
+            retval = 1;
+        }
+
+    cleanup:
+	/* release all allocated resources, whether good or bad return */
+	if (secret.data)
+	  free(secret.data);
+	if (u_principal)
+	  free(u_principal);
+	if (v_principal)
+	  free(v_principal);
+		krb5_free_keyblock_contents(context, &key_block);
+	exit(retval);
+}




More information about the cvs-krb5 mailing list