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