@@ -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,127 @@ 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 cascase: 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+ use nix:: libc:: * ;
189+
190+ if unsafe { setregid ( rgid. as_raw ( ) , egid. as_raw ( ) ) } != 0 {
191+ return Err ( nix:: errno:: Errno :: last ( ) ) ;
192+ }
193+ }
194+
195+ Ok ( ( ) )
196+ }
197+
198+ /// Fallback function for `nix::unistd::setresuid`.
199+ #[ allow( dead_code) ]
200+ fn setresuid (
201+ ruid : Uid , euid : Uid , _suid : Uid
202+ ) -> Result < ( ) , nix:: errno:: Errno > {
203+ use nix:: libc:: { c_int, uid_t} ;
204+
205+ #[ allow( dead_code) ]
206+ unsafe fn setreuid ( ruid : uid_t , _euid : uid_t ) -> c_int {
207+ unsafe { nix:: libc:: setuid ( ruid) }
208+ }
209+
210+ {
211+ use nix:: libc:: * ;
212+
213+ if unsafe { setreuid ( ruid. as_raw ( ) , euid. as_raw ( ) ) } != 0 {
214+ return Err ( nix:: errno:: Errno :: last ( ) ) ;
215+ }
216+ }
217+
218+ Ok ( ( ) )
219+ }
220+
221+ let Some ( user) = self . config . user . as_ref ( ) else {
222+ return Ok ( ( ) )
223+ } ;
224+
225+ // If we don’t have an explicit group, we use the user’s group.
226+ let gid = self . config . group . as_ref ( ) . map ( |g| {
227+ g. gid
228+ } ) . unwrap_or_else ( || {
229+ user. gid
230+ } ) ;
231+
232+ // Let the system load the supplemental groups for the user.
233+ {
234+ use nix:: unistd:: * ;
235+
236+ initgroups ( & user. c_name , gid) . map_err ( |err| {
148237 error ! (
149- "Fatal: failed to set user '{}' : {}" ,
238+ "failed to initgroups {} : {}" ,
150239 user. name, err
151240 ) ;
152- return Err ( Failed )
153- }
241+ Failed
242+ } ) ? ;
154243 }
155244
156- if let Some ( group) = self . config . group . as_ref ( ) {
157- if let Err ( err) = setgid ( group. gid ) {
245+ // Set the group ID.
246+ {
247+ use nix:: unistd:: * ;
248+
249+ setresgid ( gid, gid, gid) . map_err ( |err| {
158250 error ! (
159- "Fatal: failed to set group '{}': {}" ,
160- group. name, err
251+ "failed to set group ID: {err}"
161252 ) ;
162- return Err ( Failed )
163- }
253+ Failed
254+ } ) ? ;
164255 }
165256
166- self . write_pid_file ( ) ?;
257+ // Set the user ID.
258+ {
259+ use nix:: unistd:: * ;
260+
261+ setresuid ( user. uid , user. uid , user. uid ) . map_err ( |err| {
262+ error ! (
263+ "failed to set user ID: {err}"
264+ ) ;
265+ Failed
266+ } ) ?;
267+ }
167268
168269 Ok ( ( ) )
169270 }
@@ -404,22 +505,39 @@ mod unix {
404505 #[ derive( Clone , Debug , Deserialize , Serialize ) ]
405506 #[ serde( try_from = "String" , into = "String" , expecting = "a user name" ) ]
406507 struct UserId {
407- /// The numerical user ID.
408- uid : Uid ,
409-
410508 /// The user name.
411509 ///
412- /// We keep this information so we can produce the actual config .
510+ /// This is used for error reporting .
413511 name : String ,
512+
513+ /// The user name as a C string.
514+ ///
515+ /// This is used internally. We keep both the string and C string
516+ /// versions because conversion can cause errors and we want to catch
517+ /// those early.
518+ c_name : CString ,
519+
520+ /// The numerical user ID.
521+ uid : Uid ,
522+
523+ /// The numerical group ID of the user.
524+ gid : Gid ,
525+
414526 }
415527
416528 impl TryFrom < String > for UserId {
417529 type Error = String ;
418530
419531 fn try_from ( name : String ) -> Result < Self , Self :: Error > {
532+ let Ok ( c_name) = CString :: new ( name. clone ( ) ) else {
533+ return Err ( format ! ( "invalid user name '{name}'" ) )
534+ } ;
420535 match User :: from_name ( & name) {
421536 Ok ( Some ( user) ) => {
422- Ok ( UserId { uid : user. uid , name } )
537+ Ok ( UserId {
538+ name, c_name,
539+ gid : user. gid , uid : user. uid
540+ } )
423541 }
424542 Ok ( None ) => {
425543 Err ( format ! ( "unknown user '{name}'" ) )
@@ -452,13 +570,11 @@ mod unix {
452570 #[ derive( Clone , Debug , Deserialize , Serialize ) ]
453571 #[ serde( try_from = "String" , into = "String" , expecting = "a user name" ) ]
454572 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.
573+ /// The group name.
461574 name : String ,
575+
576+ /// The numerical group ID.
577+ gid : Gid ,
462578 }
463579
464580 impl TryFrom < String > for GroupId {
@@ -470,10 +586,10 @@ mod unix {
470586 Ok ( GroupId { gid : group. gid , name } )
471587 }
472588 Ok ( None ) => {
473- Err ( format ! ( "unknown user '{name}'" ) )
589+ Err ( format ! ( "unknown group '{name}'" ) )
474590 }
475591 Err ( err) => {
476- Err ( format ! ( "failed to resolve user '{name}': {err}" ) )
592+ Err ( format ! ( "failed to resolve group '{name}': {err}" ) )
477593 }
478594 }
479595 }
0 commit comments