Update smbrun to allow for settings environment variables.
[samba.git] / source3 / libnet / libnet_join.c
index e702ffc13be82430a7d1433c0ed9734ab8003174..3ac7f3980e5b5418a42bacad5abee22d8d8dae0d 100644 (file)
 #include "secrets.h"
 #include "rpc_client/init_lsa.h"
 #include "rpc_client/cli_pipe.h"
-#include "krb5_env.h"
 #include "../libcli/security/security.h"
 #include "passdb.h"
+#include "libsmb/libsmb.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "lib/param/loadparm.h"
+#include "libcli/auth/netlogon_creds_cli.h"
+#include "auth/credentials/credentials.h"
+#include "krb5_env.h"
 
 /****************************************************************
 ****************************************************************/
 /****************************************************************
 ****************************************************************/
 
+static void libnet_join_set_error_string(TALLOC_CTX *mem_ctx,
+                                        struct libnet_JoinCtx *r,
+                                        const char *format, ...)
+                                        PRINTF_ATTRIBUTE(3,4);
+
 static void libnet_join_set_error_string(TALLOC_CTX *mem_ctx,
                                         struct libnet_JoinCtx *r,
                                         const char *format, ...)
@@ -89,6 +99,11 @@ static void libnet_join_set_error_string(TALLOC_CTX *mem_ctx,
 /****************************************************************
 ****************************************************************/
 
+static void libnet_unjoin_set_error_string(TALLOC_CTX *mem_ctx,
+                                          struct libnet_UnjoinCtx *r,
+                                          const char *format, ...)
+                                          PRINTF_ATTRIBUTE(3,4);
+
 static void libnet_unjoin_set_error_string(TALLOC_CTX *mem_ctx,
                                           struct libnet_UnjoinCtx *r,
                                           const char *format, ...)
@@ -114,6 +129,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;
@@ -134,7 +150,10 @@ static ADS_STATUS libnet_connect_ads(const char *dns_domain_name,
                        *cp++ = '\0';
                        SAFE_FREE(my_ads->auth.realm);
                        my_ads->auth.realm = smb_xstrdup(cp);
-                       strupper_m(my_ads->auth.realm);
+                       if (!strupper_m(my_ads->auth.realm)) {
+                               ads_destroy(&my_ads);
+                               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+                       }
                }
        }
 
@@ -143,6 +162,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);
@@ -157,15 +182,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,
@@ -194,6 +255,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)
 {
@@ -204,6 +283,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,
@@ -248,7 +328,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"));
@@ -346,6 +427,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);
@@ -353,6 +439,26 @@ static ADS_STATUS libnet_join_find_machine_acct(TALLOC_CTX *mem_ctx,
        return status;
 }
 
+static ADS_STATUS libnet_join_get_machine_spns(TALLOC_CTX *mem_ctx,
+                                              struct libnet_JoinCtx *r,
+                                              char ***spn_array,
+                                              size_t *num_spns)
+{
+       ADS_STATUS status;
+
+       if (r->in.machine_name == NULL) {
+               return ADS_ERROR_SYSTEM(EINVAL);
+       }
+
+       status = ads_get_service_principal_names(mem_ctx,
+                                                r->in.ads,
+                                                r->in.machine_name,
+                                                spn_array,
+                                                num_spns);
+
+       return status;
+}
+
 /****************************************************************
  Set a machines dNSHostName and servicePrincipalName attributes
 ****************************************************************/
@@ -363,8 +469,11 @@ static ADS_STATUS libnet_join_set_machine_spn(TALLOC_CTX *mem_ctx,
        ADS_STATUS status;
        ADS_MODLIST mods;
        fstring my_fqdn;
-       const char *spn_array[3] = {NULL, NULL, NULL};
+       const char **spn_array = NULL;
+       size_t num_spns = 0;
        char *spn = NULL;
+       bool ok;
+       const char **netbios_aliases = NULL;
 
        /* Find our DN */
 
@@ -373,14 +482,32 @@ static ADS_STATUS libnet_join_set_machine_spn(TALLOC_CTX *mem_ctx,
                return status;
        }
 
+       status = libnet_join_get_machine_spns(mem_ctx,
+                                             r,
+                                             discard_const_p(char **, &spn_array),
+                                             &num_spns);
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(5, ("Retrieving the servicePrincipalNames failed.\n"));
+       }
+
        /* Windows only creates HOST/shortname & HOST/fqdn. */
 
        spn = talloc_asprintf(mem_ctx, "HOST/%s", r->in.machine_name);
        if (!spn) {
                return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
        }
-       strupper_m(spn);
-       spn_array[0] = spn;
+       if (!strupper_m(spn)) {
+               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+       }
+
+       ok = ads_element_in_array(spn_array, num_spns, spn);
+       if (!ok) {
+               ok = add_string_to_array(spn_array, spn,
+                                        &spn_array, &num_spns);
+               if (!ok) {
+                       return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+               }
+       }
 
        if (!name_to_fqdn(my_fqdn, r->in.machine_name)
            || (strchr(my_fqdn, '.') == NULL)) {
@@ -388,16 +515,92 @@ static ADS_STATUS libnet_join_set_machine_spn(TALLOC_CTX *mem_ctx,
                             r->out.dns_domain_name);
        }
 
