Exemplo n.º 1
0
static NTSTATUS check_netlogond_security(const struct auth_context *auth_context,
					 void *my_private_data,
					 TALLOC_CTX *mem_ctx,
					 const struct auth_usersupplied_info *user_info,
					 struct auth_serversupplied_info **server_info)
{
	TALLOC_CTX *frame = talloc_stackframe();
	struct netr_SamInfo3 *info3 = NULL;
	struct rpc_pipe_client *p = NULL;
	struct pipe_auth_data *auth = NULL;
	uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
	uint8_t machine_password[16];
	struct netlogon_creds_CredentialState *creds;
	NTSTATUS schannel_bind_result, status;
	struct named_mutex *mutex = NULL;
	const char *ncalrpcsock;

	DEBUG(10, ("Check auth for: [%s]\n", user_info->mapped.account_name));

	ncalrpcsock = lp_parm_const_string(
		GLOBAL_SECTION_SNUM, "auth_netlogond", "socket", NULL);

	if (ncalrpcsock == NULL) {
		ncalrpcsock = talloc_asprintf(talloc_tos(), "%s/%s",
					      get_dyn_NCALRPCDIR(), "DEFAULT");
	}

	if (ncalrpcsock == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto done;
	}

	creds = secrets_fetch_local_schannel_creds(talloc_tos());
	if (creds == NULL) {
		goto new_key;
	}

	status = netlogond_validate(talloc_tos(), auth_context, ncalrpcsock,
				    creds, user_info, &info3,
				    &schannel_bind_result);

	DEBUG(10, ("netlogond_validate returned %s\n", nt_errstr(status)));

	if (NT_STATUS_IS_OK(status)) {
		goto okay;
	}

	if (NT_STATUS_IS_OK(schannel_bind_result)) {
		/*
		 * This is a real failure from the DC
		 */
		goto done;
	}

 new_key:

	mutex = grab_named_mutex(talloc_tos(), "LOCAL_SCHANNEL_KEY", 60);
	if (mutex == NULL) {
		DEBUG(10, ("Could not get mutex LOCAL_SCHANNEL_KEY\n"));
		status = NT_STATUS_ACCESS_DENIED;
		goto done;
	}

	DEBUG(10, ("schannel bind failed, setting up new key\n"));

	status = rpc_pipe_open_ncalrpc(talloc_tos(), ncalrpcsock,
				       &ndr_table_netlogon.syntax_id, &p);

	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10, ("rpc_pipe_open_ncalrpc failed: %s\n",
			   nt_errstr(status)));
		goto done;
	}

	status = rpccli_anon_bind_data(p, &auth);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10, ("rpccli_anon_bind_data failed: %s\n",
			   nt_errstr(status)));
		goto done;
	}

	status = rpc_pipe_bind(p, auth);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10, ("rpc_pipe_bind failed: %s\n", nt_errstr(status)));
		goto done;
	}

	status = mymachinepw(machine_password);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10, ("mymachinepw failed: %s\n", nt_errstr(status)));
		goto done;
	}

	DEBUG(10, ("machinepw "));
	dump_data(10, machine_password, 16);

	status = rpccli_netlogon_setup_creds(
		p, global_myname(), lp_workgroup(), global_myname(),
		global_myname(), machine_password, SEC_CHAN_BDC, &neg_flags);

	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10, ("rpccli_netlogon_setup_creds failed: %s\n",
			   nt_errstr(status)));
		goto done;
	}

	secrets_store_local_schannel_creds(p->dc);

	/*
	 * Retry the authentication with the mutex held. This way nobody else
	 * can step on our toes.
	 */

	status = netlogond_validate(talloc_tos(), auth_context, ncalrpcsock,
				    p->dc, user_info, &info3,
				    &schannel_bind_result);

	TALLOC_FREE(p);

	DEBUG(10, ("netlogond_validate returned %s\n", nt_errstr(status)));

	if (!NT_STATUS_IS_OK(status)) {
		goto done;
	}

 okay:

	status = make_server_info_info3(mem_ctx, user_info->client.account_name,
					user_info->mapped.domain_name, server_info,
					info3);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10, ("make_server_info_info3 failed: %s\n",
			   nt_errstr(status)));
		TALLOC_FREE(frame);
		return status;
	}

	status = NT_STATUS_OK;

 done:
	TALLOC_FREE(frame);
	return status;
}
Exemplo n.º 2
0
static struct cli_state *server_cryptkey(TALLOC_CTX *mem_ctx)
{
	struct cli_state *cli = NULL;
	char *desthost = NULL;
	struct sockaddr_storage dest_ss;
	const char *p;
	char *pserver = NULL;
	bool connected_ok = False;
	struct named_mutex *mutex = NULL;
	NTSTATUS status;

