1
1
use anyhow:: { Context , Result } ;
2
2
use bootc_utils:: CommandRunExt ;
3
3
use bootc_utils:: PathQuotedDisplay ;
4
+ use openssh_keys:: PublicKey ;
4
5
use rustix:: fs:: Uid ;
5
6
use rustix:: process:: geteuid;
6
7
use rustix:: process:: getuid;
@@ -10,6 +11,8 @@ use std::collections::BTreeMap;
10
11
use std:: collections:: BTreeSet ;
11
12
use std:: fmt:: Display ;
12
13
use std:: fmt:: Formatter ;
14
+ use std:: fs:: File ;
15
+ use std:: io:: BufReader ;
13
16
use std:: os:: unix:: process:: CommandExt ;
14
17
use std:: process:: Command ;
15
18
use uzers:: os:: unix:: UserExt ;
@@ -84,12 +87,12 @@ impl Drop for UidChange {
84
87
#[ derive( Clone , Debug ) ]
85
88
pub ( crate ) struct UserKeys {
86
89
pub ( crate ) user : String ,
87
- pub ( crate ) authorized_keys : String ,
90
+ pub ( crate ) authorized_keys : Vec < PublicKey > ,
88
91
}
89
92
90
93
impl UserKeys {
91
94
pub ( crate ) fn num_keys ( & self ) -> usize {
92
- self . authorized_keys . lines ( ) . count ( )
95
+ self . authorized_keys . len ( )
93
96
}
94
97
}
95
98
@@ -135,9 +138,9 @@ impl<'a> SshdConfig<'a> {
135
138
}
136
139
}
137
140
138
- fn get_keys_from_files ( user : & uzers:: User , keyfiles : & Vec < & str > ) -> Result < String > {
141
+ fn get_keys_from_files ( user : & uzers:: User , keyfiles : & Vec < & str > ) -> Result < Vec < PublicKey > > {
139
142
let home_dir = user. home_dir ( ) ;
140
- let mut user_authorized_keys = String :: new ( ) ;
143
+ let mut user_authorized_keys: Vec < PublicKey > = Vec :: new ( ) ;
141
144
142
145
for keyfile in keyfiles {
143
146
let user_authorized_keys_path = home_dir. join ( keyfile) ;
@@ -159,16 +162,16 @@ fn get_keys_from_files(user: &uzers::User, keyfiles: &Vec<&str>) -> Result<Strin
159
162
// shouldn't through symlinks
160
163
let _uid_change = UidChange :: new ( user_uid) ?;
161
164
162
- let key = std :: fs :: read_to_string ( & user_authorized_keys_path)
165
+ let file = File :: open ( user_authorized_keys_path)
163
166
. context ( "Failed to read user's authorized keys" ) ?;
164
- user_authorized_keys . push_str ( key . as_str ( ) ) ;
165
- user_authorized_keys. push ( '\n' ) ;
167
+ let mut keys = PublicKey :: read_keys ( BufReader :: new ( file ) ) ? ;
168
+ user_authorized_keys. append ( & mut keys ) ;
166
169
}
167
170
168
171
Ok ( user_authorized_keys)
169
172
}
170
173
171
- fn get_keys_from_command ( command : & str , command_user : & str ) -> Result < String > {
174
+ fn get_keys_from_command ( command : & str , command_user : & str ) -> Result < Vec < PublicKey > > {
172
175
let user_config = uzers:: get_user_by_name ( command_user) . context ( format ! (
173
176
"authorized_keys_command_user {} not found" ,
174
177
command_user
@@ -177,9 +180,10 @@ fn get_keys_from_command(command: &str, command_user: &str) -> Result<String> {
177
180
let mut cmd = Command :: new ( command) ;
178
181
cmd. uid ( user_config. uid ( ) ) ;
179
182
let output = cmd
180
- . run_get_string ( )
183
+ . run_get_output ( )
181
184
. context ( format ! ( "running authorized_keys_command {}" , command) ) ?;
182
- Ok ( output)
185
+ let keys = PublicKey :: read_keys ( output) ?;
186
+ Ok ( keys)
183
187
}
184
188
185
189
pub ( crate ) fn get_all_users_keys ( ) -> Result < Vec < UserKeys > > {
@@ -200,26 +204,26 @@ pub(crate) fn get_all_users_keys() -> Result<Vec<UserKeys>> {
200
204
let user_info = uzers:: get_user_by_name ( user_name. as_str ( ) )
201
205
. context ( format ! ( "user {} not found" , user_name) ) ?;
202
206
203
- let mut user_authorized_keys = String :: new ( ) ;
207
+ let mut user_authorized_keys: Vec < PublicKey > = Vec :: new ( ) ;
204
208
if !sshd_config. authorized_keys_files . is_empty ( ) {
205
- let keys = get_keys_from_files ( & user_info, & sshd_config. authorized_keys_files ) ?;
206
- user_authorized_keys. push_str ( keys. as_str ( ) ) ;
209
+ let mut keys = get_keys_from_files ( & user_info, & sshd_config. authorized_keys_files ) ?;
210
+ user_authorized_keys. append ( & mut keys) ;
207
211
}
208
212
209
213
if sshd_config. authorized_keys_command != "none" {
210
- let keys = get_keys_from_command (
214
+ let mut keys = get_keys_from_command (
211
215
& sshd_config. authorized_keys_command ,
212
216
& sshd_config. authorized_keys_command_user ,
213
217
) ?;
214
- user_authorized_keys. push_str ( keys. as_str ( ) ) ;
218
+ user_authorized_keys. append ( & mut keys) ;
215
219
} ;
216
220
217
221
let user_name = user_info
218
222
. name ( )
219
223
. to_str ( )
220
224
. context ( "user name is not valid utf-8" ) ?;
221
225
222
- if user_authorized_keys. trim ( ) . is_empty ( ) {
226
+ if user_authorized_keys. is_empty ( ) {
223
227
tracing:: debug!(
224
228
"Skipping user {} because it has no SSH authorized_keys" ,
225
229
user_name
@@ -232,7 +236,6 @@ pub(crate) fn get_all_users_keys() -> Result<Vec<UserKeys>> {
232
236
authorized_keys : user_authorized_keys,
233
237
} ;
234
238
235
- tracing:: trace!( "Found user keys: {:?}" , user_keys) ;
236
239
tracing:: debug!(
237
240
"Found user {} with {} SSH authorized_keys" ,
238
241
user_keys. user,
0 commit comments