r17943: The horror, the horror. Add KDC site support by
[tprouty/samba.git] / source / nsswitch / winbindd_cm.c
index 177ac54d3ed93fa6c44f2026bbc8164d2e64e115..a09faaed949bc588cd1c2bb162b4059b0b2943d4 100644 (file)
@@ -75,9 +75,9 @@
 
 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
 {
-       *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
-       *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
-       *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
+       *username = (char *)secrets_fetch(SECRETS_AUTH_USER, NULL);
+       *domain = (char *)secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
+       *password = (char *)secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
        
        if (*username && **username) {
 
@@ -177,7 +177,6 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
        char *ipc_username, *ipc_domain, *ipc_password;
 
        BOOL got_mutex;
-       BOOL add_failed_connection = True;
 
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
 
@@ -215,7 +214,7 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
                goto done;
        }
 
-       if ((*cli = cli_initialise(NULL)) == NULL) {
+       if ((*cli = cli_initialise()) == NULL) {
                DEBUG(1, ("Could not cli_initialize\n"));
                result = NT_STATUS_NO_MEMORY;
                goto done;
@@ -233,6 +232,7 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
            (peeraddr_in->sin_family != PF_INET))
        {
                DEBUG(0,("cm_prepare_connection: %s\n", strerror(errno)));
+               result = NT_STATUS_UNSUCCESSFUL;
                goto done;
        }
 
@@ -246,6 +246,7 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
                if (!cli_session_request(*cli, &calling, &called)) {
                        DEBUG(8, ("cli_session_request failed for %s\n",
                                  controller));
+                       result = NT_STATUS_UNSUCCESSFUL;
                        goto done;
                }
        }
@@ -254,10 +255,9 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
 
        if (!cli_negprot(*cli)) {
                DEBUG(1, ("cli_negprot failed\n"));
-               cli_shutdown(*cli);
+               result = NT_STATUS_UNSUCCESSFUL;
                goto done;
        }
-
                        
        if ((*cli)->protocol >= PROTOCOL_NT1 && (*cli)->capabilities & CAP_EXTENDED_SECURITY) {
                ADS_STATUS ads_status;
@@ -294,7 +294,7 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
 
                DEBUG(5, ("connecting to %s from %s with username "
                          "[%s]\\[%s]\n",  controller, global_myname(),
-                         machine_account, machine_password));
+                         lp_workgroup(), machine_account));
 
                ads_status = cli_session_setup_spnego(*cli,
                                                      machine_account, 
@@ -326,10 +326,11 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
                          "[%s]\\[%s]\n",  controller, global_myname(),
                          ipc_domain, ipc_username));
 