        pserver = talloc_strdup(mem_ctx, lp_passwordserver());
	p = pserver;

        while(next_token_talloc(mem_ctx, &p, &desthost, LIST_SEP)) {

		desthost = talloc_sub_basic(mem_ctx,
				current_user_info.smb_name,
				current_user_info.domain,
				desthost);
		if (!desthost) {
			return NULL;
		}
		strupper_m(desthost);

		if (strequal(desthost, myhostname())) {
			DEBUG(1,("Password server loop - disabling "
				 "password server %s\n", desthost));
			continue;
		}

		if(!resolve_name( desthost, &dest_ss, 0x20, false)) {
			DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost));
			continue;
		}

		if (ismyaddr((struct sockaddr *)(void *)&dest_ss)) {
			DEBUG(1,("Password server loop - disabling password server %s\n",desthost));
			continue;
		}

		/* we use a mutex to prevent two connections at once - when a
		   Win2k PDC get two connections where one hasn't completed a
		   session setup yet it will send a TCP reset to the first
		   connection (tridge) */

		mutex = grab_named_mutex(talloc_tos(), desthost, 10);
		if (mutex == NULL) {
			return NULL;
		}

		status = cli_connect_nb(desthost, &dest_ss, 0, 0x20,
					lp_netbios_name(), Undefined, &cli);
		if (NT_STATUS_IS_OK(status)) {
			DEBUG(3,("connected to password server %s\n",desthost));
			connected_ok = True;
			break;
		}
		DEBUG(10,("server_cryptkey: failed to connect to server %s. Error %s\n",
			desthost, nt_errstr(status) ));
		TALLOC_FREE(mutex);
	}

	if (!connected_ok) {
		DEBUG(0,("password server not available\n"));
		return NULL;
	}

	/* security = server just can't function with spnego */
	cli->use_spnego = False;

	DEBUG(3,("got session\n"));

	status = cli_negprot(cli);

	if (!NT_STATUS_IS_OK(status)) {
		TALLOC_FREE(mutex);
		DEBUG(1, ("%s rejected the negprot: %s\n",
			  desthost, nt_errstr(status)));
		cli_shutdown(cli);
		return NULL;
	}

	if (cli->protocol < PROTOCOL_LANMAN2 ||
	    !(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
		TALLOC_FREE(mutex);
		DEBUG(1,("%s isn't in user level security mode\n",desthost));
		cli_shutdown(cli);
		return NULL;
	}

	/* Get the first session setup done quickly, to avoid silly
	   Win2k bugs.  (The next connection to the server will kill
	   this one...
	*/

	status = cli_session_setup(cli, "", "", 0, "", 0, "");
	if (!NT_STATUS_IS_OK(status)) {
		TALLOC_FREE(mutex);
		DEBUG(0,("%s rejected the initial session setup (%s)\n",
			 desthost, nt_errstr(status)));
		cli_shutdown(cli);
		return NULL;
	}

	TALLOC_FREE(mutex);

	DEBUG(3,("password server OK\n"));

	return cli;
}
Exemplo n.º 3
0
static NTSTATUS connect_to_domain_password_server(struct cli_state **cli,
						const char *domain,
						const char *dc_name,
						const struct sockaddr_storage *dc_ss,
						struct rpc_pipe_client **pipe_ret)
{
        NTSTATUS result;
	struct rpc_pipe_client *netlogon_pipe = NULL;

	*cli = NULL;

	*pipe_ret = NULL;

	/* TODO: Send a SAMLOGON request to determine whether this is a valid
	   logonserver.  We can avoid a 30-second timeout if the DC is down
	   if the SAMLOGON request fails as it is only over UDP. */

