@@ -1084,6 +1084,68 @@ nc_server_ssh_set_authkey_path_format(const char *path)
10841084 return ret ;
10851085}
10861086
1087+ /**
1088+ * @brief Forge the SSH protocol identification string based on the given prefix and the library versions.
1089+ *
1090+ * @param[in] prefix Optional prefix to include in the protocol string, can be NULL.
1091+ * @return Protocol string on success, NULL on error.
1092+ */
1093+ static char *
1094+ nc_server_ssh_forge_protocol_string (const char * prefix )
1095+ {
1096+ int r ;
1097+ char * protocol_str = NULL ;
1098+
1099+ if (prefix ) {
1100+ r = asprintf (& protocol_str , "%s-libnetconf2_%s-libssh_%d.%d.%d" ,
1101+ prefix , NC_VERSION ,
1102+ LIBSSH_VERSION_MAJOR , LIBSSH_VERSION_MINOR , LIBSSH_VERSION_MICRO );
1103+ } else {
1104+ r = asprintf (& protocol_str , "libnetconf2_%s-libssh_%d.%d.%d" ,
1105+ NC_VERSION ,
1106+ LIBSSH_VERSION_MAJOR , LIBSSH_VERSION_MINOR , LIBSSH_VERSION_MICRO );
1107+ }
1108+ NC_CHECK_ERRMEM_RET (r == -1 , NULL );
1109+
1110+ if (strlen (protocol_str ) > 245 ) {
1111+ ERR (NULL , "SSH protocol identification string too long (max 245 characters)." );
1112+ free (protocol_str );
1113+ return NULL ;
1114+ }
1115+
1116+ return protocol_str ;
1117+ }
1118+
1119+ API int
1120+ nc_server_ssh_set_protocol_string (const char * prefix )
1121+ {
1122+ int rc = 0 ;
1123+ char * protocol_str = NULL ;
1124+
1125+ NC_CHECK_ARG_RET (NULL , prefix , 1 );
1126+
1127+ protocol_str = nc_server_ssh_forge_protocol_string (prefix );
1128+ NC_CHECK_ERRMEM_GOTO (!protocol_str , rc = 1 , cleanup );
1129+
1130+ /* CONFIG LOCK */
1131+ if (nc_rwlock_lock (& server_opts .config_lock , NC_RWLOCK_WRITE , NC_CONFIG_LOCK_TIMEOUT , __func__ ) != 1 ) {
1132+ rc = 1 ;
1133+ goto cleanup ;
1134+ }
1135+
1136+ /* transfer ownership */
1137+ free (server_opts .ssh_protocol_string );
1138+ server_opts .ssh_protocol_string = protocol_str ;
1139+ protocol_str = NULL ;
1140+
1141+ /* CONFIG UNLOCK */
1142+ nc_rwlock_unlock (& server_opts .config_lock , __func__ );
1143+
1144+ cleanup :
1145+ free (protocol_str );
1146+ return rc ;
1147+ }
1148+
10871149/**
10881150 * @brief Get the public key type from binary data.
10891151 *
@@ -1203,6 +1265,34 @@ nc_server_ssh_auth_pubkey_compare_key(ssh_key key, struct nc_public_key *pubkeys
12031265 return ret ;
12041266}
12051267
1268+ /**
1269+ * @brief Send the SSH issue banner if configured.
1270+ *
1271+ * @param[in] session NETCONF session.
1272+ * @param[in] opts SSH server options.
1273+ */
1274+ static void
1275+ nc_server_ssh_send_banner (struct nc_session * session , struct nc_server_ssh_opts * opts )
1276+ {
1277+ if (!opts -> banner ) {
1278+ return ;
1279+ }
1280+
1281+ #if (LIBSSH_VERSION_MAJOR > 0 ) || (LIBSSH_VERSION_MAJOR == 0 && LIBSSH_VERSION_MINOR >= 10 )
1282+ ssh_string ban ;
1283+
1284+ ban = ssh_string_from_char (opts -> banner );
1285+ if (ban ) {
1286+ if (ssh_send_issue_banner (session -> ti .libssh .session , ban )) {
1287+ ERR (session , "Failed to send SSH banner (%s)." , ssh_get_error (session -> ti .libssh .session ));
1288+ }
1289+ ssh_string_free (ban );
1290+ }
1291+ #else
1292+ WRN (session , "SSH banner set but cannot be sent (libssh version 0.10.0 or later required)." );
1293+ #endif
1294+ }
1295+
12061296/**
12071297 * @brief Handle authentication request for the None method.
12081298 *
@@ -1217,11 +1307,9 @@ nc_server_ssh_auth_none(int local_users_supported, struct nc_auth_client *auth_c
12171307 assert (!local_users_supported || auth_client );
12181308
12191309 if (local_users_supported && auth_client -> none_enabled ) {
1220- /* success */
12211310 return 0 ;
12221311 }
12231312
1224- /* reply and return -1 so that this does not get counted as an unsuccessful authentication attempt */
12251313 ssh_message_reply_default (msg );
12261314 return -1 ;
12271315}
@@ -1560,6 +1648,9 @@ nc_server_ssh_auth(struct nc_session *session, struct nc_server_ssh_opts *opts,
15601648 session -> username = strdup (username );
15611649 NC_CHECK_ERRMEM_RET (!session -> username , 1 );
15621650
1651+ /* send the SSH issue banner on the first userauth request */
1652+ nc_server_ssh_send_banner (session , opts );
1653+
15631654 /* configure and count accepted auth methods */
15641655 if (local_users_supported ) {
15651656 if ((auth_client -> pubkey_store == NC_STORE_LOCAL ) ||
@@ -1972,7 +2063,8 @@ nc_accept_ssh_session(struct nc_session *session, struct nc_server_ssh_opts *opt
19722063 ssh_bind sbind = NULL ;
19732064 int rc = 1 , r ;
19742065 struct timespec ts_timeout ;
1975- const char * err_msg , * banner ;
2066+ const char * err_msg ;
2067+ char * proto_str = NULL , * proto_str_dyn = NULL ;
19762068
19772069 /* other transport-specific data */
19782070 session -> ti_type = NC_TI_SSH ;
@@ -2031,13 +2123,15 @@ nc_accept_ssh_session(struct nc_session *session, struct nc_server_ssh_opts *opt
20312123 }
20322124 }
20332125
2034- /* configure the ssh banner */
2035- if (opts -> banner ) {
2036- banner = opts -> banner ;
2126+ /* configure the ssh protocol identification string */
2127+ if (server_opts . ssh_protocol_string ) {
2128+ proto_str = server_opts . ssh_protocol_string ;
20372129 } else {
2038- banner = "libnetconf2-" NC_VERSION ;
2130+ proto_str_dyn = nc_server_ssh_forge_protocol_string (NULL );
2131+ NC_CHECK_ERRMEM_GOTO (!proto_str_dyn , rc = -1 , cleanup );
2132+ proto_str = proto_str_dyn ;
20392133 }
2040- if (ssh_bind_options_set (sbind , SSH_BIND_OPTIONS_BANNER , banner )) {
2134+ if (ssh_bind_options_set (sbind , SSH_BIND_OPTIONS_BANNER , proto_str )) {
20412135 rc = -1 ;
20422136 goto cleanup ;
20432137 }
@@ -2112,6 +2206,7 @@ nc_accept_ssh_session(struct nc_session *session, struct nc_server_ssh_opts *opt
21122206 if (sock > -1 ) {
21132207 close (sock );
21142208 }
2209+ free (proto_str_dyn );
21152210 ssh_bind_free (sbind );
21162211 return rc ;
21172212}
0 commit comments