1
1
use crate :: common:: { HARDENED_ENUM_VALUE_0 , HARDENED_ENUM_VALUE_1 , HARDENED_ENUM_VALUE_2 } ;
2
+ use crate :: sudo:: { SudoListOptions , SudoRunOptions , SudoValidateOptions } ;
3
+ use crate :: sudoers:: Sudoers ;
2
4
use crate :: system:: { Group , Hostname , Process , User } ;
3
5
4
- use super :: resolve:: CurrentUser ;
5
6
use super :: {
6
7
command:: CommandAndArguments ,
7
- resolve:: { resolve_launch_and_shell , resolve_target_user_and_group} ,
8
- Error , SudoPath , SudoString ,
8
+ resolve:: { resolve_shell , resolve_target_user_and_group, CurrentUser } ,
9
+ Error , SudoPath ,
9
10
} ;
10
11
11
- #[ derive( Clone , Copy ) ]
12
- pub enum ContextAction {
13
- List ,
14
- Run ,
15
- Validate ,
16
- }
17
-
18
- // this is a bit of a hack to keep the existing `Context` API working
19
- #[ derive( Clone ) ]
20
- pub struct OptionsForContext {
21
- pub chdir : Option < SudoPath > ,
22
- pub group : Option < SudoString > ,
23
- pub login : bool ,
24
- pub non_interactive : bool ,
25
- pub positional_args : Vec < String > ,
26
- pub reset_timestamp : bool ,
27
- pub shell : bool ,
28
- pub stdin : bool ,
29
- pub user : Option < SudoString > ,
30
- pub action : ContextAction ,
31
- }
32
-
33
12
#[ derive( Debug ) ]
34
13
pub struct Context {
35
14
// cli options
@@ -50,7 +29,7 @@ pub struct Context {
50
29
pub password_feedback : bool ,
51
30
}
52
31
53
- #[ derive( Debug , Default , PartialEq , Eq ) ]
32
+ #[ derive( Clone , Copy , Debug , Default , PartialEq , Eq ) ]
54
33
#[ repr( u32 ) ]
55
34
pub enum LaunchType {
56
35
#[ default]
@@ -60,7 +39,59 @@ pub enum LaunchType {
60
39
}
61
40
62
41
impl Context {
63
- pub fn build_from_options ( sudo_options : OptionsForContext ) -> Result < Context , Error > {
42
+ pub fn from_run_opts (
43
+ sudo_options : SudoRunOptions ,
44
+ policy : & mut Sudoers ,
45
+ ) -> Result < Context , Error > {
46
+ let hostname = Hostname :: resolve ( ) ;
47
+ let current_user = CurrentUser :: resolve ( ) ?;
48
+
49
+ let ( target_user, target_group) =
50
+ resolve_target_user_and_group ( & sudo_options. user , & sudo_options. group , & current_user) ?;
51
+
52
+ let launch = if sudo_options. login {
53
+ LaunchType :: Login
54
+ } else if sudo_options. shell {
55
+ LaunchType :: Shell
56
+ } else {
57
+ LaunchType :: Direct
58
+ } ;
59
+
60
+ let shell = resolve_shell ( launch, & current_user, & target_user) ;
61
+
62
+ let override_path = policy. search_path ( & hostname, & current_user, & target_user) ;
63
+
64
+ let command = {
65
+ let system_path;
66
+
67
+ let path = if let Some ( path) = override_path {
68
+ path
69
+ } else {
70
+ system_path = std:: env:: var ( "PATH" ) . unwrap_or_default ( ) ;
71
+ system_path. as_ref ( )
72
+ } ;
73
+
74
+ CommandAndArguments :: build_from_args ( shell, sudo_options. positional_args , path)
75
+ } ;
76
+
77
+ Ok ( Context {
78
+ hostname,
79
+ command,
80
+ current_user,
81
+ target_user,
82
+ target_group,
83
+ use_session_records : !sudo_options. reset_timestamp ,
84
+ launch,
85
+ chdir : sudo_options. chdir ,
86
+ stdin : sudo_options. stdin ,
87
+ non_interactive : sudo_options. non_interactive ,
88
+ process : Process :: new ( ) ,
89
+ use_pty : true ,
90
+ password_feedback : false ,
91
+ } )
92
+ }
93
+
94
+ pub fn from_validate_opts ( sudo_options : SudoValidateOptions ) -> Result < Context , Error > {
64
95
let hostname = Hostname :: resolve ( ) ;
65
96
let current_user = CurrentUser :: resolve ( ) ?;
66
97
let ( target_user, target_group) =
@@ -74,7 +105,7 @@ impl Context {
74
105
target_group,
75
106
use_session_records : !sudo_options. reset_timestamp ,
76
107
launch : Default :: default ( ) ,
77
- chdir : sudo_options . chdir ,
108
+ chdir : None ,
78
109
stdin : sudo_options. stdin ,
79
110
non_interactive : sudo_options. non_interactive ,
80
111
process : Process :: new ( ) ,
@@ -83,38 +114,46 @@ impl Context {
83
114
} )
84
115
}
85
116
86
- pub fn supply_command (
87
- self ,
88
- sudo_options : OptionsForContext ,
89
- secure_path : Option < & str > ,
117
+ pub fn from_list_opts (
118
+ sudo_options : SudoListOptions ,
119
+ policy : & mut Sudoers ,
90
120
) -> Result < Context , Error > {
91
- let ( launch, shell) =
92
- resolve_launch_and_shell ( & sudo_options, & self . current_user , & self . target_user ) ;
93
-
94
- let command = match sudo_options. action {
95
- ContextAction :: Run | ContextAction :: List
96
- if !sudo_options. positional_args . is_empty ( ) =>
97
- {
98
- let system_path;
99
-
100
- let path = if let Some ( path) = secure_path {
101
- path
102
- } else {
103
- system_path = std:: env:: var ( "PATH" ) . unwrap_or_default ( ) ;
104
- system_path. as_ref ( )
105
- } ;
106
-
107
- CommandAndArguments :: build_from_args ( shell, sudo_options. positional_args , path)
108
- }
109
-
110
- // FIXME `Default` is being used as `Option::None`
111
- _ => Default :: default ( ) ,
121
+ let hostname = Hostname :: resolve ( ) ;
122
+ let current_user = CurrentUser :: resolve ( ) ?;
123
+ let ( target_user, target_group) =
124
+ resolve_target_user_and_group ( & sudo_options. user , & sudo_options. group , & current_user) ?;
125
+
126
+ let override_path = policy. search_path ( & hostname, & current_user, & target_user) ;
127
+
128
+ let command = if sudo_options. positional_args . is_empty ( ) {
129
+ Default :: default ( )
130
+ } else {
131
+ let system_path;
132
+
133
+ let path = if let Some ( path) = override_path {
134
+ path
135
+ } else {
136
+ system_path = std:: env:: var ( "PATH" ) . unwrap_or_default ( ) ;
137
+ system_path. as_ref ( )
138
+ } ;
139
+
140
+ CommandAndArguments :: build_from_args ( None , sudo_options. positional_args , path)
112
141
} ;
113
142
114
- Ok ( Self {
115
- launch ,
143
+ Ok ( Context {
144
+ hostname ,
116
145
command,
117
- ..self
146
+ current_user,
147
+ target_user,
148
+ target_group,
149
+ use_session_records : !sudo_options. reset_timestamp ,
150
+ launch : Default :: default ( ) ,
151
+ chdir : None ,
152
+ stdin : sudo_options. stdin ,
153
+ non_interactive : sudo_options. non_interactive ,
154
+ process : Process :: new ( ) ,
155
+ use_pty : true ,
156
+ password_feedback : false ,
118
157
} )
119
158
}
120
159
}
@@ -125,23 +164,18 @@ mod tests {
125
164
sudo:: SudoAction ,
126
165
system:: { interface:: UserId , Hostname } ,
127
166
} ;
128
- use std:: collections:: HashMap ;
129
167
130
168
use super :: Context ;
131
169
132
170
#[ test]
133
- fn test_build_context ( ) {
171
+ fn test_build_run_context ( ) {
134
172
let options = SudoAction :: try_parse_from ( [ "sudo" , "echo" , "hello" ] )
135
173
. unwrap ( )
136
174
. try_into_run ( )
137
175
. ok ( )
138
176
. unwrap ( ) ;
139
- let path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ;
140
- let ( ctx_opts, _pipe_opts) = options. into ( ) ;
141
- let context = Context :: build_from_options ( ctx_opts, Some ( path) ) . unwrap ( ) ;
142
177
143
- let mut target_environment = HashMap :: new ( ) ;
144
- target_environment. insert ( "SUDO_USER" . to_string ( ) , context. current_user . name . clone ( ) ) ;
178
+ let context = Context :: from_run_opts ( options, & mut Default :: default ( ) ) . unwrap ( ) ;
145
179
146
180
if cfg ! ( target_os = "linux" ) {
147
181
// this assumes /bin is a symlink on /usr/bin, like it is on modern Debian/Ubuntu
0 commit comments