	/* we use a mutex to prevent two connections at once - when a 
	   Win2k PDC get two connections where one hasn't completed a 
	   session setup yet it will send a TCP reset to the first 
	   connection (tridge) */

	/*
	 * With NT4.x DC's *all* authentication must be serialized to avoid
	 * ACCESS_DENIED errors if 2 auths are done from the same machine. JRA.
	 */

	mutex = grab_named_mutex(NULL, dc_name, 10);
	if (mutex == NULL) {
		return NT_STATUS_NO_LOGON_SERVERS;
	}

	/* Attempt connection */
	result = cli_full_connection(cli, lp_netbios_name(), dc_name, dc_ss, 0,
		"IPC$", "IPC", "", "", "", 0, SMB_SIGNING_DEFAULT);

	if (!NT_STATUS_IS_OK(result)) {
		/* map to something more useful */
		if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
			result = NT_STATUS_NO_LOGON_SERVERS;
		}

		if (*cli) {
			cli_shutdown(*cli);
			*cli = NULL;
		}

		TALLOC_FREE(mutex);
		return result;
	}

	/*
	 * We now have an anonymous connection to IPC$ on the domain password server.
	 */

	/*
	 * Even if the connect succeeds we need to setup the netlogon
	 * pipe here. We do this as we may just have changed the domain
	 * account password on the PDC and yet we may be talking to
	 * a BDC that doesn't have this replicated yet. In this case
	 * a successful connect to a DC needs to take the netlogon connect
	 * into account also. This patch from "Bjart Kvarme" <*****@*****.**>.
	 */

	/* open the netlogon pipe. */
	if (lp_client_schannel()) {
		/* We also setup the creds chain in the open_schannel call. */
		result = cli_rpc_pipe_open_schannel(
			*cli, &ndr_table_netlogon, NCACN_NP,
			DCERPC_AUTH_LEVEL_PRIVACY, domain, &netlogon_pipe);
	} else {
		result = cli_rpc_pipe_open_noauth(
			*cli, &ndr_table_netlogon, &netlogon_pipe);
	}

	if (!NT_STATUS_IS_OK(result)) {
		DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \
machine %s. Error was : %s.\n", dc_name, nt_errstr(result)));
		cli_shutdown(*cli);
		*cli = NULL;
		TALLOC_FREE(mutex);
		return result;
	}