-       strlower_m(my_fqdn);
+       if (!strlower_m(my_fqdn)) {
+               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+       }
 
        if (!strequal(my_fqdn, r->in.machine_name)) {
                spn = talloc_asprintf(mem_ctx, "HOST/%s", my_fqdn);
                if (!spn) {
                        return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
                }
-               spn_array[1] = spn;
+
+               ok = ads_element_in_array(spn_array, num_spns, spn);
+               if (!ok) {
+                       ok = add_string_to_array(spn_array, spn,
+                                                &spn_array, &num_spns);
+                       if (!ok) {
+                               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+                       }
+               }
+       }
+
+       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) {
+               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+       }
+       spn_array[num_spns] = NULL;
+
        mods = ads_init_mods(mem_ctx);
        if (!mods) {
                return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
@@ -440,10 +643,25 @@ static ADS_STATUS libnet_join_set_machine_upn(TALLOC_CTX *mem_ctx,
        }
 
        if (!r->in.upn) {
+               const char *realm = r->out.dns_domain_name;
+
+               /* in case we are about to generate a keytab during the join
+                * make sure the default upn we create is usable with kinit -k.
+                * gd */
+
+               if (USE_KERBEROS_KEYTAB) {
+                       realm = talloc_strdup_upper(mem_ctx,
+                                                   r->out.dns_domain_name);
+               }
+
+               if (!realm) {
+                       return ADS_ERROR(LDAP_NO_MEMORY);
+               }
+
                r->in.upn = talloc_asprintf(mem_ctx,
                                            "host/%s@%s",
                                            r->in.machine_name,
-                                           r->out.dns_domain_name);
+                                           realm);
                if (!r->in.upn) {
                        return ADS_ERROR(LDAP_NO_MEMORY);
                }
@@ -495,8 +713,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);
        }
 
@@ -526,6 +755,56 @@ static ADS_STATUS libnet_join_set_os_attributes(TALLOC_CTX *mem_ctx,
 /****************************************************************
 ****************************************************************/
 
+static ADS_STATUS libnet_join_set_etypes(TALLOC_CTX *mem_ctx,
+                                        struct libnet_JoinCtx *r)
+{
+       ADS_STATUS status;
+       ADS_MODLIST mods;
+       const char *etype_list_str;
+
+       etype_list_str = talloc_asprintf(mem_ctx, "%d",
+                                        r->in.desired_encryption_types);
+       if (!etype_list_str) {
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
+
+       /* Find our DN */
+
+       status = libnet_join_find_machine_acct(mem_ctx, r);
+       if (!ADS_ERR_OK(status)) {
+               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);
+       if (!mods) {
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
+
+       status = ads_mod_str(mem_ctx, &mods, "msDS-SupportedEncryptionTypes",
+                            etype_list_str);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       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;
+}
+
+/****************************************************************
+****************************************************************/
+
 static bool libnet_join_create_keytab(TALLOC_CTX *mem_ctx,
                                      struct libnet_JoinCtx *r)
 {
@@ -600,9 +879,10 @@ static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx,
                                                  struct libnet_JoinCtx *r)
 {
        ADS_STATUS status;
+       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;
                }
@@ -611,7 +891,9 @@ static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx,
        status = libnet_join_set_machine_spn(mem_ctx, r);
        if (!ADS_ERR_OK(status)) {
                libnet_join_set_error_string(mem_ctx, r,
-                       "failed to set machine spn: %s",
+                       "Failed to set machine spn: %s\n"
+                       "Do you have sufficient permissions to create machine "
+                       "accounts?",
                        ads_errstr(status));
                return status;
        }
@@ -632,6 +914,56 @@ static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx,
                return status;
        }
 
+       status = libnet_join_find_machine_acct(mem_ctx, r);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       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,
+                               "failed to set machine kerberos encryption types: %s",
+                               ads_errstr(status));
+                       return status;
+               }
+       }
+
        if (!libnet_join_derive_salting_principal(mem_ctx, r)) {
                return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
        }
@@ -677,6 +1009,7 @@ static bool libnet_join_joindomain_store_secrets(TALLOC_CTX *mem_ctx,
 
 static NTSTATUS libnet_join_connect_dc_ipc(const char *dc,
                                           const char *user,
+                                          const char *domain,
                                           const char *pass,
                                           bool use_kerberos,
                                           struct cli_state **cli)
@@ -696,10 +1029,10 @@ static NTSTATUS libnet_join_connect_dc_ipc(const char *dc,
                                   NULL, 0,
                                   "IPC$", "IPC",
                                   user,
-                                  NULL,
+                                  domain,
                                   pass,
                                   flags,
-                                  Undefined);
+                                  SMB_SIGNING_IPC_DEFAULT);
 }
 
 /****************************************************************
@@ -718,6 +1051,7 @@ static NTSTATUS libnet_join_lookup_dc_rpc(TALLOC_CTX *mem_ctx,
 
        status = libnet_join_connect_dc_ipc(r->in.dc_name,
                                            r->in.admin_account,
+                                           r->in.admin_domain,
                                            r->in.admin_password,
                                            r->in.use_kerberos,
                                            cli);
@@ -725,7 +1059,7 @@ static NTSTATUS libnet_join_lookup_dc_rpc(TALLOC_CTX *mem_ctx,
                goto done;
        }
 
-       status = cli_rpc_pipe_open_noauth(*cli, &ndr_table_lsarpc.syntax_id,
+       status = cli_rpc_pipe_open_noauth(*cli, &ndr_table_lsarpc,
                                          &pipe_hnd);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0,("Error connecting to LSA pipe. Error was %s\n",
@@ -789,46 +1123,73 @@ static NTSTATUS libnet_join_joindomain_rpc_unsecure(TALLOC_CTX *mem_ctx,
                                                    struct libnet_JoinCtx *r,
                                                    struct cli_state *cli)
 {
-       struct rpc_pipe_client *pipe_hnd = NULL;
-       unsigned char orig_trust_passwd_hash[16];
-       unsigned char new_trust_passwd_hash[16];
-       fstring trust_passwd;
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct rpc_pipe_client *netlogon_pipe = NULL;
+       struct netlogon_creds_cli_context *netlogon_creds = NULL;
+       struct samr_Password current_nt_hash;
+       const char *account_name = NULL;
        NTSTATUS status;
 
-       status = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon.syntax_id,
-                                         &pipe_hnd);
+       status = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon,
+                                         &netlogon_pipe);
        if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
                return status;
        }
 
        if (!r->in.machine_password) {
-               r->in.machine_password = generate_random_str(mem_ctx, DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
-               NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
+               r->in.machine_password = generate_random_password(mem_ctx,
+                               DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH,
+                               DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+               if (r->in.machine_password == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
        }
 
-       E_md4hash(r->in.machine_password, new_trust_passwd_hash);
-
        /* according to WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED */
