Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 9105 → Rev 9106

/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