/* * Unix SMB/CIFS implementation. * NetApi Join Support * Copyright (C) Guenther Deschner 2007-2008 * * 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 "lib/netapi/netapi.h" #include "libnet/libnet.h" /**************************************************************** ****************************************************************/ static WERROR NetJoinDomainLocal(struct libnetapi_ctx *mem_ctx, const char *server_name, const char *domain_name, const char *account_ou, const char *Account, const char *password, uint32_t join_flags) { struct libnet_JoinCtx *r = NULL; WERROR werr; if (!domain_name) { return WERR_INVALID_PARAM; } werr = libnet_init_JoinCtx(mem_ctx, &r); W_ERROR_NOT_OK_RETURN(werr); r->in.domain_name = talloc_strdup(mem_ctx, domain_name); W_ERROR_HAVE_NO_MEMORY(r->in.domain_name); if (join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) { NTSTATUS status; struct DS_DOMAIN_CONTROLLER_INFO *info = NULL; uint32_t flags = DS_DIRECTORY_SERVICE_REQUIRED | DS_WRITABLE_REQUIRED | DS_RETURN_DNS_NAME; status = dsgetdcname(mem_ctx, domain_name, NULL, NULL, flags, &info); if (!NT_STATUS_IS_OK(status)) { libnetapi_set_error_string(mem_ctx, "%s", get_friendly_nt_error_msg(status)); return ntstatus_to_werror(status); } r->in.dc_name = talloc_strdup(mem_ctx, info->domain_controller_name); W_ERROR_HAVE_NO_MEMORY(r->in.dc_name); } if (account_ou) { r->in.account_ou = talloc_strdup(mem_ctx, account_ou); W_ERROR_HAVE_NO_MEMORY(r->in.account_ou); } if (Account) { r->in.admin_account = talloc_strdup(mem_ctx, Account); W_ERROR_HAVE_NO_MEMORY(r->in.admin_account); } if (password) { r->in.admin_password = talloc_strdup(mem_ctx, password); W_ERROR_HAVE_NO_MEMORY(r->in.admin_password); } r->in.join_flags = join_flags; r->in.modify_config = true; werr = libnet_Join(mem_ctx, r); if (!W_ERROR_IS_OK(werr) && r->out.error_string) { libnetapi_set_error_string(mem_ctx, "%s", r->out.error_string); } TALLOC_FREE(r); return werr; } /**************************************************************** ****************************************************************/ static WERROR NetJoinDomainRemote(struct libnetapi_ctx *ctx, const char *server_name, const char *domain_name, const char *account_ou, const char *Account, const char *password, uint32_t join_flags) { struct cli_state *cli = NULL; struct rpc_pipe_client *pipe_cli = NULL; struct wkssvc_PasswordBuffer *encrypted_password = NULL; NTSTATUS status; WERROR werr; unsigned int old_timeout = 0; status = cli_full_connection(&cli, NULL, server_name, NULL, 0, "IPC$", "IPC", ctx->username, ctx->workgroup, ctx->password, 0, Undefined, NULL); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto done; } pipe_cli = cli_rpc_pipe_open_noauth(cli, PI_WKSSVC, &status); if (!pipe_cli) { werr = ntstatus_to_werror(status); goto done; } if (password) { encode_wkssvc_join_password_buffer(ctx, password, &cli->user_session_key, &encrypted_password); } old_timeout = cli_set_timeout(cli, 60000); status = rpccli_wkssvc_NetrJoinDomain2(pipe_cli, ctx, server_name, domain_name, account_ou, Account, encrypted_password, join_flags, &werr); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto done; } done: if (cli) { cli_set_timeout(cli, old_timeout); cli_shutdown(cli); } return werr; } /**************************************************************** ****************************************************************/ static WERROR libnetapi_NetJoinDomain(struct libnetapi_ctx *ctx, const char *server_name, const char *domain_name, const char *account_ou, const char *Account, const char *password, uint32_t join_flags) { if (!domain_name) { return WERR_INVALID_PARAM; } if (!server_name || is_myname_or_ipaddr(server_name)) { return NetJoinDomainLocal(ctx, server_name, domain_name, account_ou, Account, password, join_flags); } return NetJoinDomainRemote(ctx, server_name, domain_name, account_ou, Account, password, join_flags); } /**************************************************************** NetJoinDomain ****************************************************************/ NET_API_STATUS NetJoinDomain(const char *server_name, const char *domain_name, const char *account_ou, const char *Account, const char *password, uint32_t join_flags) { struct libnetapi_ctx *ctx = NULL; NET_API_STATUS status; WERROR werr; status = libnetapi_getctx(&ctx); if (status != 0) { return status; } werr = libnetapi_NetJoinDomain(ctx, server_name, domain_name, account_ou, Account, password, join_flags); if (!W_ERROR_IS_OK(werr)) { return W_ERROR_V(werr); } return NET_API_STATUS_SUCCESS; } /**************************************************************** ****************************************************************/ static WERROR NetUnjoinDomainLocal(struct libnetapi_ctx *mem_ctx, const char *server_name, const char *account, const char *password, uint32_t unjoin_flags) { struct libnet_UnjoinCtx *r = NULL; struct dom_sid domain_sid; WERROR werr; if (!secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) { return WERR_SETUP_NOT_JOINED; } werr = libnet_init_UnjoinCtx(mem_ctx, &r); W_ERROR_NOT_OK_RETURN(werr); if (server_name) { r->in.dc_name = talloc_strdup(mem_ctx, server_name); W_ERROR_HAVE_NO_MEMORY(r->in.dc_name); } else { NTSTATUS status; const char *domain = NULL; struct DS_DOMAIN_CONTROLLER_INFO *info = NULL; uint32_t flags = DS_DIRECTORY_SERVICE_REQUIRED | DS_WRITABLE_REQUIRED | DS_RETURN_DNS_NAME; if (lp_realm()) { domain = lp_realm(); } else { domain = lp_workgroup(); } status = dsgetdcname(mem_ctx, domain, NULL, NULL, flags, &info); if (!NT_STATUS_IS_OK(status)) { libnetapi_set_error_string(mem_ctx, "%s", get_friendly_nt_error_msg(status)); return ntstatus_to_werror(status); } r->in.dc_name = talloc_strdup(mem_ctx, info->domain_controller_name); W_ERROR_HAVE_NO_MEMORY(r->in.dc_name); } if (account) { r->in.admin_account = talloc_strdup(mem_ctx, account); W_ERROR_HAVE_NO_MEMORY(r->in.admin_account); } if (password) { r->in.admin_password = talloc_strdup(mem_ctx, password); W_ERROR_HAVE_NO_MEMORY(r->in.admin_password); } r->in.unjoin_flags = unjoin_flags; r->in.modify_config = true; r->in.debug = true; r->in.domain_sid = &domain_sid; werr = libnet_Unjoin(mem_ctx, r); if (!W_ERROR_IS_OK(werr) && r->out.error_string) { libnetapi_set_error_string(mem_ctx, "%s", r->out.error_string); } TALLOC_FREE(r); return werr; } /**************************************************************** ****************************************************************/ static WERROR NetUnjoinDomainRemote(struct libnetapi_ctx *ctx, const char *server_name, const char *account, const char *password, uint32_t unjoin_flags) { struct cli_state *cli = NULL; struct rpc_pipe_client *pipe_cli = NULL; struct wkssvc_PasswordBuffer *encrypted_password = NULL; NTSTATUS status; WERROR werr; unsigned int old_timeout = 0; status = cli_full_connection(&cli, NULL, server_name, NULL, 0, "IPC$", "IPC", ctx->username, ctx->workgroup, ctx->password, 0, Undefined, NULL); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto done; } pipe_cli = cli_rpc_pipe_open_noauth(cli, PI_WKSSVC, &status); if (!pipe_cli) { werr = ntstatus_to_werror(status); goto done; } if (password) { encode_wkssvc_join_password_buffer(ctx, password, &cli->user_session_key, &encrypted_password); } old_timeout = cli_set_timeout(cli, 60000); status = rpccli_wkssvc_NetrUnjoinDomain2(pipe_cli, ctx, server_name, account, encrypted_password, unjoin_flags, &werr); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto done; } done: if (cli) { cli_set_timeout(cli, old_timeout); cli_shutdown(cli); } return werr; } /**************************************************************** ****************************************************************/ static WERROR libnetapi_NetUnjoinDomain(struct libnetapi_ctx *ctx, const char *server_name, const char *account, const char *password, uint32_t unjoin_flags) { if (!server_name || is_myname_or_ipaddr(server_name)) { return NetUnjoinDomainLocal(ctx, server_name, account, password, unjoin_flags); } return NetUnjoinDomainRemote(ctx, server_name, account, password, unjoin_flags); } /**************************************************************** NetUnjoinDomain ****************************************************************/ NET_API_STATUS NetUnjoinDomain(const char *server_name, const char *account, const char *password, uint32_t unjoin_flags) { struct libnetapi_ctx *ctx = NULL; NET_API_STATUS status; WERROR werr; status = libnetapi_getctx(&ctx); if (status != 0) { return status; } werr = libnetapi_NetUnjoinDomain(ctx, server_name, account, password, unjoin_flags); if (!W_ERROR_IS_OK(werr)) { return W_ERROR_V(werr); } return NET_API_STATUS_SUCCESS; } /**************************************************************** ****************************************************************/ static WERROR NetGetJoinInformationRemote(struct libnetapi_ctx *ctx, const char *server_name, const char **name_buffer, uint16_t *name_type) { struct cli_state *cli = NULL; struct rpc_pipe_client *pipe_cli = NULL; NTSTATUS status; WERROR werr; status = cli_full_connection(&cli, NULL, server_name, NULL, 0, "IPC$", "IPC", ctx->username, ctx->workgroup, ctx->password, 0, Undefined, NULL); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto done; } pipe_cli = cli_rpc_pipe_open_noauth(cli, PI_WKSSVC, &status); if (!pipe_cli) { werr = ntstatus_to_werror(status); goto done; } status = rpccli_wkssvc_NetrGetJoinInformation(pipe_cli, ctx, server_name, name_buffer, (enum wkssvc_NetJoinStatus *)name_type, &werr); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto done; } done: if (cli) { cli_shutdown(cli); } return werr; } /**************************************************************** ****************************************************************/ static WERROR NetGetJoinInformationLocal(struct libnetapi_ctx *ctx, const char *server_name, const char **name_buffer, uint16_t *name_type) { if ((lp_security() == SEC_ADS) && lp_realm()) { *name_buffer = talloc_strdup(ctx, lp_realm()); } else { *name_buffer = talloc_strdup(ctx, lp_workgroup()); } if (!*name_buffer) { return WERR_NOMEM; } switch (lp_server_role()) { case ROLE_DOMAIN_MEMBER: case ROLE_DOMAIN_PDC: case ROLE_DOMAIN_BDC: *name_type = NetSetupDomainName; break; case ROLE_STANDALONE: default: *name_type = NetSetupWorkgroupName; break; } return WERR_OK; } static WERROR libnetapi_NetGetJoinInformation(struct libnetapi_ctx *ctx, const char *server_name, const char **name_buffer, uint16_t *name_type) { if (!server_name || is_myname_or_ipaddr(server_name)) { return NetGetJoinInformationLocal(ctx, server_name, name_buffer, name_type); } return NetGetJoinInformationRemote(ctx, server_name, name_buffer, name_type); } /**************************************************************** NetGetJoinInformation ****************************************************************/ NET_API_STATUS NetGetJoinInformation(const char *server_name, const char **name_buffer, uint16_t *name_type) { struct libnetapi_ctx *ctx = NULL; NET_API_STATUS status; WERROR werr; status = libnetapi_getctx(&ctx); if (status != 0) { return status; } werr = libnetapi_NetGetJoinInformation(ctx, server_name, name_buffer, name_type); if (!W_ERROR_IS_OK(werr)) { return W_ERROR_V(werr); } return NET_API_STATUS_SUCCESS; } /**************************************************************** ****************************************************************/ static WERROR NetGetJoinableOUsLocal(struct libnetapi_ctx *ctx, const char *server_name, const char *domain, const char *account, const char *password, uint32_t *ou_count, const char ***ous) { #ifdef WITH_ADS NTSTATUS status; ADS_STATUS ads_status; ADS_STRUCT *ads = NULL; struct DS_DOMAIN_CONTROLLER_INFO *info = NULL; uint32_t flags = DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME; status = dsgetdcname(ctx, domain, NULL, NULL, flags, &info); if (!NT_STATUS_IS_OK(status)) { libnetapi_set_error_string(ctx, "%s", get_friendly_nt_error_msg(status)); return ntstatus_to_werror(status); } ads = ads_init(domain, domain, info->domain_controller_name); if (!ads) { return WERR_GENERAL_FAILURE; } SAFE_FREE(ads->auth.user_name); if (account) { ads->auth.user_name = SMB_STRDUP(account); } else if (ctx->username) { ads->auth.user_name = SMB_STRDUP(ctx->username); } SAFE_FREE(ads->auth.password); if (password) { ads->auth.password = SMB_STRDUP(password); } else if (ctx->password) { ads->auth.password = SMB_STRDUP(ctx->password); } ads_status = ads_connect(ads); if (!ADS_ERR_OK(ads_status)) { ads_destroy(&ads); return WERR_DEFAULT_JOIN_REQUIRED; } ads_status = ads_get_joinable_ous(ads, ctx, (char ***)ous, (size_t *)ou_count); if (!ADS_ERR_OK(ads_status)) { ads_destroy(&ads); return WERR_DEFAULT_JOIN_REQUIRED; } ads_destroy(&ads); return WERR_OK; #else return WERR_NOT_SUPPORTED; #endif } /**************************************************************** ****************************************************************/ static WERROR NetGetJoinableOUsRemote(struct libnetapi_ctx *ctx, const char *server_name, const char *domain, const char *account, const char *password, uint32_t *ou_count, const char ***ous) { struct cli_state *cli = NULL; struct rpc_pipe_client *pipe_cli = NULL; struct wkssvc_PasswordBuffer *encrypted_password = NULL; NTSTATUS status; WERROR werr; status = cli_full_connection(&cli, NULL, server_name, NULL, 0, "IPC$", "IPC", ctx->username, ctx->workgroup, ctx->password, 0, Undefined, NULL); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto done; } pipe_cli = cli_rpc_pipe_open_noauth(cli, PI_WKSSVC, &status); if (!pipe_cli) { werr = ntstatus_to_werror(status); goto done; } if (password) { encode_wkssvc_join_password_buffer(ctx, password, &cli->user_session_key, &encrypted_password); } status = rpccli_wkssvc_NetrGetJoinableOus2(pipe_cli, ctx, server_name, domain, account, encrypted_password, ou_count, ous, &werr); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto done; } done: if (cli) { cli_shutdown(cli); } return werr; } /**************************************************************** ****************************************************************/ static WERROR libnetapi_NetGetJoinableOUs(struct libnetapi_ctx *ctx, const char *server_name, const char *domain, const char *account, const char *password, uint32_t *ou_count, const char ***ous) { if (!server_name || is_myname_or_ipaddr(server_name)) { return NetGetJoinableOUsLocal(ctx, server_name, domain, account, password, ou_count, ous); } return NetGetJoinableOUsRemote(ctx, server_name, domain, account, password, ou_count, ous); } /**************************************************************** NetGetJoinableOUs ****************************************************************/ NET_API_STATUS NetGetJoinableOUs(const char *server_name, const char *domain, const char *account, const char *password, uint32_t *ou_count, const char ***ous) { struct libnetapi_ctx *ctx = NULL; NET_API_STATUS status; WERROR werr; status = libnetapi_getctx(&ctx); if (status != 0) { return status; } werr = libnetapi_NetGetJoinableOUs(ctx, server_name, domain, account, password, ou_count, ous); if (!W_ERROR_IS_OK(werr)) { return W_ERROR_V(werr); } return NET_API_STATUS_SUCCESS; }