* 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:
187
lib/groupio.c
187
lib/groupio.c
@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user