chiark / gitweb /
Added. No idea why this wasn't done before.
[become] / src / userdb.c
1 /* -*-c-*-
2  *
3  * $Id: userdb.c,v 1.2 1997/08/04 10:24:26 mdw Exp $
4  *
5  * User database management
6  *
7  * (c) 1997 EBI
8  */
9
10 /*----- Licensing notice --------------------------------------------------*
11  *
12  * This file is part of `become'
13  *
14  * `Become' is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * `Become' is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with `become'; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Revision history --------------------------------------------------*
30  *
31  * $Log: userdb.c,v $
32  * Revision 1.2  1997/08/04 10:24:26  mdw
33  * Sources placed under CVS control.
34  *
35  * Revision 1.1  1997/07/21  13:47:43  mdw
36  * Initial revision
37  *
38  */
39
40 /*----- Header files ------------------------------------------------------*/
41
42 /* --- ANSI headers --- */
43
44 #include <ctype.h>
45 #include <errno.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49
50 /* --- Unix headers --- */
51
52 #include "config.h"
53
54 #include <sys/types.h>
55
56 #ifdef HAVE_YP
57 #  include <rpcsvc/ypclnt.h>
58 #  include <rpcsvc/yp_prot.h>
59 #endif
60
61 #include <grp.h>
62 #include <pwd.h>
63 #include <unistd.h>
64
65 /* --- Local headers --- */
66
67 #include "sym.h"
68 #include "userdb.h"
69 #include "utils.h"
70
71 /*----- Type definitions --------------------------------------------------*/
72
73 /* --- A map link --- */
74
75 typedef struct userdb__node {
76   struct userdb__node *next;
77   void *rec;
78 } userdb__node;
79
80 /* --- A reference to a real record --- */
81
82 typedef struct userdb__sym {
83   sym_base _base;
84   void *rec;
85 } userdb__sym;
86
87 /* --- A name- and number-mapping --- */
88
89 typedef struct userdb__map {
90   sym_table nmap;
91   sym_table idmap;
92   userdb__node *list;
93 } userdb__map;
94
95 /*----- Static variables --------------------------------------------------*/
96
97 static userdb__map userdb__users;       /* Map of user info blocks */
98 static sym_iter userdb__useri;          /* Iterator for users */
99 static userdb__map userdb__groups;      /* Map of group info blocks */
100 static sym_iter userdb__groupi;         /* Iterator for groups */
101
102 /*----- Map management functions ------------------------------------------*/
103
104 /* --- @userdb__createMap@ --- *
105  *
106  * Arguments:   @userdb__map *m@ = pointer to a map block
107  *
108  * Returns:     ---
109  *
110  * Use:         Initialises a map table.
111  */
112
113 static void userdb__createMap(userdb__map *m)
114 {
115   sym_createTable(&m->nmap);
116   sym_createTable(&m->idmap);
117   m->list = 0;
118 }
119
120 /* --- @userdb__addToMap@ --- *
121  *
122  * Arguments:   @userdb__map *m@ = pointer to the map block
123  *              @const char *name@ = pointer to the item's name
124  *              @int id@ = the item's id number
125  *              @void *rec@ = pointer to the actual record
126  *
127  * Returns:     ---
128  *
129  * Use:         Adds an item to the given map.
130  */
131
132 static void userdb__addToMap(userdb__map *m,
133                              const char *name,
134                              int id, void *rec)
135 {
136   unsigned f;
137   userdb__sym *s;
138   userdb__node *n;
139
140   s = sym_find(&m->nmap, name, -1, sizeof(*s), &f);
141   if (!f)
142     s->rec = rec;
143
144   s = sym_find(&m->idmap, (char *)&id, sizeof(id), sizeof(*s), &f);
145   if (!f)
146     s->rec = rec;
147
148   n = xmalloc(sizeof(*n));
149   n->rec = rec;
150   n->next = m->list;
151   m->list = n;
152 }
153
154 /* --- @userdb__byName@ --- *
155  *
156  * Arguments:   @userdb__map *m@ = pointer to a map block
157  *              @const char *name@ = name to look up
158  *
159  * Returns:     A pointer to the appropriate block, or zero if not found.
160  *
161  * Use:         Looks up a name in a mapping and returns the result.
162  */
163
164 static void *userdb__byName(userdb__map *m, const char *name)
165 {
166   userdb__sym *s = sym_find(&m->nmap, name, -1, 0, 0);
167   return (s ? s->rec : 0);
168 }
169
170 /* --- @userdb__byId@ --- *
171  *
172  * Arguments:   @userdb__map *m@ = pointer to a map block
173  *              @int id@ = id number to find
174  *
175  * Returns:     A pointer to the appropriate block, or zero if not found.
176  *
177  * Use:         Looks up an ID in a mapping, and returns the result.
178  */
179
180 static void *userdb__byId(userdb__map *m, int id)
181 {
182   userdb__sym *s = sym_find(&m->idmap, (char *)&id, sizeof(id), 0, 0);
183   return (s ? s->rec : 0);
184 }
185
186 /* --- @userdb__clearMap@ --- *
187  *
188  * Arguments:   @userdb__map *m@ = pointer to a map block
189  *              @void (*freerec)(void *rec)@ = pointer to a free-record proc
190  *
191  * Returns:     ---
192  *
193  * Use:         Clears a map, emptying it and releasing the memory it
194  *              occupied.
195  */
196
197 static void userdb__clearMap(userdb__map *m, void (*freerec)(void *rec))
198 {
199   userdb__node *n, *t;
200
201   sym_destroyTable(&m->nmap);
202   sym_destroyTable(&m->idmap);
203
204   for (n = m->list; n; n = t) {
205     t = n->next;
206     freerec(n->rec);
207     free(n);
208   }
209 }
210
211 /*----- User and group block management -----------------------------------*/
212
213 /* --- @userdb__dumpUser@ --- *
214  *
215  * Arguments:   @const struct passwd *pw@ = pointer to a user block
216  *              @FILE *fp@ = pointer to stream to write on
217  *
218  * Returns:     ---
219  *
220  * Use:         Writes a user's informationt to a stream.
221  */
222
223 #ifndef NDEBUG
224
225 static void userdb__dumpUser(const struct passwd *pw, FILE *fp)
226 {
227   printf("\n"
228          "***   name == %s\n"
229          "*** passwd == %s\n"
230          "***    uid == %i\n"
231          "***    gid == %i\n"
232          "***  gecos == %s\n"
233          "***   home == %s\n"
234          "***  shell == %s\n",
235          pw->pw_name, pw->pw_passwd, (int)pw->pw_uid, (int)pw->pw_gid,
236          pw->pw_gecos, pw->pw_dir, pw->pw_shell);
237 }
238
239 #else
240
241 #define userdb__dumpUser(pw, fp) ((void)0)
242
243 #endif
244
245 /* --- @userdb_copyUser@ --- *
246  *
247  * Arguments:   @struct passwd *pw@ = pointer to block to copy
248  *
249  * Returns:     Pointer to the copy.
250  *
251  * Use:         Copies a user block.  The copy is `deep' so all the strings
252  *              are copied too.  Free the copy with @userdb_freeUser@ when
253  *              you don't want it any more.
254  */
255
256 struct passwd *userdb_copyUser(struct passwd *pw)
257 {
258   struct passwd *npw;
259
260   if (!pw)
261     return (0);
262
263   npw = xmalloc(sizeof(*npw));
264
265   npw->pw_name = xstrdup(pw->pw_name);
266   npw->pw_passwd = xstrdup(pw->pw_passwd);
267   npw->pw_uid = pw->pw_uid;
268   npw->pw_gid = pw->pw_gid;
269   npw->pw_gecos = xstrdup(pw->pw_gecos);
270   npw->pw_dir = xstrdup(pw->pw_dir);
271   npw->pw_shell = xstrdup(pw->pw_shell);
272
273   return (npw);
274 }
275
276 /* --- @userdb__buildUser@ --- *
277  *
278  * Arguments:   @char *s@ = pointer to user string
279  *
280  * Returns:     Pointer to a user block.
281  *
282  * Use:         Converts a line from a user file into a password entry.
283  *              Note that the string is corrupted by @strtok@ while it gets
284  *              parsed.
285  */
286
287 static struct passwd *userdb__buildUser(char *s)
288 {
289   struct passwd *pw = xmalloc(sizeof(*pw));
290
291   s = strtok(s, ":"); if (!s) goto tidy_0; pw->pw_name = xstrdup(s);
292   s = strtok(0, ":"); if (!s) goto tidy_1; pw->pw_passwd = xstrdup(s);
293   s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_uid = atoi(s);
294   s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_gid = atoi(s);
295   s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_gecos = xstrdup(s);
296   s = strtok(0, ":"); if (!s) goto tidy_3; pw->pw_dir = xstrdup(s);
297   s = strtok(0, ":"); if (!s) goto tidy_4; pw->pw_shell = xstrdup(s);
298   return (pw);
299
300   /* --- Error handling --- */
301
302 tidy_4:
303   free(pw->pw_dir);
304 tidy_3:
305   free(pw->pw_gecos);
306 tidy_2:
307   free(pw->pw_passwd);
308 tidy_1:
309   free(pw->pw_name);
310 tidy_0:
311   free(pw);
312
313   return (0);
314 }
315
316 /* --- @userdb_freeUser@ --- *
317  *
318  * Arguments:   @void *rec@ = pointer to a user record
319  *
320  * Returns:     ---
321  *
322  * Use:         Frees a user record.
323  */
324
325 void userdb_freeUser(void *rec)
326 {
327   struct passwd *pw;
328
329   if (!rec)
330     return;
331
332   pw = rec;
333   free(pw->pw_name);
334   free(pw->pw_passwd);
335   free(pw->pw_gecos);
336   free(pw->pw_dir);
337   free(pw->pw_shell);
338   free(pw);
339
340
341 /* --- @userdb__dumpGroup@ --- *
342  *
343  * Arguments:   @const struct group *gr@ = pointer to a group block
344  *              @FILE *fp@ = pointer to stream to write on
345  *
346  * Returns:     ---
347  *
348  * Use:         Writes a group's information to a stream.
349  */
350
351 #ifndef NDEBUG
352
353 static void userdb__dumpGroup(const struct group *gr, FILE *fp)
354 {
355   char *const *p;
356
357   printf("\n"
358          "***   name == %s\n"
359          "*** passwd == %s\n"
360          "***    gid == %i\n"
361          "*** members...\n",
362          gr->gr_name, gr->gr_passwd, (int)gr->gr_gid);
363   for (p = gr->gr_mem; *p; p++)
364     printf("***   %s\n", *p);
365 }
366
367 #else
368
369 #define userdb__dumpUser(pw, fp) ((void)0)
370
371 #endif
372
373 /* --- @userdb_copyGroup@ --- *
374  *
375  * Arguments:   @struct group *gr@ = pointer to group block
376  *
377  * Returns:     Pointer to copied block
378  *
379  * Use:         Copies a group block.  The copy is `deep' so all the strings
380  *              are copied too.  Free the copy with @userdb_freeGroup@ when
381  *              you don't want it any more.
382  */
383
384 struct group *userdb_copyGroup(struct group *gr)
385 {
386   struct group *ngr;
387   int i, max;
388
389   if (!gr)
390     return (0);
391
392   ngr = xmalloc(sizeof(*ngr));
393
394   ngr->gr_name = xstrdup(gr->gr_name);
395   ngr->gr_passwd = xstrdup(gr->gr_passwd);
396   ngr->gr_gid = gr->gr_gid;
397
398   for (max = 0; gr->gr_mem[max]; max++)
399     ;
400   ngr->gr_mem = xmalloc((max + 1) * sizeof(char *));
401   for (i = 0; i < max; i++)
402     ngr->gr_mem[i] = xstrdup(gr->gr_mem[i]);
403   ngr->gr_mem[max] = 0;
404
405   return (ngr);
406 }
407
408 /* --- @userdb__buildGroup@ --- *
409  *
410  * Arguments:   @char *s@ = pointer to group line string
411  *
412  * Returns:     Pointer to a group block
413  *
414  * Use:         Parses an entry in the groups file.  The string is garbled
415  *              by @strtok@ as we go.
416  */
417
418 static struct group *userdb__buildGroup(char *s)
419 {
420   struct group *gr = xmalloc(sizeof(*gr));
421   char *p;
422   int i;
423
424   /* --- Do the easy bits --- */
425
426   s = strtok(s, ":"); if (!s) goto tidy_0; gr->gr_name = xstrdup(s);
427   s = strtok(0, ":"); if (!s) goto tidy_1; gr->gr_passwd = xstrdup(s);
428   s = strtok(0, ":"); if (!s) goto tidy_2; gr->gr_gid = atoi(s);
429
430   /* --- Find the start of the member list --- */
431
432   s = strtok(0, "");
433   if (!s)
434     goto tidy_2;
435
436   /* --- Count the number of members --- */
437
438   p = s;
439   i = 0;
440   for (;;) {
441     i++;
442     if ((p = strpbrk(p, ",")) == 0)
443       break;
444     p++;
445   }
446
447   /* --- Allocate the block and fill it --- */
448
449   gr->gr_mem = xmalloc((i + 1) * sizeof(char *));
450   i = 0;
451   s = strtok(s, ",");
452   do {
453     gr->gr_mem[i++] = xstrdup(s);
454     s = strtok(0, ",");
455   } while (s);
456   gr->gr_mem[i] = 0;
457
458   return (gr);
459
460   /* --- Various tidying-up things --- */
461
462 tidy_2:
463   free(gr->gr_passwd);
464 tidy_1:
465   free(gr->gr_name);
466 tidy_0:
467   free(gr);
468
469   return (0);
470 }
471
472 /* --- @userdb_freeGroup@ --- *
473  *
474  * Arguments:   @void *rec@ = pointer to a group record
475  *
476  * Returns:     ---
477  *
478  * Use:         Frees a group record.
479  */
480
481 void userdb_freeGroup(void *rec)
482 {
483   struct group *gr;
484   char **p;
485
486   if (!rec)
487     return;
488
489   gr = rec;
490   free(gr->gr_name);
491   free(gr->gr_passwd);
492   for (p = gr->gr_mem; *p; p++)
493     free(*p);
494   free(gr->gr_mem);
495   free(gr);
496
497
498 /*----- Answering queries -------------------------------------------------*/
499
500 /* --- @userdb_userByName@, @userdb_userById@ --- *
501  *
502  * Arguments:   @const char *name@ = pointer to user's name
503  *              @uid_t id@ = user id to find
504  *
505  * Returns:     Pointer to user block, or zero if not found.
506  *
507  * Use:         Looks up a user by name or id.
508  */
509
510 struct passwd *userdb_userByName(const char *name)
511 { return (userdb__byName(&userdb__users, name)); }
512
513 struct passwd *userdb_userById(uid_t id)
514 { return (userdb__byId(&userdb__users, id)); }
515
516 /* --- @userdb_iterateUsers@, @userdb_iterateUsers_r@ --- *
517  *
518  * Arguments:   @userdb_iter *i@ = pointer to a symbol table iterator object
519  *
520  * Returns:     ---
521  *
522  * Use:         Initialises an iteration for the user database.
523  */
524
525 void userdb_iterateUsers(void)
526 { userdb_iterateUsers_r(&userdb__useri); }
527
528 void userdb_iterateUsers_r(userdb_iter *i)
529 { sym_createIter(i, &userdb__users.nmap); }
530
531 /* --- @userdb_nextUser@, @userdb_nextUser_r@ --- *
532  *
533  * Arguments:   @userdb_iter *i@ = pointer to a symbol table iterator oject
534  *
535  * Returns:     Pointer to the next user block, or null.
536  *
537  * Use:         Returns another user block.
538  */
539
540 struct passwd *userdb_nextUser(void)
541 { return (userdb_nextUser_r(&userdb__useri)); }
542
543 struct passwd *userdb_nextUser_r(userdb_iter *i)
544 {
545   userdb__sym *s = sym_next(i);
546   return (s ? s->rec : 0);
547 }
548
549 /* --- @userdb_groupByName@, @userdb_groupById@ --- *
550  *
551  * Arguments:   @const char *name@ = pointer to group's name
552  *              @gid_t id@ = group id to find
553  *
554  * Returns:     Pointer to group block, or zero if not found.
555  *
556  * Use:         Looks up a group by name or id.
557  */
558
559 struct group *userdb_groupByName(const char *name)
560 { return (userdb__byName(&userdb__groups, name)); }
561
562 struct group *userdb_groupById(gid_t id)
563 { return (userdb__byId(&userdb__groups, id)); }
564
565 /* --- @userdb_iterateGroups@, @userdb_iterateGroups_r@ --- *
566  *
567  * Arguments:   @userdb_iter *i@ = pointer to a symbol table iterator object
568  *
569  * Returns:     ---
570  *
571  * Use:         Initialises an iteration for the group database.
572  */
573
574 void userdb_iterateGroups(void)
575 { userdb_iterateGroups_r(&userdb__groupi); }
576
577 void userdb_iterateGroups_r(userdb_iter *i)
578 { sym_createIter(i, &userdb__groups.nmap); }
579
580 /* --- @userdb_nextGroup@, @userdb_nextGroup_r@ --- *
581  *
582  * Arguments:   @userdb_iter *i@ = pointer to a symbol table iterator oject
583  *
584  * Returns:     Pointer to the next group block, or null.
585  *
586  * Use:         Returns another group block.
587  */
588
589 struct group *userdb_nextGroup(void)
590 { return (userdb_nextGroup_r(&userdb__groupi)); }
591
592 struct group *userdb_nextGroup_r(userdb_iter *i)
593 {
594   userdb__sym *s = sym_next(i);
595   return (s ? s->rec : 0);
596 }
597
598 /*----- Yellow pages support ----------------------------------------------*/
599
600 #ifdef HAVE_YP
601
602 /* --- @userdb__foreachUser@ --- *
603  *
604  * Arguments:   @int st@ = YP protocol-level status code
605  *              @char *k@ = address of the key for this record
606  *              @int ksz@ = size of the key
607  *              @char *v@ = address of the value for this record
608  *              @int vsz@ = size of the value
609  *              @char *data@ = pointer to some data passed to me
610  *
611  * Returns:     Zero to be called again, nonzero to end the enumeration.
612  *
613  * Use:         Handles an incoming user record.
614  */
615
616 static int userdb__foreachUser(int st, char *k, int ksz,
617                                char *v, int vsz, char *data)
618 {
619   char *cv;
620   struct passwd *pw;
621
622   if (st != YP_TRUE)
623     return (-1);
624   cv = xmalloc(vsz + 1);
625   memcpy(cv, v, vsz);
626   cv[vsz] = 0;
627   pw = userdb__buildUser(cv);
628   if (pw && !userdb__byName(&userdb__users, pw->pw_name))
629     userdb__addToMap(&userdb__users, pw->pw_name, pw->pw_uid, pw);
630   free(cv);
631   return (0);
632 }
633
634 /* --- @userdb__foreachGroup@ --- *
635  *
636  * Arguments:   @int st@ = YP protocol-level status code
637  *              @char *k@ = address of the key for this record
638  *              @int ksz@ = size of the key
639  *              @char *v@ = address of the value for this record
640  *              @int vsz@ = size of the value
641  *              @char *data@ = pointer to some data passed to me
642  *
643  * Returns:     Zero to be called again, nonzero to end the enumeration.
644  *
645  * Use:         Handles an incoming user record.
646  */
647
648 static int userdb__foreachGroup(int st, char *k, int ksz,
649                                 char *v, int vsz, char *data)
650 {
651   char *cv;
652   struct group *gr;
653
654   if (st != YP_TRUE)
655     return (-1);
656   cv = xmalloc(vsz + 1);
657   memcpy(cv, v, vsz);
658   cv[vsz] = 0;
659   gr = userdb__buildGroup(cv);
660   if (gr && !userdb__byName(&userdb__groups, gr->gr_name))
661     userdb__addToMap(&userdb__groups, gr->gr_name, gr->gr_gid, gr);
662   free(cv);
663   return (0);
664 }
665
666 /* --- @userdb_yp@ --- *
667  *
668  * Arguments:   ---
669  *
670  * Returns:     ---
671  *
672  * Use:         Fetches the YP database of users.
673  */
674
675 void userdb_yp(void)
676 {
677   char *ypdom;
678
679   /* --- Bind to a server --- */
680
681   if (yp_get_default_domain(&ypdom) ||
682       yp_bind(ypdom))
683     return;
684
685   /* --- Fetch the users map --- */
686
687   {
688     static struct ypall_callback ucb = { userdb__foreachUser, 0 };
689     yp_all(ypdom, "passwd.byuid", &ucb);
690   }
691
692   /* --- Fetch the groups map --- */
693
694   {
695     static struct ypall_callback gcb = { userdb__foreachGroup, 0 };
696     yp_all(ypdom, "group.bygid", &gcb);
697   }
698
699   yp_unbind(ypdom);
700   free(ypdom);
701 }
702
703 #else
704
705 void userdb_yp(void) { ; }
706
707 #endif
708
709 /*----- Building the databases --------------------------------------------*/
710
711 /* --- @userdb_local@ --- *
712  *
713  * Arguments:   ---
714  *
715  * Returns:     ---
716  *
717  * Use:         Reads the local list of users into the maps.
718  */
719
720 void userdb_local(void)
721 {
722   D( printf("adding local users...\n"); )
723
724   /* --- Fetch users first --- */
725
726   {
727     struct passwd *pw;
728
729     setpwent();
730     while ((pw = getpwent()) != 0) {
731       D( userdb__dumpUser(pw, stdout); )
732       if (!userdb__byName(&userdb__users, pw->pw_name))
733         userdb__addToMap(&userdb__users, pw->pw_name, pw->pw_uid,
734                          userdb_copyUser(pw));
735     }
736     endpwent();
737   }
738
739   /* --- Then fetch groups --- */
740
741   {
742     struct group *gr;
743
744     setgrent();
745     while ((gr = getgrent()) != 0) {
746       D( userdb__dumpGroup(gr, stdout); )
747       if (!userdb__byName(&userdb__groups, gr->gr_name))
748         userdb__addToMap(&userdb__groups, gr->gr_name, gr->gr_gid,
749                          userdb_copyGroup(gr));
750     }
751     endgrent();
752   }
753 }
754
755 /* --- @userdb_init@ --- *
756  *
757  * Arguments:   ---
758  *
759  * Returns:     ---
760  *
761  * Use:         Initialises the user database.
762  */
763
764 void userdb_init(void)
765 {
766   userdb__createMap(&userdb__users);
767   userdb__createMap(&userdb__groups);
768 }
769
770 /* --- @userdb_reinit@ --- *
771  *
772  * Arguments:   ---
773  *
774  * Returns:     ---
775  *
776  * Use:         Reinitialises the user database.
777  */
778
779 void userdb_reinit(void)
780 {
781   userdb__clearMap(&userdb__users, userdb_freeUser);
782   userdb__clearMap(&userdb__groups, userdb_freeGroup);
783   userdb_init();
784 }
785
786 /*----- Test rig ----------------------------------------------------------*/
787
788 #ifdef TEST_RIG
789
790 void dumpit(const char *msg)
791 {
792   printf("\n\n$$$ %s\n", msg);
793
794   {
795     struct passwd *pw;
796     for (userdb_iterateUsers(); (pw = userdb_nextUser()) != 0; )
797       userdb__dumpUser(pw, stdout);
798   }
799
800   {
801     struct group *gr;
802     for (userdb_iterateGroups(); (gr = userdb_nextGroup()) != 0; )
803       userdb__dumpGroup(gr, stdout);
804   }
805 }
806
807 int main(void)
808 {
809   userdb_init();
810   dumpit("cleared");
811   userdb_local();
812   dumpit("local");
813   userdb_yp();
814   dumpit("yp");
815   return (0);
816 }
817
818 #endif
819
820 /*----- That's all, folks -------------------------------------------------*/