Fix winbindd coredump. Remember to set a ** pointer to null before
[nivanova/samba-autobuild/.git] / source3 / nsswitch / winbindd_cm.c
index 5f477c78f79c3d7ab3cc97191313d6f4c2427564..02fd15e06917afc5b7ca9d427d0a0dc5046c1536 100644 (file)
@@ -72,6 +72,7 @@ struct winbindd_cm_conn {
        fstring domain;
        fstring controller;
        fstring pipe_name;
+       size_t mutex_ref_count;
        struct cli_state *cli;
        POLICY_HND pol;
 };
@@ -90,7 +91,6 @@ struct get_dc_name_cache {
        struct get_dc_name_cache *prev, *next;
 };
 
-
 /*
   find the DC for a domain using methods appropriate for a ADS domain
 */
@@ -99,14 +99,12 @@ static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring sr
        ADS_STRUCT *ads;
        const char *realm = domain;
 
-       if (strcasecmp(realm, lp_workgroup()) == 0) {
+       if (strcasecmp(realm, lp_workgroup()) == 0)
                realm = lp_realm();
-       }
 
        ads = ads_init(realm, domain, NULL);
-       if (!ads) {
+       if (!ads)
                return False;
-       }
 
        /* we don't need to bind, just connect */
        ads->auth.flags |= ADS_AUTH_NO_BIND;
@@ -120,9 +118,8 @@ static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring sr
        ads_connect(ads);
 #endif
 
-       if (!ads->config.realm) {
+       if (!ads->config.realm)
                return False;
-       }
 
        fstrcpy(srv_name, ads->config.ldap_server_name);
        strupper(srv_name);
@@ -135,90 +132,6 @@ static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring sr
        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, exclude_ip;
-       int count, i;
-
-       zero_ip(&exclude_ip);
-
-       /* Lookup domain controller name. Try the real PDC first to avoid
-          SAM sync delays */
-
-       if (get_dc_list(True, domain, &ip_list, &count)) {
-               if (name_status_find(domain, 0x1c, 0x20, ip_list[0], srv_name)) {
-                       *dc_ip = ip_list[0];
-                       SAFE_FREE(ip_list);
-                       return True;
-               }
-               /* Didn't get name, remember not to talk to this DC. */
-               exclude_ip = ip_list[0];
-               SAFE_FREE(ip_list);
-       }
-
-       if (!get_dc_list(False, domain, &ip_list, &count)) {
-               DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
-               return False;
-       }
-
-       /* Remove the entry we've already failed with (should be the PDC). */
-       for (i = 0; i < count; i++) {
-               if (ip_equal( exclude_ip, ip_list[i]))
-                       zero_ip(&ip_list[i]);
-       }
-
-       /* Pick a nice close server */
-       /* Look for DC on local net */
-       for (i = 0; i < count; i++) {
-               if (is_zero_ip(ip_list[i]))
-                       continue;
-
-               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];
-                       SAFE_FREE(ip_list);
-                       return True;
-               }
-               zero_ip(&ip_list[i]);
-       }
-
-       /*
-        * 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];
-               SAFE_FREE(ip_list);
-               return True;
-       }
-       zero_ip(&ip_list[i]); /* Tried and failed. */
-
-       /* Finally return first DC that we can contact using a node
-          status */
-       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)
@@ -278,17 +191,16 @@ static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr
        zero_ip(&dc_ip);
 
        ret = False;
-       if (lp_security() == SEC_ADS) {
+       if (lp_security() == SEC_ADS)
                ret = cm_ads_find_dc(domain, &dc_ip, srv_name);
-       }
+
        if (!ret) {
                /* fall back on rpc methods if the ADS methods fail */
-               ret = cm_rpc_find_dc(domain, &dc_ip, srv_name);
+               ret = rpc_find_dc(domain, srv_name, &dc_ip);
        }
 
-       if (!ret) {
+       if (!ret)
                return False;
-       }
 
        /* We have a name so make the cache entry positive now */
        fstrcpy(dcc->srv_name, srv_name);
@@ -316,11 +228,16 @@ static void cm_get_ipc_userpass(char **username, char **domain, char **password)
        *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("");
