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