* NEWS, lib/getdef.c, man/login.defs.5.xml: New login.defs

variable: MAX_MEMBERS_PER_GROUP. Used for the split groups support.
* lib/commonio.c, lib/commonio.h: Add an open_hook and close_hook
  operation. They are called after the database is actually opened
  and parse, or before it is closed.
* lib/groupio.c: Add an open_hook to merge split groups, and an
  close group to split groups if MAX_MEMBERS_PER_GROUP is set.
  This fixes gpasswd and chgpasswd when split groups are used.
* lib/sgroupio.c, lib/shadowio.c, lib/pwio.c: No open or close
  hooks for these databases. (unsure about what should be the gshadow
  behavior for split groups)
This commit is contained in:
nekral-guest
2007-11-23 00:07:59 +00:00
parent a0488ccac2
commit e15fbb905c
10 changed files with 259 additions and 4 deletions

View File

@ -10,6 +10,11 @@
extern int putgrent (const struct group *, FILE *);
extern struct group *sgetgrent (const char *);
static struct commonio_entry *merge_group_entries (struct commonio_entry *,
struct commonio_entry *);
static int split_groups (unsigned int);
static int group_open_hook (void);
static void *group_dup (const void *ent)
{
const struct group *gr = ent;
@ -49,6 +54,16 @@ static int group_put (const void *ent, FILE * file)
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,
@ -56,7 +71,9 @@ static struct commonio_ops group_ops = {
group_parse,
group_put,
fgetsx,
fputsx
fputsx,
group_open_hook,
group_close_hook
};
static struct commonio_db group_db = {
@ -170,3 +187,171 @@ 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; gr1; gr1 = gr1->next) {
for (gr2 = gr1->next; 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->prev->next = gr2->next;
}
}
}
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 struct commonio_entry *merge_group_entries (struct commonio_entry *gr1,
struct commonio_entry *gr2)
{
struct group *gptr1;
struct group *gptr2;
char *member;
char **new_members;
int members = 0;
char *new_line;
int 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) {
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];
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; 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);
new->eptr = group_dup(gr->eptr);
if (NULL == new->eptr) {
errno = ENOMEM;
return 0;
}
new_gptr = (struct group *)new->eptr;
new->line = NULL;
new->changed = 1;
/* 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;
}