@@ -388,7 +305,6 @@ 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;
@@ -446,19 +362,20 @@ static NTSTATUS cm_open_connection(const char *domain, const int pipe_index,
        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));
 
        for (i = 0; retry && (i < 3); i++) {
-               
-               if (!secrets_named_mutex(new_conn->controller, 10)) {
+               BOOL got_mutex;
+               if (!(got_mutex = secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
                        DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
+                       result = NT_STATUS_POSSIBLE_DEADLOCK;
                        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);
-
+               
+               result = cli_full_connection(&new_conn->cli, global_myname(), new_conn->controller, 
+                                            &dc_ip, 0, "IPC$", "IPC", ipc_username, ipc_domain, 
+                                            ipc_password, CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK, &retry);
+               
                secrets_named_mutex_release(new_conn->controller);
 
                if (NT_STATUS_IS_OK(result))
@@ -485,7 +402,7 @@ static NTSTATUS cm_open_connection(const char *domain, const int pipe_index,
                 * specific UUID right now, i'm not going to bother.  --jerry
                 */
                if ( !is_win2k_pipe(pipe_index) )
-               add_failed_connection_entry(new_conn, result);
+                       add_failed_connection_entry(new_conn, result);
                cli_shutdown(new_conn->cli);
                return result;
        }
@@ -527,18 +444,20 @@ static BOOL connection_ok(struct winbindd_cm_conn *conn)
 
 /* Get a connection to the remote DC and open the pipe.  If there is already a connection, use that */
 
-static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out) 
+static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name,
+               struct winbindd_cm_conn **conn_out) 
 {
        struct winbindd_cm_conn *conn, conn_temp;
        NTSTATUS result;
 
+       *conn_out = NULL;
+
        for (conn = cm_conns; conn; conn = conn->next) {
                if (strequal(conn->domain, domain) && 
                    strequal(conn->pipe_name, pipe_name)) {
                        if (!connection_ok(conn)) {
-                               if (conn->cli) {
+                               if (conn->cli)
                                        cli_shutdown(conn->cli);
-                               }
                                ZERO_STRUCT(conn_temp);
                                conn_temp.next = conn->next;
                                DLIST_REMOVE(cm_conns, conn);
@@ -584,8 +503,7 @@ BOOL cm_check_for_native_mode_win2k( const char *domain )
        ZERO_STRUCT( ctr );
        
        
-       if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) 
-       {
+       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;
@@ -593,18 +511,15 @@ BOOL cm_check_for_native_mode_win2k( const char *domain )
        
        if ( conn.cli ) {
                if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli, 
-                       conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) 
-               {
+                               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) )
-       {
+                       && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
                ret = True;
-       }
 
 done:
        if ( conn.cli )
@@ -617,7 +532,7 @@ done:
 
 /* 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;
@@ -626,9 +541,8 @@ CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
 
        /* Look for existing connections */
 
-       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
+       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)) {
@@ -643,9 +557,8 @@ CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
        if (!NT_STATUS_IS_OK(result)) {
                /* Hit the cache code again.  This cleans out the old connection and gets a new one */
                if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
-                       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
+                       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
                                return NULL;
-                       }
 
                        result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
                                                     des_access, &conn->pol);
@@ -676,9 +589,8 @@ CLI_POLICY_HND *cm_get_sam_handle(char *domain)
 
        /* Look for existing connections */
 
-       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
+       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
                return NULL;
-       }
        
        /* This *shitty* code needs scrapping ! JRA */
        if (policy_handle_is_valid(&conn->pol)) {
@@ -692,9 +604,8 @@ CLI_POLICY_HND *cm_get_sam_handle(char *domain)
        if (!NT_STATUS_IS_OK(result)) {
                /* Hit the cache code again.  This cleans out the old connection and gets a new one */
                if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
-                       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
+                       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
                                return NULL;
-                       }
 
                        result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
                                                  des_access, &conn->pol);
@@ -944,37 +855,55 @@ CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
 /* 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, 
+                            uint32 sec_channel_type,
                             struct cli_state **cli)
 {
        NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
        struct winbindd_cm_conn *conn;
        uint32 neg_flags = 0x000001ff;
+       fstring lock_name;
+       BOOL got_mutex;
 
-       if (!cli) {
+       if (!cli)
                return NT_STATUS_INVALID_PARAMETER;
-       }
 
-       /* Open an initial conection */
+       /* Open an initial conection - keep the mutex. */
 
-       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
+       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn)))
                return result;
-       }
        
-       result = cli_nt_setup_creds(conn->cli, get_sec_chan(), trust_passwd, &neg_flags, 2);
+       snprintf(lock_name, sizeof(lock_name), "NETLOGON\\%s", conn->controller);
 
+       if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
+               DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
+       }
+                       
+       result = cli_nt_setup_creds(conn->cli, sec_channel_type, trust_passwd, &neg_flags, 2);
+       
+       if (got_mutex)
+               secrets_named_mutex_release(lock_name);
+                       
        if (!NT_STATUS_IS_OK(result)) {
                DEBUG(0, ("error connecting to domain password server: %s\n",
                          nt_errstr(result)));
                
                /* Hit the cache code again.  This cleans out the old connection and gets a new one */
                if (conn->cli->fd == -1) {
-                       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
+                       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn)))
                                return result;
+                       
+                       snprintf(lock_name, sizeof(lock_name), "NETLOGON\\%s", conn->controller);
+                       if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
+                               DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
                        }
                        
                        /* Try again */
-                       result = cli_nt_setup_creds( conn->cli, get_sec_chan(),trust_passwd, &neg_flags, 2);
+                       result = cli_nt_setup_creds( conn->cli, sec_channel_type,trust_passwd, &neg_flags, 2);
+                       
+                       if (got_mutex)
+                               secrets_named_mutex_release(lock_name);
                }
                
                if (!NT_STATUS_IS_OK(result)) {