diff --git a/include/git2/credential.h b/include/git2/credential.h index 33755ca916d..ece2076e440 100644 --- a/include/git2/credential.h +++ b/include/git2/credential.h @@ -78,7 +78,14 @@ typedef enum { * * @see git_credential_ssh_key_memory_new */ - GIT_CREDENTIAL_SSH_MEMORY = (1u << 6) + GIT_CREDENTIAL_SSH_MEMORY = (1u << 6), + + /** + * An SSH key-based authentication request, with a custom signature by + * a security key + * @see git_credential_ssh_custom_sk_new + */ + GIT_CREDENTIAL_SSH_CUSTOM_SK = (1u << 7) } git_credential_t; /** @@ -109,6 +116,11 @@ typedef struct git_credential_ssh_interactive git_credential_ssh_interactive; */ typedef struct git_credential_ssh_custom git_credential_ssh_custom; +/** + * A key with a custom signature function using a security key + */ +typedef struct git_credential_ssh_custom_sk git_credential_ssh_custom_sk; + /** * Credential acquisition callback. * @@ -240,6 +252,7 @@ GIT_EXTERN(int) git_credential_ssh_key_memory_new( */ #ifndef LIBSSH2_VERSION typedef struct _LIBSSH2_SESSION LIBSSH2_SESSION; +typedef struct _LIBSSH2_SK_SIG_INFO LIBSSH2_SK_SIG_INFO; typedef struct _LIBSSH2_USERAUTH_KBDINT_PROMPT LIBSSH2_USERAUTH_KBDINT_PROMPT; typedef struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE LIBSSH2_USERAUTH_KBDINT_RESPONSE; #endif @@ -337,6 +350,62 @@ GIT_EXTERN(int) git_credential_ssh_custom_new( git_credential_sign_cb sign_callback, void *payload); +/** + * Callback for credential signing with security key. + * + * @param session The libssh2 session. + * @param sig_info The signature data used to build the response. + * @param data The challenge to be signed. + * @param data_len The length of the challenge. + * @param algorithm The algorithm (e.g. LIBSSH2_HOSTKEY_TYPE_ED25519). + * @param flags The security key flags. + * @param application The relaying party, typically "ssh:". + * @param key_handle The credential id. + * @param key_handle_len The length of the credential id. + * @param abstract Additional data to pass to the callback. + * @return 0 for success, < 0 to indicate an error, > 0 to indicate + * no credential was acquired + */ +typedef int GIT_CALLBACK(git_credential_sign_sk_cb)( + LIBSSH2_SESSION *session, LIBSSH2_SK_SIG_INFO *sig_info, + const unsigned char *data, size_t data_len, + int algorithm, uint8_t flags, + const char *application, const unsigned char *key_handle, + size_t handle_len, + void **abstract); + +/** + * Create an ssh key credential with a custom signing function for a security + * key. + * + * This lets you use your own function to sign the challenge. + * + * This function and its credential type is provided for completeness + * and wraps `libssh2_userauth_publickey_sk()`, which is undocumented. + * + * The supplied credential parameter will be internally duplicated. + * + * @param out The newly created credential object. + * @param username username to use to authenticate + * @param publickey The bytes of the public key. + * @param publickey_len The length of the public key in bytes. + * @param privatekey The bytes of the private key. + * @param privatekey_len The length of the private key in bytes. + * @param sign_callback The callback method to sign the data during the challenge. + * @param payload Additional data to pass to the callback. + * @return 0 for success or an error code for failure + */ +GIT_EXTERN(int) git_credential_ssh_custom_sk_new( + git_credential **out, + const char *username, + const char *publickey, + size_t publickey_len, + const char *privatekey, + size_t privatekey_len, + const char *passphrase, + git_credential_sign_sk_cb sign_callback, + void *payload); + /** @} */ GIT_END_DECL diff --git a/include/git2/sys/credential.h b/include/git2/sys/credential.h index 0d573a3231f..4ae2f429847 100644 --- a/include/git2/sys/credential.h +++ b/include/git2/sys/credential.h @@ -85,6 +85,26 @@ struct git_credential_ssh_custom { void *payload; /**< Payload passed to prompt_callback */ }; +/** + * A security key with a custom signature function + */ +struct git_credential_ssh_custom_sk { + git_credential parent; /**< The parent credential */ + char *username; /**< The username to authenticate as */ + char *publickey; /**< The public key data */ + size_t publickey_len; /**< Length of the public key */ + char *privatekey; /**< The private key data */ + size_t privatekey_len; /**< Length of the private key */ + char *passphrase; /**< The passphrase for the security key */ + + /** + * Callback used to sign the data. + */ + git_credential_sign_sk_cb sign_callback; + + void *payload; /**< Payload passed to prompt_callback */ +}; + /** @} */ GIT_END_DECL diff --git a/src/libgit2/transports/credential.c b/src/libgit2/transports/credential.c index b47bd63a198..beed8e68e61 100644 --- a/src/libgit2/transports/credential.c +++ b/src/libgit2/transports/credential.c @@ -51,6 +51,11 @@ const char *git_credential_get_username(git_credential *cred) git_credential_ssh_custom *c = (git_credential_ssh_custom *) cred; return c->username; } + case GIT_CREDENTIAL_SSH_CUSTOM_SK: + { + git_credential_ssh_custom_sk *c = (git_credential_ssh_custom_sk *) cred; + return c->username; + } case GIT_CREDENTIAL_SSH_INTERACTIVE: { git_credential_ssh_interactive *c = (git_credential_ssh_interactive *) cred; @@ -169,6 +174,36 @@ static void ssh_custom_free(struct git_credential *cred) git__free(c); } +static void ssh_custom_sk_free(struct git_credential *cred) +{ + git_credential_ssh_custom_sk *c = (git_credential_ssh_custom_sk *)cred; + + git__free(c->username); + + if (c->publickey) { + /* Zero the memory which previously held the publickey */ + size_t key_len = strlen(c->publickey); + git__memzero(c->publickey, key_len); + git__free(c->publickey); + } + + if (c->privatekey) { + /* Zero the memory which previously held the privatekey */ + size_t key_len = strlen(c->privatekey); + git__memzero(c->privatekey, key_len); + git__free(c->privatekey); + } + + if (c->passphrase) { + /* Zero the memory which previously held the passphrase */ + size_t pass_len = strlen(c->passphrase); + git__memzero(c->passphrase, pass_len); + git__free(c->passphrase); + } + + git__free(c); +} + static void default_free(struct git_credential *cred) { git_credential_default *c = (git_credential_default *)cred; @@ -351,6 +386,58 @@ int git_credential_ssh_custom_new( return 0; } +int git_credential_ssh_custom_sk_new( + git_credential **cred, + const char *username, + const char *publickey, + size_t publickey_len, + const char *privatekey, + size_t privatekey_len, + const char *passphrase, + git_credential_sign_sk_cb sign_callback, + void *payload) +{ + git_credential_ssh_custom_sk *c; + + GIT_ASSERT_ARG(username); + GIT_ASSERT_ARG(cred); + + c = git__calloc(1, sizeof(git_credential_ssh_custom_sk)); + GIT_ERROR_CHECK_ALLOC(c); + + c->parent.credtype = GIT_CREDENTIAL_SSH_CUSTOM_SK; + c->parent.free = ssh_custom_sk_free; + + c->username = git__strdup(username); + GIT_ERROR_CHECK_ALLOC(c->username); + + if (publickey_len > 0) { + c->publickey = git__malloc(publickey_len); + GIT_ERROR_CHECK_ALLOC(c->publickey); + + memcpy(c->publickey, publickey, publickey_len); + } + + if (privatekey_len > 0) { + c->privatekey = git__malloc(privatekey_len); + GIT_ERROR_CHECK_ALLOC(c->privatekey); + + memcpy(c->privatekey, privatekey, privatekey_len); + } + + c->publickey_len = publickey_len; + c->privatekey_len = privatekey_len; + + c->passphrase = git__strdup(passphrase); + GIT_ERROR_CHECK_ALLOC(c->passphrase); + + c->sign_callback = sign_callback; + c->payload = payload; + + *cred = &c->parent; + return 0; +} + int git_credential_default_new(git_credential **cred) { git_credential_default *c; diff --git a/src/libgit2/transports/ssh_libssh2.c b/src/libgit2/transports/ssh_libssh2.c index 6469c8d6480..01d5323d645 100644 --- a/src/libgit2/transports/ssh_libssh2.c +++ b/src/libgit2/transports/ssh_libssh2.c @@ -323,6 +323,16 @@ static int _git_ssh_authenticate_session( c->publickey_len, c->sign_callback, &c->payload); break; } + case GIT_CREDENTIAL_SSH_CUSTOM_SK: { + git_credential_ssh_custom_sk *c = (git_credential_ssh_custom_sk *)cred; + + rc = libssh2_userauth_publickey_sk( + session, c->username, strlen(c->username), + (const unsigned char *)c->publickey, c->publickey_len, + c->privatekey, c->privatekey_len, + c->passphrase, c->sign_callback, &c->payload); + break; + } case GIT_CREDENTIAL_SSH_INTERACTIVE: { void **abstract = libssh2_session_abstract(session); git_credential_ssh_interactive *c = (git_credential_ssh_interactive *)cred; @@ -1033,6 +1043,7 @@ static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *use if (!git__prefixcmp(ptr, SSH_AUTH_PUBLICKEY)) { *out |= GIT_CREDENTIAL_SSH_KEY; *out |= GIT_CREDENTIAL_SSH_CUSTOM; + *out |= GIT_CREDENTIAL_SSH_CUSTOM_SK; #ifdef GIT_SSH_LIBSSH2_MEMORY_CREDENTIALS *out |= GIT_CREDENTIAL_SSH_MEMORY; #endif