From 2c7c6516f94d67e0cced14d1f00d47e223c4bdfa Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Mon, 26 Nov 2018 23:42:53 -0600 Subject: [PATCH] New function to retrieve pre-auth server banner --- docs/libssh2_userauth_banner.3 | 25 +++++++++ include/libssh2.h | 2 + src/libssh2_priv.h | 2 + src/userauth.c | 92 +++++++++++++++++++++++++++------- 4 files changed, 104 insertions(+), 17 deletions(-) create mode 100644 docs/libssh2_userauth_banner.3 diff --git a/docs/libssh2_userauth_banner.3 b/docs/libssh2_userauth_banner.3 new file mode 100644 index 00000000..b059dd12 --- /dev/null +++ b/docs/libssh2_userauth_banner.3 @@ -0,0 +1,25 @@ +.TH libssh2_userauth_banner 3 "27 Nov 2018" "libssh2 0.15" "libssh2 manual" +.SH NAME +libssh2_userauth_banner - get the server's pre-auth banner message +.SH SYNOPSIS +.nf +#include + +char * +libssh2_userauth_banner(LIBSSH2_SESSION *session, + size_t *banner_len_out); +.SH DESCRIPTION +\fIsession\fP - Session instance as returned by +.BR libssh2_session_init_ex(3) + +\fIbanner_len_out\fP - The length of the server banner returned. + +After an authentication has been attempted, such as a \fBSSH_USERAUTH_NONE\fP request sent by +.BR libssh2_userauth_list(3) , +this function can be called to retrieve the pre-auth banner sent by the server. If no such banner is sent, or if an authentication has not yet been attempted, returns NULL. +.SH RETURN VALUE +On success a UTF-8 pre-authentication banner message from the server. +On failure returns NULL. +.SH SEE ALSO +.BR libssh2_session_init_ex(3), +.BR libssh2_userauth_list(3) diff --git a/include/libssh2.h b/include/libssh2.h index b56a56ec..c8314d29 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -606,6 +606,8 @@ LIBSSH2_API const char *libssh2_session_banner_get(LIBSSH2_SESSION *session); LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, const char *username, unsigned int username_len); +LIBSSH2_API char *libssh2_userauth_banner(LIBSSH2_SESSION * session, + size_t *banner_len_out); LIBSSH2_API int libssh2_userauth_authenticated(LIBSSH2_SESSION *session); LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index 387b8a77..4d21b471 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -689,6 +689,8 @@ struct _LIBSSH2_SESSION libssh2_nonblocking_states userauth_list_state; unsigned char *userauth_list_data; size_t userauth_list_data_len; + char *userauth_banner; + size_t userauth_banner_len; packet_requirev_state_t userauth_list_packet_requirev_state; /* State variables used in libssh2_userauth_password_ex() */ diff --git a/src/userauth.c b/src/userauth.c index 37a08dd6..f454c927 100644 --- a/src/userauth.c +++ b/src/userauth.c @@ -53,6 +53,7 @@ #include "session.h" #include "userauth.h" +#define LIBSSH2_USERAUTH_MAX_BANNER 2048 /* libssh2_userauth_list * * List authentication methods @@ -63,8 +64,8 @@ static char *userauth_list(LIBSSH2_SESSION *session, const char *username, unsigned int username_len) { - static const unsigned char reply_codes[3] = - { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 }; + static const unsigned char reply_codes[4] = + { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_BANNER, 0 }; /* packet_type(1) + username_len(4) + service_len(4) + service(14)"ssh-connection" + method_len(4) = 27 */ unsigned long methods_len; @@ -118,21 +119,54 @@ static char *userauth_list(LIBSSH2_SESSION *session, const char *username, } if(session->userauth_list_state == libssh2_NB_state_sent) { - rc = _libssh2_packet_requirev(session, reply_codes, - &session->userauth_list_data, - &session->userauth_list_data_len, 0, - NULL, 0, - &session->userauth_list_packet_requirev_state); - if(rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block requesting userauth list"); - return NULL; - } - else if(rc) { - _libssh2_error(session, rc, "Failed getting response"); - session->userauth_list_state = libssh2_NB_state_idle; - return NULL; - } + do { + rc = _libssh2_packet_requirev(session, reply_codes, + &session->userauth_list_data, + &session->userauth_list_data_len, 0, + NULL, 0, + &session->userauth_list_packet_requirev_state); + if(rc == LIBSSH2_ERROR_EAGAIN) { + _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block requesting userauth list"); + return NULL; + } + else if(rc) { + _libssh2_error(session, rc, "Failed getting response"); + session->userauth_list_state = libssh2_NB_state_idle; + return NULL; + } + if (session->userauth_list_data[0] == SSH_MSG_USERAUTH_BANNER) { + methods_len = _libssh2_ntohu32(session->userauth_list_data + 1); + /* Cap to 512 bytes. */ + if (methods_len > LIBSSH2_USERAUTH_MAX_BANNER) { + _libssh2_debug(session, LIBSSH2_TRACE_AUTH, + "Banner length %u exceeds max allowed (%u)", + methods_len, LIBSSH2_USERAUTH_MAX_BANNER); + methods_len = LIBSSH2_USERAUTH_MAX_BANNER - 1; + } + + if (!session->userauth_banner) { + session->userauth_banner = LIBSSH2_ALLOC(session, methods_len + 1); + } + else if (session->userauth_banner_len < methods_len) { + session->userauth_banner = LIBSSH2_REALLOC(session, session->userauth_banner, methods_len + 1); + } + if (!session->userauth_banner) { + _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for userauth_banner"); + continue; + } + session->userauth_banner_len = methods_len; + + memmove(session->userauth_banner, session->userauth_list_data + 5, methods_len); + session->userauth_banner[methods_len] = '\0'; + _libssh2_debug(session, LIBSSH2_TRACE_AUTH, + "Banner: %s", + session->userauth_banner); + LIBSSH2_FREE(session, session->userauth_list_data); + } + else break; + } while (1); if(session->userauth_list_data[0] == SSH_MSG_USERAUTH_SUCCESS) { /* Wow, who'dve thought... */ @@ -176,6 +210,30 @@ libssh2_userauth_list(LIBSSH2_SESSION * session, const char *user, return ptr; } +/* libssh2_userauth_banner + * + * Retrieve banner message from server, if available. + * If no such message is sent by the server or if no authentication attempt has + * been made, this function returns NULL. + * libssh2_userauth_list makes a "none" authentication attempt and is + * sufficient to collect the pre-auth banner message. + * + * Banner ought to be UTF-8 encoded, and will be truncated to + * LIBSSH2_USERAUTH_MAX_BANNER bytes. Length will be returned in + * banner_len_out. + */ +LIBSSH2_API char * +libssh2_userauth_banner(LIBSSH2_SESSION * session, + size_t *banner_len_out) +{ + char *ptr = NULL; + if (session->userauth_banner) { + ptr = session->userauth_banner; + *banner_len_out = session->userauth_banner_len; + } + return ptr; +} + /* * libssh2_userauth_authenticated *