2
2
3
3
use clap:: { crate_version, App , Arg } ;
4
4
use fuser:: consts:: FOPEN_DIRECT_IO ;
5
+ #[ cfg( feature = "abi-7-26" ) ]
6
+ use fuser:: consts:: FUSE_HANDLE_KILLPRIV ;
7
+ #[ cfg( feature = "abi-7-31" ) ]
8
+ use fuser:: consts:: FUSE_WRITE_KILL_PRIV ;
5
9
use fuser:: TimeOrNow :: Now ;
6
10
use fuser:: {
7
11
Filesystem , KernelConfig , MountOption , ReplyAttr , ReplyCreate , ReplyData , ReplyDirectory ,
8
12
ReplyEmpty , ReplyEntry , ReplyOpen , ReplyStatfs , ReplyWrite , ReplyXattr , Request , TimeOrNow ,
9
13
FUSE_ROOT_ID ,
10
14
} ;
15
+ #[ cfg( feature = "abi-7-26" ) ]
16
+ use log:: info;
11
17
use log:: LevelFilter ;
12
18
use log:: { debug, warn} ;
13
19
use serde:: { Deserialize , Serialize } ;
@@ -19,6 +25,7 @@ use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
19
25
use std:: os:: raw:: c_int;
20
26
use std:: os:: unix:: ffi:: OsStrExt ;
21
27
use std:: os:: unix:: fs:: FileExt ;
28
+ #[ cfg( target_os = "linux" ) ]
22
29
use std:: os:: unix:: io:: IntoRawFd ;
23
30
use std:: path:: { Path , PathBuf } ;
24
31
use std:: sync:: atomic:: { AtomicU64 , Ordering } ;
@@ -102,6 +109,22 @@ fn parse_xattr_namespace(key: &[u8]) -> Result<XattrNamespace, c_int> {
102
109
return Err ( libc:: ENOTSUP ) ;
103
110
}
104
111
112
+ fn clear_suid_sgid ( attr : & mut InodeAttributes ) {
113
+ attr. mode &= !libc:: S_ISUID as u16 ;
114
+ // SGID is only suppose to be cleared if XGRP is set
115
+ if attr. mode & libc:: S_IXGRP as u16 != 0 {
116
+ attr. mode &= !libc:: S_ISGID as u16 ;
117
+ }
118
+ }
119
+
120
+ fn creation_gid ( parent : & InodeAttributes , gid : u32 ) -> u32 {
121
+ if parent. mode & libc:: S_ISGID as u16 != 0 {
122
+ return parent. gid ;
123
+ }
124
+
125
+ gid
126
+ }
127
+
105
128
fn xattr_access_check (
106
129
key : & [ u8 ] ,
107
130
access_mask : i32 ,
@@ -223,14 +246,40 @@ struct SimpleFS {
223
246
data_dir : String ,
224
247
next_file_handle : AtomicU64 ,
225
248
direct_io : bool ,
249
+ suid_support : bool ,
226
250
}
227
251
228
252
impl SimpleFS {
229
- fn new ( data_dir : String , direct_io : bool ) -> SimpleFS {
230
- SimpleFS {
231
- data_dir,
232
- next_file_handle : AtomicU64 :: new ( 1 ) ,
233
- direct_io,
253
+ fn new (
254
+ data_dir : String ,
255
+ direct_io : bool ,
256
+ #[ allow( unused_variables) ] suid_support : bool ,
257
+ ) -> SimpleFS {
258
+ #[ cfg( feature = "abi-7-26" ) ]
259
+ {
260
+ SimpleFS {
261
+ data_dir,
262
+ next_file_handle : AtomicU64 :: new ( 1 ) ,
263
+ direct_io,
264
+ suid_support,
265
+ }
266
+ }
267
+ #[ cfg( not( feature = "abi-7-26" ) ) ]
268
+ {
269
+ SimpleFS {
270
+ data_dir,
271
+ next_file_handle : AtomicU64 :: new ( 1 ) ,
272
+ direct_io,
273
+ suid_support : false ,
274
+ }
275
+ }
276
+ }
277
+
278
+ fn creation_mode ( & self , mode : u32 ) -> u16 {
279
+ if !self . suid_support {
280
+ ( mode & !( libc:: S_ISUID | libc:: S_ISGID ) as u32 ) as u16
281
+ } else {
282
+ mode as u16
234
283
}
235
284
}
236
285
@@ -373,6 +422,9 @@ impl SimpleFS {
373
422
attrs. last_metadata_changed = time_now ( ) ;
374
423
attrs. last_modified = time_now ( ) ;
375
424
425
+ // Clear SETUID & SETGID on truncate
426
+ clear_suid_sgid ( & mut attrs) ;
427
+
376
428
self . write_inode ( & attrs) ;
377
429
378
430
Ok ( attrs)
@@ -424,7 +476,14 @@ impl SimpleFS {
424
476
}
425
477
426
478
impl Filesystem for SimpleFS {
427
- fn init ( & mut self , _req : & Request , _config : & mut KernelConfig ) -> Result < ( ) , c_int > {
479
+ fn init (
480
+ & mut self ,
481
+ _req : & Request ,
482
+ #[ allow( unused_variables) ] config : & mut KernelConfig ,
483
+ ) -> Result < ( ) , c_int > {
484
+ #[ cfg( feature = "abi-7-26" ) ]
485
+ config. add_capabilities ( FUSE_HANDLE_KILLPRIV ) . unwrap ( ) ;
486
+
428
487
fs:: create_dir_all ( Path :: new ( & self . data_dir ) . join ( "inodes" ) ) . unwrap ( ) ;
429
488
fs:: create_dir_all ( Path :: new ( & self . data_dir ) . join ( "contents" ) ) . unwrap ( ) ;
430
489
if self . get_inode ( FUSE_ROOT_ID ) . is_err ( ) {
@@ -516,7 +575,16 @@ impl Filesystem for SimpleFS {
516
575
reply. error ( libc:: EPERM ) ;
517
576
return ;
518
577
}
519
- attrs. mode = mode as u16 ;
578
+ if req. uid ( ) != 0
579
+ && req. gid ( ) != attrs. gid
580
+ && !get_groups ( req. pid ( ) ) . contains ( & attrs. gid )
581
+ {
582
+ // If SGID is set and the file belongs to a group that the caller is not part of
583
+ // then the SGID bit is suppose to be cleared during chmod
584
+ attrs. mode = ( mode & !libc:: S_ISGID as u32 ) as u16 ;
585
+ } else {
586
+ attrs. mode = mode as u16 ;
587
+ }
520
588
attrs. last_metadata_changed = time_now ( ) ;
521
589
self . write_inode ( & attrs) ;
522
590
reply. attr ( & Duration :: new ( 0 , 0 ) , & attrs. into ( ) ) ;
@@ -547,11 +615,22 @@ impl Filesystem for SimpleFS {
547
615
return ;
548
616
}
549
617
618
+ if attrs. mode & ( libc:: S_IXUSR | libc:: S_IXGRP | libc:: S_IXOTH ) as u16 != 0 {
619
+ // SUID & SGID are suppose to be cleared when chown'ing an executable file
620
+ clear_suid_sgid ( & mut attrs) ;
621
+ }
622
+
550
623
if let Some ( uid) = uid {
551
624
attrs. uid = uid;
625
+ // Clear SETUID on owner change
626
+ attrs. mode &= !libc:: S_ISUID as u16 ;
552
627
}
553
628
if let Some ( gid) = gid {
554
629
attrs. gid = gid;
630
+ // Clear SETGID unless user is root
631
+ if req. uid ( ) != 0 {
632
+ attrs. mode &= !libc:: S_ISGID as u16 ;
633
+ }
555
634
}
556
635
attrs. last_metadata_changed = time_now ( ) ;
557
636
self . write_inode ( & attrs) ;
@@ -664,7 +743,7 @@ impl Filesystem for SimpleFS {
664
743
req : & Request ,
665
744
parent : u64 ,
666
745
name : & OsStr ,
667
- mode : u32 ,
746
+ mut mode : u32 ,
668
747
_umask : u32 ,
669
748
_rdev : u32 ,
670
749
reply : ReplyEntry ,
@@ -709,6 +788,10 @@ impl Filesystem for SimpleFS {
709
788
parent_attrs. last_metadata_changed = time_now ( ) ;
710
789
self . write_inode ( & parent_attrs) ;
711
790
791
+ if req. uid ( ) != 0 {
792
+ mode &= !( libc:: S_ISUID | libc:: S_ISGID ) as u32 ;
793
+ }
794
+
712
795
let inode = self . allocate_next_inode ( ) ;
713
796
let attrs = InodeAttributes {
714
797
inode,
@@ -718,11 +801,10 @@ impl Filesystem for SimpleFS {
718
801
last_modified : time_now ( ) ,
719
802
last_metadata_changed : time_now ( ) ,
720
803
kind : as_file_kind ( mode) ,
721
- // TODO: suid/sgid not supported
722
- mode : ( mode & !( libc:: S_ISUID | libc:: S_ISGID ) as u32 ) as u16 ,
804
+ mode : self . creation_mode ( mode) ,
723
805
hardlinks : 1 ,
724
806
uid : req. uid ( ) ,
725
- gid : req. gid ( ) ,
807
+ gid : creation_gid ( & parent_attrs , req. gid ( ) ) ,
726
808
xattrs : Default :: default ( ) ,
727
809
} ;
728
810
self . write_inode ( & attrs) ;
@@ -748,7 +830,7 @@ impl Filesystem for SimpleFS {
748
830
req : & Request ,
749
831
parent : u64 ,
750
832
name : & OsStr ,
751
- mode : u32 ,
833
+ mut mode : u32 ,
752
834
_umask : u32 ,
753
835
reply : ReplyEntry ,
754
836
) {
@@ -781,6 +863,13 @@ impl Filesystem for SimpleFS {
781
863
parent_attrs. last_metadata_changed = time_now ( ) ;
782
864
self . write_inode ( & parent_attrs) ;
783
865
866
+ if req. uid ( ) != 0 {
867
+ mode &= !( libc:: S_ISUID | libc:: S_ISGID ) as u32 ;
868
+ }
869
+ if parent_attrs. mode & libc:: S_ISGID as u16 != 0 {
870
+ mode |= libc:: S_ISGID as u32 ;
871
+ }
872
+
784
873
let inode = self . allocate_next_inode ( ) ;
785
874
let attrs = InodeAttributes {
786
875
inode,
@@ -790,11 +879,10 @@ impl Filesystem for SimpleFS {
790
879
last_modified : time_now ( ) ,
791
880
last_metadata_changed : time_now ( ) ,
792
881
kind : FileKind :: Directory ,
793
- // TODO: suid/sgid not supported
794
- mode : ( mode & !( libc:: S_ISUID | libc:: S_ISGID ) as u32 ) as u16 ,
882
+ mode : self . creation_mode ( mode) ,
795
883
hardlinks : 2 , // Directories start with link count of 2, since they have a self link
796
884
uid : req. uid ( ) ,
797
- gid : req. gid ( ) ,
885
+ gid : creation_gid ( & parent_attrs , req. gid ( ) ) ,
798
886
xattrs : Default :: default ( ) ,
799
887
} ;
800
888
self . write_inode ( & attrs) ;
@@ -938,6 +1026,29 @@ impl Filesystem for SimpleFS {
938
1026
reply : ReplyEntry ,
939
1027
) {
940
1028
debug ! ( "symlink() called with {:?} {:?} {:?}" , parent, name, link) ;
1029
+ let mut parent_attrs = match self . get_inode ( parent) {
1030
+ Ok ( attrs) => attrs,
1031
+ Err ( error_code) => {
1032
+ reply. error ( error_code) ;
1033
+ return ;
1034
+ }
1035
+ } ;
1036
+
1037
+ if !check_access (
1038
+ parent_attrs. uid ,
1039
+ parent_attrs. gid ,
1040
+ parent_attrs. mode ,
1041
+ req. uid ( ) ,
1042
+ req. gid ( ) ,
1043
+ libc:: W_OK ,
1044
+ ) {
1045
+ reply. error ( libc:: EACCES ) ;
1046
+ return ;
1047
+ }
1048
+ parent_attrs. last_modified = time_now ( ) ;
1049
+ parent_attrs. last_metadata_changed = time_now ( ) ;
1050
+ self . write_inode ( & parent_attrs) ;
1051
+
941
1052
let inode = self . allocate_next_inode ( ) ;
942
1053
let attrs = InodeAttributes {
943
1054
inode,
@@ -950,7 +1061,7 @@ impl Filesystem for SimpleFS {
950
1061
mode : 0o777 ,
951
1062
hardlinks : 1 ,
952
1063
uid : req. uid ( ) ,
953
- gid : req. gid ( ) ,
1064
+ gid : creation_gid ( & parent_attrs , req. gid ( ) ) ,
954
1065
xattrs : Default :: default ( ) ,
955
1066
} ;
956
1067
@@ -1300,7 +1411,7 @@ impl Filesystem for SimpleFS {
1300
1411
offset : i64 ,
1301
1412
data : & [ u8 ] ,
1302
1413
_write_flags : u32 ,
1303
- _flags : i32 ,
1414
+ # [ allow ( unused_variables ) ] flags : i32 ,
1304
1415
_lock_owner : Option < u64 > ,
1305
1416
reply : ReplyWrite ,
1306
1417
) {
@@ -1322,6 +1433,13 @@ impl Filesystem for SimpleFS {
1322
1433
if data. len ( ) + offset as usize > attrs. size as usize {
1323
1434
attrs. size = ( data. len ( ) + offset as usize ) as u64 ;
1324
1435
}
1436
+ // #[cfg(feature = "abi-7-31")]
1437
+ // if flags & FUSE_WRITE_KILL_PRIV as i32 != 0 {
1438
+ // clear_suid_sgid(&mut attrs);
1439
+ // }
1440
+ // XXX: In theory we should only need to do this when WRITE_KILL_PRIV is set for 7.31+
1441
+ // However, xfstests fail in that case
1442
+ clear_suid_sgid ( & mut attrs) ;
1325
1443
self . write_inode ( & attrs) ;
1326
1444
1327
1445
reply. written ( data. len ( ) as u32 ) ;
@@ -1574,7 +1692,7 @@ impl Filesystem for SimpleFS {
1574
1692
req : & Request ,
1575
1693
parent : u64 ,
1576
1694
name : & OsStr ,
1577
- mode : u32 ,
1695
+ mut mode : u32 ,
1578
1696
_umask : u32 ,
1579
1697
flags : i32 ,
1580
1698
reply : ReplyCreate ,
@@ -1619,6 +1737,10 @@ impl Filesystem for SimpleFS {
1619
1737
parent_attrs. last_metadata_changed = time_now ( ) ;
1620
1738
self . write_inode ( & parent_attrs) ;
1621
1739
1740
+ if req. uid ( ) != 0 {
1741
+ mode &= !( libc:: S_ISUID | libc:: S_ISGID ) as u32 ;
1742
+ }
1743
+
1622
1744
let inode = self . allocate_next_inode ( ) ;
1623
1745
let attrs = InodeAttributes {
1624
1746
inode,
@@ -1628,11 +1750,10 @@ impl Filesystem for SimpleFS {
1628
1750
last_modified : time_now ( ) ,
1629
1751
last_metadata_changed : time_now ( ) ,
1630
1752
kind : as_file_kind ( mode) ,
1631
- // TODO: suid/sgid not supported
1632
- mode : ( mode & !( libc:: S_ISUID | libc:: S_ISGID ) as u32 ) as u16 ,
1753
+ mode : self . creation_mode ( mode) ,
1633
1754
hardlinks : 1 ,
1634
1755
uid : req. uid ( ) ,
1635
- gid : req. gid ( ) ,
1756
+ gid : creation_gid ( & parent_attrs , req. gid ( ) ) ,
1636
1757
xattrs : Default :: default ( ) ,
1637
1758
} ;
1638
1759
self . write_inode ( & attrs) ;
@@ -1855,6 +1976,11 @@ fn main() {
1855
1976
. long ( "fsck" )
1856
1977
. help ( "Run a filesystem check" ) ,
1857
1978
)
1979
+ . arg (
1980
+ Arg :: with_name ( "suid" )
1981
+ . long ( "suid" )
1982
+ . help ( "Enable setuid support when run as root" ) ,
1983
+ )
1858
1984
. arg (
1859
1985
Arg :: with_name ( "v" )
1860
1986
. short ( "v" )
@@ -1876,10 +2002,21 @@ fn main() {
1876
2002
. filter_level ( log_level)
1877
2003
. init ( ) ;
1878
2004
1879
- let mut options = vec ! [
1880
- MountOption :: FSName ( "fuser" . to_string( ) ) ,
1881
- MountOption :: AutoUnmount ,
1882
- ] ;
2005
+ let mut options = vec ! [ MountOption :: FSName ( "fuser" . to_string( ) ) ] ;
2006
+
2007
+ #[ cfg( feature = "abi-7-26" ) ]
2008
+ {
2009
+ if matches. is_present ( "suid" ) {
2010
+ info ! ( "setuid bit support enabled" ) ;
2011
+ options. push ( MountOption :: Suid ) ;
2012
+ } else {
2013
+ options. push ( MountOption :: AutoUnmount ) ;
2014
+ }
2015
+ }
2016
+ #[ cfg( not( feature = "abi-7-26" ) ) ]
2017
+ {
2018
+ options. push ( MountOption :: AutoUnmount ) ;
2019
+ }
1883
2020
if let Ok ( enabled) = fuse_allow_other_enabled ( ) {
1884
2021
if enabled {
1885
2022
options. push ( MountOption :: AllowOther ) ;
@@ -1896,7 +2033,11 @@ fn main() {
1896
2033
. to_string ( ) ;
1897
2034
1898
2035
fuser:: mount2 (
1899
- SimpleFS :: new ( data_dir, matches. is_present ( "direct-io" ) ) ,
2036
+ SimpleFS :: new (
2037
+ data_dir,
2038
+ matches. is_present ( "direct-io" ) ,
2039
+ matches. is_present ( "suid" ) ,
2040
+ ) ,
1900
2041
mountpoint,
1901
2042
& options,
1902
2043
)
0 commit comments