/* * Copyright (c) 1990 - 1994, Julianne Frances Haugh * Copyright (c) 1996 - 2000, Marek Michałkiewicz * Copyright (c) 2001 , Michał Moskal * Copyright (c) 2005 , Tomasz Kłoczko * Copyright (c) 2007 - 2009, Nicolas François * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the copyright holders or contributors may not be used to * endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #ident "$Id$" #include #include #include "prototypes.h" #include "defines.h" #include "commonio.h" #include "getdef.h" #include "groupio.h" static /*@null@*/struct commonio_entry *merge_group_entries ( /*@null@*/ /*@returned@*/struct commonio_entry *gr1, /*@null@*/struct commonio_entry *gr2); static int split_groups (unsigned int max_members); static int group_open_hook (void); static /*@null@*/ /*@only@*/void *group_dup (const void *ent) { const struct group *gr = ent; return __gr_dup (gr); } static void group_free (/*@out@*/ /*@only@*/void *ent) { struct group *gr = ent; gr_free (gr); } static const char *group_getname (const void *ent) { const struct group *gr = ent; return gr->gr_name; } static void *group_parse (const char *line) { return (void *) sgetgrent (line); } static int group_put (const void *ent, FILE * file) { const struct group *gr = ent; return (putgrent (gr, file) == -1) ? -1 : 0; } static int group_close_hook (void) { unsigned int max_members = getdef_unum("MAX_MEMBERS_PER_GROUP", 0); if (0 == max_members) { return 1; } return split_groups (max_members); } static struct commonio_ops group_ops = { group_dup, group_free, group_getname, group_parse, group_put, fgetsx, fputsx, group_open_hook, group_close_hook }; static /*@owned@*/struct commonio_db group_db = { GROUP_FILE, /* filename */ &group_ops, /* ops */ NULL, /* fp */ #ifdef WITH_SELINUX NULL, /* scontext */ #endif NULL, /* head */ NULL, /* tail */ NULL, /* cursor */ false, /* changed */ false, /* isopen */ false, /* locked */ false /* readonly */ }; int gr_setdbname (const char *filename) { return commonio_setname (&group_db, filename); } /*@observer@*/const char *gr_dbname (void) { return group_db.filename; } int gr_lock (void) { return commonio_lock (&group_db); } int gr_open (int mode) { return commonio_open (&group_db, mode); } /*@observer@*/ /*@null@*/const struct group *gr_locate (const char *name) { return commonio_locate (&group_db, name); } /*@observer@*/ /*@null@*/const struct group *gr_locate_gid (gid_t gid) { const struct group *grp; gr_rewind (); while ( ((grp = gr_next ()) != NULL) && (grp->gr_gid != gid)) { } return grp; } int gr_update (const struct group *gr) { return commonio_update (&group_db, (const void *) gr); } int gr_remove (const char *name) { return commonio_remove (&group_db, name); } int gr_rewind (void) { return commonio_rewind (&group_db); } /*@observer@*/ /*@null@*/const struct group *gr_next (void) { return commonio_next (&group_db); } int gr_close (void) { return commonio_close (&group_db); } int gr_unlock (void) { return commonio_unlock (&group_db); } void __gr_set_changed (void) { group_db.changed = true; } /*@dependent@*/ /*@null@*/struct commonio_entry *__gr_get_head (void) { return group_db.head; } /*@observer@*/const struct commonio_db *__gr_get_db (void) { return &group_db; } void __gr_del_entry (const struct commonio_entry *ent) { commonio_del_entry (&group_db, ent); } static int gr_cmp (const void *p1, const void *p2) { gid_t u1, u2; if ((*(struct commonio_entry **) p1)->eptr == NULL) { return 1; } if ((*(struct commonio_entry **) p2)->eptr == NULL) { return -1; } u1 = ((struct group *) (*(struct commonio_entry **) p1)->eptr)->gr_gid; u2 = ((struct group *) (*(struct commonio_entry **) p2)->eptr)->gr_gid; if (u1 < u2) { return -1; } else if (u1 > u2) { return 1; } else { return 0; } } /* Sort entries by GID */ int gr_sort () { return commonio_sort (&group_db, gr_cmp); } static int group_open_hook (void) { unsigned int max_members = getdef_unum("MAX_MEMBERS_PER_GROUP", 0); struct commonio_entry *gr1, *gr2; if (0 == max_members) { return 1; } for (gr1 = group_db.head; NULL != gr1; gr1 = gr1->next) { for (gr2 = gr1->next; NULL != gr2; gr2 = gr2->next) { struct group *g1 = (struct group *)gr1->eptr; struct group *g2 = (struct group *)gr2->eptr; if (NULL != g1 && NULL != g2 && 0 == strcmp (g1->gr_name, g2->gr_name) && 0 == strcmp (g1->gr_passwd, g2->gr_passwd) && g1->gr_gid == g2->gr_gid) { /* Both group entries refer to the same * group. It is a split group. Merge the * members. */ gr1 = merge_group_entries (gr1, gr2); if (NULL == gr1) return 0; /* Unlink gr2 */ if (NULL != gr2->next) { gr2->next->prev = gr2->prev; } /* gr2 does not start with head */ assert (NULL != gr2->prev); gr2->prev->next = gr2->next; } } assert (NULL != gr1); } return 1; } /* * Merge the list of members of the two group entries. * * The commonio_entry arguments shall be group entries. * * You should not merge the members of two groups if they don't have the * same name, password and gid. * * It merge the members of the second entry in the first one, and return * the modified first entry on success, or NULL on failure (with errno * set). */ static /*@null@*/struct commonio_entry *merge_group_entries ( /*@null@*/ /*@returned@*/struct commonio_entry *gr1, /*@null@*/struct commonio_entry *gr2) { struct group *gptr1; struct group *gptr2; char **new_members; size_t members = 0; char *new_line; size_t new_line_len, i; if (NULL == gr2 || NULL == gr1) { errno = EINVAL; return NULL; } gptr1 = (struct group *)gr1->eptr; gptr2 = (struct group *)gr2->eptr; if (NULL == gptr2 || NULL == gptr1) { errno = EINVAL; return NULL; } /* Concatenate the 2 lines */ new_line_len = strlen (gr1->line) + strlen (gr2->line) +1; new_line = (char *)malloc ((new_line_len + 1) * sizeof(char*)); if (NULL == new_line) { errno = ENOMEM; return NULL; } snprintf(new_line, new_line_len, "%s\n%s", gr1->line, gr2->line); new_line[new_line_len] = '\0'; /* Concatenate the 2 list of members */ for (i=0; NULL != gptr1->gr_mem[i]; i++); members += i; for (i=0; NULL != gptr2->gr_mem[i]; i++) { char **pmember = gptr1->gr_mem; while (NULL != *pmember) { if (0 == strcmp(*pmember, gptr2->gr_mem[i])) { break; } pmember++; } if (NULL == *pmember) { members++; } } new_members = (char **)malloc ( (members+1) * sizeof(char*) ); if (NULL == new_members) { free (new_line); errno = ENOMEM; return NULL; } for (i=0; NULL != gptr1->gr_mem[i]; i++) { new_members[i] = gptr1->gr_mem[i]; } members = i; for (i=0; NULL != gptr2->gr_mem[i]; i++) { char **pmember = new_members; while (NULL != *pmember) { if (0 == strcmp(*pmember, gptr2->gr_mem[i])) { break; } pmember++; } if (NULL == *pmember) { new_members[members] = gptr2->gr_mem[i]; members++; new_members[members] = NULL; } } gr1->line = new_line; gptr1->gr_mem = new_members; return gr1; } /* * Scan the group database and split the groups which have more members * than specified, if this is the result from a current change. * * Return 0 on failure (errno set) and 1 on success. */ static int split_groups (unsigned int max_members) { struct commonio_entry *gr; for (gr = group_db.head; NULL != gr; gr = gr->next) { struct group *gptr = (struct group *)gr->eptr; struct commonio_entry *new; struct group *new_gptr; unsigned int members = 0; /* Check if this group must be split */ if (!gr->changed) continue; if (NULL == gptr) continue; for (members = 0; NULL != gptr->gr_mem[members]; members++); if (members <= max_members) continue; new = (struct commonio_entry *) malloc (sizeof *new); if (NULL == new) { errno = ENOMEM; return 0; } new->eptr = group_dup(gr->eptr); if (NULL == new->eptr) { free (new); errno = ENOMEM; return 0; } new_gptr = (struct group *)new->eptr; new->line = NULL; new->changed = true; /* Enforce the maximum number of members on gptr */ gptr->gr_mem[max_members] = NULL; /* The number of members in new_gptr will be check later */ new_gptr->gr_mem = &new_gptr->gr_mem[max_members]; /* insert the new entry in the list */ new->prev = gr; new->next = gr->next; gr->next = new; } return 1; }