Implement afsconf_GetRXGKKey
[openafs.git] / src / auth / keys.c
index aa265f1..59543e8 100644 (file)
 /* Need rx/rx.h to get working assert(), used by LOCK_GLOBAL_MUTEX */
 #include <rx/rx.h>
 #include <rx/rx_atomic.h>
+#ifdef AFS_RXGK_ENV
+#include <rx/rxgk.h>
+#endif
 
+#include <afs/opr.h>
 #include <afs/stds.h>
 #include <afs/pthread_glock.h>
 #include <afs/afsutil.h>
@@ -81,15 +85,20 @@ listToArray(struct kvnoList *kvnoEntry, struct afsconf_typedKeyList **keys)
     /* 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;
+    if (retval->nkeys > 0) {
+        retval->keys  = calloc(retval->nkeys, sizeof(struct afsconf_typedKey *));
 
-       entry = opr_queue_Entry(cursor, struct subTypeList, link);
-       retval->keys[i] = afsconf_typedKey_get(entry->key);
-       i++;
+       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;
     }
 
     *keys = retval;
@@ -295,20 +304,17 @@ _afsconf_InitKeys(struct afsconf_dir *dir)
  * 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
+ * All other keys are stored in the file KeyFileExt, 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.
+ * If the format ever needs to chanage incompatibly, a new file name
+ * will be used.
  *
  * 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)
@@ -360,6 +366,8 @@ _parseOriginalKeyFile(struct afsconf_dir *dir, char *fileName)
     for(i=0; i<nkeys; i++) {
 
        key = afsconf_typedKey_blank();
+       if (key == NULL)
+           goto fail;
 
        key->type = afsconf_rxkad;
        key->subType = 0;
@@ -415,14 +423,17 @@ _writeOriginalKeyFile(struct afsconf_dir *dir, char *fileName)
        return AFSCONF_FAILURE;
 
     typeEntry = findByType(dir, afsconf_rxkad);
-    if (typeEntry == NULL)
-       goto out;
-
-    nkeys = opr_queue_Count(&typeEntry->kvnoList);
+    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;
@@ -450,7 +461,7 @@ _parseExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
 {
     int fd, i, code;
     afs_int32 nkeys;
-    struct afsconf_typedKey *key;
+    struct afsconf_typedKey *key = NULL;
 
     fd = open(fileName, O_RDONLY);
     if (fd < 0)
@@ -465,6 +476,8 @@ _parseExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
        afs_int32 reclen;
 
        key = afsconf_typedKey_blank();
+       if (key == NULL)
+           goto fail;
 
        /* The only data version we currently parse has a reclen of 16.
         * Anything smaller indicates a corrupt key file. Anything more,
@@ -510,7 +523,6 @@ _parseExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
        code = read(fd, key->key.val, reclen);
        if (code != reclen) {
            rx_opaque_freeContents(&key->key);
-           free(key);
            goto fail;
        }
        code = addMemoryKey(dir, key, 1);
@@ -522,6 +534,9 @@ _parseExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
     return 0;
 
 fail:
+    if (key)
+       afsconf_typedKey_put(&key);
+
     close(fd);
     return EIO;
 }
@@ -757,6 +772,117 @@ afsconf_GetKey(void *rock, int kvno, struct ktc_encryptionKey *key)
     return 0;
 }
 
+static int
+_afsconf_GetLatestRXGKKey(afsconf_keyType type, struct afsconf_dir *rock,
+                         afs_int32 *avno, afs_int32 *enctype, rxgk_key *key)
+{
+#ifdef AFS_RXGK_ENV
+    struct afsconf_typedKeyList *list = NULL;
+    struct afsconf_typedKey *typedKey = NULL;
+    afs_int32 code;
+    int key_i;
+
+    code = afsconf_GetLatestKeysByType(rock, type, &list);
+    if (code != 0)
+       goto done;
+
+    for (key_i = 0; key_i < list->nkeys; key_i++) {
+       if (typedKey == NULL)
+           typedKey = list->keys[key_i];
+       else if (rxgk_enctype_better(typedKey->subType, list->keys[key_i]->subType))
+           typedKey = list->keys[key_i];
+    }
+
+    opr_Assert(typedKey != NULL);
+
+    /* We picked a key; copy to the output parameters */
+    code = rxgk_make_key(key, typedKey->key.val, typedKey->key.len,
+                        typedKey->subType);
+    if (code != 0)
+       goto done;
+    if (avno != NULL)
+       *avno = typedKey->kvno;
+    if (enctype != NULL)
+       *enctype = typedKey->subType;
+
+ done:
+    afsconf_PutTypedKeyList(&list);
+    return code;
+#else  /* AFS_RXGK_ENV */
+    return AFSCONF_NOTFOUND;
+#endif
+}
+
+/**
+ * Obtain the "best" rxgk key from KeyFileExt
+ *
+ * Return the key and its enctype and kvno, for encrypting outgoing tokens.
+ *
+ * @param[in] rock     The configuration directory to be used.
+ * @param[out] avno    The key version number of key.
+ * @param[out] enctype The RFC 3961 enctype of key.
+ * @param[out] key     The returned rxgk key.
+ */
+int
+afsconf_GetLatestRXGKKey(struct afsconf_dir *rock, afs_int32 *avno,
+                        afs_int32 *enctype, rxgk_key *key)
+{
+    return _afsconf_GetLatestRXGKKey(afsconf_rxgk, rock, avno, enctype, key);
+}
+
+static int
+_afsconf_GetRXGKKey(afsconf_keyType type, void *rock, afs_int32 *avno,
+                   afs_int32 *enctype, rxgk_key *key)
+{
+#ifdef AFS_RXGK_ENV
+    struct afsconf_dir *dir = rock;
+    struct afsconf_typedKey *typedKey;
+    afs_int32 code;
+
+    /* No information at all means "pick the best/newest one". */
+    if (*avno == 0 && *enctype == 0)
+       return _afsconf_GetLatestRXGKKey(type, dir, avno, enctype, key);
+
+    code = afsconf_GetKeyByTypes(dir, type, *avno, *enctype, &typedKey);
+    if (code != 0)
+       return code;
+
+    code = rxgk_make_key(key, typedKey->key.val, typedKey->key.len,
+                        typedKey->subType);
+    afsconf_typedKey_put(&typedKey);
+
+    return code;
+#else  /* AFS_RXGK_ENV */
+    return AFSCONF_NOTFOUND;
+#endif
+}
+
+/**
+ * Obtain a particular RXGK key from KeyFileExt
+ *
+ * Use the specified kvno and enctype to fetch an rxgk key from KeyFileExt
+ * and return it as an rxgk_key.  Specifying the kvno/enctype pair as both
+ * zeros causes the "best" rxgk key to be returned, and the kvno/enctype
+ * of that key returned to the caller.
+ *
+ * @param[in] rock      An afsconf_dir* for the configuration directory. This
+ *                     is a void* just so this can be easily used as a
+ *                     callback function that uses a void* rock.
+ * @param[inout] avno  The requested kvno (if non-zero), or zero to request
+ *                     the latest key and have its kvno returned in this
+ *                     parameter.
+ * @param[inout] enctype       The requested enctype (if non-zero), or zero
+ *                             to request the latest key and have its
+ *                             enctype returned in this parameter.
+ * @param[out] key     The returned rxgk key.
+ */
+int
+afsconf_GetRXGKKey(void *rock, afs_int32 *avno,
+                  afs_int32 *enctype, rxgk_key *key)
+{
+    return _afsconf_GetRXGKKey(afsconf_rxgk, rock, avno, enctype, key);
+}
+
 int
 afsconf_AddKey(struct afsconf_dir *dir, afs_int32 kvno, char key[8],
               afs_int32 overwrite)
