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