s3:libnet: accept empty realm for AD domains when only security=domain is set.
[samba.git] / source3 / libnet / libnet_join.c
index dd87c6d4255062b9878d60204f29012ac91cb1d5..abb9cff1a5bf76c1e058a3587bb2262d320a33ed 100644 (file)
@@ -42,6 +42,7 @@
 #include "lib/param/loadparm.h"
 #include "libcli/auth/netlogon_creds_cli.h"
 #include "auth/credentials/credentials.h"
+#include "krb5_env.h"
 
 /****************************************************************
 ****************************************************************/
@@ -118,6 +119,7 @@ static ADS_STATUS libnet_connect_ads(const char *dns_domain_name,
                                     const char *dc_name,
                                     const char *user_name,
                                     const char *password,
+                                    const char *ccname,
                                     ADS_STRUCT **ads)
 {
        ADS_STATUS status;
@@ -150,6 +152,12 @@ static ADS_STATUS libnet_connect_ads(const char *dns_domain_name,
                my_ads->auth.password = SMB_STRDUP(password);
        }
 
+       if (ccname != NULL) {
+               SAFE_FREE(my_ads->auth.ccache_name);
+               my_ads->auth.ccache_name = SMB_STRDUP(ccname);
+               setenv(KRB5_ENV_CCNAME, my_ads->auth.ccache_name, 1);
+       }
+
        status = ads_connect_user_creds(my_ads);
        if (!ADS_ERR_OK(status)) {
                ads_destroy(&my_ads);
@@ -164,15 +172,51 @@ static ADS_STATUS libnet_connect_ads(const char *dns_domain_name,
 ****************************************************************/
 
 static ADS_STATUS libnet_join_connect_ads(TALLOC_CTX *mem_ctx,
-                                         struct libnet_JoinCtx *r)
+                                         struct libnet_JoinCtx *r,
+                                         bool use_machine_creds)
 {
        ADS_STATUS status;
+       const char *username;
+       const char *password;
+       const char *ccname = NULL;
+
+       if (use_machine_creds) {
+               if (r->in.machine_name == NULL ||
+                   r->in.machine_password == NULL) {
+                       return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+               username = talloc_strdup(mem_ctx, r->in.machine_name);
+               if (username == NULL) {
+                       return ADS_ERROR(LDAP_NO_MEMORY);
+               }
+               if (username[strlen(username)] != '$') {
+                       username = talloc_asprintf(username, "%s$", username);
+                       if (username == NULL) {
+                               return ADS_ERROR(LDAP_NO_MEMORY);
+                       }
+               }
+               password = r->in.machine_password;
+               ccname = "MEMORY:libnet_join_machine_creds";
+       } else {
+               username = r->in.admin_account;
+               password = r->in.admin_password;
+
+               /*
+                * when r->in.use_kerberos is set to allow "net ads join -k" we
+                * may not override the provided credential cache - gd
+                */
+
+               if (!r->in.use_kerberos) {
+                       ccname = "MEMORY:libnet_join_user_creds";
+               }
+       }
 
        status = libnet_connect_ads(r->out.dns_domain_name,
                                    r->out.netbios_domain_name,
                                    r->in.dc_name,
-                                   r->in.admin_account,
-                                   r->in.admin_password,
+                                   username,
+                                   password,
+                                   ccname,
                                    &r->in.ads);
        if (!ADS_ERR_OK(status)) {
                libnet_join_set_error_string(mem_ctx, r,
@@ -201,6 +245,24 @@ static ADS_STATUS libnet_join_connect_ads(TALLOC_CTX *mem_ctx,
 /****************************************************************
 ****************************************************************/
 
+static ADS_STATUS libnet_join_connect_ads_user(TALLOC_CTX *mem_ctx,
+                                              struct libnet_JoinCtx *r)
+{
+       return libnet_join_connect_ads(mem_ctx, r, false);
+}
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_join_connect_ads_machine(TALLOC_CTX *mem_ctx,
+                                                 struct libnet_JoinCtx *r)
+{
+       return libnet_join_connect_ads(mem_ctx, r, true);
+}
+
+/****************************************************************
+****************************************************************/
+
 static ADS_STATUS libnet_unjoin_connect_ads(TALLOC_CTX *mem_ctx,
                                            struct libnet_UnjoinCtx *r)
 {
@@ -211,6 +273,7 @@ static ADS_STATUS libnet_unjoin_connect_ads(TALLOC_CTX *mem_ctx,
                                    r->in.dc_name,
                                    r->in.admin_account,
                                    r->in.admin_password,
+                                   NULL,
                                    &r->in.ads);
        if (!ADS_ERR_OK(status)) {
                libnet_unjoin_set_error_string(mem_ctx, r,
@@ -255,7 +318,8 @@ static ADS_STATUS libnet_join_precreate_machine_acct(TALLOC_CTX *mem_ctx,
 
        status = ads_create_machine_acct(r->in.ads,
                                         r->in.machine_name,
-                                        r->in.account_ou);
+                                        r->in.account_ou,
+                                        r->in.desired_encryption_types);
 
        if (ADS_ERR_OK(status)) {
                DEBUG(1,("machine account creation created\n"));
@@ -353,6 +417,11 @@ static ADS_STATUS libnet_join_find_machine_acct(TALLOC_CTX *mem_ctx,
                goto done;
        }
 
+       if (!ads_pull_uint32(r->in.ads, res, "msDS-SupportedEncryptionTypes",
+                            &r->out.set_encryption_types)) {
+               r->out.set_encryption_types = 0;
+       }
+
  done:
        ads_msgfree(r->in.ads, res);
        TALLOC_FREE(dn);
@@ -394,6 +463,7 @@ static ADS_STATUS libnet_join_set_machine_spn(TALLOC_CTX *mem_ctx,
        size_t num_spns = 0;
        char *spn = NULL;
        bool ok;
+       const char **netbios_aliases = NULL;
 
        /* Find our DN */
 
@@ -455,6 +525,65 @@ static ADS_STATUS libnet_join_set_machine_spn(TALLOC_CTX *mem_ctx,
                }
        }
 
+       netbios_aliases = lp_netbios_aliases();
+       if (netbios_aliases != NULL) {
+               for (; *netbios_aliases != NULL; netbios_aliases++) {
+                       /*
+                        * Add HOST/NETBIOSNAME
+                        */
+                       spn = talloc_asprintf(mem_ctx, "HOST/%s", *netbios_aliases);
+                       if (spn == NULL) {
+                               TALLOC_FREE(spn);
+                               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+                       }
+                       if (!strupper_m(spn)) {
+                               TALLOC_FREE(spn);
+                               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+                       }
+
+                       ok = ads_element_in_array(spn_array, num_spns, spn);
+                       if (ok) {
+                               TALLOC_FREE(spn);
+                               continue;
+                       }
+                       ok = add_string_to_array(spn_array, spn,
+                                                &spn_array, &num_spns);
+                       if (!ok) {
+                               TALLOC_FREE(spn);
+                               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+                       }
+                       TALLOC_FREE(spn);
+
+                       /*
+                        * Add HOST/netbiosname.domainname
+                        */
+                       if (r->out.dns_domain_name == NULL) {
+                               continue;
+                       }
+                       fstr_sprintf(my_fqdn, "%s.%s",
+                                    *netbios_aliases,
+                                    r->out.dns_domain_name);
+
+                       spn = talloc_asprintf(mem_ctx, "HOST/%s", my_fqdn);
+                       if (spn == NULL) {
+                               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+                       }
+
+                       ok = ads_element_in_array(spn_array, num_spns, spn);
+                       if (ok) {
+                               TALLOC_FREE(spn);
+                               continue;
+                       }
+                       ok = add_string_to_array(spn_array, spn,
+                                                &spn_array, &num_spns);
+                       if (!ok) {
+                               TALLOC_FREE(spn);
+                               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+                       }
+                       TALLOC_FREE(spn);
+               }
+       }
+
        /* make sure to NULL terminate the array */
        spn_array = talloc_realloc(mem_ctx, spn_array, const char *, num_spns + 1);
        if (spn_array == NULL) {
@@ -574,8 +703,19 @@ static ADS_STATUS libnet_join_set_os_attributes(TALLOC_CTX *mem_ctx,
                return ADS_ERROR(LDAP_NO_MEMORY);
        }
 
-       os_sp = talloc_asprintf(mem_ctx, "Samba %s", samba_version_string());
-       if (!os_sp) {
+       if (r->in.os_servicepack) {
+               /*
+                * if blank string then leave os_sp equal to NULL to force
+                * attribute delete (LDAP_MOD_DELETE)
+                */
+               if (!strequal(r->in.os_servicepack,"")) {
+                       os_sp = talloc_strdup(mem_ctx, r->in.os_servicepack);
+               }
+       } else {
+               os_sp = talloc_asprintf(mem_ctx, "Samba %s",
+                                       samba_version_string());
+       }
+       if (!os_sp && !strequal(r->in.os_servicepack,"")) {
                return ADS_ERROR(LDAP_NO_MEMORY);
        }
 
@@ -610,17 +750,10 @@ static ADS_STATUS libnet_join_set_etypes(TALLOC_CTX *mem_ctx,
 {
        ADS_STATUS status;
        ADS_MODLIST mods;
-       uint32_t etype_list = ENC_CRC32 | ENC_RSA_MD5 | ENC_RC4_HMAC_MD5;
        const char *etype_list_str;
 
-#ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
-       etype_list |= ENC_HMAC_SHA1_96_AES128;
-#endif
-#ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
-       etype_list |= ENC_HMAC_SHA1_96_AES256;
-#endif
-
-       etype_list_str = talloc_asprintf(mem_ctx, "%d", etype_list);
+       etype_list_str = talloc_asprintf(mem_ctx, "%d",
+                                        r->in.desired_encryption_types);
        if (!etype_list_str) {
                return ADS_ERROR(LDAP_NO_MEMORY);
        }
@@ -632,6 +765,10 @@ static ADS_STATUS libnet_join_set_etypes(TALLOC_CTX *mem_ctx,
                return status;
        }
 
+       if (r->in.desired_encryption_types == r->out.set_encryption_types) {
+               return ADS_SUCCESS;
+       }
+
        /* now do the mods */
 
        mods = ads_init_mods(mem_ctx);
@@ -645,7 +782,14 @@ static ADS_STATUS libnet_join_set_etypes(TALLOC_CTX *mem_ctx,
                return status;
        }
 
-       return ads_gen_mod(r->in.ads, r->out.dn, mods);
+       status = ads_gen_mod(r->in.ads, r->out.dn, mods);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       r->out.set_encryption_types = r->in.desired_encryption_types;
+
+       return ADS_SUCCESS;
 }
 
 /****************************************************************
@@ -725,10 +869,10 @@ static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx,
                                                  struct libnet_JoinCtx *r)
 {
        ADS_STATUS status;
-       uint32_t func_level = 0;
+       bool need_etype_update = false;
 
        if (!r->in.ads) {
-               status = libnet_join_connect_ads(mem_ctx, r);
+               status = libnet_join_connect_ads_user(mem_ctx, r);
                if (!ADS_ERR_OK(status)) {
                        return status;
                }
@@ -760,15 +904,47 @@ static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx,
                return status;
        }
 
-       status = ads_domain_func_level(r->in.ads, &func_level);
+       status = libnet_join_find_machine_acct(mem_ctx, r);
        if (!ADS_ERR_OK(status)) {
-               libnet_join_set_error_string(mem_ctx, r,
-                       "failed to query domain controller functional level: %s",
-                       ads_errstr(status));
                return status;
        }
 
-       if (func_level >= DS_DOMAIN_FUNCTION_2008) {
+       if (r->in.desired_encryption_types != r->out.set_encryption_types) {
+               uint32_t func_level = 0;
+
+               status = ads_domain_func_level(r->in.ads, &func_level);
+               if (!ADS_ERR_OK(status)) {
+                       libnet_join_set_error_string(mem_ctx, r,
+                               "failed to query domain controller functional level: %s",
+                               ads_errstr(status));
+                       return status;
+               }
+
+               if (func_level >= DS_DOMAIN_FUNCTION_2008) {
+                       need_etype_update = true;
+               }
+       }
+
+       if (need_etype_update) {
+               /*
+                * We need to reconnect as machine account in order
+                * to update msDS-SupportedEncryptionTypes reliable
+                */
+
+               if (r->in.ads->auth.ccache_name != NULL) {
+                       ads_kdestroy(r->in.ads->auth.ccache_name);
+               }
+
+               ads_destroy(&r->in.ads);
+
+               status = libnet_join_connect_ads_machine(mem_ctx, r);
+               if (!ADS_ERR_OK(status)) {
+                       libnet_join_set_error_string(mem_ctx, r,
+                               "Failed to connect as machine account: %s",
+                               ads_errstr(status));
+                       return status;
+               }
+
                status = libnet_join_set_etypes(mem_ctx, r);
                if (!ADS_ERR_OK(status)) {
                        libnet_join_set_error_string(mem_ctx, r,
@@ -846,7 +1022,7 @@ static NTSTATUS libnet_join_connect_dc_ipc(const char *dc,
                                   domain,
                                   pass,
                                   flags,
-                                  SMB_SIGNING_DEFAULT);
+                                  SMB_SIGNING_IPC_DEFAULT);
 }
 
 /****************************************************************
@@ -1355,18 +1531,15 @@ NTSTATUS libnet_join_ok(struct messaging_context *msg_ctx,
        TALLOC_CTX *frame = talloc_stackframe();
        struct cli_state *cli = NULL;
        struct rpc_pipe_client *netlogon_pipe = NULL;
+       struct cli_credentials *cli_creds = NULL;
        struct netlogon_creds_cli_context *netlogon_creds = NULL;
        struct netlogon_creds_CredentialState *creds = NULL;
        uint32_t netlogon_flags = 0;
-       enum netr_SchannelType sec_chan_type = 0;
        NTSTATUS status;
-       char *machine_password = NULL;
-       const char *machine_name = NULL;
        const char *machine_account = NULL;
+       const char *machine_domain = NULL;
+       const char *machine_password = NULL;
        int flags = 0;
-       struct samr_Password current_nt_hash;
-       struct samr_Password *previous_nt_hash = NULL;
-       bool ok;
 
        if (!dc_name) {
                TALLOC_FREE(frame);
@@ -1378,39 +1551,33 @@ NTSTATUS libnet_join_ok(struct messaging_context *msg_ctx,
                return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
-       ok = get_trust_pw_clear(netbios_domain_name,
-                               &machine_password,
-                               &machine_name,
-                               &sec_chan_type);
-       if (!ok) {
+       status = pdb_get_trust_credentials(netbios_domain_name, NULL,
+                                          frame, &cli_creds);
+       if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(frame);
-               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               return status;
        }
 
-       machine_account = talloc_asprintf(frame, "%s$", machine_name);
-       if (machine_account == NULL) {
-               SAFE_FREE(machine_password);
-               SAFE_FREE(previous_nt_hash);
-               TALLOC_FREE(frame);
-               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
-       }
+       /* we don't want any old password */
+       cli_credentials_set_old_password(cli_creds, NULL, CRED_SPECIFIED);
 
        if (use_kerberos) {
                flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
        }
 
+       machine_account = cli_credentials_get_username(cli_creds);
+       machine_domain = cli_credentials_get_domain(cli_creds);
+       machine_password = cli_credentials_get_password(cli_creds);
+
        status = cli_full_connection(&cli, NULL,
                                     dc_name,
                                     NULL, 0,
                                     "IPC$", "IPC",
                                     machine_account,
-                                    netbios_domain_name,
+                                    machine_domain,
                                     machine_password,
                                     flags,
-                                    SMB_SIGNING_DEFAULT);
-
-       E_md4hash(machine_password, current_nt_hash.hash);
-       SAFE_FREE(machine_password);
+                                    SMB_SIGNING_IPC_DEFAULT);
 
        if (!NT_STATUS_IS_OK(status)) {
                status = cli_full_connection(&cli, NULL,
@@ -1421,35 +1588,29 @@ NTSTATUS libnet_join_ok(struct messaging_context *msg_ctx,
                                             NULL,
                                             "",
                                             0,
-                                            SMB_SIGNING_DEFAULT);
+                                            SMB_SIGNING_IPC_DEFAULT);
        }
 
        if (!NT_STATUS_IS_OK(status)) {
-               SAFE_FREE(previous_nt_hash);
                TALLOC_FREE(frame);
                return status;
        }
 
-       status = rpccli_create_netlogon_creds(dc_name,
-                                             netbios_domain_name,
-                                             machine_account,
-                                             sec_chan_type,
-                                             msg_ctx,
-                                             frame,
-                                             &netlogon_creds);
+       status = rpccli_create_netlogon_creds_with_creds(cli_creds,
+                                                        dc_name,
+                                                        msg_ctx,
+                                                        frame,
+                                                        &netlogon_creds);
        if (!NT_STATUS_IS_OK(status)) {
-               SAFE_FREE(previous_nt_hash);
                cli_shutdown(cli);
                TALLOC_FREE(frame);
                return status;
        }
 
-       status = rpccli_setup_netlogon_creds(cli, NCACN_NP,
-                                            netlogon_creds,
-                                            true, /* force_reauth */
-                                            current_nt_hash,
-                                            previous_nt_hash);
-       SAFE_FREE(previous_nt_hash);
+       status = rpccli_setup_netlogon_creds_with_creds(cli, NCACN_NP,
+                                                       netlogon_creds,
+                                                       true, /* force_reauth */
+                                                       cli_creds);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0,("connect_to_domain_password_server: "
                         "unable to open the domain client session to "
@@ -1478,9 +1639,9 @@ NTSTATUS libnet_join_ok(struct messaging_context *msg_ctx,
                return NT_STATUS_OK;
        }
 
-       status = cli_rpc_pipe_open_schannel_with_key(
+       status = cli_rpc_pipe_open_schannel_with_creds(
                cli, &ndr_table_netlogon, NCACN_NP,
-               netbios_domain_name,
+               cli_creds,
                netlogon_creds, &netlogon_pipe);
 
        TALLOC_FREE(netlogon_pipe);
@@ -2095,6 +2256,16 @@ WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
 
        ctx->in.secure_channel_type = SEC_CHAN_WKSTA;
 
+       ctx->in.desired_encryption_types = ENC_CRC32 |
+                                          ENC_RSA_MD5 |
+                                          ENC_RC4_HMAC_MD5;
+#ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
+       ctx->in.desired_encryption_types |= ENC_HMAC_SHA1_96_AES128;
+#endif
+#ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
+       ctx->in.desired_encryption_types |= ENC_HMAC_SHA1_96_AES256;
+#endif
+
        *r = ctx;
 
        return WERR_OK;
@@ -2196,9 +2367,26 @@ static WERROR libnet_join_check_config(TALLOC_CTX *mem_ctx,
                        W_ERROR_HAVE_NO_MEMORY(wrong_conf);
                }
 
+               /*
+                * We should generate the warning for the special case when
+                * domain is AD, "security = domain" and the realm parameter is
+                * not set.
+                */
+               if (lp_security() == SEC_DOMAIN &&
+                   r->out.domain_is_ad &&
+                   !valid_realm) {
+                       libnet_join_set_error_string(mem_ctx, r,
+                               "Warning: when joining AD domains with security=domain, "
+                               "\"realm\" should be defined in the configuration (%s) "
+                               "and configuration modification was not requested",
+                               wrong_conf);
+                       return WERR_OK;
+               }
+
                libnet_join_set_error_string(mem_ctx, r,
                        "Invalid configuration (%s) and configuration modification "
                        "was not requested", wrong_conf);
+
                return WERR_CAN_NOT_COMPLETE;
        }
 
@@ -2226,10 +2414,27 @@ static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
 #ifdef HAVE_ADS
        ADS_STATUS ads_status;
 #endif /* HAVE_ADS */
+       const char *pre_connect_realm = NULL;
+       const char *numeric_dcip = NULL;
+       const char *sitename = NULL;
+
+       /* Before contacting a DC, we can securely know
+        * the realm only if the user specifies it.
+        */
+       if (r->in.use_kerberos &&
+           r->in.domain_name_type == JoinDomNameTypeDNS) {
+               pre_connect_realm = r->in.domain_name;
+       }
 
        if (!r->in.dc_name) {
                struct netr_DsRGetDCNameInfo *info;
                const char *dc;
+               uint32_t name_type_flags = 0;
+               if (r->in.domain_name_type == JoinDomNameTypeDNS) {
+                       name_type_flags = DS_IS_DNS_NAME;
+               } else if (r->in.domain_name_type == JoinDomNameTypeNBT) {
+                       name_type_flags = DS_IS_FLAT_NAME;
+               }
                status = dsgetdcname(mem_ctx,
                                     r->in.msg_ctx,
                                     r->in.domain_name,
@@ -2238,7 +2443,8 @@ static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
                                     DS_FORCE_REDISCOVERY |
                                     DS_DIRECTORY_SERVICE_REQUIRED |
                                     DS_WRITABLE_REQUIRED |
-                                    DS_RETURN_DNS_NAME,
+                                    DS_RETURN_DNS_NAME |
+                                    name_type_flags,
                                     &info);
                if (!NT_STATUS_IS_OK(status)) {
                        libnet_join_set_error_string(mem_ctx, r,
@@ -2251,6 +2457,47 @@ static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
                dc = strip_hostname(info->dc_unc);
                r->in.dc_name = talloc_strdup(mem_ctx, dc);
                W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
+
+               if (info->dc_address == NULL || info->dc_address[0] != '\\' ||
+                   info->dc_address[1] != '\\') {
+                       DBG_ERR("ill-formed DC address '%s'\n",
+                               info->dc_address);
+                       return WERR_DCNOTFOUND;
+               }
+
+               numeric_dcip = info->dc_address + 2;
+               sitename = info->dc_site_name;
+               /* info goes out of scope but the memory stays
+                  allocated on the talloc context */
+       }
+
+       if (pre_connect_realm != NULL) {
+               struct sockaddr_storage ss = {0};
+
+               if (numeric_dcip != NULL) {
+                       if (!interpret_string_addr(&ss, numeric_dcip,
+                                                  AI_NUMERICHOST)) {
+                               DBG_ERR(
+                                   "cannot parse IP address '%s' of DC '%s'\n",
+                                   numeric_dcip, r->in.dc_name);
+                               return WERR_DCNOTFOUND;
+                       }
+               } else {
+                       if (!interpret_string_addr(&ss, r->in.dc_name, 0)) {
+                               DBG_WARNING(
+                                   "cannot resolve IP address of DC '%s'\n",
+                                   r->in.dc_name);
+                               return WERR_DCNOTFOUND;
+                       }
+               }
+
+               /* The domain parameter is only used as modifier
+                * to krb5.conf file name. .JOIN is is not a valid
+                * NetBIOS name so it cannot clash with another domain
+                * -- Uri.
+                */
+               create_local_private_krb5_conf_for_domain(
+                   pre_connect_realm, ".JOIN", sitename, &ss);
        }
 
        status = libnet_join_lookup_dc_rpc(mem_ctx, r, &cli);
@@ -2270,18 +2517,38 @@ static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
 
        create_local_private_krb5_conf_for_domain(
                r->out.dns_domain_name, r->out.netbios_domain_name,
-               NULL, smbXcli_conn_remote_sockaddr(cli->conn));
+               sitename, smbXcli_conn_remote_sockaddr(cli->conn));
 
-       if (r->out.domain_is_ad && r->in.account_ou &&
+       if (r->out.domain_is_ad &&
            !(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE)) {
 
-               ads_status = libnet_join_connect_ads(mem_ctx, r);
+               const char *initial_account_ou = r->in.account_ou;
+
+               /*
+                * we want to create the msDS-SupportedEncryptionTypes attribute
+                * as early as possible so always try an LDAP create as the user
+                * first. We copy r->in.account_ou because it may be changed
+                * during the machine pre-creation.
+                */
+
+               ads_status = libnet_join_connect_ads_user(mem_ctx, r);
                if (!ADS_ERR_OK(ads_status)) {
                        return WERR_DEFAULT_JOIN_REQUIRED;
                }
 
                ads_status = libnet_join_precreate_machine_acct(mem_ctx, r);
-               if (!ADS_ERR_OK(ads_status)) {
+               if (ADS_ERR_OK(ads_status)) {
+
+                       /*
+                        * LDAP object create succeeded, now go to the rpc
+                        * password set routines
+                        */
+
+                       r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
+                       goto rpc_join;
+               }
+
+               if (initial_account_ou != NULL) {
                        libnet_join_set_error_string(mem_ctx, r,
                                "failed to precreate account in ou %s: %s",
                                r->in.account_ou,
@@ -2289,10 +2556,12 @@ static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
                        return WERR_DEFAULT_JOIN_REQUIRED;
                }
 
-               r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
+               DEBUG(5, ("failed to precreate account in ou %s: %s",
+                       r->in.account_ou, ads_errstr(ads_status)));
        }
 #endif /* HAVE_ADS */
 
+ rpc_join:
        if ((r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE) &&
            (r->in.join_flags & WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED)) {
                status = libnet_join_joindomain_rpc_unsecure(mem_ctx, r, cli);