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