Exemple #1
0
static ADS_STATUS ads_sasl_gensec_unwrap(struct ads_saslwrap *wrap)
{
	struct gensec_security *gensec_security =
		talloc_get_type_abort(wrap->wrap_private_data,
		struct gensec_security);
	NTSTATUS nt_status;
	DATA_BLOB unwrapped, wrapped;
	TALLOC_CTX *frame = talloc_stackframe();

	wrapped = data_blob_const(wrap->in.buf + 4, wrap->in.ofs - 4);

	nt_status = gensec_unwrap(gensec_security, frame, &wrapped, &unwrapped);
	if (!NT_STATUS_IS_OK(nt_status)) {
		TALLOC_FREE(frame);
		return ADS_ERROR_NT(nt_status);
	}

	if (wrapped.length < unwrapped.length) {
		TALLOC_FREE(frame);
		return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
	}

	/* copy the wrapped blob to the right location */
	memcpy(wrap->in.buf + 4, unwrapped.data, unwrapped.length);

	/* set how many bytes must be written to the underlying socket */
	wrap->in.left	= unwrapped.length;
	wrap->in.ofs	= 4;

	TALLOC_FREE(frame);

	return ADS_SUCCESS;
}
Exemple #2
0
static NTSTATUS common_gensec_decrypt_buffer(struct gensec_security *gensec,
					     char *buf)
{
	NTSTATUS status;
	size_t buf_len = smb_len_nbt(buf) + 4; /* Don't forget the 4 length bytes. */
	DATA_BLOB in_buf, out_buf;
	TALLOC_CTX *frame;

	if (buf_len < 8) {
		return NT_STATUS_BUFFER_TOO_SMALL;
	}

	frame = talloc_stackframe();

	in_buf = data_blob_const(buf + 8, buf_len - 8);

	status = gensec_unwrap(gensec, frame, &in_buf, &out_buf);

	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(0,("common_gensec_decrypt_buffer: gensec_unwrap failed. Error %s\n",
			 nt_errstr(status)));
		TALLOC_FREE(frame);
		return status;
	}

	if (out_buf.length > in_buf.length) {
		DEBUG(0,("common_gensec_decrypt_buffer: gensec_unwrap size (%u) too large (%u) !\n",
			(unsigned int)out_buf.length,
			(unsigned int)in_buf.length ));
		TALLOC_FREE(frame);
		return NT_STATUS_INVALID_PARAMETER;
	}

	memcpy(buf + 8, out_buf.data, out_buf.length);

	/* Reset the length and overwrite the header. */
	smb_setlen_nbt(buf, out_buf.length + 4);

	TALLOC_FREE(frame);

	return NT_STATUS_OK;
}
Exemple #3
0
/*
 * These functions are for use in the deprecated
 * gensec_socket code (public because SPNEGO must
 * use them for recursion)
 */
NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security,
					TALLOC_CTX *mem_ctx,
					const DATA_BLOB *in,
					DATA_BLOB *out,
					size_t *len_processed)
{
	if (!gensec_security->ops->unwrap_packets) {
		DATA_BLOB wrapped;
		NTSTATUS nt_status;
		size_t packet_size;
		if (in->length < 4) {
			/* Missing the header we already had! */
			DEBUG(0, ("Asked to unwrap packet of bogus length!  How did we get the short packet?!\n"));
			return NT_STATUS_INVALID_PARAMETER;
		}

		packet_size = RIVAL(in->data, 0);

		wrapped = data_blob_const(in->data + 4, packet_size);

		if (wrapped.length > (in->length - 4)) {
			DEBUG(0, ("Asked to unwrap packed of bogus length %d > %d!  How did we get this?!\n",
				  (int)wrapped.length, (int)(in->length - 4)));
			return NT_STATUS_INTERNAL_ERROR;
		}

		nt_status = gensec_unwrap(gensec_security,
					  mem_ctx,
					  &wrapped, out);
		if (!NT_STATUS_IS_OK(nt_status)) {
			return nt_status;
		}

		*len_processed = packet_size + 4;
		return nt_status;
	}
	return gensec_security->ops->unwrap_packets(gensec_security, mem_ctx, in, out,
						    len_processed);
}
Exemple #4
0
bool kpasswdd_process(struct kdc_server *kdc,
		      TALLOC_CTX *mem_ctx,
		      DATA_BLOB *input,
		      DATA_BLOB *reply,
		      struct tsocket_address *peer_addr,
		      struct tsocket_address *my_addr,
		      int datagram_reply)
{
	bool ret;
	const uint16_t header_len = 6;
	uint16_t len;
	uint16_t ap_req_len;
	uint16_t krb_priv_len;
	uint16_t version;
	NTSTATUS nt_status;
	DATA_BLOB ap_req, krb_priv_req;
	DATA_BLOB krb_priv_rep = data_blob(NULL, 0);
	DATA_BLOB ap_rep = data_blob(NULL, 0);
	DATA_BLOB kpasswd_req, kpasswd_rep;
	struct cli_credentials *server_credentials;
	struct gensec_security *gensec_security;
	TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);

	char *keytab_name;

	if (!tmp_ctx) {
		return false;
	}

	/* Be parinoid.  We need to ensure we don't just let the
	 * caller lead us into a buffer overflow */
	if (input->length <= header_len) {
		talloc_free(tmp_ctx);
		return false;
	}

	len = RSVAL(input->data, 0);
	if (input->length != len) {
		talloc_free(tmp_ctx);
		return false;
	}

	/* There are two different versions of this protocol so far,
	 * plus others in the standards pipe.  Fortunetly they all
	 * take a very similar framing */
	version = RSVAL(input->data, 2);
	ap_req_len = RSVAL(input->data, 4);
	if ((ap_req_len >= len) || (ap_req_len + header_len) >= len) {
		talloc_free(tmp_ctx);
		return false;
	}

	krb_priv_len = len - ap_req_len;
	ap_req = data_blob_const(&input->data[header_len], ap_req_len);
	krb_priv_req = data_blob_const(&input->data[header_len + ap_req_len], krb_priv_len);

	server_credentials = cli_credentials_init(tmp_ctx);
	if (!server_credentials) {
		DEBUG(1, ("Failed to init server credentials\n"));
		return false;
	}

	/* We want the credentials subsystem to use the krb5 context
	 * we already have, rather than a new context */
	cli_credentials_set_krb5_context(server_credentials, kdc->smb_krb5_context);
	cli_credentials_set_conf(server_credentials, kdc->task->lp_ctx);

	keytab_name = talloc_asprintf(server_credentials, "HDB:samba4&%p", kdc->base_ctx);

	cli_credentials_set_username(server_credentials, "kadmin/changepw", CRED_SPECIFIED);
	ret = cli_credentials_set_keytab_name(server_credentials, kdc->task->event_ctx, kdc->task->lp_ctx, keytab_name, CRED_SPECIFIED);
	if (ret != 0) {
		ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
						       KRB5_KPASSWD_HARDERROR,
						       talloc_asprintf(mem_ctx,
								       "Failed to obtain server credentials for kadmin/changepw: %s\n",
								       nt_errstr(nt_status)),
						       &krb_priv_rep);
		ap_rep.length = 0;
		if (ret) {
			goto reply;
		}
		talloc_free(tmp_ctx);
		return ret;
	}

	/* We don't strictly need to call this wrapper, and could call
	 * gensec_server_start directly, as we have no need for NTLM
	 * and we have a PAC, but this ensures that the wrapper can be
	 * safely extended for other helpful things in future */
	nt_status = samba_server_gensec_start(tmp_ctx, kdc->task->event_ctx,
					      kdc->task->msg_ctx,
					      kdc->task->lp_ctx,
					      server_credentials,
					      "kpasswd",
					      &gensec_security);
	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(tmp_ctx);
		return false;
	}

	/* The kerberos PRIV packets include these addresses.  MIT
	 * clients check that they are present */
