r17472: Remove unused variable.
[samba.git] / source4 / libnet / libnet_join.c
index 1186853520ab78d2836b99a39a57f7b88e99bd0c..1d64257ca36ca20f222c52ea8c1b708539fffda5 100644 (file)
@@ -3,6 +3,7 @@
    
    Copyright (C) Stefan Metzmacher     2004
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+   Copyright (C) Brad Henry 2005
  
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include "includes.h"
 #include "libnet/libnet.h"
-#include "librpc/gen_ndr/ndr_samr.h"
-#include "lib/crypto/crypto.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
 #include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "passdb/secrets.h"
+#include "dsdb/samdb/samdb.h"
+#include "db_wrap.h"
+#include "libcli/security/security.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+/*
+ * complete a domain join, when joining to a AD domain:
+ * 1.) connect and bind to the DRSUAPI pipe
+ * 2.) do a DsCrackNames() to find the machine account dn
+ * 3.) connect to LDAP
+ * 4.) do an ldap search to find the "msDS-KeyVersionNumber" of the machine account
+ * 5.) set the servicePrincipalName's of the machine account via LDAP, (maybe we should use DsWriteAccountSpn()...)
+ * 6.) do a DsCrackNames() to find the domain dn
+ * 7.) find out Site specific stuff, look at libnet_JoinSite() for details
+ */
+static NTSTATUS libnet_JoinADSDomain(struct libnet_context *ctx, struct libnet_JoinDomain *r)
+{
+       NTSTATUS status;
+
+       TALLOC_CTX *tmp_ctx;
+
+       const char *realm = r->out.realm;
+
+       struct dcerpc_binding *samr_binding = r->out.samr_binding;
+
+       struct dcerpc_pipe *drsuapi_pipe;
+       struct dcerpc_binding *drsuapi_binding;
+       struct drsuapi_DsBind r_drsuapi_bind;
+       struct drsuapi_DsCrackNames r_crack_names;
+       struct drsuapi_DsNameString names[1];
+       struct policy_handle drsuapi_bind_handle;
+       struct GUID drsuapi_bind_guid;
+
+       struct ldb_context *remote_ldb;
+       const struct ldb_dn *account_dn;
+       const char *account_dn_str;
+       const char *remote_ldb_url;
+       struct ldb_result *res;
+       struct ldb_message *msg;
+
+       int ret, rtn;
+
+       unsigned int kvno;
+       
+       const char * const attrs[] = {
+               "msDS-KeyVersionNumber",
+               "servicePrincipalName",
+               "dNSHostName",
+               NULL,
+       };
+
+       r->out.error_string = NULL;
+       
+       /* We need to convert between a samAccountName and domain to a
+        * DN in the directory.  The correct way to do this is with
+        * DRSUAPI CrackNames */
+
+       /* Fiddle with the bindings, so get to DRSUAPI on
+        * NCACN_IP_TCP, sealed */
+       tmp_ctx = talloc_named(r, 0, "libnet_JoinADSDomain temp context");  
+       if (!tmp_ctx) {
+               r->out.error_string = NULL;
+               return NT_STATUS_NO_MEMORY;
+       }
+                                                  
+       drsuapi_binding = talloc(tmp_ctx, struct dcerpc_binding);
+       if (!drsuapi_binding) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       *drsuapi_binding = *samr_binding;
+
+       /* DRSUAPI is only available on IP_TCP, and locally on NCALRPC */
+       if (drsuapi_binding->transport != NCALRPC) {
+               drsuapi_binding->transport = NCACN_IP_TCP;
+       }
+       drsuapi_binding->endpoint = NULL;
+       drsuapi_binding->flags |= DCERPC_SEAL;
+
+       status = dcerpc_pipe_connect_b(tmp_ctx, 
+                                      &drsuapi_pipe,
+                                      drsuapi_binding,
+                                      &dcerpc_table_drsuapi,
+                                      ctx->cred, 
+                                      ctx->event_ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               r->out.error_string = talloc_asprintf(r,
+                                       "Connection to DRSUAPI pipe of PDC of domain '%s' failed: %s",
+                                       r->in.domain_name,
+                                       nt_errstr(status));
+               talloc_free(tmp_ctx);
+               return status;
+       }
+
+       /* get a DRSUAPI pipe handle */
+       GUID_from_string(DRSUAPI_DS_BIND_GUID, &drsuapi_bind_guid);
+
+       r_drsuapi_bind.in.bind_guid = &drsuapi_bind_guid;
+       r_drsuapi_bind.in.bind_info = NULL;
+       r_drsuapi_bind.out.bind_handle = &drsuapi_bind_handle;
+
+       status = dcerpc_drsuapi_DsBind(drsuapi_pipe, tmp_ctx, &r_drsuapi_bind);
+       if (!NT_STATUS_IS_OK(status)) {
+               if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+                       r->out.error_string
+                               = talloc_asprintf(r,
+                                                 "dcerpc_drsuapi_DsBind failed - %s\n", 
+                                                 dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
+                       talloc_free(tmp_ctx);
+                       return status;
+               } else {
+                       r->out.error_string
+                               = talloc_asprintf(r,
+                                                 "dcerpc_drsuapi_DsBind failed - %s\n", 
+                                                 nt_errstr(status));
+                       talloc_free(tmp_ctx);
+                       return status;
+               }
+       } else if (!W_ERROR_IS_OK(r_drsuapi_bind.out.result)) {
+               r->out.error_string
+                               = talloc_asprintf(r,
+                                                 "DsBind failed - %s\n", 
+                                                 win_errstr(r_drsuapi_bind.out.result));
+                       talloc_free(tmp_ctx);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* Actually 'crack' the names */
+       ZERO_STRUCT(r_crack_names);
+       r_crack_names.in.bind_handle            = &drsuapi_bind_handle;
+       r_crack_names.in.level                  = 1;
+       r_crack_names.in.req.req1.unknown1      = 0x000004e4;
+       r_crack_names.in.req.req1.unknown2      = 0x00000407;
+       r_crack_names.in.req.req1.count         = 1;
+       r_crack_names.in.req.req1.names         = names;
+       r_crack_names.in.req.req1.format_flags  = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
+       r_crack_names.in.req.req1.format_offered= DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY;
+       r_crack_names.in.req.req1.format_desired= DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+       names[0].str = dom_sid_string(tmp_ctx, r->out.account_sid);
+       if (!names[0].str) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names);
+       if (!NT_STATUS_IS_OK(status)) {
+               if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+                       r->out.error_string
+                               = talloc_asprintf(r,
+                                                 "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s\n", 
+                                                 names[0].str,
+                                                 dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
+                       talloc_free(tmp_ctx);
+                       return status;
+               } else {
+                       r->out.error_string
+                               = talloc_asprintf(r,
+                                                 "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s\n", 
+                                                 names[0].str,
+                                                 nt_errstr(status));
+                       talloc_free(tmp_ctx);
+                       return status;
+               }
+       } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
+               r->out.error_string
+                               = talloc_asprintf(r,
+                                                 "DsCrackNames failed - %s\n", win_errstr(r_crack_names.out.result));
+               talloc_free(tmp_ctx);
+               return NT_STATUS_UNSUCCESSFUL;
+       } else if (r_crack_names.out.level != 1 
+                  || !r_crack_names.out.ctr.ctr1 
+                  || r_crack_names.out.ctr.ctr1->count != 1 
+                  || !r_crack_names.out.ctr.ctr1->array[0].result_name
+                  || r_crack_names.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+               r->out.error_string = talloc_asprintf(r, "DsCrackNames failed\n");
+               talloc_free(tmp_ctx);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* Store the DN of our machine account. */
+       account_dn_str = r_crack_names.out.ctr.ctr1->array[0].result_name;
+
+       account_dn = ldb_dn_explode(tmp_ctx, account_dn_str);
+       if (!account_dn) {
+               r->out.error_string = talloc_asprintf(r, "Invalid account dn: %s",
+                                                     account_dn_str);
+               talloc_free(tmp_ctx);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* Now we know the user's DN, open with LDAP, read and modify a few things */
+
+       remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s", 
+                                        drsuapi_binding->host);
+       if (!remote_ldb_url) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       remote_ldb = ldb_wrap_connect(tmp_ctx, remote_ldb_url, 
+                                     NULL, ctx->cred, 0, NULL);
+       if (!remote_ldb) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_ctx);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* search for the user's record */
+       ret = ldb_search(remote_ldb, account_dn, LDB_SCOPE_BASE, 
+                            NULL, attrs, &res);
+       if (ret != LDB_SUCCESS || res->count != 1) {
+               r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - %s\n",
+                                                     account_dn_str, ldb_errstring(remote_ldb));
+               talloc_free(tmp_ctx);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* If we have a kvno recorded in AD, we need it locally as well */
+       kvno = ldb_msg_find_uint(res->msgs[0], "msDS-KeyVersionNumber", 0);
+
+       /* Prepare a new message, for the modify */
+       msg = ldb_msg_new(tmp_ctx);
+       if (!msg) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+       msg->dn = res->msgs[0]->dn;
+
+       {
+               int i;
+               const char *service_principal_name[6];
+               const char *dns_host_name = strlower_talloc(tmp_ctx, 
+                                                           talloc_asprintf(tmp_ctx, 
+                                                                           "%s.%s", 
+                                                                           r->in.netbios_name, 
+                                                                           realm));
+
+               if (!dns_host_name) {
+                       r->out.error_string = NULL;
+                       talloc_free(tmp_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               service_principal_name[0] = talloc_asprintf(tmp_ctx, "host/%s", dns_host_name);
+               service_principal_name[1] = talloc_asprintf(tmp_ctx, "host/%s", strlower_talloc(tmp_ctx, r->in.netbios_name));
+               service_principal_name[2] = talloc_asprintf(tmp_ctx, "host/%s/%s", dns_host_name, realm);
+               service_principal_name[3] = talloc_asprintf(tmp_ctx, "host/%s/%s", strlower_talloc(tmp_ctx, r->in.netbios_name), realm);
+               service_principal_name[4] = talloc_asprintf(tmp_ctx, "host/%s/%s", dns_host_name, r->out.domain_name);
+               service_principal_name[5] = talloc_asprintf(tmp_ctx, "host/%s/%s", strlower_talloc(tmp_ctx, r->in.netbios_name), r->out.domain_name);
+               
+               for (i=0; i < ARRAY_SIZE(service_principal_name); i++) {
+                       if (!service_principal_name[i]) {
+                               r->out.error_string = NULL;
+                               talloc_free(tmp_ctx);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+                       rtn = samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "servicePrincipalName", service_principal_name[i]);
+                       if (rtn == -1) {
+                               r->out.error_string = NULL;
+                               talloc_free(tmp_ctx);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+               }
+
+               rtn = samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "dNSHostName", dns_host_name);
+               if (rtn == -1) {
+                       r->out.error_string = NULL;
+                       talloc_free(tmp_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               rtn = samdb_replace(remote_ldb, tmp_ctx, msg);
+               if (rtn != 0) {
+                       r->out.error_string
+                               = talloc_asprintf(r, 
+                                                 "Failed to replace entries on %s\n", 
+                                                 ldb_dn_linearize(tmp_ctx, msg->dn));
+                       talloc_free(tmp_ctx);
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+       }
+                               
+       /* DsCrackNames to find out the DN of the domain. */
+       r_crack_names.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
+       r_crack_names.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+       names[0].str = talloc_asprintf(tmp_ctx, "%s\\", r->out.domain_name);
+       if (!names[0].str) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names);
+       if (!NT_STATUS_IS_OK(status)) {
+               if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+                       r->out.error_string
+                               = talloc_asprintf(r,
+                                                 "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s\n", 
+                                                 r->in.domain_name, 
+                                                 dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
+                       talloc_free(tmp_ctx);
+                       return status;
+               } else {
+                       r->out.error_string
+                               = talloc_asprintf(r,
+                                                 "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s\n", 
+                                                 r->in.domain_name, 
+                                                 nt_errstr(status));
+                       talloc_free(tmp_ctx);
+                       return status;
+               }
+       } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
+               r->out.error_string
+                       = talloc_asprintf(r,
+                                         "DsCrackNames failed - %s\n", win_errstr(r_crack_names.out.result));
+               talloc_free(tmp_ctx);
+               return NT_STATUS_UNSUCCESSFUL;
+       } else if (r_crack_names.out.level != 1 
+                  || !r_crack_names.out.ctr.ctr1 
+                  || r_crack_names.out.ctr.ctr1->count != 1
+                  || !r_crack_names.out.ctr.ctr1->array[0].result_name           
+                  || r_crack_names.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+               r->out.error_string = talloc_asprintf(r, "DsCrackNames failed\n");
+               talloc_free(tmp_ctx);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* Store the account DN. */
+       r->out.account_dn_str = account_dn_str;
+       talloc_steal(r, account_dn_str);
+
+       /* Store the domain DN. */
+       r->out.domain_dn_str = r_crack_names.out.ctr.ctr1->array[0].result_name;
+       talloc_steal(r, r_crack_names.out.ctr.ctr1->array[0].result_name);
+
+       r->out.kvno = kvno;
+
+       if (r->in.acct_type == ACB_SVRTRUST) {
+               status = libnet_JoinSite(remote_ldb, r);
+       }
+       talloc_free(tmp_ctx);
+
+       return status;
+}
 
 /*
  * do a domain join using DCERPC/SAMR calls
- * 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation)
- *    is it correct to contact the the pdc of the domain of the user who's password should be set?
- * 2. do a samr_Connect to get a policy handle
- * 3. do a samr_LookupDomain to get the domain sid
- * 4. do a samr_OpenDomain to get a domain handle
- * 5. do a samr_CreateAccount to try and get a new account 
+ * - connect to the LSA pipe, to try and find out information about the domain
+ * - create a secondary connection to SAMR pipe
+ * - do a samr_Connect to get a policy handle
+ * - do a samr_LookupDomain to get the domain sid
+ * - do a samr_OpenDomain to get a domain handle
+ * - do a samr_CreateAccount to try and get a new account 
  * 
  * If that fails, do:
- * 5.1. do a samr_LookupNames to get the users rid
- * 5.2. do a samr_OpenUser to get a user handle
+ * - do a samr_LookupNames to get the users rid
+ * - do a samr_OpenUser to get a user handle
+ * - potentially delete and recreate the user
+ * - assert the account is of the right type with samrQueryUserInfo
  * 
- * 6. call libnet_SetPassword_samr_handle to set the password
+ * - call libnet_SetPassword_samr_handle to set the password
  *
- * 7. do a samrSetUserInfo to set the account flags
+ * - do a samrSetUserInfo to set the account flags
+ * - do some ADS specific things when we join as Domain Controller,
+ *    look at libnet_joinADSDomain() for the details
  */
-static NTSTATUS libnet_JoinDomain_samr(struct libnet_context *ctx, 
-                                      TALLOC_CTX *mem_ctx, union libnet_JoinDomain *r)
+NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_JoinDomain *r)
 {
-       NTSTATUS status;
-       union libnet_rpc_connect c;
+       TALLOC_CTX *tmp_ctx;
+
+       NTSTATUS status, cu_status;
+
+       struct libnet_RpcConnect *connect_with_info;
+       struct dcerpc_pipe *samr_pipe;
+
        struct samr_Connect sc;
        struct policy_handle p_handle;
-       struct samr_LookupDomain ld;
-       struct samr_String d_name;
        struct samr_OpenDomain od;
        struct policy_handle d_handle;
        struct samr_LookupNames ln;
        struct samr_OpenUser ou;
        struct samr_CreateUser2 cu;
-       struct policy_handle u_handle;
+       struct policy_handle *u_handle = NULL;
        struct samr_QueryUserInfo qui;
        struct samr_SetUserInfo sui;
        union samr_UserInfo u_info;
        union libnet_SetPassword r2;
        struct samr_GetUserPwInfo pwp;
-       struct samr_String samr_account_name;
-
-       uint32_t acct_flags;
+       struct lsa_String samr_account_name;
+       
+       uint32_t acct_flags, old_acct_flags;
        uint32_t rid, access_granted;
        int policy_min_pw_len = 0;
 
-       /* prepare connect to the SAMR pipe of users domain PDC */
-       c.pdc.level                     = LIBNET_RPC_CONNECT_PDC;
-       c.pdc.in.domain_name            = r->samr.in.domain_name;
-       c.pdc.in.dcerpc_iface_name      = DCERPC_SAMR_NAME;
-       c.pdc.in.dcerpc_iface_uuid      = DCERPC_SAMR_UUID;
-       c.pdc.in.dcerpc_iface_version   = DCERPC_SAMR_VERSION;
+       struct dom_sid *account_sid = NULL;
+       const char *password_str = NULL;
+       
+       r->out.error_string = NULL;
+       r2.samr_handle.out.error_string = NULL;
+       
+       tmp_ctx = talloc_named(mem_ctx, 0, "libnet_Join temp context");
+       if (!tmp_ctx) {
+               r->out.error_string = NULL;
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       u_handle = talloc(tmp_ctx, struct policy_handle);
+       if (!u_handle) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       connect_with_info = talloc(tmp_ctx, struct libnet_RpcConnect);
+       if (!connect_with_info) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* prepare connect to the LSA pipe of PDC */
+       if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
+               connect_with_info->in.name    = r->in.domain_name;
+       } else {
+               connect_with_info->in.binding = r->in.binding;
+       }
+
+       connect_with_info->level              = LIBNET_RPC_CONNECT_DC_INFO;
+       connect_with_info->in.dcerpc_iface    = &dcerpc_table_samr;
+
+       /*
+         establish a SAMR connection, on the same CIFS transport
+       */
+       status = libnet_RpcConnect(ctx, tmp_ctx, connect_with_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               if (r->in.binding) {
+                       r->out.error_string = talloc_asprintf(mem_ctx,
+                                                             "Connection to SAMR pipe of DC %s failed: %s",
+                                                             r->in.binding, connect_with_info->out.error_string);
+               } else {
+                       r->out.error_string = talloc_asprintf(mem_ctx,
+                                                             "Connection to SAMR pipe of PDC for %s failed: %s",
+                                                             r->in.domain_name, connect_with_info->out.error_string);
+               }
+               talloc_free(tmp_ctx);
+               return status;
+       }
+
+       samr_pipe = connect_with_info->out.dcerpc_pipe;
 
-       /* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) */
-       status = libnet_rpc_connect(ctx, mem_ctx, &c);
+       status = dcerpc_pipe_auth(tmp_ctx, &samr_pipe,
+                                 connect_with_info->out.dcerpc_pipe->binding, 
+                                 &dcerpc_table_samr, ctx->cred);
        if (!NT_STATUS_IS_OK(status)) {
-               r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                               "Connection to SAMR pipe of PDC of domain '%s' failed: %s\n",
-                                               r->samr.in.domain_name, nt_errstr(status));
+               r->out.error_string = talloc_asprintf(mem_ctx,
+                                               "SAMR bind failed: %s",
+                                               nt_errstr(status));
+               talloc_free(tmp_ctx);
                return status;
        }
 
@@ -91,380 +500,693 @@ static NTSTATUS libnet_JoinDomain_samr(struct libnet_context *ctx,
        sc.out.connect_handle = &p_handle;
 
        /* 2. do a samr_Connect to get a policy handle */
-       status = dcerpc_samr_Connect(c.pdc.out.dcerpc_pipe, mem_ctx, &sc);
+       status = dcerpc_samr_Connect(samr_pipe, tmp_ctx, &sc);
        if (!NT_STATUS_IS_OK(status)) {
-               r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                               "samr_Connect failed: %s\n",
+               r->out.error_string = talloc_asprintf(mem_ctx,
+                                               "samr_Connect failed: %s",
                                                nt_errstr(status));
-               goto disconnect;
-       }
-
-       /* check result of samr_Connect */
-       if (!NT_STATUS_IS_OK(sc.out.result)) {
-               r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                               "samr_Connect failed: %s\n", 
-                                               nt_errstr(sc.out.result));
-               status = sc.out.result;
-               goto disconnect;
+               talloc_free(tmp_ctx);
+               return status;
        }
 
-       /* prepare samr_LookupDomain */
-       d_name.string = r->samr.in.domain_name;
-       ld.in.connect_handle = &p_handle;
-       ld.in.domain_name = &d_name;
-
-       /* 3. do a samr_LookupDomain to get the domain sid */
-       status = dcerpc_samr_LookupDomain(c.pdc.out.dcerpc_pipe, mem_ctx, &ld);
-       if (!NT_STATUS_IS_OK(status)) {
-               r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                               "samr_LookupDomain for [%s] failed: %s\n",
-                                               r->samr.in.domain_name, nt_errstr(status));
-               goto disconnect;
+       /* If this is a connection on ncacn_ip_tcp to Win2k3 SP1, we don't get back this useful info */
+       if (!connect_with_info->out.domain_name) {
+               if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
+                       connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, r->in.domain_name);
+               } else {
+                       /* Bugger, we just lost our way to automaticly find the domain name */
+                       connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, lp_workgroup());
+               }
        }
 
