X-Git-Url: https://git.openafs.org/?p=openafs.git;a=blobdiff_plain;f=src%2Fauth%2Fkeys.c;h=ba6843da104254bc3fba8289e781923b2b990c10;hp=df8a618c7c9917f4c36115e76f7df2b9f946df87;hb=be8d8b7c78eb54f460489084650ba13f3946d7a2;hpb=a0adc8cd6ef62deaef8c2861f8a94f23a007fc81 diff --git a/src/auth/keys.c b/src/auth/keys.c index df8a618..ba6843d 100644 --- a/src/auth/keys.c +++ b/src/auth/keys.c @@ -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 #include #include +#include + /* Need rx/rx.h to get working assert(), used by LOCK_GLOBAL_MUTEX */ #include +#include #include #include @@ -22,260 +41,1188 @@ #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); + + if (retval->nkeys > 0) { + 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++; + } + } else { + retval->keys = NULL; } -#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; itype = 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) + nkeys = opr_queue_Count(&typeEntry->kvnoList); + else + nkeys = 0; + + if (writeWord(fd, nkeys)) + goto fail; + + 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); + 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 = NULL; - 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; itype, 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: + if (key) + afsconf_typedKey_put(&key); + + 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 */ + if (kvno != NULL) + *kvno = typedKey->kvno; + + if (key != NULL) + 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_GetAllKeys(struct afsconf_dir *dir, struct afsconf_typedKeyList **keys) +{ + int code; + struct afsconf_typedKeyList *retval; + struct opr_queue *typeCursor; + struct keyTypeList *typeEntry; + struct opr_queue *kvnoCursor; + struct kvnoList *kvnoEntry; + struct opr_queue *subCursor; + struct subTypeList *subEntry; + int count; + + LOCK_GLOBAL_MUTEX; + + code = _afsconf_Check(dir); + if (code) + goto out; + + count = 0; + /* First, work out how many keys we have in total */ + for (opr_queue_Scan(&dir->keyList, typeCursor)) { + typeEntry = opr_queue_Entry(typeCursor, struct keyTypeList, link); + for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) { + kvnoEntry = opr_queue_Entry(kvnoCursor, struct kvnoList, link); + for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) { + subEntry = opr_queue_Entry(subCursor, struct subTypeList, link); + count++; + } } } - 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; + + /* Allocate space for all of these */ + retval = malloc(sizeof(struct afsconf_typedKeyList)); + retval->nkeys = count; + + if (count > 0) { + retval->keys = calloc(retval->nkeys, + sizeof(struct afsconf_typedKey *)); + + /* Populate the key list */ + count = 0; + for (opr_queue_Scan(&dir->keyList, typeCursor)) { + typeEntry = opr_queue_Entry(typeCursor, + struct keyTypeList, link); + for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) { + kvnoEntry = opr_queue_Entry(kvnoCursor, + struct kvnoList, link); + for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) { + subEntry = opr_queue_Entry(subCursor, + struct subTypeList, link); + retval->keys[count] = afsconf_typedKey_get(subEntry->key); + count++; + } + } + } + } else { + retval->keys = NULL; } + + *keys = retval; + +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_GetKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type, + int kvno, 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 = 0; + struct subTypeList *subTypeEntry; LOCK_GLOBAL_MUTEX; - code = _afsconf_Check(adir); - if (code) { - UNLOCK_GLOBAL_MUTEX; - return AFSCONF_FAILURE; - } - 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; - } + code = _afsconf_Check(dir); + if (code) + goto out; + + subTypeEntry = findBySubType(dir, type, kvno, 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) +static struct kvnoList * +pickBestKvno(struct afsconf_dir *dir, afsconf_keyType type) { - struct afsconf_keys tkeys; - int fd; - afs_int32 i; - char tbuffer[256]; + struct keyTypeList *typeEntry; + struct kvnoList *kvnoEntry; - memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys)); + typeEntry = findByType(dir, type); + if (typeEntry == NULL) + return 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); + /* We store all of the key lists ordered, so the last entry in the + * kvno list must be the highest kvno. */ - /* 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; + 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; } - if (close(fd) < 0) - return AFSCONF_FAILURE; - return 0; + + return kvnoEntry; } + int -afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8], - afs_int32 overwrite) +afsconf_GetLatestKeysByType(struct afsconf_dir *dir, afsconf_keyType type, + struct afsconf_typedKeyList **keys) { - struct afsconf_keys *tk; - struct afsconf_key *tkey; - afs_int32 i; - int foundSlot; + int code; + struct kvnoList *kvnoEntry; 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; + + + kvnoEntry = pickBestKvno(dir, type); + if (kvnoEntry == NULL) { + code = AFSCONF_NOTFOUND; + 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 = listToArray(kvnoEntry, keys); + +out: + UNLOCK_GLOBAL_MUTEX; + return code; +} + +int +afsconf_GetLatestKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type, + int subType, struct afsconf_typedKey **key) +{ + int code; + struct kvnoList *kvnoEntry; + struct subTypeList *subTypeEntry; + + LOCK_GLOBAL_MUTEX; + + code = _afsconf_Check(dir); + if (code) + goto out; + + kvnoEntry = pickBestKvno(dir, type); + if (kvnoEntry == NULL) { + code = AFSCONF_NOTFOUND; + goto out; } - if (!foundSlot) { - if (tk->nkeys >= AFSCONF_MAXKEYS) { - UNLOCK_GLOBAL_MUTEX; - return AFSCONF_FULL; - } - tkey = &tk->key[tk->nkeys++]; + + subTypeEntry = findInKvnoList(kvnoEntry, subType); + if (subTypeEntry == NULL) { + code = AFSCONF_NOTFOUND; + goto out; } - tkey->kvno = akvno; - memcpy(tkey->key, akey, 8); - i = SaveKeys(adir); - _afsconf_Touch(adir); + + *key = afsconf_typedKey_get(subTypeEntry->key); + +out: UNLOCK_GLOBAL_MUTEX; - return i; + return code; +} + +void +afsconf_PutTypedKeyList(struct afsconf_typedKeyList **keys) +{ + int i; + + for (i=0;i<(*keys)->nkeys;i++) + afsconf_typedKey_put(&((*keys)->keys[i])); + + if ((*keys)->keys != NULL) + free((*keys)->keys); + + free(*keys); + *keys = NULL; +} + +static struct afsconf_typedKey * +afsconf_typedKey_blank(void) +{ + struct afsconf_typedKey *key; + + 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; + } + + 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; } -/* 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_AddTypedKey(struct afsconf_dir *dir, + struct afsconf_typedKey *key, + int overwrite) { - struct afsconf_keys *tk; - struct afsconf_key *tkey; - int i; - int foundFlag = 0; + 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; + 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; } } - if (!foundFlag) { - UNLOCK_GLOBAL_MUTEX; + + 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; + } + + 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); + } + + code = _afsconf_SaveKeys(dir); + _afsconf_Touch(dir); + +out: + UNLOCK_GLOBAL_MUTEX; + return code; +} + +int +afsconf_DeleteKeyBySubType(struct afsconf_dir *dir, + afsconf_keyType type, int kvno, int subType) +{ + struct keyTypeList *typeEntry; + struct kvnoList *kvnoEntry; + struct subTypeList *subTypeEntry; + int code; + + LOCK_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); }