From e66c8217f8c8944fde00e3cb944d7d27065c40a9 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 12 Oct 2004 11:58:01 +0000 Subject: r2935: This is a long-standing one in my patch-queue: A pair of net commands (usersidlist/allowedusers) to scan a file server's share and list all users who have permission to connect there. Volker (This used to be commit f7f84aa1ded70af3882e4122f34d5c7eed746993) --- source3/rpc_client/cli_srvsvc.c | 88 +++++ source3/utils/net.c | 1 + source3/utils/net_help.c | 13 +- source3/utils/net_rpc.c | 779 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 878 insertions(+), 3 deletions(-) (limited to 'source3') diff --git a/source3/rpc_client/cli_srvsvc.c b/source3/rpc_client/cli_srvsvc.c index f8580d0fe2..da4721c2c7 100644 --- a/source3/rpc_client/cli_srvsvc.c +++ b/source3/rpc_client/cli_srvsvc.c @@ -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) { diff --git a/source3/utils/net.c b/source3/utils/net.c index 9a022b2d38..2fe15c2b8e 100644 --- a/source3/utils/net.c +++ b/source3/utils/net.c @@ -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 diff --git a/source3/utils/net_help.c b/source3/utils/net_help.c index 859bb949c7..4b19bcf5aa 100644 --- a/source3/utils/net_help.c +++ b/source3/utils/net_help.c @@ -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 [] share ADD [misc. options] [targets]" - "\n\tAdds a share from a server (makes the export active)\n\n" - "net [] share DELETE [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 [] share DELETE [misc. options] [targets]" + "\n\tdeletes a share from a server (makes the export inactive)\n\n" + "net [] share ALLOWEDUSERS [] " + "[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 .\n" "net [] share MIGRATE FILES [misc. options] [targets]" "\n\tMigrates files from remote to local server\n\n" "net [] share MIGRATE SHARES [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 \tto run ADS commands\n"\ " net rap \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 diff --git a/source3/utils/net_rpc.c b/source3/utils/net_rpc.c index 9b96ad1371..018731afda 100644 --- a/source3/utils/net_rpc.c +++ b/source3/utils/net_rpc.c @@ -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; isid, + &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; jnum_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; inum_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; itoken.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; inum_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; inum_sids; + int i; + + for (i=0; iuser_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; icnum = 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