Commit 97837582bc1e191d2792af74c1f3762ed01243b9

Authored by Steve French
1 parent 28c5a02a11

[CIFS] Allow setting mode via cifs acl

Requires cifsacl mount flag to be on and CIFS_EXPERIMENTAL enabled

CC: Shirish Pargaonkar <shirishp@us.ibm.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>

Showing 6 changed files with 315 additions and 13 deletions Side-by-side Diff

... ... @@ -5,6 +5,8 @@
5 5 and sync so that events like out of disk space get reported properly on
6 6 cached files. Fix setxattr failure to certain Samba versions. Fix mount
7 7 of second share to disconnected server session (autoreconnect on this).
  8 +Add ability to modify cifs acls for handling chmod (when mounted with
  9 +cifsacl flag).
8 10  
9 11 Version 1.51
10 12 ------------
... ... @@ -129,6 +129,54 @@
129 129 return (1); /* sids compare/match */
130 130 }
131 131  
  132 +
  133 +/* copy ntsd, owner sid, and group sid from a security descriptor to another */
  134 +static void copy_sec_desc(const struct cifs_ntsd *pntsd,
  135 + struct cifs_ntsd *pnntsd, __u32 sidsoffset)
  136 +{
  137 + int i;
  138 +
  139 + struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
  140 + struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
  141 +
  142 + /* copy security descriptor control portion */
  143 + pnntsd->revision = pntsd->revision;
  144 + pnntsd->type = pntsd->type;
  145 + pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd));
  146 + pnntsd->sacloffset = 0;
  147 + pnntsd->osidoffset = cpu_to_le32(sidsoffset);
  148 + pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid));
  149 +
  150 + /* copy owner sid */
  151 + owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
  152 + le32_to_cpu(pntsd->osidoffset));
  153 + nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset);
  154 +
  155 + nowner_sid_ptr->revision = owner_sid_ptr->revision;
  156 + nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth;
  157 + for (i = 0; i < 6; i++)
  158 + nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i];
  159 + for (i = 0; i < 5; i++)
  160 + nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i];
  161 +
  162 + /* copy group sid */
  163 + group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
  164 + le32_to_cpu(pntsd->gsidoffset));
  165 + ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +
  166 + sizeof(struct cifs_sid));
  167 +
  168 + ngroup_sid_ptr->revision = group_sid_ptr->revision;
  169 + ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth;
  170 + for (i = 0; i < 6; i++)
  171 + ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i];
  172 + for (i = 0; i < 5; i++)
  173 + ngroup_sid_ptr->sub_auth[i] =
  174 + cpu_to_le32(group_sid_ptr->sub_auth[i]);
  175 +
  176 + return;
  177 +}
  178 +
  179 +
