From 4879b44829d94a1f8facf90cced3c5f23c5a8c62 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 19 Oct 2007 21:57:39 +0000 Subject: [PATCH 01/21] [CIFS] ACL support part 5 Acked-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 23 +++++++++++++++++++++++ fs/cifs/cifsproto.h | 2 +- fs/cifs/inode.c | 6 ++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index e8e56353f5a1..e8083043a26c 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -129,6 +129,29 @@ int compare_sids(struct cifs_sid *ctsid, struct cifs_sid *cwsid) return (1); /* sids compare/match */ } +void get_mode_from_acl(struct inode * inode, const char * path) +{ + + if (inode == NULL) + return; + + /* find an open readable handle + if handle found + lock handle + else open file + if no open file can not hurt to check if path is null + GetCIFSACL + for all ACEs in ACL { + if U or G or O + inode->i_mode = parse_ace(file_type, UG or O, ace->perms, inode->i_mode) + else continue + } + if handle open close it + else unlock handle */ + + return; +} + static void parse_ace(struct cifs_ace *pace, char *end_of_acl) { diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 1a883663b22d..7c445f8f233f 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -92,7 +92,7 @@ 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 get_mode_from_acl(struct inode * inode, const char * search_path); extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, const char *); extern int cifs_umount(struct super_block *, struct cifs_sb_info *); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 5e8b388be3b6..9a5c0c925bab 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -527,6 +527,12 @@ 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 + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { + cFYI(1, ("Getting mode bits from ACL")); + get_mode_from_acl(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 From c94897790e7c67dcfe3a0b6f035996398c268313 Mon Sep 17 00:00:00 2001 From: Parag Warudkar Date: Tue, 23 Oct 2007 18:09:48 +0000 Subject: [PATCH 02/21] [CIFS] remove unused funtion compile warning when experimental off get rid of couple of unused function warnings which show up when CONFIG_CIFS_EXPERIMENTAL is not defined - wrap them in #ifdef CONFIG_CIFS_EXPERIMENTAL. Patch against current git. Signed-off-by: Parag Warudkar Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f0d9a485d095..61d24f6ee64e 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2486,6 +2486,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 @@ -2569,6 +2570,7 @@ validate_ntransact(char *buf, char **ppparm, char **ppdata, } return 0; } +#endif /* CIFS_EXPERIMENTAL */ int CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, From 44093ca2fef3c52dc7d186116862d74f9a676e0f Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 23 Oct 2007 21:22:55 +0000 Subject: [PATCH 03/21] [CIFS] acl support part 6 CC: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 77 ++++++++++++++--------------------------------- fs/cifs/cifsacl.h | 12 ++------ fs/cifs/dir.c | 2 +- 3 files changed, 27 insertions(+), 64 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index e8083043a26c..154cb8449b9b 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -38,8 +38,8 @@ 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 */ @@ -131,6 +131,8 @@ int compare_sids(struct cifs_sid *ctsid, struct cifs_sid *cwsid) void get_mode_from_acl(struct inode * inode, const char * path) { + + cFYI(1, ("get mode from ACL for %s", path)); if (inode == NULL) return; @@ -159,50 +161,36 @@ static void parse_ace(struct cifs_ace *pace, char *end_of_acl) /* 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) @@ -211,7 +199,6 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, 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 */ @@ -233,45 +220,27 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, num_aces = le32_to_cpu(pdacl->num_aces); if (num_aces > 0) { - ppntace = kmalloc(num_aces * sizeof(struct cifs_ntace *), - GFP_KERNEL); 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)); + ppace[i] = (struct cifs_ace *) (acl_base + acl_size); - 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); + parse_ace(ppace[i], end_of_acl); -/* memcpy((void *)(&(cifscred->ntaces[i])), - (void *)ppntace[i], - sizeof(struct cifs_ntace)); - memcpy((void *)(&(cifscred->aces[i])), +/* 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; @@ -292,8 +261,8 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl) 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, diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h index 420f87813647..06d52006bf26 100644 --- a/fs/cifs/cifsacl.h +++ b/fs/cifs/cifsacl.h @@ -48,7 +48,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 +57,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 { diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 793404b10925..37dc97af1487 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; From 630f3f0c45a80ab907d216191ef4a205c249fa1b Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 25 Oct 2007 21:17:17 +0000 Subject: [PATCH 04/21] [CIFS] acl support part 6 Acked-by: Shirish Pargaonkar CC: Cyrill Gorcunov Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 91 ++++++++++++++++++++++++++++++++++---------- fs/cifs/cifsproto.h | 9 +++-- fs/cifs/cifssmb.c | 55 ++++++++++++++++++-------- fs/cifs/file.c | 31 +++++++++++++++ fs/cifs/inode.c | 2 +- fs/cifs/md5.c | 8 ++-- fs/cifs/misc.c | 10 ++--- fs/cifs/netmisc.c | 12 +++--- fs/cifs/smbencrypt.c | 4 +- fs/cifs/xattr.c | 7 ++-- 10 files changed, 169 insertions(+), 60 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 154cb8449b9b..14200bd45b30 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -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,28 +129,77 @@ int compare_sids(struct cifs_sid *ctsid, struct cifs_sid *cwsid) return (1); /* sids compare/match */ } -void get_mode_from_acl(struct inode * inode, const char * path) +/* + 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 access_flags, umode_t * pmode, + umode_t bits_to_set) { +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("access flags 0x%x mode now 0x%x", access_flags, *pmode); +#endif + + return; +} + +/* 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 cifsFileInfo *open_file; + int unlock_file = FALSE; + int xid; + int rc = -EIO; + __u16 fid; + struct super_block *sb; + struct cifs_sb_info *cifs_sb; + cFYI(1, ("get mode from ACL for %s", path)); if (inode == NULL) return; - /* find an open readable handle - if handle found - lock handle - else open file - if no open file can not hurt to check if path is null - GetCIFSACL - for all ACEs in ACL { - if U or G or O - inode->i_mode = parse_ace(file_type, UG or O, ace->perms, inode->i_mode) - else continue - } - if handle open close it - else unlock handle */ + xid = GetXid(); + open_file = find_readable_file(CIFS_I(inode)); + if (open_file) { + unlock_file = TRUE; + fid = open_file->netfid; + } else { + int oplock = FALSE; + /* open file */ + sb = inode->i_sb; + if (sb == NULL) { + FreeXid(xid); + return; + } + cifs_sb = CIFS_SB(sb); + rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN, + GENERIC_READ, 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; + } + } + /* rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, pntsd, acllen, + ACL_TYPE_ACCESS); */ + + if (unlock_file == TRUE) + atomic_dec(&open_file->wrtPending); + else + CIFSSMBClose(xid, cifs_sb->tcon, fid); + +/* parse ACEs e.g. + rc = parse_sec_desc(pntsd, acllen, inode); +*/ + + FreeXid(xid); return; } @@ -193,7 +242,8 @@ static void parse_ace(struct cifs_ace *pace, char *end_of_acl) 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; @@ -281,7 +331,8 @@ 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; @@ -310,14 +361,14 @@ 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); + parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr, inode); /* 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); diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 7c445f8f233f..88c02ac97c3f 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, @@ -92,7 +95,7 @@ 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 get_mode_from_acl(struct inode * inode, const char * search_path); +extern void acl_to_uid_mode(struct inode *inode, const char *search_path); extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, const char *); extern int cifs_umount(struct super_block *, struct cifs_sb_info *); @@ -311,7 +314,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 +338,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 61d24f6ee64e..cc17e98991f3 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2526,12 +2526,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 *pdatalen, __u32 *pparmlen) { 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; @@ -2568,6 +2571,8 @@ 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 */ @@ -3069,8 +3074,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; @@ -3079,6 +3083,9 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, cFYI(1, ("GetCifsACL")); + *pbuflen = 0; + *acl_inf = NULL; + rc = smb_init_ntransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, 8 /* parm len */, tcon, (void **) &pSMB); if (rc) @@ -3101,34 +3108,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) @@ -3383,7 +3408,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); @@ -3651,7 +3676,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 = @@ -4333,7 +4358,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/file.c b/fs/cifs/file.c index 1e7e4c06d9e3..68ad4ca0cfa3 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 9a5c0c925bab..9be0bbd20dfd 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -530,7 +530,7 @@ int cifs_get_inode_info(struct inode **pinode, #ifdef CONFIG_CIFS_EXPERIMENTAL if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { cFYI(1, ("Getting mode bits from ACL")); - get_mode_from_acl(inode, search_path); + acl_to_uid_mode(inode, search_path); } #endif if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { diff --git a/fs/cifs/md5.c b/fs/cifs/md5.c index e5c3e1212697..f13f96d42fcf 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 51ec681fe74a..15546c2354c5 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 f06359cb22ee..4d35c034755a 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -793,8 +793,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 +807,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 +837,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/smbencrypt.c b/fs/cifs/smbencrypt.c index 90542a39be17..bd3c4674f2ba 100644 --- a/fs/cifs/smbencrypt.c +++ b/fs/cifs/smbencrypt.c @@ -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 diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index 369e838bebd3..12b125ff0bd0 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -265,6 +265,8 @@ 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; + struct cifs_ntsd *pacl = NULL; + __u32 buflen = 0; if (experimEnabled) rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ, 0, &fid, @@ -274,9 +276,8 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, /* else rc is EOPNOTSUPP from above */ if(rc == 0) { - rc = CIFSSMBGetCIFSACL(xid, pTcon, fid, - ea_value, buf_size, - ACL_TYPE_ACCESS); + rc = CIFSSMBGetCIFSACL(xid, pTcon, fid, &pacl, + &buflen); CIFSSMBClose(xid, pTcon, fid); } } From d61e5808d9a4e7c7f25914ceae50664a6454c3ca Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 26 Oct 2007 04:32:43 +0000 Subject: [PATCH 05/21] [CIFS] acl support part 7 Also fixes typo, build break Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 25 ++++++++++++++++++++----- fs/cifs/cifsacl.h | 2 +- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 14200bd45b30..3a2d67b182d4 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -134,14 +134,29 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) 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 access_flags, umode_t * pmode, +static void access_flags_to_mode(__u32 ace_flags, umode_t *pmode, umode_t bits_to_set) { -#ifdef CONFIG_CIFS_DEBUG2 - cFYI(1, ("access flags 0x%x mode now 0x%x", access_flags, *pmode); -#endif + *pmode &= ~bits_to_set; + if (ace_flags & GENERIC_ALL) { + *pmode |= (S_IRWXUGO & bits_to_set); +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("all perms")); +#endif + return; + } + if ((ace_flags & GENERIC_WRITE) || (ace_flags & FILE_WRITE_RIGHTS)) + *pmode |= (S_IWUGO & bits_to_set); + if ((ace_flags & GENERIC_READ) || (ace_flags & FILE_READ_RIGHTS)) + *pmode |= (S_IRUGO & bits_to_set); + if ((ace_flags & GENERIC_EXECUTE) || (ace_flags & FILE_EXEC_RIGHTS)) + *pmode |= (S_IXUGO & bits_to_set); + +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("access flags 0x%x mode now 0x%x", ace_flags, *pmode); +#endif return; } @@ -242,7 +257,7 @@ static void parse_ace(struct cifs_ace *pace, char *end_of_acl) 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; diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h index 06d52006bf26..30b0caf66786 100644 --- a/fs/cifs/cifsacl.h +++ b/fs/cifs/cifsacl.h @@ -73,7 +73,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 */ From b9c7a2bb1e57f571d3b0763bdce1ce15510a7b78 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 26 Oct 2007 23:40:20 +0000 Subject: [PATCH 06/21] [CIFS] ACL support part 8 Now GetACL in getinodeinfo path when cifsacl mount option used, and ACL is parsed for SIDs. Missing only one piece now to be able to retrieve the mode Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 133 ++++++++++++++++++++++++---------------------- fs/cifs/cifssmb.c | 6 +-- 2 files changed, 73 insertions(+), 66 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 3a2d67b182d4..cad2da3a447d 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -155,69 +155,11 @@ static void access_flags_to_mode(__u32 ace_flags, umode_t *pmode, *pmode |= (S_IXUGO & bits_to_set); #ifdef CONFIG_CIFS_DEBUG2 - cFYI(1, ("access flags 0x%x mode now 0x%x", ace_flags, *pmode); + cFYI(1, ("access flags 0x%x mode now 0x%x", ace_flags, *pmode)); #endif return; } -/* 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 cifsFileInfo *open_file; - int unlock_file = FALSE; - int xid; - int rc = -EIO; - __u16 fid; - struct super_block *sb; - struct cifs_sb_info *cifs_sb; - - cFYI(1, ("get mode from ACL for %s", path)); - - if (inode == NULL) - return; - - xid = GetXid(); - open_file = find_readable_file(CIFS_I(inode)); - if (open_file) { - unlock_file = TRUE; - fid = open_file->netfid; - } else { - int oplock = FALSE; - /* open file */ - sb = inode->i_sb; - if (sb == NULL) { - FreeXid(xid); - return; - } - cifs_sb = CIFS_SB(sb); - rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN, - GENERIC_READ, 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; - } - } - - /* rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, pntsd, acllen, - ACL_TYPE_ACCESS); */ - - if (unlock_file == TRUE) - atomic_dec(&open_file->wrtPending); - else - CIFSSMBClose(xid, cifs_sb->tcon, fid); - -/* parse ACEs e.g. - rc = parse_sec_desc(pntsd, acllen, inode); -*/ - - FreeXid(xid); - return; -} - static void parse_ace(struct cifs_ace *pace, char *end_of_acl) { @@ -314,12 +256,12 @@ 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; } @@ -354,6 +296,9 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, struct cifs_acl *dacl_ptr; /* no need for SACL ptr */ char *end_of_acl = ((char *)pntsd) + acl_len; + 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 + @@ -368,6 +313,7 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, le32_to_cpu(pntsd->sacloffset), le32_to_cpu(pntsd->dacloffset))); #endif +/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */ rc = parse_sid(owner_sid_ptr, end_of_acl); if (rc) return rc; @@ -388,4 +334,65 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, return (0); } + + +/* 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 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; + __u32 acllen; + + cFYI(1, ("get mode from ACL for %s", path)); + + if (inode == NULL) + return; + + xid = GetXid(); + open_file = find_readable_file(CIFS_I(inode)); + sb = inode->i_sb; + if (sb == NULL) { + FreeXid(xid); + return; + } + 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, + GENERIC_READ, 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; + } + } + + rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, &acllen); + cFYI(1, ("GetCIFSACL rc = %d ACL len %d", rc, acllen)); + if (unlock_file == TRUE) + atomic_dec(&open_file->wrtPending); + else + CIFSSMBClose(xid, cifs_sb->tcon, fid); + + /* parse ACEs */ + if (!rc) + rc = parse_sec_desc(pntsd, acllen, inode); + kfree(pntsd); + FreeXid(xid); + return; +} #endif /* CONFIG_CIFS_EXPERIMENTAL */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index cc17e98991f3..0bb3e431ee01 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2495,7 +2495,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) { @@ -2526,7 +2526,7 @@ smb_init_ntransact(const __u16 sub_command, const int setup_count, static int validate_ntransact(char *buf, char **ppparm, char **ppdata, - __u32 *pdatalen, __u32 *pparmlen) + __u32 *pparmlen, __u32 *pdatalen) { char *end_of_smb; __u32 data_count, data_offset, parm_count, parm_offset; @@ -3086,7 +3086,7 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, *pbuflen = 0; *acl_inf = NULL; - rc = smb_init_ntransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, + rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, 8 /* parm len */, tcon, (void **) &pSMB); if (rc) return rc; From e01b64001359034d04c695388870936ed3d1b56b Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Tue, 30 Oct 2007 04:45:14 +0000 Subject: [PATCH 07/21] [CIFS] enable get mode from ACL when cifsacl mount option specified Part 9 of ACL patch series. getting mode from ACL now works in some cases (and requires CIFS_EXPERIMENTAL config option). Signed-off-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/CHANGES | 4 +++- fs/cifs/cifsacl.c | 28 +++++++++++++++++++++------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 3d419163c3d3..c65c9da863f3 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -12,7 +12,9 @@ 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 files ACL. Version 1.50 ------------ diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index cad2da3a447d..629b96c21639 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -43,8 +43,8 @@ static struct cifs_wksid wksidarr[NUM_WK_SIDS] = { /* 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}, {} }; @@ -138,8 +138,6 @@ static void access_flags_to_mode(__u32 ace_flags, umode_t *pmode, umode_t bits_to_set) { - *pmode &= ~bits_to_set; - if (ace_flags & GENERIC_ALL) { *pmode |= (S_IRWXUGO & bits_to_set); #ifdef CONFIG_CIFS_DEBUG2 @@ -147,11 +145,14 @@ static void access_flags_to_mode(__u32 ace_flags, umode_t *pmode, #endif return; } - if ((ace_flags & GENERIC_WRITE) || (ace_flags & FILE_WRITE_RIGHTS)) + if ((ace_flags & GENERIC_WRITE) || + ((ace_flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) *pmode |= (S_IWUGO & bits_to_set); - if ((ace_flags & GENERIC_READ) || (ace_flags & FILE_READ_RIGHTS)) + if ((ace_flags & GENERIC_READ) || + ((ace_flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) *pmode |= (S_IRUGO & bits_to_set); - if ((ace_flags & GENERIC_EXECUTE) || (ace_flags & FILE_EXEC_RIGHTS)) + if ((ace_flags & GENERIC_EXECUTE) || + ((ace_flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) *pmode |= (S_IXUGO & bits_to_set); #ifdef CONFIG_CIFS_DEBUG2 @@ -234,11 +235,24 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, cifscred->aces = kmalloc(num_aces * sizeof(struct cifs_ace *), GFP_KERNEL);*/ + /* reset rwx permissions for user/group/other */ + inode->i_mode &= ~(S_IRWXUGO); + for (i = 0; i < num_aces; ++i) { ppace[i] = (struct cifs_ace *) (acl_base + acl_size); parse_ace(ppace[i], end_of_acl); + if (compare_sids(&(ppace[i]->sid), pownersid)) + access_flags_to_mode(ppace[i]->access_req, + &(inode->i_mode), S_IRWXU); + if (compare_sids(&(ppace[i]->sid), pgrpsid)) + access_flags_to_mode(ppace[i]->access_req, + &(inode->i_mode), S_IRWXG); + if (compare_sids(&(ppace[i]->sid), &sid_everyone)) + access_flags_to_mode(ppace[i]->access_req, + &(inode->i_mode), S_IRWXO); + /* memcpy((void *)(&(cifscred->aces[i])), (void *)ppace[i], sizeof(struct cifs_ace)); */ From 953f868138dbf4300196780379476ab9f07f263a Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 31 Oct 2007 04:54:42 +0000 Subject: [PATCH 08/21] [CIFS] Don't request too much permission when reading an ACL We were requesting GENERIC_READ but that fails when we do not have read permission on the file (even if we could read the ACL). Also move the dump access control entry code into debug ifdef. Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 32 +++++++++++++++++++++++++------- fs/cifs/cifspdu.h | 17 +++++++++++++++++ fs/cifs/cifsproto.h | 2 ++ fs/cifs/inode.c | 9 ++++----- 4 files changed, 48 insertions(+), 12 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 629b96c21639..f1215df7fbee 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -162,7 +162,8 @@ static void access_flags_to_mode(__u32 ace_flags, umode_t *pmode, } -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; @@ -180,7 +181,6 @@ static void parse_ace(struct cifs_ace *pace, char *end_of_acl) num_subauth = pace->sid.num_subauth; if (num_subauth) { -#ifdef CONFIG_CIFS_DEBUG2 int i; cFYI(1, ("ACE revision %d num_auth %d type %d flags %d size %d", pace->sid.revision, pace->sid.num_subauth, pace->type, @@ -192,11 +192,11 @@ static void parse_ace(struct cifs_ace *pace, char *end_of_acl) /* BB add length check to make sure that we do not have huge num auths and therefore go off the end */ -#endif } return; } +#endif static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, @@ -240,9 +240,9 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, for (i = 0; i < num_aces; ++i) { ppace[i] = (struct cifs_ace *) (acl_base + acl_size); - - parse_ace(ppace[i], end_of_acl); - +#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, &(inode->i_mode), S_IRWXU); @@ -385,7 +385,7 @@ void acl_to_uid_mode(struct inode *inode, const char *path) int oplock = FALSE; /* open file */ rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN, - GENERIC_READ, 0, &fid, &oplock, NULL, + READ_CONTROL, 0, &fid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc != 0) { @@ -409,4 +409,22 @@ void acl_to_uid_mode(struct inode *inode, const char *path) FreeXid(xid); return; } + +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 */ + + /* Add/Modify the three ACEs for owner, group, everyone */ + + /* Set the security descriptor */ + kfree(pntsd); + + return rc; +} #endif /* CONFIG_CIFS_EXPERIMENTAL */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index c41ff74e9128..07464b6ac129 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 diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 88c02ac97c3f..1ffe25592b25 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -96,6 +96,8 @@ 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 *); extern int cifs_umount(struct super_block *, struct cifs_sb_info *); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 9be0bbd20dfd..7d907e84e032 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) { @@ -528,16 +528,15 @@ 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; From 1fb64bfc45b9ee5092b72474a5df216b8a0c7ff9 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 1 Nov 2007 02:12:10 +0000 Subject: [PATCH 09/21] [CIFS] when mount helper missing fix slash wrong direction in share Kernel bugzilla bug #9228 If mount helper (mount.cifs) missing, mounts with form like //10.11.12.13/c$ would not work (only mounts with slash e.g. //10.11.12.13\\c$ would work) due to problem with slash supposed to be converted to backslash by the mount helper (which is not there). If we fail on converting an IPv4 address in in4_pton then try to canonicalize the first slash (ie between sharename and host ip address) if necessary. If we have to retry to check for IPv6 address the slash is already converted if necessary. Signed-off-by: Steve French --- fs/cifs/CHANGES | 5 ++++- fs/cifs/netmisc.c | 40 +++++++++++++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index c65c9da863f3..6d3e736612ba 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -14,7 +14,10 @@ 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. When mounted with cifsacl mount option - mode bits are approximated based -on the contents of the files ACL. +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/netmisc.c b/fs/cifs/netmisc.c index 4d35c034755a..e1704da43836 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 From 7505e0525c914cdfdb54f43a7e70f038a16a5486 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 1 Nov 2007 18:03:01 +0000 Subject: [PATCH 10/21] [CIFS] If no Access Control Entries, set mode perm bits to zero Also clean up ACL code Acked-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 79 +++++++++++++++++++++++++++++++++-------------- fs/cifs/cifspdu.h | 23 ++++++++++++++ fs/cifs/connect.c | 2 +- 3 files changed, 80 insertions(+), 24 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index f1215df7fbee..bd75a3b8caff 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -223,6 +223,17 @@ 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); @@ -235,9 +246,6 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, cifscred->aces = kmalloc(num_aces * sizeof(struct cifs_ace *), GFP_KERNEL);*/ - /* reset rwx permissions for user/group/other */ - inode->i_mode &= ~(S_IRWXUGO); - for (i = 0; i < num_aces; ++i) { ppace[i] = (struct cifs_ace *) (acl_base + acl_size); #ifdef CONFIG_CIFS_DEBUG2 @@ -309,6 +317,7 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, 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; @@ -317,15 +326,14 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, 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); @@ -336,7 +344,11 @@ static 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, inode); + 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; @@ -350,9 +362,9 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, } -/* 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) +/* 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; @@ -362,19 +374,18 @@ void acl_to_uid_mode(struct inode *inode, const char *path) struct super_block *sb; struct cifs_sb_info *cifs_sb; struct cifs_ntsd *pntsd = NULL; - __u32 acllen; cFYI(1, ("get mode from ACL for %s", path)); if (inode == NULL) - return; + return NULL; xid = GetXid(); open_file = find_readable_file(CIFS_I(inode)); sb = inode->i_sb; if (sb == NULL) { FreeXid(xid); - return; + return NULL; } cifs_sb = CIFS_SB(sb); @@ -391,25 +402,44 @@ void acl_to_uid_mode(struct inode *inode, const char *path) if (rc != 0) { cERROR(1, ("Unable to open file to get ACL")); FreeXid(xid); - return; + return NULL; } } - rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, &acllen); - cFYI(1, ("GetCIFSACL rc = %d ACL len %d", rc, acllen)); + 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); - /* parse ACEs */ - if (!rc) - rc = parse_sec_desc(pntsd, acllen, inode); - kfree(pntsd); 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; @@ -419,12 +449,15 @@ int mode_to_acl(struct inode *inode, const char *path) 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 */ + /* Add/Modify the three ACEs for owner, group, everyone + while retaining the other ACEs */ /* Set the security descriptor */ - kfree(pntsd); + + kfree(pntsd); return rc; } #endif /* CONFIG_CIFS_EXPERIMENTAL */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 07464b6ac129..dbe6b846f37f 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -1228,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/connect.c b/fs/cifs/connect.c index 19ee11f7f35a..380ee9991f20 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; From 745542e210b3b15751ea9d511321924ac36b85db Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sat, 3 Nov 2007 04:34:04 +0000 Subject: [PATCH 11/21] [CIFS] allow cifs_calc_signature2 to deal with a zero length iovec Currently, cifs_calc_signature2 errors out if it gets a zero-length iovec. Fix it to silently continue in that case. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsencrypt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 632070b4275d..788f0ad6feda 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -99,11 +99,12 @@ 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) { From 09fe7ba78dedb9017401ed555ecc4435c99a7556 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sat, 3 Nov 2007 04:48:29 +0000 Subject: [PATCH 12/21] [CIFS] implement upcalls for SPNEGO blob via keyctl API Add routines to handle upcalls to userspace via keyctl for the purpose of getting a SPNEGO blob for a particular uid and server combination. Clean up the Makefile a bit and set it up to only compile cifs_spnego if CONFIG_CIFS_UPCALL is set. Also change CONFIG_CIFS_UPCALL to depend on CONFIG_KEYS rather than CONFIG_CONNECTOR. cifs_spnego.h defines the communications between kernel and userspace and is intended to be shared with userspace programs. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/Kconfig | 2 +- fs/cifs/Makefile | 7 ++++++- fs/cifs/cifsproto.h | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/Kconfig b/fs/Kconfig index cc28a69246a7..e431c38a7262 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/Makefile b/fs/cifs/Makefile index ff6ba8d823f0..45e42fb97c19 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/cifsproto.h b/fs/cifs/cifsproto.h index 1ffe25592b25..dd1d7c200ee6 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -76,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); From 84a15b935481fa651cc6ec60aed015312b67adda Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sat, 3 Nov 2007 05:02:24 +0000 Subject: [PATCH 13/21] [CIFS] Register and unregister cifs_spnego_key_type on module init/exit Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index a6fbea57c4b1..94c0f55d7669 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -43,6 +43,7 @@ #include "cifs_debug.h" #include "cifs_fs_sb.h" #include +#include #define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */ #ifdef CONFIG_CIFS_QUOTA @@ -1005,12 +1006,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 +1029,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(); @@ -1045,6 +1054,9 @@ exit_cifs(void) cFYI(0, ("exit_cifs")); #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(); From e545937a51fe0cc78cea55752764daabb81ec96d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sat, 3 Nov 2007 05:11:06 +0000 Subject: [PATCH 14/21] [CIFS] add OIDs for KRB5 and MSKRB5 to ASN1 parsing routines Also, fix the parser to recognize them and set the secType accordingly. Make CIFSSMBNegotiate not error out automatically after parsing the securityBlob. Also thanks to Q (Igor) and Simo for their help on this set of kerberos patches (and Dave Howells for help on the upcall). Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/asn1.c | 35 ++++++++++++++++++++++++----------- fs/cifs/cifsfs.c | 1 + fs/cifs/cifssmb.c | 3 +-- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/fs/cifs/asn1.c b/fs/cifs/asn1.c index 2a01f3ef96a0..bcda2c6b6a04 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/cifsfs.c b/fs/cifs/cifsfs.c index 94c0f55d7669..416dc9fe8961 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -44,6 +44,7 @@ #include "cifs_fs_sb.h" #include #include +#include "cifs_spnego.h" #define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */ #ifdef CONFIG_CIFS_QUOTA diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 0bb3e431ee01..59d7b7c037ad 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; } From f1d662a7d5e5322e583aad6b3cfec03d8f27b435 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 5 Nov 2007 14:38:08 +0000 Subject: [PATCH 15/21] [CIFS] Add upcall files for cifs to use spnego/kerberos Acked-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_spnego.c | 124 ++++++++++++++++++++++++++++++++++++++++++ fs/cifs/cifs_spnego.h | 46 ++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 fs/cifs/cifs_spnego.c create mode 100644 fs/cifs/cifs_spnego.h diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c new file mode 100644 index 000000000000..e142faf2d0ae --- /dev/null +++ b/fs/cifs/cifs_spnego.c @@ -0,0 +1,124 @@ +/* + * 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 +#include +#include +#include +#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= + NULL */ + desc_len = 2 + 5 + 32 + 1 + 5 + strlen(hostname) + + strlen(";sec=krb5") + 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, "%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; + + 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 000000000000..f443f3b35134 --- /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 */ From 63d2583f5a1a0b72fea3f2171f23f0ca8fa556ec Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 5 Nov 2007 21:46:10 +0000 Subject: [PATCH 16/21] [CIFS] Fix walking out end of cifs dacl Acked-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 4 ++-- fs/cifs/cifsacl.h | 3 +++ fs/cifs/cifsencrypt.c | 4 ++-- fs/cifs/netmisc.c | 2 +- fs/cifs/readdir.c | 2 +- fs/cifs/smbencrypt.c | 14 ++++++++------ fs/cifs/xattr.c | 4 ++-- 7 files changed, 19 insertions(+), 14 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index bd75a3b8caff..38d09fa8c1e6 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -327,7 +327,7 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, group_sid_ptr = (struct cifs_sid *)((char *)pntsd + le32_to_cpu(pntsd->gsidoffset)); dacloffset = le32_to_cpu(pntsd->dacloffset); - dacl_ptr = (struct cifs_acl *)(char *)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", @@ -346,7 +346,7 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, if (dacloffset) parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, - group_sid_ptr, inode); + group_sid_ptr, inode); else cFYI(1, ("no ACL")); /* BB grant all or default perms? */ diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h index 30b0caf66786..93a7c3462ea2 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; diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 788f0ad6feda..4ff8939c6cc7 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -108,7 +108,7 @@ static int cifs_calc_signature2(const struct kvec *iov, int n_vec, /* 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); @@ -123,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/netmisc.c b/fs/cifs/netmisc.c index e1704da43836..646e1f06941b 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -770,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) { diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 3746580e9701..82497d47429a 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -495,7 +495,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 bd3c4674f2ba..58bbfd992cc0 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 @@ -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 12b125ff0bd0..54e8ef96cb79 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -267,7 +267,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, int oplock = FALSE; struct cifs_ntsd *pacl = NULL; __u32 buflen = 0; - if (experimEnabled) + if (experimEnabled) rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ, 0, &fid, &oplock, NULL, cifs_sb->local_nls, @@ -275,7 +275,7 @@ 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) { + if (rc == 0) { rc = CIFSSMBGetCIFSACL(xid, pTcon, fid, &pacl, &buflen); CIFSSMBClose(xid, pTcon, fid); From 9eae8a8903c3d90283d338fad2cc58f2eb90adcb Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 8 Nov 2007 16:13:31 +0000 Subject: [PATCH 17/21] [CIFS] Add uid to key description so krb can handle user mounts Adds uid to key description fro supporting user mounts and minor formating changes Acked-by: Jeff Layton Signed-off-by: Igor Mammedov Signed-off-by: Steve French --- fs/cifs/cifs_spnego.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index e142faf2d0ae..ad54a3a6e434 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -76,9 +76,10 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo, const char *hostname) struct key *spnego_key; - /* version + ;ip{4|6}= + address + ;host=hostname + ;sec= + NULL */ - desc_len = 2 + 5 + 32 + 1 + 5 + strlen(hostname) + - strlen(";sec=krb5") + 1; + /* 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) @@ -87,7 +88,7 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo, const char *hostname) dp = description; /* start with version and hostname portion of UNC string */ spnego_key = ERR_PTR(-EINVAL); - sprintf(dp, "%2.2x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION, + sprintf(dp, "0x%2.2x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION, hostname); dp = description + strlen(description); @@ -109,6 +110,9 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo, const char *hostname) 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, ""); From 15b0395911eb45a0834755f0d9e84570644a8c22 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 8 Nov 2007 17:57:40 +0000 Subject: [PATCH 18/21] [CIFS] Fix incorrect mode when ACL had deny access control entries When mounted with the cifsacl mount option, we were treating any deny ACEs found like allow ACEs and it turns out for SFU and SUA Windows set these type of access control entries often. The order of ACEs is important too. The canonical order that most ACL tools and Windows explorer consruct ACLs with is to begin with DENY entries then follow with 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 We start with no permissions in the mode and build up as we find permissions (ie allow ACEs). This fixes deny ACEs so they affect the mask used to set the subsequent allow ACEs. Acked-by: Shirish Pargaonkar CC: Alexander Bokovoy Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 55 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 38d09fa8c1e6..ec445802d903 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -134,12 +134,39 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) 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, umode_t *pmode, - umode_t bits_to_set) +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 then follow with 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 & bits_to_set); + *pmode |= (S_IRWXUGO & (*pbits_to_set)); #ifdef CONFIG_CIFS_DEBUG2 cFYI(1, ("all perms")); #endif @@ -147,13 +174,13 @@ static void access_flags_to_mode(__u32 ace_flags, umode_t *pmode, } if ((ace_flags & GENERIC_WRITE) || ((ace_flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) - *pmode |= (S_IWUGO & bits_to_set); + *pmode |= (S_IWUGO & (*pbits_to_set)); if ((ace_flags & GENERIC_READ) || ((ace_flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) - *pmode |= (S_IRUGO & bits_to_set); + *pmode |= (S_IRUGO & (*pbits_to_set)); if ((ace_flags & GENERIC_EXECUTE) || ((ace_flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) - *pmode |= (S_IXUGO & bits_to_set); + *pmode |= (S_IXUGO & (*pbits_to_set)); #ifdef CONFIG_CIFS_DEBUG2 cFYI(1, ("access flags 0x%x mode now 0x%x", ace_flags, *pmode)); @@ -239,6 +266,10 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, num_aces = le32_to_cpu(pdacl->num_aces); if (num_aces > 0) { + 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); @@ -253,13 +284,19 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, #endif if (compare_sids(&(ppace[i]->sid), pownersid)) access_flags_to_mode(ppace[i]->access_req, - &(inode->i_mode), S_IRWXU); + ppace[i]->type, + &(inode->i_mode), + &user_mask); if (compare_sids(&(ppace[i]->sid), pgrpsid)) access_flags_to_mode(ppace[i]->access_req, - &(inode->i_mode), S_IRWXG); + ppace[i]->type, + &(inode->i_mode), + &group_mask); if (compare_sids(&(ppace[i]->sid), &sid_everyone)) access_flags_to_mode(ppace[i]->access_req, - &(inode->i_mode), S_IRWXO); + ppace[i]->type, + &(inode->i_mode), + &other_mask); /* memcpy((void *)(&(cifscred->aces[i])), (void *)ppace[i], From ce06c9f025120dbb2978d9b84641d76c25f17902 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 8 Nov 2007 21:12:01 +0000 Subject: [PATCH 19/21] [CIFS] add mode to acl conversion helper function Acked-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/CHANGES | 3 +++ fs/cifs/cifsacl.c | 35 +++++++++++++++++++++++++++++++++-- fs/cifs/cifsfs.h | 2 +- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 6d3e736612ba..53629b8bc8a8 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,6 @@ +Version 1.52 +------------ + Version 1.51 ------------ Fix memory leak in statfs when mounted to very old servers (e.g. diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index ec445802d903..dabbce00712b 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -138,9 +138,9 @@ 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 then follow with ALLOW, otherwise an allow entry could be + 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 + 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 @@ -188,6 +188,37 @@ static void access_flags_to_mode(__u32 ace_flags, int type, umode_t *pmode, 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; +} + #ifdef CONFIG_CIFS_DEBUG2 static void dump_ace(struct cifs_ace *pace, char *end_of_acl) diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 0a3ee5a322b0..62357d228c07 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 struct export_operations cifs_export_ops; #endif /* EXPERIMENTAL */ -#define CIFS_VERSION "1.51" +#define CIFS_VERSION "1.52" #endif /* _CIFSFS_H */ From a6f8de3d9b124c95893054fd2a78bc7be5bb9000 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 8 Nov 2007 23:10:32 +0000 Subject: [PATCH 20/21] [CIFS] Fix stale mode after readdir when cifsacl specified When mounted with cifsacl mount option, readdir can not instantiate the inode with the estimated mode based on the ACL for each file since we have not queried for the ACL for each of these files yet. So set the refresh time to zero for these inodes so that the next stat will cause the client to go to the server for the ACL info so we can build the estimated mode (this means we also will issue an extra QueryPathInfo if the stat happens within 1 second, but this is trivial compared to the time required to open/getacl/close for each). ls -l is slower when cifsacl mount option is specified, but displays correct mode information. Signed-off-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/readdir.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 82497d47429a..0f22def4bdff 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 */ From 9b8f5f573770f33b28c45255ac82e6457278c782 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 9 Nov 2007 23:25:04 +0000 Subject: [PATCH 21/21] [CIFS] fix oops on second mount to same server when null auth is used When a share is mounted using no username, cifs_mount sets volume_info.username as a NULL pointer, and the sesInfo userName as an empty string. The volume_info.username is passed to a couple of other functions to see if there is an existing unc or tcp connection that can be used. These functions assume that the username will be a valid string that can be passed to strncmp. If the pointer is NULL, then the kernel will oops if there's an existing session to which the string can be compared. This patch changes cifs_mount to set volume_info.username to an empty string in this situation, which prevents the oops and should make it so that the comparison to other null auth sessions match. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/CHANGES | 1 + fs/cifs/connect.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 53629b8bc8a8..64dd22239b21 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,5 +1,6 @@ Version 1.52 ------------ +Fix oops on second mount to server when null auth is used. Version 1.51 ------------ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 380ee9991f20..1102160f6661 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -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));