auth: Rewrite KeyFile handling code
[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     *kvno = typedKey->kvno;
733     memcpy(key, typedKey->key.val, 8);
734
735     afsconf_typedKey_put(&typedKey);
736
737     return 0;
738 }
739
740 int
741 afsconf_GetKey(void *rock, int kvno, struct ktc_encryptionKey *key)
742 {
743     struct afsconf_typedKey *typedKey;
744     int code;
745
746     code = afsconf_GetKeyByTypes(rock, afsconf_rxkad, kvno, 0, &typedKey);
747     if (code)
748         return code;
749
750     memcpy(key, typedKey->key.val, 8);
751
752     afsconf_typedKey_put(&typedKey);
753
754     return 0;
755 }
756
757 int
758 afsconf_AddKey(struct afsconf_dir *dir, afs_int32 kvno, char key[8],
759                afs_int32 overwrite)
760 {
761     struct rx_opaque buffer;
762     struct afsconf_typedKey *typedKey;
763     int code;
764
765     rx_opaque_alloc(&buffer, 8);
766     memcpy(buffer.val, key, 8);
767     typedKey = afsconf_typedKey_new(afsconf_rxkad, kvno, 0, &buffer);
768     if (typedKey == NULL)
769         return AFSCONF_FAILURE;
770
771     rx_opaque_freeContents(&buffer);
772
773     code = afsconf_AddTypedKey(dir, typedKey, overwrite);
774     afsconf_typedKey_put(&typedKey);
775     return code;
776 }
777
778 int
779 afsconf_DeleteKey(struct afsconf_dir *dir, afs_int32 kvno)
780 {
781     return afsconf_DeleteKeyByType(dir, afsconf_rxkad, kvno);
782 }
783
784 int
785 afsconf_GetKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
786                       int kvno, struct afsconf_typedKeyList **keys)
787 {
788     struct kvnoList *kvnoEntry;
789     int code;
790
791     LOCK_GLOBAL_MUTEX;
792
793     code = _afsconf_Check(dir);
794     if (code)
795         goto out;
796
797     kvnoEntry = findByKvno(dir, type, kvno);
798     if (kvnoEntry == NULL) {
799         code = AFSCONF_NOTFOUND;
800         goto out;
801     }
802
803     code = listToArray(kvnoEntry, keys);
804
805 out:
806     UNLOCK_GLOBAL_MUTEX;
807     return code;
808 }
809
810 int
811 afsconf_GetKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
812                       int kvno, int subType, struct afsconf_typedKey **key)
813 {
814     int code = 0;
815     struct subTypeList *subTypeEntry;
816
817     LOCK_GLOBAL_MUTEX;
818
819     code = _afsconf_Check(dir);
820     if (code)
821         goto out;
822
823     subTypeEntry = findBySubType(dir, type, kvno, subType);
824     if (subTypeEntry == NULL) {
825         code = AFSCONF_NOTFOUND;
826         goto out;
827     }
828
829     *key = afsconf_typedKey_get(subTypeEntry->key);
830
831 out:
832     UNLOCK_GLOBAL_MUTEX;
833     return code;
834 }
835
836 static struct kvnoList *
837 pickBestKvno(struct afsconf_dir *dir, afsconf_keyType type)
838 {
839     struct keyTypeList *typeEntry;
840     struct kvnoList    *kvnoEntry;
841
842     typeEntry = findByType(dir, type);
843     if (typeEntry == NULL)
844         return NULL;
845
846     /* We store all of the key lists ordered, so the last entry in the
847      * kvno list must be the highest kvno. */
848
849     kvnoEntry = opr_queue_Last(&typeEntry->kvnoList, struct kvnoList, link);
850
851     /* Except, if we're in the rxkad list, we might have a bcrypt entry that
852      * has a kvno of 999. So we need to skip that one
853      */
854     while (type == afsconf_rxgk && kvnoEntry->kvno == 999) {
855         kvnoEntry = opr_queue_Prev(&typeEntry->kvnoList, struct kvnoList,
856                                    link);
857         if (opr_queue_IsEnd(&typeEntry->kvnoList, &kvnoEntry->link))
858            return NULL;
859     }
860
861     return kvnoEntry;
862 }
863
864
865 int
866 afsconf_GetLatestKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
867                             struct afsconf_typedKeyList **keys)
868 {
869     int code;
870     struct kvnoList *kvnoEntry;
871
872     LOCK_GLOBAL_MUTEX;
873
874     code = _afsconf_Check(dir);
875     if (code)
876         goto out;
877
878
879     kvnoEntry = pickBestKvno(dir, type);
880     if (kvnoEntry == NULL) {
881         code = AFSCONF_NOTFOUND;
882         goto out;
883     }
884
885     code = listToArray(kvnoEntry, keys);
886
887 out:
888     UNLOCK_GLOBAL_MUTEX;
889     return code;
890 }
891
892 int
893 afsconf_GetLatestKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
894                             int subType, struct afsconf_typedKey **key)
895 {
896     int code;
897     struct kvnoList *kvnoEntry;
898     struct subTypeList *subTypeEntry;
899
900     LOCK_GLOBAL_MUTEX;
901
902     code = _afsconf_Check(dir);
903     if (code)
904         goto out;
905
906     kvnoEntry = pickBestKvno(dir, type);
907     if (kvnoEntry == NULL) {
908         code = AFSCONF_NOTFOUND;
909         goto out;
910     }
911
912     subTypeEntry = findInKvnoList(kvnoEntry, subType);
913     if (subTypeEntry == NULL) {
914         code = AFSCONF_NOTFOUND;
915         goto out;
916     }
917
918     *key = afsconf_typedKey_get(subTypeEntry->key);
919
920 out:
921     UNLOCK_GLOBAL_MUTEX;
922     return code;
923 }
924
925 void
926 afsconf_PutTypedKeyList(struct afsconf_typedKeyList **keys)
927 {
928      int i;
929
930      for (i=0;i<(*keys)->nkeys;i++)
931         afsconf_typedKey_put(&((*keys)->keys[i]));
932      free((*keys)->keys);
933      free(*keys);
934      *keys = NULL;
935 }
936
937 static struct afsconf_typedKey *
938 afsconf_typedKey_blank(void)
939 {
940     struct afsconf_typedKey *key;
941
942     key = malloc(sizeof(struct afsconf_typedKey));
943     if (key == NULL)
944         return NULL;
945
946     memset(key, 0, sizeof(struct afsconf_typedKey));
947     rx_atomic_set(&key->refcnt, 1);
948
949     return key;
950 }
951
952 struct afsconf_typedKey *
953 afsconf_typedKey_new(afsconf_keyType type, int kvno, int subType,
954                      struct rx_opaque *keyMaterial)
955 {
956     struct afsconf_typedKey *key;
957     int code;
958
959     key = afsconf_typedKey_blank();
960     if (key == NULL)
961         return key;
962
963     key->type = type;
964     key->kvno = kvno;
965     key->subType = subType;
966
967     code = rx_opaque_copy(&key->key, keyMaterial);
968     if (code != 0) {
969         free(key);
970         return NULL;
971     }
972
973     return key;
974 }
975
976 void
977 afsconf_typedKey_free(struct afsconf_typedKey **key)
978 {
979     rx_opaque_freeContents(&(*key)->key);
980     free(*key);
981     *key = NULL;
982 }
983
984 struct afsconf_typedKey *
985 afsconf_typedKey_get(struct afsconf_typedKey *key)
986 {
987      rx_atomic_inc(&key->refcnt);
988      return key;
989 }
990
991 void
992 afsconf_typedKey_put(struct afsconf_typedKey **key)
993 {
994     if (rx_atomic_dec_and_read(&(*key)->refcnt) == 0)
995         afsconf_typedKey_free(key);
996     else
997         *key = NULL;
998 }
999
1000 void
1001 afsconf_typedKey_values(struct afsconf_typedKey *key, afsconf_keyType *type,
1002                         int *kvno, int *subType, struct rx_opaque **material)
1003 {
1004     *type = key->type;
1005     *kvno = key->kvno;
1006     *subType = key->subType;
1007     *material = &key->key;
1008 }
1009
1010 int
1011 afsconf_AddTypedKey(struct afsconf_dir *dir,
1012                     struct afsconf_typedKey *key,
1013                     int overwrite)
1014 {
1015     int code;
1016
1017     LOCK_GLOBAL_MUTEX;
1018
1019     code = _afsconf_Check(dir);
1020     if (code)
1021         goto out;
1022
1023     if (key->type == afsconf_rxkad) {
1024         /* There are restrictions on rxkad keys so that we can still
1025          * return them using the old interface. We only enforce the
1026          * same restrictions as that interface does - that is, we don't
1027          * check that the key we're passed is a valid DES key */
1028         if (key->key.len != 8 || key->subType != 0) {
1029             code = AFSCONF_BADKEY;
1030             goto out;
1031         }
1032     }
1033
1034     code = addMemoryKey(dir, key, overwrite);
1035     if (code)
1036         goto out;
1037
1038     code = _afsconf_SaveKeys(dir);
1039     _afsconf_Touch(dir);
1040
1041 out:
1042     UNLOCK_GLOBAL_MUTEX;
1043     return code;
1044 }
1045
1046 int
1047 afsconf_DeleteKeyByType(struct afsconf_dir *dir,
1048                         afsconf_keyType type, int kvno)
1049 {
1050     struct keyTypeList *typeEntry;
1051     struct kvnoList *kvnoEntry;
1052     int code;
1053
1054     LOCK_GLOBAL_MUTEX;
1055
1056     code = _afsconf_Check(dir);
1057     if (code)
1058         goto out;
1059
1060     typeEntry = findByType(dir, type);
1061     if (typeEntry == NULL) {
1062         code = AFSCONF_NOTFOUND;
1063         goto out;
1064     }
1065
1066     kvnoEntry = findInTypeList(typeEntry, kvno);
1067     if (kvnoEntry == NULL) {
1068         code = AFSCONF_NOTFOUND;
1069         goto out;
1070     }
1071
1072     deleteKvnoEntry(kvnoEntry);
1073
1074     /* Remove the typeEntry, if it has no sub elements */
1075     if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
1076         opr_queue_Remove(&typeEntry->link);
1077         free(typeEntry);
1078     }
1079
1080     code = _afsconf_SaveKeys(dir);
1081     _afsconf_Touch(dir);
1082
1083 out:
1084     UNLOCK_GLOBAL_MUTEX;
1085     return code;
1086 }
1087
1088 int
1089 afsconf_DeleteKeyBySubType(struct afsconf_dir *dir,
1090                            afsconf_keyType type, int kvno, int subType)
1091 {
1092     struct keyTypeList *typeEntry;
1093     struct kvnoList *kvnoEntry;
1094     struct subTypeList *subTypeEntry;
1095     int code;
1096
1097     LOCK_GLOBAL_MUTEX;
1098
1099     code = _afsconf_Check(dir);
1100     if (code)
1101         goto out;
1102
1103     typeEntry = findByType(dir, type);
1104     if (typeEntry == NULL)
1105         return AFSCONF_NOTFOUND;
1106
1107     kvnoEntry = findInTypeList(typeEntry, kvno);
1108     if (kvnoEntry == NULL)
1109         return AFSCONF_NOTFOUND;
1110
1111     subTypeEntry = findInKvnoList(kvnoEntry, subType);
1112     if (subTypeEntry == NULL)
1113         return AFSCONF_NOTFOUND;
1114
1115     /* Remove the subTypeEntry */
1116     afsconf_typedKey_put(&subTypeEntry->key);
1117     opr_queue_Remove(&subTypeEntry->link);
1118     free(subTypeEntry);
1119
1120     /* Remove the kvnoEntry, if it has no sub elements */
1121     if (opr_queue_IsEmpty(&kvnoEntry->subTypeList)) {
1122         opr_queue_Remove(&kvnoEntry->link);
1123         free(kvnoEntry);
1124     }
1125
1126     /* Remove the typeEntry, if it has no sub elements */
1127     if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
1128         opr_queue_Remove(&typeEntry->link);
1129         free(typeEntry);
1130     }
1131
1132     code = _afsconf_SaveKeys(dir);
1133     _afsconf_Touch(dir);
1134
1135 out:
1136     UNLOCK_GLOBAL_MUTEX;
1137     return code;
1138 }
1139
1140 int
1141 afsconf_DeleteTypedKey(struct afsconf_dir *dir, struct afsconf_typedKey *key)
1142 {
1143     return afsconf_DeleteKeyBySubType(dir, key->type, key->kvno, key->subType);
1144 }