diff options
author | Andrew Bartlett <abartlet@samba.org> | 2003-02-24 02:55:00 +0000 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2003-02-24 02:55:00 +0000 |
commit | d1221c9b6c369113a531063737890b58d89bf6fe (patch) | |
tree | efc7b8b8d33b675404dc7c5bc018db47a4136212 | |
parent | e075e1dfa9a88b5edadc9c989200a52f48182cef (diff) | |
download | samba-d1221c9b6c369113a531063737890b58d89bf6fe.tar.gz samba-d1221c9b6c369113a531063737890b58d89bf6fe.tar.bz2 samba-d1221c9b6c369113a531063737890b58d89bf6fe.zip |
Merge from HEAD client-side authentication changes:
- new kerberos code, allowing the account to change it's own password
without special SD settings required
- NTLMSSP client code, now seperated from cliconnect.c
- NTLMv2 client code
- SMB signing fixes
Andrew Bartlett
(This used to be commit 837680ca517982f2e5944730581a83012d4181ae)
-rw-r--r-- | source3/include/ads.h | 3 | ||||
-rw-r--r-- | source3/include/client.h | 7 | ||||
-rw-r--r-- | source3/include/ntlmssp.h | 20 | ||||
-rw-r--r-- | source3/lib/data_blob.c | 1 | ||||
-rw-r--r-- | source3/libads/krb5_setpw.c | 429 | ||||
-rw-r--r-- | source3/libads/ldap.c | 51 | ||||
-rw-r--r-- | source3/libads/sasl.c | 7 | ||||
-rw-r--r-- | source3/libads/util.c | 10 | ||||
-rw-r--r-- | source3/libsmb/cliconnect.c | 313 | ||||
-rw-r--r-- | source3/libsmb/clientgen.c | 18 | ||||
-rw-r--r-- | source3/libsmb/clispnego.c | 321 | ||||
-rw-r--r-- | source3/libsmb/ntlmssp.c | 488 | ||||
-rw-r--r-- | source3/libsmb/smbencrypt.c | 111 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_ads.c | 1 | ||||
-rw-r--r-- | source3/param/loadparm.c | 6 | ||||
-rw-r--r-- | source3/utils/net_rpc_join.c | 8 |
16 files changed, 1121 insertions, 673 deletions
diff --git a/source3/include/ads.h b/source3/include/ads.h index 7f23e6506b..304a997b2c 100644 --- a/source3/include/ads.h +++ b/source3/include/ads.h @@ -205,3 +205,6 @@ typedef void **ADS_MODLIST; #define ADS_AUTH_NO_BIND 0x02 #define ADS_AUTH_ANON_BIND 0x04 #define ADS_AUTH_SIMPLE_BIND 0x08 + +/* Kerberos environment variable names */ +#define KRB5_ENV_CCNAME "KRB5CCNAME" diff --git a/source3/include/client.h b/source3/include/client.h index 853ee7ae42..73e29a1fff 100644 --- a/source3/include/client.h +++ b/source3/include/client.h @@ -62,9 +62,10 @@ typedef struct smb_sign_info { BOOL negotiated_smb_signing; BOOL temp_smb_signing; size_t mac_key_len; - uint8 mac_key[44]; + uint8 mac_key[64]; uint32 send_seq_num; uint32 reply_seq_num; + BOOL allow_smb_signing; } smb_sign_info; struct cli_state { @@ -121,6 +122,10 @@ struct cli_state { smb_sign_info sign_info; + /* the session key for this CLI, outside + any per-pipe authenticaion */ + unsigned char user_session_key[16]; + /* * Only used in NT domain calls. */ diff --git a/source3/include/ntlmssp.h b/source3/include/ntlmssp.h index 79d0446a77..4fa4259a6a 100644 --- a/source3/include/ntlmssp.h +++ b/source3/include/ntlmssp.h @@ -86,5 +86,25 @@ typedef struct ntlmssp_state const char *(*get_domain)(void); int server_role; + uint32 expected_state; } NTLMSSP_STATE; +typedef struct ntlmssp_client_state +{ + TALLOC_CTX *mem_ctx; + BOOL unicode; + BOOL use_ntlmv2; + char *user; + char *domain; + char *workstation; + char *password; + + const char *(*get_global_myname)(void); + const char *(*get_domain)(void); + + DATA_BLOB session_key; + + uint32 neg_flags; + +} NTLMSSP_CLIENT_STATE; + diff --git a/source3/lib/data_blob.c b/source3/lib/data_blob.c index 250c3ade88..4056212fc5 100644 --- a/source3/lib/data_blob.c +++ b/source3/lib/data_blob.c @@ -84,6 +84,7 @@ void data_blob_free(DATA_BLOB *d) if (d->free) { (d->free)(d); } + d->length = 0; } } diff --git a/source3/libads/krb5_setpw.c b/source3/libads/krb5_setpw.c index 087b0e9a71..9d8fb8d24c 100644 --- a/source3/libads/krb5_setpw.c +++ b/source3/libads/krb5_setpw.c @@ -24,13 +24,23 @@ #ifdef HAVE_KRB5 #define DEFAULT_KPASSWD_PORT 464 -#define KRB5_KPASSWD_VERS_CHANGEPW 1 -#define KRB5_KPASSWD_VERS_SETPW 0xff80 -#define KRB5_KPASSWD_ACCESSDENIED 5 -#define KRB5_KPASSWD_BAD_VERSION 6 - -/* This implements the Kerb password change protocol as specifed in - * kerb-chg-password-02.txt +#define KRB5_KPASSWD_VERS_CHANGEPW 1 +#define KRB5_KPASSWD_VERS_SETPW 2 +#define KRB5_KPASSWD_VERS_SETPW_MS 0xff80 +#define KRB5_KPASSWD_ACCESSDENIED 5 +#define KRB5_KPASSWD_BAD_VERSION 6 +#define KRB5_KPASSWD_INITIAL_FLAG_NEEDED 7 + +/* Those are defined by kerberos-set-passwd-02.txt and are probably + * not supported by M$ implementation */ +#define KRB5_KPASSWD_POLICY_REJECT 8 +#define KRB5_KPASSWD_BAD_PRINCIPAL 9 +#define KRB5_KPASSWD_ETYPE_NOSUPP 10 + +/* This implements kerberos password change protocol as specified in + * kerb-chg-password-02.txt and kerberos-set-passwd-02.txt + * as well as microsoft version of the protocol + * as specified in kerberos-set-passwd-00.txt */ static DATA_BLOB encode_krb5_setpw(const char *principal, const char *password) { @@ -101,7 +111,8 @@ static DATA_BLOB encode_krb5_setpw(const char *principal, const char *password) return ret; } -static krb5_error_code build_setpw_request(krb5_context context, +static krb5_error_code build_kpasswd_request(uint16 pversion, + krb5_context context, krb5_auth_context auth_context, krb5_data *ap_req, const char *princ, @@ -123,7 +134,14 @@ static krb5_error_code build_setpw_request(krb5_context context, return ret; } - setpw = encode_krb5_setpw(princ, passwd); + /* handle protocol differences in chpw and setpw */ + if (pversion == KRB5_KPASSWD_VERS_CHANGEPW) + setpw = data_blob(passwd, strlen(passwd)); + else if (pversion == KRB5_KPASSWD_VERS_SETPW || + pversion == KRB5_KPASSWD_VERS_SETPW_MS) + setpw = encode_krb5_setpw(princ, passwd); + else + return EINVAL; encoded_setpw.data = setpw.data; encoded_setpw.length = setpw.length; @@ -144,7 +162,7 @@ static krb5_error_code build_setpw_request(krb5_context context, /* see the RFC for details */ p = ((char *)packet->data) + 2; - RSSVAL(p, 0, 0xff80); + RSSVAL(p, 0, pversion); p += 2; RSSVAL(p, 0, ap_req->length); p += 2; @@ -160,6 +178,49 @@ static krb5_error_code build_setpw_request(krb5_context context, return 0; } +static krb5_error_code krb5_setpw_result_code_string(krb5_context context, + int result_code, + char **code_string) +{ + switch (result_code) { + case KRB5_KPASSWD_MALFORMED: + *code_string = "Malformed request error"; + break; + case KRB5_KPASSWD_HARDERROR: + *code_string = "Server error"; + break; + case KRB5_KPASSWD_AUTHERROR: + *code_string = "Authentication error"; + break; + case KRB5_KPASSWD_SOFTERROR: + *code_string = "Password change rejected"; + break; + case KRB5_KPASSWD_ACCESSDENIED: + *code_string = "Client does not have proper authorization"; + break; + case KRB5_KPASSWD_BAD_VERSION: + *code_string = "Protocol version not supported"; + break; + case KRB5_KPASSWD_INITIAL_FLAG_NEEDED: + *code_string = "Authorization ticket must have initial flag set"; + break; + case KRB5_KPASSWD_POLICY_REJECT: + *code_string = "Password rejected due to policy requirements"; + break; + case KRB5_KPASSWD_BAD_PRINCIPAL: + *code_string = "Target principal does not exist"; + break; + case KRB5_KPASSWD_ETYPE_NOSUPP: + *code_string = "Unsupported encryption type"; + break; + default: + *code_string = "Password change failed"; + break; + } + + return(0); +} + static krb5_error_code parse_setpw_reply(krb5_context context, krb5_auth_context auth_context, krb5_data *packet) @@ -194,8 +255,11 @@ static krb5_error_code parse_setpw_reply(krb5_context context, p += 2; vnum = RSVAL(p, 0); p += 2; - - if (vnum != KRB5_KPASSWD_VERS_SETPW && vnum != KRB5_KPASSWD_VERS_CHANGEPW) { + + /* FIXME: According to standard there is only one type of reply */ + if (vnum != KRB5_KPASSWD_VERS_SETPW && + vnum != KRB5_KPASSWD_VERS_SETPW_MS && + vnum != KRB5_KPASSWD_VERS_CHANGEPW) { DEBUG(1,("Bad vnum (%d) from kpasswd server\n", vnum)); return KRB5KDC_ERR_BAD_PVNO; } @@ -247,96 +311,57 @@ static krb5_error_code parse_setpw_reply(krb5_context context, free(clearresult.data); if ((res_code < KRB5_KPASSWD_SUCCESS) || - (res_code >= KRB5_KPASSWD_ACCESSDENIED)) { + (res_code > KRB5_KPASSWD_ETYPE_NOSUPP)) { return KRB5KRB_AP_ERR_MODIFIED; } - - return 0; + + if(res_code == KRB5_KPASSWD_SUCCESS) + return 0; + else { + char *errstr; + krb5_setpw_result_code_string(context, res_code, &errstr); + DEBUG(1, ("Error changing password: %s\n", errstr)); + + switch(res_code) { + case KRB5_KPASSWD_ACCESSDENIED: + return KRB5KDC_ERR_BADOPTION; + break; + case KRB5_KPASSWD_INITIAL_FLAG_NEEDED: + return KRB5KDC_ERR_BADOPTION; + /* return KV5M_ALT_METHOD; MIT-only define */ + break; + case KRB5_KPASSWD_ETYPE_NOSUPP: + return KRB5KDC_ERR_ETYPE_NOSUPP; + break; + case KRB5_KPASSWD_BAD_PRINCIPAL: + return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; + break; + case KRB5_KPASSWD_POLICY_REJECT: + return KRB5KDC_ERR_POLICY; + break; + default: + return KRB5KRB_ERR_GENERIC; + break; + } + } } -ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char *newpw, - int time_offset) +static ADS_STATUS do_krb5_kpasswd_request(krb5_context context, + const char *kdc_host, + uint16 pversion, + krb5_creds *credsp, + const char *princ, + const char *newpw) { - krb5_context context; krb5_auth_context auth_context = NULL; - krb5_principal principal; - char *princ_name; - char *realm; - krb5_creds creds, *credsp; - krb5_ccache ccache; krb5_data ap_req, chpw_req, chpw_rep; int ret, sock, addr_len; struct sockaddr remote_addr, local_addr; krb5_address local_kaddr, remote_kaddr; - ret = krb5_init_context(&context); - if (ret) { - DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret))); - return ADS_ERROR_KRB5(ret); - } - - if (time_offset != 0) { - krb5_set_real_time(context, time(NULL) + time_offset, 0); - } - - ret = krb5_cc_default(context, &ccache); - if (ret) { - krb5_free_context(context); - DEBUG(1,("Failed to get default creds (%s)\n", error_message(ret))); - return ADS_ERROR_KRB5(ret); - } - - ZERO_STRUCT(creds); - - realm = strchr(princ, '@'); - realm++; - - asprintf(&princ_name, "kadmin/changepw@%s", realm); - ret = krb5_parse_name(context, princ_name, &creds.server); - if (ret) { - krb5_free_context(context); - DEBUG(1,("Failed to parse kadmin/changepw (%s)\n", error_message(ret))); - return ADS_ERROR_KRB5(ret); - } - free(princ_name); - - /* parse the principal we got as a function argument */ - ret = krb5_parse_name(context, princ, &principal); - if (ret) { - krb5_free_context(context); - DEBUG(1,("Failed to parse %s (%s)\n", princ_name, error_message(ret))); - return ADS_ERROR_KRB5(ret); - } - - krb5_princ_set_realm(context, creds.server, - krb5_princ_realm(context, principal)); - - ret = krb5_cc_get_principal(context, ccache, &creds.client); - if (ret) { - krb5_free_principal(context, principal); - krb5_free_context(context); - DEBUG(1,("Failed to get principal from ccache (%s)\n", - error_message(ret))); - return ADS_ERROR_KRB5(ret); - } - - ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp); - if (ret) { - krb5_free_principal(context, creds.client); - krb5_free_principal(context, principal); - krb5_free_context(context); - DEBUG(1,("krb5_get_credentials failed (%s)\n", error_message(ret))); - return ADS_ERROR_KRB5(ret); - } - - /* we might have to call krb5_free_creds(...) from now on ... */ ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY, NULL, credsp, &ap_req); if (ret) { - krb5_free_creds(context, credsp); - krb5_free_principal(context, creds.client); - krb5_free_principal(context, principal); - krb5_free_context(context); DEBUG(1,("krb5_mk_req_extended failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } @@ -345,10 +370,7 @@ ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char if (sock == -1) { int rc = errno; free(ap_req.data); - krb5_free_creds(context, credsp); - krb5_free_principal(context, creds.client); - krb5_free_principal(context, principal); - krb5_free_context(context); + krb5_auth_con_free(context, auth_context); DEBUG(1,("failed to open kpasswd socket to %s (%s)\n", kdc_host, strerror(errno))); return ADS_ERROR_SYSTEM(rc); @@ -366,23 +388,17 @@ ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char if (ret) { close(sock); free(ap_req.data); - krb5_free_creds(context, credsp); - krb5_free_principal(context, creds.client); - krb5_free_principal(context, principal); - krb5_free_context(context); + krb5_auth_con_free(context, auth_context); DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } - ret = build_setpw_request(context, auth_context, &ap_req, + ret = build_kpasswd_request(pversion, context, auth_context, &ap_req, princ, newpw, &chpw_req); if (ret) { close(sock); free(ap_req.data); - krb5_free_creds(context, credsp); - krb5_free_principal(context, creds.client); - krb5_free_principal(context, principal); - krb5_free_context(context); + krb5_auth_con_free(context, auth_context); DEBUG(1,("build_setpw_request failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } @@ -391,10 +407,7 @@ ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char close(sock); free(chpw_req.data); free(ap_req.data); - krb5_free_creds(context, credsp); - krb5_free_principal(context, creds.client); - krb5_free_principal(context, principal); - krb5_free_context(context); + krb5_auth_con_free(context, auth_context); DEBUG(1,("send of chpw failed (%s)\n", strerror(errno))); return ADS_ERROR_SYSTEM(errno); } @@ -406,10 +419,7 @@ ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char if (!chpw_rep.data) { close(sock); free(ap_req.data); - krb5_free_creds(context, credsp); - krb5_free_principal(context, creds.client); - krb5_free_principal(context, principal); - krb5_free_context(context); + krb5_auth_con_free(context, auth_context); DEBUG(1,("send of chpw failed (%s)\n", strerror(errno))); errno = ENOMEM; return ADS_ERROR_SYSTEM(errno); @@ -420,10 +430,7 @@ ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char close(sock); free(chpw_rep.data); free(ap_req.data); - krb5_free_creds(context, credsp); - krb5_free_principal(context, creds.client); - krb5_free_principal(context, principal); - krb5_free_context(context); + krb5_auth_con_free(context, auth_context); DEBUG(1,("recv of chpw reply failed (%s)\n", strerror(errno))); return ADS_ERROR_SYSTEM(errno); } @@ -435,10 +442,7 @@ ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char if (ret) { free(chpw_rep.data); free(ap_req.data); - krb5_free_creds(context, credsp); - krb5_free_principal(context, creds.client); - krb5_free_principal(context, principal); - krb5_free_context(context); + krb5_auth_con_free(context, auth_context); DEBUG(1,("krb5_auth_con_setaddrs on reply failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); @@ -449,22 +453,194 @@ ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char if (ret) { free(ap_req.data); - krb5_free_creds(context, credsp); - krb5_free_principal(context, creds.client); - krb5_free_principal(context, principal); - krb5_free_context(context); + krb5_auth_con_free(context, auth_context); DEBUG(1,("parse_setpw_reply failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } free(ap_req.data); + krb5_auth_con_free(context, auth_context); + + return ADS_SUCCESS; +} + +ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char *newpw, + int time_offset) +{ + + ADS_STATUS aret; + krb5_error_code ret; + krb5_context context; + krb5_principal principal; + char *princ_name; + char *realm; + krb5_creds creds, *credsp; + krb5_ccache ccache; + + ret = krb5_init_context(&context); + if (ret) { + DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret))); + return ADS_ERROR_KRB5(ret); + } + + if (time_offset != 0) { + krb5_set_real_time(context, time(NULL) + time_offset, 0); + } + + ret = krb5_cc_default(context, &ccache); + if (ret) { + krb5_free_context(context); + DEBUG(1,("Failed to get default creds (%s)\n", error_message(ret))); + return ADS_ERROR_KRB5(ret); + } + + ZERO_STRUCT(creds); + + realm = strchr(princ, '@'); + realm++; + + asprintf(&princ_name, "kadmin/changepw@%s", realm); + ret = krb5_parse_name(context, princ_name, &creds.server); + if (ret) { + krb5_free_context(context); + DEBUG(1,("Failed to parse kadmin/changepw (%s)\n", error_message(ret))); + return ADS_ERROR_KRB5(ret); + } + free(princ_name); + + /* parse the principal we got as a function argument */ + ret = krb5_parse_name(context, princ, &principal); + if (ret) { + krb5_free_context(context); + DEBUG(1,("Failed to parse %s (%s)\n", princ_name, error_message(ret))); + return ADS_ERROR_KRB5(ret); + } + + krb5_princ_set_realm(context, creds.server, + krb5_princ_realm(context, principal)); + + ret = krb5_cc_get_principal(context, ccache, &creds.client); + if (ret) { + krb5_free_principal(context, principal); + krb5_free_context(context); + DEBUG(1,("Failed to get principal from ccache (%s)\n", + error_message(ret))); + return ADS_ERROR_KRB5(ret); + } + + ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp); + if (ret) { + krb5_free_principal(context, creds.client); + krb5_free_principal(context, principal); + krb5_free_context(context); + DEBUG(1,("krb5_get_credentials failed (%s)\n", error_message(ret))); + return ADS_ERROR_KRB5(ret); + } + + /* we might have to call krb5_free_creds(...) from now on ... */ + + aret = do_krb5_kpasswd_request(context, kdc_host, + KRB5_KPASSWD_VERS_SETPW_MS, + credsp, princ, newpw); + krb5_free_creds(context, credsp); krb5_free_principal(context, creds.client); + krb5_free_principal(context, creds.server); krb5_free_principal(context, principal); krb5_free_context(context); - return ADS_SUCCESS; + return aret; +} + +/* + we use a prompter to avoid a crash bug in the kerberos libs when + dealing with empty passwords + this prompter is just a string copy ... +*/ +static krb5_error_code +kerb_prompter(krb5_context ctx, void *data, + const char *name, + const char *banner, + int num_prompts, + krb5_prompt prompts[]) +{ + if (num_prompts == 0) return 0; + + memset(prompts[0].reply->data, 0, prompts[0].reply->length); + if (prompts[0].reply->length > 0) { + if (data) { + strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1); + prompts[0].reply->length = strlen(prompts[0].reply->data); + } else { + prompts[0].reply->length = 0; + } + } + return 0; +} + +ADS_STATUS krb5_chg_password(const char *kdc_host, + const char *principal, + const char *oldpw, + const char *newpw, + int time_offset) +{ + ADS_STATUS aret; + krb5_error_code ret; + krb5_context context; + krb5_principal princ; + krb5_get_init_creds_opt opts; + krb5_creds creds; + char *chpw_princ = NULL, *password; + + ret = krb5_init_context(&context); + if (ret) { + DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret))); + return ADS_ERROR_KRB5(ret); + } + + if ((ret = krb5_parse_name(context, principal, + &princ))) { + krb5_free_context(context); + DEBUG(1,("Failed to parse %s (%s)\n", principal, error_message(ret))); + return ADS_ERROR_KRB5(ret); + } + + krb5_get_init_creds_opt_init(&opts); + krb5_get_init_creds_opt_set_tkt_life(&opts, 5*60); + krb5_get_init_creds_opt_set_renew_life(&opts, 0); + krb5_get_init_creds_opt_set_forwardable(&opts, 0); + krb5_get_init_creds_opt_set_proxiable(&opts, 0); + + /* We have to obtain an INITIAL changepw ticket for changing password */ + asprintf(&chpw_princ, "kadmin/changepw@%s", + (char *) krb5_princ_realm(context, princ)); + password = strdup(oldpw); + ret = krb5_get_init_creds_password(context, &creds, princ, password, + kerb_prompter, NULL, + 0, chpw_princ, &opts); + SAFE_FREE(chpw_princ); + SAFE_FREE(password); + + if (ret) { + if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) + DEBUG(1,("Password incorrect while getting initial ticket")); + else + DEBUG(1,("krb5_get_init_creds_password failed (%s)\n", error_message(ret))); + + krb5_free_principal(context, princ); + krb5_free_context(context); + return ADS_ERROR_KRB5(ret); + } + + aret = do_krb5_kpasswd_request(context, kdc_host, + KRB5_KPASSWD_VERS_CHANGEPW, + &creds, principal, newpw); + + krb5_free_principal(context, princ); + krb5_free_context(context); + + return aret; } @@ -480,7 +656,12 @@ ADS_STATUS kerberos_set_password(const char *kpasswd_server, return ADS_ERROR_KRB5(ret); } - return krb5_set_password(kpasswd_server, target_principal, new_password, time_offset); + if (!strcmp(auth_principal, target_principal)) + return krb5_chg_password(kpasswd_server, target_principal, + auth_password, new_password, time_offset); + else + return krb5_set_password(kpasswd_server, target_principal, + new_password, time_offset); } @@ -515,4 +696,6 @@ ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads, return status; } + + #endif diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c index 67669fc078..bc90e90ea0 100644 --- a/source3/libads/ldap.c +++ b/source3/libads/ldap.c @@ -816,18 +816,18 @@ static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, { int curmod; LDAPMod **modlist = (LDAPMod **) *mods; - void **values; + struct berval **ber_values; + char **char_values; if (!invals) { - values = NULL; mod_op = LDAP_MOD_DELETE; } else { if (mod_op & LDAP_MOD_BVALUES) - values = (void **) ads_dup_values(ctx, - (const struct berval **)invals); + ber_values = ads_dup_values(ctx, + (const struct berval **)invals); else - values = (void **) ads_push_strvals(ctx, - (const char **) invals); + char_values = ads_push_strvals(ctx, + (const char **) invals); } /* find the first empty slot */ @@ -846,10 +846,14 @@ static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod)))) return ADS_ERROR(LDAP_NO_MEMORY); modlist[curmod]->mod_type = talloc_strdup(ctx, name); - if (mod_op & LDAP_MOD_BVALUES) - modlist[curmod]->mod_bvalues = (struct berval **) values; - else - modlist[curmod]->mod_values = (char **) values; + if (mod_op & LDAP_MOD_BVALUES) { + modlist[curmod]->mod_bvalues = ber_values; + } else if (mod_op & LDAP_MOD_DELETE) { + modlist[curmod]->mod_values = NULL; + } else { + modlist[curmod]->mod_values = char_values; + } + modlist[curmod]->mod_op = mod_op; return ADS_ERROR(LDAP_SUCCESS); } @@ -1500,16 +1504,24 @@ ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn) #endif if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY); - bval.bv_len = sd_size; - bval.bv_val = talloc(ctx, sd_size); + bval.bv_len = prs_offset(&ps_wire); + bval.bv_val = talloc(ctx, bval.bv_len); if (!bval.bv_val) { ret = ADS_ERROR(LDAP_NO_MEMORY); goto ads_set_sd_error; } - prs_copy_all_data_out(bval.bv_val, &ps_wire); - ads_mod_ber(ctx, &mods, attrs[0], &bval); - ret = ads_gen_mod(ads, dn, mods); + prs_set_offset(&ps_wire, 0); + + if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) { + ret = ADS_ERROR(LDAP_NO_MEMORY); + goto ads_set_sd_error; + } + + ret = ads_mod_ber(ctx, &mods, attrs[0], &bval); + if (ADS_ERR_OK(ret)) { + ret = ads_gen_mod(ads, dn, mods); + } ads_set_sd_error: ads_msgfree(ads, res); @@ -1554,7 +1566,7 @@ char *ads_pull_string(ADS_STRUCT *ads, char **values; char *ret = NULL; char *ux_string; - int rc; + size_t rc; values = ldap_get_values(ads->ld, msg, field); if (!values) @@ -1563,7 +1575,7 @@ char *ads_pull_string(ADS_STRUCT *ads, if (values[0]) { rc = pull_utf8_talloc(mem_ctx, &ux_string, values[0]); - if (rc != -1) + if (rc != (size_t)-1) ret = ux_string; } @@ -1725,8 +1737,11 @@ int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, count = 0; for (i=0; values[i]; i++) { ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]); - if (ret) + if (ret) { + fstring sid; + DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count]))); count++; + } } ldap_value_free_len(values); diff --git a/source3/libads/sasl.c b/source3/libads/sasl.c index 7aa77bf2a2..29d4533a54 100644 --- a/source3/libads/sasl.c +++ b/source3/libads/sasl.c @@ -241,7 +241,12 @@ static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads) ADS_STATUS status; krb5_principal principal; krb5_context ctx; - krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL}; + krb5_enctype enc_types[] = { +#ifdef ENCTYPE_ARCFOUR_HMAC + ENCTYPE_ARCFOUR_HMAC, +#endif + ENCTYPE_DES_CBC_MD5, + ENCTYPE_NULL}; gss_OID_desc nt_principal = {10, "\052\206\110\206\367\022\001\002\002\002"}; diff --git a/source3/libads/util.c b/source3/libads/util.c index 021f2d93e4..335cabc952 100644 --- a/source3/libads/util.c +++ b/source3/libads/util.c @@ -29,7 +29,7 @@ ADS_STATUS ads_change_trust_account_password(ADS_STRUCT *ads, char *host_princip char *new_password; char *service_principal; ADS_STATUS ret; - + if ((password = secrets_fetch_machine_password()) == NULL) { DEBUG(1,("Failed to retrieve password for principal %s\n", host_principal)); return ADS_ERROR_SYSTEM(ENOENT); @@ -38,15 +38,17 @@ ADS_STATUS ads_change_trust_account_password(ADS_STRUCT *ads, char *host_princip tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH); new_password = strdup(tmp_password); asprintf(&service_principal, "HOST/%s", host_principal); - - ret = kerberos_set_password(ads->auth.kdc_server, host_principal, password, - service_principal, new_password, ads->auth.time_offset); + + ret = kerberos_set_password(ads->auth.kdc_server, service_principal, password, service_principal, new_password, ads->auth.time_offset); + + if (!ADS_ERR_OK(ret)) goto failed; if (!secrets_store_machine_password(new_password)) { DEBUG(1,("Failed to save machine password\n")); return ADS_ERROR_SYSTEM(EACCES); } +failed: SAFE_FREE(service_principal); SAFE_FREE(new_password); diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index 389b7a1733..4962ffa3c9 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -2,7 +2,7 @@ Unix SMB/CIFS implementation. client connect/disconnect routines Copyright (C) Andrew Tridgell 1994-1998 - Copyright (C) Andrew Barteltt 2001-2002 + Copyright (C) Andrew Bartlett 2001-2003 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 @@ -45,7 +45,7 @@ static const struct { ****************************************************************************/ static BOOL cli_session_setup_lanman2(struct cli_state *cli, const char *user, - const char *pass, int passlen, const char *workgroup) + const char *pass, size_t passlen, const char *workgroup) { fstring pword; char *p; @@ -228,7 +228,7 @@ static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user, return True; } -static void set_signing_on_cli (struct cli_state *cli, const char* pass, uint8 response[24]) +static void set_signing_on_cli (struct cli_state *cli, uint8 user_session_key[16], DATA_BLOB response) { uint8 zero_sig[8]; ZERO_STRUCT(zero_sig); @@ -242,12 +242,18 @@ static void set_signing_on_cli (struct cli_state *cli, const char* pass, uint8 r DEBUG(3, ("smb signing enabled!\n")); cli->sign_info.use_smb_signing = True; - cli_calculate_mac_key(cli, pass, response); + cli_calculate_mac_key(cli, user_session_key, response); } else { DEBUG(5, ("smb signing NOT enabled!\n")); } } +static void set_cli_session_key (struct cli_state *cli, DATA_BLOB session_key) +{ + memcpy(cli->user_session_key, session_key.data, MIN(session_key.length, sizeof(cli->user_session_key))); +} + + static void set_temp_signing_on_cli(struct cli_state *cli) { if (cli->sign_info.negotiated_smb_signing) @@ -265,37 +271,54 @@ static void set_temp_signing_on_cli(struct cli_state *cli) ****************************************************************************/ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user, - const char *pass, int passlen, - const char *ntpass, int ntpasslen, + const char *pass, size_t passlen, + const char *ntpass, size_t ntpasslen, const char *workgroup) { uint32 capabilities = cli_session_setup_capabilities(cli); - uchar pword[24]; - uchar ntpword[24]; + DATA_BLOB lm_response = data_blob(NULL, 0); + DATA_BLOB nt_response = data_blob(NULL, 0); + DATA_BLOB session_key = data_blob(NULL, 0); + BOOL ret = False; char *p; - BOOL have_plaintext = False; - - if (passlen > sizeof(pword) || ntpasslen > sizeof(ntpword)) - return False; if (passlen != 24) { - /* non encrypted password supplied. Ignore ntpass. */ - passlen = 24; - ntpasslen = 24; - SMBencrypt(pass,cli->secblob.data,pword); - SMBNTencrypt(pass,cli->secblob.data,ntpword); + if (lp_client_ntlmv2_auth()) { + DATA_BLOB server_chal; + + server_chal = data_blob(cli->secblob.data, MIN(cli->secblob.length, 8)); + + if (!SMBNTLMv2encrypt(user, workgroup, pass, server_chal, + &lm_response, &nt_response, &session_key)) { + data_blob_free(&server_chal); + return False; + } + data_blob_free(&server_chal); + + } else { + uchar nt_hash[16]; + E_md4hash(pass, nt_hash); + + /* non encrypted password supplied. Ignore ntpass. */ + if (lp_client_lanman_auth()) { + lm_response = data_blob(NULL, 24); + SMBencrypt(pass,cli->secblob.data,lm_response.data); + } + + nt_response = data_blob(NULL, 24); + SMBNTencrypt(pass,cli->secblob.data,nt_response.data); + session_key = data_blob(NULL, 16); + SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); + } - have_plaintext = True; set_temp_signing_on_cli(cli); } else { /* pre-encrypted password supplied. Only used for security=server, can't do signing becouse we don't have oringial key */ - memcpy(pword, pass, 24); - if (ntpasslen == 24) - memcpy(ntpword, ntpass, 24); - else - ZERO_STRUCT(ntpword); + + lm_response = data_blob(pass, passlen); + nt_response = data_blob(ntpass, ntpasslen); } /* send a session setup command */ @@ -310,28 +333,33 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user, SSVAL(cli->outbuf,smb_vwv3,2); SSVAL(cli->outbuf,smb_vwv4,cli->pid); SIVAL(cli->outbuf,smb_vwv5,cli->sesskey); - SSVAL(cli->outbuf,smb_vwv7,passlen); - SSVAL(cli->outbuf,smb_vwv8,ntpasslen); + SSVAL(cli->outbuf,smb_vwv7,lm_response.length); + SSVAL(cli->outbuf,smb_vwv8,nt_response.length); SIVAL(cli->outbuf,smb_vwv11,capabilities); p = smb_buf(cli->outbuf); - memcpy(p,pword,passlen); p += passlen; - memcpy(p,ntpword,ntpasslen); p += ntpasslen; + if (lm_response.length) { + memcpy(p,lm_response.data, lm_response.length); p += lm_response.length; + } + if (nt_response.length) { + memcpy(p,nt_response.data, nt_response.length); p += nt_response.length; + } p += clistr_push(cli, p, user, -1, STR_TERMINATE); p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE); p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE); p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE); cli_setup_bcc(cli, p); - if (!cli_send_smb(cli)) - return False; - - if (!cli_receive_smb(cli)) - return False; + if (!cli_send_smb(cli) || !cli_receive_smb(cli)) { + ret = False; + goto end; + } show_msg(cli->inbuf); - if (cli_is_error(cli)) - return False; + if (cli_is_error(cli)) { + ret = False; + goto end; + } /* use the returned vuid from now on */ cli->vuid = SVAL(cli->inbuf,smb_uid); @@ -343,11 +371,16 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user, fstrcpy(cli->user_name, user); - if (have_plaintext) { + if (session_key.data) { /* Have plaintext orginal */ - set_signing_on_cli(cli, pass, ntpword); + set_cli_session_key(cli, session_key); + set_signing_on_cli(cli, session_key.data, nt_response); } - + +end: + data_blob_free(&lm_response); + data_blob_free(&nt_response); + data_blob_free(&session_key); return True; } @@ -359,11 +392,9 @@ static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob) { uint32 capabilities = cli_session_setup_capabilities(cli); char *p; - DATA_BLOB blob2; + DATA_BLOB blob2 = data_blob(NULL, 0); uint32 len; - blob2 = data_blob(NULL, 0); - capabilities |= CAP_EXTENDED_SECURITY; /* send a session setup command */ @@ -420,6 +451,13 @@ static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob) #ifdef HAVE_KRB5 /**************************************************************************** + Use in-memory credentials cache +****************************************************************************/ +static void use_in_memory_ccache(void) { + setenv(KRB5_ENV_CCNAME, "MEMORY:cliconnect", 1); +} + +/**************************************************************************** Do a spnego/kerberos encrypted session setup. ****************************************************************************/ @@ -456,126 +494,87 @@ static BOOL cli_session_setup_kerberos(struct cli_state *cli, const char *princi static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, const char *user, const char *pass, const char *workgroup) { - DATA_BLOB msg1, struct_blob; - DATA_BLOB blob, chal1, chal2, auth, challenge_blob; - uint8 challenge[8]; - uint8 nthash[24], lmhash[24], sess_key[16]; - uint32 neg_flags, chal_flags, ntlmssp_command, unkn1, unkn2; - pstring server_domain; /* FIX THIS, SHOULD be UCS2-LE */ - - neg_flags = NTLMSSP_NEGOTIATE_UNICODE | - NTLMSSP_NEGOTIATE_128 | - NTLMSSP_NEGOTIATE_NTLM | - NTLMSSP_REQUEST_TARGET; - - memset(sess_key, 0, 16); - - DEBUG(10, ("sending NTLMSSP_NEGOTIATE\n")); - - /* generate the ntlmssp negotiate packet */ - msrpc_gen(&blob, "CddAA", - "NTLMSSP", - NTLMSSP_NEGOTIATE, - neg_flags, - workgroup, - cli->calling.name); - DEBUG(10, ("neg_flags: %0X, workgroup: %s, calling name %s\n", - neg_flags, workgroup, cli->calling.name)); - /* and wrap it in a SPNEGO wrapper */ - msg1 = gen_negTokenInit(OID_NTLMSSP, blob); - data_blob_free(&blob); - - /* now send that blob on its way */ - blob = cli_session_setup_blob(cli, msg1); - - data_blob_free(&msg1); + struct ntlmssp_client_state *ntlmssp_state; + NTSTATUS nt_status; + int turn = 1; + DATA_BLOB msg1; + DATA_BLOB blob; + DATA_BLOB blob_in = data_blob(NULL, 0); + DATA_BLOB blob_out; - if (!NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_MORE_PROCESSING_REQUIRED)) + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) { return False; + } -#if 0 - file_save("chal.dat", blob.data, blob.length); -#endif - - /* the server gives us back two challenges */ - if (!spnego_parse_challenge(blob, &chal1, &chal2)) { - DEBUG(3,("Failed to parse challenges\n")); + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) { return False; } - - data_blob_free(&blob); - - /* - * Ok, chal1 and chal2 are actually two identical copies of - * the NTLMSSP Challenge BLOB, and they contain, encoded in them - * the challenge to use. - */ - - if (!msrpc_parse(&chal1, "CdUdbddB", - "NTLMSSP", - &ntlmssp_command, - &server_domain, - &chal_flags, - &challenge_blob, 8, - &unkn1, &unkn2, - &struct_blob)) { - DEBUG(0, ("Failed to parse the NTLMSSP Challenge\n")); - return False; + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, workgroup))) { + return False; } - - if (ntlmssp_command != NTLMSSP_CHALLENGE) { - DEBUG(0, ("NTLMSSP Response != NTLMSSP_CHALLENGE. Got %0X\n", - ntlmssp_command)); + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, pass))) { return False; } - - DEBUG(10, ("Challenge:\n")); - dump_data(10, challenge_blob.data, 8); - - /* encrypt the password with the challenge which is in the blob */ - memcpy(challenge, challenge_blob.data, 8); - SMBencrypt(pass, challenge,lmhash); - SMBNTencrypt(pass, challenge,nthash); - data_blob_free(&challenge_blob); - -#if 0 - file_save("nthash.dat", nthash, 24); - file_save("lmhash.dat", lmhash, 24); - file_save("chal1.dat", chal1.data, chal1.length); -#endif - - data_blob_free(&chal1); - data_blob_free(&chal2); - /* this generates the actual auth packet */ - msrpc_gen(&blob, "CdBBUUUBd", - "NTLMSSP", - NTLMSSP_AUTH, - lmhash, 24, - nthash, 24, - workgroup, - user, - cli->calling.name, - sess_key, 0, - neg_flags); - - /* wrap it in SPNEGO */ - auth = spnego_gen_auth(blob); - - data_blob_free(&blob); - - /* now send the auth packet and we should be done */ - blob = cli_session_setup_blob(cli, auth); + ntlmssp_state->use_ntlmv2 = lp_client_ntlmv2_auth(); + + do { + nt_status = ntlmssp_client_update(ntlmssp_state, + blob_in, &blob_out); + data_blob_free(&blob_in); + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + if (turn == 1) { + /* and wrap it in a SPNEGO wrapper */ + msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out); + } else { + /* wrap it in SPNEGO */ + msg1 = spnego_gen_auth(blob_out); + } + + /* now send that blob on its way */ + blob = cli_session_setup_blob(cli, msg1); + data_blob_free(&msg1); + nt_status = cli_nt_error(cli); + } + + if (!blob.length) { + if (NT_STATUS_IS_OK(nt_status)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + } + } else if ((turn == 1) && + NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + DATA_BLOB tmp_blob = data_blob(NULL, 0); + /* the server might give us back two challenges */ + if (!spnego_parse_challenge(blob, &blob_in, + &tmp_blob)) { + DEBUG(3,("Failed to parse challenges\n")); + nt_status = NT_STATUS_INVALID_PARAMETER; + } + data_blob_free(&tmp_blob); + } else { + /* the server might give us back two challenges */ + if (!spnego_parse_auth_response(blob, nt_status, + &blob_in)) { + DEBUG(3,("Failed to parse auth response\n")); + if (NT_STATUS_IS_OK(nt_status) + || NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) + nt_status = NT_STATUS_INVALID_PARAMETER; + } + } + data_blob_free(&blob); + data_blob_free(&blob_out); + turn++; + } while (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)); - data_blob_free(&auth); - data_blob_free(&blob); + if (NT_STATUS_IS_OK(nt_status)) { + set_cli_session_key(cli, ntlmssp_state->session_key); + } - if (cli_is_error(cli)) + if (!NT_STATUS_IS_OK(ntlmssp_client_end(&ntlmssp_state))) { return False; + } - set_signing_on_cli(cli, pass, nthash); - - return True; + return (NT_STATUS_IS_OK(nt_status)); } /**************************************************************************** @@ -628,7 +627,22 @@ static BOOL cli_session_setup_spnego(struct cli_state *cli, const char *user, fstrcpy(cli->user_name, user); #ifdef HAVE_KRB5 + /* If password is set we reauthenticate to kerberos server + * and do not store results */ + if (got_kerberos_mechanism && cli->use_kerberos) { + if (*pass) { + int ret; + + use_in_memory_ccache(); + ret = kerberos_kinit_password(user, pass, 0 /* no time correction for now */); + + if (ret){ + DEBUG(0, ("Kinit failed: %s\n", error_message(ret))); + return False; + } + } + return cli_session_setup_kerberos(cli, principal, workgroup); } #endif @@ -942,7 +956,10 @@ BOOL cli_negprot(struct cli_state *cli) smb_buflen(cli->inbuf)-8, STR_UNICODE|STR_NOALIGN); } - if ((cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED)) + if ((cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED)) + cli->sign_info.negotiated_smb_signing = True; + + if ((cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) && cli->sign_info.allow_smb_signing) cli->sign_info.negotiated_smb_signing = True; } else if (cli->protocol >= PROTOCOL_LANMAN1) { diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c index ed1286d627..9598f4ac96 100644 --- a/source3/libsmb/clientgen.c +++ b/source3/libsmb/clientgen.c @@ -114,9 +114,14 @@ BOOL cli_receive_smb(struct cli_state *cli) cli->smb_rw_error = smb_read_error; close(cli->fd); cli->fd = -1; + return ret; } - return ret; + if (!cli_check_sign_mac(cli)) { + DEBUG(0, ("SMB Signiture verification failed on incoming packet!\n")); + return False; + }; + return True; } /**************************************************************************** @@ -246,8 +251,10 @@ struct cli_state *cli_initialise(struct cli_state *cli) cli->outbuf = (char *)malloc(cli->bufsize); cli->inbuf = (char *)malloc(cli->bufsize); cli->oplock_handler = cli_oplock_ack; - if (lp_use_spnego()) - cli->use_spnego = True; + + cli->use_spnego = lp_client_use_spnego(); + + cli->capabilities = CAP_UNICODE | CAP_STATUS32; /* Set the CLI_FORCE_DOSERR environment variable to test client routines using DOS errors instead of STATUS32 @@ -255,9 +262,8 @@ struct cli_state *cli_initialise(struct cli_state *cli) if (getenv("CLI_FORCE_DOSERR")) cli->force_dos_errors = True; - /* A way to attempt to force SMB signing */ - if (getenv("CLI_FORCE_SMB_SIGNING")) - cli->sign_info.negotiated_smb_signing = True; + if (lp_client_signing()) + cli->sign_info.allow_smb_signing = True; if (!cli->outbuf || !cli->inbuf) goto error; diff --git a/source3/libsmb/clispnego.c b/source3/libsmb/clispnego.c index 41b5c3f990..e93f1855dd 100644 --- a/source3/libsmb/clispnego.c +++ b/source3/libsmb/clispnego.c @@ -481,297 +481,50 @@ DATA_BLOB spnego_gen_auth_response(DATA_BLOB *ntlmssp_reply, NTSTATUS nt_status) } /* - this is a tiny msrpc packet generator. I am only using this to - avoid tying this code to a particular varient of our rpc code. This - generator is not general enough for all our rpc needs, its just - enough for the spnego/ntlmssp code - - format specifiers are: - - U = unicode string (input is unix string) - a = address (input is BOOL unicode, char *unix_string) - (1 byte type, 1 byte length, unicode/ASCII string, all inline) - A = ASCII string (input is unix string) - B = data blob (pointer + length) - b = data blob in header (pointer + length) - D - d = word (4 bytes) - C = constant ascii string - */ -BOOL msrpc_gen(DATA_BLOB *blob, - const char *format, ...) + parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords +*/ +BOOL spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status, + DATA_BLOB *auth) { - int i, n; - va_list ap; - char *s; - uint8 *b; - int head_size=0, data_size=0; - int head_ofs, data_ofs; - BOOL unicode; - - /* first scan the format to work out the header and body size */ - va_start(ap, format); - for (i=0; format[i]; i++) { - switch (format[i]) { - case 'U': - s = va_arg(ap, char *); - head_size += 8; - data_size += str_charnum(s) * 2; - break; - case 'A': - s = va_arg(ap, char *); - head_size += 8; - data_size += str_ascii_charnum(s); - break; - case 'a': - unicode = va_arg(ap, BOOL); - n = va_arg(ap, int); - s = va_arg(ap, char *); - if (unicode) { - data_size += (str_charnum(s) * 2) + 4; - } else { - data_size += (str_ascii_charnum(s)) + 4; - } - break; - case 'B': - b = va_arg(ap, uint8 *); - head_size += 8; - data_size += va_arg(ap, int); - break; - case 'b': - b = va_arg(ap, uint8 *); - head_size += va_arg(ap, int); - break; - case 'd': - n = va_arg(ap, int); - head_size += 4; - break; - case 'C': - s = va_arg(ap, char *); - head_size += str_charnum(s) + 1; - break; - } - } - va_end(ap); - - /* allocate the space, then scan the format again to fill in the values */ - *blob = data_blob(NULL, head_size + data_size); - - head_ofs = 0; - data_ofs = head_size; - - va_start(ap, format); - for (i=0; format[i]; i++) { - switch (format[i]) { - case 'U': - s = va_arg(ap, char *); - n = str_charnum(s); - SSVAL(blob->data, head_ofs, n*2); head_ofs += 2; - SSVAL(blob->data, head_ofs, n*2); head_ofs += 2; - SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4; - push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN); - data_ofs += n*2; - break; - case 'A': - s = va_arg(ap, char *); - n = str_ascii_charnum(s); - SSVAL(blob->data, head_ofs, n); head_ofs += 2; - SSVAL(blob->data, head_ofs, n); head_ofs += 2; - SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4; - push_string(NULL, blob->data+data_ofs, s, n, STR_ASCII|STR_NOALIGN); - data_ofs += n; - break; - case 'a': - unicode = va_arg(ap, BOOL); - n = va_arg(ap, int); - SSVAL(blob->data, data_ofs, n); data_ofs += 2; - s = va_arg(ap, char *); - if (unicode) { - n = str_charnum(s); - SSVAL(blob->data, data_ofs, n*2); data_ofs += 2; - if (0 < n) { - push_string(NULL, blob->data+data_ofs, s, n*2, - STR_UNICODE|STR_NOALIGN); - } - data_ofs += n*2; - } else { - n = str_ascii_charnum(s); - SSVAL(blob->data, data_ofs, n); data_ofs += 2; - if (0 < n) { - push_string(NULL, blob->data+data_ofs, s, n, - STR_ASCII|STR_NOALIGN); - } - data_ofs += n; - } - break; - - case 'B': - b = va_arg(ap, uint8 *); - n = va_arg(ap, int); - SSVAL(blob->data, head_ofs, n); head_ofs += 2; - SSVAL(blob->data, head_ofs, n); head_ofs += 2; - SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4; - memcpy(blob->data+data_ofs, b, n); - data_ofs += n; - break; - case 'd': - n = va_arg(ap, int); - SIVAL(blob->data, head_ofs, n); head_ofs += 4; - break; - case 'b': - b = va_arg(ap, uint8 *); - n = va_arg(ap, int); - memcpy(blob->data + head_ofs, b, n); - head_ofs += n; - break; - case 'C': - s = va_arg(ap, char *); - head_ofs += push_string(NULL, blob->data+head_ofs, s, -1, - STR_ASCII|STR_TERMINATE); - break; - } - } - va_end(ap); + ASN1_DATA data; + uint8 negResult; - return True; -} + if (NT_STATUS_IS_OK(nt_status)) { + negResult = SPNEGO_NEG_RESULT_ACCEPT; + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + negResult = SPNEGO_NEG_RESULT_INCOMPLETE; + } else { + negResult = SPNEGO_NEG_RESULT_REJECT; + } + asn1_load(&data, blob); + asn1_start_tag(&data, ASN1_CONTEXT(1)); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_start_tag(&data, ASN1_CONTEXT(0)); + asn1_check_enumerated(&data, negResult); + asn1_end_tag(&data); -/* - this is a tiny msrpc packet parser. This the the partner of msrpc_gen - - format specifiers are: - - U = unicode string (output is unix string) - A = ascii string - B = data blob - b = data blob in header - d = word (4 bytes) - C = constant ascii string - */ -BOOL msrpc_parse(DATA_BLOB *blob, - const char *format, ...) -{ - int i; - va_list ap; - char **ps, *s; - DATA_BLOB *b; - int head_ofs = 0; - uint16 len1, len2; - uint32 ptr; - uint32 *v; - pstring p; - - va_start(ap, format); - for (i=0; format[i]; i++) { - switch (format[i]) { - case 'U': - len1 = SVAL(blob->data, head_ofs); head_ofs += 2; - len2 = SVAL(blob->data, head_ofs); head_ofs += 2; - ptr = IVAL(blob->data, head_ofs); head_ofs += 4; - /* make sure its in the right format - be strict */ - if (len1 != len2 || (len1&1) || ptr + len1 > blob->length) { - return False; - } - ps = va_arg(ap, char **); - pull_string(NULL, p, blob->data + ptr, -1, len1, - STR_UNICODE|STR_NOALIGN); - (*ps) = strdup(p); - break; - case 'A': - len1 = SVAL(blob->data, head_ofs); head_ofs += 2; - len2 = SVAL(blob->data, head_ofs); head_ofs += 2; - ptr = IVAL(blob->data, head_ofs); head_ofs += 4; - - /* make sure its in the right format - be strict */ - if (len1 != len2 || ptr + len1 > blob->length) { - return False; - } - ps = va_arg(ap, char **); - if (0 < len1) { - pull_string(NULL, p, blob->data + ptr, -1, - len1, STR_ASCII|STR_NOALIGN); - (*ps) = strdup(p); - } else { - (*ps) = NULL; - } - break; - case 'B': - len1 = SVAL(blob->data, head_ofs); head_ofs += 2; - len2 = SVAL(blob->data, head_ofs); head_ofs += 2; - ptr = IVAL(blob->data, head_ofs); head_ofs += 4; - /* make sure its in the right format - be strict */ - if (len1 != len2 || ptr + len1 > blob->length) { - return False; - } - b = (DATA_BLOB *)va_arg(ap, void *); - *b = data_blob(blob->data + ptr, len1); - break; - case 'b': - b = (DATA_BLOB *)va_arg(ap, void *); - len1 = va_arg(ap, unsigned); - *b = data_blob(blob->data + head_ofs, len1); - head_ofs += len1; - break; - case 'd': - v = va_arg(ap, uint32 *); - *v = IVAL(blob->data, head_ofs); head_ofs += 4; - break; - case 'C': - s = va_arg(ap, char *); - head_ofs += pull_string(NULL, p, blob->data+head_ofs, sizeof(p), - blob->length - head_ofs, - STR_ASCII|STR_TERMINATE); - if (strcmp(s, p) != 0) { - return False; - } - break; - } + if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) { + asn1_start_tag(&data,ASN1_CONTEXT(1)); + asn1_check_OID(&data, OID_NTLMSSP); + asn1_end_tag(&data); + + asn1_start_tag(&data,ASN1_CONTEXT(2)); + asn1_read_OctetString(&data, auth); + asn1_end_tag(&data); } - va_end(ap); - return True; -} + asn1_end_tag(&data); + asn1_end_tag(&data); -/** - * Print out the NTLMSSP flags for debugging - */ + if (data.has_error) { + DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data.ofs)); + asn1_free(&data); + data_blob_free(auth); + return False; + } -void debug_ntlmssp_flags(uint32 neg_flags) -{ - DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags)); - - if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_UNICODE\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_OEM) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_OEM\n")); - if (neg_flags & NTLMSSP_REQUEST_TARGET) - DEBUGADD(4, (" NTLMSSP_REQUEST_TARGET\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_SIGN) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SIGN\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_SEAL) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SEAL\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_LM_KEY\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NETWARE\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_NTLM) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM2\n")); - if (neg_flags & NTLMSSP_CHAL_TARGET_INFO) - DEBUGADD(4, (" NTLMSSP_CHAL_TARGET_INFO\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_128) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_128\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_KEY_EXCH\n")); + asn1_free(&data); + return True; } diff --git a/source3/libsmb/ntlmssp.c b/source3/libsmb/ntlmssp.c index 5b608e0a7a..e1509f6b63 100644 --- a/source3/libsmb/ntlmssp.c +++ b/source3/libsmb/ntlmssp.c @@ -24,11 +24,53 @@ #include "includes.h" /** - * Default challange generation code. + * Print out the NTLMSSP flags for debugging + * @param neg_flags The flags from the packet + */ + +void debug_ntlmssp_flags(uint32 neg_flags) +{ + DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags)); + + if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_UNICODE\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_OEM) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_OEM\n")); + if (neg_flags & NTLMSSP_REQUEST_TARGET) + DEBUGADD(4, (" NTLMSSP_REQUEST_TARGET\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_SIGN) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SIGN\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_SEAL) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SEAL\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_LM_KEY\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NETWARE\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_NTLM) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM2\n")); + if (neg_flags & NTLMSSP_CHAL_TARGET_INFO) + DEBUGADD(4, (" NTLMSSP_CHAL_TARGET_INFO\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_128) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_128\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_KEY_EXCH\n")); +} + +/** + * Default challenge generation code. * */ - static const uint8 *get_challenge(struct ntlmssp_state *ntlmssp_state) { static uchar chal[8]; @@ -37,71 +79,17 @@ static const uint8 *get_challenge(struct ntlmssp_state *ntlmssp_state) return chal; } -NTSTATUS ntlmssp_server_start(NTLMSSP_STATE **ntlmssp_state) -{ - TALLOC_CTX *mem_ctx; - - mem_ctx = talloc_init("NTLMSSP context"); - - *ntlmssp_state = talloc_zero(mem_ctx, sizeof(**ntlmssp_state)); - if (!*ntlmssp_state) { - DEBUG(0,("ntlmssp_start: talloc failed!\n")); - talloc_destroy(mem_ctx); - return NT_STATUS_NO_MEMORY; - } - - ZERO_STRUCTP(*ntlmssp_state); - - (*ntlmssp_state)->mem_ctx = mem_ctx; - (*ntlmssp_state)->get_challenge = get_challenge; - - (*ntlmssp_state)->get_global_myname = global_myname; - (*ntlmssp_state)->get_domain = lp_workgroup; - (*ntlmssp_state)->server_role = ROLE_DOMAIN_MEMBER; /* a good default */ - - return NT_STATUS_OK; -} - -NTSTATUS ntlmssp_server_end(NTLMSSP_STATE **ntlmssp_state) -{ - TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx; - - data_blob_free(&(*ntlmssp_state)->chal); - data_blob_free(&(*ntlmssp_state)->lm_resp); - data_blob_free(&(*ntlmssp_state)->nt_resp); - - SAFE_FREE((*ntlmssp_state)->user); - SAFE_FREE((*ntlmssp_state)->domain); - SAFE_FREE((*ntlmssp_state)->workstation); - - talloc_destroy(mem_ctx); - *ntlmssp_state = NULL; - return NT_STATUS_OK; -} - -NTSTATUS ntlmssp_server_update(NTLMSSP_STATE *ntlmssp_state, - DATA_BLOB request, DATA_BLOB *reply) -{ - uint32 ntlmssp_command; - *reply = data_blob(NULL, 0); - - if (!msrpc_parse(&request, "Cd", - "NTLMSSP", - &ntlmssp_command)) { - - return NT_STATUS_LOGON_FAILURE; - } - - if (ntlmssp_command == NTLMSSP_NEGOTIATE) { - return ntlmssp_negotiate(ntlmssp_state, request, reply); - } else if (ntlmssp_command == NTLMSSP_AUTH) { - return ntlmssp_auth(ntlmssp_state, request, reply); - } else { - return NT_STATUS_LOGON_FAILURE; - } -} +/** + * Determine correct target name flags for reply, given server role + * and negoitated falgs + * + * @param ntlmssp_state NTLMSSP State + * @param neg_flags The flags from the packet + * @param chal_flags The flags to be set in the reply packet + * @return The 'target name' string. + */ -static const char *ntlmssp_target_name(NTLMSSP_STATE *ntlmssp_state, +static const char *ntlmssp_target_name(struct ntlmssp_state *ntlmssp_state, uint32 neg_flags, uint32 *chal_flags) { if (neg_flags & NTLMSSP_REQUEST_TARGET) { @@ -119,8 +107,17 @@ static const char *ntlmssp_target_name(NTLMSSP_STATE *ntlmssp_state, } } -NTSTATUS ntlmssp_negotiate(NTLMSSP_STATE *ntlmssp_state, - DATA_BLOB request, DATA_BLOB *reply) +/** + * Next state function for the Negotiate packet + * + * @param ntlmssp_state NTLMSSP State + * @param request The request, as a DATA_BLOB + * @param request The reply, as an allocated DATA_BLOB, caller to free. + * @return Errors or MORE_PROCESSING_REQUIRED if a reply is sent. + */ + +static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, + const DATA_BLOB request, DATA_BLOB *reply) { DATA_BLOB struct_blob; fstring dnsname, dnsdomname; @@ -140,7 +137,7 @@ NTSTATUS ntlmssp_negotiate(NTLMSSP_STATE *ntlmssp_state, &neg_flags, &cliname, &domname)) { - return NT_STATUS_LOGON_FAILURE; + return NT_STATUS_INVALID_PARAMETER; } SAFE_FREE(cliname); @@ -219,11 +216,22 @@ NTSTATUS ntlmssp_negotiate(NTLMSSP_STATE *ntlmssp_state, data_blob_free(&struct_blob); + ntlmssp_state->expected_state = NTLMSSP_AUTH; + return NT_STATUS_MORE_PROCESSING_REQUIRED; } -NTSTATUS ntlmssp_auth(NTLMSSP_STATE *ntlmssp_state, - DATA_BLOB request, DATA_BLOB *reply) +/** + * Next state function for the Authenticate packet + * + * @param ntlmssp_state NTLMSSP State + * @param request The request, as a DATA_BLOB + * @param request The reply, as an allocated DATA_BLOB, caller to free. + * @return Errors or NT_STATUS_OK. + */ + +static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, + const DATA_BLOB request, DATA_BLOB *reply) { DATA_BLOB sess_key; uint32 ntlmssp_command, neg_flags; @@ -260,7 +268,7 @@ NTSTATUS ntlmssp_auth(NTLMSSP_STATE *ntlmssp_state, &ntlmssp_state->workstation, &sess_key, &neg_flags)) { - return NT_STATUS_LOGON_FAILURE; + return NT_STATUS_INVALID_PARAMETER; } data_blob_free(&sess_key); @@ -279,3 +287,339 @@ NTSTATUS ntlmssp_auth(NTLMSSP_STATE *ntlmssp_state, return nt_status; } + +/** + * Create an NTLMSSP state machine + * + * @param ntlmssp_state NTLMSSP State, allocated by this funciton + */ + +NTSTATUS ntlmssp_server_start(NTLMSSP_STATE **ntlmssp_state) +{ + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("NTLMSSP context"); + + *ntlmssp_state = talloc_zero(mem_ctx, sizeof(**ntlmssp_state)); + if (!*ntlmssp_state) { + DEBUG(0,("ntlmssp_server_start: talloc failed!\n")); + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + + (*ntlmssp_state)->mem_ctx = mem_ctx; + (*ntlmssp_state)->get_challenge = get_challenge; + + (*ntlmssp_state)->get_global_myname = global_myname; + (*ntlmssp_state)->get_domain = lp_workgroup; + (*ntlmssp_state)->server_role = ROLE_DOMAIN_MEMBER; /* a good default */ + + (*ntlmssp_state)->expected_state = NTLMSSP_NEGOTIATE; + + return NT_STATUS_OK; +} + +/** + * End an NTLMSSP state machine + * + * @param ntlmssp_state NTLMSSP State, free()ed by this funciton + */ + +NTSTATUS ntlmssp_server_end(NTLMSSP_STATE **ntlmssp_state) +{ + TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx; + + data_blob_free(&(*ntlmssp_state)->chal); + data_blob_free(&(*ntlmssp_state)->lm_resp); + data_blob_free(&(*ntlmssp_state)->nt_resp); + + SAFE_FREE((*ntlmssp_state)->user); + SAFE_FREE((*ntlmssp_state)->domain); + SAFE_FREE((*ntlmssp_state)->workstation); + + talloc_destroy(mem_ctx); + *ntlmssp_state = NULL; + return NT_STATUS_OK; +} + +/** + * Next state function for the NTLMSSP state machine + * + * @param ntlmssp_state NTLMSSP State + * @param request The request, as a DATA_BLOB + * @param request The reply, as an allocated DATA_BLOB, caller to free. + * @return Errors, NT_STATUS_MORE_PROCESSING_REQUIRED or NT_STATUS_OK. + */ + +NTSTATUS ntlmssp_server_update(NTLMSSP_STATE *ntlmssp_state, + const DATA_BLOB request, DATA_BLOB *reply) +{ + uint32 ntlmssp_command; + *reply = data_blob(NULL, 0); + + if (!msrpc_parse(&request, "Cd", + "NTLMSSP", + &ntlmssp_command)) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (ntlmssp_command != ntlmssp_state->expected_state) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (ntlmssp_command == NTLMSSP_NEGOTIATE) { + return ntlmssp_server_negotiate(ntlmssp_state, request, reply); + } else if (ntlmssp_command == NTLMSSP_AUTH) { + return ntlmssp_server_auth(ntlmssp_state, request, reply); + } else { + return NT_STATUS_INVALID_PARAMETER; + } +} + +/********************************************************************* + Client side NTLMSSP +*********************************************************************/ + +/** + * Next state function for the Initial packet + * + * @param ntlmssp_state NTLMSSP State + * @param request The request, as a DATA_BLOB. reply.data must be NULL + * @param request The reply, as an allocated DATA_BLOB, caller to free. + * @return Errors or NT_STATUS_OK. + */ + +static NTSTATUS ntlmssp_client_initial(struct ntlmssp_client_state *ntlmssp_state, + DATA_BLOB reply, DATA_BLOB *next_request) +{ + if (ntlmssp_state->unicode) { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; + } + + /* generate the ntlmssp negotiate packet */ + msrpc_gen(next_request, "CddAA", + "NTLMSSP", + NTLMSSP_NEGOTIATE, + ntlmssp_state->neg_flags, + ntlmssp_state->get_domain(), + ntlmssp_state->get_global_myname()); + + return NT_STATUS_MORE_PROCESSING_REQUIRED; +} + +/** + * Next state function for the Challenge Packet. Generate an auth packet. + * + * @param ntlmssp_state NTLMSSP State + * @param request The request, as a DATA_BLOB. reply.data must be NULL + * @param request The reply, as an allocated DATA_BLOB, caller to free. + * @return Errors or NT_STATUS_OK. + */ + +static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_state, + const DATA_BLOB reply, DATA_BLOB *next_request) +{ + uint32 chal_flags, ntlmssp_command, unkn1, unkn2; + DATA_BLOB server_domain_blob; + DATA_BLOB challenge_blob; + DATA_BLOB struct_blob; + char *server_domain; + const char *chal_parse_string; + const char *auth_gen_string; + DATA_BLOB lm_response = data_blob(NULL, 0); + DATA_BLOB nt_response = data_blob(NULL, 0); + DATA_BLOB session_key = data_blob(NULL, 0); + uint8 datagram_sess_key[16]; + + ZERO_STRUCT(datagram_sess_key); + + if (!msrpc_parse(&reply, "CdBd", + "NTLMSSP", + &ntlmssp_command, + &server_domain_blob, + &chal_flags)) { + DEBUG(0, ("Failed to parse the NTLMSSP Challenge\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + data_blob_free(&server_domain_blob); + + if (chal_flags & NTLMSSP_NEGOTIATE_UNICODE) { + chal_parse_string = "CdUdbddB"; + auth_gen_string = "CdBBUUUBd"; + ntlmssp_state->unicode = True; + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM; + } else if (chal_flags & NTLMSSP_NEGOTIATE_OEM) { + chal_parse_string = "CdAdbddB"; + auth_gen_string = "CdBBAAABd"; + ntlmssp_state->unicode = False; + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE; + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM; + } else { + return NT_STATUS_INVALID_PARAMETER; + } + + if (!msrpc_parse(&reply, chal_parse_string, + "NTLMSSP", + &ntlmssp_command, + &server_domain, + &chal_flags, + &challenge_blob, 8, + &unkn1, &unkn2, + &struct_blob)) { + DEBUG(0, ("Failed to parse the NTLMSSP Challenge\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + SAFE_FREE(server_domain); + data_blob_free(&struct_blob); + + if (challenge_blob.length != 8) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (ntlmssp_state->use_ntlmv2) { + + /* TODO: if the remote server is standalone, then we should replace 'domain' + with the server name as supplied above */ + + if (!SMBNTLMv2encrypt(ntlmssp_state->user, + ntlmssp_state->domain, + ntlmssp_state->password, challenge_blob, + &lm_response, &nt_response, &session_key)) { + data_blob_free(&challenge_blob); + return NT_STATUS_NO_MEMORY; + } + } else { + uchar nt_hash[16]; + E_md4hash(ntlmssp_state->password, nt_hash); + + /* non encrypted password supplied. Ignore ntpass. */ + if (lp_client_lanman_auth()) { + lm_response = data_blob(NULL, 24); + SMBencrypt(ntlmssp_state->password,challenge_blob.data, + lm_response.data); + } + + nt_response = data_blob(NULL, 24); + SMBNTencrypt(ntlmssp_state->password,challenge_blob.data, + nt_response.data); + session_key = data_blob(NULL, 16); + SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); + } + + data_blob_free(&challenge_blob); + + /* this generates the actual auth packet */ + if (!msrpc_gen(next_request, auth_gen_string, + "NTLMSSP", + NTLMSSP_AUTH, + lm_response.data, lm_response.length, + nt_response.data, nt_response.length, + ntlmssp_state->domain, + ntlmssp_state->user, + ntlmssp_state->get_global_myname(), + datagram_sess_key, 0, + ntlmssp_state->neg_flags)) { + + data_blob_free(&lm_response); + data_blob_free(&nt_response); + data_blob_free(&session_key); + return NT_STATUS_NO_MEMORY; + } + + data_blob_free(&lm_response); + data_blob_free(&nt_response); + + ntlmssp_state->session_key = session_key; + + return NT_STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS ntlmssp_client_start(NTLMSSP_CLIENT_STATE **ntlmssp_state) +{ + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("NTLMSSP Client context"); + + *ntlmssp_state = talloc_zero(mem_ctx, sizeof(**ntlmssp_state)); + if (!*ntlmssp_state) { + DEBUG(0,("ntlmssp_server_start: talloc failed!\n")); + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + + (*ntlmssp_state)->mem_ctx = mem_ctx; + + (*ntlmssp_state)->get_global_myname = global_myname; + (*ntlmssp_state)->get_domain = lp_workgroup; + + (*ntlmssp_state)->unicode = True; + + (*ntlmssp_state)->neg_flags = + NTLMSSP_NEGOTIATE_128 | + NTLMSSP_NEGOTIATE_NTLM | + NTLMSSP_REQUEST_TARGET; + + return NT_STATUS_OK; +} + +NTSTATUS ntlmssp_client_end(NTLMSSP_CLIENT_STATE **ntlmssp_state) +{ + TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx; + + data_blob_free(&(*ntlmssp_state)->session_key); + talloc_destroy(mem_ctx); + *ntlmssp_state = NULL; + return NT_STATUS_OK; +} + +NTSTATUS ntlmssp_client_update(NTLMSSP_CLIENT_STATE *ntlmssp_state, + DATA_BLOB reply, DATA_BLOB *next_request) +{ + uint32 ntlmssp_command; + *next_request = data_blob(NULL, 0); + + if (!reply.length) { + return ntlmssp_client_initial(ntlmssp_state, reply, next_request); + } + + if (!msrpc_parse(&reply, "Cd", + "NTLMSSP", + &ntlmssp_command)) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (ntlmssp_command == NTLMSSP_CHALLENGE) { + return ntlmssp_client_challenge(ntlmssp_state, reply, next_request); + } + return NT_STATUS_INVALID_PARAMETER; +} + +NTSTATUS ntlmssp_set_username(NTLMSSP_CLIENT_STATE *ntlmssp_state, const char *user) +{ + ntlmssp_state->user = talloc_strdup(ntlmssp_state->mem_ctx, user); + if (!ntlmssp_state->user) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +NTSTATUS ntlmssp_set_password(NTLMSSP_CLIENT_STATE *ntlmssp_state, const char *password) +{ + ntlmssp_state->password = talloc_strdup(ntlmssp_state->mem_ctx, password); + if (!ntlmssp_state->password) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +NTSTATUS ntlmssp_set_domain(NTLMSSP_CLIENT_STATE *ntlmssp_state, const char *domain) +{ + ntlmssp_state->domain = talloc_strdup(ntlmssp_state->mem_ctx, domain); + if (!ntlmssp_state->domain) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} diff --git a/source3/libsmb/smbencrypt.c b/source3/libsmb/smbencrypt.c index a57a98e3ea..aa9391325f 100644 --- a/source3/libsmb/smbencrypt.c +++ b/source3/libsmb/smbencrypt.c @@ -5,6 +5,7 @@ Modified by Jeremy Allison 1995. Copyright (C) Jeremy Allison 1995-2000. Copyright (C) Luke Kennethc Casson Leighton 1996-2000. + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003 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 @@ -294,6 +295,64 @@ void SMBsesskeygen_ntv1(const uchar kr[16], #endif } +DATA_BLOB NTLMv2_generate_response(uchar ntlm_v2_hash[16], + DATA_BLOB server_chal, size_t client_chal_length) +{ + uchar ntlmv2_response[16]; + DATA_BLOB ntlmv2_client_data; + DATA_BLOB final_response; + + /* NTLMv2 */ + + /* We also get to specify some random data */ + ntlmv2_client_data = data_blob(NULL, client_chal_length); + generate_random_buffer(ntlmv2_client_data.data, ntlmv2_client_data.length, False); + + /* Given that data, and the challenge from the server, generate a response */ + SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, ntlmv2_client_data, ntlmv2_response); + + /* put it into nt_response, for the code below to put into the packet */ + final_response = data_blob(NULL, ntlmv2_client_data.length + sizeof(ntlmv2_response)); + memcpy(final_response.data, ntlmv2_response, sizeof(ntlmv2_response)); + /* after the first 16 bytes is the random data we generated above, so the server can verify us with it */ + memcpy(final_response.data + sizeof(ntlmv2_response), ntlmv2_client_data.data, ntlmv2_client_data.length); + data_blob_free(&ntlmv2_client_data); + + return final_response; +} + +BOOL SMBNTLMv2encrypt(const char *user, const char *domain, const char *password, + const DATA_BLOB server_chal, + DATA_BLOB *lm_response, DATA_BLOB *nt_response, + DATA_BLOB *session_key) +{ + uchar nt_hash[16]; + uchar ntlm_v2_hash[16]; + E_md4hash(password, nt_hash); + + /* We don't use the NT# directly. Instead we use it mashed up with + the username and domain. + This prevents username swapping during the auth exchange + */ + if (!ntv2_owf_gen(nt_hash, user, domain, ntlm_v2_hash)) { + return False; + } + + *nt_response = NTLMv2_generate_response(ntlm_v2_hash, server_chal, 64 /* pick a number, > 8 */); + + /* LMv2 */ + + *lm_response = NTLMv2_generate_response(ntlm_v2_hash, server_chal, 8); + + *session_key = data_blob(NULL, 16); + + /* The NTLMv2 calculations also provide a session key, for signing etc later */ + /* use only the first 16 bytes of nt_response for session key */ + SMBsesskeygen_ntv2(ntlm_v2_hash, nt_response->data, session_key->data); + + return True; +} + /*********************************************************** encode a password buffer. The caller gets to figure out what to put in it. @@ -362,20 +421,20 @@ BOOL decode_pw_buffer(char in_buffer[516], char *new_pwrd, SMB signing - setup the MAC key. ************************************************************/ -void cli_calculate_mac_key(struct cli_state *cli, const char *ntpasswd, const uchar resp[24]) +void cli_calculate_mac_key(struct cli_state *cli, const uchar user_session_key[16], const DATA_BLOB response) { - /* Get first 16 bytes. */ - E_md4hash(ntpasswd,&cli->sign_info.mac_key[0]); - memcpy(&cli->sign_info.mac_key[16],resp,24); - cli->sign_info.mac_key_len = 40; + + memcpy(&cli->sign_info.mac_key[0], user_session_key, 16); + memcpy(&cli->sign_info.mac_key[16],response.data, MIN(response.length, 40 - 16)); + cli->sign_info.mac_key_len = MIN(response.length + 16, 40); cli->sign_info.use_smb_signing = True; /* These calls are INCONPATIBLE with SMB signing */ cli->readbraw_supported = False; - cli->writebraw_supported = False; + cli->writebraw_supported = False; /* Reset the sequence number in case we had a previous (aborted) attempt */ - cli->sign_info.send_seq_num = 0; + cli->sign_info.send_seq_num = 2; } /*********************************************************** @@ -411,9 +470,47 @@ void cli_caclulate_sign_mac(struct cli_state *cli) MD5Final(calc_md5_mac, &md5_ctx); memcpy(&cli->outbuf[smb_ss_field], calc_md5_mac, 8); + /* cli->outbuf[smb_ss_field+2]=0; Uncomment this to test if the remote server actually verifies signitures...*/ cli->sign_info.send_seq_num++; cli->sign_info.reply_seq_num = cli->sign_info.send_seq_num; cli->sign_info.send_seq_num++; } + +/*********************************************************** + SMB signing - check a MAC sent by server. +************************************************************/ + +BOOL cli_check_sign_mac(struct cli_state *cli) +{ + unsigned char calc_md5_mac[16]; + unsigned char server_sent_mac[8]; + struct MD5Context md5_ctx; + + if (cli->sign_info.temp_smb_signing) { + return True; + } + + if (!cli->sign_info.use_smb_signing) { + return True; + } + + /* + * Firstly put the sequence number into the first 4 bytes. + * and zero out the next 4 bytes. + */ + + memcpy(server_sent_mac, &cli->inbuf[smb_ss_field], sizeof(server_sent_mac)); + + SIVAL(cli->inbuf, smb_ss_field, cli->sign_info.reply_seq_num); + SIVAL(cli->inbuf, smb_ss_field + 4, 0); + + /* Calculate the 16 byte MAC and place first 8 bytes into the field. */ + MD5Init(&md5_ctx); + MD5Update(&md5_ctx, cli->sign_info.mac_key, cli->sign_info.mac_key_len); + MD5Update(&md5_ctx, cli->inbuf + 4, smb_len(cli->inbuf)); + MD5Final(calc_md5_mac, &md5_ctx); + + return (memcmp(server_sent_mac, calc_md5_mac, 8) == 0); +} diff --git a/source3/nsswitch/winbindd_ads.c b/source3/nsswitch/winbindd_ads.c index be4ed1c667..f6fc3a8d6c 100644 --- a/source3/nsswitch/winbindd_ads.c +++ b/source3/nsswitch/winbindd_ads.c @@ -39,7 +39,6 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) { ADS_STRUCT *ads; ADS_STATUS status; - char *ccache; if (domain->private) { return (ADS_STRUCT *)domain->private; diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index 1455f88156..daea4ab94f 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -255,6 +255,10 @@ typedef struct BOOL bAllowTrustedDomains; BOOL bLanmanAuth; BOOL bNTLMAuth; + BOOL bUseSpnego; + BOOL bClientLanManAuth; + BOOL bClientNTLMv2Auth; + BOOL bClientUseSpnego; BOOL bDebugHiresTimestamp; BOOL bDebugPid; BOOL bDebugUid; @@ -263,12 +267,12 @@ typedef struct BOOL bUnicode; BOOL bUseMmap; BOOL bHostnameLookups; - BOOL bUseSpnego; BOOL bUnixExtensions; BOOL bDisableNetbios; BOOL bKernelChangeNotify; int restrict_anonymous; int name_cache_timeout; + BOOL client_signing; } global; diff --git a/source3/utils/net_rpc_join.c b/source3/utils/net_rpc_join.c index 1b711f7b43..b0eb335986 100644 --- a/source3/utils/net_rpc_join.c +++ b/source3/utils/net_rpc_join.c @@ -264,14 +264,8 @@ int net_rpc_join_newstyle(int argc, const char **argv) ctr.switch_value = 24; ctr.info.id24 = &p24; - /* I don't think this is quite the right place for this - calculation. It should be moved somewhere where the credentials - are calculated. )-: */ - - mdfour(sess_key, cli->pwd.smb_nt_pwd, 16); - CHECK_RPC_ERR(cli_samr_set_userinfo(cli, mem_ctx, &user_pol, 24, - sess_key, &ctr), + cli->user_session_key, &ctr), "error setting trust account password"); /* Why do we have to try to (re-)set the ACB to be the same as what |