bcrypt keys are in the rxkad list, not the rxgk list
[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 KeyFileExt, 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  *   |                        number of keys                           |
308  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
309  *   | Key data ...
310  *   +-+-+-+-+-+-+-+
311  *
312  * If the format ever needs to chanage incompatibly, a new file name
313  * will be used.
314  *
315  * Key data is a sequence of the following records (note that these are
316  * not word aligned - the next record begins where the previous one ends)
317  *
318  *    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
319  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
320  *   |                         meta-data length                        |
321  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
322  *   |                           key type                              |
323  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
324  *   |                       key version number                        |
325  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
326  *   |                         key sub type                            |
327  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
328  *   |                      length of key material                     |
329  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
330  *   | Key material
331  *   +-+-+-+-+-+-+-+
332  *
333  * All values are expressed in network byte order
334  *
335  * Meta data length is the length of the initial portion of the record
336  * (itself, key type, key version number, and key sub type). In this
337  * version of the specification it would be 16. It is there to allow
338  * additional fields to be added to this specification at a later date
339  * without breaking backwards compatibility.
340  */
341
342 /* XXX - We need to be careful with failure here, because failure due to
343  * a missing file is fine, but failure due to read errors needs to be trapped,
344  * before it results in a corrupted file being written out.
345  */
346
347 static int
348 _parseOriginalKeyFile(struct afsconf_dir *dir, char *fileName)
349 {
350     int fd, code, nkeys, i;
351     struct afsconf_typedKey *key;
352
353     fd = open(fileName, O_RDONLY);
354     if (fd < 0)
355         return 0;
356
357     code = read(fd, &nkeys, sizeof(afs_int32));
358     if (code!= sizeof(afs_int32))
359         goto fail;
360
361     nkeys=ntohl(nkeys);
362     for(i=0; i<nkeys; i++) {
363
364         key = afsconf_typedKey_blank();
365         if (key == NULL)
366             goto fail;
367
368         key->type = afsconf_rxkad;
369         key->subType = 0;
370
371         code = read(fd, &key->kvno, sizeof(afs_int32));
372         if (code != sizeof(afs_int32)) {
373             free(key);
374             goto fail;
375         }
376         key->kvno = ntohl(key->kvno);
377
378         rx_opaque_alloc(&key->key, 8);
379         code = read(fd, key->key.val, 8);
380         if (code != 8) {
381             rx_opaque_freeContents(&key->key);
382             free(key);
383             goto fail;
384         }
385         code = addMemoryKey(dir, key, 1);
386         afsconf_typedKey_put(&key); /* Done with key */
387         if (code)
388             goto fail;
389     }
390     close(fd);
391     return 0;
392
393 fail:
394     close(fd);
395     return EIO;
396 }
397
398 static_inline int
399 writeWord(int fd, afs_int32 data)
400 {
401
402      data = htonl(data);
403
404      if (write(fd, &data, sizeof(afs_int32)) != sizeof(afs_int32))
405         return EIO;
406
407      return 0;
408 }
409
410 static int
411 _writeOriginalKeyFile(struct afsconf_dir *dir, char *fileName)
412 {
413     int nkeys, fd;
414     struct opr_queue *cursor;
415     struct keyTypeList *typeEntry;
416
417     fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
418     if (fd < 0)
419         return AFSCONF_FAILURE;
420
421     typeEntry = findByType(dir, afsconf_rxkad);
422     if (typeEntry)
423         nkeys = opr_queue_Count(&typeEntry->kvnoList);
424     else
425         nkeys = 0;
426
427     if (writeWord(fd, nkeys))
428         goto fail;
429
430     if (typeEntry == NULL)
431         goto out;
432
433     for (opr_queue_Scan(&typeEntry->kvnoList, cursor)) {
434         struct kvnoList *kvnoEntry;
435         struct subTypeList *subEntry;
436
437         kvnoEntry = opr_queue_Entry(cursor, struct kvnoList, link);
438         subEntry = opr_queue_First(&kvnoEntry->subTypeList,
439                                    struct subTypeList, link);
440         if (writeWord(fd, subEntry->key->kvno))
441             goto fail;
442         if (write(fd, subEntry->key->key.val, 8) != 8)
443             goto fail;
444     }
445
446 out:
447     close(fd);
448     return 0;
449
450 fail:
451     close(fd);
452     return AFSCONF_FAILURE;
453 }
454
455 static int
456 _parseExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
457 {
458     int fd, i, code;
459     afs_int32 nkeys;
460     struct afsconf_typedKey *key = NULL;
461
462     fd = open(fileName, O_RDONLY);
463     if (fd < 0)
464         return 0;
465
466     code = read(fd, &nkeys, sizeof(afs_int32));
467     if (code!= sizeof(afs_int32))
468         goto fail;
469
470     nkeys=ntohl(nkeys);
471     for(i=0; i<nkeys; i++) {
472         afs_int32 reclen;
473
474         key = afsconf_typedKey_blank();
475         if (key == NULL)
476             goto fail;
477
478         /* The only data version we currently parse has a reclen of 16.
479          * Anything smaller indicates a corrupt key file. Anything more,
480          * and we just skip the extra fields */
481         code = read(fd, &reclen, sizeof(afs_int32));
482         if (code != sizeof(afs_int32))
483             goto fail;
484         reclen = ntohl(reclen);
485         if (reclen < 16)
486             goto fail;
487         reclen-=sizeof(afs_int32);
488
489         code = read(fd, &key->type, sizeof(afs_int32));
490         if (code != sizeof(afs_int32))
491             goto fail;
492         key->type = ntohl(key->type);
493         reclen-=sizeof(afs_int32);
494
495         code = read(fd, &key->kvno, sizeof(afs_int32));
496         if (code != sizeof(afs_int32))
497             goto fail;
498         key->kvno = ntohl(key->kvno);
499         reclen-=sizeof(afs_int32);
500
501         code = read(fd, &key->subType, sizeof(afs_int32));
502         if (code != sizeof(afs_int32))
503             goto fail;
504         key->subType = ntohl(key->subType);
505         reclen-=sizeof(afs_int32);
506
507         if (reclen > 0) {
508             code = lseek(fd, reclen, SEEK_CUR);
509             if (code < 0)
510                 goto fail;
511         }
512
513         code = read(fd, &reclen, sizeof(afs_int32));
514         if (code != sizeof(afs_int32))
515             goto fail;
516         reclen = ntohl(reclen);
517
518         rx_opaque_alloc(&key->key, reclen);
519         code = read(fd, key->key.val, reclen);
520         if (code != reclen) {
521             rx_opaque_freeContents(&key->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                 count++;
851         }
852     }
853
854     /* Allocate space for all of these */
855     retval = malloc(sizeof(struct afsconf_typedKeyList));
856     retval->nkeys = count;
857
858     if (count > 0) {
859         retval->keys = calloc(retval->nkeys,
860                               sizeof(struct afsconf_typedKey *));
861
862         /* Populate the key list */
863         count = 0;
864         for (opr_queue_Scan(&dir->keyList, typeCursor)) {
865             typeEntry = opr_queue_Entry(typeCursor,
866                                         struct keyTypeList, link);
867             for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
868                 kvnoEntry = opr_queue_Entry(kvnoCursor,
869                                             struct kvnoList, link);
870                 for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) {
871                     subEntry = opr_queue_Entry(subCursor,
872                                                struct subTypeList, link);
873                     retval->keys[count] = afsconf_typedKey_get(subEntry->key);
874                     count++;
875                 }
876             }
877         }
878     } else {
879         retval->keys = NULL;
880     }
881
882     *keys = retval;
883
884 out:
885     UNLOCK_GLOBAL_MUTEX;
886     return code;
887 }
888
889 int
890 afsconf_GetKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
891                       int kvno, int subType, struct afsconf_typedKey **key)
892 {
893     int code = 0;
894     struct subTypeList *subTypeEntry;
895
896     LOCK_GLOBAL_MUTEX;
897
898     code = _afsconf_Check(dir);
899     if (code)
900         goto out;
901
902     subTypeEntry = findBySubType(dir, type, kvno, subType);
903     if (subTypeEntry == NULL) {
904         code = AFSCONF_NOTFOUND;
905         goto out;
906     }
907
908     *key = afsconf_typedKey_get(subTypeEntry->key);
909
910 out:
911     UNLOCK_GLOBAL_MUTEX;
912     return code;
913 }
914
915 static struct kvnoList *
916 pickBestKvno(struct afsconf_dir *dir, afsconf_keyType type)
917 {
918     struct keyTypeList *typeEntry;
919     struct kvnoList    *kvnoEntry;
920
921     typeEntry = findByType(dir, type);
922     if (typeEntry == NULL)
923         return NULL;
924
925     /* We store all of the key lists ordered, so the last entry in the
926      * kvno list must be the highest kvno. */
927
928     kvnoEntry = opr_queue_Last(&typeEntry->kvnoList, struct kvnoList, link);
929
930     /* Except, if we're in the rxkad list, we might have a bcrypt entry that
931      * has a kvno of 999. So we need to skip that one
932      */
933     while (type == afsconf_rxkad && kvnoEntry->kvno == 999) {
934         kvnoEntry = opr_queue_Prev(&typeEntry->kvnoList, struct kvnoList,
935                                    link);
936         if (opr_queue_IsEnd(&typeEntry->kvnoList, &kvnoEntry->link))
937            return NULL;
938     }
939
940     return kvnoEntry;
941 }
942
943
944 int
945 afsconf_GetLatestKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
946                             struct afsconf_typedKeyList **keys)
947 {
948     int code;
949     struct kvnoList *kvnoEntry;
950
951     LOCK_GLOBAL_MUTEX;
952
953     code = _afsconf_Check(dir);
954     if (code)
955         goto out;
956
957
958     kvnoEntry = pickBestKvno(dir, type);
959     if (kvnoEntry == NULL) {
960         code = AFSCONF_NOTFOUND;
961         goto out;
962     }
963
964     code = listToArray(kvnoEntry, keys);
965
966 out:
967     UNLOCK_GLOBAL_MUTEX;
968     return code;
969 }
970
971 int
972 afsconf_GetLatestKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
973                             int subType, struct afsconf_typedKey **key)
974 {
975     int code;
976     struct kvnoList *kvnoEntry;
977     struct subTypeList *subTypeEntry;
978
979     LOCK_GLOBAL_MUTEX;
980
981     code = _afsconf_Check(dir);
982     if (code)
983         goto out;
984
985     kvnoEntry = pickBestKvno(dir, type);
986     if (kvnoEntry == NULL) {
987         code = AFSCONF_NOTFOUND;
988         goto out;
989     }
990
991     subTypeEntry = findInKvnoList(kvnoEntry, subType);
992     if (subTypeEntry == NULL) {
993         code = AFSCONF_NOTFOUND;
994         goto out;
995     }
996
997     *key = afsconf_typedKey_get(subTypeEntry->key);
998
999 out:
1000     UNLOCK_GLOBAL_MUTEX;
1001     return code;
1002 }
1003
1004 void
1005 afsconf_PutTypedKeyList(struct afsconf_typedKeyList **keys)
1006 {
1007      int i;
1008
1009      for (i=0;i<(*keys)->nkeys;i++)
1010         afsconf_typedKey_put(&((*keys)->keys[i]));
1011
1012      if ((*keys)->keys != NULL)
1013         free((*keys)->keys);
1014
1015      free(*keys);
1016      *keys = NULL;
1017 }
1018
1019 static struct afsconf_typedKey *
1020 afsconf_typedKey_blank(void)
1021 {
1022     struct afsconf_typedKey *key;
1023
1024     key = calloc(1, sizeof(struct afsconf_typedKey));
1025     if (key == NULL)
1026         return NULL;
1027
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 }