1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2013-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%% 22 23%%------------------------------------------------------------------ 24-module(ssh_message). 25 26-include_lib("public_key/include/public_key.hrl"). 27 28-include("ssh.hrl"). 29-include("ssh_connect.hrl"). 30-include("ssh_auth.hrl"). 31-include("ssh_transport.hrl"). 32 33-export([encode/1, decode/1, decode_keyboard_interactive_prompts/2]). 34-export([ssh2_pubkey_decode/1, 35 ssh2_pubkey_encode/1, 36 ssh2_privkey_decode2/1, 37 %% experimental: 38 ssh2_privkey_encode/1 39 ]). 40 41-behaviour(ssh_dbg). 42-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]). 43 44ucl(B) -> 45 try unicode:characters_to_list(B) of 46 L when is_list(L) -> L; 47 {error,_Matched,Rest} -> throw({error,{bad_unicode,Rest}}) 48 catch 49 _:_ -> throw({error,bad_unicode}) 50 end. 51 52-define(unicode_list(B), ucl(B)). 53 54%%%================================================================ 55%%% 56%%% Encode/decode messages 57%%% 58 59encode(#ssh_msg_global_request{ 60 name = Name, 61 want_reply = Bool, 62 data = Data}) -> 63 <<?Ebyte(?SSH_MSG_GLOBAL_REQUEST), ?Estring(Name), ?Eboolean(Bool), ?'E...'(Data)>>; 64 65encode(#ssh_msg_request_success{data = Data}) -> 66 <<?Ebyte(?SSH_MSG_REQUEST_SUCCESS), Data/binary>>; 67 68encode(#ssh_msg_request_failure{}) -> 69 <<?Ebyte(?SSH_MSG_REQUEST_FAILURE)>>; 70 71encode(#ssh_msg_channel_open{ 72 channel_type = Type, 73 sender_channel = Sender, 74 initial_window_size = Window, 75 maximum_packet_size = Max, 76 data = Data 77 }) -> 78 <<?Ebyte(?SSH_MSG_CHANNEL_OPEN), ?Estring(Type), ?Euint32(Sender), ?Euint32(Window), ?Euint32(Max), ?'E...'(Data)>>; 79 80encode(#ssh_msg_channel_open_confirmation{ 81 recipient_channel = Recipient, 82 sender_channel = Sender, 83 initial_window_size = InitWindowSize, 84 maximum_packet_size = MaxPacketSize, 85 data = Data 86 }) -> 87 <<?Ebyte(?SSH_MSG_CHANNEL_OPEN_CONFIRMATION), 88 ?Euint32(Recipient), ?Euint32(Sender), ?Euint32(InitWindowSize), ?Euint32(MaxPacketSize), 89 ?'E...'(Data)>>; 90 91encode(#ssh_msg_channel_open_failure{ 92 recipient_channel = Recipient, 93 reason = Reason, 94 description = Desc, 95 lang = Lang 96 }) -> 97 <<?Ebyte(?SSH_MSG_CHANNEL_OPEN_FAILURE), ?Euint32(Recipient),?Euint32(Reason), ?Estring(Desc), ?Estring(Lang)>>; 98 99encode(#ssh_msg_channel_window_adjust{ 100 recipient_channel = Recipient, 101 bytes_to_add = Bytes 102 }) -> 103 <<?Ebyte(?SSH_MSG_CHANNEL_WINDOW_ADJUST), ?Euint32(Recipient), ?Euint32(Bytes)>>; 104 105encode(#ssh_msg_channel_data{ 106 recipient_channel = Recipient, 107 data = Data 108 }) -> 109 <<?Ebyte(?SSH_MSG_CHANNEL_DATA), ?Euint32(Recipient), ?Ebinary(Data)>>; 110 111encode(#ssh_msg_channel_extended_data{ 112 recipient_channel = Recipient, 113 data_type_code = DataType, 114 data = Data 115 }) -> 116 <<?Ebyte(?SSH_MSG_CHANNEL_EXTENDED_DATA), ?Euint32(Recipient), ?Euint32(DataType), ?Ebinary(Data)>>; 117 118encode(#ssh_msg_channel_eof{recipient_channel = Recipient 119 }) -> 120 <<?Ebyte(?SSH_MSG_CHANNEL_EOF), ?Euint32(Recipient)>>; 121 122encode(#ssh_msg_channel_close{ 123 recipient_channel = Recipient 124 }) -> 125 <<?Ebyte(?SSH_MSG_CHANNEL_CLOSE), ?Euint32(Recipient)>>; 126 127encode(#ssh_msg_channel_request{ 128 recipient_channel = Recipient, 129 request_type = Type, 130 want_reply = Bool, 131 data = Data 132 }) -> 133 <<?Ebyte(?SSH_MSG_CHANNEL_REQUEST), ?Euint32(Recipient), ?Estring(Type), ?Eboolean(Bool), ?'E...'(Data)>>; 134 135encode(#ssh_msg_channel_success{ 136 recipient_channel = Recipient 137 }) -> 138 <<?Ebyte(?SSH_MSG_CHANNEL_SUCCESS), ?Euint32(Recipient)>>; 139 140encode(#ssh_msg_channel_failure{ 141 recipient_channel = Recipient 142 }) -> 143 <<?Ebyte(?SSH_MSG_CHANNEL_FAILURE), ?Euint32(Recipient)>>; 144 145encode(#ssh_msg_userauth_request{ 146 user = User, 147 service = Service, 148 method = Method, 149 data = Data 150 }) -> 151 <<?Ebyte(?SSH_MSG_USERAUTH_REQUEST), ?Estring_utf8(User), ?Estring(Service), ?Estring(Method), ?'E...'(Data)>>; 152 153encode(#ssh_msg_userauth_failure{ 154 authentications = Auths, 155 partial_success = Bool 156 }) -> 157 <<?Ebyte(?SSH_MSG_USERAUTH_FAILURE), ?Estring(Auths), ?Eboolean(Bool)>>; 158 159encode(#ssh_msg_userauth_success{}) -> 160 <<?Ebyte(?SSH_MSG_USERAUTH_SUCCESS)>>; 161 162encode(#ssh_msg_userauth_banner{ 163 message = Banner, 164 language = Lang 165 }) -> 166 <<?Ebyte(?SSH_MSG_USERAUTH_BANNER), ?Estring_utf8(Banner), ?Estring(Lang)>>; 167 168encode(#ssh_msg_userauth_pk_ok{ 169 algorithm_name = Alg, 170 key_blob = KeyBlob 171 }) -> 172 <<?Ebyte(?SSH_MSG_USERAUTH_PK_OK), ?Estring(Alg), ?Ebinary(KeyBlob)>>; 173 174encode(#ssh_msg_userauth_passwd_changereq{prompt = Prompt, 175 languge = Lang 176 })-> 177 <<?Ebyte(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?Estring_utf8(Prompt), ?Estring(Lang)>>; 178 179encode(#ssh_msg_userauth_info_request{ 180 name = Name, 181 instruction = Inst, 182 language_tag = Lang, 183 num_prompts = NumPromtps, 184 data = Data}) -> 185 <<?Ebyte(?SSH_MSG_USERAUTH_INFO_REQUEST), ?Estring_utf8(Name), ?Estring_utf8(Inst), ?Estring(Lang), 186 ?Euint32(NumPromtps), ?'E...'(Data)>>; 187 188encode(#ssh_msg_userauth_info_response{ 189 num_responses = Num, 190 data = Data}) -> 191 lists:foldl(fun %%("", Acc) -> Acc; % commented out since it seem wrong 192 (Response, Acc) -> <<Acc/binary, ?Estring_utf8(Response)>> 193 end, 194 <<?Ebyte(?SSH_MSG_USERAUTH_INFO_RESPONSE), ?Euint32(Num)>>, 195 Data); 196 197encode(#ssh_msg_disconnect{ 198 code = Code, 199 description = Desc, 200 language = Lang 201 }) -> 202 <<?Ebyte(?SSH_MSG_DISCONNECT), ?Euint32(Code), ?Estring_utf8(Desc), ?Estring(Lang)>>; 203 204encode(#ssh_msg_service_request{ 205 name = Service 206 }) -> 207 <<?Ebyte(?SSH_MSG_SERVICE_REQUEST), ?Estring_utf8(Service)>>; 208 209encode(#ssh_msg_service_accept{ 210 name = Service 211 }) -> 212 <<?Ebyte(?SSH_MSG_SERVICE_ACCEPT), ?Estring_utf8(Service)>>; 213 214encode(#ssh_msg_ext_info{ 215 nr_extensions = N, 216 data = Data 217 }) -> 218 lists:foldl(fun({ExtName,ExtVal}, Acc) -> 219 <<Acc/binary, ?Estring(ExtName), ?Estring(ExtVal)>> 220 end, 221 <<?Ebyte(?SSH_MSG_EXT_INFO), ?Euint32(N)>>, 222 Data); 223 224encode(#ssh_msg_newkeys{}) -> 225 <<?Ebyte(?SSH_MSG_NEWKEYS)>>; 226 227encode(#ssh_msg_kexinit{ 228 cookie = Cookie, 229 kex_algorithms = KeyAlgs, 230 server_host_key_algorithms = HostKeyAlgs, 231 encryption_algorithms_client_to_server = EncAlgC2S, 232 encryption_algorithms_server_to_client = EncAlgS2C, 233 mac_algorithms_client_to_server = MacAlgC2S, 234 mac_algorithms_server_to_client = MacAlgS2C, 235 compression_algorithms_client_to_server = CompAlgS2C, 236 compression_algorithms_server_to_client = CompAlgC2S, 237 languages_client_to_server = LangC2S, 238 languages_server_to_client = LangS2C, 239 first_kex_packet_follows = Bool, 240 reserved = Reserved 241 }) -> 242 <<?Ebyte(?SSH_MSG_KEXINIT), Cookie/binary, 243 ?Ename_list(KeyAlgs), ?Ename_list(HostKeyAlgs), ?Ename_list(EncAlgC2S), ?Ename_list(EncAlgS2C), ?Ename_list(MacAlgC2S), 244 ?Ename_list(MacAlgS2C), ?Ename_list(CompAlgS2C), ?Ename_list(CompAlgC2S), ?Ename_list(LangC2S), ?Ename_list(LangS2C), 245 ?Eboolean(Bool), ?Euint32(Reserved)>>; 246 247encode(#ssh_msg_kexdh_init{e = E}) -> 248 <<?Ebyte(?SSH_MSG_KEXDH_INIT), ?Empint(E)>>; 249 250encode(#ssh_msg_kexdh_reply{ 251 public_host_key = {Key,SigAlg}, 252 f = F, 253 h_sig = Signature 254 }) -> 255 EncKey = ssh2_pubkey_encode(Key), 256 EncSign = encode_signature(Key, SigAlg, Signature), 257 <<?Ebyte(?SSH_MSG_KEXDH_REPLY), ?Ebinary(EncKey), ?Empint(F), ?Ebinary(EncSign)>>; 258 259encode(#ssh_msg_kex_dh_gex_request{ 260 min = Min, 261 n = N, 262 max = Max 263 }) -> 264 <<?Ebyte(?SSH_MSG_KEX_DH_GEX_REQUEST), ?Euint32(Min), ?Euint32(N), ?Euint32(Max)>>; 265 266encode(#ssh_msg_kex_dh_gex_request_old{n = N}) -> 267 <<?Ebyte(?SSH_MSG_KEX_DH_GEX_REQUEST_OLD), ?Euint32(N)>>; 268 269encode(#ssh_msg_kex_dh_gex_group{p = Prime, g = Generator}) -> 270 <<?Ebyte(?SSH_MSG_KEX_DH_GEX_GROUP), ?Empint(Prime), ?Empint(Generator)>>; 271 272encode(#ssh_msg_kex_dh_gex_init{e = Public}) -> 273 <<?Ebyte(?SSH_MSG_KEX_DH_GEX_INIT), ?Empint(Public)>>; 274 275encode(#ssh_msg_kex_dh_gex_reply{ 276 %% Will be private key encode_host_key extracts only the public part! 277 public_host_key = {Key,SigAlg}, 278 f = F, 279 h_sig = Signature 280 }) -> 281 EncKey = ssh2_pubkey_encode(Key), 282 EncSign = encode_signature(Key, SigAlg, Signature), 283 <<?Ebyte(?SSH_MSG_KEX_DH_GEX_REPLY), ?Ebinary(EncKey), ?Empint(F), ?Ebinary(EncSign)>>; 284 285encode(#ssh_msg_kex_ecdh_init{q_c = Q_c}) -> 286 <<?Ebyte(?SSH_MSG_KEX_ECDH_INIT), ?Ebinary(Q_c)>>; 287 288encode(#ssh_msg_kex_ecdh_reply{public_host_key = {Key,SigAlg}, q_s = Q_s, h_sig = Sign}) -> 289 EncKey = ssh2_pubkey_encode(Key), 290 EncSign = encode_signature(Key, SigAlg, Sign), 291 <<?Ebyte(?SSH_MSG_KEX_ECDH_REPLY), ?Ebinary(EncKey), ?Ebinary(Q_s), ?Ebinary(EncSign)>>; 292 293encode(#ssh_msg_ignore{data = Data}) -> 294 <<?Ebyte(?SSH_MSG_IGNORE), ?Estring_utf8(Data)>>; 295 296encode(#ssh_msg_unimplemented{sequence = Seq}) -> 297 <<?Ebyte(?SSH_MSG_UNIMPLEMENTED), ?Euint32(Seq)>>; 298 299encode(#ssh_msg_debug{always_display = Bool, 300 message = Msg, 301 language = Lang}) -> 302 <<?Ebyte(?SSH_MSG_DEBUG), ?Eboolean(Bool), ?Estring_utf8(Msg), ?Estring(Lang)>>. 303 304 305%% Connection Messages 306decode(<<?BYTE(?SSH_MSG_GLOBAL_REQUEST), ?DEC_BIN(Name,__0), ?BYTE(Bool), Data/binary>>) -> 307 #ssh_msg_global_request{ 308 name = Name, 309 want_reply = erl_boolean(Bool), 310 data = Data 311 }; 312decode(<<?BYTE(?SSH_MSG_REQUEST_SUCCESS), Data/binary>>) -> 313 #ssh_msg_request_success{data = Data}; 314decode(<<?BYTE(?SSH_MSG_REQUEST_FAILURE)>>) -> 315 #ssh_msg_request_failure{}; 316decode(<<?BYTE(?SSH_MSG_CHANNEL_OPEN), 317 ?DEC_BIN(Type,__0), ?UINT32(Sender), ?UINT32(Window), ?UINT32(Max), 318 Data/binary>>) -> 319 #ssh_msg_channel_open{ 320 channel_type = binary_to_list(Type), 321 sender_channel = Sender, 322 initial_window_size = Window, 323 maximum_packet_size = Max, 324 data = Data 325 }; 326decode(<<?BYTE(?SSH_MSG_CHANNEL_OPEN_CONFIRMATION), ?UINT32(Recipient), ?UINT32(Sender), 327 ?UINT32(InitWindowSize), ?UINT32(MaxPacketSize), 328 Data/binary>>) -> 329 #ssh_msg_channel_open_confirmation{ 330 recipient_channel = Recipient, 331 sender_channel = Sender, 332 initial_window_size = InitWindowSize, 333 maximum_packet_size = MaxPacketSize, 334 data = Data 335 }; 336decode(<<?BYTE(?SSH_MSG_CHANNEL_OPEN_FAILURE), ?UINT32(Recipient), ?UINT32(Reason), 337 ?DEC_BIN(Desc,__0), ?DEC_BIN(Lang,__1) >> ) -> 338 #ssh_msg_channel_open_failure{ 339 recipient_channel = Recipient, 340 reason = Reason, 341 description = ?unicode_list(Desc), 342 lang = Lang 343 }; 344decode(<<?BYTE(?SSH_MSG_CHANNEL_WINDOW_ADJUST), ?UINT32(Recipient), ?UINT32(Bytes)>>) -> 345 #ssh_msg_channel_window_adjust{ 346 recipient_channel = Recipient, 347 bytes_to_add = Bytes 348 }; 349 350decode(<<?BYTE(?SSH_MSG_CHANNEL_DATA), ?UINT32(Recipient), ?DEC_BIN(Data,__0)>>) -> 351 #ssh_msg_channel_data{ 352 recipient_channel = Recipient, 353 data = Data 354 }; 355decode(<<?BYTE(?SSH_MSG_CHANNEL_EXTENDED_DATA), ?UINT32(Recipient), 356 ?UINT32(DataType), ?DEC_BIN(Data,__0)>>) -> 357 #ssh_msg_channel_extended_data{ 358 recipient_channel = Recipient, 359 data_type_code = DataType, 360 data = Data 361 }; 362decode(<<?BYTE(?SSH_MSG_CHANNEL_EOF), ?UINT32(Recipient)>>) -> 363 #ssh_msg_channel_eof{ 364 recipient_channel = Recipient 365 }; 366decode(<<?BYTE(?SSH_MSG_CHANNEL_CLOSE), ?UINT32(Recipient)>>) -> 367 #ssh_msg_channel_close{ 368 recipient_channel = Recipient 369 }; 370decode(<<?BYTE(?SSH_MSG_CHANNEL_REQUEST), ?UINT32(Recipient), 371 ?DEC_BIN(RequestType,__0), ?BYTE(Bool), Data/binary>>=Bytes) -> 372 try 373 #ssh_msg_channel_request{ 374 recipient_channel = Recipient, 375 request_type = ?unicode_list(RequestType), 376 want_reply = erl_boolean(Bool), 377 data = Data 378 } 379 catch _:_ -> 380 %% Faulty, RFC4254 says: 381 %% "If the request is not recognized or is not 382 %% supported for the channel, SSH_MSG_CHANNEL_FAILURE is returned." 383 %% So we provoke such a message to be sent 384 #ssh_msg_channel_request{ 385 recipient_channel = Recipient, 386 request_type = faulty_msg, 387 data = Bytes 388 } 389 end; 390decode(<<?BYTE(?SSH_MSG_CHANNEL_SUCCESS), ?UINT32(Recipient)>>) -> 391 #ssh_msg_channel_success{ 392 recipient_channel = Recipient 393 }; 394decode(<<?BYTE(?SSH_MSG_CHANNEL_FAILURE), ?UINT32(Recipient)>>) -> 395 #ssh_msg_channel_failure{ 396 recipient_channel = Recipient 397 }; 398 399%%% Auth Messages 400decode(<<?BYTE(?SSH_MSG_USERAUTH_REQUEST), 401 ?DEC_BIN(User,__0), ?DEC_BIN(Service,__1), ?DEC_BIN(Method,__2), 402 Data/binary>>) -> 403 #ssh_msg_userauth_request{ 404 user = ?unicode_list(User), 405 service = ?unicode_list(Service), 406 method = ?unicode_list(Method), 407 data = Data 408 }; 409 410decode(<<?BYTE(?SSH_MSG_USERAUTH_FAILURE), 411 ?DEC_BIN(Auths,__0), 412 ?BYTE(Bool)>>) -> 413 #ssh_msg_userauth_failure { 414 authentications = ?unicode_list(Auths), 415 partial_success = erl_boolean(Bool) 416 }; 417 418decode(<<?BYTE(?SSH_MSG_USERAUTH_SUCCESS)>>) -> 419 #ssh_msg_userauth_success{}; 420 421decode(<<?BYTE(?SSH_MSG_USERAUTH_BANNER), ?DEC_BIN(Banner,__0), ?DEC_BIN(Lang,__1) >>) -> 422 #ssh_msg_userauth_banner{ 423 message = Banner, 424 language = Lang 425 }; 426 427decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_REQUEST), 428 ?DEC_BIN(Name,__0), ?DEC_BIN(Inst,__1), ?DEC_BIN(Lang,__2), 429 ?UINT32(NumPromtps), Data/binary>>) -> 430 #ssh_msg_userauth_info_request{ 431 name = Name, 432 instruction = Inst, 433 language_tag = Lang, 434 num_prompts = NumPromtps, 435 data = Data}; 436 437%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST: 438decode(<<?BYTE(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?DEC_BIN(Prompt,__0), ?DEC_BIN(Lang,__1) >>) -> 439 #ssh_msg_userauth_passwd_changereq{ 440 prompt = Prompt, 441 languge = Lang 442 }; 443 444%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST: 445decode(<<?BYTE(?SSH_MSG_USERAUTH_PK_OK), ?DEC_BIN(Alg,__0), KeyBlob/binary>>) -> 446 #ssh_msg_userauth_pk_ok{ 447 algorithm_name = Alg, 448 key_blob = KeyBlob 449 }; 450 451decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_RESPONSE), ?UINT32(Num), Data/binary>>) -> 452 #ssh_msg_userauth_info_response{ 453 num_responses = Num, 454 data = Data}; 455 456decode(<<?BYTE(?SSH_MSG_EXT_INFO), ?UINT32(N), BinData/binary>>) -> 457 Data = bin_foldr( 458 fun(Bin,Acc) when length(Acc) == N -> 459 {Bin,Acc}; 460 (<<?DEC_BIN(V0,__0), ?DEC_BIN(V1,__1), Rest/binary>>, Acc) -> 461 {Rest,[{binary_to_list(V0),binary_to_list(V1)}|Acc]} 462 end, [], BinData), 463 #ssh_msg_ext_info{ 464 nr_extensions = N, 465 data = Data 466 }; 467 468%%% Keyexchange messages 469decode(<<?BYTE(?SSH_MSG_KEXINIT), Cookie:128, Data/binary>>) -> 470 decode_kex_init(Data, [Cookie, ssh_msg_kexinit], 10); 471 472decode(<<"dh",?BYTE(?SSH_MSG_KEXDH_INIT), ?DEC_MPINT(E,__0)>>) -> 473 #ssh_msg_kexdh_init{e = E 474 }; 475 476decode(<<"dh", ?BYTE(?SSH_MSG_KEXDH_REPLY), ?DEC_BIN(Key,__0), ?DEC_MPINT(F,__1), ?DEC_BIN(Hashsign,__2)>>) -> 477 #ssh_msg_kexdh_reply{ 478 public_host_key = ssh2_pubkey_decode(Key), 479 f = F, 480 h_sig = decode_signature(Hashsign) 481 }; 482 483decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_REQUEST), ?UINT32(Min), ?UINT32(N), ?UINT32(Max)>>) -> 484 #ssh_msg_kex_dh_gex_request{ 485 min = Min, 486 n = N, 487 max = Max 488 }; 489 490decode(<<"dh_gex",?BYTE(?SSH_MSG_KEX_DH_GEX_REQUEST_OLD), ?UINT32(N)>>) -> 491 #ssh_msg_kex_dh_gex_request_old{ 492 n = N 493 }; 494 495decode(<<"dh_gex",?BYTE(?SSH_MSG_KEX_DH_GEX_GROUP), ?DEC_MPINT(Prime,__0), ?DEC_MPINT(Generator,__1) >>) -> 496 #ssh_msg_kex_dh_gex_group{ 497 p = Prime, 498 g = Generator 499 }; 500 501decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_INIT), ?DEC_MPINT(E,__0)>>) -> 502 #ssh_msg_kex_dh_gex_init{ 503 e = E 504 }; 505 506decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_REPLY), ?DEC_BIN(Key,__0), ?DEC_MPINT(F,__1), ?DEC_BIN(Hashsign,__2)>>) -> 507 #ssh_msg_kex_dh_gex_reply{ 508 public_host_key = ssh2_pubkey_decode(Key), 509 f = F, 510 h_sig = decode_signature(Hashsign) 511 }; 512 513decode(<<"ecdh",?BYTE(?SSH_MSG_KEX_ECDH_INIT), ?DEC_BIN(Q_c,__0)>>) -> 514 #ssh_msg_kex_ecdh_init{ 515 q_c = Q_c 516 }; 517 518decode(<<"ecdh",?BYTE(?SSH_MSG_KEX_ECDH_REPLY), 519 ?DEC_BIN(Key,__1), ?DEC_BIN(Q_s,__2), ?DEC_BIN(Sig,__3)>>) -> 520 #ssh_msg_kex_ecdh_reply{ 521 public_host_key = ssh2_pubkey_decode(Key), 522 q_s = Q_s, 523 h_sig = decode_signature(Sig) 524 }; 525 526decode(<<?SSH_MSG_SERVICE_REQUEST, ?DEC_BIN(Service,__0)>>) -> 527 #ssh_msg_service_request{ 528 name = ?unicode_list(Service) 529 }; 530 531decode(<<?SSH_MSG_SERVICE_ACCEPT, ?DEC_BIN(Service,__0)>>) -> 532 #ssh_msg_service_accept{ 533 name = ?unicode_list(Service) 534 }; 535 536decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code), ?DEC_BIN(Desc,__0), ?DEC_BIN(Lang,__1)>>) -> 537 #ssh_msg_disconnect{ 538 code = Code, 539 description = ?unicode_list(Desc), 540 language = Lang 541 }; 542 543%% Accept bad disconnects from ancient openssh clients that doesn't send language tag. Use english as a work-around. 544decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code), ?DEC_BIN(Desc,__0)>>) -> 545 #ssh_msg_disconnect{ 546 code = Code, 547 description = ?unicode_list(Desc), 548 language = <<"en">> 549 }; 550 551decode(<<?SSH_MSG_NEWKEYS>>) -> 552 #ssh_msg_newkeys{}; 553 554decode(<<?BYTE(?SSH_MSG_IGNORE), ?DEC_BIN(Data,__0)>>) -> 555 #ssh_msg_ignore{data = Data}; 556 557decode(<<?BYTE(?SSH_MSG_UNIMPLEMENTED), ?UINT32(Seq)>>) -> 558 #ssh_msg_unimplemented{sequence = Seq}; 559 560decode(<<?BYTE(?SSH_MSG_DEBUG), ?BYTE(Bool), ?DEC_BIN(Msg,__0), ?DEC_BIN(Lang,__1)>>) -> 561 #ssh_msg_debug{always_display = erl_boolean(Bool), 562 message = Msg, 563 language = Lang}. 564 565 566%%%================================================================ 567%%% 568%%% Encode/decode ssh public/private keys 569%%% 570 571%%%-------- public key -------- 572ssh2_pubkey_encode(#'RSAPublicKey'{modulus = N, publicExponent = E}) -> 573 <<?STRING(<<"ssh-rsa">>), ?Empint(E), ?Empint(N)>>; 574ssh2_pubkey_encode({Y, #'Dss-Parms'{p = P, q = Q, g = G}}) -> 575 <<?STRING(<<"ssh-dss">>), ?Empint(P), ?Empint(Q), ?Empint(G), ?Empint(Y)>>; 576ssh2_pubkey_encode({#'ECPoint'{point = Q}, {namedCurve,OID}}) -> 577 Curve = public_key:oid2ssh_curvename(OID), 578 KeyType = <<"ecdsa-sha2-", Curve/binary>>, 579 <<?STRING(KeyType), ?STRING(Curve), ?Estring(Q)>>; 580ssh2_pubkey_encode({ed_pub, ed25519, Key}) -> 581 <<?STRING(<<"ssh-ed25519">>), ?Estring(Key)>>; 582ssh2_pubkey_encode({ed_pub, ed448, Key}) -> 583 <<?STRING(<<"ssh-ed448">>), ?Estring(Key)>>; 584ssh2_pubkey_encode({ed_priv, ed25519, Key, _}) -> 585 <<?STRING(<<"ssh-ed25519">>), ?Estring(Key)>>; 586ssh2_pubkey_encode({ed_priv, ed448, Key, _}) -> 587 <<?STRING(<<"ssh-ed448">>), ?Estring(Key)>>. 588 589%%%-------- 590ssh2_pubkey_decode(KeyBlob) -> 591 {Key,_RestBlob} = ssh2_pubkey_decode2(KeyBlob), 592 Key. 593 594ssh2_pubkey_decode2(<<?UINT32(7), "ssh-rsa", 595 ?DEC_INT(E, _EL), 596 ?DEC_INT(N, _NL), 597 Rest/binary>>) -> 598 {#'RSAPublicKey'{modulus = N, 599 publicExponent = E 600 }, Rest}; 601ssh2_pubkey_decode2(<<?UINT32(7), "ssh-dss", 602 ?DEC_INT(P, _PL), 603 ?DEC_INT(Q, _QL), 604 ?DEC_INT(G, _GL), 605 ?DEC_INT(Y, _YL), 606 Rest/binary>>) -> 607 {{Y, #'Dss-Parms'{p = P, 608 q = Q, 609 g = G} 610 }, Rest}; 611ssh2_pubkey_decode2(<<?UINT32(TL), "ecdsa-sha2-",KeyRest/binary>>) -> 612 Sz = TL-11, 613 <<_Curve:Sz/binary, 614 ?DEC_BIN(SshName, _IL), 615 ?DEC_BIN(Q, _QL), 616 Rest/binary>> = KeyRest, 617 OID = public_key:ssh_curvename2oid(SshName), 618 {{#'ECPoint'{point = Q}, {namedCurve,OID} 619 }, Rest}; 620ssh2_pubkey_decode2(<<?UINT32(11), "ssh-ed25519", 621 ?DEC_BIN(Key, _L), 622 Rest/binary>>) -> 623 {{ed_pub, ed25519, Key}, 624 Rest}; 625ssh2_pubkey_decode2(<<?UINT32(9), "ssh-ed448", 626 ?DEC_BIN(Key, _L), 627 Rest/binary>>) -> 628 {{ed_pub, ed448, Key}, 629 Rest}. 630 631%%%-------- private key -------- 632 633%% dialyser... ssh2_privkey_decode(KeyBlob) -> 634%% dialyser... {Key,_RestBlob} = ssh2_privkey_decode2(KeyBlob), 635%% dialyser... Key. 636%% See sshkey_private_serialize_opt in sshkey.c 637 638ssh2_privkey_encode(#'RSAPrivateKey' 639 {version = 'two-prime', % Found this in public_key:generate_key/1 .. 640 modulus = N, 641 publicExponent = E, 642 privateExponent = D, 643 prime1 = P, 644 prime2 = Q, 645 %% exponent1, % D_mod_P_1 646 %% exponent2, % D_mod_Q_1 647 coefficient = IQMP 648 }) -> 649 <<?STRING(<<"ssh-rsa">>), 650 ?Empint(N), % Yes, N and E is reversed relative pubkey format 651 ?Empint(E), % --"-- 652 ?Empint(D), 653 ?Empint(IQMP), 654 ?Empint(P), 655 ?Empint(Q)>>; 656 657ssh2_privkey_encode(#'DSAPrivateKey' 658 {version = 0, 659 p = P, 660 q = Q, 661 g = G, 662 y = Y, 663 x = X 664 }) -> 665 <<?STRING(<<"ssh-dss">>), 666 ?Empint(P), 667 ?Empint(Q), 668 ?Empint(G), 669 ?Empint(Y), % Publ key 670 ?Empint(X) % Priv key 671 >>; 672 673ssh2_privkey_encode(#'ECPrivateKey' 674 {version = 1, 675 parameters = {namedCurve,OID}, 676 privateKey = Priv, 677 publicKey = Q 678 }) -> 679 CurveName = public_key:oid2ssh_curvename(OID), 680 <<?STRING(<<"ecdsa-sha2-",CurveName/binary>>), 681 ?STRING(<<"ecdsa-sha2-",CurveName/binary>>), % Yes 682 ?STRING(Q), 683 ?STRING(Priv)>>; 684 685ssh2_privkey_encode({ed_pri, Alg, Pub, Priv}) -> 686 Name = atom_to_binary(Alg), 687 <<?STRING(<<"ssh-",Name/binary>>), 688 ?STRING(Pub), 689 ?STRING(Priv)>>. 690 691%%%-------- 692ssh2_privkey_decode2(<<?UINT32(7), "ssh-rsa", 693 ?DEC_INT(N, _NL), % Yes, N and E is reversed relative pubkey format 694 ?DEC_INT(E, _EL), % --"-- 695 ?DEC_INT(D, _DL), 696 ?DEC_INT(IQMP, _IQMPL), 697 ?DEC_INT(P, _PL), 698 ?DEC_INT(Q, _QL), 699 Rest/binary>>) -> 700 {#'RSAPrivateKey'{version = 'two-prime', % Found this in public_key:generate_key/1 .. 701 modulus = N, 702 publicExponent = E, 703 privateExponent = D, 704 prime1 = P, 705 prime2 = Q, 706 %exponent1, % D_mod_P_1 707 %exponent2, % D_mod_Q_1 708 coefficient = IQMP 709 }, Rest}; 710ssh2_privkey_decode2(<<?UINT32(7), "ssh-dss", 711 ?DEC_INT(P, _PL), 712 ?DEC_INT(Q, _QL), 713 ?DEC_INT(G, _GL), 714 ?DEC_INT(Y, _YL), % Publ key 715 ?DEC_INT(X, _XL), % Priv key 716 Rest/binary>>) -> 717 {#'DSAPrivateKey'{version = 0, 718 p = P, 719 q = Q, 720 g = G, 721 y = Y, 722 x = X 723 }, Rest}; 724ssh2_privkey_decode2(<<?UINT32(TL), "ecdsa-sha2-",KeyRest/binary>>) -> 725 Sz = TL-11, 726 <<_Curve:Sz/binary, 727 ?DEC_BIN(CurveName, _SNN), 728 ?DEC_BIN(Q, _QL), 729 ?DEC_BIN(Priv, _PrivL), 730 Rest/binary>> = KeyRest, 731 OID = public_key:ssh_curvename2oid(CurveName), 732 {#'ECPrivateKey'{version = 1, 733 parameters = {namedCurve,OID}, 734 privateKey = Priv, 735 publicKey = Q 736 }, Rest}; 737ssh2_privkey_decode2(<<?UINT32(11), "ssh-ed25519", 738 ?DEC_BIN(Pub,_Lpub), 739 ?DEC_BIN(Priv,_Lpriv), 740 Rest/binary>>) -> 741 {{ed_pri, ed25519, Pub, Priv}, Rest}; 742ssh2_privkey_decode2(<<?UINT32(9), "ssh-ed448", 743 ?DEC_BIN(Pub,_Lpub), 744 ?DEC_BIN(Priv,_Lpriv), 745 Rest/binary>>) -> 746 {{ed_pri, ed448, Pub, Priv}, Rest}. 747 748 749%%%================================================================ 750%%% 751%%% Helper functions 752%%% 753 754bin_foldr(Fun, Acc, Bin) -> 755 lists:reverse(bin_foldl(Fun, Acc, Bin)). 756 757bin_foldl(_, Acc, <<>>) -> Acc; 758bin_foldl(Fun, Acc0, Bin0) -> 759 case Fun(Bin0,Acc0) of 760 {Bin0,Acc0} -> 761 Acc0; 762 {Bin,Acc} -> 763 bin_foldl(Fun, Acc, Bin) 764 end. 765 766%%%---------------------------------------------------------------- 767decode_keyboard_interactive_prompts(<<>>, Acc) -> 768 lists:reverse(Acc); 769decode_keyboard_interactive_prompts(<<0>>, Acc) -> 770 lists:reverse(Acc); 771decode_keyboard_interactive_prompts(<<?DEC_BIN(Prompt,__0), ?BYTE(Bool), Bin/binary>>, 772 Acc) -> 773 decode_keyboard_interactive_prompts(Bin, [{Prompt, erl_boolean(Bool)} | Acc]). 774 775%%%---------------------------------------------------------------- 776erl_boolean(0) -> 777 false; 778erl_boolean(1) -> 779 true. 780 781%%%---------------------------------------------------------------- 782decode_kex_init(<<?BYTE(Bool), ?UINT32(X)>>, Acc, 0) -> 783 list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc])); 784decode_kex_init(<<?BYTE(Bool)>>, Acc, 0) -> 785 %% The mandatory trailing UINT32 is missing. Assume the value it anyhow must have 786 %% See rfc 4253 7.1 787 X = 0, 788 list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc])); 789decode_kex_init(<<?DEC_BIN(Data,__0), Rest/binary>>, Acc, N) -> 790 Names = string:tokens(?unicode_list(Data), ","), 791 decode_kex_init(Rest, [Names | Acc], N -1). 792 793 794%%%================================================================ 795%%% 796%%% Signature decode/encode 797%%% 798 799decode_signature(<<?DEC_BIN(Alg,__0), ?UINT32(_), Signature/binary>>) -> 800 {binary_to_list(Alg), Signature}. 801 802 803encode_signature(#'RSAPublicKey'{}, SigAlg, Signature) -> 804 SignName = list_to_binary(atom_to_list(SigAlg)), 805 <<?Ebinary(SignName), ?Ebinary(Signature)>>; 806encode_signature({_, #'Dss-Parms'{}}, _SigAlg, Signature) -> 807 <<?Ebinary(<<"ssh-dss">>), ?Ebinary(Signature)>>; 808encode_signature({#'ECPoint'{}, {namedCurve,OID}}, _SigAlg, Signature) -> 809 Curve = public_key:oid2ssh_curvename(OID), 810 <<?Ebinary(<<"ecdsa-sha2-",Curve/binary>>), ?Ebinary(Signature)>>; 811encode_signature({ed_pub, ed25519,_}, _SigAlg, Signature) -> 812 <<?Ebinary(<<"ssh-ed25519">>), ?Ebinary(Signature)>>; 813encode_signature({ed_pub, ed448,_}, _SigAlg, Signature) -> 814 <<?Ebinary(<<"ssh-ed448">>), ?Ebinary(Signature)>>. 815 816 817 818%%%################################################################ 819%%%# 820%%%# Tracing 821%%%# 822 823ssh_dbg_trace_points() -> [ssh_messages, raw_messages]. 824 825ssh_dbg_flags(ssh_messages) -> [c]; 826ssh_dbg_flags(raw_messages) -> [c]. 827 828ssh_dbg_on(P) when P==ssh_messages ; 829 P==raw_messages -> 830 dbg:tp(?MODULE,encode,1,x), 831 dbg:tp(?MODULE,decode,1,x). 832 833ssh_dbg_off(P) when P==ssh_messages ; 834 P==raw_messages -> 835 dbg:ctpg(?MODULE,encode,1), 836 dbg:ctpg(?MODULE,decode,1). 837 838ssh_dbg_format(ssh_messages, {call,{?MODULE,encode,[Msg]}}) -> 839 Name = string:to_upper(atom_to_list(element(1,Msg))), 840 ["Going to send ",Name,":\n", 841 wr_record(ssh_dbg:shrink_bin(Msg)) 842 ]; 843ssh_dbg_format(ssh_messages, {return_from, {?MODULE,encode,1}, _Ret}) -> 844 skip; 845 846ssh_dbg_format(ssh_messages, {call, {?MODULE,decode,[_]}}) -> 847 skip; 848ssh_dbg_format(ssh_messages, {return_from,{?MODULE,decode,1},Msg}) -> 849 Name = string:to_upper(atom_to_list(element(1,Msg))), 850 ["Received ",Name,":\n", 851 wr_record(ssh_dbg:shrink_bin(Msg)), 852 case Msg of 853 #ssh_msg_userauth_request{service = "ssh-connection", 854 method = "publickey", 855 data = <<_,?DEC_BIN(Alg,__0),_/binary>>} -> 856 io_lib:format(" data decoded: ~s ... ~n", [Alg]); 857 858 #ssh_msg_channel_request{request_type = "env", 859 data = <<?DEC_BIN(Var,__0),?DEC_BIN(Val,__1)>>} -> 860 io_lib:format(" data decoded: ~s = ~s~n", [Var, Val]); 861 862 #ssh_msg_channel_request{request_type = "exec", 863 data = <<?DEC_BIN(Cmnd,__0)>>} -> 864 io_lib:format(" data decoded: ~s~n", [Cmnd]); 865 866 #ssh_msg_channel_request{request_type = "pty-req", 867 data = <<?DEC_BIN(BTermName,_TermLen), 868 ?UINT32(Width),?UINT32(Height), 869 ?UINT32(PixWidth), ?UINT32(PixHeight), 870 Modes/binary>>} -> 871 io_lib:format(" data decoded: terminal = ~s~n" 872 " width x height = ~p x ~p~n" 873 " pix-width x pix-height = ~p x ~p~n" 874 " pty-opts = ~p~n", 875 [BTermName, Width,Height, PixWidth, PixHeight, 876 ssh_connection:decode_pty_opts(Modes)]); 877 _ -> 878 "" 879 end 880 ]; 881 882ssh_dbg_format(raw_messages, {call,{?MODULE,decode,[BytesPT]}}) -> 883 ["Received plain text bytes (shown after decryption):\n", 884 io_lib:format("~p",[BytesPT]) 885 ]; 886ssh_dbg_format(raw_messages, {return_from, {?MODULE,decode,1}, _Ret}) -> 887 skip; 888 889ssh_dbg_format(raw_messages, {call, {?MODULE,encode,[_]}}) -> 890 skip; 891ssh_dbg_format(raw_messages, {return_from,{?MODULE,encode,1},BytesPT}) -> 892 ["Going to send plain text bytes (shown before encryption):\n", 893 io_lib:format("~p",[BytesPT]) 894 ]. 895 896 897?wr_record(ssh_msg_disconnect); 898?wr_record(ssh_msg_ignore); 899?wr_record(ssh_msg_unimplemented); 900?wr_record(ssh_msg_debug); 901?wr_record(ssh_msg_service_request); 902?wr_record(ssh_msg_service_accept); 903?wr_record(ssh_msg_kexinit); 904?wr_record(ssh_msg_kexdh_init); 905?wr_record(ssh_msg_kexdh_reply); 906?wr_record(ssh_msg_newkeys); 907?wr_record(ssh_msg_ext_info); 908?wr_record(ssh_msg_kex_dh_gex_request); 909?wr_record(ssh_msg_kex_dh_gex_request_old); 910?wr_record(ssh_msg_kex_dh_gex_group); 911?wr_record(ssh_msg_kex_dh_gex_init); 912?wr_record(ssh_msg_kex_dh_gex_reply); 913?wr_record(ssh_msg_kex_ecdh_init); 914?wr_record(ssh_msg_kex_ecdh_reply); 915 916?wr_record(ssh_msg_userauth_request); 917?wr_record(ssh_msg_userauth_failure); 918?wr_record(ssh_msg_userauth_success); 919?wr_record(ssh_msg_userauth_banner); 920?wr_record(ssh_msg_userauth_passwd_changereq); 921?wr_record(ssh_msg_userauth_pk_ok); 922?wr_record(ssh_msg_userauth_info_request); 923?wr_record(ssh_msg_userauth_info_response); 924 925?wr_record(ssh_msg_global_request); 926?wr_record(ssh_msg_request_success); 927?wr_record(ssh_msg_request_failure); 928?wr_record(ssh_msg_channel_open); 929?wr_record(ssh_msg_channel_open_confirmation); 930?wr_record(ssh_msg_channel_open_failure); 931?wr_record(ssh_msg_channel_window_adjust); 932?wr_record(ssh_msg_channel_data); 933?wr_record(ssh_msg_channel_extended_data); 934?wr_record(ssh_msg_channel_eof); 935?wr_record(ssh_msg_channel_close); 936?wr_record(ssh_msg_channel_request); 937?wr_record(ssh_msg_channel_success); 938?wr_record(ssh_msg_channel_failure); 939 940wr_record(R) -> io_lib:format('~p~n',[R]). 941 942