ctdb-protocol: Fix marshalling for ctdb_reply_control
[samba.git] / source3 / winbindd / winbindd_cm.c
index 25150c22e50a5d5dd52529311e71410aeb7afc81..b24a5f253f721ff302671f737bf7a3a4e3897447 100644 (file)
@@ -99,7 +99,8 @@ static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain, bool
 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.
@@ -266,7 +267,7 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
                _exit(1);
        }
 
-       if ((!get_dcs(mem_ctx, domain, &dcs, &num_dcs)) || (num_dcs == 0)) {
+       if ((!get_dcs(mem_ctx, domain, &dcs, &num_dcs, 0)) || (num_dcs == 0)) {
                /* Still offline ? Can't find DC's. */
                messaging_send_buf(winbind_messaging_context(),
                                   pid_to_procid(parent_pid),
@@ -769,7 +770,8 @@ static bool cm_is_ipc_credentials(struct cli_credentials *creds)
 
 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;
@@ -814,13 +816,17 @@ static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
        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)) {
@@ -903,7 +909,6 @@ static NTSTATUS get_trust_credentials(struct winbindd_domain *domain,
        struct cli_credentials *creds;
        NTSTATUS status;
        bool force_machine_account = false;
-       bool ok;
 
        /* If we are a DC and this is not our own domain */
 
@@ -937,24 +942,7 @@ static NTSTATUS get_trust_credentials(struct winbindd_domain *domain,
                goto ipc_fallback;
        }
 
-       if (domain->primary && lp_security() == SEC_ADS) {
-               cli_credentials_set_kerberos_state(creds,
-                                                  CRED_AUTO_USE_KERBEROS);
-       } else if (domain->active_directory) {
-               cli_credentials_set_kerberos_state(creds,
-                                                  CRED_MUST_USE_KERBEROS);
-       } else {
-               cli_credentials_set_kerberos_state(creds,
-                                                  CRED_DONT_USE_KERBEROS);
-       }
-
-       /*
-        * When we contact our own domain and get a list of the trusted domain
-        * we have the information if we are able to contact the DC with
-        * with our machine account password.
-        */
-       ok = winbindd_can_contact_domain(domain);
-       if (!ok) {
+       if (creds_domain != domain) {
                /*
                 * We can only use schannel against a direct trust
                 */
@@ -1001,6 +989,8 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
        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();
 
@@ -1040,8 +1030,7 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                goto done;
        }
 
-       *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);
@@ -1112,6 +1101,10 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                  machine_domain, machine_account,
                  machine_principal, machine_realm));
 
+       if (cli_credentials_is_anonymous(creds)) {
+               goto anon_fallback;
+       }
+
        winbindd_set_locator_kdc_envs(domain);
 
        result = cli_session_setup_creds(*cli, creds);
@@ -1119,6 +1112,11 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                goto session_setup_done;
        }
 
+       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
@@ -1130,10 +1128,6 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
            || NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS)
            || NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE))
        {
-               if (cli_credentials_is_anonymous(creds)) {
-                       goto done;
-               }
-
                if (!cm_is_ipc_credentials(creds)) {
                        goto ipc_fallback;
                }
@@ -1145,15 +1139,13 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                goto anon_fallback;
        }
 
-       DEBUG(4, ("authenticated session setup failed with %s\n",
-               nt_errstr(result)));
-
        goto done;
 
  ipc_fallback:
        TALLOC_FREE(creds);
-       result = cm_get_ipc_credentials(talloc_tos(), &creds);
-       if (!NT_STATUS_IS_OK(result)) {
+       tmp_status = cm_get_ipc_credentials(talloc_tos(), &creds);
+       if (!NT_STATUS_IS_OK(tmp_status)) {
+               result = tmp_status;
                goto done;
        }
 
@@ -1173,6 +1165,11 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                goto session_setup_done;
        }
 
+       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
@@ -1187,9 +1184,6 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                goto anon_fallback;
        }
 