Exemplo n.º 4
0
static bool tdbsam_open( const char *name )
{
	int32_t version;
	int32_t minor_version;
	NTSTATUS status;

	/* check if we are already open */

	if ( db_sam ) {
		return true;
	}

	/* Try to open tdb passwd.  Create a new one if necessary */

	db_sam = db_open(NULL, name, 0, TDB_DEFAULT, O_CREAT|O_RDWR, 0600,
			 DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
	if (db_sam == NULL) {
		DEBUG(0, ("tdbsam_open: Failed to open/create TDB passwd "
			  "[%s]\n", name));
		return false;
	}

	/* Check the version */
	status = dbwrap_fetch_int32_bystring(db_sam, TDBSAM_VERSION_STRING,
					     &version);
	if (!NT_STATUS_IS_OK(status)) {
		version = 0;	/* Version not found, assume version 0 */
	}

	/* Get the minor version */
	status = dbwrap_fetch_int32_bystring(
		db_sam, TDBSAM_MINOR_VERSION_STRING, &minor_version);
	if (!NT_STATUS_IS_OK(status)) {
		minor_version = 0; /* Minor version not found, assume 0 */
	}

	/* Compare the version */
	if (version > TDBSAM_VERSION) {
		/* Version more recent than the latest known */
		DEBUG(0, ("tdbsam_open: unknown version => %d\n", version));
		TALLOC_FREE(db_sam);
		return false;
	}

	if ( version < TDBSAM_VERSION ||
			(version == TDBSAM_VERSION &&
			 minor_version < TDBSAM_MINOR_VERSION) ) {
		/*
		 * Ok - we think we're going to have to convert.
		 * Due to the backup process we now must do to
		 * upgrade we have to get a mutex and re-check
		 * the version. Someone else may have upgraded
		 * whilst we were checking.
		 */

		struct named_mutex *mtx = grab_named_mutex(NULL,
						"tdbsam_upgrade_mutex",
						600);

		if (!mtx) {
			DEBUG(0, ("tdbsam_open: failed to grab mutex.\n"));
			TALLOC_FREE(db_sam);
			return false;
		}

		/* Re-check the version */
		status = dbwrap_fetch_int32_bystring(
			db_sam, TDBSAM_VERSION_STRING, &version);
		if (!NT_STATUS_IS_OK(status)) {
			version = 0;	/* Version not found, assume version 0 */
		}

		/* Re-check the minor version */
		status = dbwrap_fetch_int32_bystring(
			db_sam, TDBSAM_MINOR_VERSION_STRING, &minor_version);
		if (!NT_STATUS_IS_OK(status)) {
			minor_version = 0; /* Minor version not found, assume 0 */
		}

		/* Compare the version */
		if (version > TDBSAM_VERSION) {
			/* Version more recent than the latest known */
			DEBUG(0, ("tdbsam_open: unknown version => %d\n", version));
			TALLOC_FREE(db_sam);
			TALLOC_FREE(mtx);
			return false;
		}

		if ( version < TDBSAM_VERSION ||
				(version == TDBSAM_VERSION &&
				 minor_version < TDBSAM_MINOR_VERSION) ) {
			/*
			 * Note that minor versions we read that are greater
			 * than the current minor version we have hard coded
			 * are assumed to be compatible if they have the same
			 * major version. That allows previous versions of the
			 * passdb code that don't know about minor versions to
			 * still use this database. JRA.
			 */

			DEBUG(1, ("tdbsam_open: Converting version %d.%d database to "
				  "version %d.%d.\n",
					version,
					minor_version,
					TDBSAM_VERSION,
					TDBSAM_MINOR_VERSION));

			if ( !tdbsam_convert(&db_sam, name, version) ) {
				DEBUG(0, ("tdbsam_open: Error when trying to convert "
					  "tdbsam [%s]\n",name));
				TALLOC_FREE(db_sam);
				TALLOC_FREE(mtx);
				return false;
			}

			DEBUG(3, ("TDBSAM converted successfully.\n"));
		}
		TALLOC_FREE(mtx);
	}

	DEBUG(4,("tdbsam_open: successfully opened %s\n", name ));

	return true;
}
Exemplo n.º 5
0
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
			   const char *realm,
			   time_t time_offset,
			   const DATA_BLOB *ticket,
			   char **principal,
			   struct PAC_DATA **pac_data,
			   DATA_BLOB *ap_rep,
			   DATA_BLOB *session_key,
			   bool use_replay_cache)
{
	NTSTATUS sret = NT_STATUS_LOGON_FAILURE;
	NTSTATUS pac_ret;
	DATA_BLOB auth_data;
	krb5_context context = NULL;
	krb5_auth_context auth_context = NULL;
	krb5_data packet;
	krb5_ticket *tkt = NULL;
	krb5_rcache rcache = NULL;
	krb5_keyblock *keyblock = NULL;
	time_t authtime;
	krb5_error_code ret = 0;
	int flags = 0;	
	krb5_principal host_princ = NULL;
	krb5_const_principal client_principal = NULL;
	char *host_princ_s = NULL;
	bool auth_ok = False;
	bool got_auth_data = False;
	struct named_mutex *mutex = NULL;

	ZERO_STRUCT(packet);
	ZERO_STRUCT(auth_data);

	*principal = NULL;
	*pac_data = NULL;
	*ap_rep = data_blob_null;
	*session_key = data_blob_null;

	initialize_krb5_error_table();
	ret = krb5_init_context(&context);
	if (ret) {
		DEBUG(1,("ads_verify_ticket: krb5_init_context failed (%s)\n", error_message(ret)));
		return NT_STATUS_LOGON_FAILURE;
	}

	if (time_offset != 0) {
		krb5_set_real_time(context, time(NULL) + time_offset, 0);
	}

	ret = krb5_set_default_realm(context, realm);
	if (ret) {
		DEBUG(1,("ads_verify_ticket: krb5_set_default_realm failed (%s)\n", error_message(ret)));
		goto out;
	}

	/* This whole process is far more complex than I would
           like. We have to go through all this to allow us to store
           the secret internally, instead of using /etc/krb5.keytab */

	ret = krb5_auth_con_init(context, &auth_context);
	if (ret) {
		DEBUG(1,("ads_verify_ticket: krb5_auth_con_init failed (%s)\n", error_message(ret)));
		goto out;
	}

	krb5_auth_con_getflags( context, auth_context, &flags );
	if ( !use_replay_cache ) {
		/* Disable default use of a replay cache */
		flags &= ~KRB5_AUTH_CONTEXT_DO_TIME;
		krb5_auth_con_setflags( context, auth_context, flags );
	}

	if (asprintf(&host_princ_s, "%s$", global_myname()) == -1) {
		goto out;
	}

	strlower_m(host_princ_s);
	ret = smb_krb5_parse_name(context, host_princ_s, &host_princ);
	if (ret) {
		DEBUG(1,("ads_verify_ticket: smb_krb5_parse_name(%s) failed (%s)\n",
					host_princ_s, error_message(ret)));
		goto out;
	}


	if ( use_replay_cache ) {
		
		/* Lock a mutex surrounding the replay as there is no 
		   locking in the MIT krb5 code surrounding the replay 
		   cache... */

		mutex = grab_named_mutex(talloc_tos(), "replay cache mutex",
					 10);
		if (mutex == NULL) {
			DEBUG(1,("ads_verify_ticket: unable to protect "
				 "replay cache with mutex.\n"));
			ret = KRB5_CC_IO;
			goto out;
		}

		/* JRA. We must set the rcache here. This will prevent 
		   replay attacks. */
		
		ret = krb5_get_server_rcache(context, 
					     krb5_princ_component(context, host_princ, 0), 
					     &rcache);
		if (ret) {
			DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache "
				 "failed (%s)\n", error_message(ret)));
			goto out;
		}

		ret = krb5_auth_con_setrcache(context, auth_context, rcache);
		if (ret) {
			DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache "
				 "failed (%s)\n", error_message(ret)));
			goto out;
		}
	}

	/* Try secrets.tdb first and fallback to the krb5.keytab if
	   necessary */

	auth_ok = ads_secrets_verify_ticket(context, auth_context, host_princ,
					    ticket, &tkt, &keyblock, &ret);

	if (!auth_ok &&
	    (ret == KRB5KRB_AP_ERR_TKT_NYV ||
	     ret == KRB5KRB_AP_ERR_TKT_EXPIRED ||
	     ret == KRB5KRB_AP_ERR_SKEW)) {
		goto auth_failed;
	}

	if (!auth_ok && lp_use_kerberos_keytab()) {
		auth_ok = ads_keytab_verify_ticket(context, auth_context, 
						   ticket, &tkt, &keyblock, &ret);
	}

	if ( use_replay_cache ) {		
		TALLOC_FREE(mutex);
#if 0
		/* Heimdal leaks here, if we fix the leak, MIT crashes */
		if (rcache) {
			krb5_rc_close(context, rcache);
		}
#endif
	}	

 auth_failed:
	if (!auth_ok) {
		DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n", 
			 error_message(ret)));
		/* Try map the error return in case it's something like
		 * a clock skew error.
		 */
		sret = krb5_to_nt_status(ret);
		if (NT_STATUS_IS_OK(sret) || NT_STATUS_EQUAL(sret,NT_STATUS_UNSUCCESSFUL)) {
			sret = NT_STATUS_LOGON_FAILURE;
		}
		DEBUG(10,("ads_verify_ticket: returning error %s\n",
			nt_errstr(sret) ));
		goto out;
	} 
	
	authtime = get_authtime_from_tkt(tkt);
	client_principal = get_principal_from_tkt(tkt);

	ret = krb5_mk_rep(context, auth_context, &packet);
	if (ret) {
		DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n",
			error_message(ret)));
		goto out;
	}

	*ap_rep = data_blob(packet.data, packet.length);
	if (packet.data) {
		kerberos_free_data_contents(context, &packet);
		ZERO_STRUCT(packet);
	}

	get_krb5_smb_session_key(context, auth_context, session_key, True);
	dump_data_pw("SMB session key (from ticket)\n", session_key->data, session_key->length);