132 180 /*
133 181 change posix mode to reflect permissions
134 182 pmode is the existing mode (we only want to overwrite part of this
135 183  
... ... @@ -220,7 +268,34 @@
220 268 return;
221 269 }
222 270  
  271 +static __le16 fill_ace_for_sid(struct cifs_ace *pntace,
  272 + const struct cifs_sid *psid, __u64 nmode, umode_t bits)
  273 +{
  274 + int i;
  275 + __u16 size = 0;
  276 + __u32 access_req = 0;
223 277  
  278 + pntace->type = ACCESS_ALLOWED;
  279 + pntace->flags = 0x0;
  280 + mode_to_access_flags(nmode, bits, &access_req);
  281 + if (!access_req)
  282 + access_req = SET_MINIMUM_RIGHTS;
  283 + pntace->access_req = cpu_to_le32(access_req);
  284 +
  285 + pntace->sid.revision = psid->revision;
  286 + pntace->sid.num_subauth = psid->num_subauth;
  287 + for (i = 0; i < 6; i++)
  288 + pntace->sid.authority[i] = psid->authority[i];
  289 + for (i = 0; i < psid->num_subauth; i++)
  290 + pntace->sid.sub_auth[i] = psid->sub_auth[i];
  291 +
  292 + size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4);
  293 + pntace->size = cpu_to_le16(size);
  294 +
  295 + return (size);
  296 +}
  297 +
  298 +
224 299 #ifdef CONFIG_CIFS_DEBUG2
225 300 static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
226 301 {
... ... @@ -243,7 +318,7 @@
243 318 int i;
244 319 cFYI(1, ("ACE revision %d num_auth %d type %d flags %d size %d",
245 320 pace->sid.revision, pace->sid.num_subauth, pace->type,
246   - pace->flags, pace->size));
  321 + pace->flags, le16_to_cpu(pace->size)));
247 322 for (i = 0; i < num_subauth; ++i) {
248 323 cFYI(1, ("ACE sub_auth[%d]: 0x%x", i,
249 324 le32_to_cpu(pace->sid.sub_auth[i])));
... ... @@ -346,6 +421,28 @@
346 421 }
347 422  
348 423  
  424 +static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid,
  425 + struct cifs_sid *pgrpsid, __u64 nmode)
  426 +{
  427 + __le16 size = 0;
  428 + struct cifs_acl *pnndacl;
  429 +
  430 + pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl));
  431 +
  432 + size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size),
  433 + pownersid, nmode, S_IRWXU);
  434 + size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
  435 + pgrpsid, nmode, S_IRWXG);
  436 + size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
  437 + &sid_everyone, nmode, S_IRWXO);
  438 +
  439 + pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl));
  440 + pndacl->num_aces = 3;
  441 +
  442 + return (0);
  443 +}
  444 +
  445 +
349 446 static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
350 447 {
351 448 /* BB need to add parm so we can store the SID BB */
... ... @@ -432,6 +529,46 @@
432 529 }
433 530  
434 531  
  532 +/* Convert permission bits from mode to equivalent CIFS ACL */
  533 +static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
  534 + int acl_len, struct inode *inode, __u64 nmode)
  535 +{
  536 + int rc = 0;
  537 + __u32 dacloffset;
  538 + __u32 ndacloffset;
  539 + __u32 sidsoffset;
  540 + struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
  541 + struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */
  542 + struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
  543 +
  544 + if ((inode == NULL) || (pntsd == NULL) || (pnntsd == NULL))
  545 + return (-EIO);
  546 +
  547 + owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
  548 + le32_to_cpu(pntsd->osidoffset));
  549 + group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
  550 + le32_to_cpu(pntsd->gsidoffset));
  551 +
  552 + dacloffset = le32_to_cpu(pntsd->dacloffset);
  553 + dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
  554 +
  555 + ndacloffset = sizeof(struct cifs_ntsd);
  556 + ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
  557 + ndacl_ptr->revision = dacl_ptr->revision;
  558 + ndacl_ptr->size = 0;
  559 + ndacl_ptr->num_aces = 0;
  560 +
  561 + rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr, nmode);
  562 +
  563 + sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
  564 +
  565 + /* copy security descriptor control portion and owner and group sid */
  566 + copy_sec_desc(pntsd, pnntsd, sidsoffset);
  567 +
  568 + return (rc);
  569 +}
  570 +
  571 +
435 572 /* Retrieve an ACL from the server */
436 573 static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode,
437 574 const char *path)
... ... @@ -487,6 +624,64 @@
487 624 return pntsd;
488 625 }
489 626  
  627 +/* Set an ACL on the server */
  628 +static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
  629 + struct inode *inode, const char *path)
  630 +{
  631 + struct cifsFileInfo *open_file;
  632 + int unlock_file = FALSE;
  633 + int xid;
  634 + int rc = -EIO;
  635 + __u16 fid;
  636 + struct super_block *sb;
  637 + struct cifs_sb_info *cifs_sb;
  638 +
  639 +#ifdef CONFIG_CIFS_DEBUG2
  640 + cFYI(1, ("set ACL for %s from mode 0x%x", path, inode->i_mode));
  641 +#endif
  642 +
  643 + if (!inode)
  644 + return (rc);
  645 +
  646 + sb = inode->i_sb;
  647 + if (sb == NULL)
  648 + return (rc);
  649 +
  650 + cifs_sb = CIFS_SB(sb);
  651 + xid = GetXid();
  652 +
  653 + open_file = find_readable_file(CIFS_I(inode));
  654 + if (open_file) {
  655 + unlock_file = TRUE;
  656 + fid = open_file->netfid;
  657 + } else {
  658 + int oplock = FALSE;
  659 + /* open file */
  660 + rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN,
  661 + WRITE_DAC, 0, &fid, &oplock, NULL,
  662 + cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
  663 + CIFS_MOUNT_MAP_SPECIAL_CHR);
  664 + if (rc != 0) {
  665 + cERROR(1, ("Unable to open file to set ACL"));
  666 + FreeXid(xid);
  667 + return (rc);
  668 + }
  669 + }
  670 +
  671 + rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen);
  672 +#ifdef CONFIG_CIFS_DEBUG2
  673 + cFYI(1, ("SetCIFSACL rc = %d", rc));
  674 +#endif
  675 + if (unlock_file == TRUE)
  676 + atomic_dec(&open_file->wrtPending);
  677 + else
  678 + CIFSSMBClose(xid, cifs_sb->tcon, fid);
  679 +
  680 + FreeXid(xid);
  681 +
  682 + return (rc);
  683 +}
  684 +
