DEVEL15-auth-cellconfig-overflow-20090601
[openafs.git] / src / auth / cellconfig.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID
14     ("$Header$");
15
16 #include <afs/stds.h>
17 #include <afs/pthread_glock.h>
18 #ifdef UKERNEL
19 #include "afs/sysincludes.h"
20 #include "afsincludes.h"
21 #include "des/des.h"
22 #include "rx/rxkad.h"
23 #include <netdb.h>
24 #else /* UKERNEL */
25 #include <sys/types.h>
26 #ifdef AFS_NT40_ENV
27 #include <winsock2.h>
28 #include <sys/utime.h>
29 #include <io.h>
30 #include <WINNT/afssw.h>
31 #else
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <netdb.h>
35 #include <sys/file.h>
36 #include <sys/time.h>
37 #ifdef AFS_AFSDB_ENV
38 #include <arpa/nameser.h>
39 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
40 #include <arpa/nameser_compat.h>
41 #endif
42 #include <resolv.h>
43 #endif /* AFS_AFSDB_ENV */
44 #endif /* AFS_NT40_ENV */
45 #include <afs/afsint.h>
46 #include <errno.h>
47 #include <ctype.h>
48 #include <time.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <sys/stat.h>
52 #include <fcntl.h>
53 #include <string.h>
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif
57 #include <rx/rxkad.h>
58 #include <rx/rx.h>
59 #endif /* UKERNEL */
60 #include <afs/afsutil.h>
61 #include "cellconfig.h"
62 #include "keys.h"
63 #ifdef AFS_NT40_ENV
64 #ifdef AFS_AFSDB_ENV
65 #include <cm.h>
66 #include <cm_config.h>
67 /* cm_dns.h depends on cellconfig.h */
68 #include <cm_nls.h>
69 #include <cm_dns.h>
70 #endif /* AFS_AFSDB_ENV */
71 #endif
72 #include <rx/rx.h>
73 #include <rx/rxkad.h>
74
75 static struct afsconf_servPair serviceTable[] = {
76     {"afs", 7000,},
77     {"afscb", 7001,},
78     {"afsprot", 7002,},
79     {"afsvldb", 7003,},
80     {"afskauth", 7004,},
81     {"afsvol", 7005,},
82     {"afserror", 7006,},
83     {"afsnanny", 7007,},
84     {"afsupdate", 7008,},
85     {"afsrmtsys", 7009,},
86     {"afsres", 7010,},          /* residency database for MR-AFS */
87     {"afsremio", 7011,},        /* remote I/O interface for MR-AFS */
88     {0, 0}                      /* insert new services before this spot */
89 };
90
91 /* Prototypes */
92 static afs_int32 afsconf_FindService(register const char *aname);
93 static int TrimLine(char *abuffer);
94 #ifdef AFS_NT40_ENV
95 static int IsClientConfigDirectory(const char *path);
96 static int GetCellNT(struct afsconf_dir *adir);
97 #endif
98 static int afsconf_Check(register struct afsconf_dir *adir);
99 static int afsconf_Touch(register struct afsconf_dir *adir);
100 static int GetCellUnix(struct afsconf_dir *adir);
101 static int afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
102                                 char clones[]);
103 static int ParseHostLine(char *aline, register struct sockaddr_in *addr,
104                          char *aname, char *aclone);
105 static int ParseCellLine(register char *aline, register char *aname,
106                          register char *alname);
107 static int afsconf_CloseInternal(register struct afsconf_dir *adir);
108 static int afsconf_Reopen(register struct afsconf_dir *adir);
109 static int SaveKeys(struct afsconf_dir *adir);
110
111 #ifndef T_AFSDB
112 #define T_AFSDB 18              /* per RFC1183 section 1 */
113 #endif
114
115 /*
116  * Basic Rule: we touch "<AFSCONF_DIR>/CellServDB" every time we change anything, so
117  * our code can tell if there is new info in the key files, the cell server db
118  * files or any of the other files (and reopen the thing) if the date on
119  * CellServDB changes.
120  */
121
122 #if defined(AFS_SUN5_ENV) && !defined(__sparcv9)
123 /* Solaris through 10 in 32 bit mode will return EMFILE if fopen can't
124    get an fd <= 255. We allow the fileserver to claim more fds than that.
125    This has always been a problem since pr_Initialize would have the same
126    issue, but hpr_Initialize makes it more likely that we would see this. 
127    Work around it. This is not generic. It's coded with the needs of
128    afsconf_* in mind only.
129
130    http://www.opensolaris.org/os/community/onnv/flag-days/pages/2006042001/
131 */
132
133 #define BUFFER 4096
134
135 struct afsconf_iobuffer {
136     int _file;
137     char *buffer;
138     char *ptr;
139     char *endptr;
140 };
141
142 typedef struct afsconf_iobuffer afsconf_FILE;
143
144 static afsconf_FILE *
145 afsconf_fopen(const char *fname, const char *fmode)
146 {
147     int fd;
148     afsconf_FILE *iop;
149     
150     if ((fd = open(fname, O_RDONLY)) == -1) {
151         return NULL;
152     }
153     
154     iop = malloc(sizeof(struct afsconf_iobuffer));
155     if (iop == NULL) {
156         (void) close(fd);
157         errno = ENOMEM;
158         return NULL;
159     }
160     iop->_file = fd;
161     iop->buffer = malloc(BUFFER);
162     if (iop->buffer == NULL) {
163         (void) close(fd);
164         free((void *) iop);
165         errno = ENOMEM;
166         return NULL;
167     }
168     iop->ptr = iop->buffer;
169     iop->endptr = iop->buffer;
170     return iop;
171 }
172
173 static int
174 afsconf_fclose(afsconf_FILE *iop)
175 {
176     if (iop == NULL) {
177         return 0;
178     }
179     close(iop->_file);
180     free((void *)iop->buffer);
181     free((void *)iop);
182     return 0;
183 }
184
185 static char *
186 afsconf_fgets(char *s, int n, afsconf_FILE *iop)
187 {
188     char *p;
189     
190     p = s;
191     for (;;) {
192         char c;
193         
194         if (iop->ptr == iop->endptr) {
195             ssize_t len;
196             
197             if ((len = read(iop->_file, (void *)iop->buffer, BUFFER)) == -1) {
198                 return NULL;
199             }
200             if (len == 0) {
201                 *p = 0;
202                 if (s == p) {
203                     return NULL;
204                 }
205                 return s;
206             }
207             iop->ptr = iop->buffer;
208             iop->endptr = iop->buffer + len;
209         }
210         c = *iop->ptr++;
211         *p++ = c;
212         if ((p - s) == (n - 1)) {
213             *p = 0;
214             return s;
215         }
216         if (c == '\n') {
217             *p = 0;
218             return s;
219         }
220     }
221 }
222 #define fopen afsconf_fopen
223 #define fclose afsconf_fclose
224 #define fgets afsconf_fgets
225 #else
226 #define afsconf_FILE FILE
227 #endif /* AFS_SUN5_ENV && ! __sparcv9 */
228
229 /* return port number in network byte order in the low 16 bits of a long; return -1 if not found */
230 static afs_int32
231 afsconf_FindService(register const char *aname)
232 {
233     /* lookup a service name */
234     struct servent *ts;
235     register struct afsconf_servPair *tsp;
236
237 #if     defined(AFS_OSF_ENV) 
238     ts = getservbyname(aname, "");
239 #else
240     ts = (struct servent *) getservbyname(aname, NULL);
241 #endif
242     if (ts) {
243         /* we found it in /etc/services, so we use this value */
244         return ts->s_port;      /* already in network byte order */
245     }
246
247     /* not found in /etc/services, see if it is one of ours */
248     for (tsp = serviceTable;; tsp++) {
249         if (tsp->name == NULL)
250             return -1;
251         if (!strcmp(tsp->name, aname))
252             return htons(tsp->port);
253     }
254 }
255
256 static int
257 TrimLine(char *abuffer)
258 {
259     char tbuffer[256];
260     register char *tp;
261     register int tc;
262
263     tp = abuffer;
264     while ((tc = *tp)) {
265         if (!isspace(tc))
266             break;
267         tp++;
268     }
269     strcpy(tbuffer, tp);
270     strcpy(abuffer, tbuffer);
271     return 0;
272 }
273
274 #ifdef AFS_NT40_ENV
275 /*
276  * IsClientConfigDirectory() -- determine if path matches well-known
277  *     client configuration directory.
278  */
279 static int
280 IsClientConfigDirectory(const char *path)
281 {
282     const char *cdir = AFSDIR_CLIENT_ETC_DIRPATH;
283     int i;
284
285     for (i = 0; cdir[i] != '\0' && path[i] != '\0'; i++) {
286         int cc = tolower(cdir[i]);
287         int pc = tolower(path[i]);
288
289         if (cc == '\\') {
290             cc = '/';
291         }
292         if (pc == '\\') {
293             pc = '/';
294         }
295         if (cc != pc) {
296             return 0;
297         }
298     }
299
300     /* hit end of one or both; allow mismatch in existence of trailing slash */
301     if (cdir[i] != '\0') {
302         if ((cdir[i] != '\\' && cdir[i] != '/') || (cdir[i + 1] != '\0')) {
303             return 0;
304         }
305     }
306     if (path[i] != '\0') {
307         if ((path[i] != '\\' && path[i] != '/') || (path[i + 1] != '\0')) {
308             return 0;
309         }
310     }
311     return 1;
312 }
313 #endif /* AFS_NT40_ENV */
314
315
316 static int
317 afsconf_Check(register struct afsconf_dir *adir)
318 {
319     char tbuffer[256];
320 #ifdef AFS_NT40_ENV
321     char *p;
322 #endif
323     struct stat tstat;
324     register afs_int32 code;
325
326 #ifdef AFS_NT40_ENV
327     /* NT client CellServDB has different file name than NT server or Unix */
328     if (IsClientConfigDirectory(adir->name)) {
329         if (!afssw_GetClientCellServDBDir(&p)) {
330             strcompose(tbuffer, sizeof(tbuffer), p, "/",
331                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
332             free(p);
333         } else {
334             int len;
335             strncpy(tbuffer, adir->name, sizeof(tbuffer));
336             len = (int)strlen(tbuffer);
337             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
338                 strncat(tbuffer, "\\", sizeof(tbuffer));
339             }
340             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
341                     sizeof(tbuffer));
342             tbuffer[sizeof(tbuffer) - 1] = '\0';
343         }
344     } else {
345         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
346                    NULL);
347     }
348 #else
349     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
350 #endif /* AFS_NT40_ENV */
351
352     code = stat(tbuffer, &tstat);
353     if (code < 0) {
354         return code;
355     }
356     /* did file change? */
357     if (tstat.st_mtime == adir->timeRead) {
358         return 0;
359     }
360     /* otherwise file has changed, so reopen it */
361     return afsconf_Reopen(adir);
362 }
363
364 /* set modtime on file */
365 static int
366 afsconf_Touch(register struct afsconf_dir *adir)
367 {
368     char tbuffer[256];
369 #ifndef AFS_NT40_ENV
370     struct timeval tvp[2];
371 #else
372     char *p;
373 #endif
374
375     adir->timeRead = 0;         /* just in case */
376
377 #ifdef AFS_NT40_ENV
378     /* NT client CellServDB has different file name than NT server or Unix */
379
380     if (IsClientConfigDirectory(adir->name)) {
381         if (!afssw_GetClientCellServDBDir(&p)) {
382             strcompose(tbuffer, sizeof(tbuffer), p, "/",
383                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
384             free(p);
385         } else {
386             int len = (int)strlen(tbuffer);
387             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
388                 strncat(tbuffer, "\\", sizeof(tbuffer));
389             }
390             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
391                     sizeof(tbuffer));
392             tbuffer[sizeof(tbuffer) - 1] = '\0';
393         }
394     } else {
395         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
396                    NULL);
397     }
398
399     return _utime(tbuffer, NULL);
400
401 #else
402     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
403     gettimeofday(&tvp[0], NULL);
404     tvp[1] = tvp[0];
405     return utimes(tbuffer, tvp);
406 #endif /* AFS_NT40_ENV */
407 }
408
409 struct afsconf_dir *
410 afsconf_Open(register const char *adir)
411 {
412     register struct afsconf_dir *tdir;
413     register afs_int32 code;
414
415     LOCK_GLOBAL_MUTEX;
416     /* zero structure and fill in name; rest is done by internal routine */
417     tdir = (struct afsconf_dir *)malloc(sizeof(struct afsconf_dir));
418     memset(tdir, 0, sizeof(struct afsconf_dir));
419     tdir->name = (char *)malloc(strlen(adir) + 1);
420     strcpy(tdir->name, adir);
421
422     code = afsconf_OpenInternal(tdir, 0, 0);
423     if (code) {
424         char *afsconf_path, afs_confdir[128];
425
426         free(tdir->name);
427         /* Check global place only when local Open failed for whatever reason */
428         if (!(afsconf_path = getenv("AFSCONF"))) {
429             /* The "AFSCONF" environment (or contents of "/.AFSCONF") will be typically set to something like "/afs/<cell>/common/etc" where, by convention, the default files for "ThisCell" and "CellServDB" will reside; note that a major drawback is that a given afs client on that cell may NOT contain the same contents... */
430             char *home_dir;
431             afsconf_FILE *fp;
432             size_t len;
433
434             if (!(home_dir = getenv("HOME"))) {
435                 /* Our last chance is the "/.AFSCONF" file */
436                 fp = fopen("/.AFSCONF", "r");
437                 if (fp == 0) {
438                     free(tdir);
439                     UNLOCK_GLOBAL_MUTEX;
440                     return (struct afsconf_dir *)0;
441                 }
442                 fgets(afs_confdir, 128, fp);
443                 fclose(fp);
444             } else {
445                 char pathname[256];
446
447                 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
448                 fp = fopen(pathname, "r");
449                 if (fp == 0) {
450                     /* Our last chance is the "/.AFSCONF" file */
451                     fp = fopen("/.AFSCONF", "r");
452                     if (fp == 0) {
453                         free(tdir);
454                         UNLOCK_GLOBAL_MUTEX;
455                         return (struct afsconf_dir *)0;
456                     }
457                 }
458                 fgets(afs_confdir, 128, fp);
459                 fclose(fp);
460             }
461             len = strlen(afs_confdir);
462             if (len == 0) {
463                 free(tdir);
464                 UNLOCK_GLOBAL_MUTEX;
465                 return (struct afsconf_dir *)0;
466             }
467             if (afs_confdir[len - 1] == '\n') {
468                 afs_confdir[len - 1] = 0;
469             }
470             afsconf_path = afs_confdir;
471         }
472         tdir->name = (char *)malloc(strlen(afsconf_path) + 1);
473         strcpy(tdir->name, afsconf_path);
474         code = afsconf_OpenInternal(tdir, 0, 0);
475         if (code) {
476             free(tdir->name);
477             free(tdir);
478             UNLOCK_GLOBAL_MUTEX;
479             return (struct afsconf_dir *)0;
480         }
481     }
482     UNLOCK_GLOBAL_MUTEX;
483     return tdir;
484 }
485
486 static int
487 GetCellUnix(struct afsconf_dir *adir)
488 {
489     char *rc;
490     char tbuffer[256];
491     char *start, *p;
492     afsconf_FILE *fp;
493     
494     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
495     fp = fopen(tbuffer, "r");
496     if (fp == 0) {
497         return -1;
498     }
499     rc = fgets(tbuffer, 256, fp);
500     fclose(fp);
501     if (rc == NULL)
502         return -1;
503
504     start = tbuffer;
505     while (*start != '\0' && isspace(*start))
506         start++;
507     p = start;
508     while (*p != '\0' && !isspace(*p))
509         p++;
510     *p = '\0';
511     if (*start == '\0')
512         return -1;
513
514     adir->cellName = strdup(start);
515     return 0;
516 }
517
518
519 #ifdef AFS_NT40_ENV
520 static int
521 GetCellNT(struct afsconf_dir *adir)
522 {
523     if (IsClientConfigDirectory(adir->name)) {
524         /* NT client config dir; ThisCell is in registry (no file). */
525         return afssw_GetClientCellName(&adir->cellName);
526     } else {
527         /* NT server config dir; works just like Unix */
528         return GetCellUnix(adir);
529     }
530 }
531
532 /* The following procedures and structs are used on Windows only
533  * to enumerate the Cell information distributed within the 
534  * Windows registry.  (See src/WINNT/afsd/cm_config.c)
535  */
536 typedef struct _cm_enumCellRegistry {
537     afs_uint32 client;  /* non-zero if client query */
538     struct afsconf_dir *adir;
539 } cm_enumCellRegistry_t;
540
541 static long
542 cm_serverConfigProc(void *rockp, struct sockaddr_in *addrp, 
543                     char *hostNamep, unsigned short rank)
544 {
545     struct afsconf_cell *cellInfop = (struct afsconf_cell *)rockp;
546
547     if (cellInfop->numServers == MAXHOSTSPERCELL)
548         return 0;
549
550     cellInfop->hostAddr[cellInfop->numServers] = *addrp;
551     strncpy(cellInfop->hostName[cellInfop->numServers], hostNamep, MAXHOSTCHARS);
552     cellInfop->hostName[cellInfop->numServers][MAXHOSTCHARS-1] = '\0';
553     cellInfop->numServers++;
554
555     return 0;
556 }
557
558 static long
559 cm_enumCellRegistryProc(void *rockp, char * cellNamep)
560 {
561     long code;
562     cm_enumCellRegistry_t *enump = (cm_enumCellRegistry_t *)rockp;
563     char linkedName[256] = "";
564     int timeout = 0;
565     struct afsconf_entry *newEntry;
566
567
568     newEntry = malloc(sizeof(struct afsconf_entry));
569     if (newEntry == NULL)
570         return ENOMEM;
571     newEntry->cellInfo.numServers = 0;
572
573     code = cm_SearchCellRegistry(enump->client, cellNamep, NULL, linkedName, cm_serverConfigProc, &newEntry->cellInfo);
574     if (code == CM_ERROR_FORCE_DNS_LOOKUP)
575         code = cm_SearchCellByDNS(cellNamep, NULL, &timeout, cm_serverConfigProc, &newEntry->cellInfo);
576
577     if (code == 0) {
578         strncpy(newEntry->cellInfo.name, cellNamep, MAXCELLCHARS);
579         newEntry->cellInfo.name[MAXCELLCHARS-1];
580         if (linkedName[0])
581             newEntry->cellInfo.linkedCell = strdup(linkedName);
582         else
583             newEntry->cellInfo.linkedCell = NULL;
584         newEntry->cellInfo.timeout = timeout;
585         newEntry->cellInfo.flags = 0;
586
587         newEntry->next = enump->adir->entries;
588         enump->adir->entries = newEntry;
589     } else {
590         free(newEntry);
591     }
592     return code;
593 }       
594 #endif /* AFS_NT40_ENV */
595
596
597 static int
598 afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
599                      char clones[])
600 {
601     afsconf_FILE *tf;
602     register char *tp, *bp;
603     register struct afsconf_entry *curEntry;
604     struct afsconf_aliasentry *curAlias;
605     register afs_int32 code;
606     afs_int32 i;
607     char tbuffer[256], tbuf1[256];
608     struct stat tstat;
609 #ifdef AFS_NT40_ENV
610     cm_enumCellRegistry_t enumCellRegistry = {0, 0};
611 #endif /* AFS_NT40_ENV */
612
613     /* figure out the local cell name */
614 #ifdef AFS_NT40_ENV
615     i = GetCellNT(adir);
616     enumCellRegistry.adir = adir;
617 #else
618     i = GetCellUnix(adir);
619 #endif
620
621 #ifndef AFS_FREELANCE_CLIENT    /* no local cell not fatal in freelance */
622     if (i) {
623         return i;
624     }
625 #endif
626
627     /* now parse the individual lines */
628     curEntry = 0;
629
630 #ifdef AFS_NT40_ENV
631     /* NT client/server have a CellServDB that is the same format as Unix.
632      * However, the NT client uses a different file name
633      */
634     if (IsClientConfigDirectory(adir->name)) {
635         /* NT client config dir */
636         char *p;
637
638         enumCellRegistry.client = 1;
639
640         if (!afssw_GetClientCellServDBDir(&p)) {
641             strcompose(tbuffer, sizeof(tbuffer), p, "/",
642                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
643             free(p);
644         } else {
645             int len;
646             strncpy(tbuffer, adir->name, sizeof(tbuffer));
647             len = (int)strlen(tbuffer);
648             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
649                 strncat(tbuffer, "\\", sizeof(tbuffer));
650             }
651             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
652                     sizeof(tbuffer));
653             tbuffer[sizeof(tbuffer) - 1] = '\0';
654         }
655     } else {
656         /* NT server config dir */
657         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
658                    NULL);
659     }
660 #else
661     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
662 #endif /* AFS_NT40_ENV */
663
664     if (!stat(tbuffer, &tstat)) {
665         adir->timeRead = tstat.st_mtime;
666     } else {
667         adir->timeRead = 0;
668     }
669
670     strcpy(tbuf1, tbuffer);
671     tf = fopen(tbuffer, "r");
672     if (!tf) {
673         return -1;
674     }
675
676     /* The CellServDB file is now open.  
677      * The following code parses the contents of the 
678      * file and creates a list with the first cell entry
679      * in the CellServDB file at the end of the list.
680      * 
681      * No checking is performed for duplicates.
682      * The side effects of this process are that duplicate
683      * entries appended to the end of the CellServDB file
684      * take precedence and are found in a shorter period 
685      * of time.
686      */
687
688     while (1) {
689         tp = fgets(tbuffer, sizeof(tbuffer), tf);
690         if (!tp)
691             break;
692         TrimLine(tbuffer);      /* remove white space */
693         if (tbuffer[0] == 0 || tbuffer[0] == '\n')
694             continue;           /* empty line */
695         if (tbuffer[0] == '>') {
696             char linkedcell[MAXCELLCHARS];
697             /* start new cell item */
698             if (curEntry) {
699                 /* thread this guy on the list */
700                 curEntry->next = adir->entries;
701                 adir->entries = curEntry;
702                 curEntry = 0;
703             }
704             curEntry =
705                 (struct afsconf_entry *)malloc(sizeof(struct afsconf_entry));
706             memset(curEntry, 0, sizeof(struct afsconf_entry));
707             code =
708                 ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
709             if (code) {
710                 afsconf_CloseInternal(adir);
711                 fclose(tf);
712                 free(curEntry);
713                 return -1;
714             }
715             if (linkedcell[0] != '\0') {
716                 curEntry->cellInfo.linkedCell =
717                     (char *)malloc(strlen(linkedcell) + 1);
718                 strcpy(curEntry->cellInfo.linkedCell, linkedcell);
719             }
720         } else {
721             /* new host in the current cell */
722             if (!curEntry) {
723                 afsconf_CloseInternal(adir);
724                 fclose(tf);
725                 return -1;
726             }
727             i = curEntry->cellInfo.numServers;
728             if (i < MAXHOSTSPERCELL) {
729                 if (cell && !strcmp(cell, curEntry->cellInfo.name))
730                     code =
731                         ParseHostLine(tbuffer, 
732                                       &curEntry->cellInfo.hostAddr[i],
733                                       curEntry->cellInfo.hostName[i], 
734                                       &clones[i]);
735                 else
736                     code =
737                         ParseHostLine(tbuffer, 
738                                       &curEntry->cellInfo.hostAddr[i],
739                                       curEntry->cellInfo.hostName[i], 0);
740
741                 if (code) {
742                     if (code == AFSCONF_SYNTAX) {
743                         for (bp = tbuffer; *bp != '\n'; bp++) { /* Take out the <cr> from the buffer */
744                             if (!*bp)
745                                 break;
746                         }
747                         *bp = '\0';
748                         fprintf(stderr,
749                                 "Can't properly parse host line \"%s\" in configuration file %s\n",
750                                 tbuffer, tbuf1);
751                     }
752                     free(curEntry);
753                     fclose(tf);
754                     afsconf_CloseInternal(adir);
755                     return -1;
756                 }
757                 curEntry->cellInfo.numServers = ++i;
758             } else {
759                 fprintf(stderr,
760                         "Too many hosts for cell %s in configuration file %s\n", 
761                         curEntry->cellInfo.name, tbuf1);
762             }
763         }
764     }
765     fclose(tf);                 /* close the file now */
766
767     /* end the last partially-completed cell */
768     if (curEntry) {
769         curEntry->next = adir->entries;
770         adir->entries = curEntry;
771     }
772
773 #ifdef AFS_NT40_ENV
774      /* 
775       * Windows maintains a CellServDB list in the Registry
776       * that supercedes the contents of the CellServDB file.
777       * Prepending these entries to the head of the list 
778       * is sufficient to enforce the precedence.
779       */
780      cm_EnumerateCellRegistry( enumCellRegistry.client,
781                                cm_enumCellRegistryProc,
782                                &enumCellRegistry);
783 #endif /* AFS_NT40_ENV */
784
785     /* Read in the alias list */
786     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLALIAS_FILE, NULL);
787
788     tf = fopen(tbuffer, "r");
789     while (tf) {
790         char *aliasPtr;
791
792         tp = fgets(tbuffer, sizeof(tbuffer), tf);
793         if (!tp)
794             break;
795         TrimLine(tbuffer);      /* remove white space */
796
797         if (tbuffer[0] == '\0' || tbuffer[0] == '\n' || tbuffer[0] == '#')
798             continue;           /* empty line */
799
800         tp = tbuffer;
801         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t')
802             tp++;
803         if (tp[0] == '\0')
804             continue;           /* invalid line */
805
806         while (tp[0] != '\0' && (tp[0] == ' ' || tp[0] == '\t'))
807             0[tp++] = '\0';
808         if (tp[0] == '\0')
809             continue;           /* invalid line */
810
811         aliasPtr = tp;
812         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t' && tp[0] != '\r'
813                && tp[0] != '\n')
814             tp++;
815         tp[0] = '\0';
816
817         curAlias = malloc(sizeof(*curAlias));
818         memset(curAlias, 0, sizeof(*curAlias));
819
820         strcpy(curAlias->aliasInfo.aliasName, aliasPtr);
821         strcpy(curAlias->aliasInfo.realName, tbuffer);
822
823         curAlias->next = adir->alias_entries;
824         adir->alias_entries = curAlias;
825     }
826
827     if (tf != NULL)
828         fclose(tf);
829     /* now read the fs keys, if possible */
830     adir->keystr = (struct afsconf_keys *)0;
831     afsconf_IntGetKeys(adir);
832
833     return 0;
834 }
835
836 /* parse a line of the form
837  *"128.2.1.3   #hostname" or
838  *"[128.2.1.3]  #hostname" for clones
839  * into the appropriate pieces.  
840  */
841 static int
842 ParseHostLine(char *aline, register struct sockaddr_in *addr, char *aname,
843               char *aclone)
844 {
845     int c1, c2, c3, c4;
846     register afs_int32 code;
847     register char *tp;
848
849     if (*aline == '[') {
850         if (aclone)
851             *aclone = 1;
852         /* FIXME: length of aname unknown here */
853         code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
854     } else {
855         if (aclone)
856             *aclone = 0;
857         /* FIXME: length of aname unknown here */
858         code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
859     }
860     if (code != 5)
861         return AFSCONF_SYNTAX;
862     addr->sin_family = AF_INET;
863     addr->sin_port = 0;
864 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
865     addr->sin_len = sizeof(struct sockaddr_in);
866 #endif
867     tp = (char *)&addr->sin_addr;
868     *tp++ = c1;
869     *tp++ = c2;
870     *tp++ = c3;
871     *tp++ = c4;
872     return 0;
873 }
874
875 /* parse a line of the form
876  * ">cellname [linkedcellname] [#comments]"
877  * into the appropriate pieces.
878  */
879 static int
880 ParseCellLine(register char *aline, register char *aname,
881               register char *alname)
882 {
883     register int code;
884     /* FIXME: length of aname, alname unknown here */
885     code = sscanf(aline, ">%s %s", aname, alname);
886     if (code == 1)
887         *alname = '\0';
888     if (code == 2) {
889         if (*alname == '#') {
890             *alname = '\0';
891         }
892     }
893     return (code > 0 ? 0 : AFSCONF_SYNTAX);
894 }
895
896 /* call aproc(entry, arock, adir) for all cells.  Proc must return 0, or we'll stop early and return the code it returns */
897 int
898 afsconf_CellApply(struct afsconf_dir *adir,
899                   int (*aproc) (struct afsconf_cell * cell, void *arock,
900                                 struct afsconf_dir * dir), void *arock)
901 {
902     register struct afsconf_entry *tde;
903     register afs_int32 code;
904     LOCK_GLOBAL_MUTEX;
905     for (tde = adir->entries; tde; tde = tde->next) {
906         code = (*aproc) (&tde->cellInfo, arock, adir);
907         if (code) {
908             UNLOCK_GLOBAL_MUTEX;
909             return code;
910         }
911     }
912     UNLOCK_GLOBAL_MUTEX;
913     return 0;
914 }
915
916 /* call aproc(entry, arock, adir) for all cell aliases.
917  * Proc must return 0, or we'll stop early and return the code it returns
918  */
919 int
920 afsconf_CellAliasApply(struct afsconf_dir *adir,
921                        int (*aproc) (struct afsconf_cellalias * alias,
922                                      void *arock, struct afsconf_dir * dir),
923                        void *arock)
924 {
925     register struct afsconf_aliasentry *tde;
926     register afs_int32 code;
927     LOCK_GLOBAL_MUTEX;
928     for (tde = adir->alias_entries; tde; tde = tde->next) {
929         code = (*aproc) (&tde->aliasInfo, arock, adir);
930         if (code) {
931             UNLOCK_GLOBAL_MUTEX;
932             return code;
933         }
934     }
935     UNLOCK_GLOBAL_MUTEX;
936     return 0;
937 }
938
939 afs_int32 afsconf_SawCell = 0;
940
941 int
942 afsconf_GetExtendedCellInfo(struct afsconf_dir *adir, char *acellName,
943                             char *aservice, struct afsconf_cell *acellInfo,
944                             char clones[])
945 {
946     afs_int32 code;
947     char *cell;
948
949     code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
950     if (code)
951         return code;
952
953     if (acellName)
954         cell = acellName;
955     else
956         cell = (char *)&acellInfo->name;
957
958     code = afsconf_OpenInternal(adir, cell, clones);
959     return code;
960 }
961
962 #ifdef AFS_AFSDB_ENV
963 #if !defined(AFS_NT40_ENV)
964 int
965 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
966                      struct afsconf_cell *acellInfo)
967 {
968     afs_int32 code;
969     int tservice, i, len;
970     unsigned char answer[1024];
971     unsigned char *p;
972     char *dotcellname;
973     int cellnamelength;
974     char realCellName[256];
975     char host[256];
976     int server_num = 0;
977     int minttl = 0;
978     int try_init = 0;
979
980     /* The resolver isn't always MT-safe.. Perhaps this ought to be
981      * replaced with a more fine-grained lock just for the resolver
982      * operations.
983      */
984
985  retryafsdb:
986     if ( ! strchr(acellName,'.') ) {
987        cellnamelength=strlen(acellName);
988        dotcellname=malloc(cellnamelength+2);
989        memcpy(dotcellname,acellName,cellnamelength);
990        dotcellname[cellnamelength]='.';
991        dotcellname[cellnamelength+1]=0;
992        LOCK_GLOBAL_MUTEX;
993        len = res_search(dotcellname, C_IN, T_AFSDB, answer, sizeof(answer));
994        if ( len < 0 ) {
995           len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
996        }
997        UNLOCK_GLOBAL_MUTEX;
998        free(dotcellname);
999     } else {
1000        LOCK_GLOBAL_MUTEX;
1001        len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
1002        UNLOCK_GLOBAL_MUTEX;
1003     }
1004     if (len < 0) {
1005         if (try_init < 1) {
1006             try_init++;
1007             res_init();
1008             goto retryafsdb;
1009         }
1010         return AFSCONF_NOTFOUND;
1011     }
1012
1013     p = answer + sizeof(HEADER);        /* Skip header */
1014     code = dn_expand(answer, answer + len, p, host, sizeof(host));
1015     if (code < 0)
1016         return AFSCONF_NOTFOUND;
1017
1018     p += code + QFIXEDSZ;       /* Skip name */
1019
1020     while (p < answer + len) {
1021         int type, ttl, size;
1022
1023         code = dn_expand(answer, answer + len, p, host, sizeof(host));
1024         if (code < 0)
1025             return AFSCONF_NOTFOUND;
1026
1027         p += code;              /* Skip the name */
1028         type = (p[0] << 8) | p[1];
1029         p += 4;                 /* Skip type and class */
1030         ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
1031         p += 4;                 /* Skip the TTL */
1032         size = (p[0] << 8) | p[1];
1033         p += 2;                 /* Skip the size */
1034
1035         if (type == T_AFSDB) {
1036             struct hostent *he;
1037             short afsdb_type;
1038
1039             afsdb_type = (p[0] << 8) | p[1];
1040             if (afsdb_type == 1) {
1041                 /*
1042                  * We know this is an AFSDB record for our cell, of the
1043                  * right AFSDB type.  Write down the true cell name that
1044                  * the resolver gave us above.
1045                  */
1046                 strcpy(realCellName, host);
1047             }
1048
1049             code = dn_expand(answer, answer + len, p + 2, host, sizeof(host));
1050             if (code < 0)
1051                 return AFSCONF_NOTFOUND;
1052
1053             if ((afsdb_type == 1) && (server_num < MAXHOSTSPERCELL) &&
1054                 /* Do we want to get TTL data for the A record as well? */
1055                 (he = gethostbyname(host))) {
1056                 afs_int32 ipaddr;
1057                 memcpy(&ipaddr, he->h_addr, he->h_length);
1058                 acellInfo->hostAddr[server_num].sin_addr.s_addr = ipaddr;
1059                 strncpy(acellInfo->hostName[server_num], host,
1060                         sizeof(acellInfo->hostName[server_num]));
1061                 server_num++;
1062
1063                 if (!minttl || ttl < minttl)
1064                     minttl = ttl;
1065             }
1066         }
1067
1068         p += size;
1069     }
1070
1071     if (server_num == 0)        /* No AFSDB records */
1072         return AFSCONF_NOTFOUND;
1073
1074     /* Convert the real cell name to lowercase */
1075     for (p = (unsigned char *)realCellName; *p; p++)
1076         *p = tolower(*p);
1077
1078     strncpy(acellInfo->name, realCellName, sizeof(acellInfo->name));
1079     acellInfo->numServers = server_num;
1080
1081     if (aservice) {
1082         tservice = afsconf_FindService(aservice);
1083         if (tservice < 0)
1084             return AFSCONF_NOTFOUND;    /* service not found */
1085         for (i = 0; i < acellInfo->numServers; i++) {
1086             acellInfo->hostAddr[i].sin_port = tservice;
1087         }
1088     }
1089
1090     acellInfo->timeout = minttl ? (time(0) + minttl) : 0;
1091
1092     return 0;
1093 }
1094 #else /* windows */
1095 int
1096 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
1097                      struct afsconf_cell *acellInfo)
1098 {
1099     register afs_int32 i;
1100     int tservice;
1101     struct afsconf_entry DNSce;
1102     afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
1103     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
1104     unsigned short ipRanks[AFSMAXCELLHOSTS];
1105     int numServers;
1106     int rc;
1107     int ttl;
1108
1109     DNSce.cellInfo.numServers = 0;
1110     DNSce.next = NULL;
1111     rc = getAFSServer(acellName, cellHostAddrs, cellHostNames, ipRanks, &numServers,
1112                       &ttl);
1113     /* ignore the ttl here since this code is only called by transitory programs
1114      * like klog, etc. */
1115     if (rc < 0)
1116         return -1;
1117     if (numServers == 0)
1118         return -1;
1119
1120     for (i = 0; i < numServers; i++) {
1121         memcpy(&acellInfo->hostAddr[i].sin_addr.s_addr, &cellHostAddrs[i],
1122                sizeof(long));
1123         memcpy(acellInfo->hostName[i], cellHostNames[i], MAXHOSTCHARS);
1124         acellInfo->hostAddr[i].sin_family = AF_INET;
1125
1126         /* sin_port supplied by connection code */
1127     }
1128
1129     acellInfo->numServers = numServers;
1130     strcpy(acellInfo->name, acellName);
1131     if (aservice) {
1132         LOCK_GLOBAL_MUTEX;
1133         tservice = afsconf_FindService(aservice);
1134         UNLOCK_GLOBAL_MUTEX;
1135         if (tservice < 0) {
1136             return AFSCONF_NOTFOUND;    /* service not found */
1137         }
1138         for (i = 0; i < acellInfo->numServers; i++) {
1139             acellInfo->hostAddr[i].sin_port = tservice;
1140         }
1141     }
1142     acellInfo->linkedCell = NULL;       /* no linked cell */
1143     acellInfo->flags = 0;
1144     return 0;
1145 }
1146 #endif /* windows */
1147 #endif /* AFS_AFSDB_ENV */
1148
1149 int
1150 afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
1151                     struct afsconf_cell *acellInfo)
1152 {
1153     register struct afsconf_entry *tce;
1154     struct afsconf_aliasentry *tcae;
1155     struct afsconf_entry *bestce;
1156     register afs_int32 i;
1157     int tservice;
1158     char *tcell;
1159     int cnLen;
1160     int ambig;
1161     char tbuffer[64];
1162
1163     LOCK_GLOBAL_MUTEX;
1164     if (adir)
1165         afsconf_Check(adir);
1166     if (acellName) {
1167         tcell = acellName;
1168         cnLen = (int)(strlen(tcell) + 1);
1169         lcstring(tcell, tcell, cnLen);
1170         afsconf_SawCell = 1;    /* will ignore the AFSCELL switch on future */
1171         /* call to afsconf_GetLocalCell: like klog  */
1172     } else {
1173         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
1174         if (i) {
1175             UNLOCK_GLOBAL_MUTEX;
1176             return i;
1177         }
1178         tcell = tbuffer;
1179     }
1180     cnLen = strlen(tcell);
1181     bestce = (struct afsconf_entry *)0;
1182     ambig = 0;
1183     if (!adir) {
1184         UNLOCK_GLOBAL_MUTEX;
1185         return 0;
1186     }
1187
1188     /* Look through the list of aliases */
1189     for (tcae = adir->alias_entries; tcae; tcae = tcae->next) {
1190         if (strcasecmp(tcae->aliasInfo.aliasName, tcell) == 0) {
1191             tcell = tcae->aliasInfo.realName;
1192             break;
1193         }
1194     }
1195
1196     for (tce = adir->entries; tce; tce = tce->next) {
1197         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
1198             /* found our cell */
1199             bestce = tce;
1200             ambig = 0;
1201             break;
1202         }
1203         if (strlen(tce->cellInfo.name) < cnLen)
1204             continue;           /* clearly wrong */
1205         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
1206             if (bestce)
1207                 ambig = 1;      /* ambiguous unless we get exact match */
1208             bestce = tce;
1209         }
1210     }
1211     if (!ambig && bestce && bestce->cellInfo.numServers) {
1212         *acellInfo = bestce->cellInfo;  /* structure assignment */
1213         if (aservice) {
1214             tservice = afsconf_FindService(aservice);
1215             if (tservice < 0) {
1216                 UNLOCK_GLOBAL_MUTEX;
1217                 return AFSCONF_NOTFOUND;        /* service not found */
1218             }
1219             for (i = 0; i < acellInfo->numServers; i++) {
1220                 acellInfo->hostAddr[i].sin_port = tservice;
1221             }
1222         }
1223         acellInfo->timeout = 0;
1224         UNLOCK_GLOBAL_MUTEX;
1225         return 0;
1226     } else {
1227         UNLOCK_GLOBAL_MUTEX;
1228 #ifdef AFS_AFSDB_ENV
1229         return afsconf_GetAfsdbInfo(tcell, aservice, acellInfo);
1230 #else
1231         return AFSCONF_NOTFOUND;
1232 #endif /* AFS_AFSDB_ENV */
1233     }
1234 }
1235
1236 int
1237 afsconf_GetLocalCell(register struct afsconf_dir *adir, char *aname,
1238                      afs_int32 alen)
1239 {
1240     static int afsconf_showcell = 0;
1241     char *afscell_path;
1242     afs_int32 code = 0;
1243
1244     LOCK_GLOBAL_MUTEX;
1245     /*
1246      * If a cell switch was specified in a command, then it should override the 
1247      * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
1248      * is set and the cell name in the adir structure is used.
1249      * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
1250      */
1251     if (!afsconf_SawCell && (afscell_path = getenv("AFSCELL"))) {
1252         if (!afsconf_showcell) {
1253             fprintf(stderr, "Note: Operation is performed on cell %s\n",
1254                     afscell_path);
1255             afsconf_showcell = 1;
1256         }
1257         strncpy(aname, afscell_path, alen);
1258     } else {
1259         afsconf_Check(adir);
1260         if (adir->cellName) {
1261             strncpy(aname, adir->cellName, alen);
1262         } else
1263             code = AFSCONF_UNKNOWN;
1264     }
1265
1266     UNLOCK_GLOBAL_MUTEX;
1267     return (code);
1268 }
1269
1270 int
1271 afsconf_Close(struct afsconf_dir *adir)
1272 {
1273     LOCK_GLOBAL_MUTEX;
1274     afsconf_CloseInternal(adir);
1275     if (adir->name)
1276         free(adir->name);
1277     free(adir);
1278     UNLOCK_GLOBAL_MUTEX;
1279     return 0;
1280 }
1281
1282 static int
1283 afsconf_CloseInternal(register struct afsconf_dir *adir)
1284 {
1285     register struct afsconf_entry *td, *nd;
1286     struct afsconf_aliasentry *ta, *na;
1287     register char *tname;
1288
1289     tname = adir->name;         /* remember name, since that's all we preserve */
1290
1291     /* free everything we can find */
1292     if (adir->cellName)
1293         free(adir->cellName);
1294     for (td = adir->entries; td; td = nd) {
1295         nd = td->next;
1296         if (td->cellInfo.linkedCell)
1297             free(td->cellInfo.linkedCell);
1298         free(td);
1299     }
1300     for (ta = adir->alias_entries; ta; ta = na) {
1301         na = ta->next;
1302         free(ta);
1303     }
1304     if (adir->keystr)
1305         free(adir->keystr);
1306
1307     /* reinit */
1308     memset(adir, 0, sizeof(struct afsconf_dir));
1309     adir->name = tname;         /* restore it */
1310     return 0;
1311 }
1312
1313 static int
1314 afsconf_Reopen(register struct afsconf_dir *adir)
1315 {
1316     register afs_int32 code;
1317     code = afsconf_CloseInternal(adir);
1318     if (code)
1319         return code;
1320     code = afsconf_OpenInternal(adir, 0, 0);
1321     return code;
1322 }
1323
1324 /* called during opening of config file */
1325 int
1326 afsconf_IntGetKeys(struct afsconf_dir *adir)
1327 {
1328     char tbuffer[256];
1329     register int fd;
1330     struct afsconf_keys *tstr;
1331     register afs_int32 code;
1332
1333 #ifdef AFS_NT40_ENV
1334     /* NT client config dir has no KeyFile; don't risk attempting open
1335      * because there might be a random file of this name if dir is shared.
1336      */
1337     if (IsClientConfigDirectory(adir->name)) {
1338         adir->keystr = ((struct afsconf_keys *)
1339                         malloc(sizeof(struct afsconf_keys)));
1340         adir->keystr->nkeys = 0;
1341         return 0;
1342     }
1343 #endif /* AFS_NT40_ENV */
1344
1345     LOCK_GLOBAL_MUTEX;
1346     /* compute the key name and other setup */
1347     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1348     tstr = (struct afsconf_keys *)malloc(sizeof(struct afsconf_keys));
1349     adir->keystr = tstr;
1350
1351     /* read key file */
1352     fd = open(tbuffer, O_RDONLY);
1353     if (fd < 0) {
1354         tstr->nkeys = 0;
1355         UNLOCK_GLOBAL_MUTEX;
1356         return 0;
1357     }
1358     code = read(fd, tstr, sizeof(struct afsconf_keys));
1359     close(fd);
1360     if (code < sizeof(afs_int32)) {
1361         tstr->nkeys = 0;
1362         UNLOCK_GLOBAL_MUTEX;
1363         return 0;
1364     }
1365
1366     /* convert key structure to host order */
1367     tstr->nkeys = ntohl(tstr->nkeys);
1368
1369     if (code < sizeof(afs_int32) + (tstr->nkeys*sizeof(struct afsconf_key))) {
1370         tstr->nkeys = 0;
1371         UNLOCK_GLOBAL_MUTEX;
1372         return 0;
1373     }
1374
1375     for (fd = 0; fd < tstr->nkeys; fd++)
1376         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
1377
1378     UNLOCK_GLOBAL_MUTEX;
1379     return 0;
1380 }
1381
1382 /* get keys structure */
1383 int
1384 afsconf_GetKeys(struct afsconf_dir *adir, struct afsconf_keys *astr)
1385 {
1386     register afs_int32 code;
1387
1388     LOCK_GLOBAL_MUTEX;
1389     code = afsconf_Check(adir);
1390     if (code) {
1391         UNLOCK_GLOBAL_MUTEX;
1392         return AFSCONF_FAILURE;
1393     }
1394     memcpy(astr, adir->keystr, sizeof(struct afsconf_keys));
1395     UNLOCK_GLOBAL_MUTEX;
1396     return 0;
1397 }
1398
1399 /* get latest key */
1400 afs_int32
1401 afsconf_GetLatestKey(struct afsconf_dir * adir, afs_int32 * avno, 
1402                      struct ktc_encryptionKey *akey)
1403 {
1404     register int i;
1405     int maxa;
1406     register struct afsconf_key *tk;
1407     register afs_int32 best;
1408     struct afsconf_key *bestk;
1409     register afs_int32 code;
1410
1411     LOCK_GLOBAL_MUTEX;
1412     code = afsconf_Check(adir);
1413     if (code) {
1414         UNLOCK_GLOBAL_MUTEX;
1415         return AFSCONF_FAILURE;
1416     }
1417     maxa = adir->keystr->nkeys;
1418
1419     best = -1;                  /* highest kvno we've seen yet */
1420     bestk = (struct afsconf_key *)0;    /* ptr to structure providing best */
1421     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1422         if (tk->kvno == 999)
1423             continue;           /* skip bcrypt keys */
1424         if (tk->kvno > best) {
1425             best = tk->kvno;
1426             bestk = tk;
1427         }
1428     }
1429     if (bestk) {                /* found any  */
1430         if (akey)
1431             memcpy(akey, bestk->key, 8);        /* copy out latest key */
1432         if (avno)
1433             *avno = bestk->kvno;        /* and kvno to caller */
1434         UNLOCK_GLOBAL_MUTEX;
1435         return 0;
1436     }
1437     UNLOCK_GLOBAL_MUTEX;
1438     return AFSCONF_NOTFOUND;    /* didn't find any keys */
1439 }
1440
1441 /* get a particular key */
1442 int
1443 afsconf_GetKey(void *rock, int avno, struct ktc_encryptionKey *akey)
1444 {
1445     struct afsconf_dir *adir = (struct afsconf_dir *) rock;
1446     register int i, maxa;
1447     register struct afsconf_key *tk;
1448     register afs_int32 code;
1449
1450     LOCK_GLOBAL_MUTEX;
1451     code = afsconf_Check(adir);
1452     if (code) {
1453         UNLOCK_GLOBAL_MUTEX;
1454         return AFSCONF_FAILURE;
1455     }
1456     maxa = adir->keystr->nkeys;
1457
1458     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1459         if (tk->kvno == avno) {
1460             memcpy(akey, tk->key, 8);
1461             UNLOCK_GLOBAL_MUTEX;
1462             return 0;
1463         }
1464     }
1465
1466     UNLOCK_GLOBAL_MUTEX;
1467     return AFSCONF_NOTFOUND;
1468 }
1469
1470 /* save the key structure in the appropriate file */
1471 static int
1472 SaveKeys(struct afsconf_dir *adir)
1473 {
1474     struct afsconf_keys tkeys;
1475     register int fd;
1476     register afs_int32 i;
1477     char tbuffer[256];
1478
1479     memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys));
1480
1481     /* convert it to net byte order */
1482     for (i = 0; i < tkeys.nkeys; i++)
1483         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
1484     tkeys.nkeys = htonl(tkeys.nkeys);
1485
1486     /* rewrite keys file */
1487     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1488     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
1489     if (fd < 0)
1490         return AFSCONF_FAILURE;
1491     i = write(fd, &tkeys, sizeof(tkeys));
1492     if (i != sizeof(tkeys)) {
1493         close(fd);
1494         return AFSCONF_FAILURE;
1495     }
1496     if (close(fd) < 0)
1497         return AFSCONF_FAILURE;
1498     return 0;
1499 }
1500
1501 int
1502 afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8],
1503                afs_int32 overwrite)
1504 {
1505     register struct afsconf_keys *tk;
1506     register struct afsconf_key *tkey;
1507     register afs_int32 i;
1508     int foundSlot;
1509
1510     LOCK_GLOBAL_MUTEX;
1511     tk = adir->keystr;
1512
1513     if (akvno != 999) {
1514         if (akvno < 0 || akvno > 255) {
1515             UNLOCK_GLOBAL_MUTEX;
1516             return ERANGE;
1517         }
1518     }
1519     foundSlot = 0;
1520     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1521         if (tkey->kvno == akvno) {
1522             if (!overwrite) {
1523                 UNLOCK_GLOBAL_MUTEX;
1524                 return AFSCONF_KEYINUSE;
1525             }
1526             foundSlot = 1;
1527             break;
1528         }
1529     }
1530     if (!foundSlot) {
1531         if (tk->nkeys >= AFSCONF_MAXKEYS) {
1532             UNLOCK_GLOBAL_MUTEX;
1533             return AFSCONF_FULL;
1534         }
1535         tkey = &tk->key[tk->nkeys++];
1536     }
1537     tkey->kvno = akvno;
1538     memcpy(tkey->key, akey, 8);
1539     i = SaveKeys(adir);
1540     afsconf_Touch(adir);
1541     UNLOCK_GLOBAL_MUTEX;
1542     return i;
1543 }
1544
1545 /* this proc works by sliding the other guys down, rather than using a funny
1546     kvno value, so that callers can count on getting a good key in key[0].
1547 */
1548 int
1549 afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno)
1550 {
1551     register struct afsconf_keys *tk;
1552     register struct afsconf_key *tkey;
1553     register int i;
1554     int foundFlag = 0;
1555
1556     LOCK_GLOBAL_MUTEX;
1557     tk = adir->keystr;
1558
1559     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1560         if (tkey->kvno == akvno) {
1561             foundFlag = 1;
1562             break;
1563         }
1564     }
1565     if (!foundFlag) {
1566         UNLOCK_GLOBAL_MUTEX;
1567         return AFSCONF_NOTFOUND;
1568     }
1569
1570     /* otherwise slide the others down.  i and tkey point at the guy to delete */
1571     for (; i < tk->nkeys - 1; i++, tkey++) {
1572         tkey->kvno = (tkey + 1)->kvno;
1573         memcpy(tkey->key, (tkey + 1)->key, 8);
1574     }
1575     tk->nkeys--;
1576     i = SaveKeys(adir);
1577     afsconf_Touch(adir);
1578     UNLOCK_GLOBAL_MUTEX;
1579     return i;
1580 }