-       /* check result of samr_LookupDomain */
-       if (!NT_STATUS_IS_OK(ld.out.result)) {
-               r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                               "samr_LookupDomain for [%s] failed: %s\n",
-                                               r->samr.in.domain_name, nt_errstr(ld.out.result));
-               status = ld.out.result;
-               goto disconnect;
+       /* Perhaps we didn't get a SID above, because we are against ncacn_ip_tcp */
+       if (!connect_with_info->out.domain_sid) {
+               struct lsa_String name;
+               struct samr_LookupDomain l;
+               name.string = connect_with_info->out.domain_name;
+               l.in.connect_handle = &p_handle;
+               l.in.domain_name = &name;
+               
+               status = dcerpc_samr_LookupDomain(samr_pipe, tmp_ctx, &l);
+               if (!NT_STATUS_IS_OK(status)) {
+                       r->out.error_string = talloc_asprintf(mem_ctx,
+                                                             "SAMR LookupDomain failed: %s",
+                                                             nt_errstr(status));
+                       talloc_free(tmp_ctx);
+                       return status;
+               }
+               connect_with_info->out.domain_sid = l.out.sid;
        }
 
        /* prepare samr_OpenDomain */
        ZERO_STRUCT(d_handle);
        od.in.connect_handle = &p_handle;
        od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
