diff --git a/programs/network/ssh/ssh.asm b/programs/network/ssh/ssh.asm index ffc3664635..60c1502109 100644 --- a/programs/network/ssh/ssh.asm +++ b/programs/network/ssh/ssh.asm @@ -142,7 +142,7 @@ start: DEBUGF 2, "SSH: Init Console\n" invoke con_start, 1 - invoke con_init, 80, 25, 800, 250, title + invoke con_init, 80, 25, 80, 250, title cmp byte[params], 0 jne main.connect @@ -388,8 +388,9 @@ proc sshlib_callback_hostkey_problem, con_ptr, problem_type, hostkey_sz invoke con_write_asciiz, str23 ; jmp .ask .ask: - ;;; TODO: print hostkey - invoke con_write_asciiz, str24 + invoke con_write_asciiz, str24a + invoke con_write_asciiz, [hostkey_sz] + invoke con_write_asciiz, str24b .getansw: invoke con_getch2 or al, 0x20 ; convert to lowercase @@ -471,7 +472,8 @@ str22 db "The host key for the server was not found in the cache.", 10 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 +str24a db 10, "The remote host key is: ", 10, 0 +str24b db 10, 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 @@ -479,7 +481,7 @@ str24 db 10, "If you trust this host, press A to accept and store the (new) ke ssh_ident_ha: dd_n (ssh_msg_ident.length-2) ssh_msg_ident: - db "SSH-2.0-KolibriOS_SSH_0.05",13,10 + db "SSH-2.0-KolibriOS_SSH_0.06",13,10 .length = $ - ssh_msg_ident @@ -592,7 +594,8 @@ align 4 library network, 'network.obj', \ console, 'console.obj', \ - libcrash, 'libcrash.obj' + libcrash, 'libcrash.obj', \ + libini, 'libini.obj' import network, \ getaddrinfo, 'getaddrinfo', \ @@ -622,6 +625,10 @@ import libcrash, \ md5_update, 'md5_update', \ md5_final, 'md5_final' +import libini, \ + ini_get_str, 'ini_get_str', \ + ini_set_str, 'ini_set_str' + IncludeIGlobals i_end: diff --git a/programs/network/ssh/sshlib_host.inc b/programs/network/ssh/sshlib_host.inc index ae6a1efbb3..57c9a01196 100644 --- a/programs/network/ssh/sshlib_host.inc +++ b/programs/network/ssh/sshlib_host.inc @@ -18,13 +18,21 @@ ; https://datatracker.ietf.org/doc/html/rfc4253#section-6.6 ; https://datatracker.ietf.org/doc/html/rfc3447 +; https://datatracker.ietf.org/doc/html/rfc4716 + proc sshlib_host_verify con_ptr, str_host_key, str_signature, message, message_len locals - known_key_sz rb MAX_PUBLIC_KEY_SIZE + current_hkb64 rb MAX_PUBLIC_KEY_SIZE*4 ; Current Host key in Base64 + cached_hkb64 rb MAX_PUBLIC_KEY_SIZE*4 ; Cached Host key in Base64 + key_name_sz dd ? + hostname_sz dd ? + current_hk64_end dd ? endl mov eax, [con_ptr] + lea ebx, [eax + sshlib_connection.hostname_sz] + mov [hostname_sz], ebx cmp [eax+sshlib_connection.algo_hostkey], SSHLIB_HOSTKEY_RSA je .rsa ; ..add more here @@ -35,40 +43,65 @@ endl stdcall sshlib_host_verify_rsa, [str_host_key], [str_signature], [message], [message_len] test eax, eax jnz .err + mov [key_name_sz], ssh_rsa_sz .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 +; Convert the current host key to base64 + mov esi, [str_host_key] + mov ecx, [esi] + bswap ecx + add esi, 4 + lea edi, [current_hkb64] + call base64_encode + mov [current_hk64_end], edi -; TODO: verify cached host key -; jne .mismatch - - jmp .unknown ; FIXME +; Try to read the cached key for this host and key type + lea edi, [cached_hkb64] + invoke ini_get_str, known_hostsfile, [hostname_sz], [key_name_sz], edi, MAX_PUBLIC_KEY_SIZE*4, 0 + test eax, eax + jnz .unknown +; If the cached key is empty, return SSHLIB_HOSTKEY_PROBLEM_UNKNOWN + lea esi, [cached_hkb64] + cmp byte[esi], 0 + je .unknown +; Else, compare it to the current one + lea edi, [current_hkb64] + mov ecx, MAX_PUBLIC_KEY_SIZE*4 + .cmploop: + lodsb + scasb + jne .mismatch + test al, al + jz .match + dec ecx + jnz .cmploop + jmp .mismatch + .match: xor eax, eax ret .mismatch: - lea eax, [known_key_sz] +int3 + lea eax, [current_hkb64] 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] + lea eax, [current_hkb64] 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 - + lea esi, [current_hkb64] + mov ecx, [current_hk64_end] + sub ecx, esi + invoke ini_set_str, known_hostsfile, [hostname_sz], [key_name_sz], esi, ecx + xor eax, eax ret .err: @@ -234,10 +267,64 @@ endl endp +base64_encode: + + xor ebx, ebx + .loop: + lodsb + call .byte + dec ecx + jnz .loop + + .final: + mov al, 0 + test ebx, ebx + jz .f000 + call .byte + test ebx, ebx + jz .f001 + call .byte + mov byte[edi-2], '=' + + .f001: + mov byte[edi-1], '=' + + .f000: + mov byte[edi], 0 + ret + + .byte: + inc ebx + shl edx, 8 + mov dl, al + cmp ebx, 3 + je .b001 + ret + + .b001: + shl edx, 8 + inc ebx + + .b002: + rol edx, 6 + xor eax, eax + xchg al, dl + mov al, [base64_table+eax] + stosb + dec ebx + jnz .b002 + ret + + + + iglobal + known_hostsfile db '/sys/settings/known_hosts.ini', 0 + base64_table db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' rsa_sha1_t db 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14 .len = $ - rsa_sha1_t + ssh_rsa_sz db 'ssh-rsa', 0 endg