diff options
author | Andrew Bartlett <abartlet@samba.org> | 2005-03-24 04:14:06 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:11:15 -0500 |
commit | 2eb3d680625286431a3a60e37b75f47e0738f253 (patch) | |
tree | 80a6731efbcb4913d691b414d162d700d4f9059e /source4/libcli/auth | |
parent | 46b22b073cf0d0c93f2e70dd4d670dc373d5a26a (diff) | |
download | samba-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/libcli/auth')
-rw-r--r-- | source4/libcli/auth/config.mk | 3 | ||||
-rw-r--r-- | source4/libcli/auth/credentials.c | 6 | ||||
-rw-r--r-- | source4/libcli/auth/gensec.c | 210 | ||||
-rw-r--r-- | source4/libcli/auth/gensec.h | 12 | ||||
-rw-r--r-- | source4/libcli/auth/gensec.mk | 12 | ||||
-rw-r--r-- | source4/libcli/auth/gensec_krb5.c | 57 | ||||
-rw-r--r-- | source4/libcli/auth/gensec_ntlmssp.c | 21 | ||||
-rw-r--r-- | source4/libcli/auth/kerberos.h | 1 | ||||
-rw-r--r-- | source4/libcli/auth/ntlmssp_sign.c | 2 | ||||
-rw-r--r-- | source4/libcli/auth/schannel.c | 268 | ||||
-rw-r--r-- | source4/libcli/auth/schannel_sign.c | 67 | ||||
-rw-r--r-- | source4/libcli/auth/spnego.c | 10 |
12 files changed, 361 insertions, 308 deletions
diff --git a/source4/libcli/auth/config.mk b/source4/libcli/auth/config.mk index b37e214360..0c013ce0ef 100644 --- a/source4/libcli/auth/config.mk +++ b/source4/libcli/auth/config.mk @@ -1,8 +1,7 @@ ################################# # Start SUBSYSTEM GENSEC [SUBSYSTEM::LIBCLI_AUTH] -ADD_OBJ_FILES = libcli/auth/schannel_sign.o \ - libcli/auth/credentials.o \ +ADD_OBJ_FILES = libcli/auth/credentials.o \ libcli/auth/session.o \ libcli/auth/smbencrypt.o REQUIRED_SUBSYSTEMS = \ diff --git a/source4/libcli/auth/credentials.c b/source4/libcli/auth/credentials.c index 90b8313c9d..bcb462ae9d 100644 --- a/source4/libcli/auth/credentials.c +++ b/source4/libcli/auth/credentials.c @@ -192,18 +192,12 @@ next comes the client specific functions void creds_client_init(struct creds_CredentialState *creds, const struct netr_Credential *client_challenge, const struct netr_Credential *server_challenge, - const char *computer_name, - const char *domain, - const char *account_name, const struct samr_Password *machine_password, struct netr_Credential *initial_credential, uint32_t negotiate_flags) { creds->sequence = time(NULL); creds->negotiate_flags = negotiate_flags; - creds->computer_name = talloc_strdup(creds, computer_name); - creds->domain = talloc_strdup(creds, domain); - creds->account_name = talloc_strdup(creds, account_name); dump_data_pw("Client chall", client_challenge->data, sizeof(client_challenge->data)); dump_data_pw("Server chall", server_challenge->data, sizeof(server_challenge->data)); diff --git a/source4/libcli/auth/gensec.c b/source4/libcli/auth/gensec.c index 69de016156..cc7327187c 100644 --- a/source4/libcli/auth/gensec.c +++ b/source4/libcli/auth/gensec.c @@ -4,7 +4,7 @@ Generic Authentication Interface Copyright (C) Andrew Tridgell 2003 - Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 @@ -130,13 +130,7 @@ static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx, struct gensec_security **gense (*gensec_security)->ops = NULL; - ZERO_STRUCT((*gensec_security)->user); ZERO_STRUCT((*gensec_security)->target); - ZERO_STRUCT((*gensec_security)->default_user); - - (*gensec_security)->default_user.name = ""; - (*gensec_security)->default_user.domain = talloc_strdup(*gensec_security, lp_workgroup()); - (*gensec_security)->default_user.realm = talloc_strdup(*gensec_security, lp_realm()); (*gensec_security)->subcontext = False; (*gensec_security)->want_features = 0; @@ -185,8 +179,6 @@ NTSTATUS gensec_client_start(TALLOC_CTX *mem_ctx, struct gensec_security **gense (*gensec_security)->gensec_role = GENSEC_CLIENT; (*gensec_security)->password_callback = NULL; - ZERO_STRUCT((*gensec_security)->user); - return status; } @@ -507,163 +499,24 @@ BOOL gensec_have_feature(struct gensec_security *gensec_security, } /** - * Set a username on a GENSEC context - ensures it is talloc()ed - * - */ - -NTSTATUS gensec_set_username(struct gensec_security *gensec_security, const char *user) -{ - gensec_security->user.name = talloc_strdup(gensec_security, user); - if (user && !gensec_security->user.name) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} - -/** - * Set a username on a GENSEC context - ensures it is talloc()ed - * - */ - -const char *gensec_get_username(struct gensec_security *gensec_security) -{ - if (gensec_security->user.name) { - return gensec_security->user.name; - } - return gensec_security->default_user.name; -} - -/** - * Set a domain on a GENSEC context - ensures it is talloc()ed + * Associate a credentails structure with a GENSEC context - talloc_reference()s it to the context * */ -NTSTATUS gensec_set_domain(struct gensec_security *gensec_security, const char *domain) +NTSTATUS gensec_set_credentials(struct gensec_security *gensec_security, struct cli_credentials *credentials) { - gensec_security->user.domain = talloc_strdup(gensec_security, domain); - if (domain && !gensec_security->user.domain) { - return NT_STATUS_NO_MEMORY; - } + gensec_security->credentials = talloc_reference(gensec_security, credentials); return NT_STATUS_OK; } /** - * Return the NT domain for this GENSEC context + * Return the credentails structure associated with a GENSEC context * */ -const char *gensec_get_domain(struct gensec_security *gensec_security) +struct cli_credentials *gensec_get_credentials(struct gensec_security *gensec_security) { - if (gensec_security->user.domain) { - return gensec_security->user.domain; - } else if (gensec_security->user.realm) { - return gensec_security->user.realm; - } - return gensec_security->default_user.domain; -} - -/** - * Set the client workstation on a GENSEC context - ensures it is talloc()ed - * - */ - -NTSTATUS gensec_set_workstation(struct gensec_security *gensec_security, const char *workstation) -{ - gensec_security->user.workstation = talloc_strdup(gensec_security, workstation); - if (workstation && !gensec_security->user.workstation) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} - -/** - * Return the client workstation on a GENSEC context - ensures it is talloc()ed - * - */ - -const char *gensec_get_workstation(struct gensec_security *gensec_security) -{ - if (gensec_security->user.workstation) { - return gensec_security->user.workstation; - } else { - return lp_netbios_name(); - } -} - -/** - * Set a kerberos realm on a GENSEC context - ensures it is talloc()ed - * - */ - -NTSTATUS gensec_set_realm(struct gensec_security *gensec_security, const char *realm) -{ - gensec_security->user.realm = talloc_strdup(gensec_security, realm); - if (realm && !gensec_security->user.realm) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} - -/** - * Return the Krb5 realm for this context - * - */ - -const char *gensec_get_realm(struct gensec_security *gensec_security) -{ - if (gensec_security->user.realm) { - return gensec_security->user.realm; - } else if (gensec_security->user.domain) { - return gensec_security->user.domain; - } - return gensec_security->default_user.realm; -} - -/** - * Return a kerberos principal for this context, if one has been set - * - */ - -char *gensec_get_client_principal(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx) -{ - const char *realm = gensec_get_realm(gensec_security); - if (realm) { - return talloc_asprintf(mem_ctx, "%s@%s", - gensec_get_username(gensec_security), - gensec_get_realm(gensec_security)); - } else { - return talloc_strdup(mem_ctx, gensec_get_username(gensec_security)); - } -} - -/** - * Set the password outright on GENSEC context - ensures it is talloc()ed, and that we will - * not do a callback - * - */ - -NTSTATUS gensec_set_password(struct gensec_security *gensec_security, - const char *password) -{ - gensec_security->user.password = talloc_strdup(gensec_security, password); - if (password && !gensec_security->user.password) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} - -/** - * Set the target principal name (if already known) on a GENSEC context - ensures it is talloc()ed - * - */ - -NTSTATUS gensec_set_target_principal(struct gensec_security *gensec_security, const char *principal) -{ - gensec_security->target.principal = talloc_strdup(gensec_security, principal); - if (!gensec_security->target.principal) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; + return gensec_security->credentials; } /** @@ -713,54 +566,6 @@ const char *gensec_get_target_service(struct gensec_security *gensec_security) return "host"; } -const char *gensec_get_target_principal(struct gensec_security *gensec_security) -{ - const char *mechListMIC; - - if (gensec_security->target.principal) { - return gensec_security->target.principal; - } - - mechListMIC = talloc_asprintf(gensec_security,"%s$@%s", - lp_netbios_name(), - lp_realm()); - return mechListMIC; -} - -/** - * Set a password callback, if the gensec module we use demands a password - */ - -void gensec_set_password_callback(struct gensec_security *gensec_security, - gensec_password_callback callback, void *callback_private_data) -{ - gensec_security->password_callback = callback; - gensec_security->password_callback_private = callback_private_data; -} - -/** - * Get (or call back for) a password. - */ - -NTSTATUS gensec_get_password(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - char **password) -{ - if (gensec_security->user.password) { - *password = talloc_strdup(mem_ctx, gensec_security->user.password); - if (!*password) { - return NT_STATUS_NO_MEMORY; - } else { - return NT_STATUS_OK; - } - } - if (!gensec_security->password_callback) { - *password = NULL; - return NT_STATUS_OK; - } - return gensec_security->password_callback(gensec_security, mem_ctx, password); -} - /* register a GENSEC backend. @@ -821,6 +626,5 @@ const struct gensec_critical_sizes *gensec_interface_version(void) */ NTSTATUS gensec_init(void) { - gensec_dcerpc_schannel_init(); return NT_STATUS_OK; } diff --git a/source4/libcli/auth/gensec.h b/source4/libcli/auth/gensec.h index a4383d852c..91c817d48a 100644 --- a/source4/libcli/auth/gensec.h +++ b/source4/libcli/auth/gensec.h @@ -4,7 +4,7 @@ Generic Authentication Interface Copyright (C) Andrew Tridgell 2003 - Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 @@ -28,13 +28,6 @@ #define GENSEC_OID_KERBEROS5_USER2USER "1 2 840 113554 1 2 2 3" struct gensec_security; -struct gensec_user { - const char *workstation; - const char *domain; - const char *realm; - const char *name; - const char *password; -}; struct gensec_target { const char *principal; const char *hostname; @@ -105,8 +98,7 @@ struct gensec_security { void *password_callback_private; const struct gensec_security_ops *ops; void *private_data; - struct gensec_user user; - struct gensec_user default_user; + struct cli_credentials *credentials; struct gensec_target target; enum gensec_role gensec_role; BOOL subcontext; diff --git a/source4/libcli/auth/gensec.mk b/source4/libcli/auth/gensec.mk index 7e2e34081d..b4c612da14 100644 --- a/source4/libcli/auth/gensec.mk +++ b/source4/libcli/auth/gensec.mk @@ -69,6 +69,18 @@ REQUIRED_SUBSYSTEMS = AUTH ################################################ ################################################ +# Start MODULE gensec_schannel +[MODULE::gensec_schannel] +SUBSYSTEM = GENSEC +INIT_FUNCTION = gensec_schannel_init +INIT_OBJ_FILES = libcli/auth/schannel.o +ADD_OBJ_FILES = \ + libcli/auth/schannel_sign.o +REQUIRED_SUBSYSTEMS = AUTH SCHANNELDB +# End MODULE gensec_ntlmssp +################################################ + +################################################ # Start SUBSYSTEM SCHANNELDB [SUBSYSTEM::SCHANNELDB] INIT_OBJ_FILES = \ diff --git a/source4/libcli/auth/gensec_krb5.c b/source4/libcli/auth/gensec_krb5.c index 71670632b9..453485d816 100644 --- a/source4/libcli/auth/gensec_krb5.c +++ b/source4/libcli/auth/gensec_krb5.c @@ -320,7 +320,12 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security struct gensec_krb5_state *gensec_krb5_state; krb5_error_code ret; NTSTATUS nt_status; - + const char *hostname = gensec_get_target_hostname(gensec_security); + if (!hostname) { + DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n")); + return NT_STATUS_ACCESS_DENIED; + } + nt_status = gensec_krb5_start(gensec_security); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; @@ -341,22 +346,8 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security } while (1) { - if (gensec_security->target.principal) { - DEBUG(5, ("Finding ticket for target [%s]\n", gensec_security->target.principal)); - ret = ads_krb5_mk_req(gensec_krb5_state->context, - &gensec_krb5_state->auth_context, - AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED, - gensec_security->target.principal, - gensec_krb5_state->ccache, - &gensec_krb5_state->ticket); - } else { + { krb5_data in_data; - const char *hostname = gensec_get_target_hostname(gensec_security); - if (!hostname) { - DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n")); - return NT_STATUS_ACCESS_DENIED; - } - in_data.length = 0; ret = krb5_mk_req(gensec_krb5_state->context, @@ -372,36 +363,40 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security case 0: return NT_STATUS_OK; case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: - DEBUG(3, ("Server is not registered with our KDC: %s\n", - error_message(ret))); + DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n", + hostname, error_message(ret))); return NT_STATUS_ACCESS_DENIED; case KRB5KDC_ERR_PREAUTH_FAILED: case KRB5KRB_AP_ERR_TKT_EXPIRED: case KRB5_CC_END: + /* Too much clock skew - we will need to kinit to re-skew the clock */ + case KRB5KRB_AP_ERR_SKEW: + case KRB5_KDCREP_SKEW: { DEBUG(3, ("kerberos (mk_req) failed: %s\n", error_message(ret))); /* fall down to remaining code */ } + + /* just don't print a message for these really ordinary messages */ case KRB5_FCC_NOFILE: case KRB5_CC_NOTFOUND: case ENOENT: + { - char *password; + const char *password; char *ccache_string; time_t kdc_time = 0; - nt_status = gensec_get_password(gensec_security, - gensec_security, - &password); + password = cli_credentials_get_password(gensec_security->credentials); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } /* this string should be unique */ ccache_string = talloc_asprintf(gensec_krb5_state, "MEMORY:%s:%s:%s", - gensec_get_client_principal(gensec_security, gensec_krb5_state), - gensec_get_target_principal(gensec_security), + cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), + gensec_get_target_hostname(gensec_security), generate_random_str(gensec_krb5_state, 16)); ret = krb5_cc_resolve(gensec_krb5_state->context, ccache_string, &gensec_krb5_state->ccache); @@ -413,8 +408,8 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security } ret = kerberos_kinit_password_cc(gensec_krb5_state->context, gensec_krb5_state->ccache, - gensec_get_client_principal(gensec_security, gensec_krb5_state), - password, NULL, &kdc_time); + cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), + password, NULL, &kdc_time); /* cope with ticket being in the future due to clock skew */ if ((unsigned)kdc_time > time(NULL)) { @@ -422,10 +417,18 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security int time_offset =(unsigned)kdc_time-t; DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset)); krb5_set_real_time(gensec_krb5_state->context, t + time_offset + 1, 0); + break; } + if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) { + DEBUG(1,("kinit for %s failed (%s)\n", + cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), + error_message(ret))); + return NT_STATUS_TIME_DIFFERENCE_AT_DC; + } if (ret) { - DEBUG(1,("kinit failed (%s)\n", + DEBUG(1,("kinit for %s failed (%s)\n", + cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), error_message(ret))); return NT_STATUS_WRONG_PASSWORD; } diff --git a/source4/libcli/auth/gensec_ntlmssp.c b/source4/libcli/auth/gensec_ntlmssp.c index 51456d9107..5955904886 100644 --- a/source4/libcli/auth/gensec_ntlmssp.c +++ b/source4/libcli/auth/gensec_ntlmssp.c @@ -109,7 +109,7 @@ static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, NT_STATUS_NOT_OK_RETURN(nt_status); nt_status = auth_check_password(gensec_ntlmssp_state->auth_context, gensec_ntlmssp_state, - user_info, &gensec_ntlmssp_state->server_info); + user_info, &gensec_ntlmssp_state->server_info); talloc_free(user_info); NT_STATUS_NOT_OK_RETURN(nt_status); @@ -197,7 +197,7 @@ static NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_secur static NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_security) { struct gensec_ntlmssp_state *gensec_ntlmssp_state; - char *password = NULL; + const char *password = NULL; NTSTATUS nt_status; nt_status = gensec_ntlmssp_start(gensec_security); @@ -228,25 +228,20 @@ static NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_secur } nt_status = ntlmssp_set_domain(gensec_ntlmssp_state->ntlmssp_state, - gensec_security->user.domain); + cli_credentials_get_domain(gensec_security->credentials)); NT_STATUS_NOT_OK_RETURN(nt_status); nt_status = ntlmssp_set_username(gensec_ntlmssp_state->ntlmssp_state, - gensec_security->user.name); + cli_credentials_get_username(gensec_security->credentials)); NT_STATUS_NOT_OK_RETURN(nt_status); - if (gensec_security->user.name) { - nt_status = gensec_get_password(gensec_security, gensec_ntlmssp_state, &password); - NT_STATUS_NOT_OK_RETURN(nt_status); - } + password = cli_credentials_get_password(gensec_security->credentials); - if (password) { - nt_status = ntlmssp_set_password(gensec_ntlmssp_state->ntlmssp_state, password); - NT_STATUS_NOT_OK_RETURN(nt_status); - } + nt_status = ntlmssp_set_password(gensec_ntlmssp_state->ntlmssp_state, password); + NT_STATUS_NOT_OK_RETURN(nt_status); nt_status = ntlmssp_set_workstation(gensec_ntlmssp_state->ntlmssp_state, - gensec_get_workstation(gensec_security)); + cli_credentials_get_workstation(gensec_security->credentials)); gensec_security->private_data = gensec_ntlmssp_state; diff --git a/source4/libcli/auth/kerberos.h b/source4/libcli/auth/kerberos.h index 9bb6d22eb6..c9b2eae55c 100644 --- a/source4/libcli/auth/kerberos.h +++ b/source4/libcli/auth/kerberos.h @@ -93,5 +93,6 @@ krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context, void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype); BOOL kerberos_compatible_enctypes(krb5_context context, krb5_enctype enctype1, krb5_enctype enctype2); void kerberos_free_data_contents(krb5_context context, krb5_data *pdata); +krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry); #endif /* HAVE_KRB5 */ diff --git a/source4/libcli/auth/ntlmssp_sign.c b/source4/libcli/auth/ntlmssp_sign.c index 1b391306bc..347a85da77 100644 --- a/source4/libcli/auth/ntlmssp_sign.c +++ b/source4/libcli/auth/ntlmssp_sign.c @@ -3,7 +3,7 @@ * Version 3.0 * NTLMSSP Signing routines * Copyright (C) Luke Kenneth Casson Leighton 1996-2001 - * Copyright (C) Andrew Bartlett 2003 + * Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004 * * 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 diff --git a/source4/libcli/auth/schannel.c b/source4/libcli/auth/schannel.c new file mode 100644 index 0000000000..3dbf10580b --- /dev/null +++ b/source4/libcli/auth/schannel.c @@ -0,0 +1,268 @@ +/* + Unix SMB/CIFS implementation. + + 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 + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_schannel.h" +#include "auth/auth.h" +#include "libcli/auth/schannel.h" + +static size_t schannel_sig_size(struct gensec_security *gensec_security) +{ + return 32; +} + +static NTSTATUS schannel_session_key(struct gensec_security *gensec_security, + DATA_BLOB *session_key) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + struct schannel_state *state = gensec_security->private_data; + NTSTATUS status; + struct schannel_bind bind_schannel; + struct schannel_bind_ack bind_schannel_ack; + struct creds_CredentialState *creds; + + const char *workstation; + const char *domain; + *out = data_blob(NULL, 0); + + switch (gensec_security->gensec_role) { + case GENSEC_CLIENT: + if (state->state != SCHANNEL_STATE_START) { + /* we could parse the bind ack, but we don't know what it is yet */ + return NT_STATUS_OK; + } + + state->creds = talloc_reference(state, cli_credentials_get_netlogon_creds(gensec_security->credentials)); + + 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 = cli_credentials_get_domain(gensec_security->credentials); + bind_schannel.u.info23.account_name = cli_credentials_get_username(gensec_security->credentials); + 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, cli_credentials_get_workstation(gensec_security->credentials)); +#else + bind_schannel.bind_type = 3; + bind_schannel.u.info3.domain = cli_credentials_get_domain(gensec_security->credentials); + bind_schannel.u.info3.workstation = cli_credentials_get_workstation(gensec_security->credentials); +#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; + } + + state->state = SCHANNEL_STATE_UPDATE_1; + + return NT_STATUS_MORE_PROCESSING_REQUIRED; + case GENSEC_SERVER: + + if (state->state != 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, &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; + } + + state->creds = talloc_reference(state, creds); + + 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; + } + + state->state = SCHANNEL_STATE_UPDATE_1; + + return NT_STATUS_OK; + } + return NT_STATUS_INVALID_PARAMETER; +} + +/** + * 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 schannel_state *state = gensec_security->private_data; + + *creds = talloc_reference(mem_ctx, state->creds); + if (!*creds) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + + +/** + * 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. + * + */ + +static NTSTATUS 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; +} + +static NTSTATUS schannel_start(struct gensec_security *gensec_security) +{ + struct schannel_state *state; + + state = talloc(gensec_security, struct schannel_state); + if (!state) { + return NT_STATUS_NO_MEMORY; + } + + state->state = SCHANNEL_STATE_START; + state->seq_num = 0; + gensec_security->private_data = state; + + return NT_STATUS_OK; +} + +static NTSTATUS schannel_server_start(struct gensec_security *gensec_security) +{ + NTSTATUS status; + struct schannel_state *state; + + status = schannel_start(gensec_security); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + state = gensec_security->private_data; + state->initiator = False; + + return NT_STATUS_OK; +} + +static NTSTATUS schannel_client_start(struct gensec_security *gensec_security) +{ + NTSTATUS status; + struct schannel_state *state; + + status = schannel_start(gensec_security); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + state = gensec_security->private_data; + state->initiator = True; + + return NT_STATUS_OK; +} + + +static BOOL schannel_have_feature(struct gensec_security *gensec_security, + uint32_t feature) +{ + if (feature & (GENSEC_FEATURE_SIGN | + GENSEC_FEATURE_SEAL)) { + return True; + } + return False; +} + + +static const struct gensec_security_ops gensec_schannel_security_ops = { + .name = "schannel", + .auth_type = DCERPC_AUTH_TYPE_SCHANNEL, + .client_start = schannel_client_start, + .server_start = schannel_server_start, + .update = schannel_update, + .seal_packet = schannel_seal_packet, + .sign_packet = schannel_sign_packet, + .check_packet = schannel_check_packet, + .unseal_packet = schannel_unseal_packet, + .session_key = schannel_session_key, + .session_info = schannel_session_info, + .sig_size = schannel_sig_size, + .have_feature = schannel_have_feature, + .enabled = True +}; + +NTSTATUS gensec_schannel_init(void) +{ + NTSTATUS ret; + ret = gensec_register(&gensec_schannel_security_ops); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register '%s' gensec backend!\n", + gensec_schannel_security_ops.name)); + return ret; + } + + return ret; +} diff --git a/source4/libcli/auth/schannel_sign.c b/source4/libcli/auth/schannel_sign.c index d582ff2dd0..3b493bd0d3 100644 --- a/source4/libcli/auth/schannel_sign.c +++ b/source4/libcli/auth/schannel_sign.c @@ -4,6 +4,7 @@ schannel library code Copyright (C) Andrew Tridgell 2004 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 @@ -22,12 +23,9 @@ #include "includes.h" #include "lib/crypto/crypto.h" - -struct schannel_state { - uint8_t session_key[16]; - uint32_t seq_num; - BOOL initiator; -}; +#include "libcli/auth/schannel.h" +#include "libcli/auth/gensec.h" +#include "libcli/auth/credentials.h" #define NETSEC_SIGN_SIGNATURE { 0x77, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } #define NETSEC_SEAL_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 } @@ -43,7 +41,7 @@ static void netsec_deal_with_seq_num(struct schannel_state *state, uint8_t sequence_key[16]; uint8_t digest1[16]; - hmac_md5(state->session_key, zeros, sizeof(zeros), digest1); + hmac_md5(state->creds->session_key, zeros, sizeof(zeros), digest1); hmac_md5(digest1, packet_digest, 8, sequence_key); arcfour_crypt(seq_num, sequence_key, 8); @@ -102,11 +100,14 @@ static void schannel_digest(const uint8_t sess_key[16], /* unseal a packet */ -NTSTATUS schannel_unseal_packet(struct schannel_state *state, +NTSTATUS 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 schannel_state *state = gensec_security->private_data; + uint8_t digest_final[16]; uint8_t confounder[8]; uint8_t seq_num[8]; @@ -122,11 +123,11 @@ NTSTATUS schannel_unseal_packet(struct schannel_state *state, RSIVAL(seq_num, 0, state->seq_num); SIVAL(seq_num, 4, state->initiator?0:0x80); - netsec_get_sealing_key(state->session_key, seq_num, sealing_key); + netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key); arcfour_crypt(confounder, sealing_key, 8); arcfour_crypt(data, sealing_key, length); - schannel_digest(state->session_key, + schannel_digest(state->creds->session_key, netsec_sig, confounder, data, length, digest_final); @@ -150,10 +151,14 @@ NTSTATUS schannel_unseal_packet(struct schannel_state *state, /* check the signature on a packet */ -NTSTATUS schannel_check_packet(struct schannel_state *state, +NTSTATUS 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 schannel_state *state = gensec_security->private_data; + uint8_t digest_final[16]; uint8_t seq_num[8]; static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE; @@ -167,9 +172,9 @@ NTSTATUS schannel_check_packet(struct schannel_state *state, SIVAL(seq_num, 4, state->initiator?0:0x80); dump_data_pw("seq_num:\n", seq_num, 8); - dump_data_pw("sess_key:\n", state->session_key, 16); + dump_data_pw("sess_key:\n", state->creds->session_key, 16); - schannel_digest(state->session_key, + schannel_digest(state->creds->session_key, netsec_sig, NULL, data, length, digest_final); @@ -194,11 +199,14 @@ NTSTATUS schannel_check_packet(struct schannel_state *state, /* seal a packet */ -NTSTATUS schannel_seal_packet(struct schannel_state *state, +NTSTATUS 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 schannel_state *state = gensec_security->private_data; + uint8_t digest_final[16]; uint8_t confounder[8]; uint8_t seq_num[8]; @@ -210,11 +218,11 @@ NTSTATUS schannel_seal_packet(struct schannel_state *state, RSIVAL(seq_num, 0, state->seq_num); SIVAL(seq_num, 4, state->initiator?0x80:0); - schannel_digest(state->session_key, + schannel_digest(state->creds->session_key, netsec_sig, confounder, data, length, digest_final); - netsec_get_sealing_key(state->session_key, seq_num, sealing_key); + netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key); arcfour_crypt(confounder, sealing_key, 8); arcfour_crypt(data, sealing_key, length); @@ -239,11 +247,14 @@ NTSTATUS schannel_seal_packet(struct schannel_state *state, /* sign a packet */ -NTSTATUS schannel_sign_packet(struct schannel_state *state, +NTSTATUS 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 schannel_state *state = gensec_security->private_data; + uint8_t digest_final[16]; uint8_t seq_num[8]; static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE; @@ -251,7 +262,7 @@ NTSTATUS schannel_sign_packet(struct schannel_state *state, RSIVAL(seq_num, 0, state->seq_num); SIVAL(seq_num, 4, state->initiator?0x80:0); - schannel_digest(state->session_key, + schannel_digest(state->creds->session_key, netsec_sig, NULL, data, length, digest_final); @@ -271,23 +282,3 @@ NTSTATUS schannel_sign_packet(struct schannel_state *state, return NT_STATUS_OK; } - -/* - create an schannel context state -*/ -NTSTATUS schannel_start(TALLOC_CTX *mem_ctx, - struct schannel_state **state, - const uint8_t session_key[16], - BOOL initiator) -{ - (*state) = talloc(mem_ctx, struct schannel_state); - if (!(*state)) { - return NT_STATUS_NO_MEMORY; - } - - memcpy((*state)->session_key, session_key, 16); - (*state)->initiator = initiator; - (*state)->seq_num = 0; - - return NT_STATUS_OK; -} diff --git a/source4/libcli/auth/spnego.c b/source4/libcli/auth/spnego.c index 3980767165..f5a091cd78 100644 --- a/source4/libcli/auth/spnego.c +++ b/source4/libcli/auth/spnego.c @@ -610,7 +610,7 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA spnego_out.negTokenInit.mechTypes = mechlist; spnego_out.negTokenInit.reqFlags = 0; spnego_out.negTokenInit.mechListMIC - = data_blob_string_const(gensec_get_target_principal(gensec_security)); + = data_blob_string_const(talloc_asprintf(out_mem_ctx, "%s$@%s", lp_netbios_name(), lp_realm())); spnego_out.negTokenInit.mechToken = unwrapped_out; if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) { @@ -657,13 +657,7 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA } if (spnego.negTokenInit.targetPrincipal) { - DEBUG(5, ("Server claims it's principal name is %s\n", spnego.negTokenInit.targetPrincipal)); - nt_status = gensec_set_target_principal(gensec_security, - spnego.negTokenInit.targetPrincipal); - if (!NT_STATUS_IS_OK(nt_status)) { - spnego_free_data(&spnego); - return nt_status; - } + DEBUG(5, ("Server claims it's principal name is %s (ignored)\n", spnego.negTokenInit.targetPrincipal)); } nt_status = gensec_spnego_client_parse_negTokenInit(gensec_security, |