490 685 /* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */
491 686 void acl_to_uid_mode(struct inode *inode, const char *path)
492 687 {
493 688  
494 689  
495 690  
496 691  
497 692  
498 693  
499 694  
... ... @@ -510,25 +705,54 @@
510 705 }
511 706  
512 707 /* Convert mode bits to an ACL so we can update the ACL on the server */
513   -int mode_to_acl(struct inode *inode, const char *path)
  708 +int mode_to_acl(struct inode *inode, const char *path, __u64 nmode)
514 709 {
515 710 int rc = 0;
516 711 __u32 acllen = 0;
517   - struct cifs_ntsd *pntsd = NULL;
  712 + struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */
  713 + struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
518 714  
  715 +#ifdef CONFIG_CIFS_DEBUG2
519 716 cFYI(1, ("set ACL from mode for %s", path));
  717 +#endif
520 718  
521 719 /* Get the security descriptor */
522 720 pntsd = get_cifs_acl(&acllen, inode, path);
523 721  
524   - /* Add/Modify the three ACEs for owner, group, everyone
525   - while retaining the other ACEs */
  722 + /* Add three ACEs for owner, group, everyone getting rid of
  723 + other ACEs as chmod disables ACEs and set the security descriptor */
526 724  
527   - /* Set the security descriptor */
  725 + if (pntsd) {
  726 + /* allocate memory for the smb header,
  727 + set security descriptor request security descriptor
  728 + parameters, and secuirty descriptor itself */
528 729  
  730 + pnntsd = kmalloc(acllen, GFP_KERNEL);
  731 + if (!pnntsd) {
  732 + cERROR(1, ("Unable to allocate security descriptor"));
  733 + kfree(pntsd);
  734 + return (-ENOMEM);
  735 + }
529 736  
530   - kfree(pntsd);
531   - return rc;
  737 + rc = build_sec_desc(pntsd, pnntsd, acllen, inode, nmode);
  738 +
  739 +#ifdef CONFIG_CIFS_DEBUG2
  740 + cFYI(1, ("build_sec_desc rc: %d", rc));
  741 +#endif
  742 +
  743 + if (!rc) {
  744 + /* Set the security descriptor */
  745 + rc = set_cifs_acl(pnntsd, acllen, inode, path);
  746 +#ifdef CONFIG_CIFS_DEBUG2
  747 + cFYI(1, ("set_cifs_acl rc: %d", rc));
  748 +#endif
  749 + }
  750 +
  751 + kfree(pnntsd);
  752 + kfree(pntsd);
  753 + }
  754 +
  755 + return (rc);
532 756 }
533 757 #endif /* CONFIG_CIFS_EXPERIMENTAL */
... ... @@ -237,6 +237,9 @@
237 237 | DELETE | READ_CONTROL | WRITE_DAC \
238 238 | WRITE_OWNER | SYNCHRONIZE)
239 239  
  240 +#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \
  241 + | READ_CONTROL | SYNCHRONIZE)
  242 +