-               if (cli_session_setup(*cli, ipc_username,
-                                     ipc_password, strlen(ipc_password)+1,
-                                     ipc_password, strlen(ipc_password)+1,
-                                     ipc_domain)) {
+               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. */
                        cli_init_creds(*cli, ipc_username, ipc_domain, ipc_password);
                        goto session_setup_done;
@@ -341,7 +342,8 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
 
        /* Fall back to anonymous connection, this might fail later */
 
-       if (cli_session_setup(*cli, "", NULL, 0, NULL, 0, "")) {
+       if (NT_STATUS_IS_OK(cli_session_setup(*cli, "", NULL, 0,
+                                             NULL, 0, ""))) {
                DEBUG(5, ("Connected anonymously\n"));
                cli_init_creds(*cli, "", "", "");
                goto session_setup_done;
@@ -360,7 +362,7 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
 
        /* cache the server name for later connections */
 
-       saf_store( (*cli)->domain, (*cli)->desthost );
+       saf_store( domain->name, (*cli)->desthost );
 
        if (!cli_send_tconX(*cli, "IPC$", "IPC", "", 0)) {
 
@@ -371,7 +373,6 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
                if (NT_STATUS_IS_OK(result))
                        result = NT_STATUS_UNSUCCESSFUL;
 
-               cli_shutdown(*cli);
                goto done;
        }
 
@@ -385,7 +386,6 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
        }
 
        result = NT_STATUS_OK;
-       add_failed_connection = False;
 
  done:
        if (got_mutex) {
@@ -399,8 +399,12 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
        SAFE_FREE(ipc_domain);
        SAFE_FREE(ipc_password);
 
-       if (add_failed_connection) {
+       if (!NT_STATUS_IS_OK(result)) {
                add_failed_connection_entry(domain->name, controller, result);
+               if ((*cli) != NULL) {
+                       cli_shutdown(*cli);
+                       *cli = NULL;
+               }
        }
 
        return result;
@@ -578,61 +582,71 @@ static BOOL receive_getdc_response(struct in_addr dc_ip,
  convert an ip to a name
 *******************************************************************/
 
-static void dcip_to_name( const char *domainname, const char *realm, 
+static BOOL dcip_to_name( const char *domainname, const char *realm, 
                           const DOM_SID *sid, struct in_addr ip, fstring name )
 {
+       struct ip_service ip_list;
 
-       /* try GETDC requests first */
-       
-       if (send_getdc_request(ip, domainname, sid)) {
-               int i;
-               smb_msleep(100);
-               for (i=0; i<5; i++) {
-                       if (receive_getdc_response(ip, domainname, name))
-                               return;
-                       smb_msleep(500);
-               }
-       }
-
-       /* try node status request */
-
-       if ( name_status_find(domainname, 0x1c, 0x20, ip, name) )
-               return;
-
-       /* backup in case the netbios stuff fails */
-
-       fstrcpy( name, inet_ntoa(ip) );
+       ip_list.ip = ip;
+       ip_list.port = 0;
 
 #ifdef WITH_ADS
-       /* for active directory servers, try to get the ldap server name.
-          None of these failure should be considered critical for now */
+       /* For active directory servers, try to get the ldap server name.
+          None of these failures should be considered critical for now */
 
-       if ( lp_security() == SEC_ADS ) 
-       {
+       if ( lp_security() == SEC_ADS ) {
                ADS_STRUCT *ads;
-               ADS_STATUS status;
 
-               ads = ads_init( realm, domainname, NULL );
+               ads = ads_init(realm, domainname, NULL);
                ads->auth.flags |= ADS_AUTH_NO_BIND;
 
-               if ( !ads_try_connect( ads, inet_ntoa(ip), LDAP_PORT ) )  {
-                       ads_destroy( &ads );
-                       return;
-               }
-
-               status = ads_server_info(ads);
-               if ( !ADS_ERR_OK(status) ) {
+               if (ads_try_connect( ads, inet_ntoa(ip) ) )  {
+                       const char *sitename = sitename_fetch();
+                       /* We got a cldap packet. */
+                       fstrcpy(name, ads->config.ldap_server_name);
+                       namecache_store(name, 0x20, 1, &ip_list);
+
+#ifdef HAVE_KRB5
+                       if ((ads->config.flags & ADS_KDC) && sitename) {
+                               /* We're going to use this KDC for this realm/domain.
+                                  If we are using sites, then force the krb5 libs
+                                  to use this KDC. */
+
+                               create_local_private_krb5_conf_for_domain(realm,
+                                                               domainname,
+                                                               ip);
+                       }
+#endif
+                       SAFE_FREE(sitename);
                        ads_destroy( &ads );
-                       return;
+                       return True;
                }
 
-               fstrcpy(name, ads->config.ldap_server_name);
-
                ads_destroy( &ads );
        }
 #endif
 
-       return;
+       /* try GETDC requests next */
+       
+       if (send_getdc_request(ip, domainname, sid)) {
+               int i;
+               smb_msleep(100);
+               for (i=0; i<5; i++) {
+                       if (receive_getdc_response(ip, domainname, name)) {
+                               namecache_store(name, 0x20, 1, &ip_list);
+                               return True;
+                       }
+                       smb_msleep(500);
+               }
+       }
+
+       /* try node status request */
+
+       if ( name_status_find(domainname, 0x1c, 0x20, ip, name) ) {
+               namecache_store(name, 0x20, 1, &ip_list);
+               return True;
+       }
+       return False;
 }
 
 /*******************************************************************
@@ -649,7 +663,7 @@ static BOOL get_dcs(TALLOC_CTX *mem_ctx, const struct winbindd_domain *domain,
        int     iplist_size = 0;
        int     i;
        BOOL    is_our_domain;
-
+       enum security_types sec = (enum security_types)lp_security();
 
        is_our_domain = strequal(domain->name, lp_workgroup());
 
@@ -662,13 +676,27 @@ static BOOL get_dcs(TALLOC_CTX *mem_ctx, const struct winbindd_domain *domain,
                return True;
        }
 
+#ifdef WITH_ADS
+       if (sec == SEC_ADS) {
+               /* We need to make sure we know the local site before
+                  doing any DNS queries, as this will restrict the
+                  get_sorted_dc_list() call below to only fetching
+                  DNS records for the correct site. */
+
+               /* Find any DC to get the site record.
+                  We deliberately don't care about the
+                  return here. */
+               get_dc_name(domain->name, lp_realm(), dcname, &ip);
+        }
+#endif
+
        /* try standard netbios queries first */
 
        get_sorted_dc_list(domain->name, &ip_list, &iplist_size, False);
 
        /* check for security = ads and use DNS if we can */
 
-       if ( iplist_size==0 && lp_security() == SEC_ADS ) 
+       if ( iplist_size==0 && sec == SEC_ADS ) 
                get_sorted_dc_list(domain->alt_name, &ip_list, &iplist_size, True);
 
        /* FIXME!! this is where we should re-insert the GETDC requests --jerry */
@@ -702,6 +730,7 @@ static BOOL find_new_dc(TALLOC_CTX *mem_ctx,
 
        int i, fd_index;
 
+ again:
        if (!get_dcs(mem_ctx, domain, &dcs, &num_dcs) || (num_dcs == 0))
                return False;
 
@@ -721,6 +750,9 @@ static BOOL find_new_dc(TALLOC_CTX *mem_ctx,
        if ((num_dcnames == 0) || (num_dcnames != num_addrs))
                return False;
 
+       if ((addrs == NULL) || (dcnames == NULL))
+               return False;
+
        if ( !open_any_socket_out(addrs, num_addrs, 10000, &fd_index, fd) ) 
        {
                for (i=0; i<num_dcs; i++) {
@@ -732,15 +764,22 @@ static BOOL find_new_dc(TALLOC_CTX *mem_ctx,
 
        *addr = addrs[fd_index];
 
-       /* if we have no name on the server or just an IP address for 
-          the name, now try to get the name */
-
-       if ( is_ipaddress(dcnames[fd_index]) || *dcnames[fd_index] == '\0' )
-               dcip_to_name( domain->name, domain->alt_name, &domain->sid, addr->sin_addr, dcname );
-       else
+       if (*dcnames[fd_index] != '\0' && !is_ipaddress(dcnames[fd_index])) {
+               /* Ok, we've got a name for the DC */
                fstrcpy(dcname, dcnames[fd_index]);
+               return True;
+       }
 
-       return True;
+       /* Try to figure out the name */
+       if (dcip_to_name( domain->name, domain->alt_name, &domain->sid,
+                         addr->sin_addr, dcname )) {
+               return True;
+       }
+
+       /* We can not continue without the DC's name */
+       add_failed_connection_entry(domain->name, dcs[fd_index].name,
+                                   NT_STATUS_UNSUCCESSFUL);
+       goto again;
 }
 
 static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
@@ -766,8 +805,14 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
                        struct in_addr ip;
 
                        ip = *interpret_addr2( saf_servername );
-                       dcip_to_name( domain->name, domain->alt_name, &domain->sid, ip, saf_name );
-                       fstrcpy( domain->dcname, saf_name );
+                       if (dcip_to_name( domain->name, domain->alt_name,
+                                         &domain->sid, ip, saf_name )) {
+                               fstrcpy( domain->dcname, saf_name );
+                       } else {
+                               add_failed_connection_entry(
+                                       domain->name, saf_servername,
+                                       NT_STATUS_UNSUCCESSFUL);
+                       }
                } 
                else 
                {
@@ -784,26 +829,32 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
 
                result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
 
-               if ((strlen(domain->dcname) > 0) 
-                       && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, domain->dcname)) 
-                       && (resolve_name(domain->dcname, &domain->dcaddr.sin_addr, 0x20))) 
+               if ((strlen(domain->dcname) > 0)
+                       && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, domain->dcname))
+                       && (resolve_name(domain->dcname, &domain->dcaddr.sin_addr, 0x20)))
                {
                        struct sockaddr_in *addrs = NULL;
                        int num_addrs = 0;
                        int dummy = 0;
 
-
                        add_sockaddr_to_array(mem_ctx, domain->dcaddr.sin_addr, 445, &addrs, &num_addrs);
                        add_sockaddr_to_array(mem_ctx, domain->dcaddr.sin_addr, 139, &addrs, &num_addrs);
 
                        if (!open_any_socket_out(addrs, num_addrs, 10000, &dummy, &fd)) {
+                               domain->online = False;
                                fd = -1;
                        }
                }
 
                if ((fd == -1) 
-                       && !find_new_dc(mem_ctx, domain, domain->dcname, &domain->dcaddr, &fd)) 
+                       && !find_new_dc(mem_ctx, domain, domain->dcname, &domain->dcaddr, &fd))
                {
+                       /* This is the one place where we will
+                          set the global winbindd offline state
+                          to true, if a "WINBINDD_OFFLINE" entry
+                          is found in the winbindd cache. */
+                       set_global_winbindd_state_offline();
+                       domain->online = False;
                        break;
                }
 
@@ -816,11 +867,19 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
                        break;
        }
 
+       if (NT_STATUS_IS_OK(result)) {
+               if (domain->online == False) {
+                       /* We're changing state from offline to online. */
+                       set_global_winbindd_state_online();
+               }
+               domain->online = True;
+       }
+
        talloc_destroy(mem_ctx);
        return result;
 }
 
-/* Return true if a connection is still alive */
+/* Close down all open pipes on a connection. */
 
 void invalidate_cm_connection(struct winbindd_cm_conn *conn)
 {
@@ -1259,7 +1318,7 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        /* Fall back to schannel if it's a W2K pre-SP1 box. */
 
        if (!cm_get_schannel_dcinfo(domain, &p_dcinfo)) {
-               DEBUG(10, ("cm_connect_sam: Could not get schannel auth info "
+               DEBUG(10, ("cm_connect_lsa: Could not get schannel auth info "
                           "for domain %s, trying anon\n", conn->cli->domain));
                goto anonymous;
        }
@@ -1424,7 +1483,9 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
        if (conn->netlogon_pipe == NULL) {
                DEBUG(3, ("Could not open schannel'ed NETLOGON pipe. Error "
                          "was %s\n", nt_errstr(result)));
-               return result;
+                         
+               /* make sure we return something besides OK */
+               return !NT_STATUS_IS_OK(result) ? result : NT_STATUS_PIPE_NOT_AVAILABLE;
        }
 
        *cli = conn->netlogon_pipe;