summaryrefslogtreecommitdiff
path: root/source4/librpc/rpc/dcerpc_schannel.c
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2005-03-24 04:14:06 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 13:11:15 -0500
commit2eb3d680625286431a3a60e37b75f47e0738f253 (patch)
tree80a6731efbcb4913d691b414d162d700d4f9059e /source4/librpc/rpc/dcerpc_schannel.c
parent46b22b073cf0d0c93f2e70dd4d670dc373d5a26a (diff)
downloadsamba-2eb3d680625286431a3a60e37b75f47e0738f253.tar.gz
samba-2eb3d680625286431a3a60e37b75f47e0738f253.tar.bz2
samba-2eb3d680625286431a3a60e37b75f47e0738f253.zip
r6028: A MAJOR update to intergrate the new credentails system fully with
GENSEC, and to pull SCHANNEL into GENSEC, by making it less 'special'. GENSEC now no longer has it's own handling of 'set username' etc, instead it uses cli_credentials calls. In order to link the credentails code right though Samba, a lot of interfaces have changed to remove 'username, domain, password' arguments, and these have been replaced with a single 'struct cli_credentials'. In the session setup code, a new parameter 'workgroup' contains the client/server current workgroup, which seems unrelated to the authentication exchange (it was being filled in from the auth info). This allows in particular kerberos to only call back for passwords when it actually needs to perform the kinit. The kerberos code has been modified not to use the SPNEGO provided 'principal name' (in the mechListMIC), but to instead use the name the host was connected to as. This better matches Microsoft behaviour, is more secure and allows better use of standard kerberos functions. To achieve this, I made changes to our socket code so that the hostname (before name resolution) is now recorded on the socket. In schannel, most of the code from librpc/rpc/dcerpc_schannel.c is now in libcli/auth/schannel.c, and it looks much more like a standard GENSEC module. The actual sign/seal code moved to libcli/auth/schannel_sign.c in a previous commit. The schannel credentails structure is now merged with the rest of the credentails, as many of the values (username, workstation, domain) where already present there. This makes handling this in a generic manner much easier, as there is no longer a custom entry-point. The auth_domain module continues to be developed, but is now just as functional as auth_winbind. The changes here are consequential to the schannel changes. The only removed function at this point is the RPC-LOGIN test (simulating the load of a WinXP login), which needs much more work to clean it up (it contains copies of too much code from all over the torture suite, and I havn't been able to penetrate its 'structure'). Andrew Bartlett (This used to be commit 2301a4b38a21aa60917973451687063d83d18d66)
Diffstat (limited to 'source4/librpc/rpc/dcerpc_schannel.c')
-rw-r--r--source4/librpc/rpc/dcerpc_schannel.c413
1 files changed, 17 insertions, 396 deletions
diff --git a/source4/librpc/rpc/dcerpc_schannel.c b/source4/librpc/rpc/dcerpc_schannel.c
index 77453cf476..3ae2624ff9 100644
--- a/source4/librpc/rpc/dcerpc_schannel.c
+++ b/source4/librpc/rpc/dcerpc_schannel.c
@@ -4,7 +4,8 @@
dcerpc schannel operations
Copyright (C) Andrew Tridgell 2004
-
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
@@ -24,281 +25,13 @@
#include "librpc/gen_ndr/ndr_schannel.h"
#include "auth/auth.h"
-enum schannel_position {
- DCERPC_SCHANNEL_STATE_START = 0,
- DCERPC_SCHANNEL_STATE_UPDATE_1
-};
-
-struct dcerpc_schannel_state {
- enum schannel_position state;
- struct schannel_state *schannel_state;
- struct creds_CredentialState *creds;
-};
-
-/*
- wrappers for the schannel_*() functions
-
- These will become static again, when we get dynamic registration, and
- decrpc_schannel_security_ops come back here.
-*/
-static NTSTATUS dcerpc_schannel_unseal_packet(struct gensec_security *gensec_security,
- TALLOC_CTX *mem_ctx,
- uint8_t *data, size_t length,
- const uint8_t *whole_pdu, size_t pdu_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_packet(struct gensec_security *gensec_security,
- TALLOC_CTX *mem_ctx,
- const uint8_t *data, size_t length,
- const uint8_t *whole_pdu, size_t pdu_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_packet(struct gensec_security *gensec_security,
- TALLOC_CTX *mem_ctx,
- uint8_t *data, size_t length,
- const uint8_t *whole_pdu, size_t pdu_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_packet(struct gensec_security *gensec_security,
- TALLOC_CTX *mem_ctx,
- const uint8_t *data, size_t length,
- const uint8_t *whole_pdu, size_t pdu_length,
- DATA_BLOB *sig)
-{
- struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
-
- return schannel_sign_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig);
-}
-
-static size_t dcerpc_schannel_sig_size(struct gensec_security *gensec_security)
-{
- return 32;
-}
-
-static NTSTATUS dcerpc_schannel_session_key(struct gensec_security *gensec_security,
- 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)
-{
- 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 *workstation;
- const char *domain;
- *out = data_blob(NULL, 0);
-
- 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;
- }
-
- status = schannel_start(dce_schan_state,
- &dce_schan_state->schannel_state,
- dce_schan_state->creds->session_key,
- True);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(1, ("Failed to start schannel client\n"));
- return status;
- }
- talloc_steal(dce_schan_state, dce_schan_state->schannel_state);
-
- 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_get_workstation(gensec_security));
-#else
- bind_schannel.bind_type = 3;
- bind_schannel.u.info3.domain = gensec_security->user.domain;
- bind_schannel.u.info3.workstation = gensec_get_workstation(gensec_security);
-#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_INVALID_PARAMETER;
- }
-
- /* 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) {
- workstation = bind_schannel.u.info23.workstation;
- domain = bind_schannel.u.info23.domain;
- } else {
- workstation = bind_schannel.u.info3.workstation;
- domain = bind_schannel.u.info3.domain;
- }
-
- /* pull the session key for this client */
- status = schannel_fetch_session_key(out_mem_ctx, workstation,
- domain, &dce_schan_state->creds);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n",
- workstation, nt_errstr(status)));
- return status;
- }
-
- /* start up the schannel server code */
- status = schannel_start(dce_schan_state,
- &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 from client %s: %s\n",
- workstation, nt_errstr(status)));
- return status;
- }
- talloc_steal(dce_schan_state, dce_schan_state->schannel_state);
-
- 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 client %s: %s\n",
- workstation, nt_errstr(status)));
- return status;
- }
-
- dce_schan_state->state = DCERPC_SCHANNEL_STATE_UPDATE_1;
-
- return NT_STATUS_OK;
- }
- 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)
-{
- (*session_info) = talloc(gensec_security, struct auth_session_info);
- NT_STATUS_HAVE_NO_MEMORY(*session_info);
-
- ZERO_STRUCTP(*session_info);
-
- 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;
-
- *creds = talloc_reference(mem_ctx, dce_schan_state->creds);
- if (!*creds) {
- return NT_STATUS_NO_MEMORY;
- }
- return NT_STATUS_OK;
-}
-
-
-static NTSTATUS dcerpc_schannel_start(struct gensec_security *gensec_security)
-{
- struct dcerpc_schannel_state *dce_schan_state;
-
- dce_schan_state = talloc(gensec_security, struct dcerpc_schannel_state);
- if (!dce_schan_state) {
- return NT_STATUS_NO_MEMORY;
- }
-
- 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;
-
- status = dcerpc_schannel_start(gensec_security);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- return NT_STATUS_OK;
-}
-
-
/*
get a schannel key using a netlogon challenge on a secondary pipe
*/
static NTSTATUS dcerpc_schannel_key(TALLOC_CTX *tmp_ctx,
struct dcerpc_pipe *p,
struct cli_credentials *credentials,
- int chan_type,
- struct creds_CredentialState *creds)
+ int chan_type)
{
NTSTATUS status;
struct dcerpc_binding *b;
@@ -307,8 +40,12 @@ static NTSTATUS dcerpc_schannel_key(TALLOC_CTX *tmp_ctx,
struct netr_ServerAuthenticate2 a;
struct netr_Credential credentials1, credentials2, credentials3;
struct samr_Password mach_pwd;
- const char *workgroup;
uint32_t negotiate_flags;
+ struct creds_CredentialState *creds;
+ creds = talloc(tmp_ctx, struct creds_CredentialState);
+ if (!creds) {
+ return NT_STATUS_NO_MEMORY;
+ }
if (p->conn->flags & DCERPC_SCHANNEL_128) {
negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
@@ -316,8 +53,6 @@ static NTSTATUS dcerpc_schannel_key(TALLOC_CTX *tmp_ctx,
negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
}
- workgroup = cli_credentials_get_domain(credentials);
-
/*
step 1 - establish a netlogon connection, with no authentication
*/
@@ -369,9 +104,6 @@ static NTSTATUS dcerpc_schannel_key(TALLOC_CTX *tmp_ctx,
*/
E_md4hash(cli_credentials_get_password(credentials), mach_pwd.hash);
creds_client_init(creds, &credentials1, &credentials2,
- cli_credentials_get_workstation(credentials),
- cli_credentials_get_domain(credentials),
- cli_credentials_get_username(credentials),
&mach_pwd, &credentials3,
negotiate_flags);
@@ -393,6 +125,8 @@ static NTSTATUS dcerpc_schannel_key(TALLOC_CTX *tmp_ctx,
return NT_STATUS_UNSUCCESSFUL;
}
+ cli_credentials_set_netlogon_creds(credentials, creds);
+
/*
the schannel session key is now in creds.session_key
@@ -403,76 +137,6 @@ static NTSTATUS dcerpc_schannel_key(TALLOC_CTX *tmp_ctx,
return NT_STATUS_OK;
}
-/*
- 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
-*/
-NTSTATUS dcerpc_bind_auth_schannel_withkey(struct dcerpc_pipe *p,
- const char *uuid, uint_t version,
- struct creds_CredentialState *creds)
-{
- NTSTATUS status;
- struct dcerpc_schannel_state *dce_schan_state;
-
- status = gensec_client_start(p, &p->conn->security_state.generic_state);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- status = gensec_set_workstation(p->conn->security_state.generic_state, creds->computer_name);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(1, ("Failed to set schannel workstation to %s: %s\n",
- creds->computer_name, nt_errstr(status)));
- talloc_free(p->conn->security_state.generic_state);
- p->conn->security_state.generic_state = NULL;
- return status;
- }
-
- status = gensec_set_username(p->conn->security_state.generic_state, creds->account_name);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(1, ("Failed to set schannel username to %s: %s\n",
- creds->account_name, nt_errstr(status)));
- talloc_free(p->conn->security_state.generic_state);
- p->conn->security_state.generic_state = NULL;
- return status;
- }
-
- status = gensec_set_domain(p->conn->security_state.generic_state, creds->domain);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(1, ("Failed to set schannel domain to %s: %s\n",
- creds->domain, nt_errstr(status)));
- talloc_free(p->conn->security_state.generic_state);
- p->conn->security_state.generic_state = NULL;
- return status;
- }
-
- status = gensec_start_mech_by_authtype(p->conn->security_state.generic_state,
- DCERPC_AUTH_TYPE_SCHANNEL,
- dcerpc_auth_level(p->conn));
-
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(1, ("Failed to start SCHANNEL GENSEC backend: %s\n", nt_errstr(status)));
- talloc_free(p->conn->security_state.generic_state);
- p->conn->security_state.generic_state = NULL;
- return status;
- }
-
- dce_schan_state = p->conn->security_state.generic_state->private_data;
- dce_schan_state->creds = talloc_reference(dce_schan_state, creds);
-
- status = dcerpc_bind_auth(p, DCERPC_AUTH_TYPE_SCHANNEL, dcerpc_auth_level(p->conn),
- uuid, version);
-
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(1, ("Failed to bind to pipe with SCHANNEL: %s\n", nt_errstr(status)));
- talloc_free(p->conn->security_state.generic_state);
- p->conn->security_state.generic_state = NULL;
- return status;
- }
-
- return NT_STATUS_OK;
-}
-
NTSTATUS dcerpc_bind_auth_schannel(TALLOC_CTX *tmp_ctx,
struct dcerpc_pipe *p,
const char *uuid, uint_t version,
@@ -480,11 +144,6 @@ NTSTATUS dcerpc_bind_auth_schannel(TALLOC_CTX *tmp_ctx,
{
NTSTATUS status;
int chan_type = 0;
- struct creds_CredentialState *creds;
- creds = talloc(tmp_ctx, struct creds_CredentialState);
- if (!creds) {
- return NT_STATUS_NO_MEMORY;
- }
if (p->conn->flags & DCERPC_SCHANNEL_BDC) {
chan_type = SEC_CHAN_BDC;
@@ -494,58 +153,20 @@ NTSTATUS dcerpc_bind_auth_schannel(TALLOC_CTX *tmp_ctx,
chan_type = SEC_CHAN_DOMAIN;
}
+ /* Fills in NETLOGON credentials */
status = dcerpc_schannel_key(tmp_ctx,
p, credentials,
- chan_type,
- creds);
+ chan_type);
if (!NT_STATUS_IS_OK(status)) {
- DEBUG(1, ("Failed to fetch schannel session key: %s\n",
+ DEBUG(1, ("Failed to setup credentials for account %s: %s\n",
+ cli_credentials_get_username(credentials),
nt_errstr(status)));
return status;
}
- return dcerpc_bind_auth_schannel_withkey(p, uuid, version, creds);
+ return dcerpc_bind_auth_password(p, uuid, version,
+ credentials, DCERPC_AUTH_TYPE_SCHANNEL,
+ NULL);
}
-static BOOL dcerpc_schannel_have_feature(struct gensec_security *gensec_security,
- uint32_t feature)
-{
- if (feature & (GENSEC_FEATURE_SESSION_KEY |
- GENSEC_FEATURE_SIGN |
- GENSEC_FEATURE_SEAL)) {
- return True;
- }
- return False;
-}
-
-
-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,
- .sig_size = dcerpc_schannel_sig_size,
- .have_feature = dcerpc_schannel_have_feature,
- .enabled = True
-};
-
-NTSTATUS gensec_dcerpc_schannel_init(void)
-{
- NTSTATUS ret;
- ret = gensec_register(&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 ret;
-}