예제 #1
0
static NTSTATUS smbd_smb2_spnego_negotiate(struct smbd_smb2_session *session,
					struct smbd_smb2_request *smb2req,
					uint8_t in_security_mode,
					DATA_BLOB in_security_buffer,
					uint16_t *out_session_flags,
					DATA_BLOB *out_security_buffer,
					uint64_t *out_session_id)
{
	DATA_BLOB secblob_in = data_blob_null;
	DATA_BLOB chal_out = data_blob_null;
	char *kerb_mech = NULL;
	NTSTATUS status;

	/* Ensure we have no old NTLM state around. */
	TALLOC_FREE(session->auth_ntlmssp_state);

	status = parse_spnego_mechanisms(talloc_tos(), in_security_buffer,
			&secblob_in, &kerb_mech);
	if (!NT_STATUS_IS_OK(status)) {
		goto out;
	}

#ifdef HAVE_KRB5
	if (kerb_mech && ((lp_security()==SEC_ADS) ||
				USE_KERBEROS_KEYTAB) ) {
		status = smbd_smb2_session_setup_krb5(session,
				smb2req,
				in_security_mode,
				&secblob_in,
				kerb_mech,
				out_session_flags,
				out_security_buffer,
				out_session_id);

		goto out;
	}
#endif

	if (kerb_mech) {
		/* The mechtoken is a krb5 ticket, but
		 * we need to fall back to NTLM. */

		DEBUG(3,("smb2: Got krb5 ticket in SPNEGO "
			"but set to downgrade to NTLMSSP\n"));

		status = NT_STATUS_MORE_PROCESSING_REQUIRED;
	} else {
		/* Fall back to NTLMSSP. */
		status = auth_ntlmssp_prepare(session->sconn->remote_address,
					    &session->auth_ntlmssp_state);
		if (!NT_STATUS_IS_OK(status)) {
			goto out;
		}

		auth_ntlmssp_want_feature(session->auth_ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);

		status = auth_ntlmssp_start(session->auth_ntlmssp_state);
		if (!NT_STATUS_IS_OK(status)) {
			goto out;
		}

		status = auth_ntlmssp_update(session->auth_ntlmssp_state,
					     talloc_tos(),
					     secblob_in,
					     &chal_out);
	}

	if (!NT_STATUS_IS_OK(status) &&
			!NT_STATUS_EQUAL(status,
				NT_STATUS_MORE_PROCESSING_REQUIRED)) {
		goto out;
	}

	*out_security_buffer = spnego_gen_auth_response(smb2req,
						&chal_out,
						status,
						OID_NTLMSSP);
	if (out_security_buffer->data == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}
	*out_session_id = session->vuid;

  out:

	data_blob_free(&secblob_in);
	data_blob_free(&chal_out);
	TALLOC_FREE(kerb_mech);
	if (!NT_STATUS_IS_OK(status) &&
			!NT_STATUS_EQUAL(status,
				NT_STATUS_MORE_PROCESSING_REQUIRED)) {
		TALLOC_FREE(session->auth_ntlmssp_state);
		TALLOC_FREE(session);
	}
	return status;
}
예제 #2
0
static NTSTATUS smbd_smb2_spnego_auth(struct smbd_smb2_session *session,
					struct smbd_smb2_request *smb2req,
					uint8_t in_security_mode,
					DATA_BLOB in_security_buffer,
					uint16_t *out_session_flags,
					DATA_BLOB *out_security_buffer,
					uint64_t *out_session_id)
{
	DATA_BLOB auth = data_blob_null;
	DATA_BLOB auth_out = data_blob_null;
	NTSTATUS status;

	if (!spnego_parse_auth(talloc_tos(), in_security_buffer, &auth)) {
		TALLOC_FREE(session);
		return NT_STATUS_LOGON_FAILURE;
	}

	if (auth.data[0] == ASN1_APPLICATION(0)) {
		/* Might be a second negTokenTarg packet */
		DATA_BLOB secblob_in = data_blob_null;
		char *kerb_mech = NULL;

		status = parse_spnego_mechanisms(talloc_tos(),
				in_security_buffer,
				&secblob_in, &kerb_mech);
		if (!NT_STATUS_IS_OK(status)) {
			TALLOC_FREE(session);
			return status;
		}

#ifdef HAVE_KRB5
		if (kerb_mech && ((lp_security()==SEC_ADS) ||
					USE_KERBEROS_KEYTAB) ) {
			status = smbd_smb2_session_setup_krb5(session,
					smb2req,
					in_security_mode,
					&secblob_in,
					kerb_mech,
					out_session_flags,
					out_security_buffer,
					out_session_id);

			data_blob_free(&secblob_in);
			TALLOC_FREE(kerb_mech);
			if (!NT_STATUS_IS_OK(status)) {
				TALLOC_FREE(session);
			}
			return status;
		}
#endif

		/* Can't blunder into NTLMSSP auth if we have
		 * a krb5 ticket. */

		if (kerb_mech) {
			DEBUG(3,("smb2: network "
				"misconfiguration, client sent us a "
				"krb5 ticket and kerberos security "
				"not enabled\n"));
			TALLOC_FREE(session);
			data_blob_free(&secblob_in);
			TALLOC_FREE(kerb_mech);
			return NT_STATUS_LOGON_FAILURE;
		}

		data_blob_free(&secblob_in);
	}

	if (session->auth_ntlmssp_state == NULL) {
		status = auth_ntlmssp_prepare(session->sconn->remote_address,
					    &session->auth_ntlmssp_state);
		if (!NT_STATUS_IS_OK(status)) {
			data_blob_free(&auth);
			TALLOC_FREE(session);
			return status;
		}

		auth_ntlmssp_want_feature(session->auth_ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);

		status = auth_ntlmssp_start(session->auth_ntlmssp_state);
		if (!NT_STATUS_IS_OK(status)) {
			data_blob_free(&auth);
			TALLOC_FREE(session);
			return status;
		}
	}

	status = auth_ntlmssp_update(session->auth_ntlmssp_state,
				     talloc_tos(), auth,
				     &auth_out);
	/* If status is NT_STATUS_OK then we need to get the token.
	 * Map to guest is now internal to auth_ntlmssp */
	if (NT_STATUS_IS_OK(status)) {
		status = auth_ntlmssp_session_info(session,
						   session->auth_ntlmssp_state,
						   &session->session_info);
	}

	if (!NT_STATUS_IS_OK(status) &&
			!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
		TALLOC_FREE(session->auth_ntlmssp_state);
		data_blob_free(&auth);
		TALLOC_FREE(session);
		return status;
	}

	data_blob_free(&auth);

	*out_security_buffer = spnego_gen_auth_response(smb2req,
				&auth_out, status, NULL);

	if (out_security_buffer->data == NULL) {
		TALLOC_FREE(session->auth_ntlmssp_state);
		TALLOC_FREE(session);
		return NT_STATUS_NO_MEMORY;
	}

	*out_session_id = session->vuid;

	if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
		return NT_STATUS_MORE_PROCESSING_REQUIRED;
	}

	/* We're done - claim the session. */
	return smbd_smb2_common_ntlmssp_auth_return(session,
						smb2req,
						in_security_mode,
						in_security_buffer,
						out_session_flags,
						out_session_id);
}
예제 #3
0
static NTSTATUS smbd_smb2_session_setup_krb5(struct smbd_smb2_session *session,
					struct smbd_smb2_request *smb2req,
					uint8_t in_security_mode,
					const DATA_BLOB *secblob,
					const char *mechOID,
					uint16_t *out_session_flags,
					DATA_BLOB *out_security_buffer,
					uint64_t *out_session_id)
{
	DATA_BLOB ap_rep = data_blob_null;
	DATA_BLOB ap_rep_wrapped = data_blob_null;
	DATA_BLOB ticket = data_blob_null;
	DATA_BLOB session_key = data_blob_null;
	DATA_BLOB secblob_out = data_blob_null;
	uint8 tok_id[2];
	struct PAC_LOGON_INFO *logon_info = NULL;
	char *principal = NULL;
	char *user = NULL;
	char *domain = NULL;
	struct passwd *pw = NULL;
	NTSTATUS status;
	char *real_username;
	bool username_was_mapped = false;
	bool map_domainuser_to_guest = false;

	if (!spnego_parse_krb5_wrap(talloc_tos(), *secblob, &ticket, tok_id)) {
		status = NT_STATUS_LOGON_FAILURE;
		goto fail;
	}

	status = ads_verify_ticket(smb2req, lp_realm(), 0, &ticket,
				   &principal, &logon_info, &ap_rep,
				   &session_key, true);

	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1,("smb2: Failed to verify incoming ticket with error %s!\n",
			nt_errstr(status)));
		if (!NT_STATUS_EQUAL(status, NT_STATUS_TIME_DIFFERENCE_AT_DC)) {
			status = NT_STATUS_LOGON_FAILURE;
		}
		goto fail;
	}

	status = get_user_from_kerberos_info(talloc_tos(),
					     session->sconn->remote_hostname,
					     principal, logon_info,
					     &username_was_mapped,
					     &map_domainuser_to_guest,
					     &user, &domain,
					     &real_username, &pw);
	if (!NT_STATUS_IS_OK(status)) {
		goto fail;
	}

	/* save the PAC data if we have it */
	if (logon_info) {
		netsamlogon_cache_store(user, &logon_info->info3);
	}

	/* setup the string used by %U */
	sub_set_smb_name(real_username);

	/* reload services so that the new %U is taken into account */
	reload_services(smb2req->sconn->msg_ctx, smb2req->sconn->sock, true);

	status = make_session_info_krb5(session,
					user, domain, real_username, pw,
					logon_info, map_domainuser_to_guest,
					username_was_mapped,
					&session_key,
					&session->session_info);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1, ("smb2: make_server_info_krb5 failed\n"));
		goto fail;
	}

	if ((in_security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) ||
	     lp_server_signing() == Required) {
		session->do_signing = true;
	}

	if (security_session_user_level(session->session_info, NULL) < SECURITY_USER) {
		/* we map anonymous to guest internally */
		*out_session_flags |= SMB2_SESSION_FLAG_IS_GUEST;
		*out_session_flags |= SMB2_SESSION_FLAG_IS_NULL;
		/* force no signing */
		session->do_signing = false;
	}

	session->session_key = session->session_info->session_key;

	session->compat_vuser = talloc_zero(session, user_struct);
	if (session->compat_vuser == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto fail;
	}
	session->compat_vuser->auth_ntlmssp_state = NULL;
	session->compat_vuser->homes_snum = -1;
	session->compat_vuser->session_info = session->session_info;
	session->compat_vuser->session_keystr = NULL;
	session->compat_vuser->vuid = session->vuid;
	DLIST_ADD(session->sconn->smb1.sessions.validated_users, session->compat_vuser);

	if (security_session_user_level(session->session_info, NULL) >= SECURITY_USER) {
		session->compat_vuser->homes_snum =
			register_homes_share(session->session_info->unix_info->unix_name);
	}

	if (!session_claim(session->sconn, session->compat_vuser)) {
		DEBUG(1, ("smb2: Failed to claim session "
			"for vuid=%d\n",
			session->compat_vuser->vuid));
		goto fail;
	}

	session->status = NT_STATUS_OK;

	/*
	 * we attach the session to the request
	 * so that the response can be signed
	 */
	smb2req->session = session;
	if (session->do_signing) {
		smb2req->do_signing = true;
	}

	global_client_caps |= (CAP_LEVEL_II_OPLOCKS|CAP_STATUS32);
        status = NT_STATUS_OK;

	/* wrap that up in a nice GSS-API wrapping */
	ap_rep_wrapped = spnego_gen_krb5_wrap(talloc_tos(), ap_rep,
				TOK_ID_KRB_AP_REP);

	secblob_out = spnego_gen_auth_response(
					talloc_tos(),
					&ap_rep_wrapped,
					status,
					mechOID);

	*out_security_buffer = data_blob_talloc(smb2req,
						secblob_out.data,
						secblob_out.length);
	if (secblob_out.data && out_security_buffer->data == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto fail;
	}

	data_blob_free(&ap_rep);
	data_blob_free(&ap_rep_wrapped);
	data_blob_free(&ticket);
	data_blob_free(&session_key);
	data_blob_free(&secblob_out);

	*out_session_id = session->vuid;

	return NT_STATUS_OK;

  fail:

	data_blob_free(&ap_rep);
	data_blob_free(&ap_rep_wrapped);
	data_blob_free(&ticket);
	data_blob_free(&session_key);
	data_blob_free(&secblob_out);

	ap_rep_wrapped = data_blob_null;
	secblob_out = spnego_gen_auth_response(
					talloc_tos(),
					&ap_rep_wrapped,
					status,
					mechOID);

	*out_security_buffer = data_blob_talloc(smb2req,
						secblob_out.data,
						secblob_out.length);
	data_blob_free(&secblob_out);
	return status;
}
예제 #4
0
static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *inbuf, char *outbuf,
				 uint16 vuid,
				 AUTH_NTLMSSP_STATE **auth_ntlmssp_state,
				 DATA_BLOB *ntlmssp_blob, NTSTATUS nt_status, 
				 BOOL wrap) 
{
	BOOL ret;
	DATA_BLOB response;
	struct auth_serversupplied_info *server_info = NULL;

	if (NT_STATUS_IS_OK(nt_status)) {
		server_info = (*auth_ntlmssp_state)->server_info;
	} else {
		nt_status = do_map_to_guest(nt_status, 
					    &server_info, 
					    (*auth_ntlmssp_state)->ntlmssp_state->user, 
					    (*auth_ntlmssp_state)->ntlmssp_state->domain);
	}

	if (NT_STATUS_IS_OK(nt_status)) {
		int sess_vuid;
		DATA_BLOB nullblob = data_blob(NULL, 0);
		DATA_BLOB session_key = data_blob((*auth_ntlmssp_state)->ntlmssp_state->session_key.data, (*auth_ntlmssp_state)->ntlmssp_state->session_key.length);

		/* register_vuid keeps the server info */
		sess_vuid = register_vuid(server_info, session_key, nullblob, (*auth_ntlmssp_state)->ntlmssp_state->user);
		(*auth_ntlmssp_state)->server_info = NULL;

		if (sess_vuid == UID_FIELD_INVALID ) {
			nt_status = NT_STATUS_LOGON_FAILURE;
		} else {
			
			/* current_user_info is changed on new vuid */
			reload_services( True );

			set_message(outbuf,4,0,True);
			SSVAL(outbuf, smb_vwv3, 0);
			
			if (server_info->guest) {
				SSVAL(outbuf,smb_vwv2,1);
			}
			
			SSVAL(outbuf,smb_uid,sess_vuid);

			sessionsetup_start_signing_engine(server_info, inbuf);
		}
	}

	if (wrap) {
		response = spnego_gen_auth_response(ntlmssp_blob, nt_status, OID_NTLMSSP);
	} else {
		response = *ntlmssp_blob;
	}

	ret = reply_sesssetup_blob(conn, outbuf, response, nt_status);
	if (wrap) {
		data_blob_free(&response);
	}

	/* NT_STATUS_MORE_PROCESSING_REQUIRED from our NTLMSSP code tells us,
	   and the other end, that we are not finished yet. */

	if (!ret || !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
		/* NB. This is *NOT* an error case. JRA */
		auth_ntlmssp_end(auth_ntlmssp_state);
		/* Kill the intermediate vuid */
		invalidate_vuid(vuid);
	}

	return ret;
}
예제 #5
0
static int reply_spnego_kerberos(connection_struct *conn, 
				 char *inbuf, char *outbuf,
				 int length, int bufsize,
				 DATA_BLOB *secblob,
				 BOOL *p_invalidate_vuid)
{
	TALLOC_CTX *mem_ctx;
	DATA_BLOB ticket;
	char *client, *p, *domain;
	fstring netbios_domain_name;
	struct passwd *pw;
	fstring user;
	int sess_vuid;
	NTSTATUS ret;
	PAC_DATA *pac_data;
	DATA_BLOB ap_rep, ap_rep_wrapped, response;
	auth_serversupplied_info *server_info = NULL;
	DATA_BLOB session_key = data_blob(NULL, 0);
	uint8 tok_id[2];
	DATA_BLOB nullblob = data_blob(NULL, 0);
	fstring real_username;
	BOOL map_domainuser_to_guest = False;
	BOOL username_was_mapped;
	PAC_LOGON_INFO *logon_info = NULL;

	ZERO_STRUCT(ticket);
	ZERO_STRUCT(pac_data);
	ZERO_STRUCT(ap_rep);
	ZERO_STRUCT(ap_rep_wrapped);
	ZERO_STRUCT(response);

	/* Normally we will always invalidate the intermediate vuid. */
	*p_invalidate_vuid = True;

	mem_ctx = talloc_init("reply_spnego_kerberos");
	if (mem_ctx == NULL) {
		return ERROR_NT(nt_status_squash(NT_STATUS_NO_MEMORY));
	}

	if (!spnego_parse_krb5_wrap(*secblob, &ticket, tok_id)) {
		talloc_destroy(mem_ctx);
		return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE));
	}

	ret = ads_verify_ticket(mem_ctx, lp_realm(), 0, &ticket, &client, &pac_data, &ap_rep, &session_key);

	data_blob_free(&ticket);

	if (!NT_STATUS_IS_OK(ret)) {
#if 0
		/* Experiment that failed. See "only happens with a KDC" comment below. */

		if (NT_STATUS_EQUAL(ret, NT_STATUS_TIME_DIFFERENCE_AT_DC)) {

			/*
			 * Windows in this case returns NT_STATUS_MORE_PROCESSING_REQUIRED
			 * with a negTokenTarg blob containing an krb5_error struct ASN1 encoded
			 * containing KRB5KRB_AP_ERR_SKEW. The client then fixes its
			 * clock and continues rather than giving an error. JRA.
			 * -- Looks like this only happens with a KDC. JRA.
			 */

			BOOL ok = make_krb5_skew_error(&ap_rep);
			if (!ok) {
				talloc_destroy(mem_ctx);
				return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE));
			}
			ap_rep_wrapped = spnego_gen_krb5_wrap(ap_rep, TOK_ID_KRB_ERROR);
			response = spnego_gen_auth_response(&ap_rep_wrapped, ret, OID_KERBEROS5_OLD);
			reply_sesssetup_blob(conn, outbuf, response, NT_STATUS_MORE_PROCESSING_REQUIRED);

			/*
			 * In this one case we don't invalidate the intermediate vuid.
			 * as we're expecting the client to re-use it for the next
			 * sessionsetupX packet. JRA.
			 */

			*p_invalidate_vuid = False;

			data_blob_free(&ap_rep);
			data_blob_free(&ap_rep_wrapped);
			data_blob_free(&response);
			talloc_destroy(mem_ctx);
			return -1; /* already replied */
		}