#if 0
	file_save("/tmp/ticket.dat", ticket->data, ticket->length);
#endif

	/* continue when no PAC is retrieved or we couldn't decode the PAC 
	   (like accounts that have the UF_NO_AUTH_DATA_REQUIRED flag set, or
	   Kerberos tickets encrypted using a DES key) - Guenther */

	got_auth_data = get_auth_data_from_tkt(mem_ctx, &auth_data, tkt);
	if (!got_auth_data) {
		DEBUG(3,("ads_verify_ticket: did not retrieve auth data. continuing without PAC\n"));
	}

	if (got_auth_data) {
		pac_ret = decode_pac_data(mem_ctx, &auth_data, context, keyblock, client_principal, authtime, pac_data);
		if (!NT_STATUS_IS_OK(pac_ret)) {
			DEBUG(3,("ads_verify_ticket: failed to decode PAC_DATA: %s\n", nt_errstr(pac_ret)));
			*pac_data = NULL;
		}
		data_blob_free(&auth_data);
	}

#if 0
#if defined(HAVE_KRB5_TKT_ENC_PART2)
	/* MIT */
	if (tkt->enc_part2) {
		file_save("/tmp/authdata.dat",
			  tkt->enc_part2->authorization_data[0]->contents,
			  tkt->enc_part2->authorization_data[0]->length);
	}
