X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=fs%2Fcifs%2Fsess.c;h=d2153abcba6d7983678e8e08df39eee7bf4083a3;hb=852e1f2a2627274102a3c2dc70a6547aeab99cb6;hp=2ea027dda215cc2053d6645ca8a306ed959220c6;hpb=e6f194d8f6f50da6837af637b2fd839c34185f7a;p=linux-2.6 diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 2ea027dda2..d2153abcba 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -29,6 +29,7 @@ #include "ntlmssp.h" #include "nterr.h" #include +#include "cifs_spnego.h" extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); @@ -67,14 +68,59 @@ static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB) pSMB->req.hdr.Flags2 |= SMBFLG2_DFS; capabilities |= CAP_DFS; } - if (ses->capabilities & CAP_UNIX) { + if (ses->capabilities & CAP_UNIX) capabilities |= CAP_UNIX; - } /* BB check whether to init vcnum BB */ return capabilities; } +static void +unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int bytes_ret = 0; + + /* Copy OS version */ + bytes_ret = cifs_strtoUCS((__le16 *)bcc_ptr, "Linux version ", 32, + nls_cp); + bcc_ptr += 2 * bytes_ret; + bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, init_utsname()->release, + 32, nls_cp); + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* trailing null */ + + bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, + 32, nls_cp); + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* trailing null */ + + *pbcc_area = bcc_ptr; +} + +static void unicode_domain_string(char **pbcc_area, struct cifsSesInfo *ses, + const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int bytes_ret = 0; + + /* copy domain */ + if (ses->domainName == NULL) { + /* Sending null domain better than using a bogus domain name (as + we did briefly in 2.6.18) since server will use its default */ + *bcc_ptr = 0; + *(bcc_ptr+1) = 0; + bytes_ret = 0; + } else + bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->domainName, + 256, nls_cp); + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* account for null terminator */ + + *pbcc_area = bcc_ptr; +} + + static void unicode_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses, const struct nls_table *nls_cp) { @@ -100,32 +146,9 @@ static void unicode_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses, } bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* account for null termination */ - /* copy domain */ - if (ses->domainName == NULL) { - /* Sending null domain better than using a bogus domain name (as - we did briefly in 2.6.18) since server will use its default */ - *bcc_ptr = 0; - *(bcc_ptr+1) = 0; - bytes_ret = 0; - } else - bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->domainName, - 256, nls_cp); - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* account for null terminator */ - /* Copy OS version */ - bytes_ret = cifs_strtoUCS((__le16 *)bcc_ptr, "Linux version ", 32, - nls_cp); - bcc_ptr += 2 * bytes_ret; - bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, init_utsname()->release, - 32, nls_cp); - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* trailing null */ - - bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, - 32, nls_cp); - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* trailing null */ + unicode_domain_string(&bcc_ptr, ses, nls_cp); + unicode_oslm_strings(&bcc_ptr, nls_cp); *pbcc_area = bcc_ptr; } @@ -203,14 +226,11 @@ static int decode_unicode_ssetup(char **pbcc_area, int bleft, if (len >= words_left) return rc; - if (ses->serverOS) - kfree(ses->serverOS); + kfree(ses->serverOS); /* UTF-8 string will not grow more than four times as big as UCS-16 */ ses->serverOS = kzalloc(4 * len, GFP_KERNEL); - if (ses->serverOS != NULL) { - cifs_strfromUCS_le(ses->serverOS, (__le16 *)data, len, - nls_cp); - } + if (ses->serverOS != NULL) + cifs_strfromUCS_le(ses->serverOS, (__le16 *)data, len, nls_cp); data += 2 * (len + 1); words_left -= len + 1; @@ -220,8 +240,7 @@ static int decode_unicode_ssetup(char **pbcc_area, int bleft, if (len >= words_left) return rc; - if (ses->serverNOS) - kfree(ses->serverNOS); + kfree(ses->serverNOS); ses->serverNOS = kzalloc(4 * len, GFP_KERNEL); /* BB this is wrong length FIXME BB */ if (ses->serverNOS != NULL) { cifs_strfromUCS_le(ses->serverNOS, (__le16 *)data, len, @@ -240,8 +259,7 @@ static int decode_unicode_ssetup(char **pbcc_area, int bleft, if (len > words_left) return rc; - if (ses->serverDomain) - kfree(ses->serverDomain); + kfree(ses->serverDomain); ses->serverDomain = kzalloc(2 * (len + 1), GFP_KERNEL); /* BB FIXME wrong length */ if (ses->serverDomain != NULL) { cifs_strfromUCS_le(ses->serverDomain, (__le16 *)data, len, @@ -271,8 +289,7 @@ static int decode_ascii_ssetup(char **pbcc_area, int bleft, if (len >= bleft) return rc; - if (ses->serverOS) - kfree(ses->serverOS); + kfree(ses->serverOS); ses->serverOS = kzalloc(len + 1, GFP_KERNEL); if (ses->serverOS) @@ -289,8 +306,7 @@ static int decode_ascii_ssetup(char **pbcc_area, int bleft, if (len >= bleft) return rc; - if (ses->serverNOS) - kfree(ses->serverNOS); + kfree(ses->serverNOS); ses->serverNOS = kzalloc(len + 1, GFP_KERNEL); if (ses->serverNOS) @@ -325,11 +341,12 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, SESSION_SETUP_ANDX *pSMB; __u32 capabilities; int count; - int resp_buf_type = 0; - struct kvec iov[2]; + int resp_buf_type; + struct kvec iov[3]; enum securityEnum type; __u16 action; int bytes_remaining; + struct key *spnego_key = NULL; if (ses == NULL) return -EINVAL; @@ -362,20 +379,32 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, capabilities = cifs_ssetup_hdr(ses, pSMB); - /* we will send the SMB in two pieces, - a fixed length beginning part, and a - second part which will include the strings - and rest of bcc area, in order to avoid having - to do a large buffer 17K allocation */ + /* we will send the SMB in three pieces: + a fixed length beginning part, an optional + SPNEGO blob (which can be zero length), and a + last part which will include the strings + and rest of bcc area. This allows us to avoid + a large buffer 17K allocation */ iov[0].iov_base = (char *)pSMB; iov[0].iov_len = smb_buf->smb_buf_length + 4; + /* setting this here allows the code at the end of the function + to free the request buffer if there's an error */ + resp_buf_type = CIFS_SMALL_BUFFER; + /* 2000 big enough to fit max user, domain, NOS name etc. */ str_area = kmalloc(2000, GFP_KERNEL); + if (str_area == NULL) { + rc = -ENOMEM; + goto ssetup_exit; + } bcc_ptr = str_area; ses->flags &= ~CIFS_SES_LANMAN; + iov[1].iov_base = NULL; + iov[1].iov_len = 0; + if (type == LANMAN) { #ifdef CONFIG_CIFS_WEAK_PW_HASH char lnm_session_key[CIFS_SESS_KEY_SIZE]; @@ -444,8 +473,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, struct ntlmv2_resp */ if (v2_sess_key == NULL) { - cifs_small_buf_release(smb_buf); - return -ENOMEM; + rc = -ENOMEM; + goto ssetup_exit; } pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); @@ -475,25 +504,74 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, if (ses->capabilities & CAP_UNICODE) { if (iov[0].iov_len % 2) { *bcc_ptr = 0; - } bcc_ptr++; + bcc_ptr++; + } unicode_ssetup_strings(&bcc_ptr, ses, nls_cp); } else ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); - } else /* NTLMSSP or SPNEGO */ { + } else if (type == Kerberos) { +#ifdef CONFIG_CIFS_UPCALL + struct cifs_spnego_msg *msg; + spnego_key = cifs_get_spnego_key(ses); + if (IS_ERR(spnego_key)) { + rc = PTR_ERR(spnego_key); + spnego_key = NULL; + goto ssetup_exit; + } + + msg = spnego_key->payload.data; + /* bail out if key is too long */ + if (msg->sesskey_len > + sizeof(ses->server->mac_signing_key.data.krb5)) { + cERROR(1, ("Kerberos signing key too long (%u bytes)", + msg->sesskey_len)); + rc = -EOVERFLOW; + goto ssetup_exit; + } + if (first_time) { + ses->server->mac_signing_key.len = msg->sesskey_len; + memcpy(ses->server->mac_signing_key.data.krb5, + msg->data, msg->sesskey_len); + } pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; capabilities |= CAP_EXTENDED_SECURITY; pSMB->req.Capabilities = cpu_to_le32(capabilities); - /* BB set password lengths */ + iov[1].iov_base = msg->data + msg->sesskey_len; + iov[1].iov_len = msg->secblob_len; + pSMB->req.SecurityBlobLength = cpu_to_le16(iov[1].iov_len); + + if (ses->capabilities & CAP_UNICODE) { + /* unicode strings must be word aligned */ + if ((iov[0].iov_len + iov[1].iov_len) % 2) { + *bcc_ptr = 0; + bcc_ptr++; + } + unicode_oslm_strings(&bcc_ptr, nls_cp); + unicode_domain_string(&bcc_ptr, ses, nls_cp); + } else + /* BB: is this right? */ + ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); +#else /* ! CONFIG_CIFS_UPCALL */ + cERROR(1, ("Kerberos negotiated but upcall support disabled!")); + rc = -ENOSYS; + goto ssetup_exit; +#endif /* CONFIG_CIFS_UPCALL */ + } else { + cERROR(1, ("secType %d not supported!", type)); + rc = -ENOSYS; + goto ssetup_exit; } - count = (long) bcc_ptr - (long) str_area; + iov[2].iov_base = str_area; + iov[2].iov_len = (long) bcc_ptr - (long) str_area; + + count = iov[1].iov_len + iov[2].iov_len; smb_buf->smb_buf_length += count; BCC_LE(smb_buf) = cpu_to_le16(count); - iov[1].iov_base = str_area; - iov[1].iov_len = count; - rc = SendReceive2(xid, ses, iov, 2 /* num_iovecs */, &resp_buf_type, 0); + rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type, + CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR); /* SMB request buf freed in SendReceive2 */ cFYI(1, ("ssetup rc from sendrecv2 is %d", rc)); @@ -539,6 +617,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, ses, nls_cp); ssetup_exit: + if (spnego_key) + key_put(spnego_key); kfree(str_area); if (resp_buf_type == CIFS_SMALL_BUFFER) { cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base));