diff --git a/readconf.c b/readconf.c index 69d4553..fd6231d 100644 --- a/readconf.c +++ b/readconf.c @@ -157,6 +157,9 @@ typedef enum { oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, +#ifdef TCP_STEALTH + oTCPStealthSecret, +#endif oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes, oPubkeyAcceptedKeyTypes, oIgnoredUnknownOption, oDeprecated, oUnsupported @@ -281,6 +284,9 @@ static struct { { "hostbasedkeytypes", oHostbasedKeyTypes }, { "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes }, { "ignoreunknown", oIgnoreUnknown }, +#ifdef TCP_STEALTH + { "tcpstealthsecret", oTCPStealthSecret }, +#endif { NULL, oBadOption } }; @@ -1543,6 +1549,23 @@ parse_keytypes: multistate_ptr = multistate_yesnoaskconfirm; goto parse_multistate; +#ifdef TCP_STEALTH + case oTCPStealthSecret: + charptr = &options->tcp_stealth_secret; + + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", + filename, linenum); + + if (*activep && *charptr == NULL) { + *charptr = xmalloc(64 + 1); + memset(*charptr, 0x00, 64 + 1); + strncpy(*charptr, arg, 64); + } + + break; +#endif case oDeprecated: debug("%s line %d: Deprecated option \"%s\"", filename, linenum, keyword); @@ -1720,6 +1743,9 @@ initialize_options(Options * options) options->canonicalize_max_dots = -1; options->canonicalize_fallback_local = -1; options->canonicalize_hostname = -1; +#ifdef TCP_STEALTH + options->tcp_stealth_secret = NULL; +#endif options->revoked_host_keys = NULL; options->fingerprint_hash = -1; options->update_hostkeys = -1; diff --git a/readconf.h b/readconf.h index c84d068..4c01418 100644 --- a/readconf.h +++ b/readconf.h @@ -159,6 +159,9 @@ typedef struct { char *pubkey_key_types; char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ +#ifdef TCP_STEALTH + char *tcp_stealth_secret; +#endif } Options; #define SSH_CANONICALISE_NO 0 diff --git a/servconf.c b/servconf.c index b19d30e..526589d 100644 --- a/servconf.c +++ b/servconf.c @@ -168,6 +168,9 @@ initialize_server_options(ServerOptions *options) options->ip_qos_interactive = -1; options->ip_qos_bulk = -1; options->version_addendum = NULL; +#ifdef TCP_STEALTH + options->tcp_stealth_secret = NULL; +#endif options->fingerprint_hash = -1; } @@ -430,6 +433,9 @@ typedef enum { sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, sStreamLocalBindMask, sStreamLocalBindUnlink, sAllowStreamLocalForwarding, sFingerprintHash, +#ifdef TCP_STEALTH + sTCPStealthSecret, +#endif sDeprecated, sUnsupported } ServerOpCodes; @@ -571,6 +577,9 @@ static struct { { "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL }, { "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_ALL }, { "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL }, +#ifdef TCP_STEALTH + { "tcpstealthsecret", sTCPStealthSecret }, +#endif { "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL }, { NULL, sBadOption, 0 } }; @@ -1850,6 +1859,24 @@ process_server_config_line(ServerOptions *options, char *line, options->fingerprint_hash = value; break; +#ifdef TCP_STEALTH + case sTCPStealthSecret: + charptr = &options->tcp_stealth_secret; + + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", + filename, linenum); + + if (*activep && *charptr == NULL) { + *charptr = xmalloc(64 + 1); + memset(*charptr, 0x00, 64 + 1); + strncpy(*charptr, arg, 64); + } + + break; +#endif + case sDeprecated: logit("%s line %d: Deprecated option %s", filename, linenum, arg); diff --git a/servconf.h b/servconf.h index f4137af..79471d3 100644 --- a/servconf.h +++ b/servconf.h @@ -194,6 +194,9 @@ typedef struct { u_int num_auth_methods; char *auth_methods[MAX_AUTH_METHODS]; +#ifdef TCP_STEALTH + char *tcp_stealth_secret; +#endif int fingerprint_hash; } ServerOptions; @@ -216,6 +219,12 @@ struct connection_info { * NB. an option must appear in servconf.c:copy_set_server_options() or * COPY_MATCH_STRING_OPTS here but never both. */ +#ifdef TCP_STEALTH +#define M_CP_STEALTHSCRT(X) M_CP_STROPT(X); +#else +#define M_CP_STEALTHSCRT(X) +#endif + #define COPY_MATCH_STRING_OPTS() do { \ M_CP_STROPT(banner); \ M_CP_STROPT(trusted_user_ca_keys); \ @@ -234,6 +243,7 @@ struct connection_info { M_CP_STRARRAYOPT(deny_groups, num_deny_groups); \ M_CP_STRARRAYOPT(accept_env, num_accept_env); \ M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \ + M_CP_STEALTHSCRT(tcp_stealth_secret); \ } while (0) struct connection_info *get_connection_info(int, int); diff --git a/ssh.0 b/ssh.0 index 9aaf436..23d4b53 100644 --- a/ssh.0 +++ b/ssh.0 @@ -420,6 +420,20 @@ DESCRIPTION -y Send log information using the syslog(3) system module. By default this information is sent to stderr. + -z tcp_stealth_secret + Specifies the shared secret which is needed to connect to a stealth + SSH TCP server. Any string specified will be truncated to or padded + with zeroes to 64 bytes. This option needs kernel support and is + therefore only available if the required setsockopt() call is + available. + See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ + for details. + + IMPORTANT: This option should only be used for the purpose of + testing as other users could easily read out the secret from the + command line arguments. The TCPStealthSecret configuration option + is the preferred way of specifying the TCP Stealth secret. + ssh may additionally obtain configuration data from a per-user configuration file and a system-wide configuration file. The file format and configuration options are described in ssh_config(5). diff --git a/ssh.1 b/ssh.1 index cc53343..ede27b2 100644 --- a/ssh.1 +++ b/ssh.1 @@ -63,6 +63,7 @@ .Op Fl S Ar ctl_path .Op Fl W Ar host : Ns Ar port .Op Fl w Ar local_tun Ns Op : Ns Ar remote_tun +.Op Fl z Ar tcp_stealth_secret .Oo Ar user Ns @ Oc Ns Ar hostname .Op Ar command .Ek @@ -536,6 +537,7 @@ For full details of the options listed below, and their possible values, see .It StreamLocalBindUnlink .It StrictHostKeyChecking .It TCPKeepAlive +.It TCPStealthSecret .It Tunnel .It TunnelDevice .It UpdateHostKeys @@ -773,6 +775,21 @@ Send log information using the .Xr syslog 3 system module. By default this information is sent to stderr. +.It Fl z Ar tcp_stealth_secret +Specifies the shared secret which is needed to connect to a stealth SSH TCP +server. Any string specified will be truncated to or padded with zeroes to 64 +bytes. This option needs kernel support and is therefore only available if the +required +.Xr setsockopt 2 +call is available. +.Pp +See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ for details. +.Pp +.Cm IMPORTANT: +This option should only be used for the purpose of testing as other users could +easily read out the secret from the command line arguments. The +.Cm TCPStealthSecret +configuration option is the preferred way of specifying the TCP Stealth secret. .El .Pp .Nm diff --git a/ssh.c b/ssh.c index f9ff91f..49fb7e0 100644 --- a/ssh.c +++ b/ssh.c @@ -194,6 +194,14 @@ static int remote_forward_confirms_received = 0; extern int muxserver_sock; extern u_int muxclient_command; +#ifdef TCP_STEALTH +#define OPT_STEALTH "[-z tcp_stealth_secret] " +#define GETOPT_STEALTH "z:" +#else +#define OPT_STEALTH "" +#define GETOPT_STEALTH "" +#endif + /* Prints a help message to the user. This function never returns. */ static void @@ -205,7 +213,7 @@ usage(void) " [-F configfile] [-I pkcs11] [-i identity_file] [-L address]\n" " [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n" " [-Q query_option] [-R address] [-S ctl_path] [-W host:port]\n" -" [-w local_tun[:remote_tun]] [user@]hostname [command]\n" +" [-w local_tun[:remote_tun]] " OPT_STEALTH "[user@]hostname [command]\n" ); exit(255); } @@ -606,7 +614,7 @@ main(int ac, char **av) again: while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" - "ACD:E:F:GI:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) { + "ACD:E:F:GI:KL:MNO:PQ:R:S:TVw:W:XYy" GETOPT_STEALTH)) != -1) { switch (opt) { case '1': options.protocol = SSH_PROTO_1; @@ -920,6 +928,12 @@ main(int ac, char **av) case 'F': config = optarg; break; +#ifdef TCP_STEALTH + case 'z': + options.tcp_stealth_secret = xcalloc(64 + 1, sizeof(u_int8_t)); + strncpy(options.tcp_stealth_secret, optarg, 64); + break; +#endif default: usage(); } diff --git a/ssh_config.0 b/ssh_config.0 index b823c02..c734fd8 100644 --- a/ssh_config.0 +++ b/ssh_config.0 @@ -927,6 +927,15 @@ DESCRIPTION To disable TCP keepalive messages, the value should be set to M-bM-^@M-^\noM-bM-^@M-^]. + TCPStealthSecret + Specifies the shared secret which is needed to connect to a stealth + SSH TCP Server. Any string specified will be truncated to or padded + with zeroes to 64 bytes. This option needs kernel support and is + therefore only available if the required setsockopt() call is + available. + See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ + for details. + Tunnel Request tun(4) device forwarding between the client and the server. The argument must be M-bM-^@M-^\yesM-bM-^@M-^], M-bM-^@M-^\point-to-pointM-bM-^@M-^] (layer 3), M-bM-^@M-^\ethernetM-bM-^@M-^] (layer 2), or M-bM-^@M-^\noM-bM-^@M-^]. Specifying M-bM-^@M-^\yesM-bM-^@M-^] requests the diff --git a/ssh_config.5 b/ssh_config.5 index caf13a6..51bc9cf 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -1597,6 +1597,15 @@ This is important in scripts, and many users want it too. .Pp To disable TCP keepalive messages, the value should be set to .Dq no . +.It Cm TCPStealthSecret +Specifies the shared secret which is needed to connect to a stealth SSH TCP +Server. Any string specified will be truncated to or padded with zeroes to 64 +bytes. This option needs kernel support and is therefore only available if the +required +.Xr setsockopt 2 +call is available. +.Pp +See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ for details. .It Cm Tunnel Request .Xr tun 4 diff --git a/sshconnect.c b/sshconnect.c index 356ec79..807aa2e 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -283,6 +283,17 @@ ssh_create_socket(int privileged, struct addrinfo *ai) } fcntl(sock, F_SETFD, FD_CLOEXEC); +#ifdef TCP_STEALTH + if (options.tcp_stealth_secret) { + if (setsockopt(sock, IPPROTO_TCP, TCP_STEALTH, + options.tcp_stealth_secret, 64) == -1) { + error("setsockopt TCP_STEALTH: %s", strerror(errno)); + close(sock); + return -1; + } + } +#endif + /* Bind the socket to an alternative local IP address */ if (options.bind_address == NULL && !privileged) return sock; diff --git a/sshd.0 b/sshd.0 index 7eb0531..75c8c1c 100644 --- a/sshd.0 +++ b/sshd.0 @@ -7,7 +7,7 @@ SYNOPSIS sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-c host_certificate_file] [-E log_file] [-f config_file] [-g login_grace_time] [-h host_key_file] [-k key_gen_time] - [-o option] [-p port] [-u len] + [-o option] [-p port] [-u len] [-z tcp_stealth_secret] DESCRIPTION sshd (OpenSSH Daemon) is the daemon program for ssh(1). Together these @@ -143,6 +143,21 @@ DESCRIPTION in a key file. Configuration options that require DNS include using a USER@HOST pattern in AllowUsers or DenyUsers. + -z tcp_stealth_secret + Turns this SSH server into a Stealth SSH TCP Server. This option + specifies the shared secret which is needed by the clients in order + to be able to connect to the port the SSH server is listening on. + Any string specified will be truncated or padded with zeroes to 64 + bytes. This option needs kernel support and is therefore only + available if the required setsockopt() call is available. + See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ + for details. + + IMPORTANT: This option should only be used for the purpose of + testing as other users could easily read out the secret from the + command line arguments. The TCPStealthSecret configuration option + is the preferred way of specifying the TCP Stealth secret. + AUTHENTICATION The OpenSSH SSH daemon supports SSH protocols 1 and 2. The default is to use protocol 2 only, though this can be changed via the Protocol option diff --git a/sshd.8 b/sshd.8 index 6c521f2..47247a3 100644 --- a/sshd.8 +++ b/sshd.8 @@ -55,6 +55,7 @@ .Op Fl o Ar option .Op Fl p Ar port .Op Fl u Ar len +.Op Fl z Ar tcp_stealth_secret .Ek .Sh DESCRIPTION .Nm @@ -267,6 +268,24 @@ USER@HOST pattern in .Cm AllowUsers or .Cm DenyUsers . +.It Fl z Ar tcp_stealth_secret +Turns this SSH server into a stealth SSH TCP server. This option specifies the +shared secret which is needed by the clients in order to be able to connect to +the port the SSH server is listening on. Any string specified will be truncated +or padded with zeroes to 64 bytes. This option needs kernel support and is +therefore only available if the required +.Xr setsockopt 2 +call is available. +.Pp +See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ for details. + +.Cm IMPORTANT: +This option should only be used for the purpose of +testing as other users could easily read out the secret from the +command line arguments. The +.Cm TCPStealthSecret +configuration option +is the preferred way of specifying the TCP Stealth secret. .El .Sh AUTHENTICATION The OpenSSH SSH daemon supports SSH protocols 1 and 2. diff --git a/sshd.c b/sshd.c index 430569c..d2dc98d 100644 --- a/sshd.c +++ b/sshd.c @@ -1000,6 +1000,14 @@ drop_connection(int startups) return (r < p) ? 1 : 0; } +#ifdef TCP_STEALTH +#define OPT_STEALTH " [-z tcp_stealth_secret]" +#define GETOPT_STEALTH "z:" +#else +#define OPT_STEALTH "" +#define GETOPT_STEALTH "" +#endif + static void usage(void) { @@ -1015,7 +1023,7 @@ usage(void) "usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-c host_cert_file]\n" " [-E log_file] [-f config_file] [-g login_grace_time]\n" " [-h host_key_file] [-k key_gen_time] [-o option] [-p port]\n" -" [-u len]\n" +" [-u len]" OPT_STEALTH "\n" ); exit(1); } @@ -1193,6 +1201,14 @@ server_listen(void) if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) error("setsockopt SO_REUSEADDR: %s", strerror(errno)); +#ifdef TCP_STEALTH + if (options.tcp_stealth_secret != NULL) { + if (setsockopt(listen_sock, IPPROTO_TCP, TCP_STEALTH, + options.tcp_stealth_secret, 64) == -1) + error("setsockopt TCP_STEALTH: %s", + strerror(errno)); + } +#endif /* Only communicate in IPv6 over AF_INET6 sockets. */ if (ai->ai_family == AF_INET6) @@ -1508,7 +1524,8 @@ main(int ac, char **av) /* Parse command-line arguments. */ while ((opt = getopt(ac, av, - "C:E:b:c:f:g:h:k:o:p:u:46DQRTdeiqrt")) != -1) { + "z:C:E:b:c:f:g:h:k:o:p:u:" \ + "46DQRTdeiqrt" GETOPT_STEALTH)) != -1) { switch (opt) { case '4': options.address_family = AF_INET; @@ -1620,6 +1637,12 @@ main(int ac, char **av) exit(1); free(line); break; +#ifdef TCP_STEALTH + case 'z': + options.tcp_stealth_secret = xcalloc(64 + 1, sizeof(u_int8_t)); + strncpy(options.tcp_stealth_secret, optarg, 64); + break; +#endif case '?': default: usage(); diff --git a/sshd_config.0 b/sshd_config.0 index 8bda6a3..3b38822 100644 --- a/sshd_config.0 +++ b/sshd_config.0 @@ -909,6 +909,19 @@ DESCRIPTION To disable TCP keepalive messages, the value should be set to M-bM-^@M-^\noM-bM-^@M-^]. + TCPStealthSecret + Turns this SSH server into a stealth SSH TCP server. This + configuration option specifies the shared secret needed by the + clients in order to be able to connect to the port the SSH server + is listening on. This means that port scanners will receive a + TCP RST and thus will not recognize this TCP port being open. + + Any string specified will be truncated or padded with zeroes to 64 + bytes. This option needs kernel support and is therefore only + available if the required setsockopt() call is available. + See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ + for details. + TrustedUserCAKeys Specifies a file containing public keys of certificate authorities that are trusted to sign user certificates for diff --git a/sshd_config.5 b/sshd_config.5 index a37a3ac..05a6483 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -1508,6 +1508,18 @@ This avoids infinitely hanging sessions. .Pp To disable TCP keepalive messages, the value should be set to .Dq no . +.It Cm TCPStealthSecret +Turns this SSH server into a stealth SSH TCP server. This configuration option +specifies the shared secret needed by the clients in order to be able to connect +to the port the SSH server is listening on. This means that port scanners will +receive a TCP RST and thus will not recognize this TCP port being open. Any +string specified will be truncated or padded with zeroes to 64 bytes. This +option needs kernel support and is therefore only available if the required +.Xr setsockopt 2 +call is available. +.Pp +See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ for details. + .It Cm TrustedUserCAKeys Specifies a file containing public keys of certificate authorities that are trusted to sign user certificates for authentication, or