diff --git a/readconf.c b/readconf.c index fa3fab8f0..676a6d1ba 100644 --- a/readconf.c +++ b/readconf.c @@ -169,6 +169,9 @@ typedef enum { oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, +#ifdef TCP_STEALTH + oTCPStealthSecret, +#endif oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes, oPubkeyAcceptedKeyTypes, oProxyJump, oIgnoredUnknownOption, oDeprecated, oUnsupported @@ -296,6 +299,9 @@ static struct { { "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes }, { "ignoreunknown", oIgnoreUnknown }, { "proxyjump", oProxyJump }, +#ifdef TCP_STEALTH + { "tcpstealthsecret", oTCPStealthSecret }, +#endif { NULL, oBadOption } }; @@ -1656,6 +1662,23 @@ parse_keytypes: charptr = &options->identity_agent; goto parse_string; +#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); @@ -1852,6 +1875,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 cef55f71c..d7456026b 100644 --- a/readconf.h +++ b/readconf.h @@ -169,6 +169,9 @@ typedef struct { char *jump_extra; 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 795ddbab7..65adc6a12 100644 --- a/servconf.c +++ b/servconf.c @@ -162,6 +162,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; options->disable_forwarding = -1; } @@ -418,6 +421,9 @@ typedef enum { sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, sStreamLocalBindMask, sStreamLocalBindUnlink, sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding, +#ifdef TCP_STEALTH + sTCPStealthSecret, +#endif sDeprecated, sIgnore, sUnsupported } ServerOpCodes; @@ -559,6 +565,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 }, { "disableforwarding", sDisableForwarding, SSHCFG_ALL }, { NULL, sBadOption, 0 } @@ -1836,6 +1845,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: case sIgnore: case sUnsupported: diff --git a/servconf.h b/servconf.h index 5853a9747..46e3afa7e 100644 --- a/servconf.h +++ b/servconf.h @@ -188,6 +188,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; @@ -210,6 +213,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); \ @@ -228,6 +237,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 67ce809bb..b94d040fc 100644 --- a/ssh.0 +++ b/ssh.0 @@ -433,6 +433,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 4011c65aa..4a3b6d4f6 100644 --- a/ssh.1 +++ b/ssh.1 @@ -64,6 +64,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 @@ -558,6 +559,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 @@ -798,6 +800,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 ee0b16dc2..2082c1f06 100644 --- a/ssh.c +++ b/ssh.c @@ -192,6 +192,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 @@ -204,7 +212,7 @@ usage(void) " [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]\n" " [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]\n" " [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n" -" [user@]hostname [command]\n" +" " 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:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) { + "ACD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy" GETOPT_STEALTH)) != -1) { switch (opt) { case '1': options.protocol = SSH_PROTO_1; @@ -931,6 +939,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 4ca9a5ff8..0806e0083 100644 --- a/ssh_config.0 +++ b/ssh_config.0 @@ -923,6 +923,15 @@ DESCRIPTION To disable TCP keepalive messages, the value should be set to no. + 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 yes, point-to-point (layer 3), ethernet (layer 2), or no (the default). Specifying yes requests diff --git a/ssh_config.5 b/ssh_config.5 index 591365f34..5985ada41 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -1524,6 +1524,15 @@ This is important in scripts, and many users want it too. .Pp To disable TCP keepalive messages, the value should be set to .Cm 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 96b91ce1a..c57d190ad 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -282,6 +282,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 089244c93..9ff81fc81 100644 --- a/sshd.0 +++ b/sshd.0 @@ -7,6 +7,7 @@ SYNOPSIS sshd [-46DdeiqTt] [-C connection_spec] [-c host_certificate_file] [-E log_file] [-f config_file] [-g login_grace_time] [-h host_key_file] [-o option] [-p port] [-u len] + [-z tcp_stealth_secret] DESCRIPTION sshd (OpenSSH Daemon) is the daemon program for ssh(1). Together these @@ -122,6 +123,21 @@ DESCRIPTION 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 protocol 2 only. Each host has a host-specific key, used to identify the host. Whenever a client diff --git a/sshd.8 b/sshd.8 index 41fc5051a..fb93ffc42 100644 --- a/sshd.8 +++ b/sshd.8 @@ -53,6 +53,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 @@ -243,6 +244,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 protocol 2 only. diff --git a/sshd.c b/sshd.c index 1dc4d182a..f4124ac86 100644 --- a/sshd.c +++ b/sshd.c @@ -897,6 +897,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) { @@ -911,7 +919,7 @@ usage(void) fprintf(stderr, "usage: sshd [-46DdeiqTt] [-C connection_spec] [-c host_cert_file]\n" " [-E log_file] [-f config_file] [-g login_grace_time]\n" -" [-h host_key_file] [-o option] [-p port] [-u len]\n" +" [-h host_key_file] [-o option] [-p port] [-u len]\n" OPT_STEALTH "\n" ); exit(1); } @@ -1053,6 +1061,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) @@ -1400,7 +1416,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; @@ -1508,6 +1525,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 022c05226..85c5d369a 100644 --- a/sshd_config.0 +++ b/sshd_config.0 @@ -857,6 +857,19 @@ DESCRIPTION To disable TCP keepalive messages, the value should be set to no. + 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 32b29d240..9e77382d0 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -1417,6 +1417,17 @@ This avoids infinitely hanging sessions. .Pp To disable TCP keepalive messages, the value should be set to .Cm 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