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