/programs/network/ssh/dh_gex.inc |
---|
File deleted |
/programs/network/ssh/ssh_transport.inc |
---|
File deleted |
/programs/network/ssh/mcodes.inc |
---|
File deleted |
\ No newline at end of file |
/programs/network/ssh/aes256-ctr.inc |
---|
16,12 → 16,15 |
; along with this program. If not, see <http://www.gnu.org/licenses/>. |
struct aes256_ctr_context aes256_context |
counter rb AES256_BLOCKSIZE |
output rb AES256_BLOCKSIZE ; counter after aes_crypt |
ends |
proc aes256_ctr_init _counter |
push ebx esi edi |
mcall 68, 12, sizeof.aes256_ctr_context |
34,6 → 37,7 |
pop edi esi ebx |
ret |
endp |
84,7 → 88,7 |
bswap ecx |
bswap edx |
inc edx |
adc edx, 1 |
adc ecx, 0 |
adc ebx, 0 |
adc eax, 0 |
/programs/network/ssh/blowfish-ctr.inc |
---|
84,7 → 84,7 |
bswap ecx |
bswap edx |
inc edx |
adc edx, 1 |
adc ecx, 0 |
adc ebx, 0 |
adc eax, 0 |
/programs/network/ssh/encodings.inc |
---|
0,0 → 1,292 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; Written by CleverMouse ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
uglobal |
utf8_bytes_rest dd ? ; bytes rest in current UTF8 sequence |
utf8_char dd ? ; first bits of current UTF8 character |
endg |
;get_next_byte: |
;; Load next byte from the packet, translating to cp866 if necessary |
;; At input esi = pointer to data, edx = limit of data |
;; Output is either (translated) byte in al with CF set or CF cleared. |
; mov eax, [encoding] |
; jmp [get_byte_table+eax*4] |
; |
;get_byte_cp866: |
; cmp esi, edx |
; jae .nothing |
; lodsb |
;.nothing: |
; ret |
; |
;get_byte_cp1251: |
; cmp esi, edx |
; jae .nothing |
; lodsb |
; cmp al, 0x80 |
; jb @f |
; and eax, 0x7F |
; mov al, [cp1251_table+eax] |
;@@: |
; stc |
;.nothing: |
; ret |
get_byte_utf8: |
; UTF8 decoding is slightly complicated. |
; One character can occupy one or more bytes. |
; The boundary in packets theoretically can be anywhere in data, |
; so this procedure keeps internal state between calls and handles |
; one byte at a time, looping until character is read or packet is over. |
; Globally, there are two distinct tasks: decode byte sequence to unicode char |
; and convert this unicode char to our base encoding (that is cp866). |
; 1. Check that there are data. |
cmp esi, edx |
jae .nothing |
; 2. Load byte. |
lodsb |
movzx ecx, al |
; 3. Bytes in an UTF8 sequence can be of any of three types. |
; If most significant bit is cleared, sequence is one byte and usual ASCII char. |
; First byte of a sequence must be 11xxxxxx, other bytes are 10yyyyyy. |
and al, 0xC0 |
jns .single_byte |
jp .first_byte |
; 4. This byte is not first in UTF8 sequence. |
; 4a. Check that the sequence was started. If no, it is invalid byte |
; and we simply ignore it. |
cmp [utf8_bytes_rest], 0 |
jz get_byte_utf8 |
; 4b. Otherwise, it is really next byte and it gives some more bits of char. |
mov eax, [utf8_char] |
shl eax, 6 |
lea eax, [eax+ecx-0x80] |
; 4c. Decrement number of bytes rest in the sequence. |
; If it goes to zero, character is read, so return it. |
dec [utf8_bytes_rest] |
jz .got_char |
mov [utf8_char], eax |
jmp get_byte_utf8 |
; 5. If the byte is first in UTF8 sequence, calculate the number of leading 1s |
; - it equals total number of bytes in the sequence; some other bits rest for |
; leading bits in the character. |
.first_byte: |
mov eax, -1 |
@@: |
inc eax |
add cl, cl |
js @b |
mov [utf8_bytes_rest], eax |
xchg eax, ecx |
inc ecx |
shr al, cl |
mov [utf8_char], eax |
jmp get_byte_utf8 |
; 6. If the byte is ASCII char, it is the character. |
.single_byte: |
xchg eax, ecx |
.got_char: |
; We got the character, now abandon a possible sequence in progress. |
and [utf8_bytes_rest], 0 |
; Now second task. The unicode character is in eax, and now we shall convert it |
; to cp866. |
cmp eax, 0x80 |
jb .done |
; 0x410-0x43F -> 0x80-0xAF, 0x440-0x44F -> 0xE0-0xEF, 0x401 -> 0xF0, 0x451 -> 0xF1 |
cmp eax, 0x401 |
jz .YO |
cmp eax, 0x451 |
jz .yo |
cmp eax, 0x410 |
jb .unrecognized |
cmp eax, 0x440 |
jb .part1 |
cmp eax, 0x450 |
jb .part2 |
cmp eax, 0x25a0 |
jae .unrecognized |
sub eax, 0x2500 |
jb .unrecognized |
mov al, [cp866_boxes+eax] |
ret |
.part1: |
sub al, 0x10-0x80 |
.nothing: |
.done: |
ret |
.part2: |
sub al, (0x40-0xE0) and 0xFF |
ret |
.unrecognized: |
mov al, '?' |
stc |
ret |
.YO: |
mov al, 0xF0 |
stc |
ret |
.yo: |
mov al, 0xF1 |
stc |
ret |
;recode_to_cp866: |
; rep movsb |
; ret |
; |
;recode_to_cp1251: |
; xor eax, eax |
; jecxz .nothing |
; .loop: |
; lodsb |
; cmp al,0x80 |
; jb @f |
; mov al, [cp866_table-0x80+eax] |
; @@: stosb |
; loop .loop |
; .nothing: |
; ret |
recode_to_utf8: |
jecxz .nothing |
.loop: |
lodsb |
cmp al, 0x80 |
jb .single_byte |
and eax, 0x7F |
mov ax, [utf8_table+eax*2] |
stosw |
loop .loop |
ret |
.single_byte: |
stosb |
loop .loop |
.nothing: |
ret |
;recode: |
; mov eax, [encoding] |
; jmp [recode_proc+eax*4] |
;encoding dd UTF8 |
;recode_proc dd recode_to_cp866, recode_to_cp1251, recode_to_utf8 |
;get_byte_table dd get_byte_cp866, get_byte_cp1251, get_byte_utf8 |
;cp1251_table: |
; db '?','?','?','?','?','?','?','?' , '?','?','?','?','?','?','?','?' ; 8 |
; db '?','?','?','?','?',$F9,'?','?' , '?','?','?','?','?','?','?','?' ; 9 |
; db '?',$F6,$F7,'?',$FD,'?','?','?' , $F0,'?',$F2,'?','?','?','?',$F4 ; A |
; db $F8,'?','?','?','?','?','?',$FA , $F1,$FC,$F3,'?','?','?','?',$F5 ; B |
; db $80,$81,$82,$83,$84,$85,$86,$87 , $88,$89,$8A,$8B,$8C,$8D,$8E,$8F ; C |
; db $90,$91,$92,$93,$94,$95,$96,$97 , $98,$99,$9A,$9B,$9C,$9D,$9E,$9F ; D |
; db $A0,$A1,$A2,$A3,$A4,$A5,$A6,$A7 , $A8,$A9,$AA,$AB,$AC,$AD,$AE,$AF ; E |
; db $E0,$E1,$E2,$E3,$E4,$E5,$E6,$E7 , $E8,$E9,$EA,$EB,$EC,$ED,$EE,$EF ; F |
; 0 1 2 3 4 5 6 7 8 9 A B C D E F |
utf8_table: |
times 80h dw 0x98C3 ; default placeholder |
; 0x80-0xAF -> 0x90D0-0xBFD0 |
repeat 0x30 |
store byte 0xD0 at utf8_table+2*(%-1) |
store byte 0x90+%-1 at utf8_table+2*%-1 |
end repeat |
; 0xE0-0xEF -> 0x80D1-0x8FD1 |
repeat 0x10 |
store byte 0xD1 at utf8_table+2*(0xE0-0x80+%-1) |
store byte 0x80+%-1 at utf8_table+2*(0xE0-0x80+%)-1 |
end repeat |
; 0xF0 -> 0x81D0, 0xF1 -> 0x91D1 |
store dword 0x91D181D0 at utf8_table+2*(0xF0-0x80) |
;cp866_table: |
; db $C0,$C1,$C2,$C3,$C4,$C5,$C6,$C7 , $C8,$C9,$CA,$CB,$CC,$CD,$CE,$CF ; 8 |
; db $D0,$D1,$D2,$D3,$D4,$D5,$D6,$D7 , $D8,$D9,$DA,$DB,$DC,$DD,$DE,$DF ; 9 |
; db $E0,$E1,$E2,$E3,$E4,$E5,$E6,$E7 , $E8,$E9,$EA,$EB,$EC,$ED,$EE,$EF ; A |
; db '?','?','?','?','?','?','?','?' , '?','?','?','?','?','?','?','?' ; B |
; db '?','?','?','?','?','?','?','?' , '?','?','?','?','?','?','?','?' ; C |
; db '?','?','?','?','?','?','?','?' , '?','?','?','?','?','?','?','?' ; D |
; db $F0,$F1,$F2,$F3,$F4,$F5,$F6,$F7 , $F8,$F9,$FA,$FB,$FC,$FD,$FE,$FF ; E |
; db $A8,$B8,$AA,$BA,$AF,$BF,$A1,$A2 , $B0,$95,$B7,'?',$B9,$A4,'?','?' ; F |
; 0 1 2 3 4 5 6 7 8 9 A B C D E F |
; Codepoints for 0xB0-0xDF, unicode offset 0x2500 |
cp866_boxes: |
times 0xA0 db '?' |
store byte 0xB0 at cp866_boxes+0x91 |
store byte 0xB1 at cp866_boxes+0x92 |
store byte 0xB2 at cp866_boxes+0x93 |
store byte 0xB3 at cp866_boxes+0x02 |
store byte 0xB4 at cp866_boxes+0x24 |
store byte 0xB5 at cp866_boxes+0x61 |
store byte 0xB6 at cp866_boxes+0x62 |
store byte 0xB7 at cp866_boxes+0x56 |
store byte 0xB8 at cp866_boxes+0x55 |
store byte 0xB9 at cp866_boxes+0x63 |
store byte 0xBA at cp866_boxes+0x51 |
store byte 0xBB at cp866_boxes+0x57 |
store byte 0xBC at cp866_boxes+0x5D |
store byte 0xBD at cp866_boxes+0x5C |
store byte 0xBE at cp866_boxes+0x5B |
store byte 0xBF at cp866_boxes+0x10 |
store byte 0xC0 at cp866_boxes+0x14 |
store byte 0xC1 at cp866_boxes+0x34 |
store byte 0xC2 at cp866_boxes+0x2C |
store byte 0xC3 at cp866_boxes+0x1C |
store byte 0xC4 at cp866_boxes+0x00 |
store byte 0xC5 at cp866_boxes+0x3C |
store byte 0xC6 at cp866_boxes+0x5E |
store byte 0xC7 at cp866_boxes+0x5F |
store byte 0xC8 at cp866_boxes+0x5A |
store byte 0xC9 at cp866_boxes+0x54 |
store byte 0xCA at cp866_boxes+0x69 |
store byte 0xCB at cp866_boxes+0x66 |
store byte 0xCC at cp866_boxes+0x60 |
store byte 0xCD at cp866_boxes+0x50 |
store byte 0xCE at cp866_boxes+0x6C |
store byte 0xCF at cp866_boxes+0x67 |
store byte 0xD0 at cp866_boxes+0x68 |
store byte 0xD1 at cp866_boxes+0x64 |
store byte 0xD2 at cp866_boxes+0x65 |
store byte 0xD3 at cp866_boxes+0x59 |
store byte 0xD4 at cp866_boxes+0x58 |
store byte 0xD5 at cp866_boxes+0x52 |
store byte 0xD6 at cp866_boxes+0x53 |
store byte 0xD7 at cp866_boxes+0x6B |
store byte 0xD8 at cp866_boxes+0x6A |
store byte 0xD9 at cp866_boxes+0x18 |
store byte 0xDA at cp866_boxes+0x0C |
store byte 0xDB at cp866_boxes+0x88 |
store byte 0xDC at cp866_boxes+0x84 |
store byte 0xDD at cp866_boxes+0x8C |
store byte 0xDE at cp866_boxes+0x90 |
store byte 0xDF at cp866_boxes+0x80 |
/programs/network/ssh/ssh.asm |
---|
18,12 → 18,18 |
format binary as "" |
__DEBUG__ = 1 |
__DEBUG_LEVEL__ = 3 ; 1: Everything, including sinsitive information, 2: Debugging, 3: Errors only |
__DEBUG_LEVEL__ = 2 ; 1: Everything, including sensitive information, 2: Debugging, 3: Errors only |
BUFFERSIZE = 4096 |
BUFFERSIZE = 64*1024 ; Must be at least 32K according rfc4253#section-6.1 |
PACKETSIZE = 32*1024 ; Must be at least 32K according rfc4253#section-6.1 |
MAX_BITS = 8192 |
DH_PRIVATE_KEY_SIZE = 256 |
MAX_INPUT_LENGTH = 255 ;;; WHAT WAS THIS AGAIN ?! |
MAX_USERNAME_LENGTH = 256 |
MAX_PASSWORD_LENGTH = 256 |
MAX_HOSTNAME_LENGTH = 4096 |
MAX_PUBLIC_KEY_SIZE = 4096 |
use32 |
45,27 → 51,6 |
include '../../network.inc' |
include '../../develop/libraries/libcrash/trunk/libcrash.inc' |
include 'mcodes.inc' |
include 'ssh_transport.inc' |
include 'dh_gex.inc' |
include 'mpint.inc' |
include 'seed.inc' |
include 'random.inc' |
include 'aes256.inc' |
include 'aes256-ctr.inc' |
include 'aes256-cbc.inc' |
include 'blowfish.inc' |
include 'blowfish-ctr.inc' |
include 'blowfish-cbc.inc' |
include 'hmac_sha256.inc' |
include 'hmac_sha1.inc' |
include 'hmac_md5.inc' |
; macros for network byte order |
macro dd_n op { |
dd 0 or (((op) and 0FF000000h) shr 24) or \ |
79,6 → 64,15 |
(((op) and 000FFh) shl 8) |
} |
macro str string { |
local .start, .stop |
dd_n (.stop-.start) |
.start db string |
.stop: |
} |
proc dump_hex _ptr, _length |
if __DEBUG_LEVEL__ <= 1 |
pushad |
97,93 → 91,43 |
ret |
endp |
struct ssh_connection |
macro DEBUGM l, s, m { |
if __DEBUG__ |
DEBUGF l, s |
if l >=__DEBUG_LEVEL__ |
stdcall mpint_print, m |
end if |
end if |
} |
; Connection |
include 'mpint.inc' |
include 'seed.inc' |
include 'random.inc' |
hostname rb 1024 |
include 'aes256.inc' |
include 'aes256-ctr.inc' |
include 'aes256-cbc.inc' |
socketnum dd ? |
include 'blowfish.inc' |
include 'blowfish-ctr.inc' |
include 'blowfish-cbc.inc' |
sockaddr dw ? ; Address family |
port dw ? |
ip dd ? |
rb 10 |
include 'hmac_sha256.inc' |
include 'hmac_sha1.inc' |
include 'hmac_md5.inc' |
; Encryption/Decryption |
include 'sshlib.inc' |
rx_crypt_proc dd ? |
tx_crypt_proc dd ? |
rx_crypt_ctx_ptr dd ? |
tx_crypt_ctx_ptr dd ? |
rx_crypt_blocksize dd ? |
tx_crypt_blocksize dd ? |
include 'sshlib_mcodes.inc' |
include 'sshlib_transport.inc' |
include 'sshlib_connection.inc' |
include 'sshlib_dh_gex.inc' |
include 'sshlib_host.inc' |
include 'sshlib_channel.inc' |
include 'sshlib_userauth.inc' |
; Padding |
include 'encodings.inc' ; Unfortunately, we dont have UTF-8 capable console yet :( |
; rx_padsize dd ? ; = Max(8, rx_crypt_blocksize) |
tx_pad_size dd ? ; = Max(8, tx_crypt_blocksize) |
tx_pad_proc dd ? |
; Message authentication |
rx_mac_proc dd ? |
tx_mac_proc dd ? |
rx_mac_ctx hmac_sha256_context |
tx_mac_ctx hmac_sha256_context |
rx_mac_length dd ? |
tx_mac_length dd ? |
; Buffers |
rx_seq dd ? ; Packet sequence number for MAC |
rx_buffer ssh_packet_header |
rb BUFFERSIZE-sizeof.ssh_packet_header |
tx_seq dd ? ; Packet sequence number for MAC |
tx_buffer ssh_packet_header |
rb BUFFERSIZE-sizeof.ssh_packet_header |
send_data dw ? |
; Output from key exchange |
dh_K dd ? ; Shared Secret (Big endian) |
rb MAX_BITS/8 |
dh_K_length dd ? ; Length in little endian |
dh_H rb 32 ; Exchange Hash |
session_id_prefix db ? |
session_id rb 32 |
rx_iv rb 32 ; Rx initialisation vector |
tx_iv rb 32 ; Tx initialisation vector |
rx_enc_key rb 32 ; Rx encryption key |
tx_enc_key rb 32 ; Tx encryption key |
rx_int_key rb 32 ; Rx integrity key |
tx_int_key rb 32 ; Tx integrity key |
; Diffie Hellman |
dh_p dd ? |
rb MAX_BITS/8 |
dh_g dd ? |
rb MAX_BITS/8 |
dh_x dd ? |
rb MAX_BITS/8 |
dh_e dd ? |
rb MAX_BITS/8 |
dh_f dd ? |
rb MAX_BITS/8 |
dh_signature dd ? |
rb MAX_BITS/8 |
temp_ctx crash_ctx |
k_h_ctx crash_ctx |
mpint_tmp dd ? |
rb MAX_BITS/8 |
ends |
start: |
mcall 68, 11 ; Init heap |
190,7 → 134,7 |
DEBUGF 2, "SSH: Loading libraries\n" |
stdcall dll.Load, @IMPORT |
test eax, eax |
jnz exit |
jnz main.fail |
DEBUGF 2, "SSH: Init PRNG\n" |
call create_seed |
198,717 → 142,449 |
DEBUGF 2, "SSH: Init Console\n" |
invoke con_start, 1 |
invoke con_init, 80, 25, 80, 25, title |
invoke con_init, 80, 25, 800, 250, title |
; Check for parameters TODO |
; cmp byte[params], 0 |
; jne resolve |
cmp byte[params], 0 |
jne main.connect |
main: |
invoke con_cls |
; Welcome user |
invoke con_write_asciiz, str1 |
prompt: |
; write prompt |
invoke con_write_asciiz, str1a |
.prompt: |
invoke con_write_asciiz, str1b |
; Reset window title |
invoke con_set_title, title |
; Write prompt |
invoke con_write_asciiz, str2 |
; read string |
mov esi, con.hostname |
invoke con_gets, esi, 256 |
mov esi, params |
invoke con_gets, esi, MAX_HOSTNAME_LENGTH |
; check for exit |
test eax, eax |
jz done |
jz .done |
cmp byte[esi], 10 |
jz done |
jz .done |
resolve: |
mov [con.sockaddr], AF_INET4 |
mov [con.port], 22 shl 8 |
.connect: |
stdcall sshlib_connect, ssh_con, params |
cmp eax, 0 |
jg .prompt |
jl .error |
; delete terminating '\n' |
mov esi, con.hostname |
@@: |
lodsb |
cmp al, ':' |
je .do_port |
cmp al, 0x20 |
ja @r |
mov byte[esi-1], 0 |
jmp .done |
.do_port: |
xor eax, eax |
xor ebx, ebx |
mov byte[esi-1], 0 |
.portloop: |
lodsb |
cmp al, 0x20 |
jbe .port_done |
sub al, '0' |
jb hostname_error |
cmp al, 9 |
ja hostname_error |
lea ebx, [ebx*4+ebx] |
shl ebx, 1 |
add ebx, eax |
jmp .portloop |
.port_done: |
xchg bl, bh |
mov [con.port], bx |
.done: |
; resolve name |
push esp ; reserve stack place |
push esp |
invoke getaddrinfo, con.hostname, 0, 0 |
pop esi |
; test for error |
.login: |
mcall 68, 12, (MAX_USERNAME_LENGTH + MAX_PASSWORD_LENGTH) |
test eax, eax |
jnz dns_error |
jz .done ; ERR_NOMEM |
mov esi, eax |
lea edi, [eax + MAX_USERNAME_LENGTH] |
invoke con_write_asciiz, str3 |
invoke con_write_asciiz, con.hostname |
; write results |
invoke con_write_asciiz, str8 |
; convert IP address to decimal notation |
mov eax, [esi+addrinfo.ai_addr] |
mov eax, [eax+sockaddr_in.sin_addr] |
mov [con.ip], eax |
invoke inet_ntoa, eax |
; write result |
invoke con_write_asciiz, eax |
; free allocated memory |
invoke freeaddrinfo, esi |
invoke con_write_asciiz, str9 |
mcall 40, EVM_STACK + EVM_KEY |
; Create socket |
mcall socket, AF_INET4, SOCK_STREAM, 0 |
cmp eax, -1 |
jz socket_err |
mov [con.socketnum], eax |
; Connect |
DEBUGF 2, "Connecting to server\n" |
mcall connect, [con.socketnum], con.sockaddr, 18 |
test eax, eax |
jnz socket_err |
; Start calculating hash |
invoke sha256_init, con.temp_ctx |
; HASH: string V_C, the client's version string (CR and NL excluded) |
invoke sha256_update, con.temp_ctx, ssh_ident_ha, ssh_ident.length+4-2 |
; >> Send our identification string |
DEBUGF 2, "Sending ID string\n" |
mcall send, [con.socketnum], ssh_ident, ssh_ident.length, 0 |
cmp eax, -1 |
je socket_err |
; << Check protocol version of server |
mcall recv, [con.socketnum], con.rx_buffer, BUFFERSIZE, 0 |
cmp eax, -1 |
je socket_err |
DEBUGF 2, "Received ID string\n" |
cmp dword[con.rx_buffer], "SSH-" |
jne proto_err |
cmp dword[con.rx_buffer+4], "2.0-" |
jne proto_err |
; HASH: string V_S, the server's version string (CR and NL excluded) |
lea edx, [eax+2] |
sub eax, 2 |
bswap eax |
mov dword[con.rx_buffer-4], eax |
invoke sha256_update, con.temp_ctx, con.rx_buffer-4, edx |
; >> Key Exchange init |
mov [con.rx_seq], 0 |
mov [con.tx_seq], 0 |
mov [con.rx_crypt_blocksize], 4 ; minimum blocksize |
mov [con.tx_crypt_blocksize], 4 |
mov [con.rx_crypt_proc], 0 |
mov [con.tx_crypt_proc], 0 |
mov [con.rx_mac_proc], 0 |
mov [con.tx_mac_proc], 0 |
mov [con.rx_mac_length], 0 |
mov [con.tx_mac_length], 0 |
; mov [con.rx_padsize], 8 ; minimum padsize |
mov [con.tx_pad_size], 8 |
mov [con.tx_pad_proc], padding_zero |
DEBUGF 2, "Sending KEX init\n" |
mov edi, ssh_kex.cookie |
call MBRandom |
stosd |
call MBRandom |
stosd |
call MBRandom |
stosd |
call MBRandom |
stosd |
stdcall ssh_send_packet, con, ssh_kex, ssh_kex.length, 0 |
cmp eax, -1 |
je socket_err |
; HASH: string I_C, the payload of the client's SSH_MSG_KEXINIT |
mov eax, dword[con.tx_buffer+ssh_packet_header.packet_length] |
bswap eax |
movzx ebx, [con.tx_buffer+ssh_packet_header.padding_length] |
sub eax, ebx |
dec eax |
lea edx, [eax+4] |
bswap eax |
mov dword[con.tx_buffer+1], eax |
invoke sha256_update, con.temp_ctx, con.tx_buffer+1, edx |
; << Check key exchange init of server |
stdcall ssh_recv_packet, con, 0 |
cmp eax, -1 |
je socket_err |
cmp [con.rx_buffer.message_code], SSH_MSG_KEXINIT |
jne proto_err |
DEBUGF 2, "Received KEX init\n" |
lea esi, [con.rx_buffer+sizeof.ssh_packet_header+16] |
lodsd |
bswap eax |
DEBUGF 1, "kex_algorithms: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "server_host_key_algorithms: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "encryption_algorithms_client_to_server: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "encryption_algorithms_server_to_client: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "mac_algorithms_client_to_server: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "mac_algorithms_server_to_client: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "compression_algorithms_client_to_server: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "compression_algorithms_server_to_client: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "languages_client_to_server: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "languages_server_to_client: %s\n", esi |
add esi, eax |
lodsb |
DEBUGF 1, "KEX First Packet Follows: %u\n", al |
; TODO: parse this structure and init procedures accordingly |
; HASH: string I_S, the payload of the servers's SSH_MSG_KEXINIT |
mov eax, dword[con.rx_buffer+ssh_packet_header.packet_length] |
movzx ebx, [con.rx_buffer+ssh_packet_header.padding_length] |
sub eax, ebx |
dec eax |
lea edx, [eax+4] |
bswap eax |
mov dword[con.rx_buffer+sizeof.ssh_packet_header-5], eax |
invoke sha256_update, con.temp_ctx, con.rx_buffer+sizeof.ssh_packet_header-5, edx |
; Exchange keys with the server |
; TODO: host verification |
stdcall dh_gex |
test eax, eax |
jnz exit |
; Set keys and initialize transport subroutines |
DEBUGF 2, "SSH: Setting encryption keys\n" |
stdcall aes256_ctr_init, con.rx_iv |
mov [con.rx_crypt_ctx_ptr], eax |
stdcall aes256_set_encrypt_key, eax, con.rx_enc_key |
mov [con.rx_crypt_proc], aes256_ctr_crypt |
mov [con.rx_crypt_blocksize], AES256_BLOCKSIZE |
; mov [con.rx_pad_size], AES256_BLOCKSIZE |
stdcall aes256_ctr_init, con.tx_iv |
mov [con.tx_crypt_ctx_ptr], eax |
stdcall aes256_set_encrypt_key, eax, con.tx_enc_key |
mov [con.tx_crypt_proc], aes256_ctr_crypt |
mov [con.tx_crypt_blocksize], AES256_BLOCKSIZE |
mov [con.tx_pad_size], AES256_BLOCKSIZE |
mov [con.tx_pad_proc], MBRandom |
stdcall hmac_sha256_setkey, con.rx_mac_ctx, con.rx_int_key, SHA256_HASH_SIZE |
mov [con.rx_mac_proc], hmac_sha256 |
mov [con.rx_mac_length], SHA256_HASH_SIZE |
stdcall hmac_sha256_setkey, con.tx_mac_ctx, con.tx_int_key, SHA256_HASH_SIZE |
mov [con.tx_mac_proc], hmac_sha256 |
mov [con.tx_mac_length], SHA256_HASH_SIZE |
; Re-seed RNG for padding bytes |
call create_seed |
call init_random |
; TODO: erase all keys from memory and free the memory |
; >> Request service (user-auth) |
DEBUGF 2, "SSH: Requesting service\n" |
stdcall ssh_send_packet, con, ssh_request_service, ssh_request_service.length, 0 |
cmp eax, -1 |
je socket_err |
; << Check for service acceptance |
stdcall ssh_msg_handler, con, 0 |
cmp eax, -1 |
je socket_err |
cmp [con.rx_buffer.message_code], SSH_MSG_SERVICE_ACCEPT |
jne proto_err |
; >> Request user authentication |
DEBUGF 2, "SSH: User authentication\n" |
mcall 68, 12, 1024 ; FIXME |
test eax, eax |
jz done ; FIXME |
mov edi, eax |
mov ebx, eax |
mov byte[edi], SSH_MSG_USERAUTH_REQUEST |
inc edi |
; Get username |
add edi, 4 |
invoke con_write_asciiz, str12 |
invoke con_gets, edi, 256 ; FIXME |
invoke con_gets, esi, MAX_USERNAME_LENGTH |
test eax, eax |
jz done ; FIXME |
;; jz .con_closed_must_clear |
mov edx, eax |
mov ecx, 256 |
xor al, al |
repne scasb |
dec edi ; \0 |
dec edi ; \n |
push edi |
sub edi, edx |
bswap edi |
mov [edx-4], edi |
pop edi |
mov dword[edi], 0x0e000000 ; 14 Bswapped |
mov dword[edi+4], "ssh-" |
mov dword[edi+8], "conn" |
mov dword[edi+12], "ecti" |
mov word[edi+16], "on" |
add edi, 18 |
mov dword[edi], 0x08000000 ; 8 Bswapped |
mov dword[edi+4], "pass" |
mov dword[edi+8], "word" |
mov byte[edi+12], 0 ; bool |
add edi, 13 |
; Get password |
add edi, 4 |
invoke con_write_asciiz, str13 |
push eax |
invoke con_gets, edi, 256 ; FIXME |
invoke con_write_asciiz, str13a |
invoke con_gets, edi, MAX_PASSWORD_LENGTH |
test eax, eax |
jz done ; FIXME |
;; jz .con_closed_must_clear |
invoke con_write_asciiz, str13b |
mov edx, eax |
mov ecx, 256 |
xor al, al |
repne scasb |
dec edi ; \0 |
dec edi ; \n |
push edi |
sub edi, edx |
bswap edi |
mov [edx-4], edi |
pop edi |
sub edi, ebx |
push ebx |
stdcall ssh_send_packet, con, ebx, edi, 0 |
; Clear used buffer and free |
pop edx |
mov edi, edx |
; Authenticate |
stdcall sshlib_userauth_password, ssh_con, esi, edi |
; Clear and free username and password |
.clear: |
push eax |
mov ecx, 1024/4 ; FIXME |
mov edx, edi |
xor eax, eax |
mov ecx, (MAX_USERNAME_LENGTH + MAX_PASSWORD_LENGTH)/4 |
rep stosd |
mcall 68, 13, edx |
pop eax |
cmp eax, -1 |
je socket_err |
cmp eax, 0 |
jg .login ; Authentication failed |
jl .error ; An error occured |
invoke con_write_asciiz, str14 |
; Open a channel |
stdcall sshlib_chan_open, ssh_con |
cmp eax, 0 |
jg .prompt ; Authentication failed |
jl .error ; An error occured |
; << Check for userauth acceptance |
stdcall ssh_msg_handler, con, 0 |
cmp eax, -1 |
je socket_err |
cmp [con.rx_buffer.message_code], SSH_MSG_USERAUTH_SUCCESS |
jne proto_err |
; >> Open channel |
DEBUGF 2, "SSH: Open channel\n" |
stdcall ssh_send_packet, con, ssh_channel_open, ssh_channel_open.length, 0 |
cmp eax, -1 |
je socket_err |
; << Check for channel open confirmation |
stdcall ssh_msg_handler, con, 0 |
cmp eax, -1 |
je socket_err |
cmp [con.rx_buffer.message_code], SSH_MSG_CHANNEL_OPEN_CONFIRMATION |
jne proto_err |
; >> Channel request: pty |
DEBUGF 2, "SSH: Request pty\n" |
stdcall ssh_send_packet, con, ssh_channel_request, ssh_channel_request.length, 0 |
cmp eax, -1 |
je socket_err |
; << Check for channel request confirmation |
stdcall ssh_msg_handler, con, 0 |
cmp eax, -1 |
je socket_err |
cmp [con.rx_buffer.message_code], SSH_MSG_CHANNEL_SUCCESS |
jne proto_err |
; >> Channel request: shell |
DEBUGF 2, "SSH: Request shell\n" |
stdcall ssh_send_packet, con, ssh_shell_request, ssh_shell_request.length, 0 |
cmp eax, -1 |
je socket_err |
; << Check for channel request confirmation (FIXME: this may not be first packet!) |
; TODO |
; |
; stdcall ssh_msg_handler, con, 0 |
; cmp eax, -1 |
; je socket_err |
; cmp [con.rx_buffer.message_code], SSH_MSG_CHANNEL_SUCCESS |
; jne proto_err |
; Launch network thread |
; Start console input handler thread without deactivating the current window |
; Get active window ID |
mcall 18, 7 |
push eax |
mcall 51, 1, thread, mem - 2048 |
; Create thread |
mcall 51, 1, con_in_thread, mem - 2048 |
; Activate window with given ID |
pop ecx |
mcall 18, 3 |
mainloop: |
call [con_get_flags] |
test eax, 0x200 ; con window closed? |
jnz exit |
.loop: |
invoke con_get_flags |
test eax, 0x200 ; console window closed? |
jnz .con_closed |
stdcall ssh_msg_handler, con, 0 |
stdcall sshlib_msg_handler, ssh_con, 0 |
cmp eax, 0 |
jbe closed |
jle .check_err |
cmp [con.rx_buffer.message_code], SSH_MSG_CHANNEL_DATA |
cmp [ssh_con.rx_buffer.message_code], SSH_MSG_CHANNEL_DATA |
jne .dump |
mov eax, dword[con.rx_buffer.message_code+5] |
mov eax, dword[ssh_con.rx_buffer.message_code+5] |
bswap eax |
DEBUGF 1, 'SSH: got %u bytes of data !\n', eax |
lea esi, [con.rx_buffer.message_code+5+4] |
mov ecx, eax |
lea edi, [esi + eax] |
mov byte [edi], 0 |
lea esi, [ssh_con.rx_buffer.message_code+5+4] |
lea edx, [esi+eax] |
lea edi, [ssh_con.rx_buffer] |
@@: |
call get_byte_utf8 |
stosb |
cmp esi, edx |
jb @r |
xor al, al |
stosb |
lea esi, [ssh_con.rx_buffer] |
DEBUGF 3, 'SSH msg: %s\n', esi |
invoke con_write_asciiz, esi |
jmp mainloop |
jmp .loop |
.dump: |
lea esi, [con.rx_buffer] |
DEBUGF 3, "SSH: Unsupported message: " |
lea esi, [ssh_con.rx_buffer.message_code] |
mov ecx, eax |
pusha |
@@: |
lodsb |
DEBUGF 1, "%x ", eax:2 |
DEBUGF 3, "%x ", eax:2 |
dec ecx |
jnz @r |
popa |
DEBUGF 1, "\n" |
jmp mainloop |
DEBUGF 3, "\n" |
jmp .loop |
.check_err: |
jz .err_conn_closed |
cmp ebx, EWOULDBLOCK |
je .loop |
jmp .err_sock |
proto_err: |
mov eax, con.rx_buffer |
int3 |
.con_closed: |
; Send close message on the active channel |
stdcall sshlib_send_packet, ssh_con, ssh_msg_channel_close, ssh_msg_channel_close.length, 0 |
jmp .done |
DEBUGF 3, "SSH: protocol error\n" |
.error: |
; TODO: proper cleanup after error |
cmp eax, SSHLIB_ERR_NOMEM |
je .done |
cmp eax, SSHLIB_ERR_SOCKET |
je .err_sock |
cmp eax, SSHLIB_ERR_PROTOCOL |
je .err_proto |
cmp eax, SSHLIB_ERR_HOSTNAME |
je .err_hostname |
cmp eax, SSHLIB_ERR_HKEY_VERIFY_FAIL |
je .err_hostkey_fail |
cmp eax, SSHLIB_ERR_HKEY_SIGNATURE |
je .err_hostkey_signature |
cmp eax, SSHLIB_ERR_HKEY_PUBLIC_KEY |
je .err_hostkey |
jmp .done |
.err_proto: |
; lea eax, [ssh_con.rx_buffer] |
; int3 |
invoke con_write_asciiz, str7 |
jmp prompt |
jmp .prompt |
socket_err: |
DEBUGF 3, "SSH: socket error %d\n", ebx |
.err_sock: |
invoke con_write_asciiz, str6 |
jmp prompt |
dns_error: |
DEBUGF 3, "SSH: DNS error %d\n", eax |
invoke con_write_asciiz, str5 |
jmp prompt |
mov eax, str14 |
cmp ebx, ETIMEDOUT |
je .err_sock_detail |
mov eax, str15 |
cmp ebx, ECONNREFUSED |
je .err_sock_detail |
mov eax, str16 |
cmp ebx, ECONNRESET |
je .err_sock_detail |
mov eax, str17 |
.err_sock_detail: |
invoke con_write_asciiz, eax |
jmp .prompt |
hostname_error: |
.err_hostname: |
invoke con_write_asciiz, str10 |
jmp prompt |
jmp .prompt |
closed: |
.err_conn_closed: |
invoke con_write_asciiz, str11 |
jmp prompt |
jmp .prompt |
done: |
.err_hostkey: |
invoke con_write_asciiz, str19 |
jmp .prompt |
.err_hostkey_signature: |
invoke con_write_asciiz, str20 |
jmp .prompt |
.err_hostkey_fail: |
invoke con_write_asciiz, str21 |
jmp .prompt |
.done: |
invoke con_exit, 1 |
exit: |
.exit: |
DEBUGF 3, "SSH: Exiting\n" |
mcall close, [con.socketnum] |
mcall close, [ssh_con.socketnum] |
.fail: |
mcall -1 |
thread: |
mcall 40, 0 |
.loop: |
invoke con_getch2 |
mov [ssh_channel_data+9], al |
stdcall ssh_send_packet, con, ssh_channel_data, ssh_channel_data.length, MSG_DONTWAIT |
proc sshlib_callback_connecting, con_ptr, connstring_sz |
invoke con_get_flags |
test eax, 0x200 ; con window closed? |
jz .loop |
mcall -1 |
invoke con_write_asciiz, str3 |
mov eax, [con_ptr] |
lea eax, [eax+sshlib_connection.hostname_sz] |
invoke con_write_asciiz, eax |
invoke con_write_asciiz, str8 |
invoke con_write_asciiz, [connstring_sz] |
invoke con_write_asciiz, str9 |
ret |
endp |
; Handle common messages and return from specific ones |
proc ssh_msg_handler, con, flags |
.recv: |
stdcall ssh_recv_packet, [con], [flags] |
cmp eax, -1 |
je .ret |
proc sshlib_callback_hostkey_problem, con_ptr, problem_type, hostkey_sz |
cmp [con.rx_buffer.message_code], SSH_MSG_DISCONNECT |
je .disc |
cmp [con.rx_buffer.message_code], SSH_MSG_IGNORE |
je .ign |
cmp [con.rx_buffer.message_code], SSH_MSG_DEBUG |
je .dbg |
cmp [con.rx_buffer.message_code], SSH_MSG_GLOBAL_REQUEST |
je .glob |
cmp [problem_type], SSHLIB_HOSTKEY_PROBLEM_UNKNOWN |
je .unknown |
cmp [problem_type], SSHLIB_HOSTKEY_PROBLEM_MISMATCH |
je .mismatch |
.ret: |
ret |
.disc: |
mov eax, -1 |
ret |
.ign: |
jmp .recv |
.unknown: |
invoke con_write_asciiz, str22 |
jmp .ask |
.dbg: |
.glob: |
; TODO |
.mismatch: |
invoke con_write_asciiz, str23 |
; jmp .ask |
.ask: |
;;; TODO: print hostkey |
invoke con_write_asciiz, str24 |
.getansw: |
invoke con_getch2 |
or al, 0x20 ; convert to lowercase |
cmp al, 'a' |
je .accept |
cmp al, 'c' |
je .once |
cmp al, 'x' |
je .refuse |
jmp .getansw |
jmp .recv |
.accept: |
mov eax, SSHLIB_HOSTKEY_ACCEPT |
ret |
.once: |
mov eax, SSHLIB_HOSTKEY_ONCE |
ret |
.refuse: |
mov eax, SSHLIB_HOSTKEY_REFUSE |
ret |
endp |
align 16 |
con_in_thread: |
.loop: |
; TODO: check if channel is still open somehow |
invoke con_get_input, ssh_msg_channel_data.data, MAX_INPUT_LENGTH |
test eax, eax |
jz .no_input |
lea ecx, [eax + ssh_msg_channel_data.data - ssh_msg_channel_data] |
bswap eax |
mov [ssh_msg_channel_data.len], eax |
stdcall sshlib_send_packet, ssh_con, ssh_msg_channel_data, ecx, 0 |
cmp eax, 0 |
jle .exit |
.no_input: |
invoke con_get_flags |
test eax, 0x200 ; con window closed? |
jz .loop |
.exit: |
mcall -1 |
; data |
title db 'Secure Shell',0 |
str1 db 'SSH client for KolibriOS',10,10,\ |
'Please enter URL of SSH server (hostname:port)',10,10,0 |
str1a db 'SSHv2 client for KolibriOS',10,0 |
str1b db 10,'Please enter URL of SSH server (hostname:port)',10,0 |
str2 db '> ',0 |
str3 db 'Connecting to ',0 |
str4 db 10,0 |
str5 db 'Name resolution failed.',10,10,0 |
str6 db 'A socket error occured.',10,10,0 |
str7 db 'A protocol error occured.',10,10,0 |
str6 db 10, 27, '[2J',27,'[mA network error has occured.',10,0 |
str7 db 10, 27, '[2J',27,'[mAn SSH protocol error has occured.',10,0 |
str8 db ' (',0 |
str9 db ')',10,0 |
str10 db 'Invalid hostname.',10,10,0 |
str11 db 10,'Remote host closed the connection.',10,10,0 |
str10 db 'Host does not exist.',10,10,0 |
str11 db 10, 27, '[2J',27,'[mThe remote host closed the connection.',10,0 |
str12 db 'Login as: ',0 |
str13 db 'Password: ', 27, '[?25l', 27, '[30;40m', 0 |
str14 db 10, 27, '[?25h', 27, '[0m', 0 |
str13a db 'Password: ', 27, '[?25l', 27, '[30;40m', 0 |
str13b db 10, 27, '[?25h', 27, '[0m', 27, '[2J', 0 |
str14 db 'The connection timed out',10,0 |
str15 db 'The connection was refused',10,0 |
str16 db 'The connection was reset',10,0 |
str17 db 'No details available',10,0 |
;str18 db 'User authentication failed',10,0;;;; |
str19 db "The remote host's public key is invalid.", 10, 0 |
str20 db "The remote host's signature is invalid.", 10, 0 |
str21 db "The remote host failed to verify it's own public key.", 10, 0 |
str22 db "The host key for the server was not found in the cache.", 10 |
db "There is no guarantee to the servers identity !",10, 0 |
str23 db "The host key provided by the host does not match the cached one.", 10 |
db "This may indicate that the remote server has been compromised!", 10, 0 |
str24 db 10, "If you trust this host, press A to accept and store the (new) key.", 10 |
db "Press C to connect to the host but don't store the (new) key.", 10 |
db "Press X to abort.", 10, 0 |
ssh_ident_ha: |
dd_n (ssh_ident.length-2) |
ssh_ident: |
db "SSH-2.0-KolibriOS_SSH_0.04",13,10 |
.length = $ - ssh_ident |
dd_n (ssh_msg_ident.length-2) |
ssh_msg_ident: |
db "SSH-2.0-KolibriOS_SSH_0.05",13,10 |
.length = $ - ssh_msg_ident |
ssh_kex: |
ssh_msg_kex: |
db SSH_MSG_KEXINIT |
.cookie: |
rd 4 |
.kex_algorithms: |
dd_n .server_host_key_algorithms - .kex_algorithms - 4 |
db "diffie-hellman-group-exchange-sha256" ; diffie-hellman-group-exchange-sha1 |
str "diffie-hellman-group-exchange-sha256" ; diffie-hellman-group-exchange-sha1 |
.server_host_key_algorithms: |
dd_n .encryption_algorithms_client_to_server - .server_host_key_algorithms - 4 |
db "ssh-rsa" ;,ssh-dss |
str "ssh-rsa" ;,ssh-dss |
.encryption_algorithms_client_to_server: |
dd_n .encryption_algorithms_server_to_client - .encryption_algorithms_client_to_server - 4 |
db "aes256-ctr" ;,aes256-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,aes192-ctr,aes192-cbc,aes128-ctr,aes128-cbc,blowfish-ctr,blowfish-cbc,3des-ctr,3des-cbc,arcfour256,arcfour128" |
str "aes256-ctr" ;,aes256-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,aes192-ctr,aes192-cbc,aes128-ctr,aes128-cbc,blowfish-ctr,blowfish-cbc,3des-ctr,3des-cbc,arcfour256,arcfour128" |
.encryption_algorithms_server_to_client: |
dd_n .mac_algorithms_client_to_server - .encryption_algorithms_server_to_client - 4 |
db "aes256-ctr" ;,aes256-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,aes192-ctr,aes192-cbc,aes128-ctr,aes128-cbc,blowfish-ctr,blowfish-cbc,3des-ctr,3des-cbc,arcfour256,arcfour128" |
str "aes256-ctr" ;,aes256-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,aes192-ctr,aes192-cbc,aes128-ctr,aes128-cbc,blowfish-ctr,blowfish-cbc,3des-ctr,3des-cbc,arcfour256,arcfour128" |
.mac_algorithms_client_to_server: |
dd_n .mac_algorithms_server_to_client - .mac_algorithms_client_to_server - 4 |
db "hmac-sha2-256" ;,hmac-sha1,hmac-sha1-96,hmac-md5" |
str "hmac-sha2-256" ;,hmac-sha1,hmac-sha1-96,hmac-md5" |
.mac_algorithms_server_to_client: |
dd_n .compression_algorithms_client_to_server - .mac_algorithms_server_to_client - 4 |
db "hmac-sha2-256" ;,hmac-sha1,hmac-sha1-96,hmac-md5" |
str "hmac-sha2-256" ;,hmac-sha1,hmac-sha1-96,hmac-md5" |
.compression_algorithms_client_to_server: |
dd_n .compression_algorithms_server_to_client - .compression_algorithms_client_to_server - 4 |
db "none" ;,zlib" |
str "none" ;,zlib" |
.compression_algorithms_server_to_client: |
dd_n .languages_client_to_server - .compression_algorithms_server_to_client - 4 |
db "none" ;,zlib" |
str "none" ;,zlib" |
.languages_client_to_server: |
dd_n .languages_server_to_client - .languages_client_to_server - 4 |
db "" |
str "" |
.languages_server_to_client: |
dd_n .first_kex_packet_follows - .languages_server_to_client - 4 |
db "" |
str "" |
.first_kex_packet_follows: |
db 0 |
.reserved: |
dd_n 0 |
.length = $ - ssh_kex |
.length = $ - ssh_msg_kex |
ssh_gex_req: |
ssh_msg_gex_req: |
db SSH_MSG_KEX_DH_GEX_REQUEST |
dd_n 4096/4 ; DH GEX min |
dd_n 4096/2 ; DH GEX number of bits |
dd_n 4096 ; DH GEX Max |
.length = $ - ssh_gex_req |
.length = $ - ssh_msg_gex_req |
ssh_new_keys: |
ssh_msg_new_keys: |
db SSH_MSG_NEWKEYS |
.length = $ - ssh_new_keys |
.length = $ - ssh_msg_new_keys |
ssh_request_service: |
ssh_msg_request_service: |
db SSH_MSG_SERVICE_REQUEST |
dd_n 12 ; String length |
db "ssh-userauth" ; Service name |
.length = $ - ssh_request_service |
str "ssh-userauth" ; Service name |
.length = $ - ssh_msg_request_service |
ssh_request_userauth: |
db SSH_MSG_USERAUTH_REQUEST |
dd_n 9 |
db "user123" ; user name in ISO-10646 UTF-8 encoding [RFC3629] |
dd_n 14 |
db "ssh-connection" ; service name in US-ASCII |
dd_n 8 |
db "password" ; method name in US-ASCII: none, publickey, password, hostbased |
db 0 ; bool: false |
dd_n 14 |
db "pass123" |
.length = $ - ssh_request_userauth |
ssh_msg_channel_open: |
db SSH_MSG_CHANNEL_OPEN |
str "session" |
dd_n 0 ; Sender channel |
dd_n BUFFERSIZE ; Initial window size |
dd_n PACKETSIZE ; maximum packet size |
.length = $ - ssh_msg_channel_open |
ssh_channel_open: |
db SSH_MSG_CHANNEL_OPEN |
dd_n 7 |
db "session" |
ssh_msg_channel_close: |
db SSH_MSG_CHANNEL_CLOSE |
dd_n 0 ; Sender channel |
dd_n 1024 ; Initial window size |
dd_n 1024 ; maximum packet size |
.length = $ - ssh_channel_open |
.length = $ - ssh_msg_channel_close |
ssh_channel_request: |
ssh_msg_channel_request: |
db SSH_MSG_CHANNEL_REQUEST |
dd_n 0 ; Recipient channel |
dd_n 7 |
db "pty-req" |
str "pty-req" |
db 1 ; Bool: want reply |
dd_n 5 |
db "xterm" |
str "xterm" |
dd_n 80 ; terminal width (rows) |
dd_n 25 ; terminal height (rows) |
dd_n 0 ; terminal width (pixels) |
dd_n 0 ; terminal height (pixels) |
dd_n 80*8 ; terminal width (pixels) |
dd_n 25*16 ; terminal height (pixels) |
dd_n 0 ; list of supported opcodes |
.length = $ - ssh_channel_request |
.length = $ - ssh_msg_channel_request |
ssh_shell_request: |
ssh_msg_shell_request: |
db SSH_MSG_CHANNEL_REQUEST |
dd_n 0 ; Recipient channel |
dd_n 5 |
db "shell" |
str "shell" |
db 1 ; Bool: want reply |
.length = $ - ssh_shell_request |
.length = $ - ssh_msg_shell_request |
ssh_channel_data: |
ssh_msg_channel_data: |
db SSH_MSG_CHANNEL_DATA |
dd_n 0 ; Sender channel |
dd_n 1 |
db ? |
.length = $ - ssh_channel_data |
.len dd ? |
.data rb MAX_INPUT_LENGTH + 1 |
ssh_msg_channel_window_adjust: |
db SSH_MSG_CHANNEL_WINDOW_ADJUST |
dd_n 0 ; Sender channel |
.wnd dd ? |
.length = $ - ssh_msg_channel_window_adjust |
include_debug_strings |
align 4 |
931,10 → 607,9 |
con_gets, 'con_gets', \ |
con_cls, 'con_cls', \ |
con_getch2, 'con_getch2', \ |
con_set_cursor_pos, 'con_set_cursor_pos', \ |
con_write_string, 'con_write_string', \ |
con_get_flags, 'con_get_flags', \ |
con_set_flags, 'con_set_flags' |
con_set_title, 'con_set_title', \ |
con_get_input, 'con_get_input' |
import libcrash, \ |
sha256_init, 'sha256_init', \ |
953,8 → 628,9 |
IncludeUGlobals |
params rb 1024 |
params rb MAX_HOSTNAME_LENGTH |
con ssh_connection |
ssh_con sshlib_connection |
ssh_chan sshlib_channel |
mem: |
/programs/network/ssh/sshlib.inc |
---|
0,0 → 1,155 |
; sshlib.inc - SSHlib constants |
; |
; Copyright (C) 2016-2021 Jeffrey Amelynck |
; |
; This program is free software: you can redistribute it and/or modify |
; it under the terms of the GNU General Public License as published by |
; the Free Software Foundation, either version 3 of the License, or |
; (at your option) any later version. |
; |
; This program is distributed in the hope that it will be useful, |
; but WITHOUT ANY WARRANTY; without even the implied warranty of |
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
; GNU General Public License for more details. |
; |
; You should have received a copy of the GNU General Public License |
; along with this program. If not, see <http://www.gnu.org/licenses/>. |
; Error codes |
SSHLIB_ERR_NOMEM = -1 |
SSHLIB_ERR_SOCKET = -2 |
SSHLIB_ERR_PROTOCOL = -3 |
SSHLIB_ERR_HOSTNAME = -4 |
SSHLIB_ERR_DISCONNECTING = -5 |
SSHLIB_ERR_MAC_VERIFY_FAIL = -6 |
SSHLIB_ERR_HKEY_NO_ALGO = -7 |
SSHLIB_ERR_HKEY_VERIFY_FAIL = -8 |
SSHLIB_ERR_HKEY_SIGNATURE = -9 |
SSHLIB_ERR_HKEY_PUBLIC_KEY = -10 |
; Channel status codes |
SSHLIB_CHAN_STAT_CONNECTING = 0 |
SSHLIB_CHAN_STAT_CONNECTED = 1 |
SSHLIB_CHAN_STAT_EOF_RECEIVED = 2 |
SSHLIB_CHAN_STAT_CLOSING = 3 |
SSHLIB_CHAN_STAT_CLOSED = 3 |
; Connection status codes |
SSHLIB_CON_STAT_INIT = 0 |
SSHLIB_CON_STAT_KEX_DONE = 1 |
; Algorithm identifier codes |
SSHLIB_ALGO_NONE = 0 |
SSHLIB_KEX_DH_SHA1 = 1 |
SSHLIB_KEX_DH_SHA256 = 2 |
SSHLIB_HOSTKEY_DSS = 1 |
SSHLIB_HOSTKEY_RSA = 2 |
SSHLIB_HOSTKEY_RSA_SHA2_256 = 3 |
SSHLIB_HOSTKEY_RSA_SHA2_512 = 4 |
SSHLIB_CRYPT_BLOWFISH_CTR = 1 |
SSHLIB_CRYPT_BLOWFISH_CBC = 2 |
SSHLIB_CRYPT_AES128_CTR = 3 |
SSHLIB_CRYPT_AES128_CBC = 4 |
SSHLIB_CRYPT_AES192_CTR = 5 |
SSHLIB_CRYPT_AES192_CBC = 6 |
SSHLIB_CRYPT_AES256_CTR = 7 |
SSHLIB_CRYPT_AES256_CBC = 8 |
SSHLIB_HMAC_MD5 = 1 |
SSHLIB_HMAC_SHA1 = 2 |
SSHLIB_HMAC_SHA1_96 = 3 |
SSHLIB_HMAC_SHA2_256 = 4 |
SSHLIB_COMPR_NONE = 1 |
SSHLIB_COMPR_ZLIB = 2 |
; Hostkey |
SSHLIB_HOSTKEY_PROBLEM_UNKNOWN = 0 |
SSHLIB_HOSTKEY_PROBLEM_MISMATCH = 1 |
SSHLIB_HOSTKEY_REFUSE = -1 |
SSHLIB_HOSTKEY_ACCEPT = 0 |
SSHLIB_HOSTKEY_ONCE = 1 |
; SSH network packet header |
struct ssh_packet_header |
packet_length dd ? ; The length of the packet in bytes, not including 'mac' or the |
; 'packet_length' field itself. |
padding_length db ? ; Length of 'random padding' (bytes). |
message_code db ? ; First byte of payload |
ends |
; SSH connection structure |
struct sshlib_connection |
status dd ? |
socketnum dd ? |
rx_crypt_proc dd ? |
tx_crypt_proc dd ? |
rx_crypt_ctx_ptr dd ? |
tx_crypt_ctx_ptr dd ? |
rx_crypt_blocksize dd ? |
tx_crypt_blocksize dd ? |
tx_pad_size dd ? ; = Max(8, tx_crypt_blocksize) |
tx_pad_proc dd ? |
rx_mac_proc dd ? |
tx_mac_proc dd ? |
rx_mac_ctx hmac_sha256_context |
tx_mac_ctx hmac_sha256_context |
rx_mac_length dd ? |
tx_mac_length dd ? |
rx_mac_seqnr dd ? ; DO NOT MOVE |
rx_buffer ssh_packet_header |
rb BUFFERSIZE-sizeof.ssh_packet_header |
tx_mac_seqnr dd ? ; DO NOT MOVE |
tx_buffer ssh_packet_header |
rb PACKETSIZE-sizeof.ssh_packet_header |
part_ex_hash_ctx crash_ctx |
session_id rb SHA256_HASH_SIZE |
algo_kex dd ? |
algo_hostkey dd ? |
algo_crypt_rx dd ? |
algo_crypt_tx dd ? |
algo_mac_rx dd ? |
algo_mac_tx dd ? |
algo_compr_rx dd ? |
algo_compr_tx dd ? |
hostname_sz rb MAX_HOSTNAME_LENGTH |
ends |
; SSH channel structure |
struct sshlib_channel |
id dd ? ; Channel ID (big endian) |
status dd ? ; Channel status |
rcv_wnd dd ? ; Receive window |
snd_wnd dd ? ; Send window |
; rcv_callb dd ? ; TODO |
ends |
/programs/network/ssh/sshlib_channel.inc |
---|
0,0 → 1,87 |
; sshlib_channel.inc - SSH channel |
; |
; Copyright (C) 2016-2021 Jeffrey Amelynck |
; |
; This program is free software: you can redistribute it and/or modify |
; it under the terms of the GNU General Public License as published by |
; the Free Software Foundation, either version 3 of the License, or |
; (at your option) any later version. |
; |
; This program is distributed in the hope that it will be useful, |
; but WITHOUT ANY WARRANTY; without even the implied warranty of |
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
; GNU General Public License for more details. |
; |
; You should have received a copy of the GNU General Public License |
; along with this program. If not, see <http://www.gnu.org/licenses/>. |
proc sshlib_chan_open con_ptr; Channel struct ptr?! |
; >> Open channel |
DEBUGF 2, "SSH: Open channel\n" |
mov [ssh_chan.rcv_wnd], BUFFERSIZE |
mov [ssh_chan.snd_wnd], 0 |
stdcall sshlib_send_packet, [con_ptr], ssh_msg_channel_open, ssh_msg_channel_open.length, 0 |
cmp eax, 0 |
jl .err |
; << Check for channel open confirmation |
stdcall sshlib_msg_handler, [con_ptr], 0 |
cmp eax, 0 |
jl .err |
mov esi, [con_ptr] |
cmp [esi + sshlib_connection.rx_buffer.message_code], SSH_MSG_CHANNEL_OPEN_CONFIRMATION |
jne .err_proto |
; >> Channel request: pty |
DEBUGF 2, "SSH: Request pty\n" |
stdcall sshlib_send_packet, [con_ptr], ssh_msg_channel_request, ssh_msg_channel_request.length, 0 |
cmp eax, 0 |
jl .err |
; << Check for channel request confirmation |
stdcall sshlib_msg_handler, [con_ptr], 0 |
cmp eax, 0 |
jl .err |
mov esi, [con_ptr] |
cmp [esi + sshlib_connection.rx_buffer.message_code], SSH_MSG_CHANNEL_SUCCESS |
jne .err_proto |
; >> Channel request: shell |
DEBUGF 2, "SSH: Request shell\n" |
stdcall sshlib_send_packet, [con_ptr], ssh_msg_shell_request, ssh_msg_shell_request.length, 0 |
cmp eax, 0 |
jl .err |
; << Check for channel request confirmation |
; TODO: timeout |
.wait_success: |
stdcall sshlib_msg_handler, [con_ptr], 0 |
cmp eax, 0 |
jl .err |
mov esi, [con_ptr] |
cmp [esi + sshlib_connection.rx_buffer.message_code], SSH_MSG_CHANNEL_SUCCESS |
jne .wait_success |
xor eax, eax |
.err: |
ret |
.err_proto: |
mov eax, SSHLIB_ERR_PROTOCOL |
ret |
endp |
/programs/network/ssh/sshlib_connection.inc |
---|
0,0 → 1,396 |
; sshlib_connection.inc - SSH connection |
; |
; Copyright (C) 2016-2021 Jeffrey Amelynck |
; |
; This program is free software: you can redistribute it and/or modify |
; it under the terms of the GNU General Public License as published by |
; the Free Software Foundation, either version 3 of the License, or |
; (at your option) any later version. |
; |
; This program is distributed in the hope that it will be useful, |
; but WITHOUT ANY WARRANTY; without even the implied warranty of |
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
; GNU General Public License for more details. |
; |
; You should have received a copy of the GNU General Public License |
; along with this program. If not, see <http://www.gnu.org/licenses/>. |
proc sshlib_connect con_ptr, hostname_sz |
locals |
socketnum dd ? |
sockaddr sockaddr_in |
ctx_ptr dd ? |
endl |
mov edi, [con_ptr] |
lea eax, [edi + sshlib_connection.part_ex_hash_ctx] |
mov [ctx_ptr], eax |
; Set default values in sockaddr struct |
mov [sockaddr.sin_family], AF_INET4 |
mov [sockaddr.sin_port], 22 shl 8 |
; Parse hostname_sz |
; Verify length, extract port number if given and copy base url to sshlib_connection struct |
; Port number, if provided, will be written in sockaddr struct. |
; Hostname ends with any character equal to 0x20 or lower |
mov esi, [hostname_sz] |
lea edi, [edi + sshlib_connection.hostname_sz] |
mov ecx, MAX_HOSTNAME_LENGTH |
@@: |
dec ecx |
jz .err_hostname |
lodsb |
cmp al, ':' |
je .do_port |
stosb |
cmp al, 0x20 |
ja @r |
mov byte[edi-1], 0 |
jmp .hostname_ok |
.do_port: |
xor eax, eax |
xor ebx, ebx |
mov byte[edi-1], 0 |
.portloop: |
lodsb |
cmp al, 0x20 |
jbe .port_done |
sub al, '0' |
jb .err_hostname |
cmp al, 9 |
ja .err_hostname |
lea ebx, [ebx*4+ebx] |
shl ebx, 1 |
add ebx, eax |
jmp .portloop |
.port_done: |
xchg bl, bh |
mov [sockaddr.sin_port], bx |
.hostname_ok: |
; resolve name |
push esp ; reserve stack place |
push esp |
mov eax, [con_ptr] |
lea eax, [eax+sshlib_connection.hostname_sz] |
invoke getaddrinfo, eax, 0, 0 |
pop esi |
; test for error |
test eax, eax |
jnz .err_hostname |
; convert IP address to decimal notation |
mov eax, [esi+addrinfo.ai_addr] |
mov eax, [eax+sockaddr_in.sin_addr] |
mov [sockaddr.sin_addr], eax |
invoke inet_ntoa, eax |
; write result |
stdcall sshlib_callback_connecting, [con_ptr], eax |
; free allocated memory |
invoke freeaddrinfo, esi |
; Create socket |
mcall socket, AF_INET4, SOCK_STREAM, 0 |
cmp eax, -1 |
jz .err_sock |
mov [socketnum], eax |
mov ebx, [con_ptr] |
mov [ebx + sshlib_connection.socketnum], eax |
; Connect |
DEBUGF 2, "Connecting to server\n" |
lea edx, [sockaddr] |
mcall connect, [socketnum], , sizeof.sockaddr_in |
test eax, eax |
jnz .err_sock |
; Start calculating hash |
invoke sha256_init, [ctx_ptr] |
; HASH: string V_C, the client's version string (CR and NL excluded) |
invoke sha256_update, [ctx_ptr], ssh_ident_ha, ssh_msg_ident.length+4-2 |
; >> Send our identification string |
DEBUGF 2, "Sending ID string\n" |
mcall send, [socketnum], ssh_msg_ident, ssh_msg_ident.length, 0 |
cmp eax, -1 |
je .err_sock |
; << Check protocol version of server |
mov edx, [con_ptr] |
lea edx, [edx + sshlib_connection.rx_buffer + 4] |
mcall recv, [socketnum], , PACKETSIZE, 0 |
cmp eax, -1 |
je .err_sock |
DEBUGF 2, "Received ID string\n" |
cmp dword[edx], "SSH-" |
jne .err_proto |
cmp dword[edx+4], "2.0-" |
jne .err_proto |
; HASH: string V_S, the server's version string (CR and NL excluded) |
lea ecx, [eax+2] |
sub eax, 2 |
bswap eax |
sub edx, 4 |
mov dword[edx], eax |
invoke sha256_update, [ctx_ptr], edx, ecx |
; >> Key Exchange init |
mov eax, [con_ptr] |
mov [eax + sshlib_connection.status], SSHLIB_CON_STAT_INIT |
mov [eax + sshlib_connection.algo_kex], SSHLIB_ALGO_NONE |
mov [eax + sshlib_connection.algo_hostkey], SSHLIB_ALGO_NONE |
mov [eax + sshlib_connection.algo_crypt_rx], SSHLIB_ALGO_NONE |
mov [eax + sshlib_connection.algo_crypt_tx], SSHLIB_ALGO_NONE |
mov [eax + sshlib_connection.algo_mac_rx], SSHLIB_ALGO_NONE |
mov [eax + sshlib_connection.algo_mac_tx], SSHLIB_ALGO_NONE |
mov [eax + sshlib_connection.algo_compr_rx], SSHLIB_ALGO_NONE |
mov [eax + sshlib_connection.algo_compr_tx], SSHLIB_ALGO_NONE |
mov [eax + sshlib_connection.rx_mac_seqnr], 0 |
mov [eax + sshlib_connection.tx_mac_seqnr], 0 |
mov [eax + sshlib_connection.rx_crypt_blocksize], 4 ; minimum blocksize |
mov [eax + sshlib_connection.tx_crypt_blocksize], 4 |
mov [eax + sshlib_connection.rx_crypt_proc], sshlib_crypt_null |
mov [eax + sshlib_connection.tx_crypt_proc], sshlib_crypt_null |
mov [eax + sshlib_connection.rx_mac_proc], 0 |
mov [eax + sshlib_connection.tx_mac_proc], 0 |
mov [eax + sshlib_connection.rx_mac_length], 0 |
mov [eax + sshlib_connection.tx_mac_length], 0 |
mov [eax + sshlib_connection.tx_pad_size], 8 |
mov [eax + sshlib_connection.tx_pad_proc], sshlib_padd_null |
DEBUGF 2, "Sending KEX init\n" |
mov edi, ssh_msg_kex.cookie |
call MBRandom |
stosd |
call MBRandom |
stosd |
call MBRandom |
stosd |
call MBRandom |
stosd |
stdcall sshlib_send_packet, [con_ptr], ssh_msg_kex, ssh_msg_kex.length, 0 |
cmp eax, -1 |
je .err_sock |
; HASH: string I_C, the payload of the client's SSH_MSG_KEXINIT |
mov esi, [con_ptr] |
mov eax, [esi+sshlib_connection.tx_buffer.packet_length] |
bswap eax |
movzx ebx, [esi+sshlib_connection.tx_buffer.padding_length] |
sub eax, ebx |
dec eax |
lea edx, [eax+4] |
bswap eax |
lea esi, [esi+sshlib_connection.tx_buffer+1] |
mov dword[esi], eax |
invoke sha256_update, [ctx_ptr], esi, edx |
; << Check key exchange init of server |
stdcall sshlib_recv_packet, [con_ptr], 0 |
cmp eax, -1 |
je .err_sock |
mov esi, [con_ptr] |
cmp [esi + sshlib_connection.rx_buffer.message_code], SSH_MSG_KEXINIT |
jne .err_proto |
DEBUGF 2, "Received KEX init\n" |
lea esi, [esi + sshlib_connection.rx_buffer + sizeof.ssh_packet_header + 16] |
lodsd |
bswap eax |
DEBUGF 1, "kex_algorithms: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "server_host_key_algorithms: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "encryption_algorithms_client_to_server: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "encryption_algorithms_server_to_client: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "mac_algorithms_client_to_server: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "mac_algorithms_server_to_client: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "compression_algorithms_client_to_server: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "compression_algorithms_server_to_client: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "languages_client_to_server: %s\n", esi |
add esi, eax |
lodsd |
bswap eax |
DEBUGF 1, "languages_server_to_client: %s\n", esi |
add esi, eax |
lodsb |
DEBUGF 1, "KEX First Packet Follows: %u\n", al |
; TODO: parse this structure and set algorithm codes accordingly |
; FIXME: hardcoded for now |
mov esi, [con_ptr] |
mov [esi+sshlib_connection.algo_kex], SSHLIB_KEX_DH_SHA256 |
mov [esi+sshlib_connection.algo_hostkey], SSHLIB_HOSTKEY_RSA |
mov [esi+sshlib_connection.algo_crypt_rx], SSHLIB_CRYPT_AES256_CTR |
mov [esi+sshlib_connection.algo_crypt_tx], SSHLIB_CRYPT_AES256_CTR |
mov [esi+sshlib_connection.algo_mac_rx], SSHLIB_HMAC_SHA2_256 |
mov [esi+sshlib_connection.algo_mac_tx], SSHLIB_HMAC_SHA2_256 |
mov [esi+sshlib_connection.algo_compr_rx], SSHLIB_COMPR_NONE |
mov [esi+sshlib_connection.algo_compr_tx], SSHLIB_COMPR_NONE |
; HASH: string I_S, the payload of the servers's SSH_MSG_KEXINIT |
mov esi, [con_ptr] |
mov eax, [esi+sshlib_connection.rx_buffer.packet_length] |
movzx ebx, [esi+sshlib_connection.rx_buffer.padding_length] |
sub eax, ebx |
dec eax |
lea edx, [eax+4] |
bswap eax |
lea esi, [esi+sshlib_connection.rx_buffer+1] |
mov dword[esi], eax |
invoke sha256_update, [ctx_ptr], esi, edx |
; Exchange keys with the server |
stdcall sshlib_dh_gex, [con_ptr] |
test eax, eax |
jnz .err |
; Re-seed RNG for padding bytes |
call create_seed |
call init_random |
xor eax, eax |
ret |
.err_hostname: |
mov eax, SSHLIB_ERR_HOSTNAME |
ret |
.err_sock: |
mov eax, SSHLIB_ERR_SOCKET |
ret |
.err_proto: |
mov eax, SSHLIB_ERR_PROTOCOL |
ret |
.err: |
ret |
endp |
; Handle common messages and return to caller for specific ones |
proc sshlib_msg_handler, con_ptr, flags |
.recv: |
; Send a window update if advertised window drops below half |
cmp [ssh_chan.rcv_wnd], BUFFERSIZE/2 |
ja .no_wnd |
mov eax, BUFFERSIZE |
bswap eax |
mov [ssh_msg_channel_window_adjust.wnd], eax |
stdcall sshlib_send_packet, [con_ptr], ssh_msg_channel_window_adjust, ssh_msg_channel_window_adjust.length, 0 |
mov [ssh_chan.rcv_wnd], BUFFERSIZE |
.no_wnd: |
; Receive 1 SSH packet |
stdcall sshlib_recv_packet, [con_ptr], [flags] |
cmp eax, 0 |
jle .ret |
mov esi, [con_ptr] |
lea esi, [esi + sshlib_connection.rx_buffer] |
mov al, [esi + ssh_packet_header.message_code] |
inc esi |
cmp al, SSH_MSG_DISCONNECT |
je .disc |
cmp al, SSH_MSG_IGNORE |
je .ign |
cmp al, SSH_MSG_DEBUG |
je .dbg |
cmp al, SSH_MSG_GLOBAL_REQUEST |
je .glob_req |
cmp al, SSH_MSG_CHANNEL_WINDOW_ADJUST |
je .chan_win_adj |
; cmp al, SSH_MSG_CHANNEL_REQUEST |
; je .chan_req |
cmp al, SSH_MSG_CHANNEL_EOF |
je .chan_eof |
cmp al, SSH_MSG_CHANNEL_CLOSE |
je .chan_close |
.ret: |
ret |
.disc: |
DEBUGF 3, "SSH: Disconnect message received\n" |
mov eax, SSHLIB_ERR_DISCONNECTING |
ret |
.ign: |
DEBUGF 3, "SSH: Ignore MSG received\n" |
jmp .recv |
.dbg: |
DEBUGF 3, "SSH: Debug MSG received\n" |
;TODO |
jmp .recv |
.glob_req: |
DEBUGF 3, "SSH: Global MSG received\n" |
;TODO |
jmp .recv |
.chan_win_adj: |
mov eax, dword[esi] |
bswap eax |
mov [ssh_chan.snd_wnd], eax |
; TODO: validate channel number, act accordingly |
DEBUGF 3, "SSH: Channel %u window update received\n", eax |
jmp .recv |
.chan_eof: |
mov eax, dword[esi] |
bswap eax |
; TODO: validate channel number, act accordingly |
DEBUGF 3, "SSH: Channel %u EOF received\n", eax |
jmp .recv |
.chan_close: |
mov eax, dword[esi] |
bswap eax |
; TODO: validate channel number |
DEBUGF 3, "SSH: Channel %u close received\n", eax |
; Reply with close message |
stdcall sshlib_send_packet, [con_ptr], ssh_msg_channel_close, ssh_msg_channel_close.length, 0 |
xor eax, eax |
ret |
endp |
/programs/network/ssh/sshlib_dh_gex.inc |
---|
0,0 → 1,507 |
; sshlib_dh_gex.inc - Diffie Hellman Group exchange |
; |
; Copyright (C) 2015-2021 Jeffrey Amelynck |
; |
; This program is free software: you can redistribute it and/or modify |
; it under the terms of the GNU General Public License as published by |
; the Free Software Foundation, either version 3 of the License, or |
; (at your option) any later version. |
; |
; This program is distributed in the hope that it will be useful, |
; but WITHOUT ANY WARRANTY; without even the implied warranty of |
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
; GNU General Public License for more details. |
; |
; You should have received a copy of the GNU General Public License |
; along with this program. If not, see <http://www.gnu.org/licenses/>. |
; https://www.ietf.org/rfc/rfc4419.txt |
proc sshlib_dh_gex con_ptr |
locals |
mpint_tmp dd ? |
mpint_p dd ? |
mpint_g dd ? |
mpint_x dd ? |
mpint_e dd ? |
mpint_f dd ? |
mpint_K_big dd ? |
k_h_ctx dd ? |
temp_ctx dd ? |
H dd ? ; exchange hash |
rx_iv dd ? ; Rx initialisation vector |
tx_iv dd ? ; Tx initialisation vector |
rx_enc_key dd ? ; Rx encryption key |
tx_enc_key dd ? ; Tx encryption key |
rx_int_key dd ? ; Rx integrity key |
tx_int_key dd ? ; Tx integrity key |
K_length dd ? |
session_id_x rb SHA256_HASH_SIZE+1 |
str_K_S dd ? ; server public host key and certificates (K_S) |
mpint_f_big dd ? ; pointer to original |
str_s_of_H dd ? ; signature of H |
endl |
; Allocate memory for temp variables |
mov ecx, 7*(MAX_BITS/8+4) + 7*SHA256_HASH_SIZE + 2*sizeof.crash_ctx |
mcall 68, 12 |
test eax, eax |
jz .err_nomem |
; Init pointers for temp variables |
mov [mpint_tmp], eax |
add eax, (MAX_BITS/8+4) |
mov [mpint_p], eax |
add eax, (MAX_BITS/8+4) |
mov [mpint_g], eax |
add eax, (MAX_BITS/8+4) |
mov [mpint_x], eax |
add eax, (MAX_BITS/8+4) |
mov [mpint_e], eax |
add eax, (MAX_BITS/8+4) |
mov [mpint_f], eax |
add eax, (MAX_BITS/8+4) |
mov [mpint_K_big], eax |
add eax, (MAX_BITS/8+4) |
mov [k_h_ctx], eax |
add eax, sizeof.crash_ctx |
mov [temp_ctx], eax |
add eax, sizeof.crash_ctx |
mov [H], eax |
add eax, SHA256_HASH_SIZE |
mov [rx_iv], eax |
add eax, SHA256_HASH_SIZE |
mov [tx_iv], eax |
add eax, SHA256_HASH_SIZE |
mov [rx_enc_key], eax |
add eax, SHA256_HASH_SIZE |
mov [tx_enc_key], eax |
add eax, SHA256_HASH_SIZE |
mov [rx_int_key], eax |
add eax, SHA256_HASH_SIZE |
mov [tx_int_key], eax |
; add eax, SHA256_HASH_SIZE |
; Copy the partial exchange hash to our temporary one |
mov esi, [con_ptr] |
lea esi, [esi+sshlib_connection.part_ex_hash_ctx] |
mov edi, [temp_ctx] |
mov ecx, sizeof.crash_ctx/4 |
rep movsd |
;---------------------------------------------- |
; >> Send Diffie-Hellman Group Exchange Request |
DEBUGF 2, "Sending GEX\n" |
stdcall sshlib_send_packet, [con_ptr], ssh_msg_gex_req, ssh_msg_gex_req.length, 0 |
cmp eax, 0 |
jl .err |
;--------------------------------------------- |
; << Parse Diffie-Hellman Group Exchange Group |
stdcall sshlib_recv_packet, [con_ptr], 0 |
cmp eax, 0 |
jl .err |
mov ebx, [con_ptr] |
cmp [ebx + sshlib_connection.rx_buffer.message_code], SSH_MSG_KEX_DH_GEX_GROUP |
jne .err_proto |
DEBUGF 2, "Received GEX group\n" |
lea esi, [ebx + sshlib_connection.rx_buffer + sizeof.ssh_packet_header] |
stdcall mpint_to_little_endian, [mpint_p], esi |
add esi, 4 |
add esi, eax |
DEBUGM 1, "DH modulus (p): ", [mpint_p] |
stdcall mpint_to_little_endian, [mpint_g], esi |
add esi, 4 |
add esi, eax |
DEBUGM 1, "DH base (g): ", [mpint_g] |
;------------------------------------------- |
; >> Send Diffie-Hellman Group Exchange Init |
; generate a random number x, where 1 < x < (p-1)/2 |
mov edi, [mpint_x] |
mov dword[edi], DH_PRIVATE_KEY_SIZE/8 |
add edi, 4 |
mov ecx, DH_PRIVATE_KEY_SIZE/8/4 |
@@: |
push ecx |
call MBRandom |
pop ecx |
stosd |
dec ecx |
jnz @r |
; If the highest bit is set, add a zero byte |
shl eax, 1 |
jnc @f |
mov byte[edi], 0 |
mov eax, [mpint_x] |
inc dword[eax] |
@@: |
DEBUGM 1, "DH private key (x): ", [mpint_x] |
; Compute e = g^x mod p |
stdcall mpint_modexp, [mpint_e], [mpint_g], [mpint_x], [mpint_p] |
stdcall mpint_shrink, [mpint_e] |
DEBUGM 1, "DH public key (e): ", [mpint_e] |
; Create group exchange init packet |
mov byte[ebx + sshlib_connection.tx_buffer.message_code], SSH_MSG_KEX_DH_GEX_INIT |
lea edi, [ebx + sshlib_connection.tx_buffer.message_code+1] |
stdcall mpint_to_big_endian, edi, [mpint_e] |
DEBUGF 2, "Sending GEX init\n" |
mov ecx, dword[ebx + sshlib_connection.tx_buffer.message_code+1] ;;;; dword[edi] |
bswap ecx |
add ecx, 5 |
lea esi, [ebx + sshlib_connection.tx_buffer.message_code] |
stdcall sshlib_send_packet, [con_ptr], esi, ecx, 0 |
cmp eax, 0 |
jl .err |
;--------------------------------------------- |
; << Parse Diffie-Hellman Group Exchange Reply |
stdcall sshlib_recv_packet, [con_ptr], 0 |
cmp eax, 0 |
jl .err |
mov ebx, [con_ptr] |
cmp [ebx + sshlib_connection.rx_buffer.message_code], SSH_MSG_KEX_DH_GEX_REPLY |
jne .err_proto |
DEBUGF 2, "Received GEX Reply\n" |
;-------------------------------- |
; HASH: string K_S, the host key |
lea esi, [ebx + sshlib_connection.rx_buffer + sizeof.ssh_packet_header] |
mov [str_K_S], esi |
mov edx, [esi] |
bswap edx |
add edx, 4 |
lea eax, [esi+edx] |
mov [mpint_f_big], eax |
invoke sha256_update, [temp_ctx], esi, edx |
;-------------------------------------------------------------------------- |
; HASH: uint32 min, minimal size in bits of an acceptable group |
; uint32 n, preferred size in bits of the group the server will send |
; uint32 max, maximal size in bits of an acceptable group |
invoke sha256_update, [temp_ctx], ssh_msg_gex_req+sizeof.ssh_packet_header-ssh_packet_header.message_code, 12 |
;---------------------------- |
; HASH: mpint p, safe prime |
stdcall mpint_shrink, [mpint_p] |
stdcall mpint_to_big_endian, [mpint_tmp], [mpint_p] |
add eax, 4 |
invoke sha256_update, [temp_ctx], [mpint_tmp], eax |
;---------------------------------------- |
; HASH: mpint g, generator for subgroup |
stdcall mpint_shrink, [mpint_g] |
stdcall mpint_to_big_endian, [mpint_tmp], [mpint_g] |
add eax, 4 |
invoke sha256_update, [temp_ctx], [mpint_tmp], eax |
;--------------------------------------------------- |
; HASH: mpint e, exchange value sent by the client |
mov ebx, [con_ptr] |
lea esi, [ebx + sshlib_connection.tx_buffer + sizeof.ssh_packet_header] |
mov edx, [esi] |
bswap edx |
add edx, 4 |
invoke sha256_update, [temp_ctx], esi, edx |
;--------------------------------------------------- |
; HASH: mpint f, exchange value sent by the server |
mov esi, [mpint_f_big] |
mov edx, [esi] |
bswap edx |
add edx, 4 |
invoke sha256_update, [temp_ctx], esi, edx |
stdcall mpint_to_little_endian, [mpint_f], [mpint_f_big] |
mov esi, [mpint_f_big] |
add esi, eax |
add esi, 4 |
mov [str_s_of_H], esi |
DEBUGM 1, "DH exchange value (f): ", [mpint_f] |
;-------------------------------------- |
; Calculate shared secret K = f^x mod p |
stdcall mpint_modexp, [mpint_tmp], [mpint_f], [mpint_x], [mpint_p] |
stdcall mpint_shrink, [mpint_tmp] |
DEBUGM 1, "DH shared secret (K): ", [mpint_tmp] |
; We always need it in big endian order, so store it as such. |
stdcall mpint_to_big_endian, [mpint_K_big], [mpint_tmp] |
mov [K_length], eax |
;----------------------------------- |
; HASH: mpint K, the shared secret |
add eax, 4 |
invoke sha256_update, [temp_ctx], [mpint_K_big], eax |
;------------------------------- |
; Finalize the exchange hash (H) |
invoke sha256_final, [temp_ctx] |
mov esi, [temp_ctx] |
add esi, crash_ctx.hash |
mov edi, [H] |
mov ecx, SHA256_HASH_SIZE/4 |
rep movsd |
DEBUGF 1, "Exchange hash H: " |
stdcall dump_hex, [H], SHA256_HASH_SIZE/4 |
;-------------------------- |
; Set or get the session id |
mov eax, [con_ptr] |
cmp [eax + sshlib_connection.status], SSHLIB_CON_STAT_KEX_DONE |
jae @f |
; If first KEX, verify host public key |
stdcall sshlib_host_verify, [con_ptr], [str_K_S], [str_s_of_H], [H], SHA256_HASH_SIZE |
test eax, eax |
jnz .err |
mov eax, [con_ptr] |
mov esi, [H] |
lea edi, [eax + sshlib_connection.session_id] |
mov ecx, SHA256_HASH_SIZE/4 |
rep movsd |
@@: |
lea esi, [eax + sshlib_connection.session_id] |
lea edi, [session_id_x+1] |
mov ecx, SHA256_HASH_SIZE/4 |
rep movsd |
;--------------- |
; Calculate keys |
; First, calculate partial hash of K and H so we can re-use it for every key. |
invoke sha256_init, [k_h_ctx] |
mov ecx, [K_length] |
add ecx, 4 |
invoke sha256_update, [k_h_ctx], [mpint_K_big], ecx |
invoke sha256_update, [k_h_ctx], [H], SHA256_HASH_SIZE |
;--------------------------------------------------------------- |
; Initial IV client to server: HASH(K || H || "A" || session_id) |
mov esi, [k_h_ctx] |
mov edi, [temp_ctx] |
mov ecx, sizeof.crash_ctx/4 |
rep movsd |
lea edx, [session_id_x] |
mov byte[edx], 'A' |
invoke sha256_update, [temp_ctx], edx, SHA256_HASH_SIZE+1 |
invoke sha256_final, [temp_ctx] |
mov edi, [tx_iv] |
mov esi, [temp_ctx] |
mov ecx, SHA256_HASH_SIZE/4 |
rep movsd |
DEBUGF 1, "Remote IV: " |
stdcall dump_hex, [tx_iv], SHA256_HASH_SIZE/4 |
;--------------------------------------------------------------- |
; Initial IV server to client: HASH(K || H || "B" || session_id) |
mov esi, [k_h_ctx] |
mov edi, [temp_ctx] |
mov ecx, sizeof.crash_ctx/4 |
rep movsd |
lea edx, [session_id_x] |
mov byte[edx], 'B' |
invoke sha256_update, [temp_ctx], edx, SHA256_HASH_SIZE+1 |
invoke sha256_final, [temp_ctx] |
mov edi, [rx_iv] |
mov esi, [temp_ctx] |
mov ecx, SHA256_HASH_SIZE/4 |
rep movsd |
DEBUGF 1, "Local IV: " |
stdcall dump_hex, [rx_iv], SHA256_HASH_SIZE/4 |
;------------------------------------------------------------------- |
; Encryption key client to server: HASH(K || H || "C" || session_id) |
mov esi, [k_h_ctx] |
mov edi, [temp_ctx] |
mov ecx, sizeof.crash_ctx/4 |
rep movsd |
lea edx, [session_id_x] |
mov byte[edx], 'C' |
invoke sha256_update, [temp_ctx], edx, SHA256_HASH_SIZE+1 |
invoke sha256_final, [temp_ctx] |
mov edi, [tx_enc_key] |
mov esi, [temp_ctx] |
mov ecx, SHA256_HASH_SIZE/4 |
rep movsd |
DEBUGF 1, "Remote key: " |
stdcall dump_hex, [tx_enc_key], SHA256_HASH_SIZE/4 |
;------------------------------------------------------------------- |
; Encryption key server to client: HASH(K || H || "D" || session_id) |
mov esi, [k_h_ctx] |
mov edi, [temp_ctx] |
mov ecx, sizeof.crash_ctx/4 |
rep movsd |
lea edx, [session_id_x] |
mov byte[edx], 'D' |
invoke sha256_update, [temp_ctx], edx, SHA256_HASH_SIZE+1 |
invoke sha256_final, [temp_ctx] |
mov edi, [rx_enc_key] |
mov esi, [temp_ctx] |
mov ecx, SHA256_HASH_SIZE/4 |
rep movsd |
DEBUGF 1, "Local key: " |
stdcall dump_hex, [rx_enc_key], SHA256_HASH_SIZE/4 |
;------------------------------------------------------------------ |
; Integrity key client to server: HASH(K || H || "E" || session_id) |
mov esi, [k_h_ctx] |
mov edi, [temp_ctx] |
mov ecx, sizeof.crash_ctx/4 |
rep movsd |
lea edx, [session_id_x] |
mov byte[edx], 'E' |
invoke sha256_update, [temp_ctx], edx, SHA256_HASH_SIZE+1 |
invoke sha256_final, [temp_ctx] |
mov edi, [tx_int_key] |
mov esi, [temp_ctx] |
mov ecx, SHA256_HASH_SIZE/4 |
rep movsd |
DEBUGF 1, "Remote Integrity key: " |
stdcall dump_hex, [tx_int_key], SHA256_HASH_SIZE/4 |
;------------------------------------------------------------------ |
; Integrity key server to client: HASH(K || H || "F" || session_id) |
mov esi, [k_h_ctx] |
mov edi, [temp_ctx] |
mov ecx, sizeof.crash_ctx/4 |
rep movsd |
lea edx, [session_id_x] |
mov byte[edx], 'F' |
invoke sha256_update, [temp_ctx], edx, SHA256_HASH_SIZE+1 |
invoke sha256_final, [temp_ctx] |
mov edi, [rx_int_key] |
mov esi, [temp_ctx] |
mov ecx, SHA256_HASH_SIZE/4 |
rep movsd |
DEBUGF 1, "Local Integrity key: " |
stdcall dump_hex, [rx_int_key] , SHA256_HASH_SIZE/4 |
;------------------------------------- |
; << Parse Diffie-Hellman New Keys MSG |
stdcall sshlib_recv_packet, [con_ptr], 0 |
cmp eax, 0 |
jl .err |
mov ebx, [con_ptr] |
cmp [ebx + sshlib_connection.rx_buffer.message_code], SSH_MSG_NEWKEYS |
jne .err_proto |
DEBUGF 2, "Received New Keys\n" |
;------------------------------- |
; >> Reply with New Keys message |
stdcall sshlib_send_packet, [con_ptr], ssh_msg_new_keys, ssh_msg_new_keys.length, 0 |
cmp eax, 0 |
jl .err |
;---------------------------------------------- |
; Set keys and initialize transport subroutines |
DEBUGF 2, "SSH: Setting encryption keys\n" |
mov ebx, [con_ptr] |
stdcall aes256_ctr_init, [rx_iv] |
test eax, eax |
jz .err_nomem |
mov [ebx + sshlib_connection.rx_crypt_ctx_ptr], eax |
stdcall aes256_set_encrypt_key, eax, [rx_enc_key] |
mov [ebx + sshlib_connection.rx_crypt_proc], aes256_ctr_crypt |
mov [ebx + sshlib_connection.rx_crypt_blocksize], AES256_BLOCKSIZE |
stdcall aes256_ctr_init, [tx_iv] |
test eax, eax |
jz .err_nomem |
mov [ebx + sshlib_connection.tx_crypt_ctx_ptr], eax |
stdcall aes256_set_encrypt_key, eax, [tx_enc_key] |
mov [ebx + sshlib_connection.tx_crypt_proc], aes256_ctr_crypt |
mov [ebx + sshlib_connection.tx_crypt_blocksize], AES256_BLOCKSIZE |
mov [ebx + sshlib_connection.tx_pad_size], AES256_BLOCKSIZE |
mov [ebx + sshlib_connection.tx_pad_proc], MBRandom |
lea ecx, [ebx + sshlib_connection.rx_mac_ctx] |
stdcall hmac_sha256_setkey, ecx, [rx_int_key], SHA256_HASH_SIZE |
mov [ebx + sshlib_connection.rx_mac_proc], hmac_sha256 |
mov [ebx + sshlib_connection.rx_mac_length], SHA256_HASH_SIZE |
lea ecx, [ebx + sshlib_connection.tx_mac_ctx] |
stdcall hmac_sha256_setkey, ecx, [tx_int_key], SHA256_HASH_SIZE |
mov [ebx + sshlib_connection.tx_mac_proc], hmac_sha256 |
mov [ebx + sshlib_connection.tx_mac_length], SHA256_HASH_SIZE |
mov [ebx + sshlib_connection.status], SSHLIB_CON_STAT_KEX_DONE |
xor eax, eax |
.err: |
push eax |
xor eax, eax |
mov ecx, (7*(MAX_BITS/8+4) + 7*SHA256_HASH_SIZE + 2*sizeof.crash_ctx)/4 |
mov edi, [mpint_tmp] |
rep stosd |
mcall 68, 13, [mpint_tmp] |
pop eax |
ret |
.err_nomem: |
DEBUGF 3, "Out of memory during key exchange!\n" |
mov eax, SSHLIB_ERR_NOMEM |
ret |
.err_proto: |
DEBUGF 3, "Protocol error during key exchange!\n" |
mov eax, SSHLIB_ERR_PROTOCOL |
jmp .err |
endp |
/programs/network/ssh/sshlib_host.inc |
---|
0,0 → 1,243 |
; sshlib_host.inc - SSH remote host authentication |
; |
; Copyright (C) 2021 Jeffrey Amelynck |
; |
; This program is free software: you can redistribute it and/or modify |
; it under the terms of the GNU General Public License as published by |
; the Free Software Foundation, either version 3 of the License, or |
; (at your option) any later version. |
; |
; This program is distributed in the hope that it will be useful, |
; but WITHOUT ANY WARRANTY; without even the implied warranty of |
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
; GNU General Public License for more details. |
; |
; You should have received a copy of the GNU General Public License |
; along with this program. If not, see <http://www.gnu.org/licenses/>. |
; https://datatracker.ietf.org/doc/html/rfc4253#section-6.6 |
; https://datatracker.ietf.org/doc/html/rfc3447 |
proc sshlib_host_verify con_ptr, str_host_key, str_signature, message, message_len |
locals |
known_key_sz rb MAX_PUBLIC_KEY_SIZE |
endl |
mov eax, [con_ptr] |
cmp [eax+sshlib_connection.algo_hostkey], SSHLIB_HOSTKEY_RSA |
je .rsa |
; ..add more here |
mov eax, SSHLIB_ERR_HKEY_NO_ALGO |
ret |
.rsa: |
stdcall sshlib_host_verify_rsa, [str_host_key], [str_signature], [message], [message_len] |
test eax, eax |
jnz .err |
.lookup: |
; lea eax, [known_key_sz] |
; mov ebx, [con_ptr] |
; lea ebx, [ebx + sshlib_connection.hostname_sz] |
; invoke ini_get_str, known_hosts_file, ebx, ssh_rsa_sz, eax, MAX_PUBLIC_KEY_SIZE, null_sz |
; test eax, eax |
; jnz .unknown |
; TODO: verify cached host key |
; jne .mismatch |
jmp .unknown ; FIXME |
xor eax, eax |
ret |
.mismatch: |
lea eax, [known_key_sz] |
stdcall sshlib_callback_hostkey_problem, [con_ptr], SSHLIB_HOSTKEY_PROBLEM_MISMATCH, eax |
cmp eax, SSHLIB_HOSTKEY_ACCEPT |
je .store |
ret |
.unknown: |
lea eax, [known_key_sz] |
stdcall sshlib_callback_hostkey_problem, [con_ptr], SSHLIB_HOSTKEY_PROBLEM_UNKNOWN, eax |
cmp eax, SSHLIB_HOSTKEY_ACCEPT |
je .store |
ret |
.store: |
; TODO: write to know_hosts file and fall-through |
ret |
.err: |
ret |
endp |
; https://datatracker.ietf.org/doc/html/rfc3447#section-8.2.2 |
; RSASSA-PKCS1-V1_5-VERIFY |
proc sshlib_host_verify_rsa str_host_key, str_signature, M, message_len |
locals |
h_ctx dd ? |
; Signer's RSA public key |
mpint_e dd ? ; public exponent |
mpint_n dd ? ; modulus |
mpint_m dd ? |
EM dd ? |
EM_accent dd ? |
mpint_s dd ? ; rsa_signature_blob |
; k dd ? ; length of RSA modulus n |
endl |
DEBUGF 3, "SSH: Performing RSA verification\n" |
mcall 68, 12, sizeof.crash_ctx + 5*(MAX_BITS/8+4) |
test eax, eax |
jz .err_nomem |
mov [h_ctx], eax |
add eax, sizeof.crash_ctx |
mov [mpint_e], eax |
add eax, MAX_BITS/8+4 |
mov [mpint_n], eax |
add eax, MAX_BITS/8+4 |
mov [mpint_m], eax |
add eax, MAX_BITS/8+4 |
mov [EM], eax |
add eax, MAX_BITS/8+4 |
mov [EM_accent], eax |
add eax, MAX_BITS/8+4 |
mov [mpint_s], eax |
; add eax, MAX_BITS/8+4 |
; Host key |
mov esi, [str_host_key] |
mov ecx, [esi] |
bswap ecx |
cmp ecx, MAX_PUBLIC_KEY_SIZE |
ja .err_key |
; Host key type (string) |
cmp dword[esi+4], 0x07000000 |
jne .err_key |
cmp dword[esi+8], 'ssh-' |
jne .err_key |
cmp dword[esi+11], '-rsa' |
jne .err_key |
add esi, 4+4+7 |
; mpint e |
stdcall mpint_to_little_endian, [mpint_e], esi |
add esi, eax |
add esi, 4 |
; mpint n |
stdcall mpint_to_little_endian, [mpint_n], esi |
; mov [k], eax ;; HMMMM FIXME, 0-byte.. |
; Signature |
mov esi, [str_signature] |
mov ecx, [esi] |
bswap ecx ; TODO: check length |
; Host key type (string) |
cmp dword[esi+4], 0x07000000 |
jne .err_signature |
cmp dword[esi+8], 'ssh-' |
jne .err_signature |
cmp dword[esi+11], '-rsa' |
jne .err_signature |
add esi, 4+4+7 |
; RSA signature blob |
stdcall mpint_to_little_endian, [mpint_s], esi |
; cmp eax, [k] |
;;; jne .err_signature |
; RSAVP1 |
stdcall mpint_modexp, [mpint_m], [mpint_s], [mpint_e], [mpint_n] |
; I2OSP |
stdcall mpint_shrink, [mpint_m] |
stdcall mpint_grow, [mpint_m], 256 |
stdcall mpint_to_big_endian, [EM], [mpint_m] |
; EMSA-PKCS1-v1_5 |
invoke sha1_init, [h_ctx] |
invoke sha1_update, [h_ctx], [M], [message_len] |
invoke sha1_final, [h_ctx] |
mov edi, [EM_accent] |
mov al, 0x00 |
stosb |
mov al, 0x01 |
stosb |
mov ecx, 256 - (rsa_sha1_t.len + 3 + SHA1_HASH_SIZE) |
mov al, 0xff |
rep stosb |
mov al, 0x00 |
stosb |
mov esi, rsa_sha1_t |
mov ecx, rsa_sha1_t.len |
rep movsb |
mov esi, [h_ctx] |
mov ecx, SHA1_HASH_SIZE |
rep movsb |
; Compare EM with EM_accent |
mov esi, [EM] |
add esi, 4 |
mov edi, [EM_accent] |
mov ecx, 256/4 |
xor eax, eax |
.ct_cmp_loop: |
mov ebx, [esi] |
xor ebx, [edi] |
or eax, ebx |
lea esi, [esi+4] |
lea edi, [edi+4] |
dec ecx |
jnz .ct_cmp_loop |
push eax |
mcall 68, 13, [h_ctx] |
pop eax |
test eax, eax |
jnz .fail |
DEBUGF 3, "SSH: RSA verification OK!\n" |
ret |
.fail: |
DEBUGF 3, "SSH: RSA verification failed!\n" |
mov eax, SSHLIB_ERR_HKEY_VERIFY_FAIL |
ret |
.err_nomem: |
mov eax, SSHLIB_ERR_NOMEM |
ret |
.err_signature: |
mov eax, SSHLIB_ERR_HKEY_SIGNATURE |
ret |
.err_key: |
mov eax, SSHLIB_ERR_HKEY_PUBLIC_KEY |
ret |
endp |
iglobal |
rsa_sha1_t db 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14 |
.len = $ - rsa_sha1_t |
endg |
/programs/network/ssh/sshlib_mcodes.inc |
---|
0,0 → 1,33 |
SSH_MSG_DISCONNECT = 1 |
SSH_MSG_IGNORE = 2 |
SSH_MSG_UNIMPLEMENTED = 3 |
SSH_MSG_DEBUG = 4 |
SSH_MSG_SERVICE_REQUEST = 5 |
SSH_MSG_SERVICE_ACCEPT = 6 |
SSH_MSG_KEXINIT = 20 |
SSH_MSG_NEWKEYS = 21 |
SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30 |
SSH_MSG_KEX_DH_GEX_GROUP = 31 |
SSH_MSG_KEX_DH_GEX_INIT = 32 |
SSH_MSG_KEX_DH_GEX_REPLY = 33 |
SSH_MSG_KEX_DH_GEX_REQUEST = 34 |
SSH_MSG_USERAUTH_REQUEST = 50 |
SSH_MSG_USERAUTH_FAILURE = 51 |
SSH_MSG_USERAUTH_SUCCESS = 52 |
SSH_MSG_USERAUTH_BANNER = 53 |
SSH_MSG_USERAUTH_PASSWD_CHANGEREQ = 60 |
SSH_MSG_GLOBAL_REQUEST = 80 |
SSH_MSG_REQUEST_SUCCESS = 81 |
SSH_MSG_REQUEST_FAILURE = 82 |
SSH_MSG_CHANNEL_OPEN = 90 |
SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91 |
SSH_MSG_CHANNEL_OPEN_FAILURE = 92 |
SSH_MSG_CHANNEL_WINDOW_ADJUST = 93 |
SSH_MSG_CHANNEL_DATA = 94 |
SSH_MSG_CHANNEL_EXTENDED_DATA = 95 |
SSH_MSG_CHANNEL_EOF = 96 |
SSH_MSG_CHANNEL_CLOSE = 97 |
SSH_MSG_CHANNEL_REQUEST = 98 |
SSH_MSG_CHANNEL_SUCCESS = 99 |
SSH_MSG_CHANNEL_FAILURE = 100 |
/programs/network/ssh/sshlib_transport.inc |
---|
0,0 → 1,275 |
; sshlib_transport.inc - SSH transport layer |
; |
; Copyright (C) 2016-2021 Jeffrey Amelynck |
; |
; This program is free software: you can redistribute it and/or modify |
; it under the terms of the GNU General Public License as published by |
; the Free Software Foundation, either version 3 of the License, or |
; (at your option) any later version. |
; |
; This program is distributed in the hope that it will be useful, |
; but WITHOUT ANY WARRANTY; without even the implied warranty of |
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
; GNU General Public License for more details. |
; |
; You should have received a copy of the GNU General Public License |
; along with this program. If not, see <http://www.gnu.org/licenses/>. |
proc sshlib_padd_null |
xor eax, eax |
ret |
endp |
proc sshlib_crypt_null ctx, src, dst |
; Assume src == dst ! |
ret |
endp |
proc sshlib_recv_packet con_ptr, flags |
locals |
data_length dd ? ; Total length of packet without MAC |
endl |
DEBUGF 3, "> " |
; Receive first block (Read length, padding length, message code) |
mov ebx, [con_ptr] |
mov ecx, [ebx+sshlib_connection.socketnum] |
mov esi, [ebx+sshlib_connection.rx_crypt_blocksize] |
lea edx, [ebx+sshlib_connection.rx_buffer] |
mov edi, [flags] |
mcall recv |
cmp eax, 0 |
jle .sock_fail |
sub [ssh_chan.rcv_wnd], eax ;;; FIXME |
DEBUGF 1, "chunk = %u ", eax |
mov ebx, [con_ptr] |
cmp eax, [ebx+sshlib_connection.rx_crypt_blocksize] |
jne .proto_fail ; TODO: handle receives of 1, 2, and 3 bytes correctly |
; Decrypt first block |
pusha |
lea esi, [ebx+sshlib_connection.rx_buffer] |
stdcall [ebx+sshlib_connection.rx_crypt_proc], [ebx+sshlib_connection.rx_crypt_ctx_ptr], esi, esi |
popa |
; Check data length |
mov esi, [ebx + sshlib_connection.rx_buffer.packet_length] |
bswap esi ; convert length to little endian |
mov [ebx+sshlib_connection.rx_buffer.packet_length], esi |
DEBUGF 1, "packet length=%u ", esi |
cmp esi, BUFFERSIZE |
ja .proto_fail ; packet is too large |
; Calculate amount of remaining data |
add esi, 4 ; Packet length field itself is not included in the count |
sub esi, [ebx+sshlib_connection.rx_crypt_blocksize] ; Already received this amount of data |
add esi, [ebx+sshlib_connection.rx_mac_length] |
jz .packet_complete |
; Receive remaining data |
lea edx, [ebx+sshlib_connection.rx_buffer] |
add edx, [ebx+sshlib_connection.rx_crypt_blocksize] |
mov ecx, [ebx+sshlib_connection.socketnum] |
mov edi, [flags] |
.receive_loop: |
DEBUGF 3, "want %d bytes.. ", esi |
mcall recv |
cmp eax, 0 |
jle .sock_fail |
sub [ssh_chan.rcv_wnd], eax ;;; FIXME |
DEBUGF 3, "got %d bytes\n", eax |
add edx, eax |
sub esi, eax |
jnz .receive_loop |
; Decrypt data |
mov ebx, [con_ptr] |
mov ecx, [ebx + sshlib_connection.rx_buffer.packet_length] |
add ecx, 4 ; Packet_length field itself |
sub ecx, [ebx+sshlib_connection.rx_crypt_blocksize] ; Already decrypted this amount of data |
jz .decrypt_complete |
lea esi, [ebx+sshlib_connection.rx_buffer] |
add esi, [ebx+sshlib_connection.rx_crypt_blocksize] |
.decrypt_loop: |
pusha |
stdcall [ebx+sshlib_connection.rx_crypt_proc], [ebx+sshlib_connection.rx_crypt_ctx_ptr], esi, esi |
popa |
add esi, [ebx+sshlib_connection.rx_crypt_blocksize] |
sub ecx, [ebx+sshlib_connection.rx_crypt_blocksize] |
jnz .decrypt_loop |
.decrypt_complete: |
; Authenticate message |
cmp [ebx+sshlib_connection.rx_mac_proc], 0 |
je .mac_complete |
lea esi, [ebx+sshlib_connection.rx_mac_seqnr] |
mov ecx, [ebx+sshlib_connection.rx_buffer.packet_length] |
add ecx, 8 ; packet_length field itself + sequence number |
lea eax, [ebx+sshlib_connection.rx_mac_ctx] |
; push [ebx+sshlib_connection.rx_buffer.packet_length] |
mov edx, [ebx+sshlib_connection.rx_buffer.packet_length] |
bswap edx ; convert length to big endian |
mov [ebx+sshlib_connection.rx_buffer.packet_length], edx |
stdcall [ebx+sshlib_connection.rx_mac_proc], eax, esi, ecx |
; pop [ebx+sshlib_connection.rx_buffer.packet_length] |
mov edx, [ebx+sshlib_connection.rx_buffer.packet_length] |
bswap edx ; convert length to little endian |
mov [ebx+sshlib_connection.rx_buffer.packet_length], edx |
lea esi, [ebx+sshlib_connection.rx_mac_ctx] |
lea edi, [ebx+sshlib_connection.rx_buffer+4] |
add edi, [ebx+sshlib_connection.rx_buffer.packet_length] |
mov ecx, [ebx+sshlib_connection.rx_mac_length] |
shr ecx, 2 |
repe cmpsd |
jne .mac_fail |
.mac_complete: |
add byte[ebx+sshlib_connection.rx_mac_seqnr+3], 1 ; Update sequence counter |
adc byte[ebx+sshlib_connection.rx_mac_seqnr+2], 0 |
adc byte[ebx+sshlib_connection.rx_mac_seqnr+1], 0 |
adc byte[ebx+sshlib_connection.rx_mac_seqnr+0], 0 |
; Return useful data length to the caller via eax register |
.packet_complete: |
mov eax, [ebx+sshlib_connection.rx_buffer.packet_length] |
movzx ebx, [ebx+sshlib_connection.rx_buffer.padding_length] |
sub eax, ebx |
DEBUGF 1, "useful data length=%u\n", eax |
ret |
.sock_fail: |
DEBUGF 3, "ssh_recv_packet failed!\n" |
mov eax, SSHLIB_ERR_SOCKET |
ret |
.mac_fail: |
DEBUGF 3, "ssh_recv_packet message authentication failed!\n" |
mov eax, SSHLIB_ERR_MAC_VERIFY_FAIL |
xor ebx, ebx |
ret |
.proto_fail: |
DEBUGF 3, "ssh_recv_packet protocol failure!\n" |
mov eax, SSHLIB_ERR_PROTOCOL |
xor ebx, ebx |
ret |
endp |
proc sshlib_send_packet con_ptr, buf, payload_size, flags |
locals |
packet_size dd ? |
endl |
DEBUGF 2, "< " |
; Check how many bytes we should pad |
mov eax, [payload_size] |
inc eax ; padding length byte |
lea edx, [eax+4] ; total packet size (without padding and MAC) |
mov [packet_size], edx |
mov ecx, [con_ptr] |
mov ebx, [ecx+sshlib_connection.tx_pad_size] |
dec ebx |
and edx, ebx |
neg edx |
add edx, [ecx+sshlib_connection.tx_pad_size] |
add edx, [ecx+sshlib_connection.tx_pad_size] |
DEBUGF 1, "padding %u bytes ", edx |
add [packet_size], edx ; total packet size with padding |
; Start building the packet |
; First comes the packet length, in network byte order ofcourse. |
add eax, edx |
DEBUGF 1, "total size: %u ", eax |
bswap eax |
lea edi, [ecx+sshlib_connection.tx_buffer] |
stosd |
; Then the padding length |
mov al, dl |
stosb |
; And the actual payload bytes |
mov esi, [buf] |
mov ecx, [payload_size] |
rep movsb |
; Append the packet with #edx padding bytes. |
; Since we must pad at least 8 bytes, we can always use DWORD writes. |
; First do an (unaligned) write exactly following the data |
dec edx |
mov esi, edx |
shr esi, 2 ; number dwords |
mov ebx, edx |
and ebx, 3 |
inc ebx ; number bytes in first write (1-4) |
mov edx, [con_ptr] |
call [edx+sshlib_connection.tx_pad_proc] |
mov dword[edi], eax |
add edi, ebx |
; Then, do as many aligned writes as nescessary |
mov ebx, [con_ptr] |
@@: |
call [ebx+sshlib_connection.tx_pad_proc] |
stosd |
dec esi |
jnz @r |
; Append the packet with Message Authentication Code |
mov edx, [con_ptr] |
cmp [edx+sshlib_connection.tx_mac_proc], 0 |
je .mac_complete |
DEBUGF 1, "MAC sequence number: 0x%x\n", [edx+sshlib_connection.tx_mac_seqnr] |
lea esi, [edx+sshlib_connection.tx_mac_seqnr] |
mov ecx, [packet_size] |
add ecx, 4 ; Sequence number length |
lea eax, [edx+sshlib_connection.tx_mac_ctx] |
stdcall [edx+sshlib_connection.tx_mac_proc], eax, esi, ecx |
lea esi, [edx+sshlib_connection.tx_mac_ctx] |
lea edi, [edx+sshlib_connection.tx_buffer] |
add edi, [packet_size] |
mov ecx, [edx+sshlib_connection.tx_mac_length] |
shr ecx, 2 |
rep movsd |
.mac_complete: |
add byte[edx+sshlib_connection.tx_mac_seqnr+3], 1 ; Update sequence counter |
adc byte[edx+sshlib_connection.tx_mac_seqnr+2], 0 |
adc byte[edx+sshlib_connection.tx_mac_seqnr+1], 0 |
adc byte[edx+sshlib_connection.tx_mac_seqnr+0], 0 |
; Now, encrypt everything but MAC |
lea esi, [edx+sshlib_connection.tx_buffer] |
mov ecx, [packet_size] |
.encrypt_loop: |
pusha |
stdcall [edx+sshlib_connection.tx_crypt_proc], [edx+sshlib_connection.tx_crypt_ctx_ptr], esi, esi |
popa |
add esi, [edx+sshlib_connection.tx_crypt_blocksize] |
sub ecx, [edx+sshlib_connection.tx_crypt_blocksize] |
jnz .encrypt_loop |
; Send the packet |
mov ebx, [con_ptr] |
mov ecx, [ebx+sshlib_connection.socketnum] |
lea edx, [ebx+sshlib_connection.tx_buffer] |
mov esi, [packet_size] |
add esi, [ebx+sshlib_connection.tx_mac_length] |
mov edi, [flags] |
mcall send |
DEBUGF 1, "\n" |
ret |
endp |
/programs/network/ssh/sshlib_userauth.inc |
---|
0,0 → 1,149 |
; ssh_userauth.inc - SSH user authentication |
; |
; Copyright (C) 2021 Jeffrey Amelynck |
; |
; This program is free software: you can redistribute it and/or modify |
; it under the terms of the GNU General Public License as published by |
; the Free Software Foundation, either version 3 of the License, or |
; (at your option) any later version. |
; |
; This program is distributed in the hope that it will be useful, |
; but WITHOUT ANY WARRANTY; without even the implied warranty of |
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
; GNU General Public License for more details. |
; |
; You should have received a copy of the GNU General Public License |
; along with this program. If not, see <http://www.gnu.org/licenses/>. |
proc sshlib_userauth_password con_ptr, username_sz, password_sz |
; >> Request service (user-auth) |
DEBUGF 2, "SSH: Requesting service\n" |
stdcall sshlib_send_packet, [con_ptr], ssh_msg_request_service, ssh_msg_request_service.length, 0 |
cmp eax, 0 |
jl .err |
; << Check for service acceptance |
stdcall sshlib_msg_handler, [con_ptr], 0 |
cmp eax, 0 |
jl .err |
mov eax, [con_ptr] |
cmp [eax + sshlib_connection.rx_buffer.message_code], SSH_MSG_SERVICE_ACCEPT |
jne .err_proto |
; >> Request user authentication |
DEBUGF 2, "SSH: User authentication\n" |
mcall 68, 12, 1024 ; FIXME: hardcoded size |
test eax, eax |
jz .err_nomem |
mov edi, eax |
mov ebx, eax |
mov byte[edi], SSH_MSG_USERAUTH_REQUEST |
inc edi |
; Insert username |
stdcall sz_len, [username_sz] |
mov ecx, eax |
mov esi, [username_sz] |
bswap eax |
stosd |
rep movsb |
mov dword[edi], 0x0e000000 ; 14 Bswapped |
mov dword[edi+4], "ssh-" |
mov dword[edi+8], "conn" |
mov dword[edi+12], "ecti" |
mov word[edi+16], "on" |
add edi, 18 |
mov dword[edi], 0x08000000 ; 8 Bswapped |
mov dword[edi+4], "pass" |
mov dword[edi+8], "word" |
mov byte[edi+12], 0 ; bool |
add edi, 13 |
; Insert password |
stdcall sz_len, [password_sz] |
mov ecx, eax |
mov esi, [password_sz] |
bswap eax |
stosd |
rep movsb |
sub edi, ebx |
push ebx |
stdcall sshlib_send_packet, [con_ptr], ebx, edi, 0 |
; Clear used buffer and free |
pop edx |
mov edi, edx |
push eax |
mov ecx, 1024/4 ; FIXME |
xor eax, eax |
rep stosd |
mcall 68, 13, edx |
pop eax |
cmp eax, 0 |
jl .err |
; << Check for userauth acceptance |
@@: |
stdcall sshlib_msg_handler, [con_ptr], 0 |
cmp eax, 0 |
jl .err |
mov eax, [con_ptr] |
mov al, [eax + sshlib_connection.rx_buffer.message_code] |
cmp al, SSH_MSG_USERAUTH_BANNER |
je @r ; TODO |
cmp al, SSH_MSG_USERAUTH_FAILURE |
je .fail |
cmp al, SSH_MSG_USERAUTH_SUCCESS |
jne .err_proto |
xor eax, eax |
.err: |
ret |
.fail: |
xor eax, eax |
inc eax |
ret |
.err_proto: |
mov eax, SSHLIB_ERR_PROTOCOL |
ret |
.err_nomem: |
mov eax, SSHLIB_ERR_NOMEM |
ret |
endp |
; Actually, string is \n and/or \0 terminated 0_o |
proc sz_len uses ecx edi, string |
mov edi, [string] |
mov ecx, 256 ;;;; |
mov al, 10 |
repne scasb |
dec edi |
sub edi, [string] |
mov eax, edi |
ret |
endp |