Added. No idea why this wasn't done before.
[become] / src / userdb.c
CommitLineData
c4f2d992 1/* -*-c-*-
2 *
03f996bd 3 * $Id: userdb.c,v 1.2 1997/08/04 10:24:26 mdw Exp $
c4f2d992 4 *
5 * User database management
6 *
7 * (c) 1997 EBI
8 */
9
03f996bd 10/*----- Licensing notice --------------------------------------------------*
c4f2d992 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
03f996bd 25 * along with `become'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
c4f2d992 27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: userdb.c,v $
03f996bd 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
c4f2d992 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
03f996bd 52#include "config.h"
53
c4f2d992 54#include <sys/types.h>
55
03f996bd 56#ifdef HAVE_YP
57# include <rpcsvc/ypclnt.h>
58# include <rpcsvc/yp_prot.h>
59#endif
60
c4f2d992 61#include <grp.h>
62#include <pwd.h>
63#include <unistd.h>
64
65/* --- Local headers --- */
66
c4f2d992 67#include "sym.h"
68#include "userdb.h"
69#include "utils.h"
70
71/*----- Type definitions --------------------------------------------------*/
72
73/* --- A map link --- */
74
75typedef struct userdb__node {
76 struct userdb__node *next;
77 void *rec;
78} userdb__node;
79
80/* --- A reference to a real record --- */
81
82typedef struct userdb__sym {
83 sym_base _base;
84 void *rec;
85} userdb__sym;
86
87/* --- A name- and number-mapping --- */
88
89typedef struct userdb__map {
90 sym_table nmap;
91 sym_table idmap;
92 userdb__node *list;
93} userdb__map;
94
95/*----- Static variables --------------------------------------------------*/
96
97static userdb__map userdb__users; /* Map of user info blocks */
98static sym_iter userdb__useri; /* Iterator for users */
99static userdb__map userdb__groups; /* Map of group info blocks */
100static 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
113static 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
132static 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
164static 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
180static 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
197static 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
225static 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
256struct 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
287static 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
302tidy_4:
303 free(pw->pw_dir);
304tidy_3:
305 free(pw->pw_gecos);
306tidy_2:
307 free(pw->pw_passwd);
308tidy_1:
309 free(pw->pw_name);
310tidy_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
325void 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
353static 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
384struct 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
418static 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
462tidy_2:
463 free(gr->gr_passwd);
464tidy_1:
465 free(gr->gr_name);
466tidy_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
481void 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
03f996bd 498/*----- Answering queries -------------------------------------------------*/
c4f2d992 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
510struct passwd *userdb_userByName(const char *name)
511{ return (userdb__byName(&userdb__users, name)); }
512
513struct 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
525void userdb_iterateUsers(void)
526{ userdb_iterateUsers_r(&userdb__useri); }
527
528void 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
540struct passwd *userdb_nextUser(void)
541{ return (userdb_nextUser_r(&userdb__useri)); }
542
543struct 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
559struct group *userdb_groupByName(const char *name)
560{ return (userdb__byName(&userdb__groups, name)); }
561
562struct 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
574void userdb_iterateGroups(void)
575{ userdb_iterateGroups_r(&userdb__groupi); }
576
577void 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
589struct group *userdb_nextGroup(void)
590{ return (userdb_nextGroup_r(&userdb__groupi)); }
591
592struct group *userdb_nextGroup_r(userdb_iter *i)
593{
594 userdb__sym *s = sym_next(i);
595 return (s ? s->rec : 0);
596}
597
03f996bd 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
616static 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
648static 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
675void 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
705void 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
720void 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
c4f2d992 755/* --- @userdb_init@ --- *
756 *
757 * Arguments: ---
758 *
759 * Returns: ---
760 *
761 * Use: Initialises the user database.
762 */
763
764void 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
779void 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
790void 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
807int 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 -------------------------------------------------*/