diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index f439db71..a95b44c2 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -194,6 +194,7 @@ set(MAN_PAGES libssh2_trace.3 libssh2_trace_sethandler.3 libssh2_userauth_authenticated.3 + libssh2_userauth_banner.3 libssh2_userauth_hostbased_fromfile.3 libssh2_userauth_hostbased_fromfile_ex.3 libssh2_userauth_keyboard_interactive.3 diff --git a/docs/Makefile.am b/docs/Makefile.am index f0a71fc5..d9df59ca 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -164,6 +164,7 @@ dist_man_MANS = \ libssh2_trace.3 \ libssh2_trace_sethandler.3 \ libssh2_userauth_authenticated.3 \ + libssh2_userauth_banner.3 \ libssh2_userauth_hostbased_fromfile.3 \ libssh2_userauth_hostbased_fromfile_ex.3 \ libssh2_userauth_keyboard_interactive.3 \ diff --git a/docs/libssh2_userauth_banner.3 b/docs/libssh2_userauth_banner.3 new file mode 100644 index 00000000..1003b65a --- /dev/null +++ b/docs/libssh2_userauth_banner.3 @@ -0,0 +1,30 @@ +.TH libssh2_userauth_banner 3 "1 Jun 2021" "libssh2 1.9.0" "libssh2 manual" +.SH NAME +libssh2_userauth_banner - get the server's userauth banner message +.SH SYNOPSIS +.nf +#include + +int +libssh2_userauth_banner(LIBSSH2_SESSION *session, char **banner); +.SH DESCRIPTION +\fIsession\fP - Session instance as returned by +.BR libssh2_session_init_ex(3) + +\fIbanner\fP - Should point to a pointer that gets filled with banner message. + +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 userauth banner sent by +the server. If no such banner is sent, or if an authentication has not +yet been attempted, returns LIBSSH2_ERROR_MISSING_USERAUTH_BANNER. +.SH RETURN VALUE +On success returns 0 and an UTF-8 NUL-terminated string is stored in the +\fIbanner\fP. This string is internally managed by libssh2 and will be +deallocated upon session termination. +On failure returns +LIBSSH2_ERROR_MISSING_USERAUTH_BANNER. +.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 8db3015e..20ba5483 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -507,6 +507,7 @@ typedef struct _LIBSSH2_POLLFD { #define LIBSSH2_ERROR_CHANNEL_WINDOW_FULL -47 #define LIBSSH2_ERROR_KEYFILE_AUTH_FAILED -48 #define LIBSSH2_ERROR_RANDGEN -49 +#define LIBSSH2_ERROR_MISSING_USERAUTH_BANNER -50 /* this is a define to provide the old (<= 1.2.7) name */ #define LIBSSH2_ERROR_BANNER_NONE LIBSSH2_ERROR_BANNER_RECV @@ -615,6 +616,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 int libssh2_userauth_banner(LIBSSH2_SESSION *session, + char **banner); 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 aff791e7..f218a836 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -720,6 +720,7 @@ struct _LIBSSH2_SESSION libssh2_nonblocking_states userauth_list_state; unsigned char *userauth_list_data; size_t userauth_list_data_len; + char *userauth_banner; packet_requirev_state_t userauth_list_packet_requirev_state; /* State variables used in libssh2_userauth_password_ex() */ diff --git a/src/session.c b/src/session.c index 212560b8..0de5ab3f 100644 --- a/src/session.c +++ b/src/session.c @@ -994,6 +994,9 @@ session_free(LIBSSH2_SESSION *session) if(session->userauth_list_data) { LIBSSH2_FREE(session, session->userauth_list_data); } + if(session->userauth_banner) { + LIBSSH2_FREE(session, session->userauth_banner); + } if(session->userauth_pswd_data) { LIBSSH2_FREE(session, session->userauth_pswd_data); } diff --git a/src/userauth.c b/src/userauth.c index 29f58bab..84285bf0 100644 --- a/src/userauth.c +++ b/src/userauth.c @@ -63,11 +63,13 @@ 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 }; + 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; + unsigned int banner_len; unsigned char *s; int rc; @@ -134,6 +136,57 @@ static char *userauth_list(LIBSSH2_SESSION *session, const char *username, return NULL; } + if(session->userauth_list_data[0] == SSH_MSG_USERAUTH_BANNER) { + if(session->userauth_list_data_len < 5) { + LIBSSH2_FREE(session, session->userauth_list_data); + session->userauth_list_data = NULL; + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Unexpected packet size"); + return NULL; + } + banner_len = _libssh2_ntohu32(session->userauth_list_data + 1); + if(banner_len >= session->userauth_list_data_len - 5) { + LIBSSH2_FREE(session, session->userauth_list_data); + session->userauth_list_data = NULL; + _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY, + "Unexpected userauth banner size"); + return NULL; + } + session->userauth_banner = LIBSSH2_ALLOC(session, banner_len); + if(!session->userauth_banner) { + LIBSSH2_FREE(session, session->userauth_list_data); + session->userauth_list_data = NULL; + _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for userauth_banner"); + return NULL; + } + memmove(session->userauth_banner, session->userauth_list_data + 5, + banner_len); + session->userauth_banner[banner_len] = '\0'; + _libssh2_debug(session, LIBSSH2_TRACE_AUTH, + "Banner: %s", + session->userauth_banner); + LIBSSH2_FREE(session, session->userauth_list_data); + session->userauth_list_data = NULL; + /* SSH_MSG_USERAUTH_BANNER has been handled */ + reply_codes[2] = 0; + 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_SUCCESS) { /* Wow, who'dve thought... */ _libssh2_error(session, LIBSSH2_ERROR_NONE, "No error"); @@ -189,6 +242,30 @@ libssh2_userauth_list(LIBSSH2_SESSION * session, const char *user, return ptr; } +/* libssh2_userauth_banner + * + * Retrieve banner message from server, if available. + * When no such message is sent by server or if no authentication attempt has + * been made, this function returns LIBSSH2_ERROR_MISSING_AUTH_BANNER. + */ +LIBSSH2_API int +libssh2_userauth_banner(LIBSSH2_SESSION *session, char **banner) +{ + if(NULL == session) + return LIBSSH2_ERROR_MISSING_USERAUTH_BANNER; + + if(!session->userauth_banner) { + return _libssh2_error(session, + LIBSSH2_ERROR_MISSING_USERAUTH_BANNER, + "Missing userauth banner"); + } + + if(banner != NULL) + *banner = session->userauth_banner; + + return LIBSSH2_ERROR_NONE; +} + /* * libssh2_userauth_authenticated *