auth: Rewrite KeyFile handling code
authorSimon Wilkinson <sxw@your-file-system.com>
Sat, 1 Jan 2011 23:41:29 +0000 (23:41 +0000)
committerDerrick Brashear <shadow@dementia.org>
Sun, 13 Feb 2011 18:39:53 +0000 (10:39 -0800)
Extend the KeyFile API so that we can support arbitrary numbers of
different key types, each with their own key version numbers and
sub types. Completely rewrite the KeyFile implementation with this
in mind, but implement all of the "old" API in terms of the new one.

Given that the existing KeyFile is modified by third party programs,
we retain that as the storage location for all afsconf_rxkad keys.
Only keys with a type of 1, or above are stored in the new extended
keyfile.

Change-Id: I903a1de938544541a1bfecedb2a039ba24bdfdbc
Reviewed-on: http://gerrit.openafs.org/3772
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Derrick Brashear <shadow@dementia.org>

src/auth/acfg_errors.et
src/auth/cellconfig.c
src/auth/cellconfig.p.h
src/auth/internal.h
src/auth/keys.c
src/util/dirpath.hin
src/util/dirpath_nt.h
tests/auth/Makefile.in
tests/auth/keys-t.c

index 031d493..a9e5950 100644 (file)
@@ -19,4 +19,5 @@ error_table ACFG
   ec AFSCONF_FULL, "no more entries"
   ec AFSCONF_NOCELLDB, "unable to open cell database"
   ec AFSCONF_NO_SECURITY_CLASS, "unable to build security class"
+  ec AFSCONF_BADKEY, "key doesn't meet requirements"
 end
index 0f52c47..88032ea 100644 (file)
@@ -846,10 +846,11 @@ afsconf_OpenInternal(struct afsconf_dir *adir, char *cell,
     if (tf != NULL)
        fclose(tf);
     /* now read the fs keys, if possible */
-    adir->keystr = (struct afsconf_keys *)0;
-    _afsconf_IntGetKeys(adir);
 
-    return 0;
+    _afsconf_InitKeys(adir);
+    code = _afsconf_LoadKeys(adir);
+
+    return code;
 }
 
 /* parse a line of the form
@@ -1564,8 +1565,8 @@ afsconf_CloseInternal(struct afsconf_dir *adir)
        na = ta->next;
        free(ta);
     }
-    if (adir->keystr)
-       free(adir->keystr);
+
+    _afsconf_FreeAllKeys(adir);
 
     /* reinit */
     memset(adir, 0, sizeof(struct afsconf_dir));
index 05b6bdf..cf137f9 100644 (file)
@@ -37,6 +37,8 @@ Creation date:
 #include <netinet/in.h>
 #endif
 #endif
+#include <rx/rx_opaque.h>
+#include <opr/queue.h>
 
 #define        MAXCELLCHARS    64
 #define        MAXHOSTCHARS    64
@@ -81,7 +83,7 @@ struct afsconf_dir {
     char *name;                        /* pointer to dir prefix */
     char *cellName;            /* cell name, if any, we're in */
     struct afsconf_entry *entries;     /* list of cell entries */
-    struct afsconf_keys *keystr;       /* structure containing keys */
+    struct opr_queue keyList;          /* list of keys */
     afs_int32 timeRead;                /* time stamp of file last read */
     struct afsconf_aliasentry *alias_entries;  /* cell aliases */
 };
@@ -112,8 +114,11 @@ extern int afsconf_GetLocalCell(struct afsconf_dir *adir,
                                char *aname, afs_int32 alen);
 extern int afsconf_Close(struct afsconf_dir *adir);
 extern int afsconf_UpToDate(struct afsconf_dir *adir);
+
+struct afsconf_keys;
 extern int afsconf_GetKeys(struct afsconf_dir *adir,
                           struct afsconf_keys *astr);
+
 struct ktc_encryptionKey;
 extern afs_int32 afsconf_GetLatestKey(struct afsconf_dir *adir,
                                      afs_int32 * avno,
@@ -124,6 +129,53 @@ extern int afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno,
                          char akey[8], afs_int32 overwrite);
 extern int afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno);
 
