860740dbb97f84e3339c9f57bf5aa659289903b8
[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("$Header$");
14
15 #include <afs/stds.h>
16 #include <afs/pthread_glock.h>
17 #ifdef UKERNEL
18 #include "../afs/sysincludes.h"
19 #include "../afs/afsincludes.h"
20 #else /* UKERNEL */
21 #include <sys/types.h>
22 #ifdef AFS_NT40_ENV
23 #include <winsock2.h>
24 #include <sys/utime.h>
25 #include <io.h>
26 #include <WINNT/afssw.h>
27 #else
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <netdb.h>
31 #include <sys/file.h>
32 #include <sys/time.h>
33 #ifdef AFS_AFSDB_ENV
34 #include <arpa/nameser.h>
35 #include <resolv.h>
36 #endif /* AFS_AFSDB_ENV */
37 #endif /* AFS_NT40_ENV */
38 #include <errno.h>
39 #include <ctype.h>
40 #include <time.h>
41 #include <stdio.h>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44 #endif /* UKERNEL */
45 #include <afs/afsutil.h>
46 #include "cellconfig.h"
47 #include "keys.h"
48
49 static ParseHostLine();
50 static ParseCellLine();
51 static afsconf_OpenInternal();
52 static afsconf_CloseInternal();
53 static afsconf_Reopen();
54
55 static struct afsconf_servPair serviceTable [] = {
56     "afs",      7000,
57     "afscb",    7001,
58     "afsprot",  7002,
59     "afsvldb",  7003,
60     "afskauth", 7004,
61     "afsvol",   7005,
62     "afserror", 7006,
63     "afsnanny", 7007,
64     "afsupdate",7008,
65     "afsrmtsys",7009,
66     "afsres",   7010,   /* residency database for MR-AFS */
67     "afsremio", 7011,   /* remote I/O interface for MR-AFS */
68     0, 0                /* insert new services before this spot */
69 };
70
71 /*
72  * Basic Rule: we touch "<AFSCONF_DIR>/CellServDB" every time we change anything, so
73  * our code can tell if there is new info in the key files, the cell server db
74  * files or any of the other files (and reopen the thing) if the date on
75  * CellServDB changes.
76  */
77
78 /* return port number in network byte order in the low 16 bits of a long; return -1 if not found */
79 static afs_int32 afsconf_FindService(aname)
80 register char *aname; {
81     /* lookup a service name */
82     struct servent *ts;
83     register struct afsconf_servPair *tsp;
84
85 #if     defined(AFS_OSF_ENV) || defined(AFS_DEC_ENV)
86     ts = getservbyname(aname, "");
87 #else
88     ts = getservbyname(aname, (char *) 0);
89 #endif
90     if (ts) {
91         /* we found it in /etc/services, so we use this value */
92         return ts->s_port;  /* already in network byte order */
93     }
94
95     /* not found in /etc/services, see if it is one of ours */
96     for(tsp = serviceTable;; tsp++) {
97         if (tsp->name == (char *) 0) return -1;
98         if (!strcmp(tsp->name, aname)) return htons(tsp->port);
99     }
100 }
101
102 static int TrimLine(abuffer)
103 char *abuffer; {
104     char tbuffer[256];
105     register char *tp;
106     register int tc;
107
108     tp = abuffer;
109     while (tc = *tp) {
110         if (!isspace(tc)) break;
111         tp++;
112     }
113     strcpy(tbuffer, tp);
114     strcpy(abuffer, tbuffer);
115     return 0;
116 }
117
118 #ifdef AFS_NT40_ENV
119 /*
120  * IsClientConfigDirectory() -- determine if path matches well-known
121  *     client configuration directory.
122  */
123 static int IsClientConfigDirectory(const char *path)
124 {
125     const char *cdir = AFSDIR_CLIENT_ETC_DIRPATH;
126     int i;
127
128     for (i = 0; cdir[i] != '\0' && path[i] != '\0'; i++) {
129         int cc = tolower(cdir[i]);
130         int pc = tolower(path[i]);
131
132         if (cc == '\\') {
133             cc = '/';
134         }
135         if (pc == '\\') {
136             pc = '/';
137         }
138         if (cc != pc) {
139             return 0;
140         }
141     }
142
143     /* hit end of one or both; allow mismatch in existence of trailing slash */
144     if (cdir[i] != '\0') {
145         if ((cdir[i] != '\\' && cdir[i] != '/') || (cdir[i + 1] != '\0')) {
146             return 0;
147         }
148     }
149     if (path[i] != '\0') {
150         if ((path[i] != '\\' && path[i] != '/') || (path[i + 1] != '\0')) {
151             return 0;
152         }
153     }
154     return 1;
155 }
156 #endif /* AFS_NT40_ENV */
157
158
159 static int afsconf_Check(adir)
160 register struct afsconf_dir *adir; {
161     char tbuffer[256];
162     struct stat tstat;
163     register afs_int32 code;
164
165 #ifdef AFS_NT40_ENV
166     /* NT client CellServDB has different file name than NT server or Unix */
167     if (IsClientConfigDirectory(adir->name)) {
168         strcompose(tbuffer, 256,
169                    adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
170     } else {
171         strcompose(tbuffer, 256,
172                    adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
173     }
174 #else
175     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
176 #endif /* AFS_NT40_ENV */
177
178     code = stat(tbuffer, &tstat);
179     if (code < 0) {
180         return code;
181     }
182     /* did file change? */
183     if (tstat.st_mtime == adir->timeRead) {
184         return 0;
185     }
186     /* otherwise file has changed, so reopen it */
187     return afsconf_Reopen(adir);
188 }
189
190 /* set modtime on file */
191 static afsconf_Touch(adir)
192 register struct afsconf_dir *adir; {
193     char tbuffer[256];
194     struct timeval tvp[2];
195
196 #ifdef AFS_NT40_ENV
197     /* NT client CellServDB has different file name than NT server or Unix */
198     if (IsClientConfigDirectory(adir->name)) {
199         strcompose(tbuffer, 256,
200                    adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
201     } else {
202         strcompose(tbuffer, 256,
203                    adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
204     }
205 #else
206     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
207 #endif /* AFS_NT40_ENV */
208
209     adir->timeRead = 0; /* just in case */
210 #ifdef AFS_NT40_ENV
211     return _utime(tbuffer, NULL);
212 #else
213     gettimeofday(&tvp[0], NULL);
214     tvp[1] = tvp[0];
215     return utimes(tbuffer, tvp);
216 #endif  /* AFS_NT40_ENV */
217 }
218
219 struct afsconf_dir *afsconf_Open(adir)
220 register char *adir; {
221     register struct afsconf_dir *tdir;
222     register afs_int32 code;
223
224     LOCK_GLOBAL_MUTEX
225     /* zero structure and fill in name; rest is done by internal routine */
226     tdir = (struct afsconf_dir *) malloc(sizeof(struct afsconf_dir));
227     bzero(tdir, sizeof(struct afsconf_dir));
228     tdir->name = (char *) malloc(strlen(adir)+1);
229     strcpy(tdir->name, adir);
230
231     code = afsconf_OpenInternal(tdir, 0, 0);
232     if (code) {
233         char *afsconf_path, *getenv(), afs_confdir[128];
234
235         free(tdir->name);
236         /* Check global place only when local Open failed for whatever reason */
237         if (!(afsconf_path = getenv("AFSCONF"))) {
238             /* 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... */
239             char *home_dir;
240             FILE *fp;
241             int len;
242
243             if (!(home_dir = getenv("HOME"))) {
244                 /* Our last chance is the "/.AFSCONF" file */
245                 fp = fopen("/.AFSCONF", "r");
246                 if (fp == 0) {
247                     free(tdir);
248                     UNLOCK_GLOBAL_MUTEX
249                     return (struct afsconf_dir *) 0;
250                 }
251                 fgets(afs_confdir, 128, fp);
252                 fclose(fp);
253             } else {
254                 char pathname[256];
255
256                 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
257                 fp = fopen(pathname, "r");
258                 if (fp == 0) {
259                     /* Our last chance is the "/.AFSCONF" file */
260                     fp = fopen("/.AFSCONF", "r");
261                     if (fp == 0) {
262                         free(tdir);
263                         UNLOCK_GLOBAL_MUTEX
264                         return (struct afsconf_dir *) 0;
265                     }
266                     fgets(afs_confdir, 128, fp);
267                     fclose(fp);
268                 }
269                 fgets(afs_confdir, 128, fp);
270                 fclose(fp);             
271             }
272             len = strlen(afs_confdir);
273             if (len == 0) {
274                 free(tdir);
275                 UNLOCK_GLOBAL_MUTEX
276                 return (struct afsconf_dir *) 0;
277             }
278             if (afs_confdir[len-1] == '\n') {
279                 afs_confdir[len-1] = 0;
280             }
281             afsconf_path = afs_confdir;
282         }
283         tdir->name = (char *) malloc(strlen(afsconf_path)+1);
284         strcpy(tdir->name, afsconf_path);
285         code = afsconf_OpenInternal(tdir, 0, 0);
286         if (code) {
287             free(tdir->name);
288             free(tdir);
289             UNLOCK_GLOBAL_MUTEX
290             return (struct afsconf_dir *) 0;
291         }
292     }
293     UNLOCK_GLOBAL_MUTEX
294     return tdir;
295 }
296
297
298 static int GetCellUnix(struct afsconf_dir *adir)
299 {
300     int rc;
301     char tbuffer[256];
302     FILE *tf;
303
304     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
305     tf = fopen(tbuffer, "r");
306     if (tf) {
307         rc = fscanf(tf, "%s", tbuffer);
308         if (rc == 1) {
309             adir->cellName = (char *) malloc(strlen(tbuffer)+1);
310             strcpy(adir->cellName, tbuffer);
311         }
312         fclose(tf);
313     }
314     else {
315         return -1;
316     }
317     return 0;
318 }
319
320
321 #ifdef AFS_NT40_ENV
322 static int GetCellNT(struct afsconf_dir *adir)
323 {
324     if (IsClientConfigDirectory(adir->name)) {
325         /* NT client config dir; ThisCell is in registry (no file). */
326         return afssw_GetClientCellName(&adir->cellName);
327     } else {
328         /* NT server config dir; works just like Unix */
329         return GetCellUnix(adir);
330     }
331 }
332 #endif /* AFS_NT40_ENV */
333
334
335 static int afsconf_OpenInternal(adir, cell, clones)
336 register struct afsconf_dir *adir; 
337 char *cell;
338 char clones[];
339 {
340     FILE *tf;
341     register char *tp, *bp;
342     register struct afsconf_entry *curEntry;
343     register afs_int32 code;
344     afs_int32 i;
345     char tbuffer[256], tbuf1[256];
346     struct stat tstat;
347
348     /* figure out the cell name */
349 #ifdef AFS_NT40_ENV
350     i = GetCellNT(adir);
351 #else
352     i = GetCellUnix(adir);
353 #endif
354     if (i) {
355         return i;
356     }
357
358     /* now parse the individual lines */
359     curEntry = 0;
360
361 #ifdef AFS_NT40_ENV
362     /* NT client/server have a CellServDB that is the same format as Unix.
363      * However, the NT client uses a different file name
364      */
365     if (IsClientConfigDirectory(adir->name)) {
366         /* NT client config dir */
367         strcompose(tbuffer, 256,
368                    adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
369     } else {
370         /* NT server config dir */
371         strcompose(tbuffer, 256,
372                    adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
373     }
374 #else
375     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
376 #endif  /* AFS_NT40_ENV */
377
378     if (!stat(tbuffer, &tstat)) {
379         adir->timeRead = tstat.st_mtime;
380     } else {
381         adir->timeRead = 0;
382     }
383
384     strcpy(tbuf1, tbuffer);
385     tf = fopen(tbuffer, "r");
386     if (!tf) {
387         return -1;
388     }
389     while (1) {
390         tp = fgets(tbuffer, sizeof(tbuffer), tf);
391         if (!tp) break;
392         TrimLine(tbuffer);  /* remove white space */
393         if (tbuffer[0] == 0 || tbuffer[0] == '\n') continue;   /* empty line */
394         if (tbuffer[0] == '>') {
395             char linkedcell[MAXCELLCHARS];
396             /* start new cell item */
397             if (curEntry) {
398                 /* thread this guy on the list */
399                 curEntry->next = adir->entries;
400                 adir->entries = curEntry;
401                 curEntry = 0;
402             }
403             curEntry = (struct afsconf_entry *) malloc(sizeof(struct afsconf_entry));
404             bzero(curEntry, sizeof(struct afsconf_entry));
405             code = ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
406             if (code) {
407                 afsconf_CloseInternal(adir);
408                 fclose(tf);
409                 return -1;
410             }
411             if (linkedcell[0] != '\0') {
412                 curEntry->cellInfo.linkedCell =
413                     (char *) malloc(strlen(linkedcell) + 1);
414                 strcpy(curEntry->cellInfo.linkedCell, linkedcell);
415             }
416         }
417         else {
418             /* new host in the current cell */
419             if (!curEntry) {
420                 afsconf_CloseInternal(adir);
421                 fclose(tf);
422                 return -1;
423             }
424             i = curEntry->cellInfo.numServers;
425            if (cell && !strcmp(cell, curEntry->cellInfo.name)) 
426                 code = ParseHostLine(tbuffer, (char *) &curEntry->cellInfo.hostAddr[i], curEntry->cellInfo.hostName[i], &clones[i]);
427            else
428                 code = ParseHostLine(tbuffer, (char *) &curEntry->cellInfo.hostAddr[i], curEntry->cellInfo.hostName[i], 0);
429             if (code) {
430                 if (code == AFSCONF_SYNTAX) {
431                     for (bp=tbuffer; *bp != '\n'; bp++) {       /* Take out the <cr> from the buffer */
432                         if (!*bp) break;
433                     }
434                     *bp= '\0';
435                     fprintf(stderr, "Can't properly parse host line \"%s\" in configuration file %s\n", tbuffer, tbuf1);
436                 }
437                 free(curEntry);
438                 fclose(tf);
439                 afsconf_CloseInternal(adir);
440                 return -1;
441             }
442             curEntry->cellInfo.numServers = ++i;
443         }
444     }
445     fclose(tf); /* close the file now */
446
447     /* end the last partially-completed cell */
448     if (curEntry) {
449         curEntry->next = adir->entries;
450         adir->entries = curEntry;
451     }
452     
453     /* now read the fs keys, if possible */
454     adir->keystr = (struct afsconf_keys *) 0;
455     afsconf_IntGetKeys(adir);
456
457     return 0;
458 }
459
460 /* parse a line of the form
461  *"128.2.1.3   #hostname" or
462  *"[128.2.1.3]  #hostname" for clones
463  * into the appropriate pieces.  
464  */
465 static ParseHostLine(aline, addr, aname, aclone)
466     char *aclone;
467     register struct sockaddr_in *addr;
468     char *aline, *aname; 
469 {
470     int c1, c2, c3, c4;
471     register afs_int32 code;
472     register char *tp;
473
474     if (*aline == '[') {
475         if (aclone) *aclone = 1;
476         code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
477     } else {
478         if (aclone) *aclone = 0;
479         code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
480     }
481     if (code != 5) return AFSCONF_SYNTAX;
482     addr->sin_family = AF_INET;
483     addr->sin_port = 0;
484     tp = (char *) &addr->sin_addr;
485     *tp++ = c1;
486     *tp++ = c2;
487     *tp++ = c3;
488     *tp++ = c4;
489     return 0;
490 }
491
492 /* parse a line of the form
493  * ">cellname [linkedcellname] [#comments]"
494  * into the appropriate pieces.
495  */
496 static ParseCellLine(aline, aname, alname)
497 register char *aline, *aname, *alname; {
498     register int code;
499     code = sscanf(aline, ">%s %s", aname, alname);
500     if (code == 1) *alname = '\0';
501     if (code == 2) {
502         if (*alname == '#') {
503             *alname = '\0';
504         }
505     }
506     return (code > 0 ? 0 : AFSCONF_SYNTAX);
507 }
508
509 /* call aproc(entry, arock, adir) for all cells.  Proc must return 0, or we'll stop early and return the code it returns */
510 afsconf_CellApply(adir, aproc, arock)
511 struct afsconf_dir *adir;
512 int (*aproc)();
513 char *arock; {
514     register struct afsconf_entry *tde;
515     register afs_int32 code;
516     LOCK_GLOBAL_MUTEX
517     for(tde=adir->entries; tde; tde=tde->next) {
518         code = (*aproc)(&tde->cellInfo, arock, adir);
519         if (code) {
520             UNLOCK_GLOBAL_MUTEX
521             return code;
522         }
523     }
524     UNLOCK_GLOBAL_MUTEX
525     return 0;
526 }
527
528 afs_int32 afsconf_SawCell = 0;
529
530 afsconf_GetExtendedCellInfo(adir, acellName, aservice, acellInfo, clones)
531     struct afsconf_dir *adir;
532     char *aservice;
533     char *acellName;
534     struct afsconf_cell *acellInfo; 
535     char clones[];
536 {
537     afs_int32 code;
538     char *cell;
539
540     code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
541     if (code) 
542        return code;
543
544     if (acellName) 
545        cell = acellName;
546     else
547        cell = (char *) &acellInfo->name;
548
549     code = afsconf_OpenInternal(adir, cell, clones);
550     return code;
551 }
552
553 #ifdef AFS_AFSDB_ENV
554 afsconf_GetAfsdbInfo(acellName, aservice, acellInfo)
555     char *acellName;
556     char *aservice;
557     struct afsconf_cell *acellInfo;
558 {
559     afs_int32 code;
560     int tservice, len, i;
561     unsigned char answer[1024];
562     unsigned char *p;
563     char host[256];
564     int server_num = 0;
565     int minttl = 0;
566
567     /* The resolver isn't always MT-safe.. Perhaps this ought to be
568      * replaced with a more fine-grained lock just for the resolver
569      * operations.
570      */
571     LOCK_GLOBAL_MUTEX
572     len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
573     UNLOCK_GLOBAL_MUTEX
574
575     if (len < 0)
576         return AFSCONF_NOTFOUND;
577
578     p = answer + sizeof(HEADER);        /* Skip header */
579     code = dn_expand(answer, answer + len, p, host, sizeof(host));
580     if (code < 0)
581         return AFSCONF_NOTFOUND;
582     strncpy(acellInfo->name, host, sizeof(acellInfo->name));
583
584     p += code + QFIXEDSZ;       /* Skip name */
585
586     while (p < answer + len) {
587         int type, ttl, size;
588
589         code = dn_expand(answer, answer + len, p, host, sizeof(host));
590         if (code < 0)
591             return AFSCONF_NOTFOUND;
592
593         p += code;      /* Skip the name */
594         type = (p[0] << 8) | p[1];
595         p += 4;         /* Skip type and class */
596         ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
597         p += 4;         /* Skip the TTL */
598         size = (p[0] << 8) | p[1];
599         p += 2;         /* Skip the size */
600
601         if (type == T_AFSDB) {
602             struct hostent *he;
603
604             code = dn_expand(answer, answer+len, p+2, host, sizeof(host));
605             if (code < 0)
606                 return AFSCONF_NOTFOUND;
607
608             /* Do we want to get TTL data for the A record as well? */
609             he = gethostbyname(host);
610             if (he && server_num < MAXHOSTSPERCELL) {
611                 afs_int32 ipaddr;
612                 memcpy(&ipaddr, he->h_addr, he->h_length);
613                 acellInfo->hostAddr[server_num].sin_addr.s_addr = ipaddr;
614                 strncpy(acellInfo->hostName[server_num], host,
615                         sizeof(acellInfo->hostName[server_num]));
616                 server_num++;
617
618                 if (!minttl || ttl < minttl) minttl = ttl;
619             }
620         }
621
622         p += size;
623     }
624
625     if (server_num == 0)                /* No AFSDB records */
626         return AFSCONF_NOTFOUND;
627     acellInfo->numServers = server_num;
628
629     if (aservice) {
630         tservice = afsconf_FindService(aservice);
631         if (tservice < 0)
632             return AFSCONF_NOTFOUND;  /* service not found */
633         for (i=0; i<acellInfo->numServers; i++) {
634             acellInfo->hostAddr[i].sin_port = tservice;
635         }
636     }
637
638     acellInfo->timeout = minttl ? (time(0) + minttl) : 0;
639
640     return 0;
641 }
642 #endif /* AFS_AFSDB_ENV */
643
644 afsconf_GetCellInfo(adir, acellName, aservice, acellInfo)
645 struct afsconf_dir *adir;
646 char *aservice;
647 char *acellName;
648 struct afsconf_cell *acellInfo; {
649     register struct afsconf_entry *tce;
650     struct afsconf_entry *bestce;
651     register afs_int32 i;
652     int tservice;
653     char *tcell;
654     int cnLen, ambig;
655     char tbuffer[64];
656
657     LOCK_GLOBAL_MUTEX
658     if (adir) afsconf_Check(adir);
659     if (acellName) {
660         tcell = acellName;
661         cnLen = strlen(tcell)+1;
662         lcstring (tcell, tcell, cnLen);
663         afsconf_SawCell = 1;    /* will ignore the AFSCELL switch on future */
664                                 /* call to afsconf_GetLocalCell: like klog  */
665     } else {
666         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
667         if (i) {
668             UNLOCK_GLOBAL_MUTEX
669             return i;
670         }
671         tcell = tbuffer;
672     }
673     cnLen = strlen(tcell);
674     bestce = (struct afsconf_entry *) 0;
675     ambig = 0;
676     if (!adir) {
677         UNLOCK_GLOBAL_MUTEX
678         return 0;
679     }
680     for(tce=adir->entries;tce;tce=tce->next) {
681         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
682             /* found our cell */
683             bestce = tce;
684             ambig = 0;
685             break;
686         }
687         if (strlen(tce->cellInfo.name) < cnLen) continue;   /* clearly wrong */
688         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
689             if (bestce) ambig = 1;  /* ambiguous unless we get exact match */
690             bestce = tce;
691         }
692     }
693     if (!ambig && bestce) {
694         *acellInfo = bestce->cellInfo;  /* structure assignment */
695         if (aservice) {
696             tservice = afsconf_FindService(aservice);
697             if (tservice < 0) {
698                 UNLOCK_GLOBAL_MUTEX
699                 return AFSCONF_NOTFOUND;  /* service not found */
700             }
701             for(i=0;i<acellInfo->numServers;i++) {
702                 acellInfo->hostAddr[i].sin_port = tservice;
703             }
704         }
705         acellInfo->timeout = 0;
706         UNLOCK_GLOBAL_MUTEX
707         return 0;
708     }
709     else {
710         UNLOCK_GLOBAL_MUTEX
711 #ifdef AFS_AFSDB_ENV
712         return afsconf_GetAfsdbInfo(acellName, aservice, acellInfo);
713 #else
714         return AFSCONF_NOTFOUND;
715 #endif /* AFS_AFSDB_ENV */
716     }
717 }
718
719 afsconf_GetLocalCell(adir, aname, alen)
720 register struct afsconf_dir *adir;
721 char *aname;
722 afs_int32 alen; {
723     static int  afsconf_showcell = 0;
724     char        *afscell_path;
725     char        *getenv();
726     afs_int32        code = 0;
727
728    LOCK_GLOBAL_MUTEX
729    /*
730     * If a cell switch was specified in a command, then it should override the 
731     * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
732     * is set and the cell name in the adir structure is used.
733     * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
734     */
735    if ( !afsconf_SawCell && (afscell_path= getenv("AFSCELL")) ) {     
736         if ( !afsconf_showcell ) {
737             fprintf(stderr, "Note: Operation is performed on cell %s\n", afscell_path);
738             afsconf_showcell = 1;
739         }
740         strncpy(aname, afscell_path, alen);
741     } else {                                    
742         afsconf_Check(adir);
743         if (adir->cellName) {
744             strncpy(aname, adir->cellName, alen);
745         }
746         else code = AFSCONF_UNKNOWN;
747     }
748
749     UNLOCK_GLOBAL_MUTEX
750     return(code);
751 }
752
753 afsconf_Close(adir)
754 struct afsconf_dir *adir; {
755     LOCK_GLOBAL_MUTEX
756     afsconf_CloseInternal(adir);
757     if (adir->name) free(adir->name);
758     free(adir);
759     UNLOCK_GLOBAL_MUTEX
760     return 0;
761 }
762
763 static int afsconf_CloseInternal(adir)
764 register struct afsconf_dir *adir; {
765     register struct afsconf_entry *td, *nd;
766     register char *tname;
767
768     tname = adir->name; /* remember name, since that's all we preserve */
769
770     /* free everything we can find */
771     if (adir->cellName) free(adir->cellName);
772     for(td=adir->entries;td;td=nd) {
773         nd = td->next;
774         if (td->cellInfo.linkedCell)
775             free(td->cellInfo.linkedCell);
776         free(td);
777     }
778     if (adir->keystr) free(adir->keystr);
779
780     /* reinit */
781     bzero(adir, sizeof(struct afsconf_dir));
782     adir->name = tname;     /* restore it */
783     return 0;
784 }
785
786 static int afsconf_Reopen(adir)
787 register struct afsconf_dir *adir; {
788     register afs_int32 code;
789     code = afsconf_CloseInternal(adir);
790     if (code) return code;
791     code = afsconf_OpenInternal(adir, 0, 0);
792     return code;
793 }
794
795 /* called during opening of config file */
796 afsconf_IntGetKeys(adir)
797 struct afsconf_dir *adir;
798 {
799     char tbuffer[256];
800     register int fd;
801     struct afsconf_keys *tstr;
802     register afs_int32 code;
803
804 #ifdef AFS_NT40_ENV
805     /* NT client config dir has no KeyFile; don't risk attempting open
806      * because there might be a random file of this name if dir is shared.
807      */
808     if (IsClientConfigDirectory(adir->name)) {
809         adir->keystr = ((struct afsconf_keys *)
810                         malloc(sizeof(struct afsconf_keys)));
811         adir->keystr->nkeys = 0;
812         return 0;
813     }
814 #endif /* AFS_NT40_ENV */
815
816     LOCK_GLOBAL_MUTEX
817     /* compute the key name and other setup */
818
819     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
820     tstr = (struct afsconf_keys *) malloc(sizeof (struct afsconf_keys));
821     adir->keystr = tstr;
822
823     /* read key file */
824     fd = open(tbuffer, O_RDONLY);
825     if (fd < 0) {
826         tstr->nkeys = 0;
827         UNLOCK_GLOBAL_MUTEX
828         return 0;
829     }
830     code = read(fd, tstr, sizeof(struct afsconf_keys));
831     close(fd);
832     if (code < sizeof(afs_int32)) {
833         tstr->nkeys = 0;
834         UNLOCK_GLOBAL_MUTEX
835         return 0;
836     }
837
838     /* convert key structure to host order */
839     tstr->nkeys = ntohl(tstr->nkeys);
840     for(fd=0;fd<tstr->nkeys;fd++)
841         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
842
843     UNLOCK_GLOBAL_MUTEX
844     return 0;
845 }
846
847 /* get keys structure */
848 afsconf_GetKeys(adir, astr)
849 struct afsconf_dir *adir;
850 struct afsconf_keys *astr;
851 {
852     register afs_int32 code;
853
854     LOCK_GLOBAL_MUTEX
855     code = afsconf_Check(adir);
856     if (code)
857         return AFSCONF_FAILURE;
858     bcopy(adir->keystr, astr, sizeof(struct afsconf_keys));
859     UNLOCK_GLOBAL_MUTEX
860     return 0;
861 }
862
863 /* get latest key */
864 afs_int32 afsconf_GetLatestKey(adir, avno, akey)
865   IN struct afsconf_dir *adir;
866   OUT afs_int32 *avno;
867   OUT char *akey;
868 {
869     register int i;
870     int maxa;
871     register struct afsconf_key *tk;
872     register afs_int32 best;
873     struct afsconf_key *bestk;
874     register afs_int32 code;
875     
876     LOCK_GLOBAL_MUTEX
877     code = afsconf_Check(adir);
878     if (code)
879         return AFSCONF_FAILURE;
880     maxa = adir->keystr->nkeys;
881
882     best = -1;      /* highest kvno we've seen yet */
883     bestk = (struct afsconf_key *) 0;   /* ptr to structure providing best */
884     for(tk = adir->keystr->key,i=0;i<maxa;i++,tk++) {
885         if (tk->kvno == 999) continue;  /* skip bcrypt keys */
886         if (tk->kvno > best) {
887             best = tk->kvno;
888             bestk = tk;
889         }
890     }
891     if (bestk) {    /* found any  */
892         if (akey) bcopy(bestk->key, akey, 8); /* copy out latest key */
893         if (avno) *avno = bestk->kvno;  /* and kvno to caller */
894         UNLOCK_GLOBAL_MUTEX
895         return 0;
896     }
897     UNLOCK_GLOBAL_MUTEX
898     return AFSCONF_NOTFOUND;    /* didn't find any keys */
899 }
900
901 /* get a particular key */
902 afsconf_GetKey(adir, avno, akey)
903 struct afsconf_dir *adir;
904 afs_int32 avno;
905 char *akey;
906 {
907     register int i, maxa;
908     register struct afsconf_key *tk;
909     register afs_int32 code;
910
911     LOCK_GLOBAL_MUTEX
912     code = afsconf_Check(adir);
913     if (code)
914         return AFSCONF_FAILURE;
915     maxa = adir->keystr->nkeys;
916
917     for(tk = adir->keystr->key,i=0;i<maxa;i++,tk++) {
918         if (tk->kvno == avno) {
919             bcopy(tk->key, akey, 8);
920             UNLOCK_GLOBAL_MUTEX
921             return 0;
922         }
923     }
924
925     UNLOCK_GLOBAL_MUTEX
926     return AFSCONF_NOTFOUND;
927 }
928
929 /* save the key structure in the appropriate file */
930 static SaveKeys(adir)
931 struct afsconf_dir *adir;
932 {
933     struct afsconf_keys tkeys;
934     register int fd;
935     register afs_int32 i;
936     char tbuffer[256];
937
938     bcopy(adir->keystr, &tkeys, sizeof(struct afsconf_keys));
939
940     /* convert it to net byte order */
941     for(i = 0; i<tkeys.nkeys; i++ )
942         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
943     tkeys.nkeys = htonl(tkeys.nkeys);
944     
945     /* rewrite keys file */
946     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
947     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
948     if (fd < 0) return AFSCONF_FAILURE;
949     i = write(fd, &tkeys, sizeof(tkeys));
950     if (i != sizeof(tkeys)) {
951         close(fd);
952         return AFSCONF_FAILURE;
953     }
954     if (close(fd) < 0) return AFSCONF_FAILURE;
955     return 0;
956 }
957
958 afsconf_AddKey(adir, akvno, akey, overwrite)
959 struct afsconf_dir *adir;
960 afs_int32 akvno, overwrite;
961 char akey[8];
962 {
963     register struct afsconf_keys *tk;
964     register struct afsconf_key *tkey;
965     register afs_int32 i;
966     int foundSlot;
967
968     LOCK_GLOBAL_MUTEX
969     tk = adir->keystr;
970     
971     if (akvno != 999) {
972         if (akvno < 0 || akvno > 255) {
973             UNLOCK_GLOBAL_MUTEX
974             return ERANGE;
975         }
976     }
977     foundSlot = 0;
978     for(i=0, tkey = tk->key; i<tk->nkeys; i++, tkey++) {
979         if (tkey->kvno == akvno) {
980             if (!overwrite) {
981                 UNLOCK_GLOBAL_MUTEX
982                 return AFSCONF_KEYINUSE;
983             }
984             foundSlot = 1;
985             break;
986         }
987     }
988     if (!foundSlot) {
989         if (tk->nkeys >= AFSCONF_MAXKEYS) {
990             UNLOCK_GLOBAL_MUTEX
991             return AFSCONF_FULL;
992         }
993         tkey = &tk->key[tk->nkeys++];
994     }
995     tkey->kvno = akvno;
996     bcopy(akey, tkey->key, 8);
997     i = SaveKeys(adir);
998     afsconf_Touch(adir);
999     UNLOCK_GLOBAL_MUTEX
1000     return i;
1001 }
1002
1003 /* this proc works by sliding the other guys down, rather than using a funny
1004     kvno value, so that callers can count on getting a good key in key[0].
1005 */
1006 afsconf_DeleteKey(adir, akvno)
1007 struct afsconf_dir *adir;
1008 afs_int32 akvno;
1009 {
1010     register struct afsconf_keys *tk;
1011     register struct afsconf_key *tkey;
1012     register int i;
1013     int foundFlag = 0;
1014
1015     LOCK_GLOBAL_MUTEX
1016     tk = adir->keystr;
1017
1018     for(i=0, tkey = tk->key; i<tk->nkeys; i++, tkey++) {
1019         if (tkey->kvno == akvno) {
1020             foundFlag = 1;
1021             break;
1022         }
1023     }
1024     if (!foundFlag) {
1025         UNLOCK_GLOBAL_MUTEX
1026         return AFSCONF_NOTFOUND;
1027     }
1028
1029     /* otherwise slide the others down.  i and tkey point at the guy to delete */
1030     for(;i<tk->nkeys-1; i++,tkey++) {
1031         tkey->kvno = (tkey+1)->kvno;
1032         bcopy((tkey+1)->key, tkey->key, 8);
1033     }
1034     tk->nkeys--;
1035     i = SaveKeys(adir);
1036     afsconf_Touch(adir);
1037     UNLOCK_GLOBAL_MUTEX
1038     return i;
1039 }