aa265f10c54a4944febfbfe330c4a096f5948b87
[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 == NULL)
419         goto out;
420
421     nkeys = opr_queue_Count(&typeEntry->kvnoList);
422
423     if (writeWord(fd, nkeys))
424         goto fail;
425
426     for (opr_queue_Scan(&typeEntry->kvnoList, cursor)) {
427         struct kvnoList *kvnoEntry;
428         struct subTypeList *subEntry;
429
430         kvnoEntry = opr_queue_Entry(cursor, struct kvnoList, link);
431         subEntry = opr_queue_First(&kvnoEntry->subTypeList,
432                                    struct subTypeList, link);
433         if (writeWord(fd, subEntry->key->kvno))
434             goto fail;
435         if (write(fd, subEntry->key->key.val, 8) != 8)
436             goto fail;
437     }
438
439 out:
440     close(fd);
441     return 0;
442
443 fail:
444     close(fd);
445     return AFSCONF_FAILURE;
446 }
447
448 static int
449 _parseExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
450 {
451     int fd, i, code;
452     afs_int32 nkeys;
453     struct afsconf_typedKey *key;
454
455     fd = open(fileName, O_RDONLY);
456     if (fd < 0)
457         return 0;
458
459     code = read(fd, &nkeys, sizeof(afs_int32));
460     if (code!= sizeof(afs_int32))
461         goto fail;
462
463     nkeys=ntohl(nkeys);
464     for(i=0; i<nkeys; i++) {
465         afs_int32 reclen;
466
467         key = afsconf_typedKey_blank();
468
469         /* The only data version we currently parse has a reclen of 16.
470          * Anything smaller indicates a corrupt key file. Anything more,
471          * and we just skip the extra fields */
472         code = read(fd, &reclen, sizeof(afs_int32));
473         if (code != sizeof(afs_int32))
474             goto fail;
475         reclen = ntohl(reclen);
476         if (reclen < 16)
477             goto fail;
478         reclen-=sizeof(afs_int32);
479
480         code = read(fd, &key->type, sizeof(afs_int32));
481         if (code != sizeof(afs_int32))
482             goto fail;
483         key->type = ntohl(key->type);
484         reclen-=sizeof(afs_int32);
485
486         code = read(fd, &key->kvno, sizeof(afs_int32));
487         if (code != sizeof(afs_int32))
488             goto fail;
489         key->kvno = ntohl(key->kvno);
490         reclen-=sizeof(afs_int32);
491
492         code = read(fd, &key->subType, sizeof(afs_int32));
493         if (code != sizeof(afs_int32))
494             goto fail;
495         key->subType = ntohl(key->subType);
496         reclen-=sizeof(afs_int32);
497
498         if (reclen > 0) {
499             code = lseek(fd, reclen, SEEK_CUR);
500             if (code < 0)
501                 goto fail;
502         }
503
504         code = read(fd, &reclen, sizeof(afs_int32));
505         if (code != sizeof(afs_int32))
506             goto fail;
507         reclen = ntohl(reclen);
508
509         rx_opaque_alloc(&key->key, reclen);
510         code = read(fd, key->key.val, reclen);
511         if (code != reclen) {
512             rx_opaque_freeContents(&key->key);
513             free(key);
514             goto fail;
515         }
516         code = addMemoryKey(dir, key, 1);
517         afsconf_typedKey_put(&key);
518         if (code)
519             goto fail;
520     }
521     close(fd);
522     return 0;
523
524 fail:
525     close(fd);
526     return EIO;
527 }
528
529
530 static int
531 _writeExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
532 {
533     int nkeys;
534     int fd;
535
536     struct keyTypeList *typeEntry;
537     struct kvnoList    *kvnoEntry;
538     struct subTypeList *entry;
539     struct opr_queue   *keyCursor;
540     struct opr_queue   *kvnoCursor;
541     struct opr_queue   *subCursor;
542
543     fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
544     if (fd < 0)
545         return AFSCONF_FAILURE;
546
547     /* Iterate over the whole in-memory key store, and write everything
548      * except keys with type rxkad into the extended key file */
549
550     /* Write a 0 key count - we'll fill it in later */
551     nkeys = 0;
552     if (writeWord(fd, 0))
553         goto fail;
554
555     for (opr_queue_Scan(&dir->keyList, keyCursor)) {
556         typeEntry = opr_queue_Entry(keyCursor, struct keyTypeList, link);
557
558         if (typeEntry->type != afsconf_rxkad) {
559             for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
560                 kvnoEntry = opr_queue_Entry(kvnoCursor, struct kvnoList, link);
561                 for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) {
562                     entry = opr_queue_Entry(subCursor, struct subTypeList, link);
563                     if (writeWord(fd, 16)) /* record length */
564                         goto fail;
565                     if (writeWord(fd, entry->key->type))
566                         goto fail;
567                     if (writeWord(fd, entry->key->kvno))
568                         goto fail;
569                     if (writeWord(fd, entry->key->subType))
570                         goto fail;
571                     if (writeWord(fd, entry->key->key.len))
572                         goto fail;
573                     if (write(fd, entry->key->key.val,
574                               entry->key->key.len) !=
575                         entry->key->key.len)
576                         goto fail;
577                     nkeys++;
578                 }
579             }
580         }
581     }
582
583     if (lseek(fd, 0, SEEK_SET)<0)
584         goto fail;
585
586     if (writeWord(fd, nkeys))
587         goto fail;
588
589     close(fd);
590
591     return 0;
592
593 fail:
594     close(fd);
595     return AFSCONF_FAILURE;
596 }
597
598 int
599 _afsconf_LoadKeys(struct afsconf_dir *dir)
600 {
601     int code;
602     char *fileName;
603
604     /* If we're running on Windows, and we are a client, we don't have a
605      * KeyFile, so don't try and open one */
606
607 #ifdef AFS_NT40_ENV
608     if (_afsconf_IsClientConfigDirectory(dir->name))
609         return 0;
610 #endif /* AFS_NT40_ENV */
611
612     LOCK_GLOBAL_MUTEX;
613
614     /* Delete all of our existing keys */
615     _afsconf_FreeAllKeys(dir);
616
617     /* Start by opening the original KeyFile */
618     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_KEY_FILE);
619     code = _parseOriginalKeyFile(dir, fileName);
620     free(fileName);
621     if (code)
622         goto out;
623
624     /* Now open the new style KeyFile */
625     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_EXT_KEY_FILE);
626     code = _parseExtendedKeyFile(dir, fileName);
627     free(fileName);
628     if (code)
629         goto out;
630
631 out:
632     if (code)
633         _afsconf_FreeAllKeys(dir);
634
635     UNLOCK_GLOBAL_MUTEX;
636
637     return code;
638 }
639
640 static int
641 _afsconf_SaveKeys(struct afsconf_dir *dir)
642 {
643     char *fileName;
644     int code;
645
646     /* If we're running on Windows, and we are a client, we don't have a
647      * KeyFile, so don't try and open one */
648
649 #ifdef AFS_NT40_ENV
650     if (_afsconf_IsClientConfigDirectory(dir->name))
651         return 0;
652 #endif /* AFS_NT40_ENV */
653
654     LOCK_GLOBAL_MUTEX;
655
656     /* Start by opening the original KeyFile */
657     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_KEY_FILE);
658     code = _writeOriginalKeyFile(dir, fileName);
659     free(fileName);
660     if (code)
661         goto out;
662
663     /* Now open the new style KeyFile */
664     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_EXT_KEY_FILE);
665     code = _writeExtendedKeyFile(dir, fileName);
666     free(fileName);
667     if (code)
668         goto out;
669
670 out:
671     UNLOCK_GLOBAL_MUTEX;
672
673     return code;
674 }
675
676
677
678 /* get keys structure */
679 int
680 afsconf_GetKeys(struct afsconf_dir *dir, struct afsconf_keys *astr)
681 {
682     afs_int32 code;
683     struct keyTypeList *typeEntry;
684     struct opr_queue *cursor;
685
686     memset(astr, 0, sizeof(struct afsconf_keys));
687
688     LOCK_GLOBAL_MUTEX;
689
690     code = _afsconf_Check(dir);
691     if (code)
692         goto out;
693
694     typeEntry = findByType(dir, afsconf_rxkad);
695     if (typeEntry == NULL)
696         goto out;
697
698     for (opr_queue_Scan(&typeEntry->kvnoList, cursor)) {
699         struct kvnoList *kvnoEntry;
700         struct subTypeList *subEntry;
701
702         kvnoEntry = opr_queue_Entry(cursor, struct kvnoList, link);
703         subEntry = opr_queue_First(&kvnoEntry->subTypeList,
704                                    struct subTypeList, link);
705         /* XXX - If there is more than one key in this list, it's an error */
706         astr->key[astr->nkeys].kvno = subEntry->key->kvno;
707         /* XXX - If the opaque contains a number of bytes other than 8, it's
708          * an error */
709         memcpy(&astr->key[astr->nkeys].key, subEntry->key->key.val, 8);
710         astr->nkeys++;
711     }
712
713 out:
714     UNLOCK_GLOBAL_MUTEX;
715     return code;
716 }
717
718 afs_int32
719 afsconf_GetLatestKey(struct afsconf_dir *dir, afs_int32 *kvno,
720                      struct ktc_encryptionKey *key)
721 {
722     struct afsconf_typedKey *typedKey;
723     int code;
724
725     code = afsconf_GetLatestKeyByTypes(dir, afsconf_rxkad, 0, &typedKey);
726     if (code)
727         return code;
728
729     /* XXX - Should check that the key is of the correct length */
730
731     /* Copy out the relevant details */
732     if (kvno != NULL)
733         *kvno = typedKey->kvno;
734
735     if (key != NULL)
736         memcpy(key, typedKey->key.val, 8);
737
738     afsconf_typedKey_put(&typedKey);
739
740     return 0;
741 }
742
743 int
744 afsconf_GetKey(void *rock, int kvno, struct ktc_encryptionKey *key)
745 {
746     struct afsconf_typedKey *typedKey;
747     int code;
748
749     code = afsconf_GetKeyByTypes(rock, afsconf_rxkad, kvno, 0, &typedKey);
750     if (code)
751         return code;
752
753     memcpy(key, typedKey->key.val, 8);
754
755     afsconf_typedKey_put(&typedKey);
756
757     return 0;
758 }
759
760 int
761 afsconf_AddKey(struct afsconf_dir *dir, afs_int32 kvno, char key[8],
762                afs_int32 overwrite)
763 {
764     struct rx_opaque buffer;
765     struct afsconf_typedKey *typedKey;
766     int code;
767
768     rx_opaque_alloc(&buffer, 8);
769     memcpy(buffer.val, key, 8);
770     typedKey = afsconf_typedKey_new(afsconf_rxkad, kvno, 0, &buffer);
771     if (typedKey == NULL)
772         return AFSCONF_FAILURE;
773
774     rx_opaque_freeContents(&buffer);
775
776     code = afsconf_AddTypedKey(dir, typedKey, overwrite);
777     afsconf_typedKey_put(&typedKey);
778     return code;
779 }
780
781 int
782 afsconf_DeleteKey(struct afsconf_dir *dir, afs_int32 kvno)
783 {
784     return afsconf_DeleteKeyByType(dir, afsconf_rxkad, kvno);
785 }
786
787 int
788 afsconf_GetKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
789                       int kvno, struct afsconf_typedKeyList **keys)
790 {
791     struct kvnoList *kvnoEntry;
792     int code;
793
794     LOCK_GLOBAL_MUTEX;
795
796     code = _afsconf_Check(dir);
797     if (code)
798         goto out;
799
800     kvnoEntry = findByKvno(dir, type, kvno);
801     if (kvnoEntry == NULL) {
802         code = AFSCONF_NOTFOUND;
803         goto out;
804     }
805
806     code = listToArray(kvnoEntry, keys);
807
808 out:
809     UNLOCK_GLOBAL_MUTEX;
810     return code;
811 }
812
813 int
814 afsconf_GetKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
815                       int kvno, int subType, struct afsconf_typedKey **key)
816 {
817     int code = 0;
818     struct subTypeList *subTypeEntry;
819
820     LOCK_GLOBAL_MUTEX;
821
822     code = _afsconf_Check(dir);
823     if (code)
824         goto out;
825
826     subTypeEntry = findBySubType(dir, type, kvno, subType);
827     if (subTypeEntry == NULL) {
828         code = AFSCONF_NOTFOUND;
829         goto out;
830     }
831
832     *key = afsconf_typedKey_get(subTypeEntry->key);
833
834 out:
835     UNLOCK_GLOBAL_MUTEX;
836     return code;
837 }
838
839 static struct kvnoList *
840 pickBestKvno(struct afsconf_dir *dir, afsconf_keyType type)
841 {
842     struct keyTypeList *typeEntry;
843     struct kvnoList    *kvnoEntry;
844
845     typeEntry = findByType(dir, type);
846     if (typeEntry == NULL)
847         return NULL;
848
849     /* We store all of the key lists ordered, so the last entry in the
850      * kvno list must be the highest kvno. */
851
852     kvnoEntry = opr_queue_Last(&typeEntry->kvnoList, struct kvnoList, link);
853
854     /* Except, if we're in the rxkad list, we might have a bcrypt entry that
855      * has a kvno of 999. So we need to skip that one
856      */
857     while (type == afsconf_rxgk && kvnoEntry->kvno == 999) {
858         kvnoEntry = opr_queue_Prev(&typeEntry->kvnoList, struct kvnoList,
859                                    link);
860         if (opr_queue_IsEnd(&typeEntry->kvnoList, &kvnoEntry->link))
861            return NULL;
862     }
863
864     return kvnoEntry;
865 }
866
867
868 int
869 afsconf_GetLatestKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
870                             struct afsconf_typedKeyList **keys)
871 {
872     int code;
873     struct kvnoList *kvnoEntry;
874
875     LOCK_GLOBAL_MUTEX;
876
877     code = _afsconf_Check(dir);
878     if (code)
879         goto out;
880
881
882     kvnoEntry = pickBestKvno(dir, type);
883     if (kvnoEntry == NULL) {
884         code = AFSCONF_NOTFOUND;
885         goto out;
886     }
887
888     code = listToArray(kvnoEntry, keys);
889
890 out:
891     UNLOCK_GLOBAL_MUTEX;
892     return code;
893 }
894
895 int
896 afsconf_GetLatestKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
897                             int subType, struct afsconf_typedKey **key)
898 {
899     int code;
900     struct kvnoList *kvnoEntry;
901     struct subTypeList *subTypeEntry;
902
903     LOCK_GLOBAL_MUTEX;
904
905     code = _afsconf_Check(dir);
906     if (code)
907         goto out;
908
909     kvnoEntry = pickBestKvno(dir, type);
910     if (kvnoEntry == NULL) {
911         code = AFSCONF_NOTFOUND;
912         goto out;
913     }
914
915     subTypeEntry = findInKvnoList(kvnoEntry, subType);
916     if (subTypeEntry == NULL) {
917         code = AFSCONF_NOTFOUND;
918         goto out;
919     }
920
921     *key = afsconf_typedKey_get(subTypeEntry->key);
922
923 out:
924     UNLOCK_GLOBAL_MUTEX;
925     return code;
926 }
927
928 void
929 afsconf_PutTypedKeyList(struct afsconf_typedKeyList **keys)
930 {
931      int i;
932
933      for (i=0;i<(*keys)->nkeys;i++)
934         afsconf_typedKey_put(&((*keys)->keys[i]));
935      free((*keys)->keys);
936      free(*keys);
937      *keys = NULL;
938 }
939
940 static struct afsconf_typedKey *
941 afsconf_typedKey_blank(void)
942 {
943     struct afsconf_typedKey *key;
944
945     key = malloc(sizeof(struct afsconf_typedKey));
946     if (key == NULL)
947         return NULL;
948
949     memset(key, 0, sizeof(struct afsconf_typedKey));
950     rx_atomic_set(&key->refcnt, 1);
951
952     return key;
953 }
954
955 struct afsconf_typedKey *
956 afsconf_typedKey_new(afsconf_keyType type, int kvno, int subType,
957                      struct rx_opaque *keyMaterial)
958 {
959     struct afsconf_typedKey *key;
960     int code;
961
962     key = afsconf_typedKey_blank();
963     if (key == NULL)
964         return key;
965
966     key->type = type;
967     key->kvno = kvno;
968     key->subType = subType;
969
970     code = rx_opaque_copy(&key->key, keyMaterial);
971     if (code != 0) {
972         free(key);
973         return NULL;
974     }
975
976     return key;
977 }
978
979 void
980 afsconf_typedKey_free(struct afsconf_typedKey **key)
981 {
982     rx_opaque_freeContents(&(*key)->key);
983     free(*key);
984     *key = NULL;
985 }
986
987 struct afsconf_typedKey *
988 afsconf_typedKey_get(struct afsconf_typedKey *key)
989 {
990      rx_atomic_inc(&key->refcnt);
991      return key;
992 }
993
994 void
995 afsconf_typedKey_put(struct afsconf_typedKey **key)
996 {
997     if (rx_atomic_dec_and_read(&(*key)->refcnt) == 0)
998         afsconf_typedKey_free(key);
999     else
1000         *key = NULL;
1001 }
1002
1003 void
1004 afsconf_typedKey_values(struct afsconf_typedKey *key, afsconf_keyType *type,
1005                         int *kvno, int *subType, struct rx_opaque **material)
1006 {
1007     *type = key->type;
1008     *kvno = key->kvno;
1009     *subType = key->subType;
1010     *material = &key->key;
1011 }
1012
1013 int
1014 afsconf_AddTypedKey(struct afsconf_dir *dir,
1015                     struct afsconf_typedKey *key,
1016                     int overwrite)
1017 {
1018     int code;
1019
1020     LOCK_GLOBAL_MUTEX;
1021
1022     code = _afsconf_Check(dir);
1023     if (code)
1024         goto out;
1025
1026     if (key->type == afsconf_rxkad) {
1027         /* There are restrictions on rxkad keys so that we can still
1028          * return them using the old interface. We only enforce the
1029          * same restrictions as that interface does - that is, we don't
1030          * check that the key we're passed is a valid DES key */
1031         if (key->key.len != 8 || key->subType != 0) {
1032             code = AFSCONF_BADKEY;
1033             goto out;
1034         }
1035     }
1036
1037     code = addMemoryKey(dir, key, overwrite);
1038     if (code)
1039         goto out;
1040
1041     code = _afsconf_SaveKeys(dir);
1042     _afsconf_Touch(dir);
1043
1044 out:
1045     UNLOCK_GLOBAL_MUTEX;
1046     return code;
1047 }
1048
1049 int
1050 afsconf_DeleteKeyByType(struct afsconf_dir *dir,
1051                         afsconf_keyType type, int kvno)
1052 {
1053     struct keyTypeList *typeEntry;
1054     struct kvnoList *kvnoEntry;
1055     int code;
1056
1057     LOCK_GLOBAL_MUTEX;
1058
1059     code = _afsconf_Check(dir);
1060     if (code)
1061         goto out;
1062
1063     typeEntry = findByType(dir, type);
1064     if (typeEntry == NULL) {
1065         code = AFSCONF_NOTFOUND;
1066         goto out;
1067     }
1068
1069     kvnoEntry = findInTypeList(typeEntry, kvno);
1070     if (kvnoEntry == NULL) {
1071         code = AFSCONF_NOTFOUND;
1072         goto out;
1073     }
1074
1075     deleteKvnoEntry(kvnoEntry);
1076
1077     /* Remove the typeEntry, if it has no sub elements */
1078     if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
1079         opr_queue_Remove(&typeEntry->link);
1080         free(typeEntry);
1081     }
1082
1083     code = _afsconf_SaveKeys(dir);
1084     _afsconf_Touch(dir);
1085
1086 out:
1087     UNLOCK_GLOBAL_MUTEX;
1088     return code;
1089 }
1090
1091 int
1092 afsconf_DeleteKeyBySubType(struct afsconf_dir *dir,
1093                            afsconf_keyType type, int kvno, int subType)
1094 {
1095     struct keyTypeList *typeEntry;
1096     struct kvnoList *kvnoEntry;
1097     struct subTypeList *subTypeEntry;
1098     int code;
1099
1100     LOCK_GLOBAL_MUTEX;
1101
1102     code = _afsconf_Check(dir);
1103     if (code)
1104         goto out;
1105
1106     typeEntry = findByType(dir, type);
1107     if (typeEntry == NULL)
1108         return AFSCONF_NOTFOUND;
1109
1110     kvnoEntry = findInTypeList(typeEntry, kvno);
1111     if (kvnoEntry == NULL)
1112         return AFSCONF_NOTFOUND;
1113
1114     subTypeEntry = findInKvnoList(kvnoEntry, subType);
1115     if (subTypeEntry == NULL)
1116         return AFSCONF_NOTFOUND;
1117
1118     /* Remove the subTypeEntry */
1119     afsconf_typedKey_put(&subTypeEntry->key);
1120     opr_queue_Remove(&subTypeEntry->link);
1121     free(subTypeEntry);
1122
1123     /* Remove the kvnoEntry, if it has no sub elements */
1124     if (opr_queue_IsEmpty(&kvnoEntry->subTypeList)) {
1125         opr_queue_Remove(&kvnoEntry->link);
1126         free(kvnoEntry);
1127     }
1128
1129     /* Remove the typeEntry, if it has no sub elements */
1130     if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
1131         opr_queue_Remove(&typeEntry->link);
1132         free(typeEntry);
1133     }
1134
1135     code = _afsconf_SaveKeys(dir);
1136     _afsconf_Touch(dir);
1137
1138 out:
1139     UNLOCK_GLOBAL_MUTEX;
1140     return code;
1141 }
1142
1143 int
1144 afsconf_DeleteTypedKey(struct afsconf_dir *dir, struct afsconf_typedKey *key)
1145 {
1146     return afsconf_DeleteKeyBySubType(dir, key->type, key->kvno, key->subType);
1147 }