Index: INSTALL =================================================================== --- INSTALL +++ INSTALL @@ -76,6 +76,15 @@ http://www.jmknoble.net/software/x11-ssh-askpass/ +TCP Wrappers: + +If you wish to use the TCP wrappers functionality you will need at least +tcpd.h and libwrap.a, either in the standard include and library paths, +or in the directory specified by --with-tcp-wrappers. Version 7.6 is +known to work. + +http://ftp.porcupine.org/pub/security/index.html + LibEdit: sftp supports command-line editing via NetBSD's libedit. If your platform @@ -194,6 +203,9 @@ --with-osfsia, --without-osfsia will enable or disable OSF1's Security Integration Architecture. The default for OSF1 machines is enable. +--with-tcp-wrappers will enable TCP Wrappers (/etc/hosts.allow|deny) +support. + --with-utmpx enables utmpx support. utmpx support is automatic for some platforms. Index: auth-pam.c =================================================================== --- auth-pam.c +++ auth-pam.c @@ -101,6 +101,7 @@ #endif #include "monitor_wrap.h" #include "srclimit.h" +#include "blacklist_client.h" extern ServerOptions options; extern struct sshbuf *loginmsg; @@ -936,6 +937,8 @@ sshbuf_free(buffer); return (0); } + BLACKLIST_NOTIFY(NULL, BLACKLIST_BAD_USER, + sshpam_authctxt->user); error("PAM: %s for %s%.100s from %.100s", msg, sshpam_authctxt->valid ? "" : "illegal user ", sshpam_authctxt->user, sshpam_rhost); Index: auth.c =================================================================== --- auth.c +++ auth.c @@ -75,6 +75,7 @@ #include "monitor_wrap.h" #include "ssherr.h" #include "channels.h" +#include "blacklist_client.h" /* import */ extern ServerOptions options; @@ -285,8 +286,11 @@ authmsg = "Postponed"; else if (partial) authmsg = "Partial"; - else + else { authmsg = authenticated ? "Accepted" : "Failed"; + if (authenticated) + BLACKLIST_NOTIFY(ssh, BLACKLIST_AUTH_OK, "ssh"); + } if ((extra = format_method_key(authctxt)) == NULL) { if (authctxt->auth_method_info != NULL) @@ -463,6 +467,9 @@ { #ifdef HAVE_LOGIN_CAP extern login_cap_t *lc; +#ifdef HAVE_AUTH_HOSTOK + const char *from_host, *from_ip; +#endif #ifdef BSD_AUTH auth_session_t *as; #endif @@ -490,6 +497,7 @@ aix_restoreauthdb(); #endif if (pw == NULL) { + BLACKLIST_NOTIFY(ssh, BLACKLIST_BAD_USER, user); logit("Invalid user %.100s from %.100s port %d", user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); #ifdef CUSTOM_FAILED_LOGIN @@ -508,6 +516,21 @@ debug("unable to get login class: %s", user); return (NULL); } +#ifdef HAVE_AUTH_HOSTOK + from_host = auth_get_canonical_hostname(ssh, options.use_dns); + from_ip = ssh_remote_ipaddr(ssh); + if (!auth_hostok(lc, from_host, from_ip)) { + debug("Denied connection for %.200s from %.200s [%.200s].", + pw->pw_name, from_host, from_ip); + return (NULL); + } +#endif /* HAVE_AUTH_HOSTOK */ +#ifdef HAVE_AUTH_TIMEOK + if (!auth_timeok(lc, time(NULL))) { + debug("LOGIN %.200s REFUSED (TIME)", pw->pw_name); + return (NULL); + } +#endif /* HAVE_AUTH_TIMEOK */ #ifdef BSD_AUTH if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { Index: auth2.c =================================================================== --- auth2.c +++ auth2.c @@ -52,6 +52,7 @@ #include "dispatch.h" #include "pathnames.h" #include "ssherr.h" +#include "blacklist_client.h" #ifdef GSSAPI #include "ssh-gss.h" #endif @@ -442,8 +443,10 @@ } else { /* Allow initial try of "none" auth without failure penalty */ if (!partial && !authctxt->server_caused_failure && - (authctxt->attempt > 1 || strcmp(method, "none") != 0)) + (authctxt->attempt > 1 || strcmp(method, "none") != 0)) { authctxt->failures++; + BLACKLIST_NOTIFY(ssh, BLACKLIST_AUTH_FAIL, "ssh"); + } if (authctxt->failures >= options.max_authtries) { #ifdef SSH_AUDIT_EVENTS mm_audit_event(ssh, SSH_LOGIN_EXCEED_MAXTRIES); Index: configure.ac =================================================================== --- configure.ac +++ configure.ac @@ -1056,6 +1056,7 @@ [NetBSD read function is sometimes redirected, breaking atomicio comparisons against it]) ;; *-*-freebsd*) + SKIP_DISABLE_LASTLOG_DEFINE=yes AC_DEFINE([LOCKED_PASSWD_PREFIX], ["*LOCKED*"], [Account locked with pw(1)]) AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) AC_CHECK_HEADER([net/if_tap.h], , @@ -1662,6 +1663,61 @@ AC_MSG_RESULT([no]) fi +# Check whether user wants TCP wrappers support +TCPW_MSG="no" +AC_ARG_WITH([tcp-wrappers], + [ --with-tcp-wrappers[[=PATH]] Enable tcpwrappers support (optionally in PATH)], + [ + if test "x$withval" != "xno" ; then + saved_LIBS="$LIBS" + saved_LDFLAGS="$LDFLAGS" + saved_CPPFLAGS="$CPPFLAGS" + if test -n "${withval}" && \ + test "x${withval}" != "xyes"; then + if test -d "${withval}/lib"; then + if test -n "${need_dash_r}"; then + LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" + else + LDFLAGS="-L${withval}/lib ${LDFLAGS}" + fi + else + if test -n "${need_dash_r}"; then + LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}" + else + LDFLAGS="-L${withval} ${LDFLAGS}" + fi + fi + if test -d "${withval}/include"; then + CPPFLAGS="-I${withval}/include ${CPPFLAGS}" + else + CPPFLAGS="-I${withval} ${CPPFLAGS}" + fi + fi + LIBS="-lwrap $LIBS" + AC_MSG_CHECKING([for libwrap]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include +#include +int deny_severity = 0, allow_severity = 0; + ]], [[ + hosts_access(0); + ]])], [ + AC_MSG_RESULT([yes]) + AC_DEFINE([LIBWRAP], [1], + [Define if you want + TCP Wrappers support]) + SSHDLIBS="$SSHDLIBS -lwrap" + TCPW_MSG="yes" + ], [ + AC_MSG_ERROR([*** libwrap missing]) + ]) + LIBS="$saved_LIBS" + fi + ] +) + # Check whether user wants to use ldns LDNS_MSG="no" AC_ARG_WITH(ldns, @@ -1879,6 +1935,8 @@ dnl Checks for library functions. Please keep in alphabetical order AC_CHECK_FUNCS([ \ + auth_hostok \ + auth_timeok \ Blowfish_initstate \ Blowfish_expandstate \ Blowfish_expand0state \ @@ -3267,11 +3325,11 @@ LIBFIDO2=`$PKGCONFIG --libs libfido2` CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags libfido2`" else - LIBFIDO2="-lfido2 -lcbor" + LIBFIDO2="-lprivatefido2 -lprivatecbor" fi OTHERLIBS=`echo $LIBFIDO2 | sed 's/-lfido2//'` fido2_error= - AC_CHECK_LIB([fido2], [fido_init], + AC_CHECK_LIB([privatefido2], [fido_init], [ ], [ fido2_error="missing/unusable libfido2" ], [ $OTHERLIBS ] @@ -5699,6 +5757,7 @@ echo " OSF SIA support: $SIA_MSG" echo " KerberosV support: $KRB5_MSG" echo " SELinux support: $SELINUX_MSG" +echo " TCP Wrappers support: $TCPW_MSG" echo " libedit support: $LIBEDIT_MSG" echo " libldns support: $LDNS_MSG" echo " Solaris process contract support: $SPC_MSG" Index: packet.c =================================================================== --- packet.c +++ packet.c @@ -96,6 +96,7 @@ #include "packet.h" #include "ssherr.h" #include "sshbuf.h" +#include "blacklist_client.h" #ifdef PACKET_DEBUG #define DBG(x) x @@ -2014,6 +2015,7 @@ case SSH_ERR_NO_KEX_ALG_MATCH: case SSH_ERR_NO_HOSTKEY_ALG_MATCH: if (ssh->kex && ssh->kex->failed_choice) { + BLACKLIST_NOTIFY(ssh, BLACKLIST_AUTH_FAIL, "ssh"); ssh_packet_clear_keys(ssh); errno = oerrno; logdie("Unable to negotiate with %s: %s. " Index: pathnames.h =================================================================== --- pathnames.h +++ pathnames.h @@ -126,7 +126,7 @@ * Default location of askpass */ #ifndef _PATH_SSH_ASKPASS_DEFAULT -#define _PATH_SSH_ASKPASS_DEFAULT "/usr/X11R6/bin/ssh-askpass" +#define _PATH_SSH_ASKPASS_DEFAULT "/usr/local/bin/ssh-askpass" #endif /* Location of ssh-keysign for hostbased authentication */ @@ -146,7 +146,7 @@ /* xauth for X11 forwarding */ #ifndef _PATH_XAUTH -#define _PATH_XAUTH "/usr/X11R6/bin/xauth" +#define _PATH_XAUTH "/usr/local/bin/xauth" #endif /* UNIX domain socket for X11 server; displaynum will replace %u */ Index: regress/unittests/sshkey/test_sshkey.c =================================================================== --- regress/unittests/sshkey/test_sshkey.c +++ regress/unittests/sshkey/test_sshkey.c @@ -8,6 +8,7 @@ #include "includes.h" #include +#include #include #ifdef HAVE_STDINT_H #include @@ -81,7 +82,7 @@ critopts = sshbuf_new(); ASSERT_PTR_NE(critopts, NULL); - put_opt(critopts, "force-command", "/usr/local/bin/nethack"); + put_opt(critopts, "force-command", _PATH_LOCALBASE "/bin/nethack"); put_opt(critopts, "source-address", "192.168.0.0/24,127.0.0.1,::1"); exts = sshbuf_new(); Index: servconf.h =================================================================== --- servconf.h +++ servconf.h @@ -248,6 +248,8 @@ int unused_connection_timeout; char *sshd_session_path; + + int use_blacklist; } ServerOptions; /* Information about the incoming connection as used by Match */ Index: servconf.c =================================================================== --- servconf.c +++ servconf.c @@ -68,6 +68,7 @@ #include "auth.h" #include "myproposal.h" #include "digest.h" +#include "version.h" #if !defined(SSHD_PAM_SERVICE) # define SSHD_PAM_SERVICE "sshd" @@ -213,6 +214,7 @@ options->num_channel_timeouts = 0; options->unused_connection_timeout = -1; options->sshd_session_path = NULL; + options->use_blacklist = -1; } /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */ @@ -265,12 +267,16 @@ free(def_sig); } +static const char *defaultkey = "[default]"; + void servconf_add_hostkey(const char *file, const int line, ServerOptions *options, const char *path, int userprovided) { char *apath = derelativise_path(path); + if (file == defaultkey && access(path, R_OK) != 0) + return; opt_array_append2(file, line, "HostKey", &options->host_key_files, &options->host_key_file_userprovided, &options->num_host_key_files, apath, userprovided); @@ -295,26 +301,28 @@ /* Portable-specific options */ if (options->use_pam == -1) - options->use_pam = 0; + options->use_pam = 1; if (options->pam_service_name == NULL) options->pam_service_name = xstrdup(SSHD_PAM_SERVICE); /* Standard Options */ if (options->num_host_key_files == 0) { /* fill default hostkeys for protocols */ - servconf_add_hostkey("[default]", 0, options, + servconf_add_hostkey(defaultkey, 0, options, _PATH_HOST_RSA_KEY_FILE, 0); #ifdef OPENSSL_HAS_ECC - servconf_add_hostkey("[default]", 0, options, + servconf_add_hostkey(defaultkey, 0, options, _PATH_HOST_ECDSA_KEY_FILE, 0); #endif - servconf_add_hostkey("[default]", 0, options, + servconf_add_hostkey(defaultkey, 0, options, _PATH_HOST_ED25519_KEY_FILE, 0); #ifdef WITH_XMSS - servconf_add_hostkey("[default]", 0, options, + servconf_add_hostkey(defaultkey, 0, options, _PATH_HOST_XMSS_KEY_FILE, 0); #endif /* WITH_XMSS */ } + if (options->num_host_key_files == 0) + fatal("No host key files found"); /* No certificates by default */ if (options->num_ports == 0) options->ports[options->num_ports++] = SSH_DEFAULT_PORT; @@ -329,7 +337,7 @@ if (options->login_grace_time == -1) options->login_grace_time = 120; if (options->permit_root_login == PERMIT_NOT_SET) - options->permit_root_login = PERMIT_NO_PASSWD; + options->permit_root_login = PERMIT_NO; if (options->ignore_rhosts == -1) options->ignore_rhosts = 1; if (options->ignore_user_known_hosts == -1) @@ -381,7 +389,7 @@ if (options->gss_strict_acceptor == -1) options->gss_strict_acceptor = 1; if (options->password_authentication == -1) - options->password_authentication = 1; + options->password_authentication = 0; if (options->kbd_interactive_authentication == -1) options->kbd_interactive_authentication = 1; if (options->permit_empty_passwd == -1) @@ -448,17 +456,17 @@ if (options->max_sessions == -1) options->max_sessions = DEFAULT_SESSIONS_MAX; if (options->use_dns == -1) - options->use_dns = 0; + options->use_dns = 1; if (options->client_alive_interval == -1) options->client_alive_interval = 0; if (options->client_alive_count_max == -1) options->client_alive_count_max = 3; if (options->num_authkeys_files == 0) { - opt_array_append("[default]", 0, "AuthorizedKeysFiles", + opt_array_append(defaultkey, 0, "AuthorizedKeysFiles", &options->authorized_keys_files, &options->num_authkeys_files, _PATH_SSH_USER_PERMITTED_KEYS); - opt_array_append("[default]", 0, "AuthorizedKeysFiles", + opt_array_append(defaultkey, 0, "AuthorizedKeysFiles", &options->authorized_keys_files, &options->num_authkeys_files, _PATH_SSH_USER_PERMITTED_KEYS2); @@ -470,7 +478,7 @@ if (options->ip_qos_bulk == -1) options->ip_qos_bulk = IPTOS_DSCP_CS1; if (options->version_addendum == NULL) - options->version_addendum = xstrdup(""); + options->version_addendum = xstrdup(SSH_VERSION_FREEBSD); if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) options->fwd_opts.streamlocal_bind_mask = 0177; if (options->fwd_opts.streamlocal_bind_unlink == -1) @@ -489,6 +497,8 @@ options->unused_connection_timeout = 0; if (options->sshd_session_path == NULL) options->sshd_session_path = xstrdup(_PATH_SSHD_SESSION); + if (options->use_blacklist == -1) + options->use_blacklist = 0; assemble_algorithms(options); @@ -572,6 +582,7 @@ sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider, sRequiredRSASize, sChannelTimeout, sUnusedConnectionTimeout, sSshdSessionPath, + sUseBlacklist, sDeprecated, sIgnore, sUnsupported } ServerOpCodes; @@ -739,6 +750,9 @@ { "channeltimeout", sChannelTimeout, SSHCFG_ALL }, { "unusedconnectiontimeout", sUnusedConnectionTimeout, SSHCFG_ALL }, { "sshdsessionpath", sSshdSessionPath, SSHCFG_GLOBAL }, + { "useblacklist", sUseBlacklist, SSHCFG_GLOBAL }, + { "useblocklist", sUseBlacklist, SSHCFG_GLOBAL }, /* alias */ + { NULL, sBadOption, 0 } }; @@ -2658,6 +2672,10 @@ charptr = &options->sshd_session_path; goto parse_filename; + case sUseBlacklist: + intptr = &options->use_blacklist; + goto parse_flag; + case sDeprecated: case sIgnore: case sUnsupported: @@ -3203,6 +3221,7 @@ dump_cfg_fmtint(sStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink); dump_cfg_fmtint(sFingerprintHash, o->fingerprint_hash); dump_cfg_fmtint(sExposeAuthInfo, o->expose_userauth_info); + dump_cfg_fmtint(sUseBlacklist, o->use_blacklist); /* string arguments */ dump_cfg_string(sPidFile, o->pid_file); Index: session.c =================================================================== --- session.c +++ session.c @@ -977,6 +977,9 @@ struct passwd *pw = s->pw; #if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN) char *path = NULL; +#else + extern char **environ; + char **senv, **var, *val; #endif /* Initialize the environment. */ @@ -998,6 +1001,9 @@ } #endif + if (getenv("TZ")) + child_set_env(&env, &envsize, "TZ", getenv("TZ")); + #ifdef GSSAPI /* Allow any GSSAPI methods that we've used to alter * the child's environment as they see fit @@ -1015,11 +1021,30 @@ child_set_env(&env, &envsize, "LOGIN", pw->pw_name); #endif child_set_env(&env, &envsize, "HOME", pw->pw_dir); + snprintf(buf, sizeof buf, "%.200s/%.50s", _PATH_MAILDIR, pw->pw_name); + child_set_env(&env, &envsize, "MAIL", buf); #ifdef HAVE_LOGIN_CAP - if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH) < 0) - child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); - else - child_set_env(&env, &envsize, "PATH", getenv("PATH")); + child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); + child_set_env(&env, &envsize, "TERM", "su"); + /* + * Temporarily swap out our real environment with an empty one, + * let setusercontext() apply any environment variables defined + * for the user's login class, copy those variables to the child, + * free the temporary environment, and restore the original. + */ + senv = environ; + environ = xmalloc(sizeof(*environ)); + *environ = NULL; + (void)setusercontext(lc, pw, pw->pw_uid, LOGIN_SETENV|LOGIN_SETPATH); + for (var = environ; *var != NULL; ++var) { + if ((val = strchr(*var, '=')) != NULL) { + *val++ = '\0'; + child_set_env(&env, &envsize, *var, val); + } + free(*var); + } + free(environ); + environ = senv; #else /* HAVE_LOGIN_CAP */ # ifndef HAVE_CYGWIN /* @@ -1039,17 +1064,9 @@ # endif /* HAVE_CYGWIN */ #endif /* HAVE_LOGIN_CAP */ - if (!options.use_pam) { - snprintf(buf, sizeof buf, "%.200s/%.50s", - _PATH_MAILDIR, pw->pw_name); - child_set_env(&env, &envsize, "MAIL", buf); - } - /* Normal systems set SHELL by default. */ child_set_env(&env, &envsize, "SHELL", shell); - if (getenv("TZ")) - child_set_env(&env, &envsize, "TZ", getenv("TZ")); if (s->term) child_set_env(&env, &envsize, "TERM", s->term); if (s->display) @@ -1263,7 +1280,8 @@ do_nologin(struct passwd *pw) { FILE *f = NULL; - char buf[1024], *nl, *def_nl = _PATH_NOLOGIN; + const char *nl; + char buf[1024], *def_nl = _PATH_NOLOGIN; struct stat sb; #ifdef HAVE_LOGIN_CAP @@ -1353,7 +1371,7 @@ if (platform_privileged_uidswap()) { #ifdef HAVE_LOGIN_CAP if (setusercontext(lc, pw, pw->pw_uid, - (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) { + (LOGIN_SETALL & ~(LOGIN_SETENV|LOGIN_SETPATH|LOGIN_SETUSER))) < 0) { perror("unable to set user context"); exit(1); } Index: ssh-agent.1 =================================================================== --- ssh-agent.1 +++ ssh-agent.1 @@ -43,7 +43,7 @@ .Sh SYNOPSIS .Nm ssh-agent .Op Fl c | s -.Op Fl \&Dd +.Op Fl \&Ddx .Op Fl a Ar bind_address .Op Fl E Ar fingerprint_hash .Op Fl O Ar option @@ -171,6 +171,8 @@ .Xr ssh-add 1 overrides this value. Without this option the default maximum lifetime is forever. +.It Fl x +Exit after the last client has disconnected. .It Ar command Op Ar arg ... If a command (and optional arguments) is given, this is executed as a subprocess of the agent. Index: ssh-agent.c =================================================================== --- ssh-agent.c +++ ssh-agent.c @@ -198,11 +198,27 @@ /* Refuse signing of non-SSH messages for web-origin FIDO keys */ static int restrict_websafe = 1; +/* + * Client connection count; incremented in new_socket() and decremented in + * close_socket(). When it reaches 0, ssh-agent will exit. Since it is + * normally initialized to 1, it will never reach 0. However, if the -x + * option is specified, it is initialized to 0 in main(); in that case, + * ssh-agent will exit as soon as it has had at least one client but no + * longer has any. + */ +static int xcount = 1; + static void close_socket(SocketEntry *e) { size_t i; + int last = 0; + if (e->type == AUTH_CONNECTION) { + debug("xcount %d -> %d", xcount, xcount - 1); + if (--xcount == 0) + last = 1; + } close(e->fd); sshbuf_free(e->input); sshbuf_free(e->output); @@ -215,6 +231,8 @@ memset(e, '\0', sizeof(*e)); e->fd = -1; e->type = AUTH_UNUSED; + if (last) + cleanup_exit(0); } static void @@ -1897,6 +1915,10 @@ debug_f("type = %s", type == AUTH_CONNECTION ? "CONNECTION" : (type == AUTH_SOCKET ? "SOCKET" : "UNKNOWN")); + if (type == AUTH_CONNECTION) { + debug("xcount %d -> %d", xcount, xcount + 1); + ++xcount; + } set_nonblock(fd); if (fd > max_fd) @@ -2181,7 +2203,7 @@ usage(void) { fprintf(stderr, - "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n" + "usage: ssh-agent [-c | -s] [-Ddx] [-a bind_address] [-E fingerprint_hash]\n" " [-O option] [-P allowed_providers] [-t life]\n" " ssh-agent [-a bind_address] [-E fingerprint_hash] [-O option]\n" " [-P allowed_providers] [-t life] command [arg ...]\n" @@ -2216,6 +2238,7 @@ /* drop */ (void)setegid(getgid()); (void)setgid(getgid()); + setuid(geteuid()); platform_disable_tracing(0); /* strict=no */ @@ -2227,7 +2250,7 @@ __progname = ssh_get_progname(av[0]); seed_rng(); - while ((ch = getopt(ac, av, "cDdksE:a:O:P:t:")) != -1) { + while ((ch = getopt(ac, av, "cDdksE:a:O:P:t:x")) != -1) { switch (ch) { case 'E': fingerprint_hash = ssh_digest_alg_by_name(optarg); @@ -2279,6 +2302,9 @@ usage(); } break; + case 'x': + xcount = 0; + break; default: usage(); } Index: ssh-gss.h =================================================================== --- ssh-gss.h +++ ssh-gss.h @@ -28,10 +28,10 @@ #ifdef GSSAPI -#ifdef HAVE_GSSAPI_H -#include -#elif defined(HAVE_GSSAPI_GSSAPI_H) +#ifdef HAVE_GSSAPI_GSSAPI_H #include +#elif defined(HAVE_GSSAPI_H) +#include #endif #ifdef KRB5 Index: ssh.c =================================================================== --- ssh.c +++ ssh.c @@ -1438,6 +1438,26 @@ cinfo->conn_hash_hex = ssh_connection_hash(cinfo->thishost, cinfo->remhost, cinfo->portstr, cinfo->remuser, cinfo->jmphost); + /* Find canonic host name. */ + if (strchr(host, '.') == NULL) { + struct addrinfo hints; + struct addrinfo *ai = NULL; + int errgai; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = options.address_family; + hints.ai_flags = AI_CANONNAME; + hints.ai_socktype = SOCK_STREAM; + errgai = getaddrinfo(host, NULL, &hints, &ai); + if (errgai == 0) { + if (ai->ai_canonname != NULL) { + free(host); + host = xstrdup(ai->ai_canonname); + } + freeaddrinfo(ai); + } + } + /* * Expand tokens in arguments. NB. LocalCommand is expanded later, * after port-forwarding is set up, so it may pick up any local Index: ssh_config.5 =================================================================== --- ssh_config.5 +++ ssh_config.5 @@ -548,6 +548,8 @@ .Cm no (the default), the check will not be executed. +The default is +.Cm no . .It Cm Ciphers Specifies the ciphers allowed and their order of preference. Multiple ciphers must be comma-separated. @@ -2148,7 +2150,7 @@ .Xr xauth 1 program. The default is -.Pa /usr/X11R6/bin/xauth . +.Pa /usr/local/bin/xauth . .El .Sh PATTERNS A Index: sshd-session.c =================================================================== --- sshd-session.c +++ sshd-session.c @@ -1244,6 +1244,11 @@ cleanup_exit(255); } +#ifdef HAVE_LOGIN_CAP + /* Also caches remote hostname for sandboxed child. */ + auth_get_canonical_hostname(ssh, options.use_dns); +#endif + /* * The rest of the code depends on the fact that * ssh_remote_ipaddr() caches the remote ip, even if Index: sshd.8 =================================================================== --- sshd.8 +++ sshd.8 @@ -64,7 +64,7 @@ .Nm listens for connections from clients. It is normally started at boot from -.Pa /etc/rc . +.Pa /etc/rc.d/sshd . It forks a new daemon for each incoming connection. The forked daemons handle @@ -351,8 +351,9 @@ If the login is on a tty, records login time. .It Checks -.Pa /etc/nologin ; -if it exists, prints contents and quits +.Pa /etc/nologin and +.Pa /var/run/nologin ; +if one exists, it prints the contents and quits (unless root). .It Changes to run with normal user privileges. @@ -921,6 +922,12 @@ This file should be writable only by the user, and need not be readable by anyone else. .Pp +.It Pa /etc/hosts.allow +.It Pa /etc/hosts.deny +Access controls that should be enforced by tcp-wrappers are defined here. +Further details are described in +.Xr hosts_access 5 . +.Pp .It Pa /etc/hosts.equiv This file is for host-based authentication (see .Xr ssh 1 ) . @@ -1023,6 +1030,7 @@ .Xr ssh-keygen 1 , .Xr ssh-keyscan 1 , .Xr chroot 2 , +.Xr hosts_access 5 , .Xr login.conf 5 , .Xr moduli 5 , .Xr sshd_config 5 , Index: sshd.c =================================================================== --- sshd.c +++ sshd.c @@ -28,6 +28,7 @@ #include #include +#include #include #ifdef HAVE_SYS_STAT_H # include @@ -69,6 +70,15 @@ #include #endif +#ifdef __FreeBSD__ +#include +#if defined(GSSAPI) && defined(HAVE_GSSAPI_GSSAPI_H) +#include +#elif defined(GSSAPI) && defined(HAVE_GSSAPI_H) +#include +#endif +#endif + #include "xmalloc.h" #include "ssh.h" #include "sshpty.h" @@ -91,6 +101,12 @@ #include "sk-api.h" #include "addr.h" #include "srclimit.h" +#include "blacklist_client.h" + +#ifdef LIBWRAP +#include +#include +#endif /* LIBWRAP */ /* Re-exec fds */ #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) @@ -618,7 +634,14 @@ static void usage(void) { - fprintf(stderr, "%s, %s\n", SSH_RELEASE, SSH_OPENSSL_VERSION); + if (options.version_addendum != NULL && + *options.version_addendum != '\0') + fprintf(stderr, "%s %s, %s\n", + SSH_RELEASE, + options.version_addendum, SSH_OPENSSL_VERSION); + else + fprintf(stderr, "%s, %s\n", + SSH_RELEASE, SSH_OPENSSL_VERSION); fprintf(stderr, "usage: sshd [-46DdeGiqTtV] [-C connection_spec] [-c host_cert_file]\n" " [-E log_file] [-f config_file] [-g login_grace_time]\n" @@ -854,6 +877,11 @@ socklen_t fromlen; u_char rnd[256]; sigset_t nsigset, osigset; +#ifdef LIBWRAP + struct request_info req; + + request_init(&req, RQ_DAEMON, __progname, 0); +#endif /* pipes connected to unauthenticated child sshd processes */ child_alloc(); @@ -1003,6 +1031,42 @@ usleep(100 * 1000); continue; } +#ifdef LIBWRAP + /* Check whether logins are denied from this host. */ + request_set(&req, RQ_FILE, *newsock, + RQ_CLIENT_NAME, "", RQ_CLIENT_ADDR, "", 0); + sock_host(&req); + if (!hosts_access(&req)) { + const struct linger l = { .l_onoff = 1, + .l_linger = 0 }; + + (void )setsockopt(*newsock, SOL_SOCKET, + SO_LINGER, &l, sizeof(l)); + (void )close(*newsock); + /* + * Mimic message from libwrap's refuse() as + * precisely as we can afford. The authentic + * message prints the IP address and the + * hostname it resolves to in parentheses. If + * the IP address cannot be resolved to a + * hostname, the IP address will be repeated + * in parentheses. As name resolution in the + * main server loop could stall, and logging + * resolved names adds little or no value to + * incident investigation, this implementation + * only repeats the IP address in parentheses. + * This should resemble librwap's refuse() + * closely enough not to break auditing + * software like sshguard or custom scripts. + */ + syslog(LOG_WARNING, + "refused connect from %s (%s)", + eval_hostaddr(req.client), + eval_hostaddr(req.client)); + debug("Connection refused by tcp wrapper"); + continue; + } +#endif /* LIBWRAP */ if (unset_nonblock(*newsock) == -1) { close(*newsock); continue; @@ -1687,6 +1751,10 @@ /* Reinitialize the log (because of the fork above). */ log_init(__progname, options.log_level, options.log_facility, log_stderr); + /* Avoid killing the process in high-pressure swapping environments. */ + if (!inetd_flag && madvise(NULL, 0, MADV_PROTECT) != 0) + debug("madvise(): %.200s", strerror(errno)); + /* * Chdir to the root directory so that the current disk can be * unmounted if desired. @@ -1779,6 +1847,32 @@ execv(rexec_argv[0], rexec_argv); fatal("rexec of %s failed: %s", rexec_argv[0], strerror(errno)); +#ifdef __FreeBSD__ + /* + * Initialize the resolver. This may not happen automatically + * before privsep chroot(). + */ + if ((_res.options & RES_INIT) == 0) { + debug("res_init()"); + res_init(); + } +#ifdef GSSAPI + /* + * Force GSS-API to parse its configuration and load any + * mechanism plugins. + */ + { + gss_OID_set mechs; + OM_uint32 minor_status; + gss_indicate_mechs(&minor_status, &mechs); + gss_release_oid_set(&minor_status, &mechs); + } +#endif +#endif + + + + BLACKLIST_INIT(); } /* server specific fatal cleanup */ Index: sshd_config =================================================================== --- sshd_config +++ sshd_config @@ -10,6 +10,9 @@ # possible, but leave them commented. Uncommented options override the # default value. +# Note that some of FreeBSD's defaults differ from OpenBSD's, and +# FreeBSD has a few additional options. + #Port 22 #AddressFamily any #ListenAddress 0.0.0.0 @@ -29,7 +32,7 @@ # Authentication: #LoginGraceTime 2m -#PermitRootLogin prohibit-password +#PermitRootLogin no #StrictModes yes #MaxAuthTries 6 #MaxSessions 10 @@ -53,11 +56,12 @@ # Don't read the user's ~/.rhosts and ~/.shosts files #IgnoreRhosts yes -# To disable tunneled clear text passwords, change to no here! -#PasswordAuthentication yes +# Change to yes to enable built-in password authentication. +# Note that passwords may also be accepted via KbdInteractiveAuthentication. +#PasswordAuthentication no #PermitEmptyPasswords no -# Change to no to disable s/key passwords +# Change to no to disable PAM authentication #KbdInteractiveAuthentication yes # Kerberos options @@ -70,7 +74,7 @@ #GSSAPIAuthentication no #GSSAPICleanupCredentials yes -# Set this to 'yes' to enable PAM authentication, account processing, +# Set this to 'no' to disable PAM authentication, account processing, # and session processing. If this is enabled, PAM authentication will # be allowed through the KbdInteractiveAuthentication and # PasswordAuthentication. Depending on your PAM configuration, @@ -79,7 +83,7 @@ # If you just want the PAM account and session checks to run without # PAM authentication, then enable this but set PasswordAuthentication # and KbdInteractiveAuthentication to 'no'. -#UsePAM no +#UsePAM yes #AllowAgentForwarding yes #AllowTcpForwarding yes @@ -95,12 +99,13 @@ #Compression delayed #ClientAliveInterval 0 #ClientAliveCountMax 3 -#UseDNS no +#UseDNS yes #PidFile /var/run/sshd.pid #MaxStartups 10:30:100 #PermitTunnel no #ChrootDirectory none -#VersionAddendum none +#UseBlacklist no +#VersionAddendum FreeBSD-20240806 # no default banner path #Banner none Index: sshd_config.5 =================================================================== --- sshd_config.5 +++ sshd_config.5 @@ -881,7 +881,7 @@ The system-wide .Pa /etc/hosts.equiv and -.Pa /etc/shosts.equiv +.Pa /etc/ssh/shosts.equiv are still used regardless of this setting. .Pp Accepted values are @@ -1383,8 +1383,12 @@ .Cm sshd . .It Cm PasswordAuthentication Specifies whether password authentication is allowed. +Note that passwords may also be accepted via +.Cm KbdInteractiveAuthentication . +See also +.Cm UsePAM . The default is -.Cm sshd . +.Cm no . .It Cm PermitEmptyPasswords When password authentication is allowed, it specifies whether the server allows login to accounts with empty password strings. @@ -1477,7 +1481,14 @@ or .Cm no . The default is -.Cm prohibit-password . +.Cm no . +Note that if +.Cm ChallengeResponseAuthentication +and +.Cm UsePAM +are both +.Cm yes , +this setting may be overridden by the PAM policy. .Pp If this option is set to .Cm prohibit-password @@ -1972,6 +1983,20 @@ is to never expire connections for having no open channels. This option may be useful in conjunction with .Cm ChannelTimeout . +.It Cm UseBlacklist +Specifies whether +.Xr sshd 8 +attempts to send authentication success and failure messages +to the +.Xr blacklistd 8 +daemon. +The default is +.Cm no . +For forward compatibility with an upcoming +.Xr blacklistd +rename, the +.Cm UseBlocklist +alias can be used instead. .It Cm UseDNS Specifies whether .Xr sshd 8 @@ -1980,8 +2005,8 @@ very same IP address. .Pp If this option is set to -.Cm no -(the default) then only addresses and not host names may be used in +.Cm no , +then only addresses and not host names may be used in .Pa ~/.ssh/authorized_keys .Cm from and @@ -1989,6 +2014,8 @@ .Cm Match .Cm Host directives. +The default is +.Dq yes . .It Cm UsePAM Enables the Pluggable Authentication Module interface. If set to @@ -2012,12 +2039,15 @@ .Xr sshd 8 as a non-root user. The default is -.Cm no . +.Cm yes . .It Cm VersionAddendum Optionally specifies additional text to append to the SSH protocol banner sent by the server upon connection. The default is -.Cm none . +.Qq FreeBSD-20240806 . +The value +.Cm none +may be used to disable this. .It Cm X11DisplayOffset Specifies the first display number available for .Xr sshd 8 Ns 's @@ -2087,7 +2117,7 @@ .Cm none to not use one. The default is -.Pa /usr/X11R6/bin/xauth . +.Pa /usr/local/bin/xauth . .El .Sh TIME FORMATS .Xr sshd 8 Index: umac128.c =================================================================== --- umac128.c +++ umac128.c @@ -1,5 +1,12 @@ /* $OpenBSD: umac128.c,v 1.2 2018/02/08 04:12:32 dtucker Exp $ */ +/* undo ssh_namespace.h munging */ +#undef umac_new +#undef umac_update +#undef umac_final +#undef umac_delete +#undef umac_ctx + #define UMAC_OUTPUT_LEN 16 #define umac_new umac128_new #define umac_update umac128_update Index: version.h =================================================================== --- version.h +++ version.h @@ -4,3 +4,5 @@ #define SSH_PORTABLE "p1" #define SSH_RELEASE SSH_VERSION SSH_PORTABLE + +#define SSH_VERSION_FREEBSD "FreeBSD-20240806"