+struct afsconf_typedKey;
+struct afsconf_typedKeyList {
+    int nkeys;
+    struct afsconf_typedKey **keys;
+};
+
+typedef enum {
+    afsconf_rxkad = 0,
+    afsconf_rxgk  =1
+} afsconf_keyType;
+
+extern struct afsconf_typedKey *
+       afsconf_typedKey_get(struct afsconf_typedKey *);
+extern void afsconf_typedKey_put(struct afsconf_typedKey **);
+extern struct afsconf_typedKey *
+       afsconf_typedKey_new(afsconf_keyType type, int kvno,
+                            int subType, struct rx_opaque *key);
+extern void afsconf_typedKey_free(struct afsconf_typedKey **);
+
+extern void afsconf_typedKey_values(struct afsconf_typedKey *key,
+                                 afsconf_keyType *type,
+                                 int *kvno,
+                                 int *minorType,
+                                 struct rx_opaque **keyMaterial);
+
+extern int afsconf_GetKeysByType(struct afsconf_dir *dir,
+                                afsconf_keyType type, int kvno,
+                                struct afsconf_typedKeyList **);
+extern int afsconf_GetKeyByTypes(struct afsconf_dir *dir,
+                                afsconf_keyType type, int kvno, int subType,
+                                struct afsconf_typedKey **);
+extern int afsconf_GetLatestKeysByType(struct afsconf_dir *dir,
+                                      afsconf_keyType type,
+                                      struct afsconf_typedKeyList **);
+extern int afsconf_GetLatestKeyByTypes(struct afsconf_dir *dir,
+                                      afsconf_keyType type, int subType,
+                                      struct afsconf_typedKey **);
+extern void afsconf_PutTypedKeyList(struct afsconf_typedKeyList **keys);
+extern int afsconf_AddTypedKey(struct afsconf_dir *dir,
+                              struct afsconf_typedKey *key,
+                              int overwrite);
+extern int afsconf_DeleteKeyByType(struct afsconf_dir *dir,
+                                  afsconf_keyType type, int kvno);
+extern int afsconf_DeleteKeyBySubType(struct afsconf_dir *dir,
+                                     afsconf_keyType type, int kvno,
+                                     int subType);
+
 /* authcon.c */
 struct rx_securityClass;
 extern afs_int32 afsconf_ServerAuth(void *arock,
index 843e3ee..94b42d5 100644 (file)
@@ -2,3 +2,6 @@ extern int _afsconf_Check(struct afsconf_dir *adir);
 extern int _afsconf_Touch(struct afsconf_dir *adir);
 extern int _afsconf_IntGetKeys(struct afsconf_dir *adir);
 extern int _afsconf_IsClientConfigDirectory(const char *path);
+extern int _afsconf_LoadKeys(struct afsconf_dir *adir);
+extern void _afsconf_InitKeys(struct afsconf_dir *adir);
+extern void _afsconf_FreeAllKeys(struct afsconf_dir *adir);
index df8a618..e1440da 100644 (file)
@@ -1,18 +1,37 @@
 /*
- * Copyright 2000, International Business Machines Corporation and others.
- * All Rights Reserved.
+ * Copyright (c) 2010 Your File System Inc. All rights reserved.
  *
- * This software has been released under the terms of the IBM Public
- * License.  For details, see the LICENSE file in the top-level source
- * directory or online at http://www.openafs.org/dl/license10.html
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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 AUTHOR `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 AUTHOR 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.
  */
+
 #include <afsconfig.h>
 #include <afs/param.h>
 
 #include <roken.h>
 
+#include <opr/queue.h>
+
 /* Need rx/rx.h to get working assert(), used by LOCK_GLOBAL_MUTEX */
 #include <rx/rx.h>
+#include <rx/rx_atomic.h>
 
 #include <afs/stds.h>
 #include <afs/pthread_glock.h>
 #include "keys.h"
 #include "internal.h"
 
-/* called during opening of config file */
-int
-_afsconf_IntGetKeys(struct afsconf_dir *adir)
+struct afsconf_typedKey {
+    rx_atomic_t refcnt;
+    afsconf_keyType type;
+    int kvno;
+    int subType;
+    struct rx_opaque key;
+};
+
+static struct afsconf_typedKey *afsconf_typedKey_blank(void);
+
+/* Memory storage for keyfile contents. */
+
+struct keyTypeList {
+    struct opr_queue link;
+    afsconf_keyType type;
+    struct opr_queue kvnoList;
+};
+
+struct kvnoList {
+    struct opr_queue link;
+    int kvno;
+    struct opr_queue subTypeList;
+};
+
+struct subTypeList {
+    struct opr_queue link;
+    int subType;
+    struct afsconf_typedKey *key;
+};
+
+static int
+listToArray(struct kvnoList *kvnoEntry, struct afsconf_typedKeyList **keys)
 {
-    char tbuffer[256];
-    int fd;
-    struct afsconf_keys *tstr;
-    afs_int32 code;
+    struct afsconf_typedKeyList *retval;
+    struct opr_queue *cursor;
+    int i;
 
-#ifdef AFS_NT40_ENV
-    /* NT client config dir has no KeyFile; don't risk attempting open
-     * because there might be a random file of this name if dir is shared.
-     */
-    if (_afsconf_IsClientConfigDirectory(adir->name)) {
-       adir->keystr = ((struct afsconf_keys *)
-                       malloc(sizeof(struct afsconf_keys)));
-       adir->keystr->nkeys = 0;
-       return 0;
+    /* Allocate space for the keys we've got stored */
+    retval = malloc(sizeof(struct afsconf_typedKeyList));
+    retval->nkeys = opr_queue_Count(&kvnoEntry->subTypeList);
+    retval->keys  = calloc(retval->nkeys, sizeof(struct afsconf_typedKey *));
+
+    i = 0;
+    for(opr_queue_Scan(&kvnoEntry->subTypeList, cursor)) {
+       struct subTypeList *entry;
+
+       entry = opr_queue_Entry(cursor, struct subTypeList, link);
+       retval->keys[i] = afsconf_typedKey_get(entry->key);
+       i++;
     }
-#endif /* AFS_NT40_ENV */
 
-    LOCK_GLOBAL_MUTEX;
-    /* compute the key name and other setup */
-    strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
-    tstr = (struct afsconf_keys *)malloc(sizeof(struct afsconf_keys));
-    adir->keystr = tstr;
-
-    /* read key file */
-    fd = open(tbuffer, O_RDONLY);
-    if (fd < 0) {
-       tstr->nkeys = 0;
-       UNLOCK_GLOBAL_MUTEX;
+    *keys = retval;
+    return 0;
+}
+
+static struct keyTypeList *
+findByType(struct afsconf_dir *dir, afsconf_keyType type)
+{
+    struct opr_queue *cursor;
+    struct keyTypeList *entry = NULL;
+
+    for (opr_queue_Scan(&dir->keyList, cursor)) {
+       entry = opr_queue_Entry(cursor, struct keyTypeList, link);
+       if (entry->type >= type)
+           break;
+    }
+    if (entry == NULL || entry->type != type)
+       return NULL;
+
+    return entry;
+}
+
+static struct kvnoList *
+findInTypeList(struct keyTypeList *parent, int kvno)
+{
+    struct opr_queue *cursor;
+    struct kvnoList *entry = NULL;
+
+    for (opr_queue_Scan(&parent->kvnoList, cursor)) {
+       entry = opr_queue_Entry(cursor, struct kvnoList, link);
+       if (entry->kvno >= kvno)
+           break;
+    }
+    if (entry == NULL || entry->kvno != kvno)
+       return NULL;
+
+    return entry;
+}
+
+static struct kvnoList *
+findByKvno(struct afsconf_dir *dir, afsconf_keyType type, int kvno)
+{
+    struct keyTypeList *entry;
+    entry = findByType(dir, type);
+
+    if (entry == NULL)
+       return NULL;
+
+    return findInTypeList(entry, kvno);
+}
+
+static struct subTypeList *
+findInKvnoList(struct kvnoList *parent, int subType)
+{
+    struct opr_queue *cursor;
+    struct subTypeList *entry = NULL;
+
+    for (opr_queue_Scan(&parent->subTypeList, cursor)) {
+       entry = opr_queue_Entry(cursor, struct subTypeList, link);
+       if (entry->subType >= subType)
+            break;
+    }
+    if (entry == NULL || entry->subType != subType)
+       return NULL;
+
+    return entry;
+}
+
+static struct subTypeList *
+findBySubType(struct afsconf_dir *dir, afsconf_keyType type, int kvno,
+             int subType)
+{
+   struct kvnoList *entry;
+
+   entry = findByKvno(dir, type, kvno);
+   if (entry == NULL)
+       return NULL;
+
+   return findInKvnoList(entry, subType);
+}
+
+
+/* Add key. */
+static int
+addMemoryKey(struct afsconf_dir *dir, struct afsconf_typedKey *key,
+            int overwrite)
+{
+    struct opr_queue   *cursor;
+    struct keyTypeList *typeEntry = NULL;
+    struct kvnoList    *kvnoEntry = NULL;
+    struct subTypeList *subType   = NULL;
+
+    /* Find the place in the keyType list to insert the key into */
+    for (opr_queue_Scan(&dir->keyList, cursor)) {
+       typeEntry = opr_queue_Entry(cursor, struct keyTypeList, link);
+       if (typeEntry->type >= key->type)
+           break;
+    }
+
+    if (typeEntry == NULL || typeEntry->type != key->type) {
+       struct keyTypeList *list;
+
+       list = malloc(sizeof(struct keyTypeList));
+       opr_queue_Init(&list->kvnoList);
+       list->type = key->type;
+       opr_queue_InsertBefore(cursor, &list->link);
+       typeEntry = list;
+    }
+
+    /* And the place in the kvno list */
+    for (opr_queue_Scan(&typeEntry->kvnoList, cursor)) {
+       kvnoEntry = opr_queue_Entry(cursor, struct kvnoList, link);
+       if (kvnoEntry->kvno >= key->kvno)
+           break;
+    }
+
+    if (kvnoEntry == NULL || kvnoEntry->kvno != key->kvno) {
+       struct kvnoList *list;
+
+       /* In the legacy rxkad key case, we need to check to see if we've
+        * gone over the maximum of 8 keys */
+       if (key->type == afsconf_rxkad &&
+           opr_queue_Count(&typeEntry->kvnoList)>=8)
+           return AFSCONF_FULL;
+
+       list = malloc(sizeof(struct kvnoList));
+       opr_queue_Init(&list->subTypeList);
+       list->kvno = key->kvno;
+       opr_queue_InsertBefore(cursor, &list->link);
+       kvnoEntry = list;
+    }
+
+    /* And the place in the subtype list */
+    for (opr_queue_Scan(&kvnoEntry->subTypeList, cursor)) {
+       subType = opr_queue_Entry(cursor, struct subTypeList, link);
+       if (subType->subType >= key->subType)
+           break;
+    }
+
+    if (subType == NULL || subType->subType != key->subType) {
+       struct subTypeList *list;
+
+       list = malloc(sizeof(struct subTypeList));
+       list->subType = key->subType;
+       list->key = afsconf_typedKey_get(key);
+       opr_queue_InsertBefore(cursor, &list->link);
+    } else {
+       if (overwrite) {
+           /* Give up our reference to the existing key */
+           afsconf_typedKey_put(&subType->key);
+           subType->key = afsconf_typedKey_get(key);
+       } else {
+           return AFSCONF_KEYINUSE;
+       }
+    }
+    return 0;
+}
+
+static void
+deleteKvnoEntry(struct kvnoList *entry)
+{
+    struct subTypeList *subTypeEntry;
+
+    while (!opr_queue_IsEmpty(&entry->subTypeList)) {
+       subTypeEntry = opr_queue_First(&entry->subTypeList,
+                                      struct subTypeList, link);
+       afsconf_typedKey_put(&subTypeEntry->key);
+       opr_queue_Remove(&subTypeEntry->link);
+       free(subTypeEntry);
+    }
+    opr_queue_Remove(&entry->link);
+    free(entry);
+}
+
+void
+_afsconf_FreeAllKeys(struct afsconf_dir *dir)
+{
+    struct keyTypeList *typeEntry;
+    struct kvnoList *kvnoEntry;
+
+    while (!opr_queue_IsEmpty(&dir->keyList)) {
+       typeEntry = opr_queue_First(&dir->keyList, struct keyTypeList, link);
+
+       while (!opr_queue_IsEmpty(&typeEntry->kvnoList)) {
+           kvnoEntry = opr_queue_First(&typeEntry->kvnoList,
+                                       struct kvnoList, link);
+
+           deleteKvnoEntry(kvnoEntry);
+       }
+       opr_queue_Remove(&typeEntry->link);
+       free(typeEntry);
+    }
+}
+void
+_afsconf_InitKeys(struct afsconf_dir *dir)
+{
+    opr_queue_Init(&dir->keyList);
+}
+
+/* Disk based key storage. This is made somewhat complicated because we
+ * store keys in more than one place - keys of type 'rxkad' (0) are stored
+ * in the original KeyFile, so that we can continue to be compatible with
+ * utilities that directly modify that file.
+ *
+ * All other keys are stored in the file KeyFileEx, which has the following
+ * format:
+ *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
+ *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *   |                        version number                           |
+ *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *   |                        number of keys                           |
+ *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *   | Key data ...
+ *   +-+-+-+-+-+-+-+
+ *
+ * The version number is 1 at present. Version numbers higher than 1
+ * indicate a keyfile that is not backwards compatible with this
+ * specification.
+ *
+ * Key data is a sequence of the following records (note that these are
+ * not word aligned - the next record begins where the previous one ends)
+ *
+ *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
+ *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *   |                         meta-data length                        |
+ *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *   |                           key type                              |
+ *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *   |                       key version number                        |
+ *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *   |                         key sub type                            |
+ *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *   |                      length of key material                     |
+ *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *   | Key material
+ *   +-+-+-+-+-+-+-+
+ *
+ * All values are expressed in network byte order
+ *
+ * Meta data length is the length of the initial portion of the record
+ * (itself, key type, key version number, and key sub type). In this
+ * version of the specification it would be 16. It is there to allow
+ * additional fields to be added to this specification at a later date
+ * without breaking backwards compatibility.
+ */
+
+/* XXX - We need to be careful with failure here, because failure due to
+ * a missing file is fine, but failure due to read errors needs to be trapped,
+ * before it results in a corrupted file being written out.
+ */
+
+static int
+_parseOriginalKeyFile(struct afsconf_dir *dir, char *fileName)
+{
+    int fd, code, nkeys, i;
+    struct afsconf_typedKey *key;
+
+    fd = open(fileName, O_RDONLY);
+    if (fd < 0)
        return 0;
+
+    code = read(fd, &nkeys, sizeof(afs_int32));
+    if (code!= sizeof(afs_int32))
+       goto fail;
+
+    nkeys=ntohl(nkeys);
+    for(i=0; i<nkeys; i++) {
+
+       key = afsconf_typedKey_blank();
+
+       key->type = afsconf_rxkad;
+       key->subType = 0;
+
+       code = read(fd, &key->kvno, sizeof(afs_int32));
+       if (code != sizeof(afs_int32)) {
+           free(key);
+           goto fail;
+       }
+       key->kvno = ntohl(key->kvno);
+
+       rx_opaque_alloc(&key->key, 8);
+       code = read(fd, key->key.val, 8);
+       if (code != 8) {
+           rx_opaque_freeContents(&key->key);
+           free(key);
+           goto fail;
+       }
+       code = addMemoryKey(dir, key, 1);
+       afsconf_typedKey_put(&key); /* Done with key */
+       if (code)
+           goto fail;
     }
-    code = read(fd, tstr, sizeof(struct afsconf_keys));
     close(fd);
-    if (code < sizeof(afs_int32)) {
-       tstr->nkeys = 0;
-       UNLOCK_GLOBAL_MUTEX;
-       return 0;
+    return 0;
+
+fail:
+    close(fd);
+    return EIO;
+}
+
+static_inline int
+writeWord(int fd, afs_int32 data)
+{
+
+     data = htonl(data);
+
+     if (write(fd, &data, sizeof(afs_int32)) != sizeof(afs_int32))
+       return EIO;
+
+     return 0;
+}
+
+static int
+_writeOriginalKeyFile(struct afsconf_dir *dir, char *fileName)
+{
+    int nkeys, fd;
+    struct opr_queue *cursor;
+    struct keyTypeList *typeEntry;
+
+    fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
+    if (fd < 0)
+       return AFSCONF_FAILURE;
+
+    typeEntry = findByType(dir, afsconf_rxkad);
+    if (typeEntry == NULL)
+       goto out;
+
+    nkeys = opr_queue_Count(&typeEntry->kvnoList);
+
+    if (writeWord(fd, nkeys))
+       goto fail;
+
+    for (opr_queue_Scan(&typeEntry->kvnoList, cursor)) {
+       struct kvnoList *kvnoEntry;
+        struct subTypeList *subEntry;
+
+       kvnoEntry = opr_queue_Entry(cursor, struct kvnoList, link);
+        subEntry = opr_queue_First(&kvnoEntry->subTypeList,
+                                  struct subTypeList, link);
+       if (writeWord(fd, subEntry->key->kvno))
+           goto fail;
+       if (write(fd, subEntry->key->key.val, 8) != 8)
+           goto fail;
     }
 
-    /* convert key structure to host order */
-    tstr->nkeys = ntohl(tstr->nkeys);
+out:
+    close(fd);
+    return 0;
+
+fail:
+    close(fd);
+    return AFSCONF_FAILURE;
+}
+
+static int
+_parseExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
+{
+    int fd, i, code;
+    afs_int32 nkeys;
+    struct afsconf_typedKey *key;
 
-    if (code < sizeof(afs_int32) + (tstr->nkeys*sizeof(struct afsconf_key))) {
-       tstr->nkeys = 0;
-       UNLOCK_GLOBAL_MUTEX;
+    fd = open(fileName, O_RDONLY);
+    if (fd < 0)
        return 0;
+
+    code = read(fd, &nkeys, sizeof(afs_int32));
+    if (code!= sizeof(afs_int32))
+       goto fail;
+
+    nkeys=ntohl(nkeys);
+    for(i=0; i<nkeys; i++) {
+       afs_int32 reclen;
+
+       key = afsconf_typedKey_blank();
+
+       /* The only data version we currently parse has a reclen of 16.
+        * Anything smaller indicates a corrupt key file. Anything more,
+        * and we just skip the extra fields */
+       code = read(fd, &reclen, sizeof(afs_int32));
+       if (code != sizeof(afs_int32))
+           goto fail;
+       reclen = ntohl(reclen);
+       if (reclen < 16)
+           goto fail;
+       reclen-=sizeof(afs_int32);
+
+       code = read(fd, &key->type, sizeof(afs_int32));
+       if (code != sizeof(afs_int32))
+           goto fail;
+       key->type = ntohl(key->type);
+       reclen-=sizeof(afs_int32);
+
+       code = read(fd, &key->kvno, sizeof(afs_int32));
+       if (code != sizeof(afs_int32))
+           goto fail;
+       key->kvno = ntohl(key->kvno);
+       reclen-=sizeof(afs_int32);
+
+       code = read(fd, &key->subType, sizeof(afs_int32));
+       if (code != sizeof(afs_int32))
+           goto fail;
+       key->subType = ntohl(key->subType);
+       reclen-=sizeof(afs_int32);
+
+       if (reclen > 0) {
+           code = lseek(fd, reclen, SEEK_CUR);
+           if (code < 0)
+               goto fail;
+       }
+
+       code = read(fd, &reclen, sizeof(afs_int32));
+       if (code != sizeof(afs_int32))
+           goto fail;
+       reclen = ntohl(reclen);
+
+       rx_opaque_alloc(&key->key, reclen);
+       code = read(fd, key->key.val, reclen);
+       if (code != reclen) {
+           rx_opaque_freeContents(&key->key);
+           free(key);
+           goto fail;
+       }
+       code = addMemoryKey(dir, key, 1);
+       afsconf_typedKey_put(&key);
+       if (code)
+           goto fail;
     }
+    close(fd);
+    return 0;
 
-    for (fd = 0; fd < tstr->nkeys; fd++)
-       tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
+fail:
+    close(fd);
+    return EIO;
+}
+
+
+static int
+_writeExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
+{
+    int nkeys;
+    int fd;
+
+    struct keyTypeList *typeEntry;
+    struct kvnoList    *kvnoEntry;
+    struct subTypeList *entry;
+    struct opr_queue   *keyCursor;
+    struct opr_queue   *kvnoCursor;
+    struct opr_queue   *subCursor;
+
+    fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
+    if (fd < 0)
+       return AFSCONF_FAILURE;
+
+    /* Iterate over the whole in-memory key store, and write everything
+     * except keys with type rxkad into the extended key file */
+
+    /* Write a 0 key count - we'll fill it in later */
+    nkeys = 0;
+    if (writeWord(fd, 0))
+       goto fail;
+
+    for (opr_queue_Scan(&dir->keyList, keyCursor)) {
+       typeEntry = opr_queue_Entry(keyCursor, struct keyTypeList, link);
+
+       if (typeEntry->type != afsconf_rxkad) {
+           for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
+               kvnoEntry = opr_queue_Entry(kvnoCursor, struct kvnoList, link);
+               for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) {
+                   entry = opr_queue_Entry(subCursor, struct subTypeList, link);
+                   if (writeWord(fd, 16)) /* record length */
+                       goto fail;
+                   if (writeWord(fd, entry->key->type))
+                       goto fail;
+                   if (writeWord(fd, entry->key->kvno))
+                       goto fail;
+                   if (writeWord(fd, entry->key->subType))
+                       goto fail;
+                   if (writeWord(fd, entry->key->key.len))
+                       goto fail;
+                   if (write(fd, entry->key->key.val,
+                             entry->key->key.len) !=
+                       entry->key->key.len)
+                       goto fail;
+                   nkeys++;
+               }
+           }
+       }
+    }
+
+    if (lseek(fd, 0, SEEK_SET)<0)
+       goto fail;
+
+    if (writeWord(fd, nkeys))
+       goto fail;
+
+    close(fd);
 
-    UNLOCK_GLOBAL_MUTEX;
     return 0;
+
+fail:
+    close(fd);
+    return AFSCONF_FAILURE;
 }
 
+int
+_afsconf_LoadKeys(struct afsconf_dir *dir)
+{
+    int code;
+    char *fileName;
+
+    /* If we're running on Windows, and we are a client, we don't have a
+     * KeyFile, so don't try and open one */
+
+#ifdef AFS_NT40_ENV
+    if (_afsconf_IsClientConfigDirectory(dir->name))
+       return 0;
+#endif /* AFS_NT40_ENV */
+
+    LOCK_GLOBAL_MUTEX;
+
+    /* Delete all of our existing keys */
+    _afsconf_FreeAllKeys(dir);
+
+    /* Start by opening the original KeyFile */
+    asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_KEY_FILE);
+    code = _parseOriginalKeyFile(dir, fileName);
+    free(fileName);
+    if (code)
+       goto out;
+
+    /* Now open the new style KeyFile */
+    asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_EXT_KEY_FILE);
+    code = _parseExtendedKeyFile(dir, fileName);
+    free(fileName);
+    if (code)
+       goto out;
+
+out:
+    if (code)
+       _afsconf_FreeAllKeys(dir);
+
+    UNLOCK_GLOBAL_MUTEX;
+
+    return code;
+}
+
+static int
+_afsconf_SaveKeys(struct afsconf_dir *dir)
+{
+    char *fileName;
+    int code;
+
+    /* If we're running on Windows, and we are a client, we don't have a
+     * KeyFile, so don't try and open one */
+
+#ifdef AFS_NT40_ENV
+    if (_afsconf_IsClientConfigDirectory(dir->name))
+       return 0;
+#endif /* AFS_NT40_ENV */
+
+    LOCK_GLOBAL_MUTEX;
+
+    /* Start by opening the original KeyFile */
+    asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_KEY_FILE);
+    code = _writeOriginalKeyFile(dir, fileName);
+    free(fileName);
+    if (code)
+       goto out;
+
+    /* Now open the new style KeyFile */
+    asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_EXT_KEY_FILE);
+    code = _writeExtendedKeyFile(dir, fileName);
+    free(fileName);
+    if (code)
+       goto out;
+
+out:
+    UNLOCK_GLOBAL_MUTEX;
+
+    return code;
+}
+
+
+
 /* get keys structure */
 int
