Add system headers
[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
14 #include <afs/stds.h>
15 #include <afs/pthread_glock.h>
16 #ifdef UKERNEL
17 #include "afs/sysincludes.h"
18 #include "afsincludes.h"
19 #include "des/des.h"
20 #include "rx/rxkad.h"
21 #include <netdb.h>
22 #include <ctype.h>
23 #else /* UKERNEL */
24 #include <sys/types.h>
25 #ifdef AFS_NT40_ENV
26 #include <winsock2.h>
27 #include <sys/utime.h>
28 #include <io.h>
29 #include <WINNT/afssw.h>
30 #else
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <netdb.h>
34 #include <sys/file.h>
35 #include <sys/time.h>
36 #include <ctype.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, int abufsize);
94 static int IsClientConfigDirectory(const char *path);
95 #ifdef AFS_NT40_ENV
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, int abufsize)
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     strlcpy(tbuffer, tp, sizeof tbuffer);
270     strlcpy(abuffer, tbuffer, abufsize);
271     return 0;
272 }
273
274 /*
275  * IsClientConfigDirectory() -- determine if path matches well-known
276  *     client configuration directory.
277  */
278 #ifdef AFS_NT40_ENV
279 #define IS_SEP(x) ((x) == '\\' || (x) == '/')
280 #else /* AFS_NT40_ENV */
281 #define IS_SEP(x) ((x) == '/')
282 #endif /* AFS_NT40_ENV */
283 static int
284 IsClientConfigDirectory(const char *path)
285 {
286     const char *cdir = AFSDIR_CLIENT_ETC_DIRPATH;
287     int i, cc, pc;
288
289     for (i = 0; cdir[i] != '\0' && path[i] != '\0'; i++) {
290 #ifdef AFS_NT40_ENV
291         cc = tolower(cdir[i]);
292         pc = tolower(path[i]);
293
294         if (cc == '\\') {
295             cc = '/';
296         }
297         if (pc == '\\') {
298             pc = '/';
299         }
300 #else /* AFS_NT40_ENV */
301         cc = cdir[i];
302         pc = path[i];
303 #endif /* AFS_NT40_ENV */
304         if (cc != pc) {
305             return 0;
306         }
307     }
308
309     /* hit end of one or both; allow mismatch in existence of trailing slash */
310     if (cdir[i] != '\0') {
311         if (!IS_SEP(cdir[i]) || (cdir[i + 1] != '\0')) {
312             return 0;
313         }
314     }
315     if (path[i] != '\0') {
316         if (!IS_SEP(path[i]) || (path[i + 1] != '\0')) {
317             return 0;
318         }
319     }
320     return 1;
321 }
322
323
324 static int
325 afsconf_Check(register struct afsconf_dir *adir)
326 {
327     char tbuffer[256];
328 #ifdef AFS_NT40_ENV
329     char *p;
330 #endif
331     struct stat tstat;
332     register afs_int32 code;
333
334 #ifdef AFS_NT40_ENV
335     /* NT client CellServDB has different file name than NT server or Unix */
336     if (IsClientConfigDirectory(adir->name)) {
337         if (!afssw_GetClientCellServDBDir(&p)) {
338             strcompose(tbuffer, sizeof(tbuffer), p, "/",
339                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
340             free(p);
341         } else {
342             int len;
343             strncpy(tbuffer, adir->name, sizeof(tbuffer));
344             len = (int)strlen(tbuffer);
345             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
346                 strncat(tbuffer, "\\", sizeof(tbuffer));
347             }
348             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
349                     sizeof(tbuffer));
350             tbuffer[sizeof(tbuffer) - 1] = '\0';
351         }
352     } else {
353         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
354                    NULL);
355     }
356 #else
357     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
358 #endif /* AFS_NT40_ENV */
359
360     code = stat(tbuffer, &tstat);
361     if (code < 0) {
362         return code;
363     }
364     /* did file change? */
365     if (tstat.st_mtime == adir->timeRead) {
366         return 0;
367     }
368     /* otherwise file has changed, so reopen it */
369     return afsconf_Reopen(adir);
370 }
371
372 /* set modtime on file */
373 static int
374 afsconf_Touch(register struct afsconf_dir *adir)
375 {
376     char tbuffer[256];
377 #ifndef AFS_NT40_ENV
378     struct timeval tvp[2];
379 #else
380     char *p;
381 #endif
382
383     adir->timeRead = 0;         /* just in case */
384
385 #ifdef AFS_NT40_ENV
386     /* NT client CellServDB has different file name than NT server or Unix */
387
388     if (IsClientConfigDirectory(adir->name)) {
389         if (!afssw_GetClientCellServDBDir(&p)) {
390             strcompose(tbuffer, sizeof(tbuffer), p, "/",
391                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
392             free(p);
393         } else {
394             int len = (int)strlen(tbuffer);
395             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
396                 strncat(tbuffer, "\\", sizeof(tbuffer));
397             }
398             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
399                     sizeof(tbuffer));
400             tbuffer[sizeof(tbuffer) - 1] = '\0';
401         }
402     } else {
403         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
404                    NULL);
405     }
406
407     return _utime(tbuffer, NULL);
408
409 #else
410     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
411     gettimeofday(&tvp[0], NULL);
412     tvp[1] = tvp[0];
413     return utimes(tbuffer, tvp);
414 #endif /* AFS_NT40_ENV */
415 }
416
417 struct afsconf_dir *
418 afsconf_Open(register const char *adir)
419 {
420     register struct afsconf_dir *tdir;
421     register afs_int32 code;
422
423     LOCK_GLOBAL_MUTEX;
424     /* zero structure and fill in name; rest is done by internal routine */
425     tdir = (struct afsconf_dir *)malloc(sizeof(struct afsconf_dir));
426     memset(tdir, 0, sizeof(struct afsconf_dir));
427     tdir->name = strdup(adir);
428
429     code = afsconf_OpenInternal(tdir, 0, 0);
430     if (code) {
431         char *afsconf_path, afs_confdir[128];
432
433         free(tdir->name);
434         /* Check global place only when local Open failed for whatever reason */
435         if (!(afsconf_path = getenv("AFSCONF"))) {
436             /* 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... */
437             char *home_dir;
438             afsconf_FILE *fp;
439             size_t len;
440
441             if (!(home_dir = getenv("HOME"))) {
442                 /* Our last chance is the "/.AFSCONF" file */
443                 fp = fopen("/.AFSCONF", "r");
444                 if (fp == 0) {
445                     free(tdir);
446                     UNLOCK_GLOBAL_MUTEX;
447                     return (struct afsconf_dir *)0;
448                 }
449                 fgets(afs_confdir, 128, fp);
450                 fclose(fp);
451             } else {
452                 char pathname[256];
453
454                 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
455                 fp = fopen(pathname, "r");
456                 if (fp == 0) {
457                     /* Our last chance is the "/.AFSCONF" file */
458                     fp = fopen("/.AFSCONF", "r");
459                     if (fp == 0) {
460                         free(tdir);
461                         UNLOCK_GLOBAL_MUTEX;
462                         return (struct afsconf_dir *)0;
463                     }
464                 }
465                 fgets(afs_confdir, 128, fp);
466                 fclose(fp);
467             }
468             len = strlen(afs_confdir);
469             if (len == 0) {
470                 free(tdir);
471                 UNLOCK_GLOBAL_MUTEX;
472                 return (struct afsconf_dir *)0;
473             }
474             if (afs_confdir[len - 1] == '\n') {
475                 afs_confdir[len - 1] = 0;
476             }
477             afsconf_path = afs_confdir;
478         }
479         tdir->name = strdup(afsconf_path);
480         code = afsconf_OpenInternal(tdir, 0, 0);
481         if (code) {
482             free(tdir->name);
483             free(tdir);
484             UNLOCK_GLOBAL_MUTEX;
485             return (struct afsconf_dir *)0;
486         }
487     }
488     UNLOCK_GLOBAL_MUTEX;
489     return tdir;
490 }
491
492 static int
493 GetCellUnix(struct afsconf_dir *adir)
494 {
495     char *rc;
496     char tbuffer[256];
497     char *start, *p;
498     afsconf_FILE *fp;
499     
500     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
501     fp = fopen(tbuffer, "r");
502     if (fp == 0) {
503         return -1;
504     }
505     rc = fgets(tbuffer, 256, fp);
506     fclose(fp);
507     if (rc == NULL)
508         return -1;
509
510     start = tbuffer;
511     while (*start != '\0' && isspace(*start))
512         start++;
513     p = start;
514     while (*p != '\0' && !isspace(*p))
515         p++;
516     *p = '\0';
517     if (*start == '\0')
518         return -1;
519
520     adir->cellName = strdup(start);
521     return 0;
522 }
523
524
525 #ifdef AFS_NT40_ENV
526 static int
527 GetCellNT(struct afsconf_dir *adir)
528 {
529     if (IsClientConfigDirectory(adir->name)) {
530         /* NT client config dir; ThisCell is in registry (no file). */
531         return afssw_GetClientCellName(&adir->cellName);
532     } else {
533         /* NT server config dir; works just like Unix */
534         return GetCellUnix(adir);
535     }
536 }
537
538 /* The following procedures and structs are used on Windows only
539  * to enumerate the Cell information distributed within the 
540  * Windows registry.  (See src/WINNT/afsd/cm_config.c)
541  */
542 typedef struct _cm_enumCellRegistry {
543     afs_uint32 client;  /* non-zero if client query */
544     struct afsconf_dir *adir;
545 } cm_enumCellRegistry_t;
546
547 static long
548 cm_serverConfigProc(void *rockp, struct sockaddr_in *addrp, 
549                     char *hostNamep, unsigned short rank)
550 {
551     struct afsconf_cell *cellInfop = (struct afsconf_cell *)rockp;
552
553     if (cellInfop->numServers == MAXHOSTSPERCELL)
554         return 0;
555
556     cellInfop->hostAddr[cellInfop->numServers] = *addrp;
557     strncpy(cellInfop->hostName[cellInfop->numServers], hostNamep, MAXHOSTCHARS);
558     cellInfop->hostName[cellInfop->numServers][MAXHOSTCHARS-1] = '\0';
559     cellInfop->numServers++;
560
561     return 0;
562 }
563
564 static long
565 cm_enumCellRegistryProc(void *rockp, char * cellNamep)
566 {
567     long code;
568     cm_enumCellRegistry_t *enump = (cm_enumCellRegistry_t *)rockp;
569     char linkedName[256] = "";
570     int timeout = 0;
571     struct afsconf_entry *newEntry;
572
573
574     newEntry = malloc(sizeof(struct afsconf_entry));
575     if (newEntry == NULL)
576         return ENOMEM;
577     newEntry->cellInfo.numServers = 0;
578
579     code = cm_SearchCellRegistry(enump->client, cellNamep, NULL, linkedName, cm_serverConfigProc, &newEntry->cellInfo);
580     if (code == CM_ERROR_FORCE_DNS_LOOKUP)
581         code = cm_SearchCellByDNS(cellNamep, NULL, &timeout, cm_serverConfigProc, &newEntry->cellInfo);
582
583     if (code == 0) {
584         strncpy(newEntry->cellInfo.name, cellNamep, MAXCELLCHARS);
585         newEntry->cellInfo.name[MAXCELLCHARS-1];
586         if (linkedName[0])
587             newEntry->cellInfo.linkedCell = strdup(linkedName);
588         else
589             newEntry->cellInfo.linkedCell = NULL;
590         newEntry->cellInfo.timeout = timeout;
591         newEntry->cellInfo.flags = 0;
592
593         newEntry->next = enump->adir->entries;
594         enump->adir->entries = newEntry;
595     } else {
596         free(newEntry);
597     }
598     return code;
599 }       
600 #endif /* AFS_NT40_ENV */
601
602
603 static int
604 afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
605                      char clones[])
606 {
607     afsconf_FILE *tf;
608     register char *tp, *bp;
609     register struct afsconf_entry *curEntry;
610     struct afsconf_aliasentry *curAlias;
611     register afs_int32 code;
612     afs_int32 i;
613     char tbuffer[256], tbuf1[256];
614     struct stat tstat;
615 #ifdef AFS_NT40_ENV
616     cm_enumCellRegistry_t enumCellRegistry = {0, 0};
617 #endif /* AFS_NT40_ENV */
618
619     /* figure out the local cell name */
620 #ifdef AFS_NT40_ENV
621     i = GetCellNT(adir);
622     enumCellRegistry.adir = adir;
623 #else
624     i = GetCellUnix(adir);
625 #endif
626
627 #ifndef AFS_FREELANCE_CLIENT    /* no local cell not fatal in freelance */
628     if (i) {
629         return i;
630     }
631 #endif
632
633     /* now parse the individual lines */
634     curEntry = 0;
635
636 #ifdef AFS_NT40_ENV
637     /* NT client/server have a CellServDB that is the same format as Unix.
638      * However, the NT client uses a different file name
639      */
640     if (IsClientConfigDirectory(adir->name)) {
641         /* NT client config dir */
642         char *p;
643
644         enumCellRegistry.client = 1;
645
646         if (!afssw_GetClientCellServDBDir(&p)) {
647             strcompose(tbuffer, sizeof(tbuffer), p, "/",
648                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
649             free(p);
650         } else {
651             int len;
652             strncpy(tbuffer, adir->name, sizeof(tbuffer));
653             len = (int)strlen(tbuffer);
654             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
655                 strncat(tbuffer, "\\", sizeof(tbuffer));
656             }
657             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
658                     sizeof(tbuffer));
659             tbuffer[sizeof(tbuffer) - 1] = '\0';
660         }
661     } else {
662         /* NT server config dir */
663         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
664                    NULL);
665     }
666 #else
667     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
668 #endif /* AFS_NT40_ENV */
669
670     if (!stat(tbuffer, &tstat)) {
671         adir->timeRead = tstat.st_mtime;
672     } else {
673         adir->timeRead = 0;
674     }
675
676     strlcpy(tbuf1, tbuffer, sizeof tbuf1);
677     tf = fopen(tbuffer, "r");
678     if (!tf) {
679         return -1;
680     }
681
682     /* The CellServDB file is now open.  
683      * The following code parses the contents of the 
684      * file and creates a list with the first cell entry
685      * in the CellServDB file at the end of the list.
686      * 
687      * No checking is performed for duplicates.
688      * The side effects of this process are that duplicate
689      * entries appended to the end of the CellServDB file
690      * take precedence and are found in a shorter period 
691      * of time.
692      */
693
694     while (1) {
695         tp = fgets(tbuffer, sizeof(tbuffer), tf);
696         if (!tp)
697             break;
698         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
699         if (tbuffer[0] == 0 || tbuffer[0] == '\n')
700             continue;           /* empty line */
701         if (tbuffer[0] == '>') {
702             char linkedcell[MAXCELLCHARS];
703             /* start new cell item */
704             if (curEntry) {
705                 /* thread this guy on the list */
706                 curEntry->next = adir->entries;
707                 adir->entries = curEntry;
708                 curEntry = 0;
709             }
710             curEntry =
711                 (struct afsconf_entry *)malloc(sizeof(struct afsconf_entry));
712             memset(curEntry, 0, sizeof(struct afsconf_entry));
713             code =
714                 ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
715             if (code) {
716                 afsconf_CloseInternal(adir);
717                 fclose(tf);
718                 free(curEntry);
719                 return -1;
720             }
721             if (linkedcell[0] != '\0')
722                 curEntry->cellInfo.linkedCell = strdup(linkedcell);
723         } else {
724             /* new host in the current cell */
725             if (!curEntry) {
726                 afsconf_CloseInternal(adir);
727                 fclose(tf);
728                 return -1;
729             }
730             i = curEntry->cellInfo.numServers;
731             if (i < MAXHOSTSPERCELL) {
732                 if (cell && !strcmp(cell, curEntry->cellInfo.name))
733                     code =
734                         ParseHostLine(tbuffer, 
735                                       &curEntry->cellInfo.hostAddr[i],
736                                       curEntry->cellInfo.hostName[i], 
737                                       &clones[i]);
738                 else
739                     code =
740                         ParseHostLine(tbuffer, 
741                                       &curEntry->cellInfo.hostAddr[i],
742                                       curEntry->cellInfo.hostName[i], 0);
743
744                 if (code) {
745                     if (code == AFSCONF_SYNTAX) {
746                         for (bp = tbuffer; *bp != '\n'; bp++) { /* Take out the <cr> from the buffer */
747                             if (!*bp)
748                                 break;
749                         }
750                         *bp = '\0';
751                         fprintf(stderr,
752                                 "Can't properly parse host line \"%s\" in configuration file %s\n",
753                                 tbuffer, tbuf1);
754                     }
755                     free(curEntry);
756                     fclose(tf);
757                     afsconf_CloseInternal(adir);
758                     return -1;
759                 }
760                 curEntry->cellInfo.numServers = ++i;
761             } else {
762                 fprintf(stderr,
763                         "Too many hosts for cell %s in configuration file %s\n", 
764                         curEntry->cellInfo.name, tbuf1);
765             }
766         }
767     }
768     fclose(tf);                 /* close the file now */
769
770     /* end the last partially-completed cell */
771     if (curEntry) {
772         curEntry->next = adir->entries;
773         adir->entries = curEntry;
774     }
775
776 #ifdef AFS_NT40_ENV
777      /* 
778       * Windows maintains a CellServDB list in the Registry
779       * that supercedes the contents of the CellServDB file.
780       * Prepending these entries to the head of the list 
781       * is sufficient to enforce the precedence.
782       */
783      cm_EnumerateCellRegistry( enumCellRegistry.client,
784                                cm_enumCellRegistryProc,
785                                &enumCellRegistry);
786 #endif /* AFS_NT40_ENV */
787
788     /* Read in the alias list */
789     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLALIAS_FILE, NULL);
790
791     tf = fopen(tbuffer, "r");
792     while (tf) {
793         char *aliasPtr;
794
795         tp = fgets(tbuffer, sizeof(tbuffer), tf);
796         if (!tp)
797             break;
798         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
799
800         if (tbuffer[0] == '\0' || tbuffer[0] == '\n' || tbuffer[0] == '#')
801             continue;           /* empty line */
802
803         tp = tbuffer;
804         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t')
805             tp++;
806         if (tp[0] == '\0')
807             continue;           /* invalid line */
808
809         while (tp[0] != '\0' && (tp[0] == ' ' || tp[0] == '\t'))
810             0[tp++] = '\0';
811         if (tp[0] == '\0')
812             continue;           /* invalid line */
813
814         aliasPtr = tp;
815         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t' && tp[0] != '\r'
816                && tp[0] != '\n')
817             tp++;
818         tp[0] = '\0';
819
820         curAlias = malloc(sizeof(*curAlias));
821         memset(curAlias, 0, sizeof(*curAlias));
822
823         strlcpy(curAlias->aliasInfo.aliasName, aliasPtr, sizeof curAlias->aliasInfo.aliasName);
824         strlcpy(curAlias->aliasInfo.realName, tbuffer, sizeof curAlias->aliasInfo.realName);
825
826         curAlias->next = adir->alias_entries;
827         adir->alias_entries = curAlias;
828     }
829
830     if (tf != NULL)
831         fclose(tf);
832     /* now read the fs keys, if possible */
833     adir->keystr = (struct afsconf_keys *)0;
834     afsconf_IntGetKeys(adir);
835
836     return 0;
837 }
838
839 /* parse a line of the form
840  *"128.2.1.3   #hostname" or
841  *"[128.2.1.3]  #hostname" for clones
842  * into the appropriate pieces.  
843  */
844 static int
845 ParseHostLine(char *aline, register struct sockaddr_in *addr, char *aname,
846               char *aclone)
847 {
848     int c1, c2, c3, c4;
849     register afs_int32 code;
850     register char *tp;
851
852     if (*aline == '[') {
853         if (aclone)
854             *aclone = 1;
855         /* FIXME: length of aname unknown here */
856         code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
857     } else {
858         if (aclone)
859             *aclone = 0;
860         /* FIXME: length of aname unknown here */
861         code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
862     }
863     if (code != 5)
864         return AFSCONF_SYNTAX;
865     addr->sin_family = AF_INET;
866     addr->sin_port = 0;
867 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
868     addr->sin_len = sizeof(struct sockaddr_in);
869 #endif
870     tp = (char *)&addr->sin_addr;
871     *tp++ = c1;
872     *tp++ = c2;
873     *tp++ = c3;
874     *tp++ = c4;
875     return 0;
876 }
877
878 /* parse a line of the form
879  * ">cellname [linkedcellname] [#comments]"
880  * into the appropriate pieces.
881  */
882 static int
883 ParseCellLine(register char *aline, register char *aname,
884               register char *alname)
885 {
886     register int code;
887     /* FIXME: length of aname, alname unknown here */
888     code = sscanf(aline, ">%s %s", aname, alname);
889     if (code == 1)
890         *alname = '\0';
891     if (code == 2) {
892         if (*alname == '#') {
893             *alname = '\0';
894         }
895     }
896     return (code > 0 ? 0 : AFSCONF_SYNTAX);
897 }
898
899 /* call aproc(entry, arock, adir) for all cells.  Proc must return 0, or we'll stop early and return the code it returns */
900 int
901 afsconf_CellApply(struct afsconf_dir *adir,
902                   int (*aproc) (struct afsconf_cell * cell, void *arock,
903                                 struct afsconf_dir * dir), void *arock)
904 {
905     register struct afsconf_entry *tde;
906     register afs_int32 code;
907     LOCK_GLOBAL_MUTEX;
908     for (tde = adir->entries; tde; tde = tde->next) {
909         code = (*aproc) (&tde->cellInfo, arock, adir);
910         if (code) {
911             UNLOCK_GLOBAL_MUTEX;
912             return code;
913         }
914     }
915     UNLOCK_GLOBAL_MUTEX;
916     return 0;
917 }
918
919 /* call aproc(entry, arock, adir) for all cell aliases.
920  * Proc must return 0, or we'll stop early and return the code it returns
921  */
922 int
923 afsconf_CellAliasApply(struct afsconf_dir *adir,
924                        int (*aproc) (struct afsconf_cellalias * alias,
925                                      void *arock, struct afsconf_dir * dir),
926                        void *arock)
927 {
928     register struct afsconf_aliasentry *tde;
929     register afs_int32 code;
930     LOCK_GLOBAL_MUTEX;
931     for (tde = adir->alias_entries; tde; tde = tde->next) {
932         code = (*aproc) (&tde->aliasInfo, arock, adir);
933         if (code) {
934             UNLOCK_GLOBAL_MUTEX;
935             return code;
936         }
937     }
938     UNLOCK_GLOBAL_MUTEX;
939     return 0;
940 }
941
942 afs_int32 afsconf_SawCell = 0;
943
944 int
945 afsconf_GetExtendedCellInfo(struct afsconf_dir *adir, char *acellName,
946                             char *aservice, struct afsconf_cell *acellInfo,
947                             char clones[])
948 {
949     afs_int32 code;
950     char *cell;
951
952     code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
953     if (code)
954         return code;
955
956     if (acellName)
957         cell = acellName;
958     else
959         cell = (char *)&acellInfo->name;
960
961     code = afsconf_OpenInternal(adir, cell, clones);
962     return code;
963 }
964
965 #ifdef AFS_AFSDB_ENV
966 #if !defined(AFS_NT40_ENV)
967 int
968 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
969                      struct afsconf_cell *acellInfo)
970 {
971     afs_int32 code;
972     int tservice, i, len;
973     unsigned char answer[1024];
974     unsigned char *p;
975     char *dotcellname;
976     int cellnamelength;
977     char realCellName[256];
978     char host[256];
979     int server_num = 0;
980     int minttl = 0;
981     int try_init = 0;
982
983     /* The resolver isn't always MT-safe.. Perhaps this ought to be
984      * replaced with a more fine-grained lock just for the resolver
985      * operations.
986      */
987
988  retryafsdb:
989     if ( ! strchr(acellName,'.') ) {
990        cellnamelength=strlen(acellName);
991        dotcellname=malloc(cellnamelength+2);
992        memcpy(dotcellname,acellName,cellnamelength);
993        dotcellname[cellnamelength]='.';
994        dotcellname[cellnamelength+1]=0;
995        LOCK_GLOBAL_MUTEX;
996        len = res_search(dotcellname, C_IN, T_AFSDB, answer, sizeof(answer));
997        if ( len < 0 ) {
998           len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
999        }
1000        UNLOCK_GLOBAL_MUTEX;
1001        free(dotcellname);
1002     } else {
1003        LOCK_GLOBAL_MUTEX;
1004        len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
1005        UNLOCK_GLOBAL_MUTEX;
1006     }
1007     if (len < 0) {
1008         if (try_init < 1) {
1009             try_init++;
1010             res_init();
1011             goto retryafsdb;
1012         }
1013         return AFSCONF_NOTFOUND;
1014     }
1015
1016     p = answer + sizeof(HEADER);        /* Skip header */
1017     code = dn_expand(answer, answer + len, p, host, sizeof(host));
1018     if (code < 0)
1019         return AFSCONF_NOTFOUND;
1020
1021     p += code + QFIXEDSZ;       /* Skip name */
1022
1023     while (p < answer + len) {
1024         int type, ttl, size;
1025
1026         code = dn_expand(answer, answer + len, p, host, sizeof(host));
1027         if (code < 0)
1028             return AFSCONF_NOTFOUND;
1029
1030         p += code;              /* Skip the name */
1031         type = (p[0] << 8) | p[1];
1032         p += 4;                 /* Skip type and class */
1033         ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
1034         p += 4;                 /* Skip the TTL */
1035         size = (p[0] << 8) | p[1];
1036         p += 2;                 /* Skip the size */
1037
1038         if (type == T_AFSDB) {
1039             struct hostent *he;
1040             short afsdb_type;
1041
1042             afsdb_type = (p[0] << 8) | p[1];
1043             if (afsdb_type == 1) {
1044                 /*
1045                  * We know this is an AFSDB record for our cell, of the
1046                  * right AFSDB type.  Write down the true cell name that
1047                  * the resolver gave us above.
1048                  */
1049                 strlcpy(realCellName, host, sizeof realCellName);
1050             }
1051
1052             code = dn_expand(answer, answer + len, p + 2, host, sizeof(host));
1053             if (code < 0)
1054                 return AFSCONF_NOTFOUND;
1055
1056             if ((afsdb_type == 1) && (server_num < MAXHOSTSPERCELL) &&
1057                 /* Do we want to get TTL data for the A record as well? */
1058                 (he = gethostbyname(host))) {
1059                 afs_int32 ipaddr;
1060                 memcpy(&ipaddr, he->h_addr, he->h_length);
1061                 acellInfo->hostAddr[server_num].sin_addr.s_addr = ipaddr;
1062                 strncpy(acellInfo->hostName[server_num], host,
1063                         sizeof(acellInfo->hostName[server_num]));
1064                 server_num++;
1065
1066                 if (!minttl || ttl < minttl)
1067                     minttl = ttl;
1068             }
1069         }
1070
1071         p += size;
1072     }
1073
1074     if (server_num == 0)        /* No AFSDB records */
1075         return AFSCONF_NOTFOUND;
1076
1077     /* Convert the real cell name to lowercase */
1078     for (p = (unsigned char *)realCellName; *p; p++)
1079         *p = tolower(*p);
1080
1081     strncpy(acellInfo->name, realCellName, sizeof(acellInfo->name));
1082     acellInfo->numServers = server_num;
1083
1084     if (aservice) {
1085         tservice = afsconf_FindService(aservice);
1086         if (tservice < 0)
1087             return AFSCONF_NOTFOUND;    /* service not found */
1088         for (i = 0; i < acellInfo->numServers; i++) {
1089             acellInfo->hostAddr[i].sin_port = tservice;
1090         }
1091     }
1092
1093     acellInfo->timeout = minttl ? (time(0) + minttl) : 0;
1094
1095     return 0;
1096 }
1097 #else /* windows */
1098 int
1099 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
1100                      struct afsconf_cell *acellInfo)
1101 {
1102     register afs_int32 i;
1103     int tservice;
1104     struct afsconf_entry DNSce;
1105     afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
1106     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
1107     unsigned short ipRanks[AFSMAXCELLHOSTS];
1108     int numServers;
1109     int rc;
1110     int ttl;
1111
1112     DNSce.cellInfo.numServers = 0;
1113     DNSce.next = NULL;
1114     rc = getAFSServer(acellName, cellHostAddrs, cellHostNames, ipRanks, &numServers,
1115                       &ttl);
1116     /* ignore the ttl here since this code is only called by transitory programs
1117      * like klog, etc. */
1118     if (rc < 0)
1119         return -1;
1120     if (numServers == 0)
1121         return -1;
1122
1123     for (i = 0; i < numServers; i++) {
1124         memcpy(&acellInfo->hostAddr[i].sin_addr.s_addr, &cellHostAddrs[i],
1125                sizeof(long));
1126         memcpy(acellInfo->hostName[i], cellHostNames[i], MAXHOSTCHARS);
1127         acellInfo->hostAddr[i].sin_family = AF_INET;
1128
1129         /* sin_port supplied by connection code */
1130     }
1131
1132     acellInfo->numServers = numServers;
1133     strlcpy(acellInfo->name, acellName, sizeof acellInfo->name);
1134     if (aservice) {
1135         LOCK_GLOBAL_MUTEX;
1136         tservice = afsconf_FindService(aservice);
1137         UNLOCK_GLOBAL_MUTEX;
1138         if (tservice < 0) {
1139             return AFSCONF_NOTFOUND;    /* service not found */
1140         }
1141         for (i = 0; i < acellInfo->numServers; i++) {
1142             acellInfo->hostAddr[i].sin_port = tservice;
1143         }
1144     }
1145     acellInfo->linkedCell = NULL;       /* no linked cell */
1146     acellInfo->flags = 0;
1147     return 0;
1148 }
1149 #endif /* windows */
1150 #endif /* AFS_AFSDB_ENV */
1151
1152 int
1153 afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
1154                     struct afsconf_cell *acellInfo)
1155 {
1156     register struct afsconf_entry *tce;
1157     struct afsconf_aliasentry *tcae;
1158     struct afsconf_entry *bestce;
1159     register afs_int32 i;
1160     int tservice;
1161     char *tcell;
1162     int cnLen;
1163     int ambig;
1164     char tbuffer[64];
1165
1166     LOCK_GLOBAL_MUTEX;
1167     if (adir)
1168         afsconf_Check(adir);
1169     if (acellName) {
1170         tcell = acellName;
1171         cnLen = (int)(strlen(tcell) + 1);
1172         lcstring(tcell, tcell, cnLen);
1173         afsconf_SawCell = 1;    /* will ignore the AFSCELL switch on future */
1174         /* call to afsconf_GetLocalCell: like klog  */
1175     } else {
1176         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
1177         if (i) {
1178             UNLOCK_GLOBAL_MUTEX;
1179             return i;
1180         }
1181         tcell = tbuffer;
1182     }
1183     cnLen = strlen(tcell);
1184     bestce = (struct afsconf_entry *)0;
1185     ambig = 0;
1186     if (!adir) {
1187         UNLOCK_GLOBAL_MUTEX;
1188         return 0;
1189     }
1190
1191     /* Look through the list of aliases */
1192     for (tcae = adir->alias_entries; tcae; tcae = tcae->next) {
1193         if (strcasecmp(tcae->aliasInfo.aliasName, tcell) == 0) {
1194             tcell = tcae->aliasInfo.realName;
1195             break;
1196         }
1197     }
1198
1199     for (tce = adir->entries; tce; tce = tce->next) {
1200         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
1201             /* found our cell */
1202             bestce = tce;
1203             ambig = 0;
1204             break;
1205         }
1206         if (strlen(tce->cellInfo.name) < cnLen)
1207             continue;           /* clearly wrong */
1208         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
1209             if (bestce)
1210                 ambig = 1;      /* ambiguous unless we get exact match */
1211             bestce = tce;
1212         }
1213     }
1214     if (!ambig && bestce && bestce->cellInfo.numServers) {
1215         *acellInfo = bestce->cellInfo;  /* structure assignment */
1216         if (aservice) {
1217             tservice = afsconf_FindService(aservice);
1218             if (tservice < 0) {
1219                 UNLOCK_GLOBAL_MUTEX;
1220                 return AFSCONF_NOTFOUND;        /* service not found */
1221             }
1222             for (i = 0; i < acellInfo->numServers; i++) {
1223                 acellInfo->hostAddr[i].sin_port = tservice;
1224             }
1225         }
1226         acellInfo->timeout = 0;
1227
1228         /* 
1229          * Until we figure out how to separate out ubik server
1230          * queries from other server queries, only perform gethostbyname()
1231          * lookup on the specified hostnames for the client CellServDB files.
1232          */
1233         if (IsClientConfigDirectory(adir->name) && 
1234             !(acellInfo->flags & AFSCONF_CELL_FLAG_DNS_QUERIED)) {
1235             int j;
1236             short numServers=0;                                 /*Num active servers for the cell */
1237             struct sockaddr_in hostAddr[MAXHOSTSPERCELL];       /*IP addresses for cell's servers */
1238             char hostName[MAXHOSTSPERCELL][MAXHOSTCHARS];       /*Names for cell's servers */
1239
1240             memset(&hostAddr, 0, sizeof(hostAddr));
1241             memset(&hostName, 0, sizeof(hostName));
1242
1243             for ( j=0; j<acellInfo->numServers && numServers < MAXHOSTSPERCELL; j++ ) {
1244                 struct hostent *he = gethostbyname(acellInfo->hostName[j]);
1245                 int foundAddr = 0;
1246
1247                 if (he && he->h_addrtype == AF_INET) {
1248                     int i;
1249                     /* obtain all the valid address from the list */
1250                     for (i=0 ; he->h_addr_list[i] && numServers < MAXHOSTSPERCELL; i++) {
1251                         /* check to see if this is a new address; if so insert it into the list */
1252                         int k, dup;
1253                         for (k=0, dup=0; !dup && k < numServers; k++) {
1254                             if (hostAddr[k].sin_addr.s_addr == *(u_long *)he->h_addr_list[i])
1255                                 dup = 1;
1256                         }
1257                         if (dup)
1258                             continue;
1259
1260                         hostAddr[numServers].sin_family = AF_INET;
1261                         hostAddr[numServers].sin_port = acellInfo->hostAddr[0].sin_port;
1262 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
1263                         hostAddr[numServers].sin_len = sizeof(struct sockaddr_in);
1264 #endif
1265                         memcpy(&hostAddr[numServers].sin_addr.s_addr, he->h_addr_list[i], sizeof(long));
1266                         strcpy(hostName[numServers], acellInfo->hostName[j]);
1267                         foundAddr = 1;
1268                         numServers++;
1269                     }
1270                 }
1271                 if (!foundAddr) {
1272                     hostAddr[numServers] = acellInfo->hostAddr[j];
1273                     strcpy(hostName[numServers], acellInfo->hostName[j]);
1274                     numServers++;
1275                 }
1276             }
1277
1278             for (i=0; i<numServers; i++) {
1279                 acellInfo->hostAddr[i] = hostAddr[i];
1280                 strcpy(acellInfo->hostName[i], hostName[i]);
1281             }
1282             acellInfo->numServers = numServers;
1283             acellInfo->flags |= AFSCONF_CELL_FLAG_DNS_QUERIED;
1284         }
1285         UNLOCK_GLOBAL_MUTEX;
1286         return 0;
1287     } else {
1288         UNLOCK_GLOBAL_MUTEX;
1289 #ifdef AFS_AFSDB_ENV
1290         return afsconf_GetAfsdbInfo(tcell, aservice, acellInfo);
1291 #else
1292         return AFSCONF_NOTFOUND;
1293 #endif /* AFS_AFSDB_ENV */
1294     }
1295 }
1296
1297 int
1298 afsconf_GetLocalCell(register struct afsconf_dir *adir, char *aname,
1299                      afs_int32 alen)
1300 {
1301     static int afsconf_showcell = 0;
1302     char *afscell_path;
1303     afs_int32 code = 0;
1304
1305     LOCK_GLOBAL_MUTEX;
1306     /*
1307      * If a cell switch was specified in a command, then it should override the 
1308      * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
1309      * is set and the cell name in the adir structure is used.
1310      * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
1311      */
1312     if (!afsconf_SawCell && (afscell_path = getenv("AFSCELL"))) {
1313         if (!afsconf_showcell) {
1314             fprintf(stderr, "Note: Operation is performed on cell %s\n",
1315                     afscell_path);
1316             afsconf_showcell = 1;
1317         }
1318         strncpy(aname, afscell_path, alen);
1319     } else {
1320         afsconf_Check(adir);
1321         if (adir->cellName) {
1322             strncpy(aname, adir->cellName, alen);
1323         } else
1324             code = AFSCONF_UNKNOWN;
1325     }
1326
1327     UNLOCK_GLOBAL_MUTEX;
1328     return (code);
1329 }
1330
1331 int
1332 afsconf_Close(struct afsconf_dir *adir)
1333 {
1334     LOCK_GLOBAL_MUTEX;
1335     afsconf_CloseInternal(adir);
1336     if (adir->name)
1337         free(adir->name);
1338     free(adir);
1339     UNLOCK_GLOBAL_MUTEX;
1340     return 0;
1341 }
1342
1343 static int
1344 afsconf_CloseInternal(register struct afsconf_dir *adir)
1345 {
1346     register struct afsconf_entry *td, *nd;
1347     struct afsconf_aliasentry *ta, *na;
1348     register char *tname;
1349
1350     tname = adir->name;         /* remember name, since that's all we preserve */
1351
1352     /* free everything we can find */
1353     if (adir->cellName)
1354         free(adir->cellName);
1355     for (td = adir->entries; td; td = nd) {
1356         nd = td->next;
1357         if (td->cellInfo.linkedCell)
1358             free(td->cellInfo.linkedCell);
1359         free(td);
1360     }
1361     for (ta = adir->alias_entries; ta; ta = na) {
1362         na = ta->next;
1363         free(ta);
1364     }
1365     if (adir->keystr)
1366         free(adir->keystr);
1367
1368     /* reinit */
1369     memset(adir, 0, sizeof(struct afsconf_dir));
1370     adir->name = tname;         /* restore it */
1371     return 0;
1372 }
1373
1374 static int
1375 afsconf_Reopen(register struct afsconf_dir *adir)
1376 {
1377     register afs_int32 code;
1378     code = afsconf_CloseInternal(adir);
1379     if (code)
1380         return code;
1381     code = afsconf_OpenInternal(adir, 0, 0);
1382     return code;
1383 }
1384
1385 /* called during opening of config file */
1386 int
1387 afsconf_IntGetKeys(struct afsconf_dir *adir)
1388 {
1389     char tbuffer[256];
1390     register int fd;
1391     struct afsconf_keys *tstr;
1392     register afs_int32 code;
1393
1394 #ifdef AFS_NT40_ENV
1395     /* NT client config dir has no KeyFile; don't risk attempting open
1396      * because there might be a random file of this name if dir is shared.
1397      */
1398     if (IsClientConfigDirectory(adir->name)) {
1399         adir->keystr = ((struct afsconf_keys *)
1400                         malloc(sizeof(struct afsconf_keys)));
1401         adir->keystr->nkeys = 0;
1402         return 0;
1403     }
1404 #endif /* AFS_NT40_ENV */
1405
1406     LOCK_GLOBAL_MUTEX;
1407     /* compute the key name and other setup */
1408     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1409     tstr = (struct afsconf_keys *)malloc(sizeof(struct afsconf_keys));
1410     adir->keystr = tstr;
1411
1412     /* read key file */
1413     fd = open(tbuffer, O_RDONLY);
1414     if (fd < 0) {
1415         tstr->nkeys = 0;
1416         UNLOCK_GLOBAL_MUTEX;
1417         return 0;
1418     }
1419     code = read(fd, tstr, sizeof(struct afsconf_keys));
1420     close(fd);
1421     if (code < sizeof(afs_int32)) {
1422         tstr->nkeys = 0;
1423         UNLOCK_GLOBAL_MUTEX;
1424         return 0;
1425     }
1426
1427     /* convert key structure to host order */
1428     tstr->nkeys = ntohl(tstr->nkeys);
1429
1430     if (code < sizeof(afs_int32) + (tstr->nkeys*sizeof(struct afsconf_key))) {
1431         tstr->nkeys = 0;
1432         UNLOCK_GLOBAL_MUTEX;
1433         return 0;
1434     }
1435
1436     for (fd = 0; fd < tstr->nkeys; fd++)
1437         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
1438
1439     UNLOCK_GLOBAL_MUTEX;
1440     return 0;
1441 }
1442
1443 /* get keys structure */
1444 int
1445 afsconf_GetKeys(struct afsconf_dir *adir, struct afsconf_keys *astr)
1446 {
1447     register afs_int32 code;
1448
1449     LOCK_GLOBAL_MUTEX;
1450     code = afsconf_Check(adir);
1451     if (code) {
1452         UNLOCK_GLOBAL_MUTEX;
1453         return AFSCONF_FAILURE;
1454     }
1455     memcpy(astr, adir->keystr, sizeof(struct afsconf_keys));
1456     UNLOCK_GLOBAL_MUTEX;
1457     return 0;
1458 }
1459
1460 /* get latest key */
1461 afs_int32
1462 afsconf_GetLatestKey(struct afsconf_dir * adir, afs_int32 * avno, 
1463                      struct ktc_encryptionKey *akey)
1464 {
1465     register int i;
1466     int maxa;
1467     register struct afsconf_key *tk;
1468     register afs_int32 best;
1469     struct afsconf_key *bestk;
1470     register afs_int32 code;
1471
1472     LOCK_GLOBAL_MUTEX;
1473     code = afsconf_Check(adir);
1474     if (code) {
1475         UNLOCK_GLOBAL_MUTEX;
1476         return AFSCONF_FAILURE;
1477     }
1478     maxa = adir->keystr->nkeys;
1479
1480     best = -1;                  /* highest kvno we've seen yet */
1481     bestk = (struct afsconf_key *)0;    /* ptr to structure providing best */
1482     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1483         if (tk->kvno == 999)
1484             continue;           /* skip bcrypt keys */
1485         if (tk->kvno > best) {
1486             best = tk->kvno;
1487             bestk = tk;
1488         }
1489     }
1490     if (bestk) {                /* found any  */
1491         if (akey)
1492             memcpy(akey, bestk->key, 8);        /* copy out latest key */
1493         if (avno)
1494             *avno = bestk->kvno;        /* and kvno to caller */
1495         UNLOCK_GLOBAL_MUTEX;
1496         return 0;
1497     }
1498     UNLOCK_GLOBAL_MUTEX;
1499     return AFSCONF_NOTFOUND;    /* didn't find any keys */
1500 }
1501
1502 /* get a particular key */
1503 int
1504 afsconf_GetKey(void *rock, int avno, struct ktc_encryptionKey *akey)
1505 {
1506     struct afsconf_dir *adir = (struct afsconf_dir *) rock;
1507     register int i, maxa;
1508     register struct afsconf_key *tk;
1509     register afs_int32 code;
1510
1511     LOCK_GLOBAL_MUTEX;
1512     code = afsconf_Check(adir);
1513     if (code) {
1514         UNLOCK_GLOBAL_MUTEX;
1515         return AFSCONF_FAILURE;
1516     }
1517     maxa = adir->keystr->nkeys;
1518
1519     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1520         if (tk->kvno == avno) {
1521             memcpy(akey, tk->key, 8);
1522             UNLOCK_GLOBAL_MUTEX;
1523             return 0;
1524         }
1525     }
1526
1527     UNLOCK_GLOBAL_MUTEX;
1528     return AFSCONF_NOTFOUND;
1529 }
1530
1531 /* save the key structure in the appropriate file */
1532 static int
1533 SaveKeys(struct afsconf_dir *adir)
1534 {
1535     struct afsconf_keys tkeys;
1536     register int fd;
1537     register afs_int32 i;
1538     char tbuffer[256];
1539
1540     memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys));
1541
1542     /* convert it to net byte order */
1543     for (i = 0; i < tkeys.nkeys; i++)
1544         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
1545     tkeys.nkeys = htonl(tkeys.nkeys);
1546
1547     /* rewrite keys file */
1548     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1549     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
1550     if (fd < 0)
1551         return AFSCONF_FAILURE;
1552     i = write(fd, &tkeys, sizeof(tkeys));
1553     if (i != sizeof(tkeys)) {
1554         close(fd);
1555         return AFSCONF_FAILURE;
1556     }
1557     if (close(fd) < 0)
1558         return AFSCONF_FAILURE;
1559     return 0;
1560 }
1561
1562 int
1563 afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8],
1564                afs_int32 overwrite)
1565 {
1566     register struct afsconf_keys *tk;
1567     register struct afsconf_key *tkey;
1568     register afs_int32 i;
1569     int foundSlot;
1570
1571     LOCK_GLOBAL_MUTEX;
1572     tk = adir->keystr;
1573
1574     if (akvno != 999) {
1575         if (akvno < 0 || akvno > 255) {
1576             UNLOCK_GLOBAL_MUTEX;
1577             return ERANGE;
1578         }
1579     }
1580     foundSlot = 0;
1581     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1582         if (tkey->kvno == akvno) {
1583             if (!overwrite) {
1584                 UNLOCK_GLOBAL_MUTEX;
1585                 return AFSCONF_KEYINUSE;
1586             }
1587             foundSlot = 1;
1588             break;
1589         }
1590     }
1591     if (!foundSlot) {
1592         if (tk->nkeys >= AFSCONF_MAXKEYS) {
1593             UNLOCK_GLOBAL_MUTEX;
1594             return AFSCONF_FULL;
1595         }
1596         tkey = &tk->key[tk->nkeys++];
1597     }
1598     tkey->kvno = akvno;
1599     memcpy(tkey->key, akey, 8);
1600     i = SaveKeys(adir);
1601     afsconf_Touch(adir);
1602     UNLOCK_GLOBAL_MUTEX;
1603     return i;
1604 }
1605
1606 /* this proc works by sliding the other guys down, rather than using a funny
1607     kvno value, so that callers can count on getting a good key in key[0].
1608 */
1609 int
1610 afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno)
1611 {
1612     register struct afsconf_keys *tk;
1613     register struct afsconf_key *tkey;
1614     register int i;
1615     int foundFlag = 0;
1616
1617     LOCK_GLOBAL_MUTEX;
1618     tk = adir->keystr;
1619
1620     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1621         if (tkey->kvno == akvno) {
1622             foundFlag = 1;
1623             break;
1624         }
1625     }
1626     if (!foundFlag) {
1627         UNLOCK_GLOBAL_MUTEX;
1628         return AFSCONF_NOTFOUND;
1629     }
1630
1631     /* otherwise slide the others down.  i and tkey point at the guy to delete */
1632     for (; i < tk->nkeys - 1; i++, tkey++) {
1633         tkey->kvno = (tkey + 1)->kvno;
1634         memcpy(tkey->key, (tkey + 1)->key, 8);
1635     }
1636     tk->nkeys--;
1637     i = SaveKeys(adir);
1638     afsconf_Touch(adir);
1639     UNLOCK_GLOBAL_MUTEX;
1640     return i;
1641 }