-       fstrcpy(trust_passwd, r->in.admin_password);
-       strlower_m(trust_passwd);
+       E_md4hash(r->in.admin_password, current_nt_hash.hash);
 
-       /*
-        * Machine names can be 15 characters, but the max length on
-        * a password is 14.  --jerry
-        */
+       account_name = talloc_asprintf(frame, "%s$",
+                                      r->in.machine_name);
+       if (account_name == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       trust_passwd[14] = '\0';
+       status = rpccli_create_netlogon_creds(netlogon_pipe->desthost,
+                                             r->in.domain_name,
+                                             account_name,
+                                             r->in.secure_channel_type,
+                                             r->in.msg_ctx,
+                                             frame,
+                                             &netlogon_creds);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
 
-       E_md4hash(trust_passwd, orig_trust_passwd_hash);
+       status = rpccli_setup_netlogon_creds(cli, NCACN_NP,
+                                            netlogon_creds,
+                                            true, /* force_reauth */
+                                            current_nt_hash,
+                                            NULL); /* previous_nt_hash */
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
 
-       status = rpccli_netlogon_set_trust_password(pipe_hnd, mem_ctx,
-                                                   r->in.machine_name,
-                                                   orig_trust_passwd_hash,
-                                                   r->in.machine_password,
-                                                   new_trust_passwd_hash,
-                                                   r->in.secure_channel_type);
+       status = netlogon_creds_cli_ServerPasswordSet(netlogon_creds,
+                                                     netlogon_pipe->binding_handle,
+                                                     r->in.machine_password,
+                                                     NULL); /* new_version */
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
 
-       return status;
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
 }
 
 /****************************************************************
@@ -850,7 +1211,9 @@ static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
        struct samr_Ids name_types;
        union samr_UserInfo user_info;
        struct dcerpc_binding_handle *b = NULL;
+       unsigned int old_timeout = 0;
 
+       DATA_BLOB session_key = data_blob_null;
        struct samr_CryptPassword crypt_pwd;
        struct samr_CryptPasswordEx crypt_pwd_ex;
 
@@ -870,13 +1233,15 @@ static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
        }
 
        if (!r->in.machine_password) {
-               r->in.machine_password = generate_random_str(mem_ctx, DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+               r->in.machine_password = generate_random_password(mem_ctx,
+                               DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH,
+                               DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
                NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
        }
 
        /* Open the domain */
 
-       status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
+       status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr,
                                          &pipe_hnd);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0,("Error connecting to SAM pipe. Error was %s\n",
@@ -886,6 +1251,13 @@ static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
 
        b = pipe_hnd->binding_handle;
 
+       status = cli_get_session_key(mem_ctx, pipe_hnd, &session_key);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("Error getting session_key of SAM pipe. Error was %s\n",
+                       nt_errstr(status)));
+               goto done;
+       }
+
        status = dcerpc_samr_Connect2(b, mem_ctx,
                                      pipe_hnd->desthost,
                                      SAMR_ACCESS_ENUM_DOMAINS
@@ -919,7 +1291,10 @@ static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
        /* Create domain user */
 
        acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
-       strlower_m(acct_name);
+       if (!strlower_m(acct_name)) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto done;
+       }
 
        init_lsa_String(&lsa_acct_name, acct_name);
 
@@ -996,6 +1371,14 @@ static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
                status = result;
                goto done;
        }