-afsconf_GetKeys(struct afsconf_dir *adir, struct afsconf_keys *astr)
+afsconf_GetKeys(struct afsconf_dir *dir, struct afsconf_keys *astr)
 {
     afs_int32 code;
+    struct keyTypeList *typeEntry;
+    struct opr_queue *cursor;
+
+    memset(astr, 0, sizeof(struct afsconf_keys));
 
     LOCK_GLOBAL_MUTEX;
-    code = _afsconf_Check(adir);
-    if (code) {
-       UNLOCK_GLOBAL_MUTEX;
-       return AFSCONF_FAILURE;
+
+    code = _afsconf_Check(dir);
+    if (code)
+       goto out;
+
+    typeEntry = findByType(dir, afsconf_rxkad);
+    if (typeEntry == NULL)
+       goto out;
+
+    for (opr_queue_Scan(&typeEntry->kvnoList, cursor)) {
+       struct kvnoList *kvnoEntry;
+        struct subTypeList *subEntry;
+
+       kvnoEntry = opr_queue_Entry(cursor, struct kvnoList, link);
+        subEntry = opr_queue_First(&kvnoEntry->subTypeList,
+                                  struct subTypeList, link);
+       /* XXX - If there is more than one key in this list, it's an error */
+       astr->key[astr->nkeys].kvno = subEntry->key->kvno;
+       /* XXX - If the opaque contains a number of bytes other than 8, it's
+        * an error */
+       memcpy(&astr->key[astr->nkeys].key, subEntry->key->key.val, 8);
+       astr->nkeys++;
     }
-    memcpy(astr, adir->keystr, sizeof(struct afsconf_keys));
+
+out:
     UNLOCK_GLOBAL_MUTEX;
-    return 0;
+    return code;
 }
 
-/* get latest key */
 afs_int32
-afsconf_GetLatestKey(struct afsconf_dir * adir, afs_int32 * avno,
-                    struct ktc_encryptionKey *akey)
+afsconf_GetLatestKey(struct afsconf_dir *dir, afs_int32 *kvno,
+                    struct ktc_encryptionKey *key)
 {
-    int i;
-    int maxa;
-    struct afsconf_key *tk;
-    afs_int32 best;
-    struct afsconf_key *bestk;
-    afs_int32 code;
+    struct afsconf_typedKey *typedKey;
+    int code;
 
-    LOCK_GLOBAL_MUTEX;
-    code = _afsconf_Check(adir);
-    if (code) {
-       UNLOCK_GLOBAL_MUTEX;
+    code = afsconf_GetLatestKeyByTypes(dir, afsconf_rxkad, 0, &typedKey);
+    if (code)
+       return code;
+
+    /* XXX - Should check that the key is of the correct length */
+
+    /* Copy out the relevant details */
+    *kvno = typedKey->kvno;
+    memcpy(key, typedKey->key.val, 8);
+
+    afsconf_typedKey_put(&typedKey);
+
+    return 0;
+}
+
+int
+afsconf_GetKey(void *rock, int kvno, struct ktc_encryptionKey *key)
+{
+    struct afsconf_typedKey *typedKey;
+    int code;
+
+    code = afsconf_GetKeyByTypes(rock, afsconf_rxkad, kvno, 0, &typedKey);
+    if (code)
+       return code;
+
+    memcpy(key, typedKey->key.val, 8);
+
+    afsconf_typedKey_put(&typedKey);
+
+    return 0;
+}
+
+int
+afsconf_AddKey(struct afsconf_dir *dir, afs_int32 kvno, char key[8],
+              afs_int32 overwrite)
+{
+    struct rx_opaque buffer;
+    struct afsconf_typedKey *typedKey;
+    int code;
+
+    rx_opaque_alloc(&buffer, 8);
+    memcpy(buffer.val, key, 8);
+    typedKey = afsconf_typedKey_new(afsconf_rxkad, kvno, 0, &buffer);
+    if (typedKey == NULL)
        return AFSCONF_FAILURE;
+
+    rx_opaque_freeContents(&buffer);
+
+    code = afsconf_AddTypedKey(dir, typedKey, overwrite);
+    afsconf_typedKey_put(&typedKey);
+    return code;
+}
+
+int
+afsconf_DeleteKey(struct afsconf_dir *dir, afs_int32 kvno)
+{
+    return afsconf_DeleteKeyByType(dir, afsconf_rxkad, kvno);
+}
+
+int
+afsconf_GetKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
+                     int kvno, struct afsconf_typedKeyList **keys)
+{
+    struct kvnoList *kvnoEntry;
+    int code;
+
+    LOCK_GLOBAL_MUTEX;
+
+    code = _afsconf_Check(dir);
+    if (code)
+       goto out;
+
+    kvnoEntry = findByKvno(dir, type, kvno);
+    if (kvnoEntry == NULL) {
+       code = AFSCONF_NOTFOUND;
+        goto out;
     }
-    maxa = adir->keystr->nkeys;
-
-    best = -1;                 /* highest kvno we've seen yet */
-    bestk = (struct afsconf_key *)0;   /* ptr to structure providing best */
-    for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
-       if (tk->kvno == 999)
-           continue;           /* skip bcrypt keys */
-       if (tk->kvno > best) {
-           best = tk->kvno;
-           bestk = tk;
-       }
+
+    code = listToArray(kvnoEntry, keys);
+
+out:
+    UNLOCK_GLOBAL_MUTEX;
+    return code;
+}
+
+int
+afsconf_GetKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
+                     int kvno, int subType, struct afsconf_typedKey **key)
+{
+    int code = 0;
+    struct subTypeList *subTypeEntry;
+
+    LOCK_GLOBAL_MUTEX;
+
+    code = _afsconf_Check(dir);
+    if (code)
+       goto out;
+
+    subTypeEntry = findBySubType(dir, type, kvno, subType);
+    if (subTypeEntry == NULL) {
+       code = AFSCONF_NOTFOUND;
+       goto out;
     }
-    if (bestk) {               /* found any  */
-       if (akey)
-           memcpy(akey, bestk->key, 8);        /* copy out latest key */
-       if (avno)
-           *avno = bestk->kvno;        /* and kvno to caller */
-       UNLOCK_GLOBAL_MUTEX;
-       return 0;
+
+    *key = afsconf_typedKey_get(subTypeEntry->key);
+
+out:
+    UNLOCK_GLOBAL_MUTEX;
+    return code;
+}
+
+static struct kvnoList *
+pickBestKvno(struct afsconf_dir *dir, afsconf_keyType type)
+{
+    struct keyTypeList *typeEntry;
+    struct kvnoList    *kvnoEntry;
+
+    typeEntry = findByType(dir, type);
+    if (typeEntry == NULL)
+       return NULL;
+
+    /* We store all of the key lists ordered, so the last entry in the
+     * kvno list must be the highest kvno. */
+
+    kvnoEntry = opr_queue_Last(&typeEntry->kvnoList, struct kvnoList, link);
+
+    /* Except, if we're in the rxkad list, we might have a bcrypt entry that
+     * has a kvno of 999. So we need to skip that one
+     */
+    while (type == afsconf_rxgk && kvnoEntry->kvno == 999) {
+       kvnoEntry = opr_queue_Prev(&typeEntry->kvnoList, struct kvnoList,
+                                  link);
+       if (opr_queue_IsEnd(&typeEntry->kvnoList, &kvnoEntry->link))
+          return NULL;
+    }
+
+    return kvnoEntry;
+}
+
+
+int
+afsconf_GetLatestKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
+                           struct afsconf_typedKeyList **keys)
+{
+    int code;
+    struct kvnoList *kvnoEntry;
+
+    LOCK_GLOBAL_MUTEX;
+
+    code = _afsconf_Check(dir);
+    if (code)
+       goto out;
+
+
+    kvnoEntry = pickBestKvno(dir, type);
+    if (kvnoEntry == NULL) {
+       code = AFSCONF_NOTFOUND;
+       goto out;
     }
+
+    code = listToArray(kvnoEntry, keys);
+
+out:
     UNLOCK_GLOBAL_MUTEX;
-    return AFSCONF_NOTFOUND;   /* didn't find any keys */
+    return code;
 }
 