240 243  
241 244 /*
242 245 * Invalid readdir handle
... ... @@ -97,7 +97,7 @@
97 97 const unsigned char *search_path,
98 98 struct super_block *sb, int xid);
99 99 extern void acl_to_uid_mode(struct inode *inode, const char *search_path);
100   -extern int mode_to_acl(struct inode *inode, const char *path);
  100 +extern int mode_to_acl(struct inode *inode, const char *path, __u64);
101 101  
102 102 extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
103 103 const char *);
... ... @@ -342,6 +342,8 @@
342 342 const struct nls_table *nls_codepage, int remap_special_chars);
343 343 extern int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon,
344 344 __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen);
  345 +extern int CIFSSMBSetCIFSACL(const int, struct cifsTconInfo *, __u16,
  346 + struct cifs_ntsd *, __u32);
345 347 extern int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
346 348 const unsigned char *searchName,
347 349 char *acl_inf, const int buflen, const int acl_type,
... ... @@ -3156,6 +3156,71 @@
3156 3156 /* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
3157 3157 return rc;
3158 3158 }
  3159 +
  3160 +int
  3161 +CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
  3162 + struct cifs_ntsd *pntsd, __u32 acllen)
  3163 +{
  3164 + __u16 byte_count, param_count, data_count, param_offset, data_offset;
  3165 + int rc = 0;
  3166 + int bytes_returned = 0;
  3167 + SET_SEC_DESC_REQ *pSMB = NULL;
  3168 + NTRANSACT_RSP *pSMBr = NULL;
  3169 +
  3170 +setCifsAclRetry:
  3171 + rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
  3172 + (void **) &pSMBr);
  3173 + if (rc)
  3174 + return (rc);
  3175 +
  3176 + pSMB->MaxSetupCount = 0;
  3177 + pSMB->Reserved = 0;
  3178 +
  3179 + param_count = 8;
  3180 + param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
  3181 + data_count = acllen;
  3182 + data_offset = param_offset + param_count;
  3183 + byte_count = 3 /* pad */ + param_count;
  3184 +
  3185 + pSMB->DataCount = cpu_to_le32(data_count);
  3186 + pSMB->TotalDataCount = pSMB->DataCount;
  3187 + pSMB->MaxParameterCount = cpu_to_le32(4);
  3188 + pSMB->MaxDataCount = cpu_to_le32(16384);
  3189 + pSMB->ParameterCount = cpu_to_le32(param_count);
  3190 + pSMB->ParameterOffset = cpu_to_le32(param_offset);
  3191 + pSMB->TotalParameterCount = pSMB->ParameterCount;
  3192 + pSMB->DataOffset = cpu_to_le32(data_offset);
  3193 + pSMB->SetupCount = 0;
  3194 + pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
  3195 + pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
  3196 +
  3197 + pSMB->Fid = fid; /* file handle always le */
  3198 + pSMB->Reserved2 = 0;
  3199 + pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
  3200 +
  3201 + if (pntsd && acllen) {
  3202 + memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
  3203 + (char *) pntsd,
  3204 + acllen);
  3205 + pSMB->hdr.smb_buf_length += (byte_count + data_count);
  3206 +
  3207 + } else
  3208 + pSMB->hdr.smb_buf_length += byte_count;
  3209 +
  3210 + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
  3211 + (struct smb_hdr *) pSMBr, &bytes_returned, 0);
  3212 +
  3213 + cFYI(1, ("SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc));
  3214 + if (rc)
  3215 + cFYI(1, ("Set CIFS ACL returned %d", rc));
  3216 + cifs_buf_release(pSMB);
  3217 +
  3218 + if (rc == -EAGAIN)
  3219 + goto setCifsAclRetry;
  3220 +
  3221 + return (rc);
  3222 +}
  3223 +
3159 3224 #endif /* CONFIG_CIFS_EXPERIMENTAL */
3160 3225  
3161 3226 /* Legacy Query Path Information call for lookup to old servers such
... ... @@ -1607,7 +1607,13 @@
1607 1607 CIFS_MOUNT_MAP_SPECIAL_CHR);
1608 1608 else if (attrs->ia_valid & ATTR_MODE) {
1609 1609 rc = 0;
  1610 +#ifdef CONFIG_CIFS_EXPERIMENTAL
  1611 + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
  1612 + rc = mode_to_acl(direntry->d_inode, full_path, mode);
  1613 + else if ((mode & S_IWUGO) == 0) /* not writeable */ {
  1614 +#else
1610 1615 if ((mode & S_IWUGO) == 0) /* not writeable */ {
  1616 +#endif
1611 1617 if ((cifsInode->cifsAttrs & ATTR_READONLY) == 0) {
1612 1618 set_dosattr = TRUE;
1613 1619 time_buf.Attributes =
... ... @@ -1626,10 +1632,10 @@
1626 1632 if (time_buf.Attributes == 0)
1627 1633 time_buf.Attributes |= cpu_to_le32(ATTR_NORMAL);
1628 1634 }
1629   - /* BB to be implemented -
1630   - via Windows security descriptors or streams */
1631   - /* CIFSSMBWinSetPerms(xid, pTcon, full_path, mode, uid, gid,
1632   - cifs_sb->local_nls); */
  1635 +#ifdef CONFIG_CIFS_EXPERIMENTAL
  1636 + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
  1637 + mode_to_acl(direntry->d_inode, full_path, mode);
  1638 +#endif
1633 1639 }
1634 1640  
1635 1641 if (attrs->ia_valid & ATTR_ATIME) {