Change __signed to signed.
[dragonfly.git] / crypto / kerberosIV / lib / kdb / krb_dbm.c
1 /* 
2   Copyright (C) 1989 by the Massachusetts Institute of Technology
3
4    Export of this software from the United States of America is assumed
5    to require a specific license from the United States Government.
6    It is the responsibility of any person or organization contemplating
7    export to obtain such a license before exporting.
8
9 WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
10 distribute this software and its documentation for any purpose and
11 without fee is hereby granted, provided that the above copyright
12 notice appear in all copies and that both that copyright notice and
13 this permission notice appear in supporting documentation, and that
14 the name of M.I.T. not be used in advertising or publicity pertaining
15 to distribution of the software without specific, written prior
16 permission.  M.I.T. makes no representations about the suitability of
17 this software for any purpose.  It is provided "as is" without express
18 or implied warranty.
19
20   */
21
22 #include "kdb_locl.h"
23
24 RCSID("$Id: krb_dbm.c,v 1.37 1999/09/16 20:41:49 assar Exp $");
25
26 #include <xdbm.h>
27
28 #define KERB_DB_MAX_RETRY 5
29
30 #ifdef DEBUG
31 extern int debug;
32 extern long kerb_debug;
33 extern char *progname;
34 #endif
35
36 static int init = 0;
37 static char default_db_name[] = DBM_FILE;
38 static char *current_db_name = default_db_name;
39
40 static struct timeval timestamp;/* current time of request */
41 static int non_blocking = 0;
42
43 /*
44  * This module contains all of the code which directly interfaces to
45  * the underlying representation of the Kerberos database; this
46  * implementation uses a DBM or NDBM indexed "file" (actually
47  * implemented as two separate files) to store the relations, plus a
48  * third file as a semaphore to allow the database to be replaced out
49  * from underneath the KDC server.
50  */
51
52 /*
53  * Locking:
54  * 
55  * There are two distinct locking protocols used.  One is designed to
56  * lock against processes (the admin_server, for one) which make
57  * incremental changes to the database; the other is designed to lock
58  * against utilities (kdb_util, kpropd) which replace the entire
59  * database in one fell swoop.
60  *
61  * The first locking protocol is implemented using flock() in the 
62  * krb_dbl_lock() and krb_dbl_unlock routines.
63  *
64  * The second locking protocol is necessary because DBM "files" are
65  * actually implemented as two separate files, and it is impossible to
66  * atomically rename two files simultaneously.  It assumes that the
67  * database is replaced only very infrequently in comparison to the time
68  * needed to do a database read operation.
69  *
70  * A third file is used as a "version" semaphore; the modification
71  * time of this file is the "version number" of the database.
72  * At the start of a read operation, the reader checks the version
73  * number; at the end of the read operation, it checks again.  If the
74  * version number changed, or if the semaphore was nonexistant at
75  * either time, the reader sleeps for a second to let things
76  * stabilize, and then tries again; if it does not succeed after
77  * KERB_DB_MAX_RETRY attempts, it gives up.
78  * 
79  * On update, the semaphore file is deleted (if it exists) before any
80  * update takes place; at the end of the update, it is replaced, with
81  * a version number strictly greater than the version number which
82  * existed at the start of the update.
83  * 
84  * If the system crashes in the middle of an update, the semaphore
85  * file is not automatically created on reboot; this is a feature, not
86  * a bug, since the database may be inconsistant.  Note that the
87  * absence of a semaphore file does not prevent another _update_ from
88  * taking place later.  Database replacements take place automatically
89  * only on slave servers; a crash in the middle of an update will be
90  * fixed by the next slave propagation.  A crash in the middle of an
91  * update on the master would be somewhat more serious, but this would
92  * likely be noticed by an administrator, who could fix the problem and
93  * retry the operation.
94  */
95
96
97 /*
98  * Utility routine: generate name of database file.
99  */
100
101 static char *
102 gen_dbsuffix(char *db_name, char *sfx)
103 {
104     char *dbsuffix;
105     
106     if (sfx == NULL)
107         sfx = ".ok";
108
109     asprintf (&dbsuffix, "%s%s", db_name, sfx);
110     if (dbsuffix == NULL) {
111         fprintf (stderr, "gen_dbsuffix: out of memory\n");
112         exit(1);
113     }
114     return dbsuffix;
115 }
116
117 static void
118 decode_princ_key(datum *key, char *name, char *instance)
119 {
120     strlcpy (name, key->dptr, ANAME_SZ);
121     strlcpy (instance, (char *)key->dptr + ANAME_SZ, INST_SZ);
122 }
123
124 static void
125 encode_princ_contents(datum *contents, Principal *principal)
126 {
127     contents->dsize = sizeof(*principal);
128     contents->dptr = (char *) principal;
129 }
130
131 static void
132 decode_princ_contents (datum *contents, Principal *principal)
133 {
134     memcpy(principal, contents->dptr, sizeof(*principal));
135 }
136
137 static void
138 encode_princ_key (datum *key, char *name, char *instance)
139 {
140     static char keystring[ANAME_SZ + INST_SZ];
141
142     memset(keystring, 0, ANAME_SZ + INST_SZ);
143     strncpy(keystring, name, ANAME_SZ);
144     strncpy(&keystring[ANAME_SZ], instance, INST_SZ);
145     key->dptr = keystring;
146     key->dsize = ANAME_SZ + INST_SZ;
147 }
148
149 static int dblfd = -1;          /* db LOCK fd */
150 static int mylock = 0;
151 static int inited = 0;
152
153 static int
154 kerb_dbl_init(void)
155 {
156     if (!inited) {
157         char *filename = gen_dbsuffix (current_db_name, ".ok");
158         if ((dblfd = open(filename, O_RDWR)) < 0) {
159             fprintf(stderr, "kerb_dbl_init: couldn't open %s\n", filename);
160             fflush(stderr);
161             perror("open");
162             exit(1);
163         }
164         free(filename);
165         inited++;
166     }
167     return (0);
168 }
169
170 static void
171 kerb_dbl_fini(void)
172 {
173     close(dblfd);
174     dblfd = -1;
175     inited = 0;
176     mylock = 0;
177 }
178
179 static int
180 kerb_dbl_lock(int mode)
181 {
182     int flock_mode;
183     
184     if (!inited)
185         kerb_dbl_init();
186     if (mylock) {               /* Detect lock call when lock already
187                                  * locked */
188         fprintf(stderr, "Kerberos locking error (mylock)\n");
189         fflush(stderr);
190         exit(1);
191     }
192     switch (mode) {
193     case KERB_DBL_EXCLUSIVE:
194         flock_mode = LOCK_EX;
195         break;
196     case KERB_DBL_SHARED:
197         flock_mode = LOCK_SH;
198         break;
199     default:
200         fprintf(stderr, "invalid lock mode %d\n", mode);
201         abort();
202     }
203     if (non_blocking)
204         flock_mode |= LOCK_NB;
205     
206     if (flock(dblfd, flock_mode) < 0) 
207         return errno;
208     mylock++;
209     return 0;
210 }
211
212 static void
213 kerb_dbl_unlock(void)
214 {
215     if (!mylock) {              /* lock already unlocked */
216         fprintf(stderr, "Kerberos database lock not locked when unlocking.\n");
217         fflush(stderr);
218         exit(1);
219     }
220     if (flock(dblfd, LOCK_UN) < 0) {
221         fprintf(stderr, "Kerberos database lock error. (unlocking)\n");
222         fflush(stderr);
223         perror("flock");
224         exit(1);
225     }
226     mylock = 0;
227 }
228
229 int
230 kerb_db_set_lockmode(int mode)
231 {
232     int old = non_blocking;
233     non_blocking = mode;
234     return old;
235 }
236
237 /*
238  * initialization for data base routines.
239  */
240
241 int
242 kerb_db_init(void)
243 {
244     init = 1;
245     return (0);
246 }
247
248 /*
249  * gracefully shut down database--must be called by ANY program that does
250  * a kerb_db_init 
251  */
252
253 void
254 kerb_db_fini(void)
255 {
256 }
257
258 /*
259  * Set the "name" of the current database to some alternate value.
260  *
261  * Passing a null pointer as "name" will set back to the default.
262  * If the alternate database doesn't exist, nothing is changed.
263  */
264
265 int
266 kerb_db_set_name(char *name)
267 {
268     DBM *db;
269
270     if (name == NULL)
271         name = default_db_name;
272     db = dbm_open(name, 0, 0);
273     if (db == NULL)
274         return errno;
275     dbm_close(db);
276     kerb_dbl_fini();
277     current_db_name = name;
278     return 0;
279 }
280
281 /*
282  * Return the last modification time of the database.
283  */
284
285 time_t
286 kerb_get_db_age(void)
287 {
288     struct stat st;
289     char *okname;
290     time_t age;
291     
292     okname = gen_dbsuffix(current_db_name, ".ok");
293
294     if (stat (okname, &st) < 0)
295         age = 0;
296     else
297         age = st.st_mtime;
298
299     free (okname);
300     return age;
301 }
302
303 /*
304  * Remove the semaphore file; indicates that database is currently
305  * under renovation.
306  *
307  * This is only for use when moving the database out from underneath
308  * the server (for example, during slave updates).
309  */
310
311 static time_t
312 kerb_start_update(char *db_name)
313 {
314     char *okname = gen_dbsuffix(db_name, ".ok");
315     time_t age = kerb_get_db_age();
316     
317     if (unlink(okname) < 0
318         && errno != ENOENT) {
319             age = -1;
320     }
321     free (okname);
322     return age;
323 }
324
325 static int
326 kerb_end_update(char *db_name, time_t age)
327 {
328     int fd;
329     int retval = 0;
330     char *new_okname = gen_dbsuffix(db_name, ".ok#");
331     char *okname = gen_dbsuffix(db_name, ".ok");
332     
333     fd = open (new_okname, O_CREAT|O_RDWR|O_TRUNC, 0600);
334     if (fd < 0)
335         retval = errno;
336     else {
337         struct stat st;
338         struct utimbuf tv;
339         /* make sure that semaphore is "after" previous value. */
340         if (fstat (fd, &st) == 0
341             && st.st_mtime <= age) {
342             tv.actime = st.st_atime;
343             tv.modtime = age;
344             /* set times.. */
345             utime (new_okname, &tv);
346             fsync(fd);
347         }
348         close(fd);
349         if (rename (new_okname, okname) < 0)
350             retval = errno;
351     }
352
353     free (new_okname);
354     free (okname);
355
356     return retval;
357 }
358
359 static time_t
360 kerb_start_read(void)
361 {
362     return kerb_get_db_age();
363 }
364
365 static int
366 kerb_end_read(time_t age)
367 {
368     if (kerb_get_db_age() != age || age == -1) {
369         return -1;
370     }
371     return 0;
372 }
373
374 /*
375  * Create the database, assuming it's not there.
376  */
377 int
378 kerb_db_create(char *db_name)
379 {
380     char *okname = gen_dbsuffix(db_name, ".ok");
381     int fd;
382     int ret = 0;
383 #ifdef NDBM
384     DBM *db;
385
386     db = dbm_open(db_name, O_RDWR|O_CREAT|O_EXCL, 0600);
387     if (db == NULL)
388         ret = errno;
389     else
390         dbm_close(db);
391 #else
392     char *dirname = gen_dbsuffix(db_name, ".dir");
393     char *pagname = gen_dbsuffix(db_name, ".pag");
394
395     fd = open(dirname, O_RDWR|O_CREAT|O_EXCL, 0600);
396     if (fd < 0)
397         ret = errno;
398     else {
399         close(fd);
400         fd = open (pagname, O_RDWR|O_CREAT|O_EXCL, 0600);
401         if (fd < 0)
402             ret = errno;
403         else
404             close(fd);
405     }
406     if (dbminit(db_name) < 0)
407         ret = errno;
408 #endif
409     if (ret == 0) {
410         fd = open (okname, O_CREAT|O_RDWR|O_TRUNC, 0600);
411         if (fd < 0)
412             ret = errno;
413         close(fd);
414     }
415     return ret;
416 }
417
418 /*
419  * "Atomically" rename the database in a way that locks out read
420  * access in the middle of the rename.
421  *
422  * Not perfect; if we crash in the middle of an update, we don't
423  * necessarily know to complete the transaction the rename, but...
424  */
425
426 int
427 kerb_db_rename(char *from, char *to)
428 {
429 #ifdef HAVE_NEW_DB
430     char *fromdb = gen_dbsuffix (from, ".db");
431     char *todb = gen_dbsuffix (to, ".db");
432 #else
433     char *fromdir = gen_dbsuffix (from, ".dir");
434     char *todir = gen_dbsuffix (to, ".dir");
435     char *frompag = gen_dbsuffix (from , ".pag");
436     char *topag = gen_dbsuffix (to, ".pag");
437 #endif
438     char *fromok = gen_dbsuffix(from, ".ok");
439     long trans = kerb_start_update(to);
440     int ok = 0;
441     
442 #ifdef HAVE_NEW_DB
443     if (rename (fromdb, todb) == 0) {
444         unlink (fromok);
445         ok = 1;
446     }
447     free (fromdb);
448     free (todb);
449 #else
450     if ((rename (fromdir, todir) == 0)
451         && (rename (frompag, topag) == 0)) {
452         unlink (fromok);
453         ok = 1;
454     }
455     free (fromdir);
456     free (todir);
457     free (frompag);
458     free (topag);
459 #endif
460     free (fromok);
461     if (ok)
462         return kerb_end_update(to, trans);
463     else
464         return -1;
465 }
466
467 int
468 kerb_db_delete_principal (char *name, char *inst)
469 {
470     DBM *db;
471     int try;
472     int done = 0;
473     int code;
474     datum key;
475     
476     if(!init)
477         kerb_db_init();
478     
479     for(try = 0; try < KERB_DB_MAX_RETRY; try++){
480         if((code = kerb_dbl_lock(KERB_DBL_EXCLUSIVE)) != 0)
481             return -1;
482         
483         db = dbm_open(current_db_name, O_RDWR, 0600);
484         if(db == NULL)
485             return -1;
486         encode_princ_key(&key, name, inst);
487         if(dbm_delete(db, key) == 0)
488             done = 1;
489         
490         dbm_close(db);
491         kerb_dbl_unlock();
492         if(done)
493             break;
494         if(!non_blocking)
495             sleep(1);
496     }
497     if(!done)
498         return -1;
499     return 0;
500 }
501
502
503 /*
504  * look up a principal in the data base returns number of principals
505  * found , and whether there were more than requested. 
506  */
507
508 int
509 kerb_db_get_principal (char *name, char *inst, Principal *principal, 
510                        unsigned int max, int *more)
511 {
512     int     found = 0, code;
513     int     wildp, wildi;
514     datum   key, contents;
515     char    testname[ANAME_SZ], testinst[INST_SZ];
516     u_long trans;
517     int try;
518     DBM    *db;
519
520     if (!init)
521         kerb_db_init();         /* initialize database routines */
522
523     for (try = 0; try < KERB_DB_MAX_RETRY; try++) {
524         trans = kerb_start_read();
525
526         if ((code = kerb_dbl_lock(KERB_DBL_SHARED)) != 0)
527             return -1;
528
529         db = dbm_open(current_db_name, O_RDONLY, 0600);
530         if (db == NULL)
531             return -1;
532
533         *more = 0;
534
535 #ifdef DEBUG
536         if (kerb_debug & 2)
537             fprintf(stderr,
538                     "%s: db_get_principal for %s %s max = %d",
539                     progname, name, inst, max);
540 #endif
541
542         wildp = !strcmp(name, "*");
543         wildi = !strcmp(inst, "*");
544
545         if (!wildi && !wildp) { /* nothing's wild */
546             encode_princ_key(&key, name, inst);
547             contents = dbm_fetch(db, key);
548             if (contents.dptr == NULL) {
549                 found = 0;
550                 goto done;
551             }
552             decode_princ_contents(&contents, principal);
553 #ifdef DEBUG
554             if (kerb_debug & 1) {
555                 fprintf(stderr, "\t found %s %s p_n length %d t_n length %d\n",
556                         principal->name, principal->instance,
557                         strlen(principal->name),
558                         strlen(principal->instance));
559             }
560 #endif
561             found = 1;
562             goto done;
563         }
564         /* process wild cards by looping through entire database */
565
566         for (key = dbm_firstkey(db); key.dptr != NULL;
567              key = dbm_next(db, key)) {
568             decode_princ_key(&key, testname, testinst);
569             if ((wildp || !strcmp(testname, name)) &&
570                 (wildi || !strcmp(testinst, inst))) { /* have a match */
571                 if (found >= max) {
572                     *more = 1;
573                     goto done;
574                 } else {
575                     found++;
576                     contents = dbm_fetch(db, key);
577                     decode_princ_contents(&contents, principal);
578 #ifdef DEBUG
579                     if (kerb_debug & 1) {
580                         fprintf(stderr,
581                                 "\tfound %s %s p_n length %d t_n length %d\n",
582                                 principal->name, principal->instance,
583                                 strlen(principal->name),
584                                 strlen(principal->instance));
585                     }
586 #endif
587                     principal++; /* point to next */
588                 }
589             }
590         }
591
592     done:
593         kerb_dbl_unlock();      /* unlock read lock */
594         dbm_close(db);
595         if (kerb_end_read(trans) == 0)
596             break;
597         found = -1;
598         if (!non_blocking)
599             sleep(1);
600     }
601     return (found);
602 }
603
604 /* Use long * rather than DBM * so that the database structure is private */
605
606 long *
607 kerb_db_begin_update(void)
608 {
609     int code;
610
611     gettimeofday(&timestamp, NULL);
612
613     if (!init)
614         kerb_db_init();
615
616     if ((code = kerb_dbl_lock(KERB_DBL_EXCLUSIVE)) != 0)
617         return 0;
618
619     return (long *) dbm_open(current_db_name, O_RDWR, 0600);
620 }
621
622 void
623 kerb_db_end_update(long *db)
624 {
625     dbm_close((DBM *)db);
626     kerb_dbl_unlock();          /* unlock database */
627 }
628
629 int
630 kerb_db_update(long *db, Principal *principal, unsigned int max)
631 {
632     int     found = 0;
633     u_long  i;
634     datum   key, contents;
635
636 #ifdef DEBUG
637     if (kerb_debug & 2)
638         fprintf(stderr, "%s: kerb_db_put_principal  max = %d",
639             progname, max);
640 #endif
641
642     /* for each one, stuff temps, and do replace/append */
643     for (i = 0; i < max; i++) {
644         encode_princ_contents(&contents, principal);
645         encode_princ_key(&key, principal->name, principal->instance);
646         if(dbm_store((DBM *)db, key, contents, DBM_REPLACE) < 0)
647             return found; /* XXX some better mechanism to report
648                              failure should exist */
649 #ifdef DEBUG
650         if (kerb_debug & 1) {
651             fprintf(stderr, "\n put %s %s\n",
652                 principal->name, principal->instance);
653         }
654 #endif
655         found++;
656         principal++;            /* bump to next struct                     */
657     }
658     return found;
659 }
660
661 /*
662  * Update a name in the data base.  Returns number of names
663  * successfully updated.
664  */
665
666 int
667 kerb_db_put_principal(Principal *principal,
668                       unsigned max)
669
670 {
671     int found;
672     long    *db;
673
674     db = kerb_db_begin_update();
675     if (db == 0)
676         return -1;
677
678     found = kerb_db_update(db, principal, max);
679
680     kerb_db_end_update(db);
681     return (found);
682 }
683
684 void
685 kerb_db_get_stat(DB_stat *s)
686 {
687     gettimeofday(&timestamp, NULL);
688
689     s->cpu = 0;
690     s->elapsed = 0;
691     s->dio = 0;
692     s->pfault = 0;
693     s->t_stamp = timestamp.tv_sec;
694     s->n_retrieve = 0;
695     s->n_replace = 0;
696     s->n_append = 0;
697     s->n_get_stat = 0;
698     s->n_put_stat = 0;
699     /* update local copy too */
700 }
701
702 void
703 kerb_db_put_stat(DB_stat *s)
704 {
705 }
706
707 void
708 delta_stat(DB_stat *a, DB_stat *b, DB_stat *c)
709 {
710     /* c = a - b then b = a for the next time */
711
712     c->cpu = a->cpu - b->cpu;
713     c->elapsed = a->elapsed - b->elapsed;
714     c->dio = a->dio - b->dio;
715     c->pfault = a->pfault - b->pfault;
716     c->t_stamp = a->t_stamp - b->t_stamp;
717     c->n_retrieve = a->n_retrieve - b->n_retrieve;
718     c->n_replace = a->n_replace - b->n_replace;
719     c->n_append = a->n_append - b->n_append;
720     c->n_get_stat = a->n_get_stat - b->n_get_stat;
721     c->n_put_stat = a->n_put_stat - b->n_put_stat;
722
723     memcpy(b, a, sizeof(DB_stat));
724 }
725
726 /*
727  * look up a dba in the data base returns number of dbas found , and
728  * whether there were more than requested. 
729  */
730
731 int
732 kerb_db_get_dba(char *dba_name, /* could have wild card */
733                 char *dba_inst, /* could have wild card */
734                 Dba *dba,
735                 unsigned max,   /* max number of name structs to return */
736                 int *more)      /* where there more than 'max' tuples? */
737 {
738     *more = 0;
739     return (0);
740 }
741
742 int
743 kerb_db_iterate (k_iter_proc_t func, void *arg)
744 {
745     datum key, contents;
746     Principal *principal;
747     int code;
748     DBM *db;
749     
750     kerb_db_init();             /* initialize and open the database */
751     if ((code = kerb_dbl_lock(KERB_DBL_SHARED)) != 0)
752         return code;
753
754     db = dbm_open(current_db_name, O_RDONLY, 0600);
755     if (db == NULL)
756         return errno;
757
758     for (key = dbm_firstkey (db); key.dptr != NULL; key = dbm_next(db, key)) {
759         contents = dbm_fetch (db, key);
760         /* XXX may not be properly aligned */
761         principal = (Principal *) contents.dptr;
762         if ((code = (*func)(arg, principal)) != 0)
763             return code;
764     }
765     dbm_close(db);
766     kerb_dbl_unlock();
767     return 0;
768 }