Example #1
0
/**
  connect to the schannel ldb
*/
struct ldb_context *schannel_db_connect(TALLOC_CTX *mem_ctx)
{
	char *path;
	struct ldb_context *ldb;
	BOOL existed;
	const char *init_ldif = 
		"dn: @ATTRIBUTES\n" \
		"computerName: CASE_INSENSITIVE\n" \
		"flatname: CASE_INSENSITIVE\n";

	path = smbd_tmp_path(mem_ctx, "schannel.ldb");
	if (!path) {
		return NULL;
	}

	existed = file_exist(path);
	
	ldb = ldb_wrap_connect(mem_ctx, path, system_session(mem_ctx), 
			       NULL, LDB_FLG_NOSYNC, NULL);
	talloc_free(path);
	if (!ldb) {
		return NULL;
	}
	
	if (!existed) {
		gendb_add_ldif(ldb, init_ldif);
	}

	return ldb;
}
Example #2
0
static NTSTATUS sldb_init(TALLOC_CTX *mem_ctx, const struct share_ops *ops, 
			  struct tevent_context *ev_ctx,
			  struct loadparm_context *lp_ctx,
			  struct share_context **ctx)
{
	struct ldb_context *sdb;

	*ctx = talloc(mem_ctx, struct share_context);
	if (!*ctx) {
		DEBUG(0, ("ERROR: Out of memory!\n"));
		return NT_STATUS_NO_MEMORY;
	}
	
	sdb = ldb_wrap_connect(*ctx, ev_ctx, lp_ctx,
			       private_path(*ctx, lp_ctx, "share.ldb"),
			       system_session(lp_ctx),
			       NULL, 0);

	if (!sdb) {
		talloc_free(*ctx);
		return NT_STATUS_UNSUCCESSFUL;
	}

	(*ctx)->ops = ops;
	(*ctx)->priv_data = (void *)sdb;