#else
		if (!NT_STATUS_EQUAL(ret, NT_STATUS_TIME_DIFFERENCE_AT_DC)) {
			ret = NT_STATUS_LOGON_FAILURE;
		}
#endif
		DEBUG(1,("Failed to verify incoming ticket with error %s!\n", nt_errstr(ret)));	
		talloc_destroy(mem_ctx);
		return ERROR_NT(nt_status_squash(ret));
	}

	DEBUG(3,("Ticket name is [%s]\n", client));

	p = strchr_m(client, '@');
	if (!p) {
		DEBUG(3,("Doesn't look like a valid principal\n"));
		data_blob_free(&ap_rep);
		data_blob_free(&session_key);
		SAFE_FREE(client);
		talloc_destroy(mem_ctx);
		return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE));
	}

	*p = 0;

	/* save the PAC data if we have it */

	if (pac_data) {
		logon_info = get_logon_info_from_pac(pac_data);
		if (logon_info) {
			netsamlogon_cache_store( client, &logon_info->info3 );
		}
	}

	if (!strequal(p+1, lp_realm())) {
		DEBUG(3,("Ticket for foreign realm %s@%s\n", client, p+1));
		if (!lp_allow_trusted_domains()) {
			data_blob_free(&ap_rep);
			data_blob_free(&session_key);
			SAFE_FREE(client);
			talloc_destroy(mem_ctx);
			return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE));
		}
	}

	/* this gives a fully qualified user name (ie. with full realm).
	   that leads to very long usernames, but what else can we do? */

	domain = p+1;

	if (logon_info && logon_info->info3.hdr_logon_dom.uni_str_len) {

		unistr2_to_ascii(netbios_domain_name, &logon_info->info3.uni_logon_dom, -1);
		domain = netbios_domain_name;
		DEBUG(10, ("Mapped to [%s] (using PAC)\n", domain));

	} else {

		/* If we have winbind running, we can (and must) shorten the
		   username by using the short netbios name. Otherwise we will
		   have inconsistent user names. With Kerberos, we get the
		   fully qualified realm, with ntlmssp we get the short
		   name. And even w2k3 does use ntlmssp if you for example
		   connect to an ip address. */

		struct winbindd_request wb_request;
		struct winbindd_response wb_response;
		NSS_STATUS wb_result;

		ZERO_STRUCT(wb_request);
		ZERO_STRUCT(wb_response);

		DEBUG(10, ("Mapping [%s] to short name\n", domain));

		fstrcpy(wb_request.domain_name, domain);

		wb_result = winbindd_request_response(WINBINDD_DOMAIN_INFO,
					     &wb_request, &wb_response);

		if (wb_result == NSS_STATUS_SUCCESS) {

			fstrcpy(netbios_domain_name,
				wb_response.data.domain_info.name);
			domain = netbios_domain_name;

			DEBUG(10, ("Mapped to [%s] (using Winbind)\n", domain));
		} else {
			DEBUG(3, ("Could not find short name -- winbind "
				  "not running?\n"));
		}
	}

	fstr_sprintf(user, "%s%c%s", domain, *lp_winbind_separator(), client);
	
	/* lookup the passwd struct, create a new user if necessary */

	username_was_mapped = map_username( user );

	pw = smb_getpwnam( mem_ctx, user, real_username, True );

	if (pw) {
		/* if a real user check pam account restrictions */
		/* only really perfomed if "obey pam restriction" is true */
		/* do this before an eventual mappign to guest occurs */
		ret = smb_pam_accountcheck(pw->pw_name);
		if (  !NT_STATUS_IS_OK(ret)) {
			DEBUG(1, ("PAM account restriction prevents user login\n"));
			data_blob_free(&ap_rep);
			data_blob_free(&session_key);
			TALLOC_FREE(mem_ctx);
			return ERROR_NT(nt_status_squash(ret));
		}
	}

	if (!pw) {

		/* this was originally the behavior of Samba 2.2, if a user
		   did not have a local uid but has been authenticated, then 
		   map them to a guest account */

		if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_UID){ 
			map_domainuser_to_guest = True;
			fstrcpy(user,lp_guestaccount());
			pw = smb_getpwnam( mem_ctx, user, real_username, True );
		} 

		/* extra sanity check that the guest account is valid */

		if ( !pw ) {
			DEBUG(1,("Username %s is invalid on this system\n", user));
			SAFE_FREE(client);
			data_blob_free(&ap_rep);
			data_blob_free(&session_key);
			TALLOC_FREE(mem_ctx);
			return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE));
		}
	}

	/* setup the string used by %U */
	
	sub_set_smb_name( real_username );
	reload_services(True);

	if ( map_domainuser_to_guest ) {
		make_server_info_guest(&server_info);
	} else if (logon_info) {
		/* pass the unmapped username here since map_username() 
		   will be called again from inside make_server_info_info3() */
		
		ret = make_server_info_info3(mem_ctx, client, domain, 
					     &server_info, &logon_info->info3);
		if ( !NT_STATUS_IS_OK(ret) ) {
			DEBUG(1,("make_server_info_info3 failed: %s!\n",
				 nt_errstr(ret)));
			SAFE_FREE(client);
			data_blob_free(&ap_rep);
			data_blob_free(&session_key);
			TALLOC_FREE(mem_ctx);
			return ERROR_NT(nt_status_squash(ret));
		}

	} else {
		ret = make_server_info_pw(&server_info, real_username, pw);

		if ( !NT_STATUS_IS_OK(ret) ) {
			DEBUG(1,("make_server_info_pw failed: %s!\n",
				 nt_errstr(ret)));
			SAFE_FREE(client);
			data_blob_free(&ap_rep);
			data_blob_free(&session_key);
			TALLOC_FREE(mem_ctx);
			return ERROR_NT(nt_status_squash(ret));
		}

	        /* make_server_info_pw does not set the domain. Without this
		 * we end up with the local netbios name in substitutions for
		 * %D. */

		if (server_info->sam_account != NULL) {
			pdb_set_domain(server_info->sam_account, domain, PDB_SET);
		}
	}

	server_info->was_mapped |= username_was_mapped;
	
	/* we need to build the token for the user. make_server_info_guest()
	   already does this */
	
	if ( !server_info->ptok ) {
		ret = create_local_token( server_info );
		if ( !NT_STATUS_IS_OK(ret) ) {
			SAFE_FREE(client);
			data_blob_free(&ap_rep);
			data_blob_free(&session_key);
			TALLOC_FREE( mem_ctx );
			TALLOC_FREE( server_info );
			return ERROR_NT(nt_status_squash(ret));
		}
	}

	/* register_vuid keeps the server info */
	/* register_vuid takes ownership of session_key, no need to free after this.
 	   A better interface would copy it.... */
	sess_vuid = register_vuid(server_info, session_key, nullblob, client);

	SAFE_FREE(client);

	if (sess_vuid == UID_FIELD_INVALID ) {
		ret = NT_STATUS_LOGON_FAILURE;
	} else {
		/* current_user_info is changed on new vuid */
		reload_services( True );

		set_message(outbuf,4,0,True);
		SSVAL(outbuf, smb_vwv3, 0);
			
		if (server_info->guest) {
			SSVAL(outbuf,smb_vwv2,1);
		}
		
		SSVAL(outbuf, smb_uid, sess_vuid);

		sessionsetup_start_signing_engine(server_info, inbuf);
	}

        /* wrap that up in a nice GSS-API wrapping */
	if (NT_STATUS_IS_OK(ret)) {
		ap_rep_wrapped = spnego_gen_krb5_wrap(ap_rep, TOK_ID_KRB_AP_REP);
	} else {
		ap_rep_wrapped = data_blob(NULL, 0);
	}
	response = spnego_gen_auth_response(&ap_rep_wrapped, ret, OID_KERBEROS5_OLD);
	reply_sesssetup_blob(conn, outbuf, response, ret);

	data_blob_free(&ap_rep);
	data_blob_free(&ap_rep_wrapped);
	data_blob_free(&response);
	TALLOC_FREE(mem_ctx);

	return -1; /* already replied */
}
예제 #6
0
static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *inbuf, char *outbuf,
				 AUTH_NTLMSSP_STATE **auth_ntlmssp_state,
				 DATA_BLOB *ntlmssp_blob, NTSTATUS nt_status) 
{
	BOOL ret;
	DATA_BLOB response;
	struct auth_serversupplied_info *server_info = NULL;

	if (NT_STATUS_IS_OK(nt_status)) {
		server_info = (*auth_ntlmssp_state)->server_info;
	} else {
		nt_status = do_map_to_guest(nt_status, 
					    &server_info, 
					    (*auth_ntlmssp_state)->ntlmssp_state->user, 
					    (*auth_ntlmssp_state)->ntlmssp_state->domain);
	}

	if (NT_STATUS_IS_OK(nt_status)) {
		int sess_vuid;
		DATA_BLOB nullblob = data_blob(NULL, 0);
		DATA_BLOB session_key = data_blob((*auth_ntlmssp_state)->ntlmssp_state->session_key.data, (*auth_ntlmssp_state)->ntlmssp_state->session_key.length);

		/* register_vuid keeps the server info */
		sess_vuid = register_vuid(server_info, session_key, nullblob, (*auth_ntlmssp_state)->ntlmssp_state->user);
		(*auth_ntlmssp_state)->server_info = NULL;

		if (sess_vuid == -1) {
			nt_status = NT_STATUS_LOGON_FAILURE;
		} else {
			
			/* current_user_info is changed on new vuid */
			reload_services( True );

			set_message(outbuf,4,0,True);
			SSVAL(outbuf, smb_vwv3, 0);
			
			if (server_info->guest) {
				SSVAL(outbuf,smb_vwv2,1);
			}
			
			SSVAL(outbuf,smb_uid,sess_vuid);

			if (!server_info->guest && !srv_signing_started()) {
				/* We need to start the signing engine
				 * here but a W2K client sends the old
				 * "BSRSPYL " signature instead of the
				 * correct one. Subsequent packets will
				 * be correct.
				 */

				srv_check_sign_mac(inbuf, False);
			}
		}
	}

        response = spnego_gen_auth_response(ntlmssp_blob, nt_status, OID_NTLMSSP);
	ret = reply_sesssetup_blob(conn, outbuf, response, nt_status);
	data_blob_free(&response);

	/* NT_STATUS_MORE_PROCESSING_REQUIRED from our NTLMSSP code tells us,
	   and the other end, that we are not finished yet. */

	if (!ret || !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
		auth_ntlmssp_end(auth_ntlmssp_state);
	}

	return ret;
}
예제 #7
0
/****************************************************************************
reply to a session setup spnego negotiate packet for kerberos
****************************************************************************/
static int reply_spnego_kerberos(connection_struct *conn, 
				 char *inbuf, char *outbuf,
				 int length, int bufsize,
				 DATA_BLOB *secblob)
{
	DATA_BLOB ticket;
	char *client, *p, *domain;
	fstring netbios_domain_name;
	struct passwd *pw;
	char *user;
	int sess_vuid;
	NTSTATUS ret;
	DATA_BLOB auth_data;
	DATA_BLOB ap_rep, ap_rep_wrapped, response;
	auth_serversupplied_info *server_info = NULL;
	DATA_BLOB session_key = data_blob(NULL, 0);
	uint8 tok_id[2];
	DATA_BLOB nullblob = data_blob(NULL, 0);
	fstring real_username;

	ZERO_STRUCT(ticket);
	ZERO_STRUCT(auth_data);
	ZERO_STRUCT(ap_rep);
	ZERO_STRUCT(ap_rep_wrapped);
	ZERO_STRUCT(response);

	if (!spnego_parse_krb5_wrap(*secblob, &ticket, tok_id)) {
		return ERROR_NT(NT_STATUS_LOGON_FAILURE);
	}

	ret = ads_verify_ticket(lp_realm(), &ticket, &client, &auth_data, &ap_rep, &session_key);

	data_blob_free(&ticket);

	if (!NT_STATUS_IS_OK(ret)) {
		DEBUG(1,("Failed to verify incoming ticket!\n"));	
		return ERROR_NT(NT_STATUS_LOGON_FAILURE);
	}

	data_blob_free(&auth_data);

	DEBUG(3,("Ticket name is [%s]\n", client));

	p = strchr_m(client, '@');
	if (!p) {
		DEBUG(3,("Doesn't look like a valid principal\n"));
		data_blob_free(&ap_rep);
		data_blob_free(&session_key);
		SAFE_FREE(client);
		return ERROR_NT(NT_STATUS_LOGON_FAILURE);
	}

	*p = 0;
	if (!strequal(p+1, lp_realm())) {
		DEBUG(3,("Ticket for foreign realm %s@%s\n", client, p+1));
		if (!lp_allow_trusted_domains()) {
			data_blob_free(&ap_rep);
			data_blob_free(&session_key);
			SAFE_FREE(client);
			return ERROR_NT(NT_STATUS_LOGON_FAILURE);
		}
	}

	/* this gives a fully qualified user name (ie. with full realm).
	   that leads to very long usernames, but what else can we do? */

	domain = p+1;

	{
		/* If we have winbind running, we can (and must) shorten the
		   username by using the short netbios name. Otherwise we will
		   have inconsistent user names. With Kerberos, we get the
		   fully qualified realm, with ntlmssp we get the short
		   name. And even w2k3 does use ntlmssp if you for example
		   connect to an ip address. */

		struct winbindd_request wb_request;
		struct winbindd_response wb_response;
		NSS_STATUS wb_result;

		ZERO_STRUCT(wb_request);
		ZERO_STRUCT(wb_response);

		DEBUG(10, ("Mapping [%s] to short name\n", domain));

		fstrcpy(wb_request.domain_name, domain);

		wb_result = winbindd_request(WINBINDD_DOMAIN_INFO,
					     &wb_request, &wb_response);

		if (wb_result == NSS_STATUS_SUCCESS) {

			fstrcpy(netbios_domain_name,
				wb_response.data.domain_info.name);
			domain = netbios_domain_name;

			DEBUG(10, ("Mapped to [%s]\n", domain));
		} else {
			DEBUG(3, ("Could not find short name -- winbind "
				  "not running?\n"));
		}
	}

	asprintf(&user, "%s%c%s", domain, *lp_winbind_separator(), client);
	
	/* lookup the passwd struct, create a new user if necessary */

	map_username( user );

	pw = smb_getpwnam( user, real_username, True );
	
	if (!pw) {
		DEBUG(1,("Username %s is invalid on this system\n",user));
		SAFE_FREE(user);
		SAFE_FREE(client);
		data_blob_free(&ap_rep);
		data_blob_free(&session_key);
		return ERROR_NT(NT_STATUS_LOGON_FAILURE);
	}

	/* setup the string used by %U */
	
	sub_set_smb_name( real_username );
	reload_services(True);
	
	if (!NT_STATUS_IS_OK(ret = make_server_info_pw(&server_info, real_username, pw))) 
	{
		DEBUG(1,("make_server_info_from_pw failed!\n"));
		SAFE_FREE(user);
		SAFE_FREE(client);
		data_blob_free(&ap_rep);
		data_blob_free(&session_key);
		return ERROR_NT(ret);
	}

        /* make_server_info_pw does not set the domain. Without this we end up
	 * with the local netbios name in substitutions for %D. */

        if (server_info->sam_account != NULL) {
                pdb_set_domain(server_info->sam_account, domain, PDB_SET);
        }

	/* register_vuid keeps the server info */
	/* register_vuid takes ownership of session_key, no need to free after this.
 	   A better interface would copy it.... */
	sess_vuid = register_vuid(server_info, session_key, nullblob, client);

	SAFE_FREE(user);
	SAFE_FREE(client);

	if (sess_vuid == -1) {
		ret = NT_STATUS_LOGON_FAILURE;
	} else {
		/* current_user_info is changed on new vuid */
		reload_services( True );

		set_message(outbuf,4,0,True);
		SSVAL(outbuf, smb_vwv3, 0);
			
		if (server_info->guest) {
			SSVAL(outbuf,smb_vwv2,1);
		}
		
		SSVAL(outbuf, smb_uid, sess_vuid);

		if (!server_info->guest && !srv_signing_started()) {
			/* We need to start the signing engine
			 * here but a W2K client sends the old
			 * "BSRSPYL " signature instead of the
			 * correct one. Subsequent packets will
			 * be correct.
			 */
		       	srv_check_sign_mac(inbuf, False);
		}
	}

        /* wrap that up in a nice GSS-API wrapping */
	if (NT_STATUS_IS_OK(ret)) {
		ap_rep_wrapped = spnego_gen_krb5_wrap(ap_rep, TOK_ID_KRB_AP_REP);
	} else {
		ap_rep_wrapped = data_blob(NULL, 0);
	}
	response = spnego_gen_auth_response(&ap_rep_wrapped, ret, OID_KERBEROS5_OLD);
	reply_sesssetup_blob(conn, outbuf, response, ret);

	data_blob_free(&ap_rep);
	data_blob_free(&ap_rep_wrapped);
	data_blob_free(&response);

	return -1; /* already replied */
}