diff options
author | Stefan Metzmacher <metze@samba.org> | 2004-06-07 12:30:22 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 12:56:36 -0500 |
commit | c0871cb0c13599039f4e8243bd8d60d472653930 (patch) | |
tree | d8f1908ed723d9d78060aa4bfb105795c7421918 /source4/librpc/rpc | |
parent | 6564fd402d500b1e24f76f63e4335b38ef1164db (diff) | |
download | samba-c0871cb0c13599039f4e8243bd8d60d472653930.tar.gz samba-c0871cb0c13599039f4e8243bd8d60d472653930.tar.bz2 samba-c0871cb0c13599039f4e8243bd8d60d472653930.zip |
r1068: make the dcerpc client side auth/crypto code much more generic
metze
(This used to be commit 1706ff88a72c6578a109c2cf24f2f009812c3892)
Diffstat (limited to 'source4/librpc/rpc')
-rw-r--r-- | source4/librpc/rpc/dcerpc.c | 52 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc.h | 46 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_auth.c | 99 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_ntlm.c | 178 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_schannel.c | 273 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_util.c | 4 |
6 files changed, 370 insertions, 282 deletions
diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c index 5d317794e1..e80e168c89 100644 --- a/source4/librpc/rpc/dcerpc.c +++ b/source4/librpc/rpc/dcerpc.c @@ -41,8 +41,10 @@ struct dcerpc_pipe *dcerpc_pipe_init(void) p->reference_count = 0; p->mem_ctx = mem_ctx; p->call_id = 1; - p->auth_info = NULL; - p->security_state = NULL; + p->security_state.auth_info = NULL; + ZERO_STRUCT(p->security_state.user); + p->security_state.private_data = NULL; + p->security_state.ops = NULL; p->flags = 0; p->srv_max_xmit_frag = 0; p->srv_max_recv_frag = 0; @@ -56,8 +58,8 @@ void dcerpc_pipe_close(struct dcerpc_pipe *p) if (!p) return; p->reference_count--; if (p->reference_count <= 0) { - if (p->security_state) { - p->security_state->security_end(p->security_state); + if (p->security_state.ops) { + p->security_state.ops->end(&p->security_state); } p->transport.shutdown_pipe(p); talloc_destroy(p->mem_ctx); @@ -128,7 +130,7 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p, DATA_BLOB auth_blob; /* non-signed packets are simpler */ - if (!p->auth_info || !p->security_state) { + if (!p->security_state.auth_info || !p->security_state.ops) { return dcerpc_pull(blob, mem_ctx, pkt); } @@ -180,9 +182,9 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p, /* check signature or unseal the packet */ - switch (p->auth_info->auth_level) { + switch (p->security_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: - status = p->security_state->unseal_packet(p->security_state, + status = p->security_state.ops->unseal(&p->security_state, mem_ctx, pkt->u.response.stub_and_verifier.data, pkt->u.response.stub_and_verifier.length, @@ -190,7 +192,7 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p, break; case DCERPC_AUTH_LEVEL_INTEGRITY: - status = p->security_state->check_packet(p->security_state, + status = p->security_state.ops->check_sig(&p->security_state, mem_ctx, pkt->u.response.stub_and_verifier.data, pkt->u.response.stub_and_verifier.length, @@ -226,8 +228,8 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p, struct ndr_push *ndr; /* non-signed packets are simpler */ - if (!p->auth_info || !p->security_state) { - return dcerpc_push_auth(blob, mem_ctx, pkt, p->auth_info); + if (!p->security_state.auth_info || !p->security_state.ops) { + return dcerpc_push_auth(blob, mem_ctx, pkt, p->security_state.auth_info); } ndr = ndr_push_init_ctx(mem_ctx); @@ -245,29 +247,29 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p, } /* pad to 8 byte multiple */ - p->auth_info->auth_pad_length = NDR_ALIGN(ndr, 8); - ndr_push_zero(ndr, p->auth_info->auth_pad_length); + p->security_state.auth_info->auth_pad_length = NDR_ALIGN(ndr, 8); + ndr_push_zero(ndr, p->security_state.auth_info->auth_pad_length); /* sign or seal the packet */ - switch (p->auth_info->auth_level) { + switch (p->security_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: - status = p->security_state->seal_packet(p->security_state, + status = p->security_state.ops->seal(&p->security_state, mem_ctx, ndr->data + DCERPC_REQUEST_LENGTH, ndr->offset - DCERPC_REQUEST_LENGTH, - &p->auth_info->credentials); + &p->security_state.auth_info->credentials); break; case DCERPC_AUTH_LEVEL_INTEGRITY: - status = p->security_state->sign_packet(p->security_state, + status = p->security_state.ops->sign(&p->security_state, mem_ctx, ndr->data + DCERPC_REQUEST_LENGTH, ndr->offset - DCERPC_REQUEST_LENGTH, - &p->auth_info->credentials); + &p->security_state.auth_info->credentials); break; case DCERPC_AUTH_LEVEL_NONE: - p->auth_info->credentials = data_blob(NULL, 0); + p->security_state.auth_info->credentials = data_blob(NULL, 0); break; default: @@ -280,7 +282,7 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p, } /* add the auth verifier */ - status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, p->auth_info); + status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, p->security_state.auth_info); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -292,9 +294,9 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p, in these earlier as we don't know the signature length (it could be variable length) */ dcerpc_set_frag_length(blob, blob->length); - dcerpc_set_auth_length(blob, p->auth_info->credentials.length); + dcerpc_set_auth_length(blob, p->security_state.auth_info->credentials.length); - data_blob_free(&p->auth_info->credentials); + data_blob_free(&p->security_state.auth_info->credentials); return NT_STATUS_OK; } @@ -357,7 +359,7 @@ NTSTATUS dcerpc_bind(struct dcerpc_pipe *p, pkt.u.bind.auth_info = data_blob(NULL, 0); /* construct the NDR form of the packet */ - status = dcerpc_push_auth(&blob, mem_ctx, &pkt, p->auth_info); + status = dcerpc_push_auth(&blob, mem_ctx, &pkt, p->security_state.auth_info); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -386,10 +388,10 @@ NTSTATUS dcerpc_bind(struct dcerpc_pipe *p, } /* the bind_ack might contain a reply set of credentials */ - if (p->auth_info && pkt.u.bind_ack.auth_info.length) { + if (p->security_state.auth_info && pkt.u.bind_ack.auth_info.length) { status = ndr_pull_struct_blob(&pkt.u.bind_ack.auth_info, mem_ctx, - p->auth_info, + p->security_state.auth_info, (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth); } @@ -416,7 +418,7 @@ NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p, pkt.u.auth.auth_info = data_blob(NULL, 0); /* construct the NDR form of the packet */ - status = dcerpc_push_auth(&blob, mem_ctx, &pkt, p->auth_info); + status = dcerpc_push_auth(&blob, mem_ctx, &pkt, p->security_state.auth_info); if (!NT_STATUS_IS_OK(status)) { return status; } diff --git a/source4/librpc/rpc/dcerpc.h b/source4/librpc/rpc/dcerpc.h index 7bd6f98118..7694a9c230 100644 --- a/source4/librpc/rpc/dcerpc.h +++ b/source4/librpc/rpc/dcerpc.h @@ -25,24 +25,39 @@ enum dcerpc_transport_t {NCACN_NP, NCACN_IP_TCP}; /* this defines a generic security context for signed/sealed dcerpc pipes. */ -struct dcerpc_security { - void *private; - NTSTATUS (*unseal_packet)(struct dcerpc_security *, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, DATA_BLOB *sig); - NTSTATUS (*check_packet)(struct dcerpc_security *, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, const DATA_BLOB *sig); - NTSTATUS (*seal_packet)(struct dcerpc_security *, - TALLOC_CTX *mem_ctx, +struct dcerpc_security; +struct dcerpc_pipe; + +struct dcerpc_user { + const char *domain; + const char *name; + const char *password; +}; + +struct dcesrv_security_ops { + const char *name; + uint8 auth_type; + NTSTATUS (*start)(struct dcerpc_pipe *dce_pipe, struct dcerpc_security *dce_sec); + NTSTATUS (*update)(struct dcerpc_security *dce_sec, TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out); + NTSTATUS (*seal)(struct dcerpc_security *dce_sec, TALLOC_CTX *sig_mem_ctx, uint8_t *data, size_t length, DATA_BLOB *sig); - NTSTATUS (*sign_packet)(struct dcerpc_security *, - TALLOC_CTX *mem_ctx, + NTSTATUS (*sign)(struct dcerpc_security *dce_sec, TALLOC_CTX *sig_mem_ctx, const uint8_t *data, size_t length, DATA_BLOB *sig); + NTSTATUS (*check_sig)(struct dcerpc_security *dce_sec, TALLOC_CTX *sig_mem_ctx, + const uint8_t *data, size_t length, const DATA_BLOB *sig); + NTSTATUS (*unseal)(struct dcerpc_security *dce_sec, TALLOC_CTX *sig_mem_ctx, + uint8_t *data, size_t length, DATA_BLOB *sig); NTSTATUS (*session_key)(struct dcerpc_security *, DATA_BLOB *session_key); - void (*security_end)(struct dcerpc_security *); + void (*end)(struct dcerpc_security *dce_sec); +}; + +struct dcerpc_security { + struct dcerpc_auth *auth_info; + struct dcerpc_user user; + void *private_data; + const struct dcesrv_security_ops *ops; }; - struct dcerpc_pipe { TALLOC_CTX *mem_ctx; @@ -51,8 +66,7 @@ struct dcerpc_pipe { uint32_t srv_max_xmit_frag; uint32_t srv_max_recv_frag; uint_t flags; - struct dcerpc_security *security_state; - struct dcerpc_auth *auth_info; + struct dcerpc_security security_state; const char *binding_string; struct dcerpc_transport { diff --git a/source4/librpc/rpc/dcerpc_auth.c b/source4/librpc/rpc/dcerpc_auth.c index 3faf0603ce..021249847a 100644 --- a/source4/librpc/rpc/dcerpc_auth.c +++ b/source4/librpc/rpc/dcerpc_auth.c @@ -41,3 +41,102 @@ NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p, return status; } + +const struct dcesrv_security_ops *dcerpc_security_by_authtype(uint8_t auth_type) +{ + switch (auth_type) { + case DCERPC_AUTH_TYPE_SCHANNEL: + return dcerpc_schannel_security_get_ops(); + + case DCERPC_AUTH_TYPE_NTLMSSP: + return dcerpc_ntlmssp_security_get_ops(); + } + + return NULL; +} + +NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p, uint8_t auth_type, + const char *uuid, uint_t version, + const char *domain, + const char *username, + const char *password) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx; + DATA_BLOB credentials; + + mem_ctx = talloc_init("dcerpc_bind_auth"); + if (!mem_ctx) { + return NT_STATUS_NO_MEMORY; + } + + p->security_state.ops = dcerpc_security_by_authtype(auth_type); + if (!p->security_state.ops) { + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + p->security_state.user.domain = domain; + p->security_state.user.name = username; + p->security_state.user.password = password; + + status = p->security_state.ops->start(p, &p->security_state); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + p->security_state.auth_info = talloc(p->mem_ctx, sizeof(*p->security_state.auth_info)); + if (!p->security_state.auth_info) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + p->security_state.auth_info->auth_type = auth_type; + p->security_state.auth_info->auth_pad_length = 0; + p->security_state.auth_info->auth_reserved = 0; + p->security_state.auth_info->auth_context_id = random(); + p->security_state.auth_info->credentials = data_blob(NULL, 0); + + if (p->flags & DCERPC_SEAL) { + p->security_state.auth_info->auth_level = DCERPC_AUTH_LEVEL_PRIVACY; + } else if (p->flags & DCERPC_SIGN) { + p->security_state.auth_info->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY; + } else { + p->security_state.auth_info->auth_level = DCERPC_AUTH_LEVEL_NONE; + } + + status = p->security_state.ops->update(&p->security_state, mem_ctx, + p->security_state.auth_info->credentials, + &credentials); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + goto done; + } + + p->security_state.auth_info->credentials = credentials; + + status = dcerpc_bind_byuuid(p, mem_ctx, uuid, version); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = p->security_state.ops->update(&p->security_state, mem_ctx, + p->security_state.auth_info->credentials, + &credentials); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + goto done; + } + + p->security_state.auth_info->credentials = credentials; + + status = dcerpc_auth3(p, mem_ctx); +done: + talloc_destroy(mem_ctx); + + if (!NT_STATUS_IS_OK(status)) { + ZERO_STRUCT(p->security_state); + } + + return status; +} diff --git a/source4/librpc/rpc/dcerpc_ntlm.c b/source4/librpc/rpc/dcerpc_ntlm.c index db707be1b5..2cfecd939f 100644 --- a/source4/librpc/rpc/dcerpc_ntlm.c +++ b/source4/librpc/rpc/dcerpc_ntlm.c @@ -25,174 +25,138 @@ /* wrappers for the ntlmssp_*() functions */ -static NTSTATUS ntlm_unseal_packet(struct dcerpc_security *dcerpc_security, +static NTSTATUS dcerpc_ntlmssp_unseal(struct dcerpc_security *dcerpc_security, TALLOC_CTX *mem_ctx, uint8_t *data, size_t length, DATA_BLOB *sig) { - struct ntlmssp_state *ntlmssp_state = dcerpc_security->private; + struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data; + return ntlmssp_unseal_packet(ntlmssp_state, mem_ctx, data, length, sig); } -static NTSTATUS ntlm_check_packet(struct dcerpc_security *dcerpc_security, +static NTSTATUS dcerpc_ntlmssp_check_sig(struct dcerpc_security *dcerpc_security, TALLOC_CTX *mem_ctx, const uint8_t *data, size_t length, const DATA_BLOB *sig) { - struct ntlmssp_state *ntlmssp_state = dcerpc_security->private; + struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data; + return ntlmssp_check_packet(ntlmssp_state, mem_ctx, data, length, sig); } -static NTSTATUS ntlm_seal_packet(struct dcerpc_security *dcerpc_security, +static NTSTATUS dcerpc_ntlmssp_seal(struct dcerpc_security *dcerpc_security, TALLOC_CTX *mem_ctx, uint8_t *data, size_t length, DATA_BLOB *sig) { - struct ntlmssp_state *ntlmssp_state = dcerpc_security->private; + struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data; + return ntlmssp_seal_packet(ntlmssp_state, mem_ctx, data, length, sig); } -static NTSTATUS ntlm_sign_packet(struct dcerpc_security *dcerpc_security, +static NTSTATUS dcerpc_ntlmssp_sign(struct dcerpc_security *dcerpc_security, TALLOC_CTX *mem_ctx, const uint8_t *data, size_t length, DATA_BLOB *sig) { - struct ntlmssp_state *ntlmssp_state = dcerpc_security->private; + struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data; + return ntlmssp_sign_packet(ntlmssp_state, mem_ctx, data, length, sig); } -static NTSTATUS ntlm_session_key(struct dcerpc_security *dcerpc_security, +static NTSTATUS dcerpc_ntlmssp_session_key(struct dcerpc_security *dcerpc_security, DATA_BLOB *session_key) { - struct ntlmssp_state *ntlmssp_state = dcerpc_security->private; + struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data; + if (!ntlmssp_state->session_key.data) { return NT_STATUS_NO_USER_SESSION_KEY; } *session_key = ntlmssp_state->session_key; - return NT_STATUS_OK; -} -static void ntlm_security_end(struct dcerpc_security *dcerpc_security) -{ - struct ntlmssp_state *ntlmssp_state = dcerpc_security->private; - ntlmssp_end(&ntlmssp_state); + return NT_STATUS_OK; } - - -/* - do ntlm style authentication on a dcerpc pipe -*/ -NTSTATUS dcerpc_bind_auth_ntlm(struct dcerpc_pipe *p, - const char *uuid, uint_t version, - const char *domain, - const char *username, - const char *password) +static NTSTATUS dcerpc_ntlmssp_start(struct dcerpc_pipe *dce_pipe, struct dcerpc_security *dcerpc_security) { + struct ntlmssp_state *ntlmssp_state = NULL; NTSTATUS status; - struct ntlmssp_state *state; - TALLOC_CTX *mem_ctx; - DATA_BLOB credentials; - - mem_ctx = talloc_init("dcerpc_bind_auth_ntlm"); - if (!mem_ctx) { - return NT_STATUS_NO_MEMORY; - } - status = ntlmssp_client_start(&state); + status = ntlmssp_client_start(&ntlmssp_state); if (!NT_STATUS_IS_OK(status)) { return status; } - status = ntlmssp_set_domain(state, domain); + status = ntlmssp_set_domain(ntlmssp_state, dcerpc_security->user.domain); if (!NT_STATUS_IS_OK(status)) { - goto done; + return status; } - status = ntlmssp_set_username(state, username); + status = ntlmssp_set_username(ntlmssp_state, dcerpc_security->user.name); if (!NT_STATUS_IS_OK(status)) { - goto done; + return status; } - status = ntlmssp_set_password(state, password); + status = ntlmssp_set_password(ntlmssp_state, dcerpc_security->user.password); if (!NT_STATUS_IS_OK(status)) { - goto done; - } - - p->auth_info = talloc(p->mem_ctx, sizeof(*p->auth_info)); - if (!p->auth_info) { - status = NT_STATUS_NO_MEMORY; - goto done; + return status; } - - p->auth_info->auth_type = DCERPC_AUTH_TYPE_NTLMSSP; - if (p->flags & DCERPC_SEAL) { - p->auth_info->auth_level = DCERPC_AUTH_LEVEL_PRIVACY; - state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL; - } else { - /* ntlmssp does not work on dcerpc with - AUTH_LEVEL_NONE */ - state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; - p->auth_info->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY; - } - p->auth_info->auth_pad_length = 0; - p->auth_info->auth_reserved = 0; - p->auth_info->auth_context_id = random(); - p->auth_info->credentials = data_blob(NULL, 0); - p->security_state = NULL; - - status = ntlmssp_update(state, mem_ctx, - p->auth_info->credentials, - &credentials); - - if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - goto done; - } + dcerpc_security->private_data = ntlmssp_state; - p->auth_info->credentials = credentials; - - status = dcerpc_bind_byuuid(p, mem_ctx, uuid, version); - if (!NT_STATUS_IS_OK(status)) { - goto done; - } - - status = ntlmssp_update(state, mem_ctx, - p->auth_info->credentials, - &credentials); + return status; +} - if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - goto done; - } +static NTSTATUS dcerpc_ntlmssp_update(struct dcerpc_security *dcerpc_security, TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data; - p->auth_info->credentials = credentials; + return ntlmssp_update(ntlmssp_state, out_mem_ctx, in, out); +} - status = dcerpc_auth3(p, mem_ctx); +static void dcerpc_ntlmssp_end(struct dcerpc_security *dcerpc_security) +{ + struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data; - if (!NT_STATUS_IS_OK(status)) { - goto done; - } + ntlmssp_end(&ntlmssp_state); - p->security_state = talloc_p(p->mem_ctx, struct dcerpc_security); - if (!p->security_state) { - status = NT_STATUS_NO_MEMORY; - goto done; - } + dcerpc_security->private_data = NULL; +} - p->security_state->private = state; - p->security_state->unseal_packet = ntlm_unseal_packet; - p->security_state->check_packet = ntlm_check_packet; - p->security_state->seal_packet = ntlm_seal_packet; - p->security_state->sign_packet = ntlm_sign_packet; - p->security_state->session_key = ntlm_session_key; - p->security_state->security_end = ntlm_security_end; +static const struct dcesrv_security_ops dcerpc_ntlmssp_security_ops = { + .name = "ntlmssp", + .auth_type = DCERPC_AUTH_TYPE_NTLMSSP, + .start = dcerpc_ntlmssp_start, + .update = dcerpc_ntlmssp_update, + .seal = dcerpc_ntlmssp_seal, + .sign = dcerpc_ntlmssp_sign, + .check_sig = dcerpc_ntlmssp_check_sig, + .unseal = dcerpc_ntlmssp_unseal, + .session_key = dcerpc_ntlmssp_session_key, + .end = dcerpc_ntlmssp_end +}; + +const struct dcesrv_security_ops *dcerpc_ntlmssp_security_get_ops(void) +{ + return &dcerpc_ntlmssp_security_ops; +} -done: - talloc_destroy(mem_ctx); +/* + do ntlm style authentication on a dcerpc pipe +*/ +NTSTATUS dcerpc_bind_auth_ntlm(struct dcerpc_pipe *p, + const char *uuid, uint_t version, + const char *domain, + const char *username, + const char *password) +{ + NTSTATUS status; - if (!NT_STATUS_IS_OK(status)) { - p->security_state = NULL; - p->auth_info = NULL; - } + status = dcerpc_bind_auth(p, DCERPC_AUTH_TYPE_NTLMSSP, + uuid, version, + domain, username, + password); return status; } diff --git a/source4/librpc/rpc/dcerpc_schannel.c b/source4/librpc/rpc/dcerpc_schannel.c index c2645d36a2..df15edfb6f 100644 --- a/source4/librpc/rpc/dcerpc_schannel.c +++ b/source4/librpc/rpc/dcerpc_schannel.c @@ -22,54 +22,160 @@ #include "includes.h" +#define DCERPC_SCHANNEL_STATE_START 0 +#define DCERPC_SCHANNEL_STATE_UPDATE_1 1 + +struct dcerpc_schannel_state { + TALLOC_CTX *mem_ctx; + uint8_t state; + struct schannel_bind bind_schannel; + struct schannel_state *schannel_state; +}; + /* wrappers for the schannel_*() functions */ -static NTSTATUS schan_unseal_packet(struct dcerpc_security *dcerpc_security, +static NTSTATUS dcerpc_schannel_unseal(struct dcerpc_security *dcerpc_security, TALLOC_CTX *mem_ctx, uint8_t *data, size_t length, DATA_BLOB *sig) { - struct schannel_state *schannel_state = dcerpc_security->private; - return schannel_unseal_packet(schannel_state, mem_ctx, data, length, sig); + struct dcerpc_schannel_state *dce_schan_state = dcerpc_security->private_data; + + return schannel_unseal_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig); } -static NTSTATUS schan_check_packet(struct dcerpc_security *dcerpc_security, +static NTSTATUS dcerpc_schannel_check_sig(struct dcerpc_security *dcerpc_security, TALLOC_CTX *mem_ctx, const uint8_t *data, size_t length, const DATA_BLOB *sig) { - struct schannel_state *schannel_state = dcerpc_security->private; - return schannel_check_packet(schannel_state, data, length, sig); + struct dcerpc_schannel_state *dce_schan_state = dcerpc_security->private_data; + + return schannel_check_packet(dce_schan_state->schannel_state, data, length, sig); } -static NTSTATUS schan_seal_packet(struct dcerpc_security *dcerpc_security, +static NTSTATUS dcerpc_schannel_seal(struct dcerpc_security *dcerpc_security, TALLOC_CTX *mem_ctx, uint8_t *data, size_t length, DATA_BLOB *sig) { - struct schannel_state *schannel_state = dcerpc_security->private; - return schannel_seal_packet(schannel_state, mem_ctx, data, length, sig); + struct dcerpc_schannel_state *dce_schan_state = dcerpc_security->private_data; + + return schannel_seal_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig); } -static NTSTATUS schan_sign_packet(struct dcerpc_security *dcerpc_security, +static NTSTATUS dcerpc_schannel_sign(struct dcerpc_security *dcerpc_security, TALLOC_CTX *mem_ctx, const uint8_t *data, size_t length, DATA_BLOB *sig) { - struct schannel_state *schannel_state = dcerpc_security->private; - return schannel_sign_packet(schannel_state, mem_ctx, data, length, sig); + struct dcerpc_schannel_state *dce_schan_state = dcerpc_security->private_data; + + return schannel_sign_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig); } -static NTSTATUS schan_session_key(struct dcerpc_security *dcerpc_security, +static NTSTATUS dcerpc_schannel_session_key(struct dcerpc_security *dcerpc_security, DATA_BLOB *session_key) { return NT_STATUS_NOT_IMPLEMENTED; } -static void schan_security_end(struct dcerpc_security *dcerpc_security) +static NTSTATUS dcerpc_schannel_start(struct dcerpc_pipe *p, struct dcerpc_security *dcerpc_security) +{ + struct dcerpc_schannel_state *dce_schan_state; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + uint8_t session_key[16]; + int chan_type = 0; + + if (p->flags & DCERPC_SCHANNEL_BDC) { + chan_type = SEC_CHAN_BDC; + } else if (p->flags & DCERPC_SCHANNEL_WORKSTATION) { + chan_type = SEC_CHAN_WKSTA; + } else if (p->flags & DCERPC_SCHANNEL_DOMAIN) { + chan_type = SEC_CHAN_DOMAIN; + } + + status = dcerpc_schannel_key(p, dcerpc_security->user.domain, + dcerpc_security->user.name, + dcerpc_security->user.password, + chan_type, session_key); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + 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; + + status = schannel_start(&dce_schan_state->schannel_state, session_key, True); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + dce_schan_state->state = DCERPC_SCHANNEL_STATE_START; + + dcerpc_security->private_data = dce_schan_state; + + dump_data_pw("session key:\n", dce_schan_state->schannel_state->session_key, 16); + + return status; +} + +static NTSTATUS dcerpc_schannel_update(struct dcerpc_security *dcerpc_security, TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) { - struct schannel_state *schannel_state = dcerpc_security->private; - schannel_end(&schannel_state); + struct dcerpc_schannel_state *dce_schan_state = dcerpc_security->private_data; + NTSTATUS status; + struct schannel_bind bind_schannel; + + if (dce_schan_state->state != DCERPC_SCHANNEL_STATE_START) { + return NT_STATUS_OK; + } + + dce_schan_state->state = DCERPC_SCHANNEL_STATE_UPDATE_1; + + 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 = dcerpc_security->user.domain; + bind_schannel.u.info23.account_name = dcerpc_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, dcerpc_security->user.name); +#else + bind_schannel.bind_type = 3; + bind_schannel.u.info3.domain = dcerpc_security->user.domain; + bind_schannel.u.info3.account_name = dcerpc_security->user.name; +#endif + + status = ndr_push_struct_blob(out, dce_schan_state->mem_ctx, &bind_schannel, + (ndr_push_flags_fn_t)ndr_push_schannel_bind); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_MORE_PROCESSING_REQUIRED; +} + +static void dcerpc_schannel_end(struct dcerpc_security *dcerpc_security) +{ + struct dcerpc_schannel_state *dce_schan_state = dcerpc_security->private_data; + + schannel_end(&dce_schan_state->schannel_state); + + talloc_destroy(dce_schan_state->mem_ctx); + + dcerpc_security->private_data = NULL; } @@ -163,107 +269,24 @@ NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p, return NT_STATUS_OK; } - -/* - do a schannel style bind on a dcerpc pipe with the given schannel - key. The username is usually of the form HOSTNAME$ and the password - is the domain trust password -*/ -NTSTATUS dcerpc_bind_auth_schannel_key(struct dcerpc_pipe *p, - const char *uuid, uint_t version, - const char *domain, - const char *username, - const uint8_t session_key[16]) +const struct dcesrv_security_ops dcerpc_schannel_security_ops = { + .name = "schannel", + .auth_type = DCERPC_AUTH_TYPE_SCHANNEL, + .start = dcerpc_schannel_start, + .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 +}; + +const struct dcesrv_security_ops *dcerpc_schannel_security_get_ops(void) { - NTSTATUS status; - struct schannel_state *schannel_state; - const char *workgroup, *workstation; - struct schannel_bind bind_schannel; - - workstation = username; - workgroup = domain; - - /* - perform a bind with security type schannel - */ - p->auth_info = talloc(p->mem_ctx, sizeof(*p->auth_info)); - if (!p->auth_info) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - p->auth_info->auth_type = DCERPC_AUTH_TYPE_SCHANNEL; - - if (p->flags & DCERPC_SEAL) { - p->auth_info->auth_level = DCERPC_AUTH_LEVEL_PRIVACY; - } else { - /* note that DCERPC_AUTH_LEVEL_NONE does not make any - sense, and would be rejected by the server */ - p->auth_info->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY; - } - p->auth_info->auth_pad_length = 0; - p->auth_info->auth_reserved = 0; - p->auth_info->auth_context_id = random(); - p->security_state = NULL; - - 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 = domain; - bind_schannel.u.info23.account_name = username; - bind_schannel.u.info23.dnsdomain = str_format_nbt_domain(p->mem_ctx, fulldomainname); - bind_schannel.u.info23.workstation = str_format_nbt_domain(p->mem_ctx, username); -#else - bind_schannel.bind_type = 3; - bind_schannel.u.info3.domain = domain; - bind_schannel.u.info3.account_name = username; -#endif - - status = ndr_push_struct_blob(&p->auth_info->credentials, p->mem_ctx, &bind_schannel, - (ndr_push_flags_fn_t)ndr_push_schannel_bind); - if (!NT_STATUS_IS_OK(status)) { - goto done; - } - - /* send the authenticated bind request */ - status = dcerpc_bind_byuuid(p, p->mem_ctx, uuid, version); - if (!NT_STATUS_IS_OK(status)) { - goto done; - } - - p->security_state = talloc_p(p->mem_ctx, struct dcerpc_security); - if (!p->security_state) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - schannel_state = talloc_p(p->mem_ctx, struct schannel_state); - if (!schannel_state) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - status = schannel_start(&schannel_state, session_key, True); - if (!NT_STATUS_IS_OK(status)) { - goto done; - } - - dump_data_pw("session key:\n", schannel_state->session_key, 16); - - p->security_state->private = schannel_state; - p->security_state->unseal_packet = schan_unseal_packet; - p->security_state->check_packet = schan_check_packet; - p->security_state->seal_packet = schan_seal_packet; - p->security_state->sign_packet = schan_sign_packet; - p->security_state->session_key = schan_session_key; - p->security_state->security_end = schan_security_end; - -done: - return status; + return &dcerpc_schannel_security_ops; } - /* do a schannel style bind on a dcerpc pipe. The username is usually of the form HOSTNAME$ and the password is the domain trust password @@ -275,25 +298,11 @@ NTSTATUS dcerpc_bind_auth_schannel(struct dcerpc_pipe *p, const char *password) { NTSTATUS status; - uint8_t session_key[16]; - int chan_type = 0; - if (p->flags & DCERPC_SCHANNEL_BDC) { - chan_type = SEC_CHAN_BDC; - } else if (p->flags & DCERPC_SCHANNEL_WORKSTATION) { - chan_type = SEC_CHAN_WKSTA; - } else if (p->flags & DCERPC_SCHANNEL_DOMAIN) { - chan_type = SEC_CHAN_DOMAIN; - } - - status = dcerpc_schannel_key(p, domain, username, password, - chan_type, session_key); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - status = dcerpc_bind_auth_schannel_key(p, uuid, version, domain, username, session_key); + status = dcerpc_bind_auth(p, DCERPC_AUTH_TYPE_SCHANNEL, + uuid, version, + domain, username, + password); return status; } - diff --git a/source4/librpc/rpc/dcerpc_util.c b/source4/librpc/rpc/dcerpc_util.c index c7edf043ec..59233295a3 100644 --- a/source4/librpc/rpc/dcerpc_util.c +++ b/source4/librpc/rpc/dcerpc_util.c @@ -697,8 +697,8 @@ NTSTATUS dcerpc_fetch_session_key(struct dcerpc_pipe *p, { struct cli_tree *tree; - if (p->security_state) { - return p->security_state->session_key(p->security_state, session_key); + if (p->security_state.ops) { + return p->security_state.ops->session_key(&p->security_state, session_key); } tree = dcerpc_smb_tree(p); |