improve command-line help for --enable_peer_stats
[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_GetAllKeys(struct afsconf_dir *dir, struct afsconf_typedKeyList **keys)
941 {
942     int code;
943     struct afsconf_typedKeyList *retval;
944     struct opr_queue *typeCursor;
945     struct keyTypeList *typeEntry;
946     struct opr_queue *kvnoCursor;
947     struct kvnoList *kvnoEntry;
948     struct opr_queue *subCursor;
949     struct subTypeList *subEntry;
950     int count;
951
952     LOCK_GLOBAL_MUTEX;
953
954     code = _afsconf_Check(dir);
955     if (code)
956         goto out;
957
958     count = 0;
959     /* First, work out how many keys we have in total */
960     for (opr_queue_Scan(&dir->keyList, typeCursor)) {
961         typeEntry = opr_queue_Entry(typeCursor, struct keyTypeList, link);
962         for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
963             kvnoEntry = opr_queue_Entry(kvnoCursor, struct kvnoList, link);
964             for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor))
965                 count++;
966         }
967     }
968
969     /* Allocate space for all of these */
970     retval = malloc(sizeof(struct afsconf_typedKeyList));
971     retval->nkeys = count;
972
973     if (count > 0) {
974         retval->keys = calloc(retval->nkeys,
975                               sizeof(struct afsconf_typedKey *));
976
977         /* Populate the key list */
978         count = 0;
979         for (opr_queue_Scan(&dir->keyList, typeCursor)) {
980             typeEntry = opr_queue_Entry(typeCursor,
981                                         struct keyTypeList, link);
982             for (opr_queue_Scan(&typeEntry->kvnoList, kvnoCursor)) {
983                 kvnoEntry = opr_queue_Entry(kvnoCursor,
984                                             struct kvnoList, link);
985                 for (opr_queue_Scan(&kvnoEntry->subTypeList, subCursor)) {
986                     subEntry = opr_queue_Entry(subCursor,
987                                                struct subTypeList, link);
988                     retval->keys[count] = afsconf_typedKey_get(subEntry->key);
989                     count++;
990                 }
991             }
992         }
993     } else {
994         retval->keys = NULL;
995     }
996
997     *keys = retval;
998
999 out:
1000     UNLOCK_GLOBAL_MUTEX;
1001     return code;
1002 }
1003
1004 int
1005 afsconf_GetKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
1006                       int kvno, int subType, struct afsconf_typedKey **key)
1007 {
1008     int code = 0;
1009     struct subTypeList *subTypeEntry;
1010
1011     LOCK_GLOBAL_MUTEX;
1012
1013     code = _afsconf_Check(dir);
1014     if (code)
1015         goto out;
1016
1017     subTypeEntry = findBySubType(dir, type, kvno, subType);
1018     if (subTypeEntry == NULL) {
1019         code = AFSCONF_NOTFOUND;
1020         goto out;
1021     }
1022
1023     *key = afsconf_typedKey_get(subTypeEntry->key);
1024
1025 out:
1026     UNLOCK_GLOBAL_MUTEX;
1027     return code;
1028 }
1029
1030 static struct kvnoList *
1031 pickBestKvno(struct afsconf_dir *dir, afsconf_keyType type)
1032 {
1033     struct keyTypeList *typeEntry;
1034     struct kvnoList    *kvnoEntry;
1035
1036     typeEntry = findByType(dir, type);
1037     if (typeEntry == NULL)
1038         return NULL;
1039
1040     /* We store all of the key lists ordered, so the last entry in the
1041      * kvno list must be the highest kvno. */
1042
1043     kvnoEntry = opr_queue_Last(&typeEntry->kvnoList, struct kvnoList, link);
1044
1045     /* Except, if we're in the rxkad list, we might have a bcrypt entry that
1046      * has a kvno of 999. So we need to skip that one
1047      */
1048     while (type == afsconf_rxkad && kvnoEntry->kvno == 999) {
1049         kvnoEntry = opr_queue_Prev(&typeEntry->kvnoList, struct kvnoList,
1050                                    link);
1051         if (opr_queue_IsEnd(&typeEntry->kvnoList, &kvnoEntry->link))
1052            return NULL;
1053     }
1054
1055     return kvnoEntry;
1056 }
1057
1058
1059 int
1060 afsconf_GetLatestKeysByType(struct afsconf_dir *dir, afsconf_keyType type,
1061                             struct afsconf_typedKeyList **keys)
1062 {
1063     int code;
1064     struct kvnoList *kvnoEntry;
1065
1066     LOCK_GLOBAL_MUTEX;
1067
1068     code = _afsconf_Check(dir);
1069     if (code)
1070         goto out;
1071
1072
1073     kvnoEntry = pickBestKvno(dir, type);
1074     if (kvnoEntry == NULL) {
1075         code = AFSCONF_NOTFOUND;
1076         goto out;
1077     }
1078
1079     code = listToArray(kvnoEntry, keys);
1080
1081 out:
1082     UNLOCK_GLOBAL_MUTEX;
1083     return code;
1084 }
1085
1086 int
1087 afsconf_GetLatestKeyByTypes(struct afsconf_dir *dir, afsconf_keyType type,
1088                             int subType, struct afsconf_typedKey **key)
1089 {
1090     int code;
1091     struct kvnoList *kvnoEntry;
1092     struct subTypeList *subTypeEntry;
1093
1094     LOCK_GLOBAL_MUTEX;
1095
1096     code = _afsconf_Check(dir);
1097     if (code)
1098         goto out;
1099
1100     kvnoEntry = pickBestKvno(dir, type);
1101     if (kvnoEntry == NULL) {
1102         code = AFSCONF_NOTFOUND;
1103         goto out;
1104     }
1105
1106     subTypeEntry = findInKvnoList(kvnoEntry, subType);
1107     if (subTypeEntry == NULL) {
1108         code = AFSCONF_NOTFOUND;
1109         goto out;
1110     }
1111
1112     *key = afsconf_typedKey_get(subTypeEntry->key);
1113
1114 out:
1115     UNLOCK_GLOBAL_MUTEX;
1116     return code;
1117 }
1118
1119 void
1120 afsconf_PutTypedKeyList(struct afsconf_typedKeyList **keys)
1121 {
1122      int i;
1123
1124      if (*keys == NULL) {
1125         return;
1126      }
1127
1128      for (i=0;i<(*keys)->nkeys;i++)
1129         afsconf_typedKey_put(&((*keys)->keys[i]));
1130
1131      if ((*keys)->keys != NULL)
1132         free((*keys)->keys);
1133
1134      free(*keys);
1135      *keys = NULL;
1136 }
1137
1138 static struct afsconf_typedKey *
1139 afsconf_typedKey_blank(void)
1140 {
1141     struct afsconf_typedKey *key;
1142
1143     key = calloc(1, sizeof(struct afsconf_typedKey));
1144     if (key == NULL)
1145         return NULL;
1146
1147     rx_atomic_set(&key->refcnt, 1);
1148
1149     return key;
1150 }
1151
1152 struct afsconf_typedKey *
1153 afsconf_typedKey_new(afsconf_keyType type, int kvno, int subType,
1154                      struct rx_opaque *keyMaterial)
1155 {
1156     struct afsconf_typedKey *key;
1157     int code;
1158
1159     key = afsconf_typedKey_blank();
1160     if (key == NULL)
1161         return key;
1162
1163     key->type = type;
1164     key->kvno = kvno;
1165     key->subType = subType;
1166
1167     code = rx_opaque_copy(&key->key, keyMaterial);
1168     if (code != 0) {
1169         free(key);
1170         return NULL;
1171     }
1172
1173     return key;
1174 }
1175
1176 void
1177 afsconf_typedKey_free(struct afsconf_typedKey **key)
1178 {
1179     if (*key == NULL)
1180         return;
1181     rx_opaque_freeContents(&(*key)->key);
1182     free(*key);
1183     *key = NULL;
1184 }
1185
1186 struct afsconf_typedKey *
1187 afsconf_typedKey_get(struct afsconf_typedKey *key)
1188 {
1189      rx_atomic_inc(&key->refcnt);
1190      return key;
1191 }
1192
1193 void
1194 afsconf_typedKey_put(struct afsconf_typedKey **key)
1195 {
1196     if (rx_atomic_dec_and_read(&(*key)->refcnt) == 0)
1197         afsconf_typedKey_free(key);
1198     else
1199         *key = NULL;
1200 }
1201
1202 void
1203 afsconf_typedKey_values(struct afsconf_typedKey *key, afsconf_keyType *type,
1204                         int *kvno, int *subType, struct rx_opaque **material)
1205 {
1206     if (type != NULL)
1207         *type = key->type;
1208     if (kvno != NULL)
1209         *kvno = key->kvno;
1210     if (subType != NULL)
1211         *subType = key->subType;
1212     if (material != NULL)
1213         *material = &key->key;
1214 }
1215
1216 int
1217 afsconf_AddTypedKey(struct afsconf_dir *dir,
1218                     struct afsconf_typedKey *key,
1219                     int overwrite)
1220 {
1221     int code;
1222
1223     LOCK_GLOBAL_MUTEX;
1224
1225     code = _afsconf_Check(dir);
1226     if (code)
1227         goto out;
1228
1229     if (key->type == afsconf_rxkad) {
1230         /* There are restrictions on rxkad keys so that we can still
1231          * return them using the old interface. We only enforce the
1232          * same restrictions as that interface does - that is, we don't
1233          * check that the key we're passed is a valid DES key */
1234         if (key->key.len != 8 || key->subType != 0) {
1235             code = AFSCONF_BADKEY;
1236             goto out;
1237         }
1238     }
1239
1240     code = addMemoryKey(dir, key, overwrite);
1241     if (code)
1242         goto out;
1243
1244     code = _afsconf_SaveKeys(dir);
1245     _afsconf_Touch(dir);
1246
1247 out:
1248     UNLOCK_GLOBAL_MUTEX;
1249     return code;
1250 }
1251
1252 int
1253 afsconf_DeleteKeyByType(struct afsconf_dir *dir,
1254                         afsconf_keyType type, int kvno)
1255 {
1256     struct keyTypeList *typeEntry;
1257     struct kvnoList *kvnoEntry;
1258     int code;
1259
1260     LOCK_GLOBAL_MUTEX;
1261
1262     code = _afsconf_Check(dir);
1263     if (code)
1264         goto out;
1265
1266     typeEntry = findByType(dir, type);
1267     if (typeEntry == NULL) {
1268         code = AFSCONF_NOTFOUND;
1269         goto out;
1270     }
1271
1272     kvnoEntry = findInTypeList(typeEntry, kvno);
1273     if (kvnoEntry == NULL) {
1274         code = AFSCONF_NOTFOUND;
1275         goto out;
1276     }
1277
1278     deleteKvnoEntry(kvnoEntry);
1279
1280     /* Remove the typeEntry, if it has no sub elements */
1281     if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
1282         opr_queue_Remove(&typeEntry->link);
1283         free(typeEntry);
1284     }
1285
1286     code = _afsconf_SaveKeys(dir);
1287     _afsconf_Touch(dir);
1288
1289 out:
1290     UNLOCK_GLOBAL_MUTEX;
1291     return code;
1292 }
1293
1294 int
1295 afsconf_DeleteKeyBySubType(struct afsconf_dir *dir,
1296                            afsconf_keyType type, int kvno, int subType)
1297 {
1298     struct keyTypeList *typeEntry;
1299     struct kvnoList *kvnoEntry;
1300     struct subTypeList *subTypeEntry;
1301     int code;
1302
1303     LOCK_GLOBAL_MUTEX;
1304
1305     code = _afsconf_Check(dir);
1306     if (code)
1307         goto out;
1308
1309     typeEntry = findByType(dir, type);
1310     if (typeEntry == NULL)
1311         return AFSCONF_NOTFOUND;
1312
1313     kvnoEntry = findInTypeList(typeEntry, kvno);
1314     if (kvnoEntry == NULL)
1315         return AFSCONF_NOTFOUND;
1316
1317     subTypeEntry = findInKvnoList(kvnoEntry, subType);
1318     if (subTypeEntry == NULL)
1319         return AFSCONF_NOTFOUND;
1320
1321     /* Remove the subTypeEntry */
1322     afsconf_typedKey_put(&subTypeEntry->key);
1323     opr_queue_Remove(&subTypeEntry->link);
1324     free(subTypeEntry);
1325
1326     /* Remove the kvnoEntry, if it has no sub elements */
1327     if (opr_queue_IsEmpty(&kvnoEntry->subTypeList)) {
1328         opr_queue_Remove(&kvnoEntry->link);
1329         free(kvnoEntry);
1330     }
1331
1332     /* Remove the typeEntry, if it has no sub elements */
1333     if (opr_queue_IsEmpty(&typeEntry->kvnoList)) {
1334         opr_queue_Remove(&typeEntry->link);
1335         free(typeEntry);
1336     }
1337
1338     code = _afsconf_SaveKeys(dir);
1339     _afsconf_Touch(dir);
1340
1341 out:
1342     UNLOCK_GLOBAL_MUTEX;
1343     return code;
1344 }
1345
1346 int
1347 afsconf_DeleteTypedKey(struct afsconf_dir *dir, struct afsconf_typedKey *key)
1348 {
1349     return afsconf_DeleteKeyBySubType(dir, key->type, key->kvno, key->subType);
1350 }