	return NT_STATUS_OK;
}
Example #3
0
/*
  connect to the sam database
*/
NTSTATUS ldapsrv_backend_Init(struct ldapsrv_connection *conn) 
{
	conn->ldb = ldb_wrap_connect(conn, lp_sam_url(), conn->session_info,
				     NULL, conn->global_catalog ? LDB_FLG_RDONLY : 0, NULL);
	if (conn->ldb == NULL) {
		return NT_STATUS_INTERNAL_DB_CORRUPTION;
	}

	if (conn->server_credentials) {
		char **sasl_mechs = NULL;
		struct gensec_security_ops **backends = gensec_security_all();
		enum credentials_use_kerberos use_kerberos
			= cli_credentials_get_kerberos_state(conn->server_credentials);
		struct gensec_security_ops **ops
			= gensec_use_kerberos_mechs(conn, backends, use_kerberos);
		int i, j = 0;
		for (i = 0; ops && ops[i]; i++) {
			if (ops[i]->sasl_name && ops[i]->server_start) {
				char *sasl_name = talloc_strdup(conn, ops[i]->sasl_name);

				if (!sasl_name) {
					return NT_STATUS_NO_MEMORY;
				}
				sasl_mechs = talloc_realloc(conn, sasl_mechs, char *, j + 2);
				if (!sasl_mechs) {
					return NT_STATUS_NO_MEMORY;
				}
				sasl_mechs[j] = sasl_name;
				talloc_steal(sasl_mechs, sasl_name);
				sasl_mechs[j+1] = NULL;
				j++;
			}
		}
Example #4
0
static struct ldb_context *wins_config_db_connect(TALLOC_CTX *mem_ctx, 
						  struct tevent_context *ev_ctx,
						  struct loadparm_context *lp_ctx)
{
	return ldb_wrap_connect(mem_ctx, ev_ctx, lp_ctx, private_path(mem_ctx, 
			        lp_ctx, lp_wins_config_url(lp_ctx)),
				system_session(mem_ctx, lp_ctx), NULL, 0, NULL);
}
Example #5
0
/**
 * Find out user's samAccountName for given
 * user RDN. We need samAccountName value
 * when deleting users.
 */
static bool _get_account_name_for_user_rdn(struct torture_context *tctx,
					   const char *user_rdn,
					   TALLOC_CTX *mem_ctx,
					   const char **_account_name)
{
	const char *url;
	struct ldb_context *ldb;
	TALLOC_CTX *tmp_ctx;
	bool test_res = true;
	const char *hostname = torture_setting_string(tctx, "host", NULL);
	int ldb_ret;
	struct ldb_result *ldb_res;
	const char *account_name = NULL;
	static const char *attrs[] = {
		"samAccountName",
		NULL
	};

	torture_assert(tctx, hostname != NULL, "Failed to get hostname");

	tmp_ctx = talloc_new(tctx);
	torture_assert(tctx, tmp_ctx != NULL, "Failed to create temporary mem context");

	url = talloc_asprintf(tmp_ctx, "ldap://%s/", hostname);
	torture_assert_goto(tctx, url != NULL, test_res, done, "Failed to allocate URL for ldb");

	ldb = ldb_wrap_connect(tmp_ctx,
	                       tctx->ev, tctx->lp_ctx,
	                       url, NULL, cmdline_credentials, 0);
	torture_assert_goto(tctx, ldb != NULL, test_res, done, "Failed to make LDB connection");

	ldb_ret = ldb_search(ldb, tmp_ctx, &ldb_res,
	                     ldb_get_default_basedn(ldb), LDB_SCOPE_SUBTREE,
	                     attrs,
	                     "(&(objectClass=user)(name=%s))", user_rdn);
	if (LDB_SUCCESS == ldb_ret && 1 == ldb_res->count) {
		account_name = ldb_msg_find_attr_as_string(ldb_res->msgs[0], "samAccountName", NULL);
	}

	/* return user_rdn by default */
	if (!account_name) {
		account_name = user_rdn;
	}

	/* duplicate memory in parent context */
	*_account_name = talloc_strdup(mem_ctx, account_name);

done:
	talloc_free(tmp_ctx);
	return test_res;
}
Example #6
0
static NTSTATUS unbecomeDC_ldap_connect(struct libnet_UnbecomeDC_state *s)
{
	char *url;

	url = talloc_asprintf(s, "ldap://%s/", s->source_dsa.dns_name);
	NT_STATUS_HAVE_NO_MEMORY(url);

	s->ldap.ldb = ldb_wrap_connect(s, s->libnet->event_ctx, s->libnet->lp_ctx, url,
				       NULL,
				       s->libnet->cred,
				       0);
	talloc_free(url);
	if (s->ldap.ldb == NULL) {
		return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
	}

	return NT_STATUS_OK;
}
Example #7
0
static ADS_STATUS ads_connect(ADS_STRUCT *ads)
{
	struct libnet_LookupDCs *io;
	char *url;

	io = talloc_zero(ads, struct libnet_LookupDCs);

	/* We are looking for the PDC of the active domain. */
	io->in.name_type = NBT_NAME_PDC;
	io->in.domain_name = lp_workgroup(ads->netctx->lp_ctx);
	libnet_LookupDCs(ads->netctx, ads, io);

	url = talloc_asprintf(ads, "ldap://%s", io->out.dcs[0].name);
	ads->ldbctx = ldb_wrap_connect(ads, ads->netctx->event_ctx, ads->netctx->lp_ctx,
	                 url, NULL, ads->netctx->cred, 0);
	if (ads->ldbctx == NULL) {
		return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
	}

	return ADS_ERROR_NT(NT_STATUS_OK);
}
Example #8
0
/*
  connect to the SPOOLSS database
  return a ldb_context pointer on success, or NULL on failure
 */
static struct ldb_context *sptr_db_connect(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, struct loadparm_context *lp_ctx)
{
	return ldb_wrap_connect(mem_ctx, ev_ctx, lp_ctx, "spoolss.ldb", system_session(lp_ctx),
				NULL, 0);
}
Example #9
0
/*
  test ldb speed
*/
static bool test_ldb_speed(struct torture_context *torture, const void *_data)
{
	struct timeval tv;
	struct ldb_context *ldb;
	int timelimit = torture_setting_int(torture, "timelimit", 10);
	int i, count;
	TALLOC_CTX *tmp_ctx = talloc_new(torture);
	struct ldb_ldif *ldif;
	const char *init_ldif = "dn: @INDEXLIST\n" \
		"@IDXATTR: UID\n";
	float ldb_speed;

	unlink("./test.ldb");

	torture_comment(torture, "Testing ldb speed for sidmap\n");

	ldb = ldb_wrap_connect(tmp_ctx, torture->ev, torture->lp_ctx, "tdb://test.ldb", 
				NULL, NULL, LDB_FLG_NOSYNC, NULL);
	if (!ldb) {
		unlink("./test.ldb");
		talloc_free(tmp_ctx);
		torture_fail(torture, "Failed to open test.ldb");
	}

	/* add an index */
	ldif = ldb_ldif_read_string(ldb, &init_ldif);
	if (ldif == NULL) goto failed;
	if (ldb_add(ldb, ldif->msg) != LDB_SUCCESS) goto failed;
	talloc_free(ldif);

	torture_comment(torture, "Adding %d SID records\n", torture_entries);

	for (i=0;i<torture_entries;i++) {
		if (!ldb_add_record(ldb, i)) {
			torture_result(torture, TORTURE_FAIL, "Failed to add SID %d\n", i);
			goto failed;
		}
	}

	if (talloc_total_blocks(torture) > 100) {
		torture_result(torture, TORTURE_FAIL, "memory leak in ldb add\n");
		goto failed;
	}

	torture_comment(torture, "Testing for %d seconds\n", timelimit);

	tv = timeval_current();

	for (count=0;timeval_elapsed(&tv) < timelimit;count++) {
		struct ldb_dn *dn;
		struct ldb_result *res;

		i = random() % torture_entries;
		dn = ldb_dn_new_fmt(tmp_ctx, ldb, "SID=S-1-5-21-53173311-3623041448-2049097239-%u", i);
		if (ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL) != LDB_SUCCESS || res->count != 1) {
			torture_fail(torture, talloc_asprintf(torture, "Failed to find SID %d", i));
		}
		talloc_free(res);
		talloc_free(dn);
		if (ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "(UID=%u)", i) != LDB_SUCCESS || res->count != 1) {
			torture_fail(torture, talloc_asprintf(torture, "Failed to find UID %d", i));
		}
		talloc_free(res);
	}

	if (talloc_total_blocks(torture) > 100) {
		unlink("./test.ldb");
		talloc_free(tmp_ctx);
		torture_fail(torture, "memory leak in ldb search");
	}

	ldb_speed = count/timeval_elapsed(&tv);
	torture_comment(torture, "ldb speed %.2f ops/sec\n", ldb_speed);

	torture_comment(torture, "ldb/tdb speed ratio is %.2f%%\n", (100*ldb_speed/tdb_speed));
	

	unlink("./test.ldb");
	talloc_free(tmp_ctx);
	return true;

failed:
	unlink("./test.ldb");
	talloc_free(tmp_ctx);
	return false;
}
Example #10
0
static int set_ldap_credentials(struct ldb_context *ldb, bool use_external)
{
	const char *secrets_ldb_path, *sam_ldb_path;
	char *private_dir, *p, *error_string;
	struct ldb_context *secrets_ldb;
	struct cli_credentials *cred;
	struct loadparm_context *lp_ctx = ldb_get_opaque(ldb, "loadparm");
	TALLOC_CTX *tmp_ctx = talloc_new(ldb);

	if (!tmp_ctx) {
		return ldb_oom(ldb);
	}

	cred = cli_credentials_init(ldb);
	if (!cred) {
		talloc_free(tmp_ctx);
		return ldb_oom(ldb);
	}
	cli_credentials_set_anonymous(cred);
	if (use_external) {
		cli_credentials_set_forced_sasl_mech(cred, "EXTERNAL");
	} else {
		cli_credentials_set_forced_sasl_mech(cred, "DIGEST-MD5");

		/*
		 * We don't want to use krb5 to talk to our samdb - recursion
		 * here would be bad, and this account isn't in the KDC
		 * anyway
		 */
		cli_credentials_set_kerberos_state(cred, CRED_DONT_USE_KERBEROS);

		/*
		 * Work out where *our* secrets.ldb is.  It must be in
		 * the same directory as sam.ldb
		 */
		sam_ldb_path = (const char *)ldb_get_opaque(ldb, "ldb_url");
		if (!sam_ldb_path) {
			talloc_free(tmp_ctx);
			return ldb_operr(ldb);
		}
		if (strncmp("tdb://", sam_ldb_path, 6) == 0) {
			sam_ldb_path += 6;
		}
		private_dir = talloc_strdup(tmp_ctx, sam_ldb_path);
		p = strrchr(private_dir, '/');
		if (p) {
			*p = '\0';
		} else {
			private_dir = talloc_strdup(tmp_ctx, ".");
		}

		secrets_ldb_path = talloc_asprintf(private_dir, "tdb://%s/secrets.ldb",
						   private_dir);

		if (!secrets_ldb_path) {
			talloc_free(tmp_ctx);
			return ldb_oom(ldb);
		}

		/*
		 * Now that we have found the location, connect to
		 * secrets.ldb so we can read the SamDB Credentials
		 * record
		 */
		secrets_ldb = ldb_wrap_connect(tmp_ctx, NULL, lp_ctx, secrets_ldb_path,
					       NULL, NULL, 0);

		if (!NT_STATUS_IS_OK(cli_credentials_set_secrets(cred, NULL, secrets_ldb, NULL,
								 SECRETS_LDAP_FILTER, &error_string))) {
			ldb_asprintf_errstring(ldb, "Failed to read LDAP backend password from %s", secrets_ldb_path);
			talloc_free(tmp_ctx);
			return LDB_ERR_STRONG_AUTH_REQUIRED;
		}
	}

	/*
	 * Finally overwrite any supplied credentials with
	 * these ones, as only secrets.ldb contains the magic
	 * credentials to talk on the ldapi socket
	 */
	if (ldb_set_opaque(ldb, "credentials", cred)) {
		talloc_free(tmp_ctx);
		return ldb_operr(ldb);
	}
	talloc_free(tmp_ctx);
	return LDB_SUCCESS;
}
Example #11
0
/*
 * 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;
	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;

	const char * const attrs[] = {
		"msDS-KeyVersionNumber",
		"servicePrincipalName",
		"dNSHostName",
		"objectGUID",
		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_zero(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,
				       &ndr_table_drsuapi,
				       ctx->cred, 
				       ctx->event_ctx,
				       ctx->lp_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->out.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_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_drsuapi_bind);
	if (!NT_STATUS_IS_OK(status)) {
		r->out.error_string
			= talloc_asprintf(r,
					  "dcerpc_drsuapi_DsBind failed - %s",
					  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", 
						  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			= talloc(r, union drsuapi_DsNameRequest);
	if (!r_crack_names.in.req) {
		r->out.error_string = NULL;
		talloc_free(tmp_ctx);
		return NT_STATUS_NO_MEMORY;
	}
	r_crack_names.in.req->req1.codepage	= 1252; /* western european */
	r_crack_names.in.req->req1.language	= 0x00000407; /* german */
	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;
	}

	r_crack_names.out.ctr			= talloc(r, union drsuapi_DsNameCtr);
	r_crack_names.out.level_out		= talloc(r, uint32_t);
	if (!r_crack_names.out.ctr || !r_crack_names.out.level_out) {
		r->out.error_string = NULL;
		talloc_free(tmp_ctx);
		return NT_STATUS_NO_MEMORY;
	}

	status = dcerpc_drsuapi_DsCrackNames_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_crack_names);
	if (!NT_STATUS_IS_OK(status)) {
		r->out.error_string
			= talloc_asprintf(r,
					  "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
					  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", win_errstr(r_crack_names.out.result));
		talloc_free(tmp_ctx);
		return NT_STATUS_UNSUCCESSFUL;
	} else if (*r_crack_names.out.level_out != 1
		   || !r_crack_names.out.ctr->ctr1
		   || r_crack_names.out.ctr->ctr1->count != 1) {
		r->out.error_string = talloc_asprintf(r, "DsCrackNames failed");
		talloc_free(tmp_ctx);
		return NT_STATUS_INVALID_PARAMETER;
	} else if (r_crack_names.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
		r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: %d", r_crack_names.out.ctr->ctr1->array[0].status);
		talloc_free(tmp_ctx);
		return NT_STATUS_UNSUCCESSFUL;
	} else if (r_crack_names.out.ctr->ctr1->array[0].result_name == NULL) {
		r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: no result name");
		talloc_free(tmp_ctx);
		return NT_STATUS_INVALID_PARAMETER;
	}

	/* Store the DN of our machine account. */
	account_dn_str = r_crack_names.out.ctr->ctr1->array[0].result_name;

	/* 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->target_hostname);
	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, ctx->event_ctx, ctx->lp_ctx,
				      remote_ldb_url, 
				      NULL, ctx->cred, 0);
	if (!remote_ldb) {
		r->out.error_string = NULL;
		talloc_free(tmp_ctx);
		return NT_STATUS_UNSUCCESSFUL;
	}

	account_dn = ldb_dn_new(tmp_ctx, remote_ldb, account_dn_str);
	if (account_dn == NULL) {
		r->out.error_string = talloc_asprintf(r, "Invalid account dn: %s",
						      account_dn_str);
		talloc_free(tmp_ctx);
		return NT_STATUS_UNSUCCESSFUL;
	}

	/* search for the user's record */
	ret = ldb_search(remote_ldb, tmp_ctx, &res,
			 account_dn, LDB_SCOPE_BASE, attrs, NULL);
	if (ret != LDB_SUCCESS) {
		r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - %s",
						      account_dn_str, ldb_errstring(remote_ldb));
		talloc_free(tmp_ctx);
		return NT_STATUS_UNSUCCESSFUL;
	}

	if (res->count != 1) {
		r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - found %d entries",
						      account_dn_str, res->count);
		talloc_free(tmp_ctx);
		return NT_STATUS_UNSUCCESSFUL;
	}

	/* 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;

	{
		unsigned int i;
		const char *service_principal_name[2];
		const char *dns_host_name = strlower_talloc(msg,
							    talloc_asprintf(msg, 
									    "%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(msg, "HOST/%s",
							    dns_host_name);
		service_principal_name[1] = talloc_asprintf(msg, "HOST/%s",
							    r->in.netbios_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 = ldb_msg_add_string(msg, "servicePrincipalName",
						 service_principal_name[i]);
			if (rtn != LDB_SUCCESS) {
				r->out.error_string = NULL;
				talloc_free(tmp_ctx);
				return NT_STATUS_NO_MEMORY;
			}
		}

		rtn = ldb_msg_add_string(msg, "dNSHostName", dns_host_name);
		if (rtn != LDB_SUCCESS) {
			r->out.error_string = NULL;
			talloc_free(tmp_ctx);
			return NT_STATUS_NO_MEMORY;
		}

		rtn = dsdb_replace(remote_ldb, msg, 0);
		if (rtn != LDB_SUCCESS) {
			r->out.error_string
				= talloc_asprintf(r, 
						  "Failed to replace entries on %s", 
						  ldb_dn_get_linearized(msg->dn));
			talloc_free(tmp_ctx);
			return NT_STATUS_INTERNAL_DB_CORRUPTION;
		}
	}
				
	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;

	rtn = samdb_msg_add_uint(remote_ldb, msg, msg,
				 "msDS-SupportedEncryptionTypes", ENC_ALL_TYPES);
	if (rtn != LDB_SUCCESS) {
		r->out.error_string = NULL;
		talloc_free(tmp_ctx);
		return NT_STATUS_NO_MEMORY;
	}

	rtn = dsdb_replace(remote_ldb, msg, 0);
	/* The remote server may not support this attribute, if it
	 * isn't a modern schema */
	if (rtn != LDB_SUCCESS && rtn != LDB_ERR_NO_SUCH_ATTRIBUTE) {
		r->out.error_string
			= talloc_asprintf(r,
					  "Failed to replace msDS-SupportedEncryptionTypes on %s",
					  ldb_dn_get_linearized(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_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_crack_names);
	if (!NT_STATUS_IS_OK(status)) {
		r->out.error_string
			= talloc_asprintf(r,
					  "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
					  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", win_errstr(r_crack_names.out.result));
		talloc_free(tmp_ctx);
		return NT_STATUS_UNSUCCESSFUL;
	} else if (*r_crack_names.out.level_out != 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");
		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);

	/* Store the KVNO of the account, critical for some kerberos
	 * operations */
	r->out.kvno = ldb_msg_find_attr_as_uint(res->msgs[0], "msDS-KeyVersionNumber", 0);

	/* Store the account GUID. */
	r->out.account_guid = samdb_result_guid(res->msgs[0], "objectGUID");

	if (r->in.acct_type == ACB_SVRTRUST) {
		status = libnet_JoinSite(ctx, remote_ldb, r);
	}
	talloc_free(tmp_ctx);

	return status;
}
Example #12
0
static bool test_referrals(struct torture_context *tctx, TALLOC_CTX *mem_ctx,
	const char *url, const char *basedn, const char **partitions)
{
	struct ldb_context *ldb;
	struct ldb_result *res;
	const char * const *attrs = { NULL };
	struct ldb_dn *dn1, *dn2;
	int ret;
	int i, j, k;
	char *tempstr;
	bool found, l_found;

	printf("Testing referrals\n");

	if (partitions[0] == NULL) {
		printf("Partitions list empty!\n");
		return false;
	}

	if (strcmp(partitions[0], basedn) != 0) {
		printf("The first (root) partition DN should be the base DN!\n");
		return false;
	}

	ldb = ldb_wrap_connect(mem_ctx, tctx->ev, tctx->lp_ctx, url,
			       NULL, popt_get_cmdline_credentials(), 0);

	/* "partitions[i]" are the partitions for which we search the parents */
	for (i = 1; partitions[i] != NULL; i++) {
		dn1 = ldb_dn_new(mem_ctx, ldb, partitions[i]);
		if (dn1 == NULL) {
			printf("Out of memory\n");
			talloc_free(ldb);
			return false;
		}

		/* search using base scope */
		/* "partitions[j]" are the parent candidates */
		for (j = str_list_length(partitions) - 1; j >= 0; --j) {
			dn2 = ldb_dn_new(mem_ctx, ldb, partitions[j]);
			if (dn2 == NULL) {
				printf("Out of memory\n");
				talloc_free(ldb);
				return false;
			}

			ret = ldb_search(ldb, mem_ctx, &res, dn2,
					 LDB_SCOPE_BASE, attrs,
					 "(foo=bar)");
			if (ret != LDB_SUCCESS) {
				printf("%s", ldb_errstring(ldb));
				talloc_free(ldb);
				return false;
			}

			if (res->refs != NULL) {
				printf("There shouldn't be generated any referrals in the base scope!\n");
				talloc_free(ldb);
				return false;
			}

			talloc_free(res);
			talloc_free(dn2);
		}

		/* search using onelevel scope */
		found = false;
		/* "partitions[j]" are the parent candidates */
		for (j = str_list_length(partitions) - 1; j >= 0; --j) {
			dn2 = ldb_dn_new(mem_ctx, ldb, partitions[j]);
			if (dn2 == NULL) {
				printf("Out of memory\n");
				talloc_free(ldb);
				return false;
			}

			ret = ldb_search(ldb, mem_ctx, &res, dn2,
					 LDB_SCOPE_ONELEVEL, attrs,
					 "(foo=bar)");
			if (ret != LDB_SUCCESS) {
				printf("%s", ldb_errstring(ldb));
				talloc_free(ldb);
				return false;
			}

			tempstr = talloc_asprintf(mem_ctx, "/%s??base",
						  partitions[i]);
			if (tempstr == NULL) {
				printf("Out of memory\n");
				talloc_free(ldb);
				return false;
			}

			/* Try to find or find not a matching referral */
			l_found = false;
			for (k = 0; (!l_found) && (res->refs != NULL)
			    && (res->refs[k] != NULL); k++) {
				if (strstr(res->refs[k], tempstr) != NULL) {
					l_found = true;
				}
			}

			talloc_free(tempstr);

			if ((!found) && (ldb_dn_compare_base(dn2, dn1) == 0)
			    && (ldb_dn_compare(dn2, dn1) != 0)) {
				/* This is a referral candidate */
				if (!l_found) {
					printf("A required referral hasn't been found on onelevel scope (%s -> %s)!\n", partitions[j], partitions[i]);
					talloc_free(ldb);
					return false;
				}
				found = true;
			} else {
				/* This isn't a referral candidate */
				if (l_found) {
					printf("A unrequired referral has been found on onelevel scope (%s -> %s)!\n", partitions[j], partitions[i]);
					talloc_free(ldb);
					return false;
				}
			}

			talloc_free(res);
			talloc_free(dn2);
		}

		/* search using subtree scope */
		found = false;
		/* "partitions[j]" are the parent candidates */
		for (j = str_list_length(partitions) - 1; j >= 0; --j) {
			dn2 = ldb_dn_new(mem_ctx, ldb, partitions[j]);
			if (dn2 == NULL) {
				printf("Out of memory\n");
				talloc_free(ldb);
				return false;
			}

			ret = ldb_search(ldb, mem_ctx, &res, dn2,
					 LDB_SCOPE_SUBTREE, attrs,
					 "(foo=bar)");
			if (ret != LDB_SUCCESS) {
				printf("%s", ldb_errstring(ldb));
				talloc_free(ldb);
				return false;
			}

			tempstr = talloc_asprintf(mem_ctx, "/%s",
						  partitions[i]);
			if (tempstr == NULL) {
				printf("Out of memory\n");
				talloc_free(ldb);
				return false;
			}

			/* Try to find or find not a matching referral */
			l_found = false;
			for (k = 0; (!l_found) && (res->refs != NULL)
			    && (res->refs[k] != NULL); k++) {
				if (strstr(res->refs[k], tempstr) != NULL) {
					l_found = true;
				}
			}

			talloc_free(tempstr);

			if ((!found) && (ldb_dn_compare_base(dn2, dn1) == 0)
			    && (ldb_dn_compare(dn2, dn1) != 0)) {
				/* This is a referral candidate */
				if (!l_found) {
					printf("A required referral hasn't been found on subtree scope (%s -> %s)!\n", partitions[j], partitions[i]);
					talloc_free(ldb);
					return false;
				}
				found = true;
			} else {
				/* This isn't a referral candidate */
				if (l_found) {
					printf("A unrequired referral has been found on subtree scope (%s -> %s)!\n", partitions[j], partitions[i]);
					talloc_free(ldb);
					return false;
				}
			}

			talloc_free(res);
			talloc_free(dn2);
		}

		talloc_free(dn1);
	}

	talloc_free(ldb);

	return true;
}
Example #13
0
bool torture_ldap_sort(struct torture_context *torture)
{
	struct ldb_context *ldb;

	bool ret = false;
	const char *host = torture_setting_string(torture, "host", NULL);
	char *url;
	int i;
	codepoint_t j;
	struct ldb_message_element *elem;
	struct ldb_message *msg;

	struct ldb_server_sort_control **control;
	struct ldb_request *req;
	struct ldb_result *ctx;
	struct ldb_val *prev = NULL;
	const char *prev_txt = NULL;
	int prev_len = 0;
	struct ldb_val *cur = NULL;
	const char *cur_txt = NULL;
	int cur_len = 0;
	struct ldb_dn *dn;
		 
		 
	/* TALLOC_CTX* ctx;*/

	url = talloc_asprintf(torture, "ldap://%s/", host);

	ldb = ldb_wrap_connect(torture, torture->ev, torture->lp_ctx, url,
						 NULL,
						 popt_get_cmdline_credentials(),
						 0);
	torture_assert(torture, ldb, "Failed to make LDB connection to target");

	ctx = talloc_zero(ldb, struct ldb_result);

	control = talloc_array(ctx, struct ldb_server_sort_control *, 2);
	control[0] = talloc(control, struct ldb_server_sort_control);
	control[0]->attributeName = talloc_strdup(control, "cn");
	control[0]->orderingRule = NULL;
	control[0]->reverse = 0;
	control[1] = NULL;

	dn = ldb_get_default_basedn(ldb);
	ldb_dn_add_child_fmt(dn, "cn=users");
	ret = ldb_build_search_req(&req, ldb, ctx,
				   dn,
				   LDB_SCOPE_SUBTREE,
				   "(objectClass=*)", NULL,
				   NULL,
				   ctx, ldb_search_default_callback, NULL);
	torture_assert(torture, ret == LDB_SUCCESS, "Failed to build search request");

	ret = ldb_request_add_control(req, LDB_CONTROL_SERVER_SORT_OID, true, control);
	torture_assert(torture, ret == LDB_SUCCESS, "Failed to add control to search request");

	ret = ldb_request(ldb, req);
	torture_assert(torture, ret == LDB_SUCCESS, ldb_errstring(ldb));

	ret = ldb_wait(req->handle, LDB_WAIT_ALL);
	torture_assert(torture, ret == LDB_SUCCESS, ldb_errstring(ldb));

	ret = true;
	if (ctx->count > 1) {
		for (i=0;i<ctx->count;i++) {
			msg = ctx->msgs[i];
			elem = ldb_msg_find_element(msg,"cn");
			torture_assert_not_null(torture, elem, "msg lacks CN");
			cur = elem->values;
			torture_comment(torture, "cn: %s\n",cur->data);
			if (prev != NULL)
			{
				/* Do only the ascii case right now ... */
				cur_txt = (const char *) cur->data;
				cur_len = cur->length;
				prev_txt = (const char *) prev->data;
				prev_len = prev->length;
				/* Remove leading whitespace as the sort function do so ... */
				while ( cur_txt[0] == cur_txt[1] ) { cur_txt++; cur_len--;}
				while ( prev_txt[0] == prev_txt[1] ) { prev_txt++; prev_len--;}
				while( *(cur_txt) && *(prev_txt) && cur_len && prev_len ) {
					j = toupper_m(*(prev_txt))-toupper_m(*(cur_txt));
					if ( j > 0 ) {
						/* Just check that is not due to trailling white space in prev_txt 
						 * That is to say *cur_txt = 0 and prev_txt = 20 */
						/* Remove trailling whitespace */
						while ( *prev_txt == ' ' ) { prev_txt++; prev_len--;}
						while ( *cur_txt == ' ' ) { cur_txt++; cur_len--;}
						/* Now that potential whitespace are removed if we are at the end 
						 * of the cur_txt then it means that in fact strings were identical
						 */
						torture_assert(torture, *cur_txt && *prev_txt, "Data wrongly sorted");
						break;
					}
					else
					{
						if ( j == 0 )
						{
							if ( *(cur_txt) == ' ') {
								while ( cur_txt[0] == cur_txt[1] ) { cur_txt++; cur_len--;}
								while ( prev_txt[0] == prev_txt[1] ) { prev_txt++; prev_len--;}
							}
							cur_txt++;
							prev_txt++;
							prev_len--;
							cur_len--;
						}
						else
						{
							break;
						} 
					}
				}
				if ( ret != 1 ) {
					break;
				}
			}
			prev = cur; 
		}

	}

	return ret;
}
Example #14
0
/**
  connect to the secrets ldb
*/
struct ldb_context *secrets_db_connect(TALLOC_CTX *mem_ctx,
					struct loadparm_context *lp_ctx)
{
	return ldb_wrap_connect(mem_ctx, NULL, lp_ctx, "secrets.ldb",
			       NULL, NULL, 0);
}
Example #15
0
/* connect to the privilege database */
struct ldb_context *privilege_connect(TALLOC_CTX *mem_ctx, 
				      struct loadparm_context *lp_ctx)
{
	return ldb_wrap_connect(mem_ctx, NULL, lp_ctx, "privilege.ldb",
				NULL, NULL, 0);
}
Example #16
0
/*
 * 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;
	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", 
						  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", 
						  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", 
						  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", 
						  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", 
						  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", 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->out.error_string = talloc_asprintf(r, "DsCrackNames failed");
		talloc_free(tmp_ctx);
		return NT_STATUS_INVALID_PARAMETER;
	} else if (r_crack_names.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
		r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: %d", r_crack_names.out.ctr.ctr1->array[0].status);
		talloc_free(tmp_ctx);
		return NT_STATUS_UNSUCCESSFUL;
	} else if (r_crack_names.out.ctr.ctr1->array[0].result_name == NULL) {
		r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: no result name");
		talloc_free(tmp_ctx);
		return NT_STATUS_INVALID_PARAMETER;
	}

	/* Store the DN of our machine account. */
	account_dn_str = r_crack_names.out.ctr.ctr1->array[0].result_name;

	account_dn = ldb_dn_new(tmp_ctx, remote_ldb, account_dn_str);
	if (! ldb_dn_validate(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->target_hostname);
	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) {
		r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - %s",
						      account_dn_str, ldb_errstring(remote_ldb));
		talloc_free(tmp_ctx);
		return NT_STATUS_UNSUCCESSFUL;
	}

	talloc_steal(tmp_ctx, res);

	if (res->count != 1) {
		r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - found %d entries",
						      account_dn_str, res->count);
		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_attr_as_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", 
						  ldb_dn_get_linearized(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", 
						  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", 
						  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", 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");
		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;
}