* 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:
parent
a0488ccac2
commit
e15fbb905c
14
ChangeLog
14
ChangeLog
@ -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
4
NEWS
@ -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.
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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},
|
||||
|
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;
|
||||
}
|
||||
|
||||
|
@ -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 = {
|
||||
|
@ -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 = {
|
||||
|
@ -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 = {
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user