diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-20 13:37:49 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-20 13:37:49 -0700 |
commit | 91444f47b2a0e50a15849f49db8c15382cd8de1b (patch) | |
tree | f565abed6640cc07607729969643551202f77e57 /fs | |
parent | bc091c93a0f60717aa99e25c406892cd8c0187dc (diff) | |
parent | 156ecb2d8b06589098f6ce3012e6a10fef07c416 (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 to problem with getattr caused by invalidate simplification patch
[CIFS] Remove sparse warning
[CIFS] Update cifs to version 1.72
cifs: Change key name to cifs.idmap, misc. clean-up
cifs: Unconditionally copy mount options to superblock info
cifs: Use kstrndup for cifs_sb->mountdata
cifs: Simplify handling of submount options in cifs_mount.
cifs: cifs_parse_mount_options: do not tokenize mount options in-place
cifs: Add support for mounting Windows 2008 DFS shares
cifs: Extract DFS referral expansion logic to separate function
cifs: turn BCC into a static inlined function
cifs: keep BCC in little-endian format
cifs: fix some unused variable warnings in id_rb_search
CIFS: Simplify invalidate part (try #5)
CIFS: directio read/write cleanups
consistently use smb_buf_length as be32 for cifs (try 3)
cifs: Invoke id mapping functions (try #17 repost)
cifs: Add idmap key and related data structures and functions (try #17 repost)
CIFS: Add launder_page operation (try #3)
Introduce smb2 mounts as vers=2
...
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/Kconfig | 35 | ||||
-rw-r--r-- | fs/cifs/Makefile | 2 | ||||
-rw-r--r-- | fs/cifs/README | 12 | ||||
-rw-r--r-- | fs/cifs/cifs_debug.c | 2 | ||||
-rw-r--r-- | fs/cifs/cifs_fs_sb.h | 4 | ||||
-rw-r--r-- | fs/cifs/cifs_unicode.h | 3 | ||||
-rw-r--r-- | fs/cifs/cifsacl.c | 483 | ||||
-rw-r--r-- | fs/cifs/cifsacl.h | 25 | ||||
-rw-r--r-- | fs/cifs/cifsencrypt.c | 12 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 119 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 20 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 10 | ||||
-rw-r--r-- | fs/cifs/cifspdu.h | 37 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 30 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 377 | ||||
-rw-r--r-- | fs/cifs/connect.c | 279 | ||||
-rw-r--r-- | fs/cifs/export.c | 4 | ||||
-rw-r--r-- | fs/cifs/file.c | 167 | ||||
-rw-r--r-- | fs/cifs/inode.c | 129 | ||||
-rw-r--r-- | fs/cifs/misc.c | 12 | ||||
-rw-r--r-- | fs/cifs/netmisc.c | 7 | ||||
-rw-r--r-- | fs/cifs/sess.c | 9 | ||||
-rw-r--r-- | fs/cifs/smbdes.c | 418 | ||||
-rw-r--r-- | fs/cifs/smbencrypt.c | 124 | ||||
-rw-r--r-- | fs/cifs/transport.c | 66 | ||||
-rw-r--r-- | fs/cifs/xattr.c | 20 | ||||
-rw-r--r-- | fs/super.c | 3 |
27 files changed, 1289 insertions, 1120 deletions
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 7cb0f7f847e4..75c47cd8d086 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -7,6 +7,7 @@ config CIFS select CRYPTO_MD5 select CRYPTO_HMAC select CRYPTO_ARC4 + select CRYPTO_DES help This is the client VFS module for the Common Internet File System (CIFS) protocol which is the successor to the Server Message Block @@ -152,16 +153,28 @@ config CIFS_ACL Allows to fetch CIFS/NTFS ACL from the server. The DACL blob is handed over to the application/caller. -config CIFS_EXPERIMENTAL - bool "CIFS Experimental Features (EXPERIMENTAL)" +config CIFS_SMB2 + bool "SMB2 network file system support (EXPERIMENTAL)" + depends on EXPERIMENTAL && INET && BROKEN + select NLS + select KEYS + select FSCACHE + select DNS_RESOLVER + + help + This enables experimental support for the SMB2 (Server Message Block + version 2) protocol. The SMB2 protocol is the successor to the + popular CIFS and SMB network file sharing protocols. SMB2 is the + native file sharing mechanism for recent versions of Windows + operating systems (since Vista). SMB2 enablement will eventually + allow users better performance, security and features, than would be + possible with cifs. Note that smb2 mount options also are simpler + (compared to cifs) due to protocol improvements. + + Unless you are a developer or tester, say N. + +config CIFS_NFSD_EXPORT + bool "Allow nfsd to export CIFS file system (EXPERIMENTAL)" depends on CIFS && EXPERIMENTAL help - Enables cifs features under testing. These features are - experimental and currently include DFS support and directory - change notification ie fcntl(F_DNOTIFY), as well as the upcall - mechanism which will be used for Kerberos session negotiation - and uid remapping. Some of these features also may depend on - setting a value of 1 to the pseudo-file /proc/fs/cifs/Experimental - (which is disabled by default). See the file fs/cifs/README - for more details. If unsure, say N. - + Allows NFS server to export a CIFS mounted share (nfsd over cifs) diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index d87558448e3d..005d524c3a4a 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_CIFS) += cifs.o cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \ - link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o \ + link.o misc.o netmisc.o smbencrypt.o transport.o asn1.o \ cifs_unicode.o nterr.o xattr.o cifsencrypt.o \ readdir.o ioctl.o sess.o export.o diff --git a/fs/cifs/README b/fs/cifs/README index 74ab165fc646..4a3ca0e5ca24 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -704,18 +704,6 @@ the start of smb requests and responses can be enabled via: echo 1 > /proc/fs/cifs/traceSMB -Two other experimental features are under development. To test these -requires enabling CONFIG_CIFS_EXPERIMENTAL - - cifsacl support needed to retrieve approximated mode bits based on - the contents on the CIFS ACL. - - lease support: cifs will check the oplock state before calling into - the vfs to see if we can grant a lease on a file. - - DNOTIFY fcntl: needed for support of directory change - notification and perhaps later for file leases) - Per share (per client mount) statistics are available in /proc/fs/cifs/Stats if the kernel was configured with cifs statistics enabled. The statistics represent the number of successful (ie non-zero return code from the server) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 30d01bc90855..18f4272d9047 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -63,7 +63,7 @@ void cifs_dump_detail(struct smb_hdr *smb) cERROR(1, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d", smb->Command, smb->Status.CifsError, smb->Flags, smb->Flags2, smb->Mid, smb->Pid); - cERROR(1, "smb buf %p len %d", smb, smbCalcSize_LE(smb)); + cERROR(1, "smb buf %p len %d", smb, smbCalcSize(smb)); } diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index ac51cd2d33ae..a9d5692e0c20 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -58,9 +58,7 @@ struct cifs_sb_info { unsigned int mnt_cifs_flags; int prepathlen; char *prepath; /* relative path under the share to mount to */ -#ifdef CONFIG_CIFS_DFS_UPCALL - char *mountdata; /* mount options received at mount time */ -#endif + char *mountdata; /* options received at mount time or via DFS refs */ struct backing_dev_info bdi; struct delayed_work prune_tlinks; }; diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index 644dd882a560..6d02fd560566 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -82,6 +82,9 @@ 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); +extern int cifsConvertToUCS(__le16 *target, const char *source, int maxlen, + const struct nls_table *cp, int mapChars); + #endif /* diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index beeebf194234..f3c6fb9942ac 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -23,24 +23,16 @@ #include <linux/fs.h> #include <linux/slab.h> +#include <linux/string.h> +#include <linux/keyctl.h> +#include <linux/key-type.h> +#include <keys/user-type.h> #include "cifspdu.h" #include "cifsglob.h" #include "cifsacl.h" #include "cifsproto.h" #include "cifs_debug.h" - -static struct cifs_wksid wksidarr[NUM_WK_SIDS] = { - {{1, 0, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0} }, "null user"}, - {{1, 1, {0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0} }, "nobody"}, - {{1, 1, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(11), 0, 0, 0, 0} }, "net-users"}, - {{1, 1, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(18), 0, 0, 0, 0} }, "sys"}, - {{1, 2, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(32), __constant_cpu_to_le32(544), 0, 0, 0} }, "root"}, - {{1, 2, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(32), __constant_cpu_to_le32(545), 0, 0, 0} }, "users"}, - {{1, 2, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(32), __constant_cpu_to_le32(546), 0, 0, 0} }, "guest"} } -; - - /* security id for everyone/world system group */ static const struct cifs_sid sid_everyone = { 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; @@ -50,50 +42,385 @@ static const struct cifs_sid sid_authusers = { /* group users */ static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} }; +const struct cred *root_cred; -int match_sid(struct cifs_sid *ctsid) +static void +shrink_idmap_tree(struct rb_root *root, int nr_to_scan, int *nr_rem, + int *nr_del) { - int i, j; - int num_subauth, num_sat, num_saw; - struct cifs_sid *cwsid; + struct rb_node *node; + struct rb_node *tmp; + struct cifs_sid_id *psidid; + + node = rb_first(root); + while (node) { + tmp = node; + node = rb_next(tmp); + psidid = rb_entry(tmp, struct cifs_sid_id, rbnode); + if (nr_to_scan == 0 || *nr_del == nr_to_scan) + ++(*nr_rem); + else { + if (time_after(jiffies, psidid->time + SID_MAP_EXPIRE) + && psidid->refcount == 0) { + rb_erase(tmp, root); + ++(*nr_del); + } else + ++(*nr_rem); + } + } +} + +/* + * Run idmap cache shrinker. + */ +static int +cifs_idmap_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) +{ + int nr_del = 0; + int nr_rem = 0; + struct rb_root *root; + + root = &uidtree; + spin_lock(&siduidlock); + shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del); + spin_unlock(&siduidlock); + + root = &gidtree; + spin_lock(&sidgidlock); + shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del); + spin_unlock(&sidgidlock); + + return nr_rem; +} + +static struct shrinker cifs_shrinker = { + .shrink = cifs_idmap_shrinker, + .seeks = DEFAULT_SEEKS, +}; + +static int +cifs_idmap_key_instantiate(struct key *key, const void *data, size_t datalen) +{ + char *payload; + + payload = kmalloc(datalen, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + memcpy(payload, data, datalen); + key->payload.data = payload; + return 0; +} + +static inline void +cifs_idmap_key_destroy(struct key *key) +{ + kfree(key->payload.data); +} - if (!ctsid) - return -1; +struct key_type cifs_idmap_key_type = { + .name = "cifs.idmap", + .instantiate = cifs_idmap_key_instantiate, + .destroy = cifs_idmap_key_destroy, + .describe = user_describe, + .match = user_match, +}; + +static void +sid_to_str(struct cifs_sid *sidptr, char *sidstr) +{ + int i; + unsigned long saval; + char *strptr; - for (i = 0; i < NUM_WK_SIDS; ++i) { - cwsid = &(wksidarr[i].cifssid); + strptr = sidstr; - /* compare the revision */ - if (ctsid->revision != cwsid->revision) - continue; + sprintf(strptr, "%s", "S"); + strptr = sidstr + strlen(sidstr); - /* compare all of the six auth values */ - for (j = 0; j < 6; ++j) { - if (ctsid->authority[j] != cwsid->authority[j]) - break; + sprintf(strptr, "-%d", sidptr->revision); + strptr = sidstr + strlen(sidstr); + + for (i = 0; i < 6; ++i) { + if (sidptr->authority[i]) { + sprintf(strptr, "-%d", sidptr->authority[i]); + strptr = sidstr + strlen(sidstr); } - if (j < 6) - continue; /* all of the auth values did not match */ - - /* compare all of the subauth values if any */ - num_sat = ctsid->num_subauth; - num_saw = cwsid->num_subauth; - num_subauth = num_sat < num_saw ? num_sat : num_saw; - if (num_subauth) { - for (j = 0; j < num_subauth; ++j) { - if (ctsid->sub_auth[j] != cwsid->sub_auth[j]) - break; - } - if (j < num_subauth) - continue; /* all sub_auth values do not match */ + } + + for (i = 0; i < sidptr->num_subauth; ++i) { + saval = le32_to_cpu(sidptr->sub_auth[i]); + sprintf(strptr, "-%ld", saval); + strptr = sidstr + strlen(sidstr); + } +} + +static void +id_rb_insert(struct rb_root *root, struct cifs_sid *sidptr, + struct cifs_sid_id **psidid, char *typestr) +{ + int rc; + char *strptr; + struct rb_node *node = root->rb_node; + struct rb_node *parent = NULL; + struct rb_node **linkto = &(root->rb_node); + struct cifs_sid_id *lsidid; + + while (node) { + lsidid = rb_entry(node, struct cifs_sid_id, rbnode); + parent = node; + rc = compare_sids(sidptr, &((lsidid)->sid)); + if (rc > 0) { + linkto = &(node->rb_left); + node = node->rb_left; + } else if (rc < 0) { + linkto = &(node->rb_right); + node = node->rb_right; + } + } + + memcpy(&(*psidid)->sid, sidptr, sizeof(struct cifs_sid)); + (*psidid)->time = jiffies - (SID_MAP_RETRY + 1); + (*psidid)->refcount = 0; + + sprintf((*psidid)->sidstr, "%s", typestr); + strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr); + sid_to_str(&(*psidid)->sid, strptr); + + clear_bit(SID_ID_PENDING, &(*psidid)->state); + clear_bit(SID_ID_MAPPED, &(*psidid)->state); + + rb_link_node(&(*psidid)->rbnode, parent, linkto); + rb_insert_color(&(*psidid)->rbnode, root); +} + +static struct cifs_sid_id * +id_rb_search(struct rb_root *root, struct cifs_sid *sidptr) +{ + int rc; + struct rb_node *node = root->rb_node; + struct cifs_sid_id *lsidid; + + while (node) { + lsidid = rb_entry(node, struct cifs_sid_id, rbnode); + rc = compare_sids(sidptr, &((lsidid)->sid)); + if (rc > 0) { + node = node->rb_left; + } else if (rc < 0) { + node = node->rb_right; + } else /* node found */ + return lsidid; + } + + return NULL; +} + +static int +sidid_pending_wait(void *unused) +{ + schedule(); + return signal_pending(current) ? -ERESTARTSYS : 0; +} + +static int +sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, + struct cifs_fattr *fattr, uint sidtype) +{ + int rc; + unsigned long cid; + struct key *idkey; + const struct cred *saved_cred; + struct cifs_sid_id *psidid, *npsidid; + struct rb_root *cidtree; + spinlock_t *cidlock; + + if (sidtype == SIDOWNER) { + cid = cifs_sb->mnt_uid; /* default uid, in case upcall fails */ + cidlock = &siduidlock; + cidtree = &uidtree; + } else if (sidtype == SIDGROUP) { + cid = cifs_sb->mnt_gid; /* default gid, in case upcall fails */ + cidlock = &sidgidlock; + cidtree = &gidtree; + } else + return -ENOENT; + + spin_lock(cidlock); + psidid = id_rb_search(cidtree, psid); + + if (!psidid) { /* node does not exist, allocate one & attempt adding */ + spin_unlock(cidlock); + npsidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL); + if (!npsidid) + return -ENOMEM; + + npsidid->sidstr = kmalloc(SIDLEN, GFP_KERNEL); + if (!npsidid->sidstr) { + kfree(npsidid); + return -ENOMEM; + } + + spin_lock(cidlock); + psidid = id_rb_search(cidtree, psid); + if (psidid) { /* node happened to get inserted meanwhile */ + ++psidid->refcount; + spin_unlock(cidlock); + kfree(npsidid->sidstr); + kfree(npsidid); + } else { + psidid = npsidid; + id_rb_insert(cidtree, psid, &psidid, + sidtype == SIDOWNER ? "os:" : "gs:"); + ++psidid->refcount; + spin_unlock(cidlock); } + } else { + ++psidid->refcount; + spin_unlock(cidlock); + } + + /* + * If we are here, it is safe to access psidid and its fields + * since a reference was taken earlier while holding the spinlock. + * A reference on the node is put without holding the spinlock + * and it is OK to do so in this case, shrinker will not erase + * this node until all references are put and we do not access + * any fields of the node after a reference is put . + */ + if (test_bit(SID_ID_MAPPED, &psidid->state)) { + cid = psidid->id; + psidid->time = jiffies; /* update ts for accessing */ + goto sid_to_id_out; + } - cFYI(1, "matching sid: %s\n", wksidarr[i].sidname); - return 0; /* sids compare/match */ + if (time_after(psidid->time + SID_MAP_RETRY, jiffies)) + goto sid_to_id_out; + + if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) { + saved_cred = override_creds(root_cred); + idkey = request_key(&cifs_idmap_key_type, psidid->sidstr, ""); + if (IS_ERR(idkey)) + cFYI(1, "%s: Can't map SID to an id", __func__); + else { + cid = *(unsigned long *)idkey->payload.value; + psidid->id = cid; + set_bit(SID_ID_MAPPED, &psidid->state); + key_put(idkey); + kfree(psidid->sidstr); + } + revert_creds(saved_cred); + psidid->time = jiffies; /* update ts for accessing */ + clear_bit(SID_ID_PENDING, &psidid->state); + wake_up_bit(&psidid->state, SID_ID_PENDING); + } else { + rc = wait_on_bit(&psidid->state, SID_ID_PENDING, + sidid_pending_wait, TASK_INTERRUPTIBLE); + if (rc) { + cFYI(1, "%s: sidid_pending_wait interrupted %d", + __func__, rc); + --psidid->refcount; /* decremented without spinlock */ + return rc; + } + if (test_bit(SID_ID_MAPPED, &psidid->state)) + cid = psidid->id; } - cFYI(1, "No matching sid"); - return -1; +sid_to_id_out: + --psidid->refcount; /* decremented without spinlock */ + if (sidtype == SIDOWNER) + fattr->cf_uid = cid; + else + fattr->cf_gid = cid; + + return 0; +} + +int +init_cifs_idmap(void) +{ + struct cred *cred; + struct key *keyring; + int ret; + + cFYI(1, "Registering the %s key type\n", cifs_idmap_key_type.name); + + /* create an override credential set with a special thread keyring in + * which requests are cached + * + * this is used to prevent malicious redirections from being installed + * with add_key(). + */ + cred = prepare_kernel_cred(NULL); + if (!cred) + return -ENOMEM; + + keyring = key_alloc(&key_type_keyring, ".cifs_idmap", 0, 0, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto failed_put_cred; + } + + ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); + if (ret < 0) + goto failed_put_key; + + ret = register_key_type(&cifs_idmap_key_type); + if (ret < 0) + goto failed_put_key; + + /* instruct request_key() to use this special keyring as a cache for + * the results it looks up */ + cred->thread_keyring = keyring; + cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; + root_cred = cred; + + spin_lock_init(&siduidlock); + uidtree = RB_ROOT; + spin_lock_init(&sidgidlock); + gidtree = RB_ROOT; + + register_shrinker(&cifs_shrinker); + + cFYI(1, "cifs idmap keyring: %d\n", key_serial(keyring)); + return 0; + +failed_put_key: + key_put(keyring); +failed_put_cred: + put_cred(cred); + return ret; +} + +void +exit_cifs_idmap(void) +{ + key_revoke(root_cred->thread_keyring); + unregister_key_type(&cifs_idmap_key_type); + put_cred(root_cred); + unregister_shrinker(&cifs_shrinker); + cFYI(1, "Unregistered %s key type\n", cifs_idmap_key_type.name); +} + +void +cifs_destroy_idmaptrees(void) +{ + struct rb_root *root; + struct rb_node *node; + + root = &uidtree; + spin_lock(&siduidlock); + while ((node = rb_first(root))) + rb_erase(node, root); + spin_unlock(&siduidlock); + + root = &gidtree; + spin_lock(&sidgidlock); + while ((node = rb_first(root))) + rb_erase(node, root); + spin_unlock(&sidgidlock); } /* if the two SIDs (roughly equivalent to a UUID for a user or group) are @@ -104,16 +431,24 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) int num_subauth, num_sat, num_saw; if ((!ctsid) || (!cwsid)) - return 0; + return 1; /* compare the revision */ - if (ctsid->revision != cwsid->revision) - return 0; + if (ctsid->revision != cwsid->revision) { + if (ctsid->revision > cwsid->revision) + return 1; + else + return -1; + } /* compare all of the six auth values */ for (i = 0; i < 6; ++i) { - if (ctsid->authority[i] != cwsid->authority[i]) - return 0; + if (ctsid->authority[i] != cwsid->authority[i]) { + if (ctsid->authority[i] > cwsid->authority[i]) + return 1; + else + return -1; + } } /* compare all of the subauth values if any */ @@ -122,12 +457,16 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) num_subauth = num_sat < num_saw ? num_sat : num_saw; if (num_subauth) { for (i = 0; i < num_subauth; ++i) { - if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) - return 0; + if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { + if (ctsid->sub_auth[i] > cwsid->sub_auth[i]) + return 1; + else + return -1; + } } } - return 1; /* sids compare/match */ + return 0; /* sids compare/match */ } @@ -382,22 +721,22 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, #ifdef CONFIG_CIFS_DEBUG2 dump_ace(ppace[i], end_of_acl); #endif - if (compare_sids(&(ppace[i]->sid), pownersid)) + if (compare_sids(&(ppace[i]->sid), pownersid) == 0) access_flags_to_mode(ppace[i]->access_req, ppace[i]->type, &fattr->cf_mode, &user_mask); - if (compare_sids(&(ppace[i]->sid), pgrpsid)) + if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0) access_flags_to_mode(ppace[i]->access_req, ppace[i]->type, &fattr->cf_mode, &group_mask); - if (compare_sids(&(ppace[i]->sid), &sid_everyone)) + if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0) access_flags_to_mode(ppace[i]->access_req, ppace[i]->type, &fattr->cf_mode, &other_mask); - if (compare_sids(&(ppace[i]->sid), &sid_authusers)) + if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0) access_flags_to_mode(ppace[i]->access_req, ppace[i]->type, &fattr->cf_mode, @@ -475,10 +814,10 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl) /* Convert CIFS ACL to POSIX form */ -static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, - struct cifs_fattr *fattr) +static int parse_sec_desc(struct cifs_sb_info *cifs_sb, + struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr) { - int rc; + int rc = 0; struct cifs_sid *owner_sid_ptr, *group_sid_ptr; struct cifs_acl *dacl_ptr; /* no need for SACL ptr */ char *end_of_acl = ((char *)pntsd) + acl_len; @@ -500,12 +839,26 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, le32_to_cpu(pntsd->sacloffset), dacloffset); /* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */ rc = parse_sid(owner_sid_ptr, end_of_acl); - if (rc) + if (rc) { + cFYI(1, "%s: Error %d parsing Owner SID", __func__, rc); + return rc; + } + rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER); + if (rc) { + cFYI(1, "%s: Error %d mapping Owner SID to uid", __func__, rc); return rc; + } rc = parse_sid(group_sid_ptr, end_of_acl); - if (rc) + if (rc) { + cFYI(1, "%s: Error %d mapping Owner SID to gid", __func__, rc); return rc; + } + rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP); + if (rc) { + cFYI(1, "%s: Error %d mapping Group SID to gid", __func__, rc); + return rc; + } if (dacloffset) parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, @@ -520,7 +873,7 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr, sizeof(struct cifs_sid)); */ - return 0; + return rc; } @@ -688,7 +1041,7 @@ out: } /* Set an ACL on the server */ -static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, +int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, struct inode *inode, const char *path) { struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); @@ -727,7 +1080,7 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, rc = PTR_ERR(pntsd); cERROR(1, "%s: error %d getting sec desc", __func__, rc); } else { - rc = parse_sec_desc(pntsd, acllen, fattr); + rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr); kfree(pntsd); if (rc) cERROR(1, "parse sec desc failed rc = %d", rc); diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h index c4ae7d036563..5c902c7ce524 100644 --- a/fs/cifs/cifsacl.h +++ b/fs/cifs/cifsacl.h @@ -39,6 +39,15 @@ #define ACCESS_ALLOWED 0 #define ACCESS_DENIED 1 +#define SIDOWNER 1 +#define SIDGROUP 2 +#define SIDLEN 150 /* S- 1 revision- 6 authorities- max 5 sub authorities */ + +#define SID_ID_MAPPED 0 +#define SID_ID_PENDING 1 +#define SID_MAP_EXPIRE (3600 * HZ) /* map entry expires after one hour */ +#define SID_MAP_RETRY (300 * HZ) /* wait 5 minutes for next attempt to map */ + struct cifs_ntsd { __le16 revision; /* revision level */ __le16 type; @@ -74,7 +83,21 @@ struct cifs_wksid { char sidname[SIDNAMELENGTH]; } __attribute__((packed)); -extern int match_sid(struct cifs_sid *); +struct cifs_sid_id { + unsigned int refcount; /* increment with spinlock, decrement without */ + unsigned long id; + unsigned long time; + unsigned long state; + char *sidstr; + struct rb_node rbnode; + struct cifs_sid sid; +}; + +#ifdef __KERNEL__ +extern struct key_type cifs_idmap_key_type; +extern const struct cred *root_cred; +#endif /* KERNEL */ + extern int compare_sids(const struct cifs_sid *, const struct cifs_sid *); #endif /* _CIFSACL_H */ diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index d1a016be73ba..45c3f78c8f81 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -60,7 +60,7 @@ static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu, server->session_key.response, server->session_key.len); crypto_shash_update(&server->secmech.sdescmd5->shash, - cifs_pdu->Protocol, cifs_pdu->smb_buf_length); + cifs_pdu->Protocol, be32_to_cpu(cifs_pdu->smb_buf_length)); rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature); @@ -268,10 +268,11 @@ int setup_ntlm_response(struct cifsSesInfo *ses) } #ifdef CONFIG_CIFS_WEAK_PW_HASH -void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, +int calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, char *lnm_session_key) { int i; + int rc; char password_with_pad[CIFS_ENCPWD_SIZE]; memset(password_with_pad, 0, CIFS_ENCPWD_SIZE); @@ -282,7 +283,7 @@ void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, memset(lnm_session_key, 0, CIFS_SESS_KEY_SIZE); memcpy(lnm_session_key, password_with_pad, CIFS_ENCPWD_SIZE); - return; + return 0; } /* calculate old style session key */ @@ -299,10 +300,9 @@ void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, for (i = 0; i < CIFS_ENCPWD_SIZE; i++) password_with_pad[i] = toupper(password_with_pad[i]); - SMBencrypt(password_with_pad, cryptkey, lnm_session_key); + rc = SMBencrypt(password_with_pad, cryptkey, lnm_session_key); - /* clear password before we return/free memory */ - memset(password_with_pad, 0, CIFS_ENCPWD_SIZE); + return rc; } #endif /* CIFS_WEAK_PW_HASH */ diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 5c412b33cd7c..493b74ca5648 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -128,29 +128,22 @@ cifs_read_super(struct super_block *sb, void *data, } cifs_sb->bdi.ra_pages = default_backing_dev_info.ra_pages; -#ifdef CONFIG_CIFS_DFS_UPCALL - /* copy mount params to sb for use in submounts */ - /* BB: should we move this after the mount so we - * do not have to do the copy on failed mounts? - * BB: May be it is better to do simple copy before - * complex operation (mount), and in case of fail - * just exit instead of doing mount and attempting - * undo it if this copy fails?*/ + /* + * Copy mount params to sb for use in submounts. Better to do + * the copy here and deal with the error before cleanup gets + * complicated post-mount. + */ if (data) { - int len = strlen(data); - cifs_sb->mountdata = kzalloc(len + 1, GFP_KERNEL); + cifs_sb->mountdata = kstrndup(data, PAGE_SIZE, GFP_KERNEL); if (cifs_sb->mountdata == NULL) { bdi_destroy(&cifs_sb->bdi); kfree(sb->s_fs_info); sb->s_fs_info = NULL; return -ENOMEM; } - strncpy(cifs_sb->mountdata, data, len + 1); - cifs_sb->mountdata[len] = '\0'; } -#endif - rc = cifs_mount(sb, cifs_sb, data, devname); + rc = cifs_mount(sb, cifs_sb, devname); if (rc) { if (!silent) @@ -163,7 +156,7 @@ cifs_read_super(struct super_block *sb, void *data, sb->s_bdi = &cifs_sb->bdi; sb->s_blocksize = CIFS_MAX_MSGSIZE; sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */ - inode = cifs_root_iget(sb, ROOT_I); + inode = cifs_root_iget(sb); if (IS_ERR(inode)) { rc = PTR_ERR(inode); @@ -184,12 +177,12 @@ cifs_read_super(struct super_block *sb, void *data, else sb->s_d_op = &cifs_dentry_ops; -#ifdef CONFIG_CIFS_EXPERIMENTA |