v3-6-ctdb: s3: Add the "net groupfilter" command
authorVolker Lendecke <vl@samba.org>
Sun, 14 Dec 2008 23:16:56 +0000 (00:16 +0100)
committerMichael Adam <obnox@samba.org>
Thu, 13 Oct 2011 15:24:38 +0000 (17:24 +0200)
This is the start of a bad hack for even worse systems: Many Unix systems still
have the NGROUPS problem: A user can not be member of more than a very limited
number of groups. Solaris for example limits this to 16 by default. Many
Windows environments have a *LOT* more groups per user, some even go to
hundreds. Whether that is efficient is debatable, but it's there.

This patch implements the

"net groupfilter"

command with the "addsid", "delsid" and "list" subcommands. If any SIDs are
present according to "net groupfilter list" (they are stored in secrets.tdb),
then only the SIDs in that list are converted to GIDs for a user at login time.

This gives the Administrator the possibility to define a set of groups that are
used on the Unix box, making sure that no user is in more than NGROUPS of those
at a time.

This patch is incomplete in the sense that winbind is not aware of this, only
smbd. So it is kind of an emergency hack for smbd-only machines.

Volker

Signed-off-by: Michael Adam <obnox@samba.org>
source3/auth/auth_util.c
source3/include/proto.h
source3/include/secrets.h
source3/lib/util_sid.c
source3/passdb/secrets.c
source3/utils/net.c

index af4c41facbcde414bbb1a520d53f6c3fe29b55bd..fbf730b5937c23dbf66ecb605a6d852674c9c5f4 100644 (file)
@@ -30,6 +30,7 @@
 #include "../lib/util/util_pw.h"
 #include "lib/winbind_util.h"
 #include "passdb.h"
+#include "secrets.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_AUTH
@@ -448,6 +449,8 @@ NTSTATUS create_local_token(struct auth_serversupplied_info *server_info)
        size_t i;
        struct dom_sid tmp_sid;
        struct wbcUnixId *ids;