-/* get a particular key */
 int
-afsconf_GetKey(void *rock, int avno, struct ktc_encryptionKey *akey)
+afsconf_GetLatestKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
+                           int subType, struct afsconf_typedKey **key)
 {
-    struct afsconf_dir *adir = (struct afsconf_dir *) rock;
-    int i, maxa;
-    struct afsconf_key *tk;
-    afs_int32 code;
+    int code;
+    struct kvnoList *kvnoEntry;
+    struct subTypeList *subTypeEntry;
 
     LOCK_GLOBAL_MUTEX;
-    code = _afsconf_Check(adir);
-    if (code) {
-       UNLOCK_GLOBAL_MUTEX;
-       return AFSCONF_FAILURE;
+
+    code = _afsconf_Check(dir);
+    if (code)
+       goto out;
+
+    kvnoEntry = pickBestKvno(dir, type);
+    if (kvnoEntry == NULL) {
+       code = AFSCONF_NOTFOUND;
+       goto out;
     }
-    maxa = adir->keystr->nkeys;
 
-    for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
-       if (tk->kvno == avno) {
-           memcpy(akey, tk->key, 8);
-           UNLOCK_GLOBAL_MUTEX;
-           return 0;
-       }
+    subTypeEntry = findInKvnoList(kvnoEntry, subType);
+    if (subTypeEntry == NULL) {
+       code = AFSCONF_NOTFOUND;
+       goto out;
     }
 
+    *key = afsconf_typedKey_get(subTypeEntry->key);
+
+out:
     UNLOCK_GLOBAL_MUTEX;
-    return AFSCONF_NOTFOUND;
+    return code;
 }
 
-/* save the key structure in the appropriate file */
-static int
-SaveKeys(struct afsconf_dir *adir)
+void
+afsconf_PutTypedKeyList(struct afsconf_typedKeyList **keys)
 {
-    struct afsconf_keys tkeys;
-    int fd;
-    afs_int32 i;
-    char tbuffer[256];
+     int i;
 
-    memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys));
+     for (i=0;i<(*keys)->nkeys;i++)
+       afsconf_typedKey_put(&((*keys)->keys[i]));
+     free((*keys)->keys);
+     free(*keys);
+     *keys = NULL;
+}
 
