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 386bcc89..75c563ed 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -611,6 +611,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 diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index baff0fa8..d2fa6637 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -709,6 +709,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 d5dbaa48..639a43ae 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 || (session->userauth_list_data_len < 1)) { - _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 || (session->userauth_list_data_len < 1)) { + _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 && (session->userauth_list_data_len >= 5)) { + 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... */ @@ -189,6 +223,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 *