auth: Don't leak key on file parse error
[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 = NULL;
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     if (key)
534         afsconf_typedKey_put(&key);
535
536     close(fd);
537     return EIO;
538 }
539
540
541 static int
542 _writeExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
543 {
544     int nkeys;
545     int fd;
546
547     struct keyTypeList *typeEntry;
548     struct kvnoList    *kvnoEntry;
549     struct subTypeList *entry;
550     struct opr_queue   *keyCursor;
551     struct opr_queue   *kvnoCursor;
552     struct opr_queue   *subCursor;
553
554     fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
555     if (fd < 0)
556         return AFSCONF_FAILURE;
557
558     /* Iterate over the whole in-memory key store, and write everything
559      * except keys with type rxkad into the extended key file */
560
561     /* Write a 0 key count - we'll fill it in later */
562     nkeys = 0;
563     if (writeWord(fd, 0))
564         goto fail;
565
566     for (opr_queue_Scan(&dir->keyList, keyCursor)) {
567         typeEntry = opr_queue_Entry(keyCursor, struct keyTypeList, link);
568
569         if (typeEntry->type != afsconf_rxkad) {
570             for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
571                 kvnoEntry = opr_queue_Entry(kvnoCursor, struct kvnoList, link);
572                 for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) {
573                     entry = opr_queue_Entry(subCursor, struct subTypeList, link);
574                     if (writeWord(fd, 16)) /* record length */
575                         goto fail;
576                     if (writeWord(fd, entry->key->type))
577                         goto fail;
578                     if (writeWord(fd, entry->key->kvno))
579                         goto fail;
580                     if (writeWord(fd, entry->key->subType))
581                         goto fail;
582                     if (writeWord(fd, entry->key->key.len))
583                         goto fail;
584                     if (write(fd, entry->key->key.val,
585                               entry->key->key.len) !=
586                         entry->key->key.len)
587                         goto fail;
588                     nkeys++;
589                 }
590             }
591         }
592     }
593
594     if (lseek(fd, 0, SEEK_SET)<0)
595         goto fail;
596
597     if (writeWord(fd, nkeys))
598         goto fail;
599
600     close(fd);
601
602     return 0;
603
604 fail:
605     close(fd);
606     return AFSCONF_FAILURE;
607 }
608
609 int
610 _afsconf_LoadKeys(struct afsconf_dir *dir)
611 {
612     int code;
613     char *fileName;
614
615     /* If we're running on Windows, and we are a client, we don't have a
616      * KeyFile, so don't try and open one */
617
618 #ifdef AFS_NT40_ENV
619     if (_afsconf_IsClientConfigDirectory(dir->name))
620         return 0;
621 #endif /* AFS_NT40_ENV */
622
623     LOCK_GLOBAL_MUTEX;
624
625     /* Delete all of our existing keys */
626     _afsconf_FreeAllKeys(dir);
627
628     /* Start by opening the original KeyFile */
629     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_KEY_FILE);
630     code = _parseOriginalKeyFile(dir, fileName);
631     free(fileName);
632     if (code)
633         goto out;
634
635     /* Now open the new style KeyFile */
636     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_EXT_KEY_FILE);
637     code = _parseExtendedKeyFile(dir, fileName);
638     free(fileName);
639     if (code)
640         goto out;
641
642 out:
643     if (code)
644         _afsconf_FreeAllKeys(dir);
645
646     UNLOCK_GLOBAL_MUTEX;
647
648     return code;
649 }
650
651 static int
652 _afsconf_SaveKeys(struct afsconf_dir *dir)
653 {
654     char *fileName;
655     int code;
656
657     /* If we're running on Windows, and we are a client, we don't have a
658      * KeyFile, so don't try and open one */
659
660 #ifdef AFS_NT40_ENV
661     if (_afsconf_IsClientConfigDirectory(dir->name))
662         return 0;
663 #endif /* AFS_NT40_ENV */
664
665     LOCK_GLOBAL_MUTEX;
666
667     /* Start by opening the original KeyFile */
668     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_KEY_FILE);
669     code = _writeOriginalKeyFile(dir, fileName);
670     free(fileName);
671     if (code)
672         goto out;
673
674     /* Now open the new style KeyFile */
675     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_EXT_KEY_FILE);
676     code = _writeExtendedKeyFile(dir, fileName);
677     free(fileName);
678     if (code)
679         goto out;
680
681 out:
682     UNLOCK_GLOBAL_MUTEX;
683
684     return code;
685 }
686
687
688
689 /* get keys structure */
690 int
691 afsconf_GetKeys(struct afsconf_dir *dir, struct afsconf_keys *astr)
692 {
693     afs_int32 code;
694     struct keyTypeList *typeEntry;
695     struct opr_queue *cursor;
696
697     memset(astr, 0, sizeof(struct afsconf_keys));
698
699     LOCK_GLOBAL_MUTEX;
700
701     code = _afsconf_Check(dir);
702     if (code)
703         goto out;
704
705     typeEntry = findByType(dir, afsconf_rxkad);
706     if (typeEntry == NULL)
707         goto out;
708
709     for (opr_queue_Scan(&typeEntry->kvnoList, cursor)) {
710         struct kvnoList *kvnoEntry;
711         struct subTypeList *subEntry;
712
713         kvnoEntry = opr_queue_Entry(cursor, struct kvnoList, link);
714         subEntry = opr_queue_First(&kvnoEntry->subTypeList,
715                                    struct subTypeList, link);
716         /* XXX - If there is more than one key in this list, it's an error */
717         astr->key[astr->nkeys].kvno = subEntry->key->kvno;
718         /* XXX - If the opaque contains a number of bytes other than 8, it's
719          * an error */
720         memcpy(&astr->key[astr->nkeys].key, subEntry->key->key.val, 8);
721         astr->nkeys++;
722     }
723
724 out:
725     UNLOCK_GLOBAL_MUTEX;
726     return code;
727 }
728
729 afs_int32
730 afsconf_GetLatestKey(struct afsconf_dir *dir, afs_int32 *kvno,
731                      struct ktc_encryptionKey *key)
732 {
733     struct afsconf_typedKey *typedKey;
734     int code;
735
736     code = afsconf_GetLatestKeyByTypes(dir, afsconf_rxkad, 0, &typedKey);
737     if (code)
738         return code;
739
740     /* XXX - Should check that the key is of the correct length */
741
742     /* Copy out the relevant details */
743     if (kvno != NULL)
744         *kvno = typedKey->kvno;
745
746     if (key != NULL)
747         memcpy(key, typedKey->key.val, 8);
748
749     afsconf_typedKey_put(&typedKey);
750
751     return 0;
752 }
753
754 int
755 afsconf_GetKey(void *rock, int kvno, struct ktc_encryptionKey *key)
756 {
757     struct afsconf_typedKey *typedKey;
758     int code;
759
760     code = afsconf_GetKeyByTypes(rock, afsconf_rxkad, kvno, 0, &typedKey);
761     if (code)
762         return code;
763
764     memcpy(key, typedKey->key.val, 8);
765
766     afsconf_typedKey_put(&typedKey);
767
768     return 0;
769 }
770
771 int
772 afsconf_AddKey(struct afsconf_dir *dir, afs_int32 kvno, char key[8],
773                afs_int32 overwrite)
774 {
775     struct rx_opaque buffer;
776     struct afsconf_typedKey *typedKey;
777     int code;
778
779     rx_opaque_alloc(&buffer, 8);
780     memcpy(buffer.val, key, 8);
781     typedKey = afsconf_typedKey_new(afsconf_rxkad, kvno, 0, &buffer);
782     if (typedKey == NULL)
783         return AFSCONF_FAILURE;
784
785     rx_opaque_freeContents(&buffer);
786
787     code = afsconf_AddTypedKey(dir, typedKey, overwrite);
788     afsconf_typedKey_put(&typedKey);
789     return code;
790 }
791
792 int
793 afsconf_DeleteKey(struct afsconf_dir *dir, afs_int32 kvno)
794 {
795     return afsconf_DeleteKeyByType(dir, afsconf_rxkad, kvno);
796 }
797
798 int
799 afsconf_GetKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
800                       int kvno, struct afsconf_typedKeyList **keys)
801 {
802     struct kvnoList *kvnoEntry;
803     int code;
804
805     LOCK_GLOBAL_MUTEX;
806
807     code = _afsconf_Check(dir);
808     if (code)
809         goto out;
810
811     kvnoEntry = findByKvno(dir, type, kvno);
812     if (kvnoEntry == NULL) {
813         code = AFSCONF_NOTFOUND;
814         goto out;
815     }
816
817     code = listToArray(kvnoEntry, keys);
818
819 out:
820     UNLOCK_GLOBAL_MUTEX;
821     return code;
822 }
823
824 int
825 afsconf_GetAllKeys(struct afsconf_dir *dir, struct afsconf_typedKeyList **keys)
826 {
827     int code;
828     struct afsconf_typedKeyList *retval;
829     struct opr_queue *typeCursor;
830     struct keyTypeList *typeEntry;
831     struct opr_queue *kvnoCursor;
832     struct kvnoList *kvnoEntry;
833     struct opr_queue *subCursor;
834     struct subTypeList *subEntry;
835     int count;
836
837     LOCK_GLOBAL_MUTEX;
838
839     code = _afsconf_Check(dir);
840     if (code)
841         goto out;
842
843     count = 0;
844     /* First, work out how many keys we have in total */
845     for (opr_queue_Scan(&dir->keyList, typeCursor)) {
846         typeEntry = opr_queue_Entry(typeCursor, struct keyTypeList, link);
847         for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
848             kvnoEntry = opr_queue_Entry(kvnoCursor, struct kvnoList, link);
849             for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) {
850                 subEntry = opr_queue_Entry(subCursor, struct subTypeList, link);
851                 count++;
852             }
853         }
854     }
855
856     /* Allocate space for all of these */
857     retval = malloc(sizeof(struct afsconf_typedKeyList));
858     retval->nkeys = count;
859
860     if (count > 0) {
861         retval->keys = calloc(retval->nkeys,
862                               sizeof(struct afsconf_typedKey *));
863
864         /* Populate the key list */
865         count = 0;
866         for (opr_queue_Scan(&dir->keyList, typeCursor)) {
867             typeEntry = opr_queue_Entry(typeCursor,
868                                         struct keyTypeList, link);
869             for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
870                 kvnoEntry = opr_queue_Entry(kvnoCursor,
871                                             struct kvnoList, link);
872                 for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) {
873                     subEntry = opr_queue_Entry(subCursor,
874                                                struct subTypeList, link);
875                     retval->keys[count] = afsconf_typedKey_get(subEntry->key);
876                     count++;
877                 }
878             }
879         }
880     } else {
881         retval->keys = NULL;
882     }
883
884     *keys = retval;
885
886 out:
887     UNLOCK_GLOBAL_MUTEX;
888     return code;
889 }
890
891 int
892 afsconf_GetKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
893                       int kvno, int subType, struct afsconf_typedKey **key)
894 {
895     int code = 0;
896     struct subTypeList *subTypeEntry;
897
898     LOCK_GLOBAL_MUTEX;
899
900     code = _afsconf_Check(dir);
901     if (code)
902         goto out;
903
904     subTypeEntry = findBySubType(dir, type, kvno, subType);
905     if (subTypeEntry == NULL) {
906         code = AFSCONF_NOTFOUND;
907         goto out;
908     }
909
910     *key = afsconf_typedKey_get(subTypeEntry->key);
911
912 out:
913     UNLOCK_GLOBAL_MUTEX;
914     return code;
915 }
916
917 static struct kvnoList *
918 pickBestKvno(struct afsconf_dir *dir, afsconf_keyType type)
919 {
920     struct keyTypeList *typeEntry;
921     struct kvnoList    *kvnoEntry;
922
923     typeEntry = findByType(dir, type);
924     if (typeEntry == NULL)
925         return NULL;
926
927     /* We store all of the key lists ordered, so the last entry in the
928      * kvno list must be the highest kvno. */
929
930     kvnoEntry = opr_queue_Last(&typeEntry->kvnoList, struct kvnoList, link);
931
932     /* Except, if we're in the rxkad list, we might have a bcrypt entry that
933      * has a kvno of 999. So we need to skip that one
934      */
935     while (type == afsconf_rxgk && kvnoEntry->kvno == 999) {
936         kvnoEntry = opr_queue_Prev(&typeEntry->kvnoList, struct kvnoList,
937                                    link);
938         if (opr_queue_IsEnd(&typeEntry->kvnoList, &kvnoEntry->link))
939            return NULL;
940     }
941
942     return kvnoEntry;
943 }
944
945
946 int
947 afsconf_GetLatestKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
948                             struct afsconf_typedKeyList **keys)
949 {
950     int code;
951     struct kvnoList *kvnoEntry;
952
953     LOCK_GLOBAL_MUTEX;
954
955     code = _afsconf_Check(dir);
956     if (code)
957         goto out;
958
959
960     kvnoEntry = pickBestKvno(dir, type);
961     if (kvnoEntry == NULL) {
962         code = AFSCONF_NOTFOUND;
963         goto out;
964     }
965
966     code = listToArray(kvnoEntry, keys);
967
968 out:
969     UNLOCK_GLOBAL_MUTEX;
970     return code;
971 }
972
973 int
974 afsconf_GetLatestKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
975                             int subType, struct afsconf_typedKey **key)
976 {
977     int code;
978     struct kvnoList *kvnoEntry;
979     struct subTypeList *subTypeEntry;
980
981     LOCK_GLOBAL_MUTEX;
982
983     code = _afsconf_Check(dir);
984     if (code)
985         goto out;
986
987     kvnoEntry = pickBestKvno(dir, type);
988     if (kvnoEntry == NULL) {
989         code = AFSCONF_NOTFOUND;
990         goto out;
991     }
992
993     subTypeEntry = findInKvnoList(kvnoEntry, subType);
994     if (subTypeEntry == NULL) {
995         code = AFSCONF_NOTFOUND;
996         goto out;
997     }
998
999     *key = afsconf_typedKey_get(subTypeEntry->key);
1000
1001 out:
1002     UNLOCK_GLOBAL_MUTEX;
1003     return code;
1004 }
1005
1006 void
1007 afsconf_PutTypedKeyList(struct afsconf_typedKeyList **keys)
1008 {
1009      int i;
1010
1011      for (i=0;i<(*keys)->nkeys;i++)
1012         afsconf_typedKey_put(&((*keys)->keys[i]));
1013
1014      if ((*keys)->keys != NULL)
1015         free((*keys)->keys);
1016
1017      free(*keys);
1018      *keys = NULL;
1019 }
1020
1021 static struct afsconf_typedKey *
1022 afsconf_typedKey_blank(void)
1023 {
1024     struct afsconf_typedKey *key;
1025
1026     key = malloc(sizeof(struct afsconf_typedKey));
1027     if (key == NULL)
1028         return NULL;
1029
1030     memset(key, 0, sizeof(struct afsconf_typedKey));
1031     rx_atomic_set(&key->refcnt, 1);
1032
1033     return key;
1034 }
1035
1036 struct afsconf_typedKey *
1037 afsconf_typedKey_new(afsconf_keyType type, int kvno, int subType,
1038                      struct rx_opaque *keyMaterial)
1039 {
1040     struct afsconf_typedKey *key;
1041     int code;
1042
1043     key = afsconf_typedKey_blank();
1044     if (key == NULL)
1045         return key;
1046
1047     key->type = type;
1048     key->kvno = kvno;
1049     key->subType = subType;
1050
1051     code = rx_opaque_copy(&key->key, keyMaterial);
1052     if (code != 0) {
1053         free(key);
1054         return NULL;
1055     }
1056
1057     return key;
1058 }
1059
1060 void
1061 afsconf_typedKey_free(struct afsconf_typedKey **key)
1062 {
1063     rx_opaque_freeContents(&(*key)->key);
1064     free(*key);
1065     *key = NULL;
1066 }
1067
1068 struct afsconf_typedKey *
1069 afsconf_typedKey_get(struct afsconf_typedKey *key)
1070 {
1071      rx_atomic_inc(&key->refcnt);
1072      return key;
1073 }
1074
1075 void
1076 afsconf_typedKey_put(struct afsconf_typedKey **key)
1077 {
1078     if (rx_atomic_dec_and_read(&(*key)->refcnt) == 0)
1079         afsconf_typedKey_free(key);
1080     else
1081         *key = NULL;
1082 }
1083
1084 void
1085 afsconf_typedKey_values(struct afsconf_typedKey *key, afsconf_keyType *type,
1086                         int *kvno, int *subType, struct rx_opaque **material)
1087 {
1088     *type = key->type;
1089     *kvno = key->kvno;
1090     *subType = key->subType;
1091     *material = &key->key;
1092 }
1093
1094 int
1095 afsconf_AddTypedKey(struct afsconf_dir *dir,
1096                     struct afsconf_typedKey *key,
1097                     int overwrite)
1098 {
1099     int code;
1100
1101     LOCK_GLOBAL_MUTEX;
1102
1103     code = _afsconf_Check(dir);
1104     if (code)
1105         goto out;
1106
1107     if (key->type == afsconf_rxkad) {
1108         /* There are restrictions on rxkad keys so that we can still
1109          * return them using the old interface. We only enforce the
1110          * same restrictions as that interface does - that is, we don't
1111          * check that the key we're passed is a valid DES key */
1112         if (key->key.len != 8 || key->subType != 0) {
1113             code = AFSCONF_BADKEY;
1114             goto out;
1115         }
1116     }
1117
1118     code = addMemoryKey(dir, key, overwrite);
1119     if (code)
1120         goto out;
1121
1122     code = _afsconf_SaveKeys(dir);
1123     _afsconf_Touch(dir);
1124
1125 out:
1126     UNLOCK_GLOBAL_MUTEX;
1127     return code;
1128 }
1129
1130 int
1131 afsconf_DeleteKeyByType(struct afsconf_dir *dir,
1132                         afsconf_keyType type, int kvno)
1133 {
1134     struct keyTypeList *typeEntry;
1135     struct kvnoList *kvnoEntry;
1136     int code;
1137
1138     LOCK_GLOBAL_MUTEX;
1139
1140     code = _afsconf_Check(dir);
1141     if (code)
1142         goto out;
1143
1144     typeEntry = findByType(dir, type);
1145     if (typeEntry == NULL) {
1146         code = AFSCONF_NOTFOUND;
1147         goto out;
1148     }
1149
1150     kvnoEntry = findInTypeList(typeEntry, kvno);
1151     if (kvnoEntry == NULL) {
1152         code = AFSCONF_NOTFOUND;
1153         goto out;
1154     }
1155
1156     deleteKvnoEntry(kvnoEntry);
1157
1158     /* Remove the typeEntry, if it has no sub elements */
1159     if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
1160         opr_queue_Remove(&typeEntry->link);
1161         free(typeEntry);
1162     }
1163
1164     code = _afsconf_SaveKeys(dir);
1165     _afsconf_Touch(dir);
1166
1167 out:
1168     UNLOCK_GLOBAL_MUTEX;
1169     return code;
1170 }
1171
1172 int
1173 afsconf_DeleteKeyBySubType(struct afsconf_dir *dir,
1174                            afsconf_keyType type, int kvno, int subType)
1175 {
1176     struct keyTypeList *typeEntry;
1177     struct kvnoList *kvnoEntry;
1178     struct subTypeList *subTypeEntry;
1179     int code;
1180
1181     LOCK_GLOBAL_MUTEX;
1182
1183     code = _afsconf_Check(dir);
1184     if (code)
1185         goto out;
1186
1187     typeEntry = findByType(dir, type);
1188     if (typeEntry == NULL)
1189         return AFSCONF_NOTFOUND;
1190
1191     kvnoEntry = findInTypeList(typeEntry, kvno);
1192     if (kvnoEntry == NULL)
1193         return AFSCONF_NOTFOUND;
1194
1195     subTypeEntry = findInKvnoList(kvnoEntry, subType);
1196     if (subTypeEntry == NULL)
1197         return AFSCONF_NOTFOUND;
1198
1199     /* Remove the subTypeEntry */
1200     afsconf_typedKey_put(&subTypeEntry->key);
1201     opr_queue_Remove(&subTypeEntry->link);
1202     free(subTypeEntry);
1203
1204     /* Remove the kvnoEntry, if it has no sub elements */
1205     if (opr_queue_IsEmpty(&kvnoEntry->subTypeList)) {
1206         opr_queue_Remove(&kvnoEntry->link);
1207         free(kvnoEntry);
1208     }
1209
1210     /* Remove the typeEntry, if it has no sub elements */
1211     if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
1212         opr_queue_Remove(&typeEntry->link);
1213         free(typeEntry);
1214     }
1215
1216     code = _afsconf_SaveKeys(dir);
1217     _afsconf_Touch(dir);
1218
1219 out:
1220     UNLOCK_GLOBAL_MUTEX;
1221     return code;
1222 }
1223
1224 int
1225 afsconf_DeleteTypedKey(struct afsconf_dir *dir, struct afsconf_typedKey *key)
1226 {
1227     return afsconf_DeleteKeyBySubType(dir, key->type, key->kvno, key->subType);
1228 }