diff options
35 files changed, 1157 insertions, 597 deletions
diff --git a/source3/auth/auth.c b/source3/auth/auth.c index 553d9a686e..20dccc6592 100644 --- a/source3/auth/auth.c +++ b/source3/auth/auth.c @@ -88,6 +88,8 @@ static const uint8 *get_ntlm_challenge(struct auth_context *auth_context) return auth_context->challenge.data; } + auth_context->challenge_may_be_modified = False; + for (auth_method = auth_context->auth_method_list; auth_method; auth_method = auth_method->next) { if (auth_method->get_chal == NULL) { DEBUG(5, ("auth_get_challenge: module %s did not want to specify a challenge\n", auth_method->name)); @@ -127,6 +129,7 @@ static const uint8 *get_ntlm_challenge(struct auth_context *auth_context) chal, sizeof(chal)); challenge_set_by = "random"; + auth_context->challenge_may_be_modified = True; } DEBUG(5, ("auth_context challenge created by %s\n", challenge_set_by)); diff --git a/source3/auth/auth_ntlmssp.c b/source3/auth/auth_ntlmssp.c index 3af0cbaada..a5ce101e5e 100644 --- a/source3/auth/auth_ntlmssp.c +++ b/source3/auth/auth_ntlmssp.c @@ -23,13 +23,59 @@ #include "includes.h" -static const uint8 *auth_ntlmssp_get_challenge(struct ntlmssp_state *ntlmssp_state) +/** + * Return the challenge as determined by the authentication subsystem + * @return an 8 byte random challenge + */ + +static const uint8 *auth_ntlmssp_get_challenge(const struct ntlmssp_state *ntlmssp_state) { AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context; return auth_ntlmssp_state->auth_context->get_ntlm_challenge(auth_ntlmssp_state->auth_context); } -static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state) +/** + * Some authentication methods 'fix' the challenge, so we may not be able to set it + * + * @return If the effective challenge used by the auth subsystem may be modified + */ +static BOOL auth_ntlmssp_may_set_challenge(const struct ntlmssp_state *ntlmssp_state) +{ + AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context; + struct auth_context *auth_context = auth_ntlmssp_state->auth_context; + + return auth_context->challenge_may_be_modified; +} + +/** + * NTLM2 authentication modifies the effective challange, + * @param challenge The new challenge value + */ +static NTSTATUS auth_ntlmssp_set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge) +{ + AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context; + struct auth_context *auth_context = auth_ntlmssp_state->auth_context; + + SMB_ASSERT(challenge->length == 8); + + auth_context->challenge = data_blob_talloc(auth_context->mem_ctx, + challenge->data, challenge->length); + + auth_context->challenge_set_by = "NTLMSSP callback (NTLM2)"; + + DEBUG(5, ("auth_context challenge set by %s\n", auth_context->challenge_set_by)); + DEBUG(5, ("challenge is: \n")); + dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length); + return NT_STATUS_OK; +} + +/** + * Check the password on an NTLMSSP login. + * + * Return the session keys used on the connection. + */ + +static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key) { AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context; uint32 auth_flags = AUTH_FLAG_NONE; @@ -45,7 +91,7 @@ static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state) auth_flags |= AUTH_FLAG_NTLM_RESP; } else if (auth_ntlmssp_state->ntlmssp_state->nt_resp.length > 24) { auth_flags |= AUTH_FLAG_NTLMv2_RESP; - }; + } /* the client has given us its machine name (which we otherwise would not get on port 445). we need to possibly reload smb.conf if smb.conf includes depend on the machine name */ @@ -71,10 +117,26 @@ static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state) return nt_status; } - nt_status = auth_ntlmssp_state->auth_context->check_ntlm_password(auth_ntlmssp_state->auth_context, user_info, &auth_ntlmssp_state->server_info); - + nt_status = auth_ntlmssp_state->auth_context->check_ntlm_password(auth_ntlmssp_state->auth_context, + user_info, &auth_ntlmssp_state->server_info); + free_user_info(&user_info); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + if (auth_ntlmssp_state->server_info->nt_session_key.length) { + DEBUG(10, ("Got NT session key of length %u\n", auth_ntlmssp_state->server_info->nt_session_key.length)); + *nt_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx, + auth_ntlmssp_state->server_info->nt_session_key.data, + auth_ntlmssp_state->server_info->nt_session_key.length); + } + if (auth_ntlmssp_state->server_info->lm_session_key.length) { + DEBUG(10, ("Got LM session key of length %u\n", auth_ntlmssp_state->server_info->lm_session_key.length)); + *lm_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx, + auth_ntlmssp_state->server_info->lm_session_key.data, + auth_ntlmssp_state->server_info->lm_session_key.length); + } return nt_status; } @@ -106,18 +168,20 @@ NTSTATUS auth_ntlmssp_start(AUTH_NTLMSSP_STATE **auth_ntlmssp_state) (*auth_ntlmssp_state)->ntlmssp_state->auth_context = (*auth_ntlmssp_state); (*auth_ntlmssp_state)->ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge; + (*auth_ntlmssp_state)->ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge; + (*auth_ntlmssp_state)->ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge; (*auth_ntlmssp_state)->ntlmssp_state->check_password = auth_ntlmssp_check_password; (*auth_ntlmssp_state)->ntlmssp_state->server_role = lp_server_role(); return NT_STATUS_OK; } -NTSTATUS auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state) +void auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state) { TALLOC_CTX *mem_ctx = (*auth_ntlmssp_state)->mem_ctx; if ((*auth_ntlmssp_state)->ntlmssp_state) { - ntlmssp_server_end(&(*auth_ntlmssp_state)->ntlmssp_state); + ntlmssp_end(&(*auth_ntlmssp_state)->ntlmssp_state); } if ((*auth_ntlmssp_state)->auth_context) { ((*auth_ntlmssp_state)->auth_context->free)(&(*auth_ntlmssp_state)->auth_context); @@ -127,11 +191,10 @@ NTSTATUS auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state) } talloc_destroy(mem_ctx); *auth_ntlmssp_state = NULL; - return NT_STATUS_OK; } NTSTATUS auth_ntlmssp_update(AUTH_NTLMSSP_STATE *auth_ntlmssp_state, const DATA_BLOB request, DATA_BLOB *reply) { - return ntlmssp_server_update(auth_ntlmssp_state->ntlmssp_state, request, reply); + return ntlmssp_update(auth_ntlmssp_state->ntlmssp_state, request, reply); } diff --git a/source3/auth/auth_sam.c b/source3/auth/auth_sam.c index 2a00b6fb80..7352a9685b 100644 --- a/source3/auth/auth_sam.c +++ b/source3/auth/auth_sam.c @@ -33,7 +33,7 @@ static BOOL smb_pwd_check_ntlmv1(const DATA_BLOB *nt_response, const uchar *part_passwd, const DATA_BLOB *sec_blob, - uint8 user_sess_key[16]) + DATA_BLOB *user_sess_key) { /* Finish the encryption of part_passwd. */ uchar p24[24]; @@ -56,7 +56,8 @@ static BOOL smb_pwd_check_ntlmv1(const DATA_BLOB *nt_response, SMBOWFencrypt(part_passwd, sec_blob->data, p24); if (user_sess_key != NULL) { - SMBsesskeygen_ntv1(part_passwd, NULL, user_sess_key); + *user_sess_key = data_blob(NULL, 16); + SMBsesskeygen_ntv1(part_passwd, NULL, user_sess_key->data); } @@ -83,7 +84,7 @@ static BOOL smb_pwd_check_ntlmv2(const DATA_BLOB *ntv2_response, const uchar *part_passwd, const DATA_BLOB *sec_blob, const char *user, const char *domain, - uint8 user_sess_key[16]) + DATA_BLOB *user_sess_key) { /* Finish the encryption of part_passwd. */ uchar kr[16]; @@ -120,7 +121,8 @@ static BOOL smb_pwd_check_ntlmv2(const DATA_BLOB *ntv2_response, SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption); if (user_sess_key != NULL) { - SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key); + *user_sess_key = data_blob(NULL, 16); + SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data); } #if DEBUG_PASSWORD @@ -148,7 +150,8 @@ static NTSTATUS sam_password_ok(const struct auth_context *auth_context, TALLOC_CTX *mem_ctx, SAM_ACCOUNT *sampass, const auth_usersupplied_info *user_info, - uint8 user_sess_key[16]) + DATA_BLOB *user_sess_key, + DATA_BLOB *lm_sess_key) { uint16 acct_ctrl; const uint8 *nt_pw, *lm_pw; @@ -225,6 +228,16 @@ static NTSTATUS sam_password_ok(const struct auth_context *auth_context, if (smb_pwd_check_ntlmv1(&user_info->nt_resp, nt_pw, &auth_context->challenge, user_sess_key)) { + /* The LM session key for this response is not very secure, + so use it only if we otherwise allow LM authentication */ + lm_pw = pdb_get_lanman_passwd(sampass); + + if (lp_lanman_auth() && lm_pw) { + uint8 first_8_lm_hash[16]; + memcpy(first_8_lm_hash, lm_pw, 8); + memset(first_8_lm_hash + 8, '\0', 8); + *lm_sess_key = data_blob(first_8_lm_hash, 16); + } return NT_STATUS_OK; } else { DEBUG(3,("sam_password_ok: NT MD4 password check failed for user %s\n",pdb_get_username(sampass))); @@ -252,7 +265,12 @@ static NTSTATUS sam_password_ok(const struct auth_context *auth_context, DEBUG(4,("sam_password_ok: Checking LM password\n")); if (smb_pwd_check_ntlmv1(&user_info->lm_resp, lm_pw, &auth_context->challenge, - user_sess_key)) { + NULL)) { + uint8 first_8_lm_hash[16]; + memcpy(first_8_lm_hash, lm_pw, 8); + memset(first_8_lm_hash + 8, '\0', 8); + *user_sess_key = data_blob(first_8_lm_hash, 16); + *lm_sess_key = data_blob(first_8_lm_hash, 16); return NT_STATUS_OK; } } @@ -272,7 +290,7 @@ static NTSTATUS sam_password_ok(const struct auth_context *auth_context, nt_pw, &auth_context->challenge, user_info->smb_name.str, user_info->client_domain.str, - user_sess_key)) { + NULL)) { return NT_STATUS_OK; } @@ -281,7 +299,7 @@ static NTSTATUS sam_password_ok(const struct auth_context *auth_context, nt_pw, &auth_context->challenge, user_info->smb_name.str, "", - user_sess_key)) { + NULL)) { return NT_STATUS_OK; } @@ -292,7 +310,19 @@ static NTSTATUS sam_password_ok(const struct auth_context *auth_context, if (lp_ntlm_auth()) { if (smb_pwd_check_ntlmv1(&user_info->lm_resp, nt_pw, &auth_context->challenge, - user_sess_key)) { + NULL)) { + /* The session key for this response is still very odd. + It not very secure, so use it only if we otherwise + allow LM authentication */ + lm_pw = pdb_get_lanman_passwd(sampass); + + if (lp_lanman_auth() && lm_pw) { + uint8 first_8_lm_hash[16]; + memcpy(first_8_lm_hash, lm_pw, 8); + memset(first_8_lm_hash + 8, '\0', 8); + *user_sess_key = data_blob(first_8_lm_hash, 16); + *lm_sess_key = data_blob(first_8_lm_hash, 16); + } return NT_STATUS_OK; } DEBUG(3,("sam_password_ok: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",pdb_get_username(sampass))); @@ -301,7 +331,6 @@ static NTSTATUS sam_password_ok(const struct auth_context *auth_context, DEBUG(3,("sam_password_ok: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",pdb_get_username(sampass))); return NT_STATUS_WRONG_PASSWORD; } - } /* Should not be reached, but if they send nothing... */ @@ -421,8 +450,8 @@ static NTSTATUS check_sam_security(const struct auth_context *auth_context, SAM_ACCOUNT *sampass=NULL; BOOL ret; NTSTATUS nt_status; - uint8 user_sess_key[16]; - const uint8* lm_hash; + DATA_BLOB user_sess_key = data_blob(NULL, 0); + DATA_BLOB lm_sess_key = data_blob(NULL, 0); if (!user_info || !auth_context) { return NT_STATUS_UNSUCCESSFUL; @@ -446,7 +475,8 @@ static NTSTATUS check_sam_security(const struct auth_context *auth_context, return NT_STATUS_NO_SUCH_USER; } - nt_status = sam_password_ok(auth_context, mem_ctx, sampass, user_info, user_sess_key); + nt_status = sam_password_ok(auth_context, mem_ctx, sampass, + user_info, &user_sess_key, &lm_sess_key); if (!NT_STATUS_IS_OK(nt_status)) { pdb_free_sam(&sampass); @@ -465,12 +495,8 @@ static NTSTATUS check_sam_security(const struct auth_context *auth_context, return nt_status; } - lm_hash = pdb_get_lanman_passwd((*server_info)->sam_account); - if (lm_hash) { - memcpy((*server_info)->first_8_lm_hash, lm_hash, 8); - } - - memcpy((*server_info)->session_key, user_sess_key, sizeof(user_sess_key)); + (*server_info)->nt_session_key = user_sess_key; + (*server_info)->lm_session_key = lm_sess_key; return nt_status; } diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c index d7d7f53e2d..5d3f8f0277 100644 --- a/source3/auth/auth_util.c +++ b/source3/auth/auth_util.c @@ -900,7 +900,13 @@ NTSTATUS make_server_info_guest(auth_serversupplied_info **server_info) nt_status = make_server_info_sam(server_info, sampass); if (NT_STATUS_IS_OK(nt_status)) { + static const char zeros[16]; (*server_info)->guest = True; + + /* annoying, but the Guest really does have a session key, + and it is all zeros! */ + (*server_info)->nt_session_key = data_blob(zeros, sizeof(zeros)); + (*server_info)->lm_session_key = data_blob(zeros, sizeof(zeros)); } return nt_status; @@ -992,6 +998,8 @@ NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx, auth_serversupplied_info **server_info, NET_USER_INFO_3 *info3) { + static const char zeros[16]; + NTSTATUS nt_status = NT_STATUS_OK; char *found_username; const char *nt_domain; @@ -1210,10 +1218,20 @@ NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx, (*server_info)->ptok = token; SAFE_FREE(all_group_SIDs); + + /* ensure we are never given NULL session keys */ - memcpy((*server_info)->session_key, info3->user_sess_key, sizeof((*server_info)->session_key)/* 16 */); - memcpy((*server_info)->first_8_lm_hash, info3->padding, 8); + if (memcmp(info3->user_sess_key, zeros, sizeof(zeros)) == 0) { + (*server_info)->nt_session_key = data_blob(NULL, 0); + } else { + (*server_info)->nt_session_key = data_blob(info3->user_sess_key, sizeof(info3->user_sess_key)); + } + if (memcmp(info3->padding, zeros, sizeof(zeros)) == 0) { + (*server_info)->lm_session_key = data_blob(NULL, 0); + } else { + (*server_info)->lm_session_key = data_blob(info3->padding, 16); + } return NT_STATUS_OK; } @@ -1256,6 +1274,8 @@ void free_server_info(auth_serversupplied_info **server_info) delete_nt_token( &(*server_info)->ptok ); SAFE_FREE((*server_info)->groups); SAFE_FREE((*server_info)->unix_name); + data_blob_free(&(*server_info)->lm_session_key); + data_blob_free(&(*server_info)->nt_session_key); ZERO_STRUCT(**server_info); } SAFE_FREE(*server_info); diff --git a/source3/include/auth.h b/source3/include/auth.h index 6b42418be8..ecf4d539d8 100644 --- a/source3/include/auth.h +++ b/source3/include/auth.h @@ -87,10 +87,9 @@ typedef struct auth_serversupplied_info NT_USER_TOKEN *ptok; - uint8 session_key[16]; + DATA_BLOB nt_session_key; + DATA_BLOB lm_session_key; - uint8 first_8_lm_hash[8]; - uint32 sam_fill_level; /* How far is this structure filled? */ SAM_ACCOUNT *sam_account; @@ -107,6 +106,8 @@ struct auth_context { /* Who set this up in the first place? */ const char *challenge_set_by; + BOOL challenge_may_be_modified; + struct auth_methods *challenge_set_method; /* What order are the various methods in? Try to stop it changing under us */ struct auth_methods *auth_method_list; diff --git a/source3/include/client.h b/source3/include/client.h index 598e6c0bda..968b73f0b4 100644 --- a/source3/include/client.h +++ b/source3/include/client.h @@ -113,7 +113,7 @@ struct cli_state { /* the session key for this CLI, outside any per-pipe authenticaion */ - unsigned char user_session_key[16]; + DATA_BLOB user_session_key; /* * Only used in NT domain calls. @@ -133,7 +133,7 @@ struct cli_state { schannel. */ struct netsec_auth_struct auth_info; - NTLMSSP_CLIENT_STATE *ntlmssp_pipe_state; + NTLMSSP_STATE *ntlmssp_pipe_state; unsigned char sess_key[16]; /* Current session key. */ DOM_CRED clnt_cred; /* Client credential. */ diff --git a/source3/include/includes.h b/source3/include/includes.h index ebb09b6c6c..638648dc44 100644 --- a/source3/include/includes.h +++ b/source3/include/includes.h @@ -1298,7 +1298,7 @@ krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt); krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters); krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes); void free_kerberos_etypes(krb5_context context, krb5_enctype *enctypes); -BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, uint8 session_key[16], BOOL remote); +BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote); #endif /* HAVE_KRB5 */ /* TRUE and FALSE are part of the C99 standard and gcc, but diff --git a/source3/include/ntdomain.h b/source3/include/ntdomain.h index 3fe8f41d8d..b1a4107980 100644 --- a/source3/include/ntdomain.h +++ b/source3/include/ntdomain.h @@ -229,7 +229,7 @@ typedef struct pipes_struct fstring pipe_user_name; struct current_user pipe_user; - uint8 session_key[16]; + DATA_BLOB session_key; /* * Set to true when an RPC bind has been done on this pipe. diff --git a/source3/include/ntlmssp.h b/source3/include/ntlmssp.h index f1b1bc25e4..681d4071db 100644 --- a/source3/include/ntlmssp.h +++ b/source3/include/ntlmssp.h @@ -30,6 +30,7 @@ enum NTLMSSP_ROLE /* NTLMSSP message types */ enum NTLM_MESSAGE_TYPE { + NTLMSSP_INITIAL = 0 /* samba internal state */, NTLMSSP_NEGOTIATE = 1, NTLMSSP_CHALLENGE = 2, NTLMSSP_AUTH = 3, @@ -70,29 +71,10 @@ enum NTLM_MESSAGE_TYPE typedef struct ntlmssp_state { TALLOC_CTX *mem_ctx; + unsigned int ref_count; enum NTLMSSP_ROLE role; - BOOL unicode; - char *user; - char *domain; - char *workstation; - DATA_BLOB lm_resp; - DATA_BLOB nt_resp; - DATA_BLOB chal; - void *auth_context; - const uint8 *(*get_challenge)(struct ntlmssp_state *ntlmssp_state); - NTSTATUS (*check_password)(struct ntlmssp_state *ntlmssp_state); - - const char *(*get_global_myname)(void); - const char *(*get_domain)(void); - - int server_role; + enum server_types server_role; uint32 expected_state; -} NTLMSSP_STATE; - -typedef struct ntlmssp_client_state -{ - TALLOC_CTX *mem_ctx; - unsigned int ref_count; BOOL unicode; BOOL use_ntlmv2; @@ -102,30 +84,78 @@ typedef struct ntlmssp_client_state char *password; char *server_domain; - const char *(*get_global_myname)(void); - const char *(*get_domain)(void); + DATA_BLOB internal_chal; /* Random challenge as supplied to the client for NTLM authentication */ - DATA_BLOB chal; + DATA_BLOB chal; /* Random challenge as input into the actual NTLM (or NTLM2) authentication */ DATA_BLOB lm_resp; DATA_BLOB nt_resp; DATA_BLOB session_key; - uint32 neg_flags; + uint32 neg_flags; /* the current state of negotiation with the NTLMSSP partner */ + void *auth_context; + + /** + * Callback to get the 'challenge' used for NTLM authentication. + * + * @param ntlmssp_state This structure + * @return 8 bytes of challnege data, determined by the server to be the challenge for NTLM authentication + * + */ + const uint8 *(*get_challenge)(const struct ntlmssp_state *ntlmssp_state); + + /** + * Callback to find if the challenge used by NTLM authentication may be modified + * + * The NTLM2 authentication scheme modifies the effective challenge, but this is not compatiable with the + * current 'security=server' implementation.. + * + * @param ntlmssp_state This structure + * @return Can the challenge be set to arbitary values? + * + */ + BOOL (*may_set_challenge)(const struct ntlmssp_state *ntlmssp_state); + + /** + * Callback to set the 'challenge' used for NTLM authentication. + * + * The callback may use the void *auth_context to store state information, but the same value is always available + * from the DATA_BLOB chal on this structure. + * + * @param ntlmssp_state This structure + * @param challange 8 bytes of data, agreed by the client and server to be the effective challenge for NTLM2 authentication + * + */ + NTSTATUS (*set_challenge)(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge); + + /** + * Callback to check the user's password. + * + * The callback must reads the feilds of this structure for the information it needs on the user + * @param ntlmssp_state This structure + * @param nt_session_key If an NT session key is returned by the authentication process, return it here + * @param lm_session_key If an LM session key is returned by the authentication process, return it here + * + */ + NTSTATUS (*check_password)(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key); + + const char *(*get_global_myname)(void); + const char *(*get_domain)(void); + /* SMB Signing */ uint32 ntlmssp_seq_num; /* ntlmv2 */ - char cli_sign_const[16]; - char cli_seal_const[16]; - char srv_sign_const[16]; - char srv_seal_const[16]; + char send_sign_const[16]; + char send_seal_const[16]; + char recv_sign_const[16]; + char recv_seal_const[16]; - unsigned char cli_sign_hash[258]; - unsigned char cli_seal_hash[258]; - unsigned char srv_sign_hash[258]; - unsigned char srv_seal_hash[258]; + unsigned char send_sign_hash[258]; + unsigned char send_seal_hash[258]; + unsigned char recv_sign_hash[258]; + unsigned char recv_seal_hash[258]; /* ntlmv1 */ unsigned char ntlmssp_hash[258]; @@ -135,5 +165,5 @@ typedef struct ntlmssp_client_state Store it here, until we need it */ DATA_BLOB stored_response; -} NTLMSSP_CLIENT_STATE; +} NTLMSSP_STATE; diff --git a/source3/include/smb.h b/source3/include/smb.h index e41b5834f7..4bd40216d7 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -1546,7 +1546,7 @@ typedef struct user_struct NT_USER_TOKEN *nt_user_token; - uint8 session_key[16]; + DATA_BLOB session_key; char *session_keystr; /* used by utmp and pam session code. TDB key string */ diff --git a/source3/lib/data_blob.c b/source3/lib/data_blob.c index 4056212fc5..83afc591a1 100644 --- a/source3/lib/data_blob.c +++ b/source3/lib/data_blob.c @@ -61,14 +61,20 @@ DATA_BLOB data_blob_talloc(TALLOC_CTX *mem_ctx, const void *p, size_t length) { DATA_BLOB ret; - if (!p || !length) { + if (!length) { ZERO_STRUCT(ret); return ret; } - ret.data = talloc_memdup(mem_ctx, p, length); - if (ret.data == NULL) - smb_panic("data_blob_talloc: talloc_memdup failed.\n"); + if (p) { + ret.data = talloc_memdup(mem_ctx, p, length); + if (ret.data == NULL) + smb_panic("data_blob_talloc: talloc_memdup failed.\n"); + } else { + ret.data = talloc(mem_ctx, length); + if (ret.data == NULL) + smb_panic("data_blob_talloc: talloc failed.\n"); + } ret.length = length; ret.free = NULL; diff --git a/source3/libads/kerberos_verify.c b/source3/libads/kerberos_verify.c index cdea5a2fe4..b0efb8f598 100644 --- a/source3/libads/kerberos_verify.c +++ b/source3/libads/kerberos_verify.c @@ -162,7 +162,7 @@ static BOOL setup_keytab(krb5_context context, NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket, char **principal, DATA_BLOB *auth_data, DATA_BLOB *ap_rep, - uint8 session_key[16]) + DATA_BLOB *session_key) { NTSTATUS sret = NT_STATUS_LOGON_FAILURE; krb5_context context = NULL; diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index b5f7b97ae8..a920a1b7ff 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -241,9 +241,16 @@ static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user, return True; } -static void set_cli_session_key (struct cli_state *cli, DATA_BLOB session_key) +/** + * Set the user session key for a connection + * @param cli The cli structure to add it too + * @param session_key The session key used. (A copy of this is taken for the cli struct) + * + */ + +static void cli_set_session_key (struct cli_state *cli, const DATA_BLOB session_key) { - memcpy(cli->user_session_key, session_key.data, MIN(session_key.length, sizeof(cli->user_session_key))); + cli->user_session_key = data_blob(session_key.data, session_key.length); } /**************************************************************************** @@ -311,7 +318,7 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user, session_key = data_blob(NULL, 16); SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); } - cli_simple_set_signing(cli, session_key.data, nt_response); + cli_simple_set_signing(cli, session_key, nt_response); } else { /* pre-encrypted password supplied. Only used for security=server, can't do @@ -373,14 +380,16 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user, if (session_key.data) { /* Have plaintext orginal */ - set_cli_session_key(cli, session_key); + cli_set_session_key(cli, session_key); } ret = True; end: data_blob_free(&lm_response); data_blob_free(&nt_response); - data_blob_free(&session_key); + + if (!ret) + data_blob_free(&session_key); return ret; } @@ -484,19 +493,19 @@ static void use_in_memory_ccache(void) { Do a spnego/kerberos encrypted session setup. ****************************************************************************/ -static BOOL cli_session_setup_kerberos(struct cli_state *cli, const char *principal, const char *workgroup) +static NTSTATUS cli_session_setup_kerberos(struct cli_state *cli, const char *principal, const char *workgroup) { DATA_BLOB blob2, negTokenTarg; - unsigned char session_key_krb5[16]; + DATA_BLOB session_key_krb5; DATA_BLOB null_blob = data_blob(NULL, 0); DEBUG(2,("Doing kerberos session setup\n")); /* generate the encapsulated kerberos5 ticket */ - negTokenTarg = spnego_gen_negTokenTarg(principal, 0, session_key_krb5); + negTokenTarg = spnego_gen_negTokenTarg(principal, 0, &session_key_krb5); if (!negTokenTarg.data) - return False; + return NT_STATUS_UNSUCCESSFUL; #if 0 file_save("negTokenTarg.dat", negTokenTarg.data, negTokenTarg.length); @@ -509,9 +518,16 @@ static BOOL cli_session_setup_kerberos(struct cli_state *cli, const char *princi /* we don't need this blob for kerberos */ data_blob_free(&blob2); + cli_set_session_key(cli, session_key_krb5); + data_blob_free(&negTokenTarg); - return !cli_is_error(cli); + if (cli_is_error(cli)) { + if (NT_STATUS_IS_OK(cli_nt_error(cli))) { + return NT_STATUS_UNSUCCESSFUL; + } + } + return NT_STATUS_OK; } #endif /* HAVE_KRB5 */ @@ -520,10 +536,10 @@ static BOOL cli_session_setup_kerberos(struct cli_state *cli, const char *princi Do a spnego/NTLMSSP encrypted session setup. ****************************************************************************/ -static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, const char *user, +static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *user, const char *pass, const char *workgroup) { - struct ntlmssp_client_state *ntlmssp_state; + struct ntlmssp_state *ntlmssp_state; NTSTATUS nt_status; int turn = 1; DATA_BLOB msg1; @@ -534,21 +550,21 @@ static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, const char *user, cli_temp_set_signing(cli); if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) { - return False; + return nt_status; } if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) { - return False; + return nt_status; } if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, workgroup))) { - return False; + return nt_status; } if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, pass))) { - return False; + return nt_status; } do { - nt_status = ntlmssp_client_update(ntlmssp_state, + nt_status = ntlmssp_update(ntlmssp_state, blob_in, &blob_out); data_blob_free(&blob_in); if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { @@ -562,18 +578,27 @@ static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, const char *user, } cli_simple_set_signing(cli, - ntlmssp_state->session_key.data, + data_blob(ntlmssp_state->session_key.data, ntlmssp_state->session_key.length), null_blob); /* now send that blob on its way */ if (!cli_session_setup_blob_send(cli, msg1)) { - return False; + DEBUG(3, ("Failed to send NTLMSSP/SPENGO blob to server!\n")); + nt_status = NT_STATUS_UNSUCCESSFUL; + } else { + data_blob_free(&msg1); + + blob = cli_session_setup_blob_receive(cli); + + nt_status = cli_nt_error(cli); + if (cli_is_error(cli) && NT_STATUS_IS_OK(nt_status)) { + if (cli->smb_rw_error == READ_BAD_SIG) { + nt_status = NT_STATUS_ACCESS_DENIED; + } else { + nt_status = NT_STATUS_UNSUCCESSFUL; + } + } } - data_blob_free(&msg1); - - blob = cli_session_setup_blob_receive(cli); - - nt_status = cli_nt_error(cli); } if (!blob.length) { @@ -606,24 +631,22 @@ static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, const char *user, if (NT_STATUS_IS_OK(nt_status)) { fstrcpy(cli->server_domain, ntlmssp_state->server_domain); - set_cli_session_key(cli, ntlmssp_state->session_key); + cli_set_session_key(cli, ntlmssp_state->session_key); } /* we have a reference conter on ntlmssp_state, if we are signing then the state will be kept by the signing engine */ - if (!NT_STATUS_IS_OK(ntlmssp_client_end(&ntlmssp_state))) { - return False; - } - - return (NT_STATUS_IS_OK(nt_status)); + ntlmssp_end(&ntlmssp_state); + + return nt_status; } /**************************************************************************** Do a spnego encrypted session setup. ****************************************************************************/ -BOOL cli_session_setup_spnego(struct cli_state *cli, const char *user, +NTSTATUS cli_session_setup_spnego(struct cli_state *cli, const char *user, const char *pass, const char *workgroup) { char *principal; @@ -651,7 +674,7 @@ BOOL cli_session_setup_spnego(struct cli_state *cli, const char *user, reply */ if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) { data_blob_free(&blob); - return False; + return NT_STATUS_INVALID_PARAMETER; } data_blob_free(&blob); @@ -681,7 +704,7 @@ BOOL cli_session_setup_spnego(struct cli_state *cli, const char *user, if (ret){ DEBUG(0, ("Kinit failed: %s\n", error_message(ret))); - return False; + return NT_STATUS_LOGON_FAILURE; } } @@ -773,8 +796,14 @@ BOOL cli_session_setup(struct cli_state *cli, /* if the server supports extended security then use SPNEGO */ - if (cli->capabilities & CAP_EXTENDED_SECURITY) - return cli_session_setup_spnego(cli, user, pass, workgroup); + if (cli->capabilities & CAP_EXTENDED_SECURITY) { + NTSTATUS nt_status; + if (!NT_STATUS_IS_OK(nt_status = cli_session_setup_spnego(cli, user, pass, workgroup))) { + DEBUG(3, ("SPENGO login failed: %s\n", get_friendly_nt_error_msg(nt_status))); + return False; + } + return True; + } /* otherwise do a NT1 style session setup */ diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c index 9b54acf775..0873700fc0 100644 --- a/source3/libsmb/clientgen.c +++ b/source3/libsmb/clientgen.c @@ -339,7 +339,7 @@ close the session void cli_nt_session_close(struct cli_state *cli) { if (cli->ntlmssp_pipe_state) { - ntlmssp_client_end(&cli->ntlmssp_pipe_state); + ntlmssp_end(&cli->ntlmssp_pipe_state); } if (cli->nt_pipe_fnum != 0) @@ -375,9 +375,10 @@ void cli_close_connection(struct cli_state *cli) cli_free_signing_context(cli); data_blob_free(&cli->secblob); + data_blob_free(&cli->user_session_key); if (cli->ntlmssp_pipe_state) - ntlmssp_client_end(&cli->ntlmssp_pipe_state); + ntlmssp_end(&cli->ntlmssp_pipe_state); if (cli->mem_ctx) { talloc_destroy(cli->mem_ctx); diff --git a/source3/libsmb/clikrb5.c b/source3/libsmb/clikrb5.c index 1fccc04a01..5568b5e033 100644 --- a/source3/libsmb/clikrb5.c +++ b/source3/libsmb/clikrb5.c @@ -307,7 +307,7 @@ cleanup_princ: /* get a kerberos5 ticket for the given service */ -DATA_BLOB cli_krb5_get_ticket(const char *principal, time_t time_offset, unsigned char session_key_krb5[16]) +DATA_BLOB cli_krb5_get_ticket(const char *principal, time_t time_offset, DATA_BLOB *session_key_krb5) { krb5_error_code retval; krb5_data packet; @@ -369,7 +369,7 @@ failed: return data_blob(NULL, 0); } - BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, uint8 session_key[16], BOOL remote) + BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote) { krb5_keyblock *skey; krb5_error_code err; @@ -383,11 +383,11 @@ failed: err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey); if (err == 0 && skey != NULL) { DEBUG(10, ("Got KRB5 session key of length %d\n", KRB5_KEY_LENGTH(skey))); - if (KRB5_KEY_LENGTH(skey) == 16) { - memcpy(session_key, KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey)); - dump_data_pw("KRB5 Session Key:\n", session_key, 16); - ret = True; - } + *session_key = data_blob(KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey)); + dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length); + + ret = True; + krb5_free_keyblock(context, skey); } else { DEBUG(10, ("KRB5 error getting session key %d\n", err)); @@ -410,7 +410,7 @@ failed: #else /* HAVE_KRB5 */ /* this saves a few linking headaches */ -DATA_BLOB cli_krb5_get_ticket(const char *principal, time_t time_offset, unsigned char session_key_krb5[16]) +DATA_BLOB cli_krb5_get_ticket(const char *principal, time_t time_offset, DATA_BLOB *session_key_krb5) { DEBUG(0,("NO KERBEROS SUPPORT\n")); return data_blob(NULL, 0); diff --git a/source3/libsmb/clispnego.c b/source3/libsmb/clispnego.c index 63076a1a1c..92543736ff 100644 --- a/source3/libsmb/clispnego.c +++ b/source3/libsmb/clispnego.c @@ -323,7 +323,7 @@ BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2]) generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY kerberos session setup */ -DATA_BLOB spnego_gen_negTokenTarg(const char *principal, int time_offset, unsigned char session_key_krb5[16]) +DATA_BLOB spnego_gen_negTokenTarg(const char *principal, int time_offset, DATA_BLOB *session_key_krb5) { DATA_BLOB tkt, tkt_wrapped, targ; const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL}; diff --git a/source3/libsmb/ntlmssp.c b/source3/libsmb/ntlmssp.c index c51b599b04..a0da1efcc1 100644 --- a/source3/libsmb/ntlmssp.c +++ b/source3/libsmb/ntlmssp.c @@ -23,6 +23,35 @@ #include "includes.h" +static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state, + DATA_BLOB reply, DATA_BLOB *next_request); +static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, + const DATA_BLOB in, DATA_BLOB *out); +static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, + const DATA_BLOB reply, DATA_BLOB *next_request); +static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, + const DATA_BLOB request, DATA_BLOB *reply); + +/** + * Callbacks for NTLMSSP - for both client and server operating modes + * + */ + +static const struct ntlmssp_callbacks { + enum NTLMSSP_ROLE role; + enum NTLM_MESSAGE_TYPE ntlmssp_command; + NTSTATUS (*fn)(struct ntlmssp_state *ntlmssp_state, + DATA_BLOB in, DATA_BLOB *out); +} ntlmssp_callbacks[] = { + {NTLMSSP_CLIENT, NTLMSSP_INITIAL, ntlmssp_client_initial}, + {NTLMSSP_SERVER, NTLMSSP_NEGOTIATE, ntlmssp_server_negotiate}, + {NTLMSSP_CLIENT, NTLMSSP_CHALLENGE, ntlmssp_client_challenge}, + {NTLMSSP_SERVER, NTLMSSP_AUTH, ntlmssp_server_auth}, + {NTLMSSP_CLIENT, NTLMSSP_UNKNOWN, NULL}, + {NTLMSSP_SERVER, NTLMSSP_UNKNOWN, NULL} +}; + + /** * Print out the NTLMSSP flags for debugging * @param neg_flags The flags from the packet @@ -71,7 +100,7 @@ void debug_ntlmssp_flags(uint32 neg_flags) * */ -static const uint8 *get_challenge(struct ntlmssp_state *ntlmssp_state) +static const uint8 *get_challenge(const struct ntlmssp_state *ntlmssp_state) { static uchar chal[8]; generate_random_buffer(chal, sizeof(chal), False); @@ -80,6 +109,188 @@ static const uint8 *get_challenge(struct ntlmssp_state *ntlmssp_state) } /** + * Default 'we can set the challenge to anything we like' implementation + * + */ + +static BOOL may_set_challenge(const struct ntlmssp_state *ntlmssp_state) +{ + return True; +} + +/** + * Default 'we can set the challenge to anything we like' implementation + * + * Does not actually do anything, as the value is always in the structure anyway. + * + */ + +static NTSTATUS set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge) +{ + SMB_ASSERT(challenge->length == 8); + return NT_STATUS_OK; +} + +/** + * Set a username on an NTLMSSP context - ensures it is talloc()ed + * + */ + +NTSTATUS ntlmssp_set_username(NTLMSSP_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; +} + +/** + * Set a password on an NTLMSSP context - ensures it is talloc()ed + * + */ +NTSTATUS ntlmssp_set_password(NTLMSSP_STATE *ntlmssp_state, const char *password) +{ + if (!password) { + ntlmssp_state->password = NULL; + } else { + ntlmssp_state->password = talloc_strdup(ntlmssp_state->mem_ctx, password); + if (!ntlmssp_state->password) { + return NT_STATUS_NO_MEMORY; + } + } + return NT_STATUS_OK; +} + +/** + * Set a domain on an NTLMSSP context - ensures it is talloc()ed + * + */ +NTSTATUS ntlmssp_set_domain(NTLMSSP_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; +} + +/** + * Set a workstation on an NTLMSSP context - ensures it is talloc()ed + * + */ +NTSTATUS ntlmssp_set_workstation(NTLMSSP_STATE *ntlmssp_state, const char *workstation) +{ + ntlmssp_state->workstation = talloc_strdup(ntlmssp_state->mem_ctx, workstation); + if (!ntlmssp_state->domain) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +/** + * Store a DATA_BLOB containing an NTLMSSP response, for use later. + * This copies the data blob + */ + +NTSTATUS ntlmssp_store_response(NTLMSSP_STATE *ntlmssp_state, + DATA_BLOB response) +{ + ntlmssp_state->stored_response = data_blob_talloc(ntlmssp_state->mem_ctx, + response.data, response.length); + return NT_STATUS_OK; +} + +/** + * Next state function for the NTLMSSP state machine + * + * @param ntlmssp_state NTLMSSP State + * @param in The packet in from the NTLMSSP partner, as a DATA_BLOB + * @param out The reply, as an allocated DATA_BLOB, caller to free. + * @return Errors, NT_STATUS_MORE_PROCESSING_REQUIRED or NT_STATUS_OK. + */ + +NTSTATUS ntlmssp_update(NTLMSSP_STATE *ntlmssp_state, + const DATA_BLOB in, DATA_BLOB *out) +{ + DATA_BLOB input; + uint32 ntlmssp_command; + int i; + + *out = data_blob(NULL, 0); + + if (!in.length && ntlmssp_state->stored_response.length) { + input = ntlmssp_state->stored_response; + + /* we only want to read the stored response once - overwrite it */ + ntlmssp_state->stored_response = data_blob(NULL, 0); + } else { + input = in; + } + + if (!input.length) { + switch (ntlmssp_state->role) { + case NTLMSSP_CLIENT: + ntlmssp_command = NTLMSSP_INITIAL; + break; + case NTLMSSP_SERVER: + /* 'datagram' mode - no neg packet */ + ntlmssp_command = NTLMSSP_NEGOTIATE; + break; + } + } else { + if (!msrpc_parse(&input, "Cd", + "NTLMSSP", + &ntlmssp_command)) { + DEBUG(1, ("Failed to parse NTLMSSP packet, could not extract NTLMSSP command\n")); + dump_data(2, (const char *)input.data, input.length); + return NT_STATUS_INVALID_PARAMETER; + } + } + + if (ntlmssp_command != ntlmssp_state->expected_state) { + DEBUG(1, ("got NTLMSSP command %u, expected %u\n", ntlmssp_command, ntlmssp_state->expected_state)); + return NT_STATUS_INVALID_PARAMETER; + } + + for (i=0; ntlmssp_callbacks[i].fn; i++) { + if (ntlmssp_callbacks[i].role == ntlmssp_state->role + && ntlmssp_callbacks[i].ntlmssp_command == ntlmssp_command) { + return ntlmssp_callbacks[i].fn(ntlmssp_state, input, out); + } + } + + DEBUG(1, ("failed to find NTLMSSP callback for NTLMSSP mode %u, command %u\n", + ntlmssp_state->role, ntlmssp_command)); + + return NT_STATUS_INVALID_PARAMETER; +} + +/** + * End an NTLMSSP state machine + * + * @param ntlmssp_state NTLMSSP State, free()ed by this function + */ + +void ntlmssp_end(NTLMSSP_STATE **ntlmssp_state) +{ + TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx; + + (*ntlmssp_state)->ref_count--; + + if ((*ntlmssp_state)->ref_count == 0) { + data_blob_free(&(*ntlmssp_state)->chal); + data_blob_free(&(*ntlmssp_state)->lm_resp); + data_blob_free(&(*ntlmssp_state)->nt_resp); + + talloc_destroy(mem_ctx); + } + + *ntlmssp_state = NULL; + return; +} + +/** * Determine correct target name flags for reply, given server role * and negotiated flags * @@ -107,6 +318,46 @@ static const char *ntlmssp_target_name(struct ntlmssp_state *ntlmssp_state, } } +static void ntlmssp_handle_neg_flags(struct ntlmssp_state *ntlmssp_state, + uint32 neg_flags, BOOL allow_lm) { + if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM; + ntlmssp_state->unicode = True; + } else { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE; + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM; + ntlmssp_state->unicode = False; + } + + if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY && allow_lm) { + /* other end forcing us to use LM */ + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY; + ntlmssp_state->use_ntlmv2 = False; + } else { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_NTLM2)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_128)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128; + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH; + } + + if ((neg_flags & NTLMSSP_REQUEST_TARGET)) { + ntlmssp_state->neg_flags |= NTLMSSP_REQUEST_TARGET; + } + +} + + /** * Next state function for the Negotiate packet * @@ -150,31 +401,27 @@ static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, debug_ntlmssp_flags(neg_flags); } - cryptkey = ntlmssp_state->get_challenge(ntlmssp_state); + ntlmssp_handle_neg_flags(ntlmssp_state, neg_flags, lp_lanman_auth()); - data_blob_free(&ntlmssp_state->chal); - ntlmssp_state->chal = data_blob(cryptkey, 8); - - /* Give them the challenge. For now, ignore neg_flags and just - return the flags we want. Obviously this is not correct */ - - chal_flags = - NTLMSSP_NEGOTIATE_128 | - NTLMSSP_NEGOTIATE_NTLM; - - if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) { - chal_flags |= NTLMSSP_NEGOTIATE_UNICODE; - ntlmssp_state->unicode = True; - } else { - chal_flags |= NTLMSSP_NEGOTIATE_OEM; - } + chal_flags = ntlmssp_state->neg_flags; target_name = ntlmssp_target_name(ntlmssp_state, neg_flags, &chal_flags); - - if (target_name == NULL) + if (target_name == NULL) return NT_STATUS_INVALID_PARAMETER; + /* Ask our caller what challenge they would like in the packet */ + cryptkey = ntlmssp_state->get_challenge(ntlmssp_state); + + /* Check if we may set the challenge */ + if (!ntlmssp_state->may_set_challenge(ntlmssp_state)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; + } + + ntlmssp_state->chal = data_blob_talloc(ntlmssp_state->mem_ctx, cryptkey, 8); + ntlmssp_state->internal_chal = data_blob_talloc(ntlmssp_state->mem_ctx, cryptkey, 8); + + /* This should be a 'netbios domain -> DNS domain' mapping */ dnsdomname[0] = '\0'; get_mydomname(dnsdomname); @@ -184,6 +431,7 @@ static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, get_myfullname(dnsname); strlower_m(dnsname); + /* This creates the 'blob' of names that appears at the end of the packet */ if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) { const char *target_name_dns = ""; @@ -194,16 +442,17 @@ static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, } msrpc_gen(&struct_blob, "aaaaa", - ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_DOMAIN, target_name, - ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_SERVER, ntlmssp_state->get_global_myname(), - ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_DOMAIN_DNS, target_name_dns, - ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_SERVER_DNS, dnsdomname, - ntlmssp_state->unicode, 0, ""); + NTLMSSP_NAME_TYPE_DOMAIN, target_name, + NTLMSSP_NAME_TYPE_SERVER, ntlmssp_state->get_global_myname(), + NTLMSSP_NAME_TYPE_DOMAIN_DNS, dnsdomname, + NTLMSSP_NAME_TYPE_SERVER_DNS, dnsname, + 0, ""); } else { struct_blob = data_blob(NULL, 0); } { + /* Marshel the packet in the right format, be it unicode or ASCII */ const char *gen_string; if (ntlmssp_state->unicode) { gen_string = "CdUdbddB"; @@ -240,13 +489,27 @@ static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, 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; + DATA_BLOB encrypted_session_key = data_blob(NULL, 0); + DATA_BLOB nt_session_key = data_blob(NULL, 0); + DATA_BLOB lm_session_key = data_blob(NULL, 0); + DATA_BLOB session_key = data_blob(NULL, 0); + uint32 ntlmssp_command, auth_flags; NTSTATUS nt_status; + /* used by NTLM2 */ + BOOL doing_ntlm2 = False; + + uchar session_nonce[16]; + uchar session_nonce_hash[16]; + const char *parse_string; + char *domain = NULL; + char *user = NULL; + char *workstation = NULL; /* parse the NTLMSSP packet */ + *reply = data_blob(NULL, 0); + #if 0 file_save("ntlmssp_auth.dat", request.data, request.length); #endif @@ -260,9 +523,9 @@ static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, 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); + ntlmssp_state->user = NULL; + ntlmssp_state->domain = NULL; + ntlmssp_state->workstation = NULL; /* now the NTLMSSP encoded auth hashes */ if (!msrpc_parse(&request, parse_string, @@ -270,18 +533,73 @@ static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, &ntlmssp_command, &ntlmssp_state->lm_resp, &ntlmssp_state->nt_resp, - &ntlmssp_state->domain, - &ntlmssp_state->user, - &ntlmssp_state->workstation, - &sess_key, - &neg_flags)) { + &domain, + &user, + &workstation, + &encrypted_session_key, + &auth_flags)) { DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n")); dump_data(2, (const char *)request.data, request.length); - return NT_STATUS_INVALID_PARAMETER; + SAFE_FREE(domain); + SAFE_FREE(user); + SAFE_FREE(workstation); + data_blob_free(&encrypted_session_key); + auth_flags = 0; + + /* Try again with a shorter string (Win9X truncates this packet) */ + if (ntlmssp_state->unicode) { + parse_string = "CdBBUUU"; + } else { + parse_string = "CdBBAAA"; + } + + /* now the NTLMSSP encoded auth hashes */ + if (!msrpc_parse(&request, parse_string, + "NTLMSSP", + &ntlmssp_command, + &ntlmssp_state->lm_resp, + &ntlmssp_state->nt_resp, + &domain, + &user, + &workstation)) { + DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n")); + dump_data(2, (const char *)request.data, request.length); + SAFE_FREE(domain); + SAFE_FREE(user); + SAFE_FREE(workstation); + + return NT_STATUS_INVALID_PARAMETER; + } } - data_blob_free(&sess_key); - + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) { + SAFE_FREE(domain); + SAFE_FREE(user); + SAFE_FREE(workstation); + data_blob_free(&encrypted_session_key); + return nt_status; + } + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) { + SAFE_FREE(domain); + SAFE_FREE(user); + SAFE_FREE(workstation); + data_blob_free(&encrypted_session_key); + return nt_status; + } + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(ntlmssp_state, workstation))) { + SAFE_FREE(domain); + SAFE_FREE(user); + SAFE_FREE(workstation); + data_blob_free(&encrypted_session_key); + return nt_status; + } + + SAFE_FREE(domain); + SAFE_FREE(user); + SAFE_FREE(workstation); + DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n", ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, (unsigned long)ntlmssp_state->lm_resp.length, (unsigned long)ntlmssp_state->nt_resp.length)); @@ -290,9 +608,98 @@ static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, file_save("lmhash1.dat", &ntlmssp_state->lm_resp.data, &ntlmssp_state->lm_resp.length); #endif - nt_status = ntlmssp_state->check_password(ntlmssp_state); + /* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a + client challenge - *reply = data_blob(NULL, 0); + However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful. + */ + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { + if (ntlmssp_state->nt_resp.length == 24 && ntlmssp_state->lm_resp.length == 24) { + struct MD5Context md5_session_nonce_ctx; + SMB_ASSERT(ntlmssp_state->internal_chal.data && ntlmssp_state->internal_chal.length == 8); + + doing_ntlm2 = True; + + memcpy(session_nonce, ntlmssp_state->internal_chal.data, 8); + memcpy(&session_nonce[8], ntlmssp_state->lm_resp.data, 8); + + MD5Init(&md5_session_nonce_ctx); + MD5Update(&md5_session_nonce_ctx, session_nonce, 16); + MD5Final(session_nonce_hash, &md5_session_nonce_ctx); + + ntlmssp_state->chal = data_blob_talloc(ntlmssp_state->mem_ctx, session_nonce_hash, 8); + + /* LM response is no longer useful */ + data_blob_free(&ntlmssp_state->lm_resp); + + /* We changed the effective challenge - set it */ + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->set_challenge(ntlmssp_state, &ntlmssp_state->chal))) { + data_blob_free(&encrypted_session_key); + return nt_status; + } + } + } + + /* Finally, actually ask if the password is OK */ + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->check_password(ntlmssp_state, &nt_session_key, &lm_session_key))) { + data_blob_free(&encrypted_session_key); + return nt_status; + } + + dump_data_pw("NT session key:\n", nt_session_key.data, nt_session_key.length); + dump_data_pw("LM first-8:\n", lm_session_key.data, lm_session_key.length); + + /* Handle the different session key derivation for NTLM2 */ + if (doing_ntlm2) { + if (nt_session_key.data && nt_session_key.length == 16) { + session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16); + hmac_md5(nt_session_key.data, session_nonce, + sizeof(session_nonce), session_key.data); + dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); + + } + } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) { + if (lm_session_key.data && lm_session_key.length >= 8 && + ntlmssp_state->lm_resp.data && ntlmssp_state->lm_resp.length == 24) { + session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16); + SMBsesskeygen_lmv1(lm_session_key.data, ntlmssp_state->lm_resp.data, + session_key.data); + dump_data_pw("LM session key:\n", session_key.data, session_key.length); + } + } else if (nt_session_key.data) { + session_key = nt_session_key; + dump_data_pw("unmodified session key:\n", session_key.data, session_key.length); + } + + /* With KEY_EXCH, the client supplies the proposed session key, + but encrypts it with the long-term key */ + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { + if (!encrypted_session_key.data || encrypted_session_key.length != 16) { + data_blob_free(&encrypted_session_key); + DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n", + encrypted_session_key.length)); + return NT_STATUS_INVALID_PARAMETER; + } else if (!session_key.data || session_key.length != 16) { + DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n", + session_key.length)); + } else { + dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length); + SamOEMhash(encrypted_session_key.data, + session_key.data, + encrypted_session_key.length); + ntlmssp_state->session_key = data_blob_talloc(ntlmssp_state->mem_ctx, + encrypted_session_key.data, + encrypted_session_key.length); + dump_data_pw("KEY_EXCH session key:\n", session_key.data, session_key.length); + } + } else { + ntlmssp_state->session_key = session_key; + } + + data_blob_free(&encrypted_session_key); + + /* allow arbitarily many authentications */ + ntlmssp_state->expected_state = NTLMSSP_AUTH; return nt_status; } @@ -316,8 +723,12 @@ NTSTATUS ntlmssp_server_start(NTLMSSP_STATE **ntlmssp_state) return NT_STATUS_NO_MEMORY; } + (*ntlmssp_state)->role = NTLMSSP_SERVER; + (*ntlmssp_state)->mem_ctx = mem_ctx; (*ntlmssp_state)->get_challenge = get_challenge; + (*ntlmssp_state)->set_challenge = set_challenge; + (*ntlmssp_state)->may_set_challenge = may_set_challenge; (*ntlmssp_state)->get_global_myname = global_myname; (*ntlmssp_state)->get_domain = lp_workgroup; @@ -325,73 +736,18 @@ NTSTATUS ntlmssp_server_start(NTLMSSP_STATE **ntlmssp_state) (*ntlmssp_state)->expected_state = NTLMSSP_NEGOTIATE; - return NT_STATUS_OK; -} - -/** - * End an NTLMSSP state machine - * - * @param ntlmssp_state NTLMSSP State, free()ed by this function - */ - -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); + (*ntlmssp_state)->ref_count = 1; - SAFE_FREE((*ntlmssp_state)->user); - SAFE_FREE((*ntlmssp_state)->domain); - SAFE_FREE((*ntlmssp_state)->workstation); + (*ntlmssp_state)->neg_flags = + NTLMSSP_NEGOTIATE_128 | + NTLMSSP_NEGOTIATE_NTLM | + NTLMSSP_NEGOTIATE_NTLM2 | + NTLMSSP_NEGOTIATE_KEY_EXCH | + NTLMSSP_NEGOTIATE_SIGN; - 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 (request.length) { - if (!msrpc_parse(&request, "Cd", - "NTLMSSP", - &ntlmssp_command)) { - return NT_STATUS_INVALID_PARAMETER; - } - } else { - /* 'datagram' mode - no neg packet */ - ntlmssp_command = NTLMSSP_NEGOTIATE; - } - - if (ntlmssp_command != ntlmssp_state->expected_state) { - DEBUG(1, ("got NTLMSSP command %u, expected %u\n", 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 { - DEBUG(1, ("unknown NTLMSSP command %u, expected %u\n", ntlmssp_command, ntlmssp_state->expected_state)); - return NT_STATUS_INVALID_PARAMETER; - } -} - /********************************************************************* Client side NTLMSSP *********************************************************************/ @@ -405,11 +761,13 @@ NTSTATUS ntlmssp_server_update(NTLMSSP_STATE *ntlmssp_state, * @return Errors or NT_STATUS_OK. */ -static NTSTATUS ntlmssp_client_initial(struct ntlmssp_client_state *ntlmssp_state, +static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state, DATA_BLOB reply, DATA_BLOB *next_request) { if (ntlmssp_state->unicode) { ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; + } else { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM; } if (ntlmssp_state->use_ntlmv2) { @@ -426,6 +784,8 @@ static NTSTATUS ntlmssp_client_initial(struct ntlmssp_client_state *ntlmssp_stat ntlmssp_state->get_domain(), ntlmssp_state->get_global_myname()); + ntlmssp_state->expected_state = NTLMSSP_CHALLENGE; + return NT_STATUS_MORE_PROCESSING_REQUIRED; } @@ -438,7 +798,7 @@ static NTSTATUS ntlmssp_client_initial(struct ntlmssp_client_state *ntlmssp_stat * @return Errors or NT_STATUS_OK. */ -static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_state, +static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, const DATA_BLOB reply, DATA_BLOB *next_request) { uint32 chal_flags, ntlmssp_command, unkn1, unkn2; @@ -469,17 +829,16 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st DEBUG(3, ("Got challenge flags:\n")); debug_ntlmssp_flags(chal_flags); - if (chal_flags & NTLMSSP_NEGOTIATE_UNICODE) { + ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, lp_client_lanman_auth()); + + if (ntlmssp_state->unicode) { if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) { chal_parse_string = "CdUdbddB"; } else { chal_parse_string = "CdUdbdd"; } 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) { + } else { if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) { chal_parse_string = "CdAdbddB"; } else { @@ -487,32 +846,6 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st } 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 (chal_flags & NTLMSSP_NEGOTIATE_LM_KEY && lp_client_lanman_auth()) { - /* server forcing us to use LM */ - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY; - ntlmssp_state->use_ntlmv2 = False; - } else { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - } - - if (!(chal_flags & NTLMSSP_NEGOTIATE_NTLM2)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; - } - - if (!(chal_flags & NTLMSSP_NEGOTIATE_128)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128; - } - - if (!(chal_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH; } DEBUG(3, ("NTLMSSP: Set final flags:\n")); @@ -546,6 +879,7 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st if (!struct_blob.length) { /* be lazy, match win2k - we can't do NTLMv2 without it */ + DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n")); return NT_STATUS_INVALID_PARAMETER; } @@ -569,7 +903,7 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st uchar nt_session_key[16]; E_md4hash(ntlmssp_state->password, nt_hash); - lm_response = data_blob(NULL, 24); + lm_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24); generate_random_buffer(lm_response.data, 8, False); memset(lm_response.data+8, 0, 16); @@ -580,16 +914,21 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st MD5Update(&md5_session_nonce_ctx, challenge_blob.data, 8); MD5Update(&md5_session_nonce_ctx, lm_response.data, 8); MD5Final(session_nonce_hash, &md5_session_nonce_ctx); + + DEBUG(5, ("NTLMSSP challenge set by NTLM2\n")); + DEBUG(5, ("challenge is: \n")); + dump_data(5, session_nonce_hash, 8); - nt_response = data_blob(NULL, 24); + nt_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24); SMBNTencrypt(ntlmssp_state->password, session_nonce_hash, nt_response.data); - session_key = data_blob(NULL, 16); + session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16); SMBsesskeygen_ntv1(nt_hash, NULL, nt_session_key); hmac_md5(nt_session_key, session_nonce, sizeof(session_nonce), session_key.data); + dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); } else { @@ -600,22 +939,24 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st /* lanman auth is insecure, it may be disabled */ if (lp_client_lanman_auth()) { - lm_response = data_blob(NULL, 24); + lm_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24); SMBencrypt(ntlmssp_state->password,challenge_blob.data, lm_response.data); - } + } - nt_response = data_blob(NULL, 24); + nt_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24); SMBNTencrypt(ntlmssp_state->password,challenge_blob.data, nt_response.data); - session_key = data_blob(NULL, 16); + session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16); if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && lp_client_lanman_auth()) { SMBsesskeygen_lmv1(lm_hash, lm_response.data, session_key.data); + dump_data_pw("LM session key\n", session_key.data, session_key.length); } else { SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); + dump_data_pw("NT session key:\n", session_key.data, session_key.length); } } data_blob_free(&struct_blob); @@ -627,9 +968,12 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st generate_random_buffer(client_session_key, sizeof(client_session_key), False); encrypted_session_key = data_blob(client_session_key, sizeof(client_session_key)); + dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length); + SamOEMhash(encrypted_session_key.data, session_key.data, encrypted_session_key.length); data_blob_free(&session_key); - session_key = data_blob(client_session_key, sizeof(client_session_key)); + session_key = data_blob_talloc(ntlmssp_state->mem_ctx, client_session_key, sizeof(client_session_key)); + dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length); } /* this generates the actual auth packet */ @@ -644,28 +988,24 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st encrypted_session_key.data, encrypted_session_key.length, 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(&encrypted_session_key); data_blob_free(&ntlmssp_state->chal); - data_blob_free(&ntlmssp_state->lm_resp); - data_blob_free(&ntlmssp_state->nt_resp); - data_blob_free(&ntlmssp_state->session_key); ntlmssp_state->chal = challenge_blob; ntlmssp_state->lm_resp = lm_response; ntlmssp_state->nt_resp = nt_response; ntlmssp_state->session_key = session_key; + ntlmssp_state->expected_state = NTLMSSP_UNKNOWN; + return NT_STATUS_MORE_PROCESSING_REQUIRED; } -NTSTATUS ntlmssp_client_start(NTLMSSP_CLIENT_STATE **ntlmssp_state) +NTSTATUS ntlmssp_client_start(NTLMSSP_STATE **ntlmssp_state) { TALLOC_CTX *mem_ctx; @@ -678,6 +1018,8 @@ NTSTATUS ntlmssp_client_start(NTLMSSP_CLIENT_STATE **ntlmssp_state) return NT_STATUS_NO_MEMORY; } + (*ntlmssp_state)->role = NTLMSSP_CLIENT; + (*ntlmssp_state)->mem_ctx = mem_ctx; (*ntlmssp_state)->get_global_myname = global_myname; @@ -687,6 +1029,10 @@ NTSTATUS ntlmssp_client_start(NTLMSSP_CLIENT_STATE **ntlmssp_state) (*ntlmssp_state)->use_ntlmv2 = lp_client_ntlmv2_auth(); + (*ntlmssp_state)->expected_state = NTLMSSP_INITIAL; + + (*ntlmssp_state)->ref_count = 1; + (*ntlmssp_state)->neg_flags = NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_NTLM | @@ -700,101 +1046,6 @@ NTSTATUS ntlmssp_client_start(NTLMSSP_CLIENT_STATE **ntlmssp_state) NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_REQUEST_TARGET; - (*ntlmssp_state)->ref_count = 1; - - return NT_STATUS_OK; -} - -NTSTATUS ntlmssp_client_end(NTLMSSP_CLIENT_STATE **ntlmssp_state) -{ - TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx; - - (*ntlmssp_state)->ref_count--; - - if ((*ntlmssp_state)->ref_count == 0) { - data_blob_free(&(*ntlmssp_state)->chal); - data_blob_free(&(*ntlmssp_state)->lm_resp); - data_blob_free(&(*ntlmssp_state)->nt_resp); - data_blob_free(&(*ntlmssp_state)->session_key); - data_blob_free(&(*ntlmssp_state)->stored_response); - 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) -{ - NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER; - uint32 ntlmssp_command; - *next_request = data_blob(NULL, 0); - - if (!reply.length) { - /* If there is a cached reply, use it - otherwise this is the first packet */ - if (!ntlmssp_state->stored_response.length) { - return ntlmssp_client_initial(ntlmssp_state, reply, next_request); - } - - reply = ntlmssp_state->stored_response; - } - - if (!msrpc_parse(&reply, "Cd", - "NTLMSSP", - &ntlmssp_command)) { - return NT_STATUS_INVALID_PARAMETER; - } - - if (ntlmssp_command == NTLMSSP_CHALLENGE) { - nt_status = ntlmssp_client_challenge(ntlmssp_state, reply, next_request); - } - if (ntlmssp_state->stored_response.length) { - data_blob_free(&ntlmssp_state->stored_response); - } - return nt_status; -} - -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) -{ - if (!password) { - ntlmssp_state->password = NULL; - } else { - 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; -} - -/** - * Store a DATA_BLOB containing an NTLMSSP response, for use later. - * This 'keeps' the data blob - the caller must *not* free it. - */ - -NTSTATUS ntlmssp_client_store_response(NTLMSSP_CLIENT_STATE *ntlmssp_state, - DATA_BLOB response) -{ - data_blob_free(&ntlmssp_state->stored_response); - ntlmssp_state->stored_response = response; - return NT_STATUS_OK; -} diff --git a/source3/libsmb/ntlmssp_parse.c b/source3/libsmb/ntlmssp_parse.c index b136dacf5a..3444db0306 100644 --- a/source3/libsmb/ntlmssp_parse.c +++ b/source3/libsmb/ntlmssp_parse.c @@ -31,7 +31,7 @@ format specifiers are: U = unicode string (input is unix string) - a = address (input is BOOL unicode, char *unix_string) + a = address (input is 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) @@ -49,7 +49,6 @@ BOOL msrpc_gen(DATA_BLOB *blob, 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); @@ -66,14 +65,9 @@ BOOL msrpc_gen(DATA_BLOB *blob, 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; - } + data_size += (str_charnum(s) * 2) + 4; break; case 'B': b = va_arg(ap, uint8 *); @@ -124,27 +118,16 @@ BOOL msrpc_gen(DATA_BLOB *blob, 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; + 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; break; case 'B': diff --git a/source3/libsmb/ntlmssp_sign.c b/source3/libsmb/ntlmssp_sign.c index 153c234d1f..ea1a7037c9 100644 --- a/source3/libsmb/ntlmssp_sign.c +++ b/source3/libsmb/ntlmssp_sign.c @@ -102,7 +102,7 @@ enum ntlmssp_direction { NTLMSSP_RECEIVE }; -static NTSTATUS ntlmssp_make_packet_signature(NTLMSSP_CLIENT_STATE *ntlmssp_state, +static NTSTATUS ntlmssp_make_packet_signature(NTLMSSP_STATE *ntlmssp_state, const uchar *data, size_t length, enum ntlmssp_direction direction, DATA_BLOB *sig) @@ -113,7 +113,7 @@ static NTSTATUS ntlmssp_make_packet_signature(NTLMSSP_CLIENT_STATE *ntlmssp_stat uchar digest[16]; SIVAL(seq_num, 0, ntlmssp_state->ntlmssp_seq_num); - hmac_md5_init_limK_to_64((const unsigned char *)(ntlmssp_state->cli_sign_const), 16, &ctx); + hmac_md5_init_limK_to_64((const unsigned char *)(ntlmssp_state->send_sign_const), 16, &ctx); hmac_md5_update((const unsigned char *)seq_num, 4, &ctx); hmac_md5_update(data, length, &ctx); hmac_md5_final(digest, &ctx); @@ -124,10 +124,10 @@ static NTSTATUS ntlmssp_make_packet_signature(NTLMSSP_CLIENT_STATE *ntlmssp_stat } switch (direction) { case NTLMSSP_SEND: - NTLMSSPcalc_ap(ntlmssp_state->cli_sign_hash, sig->data+4, sig->length-4); + NTLMSSPcalc_ap(ntlmssp_state->send_sign_hash, sig->data+4, sig->length-4); break; case NTLMSSP_RECEIVE: - NTLMSSPcalc_ap(ntlmssp_state->srv_sign_hash, sig->data+4, sig->length-4); + NTLMSSPcalc_ap(ntlmssp_state->recv_sign_hash, sig->data+4, sig->length-4); break; } } else { @@ -144,9 +144,9 @@ static NTSTATUS ntlmssp_make_packet_signature(NTLMSSP_CLIENT_STATE *ntlmssp_stat return NT_STATUS_OK; } -NTSTATUS ntlmssp_client_sign_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state, - const uchar *data, size_t length, - DATA_BLOB *sig) +NTSTATUS ntlmssp_sign_packet(NTLMSSP_STATE *ntlmssp_state, + const uchar *data, size_t length, + DATA_BLOB *sig) { NTSTATUS nt_status = ntlmssp_make_packet_signature(ntlmssp_state, data, length, NTLMSSP_SEND, sig); @@ -161,9 +161,9 @@ NTSTATUS ntlmssp_client_sign_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state, * */ -NTSTATUS ntlmssp_client_check_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state, - const uchar *data, size_t length, - const DATA_BLOB *sig) +NTSTATUS ntlmssp_check_packet(NTLMSSP_STATE *ntlmssp_state, + const uchar *data, size_t length, + const DATA_BLOB *sig) { DATA_BLOB local_sig; NTSTATUS nt_status; @@ -204,11 +204,11 @@ NTSTATUS ntlmssp_client_check_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state, * */ -NTSTATUS ntlmssp_client_seal_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state, - uchar *data, size_t length, - DATA_BLOB *sig) +NTSTATUS ntlmssp_seal_packet(NTLMSSP_STATE *ntlmssp_state, + uchar *data, size_t length, + DATA_BLOB *sig) { - DEBUG(10,("ntlmssp_client_seal_data: seal\n")); + DEBUG(10,("ntlmssp_seal_data: seal\n")); dump_data_pw("ntlmssp clear data\n", data, length); if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { HMACMD5Context ctx; @@ -216,7 +216,7 @@ NTSTATUS ntlmssp_client_seal_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state, uchar digest[16]; SIVAL(seq_num, 0, ntlmssp_state->ntlmssp_seq_num); - hmac_md5_init_limK_to_64((const unsigned char *)(ntlmssp_state->cli_sign_const), 16, &ctx); + hmac_md5_init_limK_to_64((const unsigned char *)(ntlmssp_state->send_sign_const), 16, &ctx); hmac_md5_update((const unsigned char *)seq_num, 4, &ctx); hmac_md5_update(data, length, &ctx); hmac_md5_final(digest, &ctx); @@ -227,13 +227,13 @@ NTSTATUS ntlmssp_client_seal_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state, } dump_data_pw("ntlmssp client sealing hash:\n", - ntlmssp_state->cli_seal_hash, - sizeof(ntlmssp_state->cli_seal_hash)); - NTLMSSPcalc_ap(ntlmssp_state->cli_seal_hash, data, length); + ntlmssp_state->send_seal_hash, + sizeof(ntlmssp_state->send_seal_hash)); + NTLMSSPcalc_ap(ntlmssp_state->send_seal_hash, data, length); dump_data_pw("ntlmssp client signing hash:\n", - ntlmssp_state->cli_sign_hash, - sizeof(ntlmssp_state->cli_sign_hash)); - NTLMSSPcalc_ap(ntlmssp_state->cli_sign_hash, sig->data+4, sig->length-4); + ntlmssp_state->send_sign_hash, + sizeof(ntlmssp_state->send_sign_hash)); + NTLMSSPcalc_ap(ntlmssp_state->send_sign_hash, sig->data+4, sig->length-4); } else { uint32 crc; crc = crc32_calc_buffer((const char *)data, length); @@ -266,14 +266,14 @@ NTSTATUS ntlmssp_client_seal_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state, * */ -NTSTATUS ntlmssp_client_unseal_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state, +NTSTATUS ntlmssp_unseal_packet(NTLMSSP_STATE *ntlmssp_state, uchar *data, size_t length, DATA_BLOB *sig) { - DEBUG(10,("ntlmssp_client_unseal_data: seal\n")); + DEBUG(10,("ntlmssp__unseal_data: seal\n")); dump_data_pw("ntlmssp sealed data\n", data, length); if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { - NTLMSSPcalc_ap(ntlmssp_state->srv_seal_hash, data, length); + NTLMSSPcalc_ap(ntlmssp_state->recv_seal_hash, data, length); } else { dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash, sizeof(ntlmssp_state->ntlmssp_hash)); @@ -281,13 +281,13 @@ NTSTATUS ntlmssp_client_unseal_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state, } dump_data_pw("ntlmssp clear data\n", data, length); - return ntlmssp_client_check_packet(ntlmssp_state, data, length, sig); + return ntlmssp_check_packet(ntlmssp_state, data, length, sig); } /** Initialise the state for NTLMSSP signing. */ -NTSTATUS ntlmssp_client_sign_init(NTLMSSP_CLIENT_STATE *ntlmssp_state) +NTSTATUS ntlmssp_sign_init(NTLMSSP_STATE *ntlmssp_state) { unsigned char p24[24]; ZERO_STRUCT(p24); @@ -297,34 +297,54 @@ NTSTATUS ntlmssp_client_sign_init(NTLMSSP_CLIENT_STATE *ntlmssp_state) if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { + const char *send_sign_const; + const char *send_seal_const; + const char *recv_sign_const; + const char *recv_seal_const; + + switch (ntlmssp_state->role) { + case NTLMSSP_CLIENT: + send_sign_const = CLI_SIGN; + send_seal_const = CLI_SEAL; + recv_sign_const = SRV_SIGN; + recv_seal_const = SRV_SEAL; + break; + case NTLMSSP_SERVER: + send_sign_const = SRV_SIGN; + send_seal_const = SRV_SEAL; + recv_sign_const = CLI_SIGN; + recv_seal_const = CLI_SEAL; + break; + } + + calc_ntlmv2_hash(ntlmssp_state->send_sign_hash, + ntlmssp_state->send_sign_const, + ntlmssp_state->session_key, send_sign_const); + dump_data_pw("NTLMSSP send sign hash:\n", + ntlmssp_state->send_sign_hash, + sizeof(ntlmssp_state->send_sign_hash)); + + calc_ntlmv2_hash(ntlmssp_state->send_seal_hash, + ntlmssp_state->send_seal_const, + ntlmssp_state->session_key, send_seal_const); + dump_data_pw("NTLMSSP send sesl hash:\n", + ntlmssp_state->send_seal_hash, + sizeof(ntlmssp_state->send_seal_hash)); + + calc_ntlmv2_hash(ntlmssp_state->recv_sign_hash, + ntlmssp_state->recv_sign_const, + ntlmssp_state->session_key, send_sign_const); + dump_data_pw("NTLMSSP receive sign hash:\n", + ntlmssp_state->recv_sign_hash, + sizeof(ntlmssp_state->recv_sign_hash)); + + calc_ntlmv2_hash(ntlmssp_state->recv_seal_hash, + ntlmssp_state->recv_seal_const, + ntlmssp_state->session_key, send_seal_const); + dump_data_pw("NTLMSSP receive seal hash:\n", + ntlmssp_state->recv_sign_hash, + sizeof(ntlmssp_state->recv_sign_hash)); - calc_ntlmv2_hash(ntlmssp_state->cli_sign_hash, - ntlmssp_state->cli_sign_const, - ntlmssp_state->session_key, CLI_SIGN); - dump_data_pw("NTLMSSP client sign hash:\n", - ntlmssp_state->cli_sign_hash, - sizeof(ntlmssp_state->cli_sign_hash)); - - calc_ntlmv2_hash(ntlmssp_state->cli_seal_hash, - ntlmssp_state->cli_seal_const, - ntlmssp_state->session_key, CLI_SEAL); - dump_data_pw("NTLMSSP client sesl hash:\n", - ntlmssp_state->cli_seal_hash, - sizeof(ntlmssp_state->cli_seal_hash)); - - calc_ntlmv2_hash(ntlmssp_state->srv_sign_hash, - ntlmssp_state->srv_sign_const, - ntlmssp_state->session_key, SRV_SIGN); - dump_data_pw("NTLMSSP server sign hash:\n", - ntlmssp_state->srv_sign_hash, - sizeof(ntlmssp_state->srv_sign_hash)); - - calc_ntlmv2_hash(ntlmssp_state->srv_seal_hash, - ntlmssp_state->srv_seal_const, - ntlmssp_state->session_key, SRV_SEAL); - dump_data_pw("NTLMSSP server seal hash:\n", - ntlmssp_state->cli_sign_hash, - sizeof(ntlmssp_state->cli_sign_hash)); } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) { if (!ntlmssp_state->session_key.data || ntlmssp_state->session_key.length < 8) { diff --git a/source3/libsmb/smb_signing.c b/source3/libsmb/smb_signing.c index 91509f0fb8..eec991072d 100644 --- a/source3/libsmb/smb_signing.c +++ b/source3/libsmb/smb_signing.c @@ -405,11 +405,11 @@ static void simple_free_signing_context(struct smb_sign_info *si) SMB signing - Simple implementation - setup the MAC key. ************************************************************/ -BOOL cli_simple_set_signing(struct cli_state *cli, const uchar user_session_key[16], const DATA_BLOB response) +BOOL cli_simple_set_signing(struct cli_state *cli, const DATA_BLOB user_session_key, const DATA_BLOB response) { struct smb_basic_signing_context *data; - if (!user_session_key) + if (!user_session_key.length) return False; if (!cli_set_smb_signing_common(cli)) { @@ -425,21 +425,23 @@ BOOL cli_simple_set_signing(struct cli_state *cli, const uchar user_session_key[ cli->sign_info.signing_context = data; - data->mac_key = data_blob(NULL, response.length + 16); + data->mac_key = data_blob(NULL, response.length + user_session_key.length); - memcpy(&data->mac_key.data[0], user_session_key, 16); + memcpy(&data->mac_key.data[0], user_session_key.data, user_session_key.length); DEBUG(10, ("cli_simple_set_signing: user_session_key\n")); - dump_data(10, (const char *)user_session_key, 16); + dump_data(10, (const char *)user_session_key.data, user_session_key.length); if (response.length) { - memcpy(&data->mac_key.data[16],response.data, response.length); + memcpy(&data->mac_key.data[user_session_key.length],response.data, response.length); DEBUG(10, ("cli_simple_set_signing: response_data\n")); dump_data(10, (const char *)response.data, response.length); } else { DEBUG(10, ("cli_simple_set_signing: NULL response_data\n")); } + dump_data_pw("MAC ssession key is:\n", data->mac_key.data, data->mac_key.length); + /* Initialise the sequence number */ data->send_seq_num = 0; @@ -928,11 +930,11 @@ data->send_seq_num = %u\n", Turn on signing from this packet onwards. ************************************************************/ -void srv_set_signing(const uchar user_session_key[16], const DATA_BLOB response) +void srv_set_signing(const DATA_BLOB user_session_key, const DATA_BLOB response) { struct smb_basic_signing_context *data; - if (!user_session_key) + if (!user_session_key.length) return; if (!srv_sign_info.negotiated_smb_signing && !srv_sign_info.mandatory_signing) { @@ -957,11 +959,13 @@ void srv_set_signing(const uchar user_session_key[16], const DATA_BLOB response) srv_sign_info.signing_context = data; - data->mac_key = data_blob(NULL, response.length + 16); + data->mac_key = data_blob(NULL, response.length + user_session_key.length); - memcpy(&data->mac_key.data[0], user_session_key, 16); + memcpy(&data->mac_key.data[0], user_session_key.data, user_session_key.length); if (response.length) - memcpy(&data->mac_key.data[16],response.data, response.length); + memcpy(&data->mac_key.data[user_session_key.length],response.data, response.length); + + dump_data_pw("MAC ssession key is:\n", data->mac_key.data, data->mac_key.length); /* Initialise the sequence number */ data->send_seq_num = 0; diff --git a/source3/libsmb/smbencrypt.c b/source3/libsmb/smbencrypt.c index ec31bb5dba..2d02a23394 100644 --- a/source3/libsmb/smbencrypt.c +++ b/source3/libsmb/smbencrypt.c @@ -331,9 +331,9 @@ DATA_BLOB NTLMv2_generate_names_blob(const char *hostname, DATA_BLOB names_blob = data_blob(NULL, 0); msrpc_gen(&names_blob, "aaa", - True, NTLMSSP_NAME_TYPE_DOMAIN, domain, - True, NTLMSSP_NAME_TYPE_SERVER, hostname, - True, 0, ""); + NTLMSSP_NAME_TYPE_DOMAIN, domain, + NTLMSSP_NAME_TYPE_SERVER, hostname, + 0, ""); return names_blob; } diff --git a/source3/nsswitch/winbindd_cm.c b/source3/nsswitch/winbindd_cm.c index 4bf5a30459..ac9cd8f332 100644 --- a/source3/nsswitch/winbindd_cm.c +++ b/source3/nsswitch/winbindd_cm.c @@ -179,10 +179,9 @@ static NTSTATUS cm_open_connection(const char *domain, const int pipe_index, result = NT_STATUS_OK; - if (!cli_session_setup_spnego(new_conn->cli, machine_krb5_principal, + if (!NT_STATUS_IS_OK(result = cli_session_setup_spnego(new_conn->cli, machine_krb5_principal, machine_password, - domain)) { - result = cli_nt_error(new_conn->cli); + domain))) { DEBUG(4,("failed kerberos session setup with %s\n", nt_errstr(result))); if (NT_STATUS_IS_OK(result)) result = NT_STATUS_UNSUCCESSFUL; diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c index d43dca29c5..d849a6d5a9 100644 --- a/source3/nsswitch/winbindd_pam.c +++ b/source3/nsswitch/winbindd_pam.c @@ -225,9 +225,15 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) DATA_BLOB lm_resp, nt_resp; if (!state->privileged) { - DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access denied!\n")); + char *error_string = NULL; + DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access denied. !\n")); + DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions on %s are set correctly.\n", + get_winbind_priv_pipe_dir())); /* send a better message than ACCESS_DENIED */ - push_utf8_fstring(state->response.data.auth.error_string, "winbind client not authorized to use winbindd_pam_auth_crap"); + asprintf(&error_string, "winbind client not authorized to use winbindd_pam_auth_crap. Ensure permissions on %s are set correctly.", + get_winbind_priv_pipe_dir()); + push_utf8_fstring(state->response.data.auth.error_string, error_string); + SAFE_FREE(error_string); result = NT_STATUS_ACCESS_DENIED; goto done; } diff --git a/source3/rpc_client/cli_netlogon.c b/source3/rpc_client/cli_netlogon.c index 97bc4c65b7..70ac460303 100644 --- a/source3/rpc_client/cli_netlogon.c +++ b/source3/rpc_client/cli_netlogon.c @@ -654,11 +654,17 @@ NTSTATUS cli_netlogon_sam_network_logon(struct cli_state *cli, TALLOC_CTX *mem_c ZERO_STRUCT(netlogon_sess_key); memcpy(netlogon_sess_key, cli->sess_key, 8); - if (memcmp(zeros, info3->user_sess_key, 16) != 0) + if (memcmp(zeros, info3->user_sess_key, 16) != 0) { SamOEMhash(info3->user_sess_key, netlogon_sess_key, 16); - - if (memcmp(zeros, info3->padding, 16) != 0) + } else { + memset(info3->user_sess_key, '\0', 16); + } + + if (memcmp(zeros, info3->padding, 16) != 0) { SamOEMhash(info3->padding, netlogon_sess_key, 16); + } else { + memset(info3->padding, '\0', 16); + } /* Return results */ diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c index fdd9d3c3b1..aca0494dbd 100644 --- a/source3/rpc_client/cli_pipe.c +++ b/source3/rpc_client/cli_pipe.c @@ -264,13 +264,16 @@ static BOOL rpc_auth_pipe(struct cli_state *cli, prs_struct *rdata, later use */ DATA_BLOB ntlmssp_verf = data_blob(NULL, auth_len); - + BOOL store_ok; + /* save the reply away, for use a little later */ prs_copy_data_out((char *)ntlmssp_verf.data, &auth_verf, auth_len); + store_ok = (NT_STATUS_IS_OK(ntlmssp_store_response(cli->ntlmssp_pipe_state, + ntlmssp_verf))); - return (NT_STATUS_IS_OK(ntlmssp_client_store_response(cli->ntlmssp_pipe_state, - ntlmssp_verf))); + data_blob_free(&ntlmssp_verf); + return store_ok; } else if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) { /* nothing to do here - we don't seem to be able to @@ -307,12 +310,12 @@ static BOOL rpc_auth_pipe(struct cli_state *cli, prs_struct *rdata, DEBUG(1, ("Can't unseal - data_len < 0!!\n")); return False; } - nt_status = ntlmssp_client_unseal_packet(cli->ntlmssp_pipe_state, + nt_status = ntlmssp_unseal_packet(cli->ntlmssp_pipe_state, (unsigned char *)reply_data, data_len, &sig); } else if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) { - nt_status = ntlmssp_client_check_packet(cli->ntlmssp_pipe_state, + nt_status = ntlmssp_check_packet(cli->ntlmssp_pipe_state, (const unsigned char *)reply_data, data_len, &sig); } @@ -674,9 +677,9 @@ static NTSTATUS create_rpc_bind_req(struct cli_state *cli, prs_struct *rpc_out, DATA_BLOB request; DEBUG(5, ("Processing NTLMSSP Negotiate\n")); - nt_status = ntlmssp_client_update(cli->ntlmssp_pipe_state, - null_blob, - &request); + nt_status = ntlmssp_update(cli->ntlmssp_pipe_state, + null_blob, + &request); if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { @@ -777,9 +780,9 @@ static NTSTATUS create_rpc_bind_resp(struct cli_state *cli, /* The response is picked up from the internal cache, where it was placed by the rpc_auth_pipe() code */ - nt_status = ntlmssp_client_update(cli->ntlmssp_pipe_state, - ntlmssp_null_response, - &ntlmssp_reply); + nt_status = ntlmssp_update(cli->ntlmssp_pipe_state, + ntlmssp_null_response, + &ntlmssp_reply); if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { return nt_status; @@ -820,7 +823,7 @@ static NTSTATUS create_rpc_bind_resp(struct cli_state *cli, } if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) { - nt_status = ntlmssp_client_sign_init(cli->ntlmssp_pipe_state); + nt_status = ntlmssp_sign_init(cli->ntlmssp_pipe_state); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; @@ -994,7 +997,7 @@ BOOL rpc_api_pipe_req(struct cli_state *cli, uint8 op_num, */ if (cli->pipe_auth_flags & AUTH_PIPE_SEAL) { - nt_status = ntlmssp_client_seal_packet(cli->ntlmssp_pipe_state, + nt_status = ntlmssp_seal_packet(cli->ntlmssp_pipe_state, (unsigned char*)prs_data_p(&sec_blob), data_and_padding_size, &sign_blob); @@ -1005,7 +1008,7 @@ BOOL rpc_api_pipe_req(struct cli_state *cli, uint8 op_num, } else if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) { - nt_status = ntlmssp_client_sign_packet(cli->ntlmssp_pipe_state, + nt_status = ntlmssp_sign_packet(cli->ntlmssp_pipe_state, (unsigned char*)prs_data_p(&sec_blob), data_and_padding_size, &sign_blob); if (!NT_STATUS_IS_OK(nt_status)) { diff --git a/source3/rpc_client/cli_samr.c b/source3/rpc_client/cli_samr.c index e5e67f39dc..9bab816b81 100644 --- a/source3/rpc_client/cli_samr.c +++ b/source3/rpc_client/cli_samr.c @@ -1341,7 +1341,7 @@ NTSTATUS cli_samr_create_dom_user(struct cli_state *cli, TALLOC_CTX *mem_ctx, NTSTATUS cli_samr_set_userinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx, POLICY_HND *user_pol, uint16 switch_value, - uchar sess_key[16], SAM_USERINFO_CTR *ctr) + DATA_BLOB sess_key, SAM_USERINFO_CTR *ctr) { prs_struct qbuf, rbuf; SAMR_Q_SET_USERINFO q; @@ -1353,6 +1353,11 @@ NTSTATUS cli_samr_set_userinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx, ZERO_STRUCT(q); ZERO_STRUCT(r); + if (sess_key.length != 16) { + DEBUG(1, ("Cannot handle user session key of length [%u]\n", sess_key.length)); + return NT_STATUS_NO_USER_SESSION_KEY; + } + /* Initialise parse structures */ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); @@ -1393,7 +1398,7 @@ NTSTATUS cli_samr_set_userinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx, NTSTATUS cli_samr_set_userinfo2(struct cli_state *cli, TALLOC_CTX *mem_ctx, POLICY_HND *user_pol, uint16 switch_value, - uchar sess_key[16], SAM_USERINFO_CTR *ctr) + DATA_BLOB sess_key, SAM_USERINFO_CTR *ctr) { prs_struct qbuf, rbuf; SAMR_Q_SET_USERINFO2 q; @@ -1402,6 +1407,11 @@ NTSTATUS cli_samr_set_userinfo2(struct cli_state *cli, TALLOC_CTX *mem_ctx, DEBUG(10,("cli_samr_set_userinfo2\n")); + if (sess_key.length != 16) { + DEBUG(1, ("Cannot handle user session key of length [%u]\n", sess_key.length)); + return NT_STATUS_NO_USER_SESSION_KEY; + } + ZERO_STRUCT(q); ZERO_STRUCT(r); diff --git a/source3/rpc_parse/parse_net.c b/source3/rpc_parse/parse_net.c index 3b096e088a..ad0a91e7ea 100644 --- a/source3/rpc_parse/parse_net.c +++ b/source3/rpc_parse/parse_net.c @@ -1265,7 +1265,8 @@ void init_net_user_info3(TALLOC_CTX *ctx, NET_USER_INFO_3 *usr, uint16 logon_count, uint16 bad_pw_count, uint32 num_groups, const DOM_GID *gids, - uint32 user_flgs, uchar sess_key[16], + uint32 user_flgs, uchar nt_session_key[16], + uchar lm_session_key[16], const char *logon_srv, const char *logon_dom, const DOM_SID *dom_sid, const char *other_sids) { @@ -1307,8 +1308,8 @@ void init_net_user_info3(TALLOC_CTX *ctx, NET_USER_INFO_3 *usr, usr->buffer_groups = 1; /* indicates fill in groups, below, even if there are none */ usr->user_flgs = user_flgs; - if (sess_key != NULL) - memcpy(usr->user_sess_key, sess_key, sizeof(usr->user_sess_key)); + if (nt_session_key != NULL) + memcpy(usr->user_sess_key, nt_session_key, sizeof(usr->user_sess_key)); else memset((char *)usr->user_sess_key, '\0', sizeof(usr->user_sess_key)); @@ -1316,6 +1317,10 @@ void init_net_user_info3(TALLOC_CTX *ctx, NET_USER_INFO_3 *usr, memset((char *)usr->padding, '\0', sizeof(usr->padding)); + if (lm_session_key != NULL) + memcpy(usr->padding, lm_session_key, sizeof(usr->user_sess_key)); + + num_other_sids = init_dom_sid2s(ctx, other_sids, &usr->other_sids); usr->num_other_sids = num_other_sids; diff --git a/source3/rpc_parse/parse_samr.c b/source3/rpc_parse/parse_samr.c index 20adf0c756..1966538362 100644 --- a/source3/rpc_parse/parse_samr.c +++ b/source3/rpc_parse/parse_samr.c @@ -6302,7 +6302,7 @@ NTSTATUS make_samr_userinfo_ctr_usr21(TALLOC_CTX *ctx, SAM_USERINFO_CTR * ctr, inits a SAM_USERINFO_CTR structure. ********************************************************************/ -void init_samr_userinfo_ctr(SAM_USERINFO_CTR * ctr, uchar * sess_key, +void init_samr_userinfo_ctr(SAM_USERINFO_CTR * ctr, DATA_BLOB sess_key, uint16 switch_value, void *info) { DEBUG(5, ("init_samr_userinfo_ctr\n")); @@ -6312,13 +6312,13 @@ void init_samr_userinfo_ctr(SAM_USERINFO_CTR * ctr, uchar * sess_key, switch (switch_value) { case 0x18: - SamOEMhash(ctr->info.id24->pass, sess_key, 516); - dump_data(100, (char *)sess_key, 16); + SamOEMhash(ctr->info.id24->pass, sess_key.data, 516); + dump_data(100, (char *)sess_key.data, 16); dump_data(100, (char *)ctr->info.id24->pass, 516); break; case 0x17: - SamOEMhash(ctr->info.id23->pass, sess_key, 516); - dump_data(100, (char *)sess_key, 16); + SamOEMhash(ctr->info.id23->pass, sess_key.data, 516); + dump_data(100, (char *)sess_key.data, 16); dump_data(100, (char *)ctr->info.id23->pass, 516); break; default: @@ -6503,7 +6503,7 @@ inits a SAMR_Q_SET_USERINFO structure. ********************************************************************/ void init_samr_q_set_userinfo(SAMR_Q_SET_USERINFO * q_u, - POLICY_HND *hnd, unsigned char sess_key[16], + POLICY_HND *hnd, DATA_BLOB sess_key, uint16 switch_value, void *info) { DEBUG(5, ("init_samr_q_set_userinfo\n")); @@ -6577,7 +6577,7 @@ inits a SAMR_Q_SET_USERINFO2 structure. ********************************************************************/ void init_samr_q_set_userinfo2(SAMR_Q_SET_USERINFO2 * q_u, - POLICY_HND *hnd, unsigned char sess_key[16], + POLICY_HND *hnd, DATA_BLOB sess_key, uint16 switch_value, SAM_USERINFO_CTR * ctr) { DEBUG(5, ("init_samr_q_set_userinfo2\n")); @@ -6591,9 +6591,9 @@ void init_samr_q_set_userinfo2(SAMR_Q_SET_USERINFO2 * q_u, switch (switch_value) { case 0x12: - SamOEMhash(ctr->info.id12->lm_pwd, sess_key, 16); - SamOEMhash(ctr->info.id12->nt_pwd, sess_key, 16); - dump_data(100, (char *)sess_key, 16); + SamOEMhash(ctr->info.id12->lm_pwd, sess_key.data, 16); + SamOEMhash(ctr->info.id12->nt_pwd, sess_key.data, 16); + dump_data(100, (char *)sess_key.data, 16); dump_data(100, (char *)ctr->info.id12->lm_pwd, 16); dump_data(100, (char *)ctr->info.id12->nt_pwd, 16); break; diff --git a/source3/rpc_server/srv_netlog_nt.c b/source3/rpc_server/srv_netlog_nt.c index e6d005b157..e8bc6345de 100644 --- a/source3/rpc_server/srv_netlog_nt.c +++ b/source3/rpc_server/srv_netlog_nt.c @@ -683,7 +683,8 @@ NTSTATUS _net_sam_logon(pipes_struct *p, NET_Q_SAM_LOGON *q_u, NET_R_SAM_LOGON * pstring my_name; fstring user_sid_string; fstring group_sid_string; - uchar user_sess_key[16]; + uchar nt_session_key[16]; + uchar lm_session_key[16]; uchar netlogon_sess_key[16]; sampw = server_info->sam_account; @@ -718,10 +719,18 @@ NTSTATUS _net_sam_logon(pipes_struct *p, NET_Q_SAM_LOGON *q_u, NET_R_SAM_LOGON * ZERO_STRUCT(netlogon_sess_key); memcpy(netlogon_sess_key, p->dc.sess_key, 8); - memcpy(user_sess_key, server_info->session_key, sizeof(user_sess_key)); - SamOEMhash(user_sess_key, netlogon_sess_key, 16); + if (server_info->nt_session_key.length) { + memcpy(nt_session_key, server_info->nt_session_key.data, + MIN(sizeof(nt_session_key), server_info->nt_session_key.length)); + SamOEMhash(nt_session_key, netlogon_sess_key, 16); + } + if (server_info->lm_session_key.length) { + memcpy(lm_session_key, server_info->lm_session_key.data, + MIN(sizeof(lm_session_key), server_info->lm_session_key.length)); + SamOEMhash(lm_session_key, netlogon_sess_key, 16); + } ZERO_STRUCT(netlogon_sess_key); - + init_net_user_info3(p->mem_ctx, usr_info, user_rid, group_rid, @@ -743,14 +752,16 @@ NTSTATUS _net_sam_logon(pipes_struct *p, NET_Q_SAM_LOGON *q_u, NET_R_SAM_LOGON * num_gids, /* uint32 num_groups */ gids , /* DOM_GID *gids */ 0x20 , /* uint32 user_flgs (?) */ - user_sess_key, + server_info->nt_session_key.length ? nt_session_key : NULL, + server_info->lm_session_key.length ? lm_session_key : NULL, my_name , /* char *logon_srv */ pdb_get_domain(sampw), &domain_sid, /* DOM_SID *dom_sid */ /* Should be users domain sid, not servers - for trusted domains */ NULL); /* char *other_sids */ - ZERO_STRUCT(user_sess_key); + ZERO_STRUCT(nt_session_key); + ZERO_STRUCT(lm_session_key); } free_server_info(&server_info); return status; diff --git a/source3/rpc_server/srv_pipe.c b/source3/rpc_server/srv_pipe.c index 96261c665f..fa24efe589 100644 --- a/source3/rpc_server/srv_pipe.c +++ b/source3/rpc_server/srv_pipe.c @@ -420,9 +420,15 @@ failed authentication on named pipe %s.\n", domain, user_name, wks, p->name )); * Set up the sign/seal data. */ - { + if (server_info->lm_session_key.length != 16) { + DEBUG(1,("api_pipe_ntlmssp_verify: User [%s]\\[%s] from machine %s \ +succeeded authentication on named pipe %s, but session key was of incorrect length [%u].\n", + domain, user_name, wks, p->name, server_info->lm_session_key.length)); + free_server_info(&server_info); + return False; + } else { uchar p24[24]; - NTLMSSPOWFencrypt(server_info->first_8_lm_hash, lm_owf, p24); + NTLMSSPOWFencrypt(server_info->lm_session_key.data, lm_owf, p24); { unsigned char j = 0; int ind; @@ -468,7 +474,7 @@ failed authentication on named pipe %s.\n", domain, user_name, wks, p->name )); * Store the UNIX credential data (uid/gid pair) in the pipe structure. */ - memcpy(p->session_key, server_info->session_key, sizeof(p->session_key)); + p->session_key = data_blob(server_info->lm_session_key.data, server_info->lm_session_key.length); p->pipe_user.uid = server_info->uid; p->pipe_user.gid = server_info->gid; diff --git a/source3/rpc_server/srv_pipe_hnd.c b/source3/rpc_server/srv_pipe_hnd.c index 57e45d477f..a9fd9ec652 100644 --- a/source3/rpc_server/srv_pipe_hnd.c +++ b/source3/rpc_server/srv_pipe_hnd.c @@ -342,7 +342,7 @@ static void *make_internal_rpc_pipe_p(char *pipe_name, /* Store the session key and NT_TOKEN */ if (vuser) { - memcpy(p->session_key, vuser->session_key, sizeof(p->session_key)); + p->session_key = data_blob(vuser->session_key.data, vuser->session_key.length); p->pipe_user.nt_user_token = dup_nt_token(vuser->nt_user_token); } diff --git a/source3/rpc_server/srv_samr_nt.c b/source3/rpc_server/srv_samr_nt.c index 1cfa8b2853..1debf90d23 100644 --- a/source3/rpc_server/srv_samr_nt.c +++ b/source3/rpc_server/srv_samr_nt.c @@ -2953,7 +2953,13 @@ NTSTATUS _samr_set_userinfo(pipes_struct *p, SAMR_Q_SET_USERINFO *q_u, SAMR_R_SE break; case 24: - SamOEMhash(ctr->info.id24->pass, p->session_key, 516); + if (p->session_key.length != 16) { + /* we may have no session key at all, + and we don't know how to do the SamOEMhash + for length != 16 */ + return NT_STATUS_NO_USER_SESSION_KEY; + } + SamOEMhash(ctr->info.id24->pass, p->session_key.data, 516); dump_data(100, (char *)ctr->info.id24->pass, 516); @@ -2971,7 +2977,10 @@ NTSTATUS _samr_set_userinfo(pipes_struct *p, SAMR_Q_SET_USERINFO *q_u, SAMR_R_SE * info level and W2K SP2 drops down to level 23... JRA. */ - SamOEMhash(ctr->info.id25->pass, p->session_key, 532); + if (p->session_key.length != 16) { + return NT_STATUS_NO_USER_SESSION_KEY; + } + SamOEMhash(ctr->info.id25->pass, p->session_key.data, 532); dump_data(100, (char *)ctr->info.id25->pass, 532); @@ -2982,7 +2991,10 @@ NTSTATUS _samr_set_userinfo(pipes_struct *p, SAMR_Q_SET_USERINFO *q_u, SAMR_R_SE return NT_STATUS_INVALID_INFO_CLASS; case 23: - SamOEMhash(ctr->info.id23->pass, p->session_key, 516); + if (p->session_key.length != 16) { + return NT_STATUS_NO_USER_SESSION_KEY; + } + SamOEMhash(ctr->info.id23->pass, p->session_key.data, 516); dump_data(100, (char *)ctr->info.id23->pass, 516); diff --git a/source3/smbd/password.c b/source3/smbd/password.c index 958ed663e6..494d9ecd43 100644 --- a/source3/smbd/password.c +++ b/source3/smbd/password.c @@ -77,6 +77,8 @@ void invalidate_vuid(uint16 vuid) free_server_info(&vuser->server_info); + data_blob_free(&vuser->session_key); + DLIST_REMOVE(validated_users, vuser); /* clear the vuid from the 'cache' on each connection, and @@ -109,25 +111,36 @@ void invalidate_all_vuids(void) * @param server_info The token returned from the authentication process. * (now 'owned' by register_vuid) * + * @param session_key The User session key for the login session (now also 'owned' by register_vuid) + * + * @param respose_blob The NT challenge-response, if available. (May be freed after this call) + * + * @param smb_name The untranslated name of the user + * * @return Newly allocated vuid, biased by an offset. (This allows us to * tell random client vuid's (normally zero) from valid vuids.) * */ -int register_vuid(auth_serversupplied_info *server_info, DATA_BLOB response_blob, const char *smb_name) +int register_vuid(auth_serversupplied_info *server_info, DATA_BLOB session_key, DATA_BLOB response_blob, const char *smb_name) { user_struct *vuser = NULL; /* Ensure no vuid gets registered in share level security. */ - if(lp_security() == SEC_SHARE) + if(lp_security() == SEC_SHARE) { + data_blob_free(&session_key); return UID_FIELD_INVALID; + } /* Limit allowed vuids to 16bits - VUID_OFFSET. */ - if (num_validated_vuids >= 0xFFFF-VUID_OFFSET) + if (num_validated_vuids >= 0xFFFF-VUID_OFFSET) { + data_blob_free(&session_key); return UID_FIELD_INVALID; + } if((vuser = (user_struct *)malloc( sizeof(user_struct) )) == NULL) { DEBUG(0,("Failed to malloc users struct!\n")); + data_blob_free(&session_key); return UID_FIELD_INVALID; } @@ -156,6 +169,7 @@ int register_vuid(auth_serversupplied_info *server_info, DATA_BLOB response_blob if (vuser->n_groups) { if (!(vuser->groups = memdup(server_info->groups, sizeof(gid_t) * vuser->n_groups))) { DEBUG(0,("register_vuid: failed to memdup vuser->groups\n")); + data_blob_free(&session_key); free(vuser); free_server_info(&server_info); return UID_FIELD_INVALID; @@ -197,7 +211,7 @@ int register_vuid(auth_serversupplied_info *server_info, DATA_BLOB response_blob } } - memcpy(vuser->session_key, server_info->session_key, sizeof(vuser->session_key)); + vuser->session_key = session_key; DEBUG(10,("register_vuid: (%u,%u) %s %s %s guest=%d\n", (unsigned int)vuser->uid, @@ -211,6 +225,7 @@ int register_vuid(auth_serversupplied_info *server_info, DATA_BLOB response_blob } else { DEBUG(1, ("server_info does not contain a user_token - cannot continue\n")); free_server_info(&server_info); + data_blob_free(&session_key); SAFE_FREE(vuser->homedir); SAFE_FREE(vuser->unix_homedir); SAFE_FREE(vuser->logon_script); diff --git a/source3/smbd/sesssetup.c b/source3/smbd/sesssetup.c index 314ffbb4a9..64c25db2ab 100644 --- a/source3/smbd/sesssetup.c +++ b/source3/smbd/sesssetup.c @@ -149,7 +149,7 @@ static int reply_spnego_kerberos(connection_struct *conn, DATA_BLOB auth_data; DATA_BLOB ap_rep, ap_rep_wrapped, response; auth_serversupplied_info *server_info = NULL; - uint8 session_key[16]; + DATA_BLOB session_key; uint8 tok_id[2]; BOOL foreign = False; DATA_BLOB nullblob = data_blob(NULL, 0); @@ -164,7 +164,7 @@ static int reply_spnego_kerberos(connection_struct *conn, return ERROR_NT(NT_STATUS_LOGON_FAILURE); } - ret = ads_verify_ticket(lp_realm(), &ticket, &client, &auth_data, &ap_rep, session_key); + ret = ads_verify_ticket(lp_realm(), &ticket, &client, &auth_data, &ap_rep, &session_key); data_blob_free(&ticket); @@ -223,11 +223,8 @@ static int reply_spnego_kerberos(connection_struct *conn, return ERROR_NT(ret); } - /* Copy out the session key from the AP_REQ. */ - memcpy(server_info->session_key, session_key, sizeof(session_key)); - /* register_vuid keeps the server info */ - sess_vuid = register_vuid(server_info, nullblob, user); + sess_vuid = register_vuid(server_info, session_key, nullblob, user); free(user); @@ -297,9 +294,10 @@ static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *inbuf, char *out if (NT_STATUS_IS_OK(nt_status)) { int sess_vuid; DATA_BLOB nullblob = data_blob(NULL, 0); + DATA_BLOB session_key = data_blob((*auth_ntlmssp_state)->ntlmssp_state->session_key.data, (*auth_ntlmssp_state)->ntlmssp_state->session_key.length); /* register_vuid keeps the server info */ - sess_vuid = register_vuid(server_info, nullblob, (*auth_ntlmssp_state)->ntlmssp_state->user); + sess_vuid = register_vuid(server_info, session_key, nullblob, (*auth_ntlmssp_state)->ntlmssp_state->user); (*auth_ntlmssp_state)->server_info = NULL; if (sess_vuid == -1) { @@ -566,6 +564,8 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, NTSTATUS nt_status; BOOL doencrypt = global_encrypted_passwords_negotiated; + + DATA_BLOB session_key; START_PROFILE(SMBsesssetupX); @@ -766,18 +766,28 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, free_user_info(&user_info); - data_blob_free(&lm_resp); - data_blob_clear_free(&plaintext_password); - if (!NT_STATUS_IS_OK(nt_status)) { nt_status = do_map_to_guest(nt_status, &server_info, user, domain); } if (!NT_STATUS_IS_OK(nt_status)) { data_blob_free(&nt_resp); + data_blob_free(&lm_resp); + data_blob_clear_free(&plaintext_password); return ERROR_NT(nt_status_squash(nt_status)); } + if (server_info->nt_session_key.data) { + session_key = data_blob(server_info->nt_session_key.data, server_info->nt_session_key.length); + } else if (server_info->lm_session_key.length >= 8 && lm_resp.length == 24) { + session_key = data_blob(NULL, 16); + SMBsesskeygen_lmv1(server_info->lm_session_key.data, lm_resp.data, + session_key.data); + } + + data_blob_free(&lm_resp); + data_blob_clear_free(&plaintext_password); + /* it's ok - setup a reply */ set_message(outbuf,3,0,True); if (Protocol >= PROTOCOL_NT1) { @@ -795,7 +805,7 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, to a uid can get through without a password, on the same VC */ /* register_vuid keeps the server info */ - sess_vuid = register_vuid(server_info, nt_resp, sub_user); + sess_vuid = register_vuid(server_info, session_key, nt_resp, sub_user); data_blob_free(&nt_resp); if (sess_vuid == -1) { diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c index 13b9550347..4461f163dd 100644 --- a/source3/utils/ntlm_auth.c +++ b/source3/utils/ntlm_auth.c @@ -29,14 +29,42 @@ #define SQUID_BUFFER_SIZE 2010 -enum squid_mode { +enum stdio_helper_mode { SQUID_2_4_BASIC, SQUID_2_5_BASIC, SQUID_2_5_NTLMSSP, GSS_SPNEGO, - GSS_SPNEGO_CLIENT + GSS_SPNEGO_CLIENT, + NUM_HELPER_MODES +}; + +typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static const struct { + enum stdio_helper_mode mode; + const char *name; + stdio_helper_function fn; +} stdio_helper_protocols[] = { + { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request}, + { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request}, + { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request}, + { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request}, + { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request}, + { NUM_HELPER_MODES, NULL, NULL} }; - extern int winbindd_fd; @@ -96,7 +124,7 @@ static const char *get_winbind_domain(void) if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) != NSS_STATUS_SUCCESS) { - d_printf("could not obtain winbind domain name!\n"); + DEBUG(0, ("could not obtain winbind domain name!\n")); return NULL; } @@ -122,7 +150,7 @@ static const char *get_winbind_netbios_name(void) if (winbindd_request(WINBINDD_NETBIOS_NAME, NULL, &response) != NSS_STATUS_SUCCESS) { - d_printf("could not obtain winbind netbios name!\n"); + DEBUG(0, ("could not obtain winbind netbios name!\n")); return NULL; } @@ -264,20 +292,42 @@ static NTSTATUS contact_winbind_auth_crap(const char *username, return nt_status; } -static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state) +static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key) { - return contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain, - ntlmssp_state->workstation, - &ntlmssp_state->chal, - &ntlmssp_state->lm_resp, - &ntlmssp_state->nt_resp, - 0, - NULL, - NULL, - NULL); + static const char zeros[16]; + NTSTATUS nt_status; + char *error_string; + uint8 lm_key[8]; + uint8 nt_key[16]; + + nt_status = contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain, + ntlmssp_state->workstation, + &ntlmssp_state->chal, + &ntlmssp_state->lm_resp, + &ntlmssp_state->nt_resp, + WBFLAG_PAM_LMKEY | WBFLAG_PAM_NTKEY, + lm_key, nt_key, + &error_string); + + if (NT_STATUS_IS_OK(nt_status)) { + if (memcmp(lm_key, zeros, 8) != 0) { + *lm_session_key = data_blob(NULL, 16); + memcpy(lm_session_key->data, lm_key, 8); + memset(lm_session_key->data+8, '\0', 8); + } + + if (memcmp(nt_key, zeros, 16) != 0) { + *nt_session_key = data_blob(nt_key, 16); + } + } else { + DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3, + ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", + ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation, error_string ? error_string : "unknown error (NULL)")); + } + return nt_status; } -static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, +static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, char *buf, int length) { static NTLMSSP_STATE *ntlmssp_state = NULL; @@ -295,7 +345,7 @@ static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, } else if (strcmp(buf, "YR") == 0) { request = data_blob(NULL, 0); if (ntlmssp_state) - ntlmssp_server_end(&ntlmssp_state); + ntlmssp_end(&ntlmssp_state); } else { DEBUG(1, ("NTLMSSP query [%s] invalid", buf)); x_fprintf(x_stdout, "BH\n"); @@ -312,7 +362,7 @@ static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, DEBUG(10, ("got NTLMSSP packet:\n")); dump_data(10, (const char *)request.data, request.length); - nt_status = ntlmssp_server_update(ntlmssp_state, request, &reply); + nt_status = ntlmssp_update(ntlmssp_state, request, &reply); if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { char *reply_base64 = base64_encode_data_blob(reply); @@ -320,6 +370,9 @@ static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, SAFE_FREE(reply_base64); data_blob_free(&reply); DEBUG(10, ("NTLMSSP challenge\n")); + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) { + x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status)); + DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status))); } else if (!NT_STATUS_IS_OK(nt_status)) { x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status)); DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status))); @@ -331,7 +384,7 @@ static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, data_blob_free(&request); } -static void manage_squid_basic_request(enum squid_mode squid_mode, +static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode, char *buf, int length) { char *user, *pass; @@ -346,7 +399,7 @@ static void manage_squid_basic_request(enum squid_mode squid_mode, *pass='\0'; pass++; - if (squid_mode == SQUID_2_5_BASIC) { + if (stdio_helper_mode == SQUID_2_5_BASIC) { rfc1738_unescape(user); rfc1738_unescape(pass); } @@ -409,7 +462,7 @@ static void offer_gss_spnego_mechs(void) { return; } -static void manage_gss_spnego_request(enum squid_mode squid_mode, +static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode, char *buf, int length) { static NTLMSSP_STATE *ntlmssp_state = NULL; @@ -470,9 +523,7 @@ static void manage_gss_spnego_request(enum squid_mode squid_mode, if (request.type == SPNEGO_NEG_TOKEN_INIT) { /* Second request from Client. This is where the - client offers its mechanism to use. We currently - only support NTLMSSP, the decision for Kerberos - would be taken here. */ + client offers its mechanism to use. */ if ( (request.negTokenInit.mechTypes == NULL) || (request.negTokenInit.mechTypes[0] == NULL) ) { @@ -493,7 +544,7 @@ static void manage_gss_spnego_request(enum squid_mode squid_mode, DEBUG(1, ("Client wants a new NTLMSSP challenge, but " "already got one\n")); x_fprintf(x_stdout, "BH\n"); - ntlmssp_server_end(&ntlmssp_state); + ntlmssp_end(&ntlmssp_state); return; } @@ -510,7 +561,7 @@ static void manage_gss_spnego_request(enum squid_mode squid_mode, response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP); response.negTokenTarg.mechListMIC = data_blob(NULL, 0); - status = ntlmssp_server_update(ntlmssp_state, + status = ntlmssp_update(ntlmssp_state, request.negTokenInit.mechToken, &response.negTokenTarg.responseToken); } @@ -521,7 +572,7 @@ static void manage_gss_spnego_request(enum squid_mode squid_mode, char *principal; DATA_BLOB auth_data; DATA_BLOB ap_rep; - uint8 session_key[16]; + DATA_BLOB session_key; if ( request.negTokenInit.mechToken.data == NULL ) { DEBUG(1, ("Client did not provide Kerberos data\n")); @@ -537,7 +588,7 @@ static void manage_gss_spnego_request(enum squid_mode squid_mode, status = ads_verify_ticket(lp_realm(), &request.negTokenInit.mechToken, &principal, &auth_data, &ap_rep, - session_key); + &session_key); /* Now in "principal" we have the name we are authenticated as. */ @@ -583,7 +634,7 @@ static void manage_gss_spnego_request(enum squid_mode squid_mode, return; } - status = ntlmssp_server_update(ntlmssp_state, + status = ntlmssp_update(ntlmssp_state, request.negTokenTarg.responseToken, &response.negTokenTarg.responseToken); @@ -594,7 +645,7 @@ static void manage_gss_spnego_request(enum squid_mode squid_mode, if (NT_STATUS_IS_OK(status)) { user = strdup(ntlmssp_state->user); domain = strdup(ntlmssp_state->domain); - ntlmssp_server_end(&ntlmssp_state); + ntlmssp_end(&ntlmssp_state); } } @@ -638,7 +689,7 @@ static void manage_gss_spnego_request(enum squid_mode squid_mode, return; } -static NTLMSSP_CLIENT_STATE *client_ntlmssp_state = NULL; +static NTLMSSP_STATE *client_ntlmssp_state = NULL; static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego) { @@ -677,7 +728,7 @@ static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego) if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Could not start NTLMSSP client: %s\n", nt_errstr(status))); - ntlmssp_client_end(&client_ntlmssp_state); + ntlmssp_end(&client_ntlmssp_state); return False; } @@ -686,7 +737,7 @@ static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego) if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Could not set username: %s\n", nt_errstr(status))); - ntlmssp_client_end(&client_ntlmssp_state); + ntlmssp_end(&client_ntlmssp_state); return False; } @@ -695,7 +746,7 @@ static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego) if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Could not set domain: %s\n", nt_errstr(status))); - ntlmssp_client_end(&client_ntlmssp_state); + ntlmssp_end(&client_ntlmssp_state); return False; } @@ -704,7 +755,7 @@ static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego) if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Could not set password: %s\n", nt_errstr(status))); - ntlmssp_client_end(&client_ntlmssp_state); + ntlmssp_end(&client_ntlmssp_state); return False; } @@ -713,13 +764,13 @@ static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego) spnego.negTokenInit.reqFlags = 0; spnego.negTokenInit.mechListMIC = null_blob; - status = ntlmssp_client_update(client_ntlmssp_state, null_blob, + status = ntlmssp_update(client_ntlmssp_state, null_blob, &spnego.negTokenInit.mechToken); if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED, got: %s\n", nt_errstr(status))); - ntlmssp_client_end(&client_ntlmssp_state); + ntlmssp_end(&client_ntlmssp_state); return False; } @@ -746,23 +797,23 @@ static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego) if (client_ntlmssp_state == NULL) { DEBUG(1, ("Got NTLMSSP tArg without a client state\n")); x_fprintf(x_stdout, "BH\n"); - ntlmssp_client_end(&client_ntlmssp_state); + ntlmssp_end(&client_ntlmssp_state); return; } if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) { x_fprintf(x_stdout, "NA\n"); - ntlmssp_client_end(&client_ntlmssp_state); + ntlmssp_end(&client_ntlmssp_state); return; } if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) { x_fprintf(x_stdout, "AF\n"); - ntlmssp_client_end(&client_ntlmssp_state); + ntlmssp_end(&client_ntlmssp_state); return; } - status = ntlmssp_client_update(client_ntlmssp_state, + status = ntlmssp_update(client_ntlmssp_state, spnego.negTokenTarg.responseToken, &request); @@ -772,7 +823,7 @@ static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego) nt_errstr(status))); x_fprintf(x_stdout, "BH\n"); data_blob_free(&request); - ntlmssp_client_end(&client_ntlmssp_state); + ntlmssp_end(&client_ntlmssp_state); return; } @@ -798,7 +849,7 @@ static BOOL manage_client_krb5_init(SPNEGO_DATA spnego) { char *principal; DATA_BLOB tkt, to_server; - unsigned char session_key_krb5[16]; + DATA_BLOB session_key_krb5; SPNEGO_DATA reply; char *reply_base64; @@ -822,7 +873,7 @@ static BOOL manage_client_krb5_init(SPNEGO_DATA spnego) spnego.negTokenInit.mechListMIC.length); principal[spnego.negTokenInit.mechListMIC.length] = '\0'; - tkt = cli_krb5_get_ticket(principal, 0, session_key_krb5); + tkt = cli_krb5_get_ticket(principal, 0, &session_key_krb5); if (tkt.data == NULL) { @@ -845,9 +896,11 @@ static BOOL manage_client_krb5_init(SPNEGO_DATA spnego) return True; } - tkt = cli_krb5_get_ticket(principal, 0, session_key_krb5); + tkt = cli_krb5_get_ticket(principal, 0, &session_key_krb5); } + data_blob_free(&session_key_krb5); + ZERO_STRUCT(reply); reply.type = SPNEGO_NEG_TOKEN_INIT; @@ -896,7 +949,7 @@ static void manage_client_krb5_targ(SPNEGO_DATA spnego) #endif -static void manage_gss_spnego_client_request(enum squid_mode squid_mode, +static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode, char *buf, int length) { DATA_BLOB request; @@ -1000,7 +1053,7 @@ static void manage_gss_spnego_client_request(enum squid_mode squid_mode, x_fprintf(x_stdout, "BH\n"); } - ntlmssp_client_end(&client_ntlmssp_state); + ntlmssp_end(&client_ntlmssp_state); goto out; } @@ -1029,7 +1082,7 @@ static void manage_gss_spnego_client_request(enum squid_mode squid_mode, return; } -static void manage_squid_request(enum squid_mode squid_mode) +static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn) { char buf[SQUID_BUFFER_SIZE+1]; int length; @@ -1066,24 +1119,16 @@ static void manage_squid_request(enum squid_mode squid_mode) return; } - if (squid_mode == SQUID_2_5_BASIC || squid_mode == SQUID_2_4_BASIC) { - manage_squid_basic_request(squid_mode, buf, length); - } else if (squid_mode == SQUID_2_5_NTLMSSP) { - manage_squid_ntlmssp_request(squid_mode, buf, length); - } else if (squid_mode == GSS_SPNEGO) { - manage_gss_spnego_request(squid_mode, buf, length); - } else if (squid_mode == GSS_SPNEGO_CLIENT) { - manage_gss_spnego_client_request(squid_mode, buf, length); - } + fn(helper_mode, buf, length); } -static void squid_stream(enum squid_mode squid_mode) { +static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) { /* initialize FDescs */ x_setbuf(x_stdout, NULL); x_setbuf(x_stderr, NULL); while(1) { - manage_squid_request(squid_mode); + manage_squid_request(stdio_mode, fn); } } @@ -2019,20 +2064,15 @@ enum { } if (helper_protocol) { - if (strcmp(helper_protocol, "squid-2.5-ntlmssp")== 0) { - squid_stream(SQUID_2_5_NTLMSSP); - } else if (strcmp(helper_protocol, "squid-2.5-basic")== 0) { - squid_stream(SQUID_2_5_BASIC); - } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) { - squid_stream(SQUID_2_4_BASIC); - } else if (strcmp(helper_protocol, "gss-spnego")== 0) { - squid_stream(GSS_SPNEGO); - } else if (strcmp(helper_protocol, "gss-spnego-client") == 0) { - squid_stream(GSS_SPNEGO_CLIENT); - } else { - x_fprintf(x_stderr, "unknown helper protocol [%s]\n", helper_protocol); - exit(1); + int i; + for (i=0; i<NUM_HELPER_MODES; i++) { + if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) { + squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn); + exit(0); + } } + x_fprintf(x_stderr, "unknown helper protocol [%s]\n", helper_protocol); + exit(1); } if (!opt_username) { |