#else
	/* Heimdal */
	if (tkt->ticket.authorization_data) {
		file_save("/tmp/authdata.dat",
			  tkt->ticket.authorization_data->val->ad_data.data,
			  tkt->ticket.authorization_data->val->ad_data.length);
	}
#endif
#endif

	if ((ret = smb_krb5_unparse_name(context, client_principal, principal))) {
		DEBUG(3,("ads_verify_ticket: smb_krb5_unparse_name failed (%s)\n", 
			 error_message(ret)));
		sret = NT_STATUS_LOGON_FAILURE;
		goto out;
	}

	sret = NT_STATUS_OK;

 out:

	TALLOC_FREE(mutex);

	if (!NT_STATUS_IS_OK(sret)) {
		data_blob_free(&auth_data);
	}

	if (!NT_STATUS_IS_OK(sret)) {
		data_blob_free(ap_rep);
	}

	if (host_princ) {
		krb5_free_principal(context, host_princ);
	}

	if (keyblock) {
		krb5_free_keyblock(context, keyblock);
	}

	if (tkt != NULL) {
		krb5_free_ticket(context, tkt);
	}

	SAFE_FREE(host_princ_s);

	if (auth_context) {
		krb5_auth_con_free(context, auth_context);
	}

	if (context) {
		krb5_free_context(context);
	}

	return sret;
}
static NTSTATUS connect_to_domain_password_server(struct cli_state **cli_ret,
						const char *domain,
						const char *dc_name,
						const struct sockaddr_storage *dc_ss,
						struct rpc_pipe_client **pipe_ret,
						TALLOC_CTX *mem_ctx,
						struct netlogon_creds_cli_context **creds_ret)
{
	TALLOC_CTX *frame = talloc_stackframe();
	struct messaging_context *msg_ctx = server_messaging_context();
	NTSTATUS result;
	struct cli_state *cli = NULL;
	struct rpc_pipe_client *netlogon_pipe = NULL;
	struct netlogon_creds_cli_context *netlogon_creds = NULL;
	struct netlogon_creds_CredentialState *creds = NULL;
	uint32_t netlogon_flags = 0;
	enum netr_SchannelType sec_chan_type = 0;
	const char *_account_name = NULL;
	const char *account_name = NULL;
	struct samr_Password current_nt_hash;
	struct samr_Password *previous_nt_hash = NULL;
	bool ok;

	*cli_ret = NULL;

	*pipe_ret = NULL;
	*creds_ret = NULL;

	/* TODO: Send a SAMLOGON request to determine whether this is a valid
	   logonserver.  We can avoid a 30-second timeout if the DC is down
	   if the SAMLOGON request fails as it is only over UDP. */

	/* we use a mutex to prevent two connections at once - when a 
	   Win2k PDC get two connections where one hasn't completed a 
	   session setup yet it will send a TCP reset to the first 
	   connection (tridge) */

	/*
	 * With NT4.x DC's *all* authentication must be serialized to avoid
	 * ACCESS_DENIED errors if 2 auths are done from the same machine. JRA.
	 */