-    /* convert it to net byte order */
-    for (i = 0; i < tkeys.nkeys; i++)
-       tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
-    tkeys.nkeys = htonl(tkeys.nkeys);
+static struct afsconf_typedKey *
+afsconf_typedKey_blank(void)
+{
+    struct afsconf_typedKey *key;
 
-    /* rewrite keys file */
-    strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
-    fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
-    if (fd < 0)
-       return AFSCONF_FAILURE;
-    i = write(fd, &tkeys, sizeof(tkeys));
-    if (i != sizeof(tkeys)) {
-       close(fd);
-       return AFSCONF_FAILURE;
+    key = malloc(sizeof(struct afsconf_typedKey));
+    if (key == NULL)
+       return NULL;
+
+    memset(key, 0, sizeof(struct afsconf_typedKey));
+    rx_atomic_set(&key->refcnt, 1);
+
+    return key;
+}
+
+struct afsconf_typedKey *
+afsconf_typedKey_new(afsconf_keyType type, int kvno, int subType,
+                    struct rx_opaque *keyMaterial)
+{
+    struct afsconf_typedKey *key;
+    int code;
+
+    key = afsconf_typedKey_blank();
+    if (key == NULL)
+       return key;
+
+    key->type = type;
+    key->kvno = kvno;
+    key->subType = subType;
+
+    code = rx_opaque_copy(&key->key, keyMaterial);
+    if (code != 0) {
+       free(key);
+       return NULL;
     }
-    if (close(fd) < 0)
-       return AFSCONF_FAILURE;
-    return 0;
+
+    return key;
+}
+
+void
+afsconf_typedKey_free(struct afsconf_typedKey **key)
+{
+    rx_opaque_freeContents(&(*key)->key);
+    free(*key);
+    *key = NULL;
+}
+
+struct afsconf_typedKey *
+afsconf_typedKey_get(struct afsconf_typedKey *key)
+{
+     rx_atomic_inc(&key->refcnt);
+     return key;
+}
+
+void
+afsconf_typedKey_put(struct afsconf_typedKey **key)
+{
+    if (rx_atomic_dec_and_read(&(*key)->refcnt) == 0)
+       afsconf_typedKey_free(key);
+    else
+       *key = NULL;
+}
+
+void
+afsconf_typedKey_values(struct afsconf_typedKey *key, afsconf_keyType *type,
+                       int *kvno, int *subType, struct rx_opaque **material)
+{
+    *type = key->type;
+    *kvno = key->kvno;
+    *subType = key->subType;
+    *material = &key->key;
 }
 
 int
-afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8],
-              afs_int32 overwrite)
+afsconf_AddTypedKey(struct afsconf_dir *dir,
+                   struct afsconf_typedKey *key,
+                   int overwrite)
 {
-    struct afsconf_keys *tk;
-    struct afsconf_key *tkey;
-    afs_int32 i;
-    int foundSlot;
+    int code;
 
     LOCK_GLOBAL_MUTEX;
-    tk = adir->keystr;
 
-    if (akvno != 999) {
-       if (akvno < 0 || akvno > 255) {
-           UNLOCK_GLOBAL_MUTEX;
-           return ERANGE;
+    code = _afsconf_Check(dir);
+    if (code)
+       goto out;
+
+    if (key->type == afsconf_rxkad) {
+       /* There are restrictions on rxkad keys so that we can still
+        * return them using the old interface. We only enforce the
+        * same restrictions as that interface does - that is, we don't
+        * check that the key we're passed is a valid DES key */
+       if (key->key.len != 8 || key->subType != 0) {
+           code = AFSCONF_BADKEY;
+           goto out;
        }
     }
-    foundSlot = 0;
-    for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
-       if (tkey->kvno == akvno) {
-           if (!overwrite) {
-               UNLOCK_GLOBAL_MUTEX;
-               return AFSCONF_KEYINUSE;
-           }
-           foundSlot = 1;
-           break;
-       }
+
+    code = addMemoryKey(dir, key, overwrite);
+    if (code)
+       goto out;
+
+    code = _afsconf_SaveKeys(dir);
+    _afsconf_Touch(dir);
+
+out:
+    UNLOCK_GLOBAL_MUTEX;
+    return code;
+}
+
+int
+afsconf_DeleteKeyByType(struct afsconf_dir *dir,
+                       afsconf_keyType type, int kvno)
+{
+    struct keyTypeList *typeEntry;
+    struct kvnoList *kvnoEntry;
+    int code;
+
+    LOCK_GLOBAL_MUTEX;
+
+    code = _afsconf_Check(dir);
+    if (code)
+       goto out;
+
+    typeEntry = findByType(dir, type);
+    if (typeEntry == NULL) {
+       code = AFSCONF_NOTFOUND;
+        goto out;
     }
-    if (!foundSlot) {
-       if (tk->nkeys >= AFSCONF_MAXKEYS) {
-           UNLOCK_GLOBAL_MUTEX;
-           return AFSCONF_FULL;
-       }
-       tkey = &tk->key[tk->nkeys++];
+
+    kvnoEntry = findInTypeList(typeEntry, kvno);
+    if (kvnoEntry == NULL) {
+       code = AFSCONF_NOTFOUND;
+       goto out;
+    }
+
+    deleteKvnoEntry(kvnoEntry);
+
+    /* Remove the typeEntry, if it has no sub elements */
+    if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
+       opr_queue_Remove(&typeEntry->link);
+       free(typeEntry);
     }
-    tkey->kvno = akvno;
-    memcpy(tkey->key, akey, 8);
-    i = SaveKeys(adir);
-    _afsconf_Touch(adir);
+
+    code = _afsconf_SaveKeys(dir);
+    _afsconf_Touch(dir);
+
+out:
     UNLOCK_GLOBAL_MUTEX;
-    return i;
+    return code;
 }
 
-/* this proc works by sliding the other guys down, rather than using a funny
-    kvno value, so that callers can count on getting a good key in key[0].
-*/
 int
-afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno)
+afsconf_DeleteKeyBySubType(struct afsconf_dir *dir,
+                          afsconf_keyType type, int kvno, int subType)
 {
-    struct afsconf_keys *tk;
-    struct afsconf_key *tkey;
-    int i;
-    int foundFlag = 0;
+    struct keyTypeList *typeEntry;
+    struct kvnoList *kvnoEntry;
+    struct subTypeList *subTypeEntry;
+    int code;
 
     LOCK_GLOBAL_MUTEX;
-    tk = adir->keystr;
 
-    for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
-       if (tkey->kvno == akvno) {
-           foundFlag = 1;
-           break;
-       }
-    }
-    if (!foundFlag) {
-       UNLOCK_GLOBAL_MUTEX;
+    code = _afsconf_Check(dir);
+    if (code)
+       goto out;
+
+    typeEntry = findByType(dir, type);
+    if (typeEntry == NULL)
        return AFSCONF_NOTFOUND;
+
+    kvnoEntry = findInTypeList(typeEntry, kvno);
+    if (kvnoEntry == NULL)
+       return AFSCONF_NOTFOUND;
+
+    subTypeEntry = findInKvnoList(kvnoEntry, subType);
+    if (subTypeEntry == NULL)
+       return AFSCONF_NOTFOUND;
+
+    /* Remove the subTypeEntry */
+    afsconf_typedKey_put(&subTypeEntry->key);
+    opr_queue_Remove(&subTypeEntry->link);
+    free(subTypeEntry);
+
+    /* Remove the kvnoEntry, if it has no sub elements */
+    if (opr_queue_IsEmpty(&kvnoEntry->subTypeList)) {
+       opr_queue_Remove(&kvnoEntry->link);
+       free(kvnoEntry);
     }
 
-    /* otherwise slide the others down.  i and tkey point at the guy to delete */
-    for (; i < tk->nkeys - 1; i++, tkey++) {
-       tkey->kvno = (tkey + 1)->kvno;
-       memcpy(tkey->key, (tkey + 1)->key, 8);
+    /* Remove the typeEntry, if it has no sub elements */
+    if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
+       opr_queue_Remove(&typeEntry->link);
+       free(typeEntry);
     }
-    tk->nkeys--;
-    i = SaveKeys(adir);
-    _afsconf_Touch(adir);
+
+    code = _afsconf_SaveKeys(dir);
+    _afsconf_Touch(dir);
+
+out:
     UNLOCK_GLOBAL_MUTEX;
-    return i;
+    return code;
+}
+
+int
+afsconf_DeleteTypedKey(struct afsconf_dir *dir, struct afsconf_typedKey *key)
+{
+    return afsconf_DeleteKeyBySubType(dir, key->type, key->kvno, key->subType);
 }
index 805bf44..a1c9554 100644 (file)
@@ -121,6 +121,7 @@ ConstructLocalLogPath(const char *cpath,
 #define AFSDIR_CELLSERVDB_FILE  "CellServDB"
 #define AFSDIR_CELLALIAS_FILE   "CellAlias"
 #define AFSDIR_KEY_FILE         "KeyFile"
+#define AFSDIR_EXT_KEY_FILE    "KeyFileExt"
 #define AFSDIR_ULIST_FILE       "UserList"
 #define AFSDIR_NOAUTH_FILE      "NoAuth"
 #define AFSDIR_BUDBLOG_FILE     "BackupLog"
index e4d37a2..0363470 100644 (file)
@@ -111,6 +111,7 @@ extern int
 #define AFSDIR_CELLSERVDB_FILE  "CellServDB"
 #define AFSDIR_CELLALIAS_FILE   "CellAlias"
 #define AFSDIR_KEY_FILE         "KeyFile"
+#define AFSDIR_EXT_KEY_FILE    "KeyFileExt"
 #define AFSDIR_ULIST_FILE       "UserList"
 #define AFSDIR_NOAUTH_FILE      "NoAuth"
 #define AFSDIR_BUDBLOG_FILE     "BackupLog"
index 016a781..7414829 100644 (file)
@@ -12,7 +12,6 @@ all check test tests: $(TESTS)
 
 MODULE_LIBS =  ../tap/libtap.a \
                $(abs_top_builddir)/lib/libafsauthent.a \
-               $(abs_top_builddir)/lib/librxgk.a \
                $(abs_top_builddir)/lib/libafsrpc.a \
                $(abs_top_builddir)/lib/libafshcrypto.a \
                $(LIB_rfc3961) $(LIB_roken) -lafsutil\
index 7ca0af5..49281dc 100644 (file)
 #include <afs/afsutil.h>
 #include <rx/rxkad.h>
 
+#include <tap/basic.h>
+
 static int
 copy(char *inFile, char *outFile)
 {
     int in, out;
-    int code, written;
     char *block;
     size_t len;
 
@@ -69,11 +70,34 @@ copy(char *inFile, char *outFile)
     return 0;
 }
 
+int
+keyMatches(struct afsconf_typedKey *typedKey,
+          afsconf_keyType type, int kvno, int subType,
+          void *keyMaterial, size_t keyLen)
+{
+    afsconf_keyType keyType;
+    int keyKvno;
+    int keySubType;
+    struct rx_opaque *buffer;
+
+    afsconf_typedKey_values(typedKey, &keyType, &keyKvno, &keySubType,
+                           &buffer);
+
+    return (keyType == type && keyKvno == kvno && keySubType == subType &&
+           buffer->len == keyLen &&
+           memcmp(keyMaterial, buffer->val, buffer->len) == 0);
+}
+
+
 int main(int argc, char **argv)
 {
     struct afsconf_dir *dir;
     struct afsconf_keys keys;
     struct ktc_encryptionKey key;
+    struct rx_opaque *keyMaterial;
+    struct afsconf_typedKey *typedKey;
+    struct afsconf_typedKeyList *typedKeyList;
+
     char buffer[1024];
     char *dirEnd;
     FILE *file;
@@ -81,7 +105,7 @@ int main(int argc, char **argv)
     int code;
     int i;
 
-    plan(61);
+    plan(122);
 
     /* Create a temporary afs configuration directory */
     snprintf(buffer, sizeof(buffer), "%s/afs_XXXXXX", gettmpdir());
@@ -236,6 +260,81 @@ int main(int argc, char **argv)
     code = afsconf_AddKey(dir, 20, "\x10\x10\x10\x10\x10\x10\x10\x10",0);
     is_int(AFSCONF_FULL, code, "afsconf_AddKey fails once we've got 8 keys");
 
+    /* Check that the new interface also fails when we've got too many
+     * keys */
+    keyMaterial = rx_opaque_new("\x10\x10\x10\x10\x10\x10\x10\x10", 8);
+    typedKey = afsconf_typedKey_new(afsconf_rxkad, 20, 0, keyMaterial);
+    rx_opaque_free(&keyMaterial);
+    code = afsconf_AddTypedKey(dir, typedKey, 0);
+    afsconf_typedKey_put(&typedKey);
+    is_int(AFSCONF_FULL, code,
+          "afsconf_AddTypedKey fails for rxkad once we've got 8 keys");
+
+    /* Check the new accessors work for rxkad keys */
+    code = afsconf_GetKeyByTypes(dir, afsconf_rxkad, 4, 0, &typedKey);
+    is_int(0, code,
+          "afsconf_GetKeyByTypes works for rxkad");
+    ok(keyMatches(typedKey, afsconf_rxkad, 4, 0,
+                 "\x19\x16\xfe\xe6\xba\x77\x2f\xfd", 8),
+       " ... and returned key matches");
+
+    afsconf_typedKey_put(&typedKey);
+
+    code = afsconf_GetKeysByType(dir, afsconf_rxkad, 4, &typedKeyList);
+    is_int(0, code,
+          "afsconf_GetKeysByType works for rxkad");
+    is_int(1, typedKeyList->nkeys,
+          " ... and returns 1 key, as expected");
+    ok(keyMatches(typedKeyList->keys[0], afsconf_rxkad, 4, 0,
+                 "\x19\x16\xfe\xe6\xba\x77\x2f\xfd", 8),
+       " ... and returned key matches");
+
+    afsconf_PutTypedKeyList(&typedKeyList);
+
+    code = afsconf_GetLatestKeyByTypes(dir, afsconf_rxkad, 0, &typedKey);
+    is_int(0, code,
+          "afsconf_GetLatestKeyByTypes works for rxkad");
+    ok(keyMatches(typedKey, afsconf_rxkad, 14, 0,
+                 "\x10\x10\x10\x10\x10\x10\x10\x10", 8),
+       " ... and returned key matches");
+
+    afsconf_typedKey_put(&typedKey);
+
+    code = afsconf_GetLatestKeysByType(dir, afsconf_rxkad, &typedKeyList);
+    is_int(0, code,
+          "afsconf_GetLatestKeysByType works for rxkad");
+    is_int(1, typedKeyList->nkeys,
+          " ... and returns 1 key, as expected");
+    ok(keyMatches(typedKeyList->keys[0], afsconf_rxkad, 14, 0,
+                "\x10\x10\x10\x10\x10\x10\x10\x10", 8),
+       " ... and returned key matches");
+    afsconf_PutTypedKeyList(&typedKeyList);
+
+    /* Check that we can't delete a key that doesn't exist */
+    code = afsconf_DeleteKeyByType(dir, afsconf_rxkad, 6);
+    is_int(AFSCONF_NOTFOUND, code,
+          "afsconf_DeleteKeyByType returns NOTFOUND if key doesn't exist");
+    code = afsconf_DeleteKeyBySubType(dir, afsconf_rxkad, 6, 0);
+    is_int(AFSCONF_NOTFOUND, code,
+           "afsconf_DeleteKeyBySubType returns NOTFOUND if key doesn't exist");
+    code = afsconf_DeleteKeyBySubType(dir, afsconf_rxkad, 14, 1);
+    is_int(AFSCONF_NOTFOUND, code,
+           "afsconf_DeleteKeyBySubType doesn't delete with wrong subtype");
+    code = afsconf_GetKeyByTypes(dir, afsconf_rxkad, 14, 0, &typedKey);
+    is_int(0, code, " ... and key is still there!");
+    afsconf_typedKey_put(&typedKey);
+
+    /* Check that we can delete a key that does */
+    code = afsconf_DeleteKeyByType(dir, afsconf_rxkad, 13);
+    is_int(0, code, "afsconf_DeleteKeyByType works");
+    code = afsconf_GetKeysByType(dir, afsconf_rxkad, 13, &typedKeyList);
+    is_int(AFSCONF_NOTFOUND, code, " ... and is really gone");
+
+    code = afsconf_DeleteKeyBySubType(dir, afsconf_rxkad, 14, 0);
+    is_int(0, code, "afsconf_DeleteKeyBySubType works");
+    code = afsconf_GetKeyByTypes(dir, afsconf_rxkad, 14, 0, &typedKey);
+    is_int(AFSCONF_NOTFOUND, code, " ... and is really gone");
+
     /* Unlink the KeyFile */
     strcpy(dirEnd, "/KeyFile");
     unlink(buffer);
@@ -263,6 +362,18 @@ int main(int argc, char **argv)
     code = afsconf_GetLatestKey(dir, &kvno, &key);
     is_int(AFSCONF_NOTFOUND, code,
           "afsconf_GetLatestKey returns NOTFOUND with an empty KeyFile");
+    code = afsconf_GetKeysByType(dir, afsconf_rxkad, 1, &typedKeyList);
+    is_int(AFSCONF_NOTFOUND, code,
+          "afsconf_GetKeysByType returns NOTFOUND with an empty KeyFile");
+    code = afsconf_GetKeyByTypes(dir, afsconf_rxkad, 1, 0, &typedKey);
+    is_int(AFSCONF_NOTFOUND, code,
+          "afsconf_GetKeyByTypes returns NOTFOUND with an empty KeyFile");
+    code = afsconf_GetLatestKeysByType(dir, afsconf_rxkad, &typedKeyList);
+    is_int(AFSCONF_NOTFOUND, code,
+          "afsconf_GetLatestKeysByType returns NOTFOUND with empty KeyFile");
+    code = afsconf_GetLatestKeyByTypes(dir, afsconf_rxkad, 0, &typedKey);
+    is_int(AFSCONF_NOTFOUND, code,
+          "afsconf_GetLatestKeyByTypes returns NOTFOUND with empty KeyFile");
 
     /* Now try adding a key to an empty file */
     code = afsconf_AddKey(dir, 1, "\x10\x10\x10\x10\x10\x10\x10\x10", 1);
@@ -273,6 +384,163 @@ int main(int argc, char **argv)
     ok(memcmp(&key, "\x10\x10\x10\x10\x10\x10\x10\x10", 8) == 0,
        " ... and key");
 
+    /* And adding a key using the new interface */
+
+    keyMaterial = rx_opaque_new("\x20\x20\x20\x20\x20\x20\x20\x20", 8);
+    typedKey = afsconf_typedKey_new(afsconf_rxkad, 2, 0, keyMaterial);
+    rx_opaque_free(&keyMaterial);
+    code = afsconf_AddTypedKey(dir, typedKey, 0);
+    afsconf_typedKey_put(&typedKey);
+    is_int(0, code, "afsconf_AddTypedKey works");
+    code = afsconf_GetLatestKey(dir, &kvno, &key);
+    is_int(0, code, " ... and afsconf_GetLatestKey succeeds");
+    is_int(2, kvno, " ... with correct kvno");
+    ok(memcmp(&key, "\x20\x20\x20\x20\x20\x20\x20\x20", 8) == 0,
+       " ... and key");
+    code = afsconf_GetLatestKeyByTypes(dir, afsconf_rxkad, 0, &typedKey);
+    is_int(0, code, " ... and so does afsconf_GetLatestKeyByTypes");
+    ok(keyMatches(typedKey, afsconf_rxkad, 2, 0,
+                 "\x20\x20\x20\x20\x20\x20\x20\x20", 8),
+       " ... with correct key");
+    afsconf_typedKey_put(&typedKey);
+
+    /* And that we can't add a key to an existing kvno and type */
+    keyMaterial = rx_opaque_new("\x30\x30\x30\x30\x30\x30\x30\x30", 8);
+    typedKey = afsconf_typedKey_new(afsconf_rxkad, 2, 0, keyMaterial);
+    rx_opaque_free(&keyMaterial);
+    code = afsconf_AddTypedKey(dir, typedKey, 0);
+    afsconf_typedKey_put(&typedKey);
+    is_int(AFSCONF_KEYINUSE, code,
+          "afsconf_AddTypedKey won't overwrite without being told to");
+    code = afsconf_GetKeyByTypes(dir, afsconf_rxkad, 2, 0, &typedKey);
+    is_int(0, code, " ... and key still exists");
+    ok(keyMatches(typedKey, afsconf_rxkad, 2, 0,
+                 "\x20\x20\x20\x20\x20\x20\x20\x20", 8),
+       " ... and hasn't changed");
+    afsconf_typedKey_put(&typedKey);
+
+    /* But we can if we force */
+    keyMaterial = rx_opaque_new("\x30\x30\x30\x30\x30\x30\x30\x30", 8);
+    typedKey = afsconf_typedKey_new(afsconf_rxkad, 2, 0, keyMaterial);
+    rx_opaque_free(&keyMaterial);
+    code = afsconf_AddTypedKey(dir, typedKey, 1);
+    afsconf_typedKey_put(&typedKey);
+    is_int(0, code,  "afsconf_AddTypedKey overwrites when asked");
+    code = afsconf_GetKeyByTypes(dir, afsconf_rxkad, 2, 0, &typedKey);
+    is_int(0, code, " ... and GetKeyByTypes retrieves new key");
+    ok(keyMatches(typedKey, afsconf_rxkad, 2, 0,
+                 "\x30\x30\x30\x30\x30\x30\x30\x30", 8),
+       " ... and it is the new key");
+
+    /* Check that we can't add bad rxkad keys */
+    keyMaterial = rx_opaque_new("\x30\x30\x30\x30\x30\x30\x30", 7);
+    typedKey = afsconf_typedKey_new(afsconf_rxkad, 3, 0, keyMaterial);
+    rx_opaque_free(&keyMaterial);
+    code = afsconf_AddTypedKey(dir, typedKey, 1);
+    afsconf_typedKey_put(&typedKey);
+    is_int(AFSCONF_BADKEY, code,
+          "afsconf_AddTypedKey won't add short rxkad keys");
+    keyMaterial = rx_opaque_new("\x30\x30\x30\x30\x30\x30\x30\x30\x30", 9);
+    typedKey = afsconf_typedKey_new(afsconf_rxkad, 3, 0, keyMaterial);
+    rx_opaque_free(&keyMaterial);
+    code = afsconf_AddTypedKey(dir, typedKey, 1);
+    afsconf_typedKey_put(&typedKey);
+    is_int(AFSCONF_BADKEY, code,
+          "afsconf_AddTypedKey won't add long rxkad keys");
+    keyMaterial = rx_opaque_new("\x30\x30\x30\x30\x30\x30\x30\x30", 8);
+    typedKey = afsconf_typedKey_new(afsconf_rxkad, 3, 1, keyMaterial);
+    rx_opaque_free(&keyMaterial);
+    code = afsconf_AddTypedKey(dir, typedKey, 1);
+    afsconf_typedKey_put(&typedKey);
+    is_int(AFSCONF_BADKEY, code,
+          "afsconf_AddTypedKey won't add rxkad keys with non-zero subtype");
+
+    /* Now, test things with other key types. */
+
+    /* Add a different key type, but with same kvno as rxkad */
+    keyMaterial = rx_opaque_new("\x01", 1);
+    typedKey = afsconf_typedKey_new(1, 2, 0, keyMaterial);
+    code = afsconf_AddTypedKey(dir, typedKey, 0);
+    afsconf_typedKey_put(&typedKey);
+    is_int(0, code,
+          "afsconf_AddTypedKey can add keys with different key type");
+
+    /* Add a different subtype, with same kvno */
+    keyMaterial = rx_opaque_new("\x02\x03", 2);
+    typedKey = afsconf_typedKey_new(1, 2, 1, keyMaterial);
+    code = afsconf_AddTypedKey(dir, typedKey, 0);
+    afsconf_typedKey_put(&typedKey);
+    is_int(0, code,
+          "afsconf_AddTypedKey can add keys with different sub type");
+
+    /* Check the GetKeyByTypes returns one of the keys */
+    code = afsconf_GetKeyByTypes(dir, 1, 2, 1, &typedKey);
+    is_int(0, code, "afsconf_GetKeyByTypes returns");
+    ok(keyMatches(typedKey, 1, 2, 1, "\x02\x03", 2),
+       " ... with the right key");
+
+    /* Check that GetKeysByType returns both of the keys */
+    code = afsconf_GetKeysByType(dir, 1, 2, &typedKeyList);
+    is_int(0, code, "afsconf_GetKeysByType returns");
+    is_int(2, typedKeyList->nkeys, " ... with correct number of keys");
+    ok(keyMatches(typedKeyList->keys[0], 1, 2, 0, "\x01", 1),
+       " ... with the right key in slot 0");
+    ok(keyMatches(typedKeyList->keys[1], 1, 2, 1, "\x02\x03", 2),
+       " ... with the right key in slot 1");
+    afsconf_PutTypedKeyList(&typedKeyList);
+
+    /* Add another key, before these ones, so we can check that
+     * latest really works */
+    keyMaterial = rx_opaque_new("\x03", 1);
+    typedKey = afsconf_typedKey_new(1, 1, 0, keyMaterial);
+    code = afsconf_AddTypedKey(dir, typedKey, 0);
+    afsconf_typedKey_put(&typedKey);
+    is_int(0, code, "afsconf_AddTypedKey worked again");
+
+    /* Check that GetLatestKeyByTypes returns one */
+    code = afsconf_GetLatestKeyByTypes(dir, 1, 1, &typedKey);
+    is_int(0, code, "afsconf_GetLatestKeyByTypes returns");
+    ok(keyMatches(typedKey, 1, 2, 1, "\x02\x03", 2),
+       " ... with the right key");
+
+    /* Check that GetLatestKeysByType returns both */
+    code = afsconf_GetLatestKeysByType(dir, 1, &typedKeyList);
+    is_int(0, code, "afsconf_GetLatestKeysByType returns");
+        is_int(2, typedKeyList->nkeys, " ... with correct number of keys");
+    ok(keyMatches(typedKeyList->keys[0], 1, 2, 0, "\x01", 1),
+       " ... with the right key in slot 0");
+    ok(keyMatches(typedKeyList->keys[1], 1, 2, 1, "\x02\x03", 2),
+       " ... with the right key in slot 1");
+    afsconf_PutTypedKeyList(&typedKeyList);
+
+    /* Check that closing this instance, and reopening, still has all of
+     * the required keys
+     */
+    afsconf_Close(dir);
+
+    *dirEnd='\0';
+    dir = afsconf_Open(strdup(buffer));
+    ok(dir != NULL, "Sucessfully re-opened config directory");
+    if (dir == NULL)
+       goto out;
+
+    /* Check that GetKeysByType returns all of the keys */
+    code = afsconf_GetKeysByType(dir, 1, 1, &typedKeyList);
+    is_int(0, code, "afsconf_GetKeysByType returns after reopening");
+    is_int(1, typedKeyList->nkeys, " ... First kvno has correct number of keys");
+    ok(keyMatches(typedKeyList->keys[0], 1, 1, 0, "\x03", 1),
+       " ... and key material is correct");
+    afsconf_PutTypedKeyList(&typedKeyList);
+
+    code = afsconf_GetKeysByType(dir, 1, 2, &typedKeyList);
+    is_int(0, code, "afsconf_GetKeysByType returns after reopening");
+    is_int(2, typedKeyList->nkeys, " ... with correct number of keys");
+    ok(keyMatches(typedKeyList->keys[0], 1, 2, 0, "\x01", 1),
+       " ... with the right key in slot 0");
+    ok(keyMatches(typedKeyList->keys[1], 1, 2, 1, "\x02\x03", 2),
+       " ... with the right key in slot 1");
+    afsconf_PutTypedKeyList(&typedKeyList);
+
 out:
     strcpy(dirEnd, "/KeyFile");
     unlink(buffer);
@@ -284,4 +552,6 @@ out:
     unlink(buffer);
     *dirEnd='\0';
     rmdir(buffer);
+
+    return 0;
 }