#include "messages.h"
#include "auth/gensec/gensec.h"
#include "../libcli/smb/smbXcli_base.h"
-#include "lib/param/loadparm.h"
#include "libcli/auth/netlogon_creds_cli.h"
#include "auth.h"
#include "rpc_server/rpc_ncacn_np.h"
+#include "auth/credentials/credentials.h"
+#include "lib/param/param.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_WINBIND
static void set_dc_type_and_flags( struct winbindd_domain *domain );
static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain );
static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
- struct dc_name_ip **dcs, int *num_dcs);
+ struct dc_name_ip **dcs, int *num_dcs,
+ uint32_t request_flags);
/****************************************************************
Child failed to find DC's. Reschedule check.
pid_t parent_pid = getpid();
char *lfile = NULL;
NTSTATUS status;
+ bool ok;
if (domain->dc_probe_pid != (pid_t)-1) {
/*
if (domain->dc_probe_pid != (pid_t)0) {
/* Parent */
- messaging_register(winbind_messaging_context(), NULL,
+ messaging_register(server_messaging_context(), NULL,
MSG_WINBIND_TRY_TO_GO_ONLINE,
msg_try_to_go_online);
- messaging_register(winbind_messaging_context(), NULL,
+ messaging_register(server_messaging_context(), NULL,
MSG_WINBIND_FAILED_TO_GO_ONLINE,
msg_failed_to_go_online);
return True;
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1, ("winbindd_reinit_after_fork failed: %s\n",
nt_errstr(status)));
- messaging_send_buf(winbind_messaging_context(),
+ messaging_send_buf(server_messaging_context(),
pid_to_procid(parent_pid),
MSG_WINBIND_FAILED_TO_GO_ONLINE,
(const uint8_t *)domain->name,
}
SAFE_FREE(lfile);
+ setproctitle("dc-connect child");
+
mem_ctx = talloc_init("fork_child_dc_connect");
if (!mem_ctx) {
DEBUG(0,("talloc_init failed.\n"));
- messaging_send_buf(winbind_messaging_context(),
+ messaging_send_buf(server_messaging_context(),
pid_to_procid(parent_pid),
MSG_WINBIND_FAILED_TO_GO_ONLINE,
(const uint8_t *)domain->name,
_exit(1);
}
- if ((!get_dcs(mem_ctx, domain, &dcs, &num_dcs)) || (num_dcs == 0)) {
+ ok = get_dcs(mem_ctx, domain, &dcs, &num_dcs, 0);
+ TALLOC_FREE(mem_ctx);
+ if (!ok || (num_dcs == 0)) {
/* Still offline ? Can't find DC's. */
- messaging_send_buf(winbind_messaging_context(),
+ messaging_send_buf(server_messaging_context(),
pid_to_procid(parent_pid),
MSG_WINBIND_FAILED_TO_GO_ONLINE,
(const uint8_t *)domain->name,
/* We got a DC. Send a message to our parent to get it to
try and do the same. */
- messaging_send_buf(winbind_messaging_context(),
+ messaging_send_buf(server_messaging_context(),
pid_to_procid(parent_pid),
MSG_WINBIND_TRY_TO_GO_ONLINE,
(const uint8_t *)domain->name,
calc_new_online_timeout_check(domain);
- domain->check_online_event = tevent_add_timer(winbind_event_context(),
+ domain->check_online_event = tevent_add_timer(server_event_context(),
NULL,
timeval_current_ofs(domain->check_online_timeout,0),
check_domain_online_handler,
/* Send a message to the parent that the domain is offline. */
if (parent_pid > 1 && !domain->internal) {
- messaging_send_buf(winbind_messaging_context(),
+ messaging_send_buf(server_messaging_context(),
pid_to_procid(parent_pid),
MSG_WINBIND_DOMAIN_OFFLINE,
- (uint8 *)domain->name,
+ (uint8_t *)domain->name,
strlen(domain->name) + 1);
}
struct winbindd_child *idmap = idmap_child();
if ( idmap->pid != 0 ) {
- messaging_send_buf(winbind_messaging_context(),
+ messaging_send_buf(server_messaging_context(),
pid_to_procid(idmap->pid),
MSG_WINBIND_OFFLINE,
(const uint8_t *)domain->name,
TALLOC_FREE(domain->check_online_event);
/* Ensure we ignore any pending child messages. */
- messaging_deregister(winbind_messaging_context(),
+ messaging_deregister(server_messaging_context(),
MSG_WINBIND_TRY_TO_GO_ONLINE, NULL);
- messaging_deregister(winbind_messaging_context(),
+ messaging_deregister(server_messaging_context(),
MSG_WINBIND_FAILED_TO_GO_ONLINE, NULL);
domain->online = True;
/* Send a message to the parent that the domain is online. */
if (parent_pid > 1 && !domain->internal) {
- messaging_send_buf(winbind_messaging_context(),
+ messaging_send_buf(server_messaging_context(),
pid_to_procid(parent_pid),
MSG_WINBIND_DOMAIN_ONLINE,
- (uint8 *)domain->name,
+ (uint8_t *)domain->name,
strlen(domain->name) + 1);
}
struct winbindd_child *idmap = idmap_child();
if ( idmap->pid != 0 ) {
- messaging_send_buf(winbind_messaging_context(),
+ messaging_send_buf(server_messaging_context(),
pid_to_procid(idmap->pid),
MSG_WINBIND_ONLINE,
(const uint8_t *)domain->name,
TALLOC_FREE(domain->check_online_event);
- domain->check_online_event = tevent_add_timer(winbind_event_context(),
+ domain->check_online_event = tevent_add_timer(server_event_context(),
NULL,
tev,
check_domain_online_handler,
}
}
+static NTSTATUS cm_get_ipc_credentials(TALLOC_CTX *mem_ctx,
+ struct cli_credentials **_creds)
+{
+
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+ struct loadparm_context *lp_ctx;
+ char *username = NULL;
+ char *netbios_domain = NULL;
+ char *password = NULL;
+ struct cli_credentials *creds = NULL;
+ bool ok;
+
+ cm_get_ipc_userpass(&username, &netbios_domain, &password);
+
+ lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ DEBUG(1, ("loadparm_init_s3 failed\n"));
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto fail;
+ }
+
+ creds = cli_credentials_init(mem_ctx);
+ if (creds == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ cli_credentials_set_conf(creds, lp_ctx);
+ cli_credentials_set_kerberos_state(creds, CRED_DONT_USE_KERBEROS);
+
+ ok = cli_credentials_set_domain(creds, netbios_domain, CRED_SPECIFIED);
+ if (!ok) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ ok = cli_credentials_set_username(creds, username, CRED_SPECIFIED);
+ if (!ok) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ ok = cli_credentials_set_password(creds, password, CRED_SPECIFIED);
+ if (!ok) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ *_creds = creds;
+ creds = NULL;
+ status = NT_STATUS_OK;
+ fail:
+ TALLOC_FREE(creds);
+ SAFE_FREE(username);
+ SAFE_FREE(netbios_domain);
+ SAFE_FREE(password);
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static bool cm_is_ipc_credentials(struct cli_credentials *creds)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *ipc_account = NULL;
+ char *ipc_domain = NULL;
+ char *ipc_password = NULL;
+ const char *creds_account = NULL;
+ const char *creds_domain = NULL;
+ const char *creds_password = NULL;
+ bool ret = false;
+
+ cm_get_ipc_userpass(&ipc_account, &ipc_domain, &ipc_password);
+
+ creds_account = cli_credentials_get_username(creds);
+ creds_domain = cli_credentials_get_domain(creds);
+ creds_password = cli_credentials_get_password(creds);
+
+ if (!strequal(ipc_domain, creds_domain)) {
+ goto done;
+ }
+
+ if (!strequal(ipc_account, creds_account)) {
+ goto done;
+ }
+
+ if (!strcsequal(ipc_password, creds_password)) {
+ goto done;
+ }
+
+ ret = true;
+ done:
+ SAFE_FREE(ipc_account);
+ SAFE_FREE(ipc_domain);
+ SAFE_FREE(ipc_password);
+ TALLOC_FREE(frame);
+ return ret;
+}
+
static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
fstring dcname,
- struct sockaddr_storage *dc_ss)
+ struct sockaddr_storage *dc_ss,
+ uint32_t request_flags)
{
struct winbindd_domain *our_domain = NULL;
struct rpc_pipe_client *netlogon_pipe = NULL;
if (our_domain->active_directory) {
struct netr_DsRGetDCNameInfo *domain_info = NULL;
+ /*
+ * TODO request flags are not respected in the server
+ * (and in some cases, like REQUIRE_PDC, causes an error)
+ */
result = dcerpc_netr_DsRGetDCName(b,
mem_ctx,
our_domain->dcname,
domain->name,
NULL,
NULL,
- DS_RETURN_DNS_NAME,
+ request_flags|DS_RETURN_DNS_NAME,
&domain_info,
&werr);
if (NT_STATUS_IS_OK(result) && W_ERROR_IS_OK(werr)) {
/**
* Helper function to assemble trust password and account name
*/
-static NTSTATUS get_trust_creds(const struct winbindd_domain *domain,
- char **machine_password,
- char **machine_account,
- char **machine_krb5_principal)
+static NTSTATUS get_trust_credentials(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ bool netlogon,
+ struct cli_credentials **_creds)
{
- const char *account_name;
- const char *name = NULL;
+ const struct winbindd_domain *creds_domain = NULL;
+ struct cli_credentials *creds;
+ NTSTATUS status;
+ bool force_machine_account = false;
/* If we are a DC and this is not our own domain */
- if (IS_DC) {
- name = domain->name;
- } else {
- struct winbindd_domain *our_domain = find_our_domain();
-
- if (!our_domain)
- return NT_STATUS_INVALID_SERVER_STATE;
-
- name = our_domain->name;
- }
-
- if (!get_trust_pw_clear(name, machine_password,
- &account_name, NULL))
- {
- return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ if (!domain->active_directory) {
+ if (!netlogon) {
+ /*
+ * For non active directory domains
+ * we can only use NTLMSSP for SMB.
+ *
+ * But the trust account is not allowed
+ * to use SMB with NTLMSSP.
+ */
+ force_machine_account = true;
+ }
}
- if ((machine_account != NULL) &&
- (asprintf(machine_account, "%s$", account_name) == -1))
- {
- return NT_STATUS_NO_MEMORY;
+ if (IS_DC && !force_machine_account) {
+ creds_domain = domain;
+ } else {
+ creds_domain = find_our_domain();
+ if (creds_domain == NULL) {
+ return NT_STATUS_INVALID_SERVER_STATE;
+ }
}
- /* For now assume our machine account only exists in our domain */
-
- if (machine_krb5_principal != NULL)
- {
- struct winbindd_domain *our_domain = find_our_domain();
+ status = pdb_get_trust_credentials(creds_domain->name,
+ creds_domain->alt_name,
+ mem_ctx,
+ &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto ipc_fallback;
+ }
- if (!our_domain) {
- return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
- }
+ if (creds_domain != domain) {
+ /*
+ * We can only use schannel against a direct trust
+ */
+ cli_credentials_set_secure_channel_type(creds,
+ SEC_CHAN_NULL);
+ }
- if (our_domain->alt_name == NULL) {
- return NT_STATUS_INVALID_PARAMETER;
- }
+ *_creds = creds;
+ return NT_STATUS_OK;
- if (asprintf(machine_krb5_principal, "%s$@%s",
- account_name, our_domain->alt_name) == -1)
- {
- return NT_STATUS_NO_MEMORY;
- }
+ ipc_fallback:
+ if (netlogon) {
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
- if (!strupper_m(*machine_krb5_principal)) {
- SAFE_FREE(*machine_krb5_principal);
- return NT_STATUS_INVALID_PARAMETER;
- }
+ status = cm_get_ipc_credentials(mem_ctx, &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
+ *_creds = creds;
return NT_STATUS_OK;
}
to the pipe.
************************************************************************/
-static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
+static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
const int sockfd,
const char *controller,
struct cli_state **cli,
bool *retry)
{
- bool try_spnego = false;
bool try_ipc_auth = false;
- char *machine_password = NULL;
- char *machine_krb5_principal = NULL;
- char *machine_account = NULL;
- char *ipc_username = NULL;
- char *ipc_domain = NULL;
- char *ipc_password = NULL;
+ const char *machine_principal = NULL;
+ const char *machine_realm = NULL;
+ const char *machine_account = NULL;
+ const char *machine_domain = NULL;
int flags = 0;
- uint16_t sec_mode = 0;
+ struct cli_credentials *creds = NULL;
struct named_mutex *mutex;
NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ NTSTATUS tmp_status;
+ NTSTATUS tcon_status = NT_STATUS_NETWORK_NAME_DELETED;
+
+ enum smb_signing_setting smb_sign_client_connections = lp_client_ipc_signing();
- enum smb_signing_setting smb_sign_client_connections = lp_client_signing();
+ if (IS_AD_DC) {
+ if (domain->secure_channel_type == SEC_CHAN_NULL) {
+ /*
+ * Make sure we don't even try to
+ * connect to a foreign domain
+ * without a direct outbound trust.
+ */
+ return NT_STATUS_NO_TRUST_LSA_SECRET;
+ }
+
+ /*
+ * As AD DC we only use netlogon and lsa
+ * using schannel over an anonymous transport
+ * (ncacn_ip_tcp or ncacn_np).
+ *
+ * Currently we always establish the SMB connection,
+ * even if we don't use it, because we later use ncacn_ip_tcp.
+ *
+ * As we won't use the SMB connection there's no
+ * need to try kerberos. And NT4 domains expect
+ * an anonymous IPC$ connection anyway.
+ */
+ smb_sign_client_connections = SMB_SIGNING_OFF;
+ }
if (smb_sign_client_connections == SMB_SIGNING_DEFAULT) {
/*
* AD domain in our forest
* then require smb signing to disrupt MITM attacks
*/
- } else if ((lp_security() == SEC_ADS ||
- lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC)
+ } else if ((lp_security() == SEC_ADS)
&& domain->active_directory
&& (domain->domain_trust_attribs
& LSA_TRUST_ATTRIBUTE_WITHIN_FOREST)) {
goto done;
}
- flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
-
- *cli = cli_state_create(NULL, sockfd,
- controller, domain->alt_name,
+ *cli = cli_state_create(NULL, sockfd, controller,
smb_sign_client_connections, flags);
if (*cli == NULL) {
close(sockfd);
cli_set_timeout(*cli, 10000); /* 10 seconds */
+ set_socket_options(sockfd, lp_socket_options());
+
result = smbXcli_negprot((*cli)->conn, (*cli)->timeout,
- lp_client_min_protocol(),
- lp_client_max_protocol());
+ lp_client_ipc_min_protocol(),
+ lp_client_ipc_max_protocol());
if (!NT_STATUS_IS_OK(result)) {
DEBUG(1, ("cli_negprot failed: %s\n", nt_errstr(result)));
if (smbXcli_conn_protocol((*cli)->conn) >= PROTOCOL_NT1 &&
smb1cli_conn_capabilities((*cli)->conn) & CAP_EXTENDED_SECURITY) {
- try_spnego = true;
+ try_ipc_auth = true;
} else if (smbXcli_conn_protocol((*cli)->conn) >= PROTOCOL_SMB2_02) {
- try_spnego = true;
+ try_ipc_auth = true;
+ } else if (smb_sign_client_connections == SMB_SIGNING_REQUIRED) {
+ /*
+ * If we are forcing on SMB signing, then we must
+ * require authentication unless this is a one-way
+ * trust, and we have no stored user/password
+ */
+ try_ipc_auth = true;
+ }
+
+ if (IS_AD_DC) {
+ /*
+ * As AD DC we only use netlogon and lsa
+ * using schannel over an anonymous transport
+ * (ncacn_ip_tcp or ncacn_np).
+ *
+ * Currently we always establish the SMB connection,
+ * even if we don't use it, because we later use ncacn_ip_tcp.
+ *
+ * As we won't use the SMB connection there's no
+ * need to try kerberos. And NT4 domains expect
+ * an anonymous IPC$ connection anyway.
+ */
+ try_ipc_auth = false;
}
- if (!is_dc_trusted_domain_situation(domain->name) && try_spnego) {
- result = get_trust_creds(domain, &machine_password,
- &machine_account,
- &machine_krb5_principal);
+ if (try_ipc_auth) {
+ result = get_trust_credentials(domain, talloc_tos(), false, &creds);
if (!NT_STATUS_IS_OK(result)) {
- goto anon_fallback;
+ DEBUG(1, ("get_trust_credentials(%s) failed: %s\n",
+ domain->name, nt_errstr(result)));
+ goto done;
}
+ } else {
+ /*
+ * Without SPNEGO or NTLMSSP (perhaps via SMB2) we
+ * would try and authentication with our machine
+ * account password and fail. This is very rare in
+ * the modern world however
+ */
+ creds = cli_credentials_init_anon(talloc_tos());
+ if (creds == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ DEBUG(1, ("cli_credentials_init_anon(%s) failed: %s\n",
+ domain->name, nt_errstr(result)));
+ goto done;
+ }
+ }
- if (lp_security() == SEC_ADS) {
+ machine_principal = cli_credentials_get_principal(creds,
+ talloc_tos());
+ machine_realm = cli_credentials_get_realm(creds);
+ machine_account = cli_credentials_get_username(creds);
+ machine_domain = cli_credentials_get_domain(creds);
- /* Try a krb5 session */
+ DEBUG(5, ("connecting to %s (%s, %s) with account [%s\\%s] principal "
+ "[%s] and realm [%s]\n",
+ controller, domain->name, domain->alt_name,
+ machine_domain, machine_account,
+ machine_principal, machine_realm));
- (*cli)->use_kerberos = True;
- DEBUG(5, ("connecting to %s from %s with kerberos principal "
- "[%s] and realm [%s]\n", controller, lp_netbios_name(),
- machine_krb5_principal, domain->alt_name));
+ if (cli_credentials_is_anonymous(creds)) {
+ goto anon_fallback;
+ }
- winbindd_set_locator_kdc_envs(domain);
+ winbindd_set_locator_kdc_envs(domain);
- result = cli_session_setup(*cli,
- machine_krb5_principal,
- machine_password,
- strlen(machine_password)+1,
- machine_password,
- strlen(machine_password)+1,
- lp_workgroup());
+ result = cli_session_setup_creds(*cli, creds);
+ if (NT_STATUS_IS_OK(result)) {
+ goto session_setup_done;
+ }
- if (!NT_STATUS_IS_OK(result)) {
- DEBUG(4,("failed kerberos session setup with %s\n",
- nt_errstr(result)));
- }
+ DEBUG(1, ("authenticated session setup to %s using %s failed with %s\n",
+ controller,
+ cli_credentials_get_unparsed_name(creds, talloc_tos()),
+ nt_errstr(result)));
- if (NT_STATUS_IS_OK(result)) {
- /* Ensure creds are stored for NTLMSSP authenticated pipe access. */
- result = cli_init_creds(*cli, machine_account, lp_workgroup(), machine_password);
- if (!NT_STATUS_IS_OK(result)) {
- goto done;
- }
- goto session_setup_done;
- }
+ /*
+ * If we are not going to validiate the conneciton
+ * with SMB signing, then allow us to fall back to
+ * anonymous
+ */
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT)
+ || NT_STATUS_EQUAL(result, NT_STATUS_TRUSTED_DOMAIN_FAILURE)
+ || NT_STATUS_EQUAL(result, NT_STATUS_INVALID_ACCOUNT_NAME)
+ || NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS)
+ || NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE))
+ {
+ if (!cm_is_ipc_credentials(creds)) {
+ goto ipc_fallback;
}
- /* Fall back to non-kerberos session setup using NTLMSSP SPNEGO with the machine account. */
- (*cli)->use_kerberos = False;
-
- DEBUG(5, ("connecting to %s from %s with username "
- "[%s]\\[%s]\n", controller, lp_netbios_name(),
- lp_workgroup(), machine_account));
-
- result = cli_session_setup(*cli,
- machine_account,
- machine_password,
- strlen(machine_password)+1,
- machine_password,
- strlen(machine_password)+1,
- lp_workgroup());
- if (!NT_STATUS_IS_OK(result)) {
- DEBUG(4, ("authenticated session setup failed with %s\n",
- nt_errstr(result)));
+ if (smb_sign_client_connections == SMB_SIGNING_REQUIRED) {
+ goto done;
}
- if (NT_STATUS_IS_OK(result)) {
- /* Ensure creds are stored for NTLMSSP authenticated pipe access. */
- result = cli_init_creds(*cli, machine_account, lp_workgroup(), machine_password);
- if (!NT_STATUS_IS_OK(result)) {
- goto done;
- }
- goto session_setup_done;
- }
+ goto anon_fallback;
}
- /* Fall back to non-kerberos session setup with auth_user */
-
- (*cli)->use_kerberos = False;
-
- cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
+ goto done;
- sec_mode = smb1cli_conn_server_security_mode((*cli)->conn);
+ ipc_fallback:
+ TALLOC_FREE(creds);
+ tmp_status = cm_get_ipc_credentials(talloc_tos(), &creds);
+ if (!NT_STATUS_IS_OK(tmp_status)) {
+ result = tmp_status;
+ goto done;
+ }
- try_ipc_auth = false;
- if (try_spnego) {
- try_ipc_auth = true;
- } else if (sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
- try_ipc_auth = true;
+ if (cli_credentials_is_anonymous(creds)) {
+ goto anon_fallback;
}
- if (try_ipc_auth && (strlen(ipc_username) > 0)) {
+ machine_account = cli_credentials_get_username(creds);
+ machine_domain = cli_credentials_get_domain(creds);
- /* Only try authenticated if we have a username */
+ DEBUG(5, ("connecting to %s from %s using NTLMSSP with username "
+ "[%s]\\[%s]\n", controller, lp_netbios_name(),
+ machine_domain, machine_account));
- DEBUG(5, ("connecting to %s from %s with username "
- "[%s]\\[%s]\n", controller, lp_netbios_name(),
- ipc_domain, ipc_username));
+ result = cli_session_setup_creds(*cli, creds);
+ if (NT_STATUS_IS_OK(result)) {
+ goto session_setup_done;
+ }
- if (NT_STATUS_IS_OK(cli_session_setup(
- *cli, ipc_username,
- ipc_password, strlen(ipc_password)+1,
- ipc_password, strlen(ipc_password)+1,
- ipc_domain))) {
- /* Successful logon with given username. */
- result = cli_init_creds(*cli, ipc_username, ipc_domain, ipc_password);
- if (!NT_STATUS_IS_OK(result)) {
- goto done;
- }
- goto session_setup_done;
- } else {
- DEBUG(4, ("authenticated session setup with user %s\\%s failed.\n",
- ipc_domain, ipc_username ));
- }
+ DEBUG(1, ("authenticated session setup to %s using %s failed with %s\n",
+ controller,
+ cli_credentials_get_unparsed_name(creds, talloc_tos()),
+ nt_errstr(result)));
+
+ /*
+ * If we are not going to validiate the conneciton
+ * with SMB signing, then allow us to fall back to
+ * anonymous
+ */
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT)
+ || NT_STATUS_EQUAL(result, NT_STATUS_TRUSTED_DOMAIN_FAILURE)
+ || NT_STATUS_EQUAL(result, NT_STATUS_INVALID_ACCOUNT_NAME)
+ || NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS)
+ || NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE))
+ {
+ goto anon_fallback;
}
+ goto done;
+
anon_fallback:
+ TALLOC_FREE(creds);
+
+ if (smb_sign_client_connections == SMB_SIGNING_REQUIRED) {
+ goto done;
+ }
/* Fall back to anonymous connection, this might fail later */
- DEBUG(10,("cm_prepare_connection: falling back to anonymous "
+ DEBUG(5,("cm_prepare_connection: falling back to anonymous "
"connection for DC %s\n",
controller ));
- result = cli_session_setup(*cli, "", NULL, 0, NULL, 0, "");
+ result = cli_session_setup_anon(*cli);
if (NT_STATUS_IS_OK(result)) {
DEBUG(5, ("Connected anonymously\n"));
- result = cli_init_creds(*cli, "", "", "");
- if (!NT_STATUS_IS_OK(result)) {
- goto done;
- }
goto session_setup_done;
}
+ DEBUG(1, ("anonymous session setup to %s failed with %s\n",
+ controller, nt_errstr(result)));
+
/* We can't session setup */
goto done;
session_setup_done:
+ TALLOC_FREE(creds);
/*
* This should be a short term hack until
smbXcli_session_set_disconnect_expired((*cli)->smb2.session);
}
+ result = cli_tree_connect(*cli, "IPC$", "IPC", NULL);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
+ goto done;
+ }
+ tcon_status = result;
+
/* cache the server name for later connections */
saf_store(domain->name, controller);
- if (domain->alt_name && (*cli)->use_kerberos) {
+ if (domain->alt_name) {
saf_store(domain->alt_name, controller);
}
winbindd_set_locator_kdc_envs(domain);
- result = cli_tree_connect(*cli, "IPC$", "IPC", "", 0);
-
- if (!NT_STATUS_IS_OK(result)) {
- DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
- goto done;
- }
-
TALLOC_FREE(mutex);
*retry = False;
- /* set the domain if empty; needed for schannel connections */
- if ( !(*cli)->domain[0] ) {
- result = cli_set_domain((*cli), domain->name);
- if (!NT_STATUS_IS_OK(result)) {
- SAFE_FREE(ipc_username);
- SAFE_FREE(ipc_domain);
- SAFE_FREE(ipc_password);
- return result;
- }
- }
-
result = NT_STATUS_OK;
done:
TALLOC_FREE(mutex);
- SAFE_FREE(machine_account);
- SAFE_FREE(machine_password);
- SAFE_FREE(machine_krb5_principal);
- SAFE_FREE(ipc_username);
- SAFE_FREE(ipc_domain);
- SAFE_FREE(ipc_password);
+ TALLOC_FREE(creds);
+
+ if (NT_STATUS_IS_OK(result)) {
+ result = tcon_status;
+ }
if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(1, ("Failed to prepare SMB connection to %s: %s\n",
+ controller, nt_errstr(result)));
winbind_add_failed_connection_entry(domain, controller, result);
if ((*cli) != NULL) {
cli_shutdown(*cli);
}
static bool add_sockaddr_to_array(TALLOC_CTX *mem_ctx,
- struct sockaddr_storage *pss, uint16 port,
+ struct sockaddr_storage *pss, uint16_t port,
struct sockaddr_storage **addrs, int *num)
{
*addrs = talloc_realloc(mem_ctx, *addrs, struct sockaddr_storage, (*num)+1);
/*******************************************************************
convert an ip to a name
+ For an AD Domain, it checks the requirements of the request flags.
*******************************************************************/
-static bool dcip_to_name(TALLOC_CTX *mem_ctx,
- const struct winbindd_domain *domain,
- struct sockaddr_storage *pss,
- char **name)
+static bool dcip_check_name(TALLOC_CTX *mem_ctx,
+ const struct winbindd_domain *domain,
+ struct sockaddr_storage *pss,
+ char **name, uint32_t request_flags)
{
struct ip_service ip_list;
uint32_t nt_version = NETLOGON_NT_VERSION_1;
NTSTATUS status;
const char *dc_name;
fstring nbtname;
-
+#ifdef HAVE_ADS
+ bool is_ad_domain = false;
+#endif
ip_list.ss = *pss;
ip_list.port = 0;
None of these failures should be considered critical for now */
if ((lp_security() == SEC_ADS) && (domain->alt_name != NULL)) {
+ is_ad_domain = true;
+ } else if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
+ is_ad_domain = domain->active_directory;
+ }
+
+ if (is_ad_domain) {
ADS_STRUCT *ads;
ADS_STATUS ads_status;
char addr[INET6_ADDRSTRLEN];
ads = ads_init(domain->alt_name, domain->name, addr);
ads->auth.flags |= ADS_AUTH_NO_BIND;
+ ads->config.flags |= request_flags;
+ ads->server.no_fallback = true;
ads_status = ads_connect(ads);
if (ADS_ERR_OK(ads_status)) {
}
namecache_store(*name, 0x20, 1, &ip_list);
- DEBUG(10,("dcip_to_name: flags = 0x%x\n", (unsigned int)ads->config.flags));
+ DEBUG(10,("dcip_check_name: flags = 0x%x\n", (unsigned int)ads->config.flags));
if (domain->primary && (ads->config.flags & NBT_SERVER_KDC)) {
if (ads_closest_dc(ads)) {
}
#endif
- status = nbt_getdc(winbind_messaging_context(), 10, pss, domain->name,
+ status = nbt_getdc(server_messaging_context(), 10, pss, domain->name,
&domain->sid, nt_version, mem_ctx, &nt_version,
&dc_name, NULL);
if (NT_STATUS_IS_OK(status)) {
*******************************************************************/
static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
- struct dc_name_ip **dcs, int *num_dcs)
+ struct dc_name_ip **dcs, int *num_dcs,
+ uint32_t request_flags)
{
fstring dcname;
struct sockaddr_storage ss;
/* If not our domain, get the preferred DC, by asking our primary DC */
if ( !is_our_domain
- && get_dc_name_via_netlogon(domain, dcname, &ss)
+ && get_dc_name_via_netlogon(domain, dcname, &ss, request_flags)
&& add_one_dc_unique(mem_ctx, domain->name, dcname, &ss, dcs,
num_dcs) )
{
static bool find_new_dc(TALLOC_CTX *mem_ctx,
struct winbindd_domain *domain,
- char **dcname, struct sockaddr_storage *pss, int *fd)
+ char **dcname, struct sockaddr_storage *pss, int *fd,
+ uint32_t request_flags)
{
struct dc_name_ip *dcs = NULL;
int num_dcs = 0;
const char **dcnames = NULL;
- int num_dcnames = 0;
+ size_t num_dcnames = 0;
struct sockaddr_storage *addrs = NULL;
int num_addrs = 0;
*fd = -1;
again:
- if (!get_dcs(mem_ctx, domain, &dcs, &num_dcs) || (num_dcs == 0))
+ if (!get_dcs(mem_ctx, domain, &dcs, &num_dcs, request_flags) || (num_dcs == 0))
return False;
for (i=0; i<num_dcs; i++) {
}
/* Try to figure out the name */
- if (dcip_to_name(mem_ctx, domain, pss, dcname)) {
+ if (dcip_check_name(mem_ctx, domain, pss, dcname, request_flags)) {
return True;
}
table,
session_info,
NULL,
- winbind_messaging_context(),
+ NULL,
+ server_messaging_context(),
&cli);
} else {
status = rpc_pipe_open_internal(mem_ctx,
- &table->syntax_id,
+ table,
session_info,
NULL,
- winbind_messaging_context(),
+ NULL,
+ server_messaging_context(),
&cli);
}
if (!NT_STATUS_IS_OK(status)) {
}
static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
- struct winbindd_cm_conn *new_conn)
+ struct winbindd_cm_conn *new_conn,
+ bool need_rw_dc)
{
TALLOC_CTX *mem_ctx;
NTSTATUS result;
char *saf_servername;
int retries;
+ uint32_t request_flags = need_rw_dc ? DS_WRITABLE_REQUIRED : 0;
if ((mem_ctx = talloc_init("cm_open_connection")) == NULL) {
set_domain_offline(domain);
before talking to it. It going down may have
triggered the reconnection. */
- if ( saf_servername && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, saf_servername))) {
+ if (saf_servername && NT_STATUS_IS_OK(check_negative_conn_cache(domain->name, saf_servername))) {
+ struct sockaddr_storage ss;
+ char *dcname = NULL;
+ bool resolved = true;
- DEBUG(10,("cm_open_connection: saf_servername is '%s' for domain %s\n",
- saf_servername, domain->name ));
+ DEBUG(10, ("cm_open_connection: saf_servername is '%s' for domain %s\n",
+ saf_servername, domain->name));
/* convert an ip address to a name */
- if (is_ipaddress( saf_servername ) ) {
- char *dcname = NULL;
- struct sockaddr_storage ss;
-
+ if (is_ipaddress(saf_servername)) {
if (!interpret_string_addr(&ss, saf_servername,
- AI_NUMERICHOST)) {
+ AI_NUMERICHOST)) {
TALLOC_FREE(mem_ctx);
return NT_STATUS_UNSUCCESSFUL;
}
- if (dcip_to_name(mem_ctx, domain, &ss, &dcname)) {
- domain->dcname = talloc_strdup(domain,
- dcname);
- if (domain->dcname == NULL) {
- TALLOC_FREE(mem_ctx);
- return NT_STATUS_NO_MEMORY;
- }
- } else {
- winbind_add_failed_connection_entry(
- domain, saf_servername,
- NT_STATUS_UNSUCCESSFUL);
- }
} else {
- domain->dcname = talloc_strdup(domain, saf_servername);
+ if (!resolve_name(saf_servername, &ss, 0x20, true)) {
+ resolved = false;
+ }
+ }
+
+ if (resolved && dcip_check_name(mem_ctx, domain, &ss, &dcname, request_flags)) {
+ domain->dcname = talloc_strdup(domain,
+ dcname);
if (domain->dcname == NULL) {
TALLOC_FREE(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
+
+ domain->dcaddr = ss;
+ } else {
+ winbind_add_failed_connection_entry(domain, saf_servername,
+ NT_STATUS_UNSUCCESSFUL);
}
}
result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
- DEBUG(10,("cm_open_connection: dcname is '%s' for domain %s\n",
- domain->dcname ? domain->dcname : "", domain->name ));
+ DEBUG(10, ("cm_open_connection: dcname is '%s' for domain %s\n",
+ domain->dcname ? domain->dcname : "", domain->name));
- if (domain->dcname != NULL
- && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, domain->dcname))
- && (resolve_name(domain->dcname, &domain->dcaddr, 0x20, true)))
+ if (domain->dcname != NULL &&
+ NT_STATUS_IS_OK(check_negative_conn_cache(domain->name,
+ domain->dcname)))
{
NTSTATUS status;
}
if ((fd == -1) &&
- !find_new_dc(mem_ctx, domain, &dcname, &domain->dcaddr, &fd))
+ !find_new_dc(mem_ctx, domain, &dcname, &domain->dcaddr, &fd, request_flags))
{
/* This is the one place where we will
set the global winbindd offline state
/* Close down all open pipes on a connection. */
-void invalidate_cm_connection(struct winbindd_cm_conn *conn)
+void invalidate_cm_connection(struct winbindd_domain *domain)
{
NTSTATUS result;
+ struct winbindd_cm_conn *conn = &domain->conn;
/* We're closing down a possibly dead
connection. Don't have impossibly long (10s) timeouts. */
}
conn->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
- conn->netlogon_force_reauth = false;
- conn->netlogon_flags = 0;
- TALLOC_FREE(conn->netlogon_creds);
+ TALLOC_FREE(conn->netlogon_creds_ctx);
if (conn->cli) {
cli_shutdown(conn->cli);
smbXcli_conn_disconnect(domain->conn.cli->conn, NT_STATUS_OK);
}
- invalidate_cm_connection(&domain->conn);
+ invalidate_cm_connection(domain);
}
for (cli_state = winbindd_client_list();
return NT_STATUS_OK;
}
- invalidate_cm_connection(&domain->conn);
+ invalidate_cm_connection(domain);
if (!domain->primary && !domain->initialized) {
/*
set_dc_type_and_flags_trustinfo(domain);
}
- result = cm_open_connection(domain, &domain->conn);
+ result = cm_open_connection(domain, &domain->conn, need_rw_dc);
if (NT_STATUS_IS_OK(result) && !domain->initialized) {
set_dc_type_and_flags(domain);
WERROR werr;
struct netr_DomainTrustList trusts;
int i;
- uint32 flags = (NETR_TRUST_FLAG_IN_FOREST |
+ uint32_t flags = (NETR_TRUST_FLAG_IN_FOREST |
NETR_TRUST_FLAG_OUTBOUND |
NETR_TRUST_FLAG_INBOUND);
struct rpc_pipe_client *cli;
TALLOC_CTX *mem_ctx = NULL;
struct dcerpc_binding_handle *b;
+ if (IS_DC) {
+ /*
+ * On a DC we loaded all trusts
+ * from configuration and never learn
+ * new domains.
+ */
+ return true;
+ }
+
DEBUG(5, ("set_dc_type_and_flags_trustinfo: domain %s\n", domain->name ));
/* Our primary domain doesn't need to worry about trust flags.
static void set_dc_type_and_flags( struct winbindd_domain *domain )
{
+ if (IS_DC) {
+ /*
+ * On a DC we loaded all trusts
+ * from configuration and never learn
+ * new domains.
+ */
+ return;
+ }
+
/* we always have to contact our primary domain */
if ( domain->primary || domain->internal) {
return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
}
- if (domain->conn.netlogon_creds != NULL) {
- if (!(domain->conn.netlogon_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
- return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
- }
- *ppdc = domain->conn.netlogon_creds;
+ if (domain->conn.netlogon_creds_ctx != NULL) {
+ *ppdc = domain->conn.netlogon_creds_ctx;
return NT_STATUS_OK;
}
- result = cm_connect_netlogon(domain, &netlogon_pipe);
+ result = cm_connect_netlogon_secure(domain, &netlogon_pipe, ppdc);
if (!NT_STATUS_IS_OK(result)) {
return result;
}
- if (domain->conn.netlogon_creds == NULL) {
- return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
- }
-
- if (!(domain->conn.netlogon_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
- return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
- }
-
- *ppdc = domain->conn.netlogon_creds;
return NT_STATUS_OK;
}
struct winbindd_cm_conn *conn;
NTSTATUS status, result;
struct netlogon_creds_cli_context *p_creds;
- char *machine_password = NULL;
- char *machine_account = NULL;
- const char *domain_name = NULL;
+ struct cli_credentials *creds = NULL;
+ bool retry = false; /* allow one retry attempt for expired session */
if (sid_check_is_our_sam(&domain->sid)) {
if (domain->rodc == false || need_rw_dc == false) {
}
}
+ if (IS_AD_DC) {
+ /*
+ * In theory we should not use SAMR within
+ * winbindd at all, but that's a larger task to
+ * remove this and avoid breaking existing
+ * setups.
+ *
+ * At least as AD DC we have the restriction
+ * to avoid SAMR against trusted domains,
+ * as there're no existing setups.
+ */
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+retry:
status = init_dc_connection_rpc(domain, need_rw_dc);
if (!NT_STATUS_IS_OK(status)) {
return status;
* anonymous.
*/
- if ((conn->cli->user_name[0] == '\0') ||
- (conn->cli->domain[0] == '\0') ||
- (conn->cli->password == NULL || conn->cli->password[0] == '\0'))
- {
- status = get_trust_creds(domain, &machine_password,
- &machine_account, NULL);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(10, ("cm_connect_sam: No no user available for "
- "domain %s, trying schannel\n", conn->cli->domain));
- goto schannel;
- }
- domain_name = domain->name;
- } else {
- machine_password = SMB_STRDUP(conn->cli->password);
- machine_account = SMB_STRDUP(conn->cli->user_name);
- domain_name = conn->cli->domain;
+ result = get_trust_credentials(domain, talloc_tos(), false, &creds);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10, ("cm_connect_sam: No user available for "
+ "domain %s, trying schannel\n", domain->name));
+ goto schannel;
}
- if (!machine_password || !machine_account) {
- status = NT_STATUS_NO_MEMORY;
- goto done;
+ if (cli_credentials_is_anonymous(creds)) {
+ goto anonymous;
}
- /* We have an authenticated connection. Use a NTLMSSP SPNEGO
- authenticated SAMR pipe with sign & seal. */
- status = cli_rpc_pipe_open_spnego(conn->cli,
- &ndr_table_samr,
- NCACN_NP,
- GENSEC_OID_NTLMSSP,
- conn->auth_level,
- smbXcli_conn_remote_name(conn->cli->conn),
- domain_name,
- machine_account,
- machine_password,
- &conn->samr_pipe);
+ /*
+ * We have an authenticated connection. Use a SPNEGO
+ * authenticated SAMR pipe with sign & seal.
+ */
+ status = cli_rpc_pipe_open_with_creds(conn->cli,
+ &ndr_table_samr,
+ NCACN_NP,
+ DCERPC_AUTH_TYPE_SPNEGO,
+ conn->auth_level,
+ smbXcli_conn_remote_name(conn->cli->conn),
+ creds,
+ &conn->samr_pipe);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)
+ && !retry) {
+ invalidate_cm_connection(domain);
+ retry = true;
+ goto retry;
+ }
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10,("cm_connect_sam: failed to connect to SAMR "
"pipe for domain %s using NTLMSSP "
- "authenticated pipe: user %s\\%s. Error was "
- "%s\n", domain->name, domain_name,
- machine_account, nt_errstr(status)));
+ "authenticated pipe: user %s. Error was "
+ "%s\n", domain->name,
+ cli_credentials_get_unparsed_name(creds, talloc_tos()),
+ nt_errstr(status)));
goto schannel;
}
DEBUG(10,("cm_connect_sam: connected to SAMR pipe for "
"domain %s using NTLMSSP authenticated "
- "pipe: user %s\\%s\n", domain->name,
- domain_name, machine_account));
+ "pipe: user %s\n", domain->name,
+ cli_credentials_get_unparsed_name(creds, talloc_tos())));
status = dcerpc_samr_Connect2(conn->samr_pipe->binding_handle, mem_ctx,
conn->samr_pipe->desthost,
SEC_FLAG_MAXIMUM_ALLOWED,
&conn->sam_connect_handle,
&result);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+ invalidate_cm_connection(domain);
+ TALLOC_FREE(conn->samr_pipe);
+ retry = true;
+ goto retry;
+ }
+
if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
goto open_domain;
}
nt_errstr(status) ));
goto anonymous;
}
- status = cli_rpc_pipe_open_schannel_with_key
- (conn->cli, &ndr_table_samr, NCACN_NP,
- domain->name, p_creds, &conn->samr_pipe);
+ TALLOC_FREE(creds);
+ status = cli_rpc_pipe_open_schannel_with_creds(
+ conn->cli, &ndr_table_samr, NCACN_NP, p_creds,
+ &conn->samr_pipe);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)
+ && !retry) {
+ invalidate_cm_connection(domain);
+ retry = true;
+ goto retry;
+ }
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10,("cm_connect_sam: failed to connect to SAMR pipe for "
SEC_FLAG_MAXIMUM_ALLOWED,
&conn->sam_connect_handle,
&result);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+ invalidate_cm_connection(domain);
+ TALLOC_FREE(conn->samr_pipe);
+ retry = true;
+ goto retry;
+ }
+
if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
goto open_domain;
}
/* Finally fall back to anonymous. */
if (lp_winbind_sealed_pipes() || lp_require_strong_key()) {
status = NT_STATUS_DOWNGRADE_DETECTED;
- DEBUG(1, ("Unwilling to make SAMR connection to domain %s"
+ DEBUG(1, ("Unwilling to make SAMR connection to domain %s "
"without connection level security, "
"must set 'winbind sealed pipes = false' and "
"'require strong key = false' to proceed: %s\n",
status = cli_rpc_pipe_open_noauth(conn->cli, &ndr_table_samr,
&conn->samr_pipe);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)
+ && !retry) {
+ invalidate_cm_connection(domain);
+ retry = true;
+ goto retry;
+ }
+
if (!NT_STATUS_IS_OK(status)) {
goto done;
}
SEC_FLAG_MAXIMUM_ALLOWED,
&conn->sam_connect_handle,
&result);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+ invalidate_cm_connection(domain);
+ TALLOC_FREE(conn->samr_pipe);
+ retry = true;
+ goto retry;
+ }
+
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10,("cm_connect_sam: rpccli_samr_Connect2 failed "
"for domain %s Error was %s\n",
ZERO_STRUCT(conn->sam_domain_handle);
return status;
} else if (!NT_STATUS_IS_OK(status)) {
- invalidate_cm_connection(conn);
+ invalidate_cm_connection(domain);
return status;
}
*cli = conn->samr_pipe;
*sam_handle = conn->sam_domain_handle;
- SAFE_FREE(machine_password);
- SAFE_FREE(machine_account);
return status;
}
struct rpc_pipe_client **cli)
{
struct winbindd_cm_conn *conn;
- struct netlogon_creds_cli_context *creds;
+ struct netlogon_creds_cli_context *p_creds = NULL;
NTSTATUS status;
DEBUG(10,("cm_connect_lsa_tcp\n"));
conn = &domain->conn;
- if (conn->lsa_pipe_tcp &&
+ /*
+ * rpccli_is_connected handles more error cases
+ */
+ if (rpccli_is_connected(conn->lsa_pipe_tcp) &&
conn->lsa_pipe_tcp->transport->transport == NCACN_IP_TCP &&
- conn->lsa_pipe_tcp->auth->auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY &&
- rpccli_is_connected(conn->lsa_pipe_tcp)) {
+ conn->lsa_pipe_tcp->auth->auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) {
goto done;
}
TALLOC_FREE(conn->lsa_pipe_tcp);
- status = cm_get_schannel_creds(domain, &creds);
+ status = cm_get_schannel_creds(domain, &p_creds);
if (!NT_STATUS_IS_OK(status)) {
goto done;
}
- status = cli_rpc_pipe_open_schannel_with_key(conn->cli,
- &ndr_table_lsarpc,
- NCACN_IP_TCP,
- domain->name,
- creds,
- &conn->lsa_pipe_tcp);
+ status = cli_rpc_pipe_open_schannel_with_creds(conn->cli,
+ &ndr_table_lsarpc,
+ NCACN_IP_TCP,
+ p_creds,
+ &conn->lsa_pipe_tcp);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10,("cli_rpc_pipe_open_schannel_with_key failed: %s\n",
nt_errstr(status)));
struct winbindd_cm_conn *conn;
NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
struct netlogon_creds_cli_context *p_creds;
+ struct cli_credentials *creds = NULL;
+ bool retry = false; /* allow one retry attempt for expired session */
+retry:
result = init_dc_connection_rpc(domain, false);
if (!NT_STATUS_IS_OK(result))
return result;
TALLOC_FREE(conn->lsa_pipe);
- if ((conn->cli->user_name[0] == '\0') ||
- (conn->cli->domain[0] == '\0') ||
- (conn->cli->password == NULL || conn->cli->password[0] == '\0')) {
- DEBUG(10, ("cm_connect_lsa: No no user available for "
- "domain %s, trying schannel\n", conn->cli->domain));
+ if (IS_AD_DC) {
+ /*
+ * Make sure we only use schannel as AD DC.
+ */
+ goto schannel;
+ }
+
+ result = get_trust_credentials(domain, talloc_tos(), false, &creds);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10, ("cm_connect_lsa: No user available for "
+ "domain %s, trying schannel\n", domain->name));
goto schannel;
}
- /* We have an authenticated connection. Use a NTLMSSP SPNEGO
- * authenticated LSA pipe with sign & seal. */
- result = cli_rpc_pipe_open_spnego
+ if (cli_credentials_is_anonymous(creds)) {
+ goto anonymous;
+ }
+
+ /*
+ * We have an authenticated connection. Use a SPNEGO
+ * authenticated LSA pipe with sign & seal.
+ */
+ result = cli_rpc_pipe_open_with_creds
(conn->cli, &ndr_table_lsarpc, NCACN_NP,
- GENSEC_OID_NTLMSSP,
+ DCERPC_AUTH_TYPE_SPNEGO,
conn->auth_level,
smbXcli_conn_remote_name(conn->cli->conn),
- conn->cli->domain, conn->cli->user_name, conn->cli->password,
+ creds,
&conn->lsa_pipe);
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NETWORK_SESSION_EXPIRED)
+ && !retry) {
+ invalidate_cm_connection(domain);
+ retry = true;
+ goto retry;
+ }
+
if (!NT_STATUS_IS_OK(result)) {
DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for "
"domain %s using NTLMSSP authenticated pipe: user "
- "%s\\%s. Error was %s. Trying schannel.\n",
- domain->name, conn->cli->domain,
- conn->cli->user_name, nt_errstr(result)));
+ "%s. Error was %s. Trying schannel.\n",
+ domain->name,
+ cli_credentials_get_unparsed_name(creds, talloc_tos()),
+ nt_errstr(result)));
goto schannel;
}
DEBUG(10,("cm_connect_lsa: connected to LSA pipe for domain %s using "
- "NTLMSSP authenticated pipe: user %s\\%s\n",
- domain->name, conn->cli->domain, conn->cli->user_name ));
+ "NTLMSSP authenticated pipe: user %s\n",
+ domain->name, cli_credentials_get_unparsed_name(creds, talloc_tos())));
result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True,
SEC_FLAG_MAXIMUM_ALLOWED,
&conn->lsa_policy);
+ if (NT_STATUS_EQUAL(result, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+ invalidate_cm_connection(domain);
+ TALLOC_FREE(conn->lsa_pipe);
+ retry = true;
+ goto retry;
+ }
+
if (NT_STATUS_IS_OK(result)) {
goto done;
}
nt_errstr(result) ));
goto anonymous;
}
- result = cli_rpc_pipe_open_schannel_with_key
- (conn->cli, &ndr_table_lsarpc, NCACN_NP,
- domain->name, p_creds, &conn->lsa_pipe);
+
+ TALLOC_FREE(creds);
+ result = cli_rpc_pipe_open_schannel_with_creds(
+ conn->cli, &ndr_table_lsarpc, NCACN_NP, p_creds,
+ &conn->lsa_pipe);
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NETWORK_SESSION_EXPIRED)
+ && !retry) {
+ invalidate_cm_connection(domain);
+ retry = true;
+ goto retry;
+ }
if (!NT_STATUS_IS_OK(result)) {
DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for "
result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True,
SEC_FLAG_MAXIMUM_ALLOWED,
&conn->lsa_policy);
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+ invalidate_cm_connection(domain);
+ TALLOC_FREE(conn->lsa_pipe);
+ retry = true;
+ goto retry;
+ }
+
if (NT_STATUS_IS_OK(result)) {
goto done;
}
+ if (IS_AD_DC) {
+ /*
+ * Make sure we only use schannel as AD DC.
+ */
+ goto done;
+ }
+
DEBUG(10,("cm_connect_lsa: rpccli_lsa_open_policy failed, trying "
"anonymous\n"));
anonymous:
+ if (IS_AD_DC) {
+ /*
+ * Make sure we only use schannel as AD DC.
+ */
+ goto done;
+ }
+
if (lp_winbind_sealed_pipes() || lp_require_strong_key()) {
result = NT_STATUS_DOWNGRADE_DETECTED;
- DEBUG(1, ("Unwilling to make LSA connection to domain %s"
+ DEBUG(1, ("Unwilling to make LSA connection to domain %s "
"without connection level security, "
"must set 'winbind sealed pipes = false' and "
"'require strong key = false' to proceed: %s\n",
result = cli_rpc_pipe_open_noauth(conn->cli,
&ndr_table_lsarpc,
&conn->lsa_pipe);
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NETWORK_SESSION_EXPIRED)
+ && !retry) {
+ invalidate_cm_connection(domain);
+ retry = true;
+ goto retry;
+ }
+
if (!NT_STATUS_IS_OK(result)) {
goto done;
}
result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True,
SEC_FLAG_MAXIMUM_ALLOWED,
&conn->lsa_policy);
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+ invalidate_cm_connection(domain);
+ TALLOC_FREE(conn->lsa_pipe);
+ retry = true;
+ goto retry;
+ }
+
done:
if (!NT_STATUS_IS_OK(result)) {
- invalidate_cm_connection(conn);
+ invalidate_cm_connection(domain);
return result;
}
if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR) ||
NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) {
- invalidate_cm_connection(&domain->conn);
+ invalidate_cm_connection(domain);
status = cm_connect_lsa_tcp(domain, mem_ctx, cli);
}
if (NT_STATUS_IS_OK(status)) {
* we tried twice to connect via ncan_ip_tcp and schannel and
* failed - maybe it is a trusted domain we can't connect to ?
* do not try tcp next time - gd
+ *
+ * This also prevents NETLOGON over TCP
*/
domain->can_do_ncacn_ip_tcp = false;
}
}
/****************************************************************************
- Open the netlogon pipe to this DC. Use schannel if specified in client conf.
- session key stored in conn->netlogon_pipe->dc->sess_key.
+ Open the netlogon pipe to this DC.
****************************************************************************/
-NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
- struct rpc_pipe_client **cli)
+static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
+ enum dcerpc_transport_t transport,
+ struct rpc_pipe_client **cli)
{
- struct messaging_context *msg_ctx = winbind_messaging_context();
+ struct messaging_context *msg_ctx = server_messaging_context();
struct winbindd_cm_conn *conn;
NTSTATUS result;
enum netr_SchannelType sec_chan_type;
- const char *_account_name;
- const char *account_name;
- struct samr_Password current_nt_hash;
- struct samr_Password *previous_nt_hash = NULL;
- struct netlogon_creds_CredentialState *creds = NULL;
- bool ok;
+ struct cli_credentials *creds = NULL;
*cli = NULL;
- result = init_dc_connection_rpc(domain, true);
+ if (IS_AD_DC) {
+ if (domain->secure_channel_type == SEC_CHAN_NULL) {
+ /*
+ * Make sure we don't even try to
+ * connect to a foreign domain
+ * without a direct outbound trust.
+ */
+ return NT_STATUS_NO_TRUST_LSA_SECRET;
+ }
+ }
+
+ result = init_dc_connection_rpc(domain, domain->rodc);
if (!NT_STATUS_IS_OK(result)) {
return result;
}
}
TALLOC_FREE(conn->netlogon_pipe);
- conn->netlogon_flags = 0;
- TALLOC_FREE(conn->netlogon_creds);
+ TALLOC_FREE(conn->netlogon_creds_ctx);
- if ((!IS_DC) && (!domain->primary)) {
- goto no_schannel;
+ result = get_trust_credentials(domain, talloc_tos(), true, &creds);
+ if (!NT_STATUS_IS_OK(result)) {
+ DBG_DEBUG("No user available for domain %s when trying "
+ "schannel\n", domain->name);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
- ok = get_trust_pw_hash(domain->name,
- current_nt_hash.hash,
- &_account_name,
- &sec_chan_type);
- if (!ok) {
- DEBUG(1, ("get_trust_pw_hash failed for %s, "
- "unable to get NETLOGON credentials\n",
- domain->name));
+ if (cli_credentials_is_anonymous(creds)) {
+ DBG_WARNING("get_trust_credential only gave anonymous for %s, "
+ "unable to make get NETLOGON credentials\n",
+ domain->name);
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
- account_name = talloc_asprintf(talloc_tos(), "%s$", _account_name);
- if (account_name == NULL) {
- return NT_STATUS_NO_MEMORY;
+ sec_chan_type = cli_credentials_get_secure_channel_type(creds);
+ if (sec_chan_type == SEC_CHAN_NULL) {
+ if (transport == NCACN_IP_TCP) {
+ DBG_NOTICE("get_secure_channel_type gave SEC_CHAN_NULL "
+ "for %s, deny NCACN_IP_TCP and let the "
+ "caller fallback to NCACN_NP.\n",
+ domain->name);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ DBG_NOTICE("get_secure_channel_type gave SEC_CHAN_NULL for %s, "
+ "fallback to noauth on NCACN_NP.\n",
+ domain->name);
+
+ result = cli_rpc_pipe_open_noauth_transport(
+ conn->cli,
+ transport,
+ &ndr_table_netlogon,
+ &conn->netlogon_pipe);
+ if (!NT_STATUS_IS_OK(result)) {
+ invalidate_cm_connection(domain);
+ return result;
+ }
+
+ *cli = conn->netlogon_pipe;
+ return NT_STATUS_OK;
}
- result = rpccli_create_netlogon_creds(domain->dcname,
- domain->name,
- account_name,
- sec_chan_type,
- msg_ctx,
- domain,
- &conn->netlogon_creds);
+ result = rpccli_create_netlogon_creds_ctx(creds,
+ domain->dcname,
+ msg_ctx,
+ domain,
+ &conn->netlogon_creds_ctx);
if (!NT_STATUS_IS_OK(result)) {
DEBUG(1, ("rpccli_create_netlogon_creds failed for %s, "
"unable to create NETLOGON credentials: %s\n",
domain->name, nt_errstr(result)));
- SAFE_FREE(previous_nt_hash);
return result;
}
- result = rpccli_setup_netlogon_creds(conn->cli,
- conn->netlogon_creds,
- conn->netlogon_force_reauth,
- current_nt_hash,
- previous_nt_hash);
+ result = rpccli_connect_netlogon(
+ conn->cli, transport,
+ conn->netlogon_creds_ctx, conn->netlogon_force_reauth, creds,
+ &conn->netlogon_pipe);
conn->netlogon_force_reauth = false;
- SAFE_FREE(previous_nt_hash);
if (!NT_STATUS_IS_OK(result)) {
- DEBUG(1, ("rpccli_setup_netlogon_creds failed for %s, "
- "unable to setup NETLOGON credentials: %s\n",
- domain->name, nt_errstr(result)));
+ DBG_DEBUG("rpccli_connect_netlogon failed: %s\n",
+ nt_errstr(result));
return result;
}
- result = netlogon_creds_cli_get(conn->netlogon_creds,
- talloc_tos(),
- &creds);
- if (!NT_STATUS_IS_OK(result)) {
- DEBUG(1, ("netlogon_creds_cli_get failed for %s, "
- "unable to get NETLOGON credentials: %s\n",
- domain->name, nt_errstr(result)));
- return result;
+ *cli = conn->netlogon_pipe;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+Open a NETLOGON connection to a DC, suiteable for SamLogon calls.
+****************************************************************************/
+
+NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
+ struct rpc_pipe_client **cli)
+{
+ NTSTATUS status;
+
+ status = init_dc_connection_rpc(domain, domain->rodc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
- conn->netlogon_flags = creds->negotiate_flags;
- TALLOC_FREE(creds);
- no_schannel:
- if (!(conn->netlogon_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
- if (lp_winbind_sealed_pipes() || lp_require_strong_key()) {
- result = NT_STATUS_DOWNGRADE_DETECTED;
- DEBUG(1, ("Unwilling to make connection to domain %s"
- "without connection level security, "
- "must set 'winbind sealed pipes = false' and "
- "'require strong key = false' to proceed: %s\n",
- domain->name, nt_errstr(result)));
- invalidate_cm_connection(conn);
- return result;
+ if (domain->active_directory && domain->can_do_ncacn_ip_tcp) {
+ status = cm_connect_netlogon_transport(domain, NCACN_IP_TCP, cli);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) {
+ invalidate_cm_connection(domain);
+ status = cm_connect_netlogon_transport(domain, NCACN_IP_TCP, cli);
}
- result = cli_rpc_pipe_open_noauth(conn->cli,
- &ndr_table_netlogon,
- &conn->netlogon_pipe);
- if (!NT_STATUS_IS_OK(result)) {
- invalidate_cm_connection(conn);
- return result;
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
}
- *cli = conn->netlogon_pipe;
- return NT_STATUS_OK;
+ /*
+ * we tried twice to connect via ncan_ip_tcp and schannel and
+ * failed - maybe it is a trusted domain we can't connect to ?
+ * do not try tcp next time - gd
+ *
+ * This also prevents LSA over TCP
+ */
+ domain->can_do_ncacn_ip_tcp = false;
}
- /* Using the credentials from the first pipe, open a signed and sealed
- second netlogon pipe. The session key is stored in the schannel
- part of the new pipe auth struct.
- */
+ status = cm_connect_netlogon_transport(domain, NCACN_NP, cli);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
+ /*
+ * SMB2 session expired, needs reauthentication. Drop
+ * connection and retry.
+ */
+ invalidate_cm_connection(domain);
+ status = cm_connect_netlogon_transport(domain, NCACN_NP, cli);
+ }
- result = cli_rpc_pipe_open_schannel_with_key(
- conn->cli, &ndr_table_netlogon, NCACN_NP,
- domain->name,
- conn->netlogon_creds,
- &conn->netlogon_pipe);
- if (!NT_STATUS_IS_OK(result)) {
- DEBUG(3, ("Could not open schannel'ed NETLOGON pipe. Error "
- "was %s\n", nt_errstr(result)));
+ return status;
+}
- invalidate_cm_connection(conn);
- return result;
+NTSTATUS cm_connect_netlogon_secure(struct winbindd_domain *domain,
+ struct rpc_pipe_client **cli,
+ struct netlogon_creds_cli_context **ppdc)
+{
+ NTSTATUS status;
+
+ if (domain->secure_channel_type == SEC_CHAN_NULL) {
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
- *cli = conn->netlogon_pipe;
+ status = cm_connect_netlogon(domain, cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (domain->conn.netlogon_creds_ctx == NULL) {
+ return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+ }
+
+ *ppdc = domain->conn.netlogon_creds_ctx;
return NT_STATUS_OK;
}
}
TALLOC_FREE(freeit);
}
+
+void winbind_msg_disconnect_dc(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct winbindd_domain *domain;
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ if (domain->internal) {
+ continue;
+ }
+ invalidate_cm_connection(domain);
+ }
+}