summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2003-02-24 02:55:00 +0000
committerAndrew Bartlett <abartlet@samba.org>2003-02-24 02:55:00 +0000
commitd1221c9b6c369113a531063737890b58d89bf6fe (patch)
treeefc7b8b8d33b675404dc7c5bc018db47a4136212
parente075e1dfa9a88b5edadc9c989200a52f48182cef (diff)
downloadsamba-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.h3
-rw-r--r--source3/include/client.h7
-rw-r--r--source3/include/ntlmssp.h20
-rw-r--r--source3/lib/data_blob.c1
-rw-r--r--source3/libads/krb5_setpw.c429
-rw-r--r--source3/libads/ldap.c51
-rw-r--r--source3/libads/sasl.c7
-rw-r--r--source3/libads/util.c10
-rw-r--r--source3/libsmb/cliconnect.c313
-rw-r--r--source3/libsmb/clientgen.c18
-rw-r--r--source3/libsmb/clispnego.c321
-rw-r--r--source3/libsmb/ntlmssp.c488
-rw-r--r--source3/libsmb/smbencrypt.c111
-rw-r--r--source3/nsswitch/winbindd_ads.c1
-rw-r--r--source3/param/loadparm.c6
-rw-r--r--source3/utils/net_rpc_join.c8
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