diff options
author | Steve French <sfrench@us.ibm.com> | 2007-12-31 02:47:21 -0500 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2007-12-31 02:47:21 -0500 |
commit | 97837582bc1e191d2792af74c1f3762ed01243b9 (patch) | |
tree | c472591913d02cc4fb107815c53221044fdc9a6c /fs/cifs/cifsacl.c | |
parent | 28c5a02a11f70bb1fd8dd3b633206e2db3220308 (diff) |
[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>
Diffstat (limited to 'fs/cifs/cifsacl.c')
-rw-r--r-- | fs/cifs/cifsacl.c | 240 |
1 files changed, 232 insertions, 8 deletions
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index c312adcba4fc..a7035bd18e4e 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c | |||
@@ -129,6 +129,54 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) | |||
129 | return (1); /* sids compare/match */ | 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 | change posix mode to reflect permissions | 181 | change posix mode to reflect permissions |
134 | pmode is the existing mode (we only want to overwrite part of this | 182 | pmode is the existing mode (we only want to overwrite part of this |
@@ -220,6 +268,33 @@ static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, | |||
220 | return; | 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; | ||
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 | |||
223 | 298 | ||
224 | #ifdef CONFIG_CIFS_DEBUG2 | 299 | #ifdef CONFIG_CIFS_DEBUG2 |
225 | static void dump_ace(struct cifs_ace *pace, char *end_of_acl) | 300 | static void dump_ace(struct cifs_ace *pace, char *end_of_acl) |
@@ -243,7 +318,7 @@ static void dump_ace(struct cifs_ace *pace, char *end_of_acl) | |||
243 | int i; | 318 | int i; |
244 | cFYI(1, ("ACE revision %d num_auth %d type %d flags %d size %d", | 319 | cFYI(1, ("ACE revision %d num_auth %d type %d flags %d size %d", |
245 | pace->sid.revision, pace->sid.num_subauth, pace->type, | 320 | pace->sid.revision, pace->sid.num_subauth, pace->type, |
246 | pace->flags, pace->size)); | 321 | pace->flags, le16_to_cpu(pace->size))); |
247 | for (i = 0; i < num_subauth; ++i) { | 322 | for (i = 0; i < num_subauth; ++i) { |
248 | cFYI(1, ("ACE sub_auth[%d]: 0x%x", i, | 323 | cFYI(1, ("ACE sub_auth[%d]: 0x%x", i, |
249 | le32_to_cpu(pace->sid.sub_auth[i]))); | 324 | le32_to_cpu(pace->sid.sub_auth[i]))); |
@@ -346,6 +421,28 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, | |||
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 | static int parse_sid(struct cifs_sid *psid, char *end_of_acl) | 446 | static int parse_sid(struct cifs_sid *psid, char *end_of_acl) |
350 | { | 447 | { |
351 | /* BB need to add parm so we can store the SID BB */ | 448 | /* BB need to add parm so we can store the SID BB */ |
@@ -432,6 +529,46 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, | |||
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 | /* Retrieve an ACL from the server */ | 572 | /* Retrieve an ACL from the server */ |
436 | static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode, | 573 | static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode, |
437 | const char *path) | 574 | const char *path) |
@@ -487,6 +624,64 @@ static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode, | |||
487 | return pntsd; | 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 | /* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */ | 685 | /* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */ |
491 | void acl_to_uid_mode(struct inode *inode, const char *path) | 686 | void acl_to_uid_mode(struct inode *inode, const char *path) |
492 | { | 687 | { |
@@ -510,24 +705,53 @@ void acl_to_uid_mode(struct inode *inode, const char *path) | |||
510 | } | 705 | } |
511 | 706 | ||
512 | /* Convert mode bits to an ACL so we can update the ACL on the server */ | 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 | int rc = 0; | 710 | int rc = 0; |
516 | __u32 acllen = 0; | 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 | cFYI(1, ("set ACL from mode for %s", path)); | 716 | cFYI(1, ("set ACL from mode for %s", path)); |
717 | #endif | ||
520 | 718 | ||
521 | /* Get the security descriptor */ | 719 | /* Get the security descriptor */ |
522 | pntsd = get_cifs_acl(&acllen, inode, path); | 720 | pntsd = get_cifs_acl(&acllen, inode, path); |
523 | 721 | ||
524 | /* Add/Modify the three ACEs for owner, group, everyone | 722 | /* Add three ACEs for owner, group, everyone getting rid of |
525 | while retaining the other ACEs */ | 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); | 737 | rc = build_sec_desc(pntsd, pnntsd, acllen, inode, nmode); |
531 | return rc; | 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 | #endif /* CONFIG_CIFS_EXPERIMENTAL */ | 757 | #endif /* CONFIG_CIFS_EXPERIMENTAL */ |