-       od.in.sid = ld.out.sid;
+       od.in.sid = connect_with_info->out.domain_sid;
        od.out.domain_handle = &d_handle;
 
-       /* 4. do a samr_OpenDomain to get a domain handle */
-       status = dcerpc_samr_OpenDomain(c.pdc.out.dcerpc_pipe, mem_ctx, &od);
+       /* do a samr_OpenDomain to get a domain handle */
+       status = dcerpc_samr_OpenDomain(samr_pipe, tmp_ctx, &od);                       
        if (!NT_STATUS_IS_OK(status)) {
-               r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                               "samr_OpenDomain for [%s] failed: %s\n",
-                                               r->samr.in.domain_name, nt_errstr(status));
-               goto disconnect;
+               r->out.error_string = talloc_asprintf(mem_ctx,
+                                                     "samr_OpenDomain for [%s] failed: %s",
+                                                     dom_sid_string(tmp_ctx, connect_with_info->out.domain_sid),
+                                                     nt_errstr(status));
+               talloc_free(tmp_ctx);
+               return status;
        }
-
+       
        /* prepare samr_CreateUser2 */
-       ZERO_STRUCT(u_handle);
+       ZERO_STRUCTP(u_handle);
        cu.in.domain_handle  = &d_handle;
        cu.in.access_mask     = SEC_FLAG_MAXIMUM_ALLOWED;
