Makefile.in | 6 +- auth-options.c | 11 ++- auth-options.h | 2 +- auth-pam.c | 41 +++--- auth-rh-rsa.c | 4 + auth-rsa.c | 18 ++- auth.c | 30 ++++- auth.h | 2 + auth1.c | 12 +- auth2-hostbased.c | 4 + auth2-pubkey.c | 62 +++++++++- auth2.c | 8 +- blacklist.c | 267 +++++++++++++++++++++++++++++++++++++++ blacklist.h | 37 ++++++ channels.c | 6 + clientloop.c | 2 +- config.h.in | 3 + configure.ac | 19 +++- entropy.c | 8 +- log.c | 35 +++++- log.h | 1 + loginrec.c | 56 ++++++++ loginrec.h | 4 + monitor.c | 2 +- monitor_mm.c | 1 + mux.c | 16 ++- myproposal.h | 7 +- openbsd-compat/Makefile.in | 2 +- openbsd-compat/getpeerproc.c | 74 +++++++++++ openbsd-compat/openbsd-compat.h | 2 + openbsd-compat/xmmap.c | 4 +- pathnames.h | 4 + progressmeter.c | 2 +- readconf.c | 8 +- scard-opensc.c | 2 +- schnorr.c | 8 +- scp.c | 4 +- servconf.c | 76 ++++++++++-- servconf.h | 3 + sftp-server.0 | 4 +- sftp-server.8 | 4 +- sftp-server.c | 4 + sftp.c | 2 +- ssh-agent.1 | 11 ++- ssh-agent.c | 146 +++++++++++++++------ ssh-keygen.1 | 2 +- ssh-keygen.c | 4 +- ssh-keyscan.c | 4 +- ssh.0 | 24 ++-- ssh.1 | 11 +- ssh.c | 2 +- ssh_config | 10 +- ssh_config.0 | 14 +- ssh_config.5 | 16 ++-- sshconnect.c | 5 +- sshconnect1.c | 2 +- sshd.0 | 4 +- sshd.8 | 1 - sshd.c | 28 ++++- sshd_config | 29 +++-- sshd_config.0 | 30 +++-- sshd_config.5 | 81 ++++++++++-- 62 files changed, 1057 insertions(+), 234 deletions(-) diff --git a/Makefile.in b/Makefile.in index c5ecd5b..d5e2593 100644 --- a/Makefile.in +++ b/Makefile.in @@ -62,7 +62,7 @@ INSTALL_SSH_RAND_HELPER=@INSTALL_SSH_RAND_HELPER@ TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT) -LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \ +LIBSSH_OBJS=acss.o authfd.o authfile.o blacklist.o bufaux.o bufbn.o buffer.o \ canohost.o channels.o cipher.o cipher-acss.o cipher-aes.o \ cipher-bf1.o cipher-ctr.o cipher-3des1.o cleanup.o \ compat.o compress.o crc32.o deattack.o fatal.o hostfile.o \ @@ -284,9 +284,9 @@ install-files: scard-install $(INSTALL) -m 644 sftp-server.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 -rm -f $(DESTDIR)$(bindir)/slogin - ln -s ./ssh$(EXEEXT) $(DESTDIR)$(bindir)/slogin + ln -s ssh$(git-EXEEXT) $(DESTDIR)$(bindir)/slogin -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1 - ln -s ./ssh.1 $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1 + ln -s ssh.1 $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1 install-sysconf: if [ ! -d $(DESTDIR)$(sysconfdir) ]; then \ diff --git a/auth-options.c b/auth-options.c index ab085c2..baf5318 100644 --- a/auth-options.c +++ b/auth-options.c @@ -84,7 +84,7 @@ auth_clear_options(void) * side effect: sets key option flags */ int -auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) +auth_parse_options(struct passwd *pw, char *opts, const char *file, u_long linenum) { const char *cp; int i; @@ -98,6 +98,7 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) while (*opts && *opts != ' ' && *opts != '\t') { cp = "no-port-forwarding"; if (strncasecmp(opts, cp, strlen(cp)) == 0) { + debug("Port forwarding disabled."); auth_debug_add("Port forwarding disabled."); no_port_forwarding_flag = 1; opts += strlen(cp); @@ -105,6 +106,7 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) } cp = "no-agent-forwarding"; if (strncasecmp(opts, cp, strlen(cp)) == 0) { + debug("Agent forwarding disabled."); auth_debug_add("Agent forwarding disabled."); no_agent_forwarding_flag = 1; opts += strlen(cp); @@ -112,6 +114,7 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) } cp = "no-X11-forwarding"; if (strncasecmp(opts, cp, strlen(cp)) == 0) { + debug("X11 forwarding disabled."); auth_debug_add("X11 forwarding disabled."); no_x11_forwarding_flag = 1; opts += strlen(cp); @@ -119,6 +122,7 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) } cp = "no-pty"; if (strncasecmp(opts, cp, strlen(cp)) == 0) { + debug("Pty allocation disabled."); auth_debug_add("Pty allocation disabled."); no_pty_flag = 1; opts += strlen(cp); @@ -126,6 +130,7 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) } cp = "no-user-rc"; if (strncasecmp(opts, cp, strlen(cp)) == 0) { + debug("User rc file execution disabled."); auth_debug_add("User rc file execution disabled."); no_user_rc = 1; opts += strlen(cp); @@ -156,6 +161,7 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) goto bad_option; } forced_command[i] = '\0'; + debug("Forced command: %.900s", forced_command); auth_debug_add("Forced command: %.900s", forced_command); opts++; goto next_option; @@ -188,8 +194,8 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) goto bad_option; } s[i] = '\0'; - auth_debug_add("Adding to environment: %.900s", s); debug("Adding to environment: %.900s", s); + auth_debug_add("Adding to environment: %.900s", s); opts++; new_envstring = xmalloc(sizeof(struct envstring)); new_envstring->s = s; @@ -337,6 +343,7 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) forced_tun_device = -1; goto bad_option; } + debug("Forced tun device: %d", forced_tun_device); auth_debug_add("Forced tun device: %d", forced_tun_device); opts++; goto next_option; diff --git a/auth-options.h b/auth-options.h index 14488f7..f7495af 100644 --- a/auth-options.h +++ b/auth-options.h @@ -31,7 +31,7 @@ extern char *forced_command; extern struct envstring *custom_environment; extern int forced_tun_device; -int auth_parse_options(struct passwd *, char *, char *, u_long); +int auth_parse_options(struct passwd *, char *, const char *, u_long); void auth_clear_options(void); #endif diff --git a/auth-pam.c b/auth-pam.c index 675006e..37845a5 100644 --- a/auth-pam.c +++ b/auth-pam.c @@ -65,6 +65,8 @@ #include #endif +#include + /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */ #ifdef PAM_SUN_CODEBASE # define sshpam_const /* Solaris, HP-UX, AIX */ @@ -230,7 +232,7 @@ static int sshpam_cred_established = 0; static int sshpam_account_status = -1; static char **sshpam_env = NULL; static Authctxt *sshpam_authctxt = NULL; -static const char *sshpam_password = NULL; +static pam_userpass_t sshpam_userpass; static char badpw[] = "\b\n\r\177INCORRECT"; /* Some PAM implementations don't implement this */ @@ -602,11 +604,11 @@ sshpam_cleanup(void) return; debug("PAM: cleanup"); pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv); - if (sshpam_session_open) { + if (getpid() == sshpam_session_open) { debug("PAM: closing session"); pam_close_session(sshpam_handle, PAM_SILENT); - sshpam_session_open = 0; } + sshpam_session_open = 0; if (sshpam_cred_established) { debug("PAM: deleting credentials"); pam_setcred(sshpam_handle, PAM_DELETE_CRED); @@ -794,8 +796,9 @@ sshpam_query(void *ctx, char **name, char **info, return (0); } error("PAM: %s for %s%.100s from %.100s", msg, - sshpam_authctxt->valid ? "" : "illegal user ", - sshpam_authctxt->user, + !sshpam_authctxt->valid ? "UNKNOWN USER" : + (sshpam_authctxt->pw->pw_uid == 0 ? "ROOT USER " : ""), + sshpam_authctxt->valid ? sshpam_authctxt->user : "", get_remote_name_or_ip(utmp_len, options.use_dns)); /* FALLTHROUGH */ default: @@ -1047,7 +1050,7 @@ do_pam_session(void) pam_strerror(sshpam_handle, sshpam_err)); sshpam_err = pam_open_session(sshpam_handle, 0); if (sshpam_err == PAM_SUCCESS) - sshpam_session_open = 1; + sshpam_session_open = getpid(); else { sshpam_session_open = 0; disable_forwarding(); @@ -1132,18 +1135,15 @@ sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg, if (n <= 0 || n > PAM_MAX_NUM_MSG) return (PAM_CONV_ERR); + i = pam_userpass_conv(n, msg, resp, data); + if (i != PAM_CONV_ERR) + return i; + if ((reply = calloc(n, sizeof(*reply))) == NULL) return (PAM_CONV_ERR); for (i = 0; i < n; ++i) { switch (PAM_MSG_MEMBER(msg, i, msg_style)) { - case PAM_PROMPT_ECHO_OFF: - if (sshpam_password == NULL) - goto fail; - if ((reply[i].resp = strdup(sshpam_password)) == NULL) - goto fail; - reply[i].resp_retcode = PAM_SUCCESS; - break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: len = strlen(PAM_MSG_MEMBER(msg, i, msg)); @@ -1172,7 +1172,7 @@ sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg, return (PAM_CONV_ERR); } -static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL }; +static struct pam_conv passwd_conv = { sshpam_passwd_conv, &sshpam_userpass }; /* * Attempt password authentication via PAM @@ -1187,9 +1187,6 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password) fatal("PAM: %s called when PAM disabled or failed to " "initialise.", __func__); - sshpam_password = password; - sshpam_authctxt = authctxt; - /* * If the user logging in is invalid, or is root but is not permitted * by PermitRootLogin, use an invalid password to prevent leaking @@ -1197,7 +1194,11 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password) */ if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && options.permit_root_login != PERMIT_YES)) - sshpam_password = badpw; + password = badpw; + + sshpam_userpass.user = authctxt->valid ? authctxt->user : "UNKNOWN USER"; + sshpam_userpass.pass = password; + sshpam_authctxt = authctxt; sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, (const void *)&passwd_conv); @@ -1206,14 +1207,14 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password) pam_strerror(sshpam_handle, sshpam_err)); sshpam_err = pam_authenticate(sshpam_handle, flags); - sshpam_password = NULL; + sshpam_userpass.user = sshpam_userpass.pass = NULL; if (sshpam_err == PAM_SUCCESS && authctxt->valid) { debug("PAM: password authentication accepted for %.100s", authctxt->user); return 1; } else { debug("PAM: password authentication failed for %.100s: %s", - authctxt->valid ? authctxt->user : "an illegal user", + authctxt->valid ? authctxt->user : "UNKNOWN USER", pam_strerror(sshpam_handle, sshpam_err)); return 0; } diff --git a/auth-rh-rsa.c b/auth-rh-rsa.c index eca7502..94e6f23 100644 --- a/auth-rh-rsa.c +++ b/auth-rh-rsa.c @@ -34,6 +34,7 @@ #include "ssh-gss.h" #endif #include "monitor_wrap.h" +#include "blacklist.h" /* import */ extern ServerOptions options; @@ -48,6 +49,9 @@ auth_rhosts_rsa_key_allowed(struct passwd *pw, char *cuser, char *chost, if (!auth_rhosts(pw, cuser)) return 0; + if (blacklisted_key(client_host_key, 0)) + return 0; + host_status = check_key_in_hostfiles(pw, client_host_key, chost, _PATH_SSH_SYSTEM_HOSTFILE, options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE); diff --git a/auth-rsa.c b/auth-rsa.c index bf54620..830a107 100644 --- a/auth-rsa.c +++ b/auth-rsa.c @@ -47,6 +47,7 @@ #include "monitor_wrap.h" #include "ssh.h" #include "misc.h" +#include "blacklist.h" /* import */ extern ServerOptions options; @@ -179,13 +180,19 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) temporarily_use_uid(pw); /* The authorized keys. */ - file = authorized_keys_file(pw); - debug("trying public RSA key file %s", file); + file = authorized_keys_system_file(pw); + debug("trying public RSA key system file %s", file); f = auth_openkeyfile(file, pw, options.strict_modes); if (!f) { xfree(file); - restore_uid(); - return (0); + file = authorized_keys_file(pw); + debug("trying public RSA key file %s", file); + f = auth_openkeyfile(file, pw, options.strict_modes); + if (!f) { + xfree(file); + restore_uid(); + return (0); + } } /* Flag indicating whether the key is allowed. */ @@ -246,6 +253,9 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) "actual %d vs. announced %d.", file, linenum, BN_num_bits(key->rsa->n), bits); + if (blacklisted_key(key, 0)) + continue; + /* We have found the desired key. */ /* * If our options do not allow this key to be used, diff --git a/auth.c b/auth.c index 3585daa..b0d2dbe 100644 --- a/auth.c +++ b/auth.c @@ -268,8 +268,9 @@ auth_log(Authctxt *authctxt, int authenticated, char *method, char *info) authlog("%s %s for %s%.100s from %.200s port %d%s", authmsg, method, - authctxt->valid ? "" : "invalid user ", - authctxt->user, + !authctxt->valid ? "UNKNOWN USER" : + (authctxt->pw->pw_uid == 0 ? "ROOT USER " : ""), + authctxt->valid ? authctxt->user : "", get_remote_ipaddr(), get_remote_port(), info); @@ -287,6 +288,12 @@ auth_log(Authctxt *authctxt, int authenticated, char *method, char *info) get_canonical_hostname(options.use_dns), "ssh", &loginmsg); # endif #endif +#if HAVE_LINUX_AUDIT + if (authenticated == 0 && !authctxt->postponed) { + linux_audit_record_event(-1, authctxt->user, NULL, + get_remote_ipaddr(), "sshd", 0); + } +#endif #ifdef SSH_AUDIT_EVENTS if (authenticated == 0 && !authctxt->postponed) audit_event(audit_classify_auth(method)); @@ -360,6 +367,18 @@ authorized_keys_file2(struct passwd *pw) return expand_authorized_keys(options.authorized_keys_file2, pw); } +char * +authorized_keys_system_file(struct passwd *pw) +{ + return expand_authorized_keys(options.authorized_keys_system_file, pw); +} + +char * +authorized_keys_system_file2(struct passwd *pw) +{ + return expand_authorized_keys(options.authorized_keys_system_file2, pw); +} + /* return ok if key exists in sysfile or userfile */ HostStatus check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, @@ -527,12 +546,15 @@ getpwnamallow(const char *user) pw = getpwnam(user); if (pw == NULL) { - logit("Invalid user %.100s from %.100s", - user, get_remote_ipaddr()); + logit("UNKNOWN USER from %.100s", get_remote_ipaddr()); #ifdef CUSTOM_FAILED_LOGIN record_failed_login(user, get_canonical_hostname(options.use_dns), "ssh"); #endif +#ifdef HAVE_LINUX_AUDIT + linux_audit_record_event(-1, user, NULL, get_remote_ipaddr(), + "sshd", 0); +#endif #ifdef SSH_AUDIT_EVENTS audit_event(SSH_INVALID_USER); #endif /* SSH_AUDIT_EVENTS */ diff --git a/auth.h b/auth.h index 3a70f44..362209d 100644 --- a/auth.h +++ b/auth.h @@ -169,6 +169,8 @@ void abandon_challenge_response(Authctxt *); char *authorized_keys_file(struct passwd *); char *authorized_keys_file2(struct passwd *); +char *authorized_keys_system_file(struct passwd *); +char *authorized_keys_system_file2(struct passwd *); FILE *auth_openkeyfile(const char *, struct passwd *, int); diff --git a/auth1.c b/auth1.c index 1801661..bef4472 100644 --- a/auth1.c +++ b/auth1.c @@ -241,7 +241,9 @@ do_authloop(Authctxt *authctxt) const struct AuthMethod1 *meth; debug("Attempting authentication for %s%.100s.", - authctxt->valid ? "" : "invalid user ", authctxt->user); + !authctxt->valid ? "UNKNOWN USER" : + (authctxt->pw->pw_uid == 0 ? "ROOT USER " : ""), + authctxt->valid ? authctxt->user : ""); /* If the user has no password, accept authentication immediately. */ if (options.password_authentication && @@ -308,8 +310,7 @@ do_authloop(Authctxt *authctxt) } #endif if (!authctxt->valid && authenticated) - fatal("INTERNAL ERROR: authenticated invalid user %s", - authctxt->user); + fatal("INTERNAL ERROR: authenticated UNKNOWN USER"); #ifdef _UNICOS if (authenticated && cray_access_denied(authctxt->user)) { @@ -402,11 +403,12 @@ do_authentication(Authctxt *authctxt) if ((authctxt->pw = PRIVSEP(getpwnamallow(user))) != NULL) authctxt->valid = 1; else { - debug("do_authentication: invalid user %s", user); + debug("do_authentication: UNKNOWN USER"); authctxt->pw = fakepw(); + authctxt->valid = 0; } - setproctitle("%s%s", authctxt->valid ? user : "unknown", + setproctitle("%s%s", authctxt->valid ? user : "UNKNOWN USER", use_privsep ? " [net]" : ""); #ifdef USE_PAM diff --git a/auth2-hostbased.c b/auth2-hostbased.c index 041051c..434bd64 100644 --- a/auth2-hostbased.c +++ b/auth2-hostbased.c @@ -47,6 +47,7 @@ #endif #include "monitor_wrap.h" #include "pathnames.h" +#include "blacklist.h" /* import */ extern ServerOptions options; @@ -145,6 +146,9 @@ hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, HostStatus host_status; int len; + if (blacklisted_key(key, 0)) + return 0; + resolvedname = get_canonical_hostname(options.use_dns); ipaddr = get_remote_ipaddr(); diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 2886f12..b4cd0de 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -54,6 +54,7 @@ #endif #include "monitor_wrap.h" #include "misc.h" +#include "blacklist.h" /* import */ extern ServerOptions options; @@ -248,8 +249,8 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) } /* check whether given key is in .ssh/authorized_keys* */ -int -user_key_allowed(struct passwd *pw, Key *key) +static int +check_user_key_allowed(struct passwd *pw, Key *key) { int success; char *file; @@ -267,6 +268,63 @@ user_key_allowed(struct passwd *pw, Key *key) return success; } +/* check whether given key is in /etc/openssh/authorized_keys* */ +static int +check_system_key_allowed(struct passwd *pw, Key *key, int *found) +{ + int success = 0; + char *file; + struct stat st; + + *found = 0; + file = authorized_keys_system_file(pw); + debug("checking public key system file %s", file); + if (stat(file, &st) == 0) { + *found = 1; + success = user_key_allowed2(pw, key, file); + xfree(file); + if (success) + return success; + } else + xfree(file); + + /* try suffix "2" for backward compat, too */ + file = authorized_keys_system_file2(pw); + debug("checking public key system file2 %s", file); + if (stat(file, &st) == 0) { + *found = 1; + success = user_key_allowed2(pw, key, file); + xfree(file); + return success; + } + xfree(file); + + return success; +} + +/* + * check whether given key is in /etc/openssh/authorized_keys* + * if not found, check whether given key is in .ssh/authorized_keys* + */ +int +user_key_allowed(struct passwd *pw, Key *key) +{ + int found, success; + + /* no user given */ + if (!pw) + return 0; + + if (blacklisted_key(key, 0)) + return 0; + + success = check_system_key_allowed(pw, key, &found); + if (!found) + success = check_user_key_allowed(pw, key); + + return success; +} + Authmethod method_pubkey = { "publickey", userauth_pubkey, diff --git a/auth2.c b/auth2.c index 5d54685..197c489 100644 --- a/auth2.c +++ b/auth2.c @@ -238,8 +238,9 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) authctxt->valid = 1; debug2("input_userauth_request: setting up authctxt for %s", user); } else { - logit("input_userauth_request: invalid user %s", user); + logit("input_userauth_request: UNKNOWN USER"); authctxt->pw = fakepw(); + authctxt->valid = 0; #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_INVALID_USER)); #endif @@ -248,7 +249,7 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) if (options.use_pam) PRIVSEP(start_pam(authctxt)); #endif - setproctitle("%s%s", authctxt->valid ? user : "unknown", + setproctitle("%s%s", authctxt->valid ? user : "UNKNOWN USER", use_privsep ? " [net]" : ""); authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; @@ -294,8 +295,7 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method) char *methods; if (!authctxt->valid && authenticated) - fatal("INTERNAL ERROR: authenticated invalid user %s", - authctxt->user); + fatal("INTERNAL ERROR: authenticated UNKNOWN USER"); /* Special handling for root */ if (authenticated && authctxt->pw->pw_uid == 0 && diff --git a/blacklist.c b/blacklist.c new file mode 100644 index 0000000..89eff83 --- /dev/null +++ b/blacklist.c @@ -0,0 +1,267 @@ +/* + * Support for RSA/DSA key blacklisting based on partial fingerprints, + * developed under Openwall Project for Owl - http://www.openwall.com/Owl/ + * + * Copyright (c) 2008 Dmitry V. Levin + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * The blacklist encoding was designed by Solar Designer and Dmitry V. Levin. + * No intellectual property rights to the encoding scheme are claimed. + * + * This effort was supported by CivicActions - http://www.civicactions.com + * + * The file size to encode 294,903 of 48-bit fingerprints is just 1.3 MB, + * which corresponds to less than 4.5 bytes per fingerprint. + */ + +#include "includes.h" +#include +#include +#include +#include + +#include "atomicio.h" +#include "blacklist.h" +#include "canohost.h" +#include "log.h" +#include "pathnames.h" +#include "servconf.h" +#include "xmalloc.h" + +extern ServerOptions options; + +typedef struct +{ + /* format version identifier */ + char version[8]; + /* index size, in bits */ + uint8_t index_size; + /* offset size, in bits */ + uint8_t offset_size; + /* record size, in bits */ + uint8_t record_bits; + /* number of records */ + uint8_t records[3]; + /* offset shift */ + uint8_t shift[2]; + +} __attribute__((packed)) blacklist_header; + +static unsigned +c2u(uint8_t c) +{ + return (c >= 'a') ? (c - 'a' + 10) : (c - '0'); +} + +static blacklist_error_t +validate_blacklist(const char *fname, int fd, unsigned *bytes, + unsigned *records, unsigned *shift) +{ + unsigned expected; + struct stat st; + blacklist_header header; + + if (fstat(fd, &st)) { + error("fstat for blacklist file %s failed: %m", fname); + return BLACKLIST_ERROR_ACCESS; + } + + if (atomicio(read, fd, &header, sizeof(header)) != sizeof(header)) { + error("read blacklist file %s header failed: %m", fname); + return BLACKLIST_ERROR_ACCESS; + } + + if (memcmp(header.version, "SSH-FP", 6)) { + error("blacklist file %s has unrecognized format", fname); + return BLACKLIST_ERROR_FORMAT; + } + + if (header.index_size != 16 || header.offset_size != 16 || + memcmp(header.version, "SSH-FP00", 8)) { + error("blacklist file %s has unsupported format", fname); + return BLACKLIST_ERROR_VERSION; + } + + *bytes = (header.record_bits >> 3) - 2; + *records = + (((header.records[0] << 8) + + header.records[1]) << 8) + header.records[2]; + *shift = (header.shift[0] << 8) + header.shift[1]; + + expected = sizeof(header) + 0x20000 + (*records) * (*bytes); + if (st.st_size != expected) { + error("blacklist file %s size mismatch: " + "expected size %u, found size %lu", + fname, expected, (unsigned long) st.st_size); + return BLACKLIST_ERROR_ACCESS; + } + + return BLACKLIST_ERROR_NONE; +} + +static int +expected_offset(uint16_t index, uint16_t shift, unsigned records) +{ + return ((index * (long long) records) >> 16) - shift; +} + +static int +xlseek(const char *fname, int fd, unsigned seek) +{ + if (lseek(fd, seek, SEEK_SET) != seek) { + error("lseek for blacklist file %s failed: %m", fname); + return BLACKLIST_ERROR_ACCESS; + } + return BLACKLIST_ERROR_NONE; +} + +static blacklist_error_t +check(const char *fname, int fd, const char *s) +{ + unsigned bytes, records, shift; + unsigned num, i, j; + int off_start, off_end; + blacklist_error_t rc; + uint16_t index; + /* max number of bytes stored in record_bits, minus two bytes used for index */ + uint8_t buf[(0xff >> 3) - 2]; + + if ((rc = validate_blacklist(fname, fd, &bytes, &records, &shift))) + return rc; + + index = (((((c2u(s[0]) << 4) | c2u(s[1])) << 4) | + c2u(s[2])) << 4) | c2u(s[3]); + if (xlseek(fname, fd, sizeof(blacklist_header) + index * 2)) + return BLACKLIST_ERROR_ACCESS; + + if (atomicio(read, fd, buf, 4) != 4) { + error("read blacklist file %s offsets failed: %m", fname); + return BLACKLIST_ERROR_ACCESS; + } + + off_start = (buf[0] << 8) + buf[1] + + expected_offset(index, shift, records); + if (off_start < 0 || (unsigned) off_start > records) { + error("blacklist file %s off_start overflow [%d] for index %#x", + fname, off_start, index); + return BLACKLIST_ERROR_ACCESS; + } + if (index < 0xffff) { + off_end = (buf[2] << 8) + buf[3] + + expected_offset(index + 1, shift, records); + if (off_end < off_start || (unsigned) off_end > records) { + error("blacklist file %s off_end overflow [%d] for index %#x", + fname, off_end, index); + return BLACKLIST_ERROR_ACCESS; + } + } else + off_end = records; + + if (xlseek(fname, fd, + sizeof(blacklist_header) + 0x20000 + off_start * bytes)) + return BLACKLIST_ERROR_ACCESS; + + num = off_end - off_start; + for (i = 0; i < num; ++i) { + if (atomicio(read, fd, buf, bytes) != bytes) { + error("read blacklist file %s fingerprints failed: %m", + fname); + return BLACKLIST_ERROR_ACCESS; + } + + for (j = 0; j < bytes; ++j) + if (((c2u(s[4 + j * 2]) << 4) | c2u(s[5 + j * 2])) != + buf[j]) + break; + if (j >= bytes) { + debug("blacklisted fingerprint: %s offset=%u, number=%u", + s, off_start, i); + return BLACKLIST_ERROR_ALL; + } + } + + debug("non-blacklisted fingerprint: %s offset=%u, number=%u", + s, off_start, num); + return BLACKLIST_ERROR_NONE; +} + +static blacklist_error_t +blacklisted_fingerprint(const char *hex) +{ + int fd = -1; + blacklist_error_t rc = BLACKLIST_ERROR_ACCESS; + const char *fname = _PATH_BLACKLIST; + char *s, *p; + + debug("Checking fingerprint %s using blacklist file %s", hex, fname); + + s = xstrdup(hex); + for (p = s; *hex; ++hex) + if (*hex != ':') + *p++ = *hex; + *p = '\0'; + + if (strlen(s) != 32 || strlen(s) != strspn(s, "0123456789abcdef")) { + error("%s: invalid fingerprint", s); + goto out; + } + + if ((fd = open(fname, O_RDONLY)) < 0) { + if (ENOENT == errno) { + rc = BLACKLIST_ERROR_MISSING; + verbose("open blacklist file %s failed: %m", fname); + } else + logit("open blacklist file %s failed: %m", fname); + goto out; + } + + rc = check(fname, fd, s); + +out: + close(fd); + xfree(s); + return rc; +} + +int +blacklisted_key(Key *key, int hostkey) +{ + int rc; + const char *text; + char *fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + + switch ((rc = blacklisted_fingerprint(fp))) { + case BLACKLIST_ERROR_NONE: + break; + case BLACKLIST_ERROR_ALL: + text = (options.ignore_blacklist_errors == rc) ? + "Permitted" : "Rejected"; + if (hostkey) + logit("%s blacklisted host key %s", text, fp); + else + logit("%s blacklisted public key %s from %.100s", + text, fp, get_remote_ipaddr()); + break; + default: + if (hostkey) + logit("Unable to check blacklist for host key %s", + fp); + else + logit("Unable to check blacklist for public key %s from %.100s", + fp, get_remote_ipaddr()); + } + + xfree(fp); + return (rc > options.ignore_blacklist_errors); +} diff --git a/blacklist.h b/blacklist.h new file mode 100644 index 0000000..853ede1 --- /dev/null +++ b/blacklist.h @@ -0,0 +1,37 @@ +/* + * Support for RSA/DSA key blacklisting based on partial fingerprints, + * developed under Openwall Project for Owl - http://www.openwall.com/Owl/ + * + * Copyright (c) 2008 Dmitry V. Levin + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef BLACKLIST_H_ +#define BLACKLIST_H_ + +#include "key.h" + +int blacklisted_key(Key *, int); + +typedef enum +{ + BLACKLIST_ERROR_NONE = 0, + BLACKLIST_ERROR_MISSING, + BLACKLIST_ERROR_VERSION, + BLACKLIST_ERROR_FORMAT, + BLACKLIST_ERROR_ACCESS, + BLACKLIST_ERROR_ALL +} blacklist_error_t; + +#endif /* BLACKLIST_H_ */ diff --git a/channels.c b/channels.c index e8b8aa0..f5d8fad 100644 --- a/channels.c +++ b/channels.c @@ -2708,6 +2708,8 @@ channel_request_remote_forwarding(const char *listen_host, u_short listen_port, packet_write_wait(); /* Assume that server accepts the request */ success = 1; + debug("Forward request from remote address %s:%u to local address %s:%u succeded.", + address_to_bind, listen_port, host_to_connect, port_to_connect); } else { packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); packet_put_int(listen_port); @@ -2721,8 +2723,12 @@ channel_request_remote_forwarding(const char *listen_host, u_short listen_port, switch (type) { case SSH_SMSG_SUCCESS: success = 1; + debug("Forward request from remote port %u to local address %s:%u succeded.", + listen_port, host_to_connect, port_to_connect); break; case SSH_SMSG_FAILURE: + debug("Forward request from remote port %u to local address %s:%u failed.", + listen_port, host_to_connect, port_to_connect); break; default: /* Unknown packet */ diff --git a/clientloop.c b/clientloop.c index 9a7dc0a..4ce655e 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1554,7 +1554,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", - obytes, ibytes, total_time); + (unsigned long long)obytes, (unsigned long long)ibytes, total_time); if (total_time > 0) verbose("Bytes per second: sent %.1f, received %.1f", obytes / total_time, ibytes / total_time); diff --git a/config.h.in b/config.h.in index fc195ba..4b7abcf 100644 --- a/config.h.in +++ b/config.h.in @@ -1415,6 +1415,9 @@ /* Define if you want SELinux support. */ #undef WITH_SELINUX +/* Define if you want Linux audit support. */ +#undef HAVE_LINUX_AUDIT + /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD diff --git a/configure.ac b/configure.ac index ea9f1bb..ed3aaab 100644 --- a/configure.ac +++ b/configure.ac @@ -3407,13 +3407,25 @@ AC_ARG_WITH(selinux, fi ] ) +# Check whether user wants Linux audit support +LINUX_AUDIT_MSG="no" +AC_ARG_WITH(libaudit, + [ --with-libaudit Enable Linux audit support], + [ if test "x$withval" != "xno" ; then + AC_DEFINE(HAVE_LINUX_AUDIT,1,[Define if you want Linux audit support.]) + LINUX_AUDIT_MSG="yes" + AC_CHECK_HEADERS(libaudit.h) + SSHDLIBS="$SSHDLIBS -laudit" + fi ] +) + # Check whether user wants Kerberos 5 support KRB5_MSG="no" AC_ARG_WITH(kerberos5, [ --with-kerberos5=PATH Enable Kerberos 5 support], [ if test "x$withval" != "xno" ; then if test "x$withval" = "xyes" ; then - KRB5ROOT="/usr/local" + KRB5ROOT="/usr" else KRB5ROOT=${withval} fi @@ -3437,8 +3449,8 @@ AC_ARG_WITH(kerberos5, AC_MSG_RESULT(no) k5confopts="" fi - K5CFLAGS="`$KRB5CONF --cflags $k5confopts`" - K5LIBS="`$KRB5CONF --libs $k5confopts`" + K5CFLAGS="`env -i $KRB5CONF --cflags $k5confopts`" + K5LIBS="`env -i $KRB5CONF --libs $k5confopts`" CPPFLAGS="$CPPFLAGS $K5CFLAGS" AC_MSG_CHECKING(whether we are using Heimdal) AC_TRY_COMPILE([ #include ], @@ -4226,6 +4238,7 @@ echo " PAM support: $PAM_MSG" echo " OSF SIA support: $SIA_MSG" echo " KerberosV support: $KRB5_MSG" echo " SELinux support: $SELINUX_MSG" +echo " Linux audit support: $LINUX_AUDIT_MSG" echo " Smartcard support: $SCARD_MSG" echo " S/KEY support: $SKEY_MSG" echo " TCP Wrappers support: $TCPW_MSG" diff --git a/entropy.c b/entropy.c index 8b70539..24e5208 100644 --- a/entropy.c +++ b/entropy.c @@ -153,11 +153,11 @@ init_rng(void) { /* * OpenSSL version numbers: MNNFFPPS: major minor fix patch status - * We match major, minor, fix and status (not patch) + * We match major, minor and fix (but not patch and status) */ - if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & ~0xff0L) - fatal("OpenSSL version mismatch. Built against %lx, you " - "have %lx", OPENSSL_VERSION_NUMBER, SSLeay()); + if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & ~0xfffL) + fatal("OpenSSL version mismatch. Built against %lx, you have %lx", + (unsigned long) OPENSSL_VERSION_NUMBER, SSLeay()); #ifndef OPENSSL_PRNG_ONLY original_uid = getuid(); diff --git a/log.c b/log.c index 4a8239b..85a5746 100644 --- a/log.c +++ b/log.c @@ -54,8 +54,13 @@ static LogLevel log_level = SYSLOG_LEVEL_INFO; static int log_on_stderr = 1; +#ifdef LOG_AUTHPRIV +static int log_facility = LOG_AUTHPRIV; +#else static int log_facility = LOG_AUTH; +#endif static char *argv0; +static int log_keep = 0; extern char *__progname; @@ -324,6 +329,16 @@ log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr) #endif } +void +log_init_chroot(void) +{ + if (log_on_stderr) + return; + closelog(); + openlog(argv0 ?: __progname, LOG_PID|LOG_NDELAY, log_facility); + log_keep = 1; +} + #define MSGBUFSIZ 1024 void @@ -345,11 +360,15 @@ do_log(LogLevel level, const char *fmt, va_list args) case SYSLOG_LEVEL_FATAL: if (!log_on_stderr) txt = "fatal"; + else + txt = __progname; pri = LOG_CRIT; break; case SYSLOG_LEVEL_ERROR: if (!log_on_stderr) txt = "error"; + else + txt = __progname; pri = LOG_ERR; break; case SYSLOG_LEVEL_INFO: @@ -387,15 +406,19 @@ do_log(LogLevel level, const char *fmt, va_list args) snprintf(msgbuf, sizeof msgbuf, "%s\r\n", fmtbuf); write(STDERR_FILENO, msgbuf, strlen(msgbuf)); } else { + if (log_keep) { + syslog(pri, "%.500s", fmtbuf); + } else { #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) - openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata); - syslog_r(pri, &sdata, "%.500s", fmtbuf); - closelog_r(&sdata); + openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata); + syslog_r(pri, &sdata, "%.500s", fmtbuf); + closelog_r(&sdata); #else - openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility); - syslog(pri, "%.500s", fmtbuf); - closelog(); + openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility); + syslog(pri, "%.500s", fmtbuf); + closelog(); #endif + } } errno = saved_errno; } diff --git a/log.h b/log.h index 6505827..3bfd962 100644 --- a/log.h +++ b/log.h @@ -47,6 +47,7 @@ typedef enum { } LogLevel; void log_init(char *, LogLevel, SyslogFacility, int); +void log_init_chroot(void); SyslogFacility log_facility_number(char *); const char * log_facility_name(SyslogFacility); diff --git a/loginrec.c b/loginrec.c index f4af067..833026e 100644 --- a/loginrec.c +++ b/loginrec.c @@ -176,6 +176,10 @@ #include "auth.h" #include "buffer.h" +#ifdef HAVE_LINUX_AUDIT +# include +#endif + #ifdef HAVE_UTIL_H # include #endif @@ -202,6 +206,9 @@ int utmp_write_entry(struct logininfo *li); int utmpx_write_entry(struct logininfo *li); int wtmp_write_entry(struct logininfo *li); int wtmpx_write_entry(struct logininfo *li); +#ifdef HAVE_LINUX_AUDIT +int linux_audit_write_entry(struct logininfo *li); +#endif int lastlog_write_entry(struct logininfo *li); int syslogin_write_entry(struct logininfo *li); @@ -440,6 +447,10 @@ login_write(struct logininfo *li) /* set the timestamp */ login_set_current_time(li); +#ifdef HAVE_LINUX_AUDIT + if (linux_audit_write_entry(li) == 0) + fatal("linux_audit_write_entry failed: %s", strerror(errno)); +#endif #ifdef USE_LOGIN syslogin_write_entry(li); #endif @@ -1394,6 +1405,51 @@ wtmpx_get_entry(struct logininfo *li) } #endif /* USE_WTMPX */ +#ifdef HAVE_LINUX_AUDIT +int +linux_audit_record_event(int uid, const char *username, + const char *hostname, const char *ip, const char *ttyn, int success) +{ + char buf[64]; + int audit_fd, rc; + + audit_fd = audit_open(); + if (audit_fd < 0) { + if (errno == EINVAL || errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT) + return 1; /* No audit support in kernel */ + else + return 0; /* Must prevent login */ + } + if (username == NULL) + snprintf(buf, sizeof(buf), "uid=%d", uid); + else + snprintf(buf, sizeof(buf), "acct=%s", username); + rc = audit_log_user_message(audit_fd, AUDIT_USER_LOGIN, + buf, hostname, ip, ttyn, success); + close(audit_fd); + if (rc >= 0) + return 1; + else + return 0; +} + +int +linux_audit_write_entry(struct logininfo *li) +{ + switch(li->type) { + case LTYPE_LOGIN: + return (linux_audit_record_event(li->uid, NULL, li->hostname, + NULL, li->line, 1)); + case LTYPE_LOGOUT: + return (1); /* We only care about logins */ + default: + logit("%s: invalid type field", __func__); + return (0); + } +} +#endif /* HAVE_LINUX_AUDIT */ + /** ** Low-level libutil login() functions **/ diff --git a/loginrec.h b/loginrec.h index 859e1a6..56bc2c7 100644 --- a/loginrec.h +++ b/loginrec.h @@ -127,5 +127,9 @@ char *line_stripname(char *dst, const char *src, int dstsize); char *line_abbrevname(char *dst, const char *src, int dstsize); void record_failed_login(const char *, const char *, const char *); +#ifdef HAVE_LINUX_AUDIT +int linux_audit_record_event(int uid, const char *username, + const char *hostname, const char *ip, const char *ttyn, int success); +#endif /* HAVE_LINUX_AUDIT */ #endif /* _HAVE_LOGINREC_H_ */ diff --git a/monitor.c b/monitor.c index ace25c4..4487ea4 100644 --- a/monitor.c +++ b/monitor.c @@ -643,7 +643,7 @@ mm_answer_pwnamallow(int sock, Buffer *m) pwent = getpwnamallow(username); authctxt->user = xstrdup(username); - setproctitle("%s [priv]", pwent ? username : "unknown"); + setproctitle("%s [priv]", pwent ? username : "UNKNOWN USER"); xfree(username); buffer_clear(m); diff --git a/monitor_mm.c b/monitor_mm.c index faf9f3d..1048011 100644 --- a/monitor_mm.c +++ b/monitor_mm.c @@ -139,6 +139,7 @@ mm_destroy(struct mm_master *mm) mm_freelist(mm->mmalloc, &mm->rb_allocated); #ifdef HAVE_MMAP + memset(mm->address, 0xd0, mm->size); if (munmap(mm->address, mm->size) == -1) fatal("munmap(%p, %lu): %s", mm->address, (u_long)mm->size, strerror(errno)); diff --git a/mux.c b/mux.c index 79f8376..0a1ae08 100644 --- a/mux.c +++ b/mux.c @@ -280,16 +280,24 @@ muxserver_accept_control(void) switch (mux_command) { case SSHMUX_COMMAND_OPEN: if (options.control_master == SSHCTL_MASTER_ASK || - options.control_master == SSHCTL_MASTER_AUTO_ASK) + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + char *proc = getpeerproc(client_fd); allowed = ask_permission("Allow shared connection " - "to %s? ", host); + "to %s?\nRequest from \"%.80s\".", + host, (proc ? proc : "")); + free(proc); + } /* continue below */ break; case SSHMUX_COMMAND_TERMINATE: if (options.control_master == SSHCTL_MASTER_ASK || - options.control_master == SSHCTL_MASTER_AUTO_ASK) + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + char *proc = getpeerproc(client_fd); allowed = ask_permission("Terminate shared connection " - "to %s? ", host); + "to %s?\nRequest from \"%.80s\".", + host, (proc ? proc : "")); + free(proc); + } if (allowed) start_close = 1; /* FALLTHROUGH */ diff --git a/myproposal.h b/myproposal.h index 7bca3bc..7f60ae6 100644 --- a/myproposal.h +++ b/myproposal.h @@ -43,10 +43,11 @@ #define KEX_DEFAULT_PK_ALG "ssh-rsa,ssh-dss" #define KEX_DEFAULT_ENCRYPT \ - "aes128-ctr,aes192-ctr,aes256-ctr," \ + "aes256-ctr,aes192-ctr,aes128-ctr," \ "arcfour256,arcfour128," \ - "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc," \ - "aes192-cbc,aes256-cbc,arcfour,rijndael-cbc@lysator.liu.se" + "blowfish-cbc,aes256-cbc,aes192-cbc," \ + "aes128-cbc,3des-cbc,cast128-cbc," \ + "arcfour,rijndael-cbc@lysator.liu.se" #define KEX_DEFAULT_MAC \ "hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160," \ "hmac-ripemd160@openssh.com," \ diff --git a/openbsd-compat/Makefile.in b/openbsd-compat/Makefile.in index a60e5a6..5bd699a 100644 --- a/openbsd-compat/Makefile.in +++ b/openbsd-compat/Makefile.in @@ -18,7 +18,7 @@ LDFLAGS=-L. @LDFLAGS@ OPENBSD=base64.o basename.o bindresvport.o daemon.o dirname.o fmt_scaled.o getcwd.o getgrouplist.o getopt.o getrrsetbyname.o glob.o inet_aton.o inet_ntoa.o inet_ntop.o mktemp.o readpassphrase.o realpath.o rresvport.o setenv.o setproctitle.o sha2.o sigact.o strlcat.o strlcpy.o strmode.o strsep.o strtonum.o strtoll.o strtoul.o vis.o -COMPAT=bsd-arc4random.o bsd-asprintf.o bsd-closefrom.o bsd-cray.o bsd-cygwin_util.o bsd-getpeereid.o bsd-misc.o bsd-nextstep.o bsd-openpty.o bsd-poll.o bsd-snprintf.o bsd-statvfs.o bsd-waitpid.o fake-rfc2553.o openssl-compat.o xmmap.o xcrypt.o +COMPAT=bsd-arc4random.o bsd-asprintf.o bsd-closefrom.o bsd-cray.o bsd-cygwin_util.o bsd-getpeereid.o bsd-misc.o bsd-nextstep.o bsd-openpty.o bsd-poll.o bsd-snprintf.o bsd-statvfs.o bsd-waitpid.o fake-rfc2553.o getpeerproc.o openssl-compat.o xmmap.o xcrypt.o PORTS=port-aix.o port-irix.o port-linux.o port-solaris.o port-tun.o port-uw.o diff --git a/openbsd-compat/getpeerproc.c b/openbsd-compat/getpeerproc.c new file mode 100644 index 0000000..8b268b9 --- /dev/null +++ b/openbsd-compat/getpeerproc.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2002,2004 Damien Miller + * Copyright (c) 2007 Dmitry V. Levin + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include + +#include "xmalloc.h" + +static pid_t +getpeerpid(int s) +{ +#if defined(SO_PEERCRED) + struct ucred cred; + socklen_t len = sizeof(cred); + + if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cred, &len) < 0) + return -1; + + return cred.pid; +#else + return 0; +#endif /* defined(SO_PEERCRED) */ +} + +char * +getpeerproc(int s) +{ + pid_t pid; + int len, fd, i; + char path[PATH_MAX]; + char line[1024]; + + if (!(pid = getpeerpid(s))) + return NULL; + + len = snprintf(path, sizeof(path), "/proc/%u/cmdline", (unsigned)pid); + if (len < 0 || (size_t)len > sizeof(path) || + (fd = open(path, O_RDONLY)) < 0) + return NULL; + + if ((len = read(fd, line, sizeof(line) - 1)) > 0) + { + for (i = 0; i < len; ++i) + if (isspace(line[i]) || !isprint(line[i])) + line[i] = ' '; + if (line[len - 1] == ' ') + line[len - 1] = '\0'; + else + line[len] = '\0'; + } + + close(fd); + + return (len > 0) ? xstrdup(line) : NULL; +} diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h index 50c6d99..072ac11 100644 --- a/openbsd-compat/openbsd-compat.h +++ b/openbsd-compat/openbsd-compat.h @@ -200,6 +200,8 @@ int vasprintf(char **, const char *, va_list); int vsnprintf(char *, size_t, const char *, va_list); #endif +char *getpeerproc(int); + void *xmmap(size_t size); char *xcrypt(const char *password, const char *salt); char *shadow_pw(struct passwd *pw); diff --git a/openbsd-compat/xmmap.c b/openbsd-compat/xmmap.c index 04c6bab..e88fd23 100644 --- a/openbsd-compat/xmmap.c +++ b/openbsd-compat/xmmap.c @@ -70,7 +70,9 @@ xmmap(size_t size) if (tmpfd == -1) fatal("mkstemp(\"%s\"): %s", MM_SWAP_TEMPLATE, strerror(errno)); - unlink(tmpname); + if (unlink(tmpname)) + fatal("unlink(\"%s\"): %s", + tmpname, strerror(errno)); if (ftruncate(tmpfd, size) != 0) fatal("%s: ftruncate: %s", __func__, strerror(errno)); address = mmap(NULL, size, PROT_WRITE|PROT_READ, MAP_SHARED, diff --git a/pathnames.h b/pathnames.h index 80c5d9c..429ad83 100644 --- a/pathnames.h +++ b/pathnames.h @@ -43,6 +43,8 @@ /* Backwards compatibility */ #define _PATH_DH_PRIMES SSHDIR "/primes" +#define _PATH_BLACKLIST SSHDIR "/blacklist" + #ifndef _PATH_SSH_PROGRAM #define _PATH_SSH_PROGRAM "/usr/bin/ssh" #endif @@ -92,9 +94,11 @@ * may need to be world-readable. (This file is read by the daemon which is * running as root.) */ +#define _PATH_SSH_SYSTEM_PERMITTED_KEYS SSHDIR "/authorized_keys/%u" #define _PATH_SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" /* backward compat for protocol v2 */ +#define _PATH_SSH_SYSTEM_PERMITTED_KEYS2 SSHDIR "/authorized_keys2/%u" #define _PATH_SSH_USER_PERMITTED_KEYS2 ".ssh/authorized_keys2" /* diff --git a/progressmeter.c b/progressmeter.c index 0f95222..fe0b521 100644 --- a/progressmeter.c +++ b/progressmeter.c @@ -44,7 +44,7 @@ #define MAX_WINSIZE 512 #define PADDING 1 /* padding between the progress indicators */ #define UPDATE_INTERVAL 1 /* update the progress meter every second */ -#define STALL_TIME 5 /* we're stalled after this many seconds */ +#define STALL_TIME 60 /* we're stalled after this many seconds */ /* determines whether we can output to the terminal */ static int can_output(void); diff --git a/readconf.c b/readconf.c index 0bf5d7c..276acab 100644 --- a/readconf.c +++ b/readconf.c @@ -97,8 +97,8 @@ ForwardX11 no PasswordAuthentication yes RSAAuthentication yes - RhostsRSAAuthentication yes - StrictHostKeyChecking yes + RhostsRSAAuthentication no + StrictHostKeyChecking ask TcpKeepAlive no IdentityFile ~/.ssh/identity Port 22 @@ -1102,7 +1102,7 @@ fill_default_options(Options * options) if (options->pubkey_authentication == -1) options->pubkey_authentication = 1; if (options->challenge_response_authentication == -1) - options->challenge_response_authentication = 1; + options->challenge_response_authentication = 0; if (options->gss_authentication == -1) options->gss_authentication = 0; if (options->gss_deleg_creds == -1) @@ -1142,7 +1142,7 @@ fill_default_options(Options * options) /* options->macs, default set in myproposals.h */ /* options->hostkeyalgorithms, default set in myproposals.h */ if (options->protocol == SSH_PROTO_UNKNOWN) - options->protocol = SSH_PROTO_1|SSH_PROTO_2; + options->protocol = SSH_PROTO_2; if (options->num_identity_files == 0) { if (options->protocol & SSH_PROTO_1) { len = 2 + strlen(_PATH_SSH_CLIENT_IDENTITY) + 1; diff --git a/scard-opensc.c b/scard-opensc.c index 36dae05..e8f6d6b 100644 --- a/scard-opensc.c +++ b/scard-opensc.c @@ -96,7 +96,7 @@ sc_init(void) goto err; if (sc_reader_id >= ctx->reader_count) { r = SC_ERROR_NO_READERS_FOUND; - error("Illegal reader number %d (max %d)", sc_reader_id, + error("Invalid reader number %d (max %d)", sc_reader_id, ctx->reader_count -1); goto err; } diff --git a/schnorr.c b/schnorr.c index c17ff32..76e83e2 100644 --- a/schnorr.c +++ b/schnorr.c @@ -463,9 +463,9 @@ debug3_bn(const BIGNUM *n, const char *fmt, ...) char *out, *h; va_list args; - out = NULL; va_start(args, fmt); - vasprintf(&out, fmt, args); + if (vasprintf(&out, fmt, args) < 0) + out = NULL; va_end(args); if (out == NULL) fatal("%s: vasprintf failed", __func__); @@ -488,9 +488,9 @@ debug3_buf(const u_char *buf, u_int len, const char *fmt, ...) u_int i, j; va_list args; - out = NULL; va_start(args, fmt); - vasprintf(&out, fmt, args); + if (vasprintf(&out, fmt, args) < 0) + out = NULL; va_end(args); if (out == NULL) fatal("%s: vasprintf failed", __func__); diff --git a/scp.c b/scp.c index 3237478..5766d90 100644 --- a/scp.c +++ b/scp.c @@ -717,7 +717,7 @@ next: if (fd != -1) { if (i + (off_t)amt > stb.st_size) amt = stb.st_size - i; if (!haderr) { - if (atomicio(read, fd, bp->buf, amt) != amt) + if (atomicio(read, fd, bp->buf, amt) != (size_t) amt) haderr = errno; } /* Keep writing after error to retain sync */ @@ -726,7 +726,7 @@ next: if (fd != -1) { continue; } if (scpio(vwrite, remout, bp->buf, amt, - &statbytes) != amt) + &statbytes) != (size_t) amt) haderr = errno; } unset_nonblock(remout); diff --git a/servconf.c b/servconf.c index b51b86a..d84f7a5 100644 --- a/servconf.c +++ b/servconf.c @@ -41,6 +41,7 @@ #include "match.h" #include "channels.h" #include "groupaccess.h" +#include "blacklist.h" static void add_listen_addr(ServerOptions *, char *, int); static void add_one_listen_addr(ServerOptions *, char *, int); @@ -96,6 +97,7 @@ initialize_server_options(ServerOptions *options) options->password_authentication = -1; options->kbd_interactive_authentication = -1; options->challenge_response_authentication = -1; + options->ignore_blacklist_errors = -1; options->permit_empty_passwd = -1; options->permit_user_env = -1; options->use_login = -1; @@ -122,6 +124,8 @@ initialize_server_options(ServerOptions *options) options->client_alive_count_max = -1; options->authorized_keys_file = NULL; options->authorized_keys_file2 = NULL; + options->authorized_keys_system_file = NULL; + options->authorized_keys_system_file2 = NULL; options->num_accept_env = 0; options->permit_tun = -1; options->num_permitted_opens = -1; @@ -135,11 +139,11 @@ fill_default_server_options(ServerOptions *options) { /* Portable-specific options */ if (options->use_pam == -1) - options->use_pam = 0; + options->use_pam = 1; /* Standard Options */ if (options->protocol == SSH_PROTO_UNKNOWN) - options->protocol = SSH_PROTO_1|SSH_PROTO_2; + options->protocol = SSH_PROTO_2; if (options->num_host_key_files == 0) { /* fill default hostkeys for protocols */ if (options->protocol & SSH_PROTO_1) @@ -165,7 +169,7 @@ fill_default_server_options(ServerOptions *options) if (options->key_regeneration_time == -1) options->key_regeneration_time = 3600; if (options->permit_root_login == PERMIT_NOT_SET) - options->permit_root_login = PERMIT_YES; + options->permit_root_login = PERMIT_NO_PASSWD; if (options->ignore_rhosts == -1) options->ignore_rhosts = 1; if (options->ignore_user_known_hosts == -1) @@ -175,7 +179,7 @@ fill_default_server_options(ServerOptions *options) if (options->print_lastlog == -1) options->print_lastlog = 1; if (options->x11_forwarding == -1) - options->x11_forwarding = 0; + options->x11_forwarding = 1; if (options->x11_display_offset == -1) options->x11_display_offset = 10; if (options->x11_use_localhost == -1) @@ -217,7 +221,9 @@ fill_default_server_options(ServerOptions *options) if (options->kbd_interactive_authentication == -1) options->kbd_interactive_authentication = 0; if (options->challenge_response_authentication == -1) - options->challenge_response_authentication = 1; + options->challenge_response_authentication = 0; + if (options->ignore_blacklist_errors == -1) + options->ignore_blacklist_errors = BLACKLIST_ERROR_VERSION; if (options->permit_empty_passwd == -1) options->permit_empty_passwd = 0; if (options->permit_user_env == -1) @@ -233,11 +239,13 @@ fill_default_server_options(ServerOptions *options) if (options->gateway_ports == -1) options->gateway_ports = 0; if (options->max_startups == -1) - options->max_startups = 10; + options->max_startups = 20; if (options->max_startups_rate == -1) - options->max_startups_rate = 100; /* 100% */ + options->max_startups_rate = 30; /* 30% */ if (options->max_startups_begin == -1) - options->max_startups_begin = options->max_startups; + options->max_startups_begin = + (options->max_startups_rate >= 100) ? + options->max_startups : options->max_startups/2; if (options->max_authtries == -1) options->max_authtries = DEFAULT_AUTH_FAIL_MAX; if (options->max_sessions == -1) @@ -257,6 +265,15 @@ fill_default_server_options(ServerOptions *options) } if (options->authorized_keys_file == NULL) options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS; + if (options->authorized_keys_system_file2 == NULL) { + /* authorized_keys_system_file2 falls back to authorized_keys_system_file */ + if (options->authorized_keys_system_file != NULL) + options->authorized_keys_system_file2 = options->authorized_keys_system_file; + else + options->authorized_keys_system_file2 = _PATH_SSH_SYSTEM_PERMITTED_KEYS2; + } + if (options->authorized_keys_system_file == NULL) + options->authorized_keys_system_file = _PATH_SSH_SYSTEM_PERMITTED_KEYS; if (options->permit_tun == -1) options->permit_tun = SSH_TUNMODE_NO; if (options->zero_knowledge_password_authentication == -1) @@ -293,7 +310,7 @@ typedef enum { sListenAddress, sAddressFamily, sPrintMotd, sPrintLastLog, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost, - sStrictModes, sEmptyPasswd, sTCPKeepAlive, + sStrictModes, sIgnoreBlacklistErrors, sEmptyPasswd, sTCPKeepAlive, sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, @@ -302,6 +319,7 @@ typedef enum { sBanner, sUseDNS, sHostbasedAuthentication, sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, + sAuthorizedKeysSystemFile, sAuthorizedKeysSystemFile2, sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, sMatch, sPermitOpen, sForceCommand, sChrootDirectory, sUsePrivilegeSeparation, sAllowAgentForwarding, @@ -389,6 +407,7 @@ static struct { { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, { "strictmodes", sStrictModes, SSHCFG_GLOBAL }, + { "ignoreblacklisterrors", sIgnoreBlacklistErrors, SSHCFG_GLOBAL }, { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL }, { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL }, { "uselogin", sUseLogin, SSHCFG_GLOBAL }, @@ -417,6 +436,8 @@ static struct { { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL }, { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_GLOBAL }, { "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_GLOBAL }, + { "authorizedkeyssystemfile", sAuthorizedKeysFile, SSHCFG_GLOBAL }, + { "authorizedkeyssystemfile2", sAuthorizedKeysFile2, SSHCFG_GLOBAL}, { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL}, { "acceptenv", sAcceptEnv, SSHCFG_GLOBAL }, { "permittunnel", sPermitTunnel, SSHCFG_GLOBAL }, @@ -943,6 +964,32 @@ process_server_config_line(ServerOptions *options, char *line, intptr = &options->tcp_keep_alive; goto parse_flag; + case sIgnoreBlacklistErrors: + intptr = &options->ignore_blacklist_errors; + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: missing none/missing/version/format/access/all argument.", + filename, linenum); + value = 0; /* silence compiler */ + if (strcmp(arg, "none") == 0) + value = BLACKLIST_ERROR_NONE; + else if (strcmp(arg, "missing") == 0) + value = BLACKLIST_ERROR_MISSING; + else if (strcmp(arg, "version") == 0) + value = BLACKLIST_ERROR_VERSION; + else if (strcmp(arg, "format") == 0) + value = BLACKLIST_ERROR_FORMAT; + else if (strcmp(arg, "access") == 0) + value = BLACKLIST_ERROR_ACCESS; + else if (strcmp(arg, "all") == 0) + value = BLACKLIST_ERROR_ALL; + else + fatal("%s line %d: Bad none/missing/version/format/access/all argument: %s", + filename, linenum, arg); + if (*activep && *intptr == -1) + *intptr = value; + break; + case sEmptyPasswd: intptr = &options->permit_empty_passwd; goto parse_flag; @@ -1157,10 +1204,10 @@ process_server_config_line(ServerOptions *options, char *line, options->max_startups || options->max_startups_rate > 100 || options->max_startups_rate < 1) - fatal("%s line %d: Illegal MaxStartups spec.", + fatal("%s line %d: Invalid MaxStartups spec.", filename, linenum); } else if (n != 1) - fatal("%s line %d: Illegal MaxStartups spec.", + fatal("%s line %d: Invalid MaxStartups spec.", filename, linenum); else options->max_startups = options->max_startups_begin; @@ -1184,6 +1231,13 @@ process_server_config_line(ServerOptions *options, char *line, * * AuthorizedKeysFile /etc/ssh_keys/%u */ + case sAuthorizedKeysSystemFile: + case sAuthorizedKeysSystemFile2: + charptr = (opcode == sAuthorizedKeysSystemFile ) ? + &options->authorized_keys_system_file : + &options->authorized_keys_system_file2; + goto parse_filename; + case sAuthorizedKeysFile: case sAuthorizedKeysFile2: charptr = (opcode == sAuthorizedKeysFile) ? diff --git a/servconf.h b/servconf.h index b3ac7da..2db15cb 100644 --- a/servconf.h +++ b/servconf.h @@ -98,6 +98,7 @@ typedef struct { int challenge_response_authentication; int zero_knowledge_password_authentication; /* If true, permit jpake auth */ + int ignore_blacklist_errors; /* none/missing/version/format/access/all */ int permit_empty_passwd; /* If false, do not permit empty * passwords. */ int permit_user_env; /* If true, read ~/.ssh/environment */ @@ -139,6 +140,8 @@ typedef struct { * disconnect the session */ + char *authorized_keys_system_file; /* File containing system public keys */ + char *authorized_keys_system_file2; char *authorized_keys_file; /* File containing public keys */ char *authorized_keys_file2; diff --git a/sftp-server.0 b/sftp-server.0 index d47fc0c..3cc4486 100644 --- a/sftp-server.0 +++ b/sftp-server.0 @@ -20,8 +20,8 @@ DESCRIPTION -f log_facility Specifies the facility code that is used when logging messages from sftp-server. The possible values are: DAEMON, USER, AUTH, - LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. - The default is AUTH. + AUTHPRIV, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, + LOCAL7. The default is AUTHPRIV. -l log_level Specifies which messages will be logged by sftp-server. The pos- diff --git a/sftp-server.8 b/sftp-server.8 index 372b077..fd97135 100644 --- a/sftp-server.8 +++ b/sftp-server.8 @@ -57,9 +57,9 @@ Valid options are: .It Fl f Ar log_facility Specifies the facility code that is used when logging messages from .Nm . -The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, +The possible values are: DAEMON, USER, AUTH, AUTHPRIV, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. -The default is AUTH. +The default is AUTHPRIV. .It Fl l Ar log_level Specifies which messages will be logged by .Nm . diff --git a/sftp-server.c b/sftp-server.c index d984e60..2ecebf7 100644 --- a/sftp-server.c +++ b/sftp-server.c @@ -1332,7 +1332,11 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) fd_set *rset, *wset; int in, out, max, ch, skipargs = 0, log_stderr = 0; ssize_t len, olen, set_size; +#ifdef LOG_AUTHPRIV + SyslogFacility log_facility = SYSLOG_FACILITY_AUTHPRIV; +#else SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; +#endif char *cp, buf[4*4096]; extern char *optarg; diff --git a/sftp.c b/sftp.c index 66bd111..b7708c3 100644 --- a/sftp.c +++ b/sftp.c @@ -1235,7 +1235,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, int err_abort) { char *path1, *path2, *tmp; - int pflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i; + int pflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum = 0, i = 0; unsigned long n_arg = 0; Attrib a, *aa; char path_buf[MAXPATHLEN]; diff --git a/ssh-agent.1 b/ssh-agent.1 index 533cd6f..14e99ca 100644 --- a/ssh-agent.1 +++ b/ssh-agent.1 @@ -43,6 +43,7 @@ .Sh SYNOPSIS .Nm ssh-agent .Op Fl c Li | Fl s +.Op Fl u .Op Fl d .Op Fl a Ar bind_address .Op Fl t Ar life @@ -77,6 +78,8 @@ Generate C-shell commands on This is the default if .Ev SHELL looks like it's a csh style of shell. +.It Fl u +Run the agent in unique mode. .It Fl d Debug mode. When this option is specified @@ -178,14 +181,18 @@ environment variable holds the agent's process ID. The agent exits automatically when the command given on the command line terminates. .Sh FILES -.Bl -tag -width Ds +.Bl -tag -width Ds -compact .It Pa ~/.ssh/identity Contains the protocol version 1 RSA authentication identity of the user. +.Pp .It Pa ~/.ssh/id_dsa Contains the protocol version 2 DSA authentication identity of the user. +.Pp .It Pa ~/.ssh/id_rsa Contains the protocol version 2 RSA authentication identity of the user. -.It Pa /tmp/ssh-XXXXXXXXXX/agent.\*(Ltppid\*(Gt +.Pp +.It Pa /tmp/ssh-XXXXXXXXXX/agent.\*(Ltppid\*(Gt , +.It Pa ~/.ssh/agent Unix-domain sockets used to contain the connection to the authentication agent. These sockets should only be readable by the owner. diff --git a/ssh-agent.c b/ssh-agent.c index f77dea3..ba4e9de 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -191,16 +191,18 @@ lookup_identity(Key *key, int version) /* Check confirmation of keysign request */ static int -confirm_key(Identity *id) +confirm_key(Identity *id, char *proc) { char *p; int ret = -1; p = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); - if (ask_permission("Allow use of key %s?\nKey fingerprint %s.", - id->comment, p)) + if (ask_permission("Allow use of key %s?\nKey fingerprint %s.\n" + "Request from \"%.80s\".", + id->comment, p, (proc ? proc : ""))) ret = 0; xfree(p); + free(proc); return (ret); } @@ -268,7 +270,7 @@ process_authentication_challenge1(SocketEntry *e) goto failure; id = lookup_identity(key, 1); - if (id != NULL && (!id->confirm || confirm_key(id) == 0)) { + if (id != NULL && (!id->confirm || confirm_key(id, getpeerproc(e->fd)) == 0)) { Key *private = id->key; /* Decrypt the challenge using the private key. */ if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0) @@ -330,7 +332,7 @@ process_sign_request2(SocketEntry *e) key = key_from_blob(blob, blen); if (key != NULL) { Identity *id = lookup_identity(key, 2); - if (id != NULL && (!id->confirm || confirm_key(id) == 0)) + if (id != NULL && (!id->confirm || confirm_key(id, getpeerproc(e->fd)) == 0)) ok = key_sign(id->key, &signature, &slen, data, dlen); key_free(key); } @@ -933,13 +935,12 @@ after_select(fd_set *readset, fd_set *writeset) sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &slen); if (sock < 0) { - error("accept from AUTH_SOCKET: %s", - strerror(errno)); + error("accept from AUTH_SOCKET: %m"); break; } if (getpeereid(sock, &euid, &egid) < 0) { - error("getpeereid %d failed: %s", - sock, strerror(errno)); + error("getpeereid %d failed: %m", + sock); close(sock); break; } @@ -1028,6 +1029,26 @@ check_parent_exists(void) } } +static int +check_auth_socket(const char *authsocket) +{ + int sock, rc; + struct sockaddr_un sunaddr; + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + return sock; + + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); + + rc = connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)); + close(sock); + + return rc; +} + static void usage(void) { @@ -1037,6 +1058,7 @@ usage(void) fprintf(stderr, " -c Generate C-shell commands on stdout.\n"); fprintf(stderr, " -s Generate Bourne shell commands on stdout.\n"); fprintf(stderr, " -k Kill the current agent.\n"); + fprintf(stderr, " -u Unique mode.\n"); fprintf(stderr, " -d Debug mode.\n"); fprintf(stderr, " -a socket Bind agent socket to given name.\n"); fprintf(stderr, " -t life Default identity lifetime (seconds).\n"); @@ -1047,6 +1069,7 @@ int main(int ac, char **av) { int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0; + int u_flag = 0, already_running = 0; int sock, fd, ch, result, saved_errno; u_int nalloc; char *shell, *format, *pidstr, *agentsocket = NULL; @@ -1059,7 +1082,6 @@ main(int ac, char **av) extern int optind; extern char *optarg; pid_t pid; - char pidstrbuf[1 + 3 * sizeof pid]; struct timeval *tvp = NULL; size_t len; @@ -1081,7 +1103,7 @@ main(int ac, char **av) init_rng(); seed_rng(); - while ((ch = getopt(ac, av, "cdksa:t:")) != -1) { + while ((ch = getopt(ac, av, "cdksua:t:")) != -1) { switch (ch) { case 'c': if (s_flag) @@ -1096,6 +1118,9 @@ main(int ac, char **av) usage(); s_flag++; break; + case 'u': + u_flag++; + break; case 'd': if (d_flag) usage(); @@ -1117,7 +1142,10 @@ main(int ac, char **av) ac -= optind; av += optind; - if (ac > 0 && (c_flag || k_flag || s_flag || d_flag)) + if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || u_flag)) + usage(); + + if (u_flag && (k_flag || agentsocket)) usage(); if (ac == 0 && !c_flag && !s_flag) { @@ -1131,20 +1159,16 @@ main(int ac, char **av) pidstr = getenv(SSH_AGENTPID_ENV_NAME); if (pidstr == NULL) { - fprintf(stderr, "%s not set, cannot kill agent\n", - SSH_AGENTPID_ENV_NAME); - exit(1); + fatal("%s not set, cannot kill agent", + SSH_AGENTPID_ENV_NAME); } pid = (int)strtonum(pidstr, 2, INT_MAX, &errstr); if (errstr) { - fprintf(stderr, - "%s=\"%s\", which is not a good PID: %s\n", - SSH_AGENTPID_ENV_NAME, pidstr, errstr); - exit(1); + fatal( "%s=\"%s\", which is not a good PID: %s", + SSH_AGENTPID_ENV_NAME, pidstr, errstr); } if (kill(pid, SIGTERM) == -1) { - perror("kill"); - exit(1); + fatal("kill: %m"); } format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; printf(format, SSH_AUTHSOCKET_ENV_NAME); @@ -1154,15 +1178,22 @@ main(int ac, char **av) } parent_pid = getpid(); - if (agentsocket == NULL) { + if (u_flag) { + struct passwd *pw = getpwuid(geteuid()); + if (!pw) + fatal("getpwuid: %m"); + snprintf(socket_name, sizeof socket_name, + "%s/.ssh/agent", pw->pw_dir); + endpwent(); + } + else if (agentsocket == NULL) { /* Create private directory for agent socket */ strlcpy(socket_dir, "/tmp/ssh-XXXXXXXXXX", sizeof socket_dir); if (mkdtemp(socket_dir) == NULL) { - perror("mkdtemp: private socket dir"); - exit(1); + fatal("mkdtemp: private socket dir: %m"); } - snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir, - (long)parent_pid); + snprintf(socket_name, sizeof socket_name, + "%s/agent.%ld", socket_dir, (long)parent_pid); } else { /* Try to use specified agent socket */ socket_dir[0] = '\0'; @@ -1175,7 +1206,7 @@ main(int ac, char **av) */ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { - perror("socket"); + error("socket: %m"); *socket_name = '\0'; /* Don't unlink any existing file */ cleanup_exit(1); } @@ -1184,14 +1215,44 @@ main(int ac, char **av) strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); prev_mask = umask(0177); if (bind(sock, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) { - perror("bind"); - *socket_name = '\0'; /* Don't unlink any existing file */ - umask(prev_mask); - cleanup_exit(1); + int rc = -1; + + if (u_flag && (EADDRINUSE == errno)) { + if (check_auth_socket(socket_name) < 0) { + (void) unlink(socket_name); + rc = bind(sock, (struct sockaddr *) & sunaddr, + sizeof(sunaddr)); + } else { + already_running = 1; + rc = 0; + } + } + if (rc < 0) { + error("bind: %s: %m", socket_name); + *socket_name = '\0'; /* Don't unlink any existing file */ + umask(prev_mask); + cleanup_exit(1); + } } umask(prev_mask); + + if (already_running) { + close(sock); + if (ac == 0) { + format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; + printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, + SSH_AUTHSOCKET_ENV_NAME); + exit(0); + } + if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) < 0) { + fatal("setenv: %m"); + } + execvp(av[0], av); + fatal("execvp: %s: %m", av[0]); + } + if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { - perror("listen"); + error("listen: %m"); cleanup_exit(1); } @@ -1209,10 +1270,12 @@ main(int ac, char **av) } pid = fork(); if (pid == -1) { - perror("fork"); + error("fork: %m"); cleanup_exit(1); } if (pid != 0) { /* Parent - execute the given command. */ + char pidstrbuf[1 + 3 * sizeof pid]; + close(sock); snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid); if (ac == 0) { @@ -1226,22 +1289,23 @@ main(int ac, char **av) } if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 || setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) { - perror("setenv"); - exit(1); + fatal("setenv: %m"); } execvp(av[0], av); - perror(av[0]); - exit(1); + fatal("execvp: %s: %m", av[0]); } /* child */ log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0); if (setsid() == -1) { - error("setsid: %s", strerror(errno)); + error("setsid: %m"); cleanup_exit(1); } - (void)chdir("/"); + if (chdir("/") == -1) { + error("chdir: %m"); + cleanup_exit(1); + } if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { /* XXX might close listen socket */ (void)dup2(fd, STDIN_FILENO); @@ -1255,14 +1319,14 @@ main(int ac, char **av) /* deny core dumps, since memory contains unencrypted private keys */ rlim.rlim_cur = rlim.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &rlim) < 0) { - error("setrlimit RLIMIT_CORE: %s", strerror(errno)); + error("setrlimit RLIMIT_CORE: %m"); cleanup_exit(1); } #endif skip: new_socket(AUTH_SOCKET, sock); - if (ac > 0) + if (!u_flag && ac > 0) parent_alive_interval = 10; idtab_init(); if (!d_flag) diff --git a/ssh-keygen.1 b/ssh-keygen.1 index 3596cc1..f4e5a02 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 @@ -193,7 +193,7 @@ Show the bubblebabble digest of specified private or public key file. Specifies the number of bits in the key to create. For RSA keys, the minimum size is 768 bits and the default is 2048 bits. Generally, 2048 bits is considered sufficient. -DSA keys must be exactly 1024 bits as specified by FIPS 186-2. +DSA keys must be at least 1024 bits. .It Fl C Ar comment Provides a new comment. .It Fl c diff --git a/ssh-keygen.c b/ssh-keygen.c index da5db98..f6823ec 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1360,8 +1360,8 @@ main(int argc, char **argv) } if (bits == 0) bits = (type == KEY_DSA) ? DEFAULT_BITS_DSA : DEFAULT_BITS; - if (type == KEY_DSA && bits != 1024) - fatal("DSA keys must be 1024 bits"); + if (type == KEY_DSA && bits < 1024) + fatal("DSA keys must be at least 1024 bits"); if (!quiet) printf("Generating public/private %s key pair.\n", key_type_name); private = key_generate(type, bits); diff --git a/ssh-keyscan.c b/ssh-keyscan.c index 9a91be4..826eac8 100644 --- a/ssh-keyscan.c +++ b/ssh-keyscan.c @@ -814,11 +814,11 @@ main(int argc, char **argv) maxfd = fdlim_get(1); if (maxfd < 0) - fatal("%s: fdlim_get: bad value", __progname); + fatal("fdlim_get: bad value"); if (maxfd > MAXMAXFD) maxfd = MAXMAXFD; if (MAXCON <= 0) - fatal("%s: not enough file descriptors", __progname); + fatal("not enough file descriptors"); if (maxfd > fdlim_get(0)) fdlim_set(maxfd); fdcon = xcalloc(maxfd, sizeof(con)); diff --git a/ssh.0 b/ssh.0 index af73167..6149140 100644 --- a/ssh.0 +++ b/ssh.0 @@ -75,7 +75,7 @@ DESCRIPTION 3des. des is only supported in the ssh client for interoperabil- ity with legacy protocol 1 implementations that do not support the 3des cipher. Its use is strongly discouraged due to crypto- - graphic weaknesses. The default is ``3des''. + graphic weaknesses. The default is ``blowfish''. For protocol version 2, cipher_spec is a comma-separated list of ciphers listed in order of preference. See the Ciphers keyword @@ -373,15 +373,16 @@ DESCRIPTION error occurred. AUTHENTICATION - The OpenSSH SSH client supports SSH protocols 1 and 2. Protocol 2 is the - default, with ssh falling back to protocol 1 if it detects protocol 2 is - unsupported. These settings may be altered using the Protocol option in - ssh_config(5), or enforced using the -1 and -2 options (see above). Both - protocols support similar authentication methods, but protocol 2 is pre- - ferred since it provides additional mechanisms for confidentiality (the - traffic is encrypted using AES, 3DES, Blowfish, CAST128, or Arcfour) and - integrity (hmac-md5, hmac-sha1, umac-64, hmac-ripemd160). Protocol 1 - lacks a strong mechanism for ensuring the integrity of the connection. + The OpenSSH SSH client supports SSH protocols 1 and 2. Protocol 2 is + the default. ssh can be configured to fall back to protocol 1 if it + detects protocol 2 is unsupported. These settings may be altered using + the Protocol option in ssh_config(5), or enforced using the -1 and -2 + options (see above). Both protocols support similar authentication + methods, but protocol 2 is pre- ferred since it provides additional + mechanisms for confidentiality (the traffic is encrypted using AES, + 3DES, Blowfish, CAST128, or Arcfour) and integrity (hmac-md5, hmac-sha1, + umac-64, hmac-ripemd160). Protocol 1 lacks a strong mechanism for + ensuring the integrity of the connection. The methods available for authentication are: GSSAPI-based authentica- tion, host-based authentication, public key authentication, challenge-re- @@ -438,8 +439,7 @@ AUTHENTICATION arbitrary "challenge" text, and prompts for a response. Protocol 2 al- lows multiple challenges and responses; protocol 1 is restricted to just one challenge/response. Examples of challenge-response authentication - include BSD Authentication (see login.conf(5)) and PAM (some non-OpenBSD - systems). + include BSD Authentication and PAM (some non-OpenBSD systems). Finally, if other authentication methods fail, ssh prompts the user for a password. The password is sent to the remote host for checking; however, diff --git a/ssh.1 b/ssh.1 index 6c6271e..34dcabe 100644 --- a/ssh.1 +++ b/ssh.1 @@ -185,7 +185,7 @@ that do not support the cipher. Its use is strongly discouraged due to cryptographic weaknesses. The default is -.Dq 3des . +.Dq blowfish . .Pp For protocol version 2, .Ar cipher_spec @@ -666,9 +666,10 @@ exits with the exit status of the remote command or with 255 if an error occurred. .Sh AUTHENTICATION The OpenSSH SSH client supports SSH protocols 1 and 2. -Protocol 2 is the default, with +Protocol 2 is the default. .Nm -falling back to protocol 1 if it detects protocol 2 is unsupported. +can be configured to fall back to protocol 1 if it detects +protocol 2 is unsupported. These settings may be altered using the .Cm Protocol option in @@ -801,9 +802,7 @@ text, and prompts for a response. Protocol 2 allows multiple challenges and responses; protocol 1 is restricted to just one challenge/response. Examples of challenge-response authentication include -BSD Authentication (see -.Xr login.conf 5 ) -and PAM (some non-OpenBSD systems). +BSD Authentication and PAM (some non-OpenBSD systems). .Pp Finally, if other authentication methods fail, .Nm diff --git a/ssh.c b/ssh.c index adfe60e..7014f2e 100644 --- a/ssh.c +++ b/ssh.c @@ -260,7 +260,7 @@ main(int ac, char **av) * writable only by the owner, which is ok for all files for which we * don't set the modes explicitly. */ - umask(022); + umask(umask(077) | 022); /* * Initialize option structure to indicate that no values have been diff --git a/ssh_config b/ssh_config index f28d595..3fa636c 100644 --- a/ssh_config +++ b/ssh_config @@ -17,7 +17,11 @@ # list of available options, their meanings and defaults, please see the # ssh_config(5) man page. -# Host * +Host * + # Send locale environment variables + SendEnv LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE + SendEnv LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES LC_MONETARY + SendEnv LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE LC_TIME # ForwardAgent no # ForwardX11 no # RhostsRSAAuthentication no @@ -36,8 +40,8 @@ # IdentityFile ~/.ssh/id_dsa # Port 22 # Protocol 2,1 -# Cipher 3des -# Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc +# Cipher blowfish +# Ciphers aes256-ctr,arcfour256,blowfish-cbc,aes256-cbc # MACs hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160 # EscapeChar ~ # Tunnel no diff --git a/ssh_config.0 b/ssh_config.0 index 756fc6d..822b9cb 100644 --- a/ssh_config.0 +++ b/ssh_config.0 @@ -69,7 +69,7 @@ DESCRIPTION ChallengeResponseAuthentication Specifies whether to use challenge-response authentication. The argument to this keyword must be ``yes'' or ``no''. The default - is ``yes''. + is ``no''. CheckHostIP If this flag is set to ``yes'', ssh(1) will additionally check @@ -83,7 +83,7 @@ DESCRIPTION are supported. des is only supported in the ssh(1) client for interoperability with legacy protocol 1 implementations that do not support the 3des cipher. Its use is strongly discouraged due - to cryptographic weaknesses. The default is ``3des''. + to cryptographic weaknesses. The default is ``blowfish''. Ciphers Specifies the ciphers allowed for protocol version 2 in order of @@ -93,9 +93,9 @@ DESCRIPTION ``arcfour128'', ``arcfour256'', ``arcfour'', ``blowfish-cbc'', and ``cast128-cbc''. The default is: - aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128, - aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc, - aes256-cbc,arcfour + aes256-ctr,aes192-ctr,aes128-ctr,arcfour256,arcfour128, + blowfish-cbc,aes256-cbc,aes192-cbc,aes128-cbc,3des-cbc, + cast128-cbc,arcfour ClearAllForwardings Specifies that all local, remote, and dynamic port forwardings @@ -418,7 +418,7 @@ DESCRIPTION Protocol Specifies the protocol versions ssh(1) should support in order of preference. The possible values are `1' and `2'. Multiple ver- - sions must be comma-separated. The default is ``2,1''. This + sions must be comma-separated. The default is ``2''. Value ``2,1'' means that ssh tries version 2 and falls back to version 1 if version 2 is not available. @@ -620,7 +620,7 @@ DESCRIPTION XAuthLocation Specifies the full pathname of the xauth(1) program. The default - is /usr/X11R6/bin/xauth. + is /usr/bin/xauth. PATTERNS A pattern consists of zero or more non-whitespace characters, `*' (a diff --git a/ssh_config.5 b/ssh_config.5 index ea9a20b..de7eded 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -151,7 +151,7 @@ The argument to this keyword must be or .Dq no . The default is -.Dq yes . +.Dq no . .It Cm CheckHostIP If this flag is set to .Dq yes , @@ -183,7 +183,7 @@ that do not support the cipher. Its use is strongly discouraged due to cryptographic weaknesses. The default is -.Dq 3des . +.Dq blowfish . .It Cm Ciphers Specifies the ciphers allowed for protocol version 2 in order of preference. @@ -204,9 +204,9 @@ and .Dq cast128-cbc . The default is: .Bd -literal -offset 3n -aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128, -aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc, -aes256-cbc,arcfour +aes256-ctr,aes192-ctr,aes128-ctr,arcfour256,arcfour128, +blowfish-cbc,aes256-cbc,aes192-cbc,aes128-cbc,3des-cbc, +cast128-cbc,arcfour .Ed .It Cm ClearAllForwardings Specifies that all local, remote, and dynamic port forwardings @@ -731,8 +731,8 @@ and .Sq 2 . Multiple versions must be comma-separated. The default is -.Dq 2,1 . -This means that ssh +.Dq 2 . +Value "2,1" means that ssh tries version 2 and falls back to version 1 if version 2 is not available. .It Cm ProxyCommand @@ -1085,7 +1085,7 @@ Specifies the full pathname of the .Xr xauth 1 program. The default is -.Pa /usr/X11R6/bin/xauth . +.Pa /usr/bin/xauth . .El .Sh PATTERNS A diff --git a/sshconnect.c b/sshconnect.c index 3e57e85..48e1e0d 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -66,7 +66,6 @@ static int matching_host_key_dns = 0; /* import */ extern Options options; -extern char *__progname; extern uid_t original_real_uid; extern uid_t original_effective_uid; extern pid_t proxy_command_pid; @@ -338,7 +337,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%u", port); if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) - fatal("%s: Could not resolve hostname %.100s: %s", __progname, + fatal("Could not resolve hostname %.100s: %s", host, ssh_gai_strerror(gaierr)); for (attempt = 0; attempt < connection_attempts; attempt++) { @@ -389,7 +388,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, /* Return failure if we didn't get a successful connection. */ if (sock == -1) { - error("ssh: connect to host %s port %s: %s", + error("connect to host %s port %s: %s", host, strport, strerror(errno)); return (-1); } diff --git a/sshconnect1.c b/sshconnect1.c index fd07bbf..dd82f8d 100644 --- a/sshconnect1.c +++ b/sshconnect1.c @@ -483,7 +483,7 @@ ssh_kex(char *host, struct sockaddr *hostaddr) BIGNUM *key; Key *host_key, *server_key; int bits, rbits; - int ssh_cipher_default = SSH_CIPHER_3DES; + int ssh_cipher_default = SSH_CIPHER_BLOWFISH; u_char session_key[SSH_SESSION_KEY_LENGTH]; u_char cookie[8]; u_int supported_ciphers; diff --git a/sshd.0 b/sshd.0 index 6e37c9f..56dbfb6 100644 --- a/sshd.0 +++ b/sshd.0 @@ -556,8 +556,8 @@ FILES SEE ALSO scp(1), sftp(1), ssh(1), ssh-add(1), ssh-agent(1), ssh-keygen(1), - ssh-keyscan(1), chroot(2), hosts_access(5), login.conf(5), moduli(5), - sshd_config(5), inetd(8), sftp-server(8) + ssh-keyscan(1), chroot(2), hosts_access(5), moduli(5), sshd_config(5), + inetd(8), sftp-server(8) AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by diff --git a/sshd.8 b/sshd.8 index 111d491..7d01855 100644 --- a/sshd.8 +++ b/sshd.8 @@ -873,7 +873,6 @@ The content of this file is not sensitive; it can be world-readable. .Xr ssh-keyscan 1 , .Xr chroot 2 , .Xr hosts_access 5 , -.Xr login.conf 5 , .Xr moduli 5 , .Xr sshd_config 5 , .Xr inetd 8 , diff --git a/sshd.c b/sshd.c index 13a455d..2cf52ba 100644 --- a/sshd.c +++ b/sshd.c @@ -119,6 +119,7 @@ #include "monitor_wrap.h" #include "roaming.h" #include "version.h" +#include "blacklist.h" #ifdef LIBWRAP #include @@ -591,6 +592,10 @@ privsep_preauth_child(void) /* Demote the private keys to public keys. */ demote_sensitive_data(); + /* Prepare chrooted syslogging. */ + tzset(); + log_init_chroot(); + /* Change our root directory */ if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1) fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR, @@ -1103,15 +1108,18 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) } if (unset_nonblock(*newsock) == -1) { close(*newsock); + *newsock = -1; continue; } if (drop_connection(startups) == 1) { debug("drop connection #%d", startups); close(*newsock); + *newsock = -1; continue; } if (pipe(startup_p) == -1) { close(*newsock); + *newsock = -1; continue; } @@ -1120,6 +1128,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) error("reexec socketpair: %s", strerror(errno)); close(*newsock); + *newsock = -1; close(startup_p[0]); close(startup_p[1]); continue; @@ -1218,6 +1227,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) } close(*newsock); + *newsock = -1; /* * Ensure that our random state differs @@ -1400,7 +1410,7 @@ main(int ac, char **av) break; } } - if (rexeced_flag || inetd_flag) + if (rexeced_flag || inetd_flag || test_flag) rexec_flag = 0; if (!test_flag && (rexec_flag && (av[0] == NULL || *av[0] != '/'))) fatal("sshd re-exec requires execution with an absolute path"); @@ -1514,6 +1524,11 @@ main(int ac, char **av) sensitive_data.host_keys[i] = NULL; continue; } + if (blacklisted_key(key, 1)) { + sensitive_data.host_keys[i] = NULL; + key_free(key); + continue; + } switch (key->type) { case KEY_RSA1: sensitive_data.ssh1_host_key = key; @@ -1653,7 +1668,8 @@ main(int ac, char **av) /* Chdir to the root directory so that the current disk can be unmounted if desired. */ - chdir("/"); + if (chdir("/") == -1) + fatal("chdir(\"/\"): %s", strerror(errno)); /* ignore SIGPIPE */ signal(SIGPIPE, SIG_IGN); @@ -1872,6 +1888,11 @@ main(int ac, char **av) } authenticated: + +#ifdef HAVE_FCHOWN + fchown(newsock, authctxt->pw->pw_uid, authctxt->pw->pw_gid); +#endif + /* * Cancel the alarm we set to limit the time taken for * authentication. @@ -1922,7 +1943,8 @@ main(int ac, char **av) /* The connection has been terminated. */ packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); - verbose("Transferred: sent %llu, received %llu bytes", obytes, ibytes); + verbose("Transferred: sent %llu, received %llu bytes", + (unsigned long long)obytes, (unsigned long long)ibytes); verbose("Closing connection to %.500s port %d", remote_ip, remote_port); diff --git a/sshd_config b/sshd_config index 1b53a0e..b5c7011 100644 --- a/sshd_config +++ b/sshd_config @@ -11,15 +11,11 @@ # default value. #Port 22 +#Protocol 2 #AddressFamily any #ListenAddress 0.0.0.0 #ListenAddress :: -# Disable legacy (protocol version 1) support in the server for new -# installations. In future the default will change to require explicit -# activation of protocol 1 -Protocol 2 - # HostKey for protocol version 1 #HostKey /etc/ssh/ssh_host_key # HostKeys for protocol version 2 @@ -32,13 +28,13 @@ Protocol 2 # Logging # obsoletes QuietMode and FascistLogging -#SyslogFacility AUTH +#SyslogFacility AUTHPRIV #LogLevel INFO # Authentication: #LoginGraceTime 2m -#PermitRootLogin yes +#PermitRootLogin without-password #StrictModes yes #MaxAuthTries 6 #MaxSessions 10 @@ -61,8 +57,8 @@ Protocol 2 #PasswordAuthentication yes #PermitEmptyPasswords no -# Change to no to disable s/key passwords -#ChallengeResponseAuthentication yes +# Change to yes to enable s/key passwords +#ChallengeResponseAuthentication no # Kerberos options #KerberosAuthentication no @@ -83,12 +79,12 @@ Protocol 2 # If you just want the PAM account and session checks to run without # PAM authentication, then enable this but set PasswordAuthentication # and ChallengeResponseAuthentication to 'no'. -#UsePAM no +#UsePAM yes #AllowAgentForwarding yes #AllowTcpForwarding yes #GatewayPorts no -#X11Forwarding no +#X11Forwarding yes #X11DisplayOffset 10 #X11UseLocalhost yes #PrintMotd yes @@ -102,7 +98,7 @@ Protocol 2 #ClientAliveCountMax 3 #UseDNS yes #PidFile /var/run/sshd.pid -#MaxStartups 10 +#MaxStartups 10:30:20 #PermitTunnel no #ChrootDirectory none @@ -110,10 +106,15 @@ Protocol 2 #Banner none # override default of no subsystems -Subsystem sftp /usr/libexec/sftp-server +#Subsystem sftp /usr/libexec/sftp-server + +# Accept locale environment variables +AcceptEnv LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE +AcceptEnv LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES LC_MONETARY +AcceptEnv LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE LC_TIME # Example of overriding settings on a per-user basis #Match User anoncvs -# X11Forwarding no +# X11Forwarding yes # AllowTcpForwarding no # ForceCommand cvs server diff --git a/sshd_config.0 b/sshd_config.0 index 9e73c59..b8ee699 100644 --- a/sshd_config.0 +++ b/sshd_config.0 @@ -90,7 +90,7 @@ DESCRIPTION ChallengeResponseAuthentication Specifies whether challenge-response authentication is allowed (e.g. via PAM or though authentication styles supported in - login.conf(5)) The default is ``yes''. + login.conf(5)). The default is ``no''. ChrootDirectory Specifies a path to chroot(2) to after authentication. This @@ -109,7 +109,7 @@ DESCRIPTION ries to support the user's session. For an interactive session this requires at least a shell, typically sh(1), and basic /dev nodes such as null(4), zero(4), stdin(4), stdout(4), stderr(4), - arandom(4) and tty(4) devices. For file transfer sessions using + urandom(4) and tty(4) devices. For file transfer sessions using ``sftp'', no additional configuration of the environment is nec- essary if the in-process sftp server is used, though sessions which use logging do require /dev/log inside the chroot directory @@ -125,9 +125,9 @@ DESCRIPTION ``arcfour256'', ``arcfour'', ``blowfish-cbc'', and ``cast128-cbc''. The default is: - aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128, - aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc, - aes256-cbc,arcfour + aes256-ctr,aes192-ctr,aes128-ctr,arcfour256,arcfour128, + blowfish-cbc,aes256-cbc,aes192-cbc,aes128-cbc,3des-cbc, + cast128-cbc,arcfour ClientAliveCountMax Sets the number of client alive messages (see below) which may be @@ -362,7 +362,7 @@ DESCRIPTION Specifies the maximum number of concurrent unauthenticated con- nections to the SSH daemon. Additional connections will be dropped until authentication succeeds or the LoginGraceTime ex- - pires for a connection. The default is 10. + pires for a connection. Alternatively, random early drop can be enabled by specifying the three colon separated values ``start:rate:full'' (e.g. @@ -372,6 +372,8 @@ DESCRIPTION creases linearly and all connection attempts are refused if the number of unauthenticated connections reaches ``full'' (60). + The default is 10:30:20. + PasswordAuthentication Specifies whether password authentication is allowed. The de- fault is ``yes''. @@ -398,7 +400,7 @@ DESCRIPTION PermitRootLogin Specifies whether root can log in using ssh(1). The argument must be ``yes'', ``without-password'', ``forced-commands-only'', - or ``no''. The default is ``yes''. + or ``no''. The default is ``without-password''. If this option is set to ``without-password'', password authenti- cation is disabled for root. @@ -445,7 +447,7 @@ DESCRIPTION Protocol Specifies the protocol versions sshd(8) supports. The possible values are `1' and `2'. Multiple versions must be comma-separat- - ed. The default is ``2,1''. Note that the order of the protocol + ed. The default is ``2''. Note that the order of the protocol list does not indicate preference, because the client selects among multiple protocol versions offered by the server. Specify- ing ``2,1'' is identical to ``1,2''. @@ -494,9 +496,9 @@ DESCRIPTION SyslogFacility Gives the facility code that is used when logging messages from - sshd(8). The possible values are: DAEMON, USER, AUTH, LOCAL0, - LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. The de- - fault is AUTH. + sshd(8). The possible values are: DAEMON, USER, AUTH, AUTHPRIV, + LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. + The default is AUTHPRIV. TCPKeepAlive Specifies whether the system should send TCP keepalive messages @@ -538,7 +540,7 @@ DESCRIPTION either PasswordAuthentication or ChallengeResponseAuthentication. If UsePAM is enabled, you will not be able to run sshd(8) as a - non-root user. The default is ``no''. + non-root user. The default is ``yes''. UsePrivilegeSeparation Specifies whether sshd(8) separates privileges by creating an un- @@ -556,7 +558,7 @@ DESCRIPTION X11Forwarding Specifies whether X11 forwarding is permitted. The argument must - be ``yes'' or ``no''. The default is ``no''. + be ``yes'' or ``no''. The default is ``yes''. When X11 forwarding is enabled, there may be additional exposure to the server and to client displays if the sshd(8) proxy display @@ -590,7 +592,7 @@ DESCRIPTION XAuthLocation Specifies the full pathname of the xauth(1) program. The default - is /usr/X11R6/bin/xauth. + is /usr/bin/xauth. TIME FORMATS sshd(8) command-line arguments and configuration file options that speci- diff --git a/sshd_config.5 b/sshd_config.5 index 588aed5..0ac7bb0 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -167,6 +167,23 @@ is taken to be an absolute path or one relative to the user's home directory. The default is .Dq .ssh/authorized_keys . +.It Cm AuthorizedKeysSystemFile +Specifies the file that contains the public keys that can be used +for user authentication. +.Cm AuthorizedKeysSystemFile +may contain tokens of the form %T which are substituted during connection +set-up. The following tokens are defined: %% is replaced by a literal '%', +%h is replaced by the home directory of the user being authenticated and +%u is replaced by the username of that user. +After expansion, +.Cm AuthorizedKeysSystemFile +is taken to be an absolute path or one relative to the user's home +directory. +If that file exists, it will be tried before +.Cm AuthorizedKeysFile +file. +The default is +.Dq /etc/openssh/authorized_keys/%u . .It Cm Banner The contents of the specified file are sent to the remote user before authentication is allowed. @@ -178,9 +195,9 @@ By default, no banner is displayed. .It Cm ChallengeResponseAuthentication Specifies whether challenge-response authentication is allowed (e.g. via PAM or though authentication styles supported in -.Xr login.conf 5 ) +.Xr login.conf 5 ). The default is -.Dq yes . +.Dq no . .It Cm ChrootDirectory Specifies a path to .Xr chroot 2 @@ -210,7 +227,7 @@ nodes such as .Xr stdin 4 , .Xr stdout 4 , .Xr stderr 4 , -.Xr arandom 4 +.Xr urandom 4 and .Xr tty 4 devices. @@ -245,9 +262,9 @@ and .Dq cast128-cbc . The default is: .Bd -literal -offset 3n -aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128, -aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc, -aes256-cbc,arcfour +aes256-ctr,aes192-ctr,aes128-ctr,arcfour256,arcfour128, +blowfish-cbc,aes256-cbc,aes192-cbc,aes128-cbc,3des-cbc, +cast128-cbc,arcfour .Ed .It Cm ClientAliveCountMax Sets the number of client alive messages (see below) which may be @@ -608,12 +625,14 @@ Available keywords are .Cm HostbasedAuthentication , .Cm KbdInteractiveAuthentication , .Cm KerberosAuthentication , +.Cm Match , .Cm MaxAuthTries , .Cm MaxSessions , .Cm PasswordAuthentication , .Cm PermitEmptyPasswords , .Cm PermitOpen , .Cm PermitRootLogin , +.Cm PubkeyAuthentication , .Cm RhostsRSAAuthentication , .Cm RSAAuthentication , .Cm X11DisplayOffset , @@ -635,7 +654,6 @@ SSH daemon. Additional connections will be dropped until authentication succeeds or the .Cm LoginGraceTime expires for a connection. -The default is 10. .Pp Alternatively, random early drop can be enabled by specifying the three colon separated values @@ -653,10 +671,45 @@ The probability increases linearly and all connection attempts are refused if the number of unauthenticated connections reaches .Dq full (60). +.Pp +The default is 10:30:20. .It Cm PasswordAuthentication Specifies whether password authentication is allowed. The default is .Dq yes . +.It Cm IgnoreBlacklistErrors +Specifies whether +.Xr sshd 8 +should allow keys recorded in its blacklist of known-compromised keys. +If +.Dq all , +then attempts to authenticate with compromised keys will be logged +but accepted. +If +.Dq access , +then attempts to authenticate with compromised keys will be rejected, +but blacklist file access errors will be ignored. +If +.Dq format , +then attempts to authenticate with compromised keys will be rejected, but +blacklist file access errors due to missing blacklist file or blacklist +file unrecognized format will be ignored. +If +.Dq version , +then attempts to authenticate with compromised keys will be rejected, but +blacklist file access errors due to missing blacklist file or blacklist +file format version mismatch will be ignored. +If +.Dq missing , +then attempts to authenticate with compromised keys will be rejected, +but blacklist file access errors due to missing blacklist file will +be ignored. +If +.Dq none , +then attempts to authenticate with compromised keys, or in case of +any blacklist file access error, will be rejected. +The default is +.Dq version . .It Cm PermitEmptyPasswords When password authentication is allowed, it specifies whether the server allows login to accounts with empty password strings. @@ -699,7 +752,7 @@ The argument must be or .Dq no . The default is -.Dq yes . +.Dq without-password . .Pp If this option is set to .Dq without-password , @@ -792,7 +845,7 @@ and .Sq 2 . Multiple versions must be comma-separated. The default is -.Dq 2,1 . +.Dq 2 . Note that the order of the protocol list does not indicate preference, because the client selects among multiple protocol versions offered by the server. @@ -853,9 +906,9 @@ Note that this option applies to protocol version 2 only. .It Cm SyslogFacility Gives the facility code that is used when logging messages from .Xr sshd 8 . -The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, +The possible values are: DAEMON, USER, AUTH, AUTHPRIV, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. -The default is AUTH. +The default is AUTHPRIV. .It Cm TCPKeepAlive Specifies whether the system should send TCP keepalive messages to the other side. @@ -927,7 +980,7 @@ is enabled, you will not be able to run .Xr sshd 8 as a non-root user. The default is -.Dq no . +.Dq yes . .It Cm UsePrivilegeSeparation Specifies whether .Xr sshd 8 @@ -952,7 +1005,7 @@ The argument must be or .Dq no . The default is -.Dq no . +.Dq yes . .Pp When X11 forwarding is enabled, there may be additional exposure to the server and to client displays if the @@ -1009,7 +1062,7 @@ Specifies the full pathname of the .Xr xauth 1 program. The default is -.Pa /usr/X11R6/bin/xauth . +.Pa /usr/bin/xauth . .El .Sh TIME FORMATS .Xr sshd 8