* 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

@ -1,3 +1,17 @@
2007-11-22 Nicolas François <nicolas.francois@centraliens.net>
* 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)
2007-11-22 Nicolas François <nicolas.francois@centraliens.net>
* NEWS, src/gpasswd.c: Read the group and shadow groups using

4
NEWS
View File

@ -46,6 +46,10 @@ shadow-4.0.18.1 -> shadow-4.0.18.2 UNRELEASED
- gpasswd: Only read information from the local file group database. It
writes the changes in /etc/group and/or /etc/gshadow, but used to read
information from getgrnam (hence possibly from another group database).
- New login.defs variable: MAX_MEMBERS_PER_GROUP. It should provide a
better support for split groups. Be careful when using this variable:
not all tools support well split groups (in or out of the shadow
tool suite). It fixes gpasswd and chgpasswd when split groups are used.
*** documentation:
- Generate the translated manpages from PO at build time.

View File

@ -520,6 +520,9 @@ int commonio_open (struct commonio_db *db, int mode)
if (ferror (db->fp))
goto cleanup_errno;
if (db->ops->open_hook && !db->ops->open_hook ())
goto cleanup_errno;
db->isopen = 1;
return 1;
@ -669,6 +672,9 @@ int commonio_close (struct commonio_db *db)
goto success;
}
if (db->ops->close_hook && !db->ops->close_hook ())
goto fail;
memzero (&sb, sizeof sb);
if (db->fp) {
if (fstat (fileno (db->fp), &sb)) {

View File

@ -52,6 +52,15 @@ struct commonio_ops {
*/
char *(*fgets) (char *, int, FILE *);
int (*fputs) (const char *, FILE *);
/*
* open_hook and close_hook.
* If non NULL, these functions will be called after the database
* is open or before it is closed.
* They return 0 on failure and 1 on success.
*/
int (*open_hook) (void);
int (*close_hook) (void);
};
/*

View File

@ -68,6 +68,7 @@ static struct itemdef def_table[] = {
{"LOG_UNKFAIL_ENAB", NULL},
{"MAIL_DIR", NULL},
{"MAIL_FILE", NULL},
{"MAX_MEMBERS_PER_GROUP", NULL},
{"MD5_CRYPT_ENAB", NULL},
{"PASS_MAX_DAYS", NULL},
{"PASS_MIN_DAYS", NULL},

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;
}

View File

@ -57,7 +57,9 @@ static struct commonio_ops passwd_ops = {
passwd_parse,
passwd_put,
fgets,
fputs
fputs,
NULL, /* open_hook */
NULL /* close_hook */
};
static struct commonio_db passwd_db = {

View File

@ -100,7 +100,9 @@ static struct commonio_ops gshadow_ops = {
gshadow_parse,
gshadow_put,
fgetsx,
fputsx
fputsx,
NULL, /* open_hook */
NULL /* close_hook */
};
static struct commonio_db gshadow_db = {

View File

@ -54,7 +54,9 @@ static struct commonio_ops shadow_ops = {
shadow_parse,
shadow_put,
fgets,
fputs
fputs,
NULL, /* open_hook */
NULL /* close_hook */
};
static struct commonio_db shadow_db = {

View File

@ -121,6 +121,36 @@
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>MAX_MEMBERS_PER_GROUP (number)</term>
<listitem>
<para>
Maximum members per group entry. When the maximum is reached,
a new group entry (line) is started is
<filename>/etc/group</filename> (with the same name, same
password, and same GID).
</para>
<para>
The default value is 0, meaning that there are no limits in
the number of members in a group.
</para>
<!-- Note: on HP, split groups have the same ID, but different
names. -->
<para>
This feature (split group) permits to limit the length of
lines in the group file. This is useful to make sure that
lines for NIS groups are not larger than 1024 characters.
</para>
<para>
If you need to enforce such limit, you can use 25.
</para>
<para>
Note: split groups may not be supported by all tools (even in
the Shadow toolsuite. Yous hould not use this variable unless
you really need it.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>MD5_CRYPT_ENAB (boolean)</term>
<listitem>