-       samr_account_name.string = r->samr.in.account_name;
+       samr_account_name.string = r->in.account_name;
        cu.in.account_name    = &samr_account_name;
-       cu.in.acct_flags      = r->samr.in.acct_type;
-       cu.out.user_handle    = &u_handle;
+       cu.in.acct_flags      = r->in.acct_type;
+       cu.out.user_handle    = u_handle;
        cu.out.rid            = &rid;
        cu.out.access_granted = &access_granted;
 
-       /* 4. do a samr_CreateUser2 to get an account handle, or an error */
-       status = dcerpc_samr_CreateUser2(c.pdc.out.dcerpc_pipe, mem_ctx, &cu);
-       if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
-                       r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                                                  "samr_CreateUser2 for [%s] failed: %s\n",
-                                                                  r->samr.in.domain_name, nt_errstr(status));
-                       goto disconnect;
-
-       } else if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+       /* do a samr_CreateUser2 to get an account handle, or an error */
+       cu_status = dcerpc_samr_CreateUser2(samr_pipe, tmp_ctx, &cu);                   
+       status = cu_status;
+       if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
                /* prepare samr_LookupNames */
                ln.in.domain_handle = &d_handle;
                ln.in.num_names = 1;
-               ln.in.names = talloc_array(mem_ctx, struct samr_String, 1);
+               ln.in.names = talloc_array(tmp_ctx, struct lsa_String, 1);
                if (!ln.in.names) {
-                       r->samr.out.error_string = "Out of Memory";
+                       r->out.error_string = NULL;
+                       talloc_free(tmp_ctx);
                        return NT_STATUS_NO_MEMORY;
                }
-               ln.in.names[0].string = r->samr.in.account_name;
+               ln.in.names[0].string = r->in.account_name;
                
                /* 5. do a samr_LookupNames to get the users rid */
-               status = dcerpc_samr_LookupNames(c.pdc.out.dcerpc_pipe, mem_ctx, &ln);
+               status = dcerpc_samr_LookupNames(samr_pipe, tmp_ctx, &ln);
                if (!NT_STATUS_IS_OK(status)) {
-                       r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                                                  "samr_LookupNames for [%s] failed: %s\n",
-                                               r->samr.in.account_name, nt_errstr(status));
-                       goto disconnect;
+                       r->out.error_string = talloc_asprintf(mem_ctx,
+                                               "samr_LookupNames for [%s] failed: %s",
+                                               r->in.account_name,
+                                               nt_errstr(status));
+                       talloc_free(tmp_ctx);
+                       return status;
                }
                
-               
                /* check if we got one RID for the user */
                if (ln.out.rids.count != 1) {
-                       r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                                                  "samr_LookupNames for [%s] returns %d RIDs\n",
-                                                                  r->samr.in.account_name, ln.out.rids.count);
-                       status = NT_STATUS_INVALID_PARAMETER;
-                       goto disconnect;        
+                       r->out.error_string = talloc_asprintf(mem_ctx,
+                                                             "samr_LookupNames for [%s] returns %d RIDs\n",
+                                                             r->in.account_name, ln.out.rids.count);
+                       talloc_free(tmp_ctx);
+                       return NT_STATUS_INVALID_PARAMETER;
                }
                
                /* prepare samr_OpenUser */
-               ZERO_STRUCT(u_handle);
+               ZERO_STRUCTP(u_handle);
                ou.in.domain_handle = &d_handle;
                ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
                ou.in.rid = ln.out.rids.ids[0];
-               ou.out.user_handle = &u_handle;
+               rid = ou.in.rid;
+               ou.out.user_handle = u_handle;
                
                /* 6. do a samr_OpenUser to get a user handle */
-               status = dcerpc_samr_OpenUser(c.pdc.out.dcerpc_pipe, mem_ctx, &ou);
+               status = dcerpc_samr_OpenUser(samr_pipe, tmp_ctx, &ou); 
                if (!NT_STATUS_IS_OK(status)) {
-                       r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                                                  "samr_OpenUser for [%s] failed: %s\n",
-                                                                  r->samr.in.account_name, nt_errstr(status));
-                       goto disconnect;
+                       r->out.error_string = talloc_asprintf(mem_ctx,
+                                                       "samr_OpenUser for [%s] failed: %s",
+                                                       r->in.account_name,
+                                                       nt_errstr(status));
+                       talloc_free(tmp_ctx);
+                       return status;
                }
