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