+       struct dom_sid *filter_sids;
+       uint32_t num_filter_sids;
 
        /*
         * If winbind is not around, we can not make much use of the SIDs the
@@ -495,10 +498,35 @@ NTSTATUS create_local_token(struct auth_serversupplied_info *server_info)
                return NT_STATUS_NO_MEMORY;
        }
 
+       if (!secrets_groupfilter_fetch(talloc_tos(), &filter_sids,
+                                      &num_filter_sids)) {
+               num_filter_sids = 0;
+       }
+
        /* Start at index 1, where the groups start. */
 
        for (i=1; i<t->num_sids; i++) {
 
+               /*
+                * For secondary groups, potentially apply a group
+                * filter for hosts with a silly groups-per-user limit
+                * such as for example Solaris
+                */
+
+               if ((i > 1) && (num_filter_sids != 0)) {
+                       /*
+                        * We have a SID filter in secrets.tdb, only
+                        * convert the SIDs in that filter to GIDs.
+                        */
+                       if (bsearch(&t->sids[i], filter_sids, num_filter_sids,
+                                   sizeof(struct dom_sid),
+                                   sid_compare_sort) == NULL) {
+                               DEBUG(10, ("Filtering out SID %s\n",
+                                          sid_string_dbg(&t->sids[i])));
+                               continue;
+                       }
+               }
+
                if (ids[i].type != WBC_ID_TYPE_GID) {
                        DEBUG(10, ("Could not convert SID %s to gid, "
                                   "ignoring it\n",
@@ -512,6 +540,8 @@ NTSTATUS create_local_token(struct auth_serversupplied_info *server_info)
                }
        }
 
+       TALLOC_FREE(filter_sids);
+
        /*
         * Add the "Unix Group" SID for each gid to catch mapped groups
         * and their Unix equivalent.  This is to solve the backwards
index 393dd77a36ffe62482728d65c56fcb80d0655a9e..0ed3431f654e68c91ee24df1cf926394aa9b9140 100644 (file)
@@ -803,6 +803,7 @@ NTSTATUS sid_array_from_info3(TALLOC_CTX *mem_ctx,
                              uint32_t *num_user_sids,
                              bool include_user_group_rid,
                              bool skip_ressource_groups);
+int sid_compare_sort(const void *p1, const void *p2);
 
 /* The following definitions come from lib/util_sock.c  */
 
index 01e635c5803790ae6a46e1b9e60c8ec6fff6b544..84a2b9e33676688dda640525843e85606212dbe5 100644 (file)
@@ -54,6 +54,8 @@
 #define SECRETS_AUTH_DOMAIN      "SECRETS/AUTH_DOMAIN"
 #define SECRETS_AUTH_PASSWORD  "SECRETS/AUTH_PASSWORD"
 
+#define SECRETS_GROUPFILTER_KEY "SECRETS/GROUPFILTER"
+
 /* structure for storing machine account password
    (ie. when samba server is member of a domain */
 struct machine_acct_pass {
@@ -124,5 +126,7 @@ void secrets_fetch_ipc_userpass(char **username, char **domain, char **password)
 bool secrets_store_generic(const char *owner, const char *key, const char *secret);
 char *secrets_fetch_generic(const char *owner, const char *key);
 bool secrets_delete_generic(const char *owner, const char *key);
+bool secrets_groupfilter_fetch(TALLOC_CTX *mem_ctx, struct dom_sid **psids,
+                              uint32_t *pnum_sids);
 
 #endif /* _SECRETS_H */
index f080d3dfb0c3afa80375cfcf98a97fcb52255f96..a5472f2ae9aa8841f8f689ac0d43d1465659d7af 100644 (file)
@@ -88,6 +88,35 @@ bool sid_linearize(char *outbuf, size_t len, const struct dom_sid *sid)
        return True;
 }
 
+int sid_compare_sort(const void *p1, const void *p2)
+{
+       const struct dom_sid *sid1 = (const struct dom_sid *)p1;
+       const struct dom_sid *sid2 = (const struct dom_sid *)p2;
+       int i;
+
+       if (sid1->sid_rev_num != sid2->sid_rev_num) {
+               return sid1->sid_rev_num - sid2->sid_rev_num;
+       }
+
+       for (i = 0; i < 6; i++) {
+               if (sid1->id_auth[i] != sid2->id_auth[i]) {
+                       return sid1->id_auth[i] - sid2->id_auth[i];
+               }
+       }
+
+       if (sid1->num_auths != sid2->num_auths) {
+               return sid1->num_auths - sid2->num_auths;
+       }
+
+       for (i = 0; i<sid1->num_auths; i++) {
+               if (sid1->sub_auths[i] != sid2->sub_auths[i]) {
+                       return sid1->sub_auths[i] - sid2->sub_auths[i];
+               }
+       }
+
+       return 0;
+}
+
 /*****************************************************************
  Returns true if SID is internal (and non-mappable).
 *****************************************************************/
index 8d544f1240207df04508600ed3cd5e8ad3b7c05d..b1343cc67d81ea504f77c2a1bf86841e20263516 100644 (file)
@@ -620,3 +620,36 @@ char *secrets_fetch_generic(const char *owner, const char *key)
        return secret;
 }
 
+bool secrets_groupfilter_fetch(TALLOC_CTX *mem_ctx, struct dom_sid **psids,
+                              uint32_t *pnum_sids)
+{
+       void *buf;
+       size_t buflen;
+       struct dom_sid *sids;
+       size_t num_sids;
+
+       buf = secrets_fetch(SECRETS_GROUPFILTER_KEY, &buflen);
+
+       if (buf == NULL) {
+               *psids = NULL;
+               *pnum_sids = 0;
+               return true;
+       }
+
+       if ((buflen % sizeof(struct dom_sid)) != 0) {
+               d_fprintf(stderr, "Invalid array size in secrets.tdb\n");
+       }
+
+       num_sids = buflen / sizeof(struct dom_sid);
+       sids = TALLOC_MEMDUP(
+                       talloc_tos(), buf, num_sids * sizeof(struct dom_sid));
+       SAFE_FREE(buf);
+       if (sids == NULL) {
+               d_fprintf(stderr, "Could not allocate SIDs\n");
+               return false;
+       }
+
+       *psids = sids;
+       *pnum_sids = num_sids;
+       return true;
+}
index aef97e933fca90129d9b40946b42a70a888b3560..10305b8a294cc6448be7492f771c5c241b583b95 100644 (file)
@@ -420,6 +420,144 @@ static int net_maxrid(struct net_context *c, int argc, const char **argv)
        return 0;
 }
 
