* 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>
|
2007-11-22 Nicolas François <nicolas.francois@centraliens.net>
|
||||||
|
|
||||||
* NEWS, src/gpasswd.c: Read the group and shadow groups using
|
* 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
|
- 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
|
writes the changes in /etc/group and/or /etc/gshadow, but used to read
|
||||||
information from getgrnam (hence possibly from another group database).
|
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:
|
*** documentation:
|
||||||
- Generate the translated manpages from PO at build time.
|
- 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))
|
if (ferror (db->fp))
|
||||||
goto cleanup_errno;
|
goto cleanup_errno;
|
||||||
|
|
||||||
|
if (db->ops->open_hook && !db->ops->open_hook ())
|
||||||
|
goto cleanup_errno;
|
||||||
|
|
||||||
db->isopen = 1;
|
db->isopen = 1;
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
@ -669,6 +672,9 @@ int commonio_close (struct commonio_db *db)
|
||||||
goto success;
|
goto success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (db->ops->close_hook && !db->ops->close_hook ())
|
||||||
|
goto fail;
|
||||||
|
|
||||||
memzero (&sb, sizeof sb);
|
memzero (&sb, sizeof sb);
|
||||||
if (db->fp) {
|
if (db->fp) {
|
||||||
if (fstat (fileno (db->fp), &sb)) {
|
if (fstat (fileno (db->fp), &sb)) {
|
||||||
|
|
|
@ -52,6 +52,15 @@ struct commonio_ops {
|
||||||
*/
|
*/
|
||||||
char *(*fgets) (char *, int, FILE *);
|
char *(*fgets) (char *, int, FILE *);
|
||||||
int (*fputs) (const char *, 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},
|
{"LOG_UNKFAIL_ENAB", NULL},
|
||||||
{"MAIL_DIR", NULL},
|
{"MAIL_DIR", NULL},
|
||||||
{"MAIL_FILE", NULL},
|
{"MAIL_FILE", NULL},
|
||||||
|
{"MAX_MEMBERS_PER_GROUP", NULL},
|
||||||
{"MD5_CRYPT_ENAB", NULL},
|
{"MD5_CRYPT_ENAB", NULL},
|
||||||
{"PASS_MAX_DAYS", NULL},
|
{"PASS_MAX_DAYS", NULL},
|
||||||
{"PASS_MIN_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 int putgrent (const struct group *, FILE *);
|
||||||
extern struct group *sgetgrent (const char *);
|
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)
|
static void *group_dup (const void *ent)
|
||||||
{
|
{
|
||||||
const struct group *gr = 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;
|
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 = {
|
static struct commonio_ops group_ops = {
|
||||||
group_dup,
|
group_dup,
|
||||||
group_free,
|
group_free,
|
||||||
|
@ -56,7 +71,9 @@ static struct commonio_ops group_ops = {
|
||||||
group_parse,
|
group_parse,
|
||||||
group_put,
|
group_put,
|
||||||
fgetsx,
|
fgetsx,
|
||||||
fputsx
|
fputsx,
|
||||||
|
group_open_hook,
|
||||||
|
group_close_hook
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct commonio_db group_db = {
|
static struct commonio_db group_db = {
|
||||||
|
@ -170,3 +187,171 @@ int gr_sort ()
|
||||||
{
|
{
|
||||||
return commonio_sort (&group_db, gr_cmp);
|
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_parse,
|
||||||
passwd_put,
|
passwd_put,
|
||||||
fgets,
|
fgets,
|
||||||
fputs
|
fputs,
|
||||||
|
NULL, /* open_hook */
|
||||||
|
NULL /* close_hook */
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct commonio_db passwd_db = {
|
static struct commonio_db passwd_db = {
|
||||||
|
|
|
@ -100,7 +100,9 @@ static struct commonio_ops gshadow_ops = {
|
||||||
gshadow_parse,
|
gshadow_parse,
|
||||||
gshadow_put,
|
gshadow_put,
|
||||||
fgetsx,
|
fgetsx,
|
||||||
fputsx
|
fputsx,
|
||||||
|
NULL, /* open_hook */
|
||||||
|
NULL /* close_hook */
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct commonio_db gshadow_db = {
|
static struct commonio_db gshadow_db = {
|
||||||
|
|
|
@ -54,7 +54,9 @@ static struct commonio_ops shadow_ops = {
|
||||||
shadow_parse,
|
shadow_parse,
|
||||||
shadow_put,
|
shadow_put,
|
||||||
fgets,
|
fgets,
|
||||||
fputs
|
fputs,
|
||||||
|
NULL, /* open_hook */
|
||||||
|
NULL /* close_hook */
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct commonio_db shadow_db = {
|
static struct commonio_db shadow_db = {
|
||||||
|
|
|
@ -121,6 +121,36 @@
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
<varlistentry>
|
||||||
<term>MD5_CRYPT_ENAB (boolean)</term>
|
<term>MD5_CRYPT_ENAB (boolean)</term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user