r2935: This is a long-standing one in my patch-queue: A pair of net commands
authorVolker Lendecke <vlendec@samba.org>
Tue, 12 Oct 2004 11:58:01 +0000 (11:58 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 15:52:57 +0000 (10:52 -0500)
(usersidlist/allowedusers) to scan a file server's share and list all users
who have permission to connect there.

Volker

source/rpc_client/cli_srvsvc.c
source/utils/net.c
source/utils/net_help.c
source/utils/net_rpc.c

index f8580d0fe2e765be23a9c51ede8c7233b520b73d..da4721c2c7d1dead8244911bee875738bf5b58fb 100644 (file)
@@ -225,6 +225,94 @@ WERROR cli_srvsvc_net_share_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
        return result;
 }
 
+WERROR cli_srvsvc_net_share_get_info(struct cli_state *cli,
+                                    TALLOC_CTX *mem_ctx,
+                                    const char *sharename,
+                                    uint32 info_level,
+                                    SRV_SHARE_INFO *info)
+{
+       prs_struct qbuf, rbuf;
+       SRV_Q_NET_SHARE_GET_INFO q;
+       SRV_R_NET_SHARE_GET_INFO r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       init_srv_q_net_share_get_info(&q, cli->srv_name_slash, sharename,
+                                     info_level);
+
+       /* Marshall data and send request */
+
+       if (!srv_io_q_net_share_get_info("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SRV_NET_SHARE_GET_INFO, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!srv_io_r_net_share_get_info("", &r, &rbuf, 0))
+               goto done;
+
+       result = r.status;
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       ZERO_STRUCTP(info);
+
+       info->switch_value = info_level;
+
+       switch(info_level) {
+       case 502:
+       {
+               SRV_SHARE_INFO_502 *info502 = &info->share.info502;
+               SH_INFO_502_STR *info502_str = &info502->info_502_str;
+               
+               char *s;
+
+               info->share.info502 = r.info.share.info502;
+
+               /* Duplicate strings */
+
+               s = unistr2_tdup(mem_ctx, &info502_str->uni_netname);
+               if (s)
+                       init_unistr2(&info502_str->uni_netname,
+                                    s, UNI_STR_TERMINATE);
+
+               s = unistr2_tdup(mem_ctx, &info502_str->uni_remark);
+               if (s)
+                       init_unistr2(&info502_str->uni_remark,
+                                    s, UNI_STR_TERMINATE);
+
+               s = unistr2_tdup(mem_ctx, &info502_str->uni_path);
+               if (s)
+                       init_unistr2(&info502_str->uni_path,
+                                    s, UNI_STR_TERMINATE);
+
+               s = unistr2_tdup(mem_ctx, &info502_str->uni_passwd);
+               if (s)
+                       init_unistr2(&info502_str->uni_passwd,
+                                    s, UNI_STR_TERMINATE);
+
+               info502_str->sd = dup_sec_desc(mem_ctx, info502_str->sd);
+               break;
+       }
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
 WERROR cli_srvsvc_net_share_del(struct cli_state *cli, TALLOC_CTX *mem_ctx,
                                const char *sharename)
 {
index 9a022b2d38d1c75fa080bf0a104d5ae85f5ce30d..2fe15c2b8ef6a518a42e872f91fb5a0a451ecbe0 100644 (file)
@@ -697,6 +697,7 @@ static struct functable net_func[] = {
        {"MAXRID", net_maxrid},
        {"IDMAP", net_idmap},
        {"STATUS", net_status},
+       {"USERSIDLIST", net_usersidlist},
 #ifdef WITH_FAKE_KASERVER
        {"AFSKEY", net_afskey},
 #endif
index 859bb949c73179ee6deb3a7cad58971cd68da29c..4b19bcf5aa703248ddaf4ced4a73b435638dc829 100644 (file)
@@ -121,9 +121,14 @@ int net_help_share(int argc, const char **argv)
         "\tenumerates all exported resources (network shares) "
         "on target server\n\n"
         "net [<method>] share ADD <name=serverpath> [misc. options] [targets]"
-        "\n\tAdds a share from a server (makes the export active)\n\n"
-        "net [<method>] share DELETE <sharename> [misc. options] [targets]"
-        "\n\tDeletes a share from a server (makes the export inactive)\n\n"
+       "\n\tadds a share from a server (makes the export active)\n\n"
+       "net [<method>] share DELETE <sharename> [misc. options] [targets]"
+       "\n\tdeletes a share from a server (makes the export inactive)\n\n"
+       "net [<method>] share ALLOWEDUSERS [<filename>] "
+       "[misc. options] [targets]"
+       "\n\tshows a list of all shares together with all users allowed to"
+       "\n\taccess them. This needs the output of 'net usersidlist' on"
+       "\n\tstdin or in <filename>.\n"
         "net [<method>] share MIGRATE FILES <sharename> [misc. options] [targets]"
         "\n\tMigrates files from remote to local server\n\n"
         "net [<method>] share MIGRATE SHARES <sharename> [misc. options] [targets]"
@@ -217,6 +222,7 @@ static int net_usage(int argc, const char **argv)
                 "  net changesecretpw\tto change the machine password in the local secrets database only\n"\
                 "                    \tthis requires the -f flag as a safety barrier\n"\
                 "  net status\t\tShow server status\n"\
+               "  net usersidlist\tto get a list of all users with their SIDs\n"
                 "\n"\
                 "  net ads <command>\tto run ADS commands\n"\
                 "  net rap <command>\tto run RAP (pre-RPC) commands\n"\
@@ -254,6 +260,7 @@ int net_help(int argc, const char **argv)
                {"PASSWORD", net_rap_password_usage},
                {"TIME", net_time_usage},
                {"LOOKUP", net_lookup_usage},
+               {"USERSIDLIST", net_usersidlist_usage},
 #ifdef WITH_FAKE_KASERVER
                {"AFSKEY", net_afskey_usage},
 #endif
index 9b96ad137173716a95c9846cd6ab856e8856d25c..018731afdaafc6e0730f1664e924209784ddd9f5 100644 (file)
@@ -2973,6 +2973,784 @@ static int rpc_share_migrate(int argc, const char **argv)
        return net_run_function(argc, argv, func, rpc_share_usage);
 }
 
+struct full_alias {
+       DOM_SID sid;
+       int num_members;
+       DOM_SID *members;
+};
+
+static int num_server_aliases;
+static struct full_alias *server_aliases;
+
+/*
+ * Add an alias to the static list.
+ */
+static void push_alias(TALLOC_CTX *mem_ctx, struct full_alias *alias)
+{
+       if (server_aliases == NULL)
+               server_aliases = malloc(100 * sizeof(struct full_alias));
+
+       server_aliases[num_server_aliases] = *alias;
+       num_server_aliases += 1;
+}
+
+/*
+ * For a specific domain on the server, fetch all the aliases
+ * and their members. Add all of them to the server_aliases.
+ */
+static NTSTATUS
+rpc_fetch_domain_aliases(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                        POLICY_HND *connect_pol,
+                        const DOM_SID *domain_sid)
+{
+       uint32 start_idx, max_entries, num_entries, i;
+       struct acct_info *groups;
+       NTSTATUS result;
+       POLICY_HND domain_pol;
+
+       /* Get domain policy handle */
+       
+       result = cli_samr_open_domain(cli, mem_ctx, connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     domain_sid, &domain_pol);
+       if (!NT_STATUS_IS_OK(result))
+               return result;
+
+       start_idx = 0;
+       max_entries = 250;
+
+       do {
+               result = cli_samr_enum_als_groups(cli, mem_ctx, &domain_pol,
+                                                 &start_idx, max_entries,
+                                                 &groups, &num_entries);
+
+               for (i = 0; i < num_entries; i++) {
+
+                       POLICY_HND alias_pol;
+                       struct full_alias alias;
+                       DOM_SID *members;
+                       int j;
+
+                       result = cli_samr_open_alias(cli, mem_ctx, &domain_pol,
+                                                    MAXIMUM_ALLOWED_ACCESS,
+                                                    groups[i].rid,
+                                                    &alias_pol);
+                       if (!NT_STATUS_IS_OK(result))
+                               goto done;
+
+                       result = cli_samr_query_aliasmem(cli, mem_ctx,
+                                                        &alias_pol,
+                                                        &alias.num_members,
+                                                        &members);
+                       if (!NT_STATUS_IS_OK(result))
+                               goto done;
+
+                       result = cli_samr_close(cli, mem_ctx, &alias_pol);
+                       if (!NT_STATUS_IS_OK(result))
+                               goto done;
+
+                       alias.members = NULL;
+
+                       if (alias.num_members > 0) {
+                               alias.members = malloc(alias.num_members *
+                                                      sizeof(DOM_SID));
+
+                               for (j = 0; j < alias.num_members; j++)
+                                       sid_copy(&alias.members[j],
+                                                &members[j]);
+                       }
+
+                       sid_copy(&alias.sid, domain_sid);
+                       sid_append_rid(&alias.sid, groups[i].rid);
+
+                       push_alias(mem_ctx, &alias);
+               }
+       } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+       result = NT_STATUS_OK;
+
+ done:
+       cli_samr_close(cli, mem_ctx, &domain_pol);
+
+       return result;
+}
+
+/*
+ * Dump server_aliases as names for debugging purposes.
+ */
+static NTSTATUS
+rpc_aliaslist_dump(const DOM_SID *domain_sid, const char *domain_name,
+                  struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                  int argc, const char **argv)
+{
+       int i;
+       NTSTATUS result;
+       POLICY_HND lsa_pol;
+
+       result = cli_lsa_open_policy(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &lsa_pol);
+       if (!NT_STATUS_IS_OK(result))
+               return result;
+
+       for (i=0; i<num_server_aliases; i++) {
+               char **names;
+               char **domains;
+               uint32 *types;
+               int j;
+
+               struct full_alias *alias = &server_aliases[i];
+
+               result = cli_lsa_lookup_sids(cli, mem_ctx, &lsa_pol, 1,
+                                            &alias->sid,
+                                            &domains, &names, &types);
+               if (!NT_STATUS_IS_OK(result))
+                       continue;
+
+               DEBUG(1, ("%s\\%s %d: ", domains[0], names[0], types[0]));
+
+               if (alias->num_members == 0) {
+                       DEBUG(1, ("\n"));
+                       continue;
+               }
+
+               result = cli_lsa_lookup_sids(cli, mem_ctx, &lsa_pol,
+                                            alias->num_members,
+                                            alias->members,
+                                            &domains, &names, &types);
+
+               if (!NT_STATUS_IS_OK(result) &&
+                   !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED))
+                       continue;
+
+               for (j=0; j<alias->num_members; j++)
+                       DEBUG(1, ("%s\\%s (%d); ",
+                                 domains[j] ? domains[j] : "*unknown*", 
+                                 names[j] ? names[j] : "*unknown*",types[j]));
+               DEBUG(1, ("\n"));
+       }
+
+       cli_lsa_close(cli, mem_ctx, &lsa_pol);
+
+       return NT_STATUS_OK;
+}
+
+/*
+ * Fetch a list of all server aliases and their members into
+ * server_aliases.
+ */
+static NTSTATUS
+rpc_aliaslist_internals(const DOM_SID *domain_sid, const char *domain_name,
+                       struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                       int argc, const char **argv)
+{
+       NTSTATUS result;
+       POLICY_HND connect_pol;
+       DOM_SID global_sid_Builtin;
+
+       result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                 &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+       
+       string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+       result = rpc_fetch_domain_aliases(cli, mem_ctx, &connect_pol,
+                                         &global_sid_Builtin);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+       
+       result = rpc_fetch_domain_aliases(cli, mem_ctx, &connect_pol,
+                                         domain_sid);
+
+       cli_samr_close(cli, mem_ctx, &connect_pol);
+ done:
+       return result;
+}
+
+static void init_user_token(NT_USER_TOKEN *token, DOM_SID *user_sid)
+{
+       DOM_SID global_sid_World;
+       DOM_SID global_sid_Network;
+       DOM_SID global_sid_Authenticated_Users;
+
+       string_to_sid(&global_sid_World, "S-1-1-0");
+       string_to_sid(&global_sid_Network, "S-1-5-2");
+       string_to_sid(&global_sid_Authenticated_Users, "S-1-5-11");
+
+       token->num_sids = 4;
+
+       token->user_sids = malloc(4 * sizeof(DOM_SID));
+
+       token->user_sids[0] = *user_sid;
+       sid_copy(&token->user_sids[1], &global_sid_World);
+       sid_copy(&token->user_sids[2], &global_sid_Network);
+       sid_copy(&token->user_sids[3], &global_sid_Authenticated_Users);
+}
+
+static void free_user_token(NT_USER_TOKEN *token)
+{
+       SAFE_FREE(token->user_sids);
+}
+
+static BOOL is_sid_in_token(NT_USER_TOKEN *token, DOM_SID *sid)
+{
+       int i;
+
+       for (i=0; i<token->num_sids; i++) {
+               if (sid_compare(sid, &token->user_sids[i]) == 0)
+                       return True;
+       }
+       return False;
+}
+
+static void add_sid_to_token(NT_USER_TOKEN *token, DOM_SID *sid)
+{
+       if (is_sid_in_token(token, sid))
+               return;
+
+       token->user_sids = Realloc(token->user_sids,
+                                  (token->num_sids+1) * sizeof(DOM_SID));
+
+       sid_copy(&token->user_sids[token->num_sids], sid);
+
+       token->num_sids += 1;
+}
+
+struct user_token {
+       fstring name;
+       NT_USER_TOKEN token;
+};
+
+static void dump_user_token(struct user_token *token)
+{
+       int i;
+
+       d_printf("%s\n", token->name);
+
+       for (i=0; i<token->token.num_sids; i++) {
+               d_printf(" %s\n", sid_string_static(&token->token.user_sids[i]));
+       }
+}
+
+static BOOL is_alias_member(DOM_SID *sid, struct full_alias *alias)
+{
+       int i;
+
+       for (i=0; i<alias->num_members; i++) {
+               if (sid_compare(sid, &alias->members[i]) == 0)
+                       return True;
+       }
+
+       return False;
+}
+
+static void collect_sid_memberships(NT_USER_TOKEN *token, DOM_SID sid)
+{
+       int i;
+
+       for (i=0; i<num_server_aliases; i++) {
+               if (is_alias_member(&sid, &server_aliases[i]))
+                       add_sid_to_token(token, &server_aliases[i].sid);
+       }
+}
+
+/*
+ * We got a user token with all the SIDs we can know about without asking the
+ * server directly. These are the user and domain group sids. All of these can
+ * be members of aliases. So scan the list of aliases for each of the SIDs and
+ * add them to the token.
+ */
+
+static void collect_alias_memberships(NT_USER_TOKEN *token)
+{
+       int num_global_sids = token->num_sids;
+       int i;
+
+       for (i=0; i<num_global_sids; i++) {
+               collect_sid_memberships(token, token->user_sids[i]);
+       }
+}
+
+static BOOL get_user_sids(const char *domain, const char *user,
+                         NT_USER_TOKEN *token)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       fstring full_name;
+       NSS_STATUS result;
+
+       DOM_SID user_sid;
+
+       int i;
+
+       fstr_sprintf(full_name, "%s%c%s",
+                    domain, *lp_winbind_separator(), user);
+
+       /* First let's find out the user sid */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       fstrcpy(request.data.name.dom_name, domain);
+       fstrcpy(request.data.name.name, user);
+
+       result = winbindd_request(WINBINDD_LOOKUPNAME, &request, &response);
+
+       if (result != NSS_STATUS_SUCCESS) {
+               DEBUG(1, ("winbind could not find %s\n", full_name));
+               return False;
+       }
+
+       if (response.data.sid.type != SID_NAME_USER) {
+               DEBUG(1, ("%s is not a user\n", full_name));
+               return False;
+       }
+
+       string_to_sid(&user_sid, response.data.sid.sid);
+
+       init_user_token(token, &user_sid);
+
+       /* And now the groups winbind knows about */
+
+       ZERO_STRUCT(response);
+
+       fstrcpy(request.data.username, full_name);
+
+       result = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+       if (result != NSS_STATUS_SUCCESS) {
+               DEBUG(1, ("winbind could not get groups of %s\n", full_name));
+               return False;
+       }
+
+       for (i = 0; i < response.data.num_entries; i++) {
+               gid_t gid = ((gid_t *)response.extra_data)[i];
+               DOM_SID sid;
+
+               struct winbindd_request sidrequest;
+               struct winbindd_response sidresponse;
+
+               ZERO_STRUCT(sidrequest);
+               ZERO_STRUCT(sidresponse);
+
+               sidrequest.data.gid = gid;
+
+               result = winbindd_request(WINBINDD_GID_TO_SID,
+                                         &sidrequest, &sidresponse);
+
+               if (result != NSS_STATUS_SUCCESS) {
+                       DEBUG(1, ("winbind could not find SID of gid %d\n",
+                                 gid));
+                       return False;
+               }
+
+               DEBUG(3, (" %s\n", sidresponse.data.sid.sid));
+
+               string_to_sid(&sid, sidresponse.data.sid.sid);
+               add_sid_to_token(token, &sid);
+       }
+
+       SAFE_FREE(response.extra_data);
+
+       return True;
+}
+       
+/**
+ * Get a list of all user tokens we want to look at
+ **/
+static BOOL get_user_tokens(int *num_tokens, struct user_token **user_tokens)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       const char *extra_data;
+       fstring name;
+       int i;
+       struct user_token *result;
+
+       /* Send request to winbind daemon */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+       
+       if (winbindd_request(WINBINDD_LIST_USERS, &request, &response) !=
+           NSS_STATUS_SUCCESS)
+               return False;
+
+       /* Look through extra data */
+
+       if (!response.extra_data)
+               return False;
+
+       extra_data = (const char *)response.extra_data;
+       *num_tokens = 0;
+
+       while(next_token(&extra_data, name, ",", sizeof(fstring))) {
+               *num_tokens += 1;
+       }
+
+       result = malloc(*num_tokens * sizeof(struct user_token));
+
+       if (result == NULL) {
+               DEBUG(1, ("Could not malloc sid array\n"));
+               return False;
+       }
+
+       extra_data = (const char *)response.extra_data;
+       i=0;
+
+       while(next_token(&extra_data, name, ",", sizeof(fstring))) {
+
+               fstring domain, user;
+               char *p;
+
+               fstrcpy(result[i].name, name);
+
+               p = strchr(name, *lp_winbind_separator());
+
+               DEBUG(3, ("%s\n", name));
+
+               if (p == NULL)
+                       continue;
+
+               *p++ = '\0';
+
+               fstrcpy(domain, name);
+               strupper_m(domain);
+               fstrcpy(user, p);
+
+               get_user_sids(domain, user, &(result[i].token));
+               i+=1;
+       }
+       
+       SAFE_FREE(response.extra_data);
+
+       *user_tokens = result;
+
+       return True;
+}
+
+static BOOL get_user_tokens_from_file(FILE *f,
+                                     int *num_tokens,
+                                     struct user_token **tokens)
+{
+       struct user_token *token = NULL;
+
+       while (!feof(f)) {
+               fstring line;
+
+               if (fgets(line, sizeof(line)-1, f) == NULL) {
+                       return True;
+               }
+
+               if (line[strlen(line)-1] == '\n')
+                       line[strlen(line)-1] = '\0';
+
+               if (line[0] == ' ') {
+                       /* We have a SID */
+
+                       DOM_SID sid;
+                       string_to_sid(&sid, &line[1]);
+
+                       if (token == NULL) {
+                               DEBUG(0, ("File does not begin with username"));
+                               return False;
+                       }
+
+                       add_sid_to_token(&token->token, &sid);
+                       continue;
+               }
+
+               /* And a new user... */
+
+               *num_tokens += 1;
+               *tokens = Realloc(*tokens,
+                                 *num_tokens *
+                                 sizeof(struct user_token));
+               if (*tokens == NULL) {
+                       DEBUG(0, ("Could not realloc tokens\n"));
+                       return False;
+               }
+
+               token = &((*tokens)[*num_tokens-1]);
+
+               fstrcpy(token->name, line);
+               token->token.num_sids = 0;
+               token->token.user_sids = NULL;
+               continue;
+       }
+       
+       return False;
+}
+
+
+/*
+ * Show the list of all users that have access to a share
+ */
+
+static void show_userlist(struct cli_state *cli,
+                         TALLOC_CTX *mem_ctx, const char *netname,
+                         int num_tokens, struct user_token *tokens)
+{
+       int fnum;
+       SEC_DESC *share_sd = NULL;
+       SEC_DESC *root_sd = NULL;
+       int i;
+       SRV_SHARE_INFO info;
+       WERROR result;
+       uint16 cnum;
+
+       result = cli_srvsvc_net_share_get_info(cli, mem_ctx, netname,
+                                              502, &info);
+
+       if (!W_ERROR_IS_OK(result)) {
+               DEBUG(1, ("Coult not query secdesc for share %s\n",
+                         netname));
+               return;
+       }
+
+       share_sd = info.share.info502.info_502_str.sd;
+       if (share_sd == NULL) {
+               DEBUG(1, ("Got no secdesc for share %s\n",
+                         netname));
+       }
+
+       cnum = cli->cnum;
+
+       if (!cli_send_tconX(cli, netname, "A:", "", 0)) {
+               return;
+       }
+
+       fnum = cli_nt_create(cli, "\\", READ_CONTROL_ACCESS);
+
+       if (fnum != -1) {
+               root_sd = cli_query_secdesc(cli, fnum, mem_ctx);
+       }
+
+       for (i=0; i<num_tokens; i++) {
+               uint32 acc_granted;
+               NTSTATUS status;
+
+               if (share_sd != NULL) {
+                       if (!se_access_check(share_sd, &tokens[i].token,
+                                            1, &acc_granted, &status)) {
+                               DEBUG(1, ("Could not check share_sd for "
+                                         "user %s\n",
+                                         tokens[i].name));
+                               continue;
+                       }
+
+                       if (!NT_STATUS_IS_OK(status))
+                               continue;
+               }
+
+               if (root_sd == NULL) {
+                       d_printf(" %s\n", tokens[i].name);
+                       continue;
+               }
+
+               if (!se_access_check(root_sd, &tokens[i].token,
+                                    1, &acc_granted, &status)) {
+                       DEBUG(1, ("Could not check root_sd for user %s\n",
+                                 tokens[i].name));
+                       continue;
+               }
+
+               if (!NT_STATUS_IS_OK(status))
+                       continue;
+
+               d_printf(" %s\n", tokens[i].name);
+       }
+
+       if (fnum != -1)
+               cli_close(cli, fnum);
+       cli_tdis(cli);
+       cli->cnum = cnum;
+       
+       return;
+}
+
+struct share_list {
+       int num_shares;
+       char **shares;
+};
+
+static void collect_share(const char *name, uint32 m,
+                         const char *comment, void *state)
+{
+       struct share_list *share_list = (struct share_list *)state;
+
+       if (m != STYPE_DISKTREE)
+               return;
+
+       share_list->num_shares += 1;
+       share_list->shares = Realloc(share_list->shares,
+                                    share_list->num_shares * sizeof(char *));
+       share_list->shares[share_list->num_shares-1] = strdup(name);
+}
+
+static void rpc_share_userlist_usage(void)
+{
+       return;
+}
+       
+/** 
+ * List shares on a remote RPC server, including the security descriptors
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS 
+rpc_share_allowedusers_internals(const DOM_SID *domain_sid,
+                                const char *domain_name,
+                                struct cli_state *cli,
+                                TALLOC_CTX *mem_ctx,
+                                int argc, const char **argv)
+{
+       int ret;
+       BOOL r;
+       ENUM_HND hnd;
+       uint32 i;
+       FILE *f;
+
+       struct user_token *tokens = NULL;
+       int num_tokens = 0;
+
+       struct share_list share_list;
+
+       if (argc > 1) {
+               rpc_share_userlist_usage();
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (argc == 0) {
+               f = stdin;
+       } else {
+               f = fopen(argv[0], "r");
+       }
+
+       if (f == NULL) {
+               DEBUG(0, ("Could not open userlist: %s\n", strerror(errno)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       r = get_user_tokens_from_file(f, &num_tokens, &tokens);
+
+       if (f != stdin)
+               fclose(f);
+
+       if (!r) {
+               DEBUG(0, ("Could not read users from file\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       for (i=0; i<num_tokens; i++)
+               collect_alias_memberships(&tokens[i].token);
+
+       init_enum_hnd(&hnd, 0);
+
+       share_list.num_shares = 0;
+       share_list.shares = NULL;
+
+       ret = cli_RNetShareEnum(cli, collect_share, &share_list);
+
+       if (ret == -1) {
+               DEBUG(0, ("Error returning browse list: %s\n",
+                         cli_errstr(cli)));
+               goto done;
+       }
+
+       for (i = 0; i < share_list.num_shares; i++) {
+               char *netname = share_list.shares[i];
+
+               if (netname[strlen(netname)-1] == '$')
+                       continue;
+
+               d_printf("%s\n", netname);
+
+               show_userlist(cli, mem_ctx, netname,
+                             num_tokens, tokens);
+       }
+ done:
+       for (i=0; i<num_tokens; i++) {
+               free_user_token(&tokens[i].token);
+       }
+       SAFE_FREE(tokens);
+       SAFE_FREE(share_list.shares);
+
+       return NT_STATUS_OK;
+}
+
+static int
+rpc_share_allowedusers(int argc, const char **argv)
+{
+       int result;
+
+       result = run_rpc_command(NULL, PI_SAMR, 0,
+                                rpc_aliaslist_internals,
+                                argc, argv);
+       if (result != 0)
+               return result;
+
+       result = run_rpc_command(NULL, PI_LSARPC, 0,
+                                rpc_aliaslist_dump,
+                                argc, argv);
+       if (result != 0)
+               return result;
+
+       return run_rpc_command(NULL, PI_SRVSVC, 0,
+                              rpc_share_allowedusers_internals,
+                              argc, argv);
+}
+
+int net_usersidlist(int argc, const char **argv)
+{
+       int num_tokens = 0;
+       struct user_token *tokens = NULL;
+       int i;
+
+       if (argc != 0) {
+               net_usersidlist_usage(argc, argv);
+               return 0;
+       }
+
+       if (!get_user_tokens(&num_tokens, &tokens)) {
+               DEBUG(0, ("Could not get the user/sid list\n"));
+               return 0;
+       }
+
+       for (i=0; i<num_tokens; i++) {
+               dump_user_token(&tokens[i]);
+               free_user_token(&tokens[i].token);
+       }
+
+       SAFE_FREE(tokens);
+       return 1;
+}
+
+int net_usersidlist_usage(int argc, const char **argv)
+{
+       d_printf("net usersidlist\n"
+                "\tprints out a list of all users the running winbind knows\n"
+                "\tabout, together with all their SIDs. This is used as\n"
+                "\tinput to the 'net rpc share allowedusers' command.\n\n");
+
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
 /** 
  * 'net rpc share' entrypoint.
  * @param argc  Standard main() style argc
@@ -2985,6 +3763,7 @@ int net_rpc_share(int argc, const char **argv)
        struct functable func[] = {
                {"add", rpc_share_add},
                {"delete", rpc_share_delete},
+               {"allowedusers", rpc_share_allowedusers},
                {"migrate", rpc_share_migrate},
                {NULL, NULL}
        };