Winbind daemon connection manager
Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2002
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
#include "winbindd.h"
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
/* Global list of connections. Initially a DLIST but can become a hash
table or whatever later. */
struct get_dc_name_cache *prev, *next;
};
+
+/*
+ find the DC for a domain using methods appropriate for a ADS domain
+*/
+static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
+{
+ ADS_STRUCT *ads;
+ const char *realm = domain;
+
+ if (strcasecmp(realm, lp_workgroup()) == 0) {
+ realm = lp_realm();
+ }
+
+ ads = ads_init(realm, domain, NULL);
+ if (!ads) {
+ return False;
+ }
+
+ /* we don't need to bind, just connect */
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
+
+ DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain));
+
+#ifdef HAVE_ADS
+ /* a full ads_connect() is actually overkill, as we don't srictly need
+ to do the SASL auth in order to get the info we need, but libads
+ doesn't offer a better way right now */
+ ads_connect(ads);
+#endif
+
+ if (!ads->config.realm) {
+ return False;
+ }
+
+ fstrcpy(srv_name, ads->config.ldap_server_name);
+ strupper(srv_name);
+ *dc_ip = ads->ldap_ip;
+ ads_destroy(&ads);
+
+ DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n",
+ srv_name, inet_ntoa(*dc_ip)));
+
+ return True;
+}
+
+/*
+ find the DC for a domain using methods appropriate for a RPC domain
+*/
+static BOOL cm_rpc_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
+{
+ struct in_addr *ip_list = NULL;
+ int count, i;
+ BOOL list_ordered;
+
+ if (!get_dc_list(domain, &ip_list, &count, &list_ordered)) {
+ struct in_addr pdc_ip;
+
+ if (!get_pdc_ip(domain, &pdc_ip)) {
+ DEBUG(3, ("Could not look up any DCs for domain %s\n",
+ domain));
+ return False;
+ }
+
+ ip_list = (struct in_addr *)malloc(sizeof(struct in_addr));
+
+ if (!ip_list)
+ return False;
+
+ ip_list[0] = pdc_ip;
+ count = 1;
+ }
+
+ /* Pick a nice close server, but only if the list was not ordered */
+ if (!list_ordered && (count > 1) ) {
+ qsort(ip_list, count, sizeof(struct in_addr), QSORT_CAST ip_compare);
+ }
+
+ for (i = 0; i < count; i++) {
+ if (is_zero_ip(ip_list[i]))
+ continue;
+
+ if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
+ *dc_ip = ip_list[i];
+ SAFE_FREE(ip_list);
+ return True;
+ }
+ }
+
+
+ SAFE_FREE(ip_list);
+
+ return False;
+}
+
+
static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
{
static struct get_dc_name_cache *get_dc_name_cache;
struct get_dc_name_cache *dcc;
- struct in_addr *ip_list, dc_ip;
- int count, i;
+ struct in_addr dc_ip;
+ BOOL ret;
/* Check the cache for previous lookups */
DLIST_ADD(get_dc_name_cache, dcc);
- /* Lookup domain controller name */
-
- if (!get_dc_list(False, domain, &ip_list, &count)) {
- DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
- return False;
- }
-
- /* Pick a nice close server */
- /* Look for DC on local net */
+ zero_ip(&dc_ip);
- for (i = 0; i < count; i++) {
- if (!is_local_net(ip_list[i]))
- continue;
-
- if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
- dc_ip = ip_list[i];
- goto done;
- }
- zero_ip(&ip_list[i]);
+ ret = False;
+ if (lp_security() == SEC_ADS) {
+ ret = cm_ads_find_dc(domain, &dc_ip, srv_name);
}
-
- /*
- * Secondly try and contact a random PDC/BDC.
- */
-
- i = (sys_random() % count);
-
- if (!is_zero_ip(ip_list[i]) &&
- name_status_find(domain, 0x1c, 0x20,
- ip_list[i], srv_name)) {
- dc_ip = ip_list[i];
- goto done;
+ if (!ret) {
+ /* fall back on rpc methods if the ADS methods fail */
+ ret = cm_rpc_find_dc(domain, &dc_ip, srv_name);
}
- zero_ip(&ip_list[i]); /* Tried and failed. */
-
- /* Finally return first DC that we can contact */
-
- for (i = 0; i < count; i++) {
- if (is_zero_ip(ip_list[i]))
- continue;
- if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
- dc_ip = ip_list[i];
- goto done;
- }
+ if (!ret) {
+ return False;
}
- /* No-one to talk to )-: */
- return False; /* Boo-hoo */
-
- done:
- /* We have the netbios name and IP address of a domain controller.
- Ideally we should sent a SAMLOGON request to determine whether
- the DC is alive and kicking. If we can catch a dead DC before
- performing a cli_connect() we can avoid a 30-second timeout. */
-
/* We have a name so make the cache entry positive now */
-
fstrcpy(dcc->srv_name, srv_name);
DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
*ip_out = dc_ip;
- SAFE_FREE(ip_list);
-
return True;
}
*password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
if (*username && **username) {
- if (!*domain || !**domain) {
+
+ if (!*domain || !**domain)
*domain = smb_xstrdup(lp_workgroup());
- }
- DEBUG(3, ("IPC$ connections done by user %s\\%s\n", *domain, *username));
+ if (!*password || !**password)
+ *password = smb_xstrdup("");
+
+ DEBUG(3, ("IPC$ connections done by user %s\\%s\n",
+ *domain, *username));
+
} else {
DEBUG(3, ("IPC$ connections done anonymously\n"));
*username = smb_xstrdup("");
/* Add an entry to the failed conneciton cache */
-static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn, NTSTATUS result) {
+static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn,
+ NTSTATUS result)
+{
struct failed_connection_cache *fcc;
SMB_ASSERT(!NT_STATUS_IS_OK(result));
+ /* Check we already aren't in the cache */
+
+ for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
+ if (strequal(fcc->domain_name, new_conn->domain)) {
+ DEBUG(10, ("domain %s already tried and failed\n",
+ fcc->domain_name));
+ return;
+ }
+ }
+
/* Create negative lookup cache entry for this domain and controller */
if (!(fcc = (struct failed_connection_cache *)
/* Open a connction to the remote server, cache failures for 30 seconds */
-static NTSTATUS cm_open_connection(const char *domain,const char *pipe_name,
+static NTSTATUS cm_open_connection(const char *domain, const int pipe_index,
struct winbindd_cm_conn *new_conn)
{
struct failed_connection_cache *fcc;
- extern pstring global_myname;
NTSTATUS result;
char *ipc_username, *ipc_domain, *ipc_password;
struct in_addr dc_ip;
+ int i;
+ BOOL retry = True;
ZERO_STRUCT(dc_ip);
fstrcpy(new_conn->domain, domain);
- fstrcpy(new_conn->pipe_name, pipe_name);
+ fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
/* Look for a domain controller for this domain. Negative results
are cached so don't bother applying the caching for this
cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
- new_conn->controller, global_myname, ipc_domain, ipc_username));
+ new_conn->controller, global_myname(), ipc_domain, ipc_username));
- result = cli_full_connection(&(new_conn->cli), global_myname, new_conn->controller,
- &dc_ip, 0, "IPC$",
- "IPC", ipc_username, ipc_domain,
- ipc_password, strlen(ipc_password));
+ for (i = 0; retry && (i < 3); i++) {
+
+ if (!secrets_named_mutex(new_conn->controller, 10)) {
+ DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
+ continue;
+ }
+
+ result = cli_full_connection(&new_conn->cli, global_myname(), new_conn->controller,
+ &dc_ip, 0, "IPC$", "IPC", ipc_username, ipc_domain,
+ ipc_password, 0, &retry);
+
+ secrets_named_mutex_release(new_conn->controller);
+
+ if (NT_STATUS_IS_OK(result))
+ break;
+ }
SAFE_FREE(ipc_username);
SAFE_FREE(ipc_domain);
return result;
}
- if (!cli_nt_session_open (new_conn->cli, pipe_name)) {
+ if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
result = NT_STATUS_PIPE_NOT_AVAILABLE;
+ /*
+ * only cache a failure if we are not trying to open the
+ * **win2k** specific lsarpc UUID. This could be an NT PDC
+ * and therefore a failure is normal. This should probably
+ * be abstracted to a check for 2k specific pipes and wondering
+ * if the PDC is an NT4 box. but since there is only one 2k
+ * specific UUID right now, i'm not going to bother. --jerry
+ */
+ if ( !is_win2k_pipe(pipe_index) )
add_failed_connection_entry(new_conn, result);
cli_shutdown(new_conn->cli);
return result;
return False;
}
- if (!conn->cli || !conn->cli->initialised) {
- DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
+ if (!conn->cli) {
+ DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
+ conn->controller, conn->domain, conn->pipe_name));
+ smb_panic("connection_ok: conn->cli was null!");
+ return False;
+ }
+
+ if (!conn->cli->initialised) {
+ DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
conn->controller, conn->domain, conn->pipe_name));
+ smb_panic("connection_ok: conn->cli->initialised is False!");
return False;
}
if (conn->cli) {
cli_shutdown(conn->cli);
}
+ ZERO_STRUCT(conn_temp);
conn_temp.next = conn->next;
DLIST_REMOVE(cm_conns, conn);
SAFE_FREE(conn);
}
if (!conn) {
- if (!(conn = (struct winbindd_cm_conn *) malloc(sizeof(struct winbindd_cm_conn))))
+ if (!(conn = malloc(sizeof(*conn))))
return NT_STATUS_NO_MEMORY;
ZERO_STRUCTP(conn);
- if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, pipe_name, conn))) {
+ if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
- domain, pipe_name, get_nt_error_msg(result)));
+ domain, pipe_name, nt_errstr(result)));
SAFE_FREE(conn);
return result;
}
return NT_STATUS_OK;
}
+
+/**********************************************************************************
+**********************************************************************************/
+
+BOOL cm_check_for_native_mode_win2k( const char *domain )
+{
+ NTSTATUS result;
+ struct winbindd_cm_conn conn;
+ DS_DOMINFO_CTR ctr;
+ BOOL ret = False;
+
+ ZERO_STRUCT( conn );
+ ZERO_STRUCT( ctr );
+
+
+ if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) )
+ {
+ DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n",
+ domain, nt_errstr(result)));
+ return False;
+ }
+
+ if ( conn.cli ) {
+ if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli,
+ conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) )
+ {
+ ret = False;
+ goto done;
+ }
+ }
+
+ if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING)
+ && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
+ {
+ ret = True;
+ }
+
+done:
+ if ( conn.cli )
+ cli_shutdown( conn.cli );
+
+ return ret;
+}
+
+
+
/* Return a LSA policy handle on a domain */
-CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
+CLI_POLICY_HND *cm_get_lsa_handle(const char *domain)
{
struct winbindd_cm_conn *conn;
uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
return NULL;
}
+
+ /* This *shitty* code needs scrapping ! JRA */
+ if (policy_handle_is_valid(&conn->pol)) {
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+ return &hnd;
+ }
result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
des_access, &conn->pol);
return NULL;
}
+ /* This *shitty* code needs scrapping ! JRA */
+ if (policy_handle_is_valid(&conn->pol)) {
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+ return &hnd;
+ }
result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
des_access, &conn->pol);
/* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
netlogon pipe as no handle is returned. */
-NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
+NTSTATUS cm_get_netlogon_cli(const char *domain, const unsigned char *trust_passwd,
struct cli_state **cli)
{
NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
struct winbindd_cm_conn *conn;
+ uint32 neg_flags = 0x000001ff;
if (!cli) {
return NT_STATUS_INVALID_PARAMETER;
return result;
}
- result = new_cli_nt_setup_creds(conn->cli, trust_passwd);
+ result = cli_nt_setup_creds(conn->cli, get_sec_chan(), trust_passwd, &neg_flags, 2);
if (!NT_STATUS_IS_OK(result)) {
DEBUG(0, ("error connecting to domain password server: %s\n",
- get_nt_error_msg(result)));
+ nt_errstr(result)));
/* Hit the cache code again. This cleans out the old connection and gets a new one */
if (conn->cli->fd == -1) {
}
/* Try again */
- result = new_cli_nt_setup_creds(conn->cli, trust_passwd);
+ result = cli_nt_setup_creds( conn->cli, get_sec_chan(),trust_passwd, &neg_flags, 2);
}
if (!NT_STATUS_IS_OK(result)) {