	mutex = grab_named_mutex(NULL, dc_name, 10);
	if (mutex == NULL) {
		TALLOC_FREE(frame);
		return NT_STATUS_NO_LOGON_SERVERS;
	}

	/* Attempt connection */
	result = cli_full_connection(&cli, lp_netbios_name(), dc_name, dc_ss, 0,
		"IPC$", "IPC", "", "", "", 0, SMB_SIGNING_DEFAULT);

	if (!NT_STATUS_IS_OK(result)) {
		/* map to something more useful */
		if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
			result = NT_STATUS_NO_LOGON_SERVERS;
		}

		TALLOC_FREE(mutex);
		TALLOC_FREE(frame);
		return result;
	}

	/*
	 * We now have an anonymous connection to IPC$ on the domain password server.
	 */

	ok = get_trust_pw_hash(domain,
			       current_nt_hash.hash,
			       &_account_name,
			       &sec_chan_type);
	if (!ok) {
		cli_shutdown(cli);
		TALLOC_FREE(mutex);
		TALLOC_FREE(frame);
		return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
	}

	account_name = talloc_asprintf(talloc_tos(), "%s$", _account_name);
	if (account_name == NULL) {
		cli_shutdown(cli);
		TALLOC_FREE(mutex);
		TALLOC_FREE(frame);
		return NT_STATUS_NO_MEMORY;
	}

	result = rpccli_create_netlogon_creds(dc_name,
					      domain,
					      account_name,
					      sec_chan_type,
					      msg_ctx,
					      talloc_tos(),
					      &netlogon_creds);
	if (!NT_STATUS_IS_OK(result)) {
		cli_shutdown(cli);
		TALLOC_FREE(mutex);
		TALLOC_FREE(frame);
		SAFE_FREE(previous_nt_hash);
		return result;
	}

	result = rpccli_setup_netlogon_creds(cli,
					     netlogon_creds,
					     false, /* force_reauth */
					     current_nt_hash,
					     previous_nt_hash);
	SAFE_FREE(previous_nt_hash);
	if (!NT_STATUS_IS_OK(result)) {
		cli_shutdown(cli);
		TALLOC_FREE(mutex);
		TALLOC_FREE(frame);
		return result;
	}

	result = netlogon_creds_cli_get(netlogon_creds,
					talloc_tos(),
					&creds);
	if (!NT_STATUS_IS_OK(result)) {
		cli_shutdown(cli);
		TALLOC_FREE(mutex);
		TALLOC_FREE(frame);
		return result;
	}
	netlogon_flags = creds->negotiate_flags;
	TALLOC_FREE(creds);

	if (netlogon_flags & NETLOGON_NEG_AUTHENTICATED_RPC) {
		result = cli_rpc_pipe_open_schannel_with_key(
			cli, &ndr_table_netlogon, NCACN_NP,
			domain, netlogon_creds, &netlogon_pipe);
	} else {
		result = cli_rpc_pipe_open_noauth(cli,
					&ndr_table_netlogon,
					&netlogon_pipe);
	}

	if (!NT_STATUS_IS_OK(result)) {
		DEBUG(0,("connect_to_domain_password_server: "
			 "unable to open the domain client session to "
			 "machine %s. Flags[0x%08X] Error was : %s.\n",
			 dc_name, (unsigned)netlogon_flags,
			 nt_errstr(result)));
		cli_shutdown(cli);
		TALLOC_FREE(mutex);
		TALLOC_FREE(frame);
		return result;
	}

	if(!netlogon_pipe) {
		DEBUG(0, ("connect_to_domain_password_server: unable to open "
			  "the domain client session to machine %s. Error "
			  "was : %s.\n", dc_name, nt_errstr(result)));
		cli_shutdown(cli);
		TALLOC_FREE(mutex);
		TALLOC_FREE(frame);
		return NT_STATUS_NO_LOGON_SERVERS;
	}

	/* We exit here with the mutex *locked*. JRA */

	*cli_ret = cli;
	*pipe_ret = netlogon_pipe;
	*creds_ret = talloc_move(mem_ctx, &netlogon_creds);

	TALLOC_FREE(frame);
	return NT_STATUS_OK;
}