warn when starting without keys
[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 #ifdef AFS_RXGK_ENV
36 #include <rx/rxgk.h>
37 #endif
38
39 #include <afs/opr.h>
40 #include <afs/stds.h>
41 #include <afs/pthread_glock.h>
42 #include <afs/afsutil.h>
43
44 #include "cellconfig.h"
45 #include "keys.h"
46 #include "internal.h"
47
48 struct afsconf_typedKey {
49     rx_atomic_t refcnt;
50     afsconf_keyType type;
51     int kvno;
52     int subType;
53     struct rx_opaque key;
54 };
55
56 static struct afsconf_typedKey *afsconf_typedKey_blank(void);
57
58 /* Memory storage for keyfile contents. */
59
60 struct keyTypeList {
61     struct opr_queue link;
62     afsconf_keyType type;
63     struct opr_queue kvnoList;
64 };
65
66 struct kvnoList {
67     struct opr_queue link;
68     int kvno;
69     struct opr_queue subTypeList;
70 };
71
72 struct subTypeList {
73     struct opr_queue link;
74     int subType;
75     struct afsconf_typedKey *key;
76 };
77
78 static int
79 listToArray(struct kvnoList *kvnoEntry, struct afsconf_typedKeyList **keys)
80 {
81     struct afsconf_typedKeyList *retval;
82     struct opr_queue *cursor;
83     int i;
84
85     /* Allocate space for the keys we've got stored */
86     retval = malloc(sizeof(struct afsconf_typedKeyList));
87     retval->nkeys = opr_queue_Count(&kvnoEntry->subTypeList);
88
89     if (retval->nkeys > 0) {
90         retval->keys  = calloc(retval->nkeys, sizeof(struct afsconf_typedKey *));
91
92         i = 0;
93         for(opr_queue_Scan(&kvnoEntry->subTypeList, cursor)) {
94             struct subTypeList *entry;
95
96             entry = opr_queue_Entry(cursor, struct subTypeList, link);
97             retval->keys[i] = afsconf_typedKey_get(entry->key);
98             i++;
99         }
100     } else {
101         retval->keys = NULL;
102     }
103
104     *keys = retval;
105     return 0;
106 }
107
108 static struct keyTypeList *
109 findByType(struct afsconf_dir *dir, afsconf_keyType type)
110 {
111     struct opr_queue *cursor;
112     struct keyTypeList *entry = NULL;
113
114     for (opr_queue_Scan(&dir->keyList, cursor)) {
115         entry = opr_queue_Entry(cursor, struct keyTypeList, link);
116         if (entry->type >= type)
117             break;
118     }
119     if (entry == NULL || entry->type != type)
120         return NULL;
121
122     return entry;
123 }
124
125 static struct kvnoList *
126 findInTypeList(struct keyTypeList *parent, int kvno)
127 {
128     struct opr_queue *cursor;
129     struct kvnoList *entry = NULL;
130
131     for (opr_queue_Scan(&parent->kvnoList, cursor)) {
132         entry = opr_queue_Entry(cursor, struct kvnoList, link);
133         if (entry->kvno >= kvno)
134             break;
135     }
136     if (entry == NULL || entry->kvno != kvno)
137         return NULL;
138
139     return entry;
140 }
141
142 static struct kvnoList *
143 findByKvno(struct afsconf_dir *dir, afsconf_keyType type, int kvno)
144 {
145     struct keyTypeList *entry;
146     entry = findByType(dir, type);
147
148     if (entry == NULL)
149         return NULL;
150
151     return findInTypeList(entry, kvno);
152 }
153
154 static struct subTypeList *
155 findInKvnoList(struct kvnoList *parent, int subType)
156 {
157     struct opr_queue *cursor;
158     struct subTypeList *entry = NULL;
159
160     for (opr_queue_Scan(&parent->subTypeList, cursor)) {
161         entry = opr_queue_Entry(cursor, struct subTypeList, link);
162         if (entry->subType >= subType)
163              break;
164     }
165     if (entry == NULL || entry->subType != subType)
166         return NULL;
167
168     return entry;
169 }
170
171 static struct subTypeList *
172 findBySubType(struct afsconf_dir *dir, afsconf_keyType type, int kvno,
173               int subType)
174 {
175    struct kvnoList *entry;
176
177    entry = findByKvno(dir, type, kvno);
178    if (entry == NULL)
179         return NULL;
180
181    return findInKvnoList(entry, subType);
182 }
183
184
185 /* Add key. */
186 static int
187 addMemoryKey(struct afsconf_dir *dir, struct afsconf_typedKey *key,
188              int overwrite)
189 {
190     struct opr_queue   *cursor;
191     struct keyTypeList *typeEntry = NULL;
192     struct kvnoList    *kvnoEntry = NULL;
193     struct subTypeList *subType   = NULL;
194
195     /* Find the place in the keyType list to insert the key into */
196     for (opr_queue_Scan(&dir->keyList, cursor)) {
197         typeEntry = opr_queue_Entry(cursor, struct keyTypeList, link);
198         if (typeEntry->type >= key->type)
199             break;
200     }
201
202     if (typeEntry == NULL || typeEntry->type != key->type) {
203         struct keyTypeList *list;
204
205         list = malloc(sizeof(struct keyTypeList));
206         opr_queue_Init(&list->kvnoList);
207         list->type = key->type;
208         opr_queue_InsertBefore(cursor, &list->link);
209         typeEntry = list;
210     }
211
212     /* And the place in the kvno list */
213     for (opr_queue_Scan(&typeEntry->kvnoList, cursor)) {
214         kvnoEntry = opr_queue_Entry(cursor, struct kvnoList, link);
215         if (kvnoEntry->kvno >= key->kvno)
216             break;
217     }
218
219     if (kvnoEntry == NULL || kvnoEntry->kvno != key->kvno) {
220         struct kvnoList *list;
221
222         /* In the legacy rxkad key case, we need to check to see if we've
223          * gone over the maximum of 8 keys */
224         if (key->type == afsconf_rxkad &&
225             opr_queue_Count(&typeEntry->kvnoList)>=8)
226             return AFSCONF_FULL;
227
228         list = malloc(sizeof(struct kvnoList));
229         opr_queue_Init(&list->subTypeList);
230         list->kvno = key->kvno;
231         opr_queue_InsertBefore(cursor, &list->link);
232         kvnoEntry = list;
233     }
234
235     /* And the place in the subtype list */
236     for (opr_queue_Scan(&kvnoEntry->subTypeList, cursor)) {
237         subType = opr_queue_Entry(cursor, struct subTypeList, link);
238         if (subType->subType >= key->subType)
239             break;
240     }
241
242     if (subType == NULL || subType->subType != key->subType) {
243         struct subTypeList *list;
244
245         list = malloc(sizeof(struct subTypeList));
246         list->subType = key->subType;
247         list->key = afsconf_typedKey_get(key);
248         opr_queue_InsertBefore(cursor, &list->link);
249     } else {
250         if (overwrite) {
251             /* Give up our reference to the existing key */
252             afsconf_typedKey_put(&subType->key);
253             subType->key = afsconf_typedKey_get(key);
254         } else {
255             return AFSCONF_KEYINUSE;
256         }
257     }
258     return 0;
259 }
260
261 static void
262 deleteKvnoEntry(struct kvnoList *entry)
263 {
264     struct subTypeList *subTypeEntry;
265
266     while (!opr_queue_IsEmpty(&entry->subTypeList)) {
267         subTypeEntry = opr_queue_First(&entry->subTypeList,
268                                        struct subTypeList, link);
269         afsconf_typedKey_put(&subTypeEntry->key);
270         opr_queue_Remove(&subTypeEntry->link);
271         free(subTypeEntry);
272     }
273     opr_queue_Remove(&entry->link);
274     free(entry);
275 }
276
277 void
278 _afsconf_FreeAllKeys(struct afsconf_dir *dir)
279 {
280     struct keyTypeList *typeEntry;
281     struct kvnoList *kvnoEntry;
282
283     while (!opr_queue_IsEmpty(&dir->keyList)) {
284         typeEntry = opr_queue_First(&dir->keyList, struct keyTypeList, link);
285
286         while (!opr_queue_IsEmpty(&typeEntry->kvnoList)) {
287             kvnoEntry = opr_queue_First(&typeEntry->kvnoList,
288                                         struct kvnoList, link);
289
290             deleteKvnoEntry(kvnoEntry);
291         }
292         opr_queue_Remove(&typeEntry->link);
293         free(typeEntry);
294     }
295 }
296 void
297 _afsconf_InitKeys(struct afsconf_dir *dir)
298 {
299     opr_queue_Init(&dir->keyList);
300 }
301
302 /* Disk based key storage. This is made somewhat complicated because we
303  * store keys in more than one place - keys of type 'rxkad' (0) are stored
304  * in the original KeyFile, so that we can continue to be compatible with
305  * utilities that directly modify that file.
306  *
307  * All other keys are stored in the file KeyFileExt, which has the following
308  * format:
309  *    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
310  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
311  *   |                        number of keys                           |
312  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
313  *   | Key data ...
314  *   +-+-+-+-+-+-+-+
315  *
316  * If the format ever needs to chanage incompatibly, a new file name
317  * will be used.
318  *
319  * Key data is a sequence of the following records (note that these are
320  * not word aligned - the next record begins where the previous one ends)
321  *
322  *    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
323  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
324  *   |                         meta-data length                        |
325  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
326  *   |                           key type                              |
327  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
328  *   |                       key version number                        |
329  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
330  *   |                         key sub type                            |
331  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
332  *   |                      length of key material                     |
333  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
334  *   | Key material
335  *   +-+-+-+-+-+-+-+
336  *
337  * All values are expressed in network byte order
338  *
339  * Meta data length is the length of the initial portion of the record
340  * (itself, key type, key version number, and key sub type). In this
341  * version of the specification it would be 16. It is there to allow
342  * additional fields to be added to this specification at a later date
343  * without breaking backwards compatibility.
344  */
345
346 /* XXX - We need to be careful with failure here, because failure due to
347  * a missing file is fine, but failure due to read errors needs to be trapped,
348  * before it results in a corrupted file being written out.
349  */
350
351 static int
352 _parseOriginalKeyFile(struct afsconf_dir *dir, char *fileName)
353 {
354     int fd, code, nkeys, i;
355     struct afsconf_typedKey *key;
356
357     fd = open(fileName, O_RDONLY);
358     if (fd < 0)
359         return 0;
360
361     code = read(fd, &nkeys, sizeof(afs_int32));
362     if (code!= sizeof(afs_int32))
363         goto fail;
364
365     nkeys=ntohl(nkeys);
366     for(i=0; i<nkeys; i++) {
367
368         key = afsconf_typedKey_blank();
369         if (key == NULL)
370             goto fail;
371
372         key->type = afsconf_rxkad;
373         key->subType = 0;
374
375         code = read(fd, &key->kvno, sizeof(afs_int32));
376         if (code != sizeof(afs_int32)) {
377             free(key);
378             goto fail;
379         }
380         key->kvno = ntohl(key->kvno);
381
382         rx_opaque_alloc(&key->key, 8);
383         code = read(fd, key->key.val, 8);
384         if (code != 8) {
385             rx_opaque_freeContents(&key->key);
386             free(key);
387             goto fail;
388         }
389         code = addMemoryKey(dir, key, 1);
390         afsconf_typedKey_put(&key); /* Done with key */
391         if (code)
392             goto fail;
393     }
394     close(fd);
395     return 0;
396
397 fail:
398     close(fd);
399     return EIO;
400 }
401
402 static_inline int
403 writeWord(int fd, afs_int32 data)
404 {
405
406      data = htonl(data);
407
408      if (write(fd, &data, sizeof(afs_int32)) != sizeof(afs_int32))
409         return EIO;
410
411      return 0;
412 }
413
414 static int
415 _writeOriginalKeyFile(struct afsconf_dir *dir, char *fileName)
416 {
417     int nkeys, fd;
418     struct opr_queue *cursor;
419     struct keyTypeList *typeEntry;
420
421     fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
422     if (fd < 0)
423         return AFSCONF_FAILURE;
424
425     typeEntry = findByType(dir, afsconf_rxkad);
426     if (typeEntry)
427         nkeys = opr_queue_Count(&typeEntry->kvnoList);
428     else
429         nkeys = 0;
430
431     if (writeWord(fd, nkeys))
432         goto fail;
433
434     if (typeEntry == NULL)
435         goto out;
436
437     for (opr_queue_Scan(&typeEntry->kvnoList, cursor)) {
438         struct kvnoList *kvnoEntry;
439         struct subTypeList *subEntry;
440
441         kvnoEntry = opr_queue_Entry(cursor, struct kvnoList, link);
442         subEntry = opr_queue_First(&kvnoEntry->subTypeList,
443                                    struct subTypeList, link);
444         if (writeWord(fd, subEntry->key->kvno))
445             goto fail;
446         if (write(fd, subEntry->key->key.val, 8) != 8)
447             goto fail;
448     }
449
450 out:
451     close(fd);
452     return 0;
453
454 fail:
455     close(fd);
456     return AFSCONF_FAILURE;
457 }
458
459 static int
460 _parseExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
461 {
462     int fd, i, code;
463     afs_int32 nkeys;
464     struct afsconf_typedKey *key = NULL;
465
466     fd = open(fileName, O_RDONLY);
467     if (fd < 0)
468         return 0;
469
470     code = read(fd, &nkeys, sizeof(afs_int32));
471     if (code!= sizeof(afs_int32))
472         goto fail;
473
474     nkeys=ntohl(nkeys);
475     for(i=0; i<nkeys; i++) {
476         afs_int32 reclen;
477
478         key = afsconf_typedKey_blank();
479         if (key == NULL)
480             goto fail;
481
482         /* The only data version we currently parse has a reclen of 16.
483          * Anything smaller indicates a corrupt key file. Anything more,
484          * and we just skip the extra fields */
485         code = read(fd, &reclen, sizeof(afs_int32));
486         if (code != sizeof(afs_int32))
487             goto fail;
488         reclen = ntohl(reclen);
489         if (reclen < 16)
490             goto fail;
491         reclen-=sizeof(afs_int32);
492
493         code = read(fd, &key->type, sizeof(afs_int32));
494         if (code != sizeof(afs_int32))
495             goto fail;
496         key->type = ntohl(key->type);
497         reclen-=sizeof(afs_int32);
498
499         code = read(fd, &key->kvno, sizeof(afs_int32));
500         if (code != sizeof(afs_int32))
501             goto fail;
502         key->kvno = ntohl(key->kvno);
503         reclen-=sizeof(afs_int32);
504
505         code = read(fd, &key->subType, sizeof(afs_int32));
506         if (code != sizeof(afs_int32))
507             goto fail;
508         key->subType = ntohl(key->subType);
509         reclen-=sizeof(afs_int32);
510
511         if (reclen > 0) {
512             code = lseek(fd, reclen, SEEK_CUR);
513             if (code < 0)
514                 goto fail;
515         }
516
517         code = read(fd, &reclen, sizeof(afs_int32));
518         if (code != sizeof(afs_int32))
519             goto fail;
520         reclen = ntohl(reclen);
521
522         rx_opaque_alloc(&key->key, reclen);
523         code = read(fd, key->key.val, reclen);
524         if (code != reclen) {
525             rx_opaque_freeContents(&key->key);
526             goto fail;
527         }
528         code = addMemoryKey(dir, key, 1);
529         afsconf_typedKey_put(&key);
530         if (code)
531             goto fail;
532     }
533     close(fd);
534     return 0;
535
536 fail:
537     if (key)
538         afsconf_typedKey_put(&key);
539
540     close(fd);
541     return EIO;
542 }
543
544
545 static int
546 _writeExtendedKeyFile(struct afsconf_dir *dir, char *fileName)
547 {
548     int nkeys;
549     int fd;
550
551     struct keyTypeList *typeEntry;
552     struct kvnoList    *kvnoEntry;
553     struct subTypeList *entry;
554     struct opr_queue   *keyCursor;
555     struct opr_queue   *kvnoCursor;
556     struct opr_queue   *subCursor;
557
558     fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
559     if (fd < 0)
560         return AFSCONF_FAILURE;
561
562     /* Iterate over the whole in-memory key store, and write everything
563      * except keys with type rxkad into the extended key file */
564
565     /* Write a 0 key count - we'll fill it in later */
566     nkeys = 0;
567     if (writeWord(fd, 0))
568         goto fail;
569
570     for (opr_queue_Scan(&dir->keyList, keyCursor)) {
571         typeEntry = opr_queue_Entry(keyCursor, struct keyTypeList, link);
572
573         if (typeEntry->type != afsconf_rxkad) {
574             for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
575                 kvnoEntry = opr_queue_Entry(kvnoCursor, struct kvnoList, link);
576                 for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) {
577                     entry = opr_queue_Entry(subCursor, struct subTypeList, link);
578                     if (writeWord(fd, 16)) /* record length */
579                         goto fail;
580                     if (writeWord(fd, entry->key->type))
581                         goto fail;
582                     if (writeWord(fd, entry->key->kvno))
583                         goto fail;
584                     if (writeWord(fd, entry->key->subType))
585                         goto fail;
586                     if (writeWord(fd, entry->key->key.len))
587                         goto fail;
588                     if (write(fd, entry->key->key.val,
589                               entry->key->key.len) !=
590                         entry->key->key.len)
591                         goto fail;
592                     nkeys++;
593                 }
594             }
595         }
596     }
597
598     if (lseek(fd, 0, SEEK_SET)<0)
599         goto fail;
600
601     if (writeWord(fd, nkeys))
602         goto fail;
603
604     close(fd);
605
606     return 0;
607
608 fail:
609     close(fd);
610     return AFSCONF_FAILURE;
611 }
612
613 int
614 _afsconf_LoadKeys(struct afsconf_dir *dir)
615 {
616     int code;
617     char *fileName;
618
619     /* If we're running on Windows, and we are a client, we don't have a
620      * KeyFile, so don't try and open one */
621
622 #ifdef AFS_NT40_ENV
623     if (_afsconf_IsClientConfigDirectory(dir->name))
624         return 0;
625 #endif /* AFS_NT40_ENV */
626
627     LOCK_GLOBAL_MUTEX;
628
629     /* Delete all of our existing keys */
630     _afsconf_FreeAllKeys(dir);
631
632     /* Start by opening the original KeyFile */
633     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_KEY_FILE);
634     code = _parseOriginalKeyFile(dir, fileName);
635     free(fileName);
636     if (code)
637         goto out;
638
639     /* Now open the new style KeyFile */
640     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_EXT_KEY_FILE);
641     code = _parseExtendedKeyFile(dir, fileName);
642     free(fileName);
643     if (code)
644         goto out;
645
646 out:
647     if (code)
648         _afsconf_FreeAllKeys(dir);
649
650     UNLOCK_GLOBAL_MUTEX;
651
652     return code;
653 }
654
655 static int
656 _afsconf_SaveKeys(struct afsconf_dir *dir)
657 {
658     char *fileName;
659     int code;
660
661     /* If we're running on Windows, and we are a client, we don't have a
662      * KeyFile, so don't try and open one */
663
664 #ifdef AFS_NT40_ENV
665     if (_afsconf_IsClientConfigDirectory(dir->name))
666         return 0;
667 #endif /* AFS_NT40_ENV */
668
669     LOCK_GLOBAL_MUTEX;
670
671     /* Start by opening the original KeyFile */
672     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_KEY_FILE);
673     code = _writeOriginalKeyFile(dir, fileName);
674     free(fileName);
675     if (code)
676         goto out;
677
678     /* Now open the new style KeyFile */
679     asnprintf(&fileName, 256, "%s/%s", dir->name, AFSDIR_EXT_KEY_FILE);
680     code = _writeExtendedKeyFile(dir, fileName);
681     free(fileName);
682     if (code)
683         goto out;
684
685 out:
686     UNLOCK_GLOBAL_MUTEX;
687
688     return code;
689 }
690
691
692
693 /* get keys structure */
694 int
695 afsconf_GetKeys(struct afsconf_dir *dir, struct afsconf_keys *astr)
696 {
697     afs_int32 code;
698     struct keyTypeList *typeEntry;
699     struct opr_queue *cursor;
700
701     memset(astr, 0, sizeof(struct afsconf_keys));
702
703     LOCK_GLOBAL_MUTEX;
704
705     code = _afsconf_Check(dir);
706     if (code)
707         goto out;
708
709     typeEntry = findByType(dir, afsconf_rxkad);
710     if (typeEntry == NULL)
711         goto out;
712
713     for (opr_queue_Scan(&typeEntry->kvnoList, cursor)) {
714         struct kvnoList *kvnoEntry;
715         struct subTypeList *subEntry;
716
717         kvnoEntry = opr_queue_Entry(cursor, struct kvnoList, link);
718         subEntry = opr_queue_First(&kvnoEntry->subTypeList,
719                                    struct subTypeList, link);
720         /* XXX - If there is more than one key in this list, it's an error */
721         astr->key[astr->nkeys].kvno = subEntry->key->kvno;
722         /* XXX - If the opaque contains a number of bytes other than 8, it's
723          * an error */
724         memcpy(&astr->key[astr->nkeys].key, subEntry->key->key.val, 8);
725         astr->nkeys++;
726     }
727
728 out:
729     UNLOCK_GLOBAL_MUTEX;
730     return code;
731 }
732
733 afs_int32
734 afsconf_GetLatestKey(struct afsconf_dir *dir, afs_int32 *kvno,
735                      struct ktc_encryptionKey *key)
736 {
737     struct afsconf_typedKey *typedKey;
738     int code;
739
740     code = afsconf_GetLatestKeyByTypes(dir, afsconf_rxkad, 0, &typedKey);
741     if (code)
742         return code;
743
744     /* XXX - Should check that the key is of the correct length */
745
746     /* Copy out the relevant details */
747     if (kvno != NULL)
748         *kvno = typedKey->kvno;
749
750     if (key != NULL)
751         memcpy(key, typedKey->key.val, 8);
752
753     afsconf_typedKey_put(&typedKey);
754
755     return 0;
756 }
757
758 int
759 afsconf_GetKey(void *rock, int kvno, struct ktc_encryptionKey *key)
760 {
761     struct afsconf_typedKey *typedKey;
762     int code;
763
764     code = afsconf_GetKeyByTypes(rock, afsconf_rxkad, kvno, 0, &typedKey);
765     if (code)
766         return code;
767
768     memcpy(key, typedKey->key.val, 8);
769
770     afsconf_typedKey_put(&typedKey);
771
772     return 0;
773 }
774
775 static int
776 _afsconf_GetLatestRXGKKey(afsconf_keyType type, struct afsconf_dir *rock,
777                           afs_int32 *avno, afs_int32 *enctype, rxgk_key *key)
778 {
779 #ifdef AFS_RXGK_ENV
780     struct afsconf_typedKeyList *list = NULL;
781     struct afsconf_typedKey *typedKey = NULL;
782     afs_int32 code;
783     int key_i;
784
785     code = afsconf_GetLatestKeysByType(rock, type, &list);
786     if (code != 0)
787         goto done;
788
789     for (key_i = 0; key_i < list->nkeys; key_i++) {
790         if (typedKey == NULL)
791             typedKey = list->keys[key_i];
792         else if (rxgk_enctype_better(typedKey->subType, list->keys[key_i]->subType))
793             typedKey = list->keys[key_i];
794     }
795
796     opr_Assert(typedKey != NULL);
797
798     /* We picked a key; copy to the output parameters */
799     code = rxgk_make_key(key, typedKey->key.val, typedKey->key.len,
800                          typedKey->subType);
801     if (code != 0)
802         goto done;
803     if (avno != NULL)
804         *avno = typedKey->kvno;
805     if (enctype != NULL)
806         *enctype = typedKey->subType;
807
808  done:
809     afsconf_PutTypedKeyList(&list);
810     return code;
811 #else   /* AFS_RXGK_ENV */
812     return AFSCONF_NOTFOUND;
813 #endif
814 }
815
816 /**
817  * Obtain the "best" rxgk key from KeyFileExt
818  *
819  * Return the key and its enctype and kvno, for encrypting outgoing tokens.
820  *
821  * @param[in] rock      The configuration directory to be used.
822  * @param[out] avno     The key version number of key.
823  * @param[out] enctype  The RFC 3961 enctype of key.
824  * @param[out] key      The returned rxgk key.
825  */
826 int
827 afsconf_GetLatestRXGKKey(struct afsconf_dir *rock, afs_int32 *avno,
828                          afs_int32 *enctype, rxgk_key *key)
829 {
830     return _afsconf_GetLatestRXGKKey(afsconf_rxgk, rock, avno, enctype, key);
831 }
832
833 static int
834 _afsconf_GetRXGKKey(afsconf_keyType type, void *rock, afs_int32 *avno,
835                     afs_int32 *enctype, rxgk_key *key)
836 {
837 #ifdef AFS_RXGK_ENV
838     struct afsconf_dir *dir = rock;
839     struct afsconf_typedKey *typedKey;
840     afs_int32 code;
841
842     /* No information at all means "pick the best/newest one". */
843     if (*avno == 0 && *enctype == 0)
844         return _afsconf_GetLatestRXGKKey(type, dir, avno, enctype, key);
845
846     code = afsconf_GetKeyByTypes(dir, type, *avno, *enctype, &typedKey);
847     if (code != 0)
848         return code;
849
850     code = rxgk_make_key(key, typedKey->key.val, typedKey->key.len,
851                          typedKey->subType);
852     afsconf_typedKey_put(&typedKey);
853
854     return code;
855 #else   /* AFS_RXGK_ENV */
856     return AFSCONF_NOTFOUND;
857 #endif
858 }
859
860 /**
861  * Obtain a particular RXGK key from KeyFileExt
862  *
863  * Use the specified kvno and enctype to fetch an rxgk key from KeyFileExt
864  * and return it as an rxgk_key.  Specifying the kvno/enctype pair as both
865  * zeros causes the "best" rxgk key to be returned, and the kvno/enctype
866  * of that key returned to the caller.
867  *
868  * @param[in] rock      An afsconf_dir* for the configuration directory. This
869  *                      is a void* just so this can be easily used as a
870  *                      callback function that uses a void* rock.
871  * @param[inout] avno   The requested kvno (if non-zero), or zero to request
872  *                      the latest key and have its kvno returned in this
873  *                      parameter.
874  * @param[inout] enctype        The requested enctype (if non-zero), or zero
875  *                              to request the latest key and have its
876  *                              enctype returned in this parameter.
877  * @param[out] key      The returned rxgk key.
878  */
879 int
880 afsconf_GetRXGKKey(void *rock, afs_int32 *avno,
881                    afs_int32 *enctype, rxgk_key *key)
882 {
883     return _afsconf_GetRXGKKey(afsconf_rxgk, rock, avno, enctype, key);
884 }
885
886 int
887 afsconf_AddKey(struct afsconf_dir *dir, afs_int32 kvno, char key[8],
888                afs_int32 overwrite)
889 {
890     struct rx_opaque buffer;
891     struct afsconf_typedKey *typedKey;
892     int code;
893
894     rx_opaque_alloc(&buffer, 8);
895     memcpy(buffer.val, key, 8);
896     typedKey = afsconf_typedKey_new(afsconf_rxkad, kvno, 0, &buffer);
897     if (typedKey == NULL)
898         return AFSCONF_FAILURE;
899
900     rx_opaque_freeContents(&buffer);
901
902     code = afsconf_AddTypedKey(dir, typedKey, overwrite);
903     afsconf_typedKey_put(&typedKey);
904     return code;
905 }
906
907 int
908 afsconf_DeleteKey(struct afsconf_dir *dir, afs_int32 kvno)
909 {
910     return afsconf_DeleteKeyByType(dir, afsconf_rxkad, kvno);
911 }
912
913 int
914 afsconf_GetKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
915                       int kvno, struct afsconf_typedKeyList **keys)
916 {
917     struct kvnoList *kvnoEntry;
918     int code;
919
920     LOCK_GLOBAL_MUTEX;
921
922     code = _afsconf_Check(dir);
923     if (code)
924         goto out;
925
926     kvnoEntry = findByKvno(dir, type, kvno);
927     if (kvnoEntry == NULL) {
928         code = AFSCONF_NOTFOUND;
929         goto out;
930     }
931
932     code = listToArray(kvnoEntry, keys);
933
934 out:
935     UNLOCK_GLOBAL_MUTEX;
936     return code;
937 }
938
939 int
940 _afsconf_CountKeys(struct afsconf_dir *dir)
941 {
942     int count = 0;
943     struct opr_queue *typeCursor;
944     struct keyTypeList *typeEntry;
945     struct opr_queue *kvnoCursor;
946     struct kvnoList *kvnoEntry;
947     struct opr_queue *subCursor;
948
949     for (opr_queue_Scan(&dir->keyList, typeCursor)) {
950         typeEntry = opr_queue_Entry(typeCursor, struct keyTypeList, link);
951         for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
952             kvnoEntry = opr_queue_Entry(kvnoCursor, struct kvnoList, link);
953             for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor))
954                 count++;
955         }
956     }
957     return count;
958 }
959
960 int
961 afsconf_CountKeys(struct afsconf_dir *dir)
962 {
963     int count = 0;
964
965     LOCK_GLOBAL_MUTEX;
966     count = _afsconf_CountKeys(dir);
967     UNLOCK_GLOBAL_MUTEX;
968
969     return count;
970 }
971
972 int
973 afsconf_GetAllKeys(struct afsconf_dir *dir, struct afsconf_typedKeyList **keys)
974 {
975     int code;
976     struct afsconf_typedKeyList *retval;
977     struct opr_queue *typeCursor;
978     struct keyTypeList *typeEntry;
979     struct opr_queue *kvnoCursor;
980     struct kvnoList *kvnoEntry;
981     struct opr_queue *subCursor;
982     struct subTypeList *subEntry;
983     int count;
984
985     LOCK_GLOBAL_MUTEX;
986
987     code = _afsconf_Check(dir);
988     if (code)
989         goto out;
990
991     /* First, work out how many keys we have in total */
992     count = _afsconf_CountKeys(dir);
993
994     /* Allocate space for all of these */
995     retval = malloc(sizeof(struct afsconf_typedKeyList));
996     retval->nkeys = count;
997
998     if (count > 0) {
999         retval->keys = calloc(retval->nkeys,
1000                               sizeof(struct afsconf_typedKey *));
1001
1002         /* Populate the key list */
1003         count = 0;
1004         for (opr_queue_Scan(&dir->keyList, typeCursor)) {
1005             typeEntry = opr_queue_Entry(typeCursor,
1006                                         struct keyTypeList, link);
1007             for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
1008                 kvnoEntry = opr_queue_Entry(kvnoCursor,
1009                                             struct kvnoList, link);
1010                 for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) {
1011                     subEntry = opr_queue_Entry(subCursor,
1012                                                struct subTypeList, link);
1013                     retval->keys[count] = afsconf_typedKey_get(subEntry->key);
1014                     count++;
1015                 }
1016             }
1017         }
1018     } else {
1019         retval->keys = NULL;
1020     }
1021
1022     *keys = retval;
1023
1024 out:
1025     UNLOCK_GLOBAL_MUTEX;
1026     return code;
1027 }
1028
1029 int
1030 afsconf_GetKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
1031                       int kvno, int subType, struct afsconf_typedKey **key)
1032 {
1033     int code = 0;
1034     struct subTypeList *subTypeEntry;
1035
1036     LOCK_GLOBAL_MUTEX;
1037
1038     code = _afsconf_Check(dir);
1039     if (code)
1040         goto out;
1041
1042     subTypeEntry = findBySubType(dir, type, kvno, subType);
1043     if (subTypeEntry == NULL) {
1044         code = AFSCONF_NOTFOUND;
1045         goto out;
1046     }
1047
1048     *key = afsconf_typedKey_get(subTypeEntry->key);
1049
1050 out:
1051     UNLOCK_GLOBAL_MUTEX;
1052     return code;
1053 }
1054
1055 static struct kvnoList *
1056 pickBestKvno(struct afsconf_dir *dir, afsconf_keyType type)
1057 {
1058     struct keyTypeList *typeEntry;
1059     struct kvnoList    *kvnoEntry;
1060
1061     typeEntry = findByType(dir, type);
1062     if (typeEntry == NULL)
1063         return NULL;
1064
1065     /* We store all of the key lists ordered, so the last entry in the
1066      * kvno list must be the highest kvno. */
1067
1068     kvnoEntry = opr_queue_Last(&typeEntry->kvnoList, struct kvnoList, link);
1069
1070     /* Except, if we're in the rxkad list, we might have a bcrypt entry that
1071      * has a kvno of 999. So we need to skip that one
1072      */
1073     while (type == afsconf_rxkad && kvnoEntry->kvno == 999) {
1074         kvnoEntry = opr_queue_Prev(&typeEntry->kvnoList, struct kvnoList,
1075                                    link);
1076         if (opr_queue_IsEnd(&typeEntry->kvnoList, &kvnoEntry->link))
1077            return NULL;
1078     }
1079
1080     return kvnoEntry;
1081 }
1082
1083
1084 int
1085 afsconf_GetLatestKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
1086                             struct afsconf_typedKeyList **keys)
1087 {
1088     int code;
1089     struct kvnoList *kvnoEntry;
1090
1091     LOCK_GLOBAL_MUTEX;
1092
1093     code = _afsconf_Check(dir);
1094     if (code)
1095         goto out;
1096
1097
1098     kvnoEntry = pickBestKvno(dir, type);
1099     if (kvnoEntry == NULL) {
1100         code = AFSCONF_NOTFOUND;
1101         goto out;
1102     }
1103
1104     code = listToArray(kvnoEntry, keys);
1105
1106 out:
1107     UNLOCK_GLOBAL_MUTEX;
1108     return code;
1109 }
1110
1111 int
1112 afsconf_GetLatestKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
1113                             int subType, struct afsconf_typedKey **key)
1114 {
1115     int code;
1116     struct kvnoList *kvnoEntry;
1117     struct subTypeList *subTypeEntry;
1118
1119     LOCK_GLOBAL_MUTEX;
1120
1121     code = _afsconf_Check(dir);
1122     if (code)
1123         goto out;
1124
1125     kvnoEntry = pickBestKvno(dir, type);
1126     if (kvnoEntry == NULL) {
1127         code = AFSCONF_NOTFOUND;
1128         goto out;
1129     }
1130
1131     subTypeEntry = findInKvnoList(kvnoEntry, subType);
1132     if (subTypeEntry == NULL) {
1133         code = AFSCONF_NOTFOUND;
1134         goto out;
1135     }
1136
1137     *key = afsconf_typedKey_get(subTypeEntry->key);
1138
1139 out:
1140     UNLOCK_GLOBAL_MUTEX;
1141     return code;
1142 }
1143
1144 void
1145 afsconf_PutTypedKeyList(struct afsconf_typedKeyList **keys)
1146 {
1147      int i;
1148
1149      if (*keys == NULL) {
1150         return;
1151      }
1152
1153      for (i=0;i<(*keys)->nkeys;i++)
1154         afsconf_typedKey_put(&((*keys)->keys[i]));
1155
1156      if ((*keys)->keys != NULL)
1157         free((*keys)->keys);
1158
1159      free(*keys);
1160      *keys = NULL;
1161 }
1162
1163 static struct afsconf_typedKey *
1164 afsconf_typedKey_blank(void)
1165 {
1166     struct afsconf_typedKey *key;
1167
1168     key = calloc(1, sizeof(struct afsconf_typedKey));
1169     if (key == NULL)
1170         return NULL;
1171
1172     rx_atomic_set(&key->refcnt, 1);
1173
1174     return key;
1175 }
1176
1177 struct afsconf_typedKey *
1178 afsconf_typedKey_new(afsconf_keyType type, int kvno, int subType,
1179                      struct rx_opaque *keyMaterial)
1180 {
1181     struct afsconf_typedKey *key;
1182     int code;
1183
1184     key = afsconf_typedKey_blank();
1185     if (key == NULL)
1186         return key;
1187
1188     key->type = type;
1189     key->kvno = kvno;
1190     key->subType = subType;
1191
1192     code = rx_opaque_copy(&key->key, keyMaterial);
1193     if (code != 0) {
1194         free(key);
1195         return NULL;
1196     }
1197
1198     return key;
1199 }
1200
1201 void
1202 afsconf_typedKey_free(struct afsconf_typedKey **key)
1203 {
1204     if (*key == NULL)
1205         return;
1206     rx_opaque_freeContents(&(*key)->key);
1207     free(*key);
1208     *key = NULL;
1209 }
1210
1211 struct afsconf_typedKey *
1212 afsconf_typedKey_get(struct afsconf_typedKey *key)
1213 {
1214      rx_atomic_inc(&key->refcnt);
1215      return key;
1216 }
1217
1218 void
1219 afsconf_typedKey_put(struct afsconf_typedKey **key)
1220 {
1221     if (rx_atomic_dec_and_read(&(*key)->refcnt) == 0)
1222         afsconf_typedKey_free(key);
1223     else
1224         *key = NULL;
1225 }
1226
1227 void
1228 afsconf_typedKey_values(struct afsconf_typedKey *key, afsconf_keyType *type,
1229                         int *kvno, int *subType, struct rx_opaque **material)
1230 {
1231     if (type != NULL)
1232         *type = key->type;
1233     if (kvno != NULL)
1234         *kvno = key->kvno;
1235     if (subType != NULL)
1236         *subType = key->subType;
1237     if (material != NULL)
1238         *material = &key->key;
1239 }
1240
1241 int
1242 afsconf_AddTypedKey(struct afsconf_dir *dir,
1243                     struct afsconf_typedKey *key,
1244                     int overwrite)
1245 {
1246     int code;
1247
1248     LOCK_GLOBAL_MUTEX;
1249
1250     code = _afsconf_Check(dir);
1251     if (code)
1252         goto out;
1253
1254     if (key->type == afsconf_rxkad) {
1255         /* There are restrictions on rxkad keys so that we can still
1256          * return them using the old interface. We only enforce the
1257          * same restrictions as that interface does - that is, we don't
1258          * check that the key we're passed is a valid DES key */
1259         if (key->key.len != 8 || key->subType != 0) {
1260             code = AFSCONF_BADKEY;
1261             goto out;
1262         }
1263     }
1264
1265     code = addMemoryKey(dir, key, overwrite);
1266     if (code)
1267         goto out;
1268
1269     code = _afsconf_SaveKeys(dir);
1270     _afsconf_Touch(dir);
1271
1272 out:
1273     UNLOCK_GLOBAL_MUTEX;
1274     return code;
1275 }
1276
1277 int
1278 afsconf_DeleteKeyByType(struct afsconf_dir *dir,
1279                         afsconf_keyType type, int kvno)
1280 {
1281     struct keyTypeList *typeEntry;
1282     struct kvnoList *kvnoEntry;
1283     int code;
1284
1285     LOCK_GLOBAL_MUTEX;
1286
1287     code = _afsconf_Check(dir);
1288     if (code)
1289         goto out;
1290
1291     typeEntry = findByType(dir, type);
1292     if (typeEntry == NULL) {
1293         code = AFSCONF_NOTFOUND;
1294         goto out;
1295     }
1296
1297     kvnoEntry = findInTypeList(typeEntry, kvno);
1298     if (kvnoEntry == NULL) {
1299         code = AFSCONF_NOTFOUND;
1300         goto out;
1301     }
1302
1303     deleteKvnoEntry(kvnoEntry);
1304
1305     /* Remove the typeEntry, if it has no sub elements */
1306     if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
1307         opr_queue_Remove(&typeEntry->link);
1308         free(typeEntry);
1309     }
1310
1311     code = _afsconf_SaveKeys(dir);
1312     _afsconf_Touch(dir);
1313
1314 out:
1315     UNLOCK_GLOBAL_MUTEX;
1316     return code;
1317 }
1318
1319 int
1320 afsconf_DeleteKeyBySubType(struct afsconf_dir *dir,
1321                            afsconf_keyType type, int kvno, int subType)
1322 {
1323     struct keyTypeList *typeEntry;
1324     struct kvnoList *kvnoEntry;
1325     struct subTypeList *subTypeEntry;
1326     int code;
1327
1328     LOCK_GLOBAL_MUTEX;
1329
1330     code = _afsconf_Check(dir);
1331     if (code)
1332         goto out;
1333
1334     typeEntry = findByType(dir, type);
1335     if (typeEntry == NULL)
1336         return AFSCONF_NOTFOUND;
1337
1338     kvnoEntry = findInTypeList(typeEntry, kvno);
1339     if (kvnoEntry == NULL)
1340         return AFSCONF_NOTFOUND;
1341
1342     subTypeEntry = findInKvnoList(kvnoEntry, subType);
1343     if (subTypeEntry == NULL)
1344         return AFSCONF_NOTFOUND;
1345
1346     /* Remove the subTypeEntry */
1347     afsconf_typedKey_put(&subTypeEntry->key);
1348     opr_queue_Remove(&subTypeEntry->link);
1349     free(subTypeEntry);
1350
1351     /* Remove the kvnoEntry, if it has no sub elements */
1352     if (opr_queue_IsEmpty(&kvnoEntry->subTypeList)) {
1353         opr_queue_Remove(&kvnoEntry->link);
1354         free(kvnoEntry);
1355     }
1356
1357     /* Remove the typeEntry, if it has no sub elements */
1358     if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
1359         opr_queue_Remove(&typeEntry->link);
1360         free(typeEntry);
1361     }
1362
1363     code = _afsconf_SaveKeys(dir);
1364     _afsconf_Touch(dir);
1365
1366 out:
1367     UNLOCK_GLOBAL_MUTEX;
1368     return code;
1369 }
1370
1371 int
1372 afsconf_DeleteTypedKey(struct afsconf_dir *dir, struct afsconf_typedKey *key)
1373 {
1374     return afsconf_DeleteKeyBySubType(dir, key->type, key->kvno, key->subType);
1375 }