kolibrios-gitea/programs/network/ssh/sshlib_host.inc

420 lines
11 KiB
PHP
Raw Normal View History

; 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
; https://datatracker.ietf.org/doc/html/rfc4716
; https://datatracker.ietf.org/doc/html/rfc8017
proc sshlib_host_verify con_ptr, str_host_key, str_signature, message, message_len
locals
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
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
mov [key_name_sz], ssh_rsa_sz
.lookup:
; 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
; 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:
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, [current_hkb64]
stdcall sshlib_callback_hostkey_problem, [con_ptr], SSHLIB_HOSTKEY_PROBLEM_UNKNOWN, eax
cmp eax, SSHLIB_HOSTKEY_ACCEPT
je .store
ret
.store:
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:
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 .not_ssh_rsa
cmp dword[esi+8], 'ssh-'
jne .not_ssh_rsa
cmp dword[esi+11], '-rsa'
je .sha1
.not_ssh_rsa:
cmp dword[esi+4], 0x0c000000
jne .not_sha2
cmp dword[esi+8], 'rsa-'
jne .not_sha2
cmp dword[esi+12], 'sha2'
jne .not_sha2
cmp dword[esi+16], '-256'
je .sha2_256
cmp dword[esi+16], '-512'
je .sha2_512
.not_sha2:
jmp .err_signature
.sha1:
DEBUGF 3, "SSH: Using RSA with SHA1 hash\n"
add esi, 4+4+7
push esi
; 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
pop esi
jmp .rsa
.sha2_256:
DEBUGF 3, "SSH: Using RSA with SHA2-256 hash\n"
add esi, 4+4+12
push esi
; EMSA-PKCS1-v1_5
invoke sha256_init, [h_ctx]
invoke sha256_update, [h_ctx], [M], [message_len]
invoke sha256_final, [h_ctx]
mov edi, [EM_accent]
mov al, 0x00
stosb
mov al, 0x01
stosb
mov ecx, 256 - (rsa_sha256_T.len + 3 + SHA256_HASH_SIZE)
mov al, 0xff
rep stosb
mov al, 0x00
stosb
mov esi, rsa_sha256_T
mov ecx, rsa_sha256_T.len
rep movsb
mov esi, [h_ctx]
mov ecx, SHA256_HASH_SIZE
rep movsb
pop esi
jmp .rsa
.sha2_512:
DEBUGF 3, "SSH: Using RSA with SHA2-512 hash\n"
add esi, 4+4+12
push esi
; EMSA-PKCS1-v1_5
invoke sha512_init, [h_ctx]
invoke sha512_update, [h_ctx], [M], [message_len]
invoke sha512_final, [h_ctx]
mov edi, [EM_accent]
mov al, 0x00
stosb
mov al, 0x01
stosb
mov ecx, 256 - (rsa_sha512_T.len + 3 + SHA512_HASH_SIZE)
mov al, 0xff
rep stosb
mov al, 0x00
stosb
mov esi, rsa_sha512_T
mov ecx, rsa_sha512_T.len
rep movsb
mov esi, [h_ctx]
mov ecx, SHA512_HASH_SIZE
rep movsb
pop esi
jmp .rsa
.rsa:
; 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]
; 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
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
rsa_sha256_T db 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
.len = $ - rsa_sha256_T
rsa_sha512_T db 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40
.len = $ - rsa_sha512_T
ssh_rsa_sz db 'ssh-rsa', 0
endg