diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-11-12 11:11:39 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-11-12 11:11:39 -0800 |
commit | 46015977e70f672ae6b20a1b5fb1e361208365ba (patch) | |
tree | e3be0785eff90cc8023cd1ea03fc22d3dcf37f41 /fs | |
parent | 92d140e21f1ce8cf99320afbbcad73879128e6dc (diff) | |
parent | 9b8f5f573770f33b28c45255ac82e6457278c782 (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: (21 commits)
[CIFS] fix oops on second mount to same server when null auth is used
[CIFS] Fix stale mode after readdir when cifsacl specified
[CIFS] add mode to acl conversion helper function
[CIFS] Fix incorrect mode when ACL had deny access control entries
[CIFS] Add uid to key description so krb can handle user mounts
[CIFS] Fix walking out end of cifs dacl
[CIFS] Add upcall files for cifs to use spnego/kerberos
[CIFS] add OIDs for KRB5 and MSKRB5 to ASN1 parsing routines
[CIFS] Register and unregister cifs_spnego_key_type on module init/exit
[CIFS] implement upcalls for SPNEGO blob via keyctl API
[CIFS] allow cifs_calc_signature2 to deal with a zero length iovec
[CIFS] If no Access Control Entries, set mode perm bits to zero
[CIFS] when mount helper missing fix slash wrong direction in share
[CIFS] Don't request too much permission when reading an ACL
[CIFS] enable get mode from ACL when cifsacl mount option specified
[CIFS] ACL support part 8
[CIFS] acl support part 7
[CIFS] acl support part 6
[CIFS] acl support part 6
[CIFS] remove unused funtion compile warning when experimental off
...
Diffstat (limited to 'fs')
-rw-r--r-- | fs/Kconfig | 2 | ||||
-rw-r--r-- | fs/cifs/CHANGES | 11 | ||||
-rw-r--r-- | fs/cifs/Makefile | 7 | ||||
-rw-r--r-- | fs/cifs/asn1.c | 35 | ||||
-rw-r--r-- | fs/cifs/cifs_spnego.c | 128 | ||||
-rw-r--r-- | fs/cifs/cifs_spnego.h | 46 | ||||
-rw-r--r-- | fs/cifs/cifsacl.c | 346 | ||||
-rw-r--r-- | fs/cifs/cifsacl.h | 17 | ||||
-rw-r--r-- | fs/cifs/cifsencrypt.c | 9 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 17 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifspdu.h | 40 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 11 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 64 | ||||
-rw-r--r-- | fs/cifs/connect.c | 4 | ||||
-rw-r--r-- | fs/cifs/dir.c | 2 | ||||
-rw-r--r-- | fs/cifs/file.c | 31 | ||||
-rw-r--r-- | fs/cifs/inode.c | 15 | ||||
-rw-r--r-- | fs/cifs/md5.c | 8 | ||||
-rw-r--r-- | fs/cifs/misc.c | 10 | ||||
-rw-r--r-- | fs/cifs/netmisc.c | 54 | ||||
-rw-r--r-- | fs/cifs/readdir.c | 10 | ||||
-rw-r--r-- | fs/cifs/smbencrypt.c | 18 | ||||
-rw-r--r-- | fs/cifs/xattr.c | 11 |
24 files changed, 727 insertions, 171 deletions
diff --git a/fs/Kconfig b/fs/Kconfig index c75c9540649..429a0022850 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -2007,7 +2007,7 @@ config CIFS_EXPERIMENTAL config CIFS_UPCALL bool "Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)" depends on CIFS_EXPERIMENTAL - depends on CONNECTOR + depends on KEYS help Enables an upcall mechanism for CIFS which will be used to contact userspace helper utilities to provide SPNEGO packaged Kerberos diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 3d419163c3d..64dd22239b2 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,7 @@ +Version 1.52 +------------ +Fix oops on second mount to server when null auth is used. + Version 1.51 ------------ Fix memory leak in statfs when mounted to very old servers (e.g. @@ -12,7 +16,12 @@ leak that causes cifsd not to stop and rmmod to fail to cleanup cifs_request_buffers pool. Fix problem with POSIX Open/Mkdir on bigendian architectures. Fix possible memory corruption when EAGAIN returned on kern_recvmsg. Return better error if server -requires packet signing but client has disabled it. +requires packet signing but client has disabled it. When mounted +with cifsacl mount option - mode bits are approximated based +on the contents of the ACL of the file or directory. When cifs +mount helper is missing convert make sure that UNC name +has backslash (not forward slash) between ip address of server +and the share name. Version 1.50 ------------ diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index ff6ba8d823f..45e42fb97c1 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -3,4 +3,9 @@ # obj-$(CONFIG_CIFS) += cifs.o -cifs-objs := 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 md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o readdir.o ioctl.o sess.o export.o cifsacl.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 \ + md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o \ + readdir.o ioctl.o sess.o export.o cifsacl.o + +cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o diff --git a/fs/cifs/asn1.c b/fs/cifs/asn1.c index 2a01f3ef96a..bcda2c6b6a0 100644 --- a/fs/cifs/asn1.c +++ b/fs/cifs/asn1.c @@ -77,8 +77,12 @@ #define SPNEGO_OID_LEN 7 #define NTLMSSP_OID_LEN 10 +#define KRB5_OID_LEN 7 +#define MSKRB5_OID_LEN 7 static unsigned long SPNEGO_OID[7] = { 1, 3, 6, 1, 5, 5, 2 }; static unsigned long NTLMSSP_OID[10] = { 1, 3, 6, 1, 4, 1, 311, 2, 2, 10 }; +static unsigned long KRB5_OID[7] = { 1, 2, 840, 113554, 1, 2, 2 }; +static unsigned long MSKRB5_OID[7] = { 1, 2, 840, 48018, 1, 2, 2 }; /* * ASN.1 context. @@ -457,6 +461,7 @@ decode_negTokenInit(unsigned char *security_blob, int length, unsigned long *oid = NULL; unsigned int cls, con, tag, oidlen, rc; int use_ntlmssp = FALSE; + int use_kerberos = FALSE; *secType = NTLM; /* BB eventually make Kerberos or NLTMSSP the default*/ @@ -545,18 +550,28 @@ decode_negTokenInit(unsigned char *security_blob, int length, return 0; } if ((tag == ASN1_OJI) && (con == ASN1_PRI)) { - rc = asn1_oid_decode(&ctx, end, &oid, &oidlen); - if (rc) { + if (asn1_oid_decode(&ctx, end, &oid, &oidlen)) { + cFYI(1, ("OID len = %d oid = 0x%lx 0x%lx " "0x%lx 0x%lx", oidlen, *oid, *(oid + 1), *(oid + 2), *(oid + 3))); - rc = compare_oid(oid, oidlen, - NTLMSSP_OID, NTLMSSP_OID_LEN); - kfree(oid); - if (rc) + + if (compare_oid(oid, oidlen, + MSKRB5_OID, + MSKRB5_OID_LEN)) + use_kerberos = TRUE; + else if (compare_oid(oid, oidlen, + KRB5_OID, + KRB5_OID_LEN)) + use_kerberos = TRUE; + else if (compare_oid(oid, oidlen, + NTLMSSP_OID, + NTLMSSP_OID_LEN)) use_ntlmssp = TRUE; + + kfree(oid); } } else { cFYI(1, ("Should be an oid what is going on?")); @@ -609,12 +624,10 @@ decode_negTokenInit(unsigned char *security_blob, int length, ctx.pointer)); /* is this UTF-8 or ASCII? */ } - /* if (use_kerberos) - *secType = Kerberos - else */ - if (use_ntlmssp) { + if (use_kerberos) + *secType = Kerberos; + else if (use_ntlmssp) *secType = NTLMSSP; - } return 1; } diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c new file mode 100644 index 00000000000..ad54a3a6e43 --- /dev/null +++ b/fs/cifs/cifs_spnego.c @@ -0,0 +1,128 @@ +/* + * fs/cifs/cifs_spnego.c -- SPNEGO upcall management for CIFS + * + * Copyright (c) 2007 Red Hat, Inc. + * Author(s): Jeff Layton (jlayton@redhat.com) + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/list.h> +#include <linux/string.h> +#include <keys/user-type.h> +#include <linux/key-type.h> +#include "cifsglob.h" +#include "cifs_spnego.h" +#include "cifs_debug.h" + +/* create a new cifs key */ +static int +cifs_spnego_key_instantiate(struct key *key, const void *data, size_t datalen) +{ + char *payload; + int ret; + + ret = -ENOMEM; + payload = kmalloc(datalen, GFP_KERNEL); + if (!payload) + goto error; + + /* attach the data */ + memcpy(payload, data, datalen); + rcu_assign_pointer(key->payload.data, payload); + ret = 0; + +error: + return ret; +} + +static void +cifs_spnego_key_destroy(struct key *key) +{ + kfree(key->payload.data); +} + + +/* + * keytype for CIFS spnego keys + */ +struct key_type cifs_spnego_key_type = { + .name = "cifs.spnego", + .instantiate = cifs_spnego_key_instantiate, + .match = user_match, + .destroy = cifs_spnego_key_destroy, + .describe = user_describe, +}; + +/* get a key struct with a SPNEGO security blob, suitable for session setup */ +struct key * +cifs_get_spnego_key(struct cifsSesInfo *sesInfo, const char *hostname) +{ + struct TCP_Server_Info *server = sesInfo->server; + char *description, *dp; + size_t desc_len; + struct key *spnego_key; + + + /* version + ;ip{4|6}= + address + ;host=hostname + + ;sec= + ;uid= + NULL */ + desc_len = 4 + 5 + 32 + 1 + 5 + strlen(hostname) + + strlen(";sec=krb5") + 7 + sizeof(uid_t)*2 + 1; + spnego_key = ERR_PTR(-ENOMEM); + description = kzalloc(desc_len, GFP_KERNEL); + if (description == NULL) + goto out; + + dp = description; + /* start with version and hostname portion of UNC string */ + spnego_key = ERR_PTR(-EINVAL); + sprintf(dp, "0x%2.2x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION, + hostname); + dp = description + strlen(description); + + /* add the server address */ + if (server->addr.sockAddr.sin_family == AF_INET) + sprintf(dp, "ip4=" NIPQUAD_FMT, + NIPQUAD(server->addr.sockAddr.sin_addr)); + else if (server->addr.sockAddr.sin_family == AF_INET6) + sprintf(dp, "ip6=" NIP6_SEQFMT, + NIP6(server->addr.sockAddr6.sin6_addr)); + else + goto out; + + dp = description + strlen(description); + + /* for now, only sec=krb5 is valid */ + if (server->secType == Kerberos) + sprintf(dp, ";sec=krb5"); + else + goto out; + + dp = description + strlen(description); + sprintf(dp, ";uid=0x%x", sesInfo->linux_uid); + + cFYI(1, ("key description = %s", description)); + spnego_key = request_key(&cifs_spnego_key_type, description, ""); + + if (cifsFYI && !IS_ERR(spnego_key)) { + struct cifs_spnego_msg *msg = spnego_key->payload.data; + cifs_dump_mem("SPNEGO reply blob:", msg->data, + msg->secblob_len + msg->sesskey_len); + } + +out: + kfree(description); + return spnego_key; +} diff --git a/fs/cifs/cifs_spnego.h b/fs/cifs/cifs_spnego.h new file mode 100644 index 00000000000..f443f3b3513 --- /dev/null +++ b/fs/cifs/cifs_spnego.h @@ -0,0 +1,46 @@ +/* + * fs/cifs/cifs_spnego.h -- SPNEGO upcall management for CIFS + * + * Copyright (c) 2007 Red Hat, Inc. + * Author(s): Jeff Layton (jlayton@redhat.com) + * Steve French (sfrench@us.ibm.com) + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _CIFS_SPNEGO_H +#define _CIFS_SPNEGO_H + +#define CIFS_SPNEGO_UPCALL_VERSION 1 + +/* + * The version field should always be set to CIFS_SPNEGO_UPCALL_VERSION. + * The flags field is for future use. The request-key callout should set + * sesskey_len and secblob_len, and then concatenate the SessKey+SecBlob + * and stuff it in the data field. + */ +struct cifs_spnego_msg { + uint32_t version; + uint32_t flags; + uint32_t sesskey_len; + uint32_t secblob_len; + uint8_t data[1]; +}; + +#ifdef __KERNEL__ +extern struct key_type cifs_spnego_key_type; +#endif /* KERNEL */ + +#endif /* _CIFS_SPNEGO_H */ diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index e8e56353f5a..dabbce00712 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -38,13 +38,13 @@ static struct cifs_wksid wksidarr[NUM_WK_SIDS] = { {{1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(18), 0, 0, 0, 0} }, "sys"}, {{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(544), 0, 0, 0} }, "root"}, {{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(545), 0, 0, 0} }, "users"}, - {{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(546), 0, 0, 0} }, "guest"} -}; + {{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(546), 0, 0, 0} }, "guest"} } +; /* security id for everyone */ -static const struct cifs_sid sid_everyone = - {1, 1, {0, 0, 0, 0, 0, 0}, {} }; +static const struct cifs_sid sid_everyone = { + 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; /* group users */ static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} }; @@ -97,7 +97,7 @@ int match_sid(struct cifs_sid *ctsid) /* if the two SIDs (roughly equivalent to a UUID for a user or group) are the same returns 1, if they do not match returns 0 */ -int compare_sids(struct cifs_sid *ctsid, struct cifs_sid *cwsid) +int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) { int i; int num_subauth, num_sat, num_saw; @@ -129,66 +129,142 @@ int compare_sids(struct cifs_sid *ctsid, struct cifs_sid *cwsid) return (1); /* sids compare/match */ } +/* + change posix mode to reflect permissions + pmode is the existing mode (we only want to overwrite part of this + bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 +*/ +static void access_flags_to_mode(__u32 ace_flags, int type, umode_t *pmode, + umode_t *pbits_to_set) +{ + /* the order of ACEs is important. The canonical order is to begin with + DENY entries followed by ALLOW, otherwise an allow entry could be + encountered first, making the subsequent deny entry like "dead code" + which would be superflous since Windows stops when a match is made + for the operation you are trying to perform for your user */ + + /* For deny ACEs we change the mask so that subsequent allow access + control entries do not turn on the bits we are denying */ + if (type == ACCESS_DENIED) { + if (ace_flags & GENERIC_ALL) { + *pbits_to_set &= ~S_IRWXUGO; + } + if ((ace_flags & GENERIC_WRITE) || + ((ace_flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) + *pbits_to_set &= ~S_IWUGO; + if ((ace_flags & GENERIC_READ) || + ((ace_flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) + *pbits_to_set &= ~S_IRUGO; + if ((ace_flags & GENERIC_EXECUTE) || + ((ace_flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) + *pbits_to_set &= ~S_IXUGO; + return; + } else if (type != ACCESS_ALLOWED) { + cERROR(1, ("unknown access control type %d", type)); + return; + } + /* else ACCESS_ALLOWED type */ + + if (ace_flags & GENERIC_ALL) { + *pmode |= (S_IRWXUGO & (*pbits_to_set)); +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("all perms")); +#endif + return; + } + if ((ace_flags & GENERIC_WRITE) || + ((ace_flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) + *pmode |= (S_IWUGO & (*pbits_to_set)); + if ((ace_flags & GENERIC_READ) || + ((ace_flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) + *pmode |= (S_IRUGO & (*pbits_to_set)); + if ((ace_flags & GENERIC_EXECUTE) || + ((ace_flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) + *pmode |= (S_IXUGO & (*pbits_to_set)); + +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("access flags 0x%x mode now 0x%x", ace_flags, *pmode)); +#endif + return; +} + +/* + Generate access flags to reflect permissions mode is the existing mode. + This function is called for every ACE in the DACL whose SID matches + with either owner or group or everyone. +*/ + +static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, + __u32 *pace_flags) +{ + /* reset access mask */ + *pace_flags = 0x0; + + /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ + mode &= bits_to_use; + + /* check for R/W/X UGO since we do not know whose flags + is this but we have cleared all the bits sans RWX for + either user or group or other as per bits_to_use */ + if (mode & S_IRUGO) + *pace_flags |= SET_FILE_READ_RIGHTS; + if (mode & S_IWUGO) + *pace_flags |= SET_FILE_WRITE_RIGHTS; + if (mode & S_IXUGO) + *pace_flags |= SET_FILE_EXEC_RIGHTS; + +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("mode: 0x%x, access flags now 0x%x", mode, *pace_flags)); +#endif + return; +} + -static void parse_ace(struct cifs_ace *pace, char *end_of_acl) +#ifdef CONFIG_CIFS_DEBUG2 +static void dump_ace(struct cifs_ace *pace, char *end_of_acl) { int num_subauth; /* validate that we do not go past end of acl */ - /* XXX this if statement can be removed - if (end_of_acl < (char *)pace + sizeof(struct cifs_ace)) { + if (le16_to_cpu(pace->size) < 16) { + cERROR(1, ("ACE too small, %d", le16_to_cpu(pace->size))); + return; + } + + if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) { cERROR(1, ("ACL too small to parse ACE")); return; - } */ + } - num_subauth = pace->num_subauth; + num_subauth = pace->sid.num_subauth; if (num_subauth) { -#ifdef CONFIG_CIFS_DEBUG2 int i; - cFYI(1, ("ACE revision %d num_subauth %d", - pace->revision, pace->num_subauth)); + cFYI(1, ("ACE revision %d num_auth %d type %d flags %d size %d", + pace->sid.revision, pace->sid.num_subauth, pace->type, + pace->flags, pace->size)); for (i = 0; i < num_subauth; ++i) { cFYI(1, ("ACE sub_auth[%d]: 0x%x", i, - le32_to_cpu(pace->sub_auth[i]))); + le32_to_cpu(pace->sid.sub_auth[i]))); } /* BB add length check to make sure that we do not have huge num auths and therefore go off the end */ - - cFYI(1, ("RID %d", le32_to_cpu(pace->sub_auth[num_subauth-1]))); -#endif } return; } - -static void parse_ntace(struct cifs_ntace *pntace, char *end_of_acl) -{ - /* validate that we do not go past end of acl */ - if (end_of_acl < (char *)pntace + sizeof(struct cifs_ntace)) { - cERROR(1, ("ACL too small to parse NT ACE")); - return; - } - -#ifdef CONFIG_CIFS_DEBUG2 - cFYI(1, ("NTACE type %d flags 0x%x size %d, access Req 0x%x", - pntace->type, pntace->flags, pntace->size, - pntace->access_req)); #endif - return; -} - static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, - struct cifs_sid *pownersid, struct cifs_sid *pgrpsid) + struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, + struct inode *inode) { int i; int num_aces = 0; int acl_size; char *acl_base; - struct cifs_ntace **ppntace; struct cifs_ace **ppace; /* BB need to add parm so we can store the SID BB */ @@ -205,50 +281,63 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, le32_to_cpu(pdacl->num_aces))); #endif + /* reset rwx permissions for user/group/other. + Also, if num_aces is 0 i.e. DACL has no ACEs, + user/group/other have no permissions */ + inode->i_mode &= ~(S_IRWXUGO); + + if (!pdacl) { + /* no DACL in the security descriptor, set + all the permissions for user/group/other */ + inode->i_mode |= S_IRWXUGO; + return; + } acl_base = (char *)pdacl; acl_size = sizeof(struct cifs_acl); num_aces = le32_to_cpu(pdacl->num_aces); if (num_aces > 0) { - ppntace = kmalloc(num_aces * sizeof(struct cifs_ntace *), - GFP_KERNEL); + umode_t user_mask = S_IRWXU; + umode_t group_mask = S_IRWXG; + umode_t other_mask = S_IRWXO; + ppace = kmalloc(num_aces * sizeof(struct cifs_ace *), GFP_KERNEL); /* cifscred->cecount = pdacl->num_aces; - cifscred->ntaces = kmalloc(num_aces * - sizeof(struct cifs_ntace *), GFP_KERNEL); cifscred->aces = kmalloc(num_aces * sizeof(struct cifs_ace *), GFP_KERNEL);*/ for (i = 0; i < num_aces; ++i) { - ppntace[i] = (struct cifs_ntace *) - (acl_base + acl_size); - ppace[i] = (struct cifs_ace *) ((char *)ppntace[i] + - sizeof(struct cifs_ntace)); - - parse_ntace(ppntace[i], end_of_acl); - if (end_of_acl < ((char *)ppace[i] + - (le16_to_cpu(ppntace[i]->size) - - sizeof(struct cifs_ntace)))) { - cERROR(1, ("ACL too small to parse ACE")); - break; - } else - parse_ace(ppace[i], end_of_acl); - -/* memcpy((void *)(&(cifscred->ntaces[i])), - (void *)ppntace[i], - sizeof(struct cifs_ntace)); - memcpy((void *)(&(cifscred->aces[i])), + ppace[i] = (struct cifs_ace *) (acl_base + acl_size); +#ifdef CONFIG_CIFS_DEBUG2 + dump_ace(ppace[i], end_of_acl); +#endif + if (compare_sids(&(ppace[i]->sid), pownersid)) + access_flags_to_mode(ppace[i]->access_req, + ppace[i]->type, + &(inode->i_mode), + &user_mask); + if (compare_sids(&(ppace[i]->sid), pgrpsid)) + access_flags_to_mode(ppace[i]->access_req, + ppace[i]->type, + &(inode->i_mode), + &group_mask); + if (compare_sids(&(ppace[i]->sid), &sid_everyone)) + access_flags_to_mode(ppace[i]->access_req, + ppace[i]->type, + &(inode->i_mode), + &other_mask); + +/* memcpy((void *)(&(cifscred->aces[i])), (void *)ppace[i], sizeof(struct cifs_ace)); */ - acl_base = (char *)ppntace[i]; - acl_size = le16_to_cpu(ppntace[i]->size); + acl_base = (char *)ppace[i]; + acl_size = le16_to_cpu(ppace[i]->size); } kfree(ppace); - kfree(ppntace); } return; @@ -257,20 +346,20 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, static int parse_sid(struct cifs_sid *psid, char *end_of_acl) { - /* BB need to add parm so we can store the SID BB */ - /* validate that we do not go past end of acl */ - if (end_of_acl < (char *)psid + sizeof(struct cifs_sid)) { - cERROR(1, ("ACL too small to parse SID")); + /* validate that we do not go past end of ACL - sid must be at least 8 + bytes long (assuming no sub-auths - e.g. the null SID */ + if (end_of_acl < (char *)psid + 8) { + cERROR(1, ("ACL too small to parse SID %p", psid)); return -EINVAL; } if (psid->num_subauth) { #ifdef CONFIG_CIFS_DEBUG2 int i; - cFYI(1, ("SID revision %d num_auth %d First subauth 0x%x", - psid->revision, psid->num_subauth, psid->sub_auth[0])); + cFYI(1, ("SID revision %d num_auth %d", + psid->revision, psid->num_subauth)); for (i = 0; i < psid->num_subauth; i++) { cFYI(1, ("SID sub_auth[%d]: 0x%x ", i, @@ -289,27 +378,32 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl) /* Convert CIFS ACL to POSIX form */ -int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len) +static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, + struct inode *inode) { int rc; 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; + __u32 dacloffset; + + if ((inode == NULL) || (pntsd == NULL)) + return -EIO; owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + le32_to_cpu(pntsd->osidoffset)); group_sid_ptr = (struct cifs_sid *)((char *)pntsd + le32_to_cpu(pntsd->gsidoffset)); - dacl_ptr = (struct cifs_acl *)((char *)pntsd + - le32_to_cpu(pntsd->dacloffset)); + dacloffset = le32_to_cpu(pntsd->dacloffset); + dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); #ifdef CONFIG_CIFS_DEBUG2 cFYI(1, ("revision %d type 0x%x ooffset 0x%x goffset 0x%x " "sacloffset 0x%x dacloffset 0x%x", pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), le32_to_cpu(pntsd->gsidoffset), - le32_to_cpu(pntsd->sacloffset), - le32_to_cpu(pntsd->dacloffset))); + le32_to_cpu(pntsd->sacloffset), dacloffset)); #endif +/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */ rc = parse_sid(owner_sid_ptr, end_of_acl); if (rc) return rc; @@ -318,16 +412,120 @@ int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len) if (rc) return rc; - parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr); + if (dacloffset) + parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, + group_sid_ptr, inode); + else + cFYI(1, ("no ACL")); /* BB grant all or default perms? */ /* cifscred->uid = owner_sid_ptr->rid; cifscred->gid = group_sid_ptr->rid; memcpy((void *)(&(cifscred->osid)), (void *)owner_sid_ptr, - sizeof (struct cifs_sid)); + sizeof(struct cifs_sid)); memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr, - sizeof (struct cifs_sid)); */ + sizeof(struct cifs_sid)); */ return (0); } + + +/* Retrieve an ACL from the server */ +static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode, + const char *path) +{ + struct cifsFileInfo *open_file; + int unlock_file = FALSE; + int xid; + int rc = -EIO; + __u16 fid; + struct super_block *sb; + struct cifs_sb_info *cifs_sb; + struct cifs_ntsd *pntsd = NULL; + + cFYI(1, ("get mode from ACL for %s", path)); + + if (inode == NULL) + return NULL; + + xid = GetXid(); + open_file = find_readable_file(CIFS_I(inode)); + sb = inode->i_sb; + if (sb == NULL) { + FreeXid(xid); + return NULL; + } + cifs_sb = CIFS_SB(sb); + + if (open_file) { + unlock_file = TRUE; + fid = open_file->netfid; + } else { + int oplock = FALSE; + /* open file */ + rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN, + READ_CONTROL, 0, &fid, &oplock, NULL, + cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + if (rc != 0) { + cERROR(1, ("Unable to open file to get ACL")); + FreeXid(xid); + return NULL; + } + } + + rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, pacllen); + cFYI(1, ("GetCIFSACL rc = %d ACL len %d", rc, *pacllen)); + if (unlock_file == TRUE) + atomic_dec(&open_file->wrtPending); + else + CIFSSMBClose(xid, cifs_sb->tcon, fid); + + FreeXid(xid); + return pntsd; +} + +/* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */ +void acl_to_uid_mode(struct inode *inode, const char *path) +{ + struct cifs_ntsd *pntsd = NULL; + u32 acllen = 0; + int rc = 0; + +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("converting ACL to mode for %s", path)); +#endif + pntsd = get_cifs_acl(&acllen, inode, path); + + /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */ + if (pntsd) + rc = parse_sec_desc(pntsd, acllen, inode); + if (rc) + cFYI(1, ("parse sec desc failed rc = %d", rc)); + + kfree(pntsd); + return; +} + +/* Convert mode bits to an ACL so we can update the ACL on the server */ +int mode_to_acl(struct inode *inode, const char *path) +{ + int rc = 0; + __u32 acllen = 0; + struct cifs_ntsd *pntsd = NULL; + + cFYI(1, ("set ACL from mode for %s", path)); + + /* Get the security descriptor */ + pntsd = get_cifs_acl(&acllen, inode, path); + + /* Add/Modify the three ACEs for owner, group, everyone + while retaining the other ACEs */ + + /* Set the security descriptor */ + + + kfree(pntsd); + return rc; +} #endif /* CONFIG_CIFS_EXPERIMENTAL */ diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h index 420f8781364..93a7c3462ea 100644 --- a/fs/cifs/cifsacl.h +++ b/fs/cifs/cifsacl.h @@ -35,6 +35,9 @@ #define UBITSHIFT 6 #define GBITSHIFT 3 +#define ACCESS_ALLOWED 0 +#define ACCESS_DENIED 1 + struct cifs_ntsd { __le16 revision; /* revision level */ __le16 type; @@ -48,7 +51,7 @@ struct cifs_sid { __u8 revision; /* revision level */ __u8 num_subauth; __u8 authority[6]; - __le32 sub_auth[5]; /* sub_auth[num_subauth] */ /* BB FIXME endianness BB */ + __le32 sub_auth[5]; /* sub_auth[num_subauth] */ } __attribute__((packed)); struct cifs_acl { @@ -57,18 +60,12 @@ struct cifs_acl { __le32 num_aces; } __attribute__((packed)); -struct cifs_ntace { /* first part of ACE which contains perms */ +struct cifs_ace { __u8 type; __u8 flags; __le16 size; __le32 access_req; -} __attribute__((packed)); - -struct cifs_ace { /* last part of ACE which includes user info */ - __u8 revision; /* revision level */ - __u8 num_subauth; - __u8 authority[6]; - __le32 sub_auth[5]; + struct cifs_sid sid; /* ie UUID of user or group who gets these perms */ } __attribute__((packed)); struct cifs_wksid { @@ -79,7 +76,7 @@ struct cifs_wksid { #ifdef CONFIG_CIFS_EXPERIMENTAL extern int match_sid(struct cifs_sid *); -extern int compare_sids(struct cifs_sid *, struct cifs_sid *); +extern int compare_sids(const struct cifs_sid *, const struct cifs_sid *); #endif /* CONFIG_CIFS_EXPERIMENTAL */ diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 632070b4275..4ff8939c6cc 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -99,15 +99,16 @@ static int cifs_calc_signature2(const struct kvec *iov, int n_vec, MD5Init(&context); MD5Update(&context, (char *)&key->data, key->len); for (i = 0; i < n_vec; i++) { + if (iov[i].iov_len == 0) + continue; if (iov[i].iov_base == NULL) { cERROR(1, ("null iovec entry")); return -EIO; - } else if (iov[i].iov_len == 0) - break; /* bail out if we are sent nothing to sign */ + } /* The first entry includes a length field (which does not get signed that occupies the first 4 bytes before the header */ if (i == 0) { - if (iov[0].iov_len <= 8 ) /* cmd field at offset 9 */ + if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ break; /* nothing to sign or corrupt header */ MD5Update(&context, iov[0].iov_base+4, iov[0].iov_len-4); @@ -122,7 +123,7 @@ static int cifs_calc_signature2(const struct kvec *iov, int n_vec, int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, - __u32 * pexpected_response_sequence_number) + __u32 *pexpected_response_sequence_number) { int rc = 0; char smb_signature[20]; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index a6fbea57c4b..416dc9fe896 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -43,6 +43,8 @@ #include "cifs_debug.h" #include "cifs_fs_sb.h" #include <linux/mm.h> +#include <linux/key-type.h> +#include "cifs_spnego.h" #define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */ #ifdef CONFIG_CIFS_QUOTA @@ -1005,12 +1007,16 @@ init_cifs(void) rc = register_filesystem(&cifs_fs_type); if (rc) goto out_destroy_request_bufs; - +#ifdef CONFIG_CIFS_UPCALL + rc = register_key_type(&cifs_spnego_key_type); + if (rc) + goto out_unregister_filesystem; +#endif oplockThread = kthread_run(cifs_oplock_thread, NULL, "cifsoplockd"); if (IS_ERR(oplockThread)) { rc = PTR_ERR(oplockThread); cERROR(1, ("error %d create oplock thread", rc)); - goto out_unregister_filesystem; + goto out_unregister_key_type; } dnotifyThread = kthread_run(cifs_dnotify_thread, NULL, "cifsdnotifyd"); @@ -1024,7 +1030,11 @@ init_cifs(void) out_stop_oplock_thread: kthread_stop(oplockThread); + out_unregister_key_type: +#ifdef CONFIG_CIFS_UPCALL + unregister_key_type(&cifs_spnego_key_type); out_unregister_filesystem: +#endif unregister_filesystem(&cifs_fs_type); out_destroy_request_bufs: cifs_destroy_request_bufs(); @@ -1046,6 +1056,9 @@ exit_cifs(void) #ifdef CONFIG_PROC_FS cifs_proc_clean(); #endif +#ifdef CONFIG_CIFS_UPCALL + unregister_key_type(&cifs_spnego_key_type); +#endif unregister_filesystem(&cifs_fs_type); cifs_destroy_inodecache(); cifs_destroy_mids(); diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 5574ba3ab1f..2a21dc66f0d 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -106,5 +106,5 @@ extern int cifs_ioctl(struct inode *inode, struct file *filep, extern const struct export_operations cifs_export_ops; #endif /* EXPERIMENTAL */ -#define CIFS_VERSION "1.51" +#define CIFS_VERSION "1.52" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index c41ff74e912..dbe6b846f37 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -220,6 +220,23 @@ | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) #define FILE_EXEC_RIGHTS (FILE_EXECUTE) +#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_READ_EA | FILE_WRITE_EA \ + | FILE_DELETE_CHILD | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) + /* * Invalid readdir handle @@ -1211,6 +1228,29 @@ typedef struct smb_com_transaction_qsec_req { __le32 AclFlags; } __attribute__((packed)) QUERY_SEC_DESC_REQ; + +typedef struct smb_com_transaction_ssec_req { + struct smb_hdr hdr; /* wct = 19 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* no setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 3 = SET_SECURITY_DESC */ + __le16 ByteCount; /* bcc = 3 + 8 */ + __u8 Pad[3]; + __u16 Fid; + __u16 Reserved2; + __le32 AclFlags; +} __attribute__((packed)) SET_SEC_DESC_REQ; + typedef struct smb_com_transaction_change_notify_req { struct smb_hdr hdr; /* wct = 23 */ __u8 MaxSetupCount; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 1a883663b22..dd1d7c200ee 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -61,6 +61,9 @@ extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); extern int is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *); extern int is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *); +#ifdef CONFIG_CIFS_EXPERIMENTAL +extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *); +#endif extern unsigned int smbCalcSize(struct smb_hdr *ptr); extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); extern int decode_negTokenInit(unsigned char *security_blob, int length, @@ -73,6 +76,8 @@ extern void header_assemble(struct smb_hdr *, char /* command */ , extern int small_smb_init_no_tc(const int smb_cmd, const int wct, struct cifsSesInfo *ses, void **request_buf); +extern struct key *cifs_get_spnego_key(struct cifsSesInfo *sesInfo, + const char *hostname); extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, const int stage, const struct nls_table *nls_cp); @@ -92,6 +97,8 @@ extern int cifs_get_inode_info(struct inode **pinode, extern int cifs_get_inode_info_unix(struct inode **pinode, const unsigned char *search_path, struct super_block *sb, int xid); +extern void acl_to_uid_mode(struct inode *inode, const char *search_path); +extern int mode_to_acl(struct inode *inode, const char *path); extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, const char *); @@ -311,7 +318,6 @@ extern void setup_ntlmv2_rsp(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 parse_sec_desc(struct cifs_ntsd *, int); extern int CIFSSMBCopy(int xid, struct cifsTconInfo *source_tcon, const char *fromName, @@ -336,8 +342,7 @@ extern int CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const void *ea_value, const __u16 ea_value_len, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, - __u16 fid, char *acl_inf, const int buflen, - const int acl_type /* ACCESS vs. DEFAULT */); + __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen); extern int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char *acl_inf, const int buflen, const int acl_type, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f0d9a485d09..59d7b7c037a 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -647,8 +647,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) count - 16, &server->secType); if (rc == 1) { - /* BB Need to fill struct for sessetup here */ - rc = -EOPNOTSUPP; + rc = 0; } else { rc = -EINVAL; } @@ -2486,6 +2485,7 @@ querySymLinkRetry: return rc; } +#ifdef CONFIG_CIFS_EXPERIMENTAL /* Initialize NT TRANSACT SMB into small smb request buffer. This assumes that all NT TRANSACTS that we init here have total parm and data under about 400 bytes (to fit in small cifs @@ -2494,7 +2494,7 @@ querySymLinkRetry: MaxSetupCount (size of returned setup area) and MaxParameterCount (returned parms size) must be set by caller */ static int -smb_init_ntransact(const __u16 sub_command, const int setup_count, +smb_init_nttransact(const __u16 sub_command, const int setup_count, const int parm_len, struct cifsTconInfo *tcon, void **ret_buf) { @@ -2525,12 +2525,15 @@ smb_init_ntransact(const __u16 sub_command, const int setup_count, static int validate_ntransact(char *buf, char **ppparm, char **ppdata, - int *pdatalen, int *pparmlen) + __u32 *pparmlen, __u32 *pdatalen) { char *end_of_smb; __u32 data_count, data_offset, parm_count, parm_offset; struct smb_com_ntransact_rsp *pSMBr; + *pdatalen = 0; + *pparmlen = 0; + if (buf == NULL) return -EINVAL; @@ -2567,8 +2570,11 @@ validate_ntransact(char *buf, char **ppparm, char **ppdata, cFYI(1, ("parm count and data count larger than SMB")); return -EINVAL; } + *pdatalen = data_count; + *pparmlen = parm_count; return 0; } +#endif /* CIFS_EXPERIMENTAL */ int CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, @@ -3067,8 +3073,7 @@ GetExtAttrOut: /* Get Security Descriptor (by handle) from remote server for a file or dir */ int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, - /* BB fix up return info */ char *acl_inf, const int buflen, - const int acl_type) + struct cifs_ntsd **acl_inf, __u32 *pbuflen) { int rc = 0; int buf_type = 0; @@ -3077,7 +3082,10 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, cFYI(1, ("GetCifsACL")); - rc = smb_init_ntransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, + *pbuflen = 0; + *acl_inf = NULL; + + rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, 8 /* parm len */, tcon, (void **) &pSMB); if (rc) return rc; @@ -3099,34 +3107,52 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, if (rc) { cFYI(1, ("Send error in QuerySecDesc = %d", rc)); } else { /* decode response */ - struct cifs_ntsd *psec_desc; __le32 * parm; - int parm_len; - int data_len; - int acl_len; + __u32 parm_len; + __u32 acl_len; struct smb_com_ntransact_rsp *pSMBr; + char *pdata; /* validate_nttransact */ rc = validate_ntransact(iov[0].iov_base, (char **)&parm, - (char **)&psec_desc, - &parm_len, &data_len); + &pdata, &parm_len, pbuflen); if (rc) goto qsec_out; pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base; - cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, psec_desc)); + cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, *acl_inf)); if (le32_to_cpu(pSMBr->ParameterCount) != 4) { rc = -EIO; /* bad smb */ + *pbuflen = 0; goto qsec_out; } /* BB check that data area is minimum length and as big as acl_len */ acl_len = le32_to_cpu(*parm); - /* BB check if (acl_len > bufsize) */ + if (acl_len != *pbuflen) { + cERROR(1, ("acl length %d does not match %d", + acl_len, *pbuflen)); + if (*pbuflen > acl_len) + *pbuflen = acl_len; + } - parse_sec_desc(psec_desc, acl_len); + /* check if buffer is big enough for the acl + header followed by the smallest SID */ + if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) || + (*pbuflen >= 64 * 1024)) { + cERROR(1, ("bad acl length %d", *pbuflen)); + rc = -EINVAL; + *pbuflen = 0; + } else { + *acl_inf = kmalloc(*pbuflen, GFP_KERNEL); + if (*acl_inf == NULL) { + *pbuflen = 0; + rc = -ENOMEM; + } + memcpy(*acl_inf, pdata, *pbuflen); + } } qsec_out: if (buf_type == CIFS_SMALL_BUFFER) @@ -3381,7 +3407,7 @@ UnixQPathInfoRetry: memcpy((char *) pFindData, (char *) &pSMBr->hdr.Protocol + data_offset, - sizeof (FILE_UNIX_BASIC_INFO)); + sizeof(FILE_UNIX_BASIC_INFO)); } } cifs_buf_release(pSMB); @@ -3649,7 +3675,7 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT); pSMB->SearchHandle = searchHandle; /* always kept as le */ pSMB->SearchCount = - cpu_to_le16(CIFSMaxBufSize / sizeof (FILE_UNIX_INFO)); + cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO)); pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); pSMB->ResumeKey = psrch_inf->resume_key; pSMB->SearchFlags = @@ -4331,7 +4357,7 @@ QFSDeviceRetry: } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc || (pSMBr->ByteCount < sizeof (FILE_SYSTEM_DEVICE_INFO))) + if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO))) rc = -EIO; /* bad smb */ else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 19ee11f7f35..1102160f666 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -793,7 +793,7 @@ cifs_parse_mount_options(char *options, const char *devname, vol->linux_gid = current->gid; vol->dir_mode = S_IRWXUGO; /* 2767 perms indicate mandatory locking support */ - vol->file_mode = S_IALLUGO & ~(S_ISUID | S_IXGRP); + vol->file_mode = (S_IRWXUGO | S_ISGID) & (~S_IXGRP); /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */ vol->rw = TRUE; @@ -1790,7 +1790,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, if (volume_info.nullauth) { cFYI(1, ("null user")); - volume_info.username = NULL; + volume_info.username = ""; } else if (volume_info.username) { /* BB fixme parse for domain name here */ cFYI(1, ("Username: %s", volume_info.username)); diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 793404b1092..37dc97af148 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -593,7 +593,7 @@ static int cifs_ci_compare(struct dentry *dentry, struct qstr *a, * case take precedence. If a is not a negative dentry, this * should have no side effects */ - memcpy((unsigned char *)a->name, b->name, a->len); + memcpy(a->name, b->name, a->len); return 0; } return 1; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 1e7e4c06d9e..68ad4ca0cfa 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1026,6 +1026,37 @@ static ssize_t cifs_write(struct file *file, const char *write_data, return total_written; } +#ifdef CONFIG_CIFS_EXPERIMENTAL +struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode) +{ + struct cifsFileInfo *open_file = NULL; + + read_lock(&GlobalSMBSeslock); + /* we could simply get the first_list_entry since write-only entries + are always at the end of the list but since the first entry might + have a close pending, we go through the whole list */ + list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { + if (open_file->closePend) + continue; + if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || + (open_file->pfile->f_flags & O_RDONLY))) { + if (!open_file->invalidHandle) { + /* found a good file */ + /* lock it so it will not be closed on us */ + atomic_inc(&open_file->wrtPending); + read_unlock(&GlobalSMBSeslock); + return open_file; + } /* else might as well continue, and look for + another, or simply have the caller reopen it + again rather than trying to fix this handle */ + } else /* write only file */ + break; /* write only files are last so must be done */ + } + read_unlock(&GlobalSMBSeslock); + return NULL; +} +#endif + struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) { struct cifsFileInfo *open_file; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 5e8b388be3b..7d907e84e03 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -289,7 +289,7 @@ static int decode_sfu_inode(struct inode *inode, __u64 size, #define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */ -static int get_sfu_uid_mode(struct inode *inode, +static int get_sfu_mode(struct inode *inode, const unsigned char *path, struct cifs_sb_info *cifs_sb, int xid) { @@ -527,11 +527,16 @@ int cifs_get_inode_info(struct inode **pinode, /* BB fill in uid and gid here? with help from winbind? or retrieve from NTFS stream extended attribute */ +#ifdef CONFIG_CIFS_EXPERIMENTAL + /* fill in 0777 bits from ACL */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { + cFYI(1, ("Getting mode bits from ACL")); + acl_to_uid_mode(inode, search_path); + } +#endif if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { - /* fill in uid, gid, mode from server ACL */ - /* BB FIXME this should also take into account the - * default uid specified on mount if present */ - get_sfu_uid_mode(inode, search_path, cifs_sb, xid); + /* fill in remaining high mode bits e.g. SUID, VTX */ + get_sfu_mode(inode, search_path, cifs_sb, xid); } else if (atomic_read(&cifsInfo->inUse) == 0) { inode->i_uid = cifs_sb->mnt_uid; inode->i_gid = cifs_sb->mnt_gid; diff --git a/fs/cifs/md5.c b/fs/cifs/md5.c index e5c3e121269..f13f96d42fc 100644 --- a/fs/cifs/md5.c +++ b/fs/cifs/md5.c @@ -276,8 +276,8 @@ hmac_md5_init_rfc2104(unsigned char *key, int key_len, } /* start out by storing key in pads */ - memset(ctx->k_ipad, 0, sizeof (ctx->k_ipad)); - memset(ctx->k_opad, 0, sizeof (ctx->k_opad)); + memset(ctx->k_ipad, 0, sizeof(ctx->k_ipad)); + memset(ctx->k_opad, 0, sizeof(ctx->k_opad)); memcpy(ctx->k_ipad, key, key_len); memcpy(ctx->k_opad, key, key_len); @@ -307,8 +307,8 @@ hmac_md5_init_limK_to_64(const unsigned char *key, int key_len, } /* start out by storing key in pads */ - memset(ctx->k_ipad, 0, sizeof (ctx->k_ipad)); - memset(ctx->k_opad, 0, sizeof (ctx->k_opad)); + memset(ctx->k_ipad, 0, sizeof(ctx->k_ipad)); + memset(ctx->k_opad, 0, sizeof(ctx->k_opad)); memcpy(ctx->k_ipad, key, key_len); memcpy(ctx->k_opad, key, key_len); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 51ec681fe74..15546c2354c 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -73,7 +73,7 @@ sesInfoAlloc(void) { struct cifsSesInfo *ret_buf; - ret_buf = kzalloc(sizeof (struct cifsSesInfo), GFP_KERNEL); + ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL); if (ret_buf) { write_lock(&GlobalSMBSeslock); atomic_inc(&sesInfoAllocCount); @@ -109,7 +109,7 @@ struct cifsTconInfo * tconInfoAlloc(void) { struct cifsTconInfo *ret_buf; - ret_buf = kzalloc(sizeof (struct cifsTconInfo), GFP_KERNEL); + ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL); if (ret_buf) { write_lock(&GlobalSMBSeslock); atomic_inc(&tconInfoAllocCount); @@ -298,7 +298,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ buffer->smb_buf_length = - (2 * word_count) + sizeof (struct smb_hdr) - + (2 * word_count) + sizeof(struct smb_hdr) - 4 /* RFC 1001 length field does not count */ + 2 /* for bcc field itself */ ; /* Note that this is the only network field that has to be converted @@ -422,8 +422,8 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length) __u32 clc_len; /* calculated length */ cFYI(0, ("checkSMB Length: 0x%x, smb_buf_length: 0x%x", length, len)); - if (length < 2 + sizeof (struct smb_hdr)) { - if ((length >= sizeof (struct smb_hdr) - 1) + if (length < 2 + sizeof(struct smb_hdr)) { + if ((length >= sizeof(struct smb_hdr) - 1) && (smb->Status.CifsError != 0)) { smb->WordCount = 0; /* some error cases do not return wct and bcc */ diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index f06359cb22e..646e1f06941 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -132,6 +132,34 @@ static const struct smb_to_posix_error mapping_table_ERRHRD[] = { {0, 0} }; + +/* if the mount helper is missing we need to reverse the 1st slash + from '/' to backslash in order to format the UNC properly for + ip address parsing and for tree connect (unless the user + remembered to put the UNC name in properly). Fortunately we do + not have to call this twice (we check for IPv4 addresses + first, so it is already converted by the time we + try IPv6 addresses */ +static int canonicalize_unc(char *cp) +{ + int i; + + for (i = 0; i <= 46 /* INET6_ADDRSTRLEN */ ; i++) { + if (cp[i] == 0) + break; + if (cp[i] == '\\') + break; + if (cp[i] == '/') { +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("change slash to backslash in malformed UNC")); +#endif + cp[i] = '\\'; + return 1; + } + } + return 0; +} + /* Convert string containing dotted ip address to binary form */ /* returns 0 if invalid address */ @@ -141,11 +169,13 @@ cifs_inet_pton(int address_family, char *cp, void *dst) int ret = 0; /* calculate length by finding first slash or NULL */ - /* BB Should we convert '/' slash to '\' here since it seems already - * done before this */ - if ( address_family == AF_INET ) { - ret = in4_pton(cp, -1 /* len */, dst , '\\', NULL); - } else if ( address_family == AF_INET6 ) { + if (address_family == AF_INET) { + ret = in4_pton(cp, -1 /* len */, dst, '\\', NULL); + if (ret == 0) { + if (canonicalize_unc(cp)) + ret = in4_pton(cp, -1, dst, '\\', NULL); + } + } else if (address_family == AF_INET6) { ret = in6_pton(cp, -1 /* len */, dst , '\\', NULL); } #ifdef CONFIG_CIFS_DEBUG2 @@ -740,7 +770,7 @@ cifs_print_status(__u32 status_code) static void -ntstatus_to_dos(__u32 ntstatus, __u8 * eclass, __u16 * ecode) +ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode) { int i; if (ntstatus == 0) { @@ -793,8 +823,8 @@ map_smb_to_linux_error(struct smb_hdr *smb, int logErr) if (smberrclass == ERRDOS) { /* 1 byte field no need to byte reverse */ for (i = 0; i < - sizeof (mapping_table_ERRDOS) / - sizeof (struct smb_to_posix_error); i++) { + sizeof(mapping_table_ERRDOS) / + sizeof(struct smb_to_posix_error); i++) { if (mapping_table_ERRDOS[i].smb_err == 0) break; else if (mapping_table_ERRDOS[i].smb_err == @@ -807,8 +837,8 @@ map_smb_to_linux_error(struct smb_hdr *smb, int logErr) } else if (smberrclass == ERRSRV) { /* server class of error codes */ for (i = 0; i < - sizeof (mapping_table_ERRSRV) / - sizeof (struct smb_to_posix_error); i++) { + sizeof(mapping_table_ERRSRV) / + sizeof(struct smb_to_posix_error); i++) { if (mapping_table_ERRSRV[i].smb_err == 0) break; else if (mapping_table_ERRSRV[i].smb_err == @@ -837,14 +867,14 @@ map_smb_to_linux_error(struct smb_hdr *smb, int logErr) unsigned int smbCalcSize(struct smb_hdr *ptr) { - return (sizeof (struct smb_hdr) + (2 * ptr->WordCount) + + return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + 2 /* size of the bcc field */ + BCC(ptr)); } unsigned int smbCalcSize_LE(struct smb_hdr *ptr) { - return (sizeof (struct smb_hdr) + (2 * ptr->WordCount) + + return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + 2 /* size of the bcc field */ + le16_to_cpu(BCC_LE(ptr))); } diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 3746580e970..0f22def4bdf 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -171,7 +171,13 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, /* Linux can not store file creation time unfortunately so ignore it */ cifsInfo->cifsAttrs = attr; - cifsInfo->time = jiffies; +#ifdef CONFIG_CIFS_EXPERIMENTAL + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { + /* get more accurate mode via ACL - so force inode refresh */ + cifsInfo->time = 0; + } else +#endif /* CONFIG_CIFS_EXPERIMENTAL */ + cifsInfo->time = jiffies; /* treat dos attribute of read-only as read-only mode bit e.g. 555? */ /* 2767 perms - indicate mandatory locking */ @@ -495,7 +501,7 @@ ffirst_retry: static int cifs_unicode_bytelen(char *str) { int len; - __le16 * ustr = (__le16 *)str; + __le16 *ustr = (__le16 *)str; for (len = 0; len <= PATH_MAX; len++) { if (ustr[len] == 0) diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c index 90542a39be1..58bbfd992cc 100644 --- a/fs/cifs/smbencrypt.c +++ b/fs/cifs/smbencrypt.c @@ -80,7 +80,7 @@ SMBencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24) /* Routines for Windows NT MD4 Hash functions. */ static int -_my_wcslen(__u16 * str) +_my_wcslen(__u16 *str) { int len = 0; while (*str++ != 0) @@ -96,7 +96,7 @@ _my_wcslen(__u16 * str) */ static int -_my_mbstowcs(__u16 * dst, const unsigned char *src, int len) +_my_mbstowcs(__u16 *dst, const unsigned char *src, int len) { /* BB not a very good conversion routine - change/fix */ int i; __u16 val; @@ -125,9 +125,9 @@ E_md4hash(const unsigned char *passwd, unsigned char *p16) /* Password cannot be longer than 128 characters */ if (passwd) { len = strlen((char *) passwd); - if (len > 128) { + if (len > 128) len = 128; - } + /* Password must be converted to NT unicode */ _my_mbstowcs(wpwd, passwd, len); } else @@ -135,7 +135,7 @@ E_md4hash(const unsigned char *passwd, unsigned char *p16) wpwd[len] = 0; /* Ensure string is null terminated */ /* Calculate length in bytes */ - len = _my_wcslen(wpwd) * sizeof (__u16); + len = _my_wcslen(wpwd) * sizeof(__u16); mdfour(p16, (unsigned char *) wpwd, len); memset(wpwd, 0, 129 * 2); @@ -167,7 +167,7 @@ nt_lm_owf_gen(char *pwd, unsigned char nt_p16[16], unsigned char p16[16]) E_P16((unsigned char *) passwd, (unsigned char *) p16); /* clear out local copy of user's password (just being paranoid). */ - memset(passwd, '\0', sizeof (passwd)); + memset(passwd, '\0', sizeof(passwd)); } #endif @@ -189,8 +189,10 @@ ntv2_owf_gen(const unsigned char owf[16], const char *user_n, return; dom_u = user_u + 1024; - /* push_ucs2(NULL, user_u, user_n, (user_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); - push_ucs2(NULL, dom_u, domain_n, (domain_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); */ + /* push_ucs2(NULL, user_u, user_n, (user_l+1)*2, + STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); + push_ucs2(NULL, dom_u, domain_n, (domain_l+1)*2, + STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); */ /* BB user and domain may need to be uppercased */ user_l = cifs_strtoUCS(user_u, user_n, 511, nls_codepage); diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index 369e838bebd..54e8ef96cb7 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -265,7 +265,9 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, else if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { __u16 fid; int oplock = FALSE; - if (experimEnabled) + struct cifs_ntsd *pacl = NULL; + __u32 buflen = 0; + if (experimEnabled) rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ, 0, &fid, &oplock, NULL, cifs_sb->local_nls, @@ -273,10 +275,9 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, CIFS_MOUNT_MAP_SPECIAL_CHR); /* else rc is EOPNOTSUPP from above */ - if(rc == 0) { - rc = CIFSSMBGetCIFSACL(xid, pTcon, fid, - ea_value, buf_size, - ACL_TYPE_ACCESS); + if (rc == 0) { + rc = CIFSSMBGetCIFSACL(xid, pTcon, fid, &pacl, + &buflen); CIFSSMBClose(xid, pTcon, fid); } } |