auth: Handle empty lists of keys
[openafs.git] / src / auth / keys.c
1 /*
2  * Copyright (c) 2010 Your File System Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include <afsconfig.h>
26 #include <afs/param.h>
27
28 #include <roken.h>
29
30 #include <opr/queue.h>
31
32 /* Need rx/rx.h to get working assert(), used by LOCK_GLOBAL_MUTEX */
33 #include <rx/rx.h>
34 #include <rx/rx_atomic.h>
35
36 #include <afs/stds.h>
37 #include <afs/pthread_glock.h>
38 #include <afs/afsutil.h>
39
40 #include "cellconfig.h"
41 #include "keys.h"
42 #include "internal.h"
43
44 struct afsconf_typedKey {
45     rx_atomic_t refcnt;
46     afsconf_keyType type;
47     int kvno;
48     int subType;
49     struct rx_opaque key;
50 };
51
52 static struct afsconf_typedKey *afsconf_typedKey_blank(void);
53
54 /* Memory storage for keyfile contents. */
55
56 struct keyTypeList {
57     struct opr_queue link;
58     afsconf_keyType type;
59     struct opr_queue kvnoList;
60 };
61
62 struct kvnoList {
63     struct opr_queue link;
64     int kvno;
65     struct opr_queue subTypeList;
66 };
67
68 struct subTypeList {
69     struct opr_queue link;
70     int subType;
71     struct afsconf_typedKey *key;
72 };
73
74 static int
75 listToArray(struct kvnoList *kvnoEntry, struct afsconf_typedKeyList **keys)
76 {
77     struct afsconf_typedKeyList *retval;
78     struct opr_queue *cursor;
79     int i;
80
81     /* Allocate space for the keys we've got stored */
82     retval = malloc(sizeof(struct afsconf_typedKeyList));
83     retval->nkeys = opr_queue_Count(&kvnoEntry->subTypeList);
84
85     if (retval->nkeys > 0) {
86         retval->keys  = calloc(retval->nkeys, sizeof(struct afsconf_typedKey *));
87
88         i = 0;
89         for(opr_queue_Scan(&kvnoEntry->subTypeList, cursor)) {
90             struct subTypeList *entry;
91
92             entry = opr_queue_Entry(cursor, struct subTypeList, link);
93             retval->keys[i] = afsconf_typedKey_get(entry->key);
94             i++;
95         }
96     } else {
97         retval->keys = NULL;
98     }
99
100     *keys = retval;
101     return 0;
102 }
103
104 static struct keyTypeList *
105 findByType(struct afsconf_dir *dir, afsconf_keyType type)
106 {
107     struct opr_queue *cursor;
108     struct keyTypeList *entry = NULL;
109
110     for (opr_queue_Scan(&dir->keyList, cursor)) {
111         entry = opr_queue_Entry(cursor, struct keyTypeList, link);
112         if (entry->type >= type)
113             break;
114     }
115     if (entry == NULL || entry->type != type)
116         return NULL;
117
118     return entry;
119 }
120
121 static struct kvnoList *
122 findInTypeList(struct keyTypeList *parent, int kvno)
123 {
124     struct opr_queue *cursor;
125     struct kvnoList *entry = NULL;
126
127     for (opr_queue_Scan(&parent->kvnoList, cursor)) {
128         entry = opr_queue_Entry(cursor, struct kvnoList, link);
129         if (entry->kvno >= kvno)
130             break;
131     }
132     if (entry == NULL || entry->kvno != kvno)
133         return NULL;
134
135     return entry;
136 }
137
138 static struct kvnoList *
139 findByKvno(struct afsconf_dir *dir, afsconf_keyType type, int kvno)
140 {
141     struct keyTypeList *entry;
142     entry = findByType(dir, type);
143
144     if (entry == NULL)
145         return NULL;
146
147     return findInTypeList(entry, kvno);
148 }
149
150 static struct subTypeList *
151 findInKvnoList(struct kvnoList *parent, int subType)
152 {
153     struct opr_queue *cursor;
154     struct subTypeList *entry = NULL;
155
156     for (opr_queue_Scan(&parent->subTypeList, cursor)) {
157         entry = opr_queue_Entry(cursor, struct subTypeList, link);
158         if (entry->subType >= subType)
159              break;
160     }
161     if (entry == NULL || entry->subType != subType)
162         return NULL;
163
164     return entry;
165 }
166
167 static struct subTypeList *
168 findBySubType(struct afsconf_dir *dir, afsconf_keyType type, int kvno,
169               int subType)
170 {
171    struct kvnoList *entry;
172
173    entry = findByKvno(dir, type, kvno);
174    if (entry == NULL)
175         return NULL;
176
177    return findInKvnoList(entry, subType);
178 }
179
180
181 /* Add key. */
182 static int
183 addMemoryKey(struct afsconf_dir *dir, struct afsconf_typedKey *key,
184              int overwrite)
185 {
186     struct opr_queue   *cursor;
187     struct keyTypeList *typeEntry = NULL;
188     struct kvnoList    *kvnoEntry = NULL;
189     struct subTypeList *subType   = NULL;
190
191     /* Find the place in the keyType list to insert the key into */
192     for (opr_queue_Scan(&dir->keyList, cursor)) {
193         typeEntry = opr_queue_Entry(cursor, struct keyTypeList, link);
194         if (typeEntry->type >= key->type)
195             break;
196     }
197
198     if (typeEntry == NULL || typeEntry->type != key->type) {
199         struct keyTypeList *list;
200
201         list = malloc(sizeof(struct keyTypeList));
202         opr_queue_Init(&list->kvnoList);
203         list->type = key->type;
204         opr_queue_InsertBefore(cursor, &list->link);
205         typeEntry = list;
206     }
207
208     /* And the place in the kvno list */
209     for (opr_queue_Scan(&typeEntry->kvnoList, cursor)) {
210         kvnoEntry = opr_queue_Entry(cursor, struct kvnoList, link);
211         if (kvnoEntry->kvno >= key->kvno)
212             break;
213     }
214
215     if (kvnoEntry == NULL || kvnoEntry->kvno != key->kvno) {
216         struct kvnoList *list;
217
218         /* In the legacy rxkad key case, we need to check to see if we've
219          * gone over the maximum of 8 keys */
220         if (key->type == afsconf_rxkad &&
221             opr_queue_Count(&typeEntry->kvnoList)>=8)
222             return AFSCONF_FULL;
223
224         list = malloc(sizeof(struct kvnoList));
225         opr_queue_Init(&list->subTypeList);
226         list->kvno = key->kvno;
227         opr_queue_InsertBefore(cursor, &list->link);
228         kvnoEntry = list;
229     }
230
231     /* And the place in the subtype list */
232     for (opr_queue_Scan(&kvnoEntry->subTypeList, cursor)) {
233         subType = opr_queue_Entry(cursor, struct subTypeList, link);
234         if (subType->subType >= key->subType)
235             break;
236     }
237
238     if (subType == NULL || subType->subType != key->subType) {
239         struct subTypeList *list;
240
241         list = malloc(sizeof(struct subTypeList));
242         list->subType = key->subType;
243         list->key = afsconf_typedKey_get(key);
244         opr_queue_InsertBefore(cursor, &list->link);
245     } else {
246         if (overwrite) {
247             /* Give up our reference to the existing key */
248             afsconf_typedKey_put(&subType->key);
249             subType->key = afsconf_typedKey_get(key);
250         } else {
251             return AFSCONF_KEYINUSE;
252         }
253     }
254     return 0;
255 }
256
257 static void
258 deleteKvnoEntry(struct kvnoList *entry)
259 {
260     struct subTypeList *subTypeEntry;
261
262     while (!opr_queue_IsEmpty(&entry->subTypeList)) {
263         subTypeEntry = opr_queue_First(&entry->subTypeList,
264                                        struct subTypeList, link);
265         afsconf_typedKey_put(&subTypeEntry->key);
266         opr_queue_Remove(&subTypeEntry->link);
267         free(subTypeEntry);
268     }
269     opr_queue_Remove(&entry->link);
270     free(entry);
271 }
272
273 void
274 _afsconf_FreeAllKeys(struct afsconf_dir *dir)
275 {
276     struct keyTypeList *typeEntry;
277     struct kvnoList *kvnoEntry;
278
279     while (!opr_queue_IsEmpty(&dir->keyList)) {
280         typeEntry = opr_queue_First(&dir->keyList, struct keyTypeList, link);
281
282         while (!opr_queue_IsEmpty(&typeEntry->kvnoList)) {
283             kvnoEntry = opr_queue_First(&typeEntry->kvnoList,
284                                         struct kvnoList, link);
285
286             deleteKvnoEntry(kvnoEntry);
287         }
288         opr_queue_Remove(&typeEntry->link);
289         free(typeEntry);
290     }
291 }
292 void
293 _afsconf_InitKeys(struct afsconf_dir *dir)
294 {
295     opr_queue_Init(&dir->keyList);
296 }
297
298 /* Disk based key storage. This is made somewhat complicated because we
299  * store keys in more than one place - keys of type 'rxkad' (0) are stored
300  * in the original KeyFile, so that we can continue to be compatible with
301  * utilities that directly modify that file.
302  *
303  * All other keys are stored in the file KeyFileEx, which has the following
304  * format:
305  *    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
306  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
307  *   |                        version number                           |
308  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
309  *   |                        number of keys                           |
310  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
311  *   | Key data ...
312  *   +-+-+-+-+-+-+-+
313  *
314  * The version number is 1 at present. Version numbers higher than 1
315  * indicate a keyfile that is not backwards compatible with this
316  * specification.
317  *
318  * Key data is a sequence of the following records (note that these are
319  * not word aligned - the next record begins where the previous one ends)
320  *
321  *    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
322  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
323  *   |                         meta-data length                        |
324  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
325  *   |                           key type                              |
326  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
327  *   |                       key version number                        |
328  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
329  *   |                         key sub type                            |
330  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
331  *   |                      length of key material                     |
332  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
333  *   | Key material
334  *   +-+-+-+-+-+-+-+
335  *
336  * All values are expressed in network byte order
337  *
338  * Meta data length is the length of the initial portion of the record
339  * (itself, key type, key version number, and key sub type). In this
340  * version of the specification it would be 16. It is there to allow
341  * additional fields to be added to this specification at a later date
342  * without breaking backwards compatibility.
343  */
344
345 /* XXX - We need to be careful with failure here, because failure due to
346  * a missing file is fine, but failure due to read errors needs to be trapped,
347  * before it results in a corrupted file being written out.
348  */
349
350 static int
351 _parseOriginalKeyFile(struct afsconf_dir *dir, char *fileName)
352 {
353     int fd, code, nkeys, i;
354     struct afsconf_typedKey *key;
355
356     fd = open(fileName, O_RDONLY);
357     if (fd < 0)
358         return 0;
359
360     code = read(fd, &nkeys, sizeof(afs_int32));
361     if (code!= sizeof(afs_int32))
362         goto fail;
363
364     nkeys=ntohl(nkeys);
365     for(i=0; i<nkeys; i++) {
366
367         key = afsconf_typedKey_blank();
368
369         key->type = afsconf_rxkad;
370         key->subType = 0;
371
372         code = read(fd, &key->kvno, sizeof(afs_int32));
373         if (code != sizeof(afs_int32)) {
374             free(key);
375             goto fail;
376         }
377         key->kvno = ntohl(key->kvno);
378
379         rx_opaque_alloc(&key->key, 8);
380         code = read(fd, key->key.val, 8);
381         if (code != 8) {
382             rx_opaque_freeContents(&key->key);
383             free(key);
384             goto fail;
385         }
386         code = addMemoryKey(dir, key, 1);
387         afsconf_typedKey_put(&key); /* Done with key */
388         if (code)
389             goto fail;
390     }
391     close(fd);
392     return 0;
393
394 fail:
395     close(fd);
396     return EIO;
397 }
398
399 static_inline int
400 writeWord(int fd, afs_int32 data)
401 {
402
403      data = htonl(data);
404
405      if (write(fd, &data, sizeof(afs_int32)) != sizeof(afs_int32))
406         return EIO;
407
408      return 0;
409 }
410
411 static int
412 _writeOriginalKeyFile(struct afsconf_dir *dir, char *fileName)
413 {
414     int nkeys, fd;
415     struct opr_queue *cursor;
416     struct keyTypeList *typeEntry;
417
418     fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
419     if (fd < 0)
420         return AFSCONF_FAILURE;
421
422     typeEntry = findByType(dir, afsconf_rxkad);
423     if (typeEntry)
424         nkeys = opr_queue_Count(&typeEntry->kvnoList);
425     else
426         nkeys = 0;
427
428     if (writeWord(fd, nkeys))
429         goto fail;
430
431     if (typeEntry == NULL)
432         goto out;
433
434     for (opr_queue_Scan(&typeEntry->kvnoList, cursor)) {
435         struct kvnoList *kvnoEntry;
436         struct subTypeList *subEntry;
437
438         kvnoEntry = opr_queue_Entry(cursor, struct kvnoList, link);
439         subEntry = opr_queue_First(&kvnoEntry->subTypeList,
440                                    struct subTypeList, link);
441         if (writeWord(fd, subEntry->key->kvno))
442             goto fail;
443         if (write(fd, subEntry->key->key.val, 8) != 8)
444             goto fail;
445     }
446
447 out:
448     close(fd);
449     return 0;
450
451 fail:
452     close(fd);
453     return AFSCONF_FAILURE;
454 }
455
456 static int
457 _parseExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
458 {
459     int fd, i, code;
460     afs_int32 nkeys;
461     struct afsconf_typedKey *key;
462
463     fd = open(fileName, O_RDONLY);
464     if (fd < 0)
465         return 0;
466
467     code = read(fd, &nkeys, sizeof(afs_int32));
468     if (code!= sizeof(afs_int32))
469         goto fail;
470
471     nkeys=ntohl(nkeys);
472     for(i=0; i<nkeys; i++) {
473         afs_int32 reclen;
474
475         key = afsconf_typedKey_blank();
476
477         /* The only data version we currently parse has a reclen of 16.
478          * Anything smaller indicates a corrupt key file. Anything more,
479          * and we just skip the extra fields */
480         code = read(fd, &reclen, sizeof(afs_int32));
481         if (code != sizeof(afs_int32))
482             goto fail;
483         reclen = ntohl(reclen);
484         if (reclen < 16)
485             goto fail;
486         reclen-=sizeof(afs_int32);
487
488         code = read(fd, &key->type, sizeof(afs_int32));
489         if (code != sizeof(afs_int32))
490             goto fail;
491         key->type = ntohl(key->type);
492         reclen-=sizeof(afs_int32);
493
494         code = read(fd, &key->kvno, sizeof(afs_int32));
495         if (code != sizeof(afs_int32))
496             goto fail;
497         key->kvno = ntohl(key->kvno);
498         reclen-=sizeof(afs_int32);
499
500         code = read(fd, &key->subType, sizeof(afs_int32));
501         if (code != sizeof(afs_int32))
502             goto fail;
503         key->subType = ntohl(key->subType);
504         reclen-=sizeof(afs_int32);
505
506         if (reclen > 0) {
507             code = lseek(fd, reclen, SEEK_CUR);
508             if (code < 0)
509                 goto fail;
510         }
511
512         code = read(fd, &reclen, sizeof(afs_int32));
513         if (code != sizeof(afs_int32))
514             goto fail;
515         reclen = ntohl(reclen);
516
517         rx_opaque_alloc(&key->key, reclen);
518         code = read(fd, key->key.val, reclen);
519         if (code != reclen) {
520             rx_opaque_freeContents(&key->key);
521             free(key);
522             goto fail;
523         }
524         code = addMemoryKey(dir, key, 1);
525         afsconf_typedKey_put(&key);
526         if (code)
527             goto fail;
528     }
529     close(fd);
530     return 0;
531
532 fail:
533     close(fd);
534     return EIO;
535 }
536
537
538 static int
539 _writeExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
540 {
541     int nkeys;
542     int fd;
543
544     struct keyTypeList *typeEntry;
545     struct kvnoList    *kvnoEntry;
546     struct subTypeList *entry;
547     struct opr_queue   *keyCursor;
548     struct opr_queue   *kvnoCursor;
549     struct opr_queue   *subCursor;
550
551     fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
552     if (fd < 0)
553         return AFSCONF_FAILURE;
554
555     /* Iterate over the whole in-memory key store, and write everything
556      * except keys with type rxkad into the extended key file */
557
558     /* Write a 0 key count - we'll fill it in later */
559     nkeys = 0;
560     if (writeWord(fd, 0))
561         goto fail;
562
563     for (opr_queue_Scan(&dir->keyList, keyCursor)) {
564         typeEntry = opr_queue_Entry(keyCursor, struct keyTypeList, link);
565
566         if (typeEntry->type != afsconf_rxkad) {
567             for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
568                 kvnoEntry = opr_queue_Entry(kvnoCursor, struct kvnoList, link);
569                 for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) {
570                     entry = opr_queue_Entry(subCursor, struct subTypeList, link);
571                     if (writeWord(fd, 16)) /* record length */
572                         goto fail;
573                     if (writeWord(fd, entry->key->type))
574                         goto fail;
575                     if (writeWord(fd, entry->key->kvno))
576                         goto fail;
577                     if (writeWord(fd, entry->key->subType))
578                         goto fail;
579                     if (writeWord(fd, entry->key->key.len))
580                         goto fail;
581                     if (write(fd, entry->key->key.val,
582                               entry->key->key.len) !=
583                         entry->key->key.len)
584                         goto fail;
585                     nkeys++;
586                 }
587             }
588         }
589     }
590
591     if (lseek(fd, 0, SEEK_SET)<0)
592         goto fail;
593
594     if (writeWord(fd, nkeys))
595         goto fail;
596
597     close(fd);
598
599     return 0;
600
601 fail:
602     close(fd);
603     return AFSCONF_FAILURE;
604 }
605
606 int
607 _afsconf_LoadKeys(struct afsconf_dir *dir)
608 {
609     int code;
610     char *fileName;
611
612     /* If we're running on Windows, and we are a client, we don't have a
613      * KeyFile, so don't try and open one */
614
615 #ifdef AFS_NT40_ENV
616     if (_afsconf_IsClientConfigDirectory(dir->name))
617         return 0;
618 #endif /* AFS_NT40_ENV */
619
620     LOCK_GLOBAL_MUTEX;
621
622     /* Delete all of our existing keys */
623     _afsconf_FreeAllKeys(dir);
624
625     /* Start by opening the original KeyFile */
626     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_KEY_FILE);
627     code = _parseOriginalKeyFile(dir, fileName);
628     free(fileName);
629     if (code)
630         goto out;
631
632     /* Now open the new style KeyFile */
633     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_EXT_KEY_FILE);
634     code = _parseExtendedKeyFile(dir, fileName);
635     free(fileName);
636     if (code)
637         goto out;
638
639 out:
640     if (code)
641         _afsconf_FreeAllKeys(dir);
642
643     UNLOCK_GLOBAL_MUTEX;
644
645     return code;
646 }
647
648 static int
649 _afsconf_SaveKeys(struct afsconf_dir *dir)
650 {
651     char *fileName;
652     int code;
653
654     /* If we're running on Windows, and we are a client, we don't have a
655      * KeyFile, so don't try and open one */
656
657 #ifdef AFS_NT40_ENV
658     if (_afsconf_IsClientConfigDirectory(dir->name))
659         return 0;
660 #endif /* AFS_NT40_ENV */
661
662     LOCK_GLOBAL_MUTEX;
663
664     /* Start by opening the original KeyFile */
665     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_KEY_FILE);
666     code = _writeOriginalKeyFile(dir, fileName);
667     free(fileName);
668     if (code)
669         goto out;
670
671     /* Now open the new style KeyFile */
672     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_EXT_KEY_FILE);
673     code = _writeExtendedKeyFile(dir, fileName);
674     free(fileName);
675     if (code)
676         goto out;
677
678 out:
679     UNLOCK_GLOBAL_MUTEX;
680
681     return code;
682 }
683
684
685
686 /* get keys structure */
687 int
688 afsconf_GetKeys(struct afsconf_dir *dir, struct afsconf_keys *astr)
689 {
690     afs_int32 code;
691     struct keyTypeList *typeEntry;
692     struct opr_queue *cursor;
693
694     memset(astr, 0, sizeof(struct afsconf_keys));
695
696     LOCK_GLOBAL_MUTEX;
697
698     code = _afsconf_Check(dir);
699     if (code)
700         goto out;
701
702     typeEntry = findByType(dir, afsconf_rxkad);
703     if (typeEntry == NULL)
704         goto out;
705
706     for (opr_queue_Scan(&typeEntry->kvnoList, cursor)) {
707         struct kvnoList *kvnoEntry;
708         struct subTypeList *subEntry;
709
710         kvnoEntry = opr_queue_Entry(cursor, struct kvnoList, link);
711         subEntry = opr_queue_First(&kvnoEntry->subTypeList,
712                                    struct subTypeList, link);
713         /* XXX - If there is more than one key in this list, it's an error */
714         astr->key[astr->nkeys].kvno = subEntry->key->kvno;
715         /* XXX - If the opaque contains a number of bytes other than 8, it's
716          * an error */
717         memcpy(&astr->key[astr->nkeys].key, subEntry->key->key.val, 8);
718         astr->nkeys++;
719     }
720
721 out:
722     UNLOCK_GLOBAL_MUTEX;
723     return code;
724 }
725
726 afs_int32
727 afsconf_GetLatestKey(struct afsconf_dir *dir, afs_int32 *kvno,
728                      struct ktc_encryptionKey *key)
729 {
730     struct afsconf_typedKey *typedKey;
731     int code;
732
733     code = afsconf_GetLatestKeyByTypes(dir, afsconf_rxkad, 0, &typedKey);
734     if (code)
735         return code;
736
737     /* XXX - Should check that the key is of the correct length */
738
739     /* Copy out the relevant details */
740     if (kvno != NULL)
741         *kvno = typedKey->kvno;
742
743     if (key != NULL)
744         memcpy(key, typedKey->key.val, 8);
745
746     afsconf_typedKey_put(&typedKey);
747
748     return 0;
749 }
750
751 int
752 afsconf_GetKey(void *rock, int kvno, struct ktc_encryptionKey *key)
753 {
754     struct afsconf_typedKey *typedKey;
755     int code;
756
757     code = afsconf_GetKeyByTypes(rock, afsconf_rxkad, kvno, 0, &typedKey);
758     if (code)
759         return code;
760
761     memcpy(key, typedKey->key.val, 8);
762
763     afsconf_typedKey_put(&typedKey);
764
765     return 0;
766 }
767
768 int
769 afsconf_AddKey(struct afsconf_dir *dir, afs_int32 kvno, char key[8],
770                afs_int32 overwrite)
771 {
772     struct rx_opaque buffer;
773     struct afsconf_typedKey *typedKey;
774     int code;
775
776     rx_opaque_alloc(&buffer, 8);
777     memcpy(buffer.val, key, 8);
778     typedKey = afsconf_typedKey_new(afsconf_rxkad, kvno, 0, &buffer);
779     if (typedKey == NULL)
780         return AFSCONF_FAILURE;
781
782     rx_opaque_freeContents(&buffer);
783
784     code = afsconf_AddTypedKey(dir, typedKey, overwrite);
785     afsconf_typedKey_put(&typedKey);
786     return code;
787 }
788
789 int
790 afsconf_DeleteKey(struct afsconf_dir *dir, afs_int32 kvno)
791 {
792     return afsconf_DeleteKeyByType(dir, afsconf_rxkad, kvno);
793 }
794
795 int
796 afsconf_GetKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
797                       int kvno, struct afsconf_typedKeyList **keys)
798 {
799     struct kvnoList *kvnoEntry;
800     int code;
801
802     LOCK_GLOBAL_MUTEX;
803
804     code = _afsconf_Check(dir);
805     if (code)
806         goto out;
807
808     kvnoEntry = findByKvno(dir, type, kvno);
809     if (kvnoEntry == NULL) {
810         code = AFSCONF_NOTFOUND;
811         goto out;
812     }
813
814     code = listToArray(kvnoEntry, keys);
815
816 out:
817     UNLOCK_GLOBAL_MUTEX;
818     return code;
819 }
820
821 int
822 afsconf_GetAllKeys(struct afsconf_dir *dir, struct afsconf_typedKeyList **keys)
823 {
824     int code;
825     struct afsconf_typedKeyList *retval;
826     struct opr_queue *typeCursor;
827     struct keyTypeList *typeEntry;
828     struct opr_queue *kvnoCursor;
829     struct kvnoList *kvnoEntry;
830     struct opr_queue *subCursor;
831     struct subTypeList *subEntry;
832     int count;
833
834     LOCK_GLOBAL_MUTEX;
835
836     code = _afsconf_Check(dir);
837     if (code)
838         goto out;
839
840     count = 0;
841     /* First, work out how many keys we have in total */
842     for (opr_queue_Scan(&dir->keyList, typeCursor)) {
843         typeEntry = opr_queue_Entry(typeCursor, struct keyTypeList, link);
844         for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
845             kvnoEntry = opr_queue_Entry(kvnoCursor, struct kvnoList, link);
846             for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) {
847                 subEntry = opr_queue_Entry(subCursor, struct subTypeList, link);
848                 count++;
849             }
850         }
851     }
852
853     /* Allocate space for all of these */
854     retval = malloc(sizeof(struct afsconf_typedKeyList));
855     retval->nkeys = count;
856
857     if (count > 0) {
858         retval->keys = calloc(retval->nkeys,
859                               sizeof(struct afsconf_typedKey *));
860
861         /* Populate the key list */
862         count = 0;
863         for (opr_queue_Scan(&dir->keyList, typeCursor)) {
864             typeEntry = opr_queue_Entry(typeCursor,
865                                         struct keyTypeList, link);
866             for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
867                 kvnoEntry = opr_queue_Entry(kvnoCursor,
868                                             struct kvnoList, link);
869                 for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) {
870                     subEntry = opr_queue_Entry(subCursor,
871                                                struct subTypeList, link);
872                     retval->keys[count] = afsconf_typedKey_get(subEntry->key);
873                     count++;
874                 }
875             }
876         }
877     } else {
878         retval->keys = NULL;
879     }
880
881     *keys = retval;
882
883 out:
884     UNLOCK_GLOBAL_MUTEX;
885     return code;
886 }
887
888 int
889 afsconf_GetKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
890                       int kvno, int subType, struct afsconf_typedKey **key)
891 {
892     int code = 0;
893     struct subTypeList *subTypeEntry;
894
895     LOCK_GLOBAL_MUTEX;
896
897     code = _afsconf_Check(dir);
898     if (code)
899         goto out;
900
901     subTypeEntry = findBySubType(dir, type, kvno, subType);
902     if (subTypeEntry == NULL) {
903         code = AFSCONF_NOTFOUND;
904         goto out;
905     }
906
907     *key = afsconf_typedKey_get(subTypeEntry->key);
908
909 out:
910     UNLOCK_GLOBAL_MUTEX;
911     return code;
912 }
913
914 static struct kvnoList *
915 pickBestKvno(struct afsconf_dir *dir, afsconf_keyType type)
916 {
917     struct keyTypeList *typeEntry;
918     struct kvnoList    *kvnoEntry;
919
920     typeEntry = findByType(dir, type);
921     if (typeEntry == NULL)
922         return NULL;
923
924     /* We store all of the key lists ordered, so the last entry in the
925      * kvno list must be the highest kvno. */
926
927     kvnoEntry = opr_queue_Last(&typeEntry->kvnoList, struct kvnoList, link);
928
929     /* Except, if we're in the rxkad list, we might have a bcrypt entry that
930      * has a kvno of 999. So we need to skip that one
931      */
932     while (type == afsconf_rxgk && kvnoEntry->kvno == 999) {
933         kvnoEntry = opr_queue_Prev(&typeEntry->kvnoList, struct kvnoList,
934                                    link);
935         if (opr_queue_IsEnd(&typeEntry->kvnoList, &kvnoEntry->link))
936            return NULL;
937     }
938
939     return kvnoEntry;
940 }
941
942
943 int
944 afsconf_GetLatestKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
945                             struct afsconf_typedKeyList **keys)
946 {
947     int code;
948     struct kvnoList *kvnoEntry;
949
950     LOCK_GLOBAL_MUTEX;
951
952     code = _afsconf_Check(dir);
953     if (code)
954         goto out;
955
956
957     kvnoEntry = pickBestKvno(dir, type);
958     if (kvnoEntry == NULL) {
959         code = AFSCONF_NOTFOUND;
960         goto out;
961     }
962
963     code = listToArray(kvnoEntry, keys);
964
965 out:
966     UNLOCK_GLOBAL_MUTEX;
967     return code;
968 }
969
970 int
971 afsconf_GetLatestKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
972                             int subType, struct afsconf_typedKey **key)
973 {
974     int code;
975     struct kvnoList *kvnoEntry;
976     struct subTypeList *subTypeEntry;
977
978     LOCK_GLOBAL_MUTEX;
979
980     code = _afsconf_Check(dir);
981     if (code)
982         goto out;
983
984     kvnoEntry = pickBestKvno(dir, type);
985     if (kvnoEntry == NULL) {
986         code = AFSCONF_NOTFOUND;
987         goto out;
988     }
989
990     subTypeEntry = findInKvnoList(kvnoEntry, subType);
991     if (subTypeEntry == NULL) {
992         code = AFSCONF_NOTFOUND;
993         goto out;
994     }
995
996     *key = afsconf_typedKey_get(subTypeEntry->key);
997
998 out:
999     UNLOCK_GLOBAL_MUTEX;
1000     return code;
1001 }
1002
1003 void
1004 afsconf_PutTypedKeyList(struct afsconf_typedKeyList **keys)
1005 {
1006      int i;
1007
1008      for (i=0;i<(*keys)->nkeys;i++)
1009         afsconf_typedKey_put(&((*keys)->keys[i]));
1010
1011      if ((*keys)->keys != NULL)
1012         free((*keys)->keys);
1013
1014      free(*keys);
1015      *keys = NULL;
1016 }
1017
1018 static struct afsconf_typedKey *
1019 afsconf_typedKey_blank(void)
1020 {
1021     struct afsconf_typedKey *key;
1022
1023     key = malloc(sizeof(struct afsconf_typedKey));
1024     if (key == NULL)
1025         return NULL;
1026
1027     memset(key, 0, sizeof(struct afsconf_typedKey));
1028     rx_atomic_set(&key->refcnt, 1);
1029
1030     return key;
1031 }
1032
1033 struct afsconf_typedKey *
1034 afsconf_typedKey_new(afsconf_keyType type, int kvno, int subType,
1035                      struct rx_opaque *keyMaterial)
1036 {
1037     struct afsconf_typedKey *key;
1038     int code;
1039
1040     key = afsconf_typedKey_blank();
1041     if (key == NULL)
1042         return key;
1043
1044     key->type = type;
1045     key->kvno = kvno;
1046     key->subType = subType;
1047
1048     code = rx_opaque_copy(&key->key, keyMaterial);
1049     if (code != 0) {
1050         free(key);
1051         return NULL;
1052     }
1053
1054     return key;
1055 }
1056
1057 void
1058 afsconf_typedKey_free(struct afsconf_typedKey **key)
1059 {
1060     rx_opaque_freeContents(&(*key)->key);
1061     free(*key);
1062     *key = NULL;
1063 }
1064
1065 struct afsconf_typedKey *
1066 afsconf_typedKey_get(struct afsconf_typedKey *key)
1067 {
1068      rx_atomic_inc(&key->refcnt);
1069      return key;
1070 }
1071
1072 void
1073 afsconf_typedKey_put(struct afsconf_typedKey **key)
1074 {
1075     if (rx_atomic_dec_and_read(&(*key)->refcnt) == 0)
1076         afsconf_typedKey_free(key);
1077     else
1078         *key = NULL;
1079 }
1080
1081 void
1082 afsconf_typedKey_values(struct afsconf_typedKey *key, afsconf_keyType *type,
1083                         int *kvno, int *subType, struct rx_opaque **material)
1084 {
1085     *type = key->type;
1086     *kvno = key->kvno;
1087     *subType = key->subType;
1088     *material = &key->key;
1089 }
1090
1091 int
1092 afsconf_AddTypedKey(struct afsconf_dir *dir,
1093                     struct afsconf_typedKey *key,
1094                     int overwrite)
1095 {
1096     int code;
1097
1098     LOCK_GLOBAL_MUTEX;
1099
1100     code = _afsconf_Check(dir);
1101     if (code)
1102         goto out;
1103
1104     if (key->type == afsconf_rxkad) {
1105         /* There are restrictions on rxkad keys so that we can still
1106          * return them using the old interface. We only enforce the
1107          * same restrictions as that interface does - that is, we don't
1108          * check that the key we're passed is a valid DES key */
1109         if (key->key.len != 8 || key->subType != 0) {
1110             code = AFSCONF_BADKEY;
1111             goto out;
1112         }
1113     }
1114
1115     code = addMemoryKey(dir, key, overwrite);
1116     if (code)
1117         goto out;
1118
1119     code = _afsconf_SaveKeys(dir);
1120     _afsconf_Touch(dir);
1121
1122 out:
1123     UNLOCK_GLOBAL_MUTEX;
1124     return code;
1125 }
1126
1127 int
1128 afsconf_DeleteKeyByType(struct afsconf_dir *dir,
1129                         afsconf_keyType type, int kvno)
1130 {
1131     struct keyTypeList *typeEntry;
1132     struct kvnoList *kvnoEntry;
1133     int code;
1134
1135     LOCK_GLOBAL_MUTEX;
1136
1137     code = _afsconf_Check(dir);
1138     if (code)
1139         goto out;
1140
1141     typeEntry = findByType(dir, type);
1142     if (typeEntry == NULL) {
1143         code = AFSCONF_NOTFOUND;
1144         goto out;
1145     }
1146
1147     kvnoEntry = findInTypeList(typeEntry, kvno);
1148     if (kvnoEntry == NULL) {
1149         code = AFSCONF_NOTFOUND;
1150         goto out;
1151     }
1152
1153     deleteKvnoEntry(kvnoEntry);
1154
1155     /* Remove the typeEntry, if it has no sub elements */
1156     if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
1157         opr_queue_Remove(&typeEntry->link);
1158         free(typeEntry);
1159     }
1160
1161     code = _afsconf_SaveKeys(dir);
1162     _afsconf_Touch(dir);
1163
1164 out:
1165     UNLOCK_GLOBAL_MUTEX;
1166     return code;
1167 }
1168
1169 int
1170 afsconf_DeleteKeyBySubType(struct afsconf_dir *dir,
1171                            afsconf_keyType type, int kvno, int subType)
1172 {
1173     struct keyTypeList *typeEntry;
1174     struct kvnoList *kvnoEntry;
1175     struct subTypeList *subTypeEntry;
1176     int code;
1177
1178     LOCK_GLOBAL_MUTEX;
1179
1180     code = _afsconf_Check(dir);
1181     if (code)
1182         goto out;
1183
1184     typeEntry = findByType(dir, type);
1185     if (typeEntry == NULL)
1186         return AFSCONF_NOTFOUND;
1187
1188     kvnoEntry = findInTypeList(typeEntry, kvno);
1189     if (kvnoEntry == NULL)
1190         return AFSCONF_NOTFOUND;
1191
1192     subTypeEntry = findInKvnoList(kvnoEntry, subType);
1193     if (subTypeEntry == NULL)
1194         return AFSCONF_NOTFOUND;
1195
1196     /* Remove the subTypeEntry */
1197     afsconf_typedKey_put(&subTypeEntry->key);
1198     opr_queue_Remove(&subTypeEntry->link);
1199     free(subTypeEntry);
1200
1201     /* Remove the kvnoEntry, if it has no sub elements */
1202     if (opr_queue_IsEmpty(&kvnoEntry->subTypeList)) {
1203         opr_queue_Remove(&kvnoEntry->link);
1204         free(kvnoEntry);
1205     }
1206
1207     /* Remove the typeEntry, if it has no sub elements */
1208     if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
1209         opr_queue_Remove(&typeEntry->link);
1210         free(typeEntry);
1211     }
1212
1213     code = _afsconf_SaveKeys(dir);
1214     _afsconf_Touch(dir);
1215
1216 out:
1217     UNLOCK_GLOBAL_MUTEX;
1218     return code;
1219 }
1220
1221 int
1222 afsconf_DeleteTypedKey(struct afsconf_dir *dir, struct afsconf_typedKey *key)
1223 {
1224     return afsconf_DeleteKeyBySubType(dir, key->type, key->kvno, key->subType);
1225 }