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