+static int net_groupfilter_addsid(struct net_context *c, int argc,
+                                 const char **argv)
+{
+       struct dom_sid sid;
+       struct dom_sid *sids;
+       uint32_t num_sids;
+
+       if (argc != 1) {
+               d_fprintf(stderr, "usage: net groupfilter addsid <SID>\n");
+               return -1;
+       }
+
+       if (!string_to_sid(&sid, argv[0])) {
+               d_fprintf(stderr, "Could not convert '%s' to SID\n", argv[0]);
+               return -1;
+       }
+
+       if (!secrets_groupfilter_fetch(talloc_tos(), &sids, &num_sids)) {
+               d_fprintf(stderr, "Could not fetch sid list\n");
+               return -1;
+       }
+
+       if (!NT_STATUS_IS_OK(add_sid_to_array_unique(talloc_tos(), &sid,
+                                                    &sids, &num_sids))) {
+               d_fprintf(stderr, "add_sid_to_array_unique failed\n");
+               TALLOC_FREE(sids);
+               return -1;
+       }
+
+       qsort(sids, num_sids, sizeof(struct dom_sid), sid_compare_sort);
+
+       if (!secrets_store(SECRETS_GROUPFILTER_KEY, sids,
+                          num_sids * sizeof(struct dom_sid))) {
+               d_fprintf(stderr, "secrets_store failed\n");
+               TALLOC_FREE(sids);
+               return -1;
+       }
+
+       TALLOC_FREE(sids);
+
+       return 0;
+}
+
+static int net_groupfilter_delsid(struct net_context *c, int argc,
+                                 const char **argv)
+{
+       struct dom_sid sid;
+       struct dom_sid *sids;
+       uint32_t num_sids;
+       bool res;
+
+       if (argc != 1) {
+               d_fprintf(stderr, "usage: net groupfilter delsid <SID>\n");
+               return -1;
+       }
+
+       if (!string_to_sid(&sid, argv[0])) {
+               d_fprintf(stderr, "Could not convert '%s' to SID\n", argv[0]);
+               return -1;
+       }
+
+       if (!secrets_groupfilter_fetch(talloc_tos(), &sids, &num_sids)) {
+               d_fprintf(stderr, "Could not fetch sid list\n");
+               return -1;
+       }
+
+       del_sid_from_array(&sid, &sids, &num_sids);
+
+       if (num_sids == 0) {
+               res = secrets_delete(SECRETS_GROUPFILTER_KEY);
+       } else {
+               res = secrets_store(SECRETS_GROUPFILTER_KEY, sids,
+                                   num_sids * sizeof(struct dom_sid));
+       }
+
+       if (!res) {
+               d_fprintf(stderr, "secrets_store failed\n");
+               TALLOC_FREE(sids);
+               return -1;
+       }
+
+       TALLOC_FREE(sids);
+
+       return 0;
+}
+
+static int net_groupfilter_list(struct net_context *c, int argc,
+                               const char **argv)
+{
+       struct dom_sid *sids;
+       uint32_t num_sids;
+       int i;
+
+       if (!secrets_groupfilter_fetch(talloc_tos(), &sids, &num_sids)) {
+               d_fprintf(stderr, "Could not fetch sid list\n");
+               return -1;
+       }
+
+       for (i=0; i<num_sids; i++) {
+               d_printf("%s\n", sid_string_tos(&sids[i]));
+       }
+
+       TALLOC_FREE(sids);
+
+       return 0;
+}
+
+static int net_groupfilter(struct net_context *c, int argc,
+                          const char **argv)
+{
+       struct functable func[] = {
+               {
+                       "addsid",
+                       net_groupfilter_addsid,
+                       NET_TRANSPORT_LOCAL,
+                       "Add a SID to the groupfilter",
+                       ""
+               },
+               {
+                       "delsid",
+                       net_groupfilter_delsid,
+                       NET_TRANSPORT_LOCAL,
+                       "Delete a SID from the groupfilter",
+                       ""
+               },
+               {
+                       "list",
+                       net_groupfilter_list,
+                       NET_TRANSPORT_LOCAL,
+                       "List groupfilter SIDs",
+                       ""
+               },
+               { NULL, NULL, 0, NULL, NULL }
+       };
+
+       return net_run_function(c, argc, argv, "net groupfilter", func);
+}
+
 /* main function table */
 static struct functable net_func[] = {
        {
@@ -738,6 +876,12 @@ static struct functable net_func[] = {
                N_("  Use 'net help serverid' to get more information about "
                   "'net serverid' commands.")
        },
+       {       "groupfilter",
+               net_groupfilter,
+               NET_TRANSPORT_LOCAL,
+               "Edit the groupfilter sidlist",
+               "  "
+       },
 
 #ifdef WITH_FAKE_KASERVER
        {       "afs",