diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-05-07 21:13:24 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-05-07 21:13:24 -0700 |
commit | d7a5926978cb109b2db5985c65086483caf9e226 (patch) | |
tree | 0431ca74aec312ed69dea21a7039b4c734307723 /fs | |
parent | 8c9ed899b44c19e81859fbb0e9d659fe2f8630fc (diff) | |
parent | 90e4ee5d311d4e0729daa676b1d7f754265b5874 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6: (32 commits)
[CIFS] Fix double list addition in cifs posix open code
[CIFS] Allow raw ntlmssp code to be enabled with sec=ntlmssp
[CIFS] Fix SMB uid in NTLMSSP authenticate request
[CIFS] NTLMSSP reenabled after move from connect.c to sess.c
[CIFS] Remove sparse warning
[CIFS] remove checkpatch warning
[CIFS] Fix final user of old string conversion code
[CIFS] remove cifs_strfromUCS_le
[CIFS] NTLMSSP support moving into new file, old dead code removed
[CIFS] Fix endian conversion of vcnum field
[CIFS] Remove trailing whitespace
[CIFS] Remove sparse endian warnings
[CIFS] Add remaining ntlmssp flags and standardize field names
[CIFS] Fix build warning
cifs: fix length handling in cifs_get_name_from_search_buf
[CIFS] Remove unneeded QuerySymlink call and fix mapping for unmapped status
[CIFS] rename cifs_strndup to cifs_strndup_from_ucs
Added loop check when mounting DFS tree.
Enable dfs submounts to handle remote referrals.
[CIFS] Remove older session setup implementation
...
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/CHANGES | 13 | ||||
-rw-r--r-- | fs/cifs/README | 10 | ||||
-rw-r--r-- | fs/cifs/cifs_dfs_ref.c | 32 | ||||
-rw-r--r-- | fs/cifs/cifs_unicode.c | 198 | ||||
-rw-r--r-- | fs/cifs/cifs_unicode.h | 23 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 16 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 5 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 217 | ||||
-rw-r--r-- | fs/cifs/connect.c | 1190 | ||||
-rw-r--r-- | fs/cifs/dir.c | 15 | ||||
-rw-r--r-- | fs/cifs/file.c | 14 | ||||
-rw-r--r-- | fs/cifs/inode.c | 21 | ||||
-rw-r--r-- | fs/cifs/link.c | 114 | ||||
-rw-r--r-- | fs/cifs/misc.c | 71 | ||||
-rw-r--r-- | fs/cifs/netmisc.c | 2 | ||||
-rw-r--r-- | fs/cifs/nterr.h | 9 | ||||
-rw-r--r-- | fs/cifs/ntlmssp.h | 68 | ||||
-rw-r--r-- | fs/cifs/readdir.c | 72 | ||||
-rw-r--r-- | fs/cifs/sess.c | 340 | ||||
-rw-r--r-- | fs/cifs/smberr.h | 1 |
21 files changed, 791 insertions, 1642 deletions
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 9d1fb6ec8a5a..f20c4069c220 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,16 @@ +Version 1.58 +------------ +Guard against buffer overruns in various UCS-2 to UTF-8 string conversions +when the UTF-8 string is composed of unusually long (more than 4 byte) converted +characters. Add support for mounting root of a share which redirects immediately +to DFS target. Convert string conversion functions from Unicode to more +accurately mark string length before allocating memory (which may help the +rare cases where a UTF-8 string is much larger than the UCS2 string that +we converted from). Fix endianness of the vcnum field used during +session setup to distinguish multiple mounts to same server from different +userids. Raw NTLMSSP fixed (it requires /proc/fs/cifs/experimental +flag to be set to 2, and mount must enable krb5 to turn on extended security). + Version 1.57 ------------ Improve support for multiple security contexts to the same server. We diff --git a/fs/cifs/README b/fs/cifs/README index 07434181623b..db208ddb9899 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -651,7 +651,15 @@ Experimental When set to 1 used to enable certain experimental signing turned on in case buffer was modified just before it was sent, also this flag will be used to use the new experimental directory change - notification code). + notification code). When set to 2 enables + an additional experimental feature, "raw ntlmssp" + session establishment support (which allows + specifying "sec=ntlmssp" on mount). The Linux cifs + module will use ntlmv2 authentication encapsulated + in "raw ntlmssp" (not using SPNEGO) when + "sec=ntlmssp" is specified on mount. + This support also requires building cifs with + the CONFIG_CIFS_EXPERIMENTAL configuration flag. These experimental features and tracing can be enabled by changing flags in /proc/fs/cifs (after the cifs module has been installed or built into the diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index 5fdbf8a14472..83d62759c7c7 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -340,28 +340,24 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); for (i = 0; i < num_referrals; i++) { + int len; dump_referral(referrals+i); - /* connect to a storage node */ - if (referrals[i].flags & DFSREF_STORAGE_SERVER) { - int len; - len = strlen(referrals[i].node_name); - if (len < 2) { - cERROR(1, ("%s: Net Address path too short: %s", + /* connect to a node */ + len = strlen(referrals[i].node_name); + if (len < 2) { + cERROR(1, ("%s: Net Address path too short: %s", __func__, referrals[i].node_name)); - rc = -EINVAL; - goto out_err; - } - mnt = cifs_dfs_do_refmount(nd->path.mnt, - nd->path.dentry, - referrals + i); - cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", - __func__, + rc = -EINVAL; + goto out_err; + } + mnt = cifs_dfs_do_refmount(nd->path.mnt, + nd->path.dentry, referrals + i); + cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__, referrals[i].node_name, mnt)); - /* complete mount procedure if we accured submount */ - if (!IS_ERR(mnt)) - break; - } + /* complete mount procedure if we accured submount */ + if (!IS_ERR(mnt)) + break; } /* we need it cause for() above could exit without valid submount */ diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 7d75272a6b3f..60e3c4253de0 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -1,7 +1,7 @@ /* * fs/cifs/cifs_unicode.c * - * Copyright (c) International Business Machines Corp., 2000,2005 + * Copyright (c) International Business Machines Corp., 2000,2009 * Modified by Steve French (sfrench@us.ibm.com) * * This program is free software; you can redistribute it and/or modify @@ -26,31 +26,157 @@ #include "cifs_debug.h" /* - * NAME: cifs_strfromUCS() - * - * FUNCTION: Convert little-endian unicode string to character string + * cifs_ucs2_bytes - how long will a string be after conversion? + * @ucs - pointer to input string + * @maxbytes - don't go past this many bytes of input string + * @codepage - destination codepage * + * Walk a ucs2le string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. */ int -cifs_strfromUCS_le(char *to, const __le16 *from, - int len, const struct nls_table *codepage) +cifs_ucs2_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage) { int i; - int outlen = 0; + int charlen, outlen = 0; + int maxwords = maxbytes / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; - for (i = 0; (i < len) && from[i]; i++) { - int charlen; - /* 2.4.0 kernel or greater */ - charlen = - codepage->uni2char(le16_to_cpu(from[i]), &to[outlen], - NLS_MAX_CHARSET_SIZE); - if (charlen > 0) { + for (i = 0; from[i] && i < maxwords; i++) { + charlen = codepage->uni2char(le16_to_cpu(from[i]), tmp, + NLS_MAX_CHARSET_SIZE); + if (charlen > 0) outlen += charlen; - } else { - to[outlen++] = '?'; + else + outlen++; + } + + return outlen; +} + +/* + * cifs_mapchar - convert a little-endian char to proper char in codepage + * @target - where converted character should be copied + * @src_char - 2 byte little-endian source character + * @cp - codepage to which character should be converted + * @mapchar - should character be mapped according to mapchars mount option? + * + * This function handles the conversion of a single character. It is the + * responsibility of the caller to ensure that the target buffer is large + * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). + */ +static int +cifs_mapchar(char *target, const __le16 src_char, const struct nls_table *cp, + bool mapchar) +{ + int len = 1; + + if (!mapchar) + goto cp_convert; + + /* + * BB: Cannot handle remapping UNI_SLASH until all the calls to + * build_path_from_dentry are modified, as they use slash as + * separator. + */ + switch (le16_to_cpu(src_char)) { + case UNI_COLON: + *target = ':'; + break; + case UNI_ASTERIK: + *target = '*'; + break; + case UNI_QUESTION: + *target = '?'; + break; + case UNI_PIPE: + *target = '|'; + break; + case UNI_GRTRTHAN: + *target = '>'; + break; + case UNI_LESSTHAN: + *target = '<'; + break; + default: + goto cp_convert; + } + +out: + return len; + +cp_convert: + len = cp->uni2char(le16_to_cpu(src_char), target, + NLS_MAX_CHARSET_SIZE); + if (len <= 0) { + *target = '?'; + len = 1; + } + goto out; +} + +/* + * cifs_from_ucs2 - convert utf16le string to local charset + * @to - destination buffer + * @from - source buffer + * @tolen - destination buffer size (in bytes) + * @fromlen - source buffer size (in bytes) + * @codepage - codepage to which characters should be converted + * @mapchar - should characters be remapped according to the mapchars option? + * + * Convert a little-endian ucs2le string (as sent by the server) to a string + * in the provided codepage. The tolen and fromlen parameters are to ensure + * that the code doesn't walk off of the end of the buffer (which is always + * a danger if the alignment of the source buffer is off). The destination + * string is always properly null terminated and fits in the destination + * buffer. Returns the length of the destination string in bytes (including + * null terminator). + * + * Note that some windows versions actually send multiword UTF-16 characters + * instead of straight UCS-2. The linux nls routines however aren't able to + * deal with those characters properly. In the event that we get some of + * those characters, they won't be translated properly. + */ +int +cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, bool mapchar) +{ + int i, charlen, safelen; + int outlen = 0; + int nullsize = nls_nullsize(codepage); + int fromwords = fromlen / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + + /* + * because the chars can be of varying widths, we need to take care + * not to overflow the destination buffer when we get close to the + * end of it. Until we get to this offset, we don't need to check + * for overflow however. + */ + safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); + + for (i = 0; i < fromwords && from[i]; i++) { + /* + * check to see if converting this character might make the + * conversion bleed into the null terminator + */ + if (outlen >= safelen) { + charlen = cifs_mapchar(tmp, from[i], codepage, mapchar); + if ((outlen + charlen) > (tolen - nullsize)) + break; } + + /* put converted char into 'to' buffer */ + charlen = cifs_mapchar(&to[outlen], from[i], codepage, mapchar); + outlen += charlen; } - to[outlen] = 0; + + /* properly null-terminate string */ + for (i = 0; i < nullsize; i++) + to[outlen++] = 0; + return outlen; } @@ -88,3 +214,41 @@ cifs_strtoUCS(__le16 *to, const char *from, int len, return i; } +/* + * cifs_strndup_from_ucs - copy a string from wire format to the local codepage + * @src - source string + * @maxlen - don't walk past this many bytes in the source string + * @is_unicode - is this a unicode string? + * @codepage - destination codepage + * + * Take a string given by the server, convert it to the local codepage and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + */ +char * +cifs_strndup_from_ucs(const char *src, const int maxlen, const bool is_unicode, + const struct nls_table *codepage) +{ + int len; + char *dst; + + if (is_unicode) { + len = cifs_ucs2_bytes((__le16 *) src, maxlen, codepage); + len += nls_nullsize(codepage); + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return NULL; + cifs_from_ucs2(dst, (__le16 *) src, len, maxlen, codepage, + false); + } else { + len = strnlen(src, maxlen); + len++; + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return NULL; + strlcpy(dst, src, len); + } + + return dst; +} + diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index 14eb9a2395d3..650638275a6f 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -5,7 +5,7 @@ * Convert a unicode character to upper or lower case using * compressed tables. * - * Copyright (c) International Business Machines Corp., 2000,2007 + * Copyright (c) International Business Machines Corp., 2000,2009 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,6 +37,19 @@ #define UNIUPR_NOLOWER /* Example to not expand lower case tables */ +/* + * Windows maps these to the user defined 16 bit Unicode range since they are + * reserved symbols (along with \ and /), otherwise illegal to store + * in filenames in NTFS + */ +#define UNI_ASTERIK (__u16) ('*' + 0xF000) +#define UNI_QUESTION (__u16) ('?' + 0xF000) +#define UNI_COLON (__u16) (':' + 0xF000) +#define UNI_GRTRTHAN (__u16) ('>' + 0xF000) +#define UNI_LESSTHAN (__u16) ('<' + 0xF000) +#define UNI_PIPE (__u16) ('|' + 0xF000) +#define UNI_SLASH (__u16) ('\\' + 0xF000) + /* Just define what we want from uniupr.h. We don't want to define the tables * in each source file. */ @@ -59,8 +72,14 @@ extern struct UniCaseRange UniLowerRange[]; #endif /* UNIUPR_NOLOWER */ #ifdef __KERNEL__ -int cifs_strfromUCS_le(char *, const __le16 *, int, const struct nls_table *); +int cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, bool mapchar); +int cifs_ucs2_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage); int cifs_strtoUCS(__le16 *, const char *, int, const struct nls_table *); +char *cifs_strndup_from_ucs(const char *src, const int maxlen, + const bool is_unicode, + const struct nls_table *codepage); #endif /* diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 77e190dc2883..051b71cfdea9 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -100,5 +100,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* EXPERIMENTAL */ -#define CIFS_VERSION "1.57" +#define CIFS_VERSION "1.58" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index df40ab64cd95..a61ab772c6f6 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -82,8 +82,8 @@ enum securityEnum { LANMAN, /* Legacy LANMAN auth */ NTLM, /* Legacy NTLM012 auth with NTLM hash */ NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */ - RawNTLMSSP, /* NTLMSSP without SPNEGO */ - NTLMSSP, /* NTLMSSP via SPNEGO */ + RawNTLMSSP, /* NTLMSSP without SPNEGO, NTLMv2 hash */ + NTLMSSP, /* NTLMSSP via SPNEGO, NTLMv2 hash */ Kerberos, /* Kerberos via SPNEGO */ MSKerberos, /* MS Kerberos via SPNEGO */ }; @@ -531,6 +531,7 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param, #define CIFSSEC_MAY_PLNTXT 0 #endif /* weak passwords */ #define CIFSSEC_MAY_SEAL 0x00040 /* not supported yet */ +#define CIFSSEC_MAY_NTLMSSP 0x00080 /* raw ntlmssp with ntlmv2 */ #define CIFSSEC_MUST_SIGN 0x01001 /* note that only one of the following can be set so the @@ -543,22 +544,23 @@ require use of the stronger protocol */ #define CIFSSEC_MUST_LANMAN 0x10010 #define CIFSSEC_MUST_PLNTXT 0x20020 #ifdef CONFIG_CIFS_UPCALL -#define CIFSSEC_MASK 0x3F03F /* allows weak security but also krb5 */ +#define CIFSSEC_MASK 0xAF0AF /* allows weak security but also krb5 */ #else -#define CIFSSEC_MASK 0x37037 /* current flags supported if weak */ +#define CIFSSEC_MASK 0xA70A7 /* current flags supported if weak */ #endif /* UPCALL */ #else /* do not allow weak pw hash */ #ifdef CONFIG_CIFS_UPCALL -#define CIFSSEC_MASK 0x0F00F /* flags supported if no weak allowed */ +#define CIFSSEC_MASK 0x8F08F /* flags supported if no weak allowed */ #else -#define CIFSSEC_MASK 0x07007 /* flags supported if no weak allowed */ +#define CIFSSEC_MASK 0x87087 /* flags supported if no weak allowed */ #endif /* UPCALL */ #endif /* WEAK_PW_HASH */ #define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */ +#define CIFSSEC_MUST_NTLMSSP 0x80080 /* raw ntlmssp with ntlmv2 */ #define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2) #define CIFSSEC_MAX (CIFSSEC_MUST_SIGN | CIFSSEC_MUST_NTLMV2) -#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_LANMAN | CIFSSEC_MAY_PLNTXT | CIFSSEC_MAY_KRB5) +#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_LANMAN | CIFSSEC_MAY_PLNTXT | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP) /* ***************************************************************** * All constants go here diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 4167716d32f2..fae083930eee 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -260,8 +260,7 @@ extern int CIFSUnixCreateSymLink(const int xid, const struct nls_table *nls_codepage); extern int CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon, - const unsigned char *searchName, - char *syminfo, const int buflen, + const unsigned char *searchName, char **syminfo, const struct nls_table *nls_codepage); extern int CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, @@ -307,8 +306,6 @@ extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, __u64 *inode_number, const struct nls_table *nls_codepage, int remap_special_chars); -extern int cifs_convertUCSpath(char *target, const __le16 *source, int maxlen, - const struct nls_table *codepage); extern int cifsConvertToUCS(__le16 *target, const char *source, int maxlen, const struct nls_table *cp, int mapChars); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index a0845dc7b8a9..75e6623a8635 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1,7 +1,7 @@ /* * fs/cifs/cifssmb.c * - * Copyright (C) International Business Machines Corp., 2002,2008 + * Copyright (C) International Business Machines Corp., 2002,2009 * Author(s): Steve French (sfrench@us.ibm.com) * * Contains the routines for constructing the SMB PDUs themselves @@ -81,41 +81,6 @@ static struct { #endif /* CONFIG_CIFS_WEAK_PW_HASH */ #endif /* CIFS_POSIX */ -/* Allocates buffer into dst and copies smb string from src to it. - * caller is responsible for freeing dst if function returned 0. - * returns: - * on success - 0 - * on failure - errno - */ -static int -cifs_strncpy_to_host(char **dst, const char *src, const int maxlen, - const bool is_unicode, const struct nls_table *nls_codepage) -{ - int plen; - - if (is_unicode) { - plen = UniStrnlen((wchar_t *)src, maxlen); - *dst = kmalloc(plen + 2, GFP_KERNEL); - if (!*dst) - goto cifs_strncpy_to_host_ErrExit; - cifs_strfromUCS_le(*dst, (__le16 *)src, plen, nls_codepage); - } else { - plen = strnlen(src, maxlen); - *dst = kmalloc(plen + 2, GFP_KERNEL); - if (!*dst) - goto cifs_strncpy_to_host_ErrExit; - strncpy(*dst, src, plen); - } - (*dst)[plen] = 0; - (*dst)[plen+1] = 0; /* harmless for ASCII case, needed for Unicode */ - return 0; - -cifs_strncpy_to_host_ErrExit: - cERROR(1, ("Failed to allocate buffer for string\n")); - return -ENOMEM; -} - - /* Mark as invalid, all open files on tree connections since they were closed when session to server was lost */ static void mark_open_files_invalid(struct cifsTconInfo *pTcon) @@ -484,6 +449,14 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) cFYI(1, ("Kerberos only mechanism, enable extended security")); pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; } +#ifdef CONFIG_CIFS_EXPERIMENTAL + else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP) + pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; + else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) { + cFYI(1, ("NTLMSSP only mechanism, enable extended security")); + pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; + } +#endif count = 0; for (i = 0; i < CIFS_NUM_PROT; i++) { @@ -620,6 +593,8 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) server->secType = NTLMv2; else if (secFlags & CIFSSEC_MAY_KRB5) server->secType = Kerberos; + else if (secFlags & CIFSSEC_MAY_NTLMSSP) + server->secType = NTLMSSP; else if (secFlags & CIFSSEC_MAY_LANMAN) server->secType = LANMAN; /* #ifdef CONFIG_CIFS_EXPERIMENTAL @@ -2417,8 +2392,7 @@ winCreateHardLinkRetry: int CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon, - const unsigned char *searchName, - char *symlinkinfo, const int buflen, + const unsigned char *searchName, char **symlinkinfo, const struct nls_table *nls_codepage) { /* SMB_QUERY_FILE_UNIX_LINK */ @@ -2428,6 +2402,7 @@ CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon, int bytes_returned; int name_len; __u16 params, byte_count; + char *data_start; cFYI(1, ("In QPathSymLinkInfo (Unix) for path %s", searchName)); @@ -2482,30 +2457,26 @@ querySymLinkRetry: /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc || (pSMBr->ByteCount < 2)) /* BB also check enough total bytes returned */ - rc = -EIO; /* bad smb */ + if (rc || (pSMBr->ByteCount < 2)) + rc = -EIO; else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - __u16 count = le16_to_cpu(pSMBr->t2.DataCount); + bool is_unicode; + u16 count = le16_to_cpu(pSMBr->t2.DataCount); + + data_start = ((char *) &pSMBr->hdr.Protocol) + + le16_to_cpu(pSMBr->t2.DataOffset); + + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) + is_unicode = true; + else + is_unicode = false; - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = UniStrnlen((wchar_t *) ((char *) - &pSMBr->hdr.Protocol + data_offset), - min_t(const int, buflen, count) / 2); /* BB FIXME investigate remapping reserved chars here */ - cifs_strfromUCS_le(symlinkinfo, - (__le16 *) ((char *)&pSMBr->hdr.Protocol - + data_offset), - name_len, nls_codepage); - } else { - strncpy(symlinkinfo, - (char *) &pSMBr->hdr.Protocol + - data_offset, - min_t(const int, buflen, count)); - } - symlinkinfo[buflen] = 0; - /* just in case so calling code does not go off the end of buffer */ + *symlinkinfo = cifs_strndup_from_ucs(data_start, count, + is_unicode, nls_codepage); + if (!symlinkinfo) + rc = -ENOMEM; } } cifs_buf_release(pSMB); @@ -2603,7 +2574,6 @@ validate_ntransact(char *buf, char **ppparm, char **ppdata, *pparmlen = parm_count; return 0; } -#endif /* CIFS_EXPERIMENTAL */ int CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, @@ -2613,7 +2583,6 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, { int rc = 0; int bytes_returned; - int name_len; struct smb_com_transaction_ioctl_req *pSMB; struct smb_com_transaction_ioctl_rsp *pSMBr; @@ -2650,59 +2619,55 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, } else { /* decode response */ __u32 data_offset = le32_to_cpu(pSMBr->DataOffset); __u32 data_count = le32_to_cpu(pSMBr->DataCount); - if ((pSMBr->ByteCount < 2) || (data_offset > 512)) + if ((pSMBr->ByteCount < 2) || (data_offset > 512)) { /* BB also check enough total bytes returned */ rc = -EIO; /* bad smb */ - else { - if (data_count && (data_count < 2048)) { - char *end_of_smb = 2 /* sizeof byte count */ + - pSMBr->ByteCount + - (char *)&pSMBr->ByteCount; + goto qreparse_out; + } + if (data_count && (data_count < 2048)) { + char *end_of_smb = 2 /* sizeof byte count */ + + pSMBr->ByteCount + (char *)&pSMBr->ByteCount; - struct reparse_data *reparse_buf = + struct reparse_data *reparse_buf = (struct reparse_data *) ((char *)&pSMBr->hdr.Protocol + data_offset); - if ((char *)reparse_buf >= end_of_smb) { - rc = -EIO; - goto qreparse_out; - } - if ((reparse_buf->LinkNamesBuf + - reparse_buf->TargetNameOffset + - reparse_buf->TargetNameLen) > - end_of_smb) { - cFYI(1, ("reparse buf beyond SMB")); - rc = -EIO; - goto qreparse_out; - } + if ((char *)reparse_buf >= end_of_smb) { + rc = -EIO; + goto qreparse_out; + } + if ((reparse_buf->LinkNamesBuf + + reparse_buf->TargetNameOffset + + reparse_buf->TargetNameLen) > end_of_smb) { + cFYI(1, ("reparse buf beyond SMB")); + rc = -EIO; + goto qreparse_out; + } - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = UniStrnlen((wchar_t *) + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { + cifs_from_ucs2(symlinkinfo, (__le16 *) (reparse_buf->LinkNamesBuf + reparse_buf->TargetNameOffset), - min(buflen/2, - reparse_buf->TargetNameLen / 2)); - cifs_strfromUCS_le(symlinkinfo, - (__le16 *) (reparse_buf->LinkNamesBuf + - reparse_buf->TargetNameOffset), - name_len, nls_codepage); - } else { /* ASCII names */ - strncpy(symlinkinfo, - reparse_buf->LinkNamesBuf + - reparse_buf->TargetNameOffset, - min_t(const int, buflen, - reparse_buf->TargetNameLen)); - } - } else { - rc = -EIO; - cFYI(1, ("Invalid return data count on " - "get reparse info ioctl")); + buflen, + reparse_buf->TargetNameLen, + nls_codepage, 0); + } else { /* ASCII names */ + strncpy(symlinkinfo, + reparse_buf->LinkNamesBuf + + reparse_buf->TargetNameOffset, + min_t(const int, buflen, + reparse_buf->TargetNameLen)); } - symlinkinfo[buflen] = 0; /* just in case so the caller - does not go off the end of the buffer */ - cFYI(1, ("readlink result - %s", symlinkinfo)); + } else { + rc = -EIO; + cFYI(1, ("Invalid return data count on " + "get reparse info ioctl")); } + symlinkinfo[buflen] = 0; /* just in case so the caller + does not go off the end of the buffer */ + cFYI(1, ("readlink result - %s", symlinkinfo)); } + qreparse_out: cifs_buf_release(pSMB); @@ -2711,6 +2676,7 @@ qreparse_out: return rc; } +#endif /* CIFS_EXPERIMENTAL */ #ifdef CONFIG_CIFS_POSIX @@ -3928,27 +3894,6 @@ GetInodeNumOut: return rc; } -/* computes length of UCS string converted to host codepage - * @src: UCS string - * @maxlen: length of the input string in UCS characters - * (not in bytes) - * - * return: size of input string in host codepage - */ -static int hostlen_fromUCS(const __le16 *src, const int maxlen, - const struct nls_table *nls_codepage) { - int i; - int hostlen = 0; - char to[4]; - int charlen; - for (i = 0; (i < maxlen) && src[i]; ++i) { - charlen = nls_codepage->uni2char(le16_to_cpu(src[i]), - to, NLS_MAX_CHARSET_SIZE); - hostlen += charlen > 0 ? charlen : 1; - } - return hostlen; -} - /* parses DFS refferal V3 structure * caller is responsible for freeing target_nodes * returns: @@ -3994,7 +3939,7 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n", *num_of_nodes, - le16_to_cpu(pSMBr->DFSFlags))); + le32_to_cpu(pSMBr->DFSFlags))); *target_nodes = kzalloc(sizeof(struct dfs_info3_param) * *num_of_nodes, GFP_KERNEL); @@ -4010,14 +3955,14 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, int max_len; struct dfs_info3_param *node = (*target_nodes)+i; - node->flags = le16_to_cpu(pSMBr->DFSFlags); + node->flags = le32_to_cpu(pSMBr->DFSFlags); if (is_unicode) { __le16 *tmp = kmalloc(strlen(searchName)*2 + 2, GFP_KERNEL); cifsConvertToUCS((__le16 *) tmp, searchName, PATH_MAX, nls_codepage, remap); - node->path_consumed = hostlen_fromUCS(tmp, - le16_to_cpu(pSMBr->PathConsumed)/2, + node->path_consumed = cifs_ucs2_bytes(tmp, + le16_to_cpu(pSMBr->PathConsumed), nls_codepage); kfree(tmp); } else @@ -4029,20 +3974,24 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, /* copy DfsPath */ temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); max_len = data_end - temp; - rc = cifs_strncpy_to_host(&(node->path_name), temp, - max_len, is_unicode, nls_codepage); - if (rc) + node->path_name = cifs_strndup_from_ucs(temp, max_len, + is_unicode, nls_codepage); + if (IS_ERR(node->path_name)) { + rc = PTR_ERR(node->path_name); + node->path_name = NULL; goto parse_DFS_referrals_exit; + } /* copy link target UNC */ temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); max_len = data_end - temp; - rc = cifs_strncpy_to_host(&(node->node_name), temp, - max_len, is_unicode, nls_codepage); - if (rc) + node->node_name = cifs_strndup_from_ucs(temp, max_len, + is_unicode, nls_codepage); + if (IS_ERR(node->node_name)) { + rc = PTR_ERR(node->node_name); + node->node_name = NULL; goto parse_DFS_referrals_exit; - - ref += le16_to_cpu(ref->Size); + } } parse_DFS_referrals_exit: diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index bacdef1546b7..4aa81a507b74 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1,7 +1,7 @@ /* * fs/cifs/connect.c * - * Copyright (C) International Business Machines Corp., 2002,2008 + * Copyright (C) International Business Machines Corp., 2002,2009 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -32,6 +32,7 @@ #include <linux/kthread.h> #include <linux/pagevec.h> #include <linux/freezer.h> +#include <linux/namei.h> #include <asm/uaccess.h> #include <asm/processor.h> #include <net/ipv6.h> @@ -978,6 +979,13 @@ cifs_parse_mount_options(char *options, const char *devname, return 1; } else if (strnicmp(value, "krb5", 4) == 0) { vol->secFlg |= CIFSSEC_MAY_KRB5; +#ifdef CONFIG_CIFS_EXPERIMENTAL + } else if (strnicmp(value, "ntlmsspi", 8) == 0) { + vol->secFlg |= CIFSSEC_MAY_NTLMSSP | + CIFSSEC_MUST_SIGN; + } else if (strnicmp(value, "ntlmssp", 7) == 0) { + vol->secFlg |= CIFSSEC_MAY_NTLMSSP; +#endif } else if (strnicmp(value, "ntlmv2i", 7) == 0) { vol->secFlg |= CIFSSEC_MAY_NTLMV2 | CIFSSEC_MUST_SIGN; @@ -2278,6 +2286,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, #ifdef CONFIG_CIFS_DFS_UPCALL struct dfs_info3_param *referrals = NULL; unsigned int num_referrals = 0; + int referral_walks_count = 0; try_mount_again: #endif full_path = NULL; @@ -2525,6 +2534,16 @@ remote_path_check: /* get referral if needed */ if (rc == -EREMOTE) { #ifdef CONFIG_CIFS_DFS_UPCALL + if (referral_walks_count > MAX_NESTED_LINKS) { + /* + * BB: when we implement proper loop detection, + * we will remove this check. But now we need it + * to prevent an indefinite loop if 'DFS tree' is + * misconfigured (i.e. has loops). + */ + rc = -ELOOP; + goto mount_fail_check;< |