summaryrefslogtreecommitdiff
path: root/source4/librpc/rpc/dcerpc_schannel.c
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2004-06-29 09:40:10 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:56:49 -0500
commitdc9f55dbec5f892b39d924d5fd033b5eec1e14e4 (patch)
treedd6932fb92fef55787b9a20671dd8c8f71ef3b6d /source4/librpc/rpc/dcerpc_schannel.c
parenta440e8f3b518d25c5fb9c9fa896cb7704974f346 (diff)
downloadsamba-dc9f55dbec5f892b39d924d5fd033b5eec1e14e4.tar.gz
samba-dc9f55dbec5f892b39d924d5fd033b5eec1e14e4.tar.bz2
samba-dc9f55dbec5f892b39d924d5fd033b5eec1e14e4.zip
r1294: A nice, large, commit...
This implements gensec for Samba's server side, and brings gensec up to the standards of a full subsystem. This means that use of the subsystem is by gensec_* functions, not function pointers in structures (this is internal). This causes changes in all the existing gensec users. Our RPC server no longer contains it's own generalised security scheme, and now calls gensec directly. Gensec has also taken over the role of auth/auth_ntlmssp.c An important part of gensec, is the output of the 'session_info' struct. This is now reference counted, so that we can correctly free it when a pipe is closed, no matter if it was inherited, or created by per-pipe authentication. The schannel code is reworked, to be in the same file for client and server. ntlm_auth is reworked to use gensec. The major problem with this code is the way it relies on subsystem auto-initialisation. The primary reason for this commit now.is to allow these problems to be looked at, and fixed. There are problems with the new code: - I've tested it with smbtorture, but currently don't have VMware and valgrind working (this I'll fix soon). - The SPNEGO code is client-only at this point. - We still do not do kerberos. Andrew Bartlett (This used to be commit 07fd885fd488fd1051eacc905a2d4962f8a018ec)
Diffstat (limited to 'source4/librpc/rpc/dcerpc_schannel.c')
-rw-r--r--source4/librpc/rpc/dcerpc_schannel.c378
1 files changed, 294 insertions, 84 deletions
diff --git a/source4/librpc/rpc/dcerpc_schannel.c b/source4/librpc/rpc/dcerpc_schannel.c
index b43dd0788a..ffe60b1bae 100644
--- a/source4/librpc/rpc/dcerpc_schannel.c
+++ b/source4/librpc/rpc/dcerpc_schannel.c
@@ -22,21 +22,24 @@
#include "includes.h"
-#define DCERPC_SCHANNEL_STATE_START 0
-#define DCERPC_SCHANNEL_STATE_UPDATE_1 1
+enum schannel_position {
+ DCERPC_SCHANNEL_STATE_START = 0,
+ DCERPC_SCHANNEL_STATE_UPDATE_1
+};
struct dcerpc_schannel_state {
TALLOC_CTX *mem_ctx;
- uint8_t state;
- struct schannel_bind bind_schannel;
+ enum schannel_position state;
struct schannel_state *schannel_state;
+ struct creds_CredentialState creds;
+ char *account_name;
};
static NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p,
- const char *domain,
- const char *username,
- const char *password,
- int chan_type,
+ const char *domain,
+ const char *username,
+ const char *password,
+ int chan_type,
uint8_t new_session_key[16]);
/*
@@ -45,39 +48,39 @@ static NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p,
These will become static again, when we get dynamic registration, and
decrpc_schannel_security_ops come back here.
*/
-static NTSTATUS dcerpc_schannel_unseal(struct gensec_security *gensec_security,
- TALLOC_CTX *mem_ctx,
- uint8_t *data, size_t length, DATA_BLOB *sig)
+static NTSTATUS dcerpc_schannel_unseal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length, DATA_BLOB *sig)
{
struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
-
+
return schannel_unseal_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig);
}
-static NTSTATUS dcerpc_schannel_check_sig(struct gensec_security *gensec_security,
- TALLOC_CTX *mem_ctx,
- const uint8_t *data, size_t length,
- const DATA_BLOB *sig)
+static NTSTATUS dcerpc_schannel_check_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ const DATA_BLOB *sig)
{
struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
return schannel_check_packet(dce_schan_state->schannel_state, data, length, sig);
}
-static NTSTATUS dcerpc_schannel_seal(struct gensec_security *gensec_security,
- TALLOC_CTX *mem_ctx,
- uint8_t *data, size_t length,
- DATA_BLOB *sig)
+static NTSTATUS dcerpc_schannel_seal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ DATA_BLOB *sig)
{
struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
return schannel_seal_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig);
}
-static NTSTATUS dcerpc_schannel_sign(struct gensec_security *gensec_security,
- TALLOC_CTX *mem_ctx,
- const uint8_t *data, size_t length,
- DATA_BLOB *sig)
+static NTSTATUS dcerpc_schannel_sign_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ DATA_BLOB *sig)
{
struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
@@ -85,47 +88,233 @@ static NTSTATUS dcerpc_schannel_sign(struct gensec_security *gensec_security,
}
static NTSTATUS dcerpc_schannel_session_key(struct gensec_security *gensec_security,
- DATA_BLOB *session_key)
+ DATA_BLOB *session_key)
{
return NT_STATUS_NOT_IMPLEMENTED;
}
static NTSTATUS dcerpc_schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
- const DATA_BLOB in, DATA_BLOB *out)
+ const DATA_BLOB in, DATA_BLOB *out)
{
struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
NTSTATUS status;
struct schannel_bind bind_schannel;
+ struct schannel_bind_ack bind_schannel_ack;
+ const char *account_name;
+
+ switch (gensec_security->gensec_role) {
+ case GENSEC_CLIENT:
+ if (dce_schan_state->state != DCERPC_SCHANNEL_STATE_START) {
+ /* we could parse the bind ack, but we don't know what it is yet */
+ return NT_STATUS_OK;
+ }
+
+ bind_schannel.unknown1 = 0;
+#if 0
+ /* to support this we'd need to have access to the full domain name */
+ bind_schannel.bind_type = 23;
+ bind_schannel.u.info23.domain = gensec_security->user.domain;
+ bind_schannel.u.info23.account_name = gensec_security->user.name;
+ bind_schannel.u.info23.dnsdomain = str_format_nbt_domain(out_mem_ctx, fulldomainname);
+ bind_schannel.u.info23.workstation = str_format_nbt_domain(out_mem_ctx, gensec_security->user.name);
+#else
+ bind_schannel.bind_type = 3;
+ bind_schannel.u.info3.domain = gensec_security->user.domain;
+ bind_schannel.u.info3.account_name = gensec_security->user.name;
+#endif
+
+ status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel,
+ (ndr_push_flags_fn_t)ndr_push_schannel_bind);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("Could not create schannel bind: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ dce_schan_state->state = DCERPC_SCHANNEL_STATE_UPDATE_1;
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+ case GENSEC_SERVER:
+
+ if (dce_schan_state->state != DCERPC_SCHANNEL_STATE_START) {
+ /* no third leg on this protocol */
+ return NT_STATUS_OK;
+ }
+
+ /* parse the schannel startup blob */
+ status = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel,
+ (ndr_pull_flags_fn_t)ndr_pull_schannel_bind);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (bind_schannel.bind_type == 23) {
+ account_name = bind_schannel.u.info23.account_name;
+ } else {
+ account_name = bind_schannel.u.info3.account_name;
+ }
+
+ /* pull the session key for this client */
+ status = schannel_fetch_session_key(out_mem_ctx, account_name, &dce_schan_state->creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("Could not find session key for attempted schannel connection on %s: %s\n",
+ account_name, nt_errstr(status)));
+ return status;
+ }
+
+ dce_schan_state->account_name = talloc_strdup(dce_schan_state->mem_ctx, account_name);
+
+ /* start up the schannel server code */
+ status = schannel_start(&dce_schan_state->schannel_state,
+ dce_schan_state->creds.session_key, False);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("Could not initialise schannel state for account %s: %s\n",
+ account_name, nt_errstr(status)));
+ return status;
+ }
+
+ bind_schannel_ack.unknown1 = 1;
+ bind_schannel_ack.unknown2 = 0;
+ bind_schannel_ack.unknown3 = 0x6c0000;
+
+ status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack,
+ (ndr_push_flags_fn_t)ndr_push_schannel_bind_ack);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("Could not return schannel bind ack for account %s: %s\n",
+ account_name, nt_errstr(status)));
+ return status;
+ }
+
+ dce_schan_state->state = DCERPC_SCHANNEL_STATE_UPDATE_1;
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+ }
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+/**
+ * Return the credentials of a logged on user, including session keys
+ * etc.
+ *
+ * Only valid after a successful authentication
+ *
+ * May only be called once per authentication.
+ *
+ */
+
+NTSTATUS dcerpc_schannel_session_info(struct gensec_security *gensec_security,
+ struct auth_session_info **session_info)
+{
+ struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
+ TALLOC_CTX *mem_ctx;
+ mem_ctx = talloc_init("dcerpc_schannel_start");
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
- if (dce_schan_state->state != DCERPC_SCHANNEL_STATE_START) {
- return NT_STATUS_OK;
+ (*session_info) = talloc_p(mem_ctx, struct auth_session_info);
+ if (*session_info == NULL) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
}
- dce_schan_state->state = DCERPC_SCHANNEL_STATE_UPDATE_1;
+ ZERO_STRUCTP(*session_info);
+ (*session_info)->mem_ctx = mem_ctx;
+ (*session_info)->refcount = 1;
+
+ (*session_info)->workstation = talloc_strdup(mem_ctx, dce_schan_state->account_name);
+ if ((*session_info)->workstation == NULL) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+
+/**
+ * Return the struct creds_CredentialState.
+ *
+ * Make sure not to call this unless gensec is using schannel...
+ */
+
+NTSTATUS dcerpc_schannel_creds(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ struct creds_CredentialState **creds)
+{
+ struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
- bind_schannel.unknown1 = 0;
-#if 0
- /* to support this we'd need to have access to the full domain name */
- bind_schannel.bind_type = 23;
- bind_schannel.u.info23.domain = gensec_security->user.domain;
- bind_schannel.u.info23.account_name = gensec_security->user.name;
- bind_schannel.u.info23.dnsdomain = str_format_nbt_domain(dce_schan_state->mem_ctx, fulldomainname);
- bind_schannel.u.info23.workstation = str_format_nbt_domain(dce_schan_state->mem_ctx, gensec_security->user.name);
-#else
- bind_schannel.bind_type = 3;
- bind_schannel.u.info3.domain = gensec_security->user.domain;
- bind_schannel.u.info3.account_name = gensec_security->user.name;
-#endif
+ *creds = talloc_p(mem_ctx, struct creds_CredentialState);
+ if (*creds) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ **creds = dce_schan_state->creds;
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS dcerpc_schannel_start(struct gensec_security *gensec_security)
+{
+ struct dcerpc_schannel_state *dce_schan_state;
+ TALLOC_CTX *mem_ctx;
+ mem_ctx = talloc_init("dcerpc_schannel_start");
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dce_schan_state = talloc_p(mem_ctx, struct dcerpc_schannel_state);
+ if (!dce_schan_state) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dce_schan_state->mem_ctx = mem_ctx;
+ dce_schan_state->state = DCERPC_SCHANNEL_STATE_START;
+
+
+ gensec_security->private_data = dce_schan_state;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcerpc_schannel_server_start(struct gensec_security *gensec_security)
+{
+ NTSTATUS status;
+
+ status = dcerpc_schannel_start(gensec_security);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcerpc_schannel_client_start(struct gensec_security *gensec_security)
+{
+ NTSTATUS status;
+ struct dcerpc_schannel_state *dce_schan_state;
+
+ status = dcerpc_schannel_start(gensec_security);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ dce_schan_state = gensec_security->private_data;
- status = ndr_push_struct_blob(out, dce_schan_state->mem_ctx, &bind_schannel,
- (ndr_push_flags_fn_t)ndr_push_schannel_bind);
+ status = schannel_start(&dce_schan_state->schannel_state,
+ gensec_security->user.schan_session_key,
+ True);
if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start schannel client\n"));
return status;
}
- return NT_STATUS_MORE_PROCESSING_REQUIRED;
+ dump_data_pw("session key:\n", dce_schan_state->schannel_state->session_key, 16);
+ return NT_STATUS_OK;
}
+/*
+ end crypto state
+*/
static void dcerpc_schannel_end(struct gensec_security *gensec_security)
{
struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
@@ -138,18 +327,6 @@ static void dcerpc_schannel_end(struct gensec_security *gensec_security)
}
-static const struct gensec_security_ops gensec_dcerpc_schannel_security_ops = {
- .name = "dcerpc_schannel",
- .auth_type = DCERPC_AUTH_TYPE_SCHANNEL,
- .update = dcerpc_schannel_update,
- .seal = dcerpc_schannel_seal,
- .sign = dcerpc_schannel_sign,
- .check_sig = dcerpc_schannel_check_sig,
- .unseal = dcerpc_schannel_unseal,
- .session_key = dcerpc_schannel_session_key,
- .end = dcerpc_schannel_end
-};
-
/*
get a schannel key using a netlogon challenge on a secondary pipe
*/
@@ -251,11 +428,13 @@ NTSTATUS dcerpc_bind_auth_schannel(struct dcerpc_pipe *p,
const char *password)
{
NTSTATUS status;
- struct dcerpc_schannel_state *dce_schan_state;
- TALLOC_CTX *mem_ctx;
- uint8_t session_key[16];
int chan_type = 0;
+ status = gensec_client_start(&p->security_state.generic_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
if (p->flags & DCERPC_SCHANNEL_BDC) {
chan_type = SEC_CHAN_BDC;
} else if (p->flags & DCERPC_SCHANNEL_WORKSTATION) {
@@ -265,44 +444,75 @@ NTSTATUS dcerpc_bind_auth_schannel(struct dcerpc_pipe *p,
}
status = dcerpc_schannel_key(p, domain,
- username,
- password,
- chan_type, session_key);
+ username,
+ password,
+ chan_type,
+ p->security_state.generic_state->user.schan_session_key);
if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to fetch schannel session key: %s\n", nt_errstr(status)));
+ gensec_end(&p->security_state.generic_state);
return status;
}
-
- mem_ctx = talloc_init("dcerpc_schannel_start");
- if (!mem_ctx) {
- return NT_STATUS_NO_MEMORY;
+
+ status = gensec_set_username(p->security_state.generic_state, username);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set schannel username to %s: %s\n", username, nt_errstr(status)));
+ gensec_end(&p->security_state.generic_state);
+ return status;
+ }
+
+ status = gensec_set_domain(p->security_state.generic_state, domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set schannel domain to %s: %s\n", domain, nt_errstr(status)));
+ gensec_end(&p->security_state.generic_state);
+ return status;
}
+
+ status = gensec_start_mech_by_authtype(p->security_state.generic_state, DCERPC_AUTH_TYPE_SCHANNEL);
- dce_schan_state = talloc_p(mem_ctx, struct dcerpc_schannel_state);
- if (!dce_schan_state) {
- talloc_destroy(mem_ctx);
- return NT_STATUS_NO_MEMORY;
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start SCHANNEL GENSEC backend: %s\n", nt_errstr(status)));
+ gensec_end(&p->security_state.generic_state);
+ return status;
}
- dce_schan_state->mem_ctx = mem_ctx;
+ status = dcerpc_bind_auth(p, DCERPC_AUTH_TYPE_SCHANNEL,
+ uuid, version);
- status = schannel_start(&dce_schan_state->schannel_state, session_key, True);
if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to bind to pipe with SCHANNEL: %s\n", nt_errstr(status)));
+ gensec_end(&p->security_state.generic_state);
return status;
}
- dce_schan_state->state = DCERPC_SCHANNEL_STATE_START;
-
- p->security_state.generic_state.user.domain = domain;
- p->security_state.generic_state.user.name = username;
- p->security_state.generic_state.user.password = password;
+ return NT_STATUS_OK;
+}
- p->security_state.generic_state.ops = &gensec_dcerpc_schannel_security_ops;
- p->security_state.generic_state.private_data = dce_schan_state;
- dump_data_pw("session key:\n", dce_schan_state->schannel_state->session_key, 16);
+static const struct gensec_security_ops gensec_dcerpc_schannel_security_ops = {
+ .name = "dcerpc_schannel",
+ .auth_type = DCERPC_AUTH_TYPE_SCHANNEL,
+ .client_start = dcerpc_schannel_client_start,
+ .server_start = dcerpc_schannel_server_start,
+ .update = dcerpc_schannel_update,
+ .seal_packet = dcerpc_schannel_seal_packet,
+ .sign_packet = dcerpc_schannel_sign_packet,
+ .check_packet = dcerpc_schannel_check_packet,
+ .unseal_packet = dcerpc_schannel_unseal_packet,
+ .session_key = dcerpc_schannel_session_key,
+ .session_info = dcerpc_schannel_session_info,
+ .end = dcerpc_schannel_end
+};
- status = dcerpc_bind_auth(p, DCERPC_AUTH_TYPE_SCHANNEL,
- uuid, version);
+NTSTATUS gensec_dcerpc_schannel_init(void)
+{
+ NTSTATUS ret;
+ ret = register_backend("gensec", &gensec_dcerpc_schannel_security_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register '%s' gensec backend!\n",
+ gensec_dcerpc_schannel_security_ops.name));
+ return ret;
+ }
- return status;
+ return ret;
}