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