@@ -811,6 +937,71 @@ out:
 }
 
 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))
+               count++;
+       }
+    }
+
+    /* 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 code;
+}
+
+int
 afsconf_GetKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
                      int kvno, int subType, struct afsconf_typedKey **key)
 {
@@ -854,7 +1045,7 @@ pickBestKvno(struct afsconf_dir *dir, afsconf_keyType type)
     /* 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) {
+    while (type == afsconf_rxkad && kvnoEntry->kvno == 999) {
        kvnoEntry = opr_queue_Prev(&typeEntry->kvnoList, struct kvnoList,
                                   link);
        if (opr_queue_IsEnd(&typeEntry->kvnoList, &kvnoEntry->link))
@@ -930,9 +1121,16 @@ afsconf_PutTypedKeyList(struct afsconf_typedKeyList **keys)
 {
      int i;
 
+     if (*keys == NULL) {
+        return;
+     }
+
      for (i=0;i<(*keys)->nkeys;i++)
        afsconf_typedKey_put(&((*keys)->keys[i]));
-     free((*keys)->keys);
+
+     if ((*keys)->keys != NULL)
+       free((*keys)->keys);
+
      free(*keys);
      *keys = NULL;
 }
@@ -942,11 +1140,10 @@ afsconf_typedKey_blank(void)
 {
     struct afsconf_typedKey *key;
 
-    key = malloc(sizeof(struct afsconf_typedKey));
+    key = calloc(1, sizeof(struct afsconf_typedKey));
     if (key == NULL)
        return NULL;
 
-    memset(key, 0, sizeof(struct afsconf_typedKey));
     rx_atomic_set(&key->refcnt, 1);
 
     return key;
@@ -979,6 +1176,8 @@ afsconf_typedKey_new(afsconf_keyType type, int kvno, int subType,
 void
 afsconf_typedKey_free(struct afsconf_typedKey **key)
 {
+    if (*key == NULL)
+       return;
     rx_opaque_freeContents(&(*key)->key);
     free(*key);
     *key = NULL;
@@ -1004,10 +1203,14 @@ 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;
+    if (type != NULL)
+       *type = key->type;
+    if (kvno != NULL)
+       *kvno = key->kvno;
+    if (subType != NULL)
+       *subType = key->subType;
+    if (material != NULL)
+       *material = &key->key;
 }
 
 int