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>
Wed, 17 Dec 2008 09:41:26 +0000 (10:41 +0100)
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>
source/auth/auth_util.c
source/include/secrets.h
source/lib/util_sid.c
source/passdb/secrets.c
source/utils/net.c

index a183afbe1a3e7a794490dd3db5a2998b42226a8e..9118c733ca28736dbd9d8156a02de29762b9e040 100644 (file)
@@ -701,6 +701,8 @@ NTSTATUS create_local_token(auth_serversupplied_info *server_info)
        NTSTATUS status;
        size_t i;
        
+       struct dom_sid *filter_sids;
+       size_t num_filter_sids;
 
        mem_ctx = talloc_new(NULL);
        if (mem_ctx == NULL) {
@@ -744,11 +746,37 @@ NTSTATUS create_local_token(auth_serversupplied_info *server_info)
        server_info->n_groups = 0;
        server_info->groups = NULL;
 
+       if (!secrets_groupfilter_fetch(mem_ctx, &filter_sids,
+                                      &num_filter_sids)) {
+               num_filter_sids = 0;
+       }
+
        /* Start at index 1, where the groups start. */
 
        for (i=1; i<server_info->ptok->num_sids; i++) {
                gid_t gid;
                DOM_SID *sid = &server_info->ptok->user_sids[i];
+               size_t sidindex;
+
+               /*
+                * 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(sid, filter_sids, num_filter_sids,
+                                   sizeof(struct dom_sid),
+                                   sid_compare_sort) == NULL) {
+                               DEBUG(10, ("Filtering out SID %s\n",
+                                          sid_string_dbg(sid)));
+                               continue;
+                       }
+               }
 
                if (!sid_to_gid(sid, &gid)) {
                        DEBUG(10, ("Could not convert SID %s to gid, "
@@ -760,6 +788,9 @@ NTSTATUS create_local_token(auth_serversupplied_info *server_info)
        }
        
        debug_nt_user_token(DBGC_AUTH, 10, server_info->ptok);
+       debug_unix_user_token(DBGC_AUTH, 10, server_info->uid,
+                             server_info->gid, server_info->n_groups,
+                             server_info->groups);
 
        status = log_nt_token(mem_ctx, server_info->ptok);
 
index d9f457558bf6db2c9f5a89d34968eaaadd5f55e0..944585bf947f192ccdcca2220bc414b28349906c 100644 (file)
@@ -51,6 +51,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 {
index f656bb13dc8807c582e4471c54ef755a60076cd3..a41bb2e479564d5d21b69b4d4bb2013f4447f453 100644 (file)
@@ -467,6 +467,35 @@ int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2)
        return sid_compare_auth(sid1, sid2);
 }
 
+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, res;
+
+       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;
+}
+
 /*****************************************************************
  See if 2 SIDs are in the same domain
  this just compares the leading sub-auths
index 2168699714918817e6377c1da49408f916cbc32c..4872f59f084e92e640f3efb762e51ca466d0bd63 100644 (file)
@@ -1363,3 +1363,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,
+                              size_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 d02502c1880a8ddafb9655f099b014c02749f009..c0aa4fd88d5245f2279135cc181d85d97636cc9d 100644 (file)
@@ -937,6 +937,135 @@ static int net_maxrid(int argc, const char **argv)
        return 0;
 }
 
+static int net_groupfilter_addsid(int argc, const char **argv)
+{
+       struct dom_sid sid;
+       struct dom_sid *sids;
+       size_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(int argc, const char **argv)
+{
+       struct dom_sid sid;
+       struct dom_sid *sids;
+       size_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(int argc, const char **argv)
+{
+       struct dom_sid *sids;
+       size_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(int argc, const char **argv)
+{
+       int ret = -1;
+       struct functable2 func[] = {
+               {
+                       "addsid",
+                       net_groupfilter_addsid,
+                       "Add a SID to the groupfilter"
+               },
+               {
+                       "delsid",
+                       net_groupfilter_delsid,
+                       "Delete a SID from the groupfilter"
+               },
+               {
+                       "list",
+                       net_groupfilter_list,
+                       "List groupfilter SIDs"
+               },
+               { NULL, NULL, NULL }
+       };
+
+       return net_run_function2(argc, argv, "net groupfilter", func);
+}
+
 /****************************************************************************
 ****************************************************************************/
 
@@ -1004,6 +1133,7 @@ static struct functable net_func[] = {
        {"USERSIDLIST", net_usersidlist},
        {"CONF", net_conf},
        {"REGISTRY", net_registry},
+       {"GROUPFILTER", net_groupfilter},
 #ifdef WITH_FAKE_KASERVER
        {"AFS", net_afs},
 #endif