/*
- * 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);
}
#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;
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;
int code;
int i;
- plan(61);
+ plan(122);
/* Create a temporary afs configuration directory */
snprintf(buffer, sizeof(buffer), "%s/afs_XXXXXX", gettmpdir());
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);
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);
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);
unlink(buffer);
*dirEnd='\0';
rmdir(buffer);
+
+ return 0;
}