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