From 3f9d505353d687db14d1486530abf9147bf08033 Mon Sep 17 00:00:00 2001 From: kkoenig Date: Wed, 12 May 2021 09:13:19 -0700 Subject: [PATCH] Support ECDSA certificate authentication (#570) Files: hostkey.c, userauth.c, test_public_key_auth_succeeds_with_correct_ecdsa_key.c Notes: Support ECDSA certificate authentication Add a test for: - Existing ecdsa basic public key authentication - ecdsa public key authentication with a signed public key Credit: kkoenig --- src/hostkey.c | 39 +++++++++++++++++++ src/userauth.c | 20 +++++++++- tests/CMakeLists.txt | 4 +- tests/Makefile.am | 2 + tests/key_ecdsa | 10 +++++ tests/key_ecdsa.pub | 1 + tests/openssh_server/Dockerfile | 9 +++++ tests/openssh_server/authorized_keys | 1 + tests/openssh_server/ca_ecdsa | 12 ++++++ tests/openssh_server/ca_ecdsa.pub | 1 + tests/signed_key_ecdsa | 10 +++++ tests/signed_key_ecdsa-cert.pub | 1 + tests/signed_key_ecdsa.pub | 1 + ...key_auth_succeeds_with_correct_ecdsa_key.c | 38 ++++++++++++++++++ ...h_succeeds_with_correct_signed_ecdsa_key.c | 38 ++++++++++++++++++ 15 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 tests/key_ecdsa create mode 100644 tests/key_ecdsa.pub create mode 100644 tests/openssh_server/ca_ecdsa create mode 100644 tests/openssh_server/ca_ecdsa.pub create mode 100644 tests/signed_key_ecdsa create mode 100644 tests/signed_key_ecdsa-cert.pub create mode 100644 tests/signed_key_ecdsa.pub create mode 100644 tests/test_public_key_auth_succeeds_with_correct_ecdsa_key.c create mode 100644 tests/test_public_key_auth_succeeds_with_correct_signed_ecdsa_key.c diff --git a/src/hostkey.c b/src/hostkey.c index 1631aae6..d87a4c74 100644 --- a/src/hostkey.c +++ b/src/hostkey.c @@ -783,6 +783,42 @@ static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp521 = { hostkey_method_ssh_ecdsa_dtor, }; +static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp256_cert = { + "ecdsa-sha2-nistp256-cert-v01@openssh.com", + SHA256_DIGEST_LENGTH, + NULL, + hostkey_method_ssh_ecdsa_initPEM, + hostkey_method_ssh_ecdsa_initPEMFromMemory, + NULL, + hostkey_method_ssh_ecdsa_signv, + NULL, /* encrypt */ + hostkey_method_ssh_ecdsa_dtor, +}; + +static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp384_cert = { + "ecdsa-sha2-nistp384-cert-v01@openssh.com", + SHA384_DIGEST_LENGTH, + NULL, + hostkey_method_ssh_ecdsa_initPEM, + hostkey_method_ssh_ecdsa_initPEMFromMemory, + NULL, + hostkey_method_ssh_ecdsa_signv, + NULL, /* encrypt */ + hostkey_method_ssh_ecdsa_dtor, +}; + +static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp521_cert = { + "ecdsa-sha2-nistp521-cert-v01@openssh.com", + SHA512_DIGEST_LENGTH, + NULL, + hostkey_method_ssh_ecdsa_initPEM, + hostkey_method_ssh_ecdsa_initPEMFromMemory, + NULL, + hostkey_method_ssh_ecdsa_signv, + NULL, /* encrypt */ + hostkey_method_ssh_ecdsa_dtor, +}; + #endif /* LIBSSH2_ECDSA */ #if LIBSSH2_ED25519 @@ -999,6 +1035,9 @@ static const LIBSSH2_HOSTKEY_METHOD *hostkey_methods[] = { &hostkey_method_ecdsa_ssh_nistp256, &hostkey_method_ecdsa_ssh_nistp384, &hostkey_method_ecdsa_ssh_nistp521, + &hostkey_method_ecdsa_ssh_nistp256_cert, + &hostkey_method_ecdsa_ssh_nistp384_cert, + &hostkey_method_ecdsa_ssh_nistp521_cert, #endif #if LIBSSH2_ED25519 &hostkey_method_ssh_ed25519, diff --git a/src/userauth.c b/src/userauth.c index 5f669594..40ef9153 100644 --- a/src/userauth.c +++ b/src/userauth.c @@ -1070,7 +1070,21 @@ libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, return rc; } - +static int plain_method_len(const char *method, size_t method_len) +{ + if(!strncmp("ecdsa-sha2-nistp256-cert-v01@openssh.com", + method, + method_len) || + !strncmp("ecdsa-sha2-nistp384-cert-v01@openssh.com", + method, + method_len) || + !strncmp("ecdsa-sha2-nistp521-cert-v01@openssh.com", + method, + method_len)) { + return 19; + } + return method_len; +} int _libssh2_userauth_publickey(LIBSSH2_SESSION *session, @@ -1335,6 +1349,10 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session, s = session->userauth_pblc_packet + session->userauth_pblc_packet_len; session->userauth_pblc_b = NULL; + session->userauth_pblc_method_len = + plain_method_len((const char *)session->userauth_pblc_method, + session->userauth_pblc_method_len); + _libssh2_store_u32(&s, 4 + session->userauth_pblc_method_len + 4 + sig_len); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 99a8b8b5..cf4b3f76 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -130,7 +130,9 @@ if(CRYPTO_BACKEND STREQUAL "OpenSSL") public_key_auth_succeeds_with_correct_ed25519_key public_key_auth_succeeds_with_correct_encrypted_ed25519_key public_key_auth_succeeds_with_correct_ed25519_key_from_mem - ) + public_key_auth_succeeds_with_correct_ecdsa_key + public_key_auth_succeeds_with_correct_signed_ecdsa_key + ) endif() endif() diff --git a/tests/Makefile.am b/tests/Makefile.am index d210ad63..bf79c145 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -56,6 +56,8 @@ EXTRA_DIST = \ test_public_key_auth_succeeds_with_correct_dsa_key.c \ test_public_key_auth_succeeds_with_correct_ed25519_key.c \ test_public_key_auth_succeeds_with_correct_ed25519_key_from_mem.c \ + test_public_key_auth_succeeds_with_correct_ecdsa_key.c \ + test_public_key_auth_succeeds_with_correct_signed_ecdsa_key.c \ test_public_key_auth_succeeds_with_correct_encrypted_ed25519_key.c \ test_public_key_auth_succeeds_with_correct_encrypted_rsa_key.c \ test_public_key_auth_succeeds_with_correct_rsa_key.c \ diff --git a/tests/key_ecdsa b/tests/key_ecdsa new file mode 100644 index 00000000..6ed60773 --- /dev/null +++ b/tests/key_ecdsa @@ -0,0 +1,10 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS +1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQTosiScH/oRSazpIpPSEFcY4YVZyNby +peARi49N3qy78OE118KGc5T8eifd+n1PSb7z8PnfDwOL4jBHxW5nWx0RCocIt7tb2a349J +gfEl8PegHGcF/DwC+eesIKJvv0MfkAAADIKLgw6yi4MOsAAAATZWNkc2Etc2hhMi1uaXN0 +cDM4NAAAAAhuaXN0cDM4NAAAAGEE6LIknB/6EUms6SKT0hBXGOGFWcjW8qXgEYuPTd6su/ +DhNdfChnOU/Hon3fp9T0m+8/D53w8Di+IwR8VuZ1sdEQqHCLe7W9mt+PSYHxJfD3oBxnBf +w8AvnnrCCib79DH5AAAAMGYdHu+u2/L8zC/0S9bao9y6vKiLSuTEfZpCIsyE5jWj/vrS0n +r1lzv9kKj+5A86aQAAAAA= +-----END OPENSSH PRIVATE KEY----- diff --git a/tests/key_ecdsa.pub b/tests/key_ecdsa.pub new file mode 100644 index 00000000..597f63fc --- /dev/null +++ b/tests/key_ecdsa.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBOiyJJwf+hFJrOkik9IQVxjhhVnI1vKl4BGLj03erLvw4TXXwoZzlPx6J936fU9JvvPw+d8PA4viMEfFbmdbHREKhwi3u1vZrfj0mB8SXw96AcZwX8PAL556wgom+/Qx+Q== diff --git a/tests/openssh_server/Dockerfile b/tests/openssh_server/Dockerfile index 72e24bfe..c5ce2224 100644 --- a/tests/openssh_server/Dockerfile +++ b/tests/openssh_server/Dockerfile @@ -58,10 +58,19 @@ COPY ssh_host_ed25519_key /tmp/etc/ssh/ssh_host_ed25519_key RUN mv /tmp/etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key RUN chmod 600 /etc/ssh/ssh_host_ed25519_key +COPY ca_ecdsa.pub /tmp/etc/ssh/ca_ecdsa.pub +RUN mv /tmp/etc/ssh/ca_ecdsa.pub /etc/ssh/ca_ecdsa.pub +RUN chmod 600 /etc/ssh/ca_ecdsa.pub + +COPY ca_ecdsa /tmp/etc/ssh/ca_ecdsa +RUN mv /tmp/etc/ssh/ca_ecdsa /etc/ssh/ca_ecdsa +RUN chmod 600 /etc/ssh/ca_ecdsa + RUN adduser --disabled-password --gecos 'Test user for libssh2 integration tests' libssh2 RUN echo 'libssh2:my test password' | chpasswd RUN sed -i 's/ChallengeResponseAuthentication no/ChallengeResponseAuthentication yes/' /etc/ssh/sshd_config +RUN echo "TrustedUserCAKeys /etc/ssh/ca_ecdsa.pub" >> /etc/ssh/sshd_config # SSH login fix. Otherwise user is kicked off after login RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd diff --git a/tests/openssh_server/authorized_keys b/tests/openssh_server/authorized_keys index 870f9a2f..cdd6eef5 100644 --- a/tests/openssh_server/authorized_keys +++ b/tests/openssh_server/authorized_keys @@ -4,3 +4,4 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC92YlGoc4PJy6DzX916JJZhxkvmkWBLGJdWOL7R9B6 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDTe1lN2L/yet0Ma1JzXkQf3t1f+pauALec2FsGZy87KRJW1AOxcTTiePjlFwP1yfSK1lWXQ+uf0b61gkKqqR52FDky24HJWuYlfXlEQMn2d/PNDNVDDbO4TXKyNxxUHFJ6qYMNd4kWjOH+6rmYoWKsWV+3mDRbHagbVPEYL8wep8OTqKOqruVLVPzZyYZkBtn4XOFi6UE8WKiSVdK1Am1O5UxvlD95t32eYch6wQ9azgMqja6spe/L5UJgP83QZFknVC3wPZWkjqomVFql0FpaQclENwyY/OZMxr0cT/f7bCL6s4A/1XpbsGmC0xak4/THHbOn+0LdIej2nGV8JFoR ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIxtdyg2ZRXE70UwyPVUH3UyfDBV8GX5cPF636P6hjom ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICHxEyUTOVHXvdMFARedFQ+H9DW/n8Zy3daKKRqnTDMq +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBOiyJJwf+hFJrOkik9IQVxjhhVnI1vKl4BGLj03erLvw4TXXwoZzlPx6J936fU9JvvPw+d8PA4viMEfFbmdbHREKhwi3u1vZrfj0mB8SXw96AcZwX8PAL556wgom+/Qx+Q== diff --git a/tests/openssh_server/ca_ecdsa b/tests/openssh_server/ca_ecdsa new file mode 100644 index 00000000..d6b670c5 --- /dev/null +++ b/tests/openssh_server/ca_ecdsa @@ -0,0 +1,12 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS +1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQAfv15s+G2xg56J+audKAM4G9qOTFr +bZRo0CTwvkb/oHrf9/2RSWqYsx/0m5mYCZVlecnZqwRHAOolXbc/Yb4cGjsALUj3UDirsn +YR7Ve+SwnunkpvW/H3a98sA3sS+HCpd5RbpfWClSBOI9JEAlPtS1CrEQ7EmO7hmlFOH2cL +0qfHCyYAAAEA763VSe+t1UkAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ +AAAIUEAH79ebPhtsYOeifmrnSgDOBvajkxa22UaNAk8L5G/6B63/f9kUlqmLMf9JuZmAmV +ZXnJ2asERwDqJV23P2G+HBo7AC1I91A4q7J2Ee1XvksJ7p5Kb1vx92vfLAN7EvhwqXeUW6 +X1gpUgTiPSRAJT7UtQqxEOxJju4ZpRTh9nC9KnxwsmAAAAQgD8VJwi9RHYN13CAfhvdmjW +xVjH55J5jDjPlENU2Z+cnm01SQ+9mPFEY4wDSvfiovD1VstNJX/P97WbHw+e5XL+HwAAAA +JDQQ== +-----END OPENSSH PRIVATE KEY----- diff --git a/tests/openssh_server/ca_ecdsa.pub b/tests/openssh_server/ca_ecdsa.pub new file mode 100644 index 00000000..5086eabe --- /dev/null +++ b/tests/openssh_server/ca_ecdsa.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAB+/Xmz4bbGDnon5q50oAzgb2o5MWttlGjQJPC+Rv+get/3/ZFJapizH/SbmZgJlWV5ydmrBEcA6iVdtz9hvhwaOwAtSPdQOKuydhHtV75LCe6eSm9b8fdr3ywDexL4cKl3lFul9YKVIE4j0kQCU+1LUKsRDsSY7uGaUU4fZwvSp8cLJg== CA diff --git a/tests/signed_key_ecdsa b/tests/signed_key_ecdsa new file mode 100644 index 00000000..1e08cfd7 --- /dev/null +++ b/tests/signed_key_ecdsa @@ -0,0 +1,10 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS +1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQRv1/fnN1SuIkg222jYnySqIJ88J5M3 +bB6JGeloKhIvnZdPOSVFuFBjuF7NPrGrsm87QstQFbDiLhIlcDNHIq0VhS5itHNMjtC6Ym +RRx7mlQSXPbRRF5MclxvDJCMyAIagAAADQekojnXpKI50AAAATZWNkc2Etc2hhMi1uaXN0 +cDM4NAAAAAhuaXN0cDM4NAAAAGEEb9f35zdUriJINtto2J8kqiCfPCeTN2weiRnpaCoSL5 +2XTzklRbhQY7hezT6xq7JvO0LLUBWw4i4SJXAzRyKtFYUuYrRzTI7QumJkUce5pUElz20U +ReTHJcbwyQjMgCGoAAAAMQDmng9vaqXjHAhRssuBHeQylRKRwzOYOaToF8f+O0NmpmfLnc +/c2wOcXf2f9GBAQdYAAAAAAQIDBAUGBw== +-----END OPENSSH PRIVATE KEY----- diff --git a/tests/signed_key_ecdsa-cert.pub b/tests/signed_key_ecdsa-cert.pub new file mode 100644 index 00000000..ee32719b --- /dev/null +++ b/tests/signed_key_ecdsa-cert.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzdHAzODQtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgQCXc1JaqZ4XzkudbzP/pgEUbkioo7pl9aB09h6sg7KYAAAAIbmlzdHAzODQAAABhBG/X9+c3VK4iSDbbaNifJKognzwnkzdsHokZ6WgqEi+dl085JUW4UGO4Xs0+sauybztCy1AVsOIuEiVwM0cirRWFLmK0c0yO0LpiZFHHuaVBJc9tFEXkxyXG8MkIzIAhqAAAAAAAAAABAAAAAQAAAAhpZGVudGl0eQAAAAsAAAAHbGlic3NoMgAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAArAAAABNlY2RzYS1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQAfv15s+G2xg56J+audKAM4G9qOTFrbZRo0CTwvkb/oHrf9/2RSWqYsx/0m5mYCZVlecnZqwRHAOolXbc/Yb4cGjsALUj3UDirsnYR7Ve+SwnunkpvW/H3a98sA3sS+HCpd5RbpfWClSBOI9JEAlPtS1CrEQ7EmO7hmlFOH2cL0qfHCyYAAACnAAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAACMAAAAQgGxnhh8z8LNk5pMw0D9InyEtVRm8OJi23XEhqj/ieT/BsLZXbu65UAcMjrUn6DPdERxF9Dwe9pdIAWOLLjLHYEFBQAAAEIAlxz+XjUKa9Q2vpH0y8IgJMm0H1hTBUM1DQEoL8No1BVtgtIO20mac2fE3I35JxNDmmXoW+FuzmJnyrY9rxY+YXM= ./signed_key_ecdsa.pub diff --git a/tests/signed_key_ecdsa.pub b/tests/signed_key_ecdsa.pub new file mode 100644 index 00000000..3b208f8a --- /dev/null +++ b/tests/signed_key_ecdsa.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBG/X9+c3VK4iSDbbaNifJKognzwnkzdsHokZ6WgqEi+dl085JUW4UGO4Xs0+sauybztCy1AVsOIuEiVwM0cirRWFLmK0c0yO0LpiZFHHuaVBJc9tFEXkxyXG8MkIzIAhqA== diff --git a/tests/test_public_key_auth_succeeds_with_correct_ecdsa_key.c b/tests/test_public_key_auth_succeeds_with_correct_ecdsa_key.c new file mode 100644 index 00000000..2ea3a369 --- /dev/null +++ b/tests/test_public_key_auth_succeeds_with_correct_ecdsa_key.c @@ -0,0 +1,38 @@ +#include "session_fixture.h" + +#include + +#include + +/* configured in Dockerfile */ +static const char *USERNAME = "libssh2"; +static const char *KEY_FILE_PRIVATE = "key_ecdsa"; +static const char *KEY_FILE_PUBLIC = "key_ecdsa.pub"; + +int test(LIBSSH2_SESSION *session) +{ + int rc; + const char *userauth_list = NULL; + + userauth_list = libssh2_userauth_list(session, USERNAME, strlen(USERNAME)); + if(userauth_list == NULL) { + print_last_session_error("libssh2_userauth_list"); + return 1; + } + + if(strstr(userauth_list, "publickey") == NULL) { + fprintf(stderr, "'publickey' was expected in userauth list: %s\n", + userauth_list); + return 1; + } + + rc = libssh2_userauth_publickey_fromfile_ex( + session, USERNAME, strlen(USERNAME), KEY_FILE_PUBLIC, KEY_FILE_PRIVATE, + NULL); + if(rc != 0) { + print_last_session_error("libssh2_userauth_publickey_fromfile_ex"); + return 1; + } + + return 0; +} diff --git a/tests/test_public_key_auth_succeeds_with_correct_signed_ecdsa_key.c b/tests/test_public_key_auth_succeeds_with_correct_signed_ecdsa_key.c new file mode 100644 index 00000000..10b33cbb --- /dev/null +++ b/tests/test_public_key_auth_succeeds_with_correct_signed_ecdsa_key.c @@ -0,0 +1,38 @@ +#include "session_fixture.h" + +#include + +#include + +/* configured in Dockerfile */ +static const char *USERNAME = "libssh2"; +static const char *KEY_FILE_PRIVATE = "signed_key_ecdsa"; +static const char *KEY_FILE_PUBLIC = "signed_key_ecdsa-cert.pub"; + +int test(LIBSSH2_SESSION *session) +{ + int rc; + const char *userauth_list = NULL; + + userauth_list = libssh2_userauth_list(session, USERNAME, strlen(USERNAME)); + if(userauth_list == NULL) { + print_last_session_error("libssh2_userauth_list"); + return 1; + } + + if(strstr(userauth_list, "publickey") == NULL) { + fprintf(stderr, "'publickey' was expected in userauth list: %s\n", + userauth_list); + return 1; + } + + rc = libssh2_userauth_publickey_fromfile_ex( + session, USERNAME, strlen(USERNAME), KEY_FILE_PUBLIC, KEY_FILE_PRIVATE, + NULL); + if(rc != 0) { + print_last_session_error("libssh2_userauth_publickey_fromfile_ex"); + return 1; + } + + return 0; +}