diff --git a/docs/Makefile.am b/docs/Makefile.am index bb2e4434..6eb33537 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -10,6 +10,7 @@ dist_man_MANS = \ libssh2_agent_init.3 \ libssh2_agent_list_identities.3 \ libssh2_agent_set_identity_path.3 \ + libssh2_agent_sign.3 \ libssh2_agent_userauth.3 \ libssh2_banner_set.3 \ libssh2_base64_decode.3 \ diff --git a/docs/libssh2_agent_sign.3 b/docs/libssh2_agent_sign.3 new file mode 100644 index 00000000..670e5940 --- /dev/null +++ b/docs/libssh2_agent_sign.3 @@ -0,0 +1,52 @@ +.TH libssh2_agent_sign 3 "1 Oct 2022" "libssh2 1.11.0" "libssh2 manual" +.SH NAME +libssh2_agent_sign - sign data, with the help of ssh-agent +.SH SYNOPSIS +.nf +#include + +int +libssh2_agent_sign(LIBSSH2_AGENT *agent, + struct libssh2_agent_publickey *identity, + unsigned char **sig, + size_t *s_len, + const unsigned char *data, + size_t d_len, + const char *method, + unsigned int method_len); +.fi +.SH DESCRIPTION +\fIagent\fP - ssh-agent handle as returned by +.BR libssh2_agent_init(3) + +\fIidentity\fP - Public key to authenticate with, as returned by +.BR libssh2_agent_get_identity(3) + +\fIsig\fP - A pointer to a buffer in which to place the signature. The caller +is responsible for freeing the signature with LIBSSH2_FREE. + +\fIs_len\fP - A pointer to the length of the sig parameter. + +\fIdata\fP - The data to sign. + +\fId_len\fP - The length of the data parameter. + +\fImethod\fP - A buffer indicating the signing method. This should match the +string at the start of identity->blob. + +\fImethod_len\fP - The length of the method parameter. + +Sign data using an ssh-agent. This function can be used in a callback +registered with libssh2_session_callback_set(3) using +LIBSSH2_CALLBACK_AUTHAGENT_SIGN to sign an authentication challenge from a +server. However, the client is responsible for implementing the code that calls +this callback in response to a SSH2_AGENTC_SIGN_REQUEST message. +.SH RETURN VALUE +Returns 0 if succeeded, or a negative value for error. +.SH AVAILABILITY +Added in libssh2 1.11.0 +.SH SEE ALSO +.BR libssh2_agent_init(3) +.BR libssh2_agent_get_identity(3) +.BR libssh2_agent_userauth(3) +.BR libssh2_session_callback_set(3) diff --git a/docs/libssh2_agent_userauth.3 b/docs/libssh2_agent_userauth.3 index 8513e651..0eeea004 100644 --- a/docs/libssh2_agent_userauth.3 +++ b/docs/libssh2_agent_userauth.3 @@ -30,3 +30,4 @@ Added in libssh2 1.2 .SH SEE ALSO .BR libssh2_agent_init(3) .BR libssh2_agent_get_identity(3) +.BR libssh2_agent_sign(3) diff --git a/docs/libssh2_session_callback_set.3 b/docs/libssh2_session_callback_set.3 index 9a94c00f..861b320e 100644 --- a/docs/libssh2_session_callback_set.3 +++ b/docs/libssh2_session_callback_set.3 @@ -71,8 +71,67 @@ to the abstract pointer set in the \fIlibssh2_session_init_ex(3)\fP call. The callback returns the number of bytes read, or -1 for error. The special return code \fB-EAGAIN\fP can be returned to signal that the read was aborted to prevent getting blocked and it needs to be called again. +.IP LIBSSH2_CALLBACK_AUTHAGENT +Called during authentication process to allow the client to connect to the +ssh-agent and perform any setup, such as configuring the agent or adding keys. + +The prototype of the callback: + +.nf +void authagent(LIBSSH2_SESSION* session, LIBSSH2_CHANNEL *channel, + void **abstract); +.fi +.IP LIBSSH2_CALLBACK_AUTHAGENT_IDENTITIES +Not called by libssh2. The client is responsible for calling this method when +a SSH2_AGENTC_REQUEST_IDENTITIES message has been received. + +The prototype of the callback: + +.nf +void identities(LIBSSH2_SESSION* session, void *buffer, + const char *agent_path, + void **abstract) +.fi + +\fBbuffer\fP must be filled in by the callback. Different clients may implement +this differently. For example, one client may pass in an unsigned char ** for +this parameter, while another may pass in a pointer to a struct. + +Regardless of the type of buffer used, the client will need to send back a list +of identities in the following format. + +uint32 buffer length +uint32 number of entries +entries + +Where each entry in the entries list is of the format: + +string data +cstring comment + +\fBagent_path\fP The path to a running ssh-agent on the client machine, from +which identities can be listed. +.IP LIBSSH2_CALLBACK_AUTHAGENT_SIGN +Not called by libssh2. The client is responsible for calling this method when +a SSH2_AGENTC_SIGN_REQUEST message has been received. + +The prototype of the callback: + +.nf +void sign(LIBSSH2_SESSION* session, + unsigned char *blob, unsigned int blen, + const unsigned char *data, unsigned int dlen, + unsigned char **sig, unsigned int *sig_len, + const char *agent_path, + void **abstract); +.fi + +When interfacing with an ssh-agent installed on the client system, this method +can call libssh2_agent_sign(3) to perform signing. + .SH RETURN VALUE Pointer to previous callback handler. Returns NULL if no prior callback handler was set or the callback type was unknown. .SH SEE ALSO .BR libssh2_session_init_ex(3) +.BR libssh2_agent_sign(3) diff --git a/docs/libssh2_userauth_publickey_sk.3 b/docs/libssh2_userauth_publickey_sk.3 index 663acd5e..38df52f2 100644 --- a/docs/libssh2_userauth_publickey_sk.3 +++ b/docs/libssh2_userauth_publickey_sk.3 @@ -9,6 +9,8 @@ int libssh2_userauth_publickey_sk(LIBSSH2_SESSION *session, const char *username, size_t username_len, + const unsigned char *publickeydata, + size_t publickeydata_len, const char *privatekeydata, size_t privatekeydata_len, const char *passphrase, @@ -43,6 +45,13 @@ int name(LIBSSH2_SESSION *session, LIBSSH2_SK_SIG_INFO *sig_info, \fIusername_len\fP - Length of username parameter. +\fIpublickeydata\fP - Buffer containing the contents of a public key file. If +NULL, the public key will be extracted from the privatekeydata. When using +certificate authentication, this buffer should contain the public certificate +data. + +\fIpublickeydata_len\fP - Length of public key data. + \fIprivatekeydata\fP - Buffer containing the contents of a private key file. \fIprivatekeydata_len\fP - Length of private key data. diff --git a/include/libssh2.h b/include/libssh2.h index 68bab7c8..28698317 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -333,6 +333,22 @@ typedef struct _LIBSSH2_SK_SIG_INFO { void name(LIBSSH2_SESSION *session, LIBSSH2_CHANNEL *channel, \ const char *shost, int sport, void **abstract) +#define LIBSSH2_AUTHAGENT_FUNC(name) \ + void name(LIBSSH2_SESSION *session, LIBSSH2_CHANNEL *channel, \ + void **abstract) + +#define LIBSSH2_ADD_IDENTITIES_FUNC(name) \ + void name(LIBSSH2_SESSION *session, void *buffer, \ + const char *agent_path, void **abstract) + +#define LIBSSH2_AUTHAGENT_SIGN_FUNC(name) \ + int name(LIBSSH2_SESSION* session, \ + unsigned char *blob, unsigned int blen, \ + const unsigned char *data, unsigned int dlen, \ + unsigned char **signature, unsigned int *sigLen, \ + const char *agentPath, \ + void **abstract) + #define LIBSSH2_CHANNEL_CLOSE_FUNC(name) \ void name(LIBSSH2_SESSION *session, void **session_abstract, \ LIBSSH2_CHANNEL *channel, void **channel_abstract) @@ -348,13 +364,16 @@ typedef struct _LIBSSH2_SK_SIG_INFO { int flags, void **abstract) /* libssh2_session_callback_set() constants */ -#define LIBSSH2_CALLBACK_IGNORE 0 -#define LIBSSH2_CALLBACK_DEBUG 1 -#define LIBSSH2_CALLBACK_DISCONNECT 2 -#define LIBSSH2_CALLBACK_MACERROR 3 -#define LIBSSH2_CALLBACK_X11 4 -#define LIBSSH2_CALLBACK_SEND 5 -#define LIBSSH2_CALLBACK_RECV 6 +#define LIBSSH2_CALLBACK_IGNORE 0 +#define LIBSSH2_CALLBACK_DEBUG 1 +#define LIBSSH2_CALLBACK_DISCONNECT 2 +#define LIBSSH2_CALLBACK_MACERROR 3 +#define LIBSSH2_CALLBACK_X11 4 +#define LIBSSH2_CALLBACK_SEND 5 +#define LIBSSH2_CALLBACK_RECV 6 +#define LIBSSH2_CALLBACK_AUTHAGENT 7 +#define LIBSSH2_CALLBACK_AUTHAGENT_IDENTITIES 8 +#define LIBSSH2_CALLBACK_AUTHAGENT_SIGN 9 /* libssh2_session_method_pref() constants */ #define LIBSSH2_METHOD_KEX 0 @@ -747,6 +766,8 @@ LIBSSH2_API int libssh2_userauth_publickey_sk(LIBSSH2_SESSION *session, const char *username, size_t username_len, + const unsigned char *pubkeydata, + size_t pubkeydata_len, const char *privatekeydata, size_t privatekeydata_len, const char *passphrase, @@ -1331,6 +1352,23 @@ libssh2_agent_userauth(LIBSSH2_AGENT *agent, const char *username, struct libssh2_agent_publickey *identity); +/* + * libssh2_agent_sign() + * + * Sign a payload using a system-installed ssh-agent. + * + * Returns 0 if succeeded, or a negative value for error. + */ +LIBSSH2_API int +libssh2_agent_sign(LIBSSH2_AGENT *agent, + struct libssh2_agent_publickey *identity, + unsigned char **sig, + size_t *s_len, + const unsigned char *data, + size_t d_len, + const char *method, + unsigned int method_len); + /* * libssh2_agent_disconnect() * diff --git a/src/agent.c b/src/agent.c index de22112f..9b38d910 100644 --- a/src/agent.c +++ b/src/agent.c @@ -383,6 +383,7 @@ agent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, int rc; unsigned char *method_name = NULL; uint32_t sign_flags = 0; + ssize_t plain_len; /* Create a request to sign the data */ if(transctx->state == agent_NB_state_init) { @@ -478,9 +479,13 @@ agent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, memcpy(method_name, s, method_len); s += method_len; + plain_len = plain_method((char *)session->userauth_pblc_method, + session->userauth_pblc_method_len); + /* check to see if we match requested */ - if((size_t)method_len != session->userauth_pblc_method_len || - memcmp(method_name, session->userauth_pblc_method, method_len)) { + if(((size_t)method_len != session->userauth_pblc_method_len && + method_len != plain_len) || + memcmp(method_name, session->userauth_pblc_method, method_len)) { _libssh2_debug((session, LIBSSH2_TRACE_KEX, "Agent sign method %.*s", @@ -829,6 +834,57 @@ libssh2_agent_userauth(LIBSSH2_AGENT *agent, return rc; } +/* + * libssh2_agent_sign + * + * Sign a payload using a system-installed ssh-agent. + * + * Returns 0 if succeeded, or a negative value for error. + */ +LIBSSH2_API int +libssh2_agent_sign(LIBSSH2_AGENT *agent, + struct libssh2_agent_publickey *identity, + unsigned char **sig, + size_t *s_len, + const unsigned char *data, + size_t d_len, + const char *method, + unsigned int method_len) +{ + void *abstract = agent; + int rc; + uint32_t methodLen; + + if(agent->session->userauth_pblc_state == libssh2_NB_state_idle) { + memset(&agent->transctx, 0, sizeof(agent->transctx)); + agent->identity = identity->node; + } + + if(identity->blob_len < sizeof(uint32_t)) { + return LIBSSH2_ERROR_BUFFER_TOO_SMALL; + } + + methodLen = _libssh2_ntohu32(identity->blob); + + if(identity->blob_len < sizeof(uint32_t) + methodLen) { + return LIBSSH2_ERROR_BUFFER_TOO_SMALL; + } + + agent->session->userauth_pblc_method_len = method_len; + agent->session->userauth_pblc_method = LIBSSH2_ALLOC(agent->session, + method_len); + + memcpy(agent->session->userauth_pblc_method, method, methodLen); + + rc = agent_sign(agent->session, sig, s_len, data, d_len, &abstract); + + LIBSSH2_FREE(agent->session, agent->session->userauth_pblc_method); + agent->session->userauth_pblc_method = NULL; + agent->session->userauth_pblc_method_len = 0; + + return rc; +} + /* * libssh2_agent_disconnect * diff --git a/src/hostkey.c b/src/hostkey.c index 1586ecf8..f3823676 100644 --- a/src/hostkey.c +++ b/src/hostkey.c @@ -1257,6 +1257,18 @@ static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_ed25519 = { hostkey_method_ssh_ed25519_dtor, }; +static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_ed25519_cert = { + "ssh-ed25519-cert-v01@openssh.com", + SHA256_DIGEST_LENGTH, + hostkey_method_ssh_ed25519_init, + hostkey_method_ssh_ed25519_initPEM, + hostkey_method_ssh_ed25519_initPEMFromMemory, + hostkey_method_ssh_ed25519_sig_verify, + hostkey_method_ssh_ed25519_signv, + NULL, /* encrypt */ + hostkey_method_ssh_ed25519_dtor, +}; + #endif /* LIBSSH2_ED25519 */ @@ -1271,6 +1283,7 @@ static const LIBSSH2_HOSTKEY_METHOD *hostkey_methods[] = { #endif #if LIBSSH2_ED25519 &hostkey_method_ssh_ed25519, + &hostkey_method_ssh_ed25519_cert, #endif #if LIBSSH2_RSA #if LIBSSH2_RSA_SHA2 diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index fe7a6af5..82a5778b 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -199,6 +199,21 @@ struct iovec { channel->session->x11(((channel)->session), (channel), \ (shost), (sport), (&(channel)->session->abstract)) +#define LIBSSH2_AUTHAGENT(channel) \ + channel->session->authagent(((channel)->session), (channel), \ + (&(channel)->session->abstract)) + +#define LIBSSH2_ADD_IDENTITIES(session, buffer, agentPath) \ + session->addLocalIdentities((session), (buffer), \ + (agentPath), (&(session->abstract))) + +#define LIBSSH2_AUTHAGENT_SIGN(session, blob, blen, \ + data, dlen, sig, sigLen, \ + agentPath) \ + session->agentSignCallback((session), (blob), (blen), \ + (data), (dlen), (sig), (sigLen), \ + (agentPath), (&(session->abstract))) + #define LIBSSH2_CHANNEL_CLOSE(session, channel) \ channel->close_cb((session), &(session)->abstract, \ (channel), &(channel)->abstract) @@ -238,7 +253,8 @@ typedef enum libssh2_NB_state_jump3, libssh2_NB_state_jump4, libssh2_NB_state_jump5, - libssh2_NB_state_end + libssh2_NB_state_end, + libssh2_NB_state_jumpauthagent } libssh2_nonblocking_states; typedef struct packet_require_state_t @@ -344,6 +360,24 @@ typedef struct packet_x11_open_state_t LIBSSH2_CHANNEL *channel; } packet_x11_open_state_t; +#define AuthAgentUnavail "Auth Agent unavailable" + +typedef struct packet_authagent_state_t +{ + libssh2_nonblocking_states state; + unsigned char packet[17 + (sizeof(AuthAgentUnavail) - 1)]; + uint32_t sender_channel; + uint32_t initial_window_size; + uint32_t packet_size; + LIBSSH2_CHANNEL *channel; +} packet_authagent_state_t; + +typedef enum +{ + libssh2_requires_size_decryption = (1 << 0), + libssh2_requires_size_field_in_packet = (1 << 1) +} libssh2_crypt_flags; + struct _LIBSSH2_PACKET { struct list_node node; /* linked list header */ @@ -518,7 +552,7 @@ typedef struct _libssh2_endpoint_data char *lang_prefs; } libssh2_endpoint_data; -#define PACKETBUFSIZE (1024*16) +#define PACKETBUFSIZE MAX_SSH_PACKET_LEN struct transportpacket { @@ -606,6 +640,9 @@ struct _LIBSSH2_SESSION LIBSSH2_DISCONNECT_FUNC((*ssh_msg_disconnect)); LIBSSH2_MACERROR_FUNC((*macerror)); LIBSSH2_X11_OPEN_FUNC((*x11)); + LIBSSH2_AUTHAGENT_FUNC((*authagent)); + LIBSSH2_ADD_IDENTITIES_FUNC((*addLocalIdentities)); + LIBSSH2_AUTHAGENT_SIGN_FUNC((*agentSignCallback)); LIBSSH2_SEND_FUNC((*send)); LIBSSH2_RECV_FUNC((*recv)); @@ -820,6 +857,7 @@ struct _LIBSSH2_SESSION states */ packet_queue_listener_state_t packAdd_Qlstn_state; packet_x11_open_state_t packAdd_x11open_state; + packet_authagent_state_t packAdd_authagent_state; /* State variables used in fullpacket() */ libssh2_nonblocking_states fullpacket_state; @@ -1151,6 +1189,8 @@ int _libssh2_pem_decode_integer(unsigned char **data, size_t *datalen, /* global.c */ void _libssh2_init_if_needed(void); +/* Utility function for certificate auth */ +size_t plain_method(char *method, size_t method_len); #define ARRAY_SIZE(a) (sizeof ((a)) / sizeof ((a)[0])) diff --git a/src/packet.c b/src/packet.c index 2be61d3b..33efad8f 100644 --- a/src/packet.c +++ b/src/packet.c @@ -460,6 +460,154 @@ packet_x11_open(LIBSSH2_SESSION * session, unsigned char *data, return 0; } +/* + * packet_authagent_open + * + * Open a connection to authentication agent + */ +static inline int +packet_authagent_open(LIBSSH2_SESSION * session, + unsigned char *data, size_t datalen, + packet_authagent_state_t *authagent_state) +{ + int failure_code = SSH_OPEN_CONNECT_FAILED; + /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ + size_t packet_len = 17 + (sizeof(X11FwdUnAvil) - 1); + unsigned char *p; + LIBSSH2_CHANNEL *channel = authagent_state->channel; + int rc; + + (void)datalen; + + if(authagent_state->state == libssh2_NB_state_idle) { + unsigned char *s = data + (sizeof("auth-agent@openssh.org") - 1) + 5; + authagent_state->sender_channel = _libssh2_ntohu32(s); + s += 4; + authagent_state->initial_window_size = _libssh2_ntohu32(s); + s += 4; + authagent_state->packet_size = _libssh2_ntohu32(s); + + _libssh2_debug((session, LIBSSH2_TRACE_CONN, + "Auth Agent Connection Received on channel %lu", + authagent_state->sender_channel)); + + authagent_state->state = libssh2_NB_state_allocated; + } + + if(session->authagent) { + if(authagent_state->state == libssh2_NB_state_allocated) { + channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); + authagent_state->channel = channel; + + if(!channel) { + _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "allocate a channel for new connection"); + failure_code = SSH_OPEN_RESOURCE_SHORTAGE; + goto authagent_exit; + } + memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); + + channel->session = session; + channel->channel_type_len = sizeof("auth agent") - 1; + channel->channel_type = LIBSSH2_ALLOC(session, + channel->channel_type_len + + 1); + if(!channel->channel_type) { + _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "allocate a channel for new connection"); + LIBSSH2_FREE(session, channel); + failure_code = SSH_OPEN_RESOURCE_SHORTAGE; + goto authagent_exit; + } + memcpy(channel->channel_type, "auth agent", + channel->channel_type_len + 1); + + channel->remote.id = authagent_state->sender_channel; + channel->remote.window_size_initial = + LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT; + + channel->local.id = _libssh2_channel_nextid(session); + channel->local.window_size_initial = + authagent_state->initial_window_size; + channel->local.window_size = authagent_state->initial_window_size; + channel->local.packet_size = authagent_state->packet_size; + + _libssh2_debug((session, LIBSSH2_TRACE_CONN, + "Auth Agent Connection established: channel " + "%lu/%lu win %lu/%lu packet %lu/%lu", + channel->local.id, channel->remote.id, + channel->local.window_size, + channel->remote.window_size, + channel->local.packet_size, + channel->remote.packet_size)); + + p = authagent_state->packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; + _libssh2_store_u32(&p, channel->remote.id); + _libssh2_store_u32(&p, channel->local.id); + _libssh2_store_u32(&p, channel->remote.window_size_initial); + _libssh2_store_u32(&p, channel->remote.packet_size); + + authagent_state->state = libssh2_NB_state_created; + } + + if(authagent_state->state == libssh2_NB_state_created) { + rc = _libssh2_transport_send(session, authagent_state->packet, 17, + NULL, 0); + if(rc == LIBSSH2_ERROR_EAGAIN) { + return rc; + } + else if(rc) { + authagent_state->state = libssh2_NB_state_idle; + return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send channel open " + "confirmation"); + } + + /* Link the channel into the session */ + _libssh2_list_add(&session->channels, &channel->node); + + /* mess with stuff so we don't keep reading the same packet + over and over */ + session->packet.total_num = 0; + session->fullpacket_state = libssh2_NB_state_idle; + + /* Pass control to the callback, they may turn right around and + and free the channel, or actually use it */ + + LIBSSH2_AUTHAGENT(channel); + + authagent_state->state = libssh2_NB_state_idle; + return 0; + } + } + else + failure_code = SSH_OPEN_RESOURCE_SHORTAGE; + + /* fall-through */ +authagent_exit: + p = authagent_state->packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; + _libssh2_store_u32(&p, authagent_state->sender_channel); + _libssh2_store_u32(&p, failure_code); + _libssh2_store_str(&p, AuthAgentUnavail, sizeof(AuthAgentUnavail) - 1); + _libssh2_htonu32(p, 0); + + rc = _libssh2_transport_send(session, authagent_state->packet, packet_len, + NULL, 0); + if(rc == LIBSSH2_ERROR_EAGAIN) { + return rc; + } + else if(rc) { + authagent_state->state = libssh2_NB_state_idle; + return _libssh2_error(session, rc, "Unable to send open failure"); + } + authagent_state->state = libssh2_NB_state_idle; + return 0; +} + /* * _libssh2_packet_add * @@ -513,6 +661,8 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data, goto libssh2_packet_add_jump_point4; case libssh2_NB_state_jump5: goto libssh2_packet_add_jump_point5; + case libssh2_NB_state_jumpauthagent: + goto libssh2_packet_add_jump_authagent; default: /* nothing to do */ break; } @@ -1051,6 +1201,20 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data, rc = packet_x11_open(session, data, datalen, &session->packAdd_x11open_state); } + else if((datalen >= (sizeof("auth-agent@openssh.com") + 4)) && + ((sizeof("auth-agent@openssh.com") - 1) == + _libssh2_ntohu32(data + 1)) && + (memcmp(data + 5, "auth-agent@openssh.com", + sizeof("auth-agent@openssh.com") - 1) == 0)) { + + /* init the state struct */ + memset(&session->packAdd_authagent_state, 0, + sizeof(session->packAdd_authagent_state)); + + libssh2_packet_add_jump_authagent: + rc = packet_authagent_open(session, data, datalen, + &session->packAdd_authagent_state); + } if(rc == LIBSSH2_ERROR_EAGAIN) return rc; diff --git a/src/session.c b/src/session.c index b1f31e90..c9317dd4 100644 --- a/src/session.c +++ b/src/session.c @@ -536,6 +536,21 @@ libssh2_session_callback_set(LIBSSH2_SESSION * session, oldcb = session->recv; session->recv = callback; return oldcb; + + case LIBSSH2_CALLBACK_AUTHAGENT: + oldcb = session->authagent; + session->authagent = callback; + return oldcb; + + case LIBSSH2_CALLBACK_AUTHAGENT_IDENTITIES: + oldcb = session->addLocalIdentities; + session->addLocalIdentities = callback; + return oldcb; + + case LIBSSH2_CALLBACK_AUTHAGENT_SIGN: + oldcb = session->agentSignCallback; + session->agentSignCallback = callback; + return oldcb; } _libssh2_debug((session, LIBSSH2_TRACE_TRANS, "Setting Callback %d", cbtype)); diff --git a/src/userauth.c b/src/userauth.c index ed89b9ea..a1c62f69 100644 --- a/src/userauth.c +++ b/src/userauth.c @@ -1249,7 +1249,7 @@ libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, return rc; } -static size_t plain_method_len(const char *method, size_t method_len) +size_t plain_method(char *method, size_t method_len) { if(!strncmp("ssh-rsa-cert-v01@openssh.com", method, @@ -1268,6 +1268,29 @@ static size_t plain_method_len(const char *method, size_t method_len) method_len)) { return 19; } + + if(!strncmp("ssh-ed25519-cert-v01@openssh.com", + method, + method_len)) { + return 11; + } + + if(!strncmp("sk-ecdsa-sha2-nistp256-cert-v01@openssh.com", + method, + method_len)) { + const char *new_method = "sk-ecdsa-sha2-nistp256@openssh.com"; + memcpy(method, new_method, strlen(new_method)); + return strlen(new_method); + } + + if(!strncmp("sk-ssh-ed25519-cert-v01@openssh.com", + method, + method_len)) { + const char *new_method = "sk-ssh-ed25519@openssh.com"; + memcpy(method, new_method, strlen(new_method)); + return strlen(new_method); + } + return method_len; } @@ -1701,8 +1724,8 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session, session->userauth_pblc_b = NULL; session->userauth_pblc_method_len = - plain_method_len((const char *)session->userauth_pblc_method, - session->userauth_pblc_method_len); + plain_method((char *)session->userauth_pblc_method, + session->userauth_pblc_method_len); if(strncmp((const char *)session->userauth_pblc_method, "sk-ecdsa-sha2-nistp256@openssh.com", @@ -2291,6 +2314,8 @@ LIBSSH2_API int libssh2_userauth_publickey_sk(LIBSSH2_SESSION *session, const char *username, size_t username_len, + const unsigned char *publickeydata, + size_t publickeydata_len, const char *privatekeydata, size_t privatekeydata_len, const char *passphrase, @@ -2298,11 +2323,19 @@ libssh2_userauth_publickey_sk(LIBSSH2_SESSION *session, ((*sign_callback)), void **abstract) { + int rc = LIBSSH2_ERROR_NONE; + + unsigned char *tmp_method = NULL; + size_t tmp_method_len = 0; + + unsigned char *tmp_publickeydata = NULL; + size_t tmp_publickeydata_len = 0; + unsigned char *pubkeydata = NULL; size_t pubkeydata_len = 0; + LIBSSH2_PRIVKEY_SK sk_info = { 0 }; void *sign_abstract = &sk_info; - int rc; sk_info.sign_callback = sign_callback; sk_info.orig_abstract = abstract; @@ -2310,37 +2343,81 @@ libssh2_userauth_publickey_sk(LIBSSH2_SESSION *session, if(privatekeydata_len && privatekeydata) { if(_libssh2_sk_pub_keyfilememory(session, - &session->userauth_pblc_method, - &session->userauth_pblc_method_len, - &pubkeydata, &pubkeydata_len, + &tmp_method, + &tmp_method_len, + &tmp_publickeydata, + &tmp_publickeydata_len, &(sk_info.algorithm), &(sk_info.flags), &(sk_info.application), &(sk_info.key_handle), &(sk_info.handle_len), privatekeydata, privatekeydata_len, - passphrase)) + passphrase)) { return _libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to extract public key " "from private key."); + } + else if(publickeydata_len == 0 || !publickeydata) { + session->userauth_pblc_method = tmp_method; + session->userauth_pblc_method_len = tmp_method_len; + + pubkeydata_len = tmp_publickeydata_len; + pubkeydata = tmp_publickeydata; + } + else { + const char *ecdsa = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"; + const char *ed25519 = "sk-ssh-ed25519-cert-v01@openssh.com"; + + if(tmp_method) { + LIBSSH2_FREE(session, tmp_method); + } + + if(!strncmp((char *)publickeydata, ecdsa, strlen(ecdsa))) { + session->userauth_pblc_method_len = strlen(ecdsa); + session->userauth_pblc_method = + LIBSSH2_ALLOC(session, session->userauth_pblc_method_len); + + memcpy(session->userauth_pblc_method, ecdsa, + session->userauth_pblc_method_len); + } + else if(!strncmp((char *)publickeydata, ed25519, + strlen(ed25519))) { + session->userauth_pblc_method_len = strlen(ed25519); + session->userauth_pblc_method = + LIBSSH2_ALLOC(session, session->userauth_pblc_method_len); + + memcpy(session->userauth_pblc_method, ed25519, + session->userauth_pblc_method_len); + } + + rc = memory_read_publickey(session, + &session->userauth_pblc_method, + &session->userauth_pblc_method_len, + &pubkeydata, &pubkeydata_len, + (char *)publickeydata, + publickeydata_len); + } } else { return _libssh2_error(session, LIBSSH2_ERROR_FILE, "Invalid data in public and private key."); } - rc = _libssh2_userauth_publickey(session, username, username_len, - pubkeydata, pubkeydata_len, - libssh2_sign_sk, &sign_abstract); - - while(rc == LIBSSH2_ERROR_EAGAIN) { + if(rc == LIBSSH2_ERROR_NONE) { rc = _libssh2_userauth_publickey(session, username, username_len, pubkeydata, pubkeydata_len, libssh2_sign_sk, &sign_abstract); + + while(rc == LIBSSH2_ERROR_EAGAIN) { + rc = _libssh2_userauth_publickey(session, username, username_len, + pubkeydata, pubkeydata_len, + libssh2_sign_sk, &sign_abstract); + } } - if(pubkeydata) - LIBSSH2_FREE(session, pubkeydata); + if(tmp_publickeydata) + LIBSSH2_FREE(session, tmp_publickeydata); if(sk_info.application) { LIBSSH2_FREE(session, (void *)sk_info.application);