diff --git a/ChangeLog b/ChangeLog index 0e122cf3a..2f5cee9a2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,44 @@ +20000818 + - (djm) OpenBSD CVS changes: + - markus@cvs.openbsd.org 2000/07/22 03:14:37 + [servconf.c servconf.h sshd.8 sshd.c sshd_config] + random early drop; ok theo, niels + - deraadt@cvs.openbsd.org 2000/07/26 11:46:51 + [ssh.1] + typo + - deraadt@cvs.openbsd.org 2000/08/01 11:46:11 + [sshd.8] + many fixes from pepper@mail.reppep.com + - provos@cvs.openbsd.org 2000/08/01 13:01:42 + [Makefile.in util.c aux.c] + rename aux.c to util.c to help with cygwin port + - deraadt@cvs.openbsd.org 2000/08/02 00:23:31 + [authfd.c] + correct sun_len; Alexander@Leidinger.net + - provos@cvs.openbsd.org 2000/08/02 10:27:17 + [readconf.c sshd.8] + disable kerberos authentication by default + - provos@cvs.openbsd.org 2000/08/02 11:27:05 + [sshd.8 readconf.c auth-krb4.c] + disallow kerberos authentication if we can't verify the TGT; from + dugsong@ + kerberos authentication is on by default only if you have a srvtab. + - markus@cvs.openbsd.org 2000/08/04 14:30:07 + [auth.c] + unused + - markus@cvs.openbsd.org 2000/08/04 14:30:35 + [sshd_config] + MaxStartups + - markus@cvs.openbsd.org 2000/08/15 13:20:46 + [authfd.c] + cleanup; ok niels@ + - markus@cvs.openbsd.org 2000/08/17 14:05:10 + [session.c] + cleanup login(1)-like jobs, no duplicate utmp entries + - markus@cvs.openbsd.org 2000/08/17 14:06:34 + [session.c sshd.8 sshd.c] + sshd -u len, similar to telnetd + 20000816 - (djm) Replacement for inet_ntoa for Irix (which breaks on gcc) - (djm) Fix strerror replacement for old SunOS. Based on patch from diff --git a/Makefile.in b/Makefile.in index 0aaaa6885..ff34c4932 100644 --- a/Makefile.in +++ b/Makefile.in @@ -34,7 +34,7 @@ INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_CMDS@ TARGETS=ssh sshd ssh-add ssh-keygen ssh-agent scp $(EXTRA_TARGETS) -LIBSSH_OBJS=atomicio.o authfd.o authfile.o aux.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o dsa.o fingerprint.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o entropy.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o uuencode.o xmalloc.o +LIBSSH_OBJS=atomicio.o authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o dsa.o fingerprint.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o entropy.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o util.o uuencode.o xmalloc.o LIBOPENBSD_COMPAT_OBJS=bsd-arc4random.o bsd-base64.o bsd-bindresvport.o bsd-daemon.o bsd-inet_aton.o bsd-inet_ntoa.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-sigaction.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bsd-strsep.o fake-getaddrinfo.o fake-getnameinfo.o next-posix.o diff --git a/acconfig.h b/acconfig.h index 6c25c8fc9..86607710f 100644 --- a/acconfig.h +++ b/acconfig.h @@ -6,6 +6,9 @@ @TOP@ +/* Define if your system's struct sockaddr_un has a sun_len member */ +#undef HAVE_SUN_LEN_IN_SOCKADDR_UN + /* Define if you system's inet_ntoa is busted (e.g. Irix gcc issue) */ #undef BROKEN_INET_NTOA diff --git a/auth-krb4.c b/auth-krb4.c index e32089b74..ae2b2a3d8 100644 --- a/auth-krb4.c +++ b/auth-krb4.c @@ -9,7 +9,7 @@ #include "ssh.h" #include "servconf.h" -RCSID("$OpenBSD: auth-krb4.c,v 1.15 2000/06/22 23:54:59 djm Exp $"); +RCSID("$OpenBSD: auth-krb4.c,v 1.16 2000/08/02 17:27:04 provos Exp $"); #ifdef KRB4 char *ticket = NULL; @@ -82,11 +82,12 @@ auth_krb4_password(struct passwd * pw, const char *password) if (r == RD_AP_UNDEC) { /* * Probably didn't have a srvtab on - * localhost. Allow login. + * localhost. Disallow login. */ log("Kerberos V4 TGT for %s unverifiable, " "no srvtab installed? krb_rd_req: %s", pw->pw_name, krb_err_txt[r]); + goto kerberos_auth_failure; } else if (r != KSUCCESS) { log("Kerberos V4 %s ticket unverifiable: %s", KRB4_SERVICE_NAME, krb_err_txt[r]); @@ -94,12 +95,13 @@ auth_krb4_password(struct passwd * pw, const char *password) } } else if (r == KDC_PR_UNKNOWN) { /* - * Allow login if no rcmd service exists, but + * Disallow login if no rcmd service exists, and * log the error. */ log("Kerberos V4 TGT for %s unverifiable: %s; %s.%s " "not registered, or srvtab is wrong?", pw->pw_name, krb_err_txt[r], KRB4_SERVICE_NAME, phost); + goto kerberos_auth_failure; } else { /* * TGT is bad, forget it. Possibly spoofed! diff --git a/auth.c b/auth.c index 5aeeec6de..dc3e82116 100644 --- a/auth.c +++ b/auth.c @@ -5,7 +5,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: auth.c,v 1.7 2000/05/17 21:37:24 deraadt Exp $"); +RCSID("$OpenBSD: auth.c,v 1.8 2000/08/04 20:30:07 markus Exp $"); #include "xmalloc.h" #include "rsa.h" @@ -30,8 +30,6 @@ RCSID("$OpenBSD: auth.c,v 1.7 2000/05/17 21:37:24 deraadt Exp $"); #include "ssh2.h" #include "auth.h" #include "session.h" -#include "dispatch.h" - /* import */ extern ServerOptions options; diff --git a/authfd.c b/authfd.c index 227c99286..a34e111ac 100644 --- a/authfd.c +++ b/authfd.c @@ -14,7 +14,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: authfd.c,v 1.22 2000/07/16 08:27:20 markus Exp $"); +RCSID("$OpenBSD: authfd.c,v 1.24 2000/08/15 19:20:46 markus Exp $"); #include "ssh.h" #include "rsa.h" @@ -31,7 +31,7 @@ RCSID("$OpenBSD: authfd.c,v 1.22 2000/07/16 08:27:20 markus Exp $"); #include "kex.h" /* helper */ -int ssh_agent_get_reply(AuthenticationConnection *auth); +int decode_reply(int type); /* Returns the number of the authentication fd, or -1 if there is none. */ @@ -39,7 +39,7 @@ int ssh_get_authentication_socket() { const char *authsocket; - int sock; + int sock, len; struct sockaddr_un sunaddr; authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); @@ -48,6 +48,11 @@ ssh_get_authentication_socket() sunaddr.sun_family = AF_UNIX; strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); +#ifdef HAVE_SUN_LEN_IN_SOCKADDR_UN + sunaddr.sun_len = len = SUN_LEN(&sunaddr)+1; +#else /* HAVE_SUN_LEN_IN_SOCKADDR_UN */ + len = SUN_LEN(&sunaddr)+1; +#endif /* HAVE_SUN_LEN_IN_SOCKADDR_UN */ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) @@ -58,13 +63,67 @@ ssh_get_authentication_socket() close(sock); return -1; } - if (connect(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) { + if (connect(sock, (struct sockaddr *) & sunaddr, len) < 0) { close(sock); return -1; } return sock; } +int +ssh_request_reply(AuthenticationConnection *auth, + Buffer *request, Buffer *reply) +{ + int l, len; + char buf[1024]; + + /* Get the length of the message, and format it in the buffer. */ + len = buffer_len(request); + PUT_32BIT(buf, len); + + /* Send the length and then the packet to the agent. */ + if (atomicio(write, auth->fd, buf, 4) != 4 || + atomicio(write, auth->fd, buffer_ptr(request), + buffer_len(request)) != buffer_len(request)) { + error("Error writing to authentication socket."); + return 0; + } + /* + * Wait for response from the agent. First read the length of the + * response packet. + */ + len = 4; + while (len > 0) { + l = read(auth->fd, buf + 4 - len, len); + if (l <= 0) { + error("Error reading response length from authentication socket."); + return 0; + } + len -= l; + } + + /* Extract the length, and check it for sanity. */ + len = GET_32BIT(buf); + if (len > 256 * 1024) + fatal("Authentication response too long: %d", len); + + /* Read the rest of the response in to the buffer. */ + buffer_clear(reply); + while (len > 0) { + l = len; + if (l > sizeof(buf)) + l = sizeof(buf); + l = read(auth->fd, buf, l); + if (l <= 0) { + error("Error reading response from authentication socket."); + return 0; + } + buffer_append(reply, (char *) buf, l); + len -= l; + } + return 1; +} + /* * Closes the agent socket if it should be closed (depends on how it was * obtained). The argument must have been returned by @@ -133,62 +192,35 @@ ssh_close_authentication_connection(AuthenticationConnection *ac) int ssh_get_first_identity(AuthenticationConnection *auth, - BIGNUM *e, BIGNUM *n, char **comment) + BIGNUM *e, BIGNUM *n, char **comment) { - unsigned char msg[8192]; - int len, l; + Buffer request; + int type; /* * Send a message to the agent requesting for a list of the * identities it can represent. */ - PUT_32BIT(msg, 1); - msg[4] = SSH_AGENTC_REQUEST_RSA_IDENTITIES; - if (atomicio(write, auth->fd, msg, 5) != 5) { - error("write auth->fd: %.100s", strerror(errno)); + buffer_init(&request); + buffer_put_char(&request, SSH_AGENTC_REQUEST_RSA_IDENTITIES); + + buffer_clear(&auth->identities); + if (ssh_request_reply(auth, &request, &auth->identities) == 0) { + buffer_free(&request); return 0; } - /* Read the length of the response. XXX implement timeouts here. */ - len = 4; - while (len > 0) { - l = read(auth->fd, msg + 4 - len, len); - if (l <= 0) { - error("read auth->fd: %.100s", strerror(errno)); - return 0; - } - len -= l; - } - - /* - * Extract the length, and check it for sanity. (We cannot trust - * authentication agents). - */ - len = GET_32BIT(msg); - if (len < 1 || len > 256 * 1024) - fatal("Authentication reply message too long: %d\n", len); - - /* Read the packet itself. */ - buffer_clear(&auth->identities); - while (len > 0) { - l = len; - if (l > sizeof(msg)) - l = sizeof(msg); - l = read(auth->fd, msg, l); - if (l <= 0) - fatal("Incomplete authentication reply."); - buffer_append(&auth->identities, (char *) msg, l); - len -= l; - } + buffer_free(&request); /* Get message type, and verify that we got a proper answer. */ - buffer_get(&auth->identities, (char *) msg, 1); - if (msg[0] != SSH_AGENT_RSA_IDENTITIES_ANSWER) - fatal("Bad authentication reply message type: %d", msg[0]); + type = buffer_get_char(&auth->identities); + if (type != SSH_AGENT_RSA_IDENTITIES_ANSWER) + fatal("Bad authentication reply message type: %d", type); /* Get the number of entries in the response and check it for sanity. */ auth->howmany = buffer_get_int(&auth->identities); if (auth->howmany > 1024) - fatal("Too many identities in authentication reply: %d\n", auth->howmany); + fatal("Too many identities in authentication reply: %d\n", + auth->howmany); /* Return the first entry (if any). */ return ssh_get_next_identity(auth, e, n, comment); @@ -203,7 +235,7 @@ ssh_get_first_identity(AuthenticationConnection *auth, int ssh_get_next_identity(AuthenticationConnection *auth, - BIGNUM *e, BIGNUM *n, char **comment) + BIGNUM *e, BIGNUM *n, char **comment) { unsigned int bits; @@ -240,23 +272,22 @@ ssh_get_next_identity(AuthenticationConnection *auth, int ssh_decrypt_challenge(AuthenticationConnection *auth, - BIGNUM* e, BIGNUM *n, BIGNUM *challenge, - unsigned char session_id[16], - unsigned int response_type, - unsigned char response[16]) + BIGNUM* e, BIGNUM *n, BIGNUM *challenge, + unsigned char session_id[16], + unsigned int response_type, + unsigned char response[16]) { Buffer buffer; - unsigned char buf[8192]; - int len, l, i; + int success = 0; + int i; + int type; - /* Response type 0 is no longer supported. */ if (response_type == 0) - fatal("Compatibility with ssh protocol version 1.0 no longer supported."); + fatal("Compatibility with ssh protocol version " + "1.0 no longer supported."); - /* Format a message to the agent. */ - buf[0] = SSH_AGENTC_RSA_CHALLENGE; buffer_init(&buffer); - buffer_append(&buffer, (char *) buf, 1); + buffer_put_char(&buffer, SSH_AGENTC_RSA_CHALLENGE); buffer_put_int(&buffer, BN_num_bits(n)); buffer_put_bignum(&buffer, e); buffer_put_bignum(&buffer, n); @@ -264,77 +295,27 @@ ssh_decrypt_challenge(AuthenticationConnection *auth, buffer_append(&buffer, (char *) session_id, 16); buffer_put_int(&buffer, response_type); - /* Get the length of the message, and format it in the buffer. */ - len = buffer_len(&buffer); - PUT_32BIT(buf, len); - - /* Send the length and then the packet to the agent. */ - if (atomicio(write, auth->fd, buf, 4) != 4 || - atomicio(write, auth->fd, buffer_ptr(&buffer), - buffer_len(&buffer)) != buffer_len(&buffer)) { - error("Error writing to authentication socket."); -error_cleanup: + if (ssh_request_reply(auth, &buffer, &buffer) == 0) { buffer_free(&buffer); return 0; } - /* - * Wait for response from the agent. First read the length of the - * response packet. - */ - len = 4; - while (len > 0) { - l = read(auth->fd, buf + 4 - len, len); - if (l <= 0) { - error("Error reading response length from authentication socket."); - goto error_cleanup; - } - len -= l; - } + type = buffer_get_char(&buffer); - /* Extract the length, and check it for sanity. */ - len = GET_32BIT(buf); - if (len > 256 * 1024) - fatal("Authentication response too long: %d", len); - - /* Read the rest of the response in tothe buffer. */ - buffer_clear(&buffer); - while (len > 0) { - l = len; - if (l > sizeof(buf)) - l = sizeof(buf); - l = read(auth->fd, buf, l); - if (l <= 0) { - error("Error reading response from authentication socket."); - goto error_cleanup; - } - buffer_append(&buffer, (char *) buf, l); - len -= l; - } - - /* Get the type of the packet. */ - buffer_get(&buffer, (char *) buf, 1); - - /* Check for agent failure message. */ - if (buf[0] == SSH_AGENT_FAILURE) { + if (type == SSH_AGENT_FAILURE) { log("Agent admitted failure to authenticate using the key."); - goto error_cleanup; + } else if (type != SSH_AGENT_RSA_RESPONSE) { + fatal("Bad authentication response: %d", type); + } else { + success = 1; + /* + * Get the response from the packet. This will abort with a + * fatal error if the packet is corrupt. + */ + for (i = 0; i < 16; i++) + response[i] = buffer_get_char(&buffer); } - /* Now it must be an authentication response packet. */ - if (buf[0] != SSH_AGENT_RSA_RESPONSE) - fatal("Bad authentication response: %d", buf[0]); - - /* - * Get the response from the packet. This will abort with a fatal - * error if the packet is corrupt. - */ - for (i = 0; i < 16; i++) - response[i] = buffer_get_char(&buffer); - - /* The buffer containing the packet is no longer needed. */ buffer_free(&buffer); - - /* Correct answer. */ - return 1; + return success; } /* Encode key for a message to the agent. */ @@ -378,8 +359,7 @@ int ssh_add_identity(AuthenticationConnection *auth, Key *key, const char *comment) { Buffer buffer; - unsigned char buf[8192]; - int len; + int type; buffer_init(&buffer); @@ -395,21 +375,13 @@ ssh_add_identity(AuthenticationConnection *auth, Key *key, const char *comment) return 0; break; } - - /* Get the length of the message, and format it in the buffer. */ - len = buffer_len(&buffer); - PUT_32BIT(buf, len); - - /* Send the length and then the packet to the agent. */ - if (atomicio(write, auth->fd, buf, 4) != 4 || - atomicio(write, auth->fd, buffer_ptr(&buffer), - buffer_len(&buffer)) != buffer_len(&buffer)) { - error("Error writing to authentication socket."); + if (ssh_request_reply(auth, &buffer, &buffer) == 0) { buffer_free(&buffer); return 0; } + type = buffer_get_char(&buffer); buffer_free(&buffer); - return ssh_agent_get_reply(auth); + return decode_reply(type); } /* @@ -421,30 +393,21 @@ int ssh_remove_identity(AuthenticationConnection *auth, RSA *key) { Buffer buffer; - unsigned char buf[5]; - int len; + int type; - /* Format a message to the agent. */ buffer_init(&buffer); buffer_put_char(&buffer, SSH_AGENTC_REMOVE_RSA_IDENTITY); buffer_put_int(&buffer, BN_num_bits(key->n)); buffer_put_bignum(&buffer, key->e); buffer_put_bignum(&buffer, key->n); - /* Get the length of the message, and format it in the buffer. */ - len = buffer_len(&buffer); - PUT_32BIT(buf, len); - - /* Send the length and then the packet to the agent. */ - if (atomicio(write, auth->fd, buf, 4) != 4 || - atomicio(write, auth->fd, buffer_ptr(&buffer), - buffer_len(&buffer)) != buffer_len(&buffer)) { - error("Error writing to authentication socket."); + if (ssh_request_reply(auth, &buffer, &buffer) == 0) { buffer_free(&buffer); return 0; } + type = buffer_get_char(&buffer); buffer_free(&buffer); - return ssh_agent_get_reply(auth); + return decode_reply(type); } /* @@ -455,73 +418,27 @@ ssh_remove_identity(AuthenticationConnection *auth, RSA *key) int ssh_remove_all_identities(AuthenticationConnection *auth) { - unsigned char buf[5]; + Buffer buffer; + int type; - /* Get the length of the message, and format it in the buffer. */ - PUT_32BIT(buf, 1); - buf[4] = SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES; + buffer_init(&buffer); + buffer_put_char(&buffer, SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES); - /* Send the length and then the packet to the agent. */ - if (atomicio(write, auth->fd, buf, 5) != 5) { - error("Error writing to authentication socket."); + if (ssh_request_reply(auth, &buffer, &buffer) == 0) { + buffer_free(&buffer); return 0; } - return ssh_agent_get_reply(auth); -} - -/* - * Read for reply from agent. returns 1 for success, 0 on error - */ - -int -ssh_agent_get_reply(AuthenticationConnection *auth) -{ - Buffer buffer; - unsigned char buf[8192]; - int len, l, type; - - /* - * Wait for response from the agent. First read the length of the - * response packet. - */ - len = 4; - while (len > 0) { - l = read(auth->fd, buf + 4 - len, len); - if (l <= 0) { - error("Error reading response length from authentication socket."); - buffer_free(&buffer); - return 0; - } - len -= l; - } - - /* Extract the length, and check it for sanity. */ - len = GET_32BIT(buf); - if (len > 256 * 1024) - fatal("Response from agent too long: %d", len); - - /* Read the rest of the response in to the buffer. */ - buffer_init(&buffer); - while (len > 0) { - l = len; - if (l > sizeof(buf)) - l = sizeof(buf); - l = read(auth->fd, buf, l); - if (l <= 0) { - error("Error reading response from authentication socket."); - buffer_free(&buffer); - return 0; - } - buffer_append(&buffer, (char *) buf, l); - len -= l; - } - - /* Get the type of the packet. */ type = buffer_get_char(&buffer); buffer_free(&buffer); + return decode_reply(type); +} + +int +decode_reply(int type) +{ switch (type) { case SSH_AGENT_FAILURE: -log("SSH_AGENT_FAILURE"); + log("SSH_AGENT_FAILURE"); return 0; case SSH_AGENT_SUCCESS: return 1; diff --git a/bsd-mktemp.c b/bsd-mktemp.c index 7c02ea1a2..23831fa91 100644 --- a/bsd-mktemp.c +++ b/bsd-mktemp.c @@ -52,6 +52,7 @@ static char rcsid[] = "$OpenBSD: mktemp.c,v 1.13 1998/06/30 23:03:13 deraadt Exp #include #include "bsd-misc.h" +#include "bsd-arc4random.h" static int _gettemp(char *, int *, int, int); diff --git a/configure.in b/configure.in index 974d0df6b..e9467011b 100644 --- a/configure.in +++ b/configure.in @@ -686,6 +686,22 @@ OSSH_CHECK_HEADER_FOR_FIELD(ut_time, utmp.h, HAVE_TIME_IN_UTMP) OSSH_CHECK_HEADER_FOR_FIELD(ut_time, utmpx.h, HAVE_TIME_IN_UTMPX) OSSH_CHECK_HEADER_FOR_FIELD(ut_tv, utmpx.h, HAVE_TV_IN_UTMPX) +AC_CACHE_CHECK([for sun_len field in struct sockaddr_un], + ac_cv_have_sun_len_in_struct_sockaddr_un, [ + AC_TRY_COMPILE( + [ +#include +#include + ], + [ struct sockaddr_un s; s.sun_len = 1; ], + [ ac_cv_have_sun_len_in_struct_sockaddr_un="yes" ], + [ ac_cv_have_sun_len_in_struct_sockaddr_un="no" ], + ) +]) +if test "x$ac_cv_have_sun_len_in_struct_sockaddr_un" = "xyes" ; then + AC_DEFINE(HAVE_SUN_LEN_IN_SOCKADDR_UN) +fi + AC_CACHE_CHECK([for ss_family field in struct sockaddr_storage], ac_cv_have_ss_family_in_struct_ss, [ AC_TRY_COMPILE( diff --git a/readconf.c b/readconf.c index 06cfaa1a3..f31b1c4e6 100644 --- a/readconf.c +++ b/readconf.c @@ -14,7 +14,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: readconf.c,v 1.43 2000/07/14 22:59:46 markus Exp $"); +RCSID("$OpenBSD: readconf.c,v 1.45 2000/08/02 17:27:04 provos Exp $"); #include "ssh.h" #include "cipher.h" diff --git a/servconf.c b/servconf.c index 477204cfd..6affb51e9 100644 --- a/servconf.c +++ b/servconf.c @@ -12,7 +12,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: servconf.c,v 1.49 2000/07/14 22:59:46 markus Exp $"); +RCSID("$OpenBSD: servconf.c,v 1.50 2000/07/22 09:14:36 markus Exp $"); #include "ssh.h" #include "servconf.h" @@ -76,6 +76,8 @@ initialize_server_options(ServerOptions *options) options->protocol = SSH_PROTO_UNKNOWN; options->gateway_ports = -1; options->num_subsystems = 0; + options->max_startups_begin = -1; + options->max_startups_rate = -1; options->max_startups = -1; } @@ -162,6 +164,10 @@ fill_default_server_options(ServerOptions *options) options->gateway_ports = 0; if (options->max_startups == -1) options->max_startups = 10; + if (options->max_startups_rate == -1) + options->max_startups_rate = 100; /* 100% */ + if (options->max_startups_begin == -1) + options->max_startups_begin = options->max_startups; } /* Keyword tokens. */ @@ -644,6 +650,22 @@ parse_flag: break; case sMaxStartups: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing MaxStartups spec.", + filename, linenum); + if (sscanf(arg, "%d:%d:%d", + &options->max_startups_begin, + &options->max_startups_rate, + &options->max_startups) == 3) { + if (options->max_startups_begin > + options->max_startups || + options->max_startups_rate > 100 || + options->max_startups_rate < 1) + fatal("%s line %d: Illegal MaxStartups spec.", + filename, linenum); + break; + } intptr = &options->max_startups; goto parse_int; diff --git a/servconf.h b/servconf.h index 95593722d..3b65c6a6f 100644 --- a/servconf.h +++ b/servconf.h @@ -13,7 +13,7 @@ * */ -/* RCSID("$OpenBSD: servconf.h,v 1.26 2000/06/26 21:59:18 markus Exp $"); */ +/* RCSID("$OpenBSD: servconf.h,v 1.27 2000/07/22 09:14:36 markus Exp $"); */ #ifndef SERVCONF_H #define SERVCONF_H @@ -100,6 +100,8 @@ typedef struct { char *subsystem_name[MAX_SUBSYSTEMS]; char *subsystem_command[MAX_SUBSYSTEMS]; + int max_startups_begin; + int max_startups_rate; int max_startups; } ServerOptions; diff --git a/session.c b/session.c index e68718a7e..d65b06984 100644 --- a/session.c +++ b/session.c @@ -8,7 +8,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.23 2000/07/11 08:11:33 deraadt Exp $"); +RCSID("$OpenBSD: session.c,v 1.25 2000/08/17 20:06:34 markus Exp $"); #include "xmalloc.h" #include "ssh.h" @@ -85,6 +85,7 @@ void session_pty_cleanup(Session *s); void session_proctitle(Session *s); void do_exec_pty(Session *s, const char *command, struct passwd * pw); void do_exec_no_pty(Session *s, const char *command, struct passwd * pw); +void do_login(Session *s); void do_child(const char *command, struct passwd * pw, const char *term, @@ -101,6 +102,7 @@ static const char *__progname = "sshd"; extern int log_stderr; extern int debug_flag; +extern unsigned int utmp_len; extern int startup_pipe; @@ -523,35 +525,14 @@ do_exec_no_pty(Session *s, const char *command, struct passwd * pw) void do_exec_pty(Session *s, const char *command, struct passwd * pw) { - FILE *f; - char buf[100], *time_string; - char line[256]; - const char *hostname; int fdout, ptyfd, ttyfd, ptymaster; - int quiet_login; pid_t pid; - socklen_t fromlen; - struct sockaddr_storage from; - struct stat st; - time_t last_login_time; if (s == NULL) fatal("do_exec_pty: no session"); ptyfd = s->ptyfd; ttyfd = s->ttyfd; - /* Get remote host name. */ - hostname = get_canonical_hostname(); - - /* - * Get the time when the user last logged in. Buf will be set to - * contain the hostname the last login was from. - */ - if (!options.use_login) { - last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, - buf, sizeof(buf)); - } - #ifdef USE_PAM do_pam_session(pw->pw_name, s->tty); do_pam_setcred(); @@ -559,10 +540,7 @@ do_exec_pty(Session *s, const char *command, struct passwd * pw) /* Fork the child. */ if ((pid = fork()) == 0) { - pid = getpid(); - - /* Child. Reinitialize the log because the pid has - changed. */ + /* Child. Reinitialize the log because the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Close the master side of the pseudo tty. */ @@ -586,82 +564,10 @@ do_exec_pty(Session *s, const char *command, struct passwd * pw) /* Close the extra descriptor for the pseudo tty. */ close(ttyfd); -/* XXXX ? move to do_child() ??*/ - /* - * Get IP address of client. This is needed because we want - * to record where the user logged in from. If the - * connection is not a socket, let the ip address be 0.0.0.0. - */ - memset(&from, 0, sizeof(from)); - if (packet_connection_is_on_socket()) { - fromlen = sizeof(from); - if (getpeername(packet_get_connection_in(), - (struct sockaddr *) & from, &fromlen) < 0) { - debug("getpeername: %.100s", strerror(errno)); - fatal_cleanup(); - } - } - /* Record that there was a login on that terminal. */ - if (!options.use_login || command != NULL) - record_login(pid, s->tty, pw->pw_name, pw->pw_uid, - hostname, (struct sockaddr *)&from); + /* record login, etc. similar to login(1) */ + if (command == NULL && !options.use_login) + do_login(s); - /* Check if .hushlogin exists. */ - snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir); - quiet_login = stat(line, &st) >= 0; - -#ifdef USE_PAM - if (!quiet_login) - print_pam_messages(); -#endif /* USE_PAM */ - - /* - * If the user has logged in before, display the time of last - * login. However, don't display anything extra if a command - * has been specified (so that ssh can be used to execute - * commands on a remote machine without users knowing they - * are going to another machine). Login(1) will do this for - * us as well, so check if login(1) is used - */ - if (command == NULL && last_login_time != 0 && !quiet_login && - !options.use_login) { - /* Convert the date to a string. */ - time_string = ctime(&last_login_time); - /* Remove the trailing newline. */ - if (strchr(time_string, '\n')) - *strchr(time_string, '\n') = 0; - /* Display the last login time. Host if displayed - if known. */ - if (strcmp(buf, "") == 0) - printf("Last login: %s\r\n", time_string); - else - printf("Last login: %s from %s\r\n", time_string, buf); - } - /* - * Print /etc/motd unless a command was specified or printing - * it was disabled in server options or login(1) will be - * used. Note that some machines appear to print it in - * /etc/profile or similar. - */ - if (command == NULL && options.print_motd && !quiet_login && - !options.use_login) { - /* Print /etc/motd if it exists. */ - f = fopen("/etc/motd", "r"); - if (f) { - while (fgets(line, sizeof(line), f)) - fputs(line, stdout); - fclose(f); - } - } -#if defined(WITH_AIXAUTHENTICATE) - /* - * AIX handles the lastlog info differently. Display it here. - */ - if (command == NULL && aixloginmsg && *aixloginmsg && - !quiet_login && !options.use_login) { - printf("%s\n", aixloginmsg); - } -#endif /* Do common processing for the child, such as execing the command. */ do_child(command, pw, s->term, s->display, s->auth_proto, s->auth_data, s->tty); @@ -699,6 +605,87 @@ do_exec_pty(Session *s, const char *command, struct passwd * pw) } } +const char * +get_remote_name_or_ip(void) +{ + static const char *remote = ""; + if (utmp_len > 0) + remote = get_canonical_hostname(); + if (utmp_len == 0 || strlen(remote) > utmp_len) + remote = get_remote_ipaddr(); + return remote; +} + +/* administrative, login(1)-like work */ +void +do_login(Session *s) +{ + FILE *f; + char *time_string; + char buf[256]; + socklen_t fromlen; + struct sockaddr_storage from; + struct stat st; + time_t last_login_time; + struct passwd * pw = s->pw; + pid_t pid = getpid(); + + /* + * Get IP address of client. If the connection is not a socket, let + * the address be 0.0.0.0. + */ + memset(&from, 0, sizeof(from)); + if (packet_connection_is_on_socket()) { + fromlen = sizeof(from); + if (getpeername(packet_get_connection_in(), + (struct sockaddr *) & from, &fromlen) < 0) { + debug("getpeername: %.100s", strerror(errno)); + fatal_cleanup(); + } + } + + /* Record that there was a login on that tty from the remote host. */ + record_login(pid, s->tty, pw->pw_name, pw->pw_uid, + get_remote_name_or_ip(), (struct sockaddr *)&from); + + /* Done if .hushlogin exists. */ + snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); + if (stat(buf, &st) >= 0) + return; + +#ifdef USE_PAM + print_pam_messages(); +#endif /* USE_PAM */ +#ifdef WITH_AIXAUTHENTICATE + if (aixloginmsg && *aixloginmsg) + printf("%s\n", aixloginmsg); +#endif /* WITH_AIXAUTHENTICATE */ + + /* + * Get the time when the user last logged in. 'buf' will be set + * to contain the hostname the last login was from. + */ + last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, + buf, sizeof(buf)); + if (last_login_time != 0) { + time_string = ctime(&last_login_time); + if (strchr(time_string, '\n')) + *strchr(time_string, '\n') = 0; + if (strcmp(buf, "") == 0) + printf("Last login: %s\r\n", time_string); + else + printf("Last login: %s from %s\r\n", time_string, buf); + } + if (options.print_motd) { + f = fopen("/etc/motd", "r"); + if (f) { + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stdout); + fclose(f); + } + } +} + /* * Sets the value of the given variable in the environment. If the variable * already exists, its value is overriden. @@ -1265,8 +1252,9 @@ do_child(const char *command, struct passwd * pw, const char *term, } else { /* Launch login(1). */ - execl(LOGIN_PROGRAM, "login", "-h", get_remote_ipaddr(), - "-p", "-f", "--", pw->pw_name, NULL); + execl(LOGIN_PROGRAM, "login", + "-h", get_remote_name_or_ip(), + "-p", "-f", "--", pw->pw_name, NULL); /* Login couldn't be executed, die. */ diff --git a/ssh.1 b/ssh.1 index cd56e7bef..39368e0ce 100644 --- a/ssh.1 +++ b/ssh.1 @@ -9,7 +9,7 @@ .\" .\" Created: Sat Apr 22 21:55:14 1995 ylo .\" -.\" $Id: ssh.1,v 1.28 2000/06/07 09:55:44 djm Exp $ +.\" $Id: ssh.1,v 1.29 2000/08/18 03:59:06 djm Exp $ .\" .Dd September 25, 1999 .Dt SSH 1 @@ -994,7 +994,7 @@ If the current session has no tty, this variable is not set. .It Ev TZ The timezone variable is set to indicate the present timezone if it -was set when the daemon was started (e.i., the daemon passes the value +was set when the daemon was started (i.e., the daemon passes the value on to new connections). .It Ev USER Set to the name of the user logging in. diff --git a/sshd.8 b/sshd.8 index b6aefe491..c8b99df38 100644 --- a/sshd.8 +++ b/sshd.8 @@ -9,7 +9,7 @@ .\" .\" Created: Sat Apr 22 21:55:14 1995 ylo .\" -.\" $Id: sshd.8,v 1.25 2000/07/11 07:31:39 djm Exp $ +.\" $Id: sshd.8,v 1.26 2000/08/18 03:59:06 djm Exp $ .\" .Dd September 25, 1999 .Dt SSHD 8 @@ -26,6 +26,7 @@ .Op Fl h Ar host_key_file .Op Fl k Ar key_gen_time .Op Fl p Ar port +.Op Fl u Ar len .Op Fl V Ar client_protocol_id .Sh DESCRIPTION .Nm @@ -104,7 +105,7 @@ into the machine). .Pp .Ss SSH protocol version 2 .Pp -Version 2 works similar: +Version 2 works similarly: Each host has a host-specific DSA key used to identify the host. However, when the daemon starts, it does not generate a server key. Forward security is provided through a Diffie-Hellman key agreement. @@ -211,6 +212,22 @@ Quiet mode. Nothing is sent to the system log. Normally the beginning, authentication, and termination of each connection is logged. +.It Fl u Ar len +This option is used to specify the size of the field +in the +.Li utmp +structure that holds the remote host name. +If the resolved host name is longer than +.Ar len , +the dotted decimal value will be used instead. +This allows hosts with very long host names that +overflow this field to still be uniquely identified. +Specifying +.Fl u0 +indicates that only dotted decimal addresses +should be put into the +.Pa utmp +file. .It Fl Q Do not print an error message if RSA support is missing. .It Fl V Ar client_protocol_id @@ -257,7 +274,7 @@ and .Ql ? can be used as wildcards in the patterns. -Only group names are valid, a numerical group ID isn't recognized. +Only group names are valid; a numerical group ID isn't recognized. By default login is allowed regardless of the primary group. .Pp .It Cm AllowUsers @@ -270,7 +287,7 @@ and .Ql ? can be used as wildcards in the patterns. -Only user names are valid, a numerical user ID isn't recognized. +Only user names are valid; a numerical user ID isn't recognized. By default login is allowed regardless of the user name. .Pp .It Cm Ciphers @@ -294,7 +311,7 @@ and .Ql ? can be used as wildcards in the patterns. -Only group names are valid, a numerical group ID isn't recognized. +Only group names are valid; a numerical group ID isn't recognized. By default login is allowed regardless of the primary group. .Pp .It Cm DenyUsers @@ -305,7 +322,7 @@ Login is disallowed for user names that match one of the patterns. and .Ql ? can be used as wildcards in the patterns. -Only user names are valid, a numerical user ID isn't recognized. +Only user names are valid; a numerical user ID isn't recognized. By default login is allowed regardless of the user name. .It Cm DSAAuthentication Specifies whether DSA authentication is allowed. @@ -321,7 +338,7 @@ or .Dq no . The default is .Dq no . -.It Cm HostDsaKey +.It Cm HostDSAKey Specifies the file containing the private DSA host key (default .Pa /etc/ssh_host_dsa_key ) used by SSH protocol 2.0. @@ -383,7 +400,8 @@ Specifies whether Kerberos authentication is allowed. This can be in the form of a Kerberos ticket, or if .Cm PasswordAuthentication is yes, the password provided by the user will be validated through -the Kerberos KDC. +the Kerberos KDC. To use this option, the server needs a +Kerberos servtab which allows the verification of the KDC's identity. Default is .Dq yes . .It Cm KerberosOrLocalPasswd @@ -443,11 +461,28 @@ 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 +.Dq start:rate:full +(e.g. "10:30:60"). +.Nm +will refuse connection attempts with a probabillity of +.Dq rate/100 +(30%) +if there are currently +.Dq start +(10) +unauthenticated connections. +The probabillity increases linearly and all connection attempts +are refused if the number of unauthenticated connections reaches +.Dq full +(60). .It Cm PasswordAuthentication Specifies whether password authentication is allowed. The default is .Dq yes . -Note that this option applies to both protocol version 1 and 2. +Note that this option applies to both protocol versions 1 and 2. .It Cm PermitEmptyPasswords When password authentication is allowed, it specifies whether the server allows login to accounts with empty password strings. @@ -568,7 +603,7 @@ Specifies whether is used for interactive login sessions. Note that .Xr login 1 -is not never for remote command execution. +is never used for remote command execution. The default is .Dq no . .It Cm X11DisplayOffset @@ -666,7 +701,7 @@ You don't want to type them in; instead, copy the .Pa identity.pub file and edit it. .Pp -The options (if present) consists of comma-separated option +The options (if present) consist of comma-separated option specifications. No spaces are permitted, except within double quotes. The following option specifications are supported: @@ -740,7 +775,7 @@ and files contain host public keys for all known hosts. The global file should be prepared by the administrator (optional), and the per-user file is -maintained automatically: whenever the user connects an unknown host +maintained automatically: whenever the user connects from an unknown host its key is added to the per-user file. .Pp Each line in these files contains the following fields: hostnames, @@ -815,7 +850,7 @@ Contains the process ID of the listening for connections (if there are several daemons running concurrently for different ports, this contains the pid of the one started last). -The contents of this file are not sensitive; it can be world-readable. +The content of this file is not sensitive; it can be world-readable. .It Pa $HOME/.ssh/authorized_keys Lists the RSA keys that can be used to log into the user's account. This file must be readable by root (which may on some machines imply @@ -843,7 +878,7 @@ These files are consulted when using rhosts with RSA host authentication to check the public key of the host. The key must be listed in one of these files to be accepted. The client uses the same files -to verify that the remote host is the one we intended to connect. +to verify that the remote host is the one it intended to connect. These files should be writable only by root/the owner. .Pa /etc/ssh_known_hosts should be world-readable, and @@ -882,7 +917,7 @@ this file is exactly the same as for .Pa .rhosts . However, this file is not used by rlogin and rshd, so using this permits access using SSH only. -.Pa /etc/hosts.equiv +.It Pa /etc/hosts.equiv This file is used during .Pa .rhosts authentication. diff --git a/sshd.c b/sshd.c index b6db074c8..ae02f2c40 100644 --- a/sshd.c +++ b/sshd.c @@ -14,7 +14,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.123 2000/07/18 01:25:01 djm Exp $"); +RCSID("$OpenBSD: sshd.c,v 1.125 2000/08/17 20:06:34 markus Exp $"); #include "xmalloc.h" #include "rsa.h" @@ -139,6 +139,9 @@ unsigned char session_id[16]; unsigned char *session_id2 = NULL; int session_id2_len = 0; +/* record remote hostname or ip */ +unsigned int utmp_len = MAXHOSTNAMELEN; + /* Prototypes for various functions defined later in this file. */ void do_ssh1_kex(); void do_ssh2_kex(); @@ -400,6 +403,35 @@ destroy_sensitive_data(void) key_free(sensitive_data.dsa_host_key); } +/* + * returns 1 if connection should be dropped, 0 otherwise. + * dropping starts at connection #max_startups_begin with a probability + * of (max_startups_rate/100). the probability increases linearly until + * all connections are dropped for startups > max_startups + */ +int +drop_connection(int startups) +{ + double p, r; + + if (startups < options.max_startups_begin) + return 0; + if (startups >= options.max_startups) + return 1; + if (options.max_startups_rate == 100) + return 1; + + p = 100 - options.max_startups_rate; + p *= startups - options.max_startups_begin; + p /= (double) (options.max_startups - options.max_startups_begin); + p += options.max_startups_rate; + p /= 100.0; + r = arc4random() / (double) UINT_MAX; + + debug("drop_connection: p %g, r %g", p, r); + return (r < p) ? 1 : 0; +} + int *startup_pipes = NULL; /* options.max_startup sized array of fd ints */ int startup_pipe; /* in child */ @@ -441,7 +473,7 @@ main(int ac, char **av) initialize_server_options(&options); /* Parse command-line arguments. */ - while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ46")) != EOF) { + while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:diqQ46")) != EOF) { switch (opt) { case '4': IPv4or6 = AF_INET; @@ -488,6 +520,9 @@ main(int ac, char **av) /* only makes sense with inetd_flag, i.e. no listen() */ inetd_flag = 1; break; + case 'u': + utmp_len = atoi(optarg); + break; case '?': default: fprintf(stderr, "sshd version %s\n", SSH_VERSION); @@ -503,6 +538,7 @@ main(int ac, char **av) fprintf(stderr, " -b bits Size of server RSA key (default: 768 bits)\n"); fprintf(stderr, " -h file File from which to read host key (default: %s)\n", HOST_KEY_FILE); + fprintf(stderr, " -u len Maximum hostname length for utmp recording\n"); fprintf(stderr, " -4 Use IPv4 only\n"); fprintf(stderr, " -6 Use IPv6 only\n"); exit(1); @@ -823,7 +859,8 @@ main(int ac, char **av) error("newsock del O_NONBLOCK: %s", strerror(errno)); continue; } - if (startups >= options.max_startups) { + if (drop_connection(startups) == 1) { + debug("drop connection #%d", startups); close(newsock); continue; } diff --git a/sshd_config b/sshd_config index d3bab840a..a97b780e8 100644 --- a/sshd_config +++ b/sshd_config @@ -51,3 +51,4 @@ CheckMail no UseLogin no #Subsystem sftp /usr/local/sbin/sftpd +#MaxStartups 10:30:60