-       }
-
-       pwp.in.user_handle = &u_handle;
-
-       status = dcerpc_samr_GetUserPwInfo(c.pdc.out.dcerpc_pipe, mem_ctx, &pwp);
-       if (NT_STATUS_IS_OK(status)) {
-               policy_min_pw_len = pwp.out.info.min_password_length;
-       }
-
-       r->samr.out.join_password = generate_random_str(mem_ctx, MAX(8, policy_min_pw_len));
-
-       r2.samr_handle.level            = LIBNET_SET_PASSWORD_SAMR_HANDLE;
-       r2.samr_handle.in.account_name  = r->samr.in.account_name;
-       r2.samr_handle.in.newpassword   = r->samr.out.join_password;
-       r2.samr_handle.in.user_handle   = &u_handle;
-       r2.samr_handle.in.dcerpc_pipe   = c.pdc.out.dcerpc_pipe;
-
-       status = libnet_SetPassword(ctx, mem_ctx, &r2);
 
-       r->samr.out.error_string = r2.samr_handle.out.error_string;
+               if (r->in.recreate_account) {
+                       struct samr_DeleteUser d;
+                       d.in.user_handle = u_handle;
+                       d.out.user_handle = u_handle;
+                       status = dcerpc_samr_DeleteUser(samr_pipe, mem_ctx, &d);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               r->out.error_string = talloc_asprintf(mem_ctx,
+                                                                     "samr_DeleteUser (for recreate) of [%s] failed: %s",
+                                                                     r->in.account_name,
+                                                                     nt_errstr(status));
+                               talloc_free(tmp_ctx);
+                               return status;
+                       }
 
-       if (!NT_STATUS_IS_OK(status)) {
-               goto disconnect;
+                       /* We want to recreate, so delete and another samr_CreateUser2 */
+                       
+                       /* &cu filled in above */
+                       status = dcerpc_samr_CreateUser2(samr_pipe, tmp_ctx, &cu);                      
+                       if (!NT_STATUS_IS_OK(status)) {
+                               r->out.error_string = talloc_asprintf(mem_ctx,
+                                                                     "samr_CreateUser2 (recreate) for [%s] failed: %s\n",
+                                                                     r->in.account_name, nt_errstr(status));
+                               talloc_free(tmp_ctx);
+                               return status;
+                       }
+               }
+       } else if (!NT_STATUS_IS_OK(status)) {
+               r->out.error_string = talloc_asprintf(mem_ctx,
+                                                     "samr_CreateUser2 for [%s] failed: %s\n",
+                                                     r->in.account_name, nt_errstr(status));
+               talloc_free(tmp_ctx);
+               return status;
        }
 
-       /* prepare samr_SetUserInfo level 23 */
-       qui.in.user_handle = &u_handle;
+       /* prepare samr_QueryUserInfo (get flags) */
+       qui.in.user_handle = u_handle;
        qui.in.level = 16;
        
-       status = dcerpc_samr_QueryUserInfo(c.pdc.out.dcerpc_pipe, mem_ctx, &qui);
+       status = dcerpc_samr_QueryUserInfo(samr_pipe, tmp_ctx, &qui);
        if (!NT_STATUS_IS_OK(status)) {
-               r->samr.out.error_string
-                       = talloc_asprintf(mem_ctx,
-                                         "samr_QueryUserInfo for [%s] failed: %s\n",
-                                         r->samr.in.account_name, nt_errstr(status));
-               goto disconnect;
+               r->out.error_string = talloc_asprintf(mem_ctx,
+                                               "samr_QueryUserInfo for [%s] failed: %s",
+                                               r->in.account_name,
+                                               nt_errstr(status));
+               talloc_free(tmp_ctx);
+               return status;
        }
+       
        if (!qui.out.info) {
                status = NT_STATUS_INVALID_PARAMETER;
-               r->samr.out.error_string
+               r->out.error_string
                        = talloc_asprintf(mem_ctx,
                                          "samr_QueryUserInfo failed to return qui.out.info for [%s]: %s\n",
-                                         r->samr.in.account_name, nt_errstr(status));
-               goto disconnect;
+                                         r->in.account_name, nt_errstr(status));
+               talloc_free(tmp_ctx);
+               return status;
        }
