@@ -15,6 +15,7 @@ pub use self::noop::{Args, Config, Process};
1515mod unix {
1616 use std:: io;
1717 use std:: env:: set_current_dir;
18+ use std:: ffi:: { CStr , CString } ;
1819 use std:: fs:: { File , OpenOptions } ;
1920 use std:: io:: Write ;
2021 use std:: os:: fd:: { AsFd , AsRawFd } ;
@@ -27,7 +28,7 @@ mod unix {
2728 use nix:: sys:: stat:: umask;
2829 use nix:: unistd:: { Gid , Group , Uid , User } ;
2930 use nix:: unistd:: {
30- close, chroot, dup2, fork, getpid, setgid , setsid, setuid ,
31+ close, chroot, dup2, fork, getpid, setsid,
3132 } ;
3233 use serde:: { Deserialize , Serialize } ;
3334 use crate :: config:: { ConfigFile , ConfigPath } ;
@@ -143,27 +144,131 @@ mod unix {
143144 }
144145 }
145146
146- if let Some ( user) = self . config . user . as_ref ( ) {
147- if let Err ( err) = setuid ( user. uid ) {
147+ self . set_user_and_group ( ) ?;
148+
149+ self . write_pid_file ( ) ?;
150+
151+ Ok ( ( ) )
152+ }
153+
154+ /// Changes the user and group IDs.
155+ fn set_user_and_group ( & self ) -> Result < ( ) , Failed > {
156+ // Unfortunately, this isn’t quite as portable as we want it to
157+ // be as most of the function we use are not available on some
158+ // platforms. Instead of copying the cfg attributes from the nix
159+ // crate, we define fallback functions and overwrite their symbol
160+ // if possible using a glob import.
161+ //
162+ // For setting uid and gid, we need to cascade: Use `setresuid`
163+ // if available, otherwise use `setreuid` if available, otherwise
164+ // use `setuid`; analogous for gid. We achieve this by having
165+ // the fallback call the next step which may itself be a fallback.
166+
167+ /// Dummy fallback function for `nix::unistd::initgroups`.
168+ #[ allow( dead_code) ]
169+ fn initgroups (
170+ _user : & CStr , _group : Gid
171+ ) -> Result < ( ) , nix:: errno:: Errno > {
172+ Ok ( ( ) )
173+ }
174+
175+ /// Fallback function for `nix::unistd::setresgid`.
176+ #[ allow( dead_code) ]
177+ fn setresgid (
178+ rgid : Gid , egid : Gid , _sgid : Gid
179+ ) -> Result < ( ) , nix:: errno:: Errno > {
180+ use nix:: libc:: { c_int, gid_t} ;
181+
182+ #[ allow( dead_code) ]
183+ unsafe fn setregid ( rgid : gid_t , _egid : gid_t ) -> c_int {
184+ unsafe { nix:: libc:: setgid ( rgid) }
185+ }
186+
187+ {
188+ #[ allow( unused_imports) ]
189+ use nix:: libc:: * ;
190+
191+ if unsafe { setregid ( rgid. as_raw ( ) , egid. as_raw ( ) ) } != 0 {
192+ return Err ( nix:: errno:: Errno :: last ( ) ) ;
193+ }
194+ }
195+
196+ Ok ( ( ) )
197+ }
198+
199+ /// Fallback function for `nix::unistd::setresuid`.
200+ #[ allow( dead_code) ]
201+ fn setresuid (
202+ ruid : Uid , euid : Uid , _suid : Uid
203+ ) -> Result < ( ) , nix:: errno:: Errno > {
204+ use nix:: libc:: { c_int, uid_t} ;
205+
206+ #[ allow( dead_code) ]
207+ unsafe fn setreuid ( ruid : uid_t , _euid : uid_t ) -> c_int {
208+ unsafe { nix:: libc:: setuid ( ruid) }
209+ }
210+
211+ {
212+ #[ allow( unused_imports) ]
213+ use nix:: libc:: * ;
214+
215+ if unsafe { setreuid ( ruid. as_raw ( ) , euid. as_raw ( ) ) } != 0 {
216+ return Err ( nix:: errno:: Errno :: last ( ) ) ;
217+ }
218+ }
219+
220+ Ok ( ( ) )
221+ }
222+
223+ let Some ( user) = self . config . user . as_ref ( ) else {
224+ return Ok ( ( ) )
225+ } ;
226+
227+ // If we don’t have an explicit group, we use the user’s group.
228+ let gid = self . config . group . as_ref ( ) . map ( |g| {
229+ g. gid
230+ } ) . unwrap_or_else ( || {
231+ user. gid
232+ } ) ;
233+
234+ // Let the system load the supplemental groups for the user.
235+ {
236+ #[ allow( unused_imports) ]
237+ use nix:: unistd:: * ;
238+
239+ initgroups ( & user. c_name , gid) . map_err ( |err| {
148240 error ! (
149- "Fatal: failed to set user '{}': {}" ,
150- user. name, err
241+ "failed to initialize the group access list: {err}" ,
151242 ) ;
152- return Err ( Failed )
153- }
243+ Failed
244+ } ) ? ;
154245 }
155246
156- if let Some ( group) = self . config . group . as_ref ( ) {
157- if let Err ( err) = setgid ( group. gid ) {
247+ // Set the group ID.
248+ {
249+ #[ allow( unused_imports) ]
250+ use nix:: unistd:: * ;
251+
252+ setresgid ( gid, gid, gid) . map_err ( |err| {
158253 error ! (
159- "Fatal: failed to set group '{}': {}" ,
160- group. name, err
254+ "failed to set group ID: {err}"
161255 ) ;
162- return Err ( Failed )
163- }
256+ Failed
257+ } ) ? ;
164258 }
165259
166- self . write_pid_file ( ) ?;
260+ // Set the user ID.
261+ {
262+ #[ allow( unused_imports) ]
263+ use nix:: unistd:: * ;
264+
265+ setresuid ( user. uid , user. uid , user. uid ) . map_err ( |err| {
266+ error ! (
267+ "failed to set user ID: {err}"
268+ ) ;
269+ Failed
270+ } ) ?;
271+ }
167272
168273 Ok ( ( ) )
169274 }
@@ -404,22 +509,39 @@ mod unix {
404509 #[ derive( Clone , Debug , Deserialize , Serialize ) ]
405510 #[ serde( try_from = "String" , into = "String" , expecting = "a user name" ) ]
406511 struct UserId {
407- /// The numerical user ID.
408- uid : Uid ,
409-
410512 /// The user name.
411513 ///
412- /// We keep this information so we can produce the actual config .
514+ /// This is used for error reporting .
413515 name : String ,
516+
517+ /// The user name as a C string.
518+ ///
519+ /// This is used internally. We keep both the string and C string
520+ /// versions because conversion can cause errors, so it best happens
521+ /// already when creating an object.
522+ c_name : CString ,
523+
524+ /// The numerical user ID.
525+ uid : Uid ,
526+
527+ /// The numerical group ID of the user.
528+ gid : Gid ,
529+
414530 }
415531
416532 impl TryFrom < String > for UserId {
417533 type Error = String ;
418534
419535 fn try_from ( name : String ) -> Result < Self , Self :: Error > {
536+ let Ok ( c_name) = CString :: new ( name. clone ( ) ) else {
537+ return Err ( format ! ( "invalid user name '{name}'" ) )
538+ } ;
420539 match User :: from_name ( & name) {
421540 Ok ( Some ( user) ) => {
422- Ok ( UserId { uid : user. uid , name } )
541+ Ok ( UserId {
542+ name, c_name,
543+ gid : user. gid , uid : user. uid
544+ } )
423545 }
424546 Ok ( None ) => {
425547 Err ( format ! ( "unknown user '{name}'" ) )
@@ -452,13 +574,11 @@ mod unix {
452574 #[ derive( Clone , Debug , Deserialize , Serialize ) ]
453575 #[ serde( try_from = "String" , into = "String" , expecting = "a user name" ) ]
454576 struct GroupId {
455- /// The numerical user ID.
456- gid : Gid ,
457-
458- /// The user name.
459- ///
460- /// We keep this information so we can produce the actual config.
577+ /// The group name.
461578 name : String ,
579+
580+ /// The numerical group ID.
581+ gid : Gid ,
462582 }
463583
464584 impl TryFrom < String > for GroupId {
@@ -470,10 +590,10 @@ mod unix {
470590 Ok ( GroupId { gid : group. gid , name } )
471591 }
472592 Ok ( None ) => {
473- Err ( format ! ( "unknown user '{name}'" ) )
593+ Err ( format ! ( "unknown group '{name}'" ) )
474594 }
475595 Err ( err) => {
476- Err ( format ! ( "failed to resolve user '{name}': {err}" ) )
596+ Err ( format ! ( "failed to resolve group '{name}': {err}" ) )
477597 }
478598 }
479599 }
0 commit comments