@@ -19,12 +19,35 @@ open Util
19
19
let src = Logs.Src. create " awa.server" ~doc: " AWA server"
20
20
module Log = (val Logs. src_log src : Logs.LOG )
21
21
22
+ type auth_state =
23
+ | Preauth
24
+ | Inprogress of (string * string * int )
25
+ | Done
26
+
27
+ type pubkeyauth = {
28
+ pubkey : Hostkey .pub ;
29
+ session_id : string ;
30
+ service : string ;
31
+ sig_alg : Hostkey .alg ;
32
+ signed : string ;
33
+ }
34
+
35
+ let pubkey_of_pubkeyauth { pubkey; _ } = pubkey
36
+
37
+ let verify_pubkeyauth ~user { pubkey; session_id; service ; sig_alg ; signed } =
38
+ Auth. verify_signature user sig_alg pubkey session_id service signed
39
+
40
+ type userauth =
41
+ | Password of string
42
+ | Pubkey of pubkeyauth
43
+
22
44
type event =
23
45
| Channel_exec of (int32 * string )
24
46
| Channel_subsystem of (int32 * string )
25
47
| Channel_data of (int32 * Cstruct .t )
26
48
| Channel_eof of int32
27
49
| Disconnected of string
50
+ | Userauth of string * userauth
28
51
| Pty of (string * int32 * int32 * int32 * int32 * string )
29
52
| Pty_set of (int32 * int32 * int32 * int32 )
30
53
| Set_env of (string * string )
@@ -36,6 +59,8 @@ let pp_event ppf = function
36
59
| Channel_data (c , data ) -> Fmt. pf ppf " channel data %lu: %d bytes" c (Cstruct. length data)
37
60
| Channel_eof c -> Fmt. pf ppf " channel end-of-file %lu" c
38
61
| Disconnected s -> Fmt. pf ppf " disconnected with messsage %S" s
62
+ | Userauth (user , Password _ ) -> Fmt. pf ppf " userauth password for %S" user
63
+ | Userauth (user , Pubkey _ ) -> Fmt. pf ppf " userauth pubkey for %S" user
39
64
| Pty _ -> Fmt. pf ppf " pty"
40
65
| Pty_set _ -> Fmt. pf ppf " pty set"
41
66
| Set_env (k , v ) -> Fmt. pf ppf " Set env %S=%S" k v
@@ -57,8 +82,7 @@ type t = {
57
82
keying : bool ; (* keying = sent KEXINIT *)
58
83
key_eol : Mtime .t option ; (* Keys end of life, in ns *)
59
84
expect : Ssh .message_id option ; (* Messages to expect, None if any *)
60
- auth_state : Auth .state ; (* username * service in progress *)
61
- user_db : Auth .db ; (* username database *)
85
+ auth_state : auth_state ; (* username * service in progress *)
62
86
channels : Channel .db ; (* Ssh channels *)
63
87
ignore_next_packet : bool ; (* Ignore the next packet from the wire *)
64
88
dh_group : (Mirage_crypto_pk.Dh .group * int32 * int32 * int32 ) option ; (* used for GEX (RFC 4419) *)
@@ -78,7 +102,7 @@ let guard_msg t msg =
78
102
let host_key_algs key =
79
103
List. filter Hostkey. (alg_matches (priv_to_typ key)) Hostkey. preferred_algs
80
104
81
- let make host_key user_db =
105
+ let make host_key =
82
106
let open Ssh in
83
107
let server_kexinit =
84
108
let algs = host_key_algs host_key in
@@ -101,8 +125,7 @@ let make host_key user_db =
101
125
keying = true ;
102
126
key_eol = None ;
103
127
expect = Some MSG_VERSION ;
104
- auth_state = Auth. Preauth ;
105
- user_db;
128
+ auth_state = Preauth ;
106
129
channels = Channel. empty_db;
107
130
ignore_next_packet = false ;
108
131
dh_group = None ;
@@ -168,9 +191,8 @@ let make_reply_with_event t msg e = Ok (t, [ msg ], Some e)
168
191
let make_disconnect t code s =
169
192
Ok (t, [ Ssh. disconnect_msg code s ], Some (Disconnected s))
170
193
171
- let rec input_userauth_request t username service auth_method =
194
+ let input_userauth_request t username service auth_method =
172
195
let open Ssh in
173
- let open Auth in
174
196
let inc_nfailed t =
175
197
match t.auth_state with
176
198
| Preauth | Done -> Error " Unexpected auth_state"
@@ -185,73 +207,72 @@ let rec input_userauth_request t username service auth_method =
185
207
let * t = inc_nfailed t in
186
208
make_reply t (Msg_userauth_failure ([ " publickey" ; " password" ], false ))
187
209
in
188
- let discard t = make_noreply t in
189
- let success t =
190
- make_reply { t with auth_state = Done ; expect = None } Msg_userauth_success
191
- in
192
210
let try_probe t pubkey =
193
211
make_reply t (Msg_userauth_pk_ok pubkey)
194
212
in
195
- let try_auth t b = if b then success t else failure t in
196
- let handle_auth t =
197
- (* XXX verify all fail cases, what should we do and so on *)
198
- let * session_id = guard_some t.session_id " No session_id" in
199
- let * () = guard (service = " ssh-connection" ) " Bad service" in
200
- match auth_method with
201
- | Pubkey (pkalg , pubkey_raw , None) -> (* Public key probing *)
202
- begin match Wire. pubkey_of_blob pubkey_raw with
203
- | Ok pubkey when Hostkey. comptible_alg pubkey pkalg ->
204
- try_probe t pubkey
205
- | Ok _ ->
213
+ (* XXX verify all fail cases, what should we do and so on *)
214
+ let * session_id = guard_some t.session_id " No session_id" in
215
+ let * () = guard (service = " ssh-connection" ) " Bad service" in
216
+ match auth_method with
217
+ | Pubkey (pkalg , pubkey_raw , None) -> (* Public key probing *)
218
+ begin match Wire. pubkey_of_blob pubkey_raw with
219
+ | Ok pubkey when Hostkey. comptible_alg pubkey pkalg ->
220
+ try_probe t pubkey
221
+ | Ok _ ->
222
+ Log. debug (fun m -> m " Client offered unsupported or incompatible signature algorithm %s"
223
+ pkalg);
224
+ failure t
225
+ | Error `Unsupported keytype ->
226
+ Log. debug (fun m -> m " Client offered unsupported key type %s" keytype);
227
+ failure t
228
+ | Error `Msg s ->
229
+ Log. warn (fun m -> m " Failed to decode public key (while client offered a key): %s" s);
230
+ disconnect t DISCONNECT_PROTOCOL_ERROR " public key decoding failed"
231
+ end
232
+ | Pubkey (pkalg , pubkey_raw , Some (sig_alg , signed )) -> (* Public key authentication *)
233
+ begin match Wire. pubkey_of_blob pubkey_raw with
234
+ | Ok pubkey when Hostkey. comptible_alg pubkey pkalg &&
235
+ String. equal pkalg sig_alg ->
236
+ (* NOTE: for backwards compatibility with older OpenSSH clients we
237
+ should be more lenient if the sig_alg is "ssh-rsa-cert-v01" (if we
238
+ ever implement that). See
239
+ https://github.com/openssh/openssh-portable/blob/master/ssh-rsa.c#L504-L507 *)
240
+ (* XXX: this should be fine due to the previous [Hostkey.comptible_alg] *)
241
+ (* TODO: avoid Result.get_ok :/ *)
242
+ let sig_alg = Result. get_ok (Hostkey. alg_of_string sig_alg) in
243
+ Ok (t, [] , Some (Userauth (username, Pubkey { pubkey; session_id; service; sig_alg; signed })))
244
+ | Ok pubkey ->
245
+ if Hostkey. comptible_alg pubkey pkalg then
206
246
Log. debug (fun m -> m " Client offered unsupported or incompatible signature algorithm %s"
207
- pkalg);
208
- failure t
209
- | Error `Unsupported keytype ->
210
- Log. debug (fun m -> m " Client offered unsupported key type %s" keytype);
211
- failure t
212
- | Error `Msg s ->
213
- Log. warn (fun m -> m " Failed to decode public key (while client offered a key): %s" s);
214
- disconnect t DISCONNECT_PROTOCOL_ERROR " public key decoding failed"
215
- end
216
- | Pubkey (pkalg , pubkey_raw , Some (sig_alg , signed )) -> (* Public key authentication *)
217
- begin match Wire. pubkey_of_blob pubkey_raw with
218
- | Ok pubkey when Hostkey. comptible_alg pubkey pkalg &&
219
- String. equal pkalg sig_alg ->
220
- (* NOTE: for backwards compatibility with older OpenSSH clients we
221
- should be more lenient if the sig_alg is "ssh-rsa-cert-v01" (if we
222
- ever implement that). See
223
- https://github.com/openssh/openssh-portable/blob/master/ssh-rsa.c#L504-L507 *)
224
- (* XXX: this should be fine due to the previous [Hostkey.comptible_alg] *)
225
- (* TODO: avoid Result.get_ok :/ *)
226
- let sig_alg = Result. get_ok (Hostkey. alg_of_string sig_alg) in
227
- try_auth t (by_pubkey username sig_alg pubkey session_id service signed t.user_db)
228
- | Ok pubkey ->
229
- if Hostkey. comptible_alg pubkey pkalg then
230
- Log. debug (fun m -> m " Client offered unsupported or incompatible signature algorithm %s"
231
- pkalg)
232
- else
233
- Log. debug (fun m -> m " Client offered signature using algorithm different from advertised: %s vs %s"
234
- sig_alg pkalg);
235
- failure t
236
- | Error `Unsupported keytype ->
237
- Log. debug (fun m -> m " Client attempted authentication with unsupported key type %s" keytype);
238
- failure t
239
- | Error `Msg s ->
240
- Log. warn (fun m -> m " Failed to decode public key (while authenticating): %s" s);
241
- disconnect t DISCONNECT_PROTOCOL_ERROR " public key decoding failed"
242
- end
243
- | Password (password , None) -> (* Password authentication *)
244
- try_auth t (by_password username password t.user_db)
245
- (* Change of password, or keyboard_interactive, or Authnone won't be supported *)
246
- | Password (_ , Some _ ) | Keyboard_interactive _ | Authnone -> failure t
247
- in
247
+ pkalg)
248
+ else
249
+ Log. debug (fun m -> m " Client offered signature using algorithm different from advertised: %s vs %s"
250
+ sig_alg pkalg);
251
+ failure t
252
+ | Error `Unsupported keytype ->
253
+ Log. debug (fun m -> m " Client attempted authentication with unsupported key type %s" keytype);
254
+ failure t
255
+ | Error `Msg s ->
256
+ Log. warn (fun m -> m " Failed to decode public key (while authenticating): %s" s);
257
+ disconnect t DISCONNECT_PROTOCOL_ERROR " public key decoding failed"
258
+ end
259
+ | Password (password , None) -> (* Password authentication *)
260
+ Ok (t, [] , Some (Userauth (username, Password password)))
261
+ (* Change of password, or keyboard_interactive, or Authnone won't be supported *)
262
+ | Password (_ , Some _ ) | Keyboard_interactive _ | Authnone -> failure t
263
+
264
+ let input_userauth_request t username service auth_method =
248
265
(* See if we can actually authenticate *)
249
266
match t.auth_state with
250
- | Done -> discard t (* RFC tells us we must discard requests if already authenticated *)
267
+ | Done -> make_noreply t (* RFC tells us we must discard requests if already authenticated *)
251
268
| Preauth -> (* Recurse, but now Inprogress *)
252
269
let t = { t with auth_state = Inprogress (username, service, 0 ) } in
253
270
input_userauth_request t username service auth_method
254
271
| Inprogress (prev_username , prev_service , nfailed ) ->
272
+ let disconnect t code s =
273
+ let t = { t with auth_state = Inprogress (prev_username, prev_service, succ nfailed) } in
274
+ make_disconnect t code s
275
+ in
255
276
if service <> " ssh-connection" then
256
277
disconnect t DISCONNECT_SERVICE_NOT_AVAILABLE
257
278
(sprintf " Don't know service `%s`" service)
@@ -264,7 +285,23 @@ let rec input_userauth_request t username service auth_method =
264
285
else if nfailed > 10 then
265
286
Error " Maximum authentication attempts reached, already sent disconnect"
266
287
else
267
- handle_auth t
288
+ input_userauth_request t username service auth_method
289
+
290
+ let reject_userauth t _userauth =
291
+ match t.auth_state with
292
+ | Inprogress (u , s , nfailed ) ->
293
+ let t = { t with auth_state = Inprogress (u, s, succ nfailed) } in
294
+ Ok (t, Ssh. Msg_userauth_failure ([ " publickey" ; " password" ], false ))
295
+ | Done | Preauth ->
296
+ Error " userauth in unexpected state"
297
+
298
+ let accept_userauth t _userauth =
299
+ match t.auth_state with
300
+ | Inprogress _ ->
301
+ let t = { t with auth_state = Done ; expect = None } in
302
+ Ok (t, Ssh. Msg_userauth_success )
303
+ | Done | Preauth ->
304
+ Error " userauth in unexpected state"
268
305
269
306
let input_channel_open t send_channel init_win_size max_pkt_size data =
270
307
let open Ssh in
0 commit comments