[CIFS] Support for setting up SMB sessions to legacy lanman servers part 2

This commit is contained in:
Steve French 2006-06-01 19:20:10 +00:00
parent 9c53588ec9
commit 7c7b25bc8e
9 changed files with 150 additions and 75 deletions

View File

@ -508,7 +508,7 @@ cifs_proc_init(void)
pde->write_proc = multiuser_mount_write;
pde =
create_proc_read_entry("ExtendedSecurity", 0, proc_fs_cifs,
create_proc_read_entry("SecurityFlags", 0, proc_fs_cifs,
extended_security_read, NULL);
if (pde)
pde->write_proc = extended_security_write;
@ -547,7 +547,7 @@ cifs_proc_clean(void)
remove_proc_entry("MultiuserMount", proc_fs_cifs);
remove_proc_entry("OplockEnabled", proc_fs_cifs);
/* remove_proc_entry("NTLMV2Enabled",proc_fs_cifs); */
remove_proc_entry("ExtendedSecurity",proc_fs_cifs);
remove_proc_entry("SecurityFlags",proc_fs_cifs);
/* remove_proc_entry("PacketSigningEnabled",proc_fs_cifs); */
remove_proc_entry("LinuxExtensionsEnabled",proc_fs_cifs);
remove_proc_entry("Experimental",proc_fs_cifs);

View File

@ -26,6 +26,7 @@
#include "md5.h"
#include "cifs_unicode.h"
#include "cifsproto.h"
#include <linux/ctype.h>
/* Calculate and return the CIFS signature based on the mac key and the smb pdu */
/* the 16 byte signature must be allocated by the caller */
@ -35,6 +36,8 @@
extern void mdfour(unsigned char *out, unsigned char *in, int n);
extern void E_md4hash(const unsigned char *passwd, unsigned char *p16);
extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
unsigned char *p24);
static int cifs_calculate_signature(const struct smb_hdr * cifs_pdu,
const char * key, char * signature)
@ -45,7 +48,7 @@ static int cifs_calculate_signature(const struct smb_hdr * cifs_pdu,
return -EINVAL;
MD5Init(&context);
MD5Update(&context,key,CIFS_SESSION_KEY_SIZE+16);
MD5Update(&context,key,CIFS_SESS_KEY_SIZE+16);
MD5Update(&context,cifs_pdu->Protocol,cifs_pdu->smb_buf_length);
MD5Final(signature,&context);
return 0;
@ -90,7 +93,7 @@ static int cifs_calc_signature2(const struct kvec * iov, int n_vec,
return -EINVAL;
MD5Init(&context);
MD5Update(&context,key,CIFS_SESSION_KEY_SIZE+16);
MD5Update(&context,key,CIFS_SESS_KEY_SIZE+16);
for(i=0;i<n_vec;i++) {
if(iov[i].iov_base == NULL) {
cERROR(1,("null iovec entry"));
@ -204,7 +207,7 @@ int cifs_calculate_mac_key(char * key, const char * rn, const char * password)
E_md4hash(password, temp_key);
mdfour(key,temp_key,16);
memcpy(key+16,rn, CIFS_SESSION_KEY_SIZE);
memcpy(key+16,rn, CIFS_SESS_KEY_SIZE);
return 0;
}
@ -261,6 +264,37 @@ int CalcNTLMv2_partial_mac_key(struct cifsSesInfo * ses, struct nls_table * nls_
kfree(unicode_buf);
return 0;
}
#ifdef CONFIG_CIFS_WEAK_PW_HASH
void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key)
{
int i;
char password_with_pad[CIFS_ENCPWD_SIZE];
memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
/* calculate old style session key */
/* calling toupper is less broken than repeatedly
calling nls_toupper would be since that will never
work for UTF8, but neither handles multibyte code pages
but the only alternative would be converting to UCS-16 (Unicode)
(using a routine something like UniStrupr) then
uppercasing and then converting back from Unicode - which
would only worth doing it if we knew it were utf8. Basically
utf8 and other multibyte codepages each need their own strupper
function since a byte at a time will ont work. */
for(i = 0; i < CIFS_ENCPWD_SIZE; i++) {
password_with_pad[i] = toupper(password_with_pad[i]);
}
SMBencrypt(password_with_pad, ses->server->cryptKey, lnm_session_key);
/* clear password before we return/free memory */
memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
}
#endif /* CIFS_WEAK_PW_HASH */
void CalcNTLMv2_response(const struct cifsSesInfo * ses,char * v2_session_response)
{
struct HMACMD5Context context;

View File

@ -158,7 +158,7 @@ struct TCP_Server_Info {
/* 16th byte of RFC1001 workstation name is always null */
char workstation_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
__u32 sequence_number; /* needed for CIFS PDU signature */
char mac_signing_key[CIFS_SESSION_KEY_SIZE + 16];
char mac_signing_key[CIFS_SESS_KEY_SIZE + 16];
};
/*

View File

@ -116,7 +116,8 @@
/*
* Size of the session key (crypto key encrypted with the password
*/
#define CIFS_SESSION_KEY_SIZE (24)
#define CIFS_SESS_KEY_SIZE (24)
#define V2_SESS_KEY_SIZE (86)
/*
* Maximum user name length

View File

@ -287,6 +287,9 @@ extern int cifs_verify_signature(struct smb_hdr *, const char * mac_key,
extern int cifs_calculate_mac_key(char * key,const char * rn,const char * pass);
extern int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *, struct nls_table *);
extern void CalcNTLMv2_response(const struct cifsSesInfo *,char * );
#ifdef CONFIG_CIFS_WEAK_PW_HASH
extern void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key);
#endif /* CIFS_WEAK_PW_HASH */
extern int CIFSSMBCopy(int xid,
struct cifsTconInfo *source_tcon,
const char *fromName,

View File

@ -438,12 +438,19 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
goto neg_err_exit;
} else if((pSMBr->hdr.WordCount == 13) &&
(pSMBr->DialectIndex == LANMAN_PROT)) {
#ifdef CONFIG_CIFS_WEAK_PW_HASH
struct lanman_neg_rsp * rsp =
(struct lanman_neg_rsp *)pSMBr;
/* BB Mark ses struct as negotiated lanman level BB */
server->secType = LANMAN;
if((extended_security & CIFSSEC_MAY_LANMAN) ||
(extended_security & CIFSSEC_MAY_PLNTXT))
server->secType = LANMAN;
else {
cERROR(1, ("mount failed weak security disabled"
" in /proc/fs/cifs/SecurityFlags"));
rc = -EOPNOTSUPP;
goto neg_err_exit;
}
server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
@ -469,6 +476,11 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
}
cFYI(1,("LANMAN negotiated")); /* BB removeme BB */
#else /* weak security disabled */
cERROR(1,("mount failed, cifs module not built with "
"CIFS_WEAK_PW_HASH support"));
rc = -EOPNOTSUPP;
#endif /* WEAK_PW_HASH */
goto neg_err_exit;
} else if(pSMBr->hdr.WordCount != 17) {
/* unknown wct */
@ -479,8 +491,13 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
server->secMode = pSMBr->SecurityMode;
if((server->secMode & SECMODE_USER) == 0)
cFYI(1,("share mode security"));
server->secType = NTLM; /* BB override default for
NTLMv2 or kerberos v5 */
if(extended_security & CIFSSEC_MUST_NTLMV2)
server->secType = NTLMv2;
else
server->secType = NTLM;
/* else krb5 ... */
/* one byte - no need to convert this or EncryptionKeyLen
from little endian */
server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);

View File

@ -1990,7 +1990,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
static int
CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
char session_key[CIFS_SESSION_KEY_SIZE],
char session_key[CIFS_SESS_KEY_SIZE],
const struct nls_table *nls_codepage)
{
struct smb_hdr *smb_buffer;
@ -2048,15 +2048,15 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
pSMB->req_no_secext.CaseInsensitivePasswordLength =
cpu_to_le16(CIFS_SESSION_KEY_SIZE);
cpu_to_le16(CIFS_SESS_KEY_SIZE);
pSMB->req_no_secext.CaseSensitivePasswordLength =
cpu_to_le16(CIFS_SESSION_KEY_SIZE);
cpu_to_le16(CIFS_SESS_KEY_SIZE);
bcc_ptr = pByteArea(smb_buffer);
memcpy(bcc_ptr, (char *) session_key, CIFS_SESSION_KEY_SIZE);
bcc_ptr += CIFS_SESSION_KEY_SIZE;
memcpy(bcc_ptr, (char *) session_key, CIFS_SESSION_KEY_SIZE);
bcc_ptr += CIFS_SESSION_KEY_SIZE;
memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE);
bcc_ptr += CIFS_SESS_KEY_SIZE;
memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE);
bcc_ptr += CIFS_SESS_KEY_SIZE;
if (ses->capabilities & CAP_UNICODE) {
if ((long) bcc_ptr % 2) { /* must be word aligned for Unicode */
@ -3004,14 +3004,14 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
SecurityBlob->LmChallengeResponse.Buffer = 0;
SecurityBlob->NtChallengeResponse.Length =
cpu_to_le16(CIFS_SESSION_KEY_SIZE);
cpu_to_le16(CIFS_SESS_KEY_SIZE);
SecurityBlob->NtChallengeResponse.MaximumLength =
cpu_to_le16(CIFS_SESSION_KEY_SIZE);
memcpy(bcc_ptr, ntlm_session_key, CIFS_SESSION_KEY_SIZE);
cpu_to_le16(CIFS_SESS_KEY_SIZE);
memcpy(bcc_ptr, ntlm_session_key, CIFS_SESS_KEY_SIZE);
SecurityBlob->NtChallengeResponse.Buffer =
cpu_to_le32(SecurityBlobLength);
SecurityBlobLength += CIFS_SESSION_KEY_SIZE;
bcc_ptr += CIFS_SESSION_KEY_SIZE;
SecurityBlobLength += CIFS_SESS_KEY_SIZE;
bcc_ptr += CIFS_SESS_KEY_SIZE;
if (ses->capabilities & CAP_UNICODE) {
if (domain == NULL) {
@ -3350,22 +3350,33 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
bcc_ptr = &pSMB->Password[0];
if((ses->server->secMode) & SECMODE_USER) {
pSMB->PasswordLength = cpu_to_le16(1); /* minimum */
*bcc_ptr = 0; /* password is null byte */
bcc_ptr++; /* skip password */
/* already aligned so no need to do it below */
} else {
pSMB->PasswordLength = cpu_to_le16(CIFS_SESSION_KEY_SIZE);
pSMB->PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE);
/* BB FIXME add code to fail this if NTLMv2 or Kerberos
specified as required (when that support is added to
the vfs in the future) as only NTLM or the much
weaker LANMAN (which we do not send) is accepted
weaker LANMAN (which we do not send by default) is accepted
by Samba (not sure whether other servers allow
NTLMv2 password here) */
#ifdef CONFIG_CIFS_WEAK_PW_HASH
if((extended_security & CIFSSEC_MAY_LANMAN) &&
(ses->server->secType == LANMAN))
calc_lanman_hash(ses, bcc_ptr);
else
#endif /* CIFS_WEAK_PW_HASH */
SMBNTencrypt(ses->password,
ses->server->cryptKey,
bcc_ptr);
bcc_ptr += CIFS_SESSION_KEY_SIZE;
*bcc_ptr = 0;
bcc_ptr++; /* align */
bcc_ptr += CIFS_SESS_KEY_SIZE;
if(ses->capabilities & CAP_UNICODE) {
/* must align unicode strings */
*bcc_ptr = 0; /* null byte password */
bcc_ptr++;
}
}
if(ses->server->secMode &
@ -3507,7 +3518,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
struct nls_table * nls_info)
{
int rc = 0;
char ntlm_session_key[CIFS_SESSION_KEY_SIZE];
char ntlm_session_key[CIFS_SESS_KEY_SIZE];
int ntlmv2_flag = FALSE;
int first_time = 0;

View File

@ -84,7 +84,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = {
static const struct smb_to_posix_error mapping_table_ERRSRV[] = {
{ERRerror, -EIO},
{ERRbadpw, -EPERM},
{ERRbadpw, -EACCES}, /* was EPERM */
{ERRbadtype, -EREMOTE},
{ERRaccess, -EACCES},
{ERRinvtid, -ENXIO},

View File

@ -28,12 +28,8 @@
#include "cifs_debug.h"
#include "ntlmssp.h"
#include "nterr.h"
#include <linux/ctype.h>
#include <linux/utsname.h>
extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
unsigned char *p24);
extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
unsigned char *p24);
@ -80,7 +76,7 @@ static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)
return capabilities;
}
void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
static void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
const struct nls_table * nls_cp)
{
char * bcc_ptr = *pbcc_area;
@ -130,7 +126,7 @@ void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
*pbcc_area = bcc_ptr;
}
void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
static void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
const struct nls_table * nls_cp)
{
char * bcc_ptr = *pbcc_area;
@ -173,7 +169,7 @@ void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
*pbcc_area = bcc_ptr;
}
int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
static int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
const struct nls_table * nls_cp)
{
int rc = 0;
@ -255,7 +251,7 @@ int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
return rc;
}
int decode_ascii_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
static int decode_ascii_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
const struct nls_table * nls_cp)
{
int rc = 0;
@ -317,7 +313,6 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
{
int rc = 0;
int wct;
int i;
struct smb_hdr *smb_buf;
char *bcc_ptr;
SESSION_SETUP_ANDX *pSMB;
@ -343,7 +338,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
return -EOPNOTSUPP;
#endif
wct = 10; /* lanman 2 style sessionsetup */
} else if(type == NTLM) /* NTLMv2 may retry NTLM */
} else if((type == NTLM) || (type == NTLMv2)) /* NTLMv2 may retry NTLM */
wct = 13; /* old style NTLM sessionsetup */
else /* same size for negotiate or auth, NTLMSSP or extended security */
wct = 12;
@ -360,41 +355,22 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
if(type == LANMAN) {
#ifdef CONFIG_CIFS_WEAK_PW_HASH
char lnm_session_key[CIFS_SESSION_KEY_SIZE];
char password_with_pad[CIFS_ENCPWD_SIZE];
char lnm_session_key[CIFS_SESS_KEY_SIZE];
/* no capabilities flags in old lanman negotiation */
pSMB->old_req.PasswordLength = CIFS_SESSION_KEY_SIZE;
pSMB->old_req.PasswordLength = CIFS_SESS_KEY_SIZE;
/* BB calculate hash with password */
/* and copy into bcc */
memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
/* calculate old style session key */
/* toupper may be less broken then repeatedly calling
nls_toupper would be, but neither handles multibyte code pages
but the only alternative would be converting to UCS-16 (Unicode)
uppercasing and converting back which is only worth doing if
we knew it were utf8. utf8 code page needs its own
toupper and tolower and strnicmp functions */
for(i = 0; i< CIFS_ENCPWD_SIZE; i++) {
password_with_pad[i] = toupper(password_with_pad[i]);
}
SMBencrypt(password_with_pad, ses->server->cryptKey,
lnm_session_key);
calc_lanman_hash(ses, lnm_session_key);
#ifdef CONFIG_CIFS_DEBUG2
cifs_dump_mem("cryptkey: ",ses->server->cryptKey,
CIFS_SESSION_KEY_SIZE);
CIFS_SESS_KEY_SIZE);
#endif
/* clear password before we return/free memory */
memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESSION_KEY_SIZE);
bcc_ptr += CIFS_SESSION_KEY_SIZE;
memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE);
bcc_ptr += CIFS_SESS_KEY_SIZE;
/* can not sign if LANMAN negotiated so no need
to calculate signing key? but what if server
@ -406,13 +382,13 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
#endif
} else if (type == NTLM) {
char ntlm_session_key[CIFS_SESSION_KEY_SIZE];
char ntlm_session_key[CIFS_SESS_KEY_SIZE];
pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
pSMB->req_no_secext.CaseInsensitivePasswordLength =
cpu_to_le16(CIFS_SESSION_KEY_SIZE);
cpu_to_le16(CIFS_SESS_KEY_SIZE);
pSMB->req_no_secext.CaseSensitivePasswordLength =
cpu_to_le16(CIFS_SESSION_KEY_SIZE);
cpu_to_le16(CIFS_SESS_KEY_SIZE);
/* calculate session key */
SMBNTencrypt(ses->password, ses->server->cryptKey,
@ -420,15 +396,48 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
if(first_time) /* should this be moved into common code
with similar ntlmv2 path? */
cifs_calculate_mac_key(
ses->server->mac_signing_key,
cifs_calculate_mac_key( ses->server->mac_signing_key,
ntlm_session_key, ses->password);
/* copy session key */
memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESSION_KEY_SIZE);
bcc_ptr += CIFS_SESSION_KEY_SIZE;
memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESSION_KEY_SIZE);
bcc_ptr += CIFS_SESSION_KEY_SIZE;
memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESS_KEY_SIZE);
bcc_ptr += CIFS_SESS_KEY_SIZE;
memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESS_KEY_SIZE);
bcc_ptr += CIFS_SESS_KEY_SIZE;
if(ses->capabilities & CAP_UNICODE)
unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
else
ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
} else if (type == NTLMv2) {
char * v2_sess_key = kmalloc(V2_SESS_KEY_SIZE, GFP_KERNEL);
if(v2_sess_key == NULL) {
cifs_small_buf_release(smb_buf);
return -ENOMEM;
}
pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
/* LM2 password would be here if we supported it */
pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
/* cpu_to_le16(LM2_SESS_KEY_SIZE); */
pSMB->req_no_secext.CaseSensitivePasswordLength =
cpu_to_le16(V2_SESS_KEY_SIZE);
/* calculate session key */
CalcNTLMv2_response(ses, v2_sess_key);
if(first_time) /* should this be moved into common code
with similar ntlmv2 path? */
/* cifs_calculate_ntlmv2_mac_key(ses->server->mac_signing_key,
response BB FIXME, v2_sess_key); */
/* copy session key */
/* memcpy(bcc_ptr, (char *)ntlm_session_key,LM2_SESS_KEY_SIZE);
bcc_ptr += LM2_SESS_KEY_SIZE; */
memcpy(bcc_ptr, (char *)v2_sess_key, V2_SESS_KEY_SIZE);
bcc_ptr += V2_SESS_KEY_SIZE;
if(ses->capabilities & CAP_UNICODE)
unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
else