s4-libnet: added libnet_vampire_replicate_init()
[kai/samba.git] / source4 / libnet / libnet_vampire.c
index ce65a6e460a2a05a3076a6883d1ea8bb591a0558..955aa32318afe4832c24b85413461ceb0b8d2923 100644 (file)
@@ -1,13 +1,15 @@
 /* 
    Unix SMB/CIFS implementation.
    
-   Extract the user/system database from a remote SamSync server
+   Extract the user/system database from a remote server
 
-   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+   Copyright (C) Stefan Metzmacher     2004-2006
+   Copyright (C) Brad Henry 2005
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2008
    
    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
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 
 #include "includes.h"
 #include "libnet/libnet.h"
+#include "lib/events/events.h"
+#include "dsdb/samdb/samdb.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "system/time.h"
+#include "ldb_wrap.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+#include "param/provision.h"
+#include "libcli/security/dom_sid.h"
+#include "dsdb/common/util.h"
 
+/* 
+List of tasks vampire.py must perform:
+- Domain Join
+ - but don't write the secrets.ldb
+ - results for this should be enough to handle the provision
+- if vampire method is samsync 
+ - Provision using these results 
+  - do we still want to support this NT4 technology?
+- Start samsync with libnet code
+ - provision in the callback 
+- Write out the secrets database, using the code from libnet_Join
+
+*/
+struct libnet_vampire_cb_state {
+       const char *netbios_name;
+       const char *domain_name;
+       const char *realm;
+       struct cli_credentials *machine_account;
+
+       /* Schema loaded from local LDIF files */
+       struct dsdb_schema *provision_schema;
+
+        /* 1st pass, with some OIDs/attribute names/class names not
+        * converted, because we may not know them yet */
+       struct dsdb_schema *self_made_schema;
+
+       /* 2nd pass, with full ID->OID->name table */
+       struct dsdb_schema *self_corrected_schema;
 
-/**
- * Decrypt and extract the user's passwords.  
- * 
- * The writes decrypted (no longer 'RID encrypted' or arcfour encrypted) passwords back into the structure
- */
-static NTSTATUS fix_user(TALLOC_CTX *mem_ctx,
-                        struct creds_CredentialState *creds,
-                        enum netr_SamDatabaseID database,
-                        struct netr_DELTA_ENUM *delta,
-                        char **error_string) 
+       /* prefixMap in LDB format, from the remote DRS server */
+       DATA_BLOB prefixmap_blob;
+       const struct dsdb_schema *schema;
+
+       struct ldb_context *ldb;
+
+       struct {
+               uint32_t object_count;
+               struct drsuapi_DsReplicaObjectListItemEx *first_object;
+               struct drsuapi_DsReplicaObjectListItemEx *last_object;
+       } schema_part;
+
+       const char *targetdir;
+
+       struct loadparm_context *lp_ctx;
+       struct tevent_context *event_ctx;
+       unsigned total_objects;
+       char *last_partition;
+       const char *server_dn_str;
+};
+
+/* initialise a state structure ready for replication of chunks */
+void *libnet_vampire_replicate_init(TALLOC_CTX *mem_ctx,
+                                   struct ldb_context *samdb,
+                                   struct loadparm_context *lp_ctx)
 {
+       struct libnet_vampire_cb_state *s = talloc_zero(mem_ctx, struct libnet_vampire_cb_state);
+       if (!s) {
+               return NULL;
+       }
 
-       uint32_t rid = delta->delta_id_union.rid;
-       struct netr_DELTA_USER *user = delta->delta_union.user;
-       struct samr_Password lm_hash;
-       struct samr_Password nt_hash;
-       const char *username = user->account_name.string;
-       NTSTATUS nt_status;
-
-       if (user->lm_password_present) {
-               sam_rid_crypt(rid, user->lmpassword.hash, lm_hash.hash, 0);
-               user->lmpassword = lm_hash;
-       }
-
-       if (user->nt_password_present) {
-               sam_rid_crypt(rid, user->ntpassword.hash, nt_hash.hash, 0);
-               user->ntpassword = nt_hash;
-       }
-
-       if (user->user_private_info.SensitiveData) {
-               DATA_BLOB data;
-               struct netr_USER_KEYS keys;
-               data.data = user->user_private_info.SensitiveData;
-               data.length = user->user_private_info.DataLength;
-               creds_arcfour_crypt(creds, data.data, data.length);
-               user->user_private_info.SensitiveData = data.data;
-               user->user_private_info.DataLength = data.length;
-
-               nt_status = ndr_pull_struct_blob(&data, mem_ctx, &keys, (ndr_pull_flags_fn_t)ndr_pull_netr_USER_KEYS);
-               if (NT_STATUS_IS_OK(nt_status)) {
-                       if (keys.keys.keys2.lmpassword.length == 16) {
-                               sam_rid_crypt(rid, keys.keys.keys2.lmpassword.pwd.hash, lm_hash.hash, 0);
-                               user->lmpassword = lm_hash;
-                               user->lm_password_present = True;
-                       }
-                       if (keys.keys.keys2.ntpassword.length == 16) {
-                               sam_rid_crypt(rid, keys.keys.keys2.ntpassword.pwd.hash, nt_hash.hash, 0);
-                               user->ntpassword = nt_hash;
-                               user->nt_password_present = True;
-                       }
-               } else {
-                       *error_string = talloc_asprintf(mem_ctx, "Failed to parse Sensitive Data for %s:\n", username);
-                       dump_data(10, data.data, data.length);
-                       return nt_status;
-               }
+       s->ldb              = samdb;
+       s->lp_ctx           = lp_ctx;
+       s->provision_schema = dsdb_get_schema(s->ldb, s);
+
+       return s;
+}
+
+/* Caller is expected to keep supplied pointers around for the lifetime of the structure */
+void *libnet_vampire_cb_state_init(TALLOC_CTX *mem_ctx,
+                                  struct loadparm_context *lp_ctx, struct tevent_context *event_ctx,
+                                  const char *netbios_name, const char *domain_name, const char *realm,
+                                  const char *targetdir)
+{
+       struct libnet_vampire_cb_state *s = talloc_zero(mem_ctx, struct libnet_vampire_cb_state);
+       if (!s) {
+               return NULL;
        }
-       return NT_STATUS_OK;
+
+       s->lp_ctx = lp_ctx;
+       s->event_ctx = event_ctx;
+       s->netbios_name = netbios_name;
+       s->domain_name = domain_name;
+       s->realm = realm;
+       s->targetdir = targetdir;
+       return s;
 }
 
-/**
- * Decrypt and extract the secrets
- * 
- * The writes decrypted secrets back into the structure
- */
-static NTSTATUS fix_secret(TALLOC_CTX *mem_ctx,
-                          struct creds_CredentialState *creds,
-                          enum netr_SamDatabaseID database,
-                          struct netr_DELTA_ENUM *delta,
-                          char **error_string) 
+struct ldb_context *libnet_vampire_cb_ldb(struct libnet_vampire_cb_state *state)
 {
-       struct netr_DELTA_SECRET *secret = delta->delta_union.secret;
-       creds_arcfour_crypt(creds, secret->current_cipher.cipher_data, 
-                           secret->current_cipher.maxlen); 
+       state = talloc_get_type_abort(state, struct libnet_vampire_cb_state);
+       return state->ldb;
+}
+
+struct loadparm_context *libnet_vampire_cb_lp_ctx(struct libnet_vampire_cb_state *state)
+{
+       state = talloc_get_type_abort(state, struct libnet_vampire_cb_state);
+       return state->lp_ctx;
+}
+
+NTSTATUS libnet_vampire_cb_prepare_db(void *private_data,
+                                     const struct libnet_BecomeDC_PrepareDB *p)
+{
+       struct libnet_vampire_cb_state *s = talloc_get_type(private_data, struct libnet_vampire_cb_state);
+       struct provision_settings settings;
+       struct provision_result result;
+       NTSTATUS status;
+
+       ZERO_STRUCT(settings);
+       settings.site_name = p->dest_dsa->site_name;
+       settings.root_dn_str = p->forest->root_dn_str;
+       settings.domain_dn_str = p->domain->dn_str;
+       settings.config_dn_str = p->forest->config_dn_str;
+       settings.schema_dn_str = p->forest->schema_dn_str;
+       settings.netbios_name = p->dest_dsa->netbios_name;
+       settings.realm = s->realm;
+       settings.domain = s->domain_name;
+       settings.server_dn_str = p->dest_dsa->server_dn_str;
+       settings.machine_password = generate_random_password(s, 16, 255);
+       settings.targetdir = s->targetdir;
+
+       status = provision_bare(s, s->lp_ctx, &settings, &result);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       s->ldb = talloc_steal(s, result.samdb);
+       s->lp_ctx = talloc_steal(s, result.lp_ctx);
+       s->provision_schema = dsdb_get_schema(s->ldb, s);
+       s->server_dn_str = talloc_steal(s, p->dest_dsa->server_dn_str);
+
+       /* wrap the entire vapire operation in a transaction.  This
+          isn't just cosmetic - we use this to ensure that linked
+          attribute back links are added at the end by relying on a
+          transaction commit hook in the linked attributes module. We
+          need to do this as the order of objects coming from the
+          server is not sufficiently deterministic to know that the
+          record that a backlink needs to be created in has itself
+          been created before the object containing the forward link
+          has come over the wire */
+       if (ldb_transaction_start(s->ldb) != LDB_SUCCESS) {
+               return NT_STATUS_FOOBAR;
+       }
+
+        return NT_STATUS_OK;
 
-       creds_arcfour_crypt(creds, secret->old_cipher.cipher_data, 
-                           secret->old_cipher.maxlen); 
 
-       return NT_STATUS_OK;
 }
 
-/**
- * Fix up the delta, dealing with encryption issues so that the final
- * callback need only do the printing or application logic
- */
+NTSTATUS libnet_vampire_cb_check_options(void *private_data,
+                                        const struct libnet_BecomeDC_CheckOptions *o)
+{
+       struct libnet_vampire_cb_state *s = talloc_get_type(private_data, struct libnet_vampire_cb_state);
+
+       DEBUG(0,("Become DC [%s] of Domain[%s]/[%s]\n",
+               s->netbios_name,
+               o->domain->netbios_name, o->domain->dns_name));
+
+       DEBUG(0,("Promotion Partner is Server[%s] from Site[%s]\n",
+               o->source_dsa->dns_name, o->source_dsa->site_name));
 
-static NTSTATUS fix_delta(TALLOC_CTX *mem_ctx,                 
-                         struct creds_CredentialState *creds,
-                         enum netr_SamDatabaseID database,
-                         struct netr_DELTA_ENUM *delta,
-                         char **error_string)
+       DEBUG(0,("Options:crossRef behavior_version[%u]\n"
+                      "\tschema object_version[%u]\n"
+                      "\tdomain behavior_version[%u]\n"
+                      "\tdomain w2k3_update_revision[%u]\n", 
+               o->forest->crossref_behavior_version,
+               o->forest->schema_object_version,
+               o->domain->behavior_version,
+               o->domain->w2k3_update_revision));
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS libnet_vampire_cb_apply_schema(struct libnet_vampire_cb_state *s,
+                                              const struct libnet_BecomeDC_StoreChunk *c)
 {
-       NTSTATUS nt_status = NT_STATUS_OK;
-       *error_string = NULL;
-       switch (delta->delta_type) {
-       case NETR_DELTA_USER:
-       {
-               nt_status = fix_user(mem_ctx, 
-                                    creds,
-                                    database,
-                                    delta,
-                                    error_string);
+       WERROR status;
+       const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
+       uint32_t object_count;
+       struct drsuapi_DsReplicaObjectListItemEx *first_object;
+       const struct drsuapi_DsReplicaObjectListItemEx *cur;
+       uint32_t linked_attributes_count;
+       struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
+       const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
+       struct dsdb_extended_replicated_objects *schema_objs;
+       struct repsFromTo1 *s_dsa;
+       char *tmp_dns_name;
+       struct ldb_context *schema_ldb;
+       struct ldb_message *msg;
+       struct ldb_message_element *prefixMap_el;
+       uint32_t i;
+       int ret;
+       bool ok;
+       uint64_t seq_num;
+
+       DEBUG(0,("Analyze and apply schema objects\n"));
+
+       s_dsa                   = talloc_zero(s, struct repsFromTo1);
+       NT_STATUS_HAVE_NO_MEMORY(s_dsa);
+       s_dsa->other_info       = talloc(s_dsa, struct repsFromTo1OtherInfo);
+       NT_STATUS_HAVE_NO_MEMORY(s_dsa->other_info);
+
+       switch (c->ctr_level) {
+       case 1:
+               mapping_ctr                     = &c->ctr1->mapping_ctr;
+               object_count                    = s->schema_part.object_count;
+               first_object                    = s->schema_part.first_object;
+               linked_attributes_count         = 0;
+               linked_attributes               = NULL;
+               s_dsa->highwatermark            = c->ctr1->new_highwatermark;
+               s_dsa->source_dsa_obj_guid      = c->ctr1->source_dsa_guid;
+               s_dsa->source_dsa_invocation_id = c->ctr1->source_dsa_invocation_id;
+               uptodateness_vector             = NULL; /* TODO: map it */
                break;
-       }
-       case NETR_DELTA_SECRET:
-       {
-               nt_status = fix_secret(mem_ctx, 
-                                      creds,
-                                      database,
-                                      delta,
-                                      error_string);
+       case 6:
+               mapping_ctr                     = &c->ctr6->mapping_ctr;
+               object_count                    = s->schema_part.object_count;
+               first_object                    = s->schema_part.first_object;
+               linked_attributes_count         = c->ctr6->linked_attributes_count;
+               linked_attributes               = c->ctr6->linked_attributes;
+               s_dsa->highwatermark            = c->ctr6->new_highwatermark;
+               s_dsa->source_dsa_obj_guid      = c->ctr6->source_dsa_guid;
+               s_dsa->source_dsa_invocation_id = c->ctr6->source_dsa_invocation_id;
+               uptodateness_vector             = c->ctr6->uptodateness_vector;
                break;
-       }
        default:
-               break;
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       s_dsa->replica_flags            = DRSUAPI_DRS_WRIT_REP
+                                       | DRSUAPI_DRS_INIT_SYNC
+                                       | DRSUAPI_DRS_PER_SYNC;
+       memset(s_dsa->schedule, 0x11, sizeof(s_dsa->schedule));
+
+       tmp_dns_name    = GUID_string(s_dsa->other_info, &s_dsa->source_dsa_obj_guid);
+       NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name);
+       tmp_dns_name    = talloc_asprintf_append_buffer(tmp_dns_name, "._msdcs.%s", c->forest->dns_name);
+       NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name);
+       s_dsa->other_info->dns_name = tmp_dns_name;
+
+       schema_ldb = provision_get_schema(s, s->lp_ctx, &s->prefixmap_blob);
+       if (!schema_ldb) {
+               DEBUG(0,("Failed to re-load from local provision using remote prefixMap.  Will continue with local prefixMap\n"));
+               s->provision_schema = dsdb_get_schema(s->ldb, s);
+       } else {
+               s->provision_schema = dsdb_get_schema(schema_ldb, s);
+               ret = dsdb_reference_schema(s->ldb, s->provision_schema, false);
+               if (ret != LDB_SUCCESS) {
+                       DEBUG(0,("Failed to attach schema from local provision using remote prefixMap."));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+               talloc_free(schema_ldb);
+       }
+
+       s->provision_schema->relax_OID_conversions = true;
+
+       /* Now convert the schema elements, using the schema we loaded locally */
+       for (i=0, cur = first_object; cur; cur = cur->next_object, i++) {
+               struct dsdb_extended_replicated_object object;
+               TALLOC_CTX *tmp_ctx = talloc_new(s);
+               NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+               /* Convert the objects into LDB messages using the
+                * provision schema, and either the provision or DRS
+                * prefix map - it should not matter, as these are
+                * just schema objects, so the critical parts.  At
+                * most we would mix up the mayContain etc for new
+                * schema classes */
+               status = dsdb_convert_object_ex(s->ldb, s->provision_schema,
+                                               cur, c->gensec_skey,
+                                               tmp_ctx, &object);
+               if (!W_ERROR_IS_OK(status)) {
+                       DEBUG(1,("Warning: Failed to convert schema object %s into ldb msg\n", cur->object.identifier->dn));
+               } else {
+                       /* Convert the schema from ldb_message format
+                        * (OIDs as OID strings) into schema, using
+                        * the remote prefixMap */
+                       status = dsdb_schema_set_el_from_ldb_msg(s->ldb, s->self_made_schema, object.msg);
+                       if (!W_ERROR_IS_OK(status)) {
+                               DEBUG(1,("Warning: failed to convert object %s into a schema element: %s\n",
+                                        ldb_dn_get_linearized(object.msg->dn),
+                                        win_errstr(status)));
+                       }
+               }
+               talloc_free(tmp_ctx);
        }
-       return nt_status;
+
+       /* attach the schema we just brought over DRS to the ldb, so we can use it in dsdb_convert_object_ex below */
+       ret = dsdb_set_schema(s->ldb, s->self_made_schema);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0,("Failed to attach 1st pass schema from DRS.\n"));
+               return NT_STATUS_FOOBAR;
+       }
+
+       /* Now convert the schema elements again, using the schema we loaded over DRS */
+       for (i=0, cur = first_object; cur; cur = cur->next_object, i++) {
+               struct dsdb_extended_replicated_object object;
+               TALLOC_CTX *tmp_ctx = talloc_new(s);
+               NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+               /* Convert the objects into LDB messages using the
+                * self_made_schema, and the DRS prefix map.  We now
+                * know the full schema int->OID->name mapping, so we
+                * can get it right this time */
+               status = dsdb_convert_object_ex(s->ldb, s->self_made_schema,
+                                               cur, c->gensec_skey,
+                                               tmp_ctx, &object);
+               if (!W_ERROR_IS_OK(status)) {
+                       DEBUG(0,("ERROR: Failed to convert schema object %s into ldb msg\n", cur->object.identifier->dn));
+               } else {
+                       /* Convert the schema from ldb_message format
+                        * (OIDs as OID strings) into schema, using
+                        * the remote prefixMap, now that we know
+                        * names for all the schema elements (from the
+                        * first conversion) */
+                       status = dsdb_schema_set_el_from_ldb_msg(s->ldb, s->self_corrected_schema, object.msg);
+                       if (!W_ERROR_IS_OK(status)) {
+                               DEBUG(0,("ERROR: failed to convert object %s into a schema element: %s\n",
+                                        ldb_dn_get_linearized(object.msg->dn),
+                                        win_errstr(status)));
+                       }
+               }
+               talloc_free(tmp_ctx);
+       }
+
+       /* We don't want to use the s->self_made_schema any more */
+       s->self_made_schema = NULL;
+
+       /* attach the schema we just brought over DRS to the ldb */
+       ret = dsdb_set_schema(s->ldb, s->self_corrected_schema);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0,("Failed to attach 2nd pass (corrected) schema from DRS.\n"));
+               return NT_STATUS_FOOBAR;
+       }
+
+       /* we don't want to access the self made schema anymore */
+       s->schema = s->self_corrected_schema;
+       s->self_corrected_schema = NULL;
+
+       /* Now convert the schema elements again, using the schema we finalised, ready to actually import */
+       status = dsdb_extended_replicated_objects_convert(s->ldb,
+                                                         c->partition->nc.dn,
+                                                         mapping_ctr,
+                                                         object_count,
+                                                         first_object,
+                                                         linked_attributes_count,
+                                                         linked_attributes,
+                                                         s_dsa,
+                                                         uptodateness_vector,
+                                                         c->gensec_skey,
+                                                         s, &schema_objs);
+       if (!W_ERROR_IS_OK(status)) {
+               DEBUG(0,("Failed to convert objects when trying to import over DRS (2nd pass, to store remote schema): %s\n", win_errstr(status)));
+               return werror_to_ntstatus(status);
+       }
+
+       if (lpcfg_parm_bool(s->lp_ctx, NULL, "become dc", "dump objects", false)) {
+               for (i=0; i < schema_objs->num_objects; i++) {
+                       struct ldb_ldif ldif;
+                       fprintf(stdout, "#\n");
+                       ldif.changetype = LDB_CHANGETYPE_NONE;
+                       ldif.msg = schema_objs->objects[i].msg;
+                       ldb_ldif_write_file(s->ldb, stdout, &ldif);
+                       NDR_PRINT_DEBUG(replPropertyMetaDataBlob, schema_objs->objects[i].meta_data);
+               }
+       }
+
+       status = dsdb_extended_replicated_objects_commit(s->ldb, schema_objs, &seq_num);
+       if (!W_ERROR_IS_OK(status)) {
+               DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
+               return werror_to_ntstatus(status);
+       }
+
+       msg = ldb_msg_new(schema_objs);
+       NT_STATUS_HAVE_NO_MEMORY(msg);
+       msg->dn = schema_objs->partition_dn;
+
+       /* We must ensure a prefixMap has been written.  Unlike other
+        * attributes (including schemaInfo), it is not replicated in
+        * the normal replication stream.  We can use the one from
+        * s->prefixmap_blob because we operate with one, unchanging
+        * prefixMap for this entire operation.  */
+       ret = ldb_msg_add_value(msg, "prefixMap", &s->prefixmap_blob, &prefixMap_el);
+       if (ret != LDB_SUCCESS) {
+               return NT_STATUS_FOOBAR;
+       }
+       /* We want to know if a prefixMap was written already, as it
+        * would mean that the above comment was not true, and we have
+        * somehow updated the prefixMap during this transaction */
+       prefixMap_el->flags = LDB_FLAG_MOD_ADD;
+
+       ret = ldb_modify(s->ldb, msg);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0,("Failed to add prefixMap: %s\n", ldb_errstring(s->ldb)));
+               return NT_STATUS_FOOBAR;
+       }
+
+       talloc_free(s_dsa);
+       talloc_free(schema_objs);
+
+       /* We must set these up to ensure the replMetaData is written
+        * correctly, before our NTDS Settings entry is replicated */
+       ok = samdb_set_ntds_invocation_id(s->ldb, &c->dest_dsa->invocation_id);
+       if (!ok) {
+               DEBUG(0,("Failed to set cached ntds invocationId\n"));
+               return NT_STATUS_FOOBAR;
+       }
+       ok = samdb_set_ntds_objectGUID(s->ldb, &c->dest_dsa->ntds_guid);
+       if (!ok) {
+               DEBUG(0,("Failed to set cached ntds objectGUID\n"));
+               return NT_STATUS_FOOBAR;
+       }
+
+       s->schema = dsdb_get_schema(s->ldb, s);
+       if (!s->schema) {
+               DEBUG(0,("Failed to get loaded dsdb_schema\n"));
+               return NT_STATUS_FOOBAR;
+       }
+
+       return NT_STATUS_OK;
 }
 
-NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_SamSync *r)
+NTSTATUS libnet_vampire_cb_schema_chunk(void *private_data,
+                                       const struct libnet_BecomeDC_StoreChunk *c)
 {
-       NTSTATUS nt_status, dbsync_nt_status;
-       TALLOC_CTX *samsync_ctx, *loop_ctx, *delta_ctx;
-       struct creds_CredentialState *creds;
-       struct netr_DatabaseSync dbsync;
-       struct cli_credentials *machine_account;
-       struct dcerpc_pipe *p;
-       struct libnet_context *machine_net_ctx;
-       struct libnet_RpcConnectDCInfo *c;
-       struct libnet_SamSync_state *state;
-       const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS}; 
-       int i;
-
-       samsync_ctx = talloc_named(mem_ctx, 0, "SamSync top context");
-
-       if (!r->in.machine_account) { 
-               machine_account = cli_credentials_init(samsync_ctx);
-               if (!machine_account) {
-                       talloc_free(samsync_ctx);
-                       return NT_STATUS_NO_MEMORY;
+       struct libnet_vampire_cb_state *s = talloc_get_type(private_data, struct libnet_vampire_cb_state);
+       WERROR status;
+       const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
+       uint32_t nc_object_count;
+       uint32_t object_count;
+       struct drsuapi_DsReplicaObjectListItemEx *first_object;
+       struct drsuapi_DsReplicaObjectListItemEx *cur;
+       uint32_t nc_linked_attributes_count;
+       uint32_t linked_attributes_count;
+       struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
+
+       switch (c->ctr_level) {
+       case 1:
+               mapping_ctr                     = &c->ctr1->mapping_ctr;
+               nc_object_count                 = c->ctr1->extended_ret; /* maybe w2k send this unexpected? */
+               object_count                    = c->ctr1->object_count;
+               first_object                    = c->ctr1->first_object;
+               nc_linked_attributes_count      = 0;
+               linked_attributes_count         = 0;
+               linked_attributes               = NULL;
+               break;
+       case 6:
+               mapping_ctr                     = &c->ctr6->mapping_ctr;
+               nc_object_count                 = c->ctr6->nc_object_count;
+               object_count                    = c->ctr6->object_count;
+               first_object                    = c->ctr6->first_object;
+               nc_linked_attributes_count      = c->ctr6->nc_linked_attributes_count;
+               linked_attributes_count         = c->ctr6->linked_attributes_count;
+               linked_attributes               = c->ctr6->linked_attributes;
+               break;
+       default:
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (nc_object_count) {
+               DEBUG(0,("Schema-DN[%s] objects[%u/%u] linked_values[%u/%u]\n",
+                       c->partition->nc.dn, object_count, nc_object_count,
+                       linked_attributes_count, nc_linked_attributes_count));
+       } else {
+               DEBUG(0,("Schema-DN[%s] objects[%u] linked_values[%u]\n",
+               c->partition->nc.dn, object_count, linked_attributes_count));
+       }
+
+       if (!s->self_made_schema) {
+               WERROR werr;
+               struct drsuapi_DsReplicaOIDMapping_Ctr mapping_ctr_without_schema_info;
+               /* Put the DRS prefixmap aside for the schema we are
+                * about to load in the provision, and into the one we
+                * are making with the help of DRS */
+
+               mapping_ctr_without_schema_info = *mapping_ctr;
+
+               /* This strips off the 0xFF schema info from the end,
+                * because we don't want it in the blob */
+               if (mapping_ctr_without_schema_info.num_mappings > 0) {
+                       mapping_ctr_without_schema_info.num_mappings--;
+               }
+               werr = dsdb_get_drsuapi_prefixmap_as_blob(&mapping_ctr_without_schema_info, s, &s->prefixmap_blob);
+               if (!W_ERROR_IS_OK(werr)) {
+                       return werror_to_ntstatus(werr);
+               }
+
+               /* Set up two manually-constructed schema - the local
+                * schema from the provision will be used to build
+                * one, which will then in turn be used to build the
+                * other. */
+               s->self_made_schema = dsdb_new_schema(s);
+               NT_STATUS_HAVE_NO_MEMORY(s->self_made_schema);
+               s->self_corrected_schema = dsdb_new_schema(s);
+               NT_STATUS_HAVE_NO_MEMORY(s->self_corrected_schema);
+
+               status = dsdb_load_prefixmap_from_drsuapi(s->self_made_schema, mapping_ctr);
+               if (!W_ERROR_IS_OK(status)) {
+                       return werror_to_ntstatus(status);
+               }
+
+               status = dsdb_load_prefixmap_from_drsuapi(s->self_corrected_schema, mapping_ctr);
+               if (!W_ERROR_IS_OK(status)) {
+                       return werror_to_ntstatus(status);
                }
-               cli_credentials_set_conf(machine_account);
-               nt_status = cli_credentials_set_machine_account(machine_account);
-               if (!NT_STATUS_IS_OK(nt_status)) {
-                       r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain machine account password - are we joined to the domain?");
-                       talloc_free(samsync_ctx);
-                       return nt_status;
+       } else {
+               status = dsdb_schema_pfm_contains_drsuapi_pfm(s->self_made_schema->prefixmap, mapping_ctr);
+               if (!W_ERROR_IS_OK(status)) {
+                       return werror_to_ntstatus(status);
                }
+       }
+
+       if (!s->schema_part.first_object) {
+               s->schema_part.object_count = object_count;
+               s->schema_part.first_object = talloc_steal(s, first_object);
        } else {
-               machine_account = r->in.machine_account;
+               s->schema_part.object_count             += object_count;
+               s->schema_part.last_object->next_object = talloc_steal(s->schema_part.last_object,
+                                                                      first_object);
        }
+       for (cur = first_object; cur->next_object; cur = cur->next_object) {}
+       s->schema_part.last_object = cur;
 
-       /* We cannot do this unless we are a BDC.  Check, before we get odd errors later */
-       if (cli_credentials_get_secure_channel_type(machine_account) != SEC_CHAN_BDC) {
-               r->out.error_string
-                       = talloc_asprintf(mem_ctx, 
-                                         "Our join to domain %s is not as a BDC (%d), please rejoin as a BDC",
-                                         
-                                         cli_credentials_get_domain(machine_account),
-                                         cli_credentials_get_secure_channel_type(machine_account));
-               talloc_free(samsync_ctx);
-               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       if (!c->partition->more_data) {
+               return libnet_vampire_cb_apply_schema(s, c);
        }
 
-       c = talloc(samsync_ctx, struct libnet_RpcConnectDCInfo);
-       if (!c) {
-               r->out.error_string = NULL;
-               talloc_free(samsync_ctx);
-               return NT_STATUS_NO_MEMORY;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS libnet_vampire_cb_store_chunk(void *private_data,
+                            const struct libnet_BecomeDC_StoreChunk *c)
+{
+       struct libnet_vampire_cb_state *s = talloc_get_type(private_data, struct libnet_vampire_cb_state);
+       WERROR status;
+       const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
+       uint32_t nc_object_count;
+       uint32_t object_count;
+       struct drsuapi_DsReplicaObjectListItemEx *first_object;
+       uint32_t nc_linked_attributes_count;
+       uint32_t linked_attributes_count;
+       struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
+       const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
+       struct dsdb_extended_replicated_objects *objs;
+       struct repsFromTo1 *s_dsa;
+       char *tmp_dns_name;
+       uint32_t i;
+       uint64_t seq_num;
+
+       s_dsa                   = talloc_zero(s, struct repsFromTo1);
+       NT_STATUS_HAVE_NO_MEMORY(s_dsa);
+       s_dsa->other_info       = talloc(s_dsa, struct repsFromTo1OtherInfo);
+       NT_STATUS_HAVE_NO_MEMORY(s_dsa->other_info);
+
+       switch (c->ctr_level) {
+       case 1:
+               mapping_ctr                     = &c->ctr1->mapping_ctr;
+               nc_object_count                 = c->ctr1->extended_ret; /* maybe w2k send this unexpected? */
+               object_count                    = c->ctr1->object_count;
+               first_object                    = c->ctr1->first_object;
+               nc_linked_attributes_count      = 0;
+               linked_attributes_count         = 0;
+               linked_attributes               = NULL;
+               s_dsa->highwatermark            = c->ctr1->new_highwatermark;
+               s_dsa->source_dsa_obj_guid      = c->ctr1->source_dsa_guid;
+               s_dsa->source_dsa_invocation_id = c->ctr1->source_dsa_invocation_id;
+               uptodateness_vector             = NULL; /* TODO: map it */
+               break;
+       case 6:
+               mapping_ctr                     = &c->ctr6->mapping_ctr;
+               nc_object_count                 = c->ctr6->nc_object_count;
+               object_count                    = c->ctr6->object_count;
+               first_object                    = c->ctr6->first_object;
+               nc_linked_attributes_count      = c->ctr6->nc_linked_attributes_count;
+               linked_attributes_count         = c->ctr6->linked_attributes_count;
+               linked_attributes               = c->ctr6->linked_attributes;
+               s_dsa->highwatermark            = c->ctr6->new_highwatermark;
+               s_dsa->source_dsa_obj_guid      = c->ctr6->source_dsa_guid;
+               s_dsa->source_dsa_invocation_id = c->ctr6->source_dsa_invocation_id;
+               uptodateness_vector             = c->ctr6->uptodateness_vector;
+               break;
+       default:
+               return NT_STATUS_INVALID_PARAMETER;
        }
 
-       if (r->in.binding_string) {
-               c->level      = LIBNET_RPC_CONNECT_BINDING;
-               c->in.binding = r->in.binding_string;
+       s_dsa->replica_flags            = DRSUAPI_DRS_WRIT_REP
+                                       | DRSUAPI_DRS_INIT_SYNC
+                                       | DRSUAPI_DRS_PER_SYNC;
+       memset(s_dsa->schedule, 0x11, sizeof(s_dsa->schedule));
+
+       tmp_dns_name    = GUID_string(s_dsa->other_info, &s_dsa->source_dsa_obj_guid);
+       NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name);
+       tmp_dns_name    = talloc_asprintf_append_buffer(tmp_dns_name, "._msdcs.%s", c->forest->dns_name);
+       NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name);
+       s_dsa->other_info->dns_name = tmp_dns_name;
+
+       /* we want to show a count per partition */
+       if (!s->last_partition || strcmp(s->last_partition, c->partition->nc.dn) != 0) {
+               s->total_objects = 0;
+               talloc_free(s->last_partition);
+               s->last_partition = talloc_strdup(s, c->partition->nc.dn);
+       }
+       s->total_objects += object_count;
+
+       if (nc_object_count) {
+               DEBUG(0,("Partition[%s] objects[%u/%u] linked_values[%u/%u]\n",
+                       c->partition->nc.dn, s->total_objects, nc_object_count,
+                       linked_attributes_count, nc_linked_attributes_count));
        } else {
-               /* prepare connect to the NETLOGON pipe of PDC */
-               c->level      = LIBNET_RPC_CONNECT_PDC;
-               c->in.name    = cli_credentials_get_domain(machine_account);
+               DEBUG(0,("Partition[%s] objects[%u] linked_values[%u]\n",
+               c->partition->nc.dn, s->total_objects, linked_attributes_count));
        }
-       c->in.dcerpc_iface      = &dcerpc_table_netlogon;
 
-       /* We must do this as the machine, not as any command-line
-        * user.  So we override the credentials in the
-        * libnet_context */
-       machine_net_ctx = talloc(samsync_ctx, struct libnet_context);
-       if (!machine_net_ctx) {
-               r->out.error_string = NULL;
-               talloc_free(samsync_ctx);
-               return NT_STATUS_NO_MEMORY;
+
+       status = dsdb_extended_replicated_objects_convert(s->ldb,
+                                                         c->partition->nc.dn,
+                                                         mapping_ctr,
+                                                         object_count,
+                                                         first_object,
+                                                         linked_attributes_count,
+                                                         linked_attributes,
+                                                         s_dsa,
+                                                         uptodateness_vector,
+                                                         c->gensec_skey,
+                                                         s, &objs);
+       if (!W_ERROR_IS_OK(status)) {
+               DEBUG(0,("Failed to convert objects: %s\n", win_errstr(status)));
+               return werror_to_ntstatus(status);
        }
-       *machine_net_ctx = *ctx;
-       machine_net_ctx->cred = machine_account;
 
-       /* connect to the NETLOGON pipe of the PDC */
-       nt_status = libnet_RpcConnectDCInfo(machine_net_ctx, c);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               if (r->in.binding_string) {
-                       r->out.error_string = talloc_asprintf(mem_ctx,
-                                                             "Connection to NETLOGON pipe of DC %s failed: %s",
-                                                             r->in.binding_string, c->out.error_string);
-               } else {
-                       r->out.error_string = talloc_asprintf(mem_ctx,
-                                                             "Connection to NETLOGON pipe of DC for %s failed: %s",
-                                                             c->in.name, c->out.error_string);
+       if (lpcfg_parm_bool(s->lp_ctx, NULL, "become dc", "dump objects", false)) {
+               for (i=0; i < objs->num_objects; i++) {
+                       struct ldb_ldif ldif;
+                       fprintf(stdout, "#\n");
+                       ldif.changetype = LDB_CHANGETYPE_NONE;
+                       ldif.msg = objs->objects[i].msg;
+                       ldb_ldif_write_file(s->ldb, stdout, &ldif);
+                       NDR_PRINT_DEBUG(replPropertyMetaDataBlob, objs->objects[i].meta_data);
                }
-               talloc_free(samsync_ctx);
-               return nt_status;
+       }
+       status = dsdb_extended_replicated_objects_commit(s->ldb,
+                                                        objs, &seq_num);
+       if (!W_ERROR_IS_OK(status)) {
+               DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
+               return werror_to_ntstatus(status);
        }
 
-       /* This makes a new pipe, on which we can do schannel.  We
-        * should do this in the RpcConnect code, but the abstaction
-        * layers do not suit yet */
+       talloc_free(s_dsa);
+       talloc_free(objs);
 
-       nt_status = dcerpc_secondary_connection(c->out.dcerpc_pipe, &p,
-                                               c->out.dcerpc_pipe->binding);
+       for (i=0; i < linked_attributes_count; i++) {
+               const struct dsdb_attribute *sa;
+
+               if (!linked_attributes[i].identifier) {
+                       return NT_STATUS_FOOBAR;                
+               }
+
+               if (!linked_attributes[i].value.blob) {
+                       return NT_STATUS_FOOBAR;                
+               }
+
+               sa = dsdb_attribute_by_attributeID_id(s->schema,
+                                                     linked_attributes[i].attid);
+               if (!sa) {
+                       return NT_STATUS_FOOBAR;
+               }
 
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               r->out.error_string = talloc_asprintf(mem_ctx,
-                                                     "Secondary connection to NETLOGON pipe of DC %s failed: %s",
-                                                     dcerpc_server_name(p), nt_errstr(nt_status));
-               talloc_free(samsync_ctx);
-               return nt_status;
+               if (lpcfg_parm_bool(s->lp_ctx, NULL, "become dc", "dump objects", false)) {
+                       DEBUG(0,("# %s\n", sa->lDAPDisplayName));
+                       NDR_PRINT_DEBUG(drsuapi_DsReplicaLinkedAttribute, &linked_attributes[i]);
+                       dump_data(0,
+                               linked_attributes[i].value.blob->data,
+                               linked_attributes[i].value.blob->length);
+               }
        }
 
-       nt_status = dcerpc_bind_auth_schannel(samsync_ctx, p, &dcerpc_table_netlogon,
-                                             machine_account, DCERPC_AUTH_LEVEL_PRIVACY);
+       return NT_STATUS_OK;
+}
 
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               r->out.error_string = talloc_asprintf(mem_ctx,
-                                                     "SCHANNEL authentication to NETLOGON pipe of DC %s failed: %s",
-                                                     dcerpc_server_name(p), nt_errstr(nt_status));
-               talloc_free(samsync_ctx);
-               return nt_status;
+static NTSTATUS update_dnshostname_for_server(TALLOC_CTX *mem_ctx,
+                                             struct ldb_context *ldb,
+                                             const char *server_dn_str,
+                                             const char *netbios_name,
+                                             const char *realm)
+{
+       int ret;
+       struct ldb_message *msg;
+       struct ldb_message_element *el;
+       struct ldb_dn *server_dn;
+       const char *dNSHostName = strlower_talloc(mem_ctx,
+                                                 talloc_asprintf(mem_ctx,
+                                                                 "%s.%s",
+                                                                 netbios_name,
+                                                                 realm));
+       msg = ldb_msg_new(mem_ctx);
+       if (msg == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
-       state = talloc(samsync_ctx, struct libnet_SamSync_state);
-       if (!state) {
-               r->out.error_string = NULL;
-               talloc_free(samsync_ctx);
-               return nt_status;
-       }               
-
-       state->domain_name     = c->out.domain_name;
-       state->domain_sid      = c->out.domain_sid;
-       state->realm           = c->out.realm;
-       state->domain_guid     = c->out.guid;
-       state->machine_net_ctx = machine_net_ctx;
-       state->netlogon_pipe   = p;
-
-       /* initialise the callback layer.  It may wish to contact the
-        * server with ldap, now we know the name */
-       
-       if (r->in.init_fn) {
-               char *error_string;
-               nt_status = r->in.init_fn(samsync_ctx, 
-                                         r->in.fn_ctx,
-                                         state, 
-                                         &error_string); 
-               if (!NT_STATUS_IS_OK(nt_status)) {
-                       r->out.error_string = talloc_steal(mem_ctx, error_string);
-                       talloc_free(samsync_ctx);
-                       return nt_status;
-               }
+       server_dn = ldb_dn_new(mem_ctx, ldb, server_dn_str);
+       if (!server_dn) {
+               return NT_STATUS_INTERNAL_ERROR;
        }
 
-       /* get NETLOGON credentails */
+       msg->dn = server_dn;
+       ret = ldb_msg_add_empty(msg, "dNSHostName", LDB_FLAG_MOD_ADD, &el);
+       if (ret != LDB_SUCCESS) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
 
-       nt_status = dcerpc_schannel_creds(p->conn->security_state.generic_state, samsync_ctx, &creds);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain NETLOGON credentials from DCERPC/GENSEC layer");
-               talloc_free(samsync_ctx);
-               return nt_status;
+       ret = ldb_msg_add_steal_string(msg,
+                                      "dNSHostName",
+                                      talloc_asprintf(el->values, "%s", dNSHostName));
+       if (ret != LDB_SUCCESS) {
+               return NT_STATUS_INTERNAL_ERROR;
        }
 
-       /* Setup details for the syncronisation */
-       dbsync.in.logon_server = talloc_asprintf(samsync_ctx, "\\\\%s", dcerpc_server_name(p));
-       dbsync.in.computername = cli_credentials_get_workstation(machine_account);
-       dbsync.in.preferredmaximumlength = (uint32_t)-1;
-       ZERO_STRUCT(dbsync.in.return_authenticator);
+       ret = dsdb_modify(ldb, msg, DSDB_MODIFY_PERMISSIVE);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0,(__location__ ": Failed to add dnsHostName to the Server object: %s\n",
+                        ldb_errstring(ldb)));
+               return NT_STATUS_INTERNAL_ERROR;
+       }
 
-       for (i=0;i< ARRAY_SIZE(database_ids); i++) { 
-               dbsync.in.sync_context = 0;
-               dbsync.in.database_id = database_ids[i]; 
-               
-               do {
-                       int d;
-                       loop_ctx = talloc_named(samsync_ctx, 0, "DatabaseSync loop context");
-                       creds_client_authenticator(creds, &dbsync.in.credential);
-                       
-                       dbsync_nt_status = dcerpc_netr_DatabaseSync(p, loop_ctx, &dbsync);
-                       if (!NT_STATUS_IS_OK(dbsync_nt_status) &&
-                           !NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)) {
-                               r->out.error_string = talloc_asprintf(mem_ctx, "DatabaseSync failed - %s", nt_errstr(nt_status));
-                               talloc_free(samsync_ctx);
-                               return nt_status;
-                       }
-                       
-                       if (!creds_client_check(creds, &dbsync.out.return_authenticator.cred)) {
-                               r->out.error_string = talloc_strdup(mem_ctx, "Credential chaining on incoming DatabaseSync failed");
-                               talloc_free(samsync_ctx);
-                               return NT_STATUS_ACCESS_DENIED;
-                       }
-                       
-                       dbsync.in.sync_context = dbsync.out.sync_context;
-                       
-                       /* For every single remote 'delta' entry: */
-                       for (d=0; d < dbsync.out.delta_enum_array->num_deltas; d++) {
-                               char *error_string = NULL;
-                               delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context");
-                               /* 'Fix' elements, by decrypting and
-                                * de-obfustiating the data */
-                               nt_status = fix_delta(delta_ctx, 
-                                                     creds, 
-                                                     dbsync.in.database_id,
-                                                     &dbsync.out.delta_enum_array->delta_enum[d], 
-                                                     &error_string);
-                               if (!NT_STATUS_IS_OK(nt_status)) {
-                                       r->out.error_string = talloc_steal(mem_ctx, error_string);
-                                       talloc_free(samsync_ctx);
-                                       return nt_status;
-                               }
-
-                               /* Now call the callback.  This will
-                                * do something like print the data or
-                                * write to an ldb */
-                               nt_status = r->in.delta_fn(delta_ctx, 
-                                                          r->in.fn_ctx,
-                                                          dbsync.in.database_id,
-                                                          &dbsync.out.delta_enum_array->delta_enum[d], 
-                                                          &error_string);
-                               if (!NT_STATUS_IS_OK(nt_status)) {
-                                       r->out.error_string = talloc_steal(mem_ctx, error_string);
-                                       talloc_free(samsync_ctx);
-                                       return nt_status;
-                               }
-                               talloc_free(delta_ctx);
-                       }
-                       talloc_free(loop_ctx);
-               } while (NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES));
+       return NT_STATUS_OK;
+}
+
+
+NTSTATUS libnet_Vampire(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, 
+                       struct libnet_Vampire *r)
+{
+       struct libnet_JoinDomain *join;
+       struct libnet_Replicate rep;
+       NTSTATUS status;
+
+       const char *account_name;
+       const char *netbios_name;
+       
+       r->out.error_string = NULL;
+
+       join = talloc_zero(mem_ctx, struct libnet_JoinDomain);
+       if (!join) {
+               return NT_STATUS_NO_MEMORY;
+       }
                
-               if (!NT_STATUS_IS_OK(dbsync_nt_status)) {
-                       r->out.error_string = talloc_asprintf(mem_ctx, "libnet_SamSync_netlogon failed: unexpected inconsistancy. Should not get error %s here", nt_errstr(nt_status));
-                       talloc_free(samsync_ctx);
-                       return dbsync_nt_status;
+       if (r->in.netbios_name != NULL) {
+               netbios_name = r->in.netbios_name;
+       } else {
+               netbios_name = talloc_reference(join, lpcfg_netbios_name(ctx->lp_ctx));
+               if (!netbios_name) {
+                       talloc_free(join);
+                       r->out.error_string = NULL;
+                       return NT_STATUS_NO_MEMORY;
                }
-               nt_status = NT_STATUS_OK;
        }
-       talloc_free(samsync_ctx);
-       return nt_status;
+
+       account_name = talloc_asprintf(join, "%s$", netbios_name);
+       if (!account_name) {
+               talloc_free(join);
+               r->out.error_string = NULL;
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       /* Re-use the domain we are joining as the domain for the user
+        * to be authenticated with, unless they specified
+        * otherwise */
+       cli_credentials_set_domain(ctx->cred, r->in.domain_name, CRED_GUESS_ENV);
+
+       join->in.domain_name    = r->in.domain_name;
+       join->in.account_name   = account_name;
+       join->in.netbios_name   = netbios_name;
+       join->in.level          = LIBNET_JOINDOMAIN_AUTOMATIC;
+       join->in.acct_type      = ACB_WSTRUST;
+       join->in.recreate_account = false;
+       status = libnet_JoinDomain(ctx, join, join);
+       if (!NT_STATUS_IS_OK(status)) {
+               r->out.error_string = talloc_steal(mem_ctx, join->out.error_string);
+               talloc_free(join);
+               return status;
+       }
+
+       rep.in.domain_name   = join->out.domain_name;
+       rep.in.netbios_name  = netbios_name;
+       rep.in.targetdir     = r->in.targetdir;
+       rep.in.domain_sid    = join->out.domain_sid;
+       rep.in.realm         = join->out.realm;
+       rep.in.server        = join->out.samr_binding->host;
+       rep.in.join_password = join->out.join_password;
+       rep.in.kvno          = join->out.kvno;
+
+       status = libnet_Replicate(ctx, mem_ctx, &rep);
+
+       r->out.domain_sid   = join->out.domain_sid;
+       r->out.domain_name  = join->out.domain_name;
+       r->out.error_string = rep.out.error_string;
+
+       return status;
 }
 
+
+
+NTSTATUS libnet_Replicate(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+                         struct libnet_Replicate *r)
+{
+       struct provision_store_self_join_settings *set_secrets;
+       struct libnet_BecomeDC b;
+       struct libnet_vampire_cb_state *s;
+       struct ldb_message *msg;
+       const char *error_string;
+       int ldb_ret;
+       uint32_t i;
+       NTSTATUS status;
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       const char *account_name;
+       const char *netbios_name;
+
+       r->out.error_string = NULL;
+
+       netbios_name = r->in.netbios_name;
+       account_name = talloc_asprintf(tmp_ctx, "%s$", netbios_name);
+       if (!account_name) {
+               talloc_free(tmp_ctx);
+               r->out.error_string = NULL;
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       /* Re-use the domain we are joining as the domain for the user
+        * to be authenticated with, unless they specified
+        * otherwise */
+       cli_credentials_set_domain(ctx->cred, r->in.domain_name, CRED_GUESS_ENV);
+
+       s = libnet_vampire_cb_state_init(mem_ctx, ctx->lp_ctx, ctx->event_ctx,
+                                        netbios_name, r->in.domain_name, r->in.realm,
+                                        r->in.targetdir);
+       if (!s) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       talloc_steal(s, tmp_ctx);
+
+       ZERO_STRUCT(b);
+
+       /* Be more robust:
+        * We now know the domain and realm for sure - if they didn't
+        * put one on the command line, use this for the rest of the
+        * join */
+       cli_credentials_set_realm(ctx->cred, r->in.realm, CRED_GUESS_ENV);
+       cli_credentials_set_domain(ctx->cred, r->in.domain_name, CRED_GUESS_ENV);
+
+       /* Now set these values into the smb.conf - we probably had
+        * empty or useless defaults here from whatever smb.conf we
+        * started with */
+       lpcfg_set_cmdline(s->lp_ctx, "realm", r->in.realm);
+       lpcfg_set_cmdline(s->lp_ctx, "workgroup", r->in.domain_name);
+
+       b.in.domain_dns_name            = r->in.realm;
+       b.in.domain_netbios_name        = r->in.domain_name;
+       b.in.domain_sid                 = r->in.domain_sid;
+       b.in.source_dsa_address         = r->in.server;
+       b.in.dest_dsa_netbios_name      = netbios_name;
+
+       b.in.callbacks.private_data     = s;
+       b.in.callbacks.check_options    = libnet_vampire_cb_check_options;
+       b.in.callbacks.prepare_db       = libnet_vampire_cb_prepare_db;
+       b.in.callbacks.schema_chunk     = libnet_vampire_cb_schema_chunk;
+       b.in.callbacks.config_chunk     = libnet_vampire_cb_store_chunk;
+       b.in.callbacks.domain_chunk     = libnet_vampire_cb_store_chunk;
+
+       b.in.rodc_join = lpcfg_parm_bool(s->lp_ctx, NULL, "repl", "RODC", false);
+
+       status = libnet_BecomeDC(ctx, s, &b);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("libnet_BecomeDC() failed - %s\n", nt_errstr(status));
+               talloc_free(s);
+               return status;
+       }
+
+       msg = ldb_msg_new(s);
+       if (!msg) {
+               printf("ldb_msg_new() failed\n");
+               talloc_free(s);
+               return NT_STATUS_NO_MEMORY;
+       }
+       msg->dn = ldb_dn_new(msg, s->ldb, "@ROOTDSE");
+       if (!msg->dn) {
+               printf("ldb_msg_new(@ROOTDSE) failed\n");
+               talloc_free(s);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ldb_ret = ldb_msg_add_string(msg, "isSynchronized", "TRUE");
+       if (ldb_ret != LDB_SUCCESS) {
+               printf("ldb_msg_add_string(msg, isSynchronized, TRUE) failed: %d\n", ldb_ret);
+               talloc_free(s);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       for (i=0; i < msg->num_elements; i++) {
+               msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+       }
+
+       printf("mark ROOTDSE with isSynchronized=TRUE\n");
+       ldb_ret = ldb_modify(s->ldb, msg);
+       if (ldb_ret != LDB_SUCCESS) {
+               printf("ldb_modify() failed: %d : %s\n", ldb_ret, ldb_errstring(s->ldb));
+               talloc_free(s);
+               return NT_STATUS_INTERNAL_DB_ERROR;
+       }
+       /* during dcpromo the 2nd computer adds dNSHostName attribute to his Server object
+        * the attribute appears on the original DC after replication
+        */
+       status = update_dnshostname_for_server(s, s->ldb, s->server_dn_str, s->netbios_name, s->realm);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("Failed to update dNSHostName on Server object - %s\n", nt_errstr(status));
+               talloc_free(s);
+               return status;
+       }
+       /* prepare the transaction - this prepares to commit all the changes in
+          the ldb from the whole vampire.  Note that this 
+          triggers the writing of the linked attribute backlinks.
+       */
+       if (ldb_transaction_prepare_commit(s->ldb) != LDB_SUCCESS) {
+               printf("Failed to prepare_commit vampire transaction: %s\n", ldb_errstring(s->ldb));
+               return NT_STATUS_INTERNAL_DB_ERROR;
+       }
+
+       set_secrets = talloc(s, struct provision_store_self_join_settings);
+       if (!set_secrets) {
+               r->out.error_string = NULL;
+               talloc_free(s);
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       ZERO_STRUCTP(set_secrets);
+       set_secrets->domain_name = r->in.domain_name;
+       set_secrets->realm = r->in.realm;
+       set_secrets->netbios_name = netbios_name;
+       set_secrets->secure_channel_type = SEC_CHAN_BDC;
+       set_secrets->machine_password = r->in.join_password;
+       set_secrets->key_version_number = r->in.kvno;
+       set_secrets->domain_sid = r->in.domain_sid;
+       
+       status = provision_store_self_join(ctx, s->lp_ctx, ctx->event_ctx, set_secrets, &error_string);
+       if (!NT_STATUS_IS_OK(status)) {
+               r->out.error_string = talloc_steal(mem_ctx, error_string);
+               talloc_free(s);
+               return status;
+       }
+
+       /* commit the transaction now we know the secrets were written
+        * out properly
+       */
+       if (ldb_transaction_commit(s->ldb) != LDB_SUCCESS) {
+               printf("Failed to commit vampire transaction\n");
+               return NT_STATUS_INTERNAL_DB_ERROR;
+       }
+
+       talloc_free(s);
+
+       return NT_STATUS_OK;
+}