3 * $Id: userdb.c,v 1.6 1998/01/12 16:46:33 mdw Exp $
5 * User database management
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of `become'
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.
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.
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.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.6 1998/01/12 16:46:33 mdw
35 * Revision 1.5 1997/09/17 10:24:08 mdw
36 * Use `uid_t' instead of `int' for uids and gids. Not quite sure why I
37 * didn't do this before.
39 * Revision 1.4 1997/08/20 16:24:58 mdw
40 * Patch memory leak. Rename `userdb_reinit' to `userdb_end' for more
43 * Revision 1.3 1997/08/07 09:44:29 mdw
44 * Read NIS-based passwords from the YP server directly, rather than using
45 * `popen(ypcat)', which is probably both slower and less secure.
47 * Revision 1.2 1997/08/04 10:24:26 mdw
48 * Sources placed under CVS control.
50 * Revision 1.1 1997/07/21 13:47:43 mdw
55 /*----- Header files ------------------------------------------------------*/
57 /* --- ANSI headers --- */
65 /* --- Unix headers --- */
69 #include <sys/types.h>
73 # include <rpcsvc/ypclnt.h>
74 # include <rpcsvc/yp_prot.h>
81 /* --- Local headers --- */
88 /*----- Type definitions --------------------------------------------------*/
90 /* --- A map link --- */
92 typedef struct userdb__node {
93 struct userdb__node *next;
97 /* --- A reference to a real record --- */
99 typedef struct userdb__sym {
104 /* --- A name- and number-mapping --- */
106 typedef struct userdb__map {
112 /*----- Static variables --------------------------------------------------*/
114 static userdb__map userdb__users; /* Map of user info blocks */
115 static sym_iter userdb__useri; /* Iterator for users */
116 static userdb__map userdb__groups; /* Map of group info blocks */
117 static sym_iter userdb__groupi; /* Iterator for groups */
119 /*----- Map management functions ------------------------------------------*/
121 /* --- @userdb__createMap@ --- *
123 * Arguments: @userdb__map *m@ = pointer to a map block
127 * Use: Initialises a map table.
130 static void userdb__createMap(userdb__map *m)
132 sym_createTable(&m->nmap);
133 sym_createTable(&m->idmap);
137 /* --- @userdb__addToMap@ --- *
139 * Arguments: @userdb__map *m@ = pointer to the map block
140 * @const char *name@ = pointer to the item's name
141 * @uid_t id@ = the item's id number
142 * @void *rec@ = pointer to the actual record
146 * Use: Adds an item to the given map.
149 static void userdb__addToMap(userdb__map *m,
157 s = sym_find(&m->nmap, name, -1, sizeof(*s), &f);
161 s = sym_find(&m->idmap, (char *)&id, sizeof(id), sizeof(*s), &f);
165 n = xmalloc(sizeof(*n));
171 /* --- @userdb__byName@ --- *
173 * Arguments: @userdb__map *m@ = pointer to a map block
174 * @const char *name@ = name to look up
176 * Returns: A pointer to the appropriate block, or zero if not found.
178 * Use: Looks up a name in a mapping and returns the result.
181 static void *userdb__byName(userdb__map *m, const char *name)
183 userdb__sym *s = sym_find(&m->nmap, name, -1, 0, 0);
184 return (s ? s->rec : 0);
187 /* --- @userdb__byId@ --- *
189 * Arguments: @userdb__map *m@ = pointer to a map block
190 * @uid_t id@ = id number to find
192 * Returns: A pointer to the appropriate block, or zero if not found.
194 * Use: Looks up an ID in a mapping, and returns the result.
197 static void *userdb__byId(userdb__map *m, uid_t id)
199 userdb__sym *s = sym_find(&m->idmap, (char *)&id, sizeof(id), 0, 0);
200 return (s ? s->rec : 0);
203 /* --- @userdb__clearMap@ --- *
205 * Arguments: @userdb__map *m@ = pointer to a map block
206 * @void (*freerec)(void *rec)@ = pointer to a free-record proc
210 * Use: Clears a map, emptying it and releasing the memory it
214 static void userdb__clearMap(userdb__map *m, void (*freerec)(void *rec))
218 sym_destroyTable(&m->nmap);
219 sym_destroyTable(&m->idmap);
221 for (n = m->list; n; n = t) {
228 /*----- User and group block management -----------------------------------*/
230 /* --- @userdb__dumpUser@ --- *
232 * Arguments: @const struct passwd *pw@ = pointer to a user block
236 * Use: Writes a user's informationt to a stream.
241 static void userdb__dumpUser(const struct passwd *pw)
244 "debug: name `%s' passwd `%s' uid %i gid %i",
245 pw->pw_name, pw->pw_passwd, (int)pw->pw_uid, (int)pw->pw_gid);
247 "debug: ... gecos `%s' home `%s' shell `%s'",
248 pw->pw_gecos, pw->pw_dir, pw->pw_shell);
253 /* --- @userdb_copyUser@ --- *
255 * Arguments: @struct passwd *pw@ = pointer to block to copy
257 * Returns: Pointer to the copy.
259 * Use: Copies a user block. The copy is `deep' so all the strings
260 * are copied too. Free the copy with @userdb_freeUser@ when
261 * you don't want it any more.
264 struct passwd *userdb_copyUser(struct passwd *pw)
271 npw = xmalloc(sizeof(*npw));
273 npw->pw_name = xstrdup(pw->pw_name);
274 npw->pw_passwd = xstrdup(pw->pw_passwd);
275 npw->pw_uid = pw->pw_uid;
276 npw->pw_gid = pw->pw_gid;
277 npw->pw_gecos = xstrdup(pw->pw_gecos);
278 npw->pw_dir = xstrdup(pw->pw_dir);
279 npw->pw_shell = xstrdup(pw->pw_shell);
284 /* --- @userdb__buildUser@ --- *
286 * Arguments: @char *s@ = pointer to user string
288 * Returns: Pointer to a user block.
290 * Use: Converts a line from a user file into a password entry.
291 * Note that the string is corrupted by @strtok@ while it gets
295 static struct passwd *userdb__buildUser(char *s)
297 struct passwd *pw = xmalloc(sizeof(*pw));
299 s = strtok(s, ":"); if (!s) goto tidy_0; pw->pw_name = xstrdup(s);
300 s = strtok(0, ":"); if (!s) goto tidy_1; pw->pw_passwd = xstrdup(s);
301 s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_uid = (uid_t)atol(s);
302 s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_gid = (gid_t)atol(s);
303 s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_gecos = xstrdup(s);
304 s = strtok(0, ":"); if (!s) goto tidy_3; pw->pw_dir = xstrdup(s);
305 s = strtok(0, ":"); if (!s) goto tidy_4; pw->pw_shell = xstrdup(s);
308 /* --- Error handling --- */
324 /* --- @userdb_freeUser@ --- *
326 * Arguments: @void *rec@ = pointer to a user record
330 * Use: Frees a user record.
333 void userdb_freeUser(void *rec)
349 /* --- @userdb__dumpGroup@ --- *
351 * Arguments: @const struct group *gr@ = pointer to a group block
352 * @FILE *fp@ = pointer to stream to write on
356 * Use: Writes a group's information to a stream.
361 static void userdb__dumpGroup(const struct group *gr)
366 "debug: name `%s' passwd `%s' gid %i",
367 gr->gr_name, gr->gr_passwd, (int)gr->gr_gid);
368 for (p = gr->gr_mem; *p; p++)
369 trace(TRACE_DEBUG,"debug: ... `%s'", *p);
374 /* --- @userdb_copyGroup@ --- *
376 * Arguments: @struct group *gr@ = pointer to group block
378 * Returns: Pointer to copied block
380 * Use: Copies a group block. The copy is `deep' so all the strings
381 * are copied too. Free the copy with @userdb_freeGroup@ when
382 * you don't want it any more.
385 struct group *userdb_copyGroup(struct group *gr)
393 ngr = xmalloc(sizeof(*ngr));
395 ngr->gr_name = xstrdup(gr->gr_name);
396 ngr->gr_passwd = xstrdup(gr->gr_passwd);
397 ngr->gr_gid = gr->gr_gid;
399 for (max = 0; gr->gr_mem[max]; max++)
401 ngr->gr_mem = xmalloc((max + 1) * sizeof(char *));
402 for (i = 0; i < max; i++)
403 ngr->gr_mem[i] = xstrdup(gr->gr_mem[i]);
404 ngr->gr_mem[max] = 0;
409 /* --- @userdb__buildGroup@ --- *
411 * Arguments: @char *s@ = pointer to group line string
413 * Returns: Pointer to a group block
415 * Use: Parses an entry in the groups file. The string is garbled
416 * by @strtok@ as we go.
419 static struct group *userdb__buildGroup(char *s)
421 struct group *gr = xmalloc(sizeof(*gr));
425 /* --- Do the easy bits --- */
427 s = strtok(s, ":"); if (!s) goto tidy_0; gr->gr_name = xstrdup(s);
428 s = strtok(0, ":"); if (!s) goto tidy_1; gr->gr_passwd = xstrdup(s);
429 s = strtok(0, ":"); if (!s) goto tidy_2; gr->gr_gid = (gid_t)atol(s);
431 /* --- Find the start of the member list --- */
437 /* --- Count the number of members --- */
443 if ((p = strpbrk(p, ",")) == 0)
448 /* --- Allocate the block and fill it --- */
450 gr->gr_mem = xmalloc((i + 1) * sizeof(char *));
454 gr->gr_mem[i++] = xstrdup(s);
461 /* --- Various tidying-up things --- */
473 /* --- @userdb_freeGroup@ --- *
475 * Arguments: @void *rec@ = pointer to a group record
479 * Use: Frees a group record.
482 void userdb_freeGroup(void *rec)
493 for (p = gr->gr_mem; *p; p++)
499 /*----- Answering queries -------------------------------------------------*/
501 /* --- @userdb_userByName@, @userdb_userById@ --- *
503 * Arguments: @const char *name@ = pointer to user's name
504 * @uid_t id@ = user id to find
506 * Returns: Pointer to user block, or zero if not found.
508 * Use: Looks up a user by name or id.
511 struct passwd *userdb_userByName(const char *name)
512 { return (userdb__byName(&userdb__users, name)); }
514 struct passwd *userdb_userById(uid_t id)
515 { return (userdb__byId(&userdb__users, id)); }
517 /* --- @userdb_iterateUsers@, @userdb_iterateUsers_r@ --- *
519 * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator object
523 * Use: Initialises an iteration for the user database.
526 void userdb_iterateUsers(void)
527 { userdb_iterateUsers_r(&userdb__useri); }
529 void userdb_iterateUsers_r(userdb_iter *i)
530 { sym_createIter(i, &userdb__users.nmap); }
532 /* --- @userdb_nextUser@, @userdb_nextUser_r@ --- *
534 * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator oject
536 * Returns: Pointer to the next user block, or null.
538 * Use: Returns another user block.
541 struct passwd *userdb_nextUser(void)
542 { return (userdb_nextUser_r(&userdb__useri)); }
544 struct passwd *userdb_nextUser_r(userdb_iter *i)
546 userdb__sym *s = sym_next(i);
547 return (s ? s->rec : 0);
550 /* --- @userdb_groupByName@, @userdb_groupById@ --- *
552 * Arguments: @const char *name@ = pointer to group's name
553 * @gid_t id@ = group id to find
555 * Returns: Pointer to group block, or zero if not found.
557 * Use: Looks up a group by name or id.
560 struct group *userdb_groupByName(const char *name)
561 { return (userdb__byName(&userdb__groups, name)); }
563 struct group *userdb_groupById(gid_t id)
564 { return (userdb__byId(&userdb__groups, id)); }
566 /* --- @userdb_iterateGroups@, @userdb_iterateGroups_r@ --- *
568 * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator object
572 * Use: Initialises an iteration for the group database.
575 void userdb_iterateGroups(void)
576 { userdb_iterateGroups_r(&userdb__groupi); }
578 void userdb_iterateGroups_r(userdb_iter *i)
579 { sym_createIter(i, &userdb__groups.nmap); }
581 /* --- @userdb_nextGroup@, @userdb_nextGroup_r@ --- *
583 * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator oject
585 * Returns: Pointer to the next group block, or null.
587 * Use: Returns another group block.
590 struct group *userdb_nextGroup(void)
591 { return (userdb_nextGroup_r(&userdb__groupi)); }
593 struct group *userdb_nextGroup_r(userdb_iter *i)
595 userdb__sym *s = sym_next(i);
596 return (s ? s->rec : 0);
599 /*----- Yellow pages support ----------------------------------------------*/
603 /* --- @userdb__foreachUser@ --- *
605 * Arguments: @int st@ = YP protocol-level status code
606 * @char *k@ = address of the key for this record
607 * @int ksz@ = size of the key
608 * @char *v@ = address of the value for this record
609 * @int vsz@ = size of the value
610 * @char *data@ = pointer to some data passed to me
612 * Returns: Zero to be called again, nonzero to end the enumeration.
614 * Use: Handles an incoming user record.
617 static int userdb__foreachUser(int st, char *k, int ksz,
618 char *v, int vsz, char *data)
625 cv = xmalloc(vsz + 1);
628 T( trace(TRACE_DEBUG, "debug: nis string: `%s'", cv); )
629 pw = userdb__buildUser(cv);
630 if (pw && !userdb__byName(&userdb__users, pw->pw_name)) {
631 IF_TRACING(TRACE_DEBUG, userdb__dumpUser(pw); )
632 userdb__addToMap(&userdb__users, pw->pw_name, pw->pw_uid, pw);
639 /* --- @userdb__foreachGroup@ --- *
641 * Arguments: @int st@ = YP protocol-level status code
642 * @char *k@ = address of the key for this record
643 * @int ksz@ = size of the key
644 * @char *v@ = address of the value for this record
645 * @int vsz@ = size of the value
646 * @char *data@ = pointer to some data passed to me
648 * Returns: Zero to be called again, nonzero to end the enumeration.
650 * Use: Handles an incoming user record.
653 static int userdb__foreachGroup(int st, char *k, int ksz,
654 char *v, int vsz, char *data)
661 cv = xmalloc(vsz + 1);
664 T( trace(TRACE_DEBUG, "debug: nis string: `%s'", cv); )
665 gr = userdb__buildGroup(cv);
666 if (gr && !userdb__byName(&userdb__groups, gr->gr_name)) {
667 IF_TRACING(TRACE_DEBUG, userdb__dumpGroup(gr); )
668 userdb__addToMap(&userdb__groups, gr->gr_name, gr->gr_gid, gr);
670 userdb_freeGroup(gr);
675 /* --- @userdb_yp@ --- *
681 * Use: Fetches the YP database of users.
688 /* --- Bind to a server --- */
690 if (yp_get_default_domain(&ypdom) ||
694 T( trace(TRACE_DEBUG, "debug: adding NIS users"); )
696 /* --- Fetch the users map --- */
699 static struct ypall_callback ucb = { userdb__foreachUser, 0 };
700 yp_all(ypdom, "passwd.byuid", &ucb);
703 /* --- Fetch the groups map --- */
706 static struct ypall_callback gcb = { userdb__foreachGroup, 0 };
707 yp_all(ypdom, "group.bygid", &gcb);
715 void userdb_yp(void) { ; }
719 /*----- Building the databases --------------------------------------------*/
721 /* --- @userdb_local@ --- *
727 * Use: Reads the local list of users into the maps.
730 void userdb_local(void)
732 T( trace(TRACE_DEBUG, "debug: adding local users"); )
734 /* --- Fetch users first --- */
740 while ((pw = getpwent()) != 0) {
741 IF_TRACING(TRACE_DEBUG, userdb__dumpUser(pw); )
742 if (!userdb__byName(&userdb__users, pw->pw_name))
743 userdb__addToMap(&userdb__users, pw->pw_name, pw->pw_uid,
744 userdb_copyUser(pw));
749 /* --- Then fetch groups --- */
755 while ((gr = getgrent()) != 0) {
756 IF_TRACING(TRACE_DEBUG, userdb__dumpGroup(gr); )
757 if (!userdb__byName(&userdb__groups, gr->gr_name))
758 userdb__addToMap(&userdb__groups, gr->gr_name, gr->gr_gid,
759 userdb_copyGroup(gr));
765 /* --- @userdb_init@ --- *
771 * Use: Initialises the user database.
774 void userdb_init(void)
776 userdb__createMap(&userdb__users);
777 userdb__createMap(&userdb__groups);
780 /* --- @userdb_end@ --- *
786 * Use: Closes down the user database.
789 void userdb_end(void)
791 userdb__clearMap(&userdb__users, userdb_freeUser);
792 userdb__clearMap(&userdb__groups, userdb_freeGroup);
795 /*----- Test rig ----------------------------------------------------------*/
799 void dumpit(const char *msg)
801 trace(TRACE_DEBUG, "debug: %s", msg);
805 for (userdb_iterateUsers(); (pw = userdb_nextUser()) != 0; )
806 userdb__dumpUser(pw);
811 for (userdb_iterateGroups(); (gr = userdb_nextGroup()) != 0; )
812 userdb__dumpGroup(gr);
819 /* traceon(stdout, TRACE_ALL); */
823 printf("loaded (%lu)\n", track_memused());
827 printf("cleared (%lu)\n", track_memused());
828 /* track_memlist(); */
832 printf("reloaded (%lu)\n", track_memused());
840 /*----- That's all, folks -------------------------------------------------*/