/* Samba Unix/Linux SMB client library Distributed SMB/CIFS Server Management Utility Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org) Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com) Copyright (C) 2004,2008 Guenther Deschner (gd@samba.org) Copyright (C) 2005 Jeremy Allison (jra@samba.org) Copyright (C) 2006 Jelmer Vernooij (jelmer@samba.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" #include "utils/net.h" #include "libsmb/namequery.h" #include "rpc_client/cli_pipe.h" #include "../libcli/auth/libcli_auth.h" #include "../librpc/gen_ndr/ndr_samr_c.h" #include "rpc_client/cli_samr.h" #include "rpc_client/init_samr.h" #include "../librpc/gen_ndr/ndr_lsa_c.h" #include "rpc_client/cli_lsarpc.h" #include "../librpc/gen_ndr/ndr_netlogon_c.h" #include "../librpc/gen_ndr/ndr_srvsvc_c.h" #include "../librpc/gen_ndr/ndr_spoolss.h" #include "../librpc/gen_ndr/ndr_initshutdown_c.h" #include "../librpc/gen_ndr/ndr_winreg_c.h" #include "secrets.h" #include "lib/netapi/netapi.h" #include "lib/netapi/netapi_net.h" #include "librpc/gen_ndr/libnet_join.h" #include "libnet/libnet_join.h" #include "rpc_client/init_lsa.h" #include "../libcli/security/security.h" #include "libsmb/libsmb.h" #include "clirap2.h" #include "nsswitch/libwbclient/wbclient.h" #include "passdb.h" #include "../libcli/smb/smbXcli_base.h" #include "libsmb/dsgetdcname.h" #include "lib/util/string_wrappers.h" static int net_mode_share; static NTSTATUS sync_files(struct copy_clistate *cp_clistate, const char *mask); /** * @file net_rpc.c * * @brief RPC based subcommands for the 'net' utility. * * This file should contain much of the functionality that used to * be found in rpcclient, except that the commands should change * less often, and the functionality should be sane (the user is not * expected to know a rid/sid before they conduct an operation etc.) * * @todo Perhaps eventually these should be split out into a number * of files, as this could get quite big. **/ /** * Many of the RPC functions need the domain sid. This function gets * it at the start of every run * * @param cli A cli_state already connected to the remote machine * * @return The Domain SID of the remote machine. **/ NTSTATUS net_get_remote_domain_sid(struct cli_state *cli, TALLOC_CTX *mem_ctx, struct dom_sid **domain_sid, const char **domain_name) { struct rpc_pipe_client *lsa_pipe = NULL; struct policy_handle pol; NTSTATUS status, result; union lsa_PolicyInformation *info = NULL; struct dcerpc_binding_handle *b; status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &lsa_pipe); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Could not initialise lsa pipe\n")); return status; } b = lsa_pipe->binding_handle; status = rpccli_lsa_open_policy(lsa_pipe, mem_ctx, false, SEC_FLAG_MAXIMUM_ALLOWED, &pol); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, "open_policy %s: %s\n", _("failed"), nt_errstr(status)); return status; } status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, &pol, LSA_POLICY_INFO_ACCOUNT_DOMAIN, &info, &result); if (any_nt_status_not_ok(status, result, &status)) { d_fprintf(stderr, "lsaquery %s: %s\n", _("failed"), nt_errstr(status)); return status; } *domain_name = info->account_domain.name.string; *domain_sid = info->account_domain.sid; dcerpc_lsa_Close(b, mem_ctx, &pol, &result); TALLOC_FREE(lsa_pipe); return NT_STATUS_OK; } /** * Run a single RPC command, from start to finish. * * @param pipe_name the pipe to connect to (usually a PIPE_ constant) * @param conn_flag a NET_FLAG_ combination. Passed to * net_make_ipc_connection. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * @return A shell status integer (0 for success). */ int run_rpc_command(struct net_context *c, struct cli_state *cli_arg, const struct ndr_interface_table *table, int conn_flags, rpc_command_fn fn, int argc, const char **argv) { struct cli_state *cli = NULL; struct rpc_pipe_client *pipe_hnd = NULL; TALLOC_CTX *mem_ctx; NTSTATUS nt_status; struct dom_sid *domain_sid; const char *domain_name; int ret = -1; /* make use of cli_state handed over as an argument, if possible */ if (!cli_arg) { nt_status = net_make_ipc_connection(c, conn_flags, &cli); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(1, ("failed to make ipc connection: %s\n", nt_errstr(nt_status))); return -1; } } else { cli = cli_arg; } if (!cli) { return -1; } /* Create mem_ctx */ if (!(mem_ctx = talloc_init("run_rpc_command"))) { DEBUG(0, ("talloc_init() failed\n")); goto fail; } nt_status = net_get_remote_domain_sid(cli, mem_ctx, &domain_sid, &domain_name); if (!NT_STATUS_IS_OK(nt_status)) { goto fail; } if (!(conn_flags & NET_FLAGS_NO_PIPE)) { if (lp_client_schannel() && (ndr_syntax_id_equal(&table->syntax_id, &ndr_table_netlogon.syntax_id))) { const char *remote_name = smbXcli_conn_remote_name(cli->conn); const struct sockaddr_storage *remote_sockaddr = smbXcli_conn_remote_sockaddr(cli->conn); /* Always try and create an schannel netlogon pipe. */ TALLOC_FREE(c->netlogon_creds); nt_status = cli_rpc_pipe_open_schannel( cli, c->msg_ctx, table, NCACN_NP, domain_name, remote_name, remote_sockaddr, &pipe_hnd, c, &c->netlogon_creds); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Could not initialise schannel netlogon pipe. Error was %s\n", nt_errstr(nt_status) )); goto fail; } } else { if (conn_flags & NET_FLAGS_SEAL) { nt_status = cli_rpc_pipe_open_with_creds( cli, table, (conn_flags & NET_FLAGS_TCP) ? NCACN_IP_TCP : NCACN_NP, DCERPC_AUTH_TYPE_NTLMSSP, DCERPC_AUTH_LEVEL_PRIVACY, smbXcli_conn_remote_name(cli->conn), smbXcli_conn_remote_sockaddr(cli->conn), c->creds, &pipe_hnd); } else { nt_status = cli_rpc_pipe_open_noauth( cli, table, &pipe_hnd); } if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Could not initialise pipe %s. Error was %s\n", table->name, nt_errstr(nt_status) )); goto fail; } } } nt_status = fn(c, domain_sid, domain_name, cli, pipe_hnd, mem_ctx, argc, argv); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(1, ("rpc command function failed! (%s)\n", nt_errstr(nt_status))); } else { ret = 0; DEBUG(5, ("rpc command function succeeded\n")); } if (!(conn_flags & NET_FLAGS_NO_PIPE)) { if (pipe_hnd) { TALLOC_FREE(pipe_hnd); } } fail: /* close the connection only if it was opened here */ if (!cli_arg) { cli_shutdown(cli); } talloc_destroy(mem_ctx); return ret; } /** * Force a change of the trust account password. * * All parameters are provided by the run_rpc_command function, except for * argc, argv which are passed 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, destroyed 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_changetrustpw_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { NTSTATUS status; const char *dcname = NULL; if (cli == NULL) { return NT_STATUS_INTERNAL_ERROR; } dcname = smbXcli_conn_remote_name(cli->conn); status = trust_pw_change(c->netlogon_creds, c->msg_ctx, pipe_hnd->binding_handle, c->opt_target_workgroup, dcname, true); /* force */ if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Failed to change machine account password: %s\n"), nt_errstr(status)); return status; } return NT_STATUS_OK; } /** * Force a change of the trust account password. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ int net_rpc_changetrustpw(struct net_context *c, int argc, const char **argv) { int conn_flags = NET_FLAGS_PDC; if (!c->opt_user_specified && !c->opt_kerberos) { conn_flags |= NET_FLAGS_ANONYMOUS; } if (c->display_usage) { d_printf( "%s\n" "net rpc changetrustpw\n" " %s\n", _("Usage:"), _("Change the machine trust password")); return 0; } return run_rpc_command(c, NULL, &ndr_table_netlogon, conn_flags, rpc_changetrustpw_internals, argc, argv); } /** * Join a domain, the old way. This function exists to allow * the message to be displayed when oldjoin was explicitly * requested, but not when it was implied by "net rpc join". * * This uses 'machinename' as the initial password, and changes it. * * The password should be created with 'server manager' or equiv first. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int net_rpc_oldjoin(struct net_context *c, int argc, const char **argv) { struct libnet_JoinCtx *r = NULL; TALLOC_CTX *mem_ctx; WERROR werr; const char *domain = lp_workgroup(); /* FIXME */ bool modify_config = lp_config_backend_is_registry(); enum netr_SchannelType sec_chan_type; char *pw = NULL; if (c->display_usage) { d_printf("Usage:\n" "net rpc oldjoin\n" " Join a domain the old way\n"); return 0; } net_warn_member_options(); mem_ctx = talloc_init("net_rpc_oldjoin"); if (!mem_ctx) { return -1; } werr = libnet_init_JoinCtx(mem_ctx, &r); if (!W_ERROR_IS_OK(werr)) { goto fail; } /* check what type of join - if the user wants to join as a BDC, the server must agree that we are a BDC. */ if (argc >= 0) { sec_chan_type = get_sec_channel_type(argv[0]); } else { sec_chan_type = get_sec_channel_type(NULL); } if (!c->msg_ctx) { d_fprintf(stderr, _("Could not initialise message context. " "Try running as root\n")); werr = WERR_ACCESS_DENIED; goto fail; } pw = talloc_strndup(r, lp_netbios_name(), 14); if (pw == NULL) { werr = WERR_NOT_ENOUGH_MEMORY; goto fail; } r->in.msg_ctx = c->msg_ctx; r->in.domain_name = domain; r->in.secure_channel_type = sec_chan_type; r->in.dc_name = c->opt_host; r->in.admin_account = ""; r->in.admin_password = strlower_talloc(r, pw); if (r->in.admin_password == NULL) { werr = WERR_NOT_ENOUGH_MEMORY; goto fail; } r->in.debug = true; r->in.modify_config = modify_config; r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | WKSSVC_JOIN_FLAGS_JOIN_UNSECURE | WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED; werr = libnet_Join(mem_ctx, r); if (!W_ERROR_IS_OK(werr)) { goto fail; } /* Check the short name of the domain */ if (!modify_config && !strequal(lp_workgroup(), r->out.netbios_domain_name)) { d_printf("The workgroup in %s does not match the short\n", get_dyn_CONFIGFILE()); d_printf("domain name obtained from the server.\n"); d_printf("Using the name [%s] from the server.\n", r->out.netbios_domain_name); d_printf("You should set \"workgroup = %s\" in %s.\n", r->out.netbios_domain_name, get_dyn_CONFIGFILE()); } d_printf("Using short domain name -- %s\n", r->out.netbios_domain_name); if (r->out.dns_domain_name) { d_printf("Joined '%s' to realm '%s'\n", r->in.machine_name, r->out.dns_domain_name); } else { d_printf("Joined '%s' to domain '%s'\n", r->in.machine_name, r->out.netbios_domain_name); } /* print out informative error string in case there is one */ if (r->out.error_string != NULL) { d_printf("%s\n", r->out.error_string); } TALLOC_FREE(mem_ctx); return 0; fail: if (c->opt_flags & NET_FLAGS_EXPECT_FALLBACK) { goto cleanup; } /* issue an overall failure message at the end. */ d_fprintf(stderr, _("Failed to join domain: %s\n"), r && r->out.error_string ? r->out.error_string : get_friendly_werror_msg(werr)); cleanup: TALLOC_FREE(mem_ctx); return -1; } /** * check that a join is OK * * @return A shell status integer (0 for success) * **/ int net_rpc_testjoin(struct net_context *c, int argc, const char **argv) { NTSTATUS status; TALLOC_CTX *mem_ctx; const char *domain = c->opt_target_workgroup; const char *dc = c->opt_host; if (c->display_usage) { d_printf("Usage\n" "net rpc testjoin\n" " Test if a join is OK\n"); return 0; } net_warn_member_options(); mem_ctx = talloc_init("net_rpc_testjoin"); if (!mem_ctx) { return -1; } if (!dc) { struct netr_DsRGetDCNameInfo *info; if (!c->msg_ctx) { d_fprintf(stderr, _("Could not initialise message context. " "Try running as root\n")); talloc_destroy(mem_ctx); return -1; } status = dsgetdcname(mem_ctx, c->msg_ctx, domain, NULL, NULL, DS_RETURN_DNS_NAME, &info); if (!NT_STATUS_IS_OK(status)) { talloc_destroy(mem_ctx); return -1; } dc = strip_hostname(info->dc_unc); } /* Display success or failure */ status = libnet_join_ok(c->msg_ctx, c->opt_workgroup, dc, c->opt_kerberos); if (!NT_STATUS_IS_OK(status)) { fprintf(stderr,"Join to domain '%s' is not valid: %s\n", domain, nt_errstr(status)); talloc_destroy(mem_ctx); return -1; } printf("Join to '%s' is OK\n",domain); talloc_destroy(mem_ctx); return 0; } /** * Join a domain using the administrator username and password * * @param argc Standard main() style argc * @param argc Standard main() style argv. Initial components are already * stripped. Currently not used. * @return A shell status integer (0 for success) * **/ static int net_rpc_join_newstyle(struct net_context *c, int argc, const char **argv) { struct libnet_JoinCtx *r = NULL; TALLOC_CTX *mem_ctx; WERROR werr; const char *domain = lp_workgroup(); /* FIXME */ bool modify_config = lp_config_backend_is_registry(); enum netr_SchannelType sec_chan_type; if (c->display_usage) { d_printf("Usage:\n" "net rpc join\n" " Join a domain the new way\n"); return 0; } net_warn_member_options(); mem_ctx = talloc_init("net_rpc_join_newstyle"); if (!mem_ctx) { return -1; } werr = libnet_init_JoinCtx(mem_ctx, &r); if (!W_ERROR_IS_OK(werr)) { goto fail; } /* check what type of join - if the user wants to join as a BDC, the server must agree that we are a BDC. */ if (argc >= 0) { sec_chan_type = get_sec_channel_type(argv[0]); } else { sec_chan_type = get_sec_channel_type(NULL); } if (!c->msg_ctx) { d_fprintf(stderr, _("Could not initialise message context. " "Try running as root\n")); werr = WERR_ACCESS_DENIED; goto fail; } r->in.msg_ctx = c->msg_ctx; r->in.domain_name = domain; r->in.secure_channel_type = sec_chan_type; r->in.dc_name = c->opt_host; r->in.admin_account = c->opt_user_name; r->in.admin_password = net_prompt_pass(c, c->opt_user_name); r->in.debug = true; r->in.use_kerberos = c->opt_kerberos; r->in.modify_config = modify_config; r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE | WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED; werr = libnet_Join(mem_ctx, r); if (!W_ERROR_IS_OK(werr)) { goto fail; } /* Check the short name of the domain */ if (!modify_config && !strequal(lp_workgroup(), r->out.netbios_domain_name)) { d_printf("The workgroup in %s does not match the short\n", get_dyn_CONFIGFILE()); d_printf("domain name obtained from the server.\n"); d_printf("Using the name [%s] from the server.\n", r->out.netbios_domain_name); d_printf("You should set \"workgroup = %s\" in %s.\n", r->out.netbios_domain_name, get_dyn_CONFIGFILE()); } d_printf("Using short domain name -- %s\n", r->out.netbios_domain_name); if (r->out.dns_domain_name) { d_printf("Joined '%s' to realm '%s'\n", r->in.machine_name, r->out.dns_domain_name); } else { d_printf("Joined '%s' to domain '%s'\n", r->in.machine_name, r->out.netbios_domain_name); } /* print out informative error string in case there is one */ if (r->out.error_string != NULL) { d_printf("%s\n", r->out.error_string); } TALLOC_FREE(mem_ctx); return 0; fail: /* issue an overall failure message at the end. */ d_printf("Failed to join domain: %s\n", r && r->out.error_string ? r->out.error_string : get_friendly_werror_msg(werr)); TALLOC_FREE(mem_ctx); return -1; } /** * 'net rpc join' entrypoint. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped * * Main 'net_rpc_join()' (where the admin username/password is used) is * in net_rpc_join.c. * Try to just change the password, but if that doesn't work, use/prompt * for a username/password. **/ int net_rpc_join(struct net_context *c, int argc, const char **argv) { int ret; if (c->display_usage) { d_printf("%s\n%s", _("Usage:"), _("net rpc join -U [%%password] \n" " Join a domain\n" " username\tName of the admin user" " password\tPassword of the admin user, will " "prompt if not specified\n" " type\tCan be one of the following:\n" "\t\tMEMBER\tJoin as member server (default)\n" "\t\tBDC\tJoin as BDC\n" "\t\tPDC\tJoin as PDC\n")); return 0; } if (lp_server_role() == ROLE_STANDALONE) { d_printf(_("cannot join as standalone machine\n")); return -1; } net_warn_member_options(); if (strlen(lp_netbios_name()) > 15) { d_printf(_("Our netbios name can be at most 15 chars long, " "\"%s\" is %u chars long\n"), lp_netbios_name(), (unsigned int)strlen(lp_netbios_name())); return -1; } c->opt_flags |= NET_FLAGS_EXPECT_FALLBACK; ret = net_rpc_oldjoin(c, argc, argv); c->opt_flags &= ~NET_FLAGS_EXPECT_FALLBACK; if (ret == 0) { return 0; } return net_rpc_join_newstyle(c, argc, argv); } /** * display info about a rpc domain * * All parameters are provided by the run_rpc_command function, except for * argc, argv which are passed 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, destroyed 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. **/ NTSTATUS rpc_info_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { struct policy_handle connect_pol, domain_pol; NTSTATUS status, result; union samr_DomainInfo *info = NULL; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; /* Get sam policy handle */ status = dcerpc_samr_Connect2(b, mem_ctx, pipe_hnd->desthost, MAXIMUM_ALLOWED_ACCESS, &connect_pol, &result); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Could not connect to SAM: %s\n"), nt_errstr(status)); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_fprintf(stderr, _("Could not connect to SAM: %s\n"), nt_errstr(result)); goto done; } /* Get domain policy handle */ status = dcerpc_samr_OpenDomain(b, mem_ctx, &connect_pol, MAXIMUM_ALLOWED_ACCESS, discard_const_p(struct dom_sid2, domain_sid), &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Could not open domain: %s\n"), nt_errstr(status)); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_fprintf(stderr, _("Could not open domain: %s\n"), nt_errstr(result)); goto done; } status = dcerpc_samr_QueryDomainInfo(b, mem_ctx, &domain_pol, 2, &info, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } status = result; if (NT_STATUS_IS_OK(result)) { struct dom_sid_buf sid_str; d_printf(_("Domain Name: %s\n"), info->general.domain_name.string); d_printf(_("Domain SID: %s\n"), dom_sid_str_buf(domain_sid, &sid_str)); d_printf(_("Sequence number: %llu\n"), (unsigned long long)info->general.sequence_num); d_printf(_("Num users: %u\n"), info->general.num_users); d_printf(_("Num domain groups: %u\n"),info->general.num_groups); d_printf(_("Num local groups: %u\n"),info->general.num_aliases); } done: return status; } /** * 'net rpc info' entrypoint. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. **/ int net_rpc_info(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc info\n" " %s\n", _("Usage:"), _("Display information about the domain")); return 0; } net_warn_member_options(); return run_rpc_command(c, NULL, &ndr_table_samr, NET_FLAGS_PDC, rpc_info_internals, argc, argv); } /** * Fetch domain SID into the local secrets.tdb. * * All parameters are provided by the run_rpc_command function, except for * argc, argv which are passed 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, destroyed 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_getsid_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { struct dom_sid_buf sid_str; d_printf(_("Storing SID %s for Domain %s in secrets.tdb\n"), dom_sid_str_buf(domain_sid, &sid_str), domain_name); if (!secrets_store_domain_sid(domain_name, domain_sid)) { DEBUG(0,("Can't store domain SID\n")); return NT_STATUS_UNSUCCESSFUL; } return NT_STATUS_OK; } /** * 'net rpc getsid' entrypoint. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. **/ int net_rpc_getsid(struct net_context *c, int argc, const char **argv) { int conn_flags = NET_FLAGS_PDC; if (!c->opt_user_specified && !c->opt_kerberos) { conn_flags |= NET_FLAGS_ANONYMOUS; } if (c->display_usage) { d_printf( "%s\n" "net rpc getsid\n" " %s\n", _("Usage:"), _("Fetch domain SID into local secrets.tdb")); return 0; } return run_rpc_command(c, NULL, &ndr_table_samr, conn_flags, rpc_getsid_internals, argc, argv); } /****************************************************************************/ /** * Basic usage function for 'net rpc user'. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. **/ static int rpc_user_usage(struct net_context *c, int argc, const char **argv) { return net_user_usage(c, argc, argv); } /** * Add a new user to a remote RPC server. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_user_add(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; struct USER_INFO_1 info1; uint32_t parm_error = 0; if (argc < 1 || c->display_usage) { rpc_user_usage(c, argc, argv); return 0; } ZERO_STRUCT(info1); info1.usri1_name = argv[0]; if (argc == 2) { info1.usri1_password = argv[1]; } status = NetUserAdd(c->opt_host, 1, (uint8_t *)&info1, &parm_error); if (status != 0) { d_fprintf(stderr,_("Failed to add user '%s' with error: %s.\n"), argv[0], libnetapi_get_error_string(c->netapi_ctx, status)); return -1; } else { d_printf(_("Added user '%s'.\n"), argv[0]); } return 0; } /** * Rename a user on a remote RPC server. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_user_rename(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; struct USER_INFO_0 u0; uint32_t parm_err = 0; if (argc != 2 || c->display_usage) { rpc_user_usage(c, argc, argv); return 0; } u0.usri0_name = argv[1]; status = NetUserSetInfo(c->opt_host, argv[0], 0, (uint8_t *)&u0, &parm_err); if (status) { d_fprintf(stderr, _("Failed to rename user from %s to %s - %s\n"), argv[0], argv[1], libnetapi_get_error_string(c->netapi_ctx, status)); } else { d_printf(_("Renamed user from %s to %s\n"), argv[0], argv[1]); } return status; } /** * Set a user's primary group * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_user_setprimarygroup(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; uint8_t *buffer; struct GROUP_INFO_2 *g2; struct USER_INFO_1051 u1051; uint32_t parm_err = 0; if (argc != 2 || c->display_usage) { rpc_user_usage(c, argc, argv); return 0; } status = NetGroupGetInfo(c->opt_host, argv[1], 2, &buffer); if (status) { d_fprintf(stderr, _("Failed to find group name %s -- %s\n"), argv[1], libnetapi_get_error_string(c->netapi_ctx, status)); return status; } g2 = (struct GROUP_INFO_2 *)buffer; u1051.usri1051_primary_group_id = g2->grpi2_group_id; NetApiBufferFree(buffer); status = NetUserSetInfo(c->opt_host, argv[0], 1051, (uint8_t *)&u1051, &parm_err); if (status) { d_fprintf(stderr, _("Failed to set user's primary group %s to %s - " "%s\n"), argv[0], argv[1], libnetapi_get_error_string(c->netapi_ctx, status)); } else { d_printf(_("Set primary group of user %s to %s\n"), argv[0], argv[1]); } return status; } /** * Delete a user from a remote RPC server. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_user_delete(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; if (argc < 1 || c->display_usage) { rpc_user_usage(c, argc, argv); return 0; } status = NetUserDel(c->opt_host, argv[0]); if (status != 0) { d_fprintf(stderr, _("Failed to delete user '%s' with: %s.\n"), argv[0], libnetapi_get_error_string(c->netapi_ctx, status)); return -1; } else { d_printf(_("Deleted user '%s'.\n"), argv[0]); } return 0; } /** * Set a user's password on a remote RPC server. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_user_password(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; char *prompt = NULL; struct USER_INFO_1003 u1003; uint32_t parm_err = 0; int ret; if (argc < 1 || c->display_usage) { rpc_user_usage(c, argc, argv); return 0; } if (argv[1]) { u1003.usri1003_password = argv[1]; } else { char pwd[256] = {0}; ret = asprintf(&prompt, _("Enter new password for %s:"), argv[0]); if (ret == -1) { return -1; } ret = samba_getpass(prompt, pwd, sizeof(pwd), false, false); SAFE_FREE(prompt); if (ret < 0) { return -1; } u1003.usri1003_password = talloc_strdup(c, pwd); if (u1003.usri1003_password == NULL) { return -1; } } status = NetUserSetInfo(c->opt_host, argv[0], 1003, (uint8_t *)&u1003, &parm_err); /* Display results */ if (status != 0) { d_fprintf(stderr, _("Failed to set password for '%s' with error: %s.\n"), argv[0], libnetapi_get_error_string(c->netapi_ctx, status)); return -1; } return 0; } /** * List a user's groups from a remote RPC server. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success) **/ static int rpc_user_info(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; struct GROUP_USERS_INFO_0 *u0 = NULL; uint32_t entries_read = 0; uint32_t total_entries = 0; uint32_t i; if (argc < 1 || c->display_usage) { rpc_user_usage(c, argc, argv); return 0; } status = NetUserGetGroups(c->opt_host, argv[0], 0, (uint8_t **)(void *)&u0, (uint32_t)-1, &entries_read, &total_entries); if (status != 0) { d_fprintf(stderr, _("Failed to get groups for '%s' with error: %s.\n"), argv[0], libnetapi_get_error_string(c->netapi_ctx, status)); return -1; } for (i=0; i < entries_read; i++) { printf("%s\n", u0->grui0_name); u0++; } return 0; } /** * List users on a remote RPC server. * * All parameters are provided by the run_rpc_command function, except for * argc, argv which are passed 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, destroyed 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 int rpc_user_list(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; uint32_t start_idx=0, num_entries, i, loop_count = 0; struct NET_DISPLAY_USER *info = NULL; void *buffer = NULL; /* Query domain users */ if (c->opt_long_list_entries) d_printf(_("\nUser name Comment" "\n-----------------------------\n")); do { uint32_t max_entries, max_size; dcerpc_get_query_dispinfo_params( loop_count, &max_entries, &max_size); status = NetQueryDisplayInformation(c->opt_host, 1, start_idx, max_entries, max_size, &num_entries, &buffer); if (status != 0 && status != ERROR_MORE_DATA) { return status; } info = (struct NET_DISPLAY_USER *)buffer; for (i = 0; i < num_entries; i++) { if (c->opt_long_list_entries) printf("%-21.21s %s\n", info->usri1_name, info->usri1_comment); else printf("%s\n", info->usri1_name); info++; } NetApiBufferFree(buffer); loop_count++; start_idx += num_entries; } while (status == ERROR_MORE_DATA); return status; } /** * 'net rpc user' entrypoint. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. **/ int net_rpc_user(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; struct functable func[] = { { "add", rpc_user_add, NET_TRANSPORT_RPC, N_("Add specified user"), N_("net rpc user add\n" " Add specified user") }, { "info", rpc_user_info, NET_TRANSPORT_RPC, N_("List domain groups of user"), N_("net rpc user info\n" " List domain groups of user") }, { "delete", rpc_user_delete, NET_TRANSPORT_RPC, N_("Remove specified user"), N_("net rpc user delete\n" " Remove specified user") }, { "password", rpc_user_password, NET_TRANSPORT_RPC, N_("Change user password"), N_("net rpc user password\n" " Change user password") }, { "rename", rpc_user_rename, NET_TRANSPORT_RPC, N_("Rename specified user"), N_("net rpc user rename\n" " Rename specified user") }, { "setprimarygroup", rpc_user_setprimarygroup, NET_TRANSPORT_RPC, "Set a user's primary group", "net rpc user setprimarygroup\n" " Set a user's primary group" }, {NULL, NULL, 0, NULL, NULL} }; status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds); if (status != 0) { return -1; } if (argc == 0) { if (c->display_usage) { d_printf( "%s\n" "net rpc user\n" " %s\n", _("Usage:"), _("List all users")); net_display_usage_from_functable(func); return 0; } return rpc_user_list(c, argc, argv); } return net_run_function(c, argc, argv, "net rpc user", func); } static NTSTATUS rpc_sh_user_list(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, int argc, const char **argv) { return werror_to_ntstatus(W_ERROR(rpc_user_list(c, argc, argv))); } static NTSTATUS rpc_sh_user_info(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, int argc, const char **argv) { return werror_to_ntstatus(W_ERROR(rpc_user_info(c, argc, argv))); } static NTSTATUS rpc_sh_handle_user(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, int argc, const char **argv, NTSTATUS (*fn)( struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, struct policy_handle *user_hnd, int argc, const char **argv)) { struct policy_handle connect_pol, domain_pol, user_pol; NTSTATUS status, result; struct dom_sid sid; uint32_t rid; enum lsa_SidType type; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; if (argc == 0) { d_fprintf(stderr, "%s %s \n", _("Usage:"), ctx->whoami); return NT_STATUS_INVALID_PARAMETER; } ZERO_STRUCT(connect_pol); ZERO_STRUCT(domain_pol); ZERO_STRUCT(user_pol); status = net_rpc_lookup_name(c, mem_ctx, ctx->cli, argv[0], NULL, NULL, &sid, &type); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Could not lookup %s: %s\n"), argv[0], nt_errstr(status)); goto done; } if (type != SID_NAME_USER) { d_fprintf(stderr, _("%s is a %s, not a user\n"), argv[0], sid_type_lookup(type)); status = NT_STATUS_NO_SUCH_USER; goto done; } if (!sid_peek_check_rid(ctx->domain_sid, &sid, &rid)) { d_fprintf(stderr, _("%s is not in our domain\n"), argv[0]); status = NT_STATUS_NO_SUCH_USER; goto done; } status = dcerpc_samr_Connect2(b, mem_ctx, pipe_hnd->desthost, MAXIMUM_ALLOWED_ACCESS, &connect_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } status = dcerpc_samr_OpenDomain(b, mem_ctx, &connect_pol, MAXIMUM_ALLOWED_ACCESS, ctx->domain_sid, &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } status = dcerpc_samr_OpenUser(b, mem_ctx, &domain_pol, MAXIMUM_ALLOWED_ACCESS, rid, &user_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } status = fn(c, mem_ctx, ctx, pipe_hnd, &user_pol, argc-1, argv+1); done: if (is_valid_policy_hnd(&user_pol)) { dcerpc_samr_Close(b, mem_ctx, &user_pol, &result); } if (is_valid_policy_hnd(&domain_pol)) { dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result); } if (is_valid_policy_hnd(&connect_pol)) { dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result); } return status; } static NTSTATUS rpc_sh_user_show_internals(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, struct policy_handle *user_hnd, int argc, const char **argv) { NTSTATUS status, result; union samr_UserInfo *info = NULL; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; if (argc != 0) { d_fprintf(stderr, "%s %s show \n", _("Usage:"), ctx->whoami); return NT_STATUS_INVALID_PARAMETER; } status = dcerpc_samr_QueryUserInfo(b, mem_ctx, user_hnd, 21, &info, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } d_printf(_("user rid: %d, group rid: %d\n"), info->info21.rid, info->info21.primary_gid); return result; } static NTSTATUS rpc_sh_user_show(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, int argc, const char **argv) { return rpc_sh_handle_user(c, mem_ctx, ctx, pipe_hnd, argc, argv, rpc_sh_user_show_internals); } #define FETCHSTR(name, rec) \ do { if (strequal(ctx->thiscmd, name)) { \ oldval = talloc_strdup(mem_ctx, info->info21.rec.string); } \ } while (0); #define SETSTR(name, rec, flag) \ do { if (strequal(ctx->thiscmd, name)) { \ init_lsa_String(&(info->info21.rec), argv[0]); \ info->info21.fields_present |= SAMR_FIELD_##flag; } \ } while (0); static NTSTATUS rpc_sh_user_str_edit_internals(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, struct policy_handle *user_hnd, int argc, const char **argv) { NTSTATUS status, result; const char *username; const char *oldval = ""; union samr_UserInfo *info = NULL; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; if (argc > 1) { d_fprintf(stderr, "%s %s [new value|NULL]\n", _("Usage:"), ctx->whoami); return NT_STATUS_INVALID_PARAMETER; } status = dcerpc_samr_QueryUserInfo(b, mem_ctx, user_hnd, 21, &info, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } username = talloc_strdup(mem_ctx, info->info21.account_name.string); FETCHSTR("fullname", full_name); FETCHSTR("homedir", home_directory); FETCHSTR("homedrive", home_drive); FETCHSTR("logonscript", logon_script); FETCHSTR("profilepath", profile_path); FETCHSTR("description", description); if (argc == 0) { d_printf(_("%s's %s: [%s]\n"), username, ctx->thiscmd, oldval); goto done; } if (strcmp(argv[0], "NULL") == 0) { argv[0] = ""; } ZERO_STRUCT(info->info21); SETSTR("fullname", full_name, FULL_NAME); SETSTR("homedir", home_directory, HOME_DIRECTORY); SETSTR("homedrive", home_drive, HOME_DRIVE); SETSTR("logonscript", logon_script, LOGON_SCRIPT); SETSTR("profilepath", profile_path, PROFILE_PATH); SETSTR("description", description, DESCRIPTION); status = dcerpc_samr_SetUserInfo(b, mem_ctx, user_hnd, 21, info, &result); if (!NT_STATUS_IS_OK(status)) { return status; } status = result; d_printf(_("Set %s's %s from [%s] to [%s]\n"), username, ctx->thiscmd, oldval, argv[0]); done: return status; } #define HANDLEFLG(name, rec) \ do { if (strequal(ctx->thiscmd, name)) { \ oldval = (oldflags & ACB_##rec) ? "yes" : "no"; \ if (newval) { \ newflags = oldflags | ACB_##rec; \ } else { \ newflags = oldflags & ~ACB_##rec; \ } } } while (0); static NTSTATUS rpc_sh_user_str_edit(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, int argc, const char **argv) { return rpc_sh_handle_user(c, mem_ctx, ctx, pipe_hnd, argc, argv, rpc_sh_user_str_edit_internals); } static NTSTATUS rpc_sh_user_flag_edit_internals(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, struct policy_handle *user_hnd, int argc, const char **argv) { NTSTATUS status, result; const char *username; const char *oldval = "unknown"; uint32_t oldflags, newflags; bool newval; union samr_UserInfo *info = NULL; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; if ((argc > 1) || ((argc == 1) && !strequal(argv[0], "yes") && !strequal(argv[0], "no"))) { /* TRANSLATORS: The yes|no here are program keywords. Please do not translate. */ d_fprintf(stderr, _("Usage: %s [yes|no]\n"), ctx->whoami); return NT_STATUS_INVALID_PARAMETER; } newval = strequal(argv[0], "yes"); status = dcerpc_samr_QueryUserInfo(b, mem_ctx, user_hnd, 21, &info, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } username = talloc_strdup(mem_ctx, info->info21.account_name.string); oldflags = info->info21.acct_flags; newflags = info->info21.acct_flags; HANDLEFLG("disabled", DISABLED); HANDLEFLG("pwnotreq", PWNOTREQ); HANDLEFLG("autolock", AUTOLOCK); HANDLEFLG("pwnoexp", PWNOEXP); if (argc == 0) { d_printf(_("%s's %s flag: %s\n"), username, ctx->thiscmd, oldval); goto done; } ZERO_STRUCT(info->info21); info->info21.acct_flags = newflags; info->info21.fields_present = SAMR_FIELD_ACCT_FLAGS; status = dcerpc_samr_SetUserInfo(b, mem_ctx, user_hnd, 21, info, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } status = result; if (NT_STATUS_IS_OK(result)) { d_printf(_("Set %s's %s flag from [%s] to [%s]\n"), username, ctx->thiscmd, oldval, argv[0]); } done: return status; } static NTSTATUS rpc_sh_user_flag_edit(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, int argc, const char **argv) { return rpc_sh_handle_user(c, mem_ctx, ctx, pipe_hnd, argc, argv, rpc_sh_user_flag_edit_internals); } struct rpc_sh_cmd *net_rpc_user_edit_cmds(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx) { static struct rpc_sh_cmd cmds[] = { { "fullname", NULL, &ndr_table_samr, rpc_sh_user_str_edit, N_("Show/Set a user's full name") }, { "homedir", NULL, &ndr_table_samr, rpc_sh_user_str_edit, N_("Show/Set a user's home directory") }, { "homedrive", NULL, &ndr_table_samr, rpc_sh_user_str_edit, N_("Show/Set a user's home drive") }, { "logonscript", NULL, &ndr_table_samr, rpc_sh_user_str_edit, N_("Show/Set a user's logon script") }, { "profilepath", NULL, &ndr_table_samr, rpc_sh_user_str_edit, N_("Show/Set a user's profile path") }, { "description", NULL, &ndr_table_samr, rpc_sh_user_str_edit, N_("Show/Set a user's description") }, { "disabled", NULL, &ndr_table_samr, rpc_sh_user_flag_edit, N_("Show/Set whether a user is disabled") }, { "autolock", NULL, &ndr_table_samr, rpc_sh_user_flag_edit, N_("Show/Set whether a user locked out") }, { "pwnotreq", NULL, &ndr_table_samr, rpc_sh_user_flag_edit, N_("Show/Set whether a user does not need a password") }, { "pwnoexp", NULL, &ndr_table_samr, rpc_sh_user_flag_edit, N_("Show/Set whether a user's password does not expire") }, { NULL, NULL, 0, NULL, NULL } }; return cmds; } struct rpc_sh_cmd *net_rpc_user_cmds(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx) { static struct rpc_sh_cmd cmds[] = { { "list", NULL, &ndr_table_samr, rpc_sh_user_list, N_("List available users") }, { "info", NULL, &ndr_table_samr, rpc_sh_user_info, N_("List the domain groups a user is member of") }, { "show", NULL, &ndr_table_samr, rpc_sh_user_show, N_("Show info about a user") }, { "edit", net_rpc_user_edit_cmds, 0, NULL, N_("Show/Modify a user's fields") }, { NULL, NULL, 0, NULL, NULL } }; return cmds; } /****************************************************************************/ /** * Basic usage function for 'net rpc group'. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. **/ static int rpc_group_usage(struct net_context *c, int argc, const char **argv) { return net_group_usage(c, argc, argv); } /** * Delete group on a remote RPC server. * * All parameters are provided by the run_rpc_command function, except for * argc, argv which are passed 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, destroyed 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_group_delete_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { struct policy_handle connect_pol, domain_pol, group_pol, user_pol; bool group_is_primary = false; NTSTATUS status, result; uint32_t group_rid; struct samr_RidAttrArray *rids = NULL; /* char **names; */ uint32_t i; /* struct samr_RidWithAttribute *user_gids; */ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; struct samr_Ids group_rids, name_types; struct lsa_String lsa_acct_name; union samr_UserInfo *info = NULL; if (argc < 1 || c->display_usage) { rpc_group_usage(c, argc,argv); return NT_STATUS_OK; /* ok? */ } status = dcerpc_samr_Connect2(b, mem_ctx, pipe_hnd->desthost, MAXIMUM_ALLOWED_ACCESS, &connect_pol, &result); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Request samr_Connect2 failed\n")); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_fprintf(stderr, _("Request samr_Connect2 failed\n")); goto done; } status = dcerpc_samr_OpenDomain(b, mem_ctx, &connect_pol, MAXIMUM_ALLOWED_ACCESS, discard_const_p(struct dom_sid2, domain_sid), &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Request open_domain failed\n")); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_fprintf(stderr, _("Request open_domain failed\n")); goto done; } init_lsa_String(&lsa_acct_name, argv[0]); status = dcerpc_samr_LookupNames(b, mem_ctx, &domain_pol, 1, &lsa_acct_name, &group_rids, &name_types, &result); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Lookup of '%s' failed\n"),argv[0]); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_fprintf(stderr, _("Lookup of '%s' failed\n"),argv[0]); goto done; } if (group_rids.count != 1) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto done; } if (name_types.count != 1) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto done; } switch (name_types.ids[0]) { case SID_NAME_DOM_GRP: status = dcerpc_samr_OpenGroup(b, mem_ctx, &domain_pol, MAXIMUM_ALLOWED_ACCESS, group_rids.ids[0], &group_pol, &result); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Request open_group failed")); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_fprintf(stderr, _("Request open_group failed")); goto done; } group_rid = group_rids.ids[0]; status = dcerpc_samr_QueryGroupMember(b, mem_ctx, &group_pol, &rids, &result); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Unable to query group members of %s"), argv[0]); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_fprintf(stderr, _("Unable to query group members of %s"), argv[0]); goto done; } if (c->opt_verbose) { d_printf( _("Domain Group %s (rid: %d) has %d members\n"), argv[0],group_rid, rids->count); } /* Check if group is anyone's primary group */ for (i = 0; i < rids->count; i++) { status = dcerpc_samr_OpenUser(b, mem_ctx, &domain_pol, MAXIMUM_ALLOWED_ACCESS, rids->rids[i], &user_pol, &result); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Unable to open group member %d\n"), rids->rids[i]); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_fprintf(stderr, _("Unable to open group member %d\n"), rids->rids[i]); goto done; } status = dcerpc_samr_QueryUserInfo(b, mem_ctx, &user_pol, 21, &info, &result); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Unable to lookup userinfo for group " "member %d\n"), rids->rids[i]); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_fprintf(stderr, _("Unable to lookup userinfo for group " "member %d\n"), rids->rids[i]); goto done; } if (info->info21.primary_gid == group_rid) { if (c->opt_verbose) { d_printf(_("Group is primary group " "of %s\n"), info->info21.account_name.string); } group_is_primary = true; } dcerpc_samr_Close(b, mem_ctx, &user_pol, &result); } if (group_is_primary) { d_fprintf(stderr, _("Unable to delete group because " "some of it's members have it as primary " "group\n")); status = NT_STATUS_MEMBERS_PRIMARY_GROUP; goto done; } /* remove all group members */ for (i = 0; i < rids->count; i++) { if (c->opt_verbose) d_printf(_("Remove group member %d..."), rids->rids[i]); status = dcerpc_samr_DeleteGroupMember(b, mem_ctx, &group_pol, rids->rids[i], &result); if (!NT_STATUS_IS_OK(status)) { goto done; } status = result; if (NT_STATUS_IS_OK(result)) { if (c->opt_verbose) d_printf(_("ok\n")); } else { if (c->opt_verbose) d_printf("%s\n", _("failed")); goto done; } } status = dcerpc_samr_DeleteDomainGroup(b, mem_ctx, &group_pol, &result); if (!NT_STATUS_IS_OK(status)) { break; } status = result; break; /* removing a local group is easier... */ case SID_NAME_ALIAS: status = dcerpc_samr_OpenAlias(b, mem_ctx, &domain_pol, MAXIMUM_ALLOWED_ACCESS, group_rids.ids[0], &group_pol, &result); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Request open_alias failed\n")); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_fprintf(stderr, _("Request open_alias failed\n")); goto done; } status = dcerpc_samr_DeleteDomAlias(b, mem_ctx, &group_pol, &result); if (!NT_STATUS_IS_OK(status)) { break; } status = result; break; default: d_fprintf(stderr, _("%s is of type %s. This command is only " "for deleting local or global groups\n"), argv[0],sid_type_lookup(name_types.ids[0])); status = NT_STATUS_UNSUCCESSFUL; goto done; } if (NT_STATUS_IS_OK(status)) { if (c->opt_verbose) d_printf(_("Deleted %s '%s'\n"), sid_type_lookup(name_types.ids[0]), argv[0]); } else { d_fprintf(stderr, _("Deleting of %s failed: %s\n"), argv[0], get_friendly_nt_error_msg(status)); } done: return status; } static int rpc_group_delete(struct net_context *c, int argc, const char **argv) { return run_rpc_command(c, NULL, &ndr_table_samr, 0, rpc_group_delete_internals, argc,argv); } static int rpc_group_add_internals(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; struct GROUP_INFO_1 info1; uint32_t parm_error = 0; if (argc != 1 || c->display_usage) { rpc_group_usage(c, argc, argv); return 0; } ZERO_STRUCT(info1); info1.grpi1_name = argv[0]; if (c->opt_comment && strlen(c->opt_comment) > 0) { info1.grpi1_comment = c->opt_comment; } status = NetGroupAdd(c->opt_host, 1, (uint8_t *)&info1, &parm_error); if (status != 0) { d_fprintf(stderr, _("Failed to add group '%s' with error: %s.\n"), argv[0], libnetapi_get_error_string(c->netapi_ctx, status)); return -1; } else { d_printf(_("Added group '%s'.\n"), argv[0]); } return 0; } static int rpc_alias_add_internals(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; struct LOCALGROUP_INFO_1 info1; uint32_t parm_error = 0; if (argc != 1 || c->display_usage) { rpc_group_usage(c, argc, argv); return 0; } ZERO_STRUCT(info1); info1.lgrpi1_name = argv[0]; if (c->opt_comment && strlen(c->opt_comment) > 0) { info1.lgrpi1_comment = c->opt_comment; } status = NetLocalGroupAdd(c->opt_host, 1, (uint8_t *)&info1, &parm_error); if (status != 0) { d_fprintf(stderr, _("Failed to add alias '%s' with error: %s.\n"), argv[0], libnetapi_get_error_string(c->netapi_ctx, status)); return -1; } else { d_printf(_("Added alias '%s'.\n"), argv[0]); } return 0; } static int rpc_group_add(struct net_context *c, int argc, const char **argv) { if (c->opt_localgroup) return rpc_alias_add_internals(c, argc, argv); return rpc_group_add_internals(c, argc, argv); } static NTSTATUS get_sid_from_name(struct cli_state *cli, TALLOC_CTX *mem_ctx, const char *name, struct dom_sid *sid, enum lsa_SidType *type) { struct dom_sid *sids = NULL; enum lsa_SidType *types = NULL; struct rpc_pipe_client *pipe_hnd = NULL; struct policy_handle lsa_pol; NTSTATUS status, result; struct dcerpc_binding_handle *b; status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &pipe_hnd); if (!NT_STATUS_IS_OK(status)) { goto done; } b = pipe_hnd->binding_handle; status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, false, SEC_FLAG_MAXIMUM_ALLOWED, &lsa_pol); if (!NT_STATUS_IS_OK(status)) { goto done; } status = rpccli_lsa_lookup_names(pipe_hnd, mem_ctx, &lsa_pol, 1, &name, NULL, 1, &sids, &types); if (NT_STATUS_IS_OK(status)) { sid_copy(sid, &sids[0]); *type = types[0]; } dcerpc_lsa_Close(b, mem_ctx, &lsa_pol, &result); done: if (pipe_hnd) { TALLOC_FREE(pipe_hnd); } if (!NT_STATUS_IS_OK(status) && (strncasecmp_m(name, "S-", 2) == 0)) { /* Try as S-1-5-whatever */ struct dom_sid tmp_sid; if (string_to_sid(&tmp_sid, name)) { sid_copy(sid, &tmp_sid); *type = SID_NAME_UNKNOWN; status = NT_STATUS_OK; } } return status; } static NTSTATUS rpc_add_groupmem(struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, const struct dom_sid *group_sid, const char *member) { struct policy_handle connect_pol, domain_pol; NTSTATUS status, result; uint32_t group_rid; struct policy_handle group_pol; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; struct samr_Ids rids, rid_types; struct lsa_String lsa_acct_name; struct dom_sid sid; sid_copy(&sid, group_sid); if (!sid_split_rid(&sid, &group_rid)) { return NT_STATUS_UNSUCCESSFUL; } /* Get sam policy handle */ status = dcerpc_samr_Connect2(b, mem_ctx, pipe_hnd->desthost, MAXIMUM_ALLOWED_ACCESS, &connect_pol, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } /* Get domain policy handle */ status = dcerpc_samr_OpenDomain(b, mem_ctx, &connect_pol, MAXIMUM_ALLOWED_ACCESS, &sid, &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } init_lsa_String(&lsa_acct_name, member); status = dcerpc_samr_LookupNames(b, mem_ctx, &domain_pol, 1, &lsa_acct_name, &rids, &rid_types, &result); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Could not lookup up group member %s\n"), member); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_fprintf(stderr, _("Could not lookup up group member %s\n"), member); goto done; } if (rids.count != 1) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto done; } if (rid_types.count != 1) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto done; } status = dcerpc_samr_OpenGroup(b, mem_ctx, &domain_pol, MAXIMUM_ALLOWED_ACCESS, group_rid, &group_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } status = dcerpc_samr_AddGroupMember(b, mem_ctx, &group_pol, rids.ids[0], 0x0005, /* unknown flags */ &result); if (!NT_STATUS_IS_OK(status)) { goto done; } status = result; done: dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result); return status; } static NTSTATUS rpc_add_aliasmem(struct rpc_pipe_client *pipe_hnd, struct cli_state *cli, TALLOC_CTX *mem_ctx, const struct dom_sid *alias_sid, const char *member) { struct policy_handle connect_pol, domain_pol; NTSTATUS status, result; uint32_t alias_rid; struct policy_handle alias_pol; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; struct dom_sid member_sid; enum lsa_SidType member_type; struct dom_sid sid; sid_copy(&sid, alias_sid); if (!sid_split_rid(&sid, &alias_rid)) { return NT_STATUS_UNSUCCESSFUL; } result = get_sid_from_name(cli, mem_ctx, member, &member_sid, &member_type); if (!NT_STATUS_IS_OK(result)) { d_fprintf(stderr, _("Could not lookup up group member %s\n"), member); return result; } /* Get sam policy handle */ status = dcerpc_samr_Connect2(b, mem_ctx, pipe_hnd->desthost, MAXIMUM_ALLOWED_ACCESS, &connect_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } /* Get domain policy handle */ status = dcerpc_samr_OpenDomain(b, mem_ctx, &connect_pol, MAXIMUM_ALLOWED_ACCESS, &sid, &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } status = dcerpc_samr_OpenAlias(b, mem_ctx, &domain_pol, MAXIMUM_ALLOWED_ACCESS, alias_rid, &alias_pol, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } status = dcerpc_samr_AddAliasMember(b, mem_ctx, &alias_pol, &member_sid, &result); if (!NT_STATUS_IS_OK(status)) { return status; } status = result; done: dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result); return status; } static NTSTATUS rpc_group_addmem_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { struct dom_sid group_sid; enum lsa_SidType group_type; if (argc != 2 || c->display_usage) { d_printf("%s\n%s", _("Usage:"), _("net rpc group addmem \n" " Add a member to a group\n" " group\tGroup to add member to\n" " member\tMember to add to group\n")); return NT_STATUS_UNSUCCESSFUL; } if (!NT_STATUS_IS_OK(get_sid_from_name(cli, mem_ctx, argv[0], &group_sid, &group_type))) { d_fprintf(stderr, _("Could not lookup group name %s\n"), argv[0]); return NT_STATUS_UNSUCCESSFUL; } if (group_type == SID_NAME_DOM_GRP) { NTSTATUS result = rpc_add_groupmem(pipe_hnd, mem_ctx, &group_sid, argv[1]); if (!NT_STATUS_IS_OK(result)) { d_fprintf(stderr, _("Could not add %s to %s: %s\n"), argv[1], argv[0], nt_errstr(result)); } return result; } if (group_type == SID_NAME_ALIAS) { NTSTATUS result = rpc_add_aliasmem(pipe_hnd, cli, mem_ctx, &group_sid, argv[1]); if (!NT_STATUS_IS_OK(result)) { d_fprintf(stderr, _("Could not add %s to %s: %s\n"), argv[1], argv[0], nt_errstr(result)); } return result; } d_fprintf(stderr, _("Can only add members to global or local groups " "which %s is not\n"), argv[0]); return NT_STATUS_UNSUCCESSFUL; } static int rpc_group_addmem(struct net_context *c, int argc, const char **argv) { return run_rpc_command(c, NULL, &ndr_table_samr, 0, rpc_group_addmem_internals, argc, argv); } static NTSTATUS rpc_del_groupmem(struct net_context *c, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, const struct dom_sid *group_sid, const char *member) { struct policy_handle connect_pol, domain_pol; NTSTATUS status, result; uint32_t group_rid; struct policy_handle group_pol; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; struct samr_Ids rids, rid_types; struct lsa_String lsa_acct_name; struct dom_sid sid; sid_copy(&sid, group_sid); if (!sid_split_rid(&sid, &group_rid)) return NT_STATUS_UNSUCCESSFUL; /* Get sam policy handle */ status = dcerpc_samr_Connect2(b, mem_ctx, pipe_hnd->desthost, MAXIMUM_ALLOWED_ACCESS, &connect_pol, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } /* Get domain policy handle */ status = dcerpc_samr_OpenDomain(b, mem_ctx, &connect_pol, MAXIMUM_ALLOWED_ACCESS, &sid, &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } init_lsa_String(&lsa_acct_name, member); status = dcerpc_samr_LookupNames(b, mem_ctx, &domain_pol, 1, &lsa_acct_name, &rids, &rid_types, &result); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Could not lookup up group member %s\n"), member); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_fprintf(stderr, _("Could not lookup up group member %s\n"), member); goto done; } if (rids.count != 1) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto done; } if (rid_types.count != 1) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto done; } status = dcerpc_samr_OpenGroup(b, mem_ctx, &domain_pol, MAXIMUM_ALLOWED_ACCESS, group_rid, &group_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } status = dcerpc_samr_DeleteGroupMember(b, mem_ctx, &group_pol, rids.ids[0], &result); if (!NT_STATUS_IS_OK(status)) { goto done; } status = result; done: dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result); return status; } static NTSTATUS rpc_del_aliasmem(struct rpc_pipe_client *pipe_hnd, struct cli_state *cli, TALLOC_CTX *mem_ctx, const struct dom_sid *alias_sid, const char *member) { struct policy_handle connect_pol, domain_pol; NTSTATUS status, result; uint32_t alias_rid; struct policy_handle alias_pol; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; struct dom_sid member_sid; enum lsa_SidType member_type; struct dom_sid sid; sid_copy(&sid, alias_sid); if (!sid_split_rid(&sid, &alias_rid)) return NT_STATUS_UNSUCCESSFUL; result = get_sid_from_name(cli, mem_ctx, member, &member_sid, &member_type); if (!NT_STATUS_IS_OK(result)) { d_fprintf(stderr, _("Could not lookup up group member %s\n"), member); return result; } /* Get sam policy handle */ status = dcerpc_samr_Connect2(b, mem_ctx, pipe_hnd->desthost, MAXIMUM_ALLOWED_ACCESS, &connect_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } /* Get domain policy handle */ status = dcerpc_samr_OpenDomain(b, mem_ctx, &connect_pol, MAXIMUM_ALLOWED_ACCESS, &sid, &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } status = dcerpc_samr_OpenAlias(b, mem_ctx, &domain_pol, MAXIMUM_ALLOWED_ACCESS, alias_rid, &alias_pol, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } status = dcerpc_samr_DeleteAliasMember(b, mem_ctx, &alias_pol, &member_sid, &result); if (!NT_STATUS_IS_OK(status)) { return status; } status = result; done: dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result); return status; } static NTSTATUS rpc_group_delmem_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { struct dom_sid group_sid; enum lsa_SidType group_type; if (argc != 2 || c->display_usage) { d_printf("%s\n%s", _("Usage:"), _("net rpc group delmem \n" " Delete a member from a group\n" " group\tGroup to delete member from\n" " member\tMember to delete from group\n")); return NT_STATUS_UNSUCCESSFUL; } if (!NT_STATUS_IS_OK(get_sid_from_name(cli, mem_ctx, argv[0], &group_sid, &group_type))) { d_fprintf(stderr, _("Could not lookup group name %s\n"), argv[0]); return NT_STATUS_UNSUCCESSFUL; } if (group_type == SID_NAME_DOM_GRP) { NTSTATUS result = rpc_del_groupmem(c, pipe_hnd, mem_ctx, &group_sid, argv[1]); if (!NT_STATUS_IS_OK(result)) { d_fprintf(stderr, _("Could not del %s from %s: %s\n"), argv[1], argv[0], nt_errstr(result)); } return result; } if (group_type == SID_NAME_ALIAS) { NTSTATUS result = rpc_del_aliasmem(pipe_hnd, cli, mem_ctx, &group_sid, argv[1]); if (!NT_STATUS_IS_OK(result)) { d_fprintf(stderr, _("Could not del %s from %s: %s\n"), argv[1], argv[0], nt_errstr(result)); } return result; } d_fprintf(stderr, _("Can only delete members from global or local " "groups which %s is not\n"), argv[0]); return NT_STATUS_UNSUCCESSFUL; } static int rpc_group_delmem(struct net_context *c, int argc, const char **argv) { return run_rpc_command(c, NULL, &ndr_table_samr, 0, rpc_group_delmem_internals, argc, argv); } /** * List groups on a remote RPC server. * * 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, destroyed 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_group_list_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { struct policy_handle connect_pol, domain_pol; NTSTATUS status, result; uint32_t start_idx=0, max_entries=250, num_entries, i, loop_count = 0; struct samr_SamArray *groups = NULL; bool global = false; bool local = false; bool builtin = false; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; if (c->display_usage) { d_printf("%s\n%s", _("Usage:"), _("net rpc group list [global] [local] [builtin]\n" " List groups on RPC server\n" " global\tList global groups\n" " local\tList local groups\n" " builtin\tList builtin groups\n" " If none of global, local or builtin is " "specified, all three options are considered " "set\n")); return NT_STATUS_OK; } if (argc == 0) { global = true; local = true; builtin = true; } for (i=0; idesthost, MAXIMUM_ALLOWED_ACCESS, &connect_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } /* Get domain policy handle */ status = dcerpc_samr_OpenDomain(b, mem_ctx, &connect_pol, MAXIMUM_ALLOWED_ACCESS, discard_const_p(struct dom_sid2, domain_sid), &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } /* Query domain groups */ if (c->opt_long_list_entries) d_printf(_("\nGroup name Comment" "\n-----------------------------\n")); do { uint32_t max_size, total_size, returned_size; union samr_DispInfo info; if (!global) break; dcerpc_get_query_dispinfo_params( loop_count, &max_entries, &max_size); status = dcerpc_samr_QueryDisplayInfo(b, mem_ctx, &domain_pol, 3, start_idx, max_entries, max_size, &total_size, &returned_size, &info, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } num_entries = info.info3.count; start_idx += info.info3.count; if (!NT_STATUS_IS_OK(result) && !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) break; for (i = 0; i < num_entries; i++) { const char *group = NULL; const char *desc = NULL; group = info.info3.entries[i].account_name.string; desc = info.info3.entries[i].description.string; if (c->opt_long_list_entries) printf("%-21.21s %-50.50s\n", group, desc); else printf("%s\n", group); } } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); /* query domain aliases */ start_idx = 0; do { if (!local) break; status = dcerpc_samr_EnumDomainAliases(b, mem_ctx, &domain_pol, &start_idx, &groups, 0xffff, &num_entries, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result) && !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) break; for (i = 0; i < num_entries; i++) { const char *description = NULL; if (c->opt_long_list_entries) { struct policy_handle alias_pol; union samr_AliasInfo *info = NULL; NTSTATUS _result; status = dcerpc_samr_OpenAlias(b, mem_ctx, &domain_pol, 0x8, groups->entries[i].idx, &alias_pol, &_result); if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) { status = dcerpc_samr_QueryAliasInfo(b, mem_ctx, &alias_pol, 3, &info, &_result); if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) { status = dcerpc_samr_Close(b, mem_ctx, &alias_pol, &_result); if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) { description = info->description.string; } } } } if (description != NULL) { printf("%-21.21s %-50.50s\n", groups->entries[i].name.string, description); } else { printf("%s\n", groups->entries[i].name.string); } } } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result); /* Get builtin policy handle */ status = dcerpc_samr_OpenDomain(b, mem_ctx, &connect_pol, MAXIMUM_ALLOWED_ACCESS, discard_const_p(struct dom_sid2, &global_sid_Builtin), &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } /* query builtin aliases */ start_idx = 0; do { if (!builtin) break; status = dcerpc_samr_EnumDomainAliases(b, mem_ctx, &domain_pol, &start_idx, &groups, max_entries, &num_entries, &result); if (!NT_STATUS_IS_OK(status)) { break; } if (!NT_STATUS_IS_OK(result) && !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) { status = result; break; } for (i = 0; i < num_entries; i++) { const char *description = NULL; if (c->opt_long_list_entries) { struct policy_handle alias_pol; union samr_AliasInfo *info = NULL; NTSTATUS _result; status = dcerpc_samr_OpenAlias(b, mem_ctx, &domain_pol, 0x8, groups->entries[i].idx, &alias_pol, &_result); if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) { status = dcerpc_samr_QueryAliasInfo(b, mem_ctx, &alias_pol, 3, &info, &_result); if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) { status = dcerpc_samr_Close(b, mem_ctx, &alias_pol, &_result); if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) { description = info->description.string; } } } } if (description != NULL) { printf("%-21.21s %-50.50s\n", groups->entries[i].name.string, description); } else { printf("%s\n", groups->entries[i].name.string); } } } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); status = result; done: return status; } static int rpc_group_list(struct net_context *c, int argc, const char **argv) { return run_rpc_command(c, NULL, &ndr_table_samr, 0, rpc_group_list_internals, argc, argv); } static NTSTATUS rpc_list_group_members(struct net_context *c, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, const char *domain_name, const struct dom_sid *domain_sid, struct policy_handle *domain_pol, uint32_t rid) { NTSTATUS result, status; struct policy_handle group_pol; uint32_t num_members, *group_rids; uint32_t i; struct samr_RidAttrArray *rids = NULL; struct lsa_Strings names; struct samr_Ids types; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; status = dcerpc_samr_OpenGroup(b, mem_ctx, domain_pol, MAXIMUM_ALLOWED_ACCESS, rid, &group_pol, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } status = dcerpc_samr_QueryGroupMember(b, mem_ctx, &group_pol, &rids, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } num_members = rids->count; group_rids = rids->rids; while (num_members > 0) { uint32_t this_time = 512; if (num_members < this_time) this_time = num_members; status = dcerpc_samr_LookupRids(b, mem_ctx, domain_pol, this_time, group_rids, &names, &types, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } if (names.count != this_time) { return NT_STATUS_INVALID_NETWORK_RESPONSE; } if (types.count != this_time) { return NT_STATUS_INVALID_NETWORK_RESPONSE; } /* We only have users as members, but make the output the same as the output of alias members */ for (i = 0; i < this_time; i++) { if (c->opt_long_list_entries) { struct dom_sid sid; struct dom_sid_buf sid_str; sid_compose(&sid, domain_sid, group_rids[i]); printf("%s %s\\%s %d\n", dom_sid_str_buf(&sid, &sid_str), domain_name, names.names[i].string, SID_NAME_USER); } else { printf("%s\\%s\n", domain_name, names.names[i].string); } } num_members -= this_time; group_rids += 512; } return NT_STATUS_OK; } static NTSTATUS rpc_list_alias_members(struct net_context *c, struct rpc_pipe_client *pipe_hnd, struct cli_state *cli, TALLOC_CTX *mem_ctx, struct policy_handle *domain_pol, uint32_t rid) { NTSTATUS result, status; struct rpc_pipe_client *lsa_pipe; struct policy_handle alias_pol, lsa_pol; uint32_t num_members; struct dom_sid *alias_sids; char **domains; char **names; enum lsa_SidType *types; uint32_t i; struct lsa_SidArray sid_array; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; status = dcerpc_samr_OpenAlias(b, mem_ctx, domain_pol, MAXIMUM_ALLOWED_ACCESS, rid, &alias_pol, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } status = dcerpc_samr_GetMembersInAlias(b, mem_ctx, &alias_pol, &sid_array, &result); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Couldn't list alias members\n")); return status; } if (!NT_STATUS_IS_OK(result)) { d_fprintf(stderr, _("Couldn't list alias members\n")); return result; } num_members = sid_array.num_sids; if (num_members == 0) { return NT_STATUS_OK; } result = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &lsa_pipe); if (!NT_STATUS_IS_OK(result)) { d_fprintf(stderr, _("Couldn't open LSA pipe. Error was %s\n"), nt_errstr(result) ); return result; } result = rpccli_lsa_open_policy(lsa_pipe, mem_ctx, true, SEC_FLAG_MAXIMUM_ALLOWED, &lsa_pol); if (!NT_STATUS_IS_OK(result)) { d_fprintf(stderr, _("Couldn't open LSA policy handle\n")); TALLOC_FREE(lsa_pipe); return result; } alias_sids = talloc_zero_array(mem_ctx, struct dom_sid, num_members); if (!alias_sids) { d_fprintf(stderr, _("Out of memory\n")); TALLOC_FREE(lsa_pipe); return NT_STATUS_NO_MEMORY; } for (i=0; iopt_long_list_entries) { printf("%s %s\\%s %d\n", sid_str.buf, domains[i] ? domains[i] : _("*unknown*"), names[i] ? names[i] : _("*unknown*"), types[i]); } else { if (domains[i]) printf("%s\\%s\n", domains[i], names[i]); else printf("%s\n", sid_str.buf); } } TALLOC_FREE(lsa_pipe); return NT_STATUS_OK; } static NTSTATUS rpc_group_members_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { NTSTATUS result, status; struct policy_handle connect_pol, domain_pol; struct samr_Ids rids, rid_types; struct lsa_String lsa_acct_name; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; /* Get sam policy handle */ status = dcerpc_samr_Connect2(b, mem_ctx, pipe_hnd->desthost, MAXIMUM_ALLOWED_ACCESS, &connect_pol, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } /* Get domain policy handle */ status = dcerpc_samr_OpenDomain(b, mem_ctx, &connect_pol, MAXIMUM_ALLOWED_ACCESS, discard_const_p(struct dom_sid2, domain_sid), &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } init_lsa_String(&lsa_acct_name, argv[0]); /* sure? */ status = dcerpc_samr_LookupNames(b, mem_ctx, &domain_pol, 1, &lsa_acct_name, &rids, &rid_types, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { /* Ok, did not find it in the global sam, try with builtin */ struct dom_sid sid_Builtin; dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result); sid_copy(&sid_Builtin, &global_sid_Builtin); status = dcerpc_samr_OpenDomain(b, mem_ctx, &connect_pol, MAXIMUM_ALLOWED_ACCESS, &sid_Builtin, &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { d_fprintf(stderr, _("Couldn't find group %s\n"), argv[0]); return result; } status = dcerpc_samr_LookupNames(b, mem_ctx, &domain_pol, 1, &lsa_acct_name, &rids, &rid_types, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { d_fprintf(stderr, _("Couldn't find group %s\n"), argv[0]); return result; } } if (rids.count != 1) { d_fprintf(stderr, _("Couldn't find group %s\n"), argv[0]); return NT_STATUS_INVALID_NETWORK_RESPONSE; } if (rid_types.count != 1) { d_fprintf(stderr, _("Couldn't find group %s\n"), argv[0]); return NT_STATUS_INVALID_NETWORK_RESPONSE; } if (rid_types.ids[0] == SID_NAME_DOM_GRP) { return rpc_list_group_members(c, pipe_hnd, mem_ctx, domain_name, domain_sid, &domain_pol, rids.ids[0]); } if (rid_types.ids[0] == SID_NAME_ALIAS) { return rpc_list_alias_members(c, pipe_hnd, cli, mem_ctx, &domain_pol, rids.ids[0]); } return NT_STATUS_NO_SUCH_GROUP; } static int rpc_group_members(struct net_context *c, int argc, const char **argv) { if (argc != 1 || c->display_usage) { return rpc_group_usage(c, argc, argv); } return run_rpc_command(c, NULL, &ndr_table_samr, 0, rpc_group_members_internals, argc, argv); } static int rpc_group_rename_internals(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; struct GROUP_INFO_0 g0; uint32_t parm_err; if (argc != 2) { d_printf(_("Usage:\n")); d_printf("net rpc group rename group newname\n"); return -1; } g0.grpi0_name = argv[1]; status = NetGroupSetInfo(c->opt_host, argv[0], 0, (uint8_t *)&g0, &parm_err); if (status != 0) { d_fprintf(stderr, _("Renaming group %s failed with: %s\n"), argv[0], libnetapi_get_error_string(c->netapi_ctx, status)); return -1; } return 0; } static int rpc_group_rename(struct net_context *c, int argc, const char **argv) { if (argc != 2 || c->display_usage) { return rpc_group_usage(c, argc, argv); } return rpc_group_rename_internals(c, argc, argv); } /** * 'net rpc group' entrypoint. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. **/ int net_rpc_group(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; struct functable func[] = { { "add", rpc_group_add, NET_TRANSPORT_RPC, N_("Create specified group"), N_("net rpc group add\n" " Create specified group") }, { "delete", rpc_group_delete, NET_TRANSPORT_RPC, N_("Delete specified group"), N_("net rpc group delete\n" " Delete specified group") }, { "addmem", rpc_group_addmem, NET_TRANSPORT_RPC, N_("Add member to group"), N_("net rpc group addmem\n" " Add member to group") }, { "delmem", rpc_group_delmem, NET_TRANSPORT_RPC, N_("Remove member from group"), N_("net rpc group delmem\n" " Remove member from group") }, { "list", rpc_group_list, NET_TRANSPORT_RPC, N_("List groups"), N_("net rpc group list\n" " List groups") }, { "members", rpc_group_members, NET_TRANSPORT_RPC, N_("List group members"), N_("net rpc group members\n" " List group members") }, { "rename", rpc_group_rename, NET_TRANSPORT_RPC, N_("Rename group"), N_("net rpc group rename\n" " Rename group") }, {NULL, NULL, 0, NULL, NULL} }; status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds); if (status != 0) { return -1; } if (argc == 0) { if (c->display_usage) { d_printf(_("Usage:\n")); d_printf(_("net rpc group\n" " Alias for net rpc group list global " "local builtin\n")); net_display_usage_from_functable(func); return 0; } return run_rpc_command(c, NULL, &ndr_table_samr, 0, rpc_group_list_internals, argc, argv); } return net_run_function(c, argc, argv, "net rpc group", func); } /****************************************************************************/ static int rpc_share_usage(struct net_context *c, int argc, const char **argv) { return net_share_usage(c, argc, argv); } /** * Add a share on a remote RPC server. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_share_add(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; char *sharename; char *path; uint32_t type = STYPE_DISKTREE; /* only allow disk shares to be added */ uint32_t num_users=0, perms=0; char *password=NULL; /* don't allow a share password */ struct SHARE_INFO_2 i2; uint32_t parm_error = 0; if ((argc < 1) || !strchr(argv[0], '=') || c->display_usage) { return rpc_share_usage(c, argc, argv); } if ((sharename = talloc_strdup(c, argv[0])) == NULL) { return -1; } path = strchr(sharename, '='); if (!path) { return -1; } *path++ = '\0'; i2.shi2_netname = sharename; i2.shi2_type = type; i2.shi2_remark = c->opt_comment; i2.shi2_permissions = perms; i2.shi2_max_uses = c->opt_maxusers; i2.shi2_current_uses = num_users; i2.shi2_path = path; i2.shi2_passwd = password; status = NetShareAdd(c->opt_host, 2, (uint8_t *)&i2, &parm_error); if (status != 0) { printf(_("NetShareAdd failed with: %s\n"), libnetapi_get_error_string(c->netapi_ctx, status)); } return status; } /** * Delete a share on a remote RPC server. * * @param domain_sid The domain sid acquired from the remote server. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_share_delete(struct net_context *c, int argc, const char **argv) { if (argc < 1 || c->display_usage) { return rpc_share_usage(c, argc, argv); } return NetShareDel(c->opt_host, argv[0], 0); } /** * Formatted print of share info * * @param r pointer to SHARE_INFO_1 to format **/ static void display_share_info_1(struct net_context *c, struct SHARE_INFO_1 *r) { if (c->opt_long_list_entries) { d_printf("%-12s %-8.8s %-50s\n", r->shi1_netname, net_share_type_str(r->shi1_type & ~(STYPE_TEMPORARY|STYPE_HIDDEN)), r->shi1_remark); } else { d_printf("%s\n", r->shi1_netname); } } static WERROR get_share_info(struct net_context *c, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, uint32_t level, int argc, const char **argv, struct srvsvc_NetShareInfoCtr *info_ctr) { WERROR result; NTSTATUS status; union srvsvc_NetShareInfo info; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; /* no specific share requested, enumerate all */ if (argc == 0) { uint32_t preferred_len = 0xffffffff; uint32_t total_entries = 0; uint32_t resume_handle = 0; info_ctr->level = level; status = dcerpc_srvsvc_NetShareEnumAll(b, mem_ctx, pipe_hnd->desthost, info_ctr, preferred_len, &total_entries, &resume_handle, &result); if (!NT_STATUS_IS_OK(status)) { return ntstatus_to_werror(status); } return result; } /* request just one share */ status = dcerpc_srvsvc_NetShareGetInfo(b, mem_ctx, pipe_hnd->desthost, argv[0], level, &info, &result); if (!NT_STATUS_IS_OK(status)) { result = ntstatus_to_werror(status); goto done; } if (!W_ERROR_IS_OK(result)) { goto done; } /* construct ctr */ ZERO_STRUCTP(info_ctr); info_ctr->level = level; switch (level) { case 1: { struct srvsvc_NetShareCtr1 *ctr1; ctr1 = talloc_zero(mem_ctx, struct srvsvc_NetShareCtr1); W_ERROR_HAVE_NO_MEMORY(ctr1); ctr1->count = 1; ctr1->array = info.info1; info_ctr->ctr.ctr1 = ctr1; break; } case 2: { struct srvsvc_NetShareCtr2 *ctr2; ctr2 = talloc_zero(mem_ctx, struct srvsvc_NetShareCtr2); W_ERROR_HAVE_NO_MEMORY(ctr2); ctr2->count = 1; ctr2->array = info.info2; info_ctr->ctr.ctr2 = ctr2; break; } case 502: { struct srvsvc_NetShareCtr502 *ctr502; ctr502 = talloc_zero(mem_ctx, struct srvsvc_NetShareCtr502); W_ERROR_HAVE_NO_MEMORY(ctr502); ctr502->count = 1; ctr502->array = info.info502; info_ctr->ctr.ctr502 = ctr502; break; } } /* switch */ done: return result; } /*** * 'net rpc share list' entrypoint. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. **/ static int rpc_share_list(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; struct SHARE_INFO_1 *i1 = NULL; uint32_t entries_read = 0; uint32_t total_entries = 0; uint32_t resume_handle = 0; uint32_t i, level = 1; if (c->display_usage) { d_printf( "%s\n" "net rpc share list\n" " %s\n", _("Usage:"), _("List shares on remote server")); return 0; } status = NetShareEnum(c->opt_host, level, (uint8_t **)(void *)&i1, (uint32_t)-1, &entries_read, &total_entries, &resume_handle); if (status != 0) { goto done; } /* Display results */ if (c->opt_long_list_entries) { d_printf(_( "\nEnumerating shared resources (exports) on remote server:\n\n" "\nShare name Type Description\n" "---------- ---- -----------\n")); } for (i = 0; i < entries_read; i++) display_share_info_1(c, &i1[i]); done: return status; } static bool check_share_availability(struct cli_state *cli, const char *netname) { NTSTATUS status; status = cli_tree_connect(cli, netname, "A:", NULL); if (!NT_STATUS_IS_OK(status)) { d_printf(_("skipping [%s]: not a file share.\n"), netname); return false; } status = cli_tdis(cli); if (!NT_STATUS_IS_OK(status)) { d_printf(_("cli_tdis returned %s\n"), nt_errstr(status)); return false; } return true; } static bool check_share_sanity(struct net_context *c, struct cli_state *cli, const char *netname, uint32_t type) { /* only support disk shares */ if (! ( type == STYPE_DISKTREE || type == (STYPE_DISKTREE | STYPE_HIDDEN)) ) { printf(_("share [%s] is not a diskshare (type: %x)\n"), netname, type); return false; } /* skip builtin shares */ /* FIXME: should print$ be added too ? */ if (strequal(netname,"IPC$") || strequal(netname,"ADMIN$") || strequal(netname,"global")) return false; if (c->opt_exclude && in_list(netname, c->opt_exclude, false)) { printf(_("excluding [%s]\n"), netname); return false; } return check_share_availability(cli, netname); } /** * Migrate shares from a remote RPC server to the local RPC server. * * All parameters are provided by the run_rpc_command function, except for * argc, argv which are passed 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, destroyed 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_migrate_shares_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { WERROR result; NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; struct srvsvc_NetShareInfoCtr ctr_src; uint32_t i; struct rpc_pipe_client *srvsvc_pipe = NULL; struct cli_state *cli_dst = NULL; uint32_t level = 502; /* includes secdesc */ uint32_t parm_error = 0; struct dcerpc_binding_handle *b; result = get_share_info(c, pipe_hnd, mem_ctx, level, argc, argv, &ctr_src); if (!W_ERROR_IS_OK(result)) goto done; /* connect destination PI_SRVSVC */ nt_status = connect_dst_pipe(c, &cli_dst, &srvsvc_pipe, &ndr_table_srvsvc); if (!NT_STATUS_IS_OK(nt_status)) return nt_status; b = srvsvc_pipe->binding_handle; for (i = 0; i < ctr_src.ctr.ctr502->count; i++) { union srvsvc_NetShareInfo info; struct srvsvc_NetShareInfo502 info502 = ctr_src.ctr.ctr502->array[i]; /* reset error-code */ nt_status = NT_STATUS_UNSUCCESSFUL; if (!check_share_sanity(c, cli, info502.name, info502.type)) continue; /* finally add the share on the dst server */ printf(_("migrating: [%s], path: %s, comment: %s, without " "share-ACLs\n"), info502.name, info502.path, info502.comment); info.info502 = &info502; nt_status = dcerpc_srvsvc_NetShareAdd(b, mem_ctx, srvsvc_pipe->desthost, 502, &info, &parm_error, &result); if (!NT_STATUS_IS_OK(nt_status)) { printf(_("cannot add share: %s\n"), nt_errstr(nt_status)); goto done; } if (W_ERROR_V(result) == W_ERROR_V(WERR_FILE_EXISTS)) { printf(_(" [%s] does already exist\n"), info502.name); continue; } if (!W_ERROR_IS_OK(result)) { nt_status = werror_to_ntstatus(result); printf(_("cannot add share: %s\n"), win_errstr(result)); goto done; } } nt_status = NT_STATUS_OK; done: if (cli_dst) { cli_shutdown(cli_dst); } return nt_status; } /** * Migrate shares from a RPC server to another. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_share_migrate_shares(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc share migrate shares\n" " %s\n", _("Usage:"), _("Migrate shares to local server")); return 0; } if (!c->opt_host) { printf(_("no server to migrate\n")); return -1; } return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, rpc_share_migrate_shares_internals, argc, argv); } /** * Copy a file/dir * * @param f file_info * @param mask current search mask * @param state arg-pointer * **/ static NTSTATUS copy_fn(struct file_info *f, const char *mask, void *state) { static NTSTATUS nt_status; static struct copy_clistate *local_state; static fstring filename, new_mask; fstring dir; char *old_dir; struct net_context *c; local_state = (struct copy_clistate *)state; nt_status = NT_STATUS_UNSUCCESSFUL; c = local_state->c; if (strequal(f->name, ".") || strequal(f->name, "..")) return NT_STATUS_OK; DEBUG(3,("got mask: %s, name: %s\n", mask, f->name)); /* DIRECTORY */ if (f->attr & FILE_ATTRIBUTE_DIRECTORY) { DEBUG(3,("got dir: %s\n", f->name)); fstrcpy(dir, local_state->cwd); fstrcat(dir, "\\"); fstrcat(dir, f->name); switch (net_mode_share) { case NET_MODE_SHARE_MIGRATE: /* create that directory */ nt_status = net_copy_file(c, local_state->mem_ctx, local_state->cli_share_src, local_state->cli_share_dst, dir, dir, c->opt_acls? true : false, c->opt_attrs? true : false, c->opt_timestamps? true:false, false); break; default: d_fprintf(stderr, _("Unsupported mode %d\n"), net_mode_share); return NT_STATUS_INTERNAL_ERROR; } if (!NT_STATUS_IS_OK(nt_status)) { printf(_("could not handle dir %s: %s\n"), dir, nt_errstr(nt_status)); return nt_status; } /* search below that directory */ if (strlcpy(new_mask, dir, sizeof(new_mask)) >= sizeof(new_mask)) { return NT_STATUS_NO_MEMORY; } if (strlcat(new_mask, "\\*", sizeof(new_mask)) >= sizeof(new_mask)) { return NT_STATUS_NO_MEMORY; } old_dir = local_state->cwd; local_state->cwd = dir; nt_status = sync_files(local_state, new_mask); if (!NT_STATUS_IS_OK(nt_status)) { printf(_("could not handle files\n")); } local_state->cwd = old_dir; return nt_status; } /* FILE */ fstrcpy(filename, local_state->cwd); fstrcat(filename, "\\"); fstrcat(filename, f->name); DEBUG(3,("got file: %s\n", filename)); switch (net_mode_share) { case NET_MODE_SHARE_MIGRATE: nt_status = net_copy_file(c, local_state->mem_ctx, local_state->cli_share_src, local_state->cli_share_dst, filename, filename, c->opt_acls? true : false, c->opt_attrs? true : false, c->opt_timestamps? true: false, true); break; default: d_fprintf(stderr, _("Unsupported file mode %d\n"), net_mode_share); return NT_STATUS_INTERNAL_ERROR; } if (!NT_STATUS_IS_OK(nt_status)) printf(_("could not handle file %s: %s\n"), filename, nt_errstr(nt_status)); return nt_status; } /** * sync files, can be called recursively to list files * and then call copy_fn for each file * * @param cp_clistate pointer to the copy_clistate we work with * @param mask the current search mask * * @return Boolean result **/ static NTSTATUS sync_files(struct copy_clistate *cp_clistate, const char *mask) { struct cli_state *targetcli; char *targetpath = NULL; NTSTATUS status; DEBUG(3,("calling cli_list with mask: %s\n", mask)); status = cli_resolve_path(talloc_tos(), "", NULL, cp_clistate->cli_share_src, mask, &targetcli, &targetpath); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("cli_resolve_path %s failed with error: " "%s\n"), mask, nt_errstr(status)); return status; } status = cli_list(targetcli, targetpath, cp_clistate->attribute, copy_fn, cp_clistate); if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("listing %s failed with error: %s\n"), mask, nt_errstr(status)); } return status; } /** * Set the top level directory permissions before we do any further copies. * Should set up ACL inheritance. **/ bool copy_top_level_perms(struct net_context *c, struct copy_clistate *cp_clistate, const char *sharename) { NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; switch (net_mode_share) { case NET_MODE_SHARE_MIGRATE: DEBUG(3,("calling net_copy_fileattr for '.' directory in share %s\n", sharename)); nt_status = net_copy_fileattr(c, cp_clistate->mem_ctx, cp_clistate->cli_share_src, cp_clistate->cli_share_dst, "\\", "\\", c->opt_acls? true : false, c->opt_attrs? true : false, c->opt_timestamps? true: false, false); break; default: d_fprintf(stderr, _("Unsupported mode %d\n"), net_mode_share); break; } if (!NT_STATUS_IS_OK(nt_status)) { printf(_("Could handle directory attributes for top level " "directory of share %s. Error %s\n"), sharename, nt_errstr(nt_status)); return false; } return true; } /** * Sync all files inside a remote share to another share (over smb). * * All parameters are provided by the run_rpc_command function, except for * argc, argv which are passed 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, destroyed 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_migrate_files_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { WERROR result; NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; struct srvsvc_NetShareInfoCtr ctr_src; uint32_t i; uint32_t level = 502; struct copy_clistate cp_clistate; bool got_src_share = false; bool got_dst_share = false; const char *mask = "\\*"; char *dst = NULL; dst = SMB_STRDUP(c->opt_destination?c->opt_destination:"127.0.0.1"); if (dst == NULL) { nt_status = NT_STATUS_NO_MEMORY; goto done; } result = get_share_info(c, pipe_hnd, mem_ctx, level, argc, argv, &ctr_src); if (!W_ERROR_IS_OK(result)) goto done; for (i = 0; i < ctr_src.ctr.ctr502->count; i++) { struct srvsvc_NetShareInfo502 info502 = ctr_src.ctr.ctr502->array[i]; if (!check_share_sanity(c, cli, info502.name, info502.type)) continue; /* one might not want to mirror whole discs :) */ if (strequal(info502.name, "print$") || info502.name[1] == '$') { d_printf(_("skipping [%s]: builtin/hidden share\n"), info502.name); continue; } switch (net_mode_share) { case NET_MODE_SHARE_MIGRATE: printf("syncing"); break; default: d_fprintf(stderr, _("Unsupported mode %d\n"), net_mode_share); break; } printf(_(" [%s] files and directories %s ACLs, %s DOS " "Attributes %s\n"), info502.name, c->opt_acls ? _("including") : _("without"), c->opt_attrs ? _("including") : _("without"), c->opt_timestamps ? _("(preserving timestamps)") : ""); cp_clistate.mem_ctx = mem_ctx; cp_clistate.cli_share_src = NULL; cp_clistate.cli_share_dst = NULL; cp_clistate.cwd = NULL; cp_clistate.attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY; cp_clistate.c = c; /* open share source */ nt_status = connect_to_service(c, &cp_clistate.cli_share_src, smbXcli_conn_remote_sockaddr(cli->conn), smbXcli_conn_remote_name(cli->conn), info502.name, "A:"); if (!NT_STATUS_IS_OK(nt_status)) goto done; got_src_share = true; if (net_mode_share == NET_MODE_SHARE_MIGRATE) { /* open share destination */ nt_status = connect_to_service(c, &cp_clistate.cli_share_dst, NULL, dst, info502.name, "A:"); if (!NT_STATUS_IS_OK(nt_status)) goto done; got_dst_share = true; } if (!copy_top_level_perms(c, &cp_clistate, info502.name)) { d_fprintf(stderr, _("Could not handle the top level " "directory permissions for the " "share: %s\n"), info502.name); nt_status = NT_STATUS_UNSUCCESSFUL; goto done; } nt_status = sync_files(&cp_clistate, mask); if (!NT_STATUS_IS_OK(nt_status)) { d_fprintf(stderr, _("could not handle files for share: " "%s\n"), info502.name); goto done; } } nt_status = NT_STATUS_OK; done: if (got_src_share) cli_shutdown(cp_clistate.cli_share_src); if (got_dst_share) cli_shutdown(cp_clistate.cli_share_dst); SAFE_FREE(dst); return nt_status; } static int rpc_share_migrate_files(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net share migrate files\n" " %s\n", _("Usage:"), _("Migrate files to local server")); return 0; } if (!c->opt_host) { d_printf(_("no server to migrate\n")); return -1; } return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, rpc_share_migrate_files_internals, argc, argv); } /** * Migrate share-ACLs from a remote RPC server to the local RPC server. * * All parameters are provided by the run_rpc_command function, except for * argc, argv which are passed 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, destroyed 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_migrate_security_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { WERROR result; NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; struct srvsvc_NetShareInfoCtr ctr_src; union srvsvc_NetShareInfo info; uint32_t i; struct rpc_pipe_client *srvsvc_pipe = NULL; struct cli_state *cli_dst = NULL; uint32_t level = 502; /* includes secdesc */ uint32_t parm_error = 0; struct dcerpc_binding_handle *b; result = get_share_info(c, pipe_hnd, mem_ctx, level, argc, argv, &ctr_src); if (!W_ERROR_IS_OK(result)) goto done; /* connect destination PI_SRVSVC */ nt_status = connect_dst_pipe(c, &cli_dst, &srvsvc_pipe, &ndr_table_srvsvc); if (!NT_STATUS_IS_OK(nt_status)) return nt_status; b = srvsvc_pipe->binding_handle; for (i = 0; i < ctr_src.ctr.ctr502->count; i++) { struct srvsvc_NetShareInfo502 info502 = ctr_src.ctr.ctr502->array[i]; /* reset error-code */ nt_status = NT_STATUS_UNSUCCESSFUL; if (!check_share_sanity(c, cli, info502.name, info502.type)) continue; printf(_("migrating: [%s], path: %s, comment: %s, including " "share-ACLs\n"), info502.name, info502.path, info502.comment); if (c->opt_verbose) display_sec_desc(info502.sd_buf.sd); /* FIXME: shouldn't we be able to just set the security descriptor ? */ info.info502 = &info502; /* finally modify the share on the dst server */ nt_status = dcerpc_srvsvc_NetShareSetInfo(b, mem_ctx, srvsvc_pipe->desthost, info502.name, level, &info, &parm_error, &result); if (!NT_STATUS_IS_OK(nt_status)) { printf(_("cannot set share-acl: %s\n"), nt_errstr(nt_status)); goto done; } if (!W_ERROR_IS_OK(result)) { nt_status = werror_to_ntstatus(result); printf(_("cannot set share-acl: %s\n"), win_errstr(result)); goto done; } } nt_status = NT_STATUS_OK; done: if (cli_dst) { cli_shutdown(cli_dst); } return nt_status; } /** * Migrate share-acls from a RPC server to another. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_share_migrate_security(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc share migrate security\n" " %s\n", _("Usage:"), _("Migrate share-acls to local server")); return 0; } if (!c->opt_host) { d_printf(_("no server to migrate\n")); return -1; } return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, rpc_share_migrate_security_internals, argc, argv); } /** * Migrate shares (including share-definitions, share-acls and files with acls/attrs) * from one server to another. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). * **/ static int rpc_share_migrate_all(struct net_context *c, int argc, const char **argv) { int ret; if (c->display_usage) { d_printf( "%s\n" "net rpc share migrate all\n" " %s\n", _("Usage:"), _("Migrates shares including all share settings")); return 0; } if (!c->opt_host) { d_printf(_("no server to migrate\n")); return -1; } /* order is important. we don't want to be locked out by the share-acl * before copying files - gd */ ret = run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, rpc_share_migrate_shares_internals, argc, argv); if (ret) return ret; ret = run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, rpc_share_migrate_files_internals, argc, argv); if (ret) return ret; return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, rpc_share_migrate_security_internals, argc, argv); } /** * 'net rpc share migrate' entrypoint. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. **/ static int rpc_share_migrate(struct net_context *c, int argc, const char **argv) { struct functable func[] = { { "all", rpc_share_migrate_all, NET_TRANSPORT_RPC, N_("Migrate shares from remote to local server"), N_("net rpc share migrate all\n" " Migrate shares from remote to local server") }, { "files", rpc_share_migrate_files, NET_TRANSPORT_RPC, N_("Migrate files from remote to local server"), N_("net rpc share migrate files\n" " Migrate files from remote to local server") }, { "security", rpc_share_migrate_security, NET_TRANSPORT_RPC, N_("Migrate share-ACLs from remote to local server"), N_("net rpc share migrate security\n" " Migrate share-ACLs from remote to local server") }, { "shares", rpc_share_migrate_shares, NET_TRANSPORT_RPC, N_("Migrate shares from remote to local server"), N_("net rpc share migrate shares\n" " Migrate shares from remote to local server") }, {NULL, NULL, 0, NULL, NULL} }; net_mode_share = NET_MODE_SHARE_MIGRATE; return net_run_function(c, argc, argv, "net rpc share migrate", func); } struct full_alias { struct dom_sid sid; uint32_t num_members; struct 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(struct full_alias *alias) { size_t array_size; if (server_aliases == NULL) { server_aliases = talloc_array(NULL, struct full_alias, 100); if (server_aliases == NULL) { smb_panic("talloc_array failed"); } } array_size = talloc_array_length(server_aliases); if (array_size == num_server_aliases) { server_aliases = talloc_realloc(NULL, server_aliases, struct full_alias, array_size + 100); if (server_aliases == NULL) { smb_panic("talloc_realloc failed"); } } 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 rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, struct policy_handle *connect_pol, const struct dom_sid *domain_sid) { uint32_t start_idx, max_entries, num_entries, i; struct samr_SamArray *groups = NULL; NTSTATUS result, status; struct policy_handle domain_pol; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; /* Get domain policy handle */ status = dcerpc_samr_OpenDomain(b, mem_ctx, connect_pol, MAXIMUM_ALLOWED_ACCESS, discard_const_p(struct dom_sid2, domain_sid), &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (!NT_STATUS_IS_OK(result)) { return result; } start_idx = 0; max_entries = 250; do { status = dcerpc_samr_EnumDomainAliases(b, mem_ctx, &domain_pol, &start_idx, &groups, max_entries, &num_entries, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } for (i = 0; i < num_entries; i++) { struct policy_handle alias_pol; struct full_alias alias; struct lsa_SidArray sid_array; int j; NTSTATUS _result; status = dcerpc_samr_OpenAlias(b, mem_ctx, &domain_pol, MAXIMUM_ALLOWED_ACCESS, groups->entries[i].idx, &alias_pol, &_result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(_result)) { status = _result; goto done; } status = dcerpc_samr_GetMembersInAlias(b, mem_ctx, &alias_pol, &sid_array, &_result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(_result)) { status = _result; goto done; } alias.num_members = sid_array.num_sids; status = dcerpc_samr_Close(b, mem_ctx, &alias_pol, &_result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(_result)) { status = _result; goto done; } alias.members = NULL; if (alias.num_members > 0) { alias.members = SMB_MALLOC_ARRAY(struct dom_sid, alias.num_members); if (alias.members == NULL) { status = NT_STATUS_NO_MEMORY; goto done; } for (j = 0; j < alias.num_members; j++) sid_copy(&alias.members[j], sid_array.sids[j].sid); } sid_compose(&alias.sid, domain_sid, groups->entries[i].idx); push_alias(&alias); } } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); status = NT_STATUS_OK; done: dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result); return status; } /* * Dump server_aliases as names for debugging purposes. */ static NTSTATUS rpc_aliaslist_dump(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { uint32_t i; NTSTATUS result; struct policy_handle lsa_pol; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; result = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true, SEC_FLAG_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 = rpccli_lsa_lookup_sids(pipe_hnd, 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")); } dcerpc_lsa_Close(b, mem_ctx, &lsa_pol, &result); return NT_STATUS_OK; } /* * Fetch a list of all server aliases and their members into * server_aliases. */ static NTSTATUS rpc_aliaslist_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { NTSTATUS result, status; struct policy_handle connect_pol; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; status = dcerpc_samr_Connect2(b, mem_ctx, pipe_hnd->desthost, MAXIMUM_ALLOWED_ACCESS, &connect_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } status = rpc_fetch_domain_aliases(pipe_hnd, mem_ctx, &connect_pol, &global_sid_Builtin); if (!NT_STATUS_IS_OK(status)) { goto done; } status = rpc_fetch_domain_aliases(pipe_hnd, mem_ctx, &connect_pol, domain_sid); dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result); done: return status; } struct user_token { fstring name; struct security_token *token; }; static void add_sid_to_token(struct security_token *token, const struct dom_sid *sid) { NTSTATUS status = add_sid_to_array_unique(token, sid, &token->sids, &token->num_sids); /* * This is both very unlikely and mostly harmless in a command * line tool */ SMB_ASSERT(NT_STATUS_IS_OK(status)); } static void init_user_token(struct user_token *token_list, struct security_token **token, struct dom_sid *user_sid) { /* * This token is not from the auth stack, only has user SIDs * and must fail if conditional ACEs are found in the security * descriptor */ *token = security_token_initialise(token_list, CLAIMS_EVALUATION_INVALID_STATE); SMB_ASSERT(*token); add_sid_to_token(*token, user_sid); add_sid_to_token(*token, &global_sid_World); add_sid_to_token(*token, &global_sid_Network); add_sid_to_token(*token, &global_sid_Authenticated_Users); } static void dump_user_token(struct user_token *token) { uint32_t i; d_printf("%s\n", token->name); for (i=0; itoken->num_sids; i++) { struct dom_sid_buf buf; d_printf(" %s\n", dom_sid_str_buf(&token->token->sids[i], &buf)); } } static bool is_alias_member(struct dom_sid *sid, struct full_alias *alias) { uint32_t i; for (i=0; inum_members; i++) { if (dom_sid_equal(sid, &alias->members[i])) { return true; } } return false; } static void collect_sid_memberships(struct security_token *token, struct dom_sid sid) { int i; for (i=0; inum_sids; int i; for (i=0; isids[i]); } } static bool get_user_sids(const char *domain, const char *user, struct user_token *token_list, struct security_token **token) { wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; enum wbcSidType type; fstring full_name; struct wbcDomainSid wsid; char sid_str[WBC_SID_STRING_BUFLEN]; struct dom_sid user_sid; uint32_t num_groups; gid_t *groups = NULL; uint32_t i; fstr_sprintf(full_name, "%s%c%s", domain, *lp_winbind_separator(), user); /* First let's find out the user sid */ wbc_status = wbcLookupName(domain, user, &wsid, &type); if (!WBC_ERROR_IS_OK(wbc_status)) { DEBUG(1, ("winbind could not find %s: %s\n", full_name, wbcErrorString(wbc_status))); return false; } wbcSidToStringBuf(&wsid, sid_str, sizeof(sid_str)); if (type != WBC_SID_NAME_USER) { DEBUG(1, ("%s is not a user\n", full_name)); return false; } if (!string_to_sid(&user_sid, sid_str)) { DEBUG(1,("Could not convert sid %s from string\n", sid_str)); return false; } init_user_token(token_list, token, &user_sid); /* And now the groups winbind knows about */ wbc_status = wbcGetGroups(full_name, &num_groups, &groups); if (!WBC_ERROR_IS_OK(wbc_status)) { DEBUG(1, ("winbind could not get groups of %s: %s\n", full_name, wbcErrorString(wbc_status))); return false; } for (i = 0; i < num_groups; i++) { gid_t gid = groups[i]; struct dom_sid sid; bool ok; wbc_status = wbcGidToSid(gid, &wsid); if (!WBC_ERROR_IS_OK(wbc_status)) { DEBUG(1, ("winbind could not find SID of gid %u: %s\n", (unsigned int)gid, wbcErrorString(wbc_status))); wbcFreeMemory(groups); return false; } wbcSidToStringBuf(&wsid, sid_str, sizeof(sid_str)); DEBUG(3, (" %s\n", sid_str)); ok = string_to_sid(&sid, sid_str); if (!ok) { DEBUG(1, ("Failed to convert string to SID\n")); wbcFreeMemory(groups); return false; } add_sid_to_token(*token, &sid); } wbcFreeMemory(groups); return true; } /** * Get a list of all user tokens we want to look at **/ static bool get_user_tokens(struct net_context *c, int *num_tokens, struct user_token **user_tokens) { wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; uint32_t i, num_users; const char **users; struct user_token *result; TALLOC_CTX *frame = NULL; if (lp_winbind_use_default_domain() && (c->opt_target_workgroup == NULL)) { d_fprintf(stderr, _("winbind use default domain = yes set, " "please specify a workgroup\n")); return false; } /* Send request to winbind daemon */ wbc_status = wbcListUsers(NULL, &num_users, &users); if (!WBC_ERROR_IS_OK(wbc_status)) { DEBUG(1, (_("winbind could not list users: %s\n"), wbcErrorString(wbc_status))); return false; } result = talloc_zero_array(NULL, struct user_token, num_users); if (result == NULL) { DBG_ERR("Could not talloc token array\n"); wbcFreeMemory(users); return false; } frame = talloc_stackframe(); for (i=0; i < num_users; i++) { fstring domain, user; char *p; fstrcpy(result[i].name, users[i]); p = strchr(users[i], *lp_winbind_separator()); DEBUG(3, ("%s\n", users[i])); if (p == NULL) { fstrcpy(domain, c->opt_target_workgroup); fstrcpy(user, users[i]); } else { *p++ = '\0'; fstrcpy(domain, users[i]); if (!strupper_m(domain)) { DEBUG(1, ("strupper_m %s failed\n", domain)); wbcFreeMemory(users); return false; } fstrcpy(user, p); } get_user_sids(domain, user, result, &(result[i].token)); } TALLOC_FREE(frame); wbcFreeMemory(users); *num_tokens = num_users; *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 ((strlen(line) > 0) && (line[strlen(line)-1] == '\n')) { line[strlen(line)-1] = '\0'; } if (line[0] == ' ') { /* We have a SID */ struct dom_sid sid; if(!string_to_sid(&sid, &line[1])) { DEBUG(1,("get_user_tokens_from_file: Could " "not convert sid %s \n",&line[1])); return false; } if (token == NULL) { DEBUG(0, ("File does not begin with username\n")); return false; } add_sid_to_token(token->token, &sid); continue; } /* And a new user... */ *num_tokens += 1; *tokens = talloc_realloc(NULL, *tokens, struct user_token, *num_tokens); if (*tokens == NULL) { DBG_ERR("Could not talloc_realloc tokens\n"); return false; } token = &((*tokens)[*num_tokens-1]); if (strlcpy(token->name, line, sizeof(token->name)) >= sizeof(token->name)) { return false; } token->token = security_token_initialise(*tokens, CLAIMS_EVALUATION_INVALID_STATE); if (token->token == NULL) { DBG_ERR("security_token_initialise() failed: " "Could not allocate security_token with \n"); return false; } continue; } return false; } /* * Show the list of all users that have access to a share */ static void show_userlist(struct rpc_pipe_client *pipe_hnd, struct cli_state *cli, TALLOC_CTX *mem_ctx, const char *netname, int num_tokens, struct user_token *tokens) { uint16_t fnum; struct security_descriptor *share_sd = NULL; struct security_descriptor *root_sd = NULL; int i; union srvsvc_NetShareInfo info; WERROR result; NTSTATUS status; struct smbXcli_tcon *orig_tcon = NULL; char *orig_share = NULL; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; status = dcerpc_srvsvc_NetShareGetInfo(b, mem_ctx, pipe_hnd->desthost, netname, 502, &info, &result); if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result)) { DEBUG(1, ("Could not query secdesc for share %s\n", netname)); return; } share_sd = info.info502->sd_buf.sd; if (share_sd == NULL) { DEBUG(1, ("Got no secdesc for share %s\n", netname)); } if (cli_state_has_tcon(cli)) { cli_state_save_tcon_share(cli, &orig_tcon, &orig_share); } if (!NT_STATUS_IS_OK(cli_tree_connect(cli, netname, "A:", NULL))) { cli_state_restore_tcon_share(cli, orig_tcon, orig_share); return; } if (!NT_STATUS_IS_OK(cli_ntcreate(cli, "\\", 0, READ_CONTROL_ACCESS, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0x0, 0x0, &fnum, NULL))) { cli_query_secdesc(cli, fnum, mem_ctx, &root_sd); } for (i=0; ibinding_handle; if (argc != 0) { /* Show results only for shares listed on the command line. */ while (*argv) { const char *netname = *argv++; d_printf("%s\n", netname); show_userlist(pipe_hnd, cli, mem_ctx, netname, num_tokens, tokens); } goto done; } /* Issue the NetShareEnum RPC call and retrieve the response */ nt_status = dcerpc_srvsvc_NetShareEnumAll(b, talloc_tos(), pipe_hnd->desthost, &info_ctr, preferred_len, &total_entries, &resume_handle, &result); /* Was it successful? */ if (!NT_STATUS_IS_OK(nt_status)) { /* Nope. Go clean up. */ goto done; } if (!W_ERROR_IS_OK(result)) { /* Nope. Go clean up. */ nt_status = werror_to_ntstatus(result); goto done; } if (total_entries == 0) { goto done; } /* For each returned entry... */ for (i = 0; i < info_ctr.ctr.ctr1->count; i++) { const char *netname = info_ctr.ctr.ctr1->array[i].name; if (info_ctr.ctr.ctr1->array[i].type != STYPE_DISKTREE) { continue; } d_printf("%s\n", netname); show_userlist(pipe_hnd, cli, mem_ctx, netname, num_tokens, tokens); } done: TALLOC_FREE(tokens); TALLOC_FREE(server_aliases); return nt_status; } static int rpc_share_allowedusers(struct net_context *c, int argc, const char **argv) { int result; if (c->display_usage) { d_printf( "%s\n" "net rpc share allowedusers\n" " %s\n", _("Usage:"), _("List allowed users")); return 0; } result = run_rpc_command(c, NULL, &ndr_table_samr, 0, rpc_aliaslist_internals, argc, argv); if (result != 0) return result; result = run_rpc_command(c, NULL, &ndr_table_lsarpc, 0, rpc_aliaslist_dump, argc, argv); if (result != 0) return result; return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, rpc_share_allowedusers_internals, argc, argv); } int net_usersidlist(struct net_context *c, int argc, const char **argv) { int num_tokens = 0; struct user_token *tokens = NULL; int i; if (argc != 0) { net_usersidlist_usage(c, argc, argv); return 0; } if (!get_user_tokens(c, &num_tokens, &tokens)) { DEBUG(0, ("Could not get the user/sid list\n")); return -1; } for (i=0; inetapi_ctx, c->lp_ctx, c->creds); if (status != 0) { return -1; } if (argc == 0) { if (c->display_usage) { d_printf("%s\n%s", _("Usage:"), _("net rpc share\n" " List shares\n" " Alias for net rpc share list\n")); net_display_usage_from_functable(func); return 0; } return rpc_share_list(c, argc, argv); } return net_run_function(c, argc, argv, "net rpc share", func); } static NTSTATUS rpc_sh_share_list(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, int argc, const char **argv) { return werror_to_ntstatus(W_ERROR(rpc_share_list(c, argc, argv))); } static NTSTATUS rpc_sh_share_add(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, int argc, const char **argv) { NET_API_STATUS status; uint32_t parm_err = 0; struct SHARE_INFO_2 i2; if ((argc < 2) || (argc > 3)) { d_fprintf(stderr, _("Usage: %s [comment]\n"), ctx->whoami); return NT_STATUS_INVALID_PARAMETER; } i2.shi2_netname = argv[0]; i2.shi2_type = STYPE_DISKTREE; i2.shi2_remark = (argc == 3) ? argv[2] : ""; i2.shi2_permissions = 0; i2.shi2_max_uses = 0; i2.shi2_current_uses = 0; i2.shi2_path = argv[1]; i2.shi2_passwd = NULL; status = NetShareAdd(pipe_hnd->desthost, 2, (uint8_t *)&i2, &parm_err); return werror_to_ntstatus(W_ERROR(status)); } static NTSTATUS rpc_sh_share_delete(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, int argc, const char **argv) { if (argc != 1) { d_fprintf(stderr, "%s %s \n", _("Usage:"), ctx->whoami); return NT_STATUS_INVALID_PARAMETER; } return werror_to_ntstatus(W_ERROR(NetShareDel(pipe_hnd->desthost, argv[0], 0))); } static NTSTATUS rpc_sh_share_info(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, struct rpc_pipe_client *pipe_hnd, int argc, const char **argv) { union srvsvc_NetShareInfo info; WERROR result; NTSTATUS status; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; if (argc != 1) { d_fprintf(stderr, "%s %s \n", _("Usage:"), ctx->whoami); return NT_STATUS_INVALID_PARAMETER; } status = dcerpc_srvsvc_NetShareGetInfo(b, mem_ctx, pipe_hnd->desthost, argv[0], 2, &info, &result); if (!NT_STATUS_IS_OK(status)) { result = ntstatus_to_werror(status); goto done; } if (!W_ERROR_IS_OK(result)) { goto done; } d_printf(_("Name: %s\n"), info.info2->name); d_printf(_("Comment: %s\n"), info.info2->comment); d_printf(_("Path: %s\n"), info.info2->path); d_printf(_("Password: %s\n"), info.info2->password); done: return werror_to_ntstatus(result); } struct rpc_sh_cmd *net_rpc_share_cmds(struct net_context *c, TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx) { static struct rpc_sh_cmd cmds[] = { { "list", NULL, &ndr_table_srvsvc, rpc_sh_share_list, N_("List available shares") }, { "add", NULL, &ndr_table_srvsvc, rpc_sh_share_add, N_("Add a share") }, { "delete", NULL, &ndr_table_srvsvc, rpc_sh_share_delete, N_("Delete a share") }, { "info", NULL, &ndr_table_srvsvc, rpc_sh_share_info, N_("Get information about a share") }, { NULL, NULL, 0, NULL, NULL } }; return cmds; } /****************************************************************************/ static int rpc_file_usage(struct net_context *c, int argc, const char **argv) { return net_file_usage(c, argc, argv); } /** * Close a file on a remote RPC server. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_file_close(struct net_context *c, int argc, const char **argv) { if (argc < 1 || c->display_usage) { return rpc_file_usage(c, argc, argv); } return NetFileClose(c->opt_host, atoi(argv[0])); } /** * Formatted print of open file info * * @param r struct FILE_INFO_3 contents **/ static void display_file_info_3(struct FILE_INFO_3 *r) { d_printf("%-7.1" PRIu32 " %-20.20s 0x%-4.2x %-6.1u %s\n", r->fi3_id, r->fi3_username, r->fi3_permissions, r->fi3_num_locks, r->fi3_pathname); } /** * List files for a user on a remote RPC server. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success).. **/ static int rpc_file_user(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; uint32_t preferred_len = 0xffffffff, i; char *username=NULL; uint32_t total_entries = 0; uint32_t entries_read = 0; uint32_t resume_handle = 0; struct FILE_INFO_3 *i3 = NULL; if (c->display_usage) { return rpc_file_usage(c, argc, argv); } /* if argc > 0, must be user command */ if (argc > 0) { username = smb_xstrdup(argv[0]); } status = NetFileEnum(c->opt_host, NULL, username, 3, (uint8_t **)(void *)&i3, preferred_len, &entries_read, &total_entries, &resume_handle); if (status != 0) { goto done; } /* Display results */ d_printf(_( "\nEnumerating open files on remote server:\n\n" "\nFileId Opened by Perms Locks Path" "\n------ --------- ----- ----- ---- \n")); for (i = 0; i < entries_read; i++) { display_file_info_3(&i3[i]); } done: SAFE_FREE(username); return status; } /** * 'net rpc file' entrypoint. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. **/ int net_rpc_file(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; struct functable func[] = { { "close", rpc_file_close, NET_TRANSPORT_RPC, N_("Close opened file"), N_("net rpc file close\n" " Close opened file") }, { "user", rpc_file_user, NET_TRANSPORT_RPC, N_("List files opened by user"), N_("net rpc file user\n" " List files opened by user") }, #if 0 { "info", rpc_file_info, NET_TRANSPORT_RPC, N_("Display information about opened file"), N_("net rpc file info\n" " Display information about opened file") }, #endif {NULL, NULL, 0, NULL, NULL} }; status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds); if (status != 0) { return -1; } if (argc == 0) { if (c->display_usage) { d_printf(_("Usage:\n")); d_printf(_("net rpc file\n" " List opened files\n")); net_display_usage_from_functable(func); return 0; } return rpc_file_user(c, argc, argv); } return net_run_function(c, argc, argv, "net rpc file", func); } /** * ABORT the shutdown of a remote RPC Server, over initshutdown pipe. * * All parameters are provided by the run_rpc_command function, except for * argc, argv which are passed through. * * @param c A net_context structure. * @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, destroyed 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_shutdown_abort_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { NTSTATUS status = NT_STATUS_UNSUCCESSFUL; WERROR result; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; status = dcerpc_initshutdown_Abort(b, mem_ctx, NULL, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (W_ERROR_IS_OK(result)) { d_printf(_("\nShutdown successfully aborted\n")); DEBUG(5,("cmd_shutdown_abort: query succeeded\n")); } else DEBUG(5,("cmd_shutdown_abort: query failed\n")); return werror_to_ntstatus(result); } /** * ABORT the shutdown of a remote RPC Server, over winreg pipe. * * All parameters are provided by the run_rpc_command function, except for * argc, argv which are passed through. * * @param c A net_context structure. * @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, destroyed 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_reg_shutdown_abort_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { NTSTATUS result = NT_STATUS_UNSUCCESSFUL; WERROR werr; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; result = dcerpc_winreg_AbortSystemShutdown(b, mem_ctx, NULL, &werr); if (!NT_STATUS_IS_OK(result)) { DEBUG(5,("cmd_reg_abort_shutdown: query failed\n")); return result; } if (W_ERROR_IS_OK(werr)) { d_printf(_("\nShutdown successfully aborted\n")); DEBUG(5,("cmd_reg_abort_shutdown: query succeeded\n")); } else DEBUG(5,("cmd_reg_abort_shutdown: query failed\n")); return werror_to_ntstatus(werr); } /** * ABORT the shutdown of a remote RPC server. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_shutdown_abort(struct net_context *c, int argc, const char **argv) { int rc = -1; if (c->display_usage) { d_printf( "%s\n" "net rpc abortshutdown\n" " %s\n", _("Usage:"), _("Abort a scheduled shutdown")); return 0; } rc = run_rpc_command(c, NULL, &ndr_table_initshutdown, 0, rpc_shutdown_abort_internals, argc, argv); if (rc == 0) return rc; DEBUG(1, ("initshutdown pipe didn't work, trying winreg pipe\n")); return run_rpc_command(c, NULL, &ndr_table_winreg, 0, rpc_reg_shutdown_abort_internals, argc, argv); } /** * Shut down a remote RPC Server via initshutdown pipe. * * All parameters are provided by the run_rpc_command function, except for * argc, argv which are passed through. * * @param c A net_context structure. * @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, destroyed 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. **/ NTSTATUS rpc_init_shutdown_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { NTSTATUS status = NT_STATUS_UNSUCCESSFUL; WERROR result; const char *msg = N_("This machine will be shutdown shortly"); uint32_t timeout = 20; struct lsa_StringLarge msg_string; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; if (c->opt_comment) { msg = c->opt_comment; } if (c->opt_timeout) { timeout = c->opt_timeout; } msg_string.string = msg; /* create an entry */ status = dcerpc_initshutdown_Init(b, mem_ctx, NULL, &msg_string, timeout, c->opt_force, c->opt_reboot, &result); if (!NT_STATUS_IS_OK(status)) { return status; } if (W_ERROR_IS_OK(result)) { d_printf(_("\nShutdown of remote machine succeeded\n")); DEBUG(5,("Shutdown of remote machine succeeded\n")); } else { DEBUG(1,("Shutdown of remote machine failed!\n")); } return werror_to_ntstatus(result); } /** * Shut down a remote RPC Server via winreg pipe. * * All parameters are provided by the run_rpc_command function, except for * argc, argv which are passed through. * * @param c A net_context structure. * @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, destroyed 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. **/ NTSTATUS rpc_reg_shutdown_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { const char *msg = N_("This machine will be shutdown shortly"); uint32_t timeout = 20; struct lsa_StringLarge msg_string; NTSTATUS result; WERROR werr; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; if (c->opt_comment) { msg = c->opt_comment; } msg_string.string = msg; if (c->opt_timeout) { timeout = c->opt_timeout; } /* create an entry */ result = dcerpc_winreg_InitiateSystemShutdown(b, mem_ctx, NULL, &msg_string, timeout, c->opt_force, c->opt_reboot, &werr); if (!NT_STATUS_IS_OK(result)) { d_fprintf(stderr, "\nShutdown of remote machine failed\n"); return result; } if (W_ERROR_IS_OK(werr)) { d_printf(_("\nShutdown of remote machine succeeded\n")); } else { d_fprintf(stderr, "\nShutdown of remote machine failed\n"); if ( W_ERROR_EQUAL(werr, WERR_MACHINE_LOCKED) ) d_fprintf(stderr, "\nMachine locked, use -f switch to force\n"); else d_fprintf(stderr, "\nresult was: %s\n", win_errstr(werr)); } return werror_to_ntstatus(werr); } /** * Shut down a remote RPC server. * * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_shutdown(struct net_context *c, int argc, const char **argv) { int rc = -1; if (c->display_usage) { d_printf( "%s\n" "net rpc shutdown\n" " %s\n", _("Usage:"), _("Shut down a remote RPC server")); return 0; } rc = run_rpc_command(c, NULL, &ndr_table_initshutdown, 0, rpc_init_shutdown_internals, argc, argv); if (rc) { DEBUG(1, ("initshutdown pipe failed, trying winreg pipe\n")); rc = run_rpc_command(c, NULL, &ndr_table_winreg, 0, rpc_reg_shutdown_internals, argc, argv); } return rc; } /*************************************************************************** NT Domain trusts code (i.e. 'net rpc trustdom' functionality) ***************************************************************************/ /** * Add interdomain trust account to the RPC server. * All parameters (except for argc and argv) are passed by run_rpc_command * function. * * @param c A net_context structure. * @param domain_sid The domain sid acquired from the server. * @param cli A cli_state connected to the server. * @param mem_ctx Talloc context, destroyed 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 code. */ static NTSTATUS rpc_trustdom_add_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { struct policy_handle connect_pol, domain_pol, user_pol; NTSTATUS status, result; char *acct_name; struct lsa_String lsa_acct_name; uint32_t acb_info; uint32_t acct_flags=0; uint32_t user_rid; uint32_t access_granted = 0; union samr_UserInfo info; unsigned int orig_timeout; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; DATA_BLOB session_key = data_blob_null; TALLOC_CTX *frame = NULL; if (argc != 2) { d_printf("%s\n%s", _("Usage:"), _(" net rpc trustdom add " "\n")); return NT_STATUS_INVALID_PARAMETER; } frame = talloc_stackframe(); /* * Make valid trusting domain account (ie. uppercased and with '$' appended) */ if (asprintf(&acct_name, "%s$", argv[0]) < 0) { status = NT_STATUS_NO_MEMORY; } if (!strupper_m(acct_name)) { status = NT_STATUS_INVALID_PARAMETER; goto done; } init_lsa_String(&lsa_acct_name, acct_name); status = cli_get_session_key(frame, pipe_hnd, &session_key); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("Error getting session_key of SAM pipe. Error was %s\n", nt_errstr(status))); goto done; } /* Get samr policy handle */ status = dcerpc_samr_Connect2(b, frame, pipe_hnd->desthost, MAXIMUM_ALLOWED_ACCESS, &connect_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } /* Get domain policy handle */ status = dcerpc_samr_OpenDomain(b, frame, &connect_pol, MAXIMUM_ALLOWED_ACCESS, discard_const_p(struct dom_sid2, domain_sid), &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } /* This call can take a long time - allow the server to time out. * 35 seconds should do it. */ orig_timeout = rpccli_set_timeout(pipe_hnd, 35000); /* Create trusting domain's account */ acb_info = ACB_NORMAL; acct_flags = SEC_GENERIC_READ | SEC_GENERIC_WRITE | SEC_GENERIC_EXECUTE | SEC_STD_WRITE_DAC | SEC_STD_DELETE | SAMR_USER_ACCESS_SET_PASSWORD | SAMR_USER_ACCESS_GET_ATTRIBUTES | SAMR_USER_ACCESS_SET_ATTRIBUTES; status = dcerpc_samr_CreateUser2(b, frame, &domain_pol, &lsa_acct_name, acb_info, acct_flags, &user_pol, &access_granted, &user_rid, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } /* And restore our original timeout. */ rpccli_set_timeout(pipe_hnd, orig_timeout); if (!NT_STATUS_IS_OK(result)) { status = result; d_printf(_("net rpc trustdom add: create user %s failed %s\n"), acct_name, nt_errstr(result)); goto done; } { struct samr_CryptPassword crypt_pwd; ZERO_STRUCT(info.info23); status = init_samr_CryptPassword(argv[1], &session_key, &crypt_pwd); if (!NT_STATUS_IS_OK(status)) { goto done; } info.info23.info.fields_present = SAMR_FIELD_ACCT_FLAGS | SAMR_FIELD_NT_PASSWORD_PRESENT; info.info23.info.acct_flags = ACB_DOMTRUST; info.info23.password = crypt_pwd; status = dcerpc_samr_SetUserInfo2(b, frame, &user_pol, 23, &info, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; DEBUG(0,("Could not set trust account password: %s\n", nt_errstr(result))); goto done; } } status = NT_STATUS_OK; done: SAFE_FREE(acct_name); data_blob_clear_free(&session_key); TALLOC_FREE(frame); return status; } /** * Create interdomain trust account for a remote domain. * * @param argc Standard argc. * @param argv Standard argv without initial components. * * @return Integer status (0 means success). **/ static int rpc_trustdom_add(struct net_context *c, int argc, const char **argv) { if (argc > 0 && !c->display_usage) { return run_rpc_command(c, NULL, &ndr_table_samr, 0, rpc_trustdom_add_internals, argc, argv); } else { d_printf("%s\n%s", _("Usage:"), _("net rpc trustdom add \n")); return -1; } } /** * Remove interdomain trust account from the RPC server. * All parameters (except for argc and argv) are passed by run_rpc_command * function. * * @param c A net_context structure. * @param domain_sid The domain sid acquired from the server. * @param cli A cli_state connected to the server. * @param mem_ctx Talloc context, destroyed 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 code. */ static NTSTATUS rpc_trustdom_del_internals(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { struct policy_handle connect_pol, domain_pol, user_pol; NTSTATUS status, result; char *acct_name; struct dom_sid trust_acct_sid; struct samr_Ids user_rids, name_types; struct lsa_String lsa_acct_name; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; if (argc != 1) { d_printf("%s\n%s", _("Usage:"), _(" net rpc trustdom del \n")); return NT_STATUS_INVALID_PARAMETER; } /* * Make valid trusting domain account (ie. uppercased and with '$' appended) */ acct_name = talloc_asprintf(mem_ctx, "%s$", argv[0]); if (acct_name == NULL) return NT_STATUS_NO_MEMORY; if (!strupper_m(acct_name)) { TALLOC_FREE(acct_name); return NT_STATUS_INVALID_PARAMETER; } /* Get samr policy handle */ status = dcerpc_samr_Connect2(b, mem_ctx, pipe_hnd->desthost, MAXIMUM_ALLOWED_ACCESS, &connect_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } /* Get domain policy handle */ status = dcerpc_samr_OpenDomain(b, mem_ctx, &connect_pol, MAXIMUM_ALLOWED_ACCESS, discard_const_p(struct dom_sid2, domain_sid), &domain_pol, &result); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; goto done; } init_lsa_String(&lsa_acct_name, acct_name); status = dcerpc_samr_LookupNames(b, mem_ctx, &domain_pol, 1, &lsa_acct_name, &user_rids, &name_types, &result); if (!NT_STATUS_IS_OK(status)) { d_printf(_("net rpc trustdom del: LookupNames on user %s " "failed %s\n"), acct_name, nt_errstr(status)); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_printf(_("net rpc trustdom del: LookupNames on user %s " "failed %s\n"), acct_name, nt_errstr(result) ); goto done; } if (user_rids.count != 1) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto done; } if (name_types.count != 1) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto done; } status = dcerpc_samr_OpenUser(b, mem_ctx, &domain_pol, MAXIMUM_ALLOWED_ACCESS, user_rids.ids[0], &user_pol, &result); if (!NT_STATUS_IS_OK(status)) { d_printf(_("net rpc trustdom del: OpenUser on user %s failed " "%s\n"), acct_name, nt_errstr(status) ); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_printf(_("net rpc trustdom del: OpenUser on user %s failed " "%s\n"), acct_name, nt_errstr(result) ); goto done; } /* append the rid to the domain sid */ if (!sid_compose(&trust_acct_sid, domain_sid, user_rids.ids[0])) { goto done; } /* remove the sid */ status = dcerpc_samr_RemoveMemberFromForeignDomain(b, mem_ctx, &user_pol, &trust_acct_sid, &result); if (!NT_STATUS_IS_OK(status)) { d_printf(_("net rpc trustdom del: RemoveMemberFromForeignDomain" " on user %s failed %s\n"), acct_name, nt_errstr(status)); goto done; } if (!NT_STATUS_IS_OK(result)) { status = result; d_printf(_("net rpc trustdom del: RemoveMemberFromForeignDomain" " on user %s failed %s\n"), acct_name, nt_errstr(result) ); goto done; } /* Delete user */ status = dcerpc_samr_DeleteUser(b, mem_ctx, &user_pol, &result); if (!NT_STATUS_IS_OK(status)) { d_printf(_("net rpc trustdom del: DeleteUser on user %s failed " "%s\n"), acct_name, nt_errstr(status)); goto done; } if (!NT_STATUS_IS_OK(result)) { result = status; d_printf(_("net rpc trustdom del: DeleteUser on user %s failed " "%s\n"), acct_name, nt_errstr(result) ); goto done; } if (!NT_STATUS_IS_OK(result)) { d_printf(_("Could not set trust account password: %s\n"), nt_errstr(result)); goto done; } done: return status; } /** * Delete interdomain trust account for a remote domain. * * @param argc Standard argc. * @param argv Standard argv without initial components. * * @return Integer status (0 means success). **/ static int rpc_trustdom_del(struct net_context *c, int argc, const char **argv) { if (argc > 0 && !c->display_usage) { return run_rpc_command(c, NULL, &ndr_table_samr, 0, rpc_trustdom_del_internals, argc, argv); } else { d_printf("%s\n%s", _("Usage:"), _("net rpc trustdom del \n")); return -1; } } static NTSTATUS rpc_trustdom_get_pdc(struct net_context *c, struct cli_state *cli, TALLOC_CTX *mem_ctx, const char *domain_name) { char *dc_name = NULL; const char *buffer = NULL; struct rpc_pipe_client *netr; NTSTATUS status; WERROR result; struct dcerpc_binding_handle *b; /* Use NetServerEnum2 */ if (cli_get_pdc_name(cli, domain_name, &dc_name)) { SAFE_FREE(dc_name); return NT_STATUS_OK; } DEBUG(1,("NetServerEnum2 error: Couldn't find primary domain controller " "for domain %s\n", domain_name)); /* Try netr_GetDcName */ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon, &netr); if (!NT_STATUS_IS_OK(status)) { return status; } b = netr->binding_handle; status = dcerpc_netr_GetDcName(b, mem_ctx, netr->desthost, domain_name, &buffer, &result); TALLOC_FREE(netr); if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(result)) { return status; } DEBUG(1,("netr_GetDcName error: Couldn't find primary domain controller " "for domain %s\n", domain_name)); if (!NT_STATUS_IS_OK(status)) { return status; } return werror_to_ntstatus(result); } /** * Establish trust relationship to a trusting domain. * Interdomain account must already be created on remote PDC. * * @param c A net_context structure. * @param argc Standard argc. * @param argv Standard argv without initial components. * * @return Integer status (0 means success). **/ static int rpc_trustdom_establish(struct net_context *c, int argc, const char **argv) { struct cli_state *cli = NULL; struct sockaddr_storage server_ss; struct rpc_pipe_client *pipe_hnd = NULL; struct policy_handle connect_hnd; TALLOC_CTX *mem_ctx; NTSTATUS nt_status, result; struct dom_sid *domain_sid; char* domain_name; char* acct_name; const char *pwd = NULL; fstring pdc_name; union lsa_PolicyInformation *info = NULL; struct dcerpc_binding_handle *b; union lsa_revision_info out_revision_info = { .info1 = { .revision = 0, }, }; uint32_t out_version = 0; /* * Connect to \\server\ipc$ as 'our domain' account with password */ if (argc != 1 || c->display_usage) { d_printf("%s\n%s", _("Usage:"), _("net rpc trustdom establish \n")); return -1; } domain_name = smb_xstrdup(argv[0]); if (!strupper_m(domain_name)) { SAFE_FREE(domain_name); return -1; } /* account name used at first is our domain's name with '$' */ if (asprintf(&acct_name, "%s$", lp_workgroup()) == -1) { return -1; } if (!strupper_m(acct_name)) { SAFE_FREE(domain_name); SAFE_FREE(acct_name); return -1; } cli_credentials_set_username(c->creds, acct_name, CRED_SPECIFIED); /* * opt_workgroup will be used by connection functions further, * hence it should be set to remote domain name instead of ours */ if (c->opt_workgroup) { c->opt_workgroup = smb_xstrdup(domain_name); }; /* find the domain controller */ if (!net_find_pdc(&server_ss, pdc_name, domain_name)) { DEBUG(0, ("Couldn't find domain controller for domain %s\n", domain_name)); return -1; } /* connect to ipc$ as username/password */ nt_status = connect_to_ipc(c, &cli, &server_ss, pdc_name); if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) { /* Is it trusting domain account for sure ? */ DEBUG(0, ("Couldn't verify trusting domain account. Error was %s\n", nt_errstr(nt_status))); return -1; } /* store who we connected to */ saf_store( domain_name, pdc_name ); /* * Connect to \\server\ipc$ again (this time anonymously) */ nt_status = connect_to_ipc_anonymous(c, &cli, &server_ss, (char*)pdc_name); if (NT_STATUS_IS_ERR(nt_status)) { DEBUG(0, ("Couldn't connect to domain %s controller. Error was %s.\n", domain_name, nt_errstr(nt_status))); return -1; } if (!(mem_ctx = talloc_init("establishing trust relationship to " "domain %s", domain_name))) { DEBUG(0, ("talloc_init() failed\n")); cli_shutdown(cli); return -1; } /* Make sure we're talking to a proper server */ nt_status = rpc_trustdom_get_pdc(c, cli, mem_ctx, domain_name); if (!NT_STATUS_IS_OK(nt_status)) { cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; } /* * Call LsaOpenPolicy and LsaQueryInfo */ nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &pipe_hnd); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Could not initialise lsa pipe. Error was %s\n", nt_errstr(nt_status) )); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; } b = pipe_hnd->binding_handle; nt_status = dcerpc_lsa_open_policy_fallback(b, mem_ctx, pipe_hnd->srv_name_slash, true, KEY_QUERY_VALUE, &out_version, &out_revision_info, &connect_hnd, &result); if (any_nt_status_not_ok(nt_status, result, &nt_status)) { DBG_ERR("Couldn't open policy handle: %s\n", nt_errstr(nt_status)); cli_shutdown(cli); talloc_free(mem_ctx); return -1; } /* Querying info level 5 */ nt_status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, &connect_hnd, LSA_POLICY_INFO_ACCOUNT_DOMAIN, &info, &result); if (NT_STATUS_IS_ERR(nt_status)) { DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", nt_errstr(nt_status))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; } if (NT_STATUS_IS_ERR(result)) { DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", nt_errstr(result))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; } domain_sid = info->account_domain.sid; /* There should be actually query info level 3 (following nt serv behaviour), but I still don't know if it's _really_ necessary */ /* * Store the password in secrets db */ pwd = cli_credentials_get_password(c->creds); if (!pdb_set_trusteddom_pw(domain_name, pwd, domain_sid)) { DEBUG(0, ("Storing password for trusted domain failed.\n")); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; } /* * Close the pipes and clean up */ nt_status = dcerpc_lsa_Close(b, mem_ctx, &connect_hnd, &result); if (NT_STATUS_IS_ERR(nt_status)) { DEBUG(0, ("Couldn't close LSA pipe. Error was %s\n", nt_errstr(nt_status))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; } cli_shutdown(cli); talloc_destroy(mem_ctx); d_printf(_("Trust to domain %s established\n"), domain_name); return 0; } /** * Revoke trust relationship to the remote domain. * * @param c A net_context structure. * @param argc Standard argc. * @param argv Standard argv without initial components. * * @return Integer status (0 means success). **/ static int rpc_trustdom_revoke(struct net_context *c, int argc, const char **argv) { char* domain_name; int rc = -1; if (argc < 1 || c->display_usage) { d_printf("%s\n%s", _("Usage:"), _("net rpc trustdom revoke \n" " Revoke trust relationship\n" " domain_name\tName of domain to revoke trust\n")); return -1; } /* generate upper cased domain name */ domain_name = smb_xstrdup(argv[0]); if (!strupper_m(domain_name)) { SAFE_FREE(domain_name); return -1; } /* delete password of the trust */ if (!pdb_del_trusteddom_pw(domain_name)) { DEBUG(0, ("Failed to revoke relationship to the trusted domain %s\n", domain_name)); goto done; }; rc = 0; done: SAFE_FREE(domain_name); return rc; } static NTSTATUS rpc_query_domain_sid(struct net_context *c, const struct dom_sid *domain_sid, const char *domain_name, struct cli_state *cli, struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, int argc, const char **argv) { struct dom_sid_buf sid_str; d_printf("%s\n", dom_sid_str_buf(domain_sid, &sid_str)); return NT_STATUS_OK; } static void print_trusted_domain(struct dom_sid *dom_sid, const char *trusted_dom_name) { struct dom_sid_buf sid_str; d_printf("%-20s%s\n", trusted_dom_name, dom_sid_str_buf(dom_sid, &sid_str)); } static NTSTATUS vampire_trusted_domain(struct rpc_pipe_client *pipe_hnd, TALLOC_CTX *mem_ctx, struct policy_handle *pol, struct dom_sid dom_sid, const char *trusted_dom_name) { NTSTATUS nt_status, result; union lsa_TrustedDomainInfo *info = NULL; char *cleartextpwd = NULL; DATA_BLOB session_key; DATA_BLOB data = data_blob_null; struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; nt_status = dcerpc_lsa_QueryTrustedDomainInfoBySid(b, mem_ctx, pol, &dom_sid, LSA_TRUSTED_DOMAIN_INFO_PASSWORD, &info, &result); if (NT_STATUS_IS_ERR(nt_status)) { DEBUG(0,("Could not query trusted domain info. Error was %s\n", nt_errstr(nt_status))); goto done; } if (NT_STATUS_IS_ERR(result)) { nt_status = result; DEBUG(0,("Could not query trusted domain info. Error was %s\n", nt_errstr(result))); goto done; } data = data_blob(info->password.password->data, info->password.password->length); nt_status = cli_get_session_key(mem_ctx, pipe_hnd, &session_key); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Could not retrieve session key: %s\n", nt_errstr(nt_status))); goto done; } cleartextpwd = sess_decrypt_string(mem_ctx, &data, &session_key); data_blob_free(&session_key); if (cleartextpwd == NULL) { DEBUG(0,("retrieved NULL password\n")); nt_status = NT_STATUS_UNSUCCESSFUL; goto done; } if (!pdb_set_trusteddom_pw(trusted_dom_name, cleartextpwd, &dom_sid)) { DEBUG(0, ("Storing password for trusted domain failed.\n")); nt_status = NT_STATUS_UNSUCCESSFUL; goto done; } #ifdef DEBUG_PASSWORD { struct dom_sid_buf buf; DEBUG(100,("successfully vampired trusted domain [%s], " "sid: [%s], password: [%s]\n", trusted_dom_name, dom_sid_str_buf(&dom_sid, &buf), cleartextpwd)); } #endif done: SAFE_FREE(cleartextpwd); data_blob_free(&data); return nt_status; } static int rpc_trustdom_vampire(struct net_context *c, int argc, const char **argv) { /* common variables */ TALLOC_CTX* mem_ctx; struct cli_state *cli = NULL; struct rpc_pipe_client *pipe_hnd = NULL; NTSTATUS nt_status, result; const char *domain_name = NULL; struct policy_handle connect_hnd; union lsa_PolicyInformation *info = NULL; /* trusted domains listing variables */ unsigned int enum_ctx = 0; struct lsa_DomainList dom_list; fstring pdc_name; struct dcerpc_binding_handle *b; union lsa_revision_info out_revision_info = { .info1 = { .revision = 0, }, }; uint32_t out_version = 0; if (c->display_usage) { d_printf( "%s\n" "net rpc trustdom vampire\n" " %s\n", _("Usage:"), _("Vampire trust relationship from remote server")); return 0; } /* * Listing trusted domains (stored in secrets.tdb, if local) */ mem_ctx = talloc_init("trust relationships vampire"); /* * set domain and pdc name to local samba server (default) * or to remote one given in command line */ if (strcasecmp_m(c->opt_workgroup, lp_workgroup())) { domain_name = c->opt_workgroup; c->opt_target_workgroup = c->opt_workgroup; } else { fstrcpy(pdc_name, lp_netbios_name()); domain_name = talloc_strdup(mem_ctx, lp_workgroup()); c->opt_target_workgroup = domain_name; }; /* open \PIPE\lsarpc and open policy handle */ nt_status = net_make_ipc_connection(c, NET_FLAGS_PDC, &cli); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Couldn't connect to domain controller: %s\n", nt_errstr(nt_status))); talloc_destroy(mem_ctx); return -1; }; nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &pipe_hnd); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Could not initialise lsa pipe. Error was %s\n", nt_errstr(nt_status) )); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; b = pipe_hnd->binding_handle; nt_status = dcerpc_lsa_open_policy_fallback(b, mem_ctx, pipe_hnd->srv_name_slash, false, KEY_QUERY_VALUE, &out_version, &out_revision_info, &connect_hnd, &result); if (any_nt_status_not_ok(nt_status, result, &nt_status)) { DBG_ERR("Couldn't open policy handle: %s\n", nt_errstr(nt_status)); cli_shutdown(cli); talloc_free(mem_ctx); return -1; } /* query info level 5 to obtain sid of a domain being queried */ nt_status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, &connect_hnd, LSA_POLICY_INFO_ACCOUNT_DOMAIN, &info, &result); if (NT_STATUS_IS_ERR(nt_status)) { DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", nt_errstr(nt_status))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; } if (NT_STATUS_IS_ERR(result)) { DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", nt_errstr(result))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; } /* * Keep calling LsaEnumTrustdom over opened pipe until * the end of enumeration is reached */ d_printf(_("Vampire trusted domains:\n\n")); do { uint32_t i; nt_status = dcerpc_lsa_EnumTrustDom(b, mem_ctx, &connect_hnd, &enum_ctx, &dom_list, (uint32_t)-1, &result); if (NT_STATUS_IS_ERR(nt_status)) { DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n", nt_errstr(nt_status))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; if (NT_STATUS_IS_ERR(result)) { nt_status = result; DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n", nt_errstr(result))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; for (i = 0; i < dom_list.count; i++) { print_trusted_domain(dom_list.domains[i].sid, dom_list.domains[i].name.string); nt_status = vampire_trusted_domain(pipe_hnd, mem_ctx, &connect_hnd, *dom_list.domains[i].sid, dom_list.domains[i].name.string); if (!NT_STATUS_IS_OK(nt_status)) { cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; } }; /* * in case of no trusted domains say something rather * than just display blank line */ if (!dom_list.count) d_printf(_("none\n")); } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES)); /* close this connection before doing next one */ nt_status = dcerpc_lsa_Close(b, mem_ctx, &connect_hnd, &result); if (NT_STATUS_IS_ERR(nt_status)) { DEBUG(0, ("Couldn't properly close lsa policy handle. Error was %s\n", nt_errstr(nt_status))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; /* close lsarpc pipe and connection to IPC$ */ cli_shutdown(cli); talloc_destroy(mem_ctx); return 0; } static int rpc_trustdom_list(struct net_context *c, int argc, const char **argv) { /* common variables */ TALLOC_CTX* mem_ctx; struct cli_state *cli = NULL, *remote_cli = NULL; struct rpc_pipe_client *pipe_hnd = NULL; NTSTATUS nt_status, result; const char *domain_name = NULL; struct dom_sid *queried_dom_sid; int ascii_dom_name_len; struct policy_handle connect_hnd; union lsa_PolicyInformation *info = NULL; struct dcerpc_binding_handle *b = NULL; /* trusted domains listing variables */ unsigned int num_domains, enum_ctx = 0; uint32_t i; struct lsa_DomainList dom_list; fstring pdc_name; bool found_domain; /* trusting domains listing variables */ struct policy_handle domain_hnd; struct samr_SamArray *trusts = NULL; union lsa_revision_info out_revision_info = { .info1 = { .revision = 0, }, }; uint32_t out_version = 0; if (c->display_usage) { d_printf( "%s\n" "net rpc trustdom list\n" " %s\n", _("Usage:"), _("List incoming and outgoing trust relationships")); return 0; } /* * Listing trusted domains (stored in secrets.tdb, if local) */ mem_ctx = talloc_init("trust relationships listing"); /* * set domain and pdc name to local samba server (default) * or to remote one given in command line */ if (strcasecmp_m(c->opt_workgroup, lp_workgroup())) { domain_name = c->opt_workgroup; c->opt_target_workgroup = c->opt_workgroup; } else { fstrcpy(pdc_name, lp_netbios_name()); domain_name = talloc_strdup(mem_ctx, lp_workgroup()); c->opt_target_workgroup = domain_name; }; /* open \PIPE\lsarpc and open policy handle */ nt_status = net_make_ipc_connection(c, NET_FLAGS_PDC, &cli); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Couldn't connect to domain controller: %s\n", nt_errstr(nt_status))); talloc_destroy(mem_ctx); return -1; }; nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &pipe_hnd); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Could not initialise lsa pipe. Error was %s\n", nt_errstr(nt_status) )); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; b = pipe_hnd->binding_handle; nt_status = dcerpc_lsa_open_policy_fallback(b, mem_ctx, pipe_hnd->srv_name_slash, true, KEY_QUERY_VALUE, &out_version, &out_revision_info, &connect_hnd, &result); if (any_nt_status_not_ok(nt_status, result, &nt_status)) { DBG_ERR("Couldn't open policy handle: %s\n", nt_errstr(nt_status)); cli_shutdown(cli); talloc_free(mem_ctx); return -1; } /* query info level 5 to obtain sid of a domain being queried */ nt_status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, &connect_hnd, LSA_POLICY_INFO_ACCOUNT_DOMAIN, &info, &result); if (NT_STATUS_IS_ERR(nt_status)) { DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", nt_errstr(nt_status))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; } if (NT_STATUS_IS_ERR(result)) { DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", nt_errstr(result))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; } queried_dom_sid = info->account_domain.sid; /* * Keep calling LsaEnumTrustdom over opened pipe until * the end of enumeration is reached */ d_printf(_("Trusted domains list:\n\n")); found_domain = false; do { nt_status = dcerpc_lsa_EnumTrustDom(b, mem_ctx, &connect_hnd, &enum_ctx, &dom_list, (uint32_t)-1, &result); if (NT_STATUS_IS_ERR(nt_status)) { DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n", nt_errstr(nt_status))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; if (NT_STATUS_IS_ERR(result)) { DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n", nt_errstr(result))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; for (i = 0; i < dom_list.count; i++) { print_trusted_domain(dom_list.domains[i].sid, dom_list.domains[i].name.string); found_domain = true; }; } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES)); /* * in case of no trusted domains say something rather * than just display blank line */ if (!found_domain) { d_printf(_("none\n")); } /* close this connection before doing next one */ nt_status = dcerpc_lsa_Close(b, mem_ctx, &connect_hnd, &result); if (NT_STATUS_IS_ERR(nt_status)) { DEBUG(0, ("Couldn't properly close lsa policy handle. Error was %s\n", nt_errstr(nt_status))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; TALLOC_FREE(pipe_hnd); /* * Listing trusting domains (stored in passdb backend, if local) */ d_printf(_("\nTrusting domains list:\n\n")); /* * Open \PIPE\samr and get needed policy handles */ nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr, &pipe_hnd); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Could not initialise samr pipe. Error was %s\n", nt_errstr(nt_status))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; b = pipe_hnd->binding_handle; /* SamrConnect2 */ nt_status = dcerpc_samr_Connect2(b, mem_ctx, pipe_hnd->desthost, SAMR_ACCESS_LOOKUP_DOMAIN, &connect_hnd, &result); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Couldn't open SAMR policy handle. Error was %s\n", nt_errstr(nt_status))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; if (!NT_STATUS_IS_OK(result)) { nt_status = result; DEBUG(0, ("Couldn't open SAMR policy handle. Error was %s\n", nt_errstr(result))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; /* SamrOpenDomain - we have to open domain policy handle in order to be able to enumerate accounts*/ nt_status = dcerpc_samr_OpenDomain(b, mem_ctx, &connect_hnd, SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS, queried_dom_sid, &domain_hnd, &result); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Couldn't open domain object. Error was %s\n", nt_errstr(nt_status))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; if (!NT_STATUS_IS_OK(result)) { nt_status = result; DEBUG(0, ("Couldn't open domain object. Error was %s\n", nt_errstr(result))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; /* * perform actual enumeration */ found_domain = false; enum_ctx = 0; /* reset enumeration context from last enumeration */ do { nt_status = dcerpc_samr_EnumDomainUsers(b, mem_ctx, &domain_hnd, &enum_ctx, ACB_DOMTRUST, &trusts, 0xffff, &num_domains, &result); if (NT_STATUS_IS_ERR(nt_status)) { DEBUG(0, ("Couldn't enumerate accounts. Error was: %s\n", nt_errstr(nt_status))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; if (NT_STATUS_IS_ERR(result)) { nt_status = result; DEBUG(0, ("Couldn't enumerate accounts. Error was: %s\n", nt_errstr(result))); cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; }; for (i = 0; i < num_domains; i++) { char *str = discard_const_p(char, trusts->entries[i].name.string); found_domain = true; /* * get each single domain's sid (do we _really_ need this ?): * 1) connect to domain's pdc * 2) query the pdc for domain's sid */ /* get rid of '$' tail */ ascii_dom_name_len = strlen(str); if (ascii_dom_name_len && ascii_dom_name_len < FSTRING_LEN) str[ascii_dom_name_len - 1] = '\0'; /* set opt_* variables to remote domain */ if (!strupper_m(str)) { cli_shutdown(cli); talloc_destroy(mem_ctx); return -1; } c->opt_workgroup = talloc_strdup(mem_ctx, str); c->opt_target_workgroup = c->opt_workgroup; d_printf("%-20s", str); /* connect to remote domain controller */ nt_status = net_make_ipc_connection(c, NET_FLAGS_PDC | NET_FLAGS_ANONYMOUS, &remote_cli); if (NT_STATUS_IS_OK(nt_status)) { /* query for domain's sid */ if (run_rpc_command( c, remote_cli, &ndr_table_lsarpc, 0, rpc_query_domain_sid, argc, argv)) d_printf(_("strange - couldn't get domain's sid\n")); cli_shutdown(remote_cli); } else { d_fprintf(stderr, _("domain controller is not " "responding: %s\n"), nt_errstr(nt_status)); d_printf(_("couldn't get domain's sid\n")); } } } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); if (!found_domain) { d_printf("none\n"); } /* close opened samr and domain policy handles */ nt_status = dcerpc_samr_Close(b, mem_ctx, &domain_hnd, &result); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Couldn't properly close domain policy handle for domain %s\n", domain_name)); }; nt_status = dcerpc_samr_Close(b, mem_ctx, &connect_hnd, &result); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Couldn't properly close samr policy handle for domain %s\n", domain_name)); }; /* close samr pipe and connection to IPC$ */ cli_shutdown(cli); talloc_destroy(mem_ctx); return 0; } /** * Entrypoint for 'net rpc trustdom' code. * * @param argc Standard argc. * @param argv Standard argv without initial components. * * @return Integer status (0 means success). */ static int rpc_trustdom(struct net_context *c, int argc, const char **argv) { struct functable func[] = { { "add", rpc_trustdom_add, NET_TRANSPORT_RPC, N_("Add trusting domain's account"), N_("net rpc trustdom add\n" " Add trusting domain's account") }, { "del", rpc_trustdom_del, NET_TRANSPORT_RPC, N_("Remove trusting domain's account"), N_("net rpc trustdom del\n" " Remove trusting domain's account") }, { "establish", rpc_trustdom_establish, NET_TRANSPORT_RPC, N_("Establish outgoing trust relationship"), N_("net rpc trustdom establish\n" " Establish outgoing trust relationship") }, { "revoke", rpc_trustdom_revoke, NET_TRANSPORT_RPC, N_("Revoke outgoing trust relationship"), N_("net rpc trustdom revoke\n" " Revoke outgoing trust relationship") }, { "list", rpc_trustdom_list, NET_TRANSPORT_RPC, N_("List in- and outgoing domain trusts"), N_("net rpc trustdom list\n" " List in- and outgoing domain trusts") }, { "vampire", rpc_trustdom_vampire, NET_TRANSPORT_RPC, N_("Vampire trusts from remote server"), N_("net rpc trustdom vampire\n" " Vampire trusts from remote server") }, {NULL, NULL, 0, NULL, NULL} }; return net_run_function(c, argc, argv, "net rpc trustdom", func); } /** * Check if a server will take rpc commands * @param flags Type of server to connect to (PDC, DMB, localhost) * if the host is not explicitly specified * @return bool (true means rpc supported) */ bool net_rpc_check(struct net_context *c, unsigned flags) { struct cli_state *cli; bool ret = false; struct sockaddr_storage server_ss; char *server_name = NULL; NTSTATUS status; /* flags (i.e. server type) may depend on command */ if (!net_find_server(c, NULL, flags, &server_ss, &server_name)) return false; status = cli_connect_nb(server_name, &server_ss, 0, 0x20, lp_netbios_name(), SMB_SIGNING_IPC_DEFAULT, 0, &cli); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { DBG_ERR("NetBIOS support disabled, unable to connect\n"); } return false; } status = smbXcli_negprot(cli->conn, cli->timeout, lp_client_min_protocol(), lp_client_max_protocol(), NULL, NULL, NULL); if (!NT_STATUS_IS_OK(status)) goto done; if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_NT1) goto done; ret = true; done: cli_shutdown(cli); return ret; } /* synchronise sam database via samsync rpc calls */ static int rpc_vampire(struct net_context *c, int argc, const char **argv) { struct functable func[] = { { "keytab", rpc_vampire_keytab, NET_TRANSPORT_RPC, N_("Dump remote SAM database to Kerberos Keytab"), N_("net rpc vampire keytab\n" " Dump remote SAM database to Kerberos keytab " "file") }, { "passdb", rpc_vampire_passdb, NET_TRANSPORT_RPC, N_("Dump remote SAM database to passdb"), N_("net rpc vampire passdb\n" " Dump remote SAM database to passdb") }, {NULL, NULL, 0, NULL, NULL} }; if (argc == 0) { if (c->display_usage) { d_printf( "%s\n" "net rpc vampire\n" " %s\n", _("Usage:"), _("Vampire remote SAM database")); return 0; } return rpc_vampire_passdb(c, argc, argv); } return net_run_function(c, argc, argv, "net rpc vampire", func); } /** * Migrate everything from a print server. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). * * The order is important ! * To successfully add drivers the print queues have to exist ! * Applying ACLs should be the last step, because you're easily locked out. * **/ static int rpc_printer_migrate_all(struct net_context *c, int argc, const char **argv) { int ret; if (c->display_usage) { d_printf( "%s\n" "net rpc printer migrate all\n" " %s\n", _("Usage:"), _("Migrate everything from a print server")); return 0; } if (!c->opt_host) { d_printf(_("no server to migrate\n")); return -1; } ret = run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_migrate_printers_internals, argc, argv); if (ret) return ret; ret = run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_migrate_drivers_internals, argc, argv); if (ret) return ret; ret = run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_migrate_forms_internals, argc, argv); if (ret) return ret; ret = run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_migrate_settings_internals, argc, argv); if (ret) return ret; return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_migrate_security_internals, argc, argv); } /** * Migrate print drivers from a print server. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_printer_migrate_drivers(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc printer migrate drivers\n" " %s\n", _("Usage:"), _("Migrate print-drivers from a print-server")); return 0; } if (!c->opt_host) { d_printf(_("no server to migrate\n")); return -1; } return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_migrate_drivers_internals, argc, argv); } /** * Migrate print-forms from a print-server. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_printer_migrate_forms(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc printer migrate forms\n" " %s\n", _("Usage:"), _("Migrate print-forms from a print-server")); return 0; } if (!c->opt_host) { d_printf(_("no server to migrate\n")); return -1; } return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_migrate_forms_internals, argc, argv); } /** * Migrate printers from a print-server. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_printer_migrate_printers(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc printer migrate printers\n" " %s\n", _("Usage:"), _("Migrate printers from a print-server")); return 0; } if (!c->opt_host) { d_printf(_("no server to migrate\n")); return -1; } return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_migrate_printers_internals, argc, argv); } /** * Migrate printer-ACLs from a print-server * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_printer_migrate_security(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc printer migrate security\n" " %s\n", _("Usage:"), _("Migrate printer-ACLs from a print-server")); return 0; } if (!c->opt_host) { d_printf(_("no server to migrate\n")); return -1; } return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_migrate_security_internals, argc, argv); } /** * Migrate printer-settings from a print-server. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_printer_migrate_settings(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc printer migrate settings\n" " %s\n", _("Usage:"), _("Migrate printer-settings from a " "print-server")); return 0; } if (!c->opt_host) { d_printf(_("no server to migrate\n")); return -1; } return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_migrate_settings_internals, argc, argv); } /** * 'net rpc printer' entrypoint. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. **/ int rpc_printer_migrate(struct net_context *c, int argc, const char **argv) { /* ouch: when addriver and setdriver are called from within rpc_printer_migrate_drivers_internals, the printer-queue already *has* to exist */ struct functable func[] = { { "all", rpc_printer_migrate_all, NET_TRANSPORT_RPC, N_("Migrate all from remote to local print server"), N_("net rpc printer migrate all\n" " Migrate all from remote to local print server") }, { "drivers", rpc_printer_migrate_drivers, NET_TRANSPORT_RPC, N_("Migrate drivers to local server"), N_("net rpc printer migrate drivers\n" " Migrate drivers to local server") }, { "forms", rpc_printer_migrate_forms, NET_TRANSPORT_RPC, N_("Migrate forms to local server"), N_("net rpc printer migrate forms\n" " Migrate forms to local server") }, { "printers", rpc_printer_migrate_printers, NET_TRANSPORT_RPC, N_("Migrate printers to local server"), N_("net rpc printer migrate printers\n" " Migrate printers to local server") }, { "security", rpc_printer_migrate_security, NET_TRANSPORT_RPC, N_("Migrate printer ACLs to local server"), N_("net rpc printer migrate security\n" " Migrate printer ACLs to local server") }, { "settings", rpc_printer_migrate_settings, NET_TRANSPORT_RPC, N_("Migrate printer settings to local server"), N_("net rpc printer migrate settings\n" " Migrate printer settings to local server") }, {NULL, NULL, 0, NULL, NULL} }; return net_run_function(c, argc, argv, "net rpc printer migrate",func); } /** * List printers on a remote RPC server. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_printer_list(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc printer list\n" " %s\n", _("Usage:"), _("List printers on a remote RPC server")); return 0; } return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_list_internals, argc, argv); } /** * List printer-drivers on a remote RPC server. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_printer_driver_list(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc printer driver\n" " %s\n", _("Usage:"), _("List printer-drivers on a remote RPC server")); return 0; } return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_driver_list_internals, argc, argv); } /** * Publish printer in ADS via MSRPC. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_printer_publish_publish(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc printer publish publish\n" " %s\n", _("Usage:"), _("Publish printer in ADS via MSRPC")); return 0; } return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_publish_publish_internals, argc, argv); } /** * Update printer in ADS via MSRPC. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_printer_publish_update(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc printer publish update\n" " %s\n", _("Usage:"), _("Update printer in ADS via MSRPC")); return 0; } return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_publish_update_internals, argc, argv); } /** * UnPublish printer in ADS via MSRPC. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_printer_publish_unpublish(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc printer publish unpublish\n" " %s\n", _("Usage:\n"), _("UnPublish printer in ADS via MSRPC")); return 0; } return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_publish_unpublish_internals, argc, argv); } /** * List published printers via MSRPC. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_printer_publish_list(struct net_context *c, int argc, const char **argv) { if (c->display_usage) { d_printf( "%s\n" "net rpc printer publish list\n" " %s\n", _("Usage:"), _("List published printers via MSRPC")); return 0; } return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_publish_list_internals, argc, argv); } /** * Publish printer in ADS. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. * * @return A shell status integer (0 for success). **/ static int rpc_printer_publish(struct net_context *c, int argc, const char **argv) { struct functable func[] = { { "publish", rpc_printer_publish_publish, NET_TRANSPORT_RPC, N_("Publish printer in AD"), N_("net rpc printer publish publish\n" " Publish printer in AD") }, { "update", rpc_printer_publish_update, NET_TRANSPORT_RPC, N_("Update printer in AD"), N_("net rpc printer publish update\n" " Update printer in AD") }, { "unpublish", rpc_printer_publish_unpublish, NET_TRANSPORT_RPC, N_("Unpublish printer"), N_("net rpc printer publish unpublish\n" " Unpublish printer") }, { "list", rpc_printer_publish_list, NET_TRANSPORT_RPC, N_("List published printers"), N_("net rpc printer publish list\n" " List published printers") }, {NULL, NULL, 0, NULL, NULL} }; if (argc == 0) { if (c->display_usage) { d_printf(_("Usage:\n")); d_printf(_("net rpc printer publish\n" " List published printers\n" " Alias of net rpc printer publish " "list\n")); net_display_usage_from_functable(func); return 0; } return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_publish_list_internals, argc, argv); } return net_run_function(c, argc, argv, "net rpc printer publish",func); } /** * Display rpc printer help page. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. **/ int rpc_printer_usage(struct net_context *c, int argc, const char **argv) { d_printf(_("net rpc printer LIST [printer] [misc. options] [targets]\n" "\tlists all printers on print-server\n\n")); d_printf(_("net rpc printer DRIVER [printer] [misc. options] [targets]\n" "\tlists all printer-drivers on print-server\n\n")); d_printf(_("net rpc printer PUBLISH action [printer] [misc. options] [targets]\n" "\tpublishes printer settings in Active Directory\n" "\taction can be one of PUBLISH, UPDATE, UNPUBLISH or LIST\n\n")); d_printf(_("net rpc printer MIGRATE PRINTERS [printer] [misc. options] [targets]" "\n\tmigrates printers from remote to local server\n\n")); d_printf(_("net rpc printer MIGRATE SETTINGS [printer] [misc. options] [targets]" "\n\tmigrates printer-settings from remote to local server\n\n")); d_printf(_("net rpc printer MIGRATE DRIVERS [printer] [misc. options] [targets]" "\n\tmigrates printer-drivers from remote to local server\n\n")); d_printf(_("net rpc printer MIGRATE FORMS [printer] [misc. options] [targets]" "\n\tmigrates printer-forms from remote to local server\n\n")); d_printf(_("net rpc printer MIGRATE SECURITY [printer] [misc. options] [targets]" "\n\tmigrates printer-ACLs from remote to local server\n\n")); d_printf(_("net rpc printer MIGRATE ALL [printer] [misc. options] [targets]" "\n\tmigrates drivers, forms, queues, settings and acls from\n" "\tremote to local print-server\n\n")); net_common_methods_usage(c, argc, argv); net_common_flags_usage(c, argc, argv); d_printf(_( "\t-v or --verbose\t\t\tgive verbose output\n" "\t --destination\t\tmigration target server (default: localhost)\n")); return -1; } /** * 'net rpc printer' entrypoint. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. **/ int net_rpc_printer(struct net_context *c, int argc, const char **argv) { struct functable func[] = { { "list", rpc_printer_list, NET_TRANSPORT_RPC, N_("List all printers on print server"), N_("net rpc printer list\n" " List all printers on print server") }, { "migrate", rpc_printer_migrate, NET_TRANSPORT_RPC, N_("Migrate printer to local server"), N_("net rpc printer migrate\n" " Migrate printer to local server") }, { "driver", rpc_printer_driver_list, NET_TRANSPORT_RPC, N_("List printer drivers"), N_("net rpc printer driver\n" " List printer drivers") }, { "publish", rpc_printer_publish, NET_TRANSPORT_RPC, N_("Publish printer in AD"), N_("net rpc printer publish\n" " Publish printer in AD") }, {NULL, NULL, 0, NULL, NULL} }; if (argc == 0) { if (c->display_usage) { d_printf(_("Usage:\n")); d_printf(_("net rpc printer\n" " List printers\n")); net_display_usage_from_functable(func); return 0; } return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, rpc_printer_list_internals, argc, argv); } return net_run_function(c, argc, argv, "net rpc printer", func); } /** * 'net rpc' entrypoint. * * @param c A net_context structure. * @param argc Standard main() style argc. * @param argv Standard main() style argv. Initial components are already * stripped. **/ int net_rpc(struct net_context *c, int argc, const char **argv) { NET_API_STATUS status; struct functable func[] = { { "audit", net_rpc_audit, NET_TRANSPORT_RPC, N_("Modify global audit settings"), N_("net rpc audit\n" " Modify global audit settings") }, { "info", net_rpc_info, NET_TRANSPORT_RPC, N_("Show basic info about a domain"), N_("net rpc info\n" " Show basic info about a domain") }, { "join", net_rpc_join, NET_TRANSPORT_RPC, N_("Join a domain"), N_("net rpc join\n" " Join a domain") }, { "oldjoin", net_rpc_oldjoin, NET_TRANSPORT_RPC, N_("Join a domain created in server manager"), N_("net rpc oldjoin\n" " Join a domain created in server manager") }, { "testjoin", net_rpc_testjoin, NET_TRANSPORT_RPC, N_("Test that a join is valid"), N_("net rpc testjoin\n" " Test that a join is valid") }, { "user", net_rpc_user, NET_TRANSPORT_RPC, N_("List/modify users"), N_("net rpc user\n" " List/modify users") }, { "password", rpc_user_password, NET_TRANSPORT_RPC, N_("Change a user password"), N_("net rpc password\n" " Change a user password\n" " Alias for net rpc user password") }, { "group", net_rpc_group, NET_TRANSPORT_RPC, N_("List/modify groups"), N_("net rpc group\n" " List/modify groups") }, { "share", net_rpc_share, NET_TRANSPORT_RPC, N_("List/modify shares"), N_("net rpc share\n" " List/modify shares") }, { "file", net_rpc_file, NET_TRANSPORT_RPC, N_("List open files"), N_("net rpc file\n" " List open files") }, { "printer", net_rpc_printer, NET_TRANSPORT_RPC, N_("List/modify printers"), N_("net rpc printer\n" " List/modify printers") }, { "changetrustpw", net_rpc_changetrustpw, NET_TRANSPORT_RPC, N_("Change trust account password"), N_("net rpc changetrustpw\n" " Change trust account password") }, { "trustdom", rpc_trustdom, NET_TRANSPORT_RPC, N_("Modify domain trusts"), N_("net rpc trustdom\n" " Modify domain trusts") }, { "abortshutdown", rpc_shutdown_abort, NET_TRANSPORT_RPC, N_("Abort a remote shutdown"), N_("net rpc abortshutdown\n" " Abort a remote shutdown") }, { "shutdown", rpc_shutdown, NET_TRANSPORT_RPC, N_("Shutdown a remote server"), N_("net rpc shutdown\n" " Shutdown a remote server") }, { "vampire", rpc_vampire, NET_TRANSPORT_RPC, N_("Sync a remote NT PDC's data into local passdb"), N_("net rpc vampire\n" " Sync a remote NT PDC's data into local passdb") }, { "getsid", net_rpc_getsid, NET_TRANSPORT_RPC, N_("Fetch the domain sid into local secrets.tdb"), N_("net rpc getsid\n" " Fetch the domain sid into local secrets.tdb") }, { "rights", net_rpc_rights, NET_TRANSPORT_RPC, N_("Manage privileges assigned to SID"), N_("net rpc rights\n" " Manage privileges assigned to SID") }, { "service", net_rpc_service, NET_TRANSPORT_RPC, N_("Start/stop/query remote services"), N_("net rpc service\n" " Start/stop/query remote services") }, { "registry", net_rpc_registry, NET_TRANSPORT_RPC, N_("Manage registry hives"), N_("net rpc registry\n" " Manage registry hives") }, { "shell", net_rpc_shell, NET_TRANSPORT_RPC, N_("Open interactive shell on remote server"), N_("net rpc shell\n" " Open interactive shell on remote server") }, { "trust", net_rpc_trust, NET_TRANSPORT_RPC, N_("Manage trusts"), N_("net rpc trust\n" " Manage trusts") }, { "conf", net_rpc_conf, NET_TRANSPORT_RPC, N_("Configure a remote samba server"), N_("net rpc conf\n" " Configure a remote samba server") }, {NULL, NULL, 0, NULL, NULL} }; status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds); if (status != 0) { return -1; } return net_run_function(c, argc, argv, "net rpc", func); }