-       DEBUG(4, ("authenticated session setup failed with %s\n",
-               nt_errstr(result)));
-
        goto done;
 
  anon_fallback:
@@ -1200,7 +1194,7 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
        }
 
        /* 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 ));
 
@@ -1210,6 +1204,9 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                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;
 
@@ -1227,17 +1224,17 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                smbXcli_session_set_disconnect_expired((*cli)->smb2.session);
        }
 
-       result = cli_tree_connect(*cli, "IPC$", "IPC", "", 0);
-
+       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);
        }
 
@@ -1252,7 +1249,13 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
        TALLOC_FREE(mutex);
        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);
@@ -1327,12 +1330,13 @@ static bool add_sockaddr_to_array(TALLOC_CTX *mem_ctx,
 
 /*******************************************************************
  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;
@@ -1364,6 +1368,8 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
 
                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)) {
@@ -1375,7 +1381,7 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
                        }
                        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)) {
@@ -1456,7 +1462,8 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
 *******************************************************************/
 
 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;
@@ -1470,7 +1477,7 @@ static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
 
        /* 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) )
        {
@@ -1587,7 +1594,8 @@ static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
 
 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;
@@ -1606,7 +1614,7 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx,
        *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++) {
@@ -1654,7 +1662,7 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx,
        }
 
        /* 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;
        }
 
@@ -1780,6 +1788,7 @@ NTSTATUS wb_open_internal_pipe(TALLOC_CTX *mem_ctx,
                                                 table,
                                                 session_info,
                                                 NULL,
+                                                NULL,
                                                 winbind_messaging_context(),
                                                 &cli);
        } else {
@@ -1787,6 +1796,7 @@ NTSTATUS wb_open_internal_pipe(TALLOC_CTX *mem_ctx,
                                                table,
                                                session_info,
                                                NULL,
+                                               NULL,
                                                winbind_messaging_context(),
                                                &cli);
        }
@@ -1804,12 +1814,14 @@ NTSTATUS wb_open_internal_pipe(TALLOC_CTX *mem_ctx,
 }
 
 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);
@@ -1825,39 +1837,39 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *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);
                }
        }
 
@@ -1868,12 +1880,12 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
 
                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;
 
@@ -1886,7 +1898,7 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
                }
 
                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
@@ -2116,7 +2128,7 @@ static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain, bool
                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);
@@ -2914,10 +2926,12 @@ static NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain,
 
        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;
        }
 
@@ -3226,8 +3240,8 @@ static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
 
        result = get_trust_credentials(domain, talloc_tos(), true, &creds);
        if (!NT_STATUS_IS_OK(result)) {
-               DEBUG(10, ("cm_connect_sam: No user available for "
-                          "domain %s when trying schannel\n", domain->name));
+               DBG_DEBUG("No user available for domain %s when trying "
+                         "schannel\n", domain->name);
                return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
@@ -3239,9 +3253,28 @@ static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
 
        sec_chan_type = cli_credentials_get_secure_channel_type(creds);
        if (sec_chan_type == SEC_CHAN_NULL) {
-               DBG_WARNING("get_secure_channel_type gave SEC_CHAN_NULL for %s\n",
-                           domain->name);
-               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               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_with_creds(creds,
@@ -3280,11 +3313,6 @@ static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
        conn->netlogon_flags = netlogon_creds->negotiate_flags;
        TALLOC_FREE(netlogon_creds);
 
-       /*
-        * FIXME: Document in which case we are not able to contact
-        * a DC without schannel. Which information do we try to get
-        * from this DC?
-        */
        if (!(conn->netlogon_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
                if (lp_winbind_sealed_pipes() || lp_require_strong_key()) {
                        result = NT_STATUS_DOWNGRADE_DETECTED;
@@ -3332,7 +3360,7 @@ static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
 }
 
 /****************************************************************************
-Open a LSA connection to a DC, suiteable for LSA lookup calls.
+Open a NETLOGON connection to a DC, suiteable for SamLogon calls.
 ****************************************************************************/
 
 NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,