#if 0
	/* Skip this part for now, it breaks with a NetAPP filer and
	 * in any case where the client address is behind NAT.  If
	 * older MIT clients need this, we might have to insert more
	 * complex code */

	nt_status = gensec_set_local_address(gensec_security, peer_addr);
	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(tmp_ctx);
		return false;
	}
#endif

	nt_status = gensec_set_local_address(gensec_security, my_addr);
	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(tmp_ctx);
		return false;
	}

	/* We want the GENSEC wrap calls to generate PRIV tokens */
	gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);

	nt_status = gensec_start_mech_by_name(gensec_security, "krb5");
	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(tmp_ctx);
		return false;
	}

	/* Accept the AP-REQ and generate teh AP-REP we need for the reply */
	nt_status = gensec_update(gensec_security, tmp_ctx, ap_req, &ap_rep);
	if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {

		ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
						       KRB5_KPASSWD_HARDERROR,
						       talloc_asprintf(mem_ctx,
								       "gensec_update failed: %s",
								       nt_errstr(nt_status)),
						       &krb_priv_rep);
		ap_rep.length = 0;
		if (ret) {
			goto reply;
		}
		talloc_free(tmp_ctx);
		return ret;
	}

	/* Extract the data from the KRB-PRIV half of the message */
	nt_status = gensec_unwrap(gensec_security, tmp_ctx, &krb_priv_req, &kpasswd_req);
	if (!NT_STATUS_IS_OK(nt_status)) {
		ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
						       KRB5_KPASSWD_HARDERROR,
						       talloc_asprintf(mem_ctx,
								       "gensec_unwrap failed: %s",
								       nt_errstr(nt_status)),
						       &krb_priv_rep);
		ap_rep.length = 0;
		if (ret) {
			goto reply;
		}
		talloc_free(tmp_ctx);
		return ret;
	}

	/* Figure out something to do with it (probably changing a password...) */
	ret = kpasswd_process_request(kdc, tmp_ctx,
				      gensec_security,
				      version,
				      &kpasswd_req, &kpasswd_rep);
	if (!ret) {
		/* Argh! */
		return false;
	}

	/* And wrap up the reply: This ensures that the error message
	 * or success can be verified by the client */
	nt_status = gensec_wrap(gensec_security, tmp_ctx,
				&kpasswd_rep, &krb_priv_rep);
	if (!NT_STATUS_IS_OK(nt_status)) {
		ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
						       KRB5_KPASSWD_HARDERROR,
						       talloc_asprintf(mem_ctx,
								       "gensec_wrap failed: %s",
								       nt_errstr(nt_status)),
						       &krb_priv_rep);
		ap_rep.length = 0;
		if (ret) {
			goto reply;
		}
		talloc_free(tmp_ctx);
		return ret;
	}

reply:
	*reply = data_blob_talloc(mem_ctx, NULL, krb_priv_rep.length + ap_rep.length + header_len);
	if (!reply->data) {
		return false;
	}

	RSSVAL(reply->data, 0, reply->length);
	RSSVAL(reply->data, 2, 1); /* This is a version 1 reply, MS change/set or otherwise */
	RSSVAL(reply->data, 4, ap_rep.length);
	memcpy(reply->data + header_len,
	       ap_rep.data,
	       ap_rep.length);
	memcpy(reply->data + header_len + ap_rep.length,
	       krb_priv_rep.data,
	       krb_priv_rep.length);

	talloc_free(tmp_ctx);
	return ret;
}