-       
-       if ((qui.out.info->info16.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST)) 
-           != r->samr.in.acct_type) {
-               acct_flags = (qui.out.info->info16.acct_flags & ~(ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST))
-                             | r->samr.in.acct_type;
+
+       old_acct_flags = (qui.out.info->info16.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST));
+       /* Possibly bail if the account is of the wrong type */
+       if (old_acct_flags
+           != r->in.acct_type) {
+               const char *old_account_type, *new_account_type;
+               switch (old_acct_flags) {
+               case ACB_WSTRUST:
+                       old_account_type = "domain member (member)";
+                       break;
+               case ACB_SVRTRUST:
+                       old_account_type = "domain controller (bdc)";
+                       break;
+               case ACB_DOMTRUST:
+                       old_account_type = "trusted domain";
+                       break;
+               default:
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               switch (r->in.acct_type) {
+               case ACB_WSTRUST:
+                       new_account_type = "domain member (member)";
+                       break;
+               case ACB_SVRTRUST:
+                       new_account_type = "domain controller (bdc)";
+                       break;
+               case ACB_DOMTRUST:
+                       new_account_type = "trusted domain";
+                       break;
+               default:
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               if (!NT_STATUS_EQUAL(cu_status, NT_STATUS_USER_EXISTS)) {
+                       /* We created a new user, but they didn't come out the right type?!? */
+                       r->out.error_string
+                               = talloc_asprintf(mem_ctx,
+                                                 "We asked to create a new machine account (%s) of type %s, "
+                                                 "but we got an account of type %s.  This is unexpected.  "
+                                                 "Perhaps delete the account and try again.\n",
+                                                 r->in.account_name, new_account_type, old_account_type);
+                       talloc_free(tmp_ctx);
+                       return NT_STATUS_INVALID_PARAMETER;
+               } else {
+                       /* The account is of the wrong type, so bail */
+
+                       /* TODO: We should allow a --force option to override, and redo this from the top setting r.in.recreate_account */
+                       r->out.error_string
+                               = talloc_asprintf(mem_ctx,
+                                                 "The machine account (%s) already exists in the domain %s, "
+                                                 "but is a %s.  You asked to join as a %s.  Please delete "
+                                                 "the account and try again.\n",
+                                                 r->in.account_name, connect_with_info->out.domain_name, old_account_type, new_account_type);
+                       talloc_free(tmp_ctx);
+                       return NT_STATUS_USER_EXISTS;
+               }
        } else {
                acct_flags = qui.out.info->info16.acct_flags;
        }
        
        acct_flags = (acct_flags & ~ACB_DISABLED);
 
+       /* Find out what password policy this user has */
+       pwp.in.user_handle = u_handle;
+
+       status = dcerpc_samr_GetUserPwInfo(samr_pipe, tmp_ctx, &pwp);                           
+       if (NT_STATUS_IS_OK(status)) {
+               policy_min_pw_len = pwp.out.info.min_password_length;
+       }
+       
+       /* Grab a password of that minimum length */
+       
+       password_str = generate_random_str(tmp_ctx, MAX(8, policy_min_pw_len)); 
+
+       r2.samr_handle.level            = LIBNET_SET_PASSWORD_SAMR_HANDLE;
+       r2.samr_handle.in.account_name  = r->in.account_name;
+       r2.samr_handle.in.newpassword   = password_str;
+       r2.samr_handle.in.user_handle   = u_handle;
+       r2.samr_handle.in.dcerpc_pipe   = samr_pipe;
+
+       status = libnet_SetPassword(ctx, tmp_ctx, &r2); 
+       if (!NT_STATUS_IS_OK(status)) {
+               r->out.error_string = talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
+               talloc_free(tmp_ctx);
+               return status;
+       }
+
+       /* reset flags (if required) */
        if (acct_flags != qui.out.info->info16.acct_flags) {
                ZERO_STRUCT(u_info);
                u_info.info16.acct_flags = acct_flags;
 
-               sui.in.user_handle = &u_handle;
+               sui.in.user_handle = u_handle;
                sui.in.info = &u_info;
                sui.in.level = 16;
                
-               dcerpc_samr_SetUserInfo(c.pdc.out.dcerpc_pipe, mem_ctx, &sui);
+               dcerpc_samr_SetUserInfo(samr_pipe, tmp_ctx, &sui);
                if (!NT_STATUS_IS_OK(status)) {
-                       r->samr.out.error_string
-                               = talloc_asprintf(mem_ctx,
-                                                 "samr_SetUserInfo for [%s] failed to remove ACB_DISABLED flag: %s\n",
-                                                 r->samr.in.account_name, nt_errstr(status));
-                       goto disconnect;
+                       r->out.error_string = talloc_asprintf(mem_ctx,
+                                                       "samr_SetUserInfo for [%s] failed to remove ACB_DISABLED flag: %s",
+                                                       r->in.account_name,
+                                                       nt_errstr(status));
+                       talloc_free(tmp_ctx);
+                       return status;
                }
        }
 
-disconnect:
-       /* close connection */
-       talloc_free(c.pdc.out.dcerpc_pipe);
-
-       return status;
-}
-
-static NTSTATUS libnet_JoinDomain_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_JoinDomain *r)
-{
-       NTSTATUS status;
-       union libnet_JoinDomain r2;
+       account_sid = dom_sid_add_rid(mem_ctx, connect_with_info->out.domain_sid, rid);
+       if (!account_sid) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       r2.samr.level           = LIBNET_JOIN_DOMAIN_SAMR;
-       r2.samr.in.account_name = r->generic.in.account_name;
-       r2.samr.in.domain_name  = r->generic.in.domain_name;
-       r2.samr.in.acct_type    = r->generic.in.acct_type;
+       /* Finish out by pushing various bits of status data out for the caller to use */
+       r->out.join_password = password_str;
+       talloc_steal(mem_ctx, r->out.join_password);
 
-       status = libnet_JoinDomain(ctx, mem_ctx, &r2);
+       r->out.domain_sid = connect_with_info->out.domain_sid;
+       talloc_steal(mem_ctx, r->out.domain_sid);
 
-       r->generic.out.error_string = r2.samr.out.error_string;
-       r->generic.out.join_password = r2.samr.out.join_password;
+       r->out.account_sid = account_sid;
+       talloc_steal(mem_ctx, r->out.account_sid);
 
-       return status;
-}
+       r->out.domain_name = connect_with_info->out.domain_name;
+       talloc_steal(mem_ctx, r->out.domain_name);
+       r->out.realm = connect_with_info->out.realm;
+       talloc_steal(mem_ctx, r->out.realm);
+       r->out.samr_pipe = samr_pipe;
+       talloc_steal(mem_ctx, samr_pipe);
+       r->out.samr_binding = samr_pipe->binding;
+       talloc_steal(mem_ctx, r->out.samr_binding);
+       r->out.user_handle = u_handle;
+       talloc_steal(mem_ctx, u_handle);
+       r->out.error_string = r2.samr_handle.out.error_string;
+       talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
+       r->out.kvno = 0;
+       r->out.server_dn_str = NULL;
+       talloc_free(tmp_ctx); 
 
-NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_JoinDomain *r)
-{
-       switch (r->generic.level) {
-               case LIBNET_JOIN_DOMAIN_GENERIC:
-                       return libnet_JoinDomain_generic(ctx, mem_ctx, r);
-               case LIBNET_JOIN_DOMAIN_SAMR:
-                       return libnet_JoinDomain_samr(ctx, mem_ctx, r);
+       /* Now, if it was AD, then we want to start looking changing a
+        * few more things.  Otherwise, we are done. */
+       if (r->out.realm) {
+               status = libnet_JoinADSDomain(ctx, r);
+               return status;
        }
 
-       return NT_STATUS_INVALID_LEVEL;
+       return status;
 }
 
-
 static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, 
                                           TALLOC_CTX *mem_ctx, 
-                                          union libnet_Join *r)
+                                          struct libnet_Join *r)
 {
        NTSTATUS status;
-       int ret;
-
+       TALLOC_CTX *tmp_mem;
+       struct libnet_JoinDomain *r2;
+       int ret, rtn;
        struct ldb_context *ldb;
-       union libnet_JoinDomain r2;
-       const char *base_dn = "cn=Primary Domains";
-       const struct ldb_val *prior_secret;
-       const char *prior_modified_time;
+       const struct ldb_dn *base_dn;
        struct ldb_message **msgs, *msg;
-       char *sct;
-       const char *attrs[] = {
+       const char *sct;
+       const char * const attrs[] = {
                "whenChanged",
                "secret",
-               "priorSecret"
+               "priorSecret",
                "priorChanged",
+               "krb5Keytab",
+               "privateKeytab",
                NULL
        };
+       uint32_t acct_type = 0;
+       const char *account_name;
+       const char *netbios_name;
+       char *filter;
+       
+       r->out.error_string = NULL;
 
-       r2.generic.level = LIBNET_JOIN_DOMAIN_GENERIC;
-
-       if (r->generic.in.secure_channel_type == SEC_CHAN_BDC) {
-               r2.generic.in.acct_type = ACB_SVRTRUST;
-       } else if (r->generic.in.secure_channel_type == SEC_CHAN_WKSTA) {
-               r2.generic.in.acct_type = ACB_WSTRUST;
+       tmp_mem = talloc_new(mem_ctx);
+       if (!tmp_mem) {
+               return NT_STATUS_NO_MEMORY;
        }
-       r2.generic.in.domain_name  = r->generic.in.domain_name;
-
-       r2.generic.in.account_name = talloc_asprintf(mem_ctx, "%s$", lp_netbios_name());
 
-       /* Local secrets are stored in secrets.ldb */
-       ldb = secrets_db_connect(mem_ctx);
+       r2 = talloc(tmp_mem, struct libnet_JoinDomain);
+       if (!r2) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_mem);
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       if (r->in.join_type == SEC_CHAN_BDC) {
+               acct_type = ACB_SVRTRUST;
+       } else if (r->in.join_type == SEC_CHAN_WKSTA) {
+               acct_type = ACB_WSTRUST;
+       } else {
+               r->out.error_string = NULL;
+               talloc_free(tmp_mem);   
+               return NT_STATUS_INVALID_PARAMETER;
+       }
 
-       /* join domain */
-       status = libnet_JoinDomain(ctx, mem_ctx, &r2);
+       if (r->in.netbios_name != NULL) {
+               netbios_name = r->in.netbios_name;
+       } else {
+               netbios_name = talloc_reference(tmp_mem, lp_netbios_name());
+               if (!netbios_name) {
+                       r->out.error_string = NULL;
+                       talloc_free(tmp_mem);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
 
-       r->generic.out.error_string = r2.generic.out.error_string;
+       account_name = talloc_asprintf(tmp_mem, "%s$", netbios_name);
+       if (!account_name) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_mem);
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       /*
+        * Local secrets are stored in secrets.ldb 
+        * open it to make sure we can write the info into it after the join
+        */
+       ldb = secrets_db_connect(tmp_mem);
+       if (!ldb) {
+               r->out.error_string
+                       = talloc_asprintf(mem_ctx, 
+                                         "Could not open secrets database\n");
+               talloc_free(tmp_mem);
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
 
-       /* store in secrets.ldb or samdb.ldb, depending on secret type */
+       /*
+        * join the domain
+        */
+       ZERO_STRUCTP(r2);
+       r2->in.domain_name      = r->in.domain_name;
+       r2->in.account_name     = account_name;
+       r2->in.netbios_name     = netbios_name;
+       r2->in.level            = LIBNET_JOINDOMAIN_AUTOMATIC;
+       r2->in.acct_type        = acct_type;
+       r2->in.recreate_account = False;
+       status = libnet_JoinDomain(ctx, r2, r2);
        if (!NT_STATUS_IS_OK(status)) {
+               r->out.error_string = talloc_steal(mem_ctx, r2->out.error_string);
+               talloc_free(tmp_mem);
                return status;
        }
+       
+       /*
+        * now prepare the record for secrets.ldb
+        */
+       sct = talloc_asprintf(tmp_mem, "%d", r->in.join_type); 
+       if (!sct) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_mem);
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       msg = ldb_msg_new(tmp_mem);
+       if (!msg) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_mem);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       base_dn = ldb_dn_explode(tmp_mem, "cn=Primary Domains");
+       if (!base_dn) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_mem);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       sct = talloc_asprintf(mem_ctx, "%d", r->generic.in.secure_channel_type);
-       msg = ldb_msg_new(mem_ctx);
+       msg->dn = ldb_dn_build_child(tmp_mem, "flatname", r2->out.domain_name, base_dn);
+       if (!msg->dn) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_mem);
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "flatname", r2->out.domain_name);
+       if (rtn == -1) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_mem);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       /* search for the secret record */
-       ret = samdb_search(ldb,
-                          mem_ctx, base_dn, &msgs, attrs,
-                          "(&(flatname=%s)(objectclass=primaryDomain))", 
-                          r->generic.in.domain_name);
-       if (ret == 0) {
-               msg->dn = talloc_asprintf(mem_ctx, "flatname=%s,%s", 
-                                         r->generic.in.domain_name,
-                                         base_dn);
-               
-               samdb_msg_add_string(ldb, mem_ctx, msg, "flatname", r->generic.in.domain_name);
-               samdb_msg_add_string(ldb, mem_ctx, msg, "objectClass", "primaryDomain");
-               samdb_msg_add_string(ldb, mem_ctx, msg, "secret", r2.generic.out.join_password);
+       if (r2->out.realm) {
+               rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "realm", r2->out.realm);
+               if (rtn == -1) {
+                       r->out.error_string = NULL;
+                       talloc_free(tmp_mem);
+                       return NT_STATUS_NO_MEMORY;
+               }
 
-               samdb_msg_add_string(ldb, mem_ctx, msg, "samAccountName", r2.generic.in.account_name);
+               rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "objectClass", "primaryDomain");
+               if (rtn == -1) {
+                       r->out.error_string = NULL;
+                       talloc_free(tmp_mem);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "objectClass", "primaryDomain");
+       if (rtn == -1) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_mem);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-               samdb_msg_add_string(ldb, mem_ctx, msg, "secureChannelType", sct);
+       rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "secret", r2->out.join_password);
+       if (rtn == -1) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_mem);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-               /* create the secret */
-               ret = samdb_add(ldb, mem_ctx, msg);
-               if (ret != 0) {
-                       r->generic.out.error_string
-                               = talloc_asprintf(mem_ctx, 
-                                                 "Failed to create secret record %s\n", 
-                                                 msg->dn);
-                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+       rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "samAccountName", r2->in.account_name);
+       if (rtn == -1) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_mem);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "secureChannelType", sct);
+       if (rtn == -1) {
+               r->out.error_string = NULL;
+               talloc_free(tmp_mem);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (r2->out.kvno) {
+               rtn = samdb_msg_add_uint(ldb, tmp_mem, msg, "msDS-KeyVersionNumber",
+                                        r2->out.kvno);
+               if (rtn == -1) {
+                       r->out.error_string = NULL;
+                       talloc_free(tmp_mem);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       if (r2->out.domain_sid) {
+               rtn = samdb_msg_add_dom_sid(ldb, tmp_mem, msg, "objectSid",
+                                           r2->out.domain_sid);
+               if (rtn == -1) {
+                       r->out.error_string = NULL;
+                       talloc_free(tmp_mem);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       /* 
+        * search for the secret record
+        * - remove the records we find
+        * - and fetch the old secret and store it under priorSecret
+        */
+       ret = gendb_search(ldb,
+                          tmp_mem, base_dn,
+                          &msgs, attrs,
+                          "(|" SECRETS_PRIMARY_DOMAIN_FILTER "(realm=%s))",
+                          r2->out.domain_name, r2->out.realm);
+       if (ret == 0) {
+               rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secretsKeytab", "secrets.keytab");
+               if (rtn == -1) {
+                       r->out.error_string = NULL;
+                       talloc_free(tmp_mem);
+                       return NT_STATUS_NO_MEMORY;
                }
-               return NT_STATUS_OK;
-       } else if (ret != 1) {
-               r->generic.out.error_string
+       } else if (ret == -1) {
+               r->out.error_string
                        = talloc_asprintf(mem_ctx, 
-                                         "Found %d records matching cn=%s under DN %s\n", ret, 
-                                         r->generic.in.domain_name, base_dn);
+                                         "Search for domain: %s and realm: %s failed: %s", 
+                                         r2->out.domain_name, r2->out.realm, ldb_errstring(ldb));
+               talloc_free(tmp_mem);
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
-       }
+       } else {
+               const struct ldb_val *private_keytab;
+               const struct ldb_val *krb5_keytab;
+               const struct ldb_val *prior_secret;
+               const struct ldb_val *prior_modified_time;
+               int i;
 
-       msg->dn = msgs[0]->dn;
+               for (i = 0; i < ret; i++) {
+                       ldb_delete(ldb, msgs[i]->dn);
+               }
 
-       prior_secret = ldb_msg_find_ldb_val(msgs[0], "secret");
-       if (prior_secret) {
-               samdb_msg_set_value(ldb, mem_ctx, msg, "priorSecret", prior_secret);
-       }
-       samdb_msg_set_string(ldb, mem_ctx, msg, "secret", r2.generic.out.join_password);
-       
-       prior_modified_time = ldb_msg_find_string(msgs[0], 
-                                                "whenChanged", NULL);
-       if (prior_modified_time) {
-               samdb_msg_set_string(ldb, mem_ctx, msg, "priorWhenChanged", 
-                                    prior_modified_time);
+               prior_secret = ldb_msg_find_ldb_val(msgs[0], "secret");
+               if (prior_secret) {
+                       rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "priorSecret", prior_secret);
+                       if (rtn == -1) {
+                               r->out.error_string = NULL;
+                               talloc_free(tmp_mem);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+               }
+               rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secret", r2->out.join_password);
+               if (rtn == -1) {
+                       r->out.error_string = NULL;
+                       talloc_free(tmp_mem);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               prior_modified_time = ldb_msg_find_ldb_val(msgs[0], 
+                                                          "whenChanged");
+               if (prior_modified_time) {
+                       rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "priorWhenChanged", 
+                                                 prior_modified_time);
+                       if (rtn == -1) {
+                               r->out.error_string = NULL;
+                               talloc_free(tmp_mem);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+               }
+
+               rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "samAccountName", r2->in.account_name);
+               if (rtn == -1) {
+                       r->out.error_string = NULL;
+                       talloc_free(tmp_mem);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secureChannelType", sct);
+               if (rtn == -1) {
+                       r->out.error_string = NULL;
+                       talloc_free(tmp_mem);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               /* We will want to keep the keytab names */
+               private_keytab = ldb_msg_find_ldb_val(msgs[0], "privateKeytab");
+               if (private_keytab) {
+                       rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "privateKeytab", private_keytab);
+                       if (rtn == -1) {
+                               r->out.error_string = NULL;
+                               talloc_free(tmp_mem);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+               }
+               krb5_keytab = ldb_msg_find_ldb_val(msgs[0], "krb5Keytab");
+               if (krb5_keytab) {
+                       rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "krb5Keytab", krb5_keytab);
+                       if (rtn == -1) {
+                               r->out.error_string = NULL;
+                               talloc_free(tmp_mem);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+               }
        }
-       
-       samdb_msg_set_string(ldb, mem_ctx, msg, "samAccountName", r2.generic.in.account_name);
-       samdb_msg_set_string(ldb, mem_ctx, msg, "secureChannelType", sct);
 
-       /* update the secret */
-       ret = samdb_replace(ldb, mem_ctx, msg);
+       /* create the secret */
+       ret = samdb_add(ldb, tmp_mem, msg);
        if (ret != 0) {
-               DEBUG(0,("Failed to create secret record %s\n", msg->dn));
+               r->out.error_string = talloc_asprintf(mem_ctx, "Failed to create secret record %s\n", 
+                                                     ldb_dn_linearize(ldb, msg->dn));
+               talloc_free(tmp_mem);
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }
-       return NT_STATUS_OK;
-}
 
-NTSTATUS libnet_Join_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_Join *r)
-{
-       NTSTATUS nt_status;
-       union libnet_Join r2;
-       r2.generic.in.secure_channel_type = r->generic.in.secure_channel_type;
-       r2.generic.in.domain_name = r->generic.in.domain_name;
-       
-       if ((r->generic.in.secure_channel_type == SEC_CHAN_WKSTA)
-           || (r->generic.in.secure_channel_type == SEC_CHAN_BDC)) {
-               r2.generic.level = LIBNET_JOIN_PRIMARY;
-               nt_status = libnet_Join(ctx, mem_ctx, &r2);
-       } else {
-               r->generic.out.error_string
-                       = talloc_asprintf(mem_ctx, "Invalid secure channel type specified (%08X) attempting to join domain %s",
-                                        r->generic.in.secure_channel_type, r->generic.in.domain_name);
-               return NT_STATUS_INVALID_PARAMETER;
+       if (r2->out.realm) {
+               struct cli_credentials *creds;
+               /* Make a credentials structure from it */
+               creds = cli_credentials_init(mem_ctx);
+               if (!creds) {
+                       r->out.error_string = NULL;
+                       talloc_free(tmp_mem);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               cli_credentials_set_conf(creds);
+               filter = talloc_asprintf(mem_ctx, "dn=%s", ldb_dn_linearize(mem_ctx, msg->dn));
+               status = cli_credentials_set_secrets(creds, NULL, filter);
+               if (!NT_STATUS_IS_OK(status)) {
+                       r->out.error_string = talloc_asprintf(mem_ctx, "Failed to read secrets for keytab update for %s\n", 
+                                                             filter);
+                       talloc_free(tmp_mem);
+                       return status;
+               } 
+               ret = cli_credentials_update_keytab(creds);
+               if (ret != 0) {
+                       r->out.error_string = talloc_asprintf(mem_ctx, "Failed to update keytab for %s\n", 
+                                                             filter);
+                       talloc_free(tmp_mem);
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
        }
-       r->generic.out.error_string = r2.generic.out.error_string;
-       return nt_status;
+
+       /* move all out parameter to the callers TALLOC_CTX */
+       r->out.error_string     = NULL;
+       r->out.join_password    = r2->out.join_password;
+       talloc_steal(mem_ctx, r2->out.join_password);
+       r->out.domain_sid       = r2->out.domain_sid;
+       talloc_steal(mem_ctx, r2->out.domain_sid);
+       r->out.domain_name      = r2->out.domain_name;
+       talloc_steal(mem_ctx, r2->out.domain_name);
+       talloc_free(tmp_mem);
+       return NT_STATUS_OK;
 }
 
-NTSTATUS libnet_Join(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_Join *r)
+NTSTATUS libnet_Join(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_Join *r)
 {
-       switch (r->generic.level) {
-       case LIBNET_JOIN_GENERIC:
-               return libnet_Join_generic(ctx, mem_ctx, r);
-       case LIBNET_JOIN_PRIMARY:
-               return libnet_Join_primary_domain(ctx, mem_ctx, r);
+       switch (r->in.join_type) {
+               case SEC_CHAN_WKSTA:
+                       return libnet_Join_primary_domain(ctx, mem_ctx, r);
+               case SEC_CHAN_BDC:
+                       return libnet_Join_primary_domain(ctx, mem_ctx, r);
+               case SEC_CHAN_DOMAIN:
+                       break;
        }
 
-       return NT_STATUS_INVALID_LEVEL;
+       r->out.error_string = talloc_asprintf(mem_ctx,
+                                             "Invalid join type specified (%08X) attempting to join domain %s",
+                                             r->in.join_type, r->in.domain_name);
+       return NT_STATUS_INVALID_PARAMETER;
 }
-