+       if (user_rids.count != 1) {
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               goto done;
+       }
+       if (name_types.count != 1) {
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               goto done;
+       }
 
        if (name_types.ids[0] != SID_NAME_USER) {
                DEBUG(0,("%s is not a user account (type=%d)\n",
@@ -1030,11 +1413,11 @@ static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
        ZERO_STRUCT(user_info.info16);
        user_info.info16.acct_flags = acct_flags;
 
-       status = dcerpc_samr_SetUserInfo(b, mem_ctx,
-                                        &user_pol,
-                                        16,
-                                        &user_info,
-                                        &result);
+       status = dcerpc_samr_SetUserInfo2(b, mem_ctx,
+                                         &user_pol,
+                                         UserControlInformation,
+                                         &user_info,
+                                         &result);
        if (!NT_STATUS_IS_OK(status)) {
                dcerpc_samr_DeleteUser(b, mem_ctx,
                                       &user_pol,
@@ -1061,8 +1444,14 @@ static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
 
        /* Set password on machine account - first try level 26 */
 
+       /*
+        * increase the timeout as password filter modules on the DC
+        * might delay the operation for a significant amount of time
+        */
+       old_timeout = rpccli_set_timeout(pipe_hnd, 600000);
+
        init_samr_CryptPasswordEx(r->in.machine_password,
-                                 &cli->user_session_key,
+                                 &session_key,
                                  &crypt_pwd_ex);
 
        user_info.info26.password = crypt_pwd_ex;
@@ -1070,16 +1459,16 @@ static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
 
        status = dcerpc_samr_SetUserInfo2(b, mem_ctx,
                                          &user_pol,
-                                         26,
+                                         UserInternal5InformationNew,
                                          &user_info,
                                          &result);
 
-       if (NT_STATUS_EQUAL(status, NT_STATUS(DCERPC_FAULT_INVALID_TAG))) {
+       if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE)) {
 
                /* retry with level 24 */
 
                init_samr_CryptPassword(r->in.machine_password,
-                                       &cli->user_session_key,
+                                       &session_key,
                                        &crypt_pwd);
 
                user_info.info24.password = crypt_pwd;
@@ -1087,11 +1476,13 @@ static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
 
                status = dcerpc_samr_SetUserInfo2(b, mem_ctx,
                                                  &user_pol,
-                                                 24,
+                                                 UserInternal5Information,
                                                  &user_info,
                                                  &result);
        }
 
+       old_timeout = rpccli_set_timeout(pipe_hnd, old_timeout);
+
        if (!NT_STATUS_IS_OK(status)) {
 
                dcerpc_samr_DeleteUser(b, mem_ctx,
@@ -1123,6 +1514,8 @@ static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
                return status;
        }
 
+       data_blob_clear_free(&session_key);
+
        if (is_valid_policy_hnd(&sam_pol)) {
                dcerpc_samr_Close(b, mem_ctx, &sam_pol, &result);
        }
@@ -1140,48 +1533,61 @@ static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
 /****************************************************************
 ****************************************************************/
 
-NTSTATUS libnet_join_ok(const char *netbios_domain_name,
-                       const char *machine_name,
-                       const char *dc_name)
+NTSTATUS libnet_join_ok(struct messaging_context *msg_ctx,
+                       const char *netbios_domain_name,
+                       const char *dc_name,
+                       const bool use_kerberos)
 {
-       uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+       TALLOC_CTX *frame = talloc_stackframe();
        struct cli_state *cli = NULL;
-       struct rpc_pipe_client *pipe_hnd = 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;
        NTSTATUS status;
-       char *machine_password = NULL;
-       char *machine_account = NULL;
+       const char *machine_account = NULL;
+       const char *machine_domain = NULL;
+       const char *machine_password = NULL;
+       int flags = 0;
 
        if (!dc_name) {
+               TALLOC_FREE(frame);
                return NT_STATUS_INVALID_PARAMETER;
        }
 
        if (!secrets_init()) {
+               TALLOC_FREE(frame);
                return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
-       machine_password = secrets_fetch_machine_password(netbios_domain_name,
-                                                         NULL, NULL);
-       if (!machine_password) {
-               return NT_STATUS_NO_TRUST_LSA_SECRET;
+       status = pdb_get_trust_credentials(netbios_domain_name, NULL,
+                                          frame, &cli_creds);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
        }
 
-       if (asprintf(&machine_account, "%s$", machine_name) == -1) {
-               SAFE_FREE(machine_password);
-               return NT_STATUS_NO_MEMORY;
+       /* 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,
-                                    NULL,
+                                    machine_domain,
                                     machine_password,
-                                    0,
-                                    Undefined);
-       free(machine_account);
-       free(machine_password);
+                                    flags,
+                                    SMB_SIGNING_IPC_DEFAULT);
 
        if (!NT_STATUS_IS_OK(status)) {
                status = cli_full_connection(&cli, NULL,
@@ -1192,48 +1598,77 @@ NTSTATUS libnet_join_ok(const char *netbios_domain_name,
                                             NULL,
                                             "",
                                             0,
-                                            Undefined);
+                                            SMB_SIGNING_IPC_DEFAULT);
        }
 
        if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
                return status;
        }
 
-       status = get_schannel_session_key(cli, netbios_domain_name,
-                                         &neg_flags, &netlogon_pipe);
+       status = rpccli_create_netlogon_creds_with_creds(cli_creds,
+                                                        dc_name,
+                                                        msg_ctx,
+                                                        frame,
+                                                        &netlogon_creds);
        if (!NT_STATUS_IS_OK(status)) {
-               if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_NETWORK_RESPONSE)) {
-                       cli_shutdown(cli);
-                       return NT_STATUS_OK;
-               }
+               cli_shutdown(cli);
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       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 "
+                        "machine %s. Flags[0x%08X] Error was : %s.\n",
+                        dc_name, (unsigned)netlogon_flags,
+                        nt_errstr(status)));
+               cli_shutdown(cli);
+               TALLOC_FREE(frame);
+               return status;
+       }
 
-               DEBUG(0,("libnet_join_ok: failed to get schannel session "
-                       "key from server %s for domain %s. Error was %s\n",
-               cli->desthost, netbios_domain_name, nt_errstr(status)));
+       status = netlogon_creds_cli_get(netlogon_creds,
+                                       talloc_tos(),
+                                       &creds);
+       if (!NT_STATUS_IS_OK(status)) {
                cli_shutdown(cli);
+               TALLOC_FREE(frame);
                return status;
        }
+       netlogon_flags = creds->negotiate_flags;
+       TALLOC_FREE(creds);
 
-       if (!lp_client_schannel()) {
+       if (!(netlogon_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
                cli_shutdown(cli);
+               TALLOC_FREE(frame);
                return NT_STATUS_OK;
        }
 
-       status = cli_rpc_pipe_open_schannel_with_key(
-               cli, &ndr_table_netlogon.syntax_id, NCACN_NP,
-               DCERPC_AUTH_LEVEL_PRIVACY,
-               netbios_domain_name, &netlogon_pipe->dc, &pipe_hnd);
+       status = cli_rpc_pipe_open_schannel_with_creds(
+               cli, &ndr_table_netlogon, NCACN_NP,
+               cli_creds,
+               netlogon_creds, &netlogon_pipe);
 
-       cli_shutdown(cli);
+       TALLOC_FREE(netlogon_pipe);
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0,("libnet_join_ok: failed to open schannel session "
                        "on netlogon pipe to server %s for domain %s. "
                        "Error was %s\n",
-                       cli->desthost, netbios_domain_name, nt_errstr(status)));
+                       smbXcli_conn_remote_name(cli->conn),
+                       netbios_domain_name, nt_errstr(status)));
+               cli_shutdown(cli);
+               TALLOC_FREE(frame);
                return status;
        }
 
+       cli_shutdown(cli);
+       TALLOC_FREE(frame);
        return NT_STATUS_OK;
 }
 
@@ -1245,14 +1680,15 @@ static WERROR libnet_join_post_verify(TALLOC_CTX *mem_ctx,
 {
        NTSTATUS status;
 
-       status = libnet_join_ok(r->out.netbios_domain_name,
-                               r->in.machine_name,
-                               r->in.dc_name);
+       status = libnet_join_ok(r->in.msg_ctx,
+                               r->out.netbios_domain_name,
+                               r->in.dc_name,
+                               r->in.use_kerberos);
        if (!NT_STATUS_IS_OK(status)) {
                libnet_join_set_error_string(mem_ctx, r,
                        "failed to verify domain membership after joining: %s",
                        get_friendly_nt_error_msg(status));
-               return WERR_SETUP_NOT_JOINED;
+               return WERR_NERR_SETUPNOTJOINED;
        }
 
        return WERR_OK;
@@ -1291,7 +1727,7 @@ static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
        struct samr_Ids user_rids;
        struct samr_Ids name_types;
        union samr_UserInfo *info = NULL;
-       struct dcerpc_binding_handle *b;
+       struct dcerpc_binding_handle *b = NULL;
 
        ZERO_STRUCT(sam_pol);
        ZERO_STRUCT(domain_pol);
@@ -1299,6 +1735,7 @@ static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
 
        status = libnet_join_connect_dc_ipc(r->in.dc_name,
                                            r->in.admin_account,
+                                           r->in.admin_domain,
                                            r->in.admin_password,
                                            r->in.use_kerberos,
                                            &cli);
@@ -1308,7 +1745,7 @@ static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
 
        /* Open the domain */
 
-       status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
+       status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr,
                                          &pipe_hnd);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0,("Error connecting to SAM pipe. Error was %s\n",
@@ -1348,7 +1785,10 @@ static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
        /* Create domain user */
 
        acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
-       strlower_m(acct_name);
+       if (!strlower_m(acct_name)) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto done;
+       }
 
        init_lsa_String(&lsa_acct_name, acct_name);
 
@@ -1367,6 +1807,14 @@ static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
                status = result;
                goto done;
        }
+       if (user_rids.count != 1) {
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               goto done;
+       }
+       if (name_types.count != 1) {
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               goto done;
+       }
 
        if (name_types.ids[0] != SID_NAME_USER) {
                DEBUG(0, ("%s is not a user account (type=%d)\n", acct_name,
@@ -1432,7 +1880,7 @@ static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
        dcerpc_samr_Close(b, mem_ctx, &user_pol, &result);
 
 done:
-       if (pipe_hnd) {
+       if (pipe_hnd && b) {
                if (is_valid_policy_hnd(&domain_pol)) {
                        dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result);
                }
@@ -1454,40 +1902,61 @@ done:
 
 static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
 {
-       WERROR werr;
+       WERROR werr = WERR_OK;
+       sbcErr err;
        struct smbconf_ctx *ctx;
 
-       werr = smbconf_init_reg(r, &ctx, NULL);
-       if (!W_ERROR_IS_OK(werr)) {
+       err = smbconf_init_reg(r, &ctx, NULL);
+       if (!SBC_ERROR_IS_OK(err)) {
+               werr = WERR_SERVICE_DOES_NOT_EXIST;
                goto done;
        }
 
        if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
 
-               werr = smbconf_set_global_parameter(ctx, "security", "user");
-               W_ERROR_NOT_OK_GOTO_DONE(werr);
+               err = smbconf_set_global_parameter(ctx, "security", "user");
+               if (!SBC_ERROR_IS_OK(err)) {
+                       werr = WERR_SERVICE_DOES_NOT_EXIST;
+                       goto done;
+               }
 
-               werr = smbconf_set_global_parameter(ctx, "workgroup",
-                                                   r->in.domain_name);
+               err = smbconf_set_global_parameter(ctx, "workgroup",
+                                                  r->in.domain_name);
+               if (!SBC_ERROR_IS_OK(err)) {
+                       werr = WERR_SERVICE_DOES_NOT_EXIST;
+                       goto done;
+               }
 
                smbconf_delete_global_parameter(ctx, "realm");
                goto done;
        }
 
-       werr = smbconf_set_global_parameter(ctx, "security", "domain");
-       W_ERROR_NOT_OK_GOTO_DONE(werr);
+       err = smbconf_set_global_parameter(ctx, "security", "domain");
+       if (!SBC_ERROR_IS_OK(err)) {
+               werr = WERR_SERVICE_DOES_NOT_EXIST;
+               goto done;
+       }
 
-       werr = smbconf_set_global_parameter(ctx, "workgroup",
-                                           r->out.netbios_domain_name);
-       W_ERROR_NOT_OK_GOTO_DONE(werr);
+       err = smbconf_set_global_parameter(ctx, "workgroup",
+                                          r->out.netbios_domain_name);
+       if (!SBC_ERROR_IS_OK(err)) {
+               werr = WERR_SERVICE_DOES_NOT_EXIST;
+               goto done;
+       }
 
        if (r->out.domain_is_ad) {
-               werr = smbconf_set_global_parameter(ctx, "security", "ads");
-               W_ERROR_NOT_OK_GOTO_DONE(werr);
+               err = smbconf_set_global_parameter(ctx, "security", "ads");
+               if (!SBC_ERROR_IS_OK(err)) {
+                       werr = WERR_SERVICE_DOES_NOT_EXIST;
+                       goto done;
+               }
 
-               werr = smbconf_set_global_parameter(ctx, "realm",
-                                                   r->out.dns_domain_name);
-               W_ERROR_NOT_OK_GOTO_DONE(werr);
+               err = smbconf_set_global_parameter(ctx, "realm",
+                                                  r->out.dns_domain_name);
+               if (!SBC_ERROR_IS_OK(err)) {
+                       werr = WERR_SERVICE_DOES_NOT_EXIST;
+                       goto done;
+               }
        }
 
  done:
@@ -1501,20 +1970,28 @@ static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
 static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
 {
        WERROR werr = WERR_OK;
+       sbcErr err;
        struct smbconf_ctx *ctx;
 
-       werr = smbconf_init_reg(r, &ctx, NULL);
-       if (!W_ERROR_IS_OK(werr)) {
+       err = smbconf_init_reg(r, &ctx, NULL);
+       if (!SBC_ERROR_IS_OK(err)) {
+               werr = WERR_SERVICE_DOES_NOT_EXIST;
                goto done;
        }
 
        if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
 
-               werr = smbconf_set_global_parameter(ctx, "security", "user");
-               W_ERROR_NOT_OK_GOTO_DONE(werr);
+               err = smbconf_set_global_parameter(ctx, "security", "user");
+               if (!SBC_ERROR_IS_OK(err)) {
+                       werr = WERR_SERVICE_DOES_NOT_EXIST;
+                       goto done;
+               }
 
-               werr = smbconf_delete_global_parameter(ctx, "workgroup");
-               W_ERROR_NOT_OK_GOTO_DONE(werr);
+               err = smbconf_delete_global_parameter(ctx, "workgroup");
+               if (!SBC_ERROR_IS_OK(err)) {
+                       werr = WERR_SERVICE_DOES_NOT_EXIST;
+                       goto done;
+               }
 
                smbconf_delete_global_parameter(ctx, "realm");
        }
@@ -1544,7 +2021,7 @@ static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
                return werr;
        }
 
-       lp_load(get_dyn_CONFIGFILE(),true,false,false,true);
+       lp_load_global(get_dyn_CONFIGFILE());
 
        r->out.modified_config = true;
        r->out.result = werr;
@@ -1572,7 +2049,7 @@ static WERROR libnet_unjoin_config(struct libnet_UnjoinCtx *r)
                return werr;
        }
 
-       lp_load(get_dyn_CONFIGFILE(),true,false,false,true);
+       lp_load_global(get_dyn_CONFIGFILE());
 
        r->out.modified_config = true;
        r->out.result = werr;
@@ -1631,19 +2108,45 @@ static WERROR libnet_join_pre_processing(TALLOC_CTX *mem_ctx,
        if (!r->in.domain_name) {
                libnet_join_set_error_string(mem_ctx, r,
                        "No domain name defined");
-               return WERR_INVALID_PARAM;
+               return WERR_INVALID_PARAMETER;
        }
 
+       if (strlen(r->in.machine_name) > 15) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "Our netbios name can be at most 15 chars long, "
+                         "\"%s\" is %u chars long\n",
+                         r->in.machine_name,
+                        (unsigned int)strlen(r->in.machine_name));
+               return WERR_INVALID_PARAMETER;
+        }
+
        if (!libnet_parse_domain_dc(mem_ctx, r->in.domain_name,
                                    &r->in.domain_name,
                                    &r->in.dc_name)) {
                libnet_join_set_error_string(mem_ctx, r,
                        "Failed to parse domain name");
-               return WERR_INVALID_PARAM;
+               return WERR_INVALID_PARAMETER;
        }
 
-       if (IS_DC) {
-               return WERR_SETUP_DOMAIN_CONTROLLER;
+       if (!r->in.admin_domain) {
+               char *admin_domain = NULL;
+               char *admin_account = NULL;
+               bool ok;
+
+               ok = split_domain_user(mem_ctx,
+                                      r->in.admin_account,
+                                      &admin_domain,
+                                      &admin_account);
+               if (!ok) {
+                       return WERR_NOT_ENOUGH_MEMORY;
+               }
+
+               if (admin_domain != NULL) {
+                       r->in.admin_domain = admin_domain;
+               } else {
+                       r->in.admin_domain = r->in.domain_name;
+               }
+               r->in.admin_account = admin_account;
        }
 
        if (!secrets_init()) {
@@ -1667,7 +2170,7 @@ static void libnet_join_add_dom_rids_to_builtins(struct dom_sid *domain_sid)
        if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) {
                DEBUG(10,("Unable to auto-add domain administrators to "
                          "BUILTIN\\Administrators during join because "
-                         "winbindd must be running."));
+                         "winbindd must be running.\n"));
        } else if (!NT_STATUS_IS_OK(status)) {
                DEBUG(5, ("Failed to auto-add domain administrators to "
                          "BUILTIN\\Administrators during join: %s\n",
@@ -1678,7 +2181,7 @@ static void libnet_join_add_dom_rids_to_builtins(struct dom_sid *domain_sid)
        status = create_builtin_users(domain_sid);
        if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) {
                DEBUG(10,("Unable to auto-add domain users to BUILTIN\\users "
-                         "during join because winbindd must be running."));
+                         "during join because winbindd must be running.\n"));
        } else if (!NT_STATUS_IS_OK(status)) {
                DEBUG(5, ("Failed to auto-add domain administrators to "
                          "BUILTIN\\Administrators during join: %s\n",
@@ -1719,7 +2222,7 @@ static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx,
 
                ads_status  = libnet_join_post_processing_ads(mem_ctx, r);
                if (!ADS_ERR_OK(ads_status)) {
-                       return WERR_GENERAL_FAILURE;
+                       return WERR_GEN_FAILURE;
                }
        }
 #endif /* HAVE_ADS */
@@ -1734,17 +2237,10 @@ static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx,
 
 static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
 {
-       const char *krb5_cc_env = NULL;
-
        if (r->in.ads) {
                ads_destroy(&r->in.ads);
        }
 
-       krb5_cc_env = getenv(KRB5_ENV_CCNAME);
-       if (krb5_cc_env && StrCaseCmp(krb5_cc_env, "MEMORY:libnetjoin")) {
-               unsetenv(KRB5_ENV_CCNAME);
-       }
-
        return 0;
 }
 
@@ -1753,17 +2249,10 @@ static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
 
 static int libnet_destroy_UnjoinCtx(struct libnet_UnjoinCtx *r)
 {
-       const char *krb5_cc_env = NULL;
-
        if (r->in.ads) {
                ads_destroy(&r->in.ads);
        }
 
-       krb5_cc_env = getenv(KRB5_ENV_CCNAME);
-       if (krb5_cc_env && StrCaseCmp(krb5_cc_env, "MEMORY:libnetjoin")) {
-               unsetenv(KRB5_ENV_CCNAME);
-       }
-
        return 0;
 }
 
@@ -1774,27 +2263,29 @@ WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
                           struct libnet_JoinCtx **r)
 {
        struct libnet_JoinCtx *ctx;
-       const char *krb5_cc_env = NULL;
 
        ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
        if (!ctx) {
-               return WERR_NOMEM;
+               return WERR_NOT_ENOUGH_MEMORY;
        }
 
        talloc_set_destructor(ctx, libnet_destroy_JoinCtx);
 
-       ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
+       ctx->in.machine_name = talloc_strdup(mem_ctx, lp_netbios_name());
        W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
 
-       krb5_cc_env = getenv(KRB5_ENV_CCNAME);
-       if (!krb5_cc_env || (strlen(krb5_cc_env) == 0)) {
-               krb5_cc_env = talloc_strdup(mem_ctx, "MEMORY:libnetjoin");
-               W_ERROR_HAVE_NO_MEMORY(krb5_cc_env);
-               setenv(KRB5_ENV_CCNAME, krb5_cc_env, 1);
-       }
-
        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;
@@ -1807,25 +2298,17 @@ WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
                             struct libnet_UnjoinCtx **r)
 {
        struct libnet_UnjoinCtx *ctx;
-       const char *krb5_cc_env = NULL;
 
        ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
        if (!ctx) {
-               return WERR_NOMEM;
+               return WERR_NOT_ENOUGH_MEMORY;
        }
 
        talloc_set_destructor(ctx, libnet_destroy_UnjoinCtx);
 
-       ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
+       ctx->in.machine_name = talloc_strdup(mem_ctx, lp_netbios_name());
        W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
 
-       krb5_cc_env = getenv(KRB5_ENV_CCNAME);
-       if (!krb5_cc_env || (strlen(krb5_cc_env) == 0)) {
-               krb5_cc_env = talloc_strdup(mem_ctx, "MEMORY:libnetjoin");
-               W_ERROR_HAVE_NO_MEMORY(krb5_cc_env);
-               setenv(KRB5_ENV_CCNAME, krb5_cc_env, 1);
-       }
-
        *r = ctx;
 
        return WERR_OK;
@@ -1840,6 +2323,7 @@ static WERROR libnet_join_check_config(TALLOC_CTX *mem_ctx,
        bool valid_security = false;
        bool valid_workgroup = false;
        bool valid_realm = false;
+       bool ignored_realm = false;
 
        /* check if configuration is already set correctly */
 
@@ -1847,7 +2331,9 @@ static WERROR libnet_join_check_config(TALLOC_CTX *mem_ctx,
 
        switch (r->out.domain_is_ad) {
                case false:
-                       valid_security = (lp_security() == SEC_DOMAIN);
+                       valid_security = (lp_security() == SEC_DOMAIN)
+                               || (lp_server_role() == ROLE_DOMAIN_PDC)
+                               || (lp_server_role() == ROLE_DOMAIN_BDC);
                        if (valid_workgroup && valid_security) {
                                /* nothing to be done */
                                return WERR_OK;
@@ -1857,11 +2343,27 @@ static WERROR libnet_join_check_config(TALLOC_CTX *mem_ctx,
                        valid_realm = strequal(lp_realm(), r->out.dns_domain_name);
                        switch (lp_security()) {
                        case SEC_DOMAIN:
+                               if (!valid_realm && lp_winbind_rpc_only()) {
+                                       valid_realm = true;
+                                       ignored_realm = true;
+                               }
+                               /* FALL THROUGH */
                        case SEC_ADS:
                                valid_security = true;
                        }
 
                        if (valid_workgroup && valid_realm && valid_security) {
+                               if (ignored_realm && !r->in.modify_config)
+                               {
+                                       libnet_join_set_error_string(mem_ctx, r,
+                                               "Warning: ignoring realm when "
+                                               "joining AD domain with "
+                                               "'security=domain' and "
+                                               "'winbind rpc only = yes'. "
+                                               "(realm set to '%s', "
+                                               "should be '%s').", lp_realm(),
+                                               r->out.dns_domain_name);
+                               }
                                /* nothing to be done */
                                return WERR_OK;
                        }
@@ -1891,7 +2393,6 @@ static WERROR libnet_join_check_config(TALLOC_CTX *mem_ctx,
                if (!valid_security) {
                        const char *sec = NULL;
                        switch (lp_security()) {
-                       case SEC_SHARE: sec = "share"; break;
                        case SEC_USER:  sec = "user"; break;
                        case SEC_DOMAIN: sec = "domain"; break;
                        case SEC_ADS: sec = "ads"; break;
@@ -1933,10 +2434,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,
@@ -1945,19 +2463,61 @@ 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,
-                               "failed to find DC for domain %s",
+                               "failed to find DC for domain %s - %s",
                                r->in.domain_name,
                                get_friendly_nt_error_msg(status));
-                       return WERR_DCNOTFOUND;
+                       return WERR_NERR_DCNOTFOUND;
                }
 
                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_NERR_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_NERR_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_NERR_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);
@@ -1974,27 +2534,56 @@ static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
        }
 
 #ifdef HAVE_ADS
-       if (r->out.domain_is_ad && r->in.account_ou &&
+
+       if (r->out.domain_is_ad) {
+               create_local_private_krb5_conf_for_domain(
+                       r->out.dns_domain_name, r->out.netbios_domain_name,
+                       sitename, smbXcli_conn_remote_sockaddr(cli->conn));
+       }
+
+       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;
+                       return WERR_NERR_DEFAULTJOINREQUIRED;
                }
 
                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,
                                ads_errstr(ads_status));
-                       return WERR_DEFAULT_JOIN_REQUIRED;
+                       return WERR_NERR_DEFAULTJOINREQUIRED;
                }
 
-               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);
@@ -2006,14 +2595,14 @@ static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
                        "failed to join domain '%s' over rpc: %s",
                        r->in.domain_name, get_friendly_nt_error_msg(status));
                if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
-                       return WERR_SETUP_ALREADY_JOINED;
+                       return WERR_NERR_SETUPALREADYJOINED;
                }
                werr = ntstatus_to_werror(status);
                goto done;
        }
 
        if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) {
-               werr = WERR_SETUP_NOT_JOINED;
+               werr = WERR_NERR_SETUPNOTJOINED;
                goto done;
        }
 
@@ -2047,6 +2636,7 @@ static WERROR libnet_join_rollback(TALLOC_CTX *mem_ctx,
        u->in.admin_account     = r->in.admin_account;
        u->in.admin_password    = r->in.admin_password;
        u->in.modify_config     = r->in.modify_config;
+       u->in.use_kerberos      = r->in.use_kerberos;
        u->in.unjoin_flags      = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
                                  WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
 
@@ -2068,6 +2658,8 @@ WERROR libnet_Join(TALLOC_CTX *mem_ctx,
                LIBNET_JOIN_IN_DUMP_CTX(mem_ctx, r);
        }
 
+       ZERO_STRUCT(r->out);
+
        werr = libnet_join_pre_processing(mem_ctx, r);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
@@ -2114,7 +2706,7 @@ static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
                if (!secrets_fetch_domain_sid(lp_workgroup(), &sid)) {
                        libnet_unjoin_set_error_string(mem_ctx, r,
                                "Unable to fetch domain sid: are we joined?");
-                       return WERR_SETUP_NOT_JOINED;
+                       return WERR_NERR_SETUPNOTJOINED;
                }
                r->in.domain_sid = dom_sid_dup(mem_ctx, &sid);
                W_ERROR_HAVE_NO_MEMORY(r->in.domain_sid);
@@ -2140,10 +2732,10 @@ static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
                                     &info);
                if (!NT_STATUS_IS_OK(status)) {
                        libnet_unjoin_set_error_string(mem_ctx, r,
-                               "failed to find DC for domain %s",
+                               "failed to find DC for domain %s - %s",
                                r->in.domain_name,
                                get_friendly_nt_error_msg(status));
-                       return WERR_DCNOTFOUND;
+                       return WERR_NERR_DCNOTFOUND;
                }
 
                dc = strip_hostname(info->dc_unc);
@@ -2189,7 +2781,7 @@ static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
                                "failed to disable machine account via rpc: %s",
                                get_friendly_nt_error_msg(status));
                        if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
-                               return WERR_SETUP_NOT_JOINED;
+                               return WERR_NERR_SETUPNOTJOINED;
                        }
                        return ntstatus_to_werror(status);
                }
@@ -2214,7 +2806,7 @@ static WERROR libnet_unjoin_pre_processing(TALLOC_CTX *mem_ctx,
        if (!r->in.domain_name) {
                libnet_unjoin_set_error_string(mem_ctx, r,
                        "No domain name defined");
-               return WERR_INVALID_PARAM;
+               return WERR_INVALID_PARAMETER;
        }
 
        if (!libnet_parse_domain_dc(mem_ctx, r->in.domain_name,
@@ -2222,11 +2814,32 @@ static WERROR libnet_unjoin_pre_processing(TALLOC_CTX *mem_ctx,
                                    &r->in.dc_name)) {
                libnet_unjoin_set_error_string(mem_ctx, r,
                        "Failed to parse domain name");
-               return WERR_INVALID_PARAM;
+               return WERR_INVALID_PARAMETER;
        }
 
        if (IS_DC) {
-               return WERR_SETUP_DOMAIN_CONTROLLER;
+               return WERR_NERR_SETUPDOMAINCONTROLLER;
+       }
+
+       if (!r->in.admin_domain) {
+               char *admin_domain = NULL;
+               char *admin_account = NULL;
+               bool ok;
+
+               ok = split_domain_user(mem_ctx,
+                                      r->in.admin_account,
+                                      &admin_domain,
+                                      &admin_account);
+               if (!ok) {
+                       return WERR_NOT_ENOUGH_MEMORY;
+               }
+
+               if (admin_domain != NULL) {
+                       r->in.admin_domain = admin_domain;
+               } else {
+                       r->in.admin_domain = r->in.domain_name;
+               }
+               r->in.admin_account = admin_account;
        }
 
        if (!secrets_init()) {