]> granicus.if.org Git - shadow/commitdiff
* NEWS, lib/getdef.c, man/login.defs.5.xml: New login.defs
authornekral-guest <nekral-guest@5a98b0ae-9ef6-0310-add3-de5d479b70d7>
Fri, 23 Nov 2007 00:07:59 +0000 (00:07 +0000)
committernekral-guest <nekral-guest@5a98b0ae-9ef6-0310-add3-de5d479b70d7>
Fri, 23 Nov 2007 00:07:59 +0000 (00:07 +0000)
  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)

ChangeLog
NEWS
lib/commonio.c
lib/commonio.h
lib/getdef.c
lib/groupio.c
lib/pwio.c
lib/sgroupio.c
lib/shadowio.c
man/login.defs.5.xml

index dc9674f529afd3cb02afdb74b4b574f1ae51bfd8..3c9a5735b02ffab7bfce45c794bc047ece4736fc 100644 (file)
--- a/ChangeLog
+++ b/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
diff --git a/NEWS b/NEWS
index 5f2fbfc7d13ef79923811ea894a63fbab3a31bc9..818badb741cb425ca2d485a075ef1d69718893ee 100644 (file)
--- a/NEWS
+++ b/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.
index a1e7e1efd38e8924ace90f0de7d858102712e5d0..50197f97dd6a9d84dfe1b4d6f8d6453748731094 100644 (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)) {
index 7786d661253b314af1b4cd5c233005bdb3f1db3c..b5dfc749a59a367d29f772496221c180ca3369c7 100644 (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);
 };
 
 /*
index df474c81a883dbfbb49dade58b6f3551b36f0710..02f2ecafe90d1d8aae83ea597ca918d625e9ce23 100644 (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},
index 686dca954b9ffc5db7ae3f41362e77d6600149bb..8ac673d8956f5b1fb11893a730a7190444d188e6 100644 (file)
 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;
+}
+
index ae99b411360c2ba31da56e7c02409252cb135342..cbedb04b9a489896900a15cf6648add8a85bf84a 100644 (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 = {
index cf3f2dd142a6fcb2a796daf72c7c5aed4c3dc075..dad92f4c0ffc4d053ca5217af5878041831012bc 100644 (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 = {
index d22f0c6ea4e9425e930f0654ed4a260562dd4c6e..e83e8ce1b75cd9ada1f2ff70890bbebd0c30f4a2 100644 (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 = {
index 9631b99f6890009882ad1bf21449ef99b9a86e85..a9804e77005b7eb0b76403332236048a4e63075a 100644 (file)
          </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>