Commit 97837582bc1e191d2792af74c1f3762ed01243b9
1 parent
28c5a02a11
Exists in
master
and in
7 other branches
[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
fs/cifs/CHANGES
... | ... | @@ -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 | ------------ |
fs/cifs/cifsacl.c
... | ... | @@ -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 */ |
fs/cifs/cifspdu.h
fs/cifs/cifsproto.h
... | ... | @@ -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, |
fs/cifs/cifssmb.c
... | ... | @@ -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 |
fs/cifs/inode.c
... | ... | @@ -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) { |