Index: head/crypto/openssh/canohost.c =================================================================== --- head/crypto/openssh/canohost.c (revision 62100) +++ head/crypto/openssh/canohost.c (revision 62101) @@ -1,274 +1,304 @@ /* * * canohost.c * * Author: Tatu Ylonen * * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * Created: Sun Jul 2 17:52:22 1995 ylo * * Functions for returning the canonical host name of the remote site. * + * $FreeBSD$ */ #include "includes.h" RCSID("$Id: canohost.c,v 1.12 2000/04/14 10:30:30 markus Exp $"); #include "packet.h" #include "xmalloc.h" #include "ssh.h" /* * Return the canonical name of the host at the other end of the socket. The * caller should free the returned string with xfree. */ char * get_remote_hostname(int socket) { struct sockaddr_storage from; int i; socklen_t fromlen; struct addrinfo hints, *ai, *aitop; char name[MAXHOSTNAMELEN]; char ntop[NI_MAXHOST], ntop2[NI_MAXHOST]; /* Get IP address of client. */ fromlen = sizeof(from); memset(&from, 0, sizeof(from)); if (getpeername(socket, (struct sockaddr *) & from, &fromlen) < 0) { debug("getpeername failed: %.100s", strerror(errno)); fatal_cleanup(); } if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed"); /* Map the IP address to a host name. */ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), NULL, 0, NI_NAMEREQD) == 0) { /* Got host name. */ name[sizeof(name) - 1] = '\0'; /* * Convert it to all lowercase (which is expected by the rest * of this software). */ for (i = 0; name[i]; i++) if (isupper(name[i])) name[i] = tolower(name[i]); /* * Map it back to an IP address and check that the given * address actually is an address of this host. This is * necessary because anyone with access to a name server can * define arbitrary names for an IP address. Mapping from * name to IP address can be trusted better (but can still be * fooled if the intruder has access to the name server of * the domain). */ memset(&hints, 0, sizeof(hints)); hints.ai_family = from.ss_family; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { log("reverse mapping checking getaddrinfo for %.700s failed - POSSIBLE BREAKIN ATTEMPT!", name); strlcpy(name, ntop, sizeof name); goto check_ip_options; } /* Look for the address from the list of addresses. */ for (ai = aitop; ai; ai = ai->ai_next) { if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && (strcmp(ntop, ntop2) == 0)) break; } freeaddrinfo(aitop); /* If we reached the end of the list, the address was not there. */ if (!ai) { /* Address not found for the host name. */ log("Address %.100s maps to %.600s, but this does not map back to the address - POSSIBLE BREAKIN ATTEMPT!", ntop, name); strlcpy(name, ntop, sizeof name); goto check_ip_options; } /* Address was found for the host name. We accept the host name. */ } else { /* Host name not found. Use ascii representation of the address. */ strlcpy(name, ntop, sizeof name); log("Could not reverse map address %.100s.", name); } check_ip_options: /* * If IP options are supported, make sure there are none (log and * disconnect them if any are found). Basically we are worried about * source routing; it can be used to pretend you are somebody * (ip-address) you are not. That itself may be "almost acceptable" * under certain circumstances, but rhosts autentication is useless * if source routing is accepted. Notice also that if we just dropped * source routing here, the other side could use IP spoofing to do * rest of the interaction and could still bypass security. So we * exit here if we detect any IP options. */ /* IP options -- IPv4 only */ if (from.ss_family == AF_INET) { unsigned char options[200], *ucp; char text[1024], *cp; socklen_t option_size; int ipproto; struct protoent *ip; if ((ip = getprotobyname("ip")) != NULL) ipproto = ip->p_proto; else ipproto = IPPROTO_IP; option_size = sizeof(options); if (getsockopt(0, ipproto, IP_OPTIONS, (char *) options, &option_size) >= 0 && option_size != 0) { cp = text; /* Note: "text" buffer must be at least 3x as big as options. */ for (ucp = options; option_size > 0; ucp++, option_size--, cp += 3) sprintf(cp, " %2.2x", *ucp); log("Connection from %.100s with IP options:%.800s", ntop, text); packet_disconnect("Connection from %.100s with IP options:%.800s", ntop, text); } } return xstrdup(name); } /* * Return the canonical name of the host in the other side of the current * connection. The host name is cached, so it is efficient to call this * several times. */ const char * get_canonical_hostname() { static char *canonical_host_name = NULL; /* Check if we have previously retrieved this same name. */ if (canonical_host_name != NULL) return canonical_host_name; /* Get the real hostname if socket; otherwise return UNKNOWN. */ if (packet_connection_is_on_socket()) canonical_host_name = get_remote_hostname(packet_get_connection_in()); else canonical_host_name = xstrdup("UNKNOWN"); return canonical_host_name; } /* * Returns the IP-address of the remote host as a string. The returned * string must not be freed. */ const char * get_remote_ipaddr() { static char *canonical_host_ip = NULL; struct sockaddr_storage from; socklen_t fromlen; int socket; char ntop[NI_MAXHOST]; /* Check whether we have chached the name. */ if (canonical_host_ip != NULL) return canonical_host_ip; /* If not a socket, return UNKNOWN. */ if (!packet_connection_is_on_socket()) { canonical_host_ip = xstrdup("UNKNOWN"); return canonical_host_ip; } /* Get client socket. */ socket = packet_get_connection_in(); /* Get IP address of client. */ fromlen = sizeof(from); memset(&from, 0, sizeof(from)); if (getpeername(socket, (struct sockaddr *) & from, &fromlen) < 0) { debug("getpeername failed: %.100s", strerror(errno)); fatal_cleanup(); } /* Get the IP address in ascii. */ if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) - fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed"); + fatal("get_remote_ipaddr: getnameinfo NI_NUMERICHOST failed"); canonical_host_ip = xstrdup(ntop); /* Return ip address string. */ return canonical_host_ip; +} + +/* + * Returns the IP-address of the local host as a string. The returned + * string must be freed. + */ + +const char * +get_ipaddr(int socket) +{ + static char *canonical_host_ip = NULL; + struct sockaddr_storage from; + socklen_t fromlen; + char ntop[NI_MAXHOST]; + + /* Get IP address of server. */ + fromlen = sizeof(from); + memset(&from, 0, sizeof(from)); + if (getsockname(socket, (struct sockaddr *)&from, &fromlen) < 0) { + debug("getsockname failed: %.100s", strerror(errno)); + fatal_cleanup(); + } + /* Get the IP address in ascii. */ + if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop), + NULL, 0, NI_NUMERICHOST) != 0) + fatal("get_local_ipaddr: getnameinfo NI_NUMERICHOST failed"); + + /* Return ip address string. */ + return xstrdup(ntop); } /* Returns the local/remote port for the socket. */ int get_sock_port(int sock, int local) { struct sockaddr_storage from; socklen_t fromlen; char strport[NI_MAXSERV]; /* Get IP address of client. */ fromlen = sizeof(from); memset(&from, 0, sizeof(from)); if (local) { if (getsockname(sock, (struct sockaddr *)&from, &fromlen) < 0) { error("getsockname failed: %.100s", strerror(errno)); return 0; } } else { if (getpeername(sock, (struct sockaddr *) & from, &fromlen) < 0) { debug("getpeername failed: %.100s", strerror(errno)); fatal_cleanup(); } } /* Return port number. */ if (getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0, strport, sizeof(strport), NI_NUMERICSERV) != 0) fatal("get_sock_port: getnameinfo NI_NUMERICSERV failed"); return atoi(strport); } /* Returns remote/local port number for the current connection. */ int get_port(int local) { /* * If the connection is not a socket, return 65535. This is * intentionally chosen to be an unprivileged port number. */ if (!packet_connection_is_on_socket()) return 65535; /* Get socket and return the port number. */ return get_sock_port(packet_get_connection_in(), local); } int get_peer_port(int sock) { return get_sock_port(sock, 0); } int get_remote_port() { return get_port(0); } int get_local_port() { return get_port(1); } Index: head/crypto/openssh/ssh.h =================================================================== --- head/crypto/openssh/ssh.h (revision 62100) +++ head/crypto/openssh/ssh.h (revision 62101) @@ -1,531 +1,536 @@ /* * * ssh.h * * Author: Tatu Ylonen * * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * Created: Fri Mar 17 17:09:37 1995 ylo * * Generic header file for ssh. * * $FreeBSD$ */ /* RCSID("$Id: ssh.h,v 1.46 2000/05/17 08:20:15 markus Exp $"); */ #ifndef SSH_H #define SSH_H #include "rsa.h" #include "cipher.h" /* * XXX * The default cipher used if IDEA is not supported by the remote host. It is * recommended that this be one of the mandatory ciphers (DES, 3DES), though * that is not required. */ #define SSH_FALLBACK_CIPHER SSH_CIPHER_3DES /* Cipher used for encrypting authentication files. */ #define SSH_AUTHFILE_CIPHER SSH_CIPHER_3DES /* Default port number. */ #define SSH_DEFAULT_PORT 22 /* Maximum number of TCP/IP ports forwarded per direction. */ #define SSH_MAX_FORWARDS_PER_DIRECTION 100 /* * Maximum number of RSA authentication identity files that can be specified * in configuration files or on the command line. */ #define SSH_MAX_IDENTITY_FILES 100 /* * Major protocol version. Different version indicates major incompatiblity * that prevents communication. * * Minor protocol version. Different version indicates minor incompatibility * that does not prevent interoperation. */ #define PROTOCOL_MAJOR_1 1 #define PROTOCOL_MINOR_1 5 /* We support both SSH1 and SSH2 */ #define PROTOCOL_MAJOR_2 2 #define PROTOCOL_MINOR_2 0 /* * Name for the service. The port named by this service overrides the * default port if present. */ #define SSH_SERVICE_NAME "ssh" #define ETCDIR "/etc/ssh" #define PIDDIR "/var/run" /* * System-wide file containing host keys of known hosts. This file should be * world-readable. */ #define SSH_SYSTEM_HOSTFILE ETCDIR "/ssh_known_hosts" #define SSH_SYSTEM_HOSTFILE2 ETCDIR "/ssh_known_hosts2" /* * Of these, ssh_host_key must be readable only by root, whereas ssh_config * should be world-readable. */ #define HOST_KEY_FILE ETCDIR "/ssh_host_key" #define SERVER_CONFIG_FILE ETCDIR "/sshd_config" #define HOST_CONFIG_FILE ETCDIR "/ssh_config" #define HOST_DSA_KEY_FILE ETCDIR "/ssh_host_dsa_key" #define SSH_PROGRAM "/usr/bin/ssh" /* * The process id of the daemon listening for connections is saved here to * make it easier to kill the correct daemon when necessary. */ #define SSH_DAEMON_PID_FILE PIDDIR "/sshd.pid" /* * The directory in user\'s home directory in which the files reside. The * directory should be world-readable (though not all files are). */ #define SSH_USER_DIR ".ssh" /* * Per-user file containing host keys of known hosts. This file need not be * readable by anyone except the user him/herself, though this does not * contain anything particularly secret. */ #define SSH_USER_HOSTFILE "~/.ssh/known_hosts" #define SSH_USER_HOSTFILE2 "~/.ssh/known_hosts2" /* * Name of the default file containing client-side authentication key. This * file should only be readable by the user him/herself. */ #define SSH_CLIENT_IDENTITY ".ssh/identity" #define SSH_CLIENT_ID_DSA ".ssh/id_dsa" /* * Configuration file in user\'s home directory. This file need not be * readable by anyone but the user him/herself, but does not contain anything * particularly secret. If the user\'s home directory resides on an NFS * volume where root is mapped to nobody, this may need to be world-readable. */ #define SSH_USER_CONFFILE ".ssh/config" /* * File containing a list of those rsa keys that permit logging in as this * user. This file need not be readable by anyone but the user him/herself, * but does not contain anything particularly secret. If the user\'s home * directory resides on an NFS volume where root is mapped to nobody, this * may need to be world-readable. (This file is read by the daemon which is * running as root.) */ #define SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" #define SSH_USER_PERMITTED_KEYS2 ".ssh/authorized_keys2" /* * Per-user and system-wide ssh "rc" files. These files are executed with * /bin/sh before starting the shell or command if they exist. They will be * passed "proto cookie" as arguments if X11 forwarding with spoofing is in * use. xauth will be run if neither of these exists. */ #define SSH_USER_RC ".ssh/rc" #define SSH_SYSTEM_RC ETCDIR "/sshrc" /* * Ssh-only version of /etc/hosts.equiv. Additionally, the daemon may use * ~/.rhosts and /etc/hosts.equiv if rhosts authentication is enabled. */ #define SSH_HOSTS_EQUIV ETCDIR "/shosts.equiv" /* * Name of the environment variable containing the pathname of the * authentication socket. */ #define SSH_AUTHSOCKET_ENV_NAME "SSH_AUTH_SOCK" /* * Name of the environment variable containing the pathname of the * authentication socket. */ #define SSH_AGENTPID_ENV_NAME "SSH_AGENT_PID" /* * Default path to ssh-askpass used by ssh-add, * environment variable for overwriting the default location */ #define SSH_ASKPASS_DEFAULT "/usr/X11R6/bin/ssh-askpass" #define SSH_ASKPASS_ENV "SSH_ASKPASS" /* * Force host key length and server key length to differ by at least this * many bits. This is to make double encryption with rsaref work. */ #define SSH_KEY_BITS_RESERVED 128 /* * Length of the session key in bytes. (Specified as 256 bits in the * protocol.) */ #define SSH_SESSION_KEY_LENGTH 32 /* Name of Kerberos service for SSH to use. */ #define KRB4_SERVICE_NAME "rcmd" /* * Authentication methods. New types can be added, but old types should not * be removed for compatibility. The maximum allowed value is 31. */ #define SSH_AUTH_RHOSTS 1 #define SSH_AUTH_RSA 2 #define SSH_AUTH_PASSWORD 3 #define SSH_AUTH_RHOSTS_RSA 4 #define SSH_AUTH_TIS 5 #define SSH_AUTH_KRB4 6 #define SSH_PASS_KRB4_TGT 7 /* 8 to 15 are reserved */ #define SSH_PASS_AFS_TOKEN 21 #define SSH_AUTH_KRB5 29 #define SSH_PASS_KRB5_TGT 30 /* Protocol flags. These are bit masks. */ #define SSH_PROTOFLAG_SCREEN_NUMBER 1 /* X11 forwarding includes screen */ #define SSH_PROTOFLAG_HOST_IN_FWD_OPEN 2 /* forwarding opens contain host */ /* * Definition of message types. New values can be added, but old values * should not be removed or without careful consideration of the consequences * for compatibility. The maximum value is 254; value 255 is reserved for * future extension. */ /* Message name */ /* msg code */ /* arguments */ #define SSH_MSG_NONE 0 /* no message */ #define SSH_MSG_DISCONNECT 1 /* cause (string) */ #define SSH_SMSG_PUBLIC_KEY 2 /* ck,msk,srvk,hostk */ #define SSH_CMSG_SESSION_KEY 3 /* key (BIGNUM) */ #define SSH_CMSG_USER 4 /* user (string) */ #define SSH_CMSG_AUTH_RHOSTS 5 /* user (string) */ #define SSH_CMSG_AUTH_RSA 6 /* modulus (BIGNUM) */ #define SSH_SMSG_AUTH_RSA_CHALLENGE 7 /* int (BIGNUM) */ #define SSH_CMSG_AUTH_RSA_RESPONSE 8 /* int (BIGNUM) */ #define SSH_CMSG_AUTH_PASSWORD 9 /* pass (string) */ #define SSH_CMSG_REQUEST_PTY 10 /* TERM, tty modes */ #define SSH_CMSG_WINDOW_SIZE 11 /* row,col,xpix,ypix */ #define SSH_CMSG_EXEC_SHELL 12 /* */ #define SSH_CMSG_EXEC_CMD 13 /* cmd (string) */ #define SSH_SMSG_SUCCESS 14 /* */ #define SSH_SMSG_FAILURE 15 /* */ #define SSH_CMSG_STDIN_DATA 16 /* data (string) */ #define SSH_SMSG_STDOUT_DATA 17 /* data (string) */ #define SSH_SMSG_STDERR_DATA 18 /* data (string) */ #define SSH_CMSG_EOF 19 /* */ #define SSH_SMSG_EXITSTATUS 20 /* status (int) */ #define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 21 /* channel (int) */ #define SSH_MSG_CHANNEL_OPEN_FAILURE 22 /* channel (int) */ #define SSH_MSG_CHANNEL_DATA 23 /* ch,data (int,str) */ #define SSH_MSG_CHANNEL_CLOSE 24 /* channel (int) */ #define SSH_MSG_CHANNEL_CLOSE_CONFIRMATION 25 /* channel (int) */ /* SSH_CMSG_X11_REQUEST_FORWARDING 26 OBSOLETE */ #define SSH_SMSG_X11_OPEN 27 /* channel (int) */ #define SSH_CMSG_PORT_FORWARD_REQUEST 28 /* p,host,hp (i,s,i) */ #define SSH_MSG_PORT_OPEN 29 /* ch,h,p (i,s,i) */ #define SSH_CMSG_AGENT_REQUEST_FORWARDING 30 /* */ #define SSH_SMSG_AGENT_OPEN 31 /* port (int) */ #define SSH_MSG_IGNORE 32 /* string */ #define SSH_CMSG_EXIT_CONFIRMATION 33 /* */ #define SSH_CMSG_X11_REQUEST_FORWARDING 34 /* proto,data (s,s) */ #define SSH_CMSG_AUTH_RHOSTS_RSA 35 /* user,mod (s,mpi) */ #define SSH_MSG_DEBUG 36 /* string */ #define SSH_CMSG_REQUEST_COMPRESSION 37 /* level 1-9 (int) */ #define SSH_CMSG_MAX_PACKET_SIZE 38 /* size 4k-1024k (int) */ #define SSH_CMSG_AUTH_TIS 39 /* we use this for s/key */ #define SSH_SMSG_AUTH_TIS_CHALLENGE 40 /* challenge (string) */ #define SSH_CMSG_AUTH_TIS_RESPONSE 41 /* response (string) */ #define SSH_CMSG_AUTH_KRB4 42 /* (KTEXT) */ #define SSH_SMSG_AUTH_KRB4_RESPONSE 43 /* (KTEXT) */ #define SSH_CMSG_HAVE_KRB4_TGT 44 /* credentials (s) */ #define SSH_CMSG_HAVE_AFS_TOKEN 65 /* token (s) */ #define SSH_CMSG_AUTH_KRB5 110 #define SSH_SMSG_AUTH_KRB5_RESPONSE 111 #define SSH_CMSG_HAVE_KRB5_TGT 112 /*------------ definitions for login.c -------------*/ /* * Returns the time when the user last logged in. Returns 0 if the * information is not available. This must be called before record_login. * The host from which the user logged in is stored in buf. */ unsigned long get_last_login_time(uid_t uid, const char *logname, char *buf, unsigned int bufsize); /* * Records that the user has logged in. This does many things normally done * by login(1). */ void record_login(pid_t pid, const char *ttyname, const char *user, uid_t uid, const char *host, struct sockaddr *addr); /* * Records that the user has logged out. This does many thigs normally done * by login(1) or init. */ void record_logout(pid_t pid, const char *ttyname); /*------------ definitions for sshconnect.c ----------*/ /* * Opens a TCP/IP connection to the remote server on the given host. If port * is 0, the default port will be used. If anonymous is zero, a privileged * port will be allocated to make the connection. This requires super-user * privileges if anonymous is false. Connection_attempts specifies the * maximum number of tries, one per second. This returns true on success, * and zero on failure. If the connection is successful, this calls * packet_set_connection for the connection. */ int ssh_connect(const char *host, struct sockaddr_storage * hostaddr, u_short port, int connection_attempts, int anonymous, uid_t original_real_uid, const char *proxy_command); /* * Starts a dialog with the server, and authenticates the current user on the * server. This does not need any extra privileges. The basic connection to * the server must already have been established before this is called. If * login fails, this function prints an error and never returns. This * initializes the random state, and leaves it initialized (it will also have * references from the packet module). */ void ssh_login(int host_key_valid, RSA * host_key, const char *host, struct sockaddr * hostaddr, uid_t original_real_uid); /*------------ Definitions for various authentication methods. -------*/ /* * Tries to authenticate the user using the .rhosts file. Returns true if * authentication succeeds. If ignore_rhosts is non-zero, this will not * consider .rhosts and .shosts (/etc/hosts.equiv will still be used). */ int auth_rhosts(struct passwd * pw, const char *client_user); /* * Tries to authenticate the user using the .rhosts file and the host using * its host key. Returns true if authentication succeeds. */ int auth_rhosts_rsa(struct passwd * pw, const char *client_user, RSA* client_host_key); /* * Tries to authenticate the user using password. Returns true if * authentication succeeds. */ int auth_password(struct passwd * pw, const char *password); /* * Performs the RSA authentication dialog with the client. This returns 0 if * the client could not be authenticated, and 1 if authentication was * successful. This may exit if there is a serious protocol violation. */ int auth_rsa(struct passwd * pw, BIGNUM * client_n); /* * Parses an RSA key (number of bits, e, n) from a string. Moves the pointer * over the key. Skips any whitespace at the beginning and at end. */ int auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n); /* * Returns the name of the machine at the other end of the socket. The * returned string should be freed by the caller. */ char *get_remote_hostname(int socket); /* * Return the canonical name of the host in the other side of the current * connection (as returned by packet_get_connection). The host name is * cached, so it is efficient to call this several times. */ const char *get_canonical_hostname(void); /* + * Returns the local IP address as an ascii string. + */ +const char *get_ipaddr(int socket); + +/* * Returns the remote IP address as an ascii string. The value need not be * freed by the caller. */ const char *get_remote_ipaddr(void); /* Returns the port number of the peer of the socket. */ int get_peer_port(int sock); /* Returns the port number of the remote/local host. */ int get_remote_port(void); int get_local_port(void); /* * Performs the RSA authentication challenge-response dialog with the client, * and returns true (non-zero) if the client gave the correct answer to our * challenge; returns zero if the client gives a wrong answer. */ int auth_rsa_challenge_dialog(RSA *pk); /* * Reads a passphrase from /dev/tty with echo turned off. Returns the * passphrase (allocated with xmalloc). Exits if EOF is encountered. If * from_stdin is true, the passphrase will be read from stdin instead. */ char *read_passphrase(const char *prompt, int from_stdin); /*------------ Definitions for logging. -----------------------*/ /* Supported syslog facilities and levels. */ typedef enum { SYSLOG_FACILITY_DAEMON, SYSLOG_FACILITY_USER, SYSLOG_FACILITY_AUTH, SYSLOG_FACILITY_LOCAL0, SYSLOG_FACILITY_LOCAL1, SYSLOG_FACILITY_LOCAL2, SYSLOG_FACILITY_LOCAL3, SYSLOG_FACILITY_LOCAL4, SYSLOG_FACILITY_LOCAL5, SYSLOG_FACILITY_LOCAL6, SYSLOG_FACILITY_LOCAL7 } SyslogFacility; typedef enum { SYSLOG_LEVEL_QUIET, SYSLOG_LEVEL_FATAL, SYSLOG_LEVEL_ERROR, SYSLOG_LEVEL_INFO, SYSLOG_LEVEL_VERBOSE, SYSLOG_LEVEL_DEBUG } LogLevel; /* Initializes logging. */ void log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr); /* Logging implementation, depending on server or client */ void do_log(LogLevel level, const char *fmt, va_list args); /* name to facility/level */ SyslogFacility log_facility_number(char *name); LogLevel log_level_number(char *name); /* Output a message to syslog or stderr */ void fatal(const char *fmt,...) __attribute__((format(printf, 1, 2))); void error(const char *fmt,...) __attribute__((format(printf, 1, 2))); void log(const char *fmt,...) __attribute__((format(printf, 1, 2))); void verbose(const char *fmt,...) __attribute__((format(printf, 1, 2))); void debug(const char *fmt,...) __attribute__((format(printf, 1, 2))); /* same as fatal() but w/o logging */ void fatal_cleanup(void); /* * Registers a cleanup function to be called by fatal()/fatal_cleanup() * before exiting. It is permissible to call fatal_remove_cleanup for the * function itself from the function. */ void fatal_add_cleanup(void (*proc) (void *context), void *context); /* Removes a cleanup function to be called at fatal(). */ void fatal_remove_cleanup(void (*proc) (void *context), void *context); /* ---- misc */ /* * Expands tildes in the file name. Returns data allocated by xmalloc. * Warning: this calls getpw*. */ char *tilde_expand_filename(const char *filename, uid_t my_uid); /* remove newline at end of string */ char *chop(char *s); /* set filedescriptor to non-blocking */ void set_nonblock(int fd); /* * Performs the interactive session. This handles data transmission between * the client and the program. Note that the notion of stdin, stdout, and * stderr in this function is sort of reversed: this function writes to stdin * (of the child program), and reads from stdout and stderr (of the child * program). */ void server_loop(pid_t pid, int fdin, int fdout, int fderr); void server_loop2(void); /* Client side main loop for the interactive session. */ int client_loop(int have_pty, int escape_char); /* Linked list of custom environment strings (see auth-rsa.c). */ struct envstring { struct envstring *next; char *s; }; /* * Ensure all of data on socket comes through. f==read || f==write */ ssize_t atomicio(ssize_t (*f)(), int fd, void *s, size_t n); #ifdef KRB5 #include int auth_krb5(); /* XXX Doplnit prototypy */ int auth_krb5_tgt(); int krb5_init(); void krb5_cleanup_proc(void *ignore); int auth_krb5_password(struct passwd *pw, const char *password); #endif /* KRB5 */ #ifdef KRB4 #include /* * Performs Kerberos v4 mutual authentication with the client. This returns 0 * if the client could not be authenticated, and 1 if authentication was * successful. This may exit if there is a serious protocol violation. */ int auth_krb4(const char *server_user, KTEXT auth, char **client); int krb4_init(uid_t uid); void krb4_cleanup_proc(void *ignore); int auth_krb4_password(struct passwd * pw, const char *password); #ifdef AFS #include /* Accept passed Kerberos v4 ticket-granting ticket and AFS tokens. */ int auth_krb4_tgt(struct passwd * pw, const char *string); int auth_afs_token(struct passwd * pw, const char *token_string); int creds_to_radix(CREDENTIALS * creds, unsigned char *buf, size_t buflen); int radix_to_creds(const char *buf, CREDENTIALS * creds); #endif /* AFS */ #endif /* KRB4 */ #ifdef SKEY #include char *skey_fake_keyinfo(char *username); int auth_skey_password(struct passwd * pw, const char *password); #endif /* SKEY */ /* AF_UNSPEC or AF_INET or AF_INET6 */ extern int IPv4or6; #endif /* SSH_H */ Index: head/crypto/openssh/sshd.c =================================================================== --- head/crypto/openssh/sshd.c (revision 62100) +++ head/crypto/openssh/sshd.c (revision 62101) @@ -1,1378 +1,1381 @@ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Created: Fri Mar 17 17:09:28 1995 ylo * This program is the ssh daemon. It listens for connections from clients, and * performs authentication, executes use commands or shell, and forwards * information to/from the application to the user client over an encrypted * connection. This can also handle forwarding of X11, TCP/IP, and authentication * agent connections. * * SSH2 implementation, * Copyright (c) 2000 Markus Friedl. All rights reserved. * * $FreeBSD$ */ #include "includes.h" RCSID("$OpenBSD: sshd.c,v 1.118 2000/05/25 20:45:20 markus Exp $"); #include "xmalloc.h" #include "rsa.h" #include "ssh.h" #include "pty.h" #include "packet.h" #include "cipher.h" #include "mpaux.h" #include "servconf.h" #include "uidswap.h" #include "compat.h" #include "buffer.h" #include #include #include "ssh2.h" #include #include #include #include "kex.h" #include #include #include "key.h" #include "dsa.h" #include "auth.h" #include "myproposal.h" #include "authfile.h" #ifdef LIBWRAP #include #include int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; #endif /* LIBWRAP */ #ifndef O_NOCTTY #define O_NOCTTY 0 #endif #ifdef KRB5 #include #endif /* KRB5 */ /* Server configuration options. */ ServerOptions options; /* Name of the server configuration file. */ char *config_file_name = SERVER_CONFIG_FILE; /* * Flag indicating whether IPv4 or IPv6. This can be set on the command line. * Default value is AF_UNSPEC means both IPv4 and IPv6. */ int IPv4or6 = AF_UNSPEC; /* * Debug mode flag. This can be set on the command line. If debug * mode is enabled, extra debugging output will be sent to the system * log, the daemon will not go to background, and will exit after processing * the first connection. */ int debug_flag = 0; /* Flag indicating that the daemon is being started from inetd. */ int inetd_flag = 0; /* debug goes to stderr unless inetd_flag is set */ int log_stderr = 0; /* argv[0] without path. */ char *av0; /* Saved arguments to main(). */ char **saved_argv; /* * The sockets that the server is listening; this is used in the SIGHUP * signal handler. */ #define MAX_LISTEN_SOCKS 16 int listen_socks[MAX_LISTEN_SOCKS]; int num_listen_socks = 0; /* * the client's version string, passed by sshd2 in compat mode. if != NULL, * sshd will skip the version-number exchange */ char *client_version_string = NULL; char *server_version_string = NULL; /* * Any really sensitive data in the application is contained in this * structure. The idea is that this structure could be locked into memory so * that the pages do not get written into swap. However, there are some * problems. The private key contains BIGNUMs, and we do not (in principle) * have access to the internals of them, and locking just the structure is * not very useful. Currently, memory locking is not implemented. */ struct { RSA *private_key; /* Private part of empheral server key. */ RSA *host_key; /* Private part of host key. */ Key *dsa_host_key; /* Private DSA host key. */ } sensitive_data; /* * Flag indicating whether the current session key has been used. This flag * is set whenever the key is used, and cleared when the key is regenerated. */ int key_used = 0; /* This is set to true when SIGHUP is received. */ int received_sighup = 0; /* Public side of the server key. This value is regenerated regularly with the private key. */ RSA *public_key; /* session identifier, used by RSA-auth */ unsigned char session_id[16]; /* same for ssh2 */ unsigned char *session_id2 = NULL; int session_id2_len = 0; /* These are used to implement connections_per_period. */ -struct magic_connection { +struct ratelim_connection { struct timeval connections_begin; unsigned int connections_this_period; -} *magic_connections; -/* Magic number, too! TODO: this doesn't have to be static. */ -const size_t MAGIC_CONNECTIONS_SIZE = 1; +} *ratelim_connections; -static __inline int -magic_hash(struct sockaddr *sa) { - - return 0; +static void +ratelim_init(void) { + ratelim_connections = calloc(num_listen_socks, + sizeof(struct ratelim_connection)); + if (ratelim_connections == NULL) + fatal("calloc: %s", strerror(errno)); } static __inline struct timeval timevaldiff(struct timeval *tv1, struct timeval *tv2) { struct timeval diff; int carry; carry = tv1->tv_usec > tv2->tv_usec; diff.tv_sec = tv2->tv_sec - tv1->tv_sec - (carry ? 0 : 1); diff.tv_usec = tv2->tv_usec - tv1->tv_usec + (carry ? 1000000 : 0); return diff; } /* Prototypes for various functions defined later in this file. */ void do_ssh1_kex(); void do_ssh2_kex(); /* * Close all listening sockets */ void close_listen_socks(void) { int i; for (i = 0; i < num_listen_socks; i++) close(listen_socks[i]); num_listen_socks = -1; } /* * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; * the effect is to reread the configuration file (and to regenerate * the server key). */ void sighup_handler(int sig) { received_sighup = 1; signal(SIGHUP, sighup_handler); } /* * Called from the main program after receiving SIGHUP. * Restarts the server. */ void sighup_restart() { log("Received SIGHUP; restarting."); close_listen_socks(); execv(saved_argv[0], saved_argv); log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno)); exit(1); } /* * Generic signal handler for terminating signals in the master daemon. * These close the listen socket; not closing it seems to cause "Address * already in use" problems on some machines, which is inconvenient. */ void sigterm_handler(int sig) { log("Received signal %d; terminating.", sig); close_listen_socks(); unlink(options.pid_file); exit(255); } /* * SIGCHLD handler. This is called whenever a child dies. This will then * reap any zombies left by exited c. */ void main_sigchld_handler(int sig) { int save_errno = errno; int status; while (waitpid(-1, &status, WNOHANG) > 0) ; signal(SIGCHLD, main_sigchld_handler); errno = save_errno; } /* * Signal handler for the alarm after the login grace period has expired. */ void grace_alarm_handler(int sig) { /* Close the connection. */ packet_close(); /* Log error and exit. */ fatal("Timeout before authentication for %s.", get_remote_ipaddr()); } /* * Signal handler for the key regeneration alarm. Note that this * alarm only occurs in the daemon waiting for connections, and it does not * do anything with the private key or random state before forking. * Thus there should be no concurrency control/asynchronous execution * problems. */ /* XXX do we really want this work to be done in a signal handler ? -m */ void key_regeneration_alarm(int sig) { int save_errno = errno; /* Check if we should generate a new key. */ if (key_used) { /* This should really be done in the background. */ log("Generating new %d bit RSA key.", options.server_key_bits); if (sensitive_data.private_key != NULL) RSA_free(sensitive_data.private_key); sensitive_data.private_key = RSA_new(); if (public_key != NULL) RSA_free(public_key); public_key = RSA_new(); rsa_generate_key(sensitive_data.private_key, public_key, options.server_key_bits); arc4random_stir(); key_used = 0; log("RSA key generation complete."); } /* Reschedule the alarm. */ signal(SIGALRM, key_regeneration_alarm); alarm(options.key_regeneration_time); errno = save_errno; } void sshd_exchange_identification(int sock_in, int sock_out) { int i, mismatch; int remote_major, remote_minor; int major, minor; char *s; char buf[256]; /* Must not be larger than remote_version. */ char remote_version[256]; /* Must be at least as big as buf. */ if ((options.protocol & SSH_PROTO_1) && (options.protocol & SSH_PROTO_2)) { major = PROTOCOL_MAJOR_1; minor = 99; } else if (options.protocol & SSH_PROTO_2) { major = PROTOCOL_MAJOR_2; minor = PROTOCOL_MINOR_2; } else { major = PROTOCOL_MAJOR_1; minor = PROTOCOL_MINOR_1; } snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION); server_version_string = xstrdup(buf); if (client_version_string == NULL) { /* Send our protocol version identification. */ if (atomicio(write, sock_out, server_version_string, strlen(server_version_string)) != strlen(server_version_string)) { log("Could not write ident string to %s.", get_remote_ipaddr()); fatal_cleanup(); } /* Read other side\'s version identification. */ for (i = 0; i < sizeof(buf) - 1; i++) { if (read(sock_in, &buf[i], 1) != 1) { log("Did not receive ident string from %s.", get_remote_ipaddr()); fatal_cleanup(); } if (buf[i] == '\r') { buf[i] = '\n'; buf[i + 1] = 0; continue; } if (buf[i] == '\n') { /* buf[i] == '\n' */ buf[i + 1] = 0; break; } } buf[sizeof(buf) - 1] = 0; client_version_string = xstrdup(buf); } /* * Check that the versions match. In future this might accept * several versions and set appropriate flags to handle them. */ if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) != 3) { s = "Protocol mismatch.\n"; (void) atomicio(write, sock_out, s, strlen(s)); close(sock_in); close(sock_out); log("Bad protocol version identification '%.100s' from %s", client_version_string, get_remote_ipaddr()); fatal_cleanup(); } debug("Client protocol version %d.%d; client software version %.100s", remote_major, remote_minor, remote_version); compat_datafellows(remote_version); mismatch = 0; switch(remote_major) { case 1: if (remote_minor == 99) { if (options.protocol & SSH_PROTO_2) enable_compat20(); else mismatch = 1; break; } if (!(options.protocol & SSH_PROTO_1)) { mismatch = 1; break; } if (remote_minor < 3) { packet_disconnect("Your ssh version is too old and" "is no longer supported. Please install a newer version."); } else if (remote_minor == 3) { /* note that this disables agent-forwarding */ enable_compat13(); } break; case 2: if (options.protocol & SSH_PROTO_2) { enable_compat20(); break; } /* FALLTHROUGH */ default: mismatch = 1; break; } chop(server_version_string); chop(client_version_string); debug("Local version string %.200s", server_version_string); if (mismatch) { s = "Protocol major versions differ.\n"; (void) atomicio(write, sock_out, s, strlen(s)); close(sock_in); close(sock_out); log("Protocol major versions differ for %s: %.200s vs. %.200s", get_remote_ipaddr(), server_version_string, client_version_string); fatal_cleanup(); } if (compat20) packet_set_ssh2_format(); } void destroy_sensitive_data(void) { /* Destroy the private and public keys. They will no longer be needed. */ if (public_key) RSA_free(public_key); if (sensitive_data.private_key) RSA_free(sensitive_data.private_key); if (sensitive_data.host_key) RSA_free(sensitive_data.host_key); if (sensitive_data.dsa_host_key != NULL) key_free(sensitive_data.dsa_host_key); } /* * Main program for the daemon. */ int main(int ac, char **av) { extern char *optarg; extern int optind; int opt, sock_in = 0, sock_out = 0, newsock, i, fdsetsz, on = 1; pid_t pid; socklen_t fromlen; + int ratelim_exceeded = 0; int silent = 0; fd_set *fdset; struct sockaddr_storage from; const char *remote_ip; int remote_port; FILE *f; struct linger linger; struct addrinfo *ai; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; int listen_sock, maxfd; - int connections_per_period_exceeded = 0; /* Save argv[0]. */ saved_argv = av; if (strchr(av[0], '/')) av0 = strrchr(av[0], '/') + 1; else av0 = av[0]; /* Initialize configuration options to their default values. */ initialize_server_options(&options); /* Parse command-line arguments. */ while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ46")) != EOF) { switch (opt) { case '4': IPv4or6 = AF_INET; break; case '6': IPv4or6 = AF_INET6; break; case 'f': config_file_name = optarg; break; case 'd': debug_flag = 1; options.log_level = SYSLOG_LEVEL_DEBUG; break; case 'i': inetd_flag = 1; break; case 'Q': silent = 1; break; case 'q': options.log_level = SYSLOG_LEVEL_QUIET; break; case 'b': options.server_key_bits = atoi(optarg); break; case 'p': options.ports_from_cmdline = 1; if (options.num_ports >= MAX_PORTS) fatal("too many ports.\n"); options.ports[options.num_ports++] = atoi(optarg); break; case 'g': options.login_grace_time = atoi(optarg); break; case 'k': options.key_regeneration_time = atoi(optarg); break; case 'h': options.host_key_file = optarg; break; case 'V': client_version_string = optarg; /* only makes sense with inetd_flag, i.e. no listen() */ inetd_flag = 1; break; case '?': default: fprintf(stderr, "sshd version %s\n", SSH_VERSION); fprintf(stderr, "Usage: %s [options]\n", av0); fprintf(stderr, "Options:\n"); fprintf(stderr, " -f file Configuration file (default %s)\n", SERVER_CONFIG_FILE); fprintf(stderr, " -d Debugging mode\n"); fprintf(stderr, " -i Started from inetd\n"); fprintf(stderr, " -q Quiet (no logging)\n"); fprintf(stderr, " -p port Listen on the specified port (default: 22)\n"); fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n"); fprintf(stderr, " -g seconds Grace period for authentication (default: 300)\n"); 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, " -4 Use IPv4 only\n"); fprintf(stderr, " -6 Use IPv6 only\n"); exit(1); } } /* * Force logging to stderr until we have loaded the private host * key (unless started from inetd) */ log_init(av0, options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility, !silent && !inetd_flag); /* Read server configuration options from the configuration file. */ read_server_config(&options, config_file_name); /* Fill in default values for those options not explicitly set. */ fill_default_server_options(&options); /* Check that there are no remaining arguments. */ if (optind < ac) { fprintf(stderr, "Extra argument %s.\n", av[optind]); exit(1); } debug("sshd version %.100s", SSH_VERSION); sensitive_data.dsa_host_key = NULL; sensitive_data.host_key = NULL; /* check if RSA support exists */ if ((options.protocol & SSH_PROTO_1) && rsa_alive() == 0) { log("no RSA support in libssl and libcrypto. See ssl(8)"); log("Disabling protocol version 1"); options.protocol &= ~SSH_PROTO_1; } /* Load the RSA/DSA host key. It must have empty passphrase. */ if (options.protocol & SSH_PROTO_1) { Key k; sensitive_data.host_key = RSA_new(); k.type = KEY_RSA; k.rsa = sensitive_data.host_key; errno = 0; if (!load_private_key(options.host_key_file, "", &k, NULL)) { error("Could not load host key: %.200s: %.100s", options.host_key_file, strerror(errno)); log("Disabling protocol version 1"); options.protocol &= ~SSH_PROTO_1; } k.rsa = NULL; } if (options.protocol & SSH_PROTO_2) { sensitive_data.dsa_host_key = key_new(KEY_DSA); if (!load_private_key(options.host_dsa_key_file, "", sensitive_data.dsa_host_key, NULL)) { error("Could not load DSA host key: %.200s", options.host_dsa_key_file); log("Disabling protocol version 2"); options.protocol &= ~SSH_PROTO_2; } } if (! options.protocol & (SSH_PROTO_1|SSH_PROTO_2)) { if (silent == 0) fprintf(stderr, "sshd: no hostkeys available -- exiting.\n"); log("sshd: no hostkeys available -- exiting.\n"); exit(1); } /* Check certain values for sanity. */ if (options.protocol & SSH_PROTO_1) { if (options.server_key_bits < 512 || options.server_key_bits > 32768) { fprintf(stderr, "Bad server key size.\n"); exit(1); } /* * Check that server and host key lengths differ sufficiently. This * is necessary to make double encryption work with rsaref. Oh, I * hate software patents. I dont know if this can go? Niels */ if (options.server_key_bits > BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && options.server_key_bits < BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { options.server_key_bits = BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; debug("Forcing server key to %d bits to make it differ from host key.", options.server_key_bits); } } /* Initialize the log (it is reinitialized below in case we forked). */ if (debug_flag && !inetd_flag) log_stderr = 1; log_init(av0, options.log_level, options.log_facility, log_stderr); /* * If not in debugging mode, and not started from inetd, disconnect * from the controlling terminal, and fork. The original process * exits. */ if (!debug_flag && !inetd_flag) { #ifdef TIOCNOTTY int fd; #endif /* TIOCNOTTY */ if (daemon(0, 0) < 0) fatal("daemon() failed: %.200s", strerror(errno)); /* Disconnect from the controlling tty. */ #ifdef TIOCNOTTY fd = open("/dev/tty", O_RDWR | O_NOCTTY); if (fd >= 0) { (void) ioctl(fd, TIOCNOTTY, NULL); close(fd); } #endif /* TIOCNOTTY */ } /* Reinitialize the log (because of the fork above). */ log_init(av0, options.log_level, options.log_facility, log_stderr); /* Do not display messages to stdout in RSA code. */ rsa_set_verbose(0); /* Initialize the random number generator. */ arc4random_stir(); /* Chdir to the root directory so that the current disk can be unmounted if desired. */ chdir("/"); /* Start listening for a socket, unless started from inetd. */ if (inetd_flag) { int s1, s2; s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ s2 = dup(s1); sock_in = dup(0); sock_out = dup(1); /* * We intentionally do not close the descriptors 0, 1, and 2 * as our code for setting the descriptors won\'t work if * ttyfd happens to be one of those. */ debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); if (options.protocol & SSH_PROTO_1) { public_key = RSA_new(); sensitive_data.private_key = RSA_new(); log("Generating %d bit RSA key.", options.server_key_bits); rsa_generate_key(sensitive_data.private_key, public_key, options.server_key_bits); arc4random_stir(); log("RSA key generation complete."); } } else { for (ai = options.listen_addrs; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; if (num_listen_socks >= MAX_LISTEN_SOCKS) fatal("Too many listen sockets. " "Enlarge MAX_LISTEN_SOCKS"); if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error("getnameinfo failed"); continue; } /* Create socket for listening. */ listen_sock = socket(ai->ai_family, SOCK_STREAM, 0); if (listen_sock < 0) { /* kernel may not support ipv6 */ verbose("socket: %.100s", strerror(errno)); continue; } if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) < 0) { error("listen_sock O_NONBLOCK: %s", strerror(errno)); close(listen_sock); continue; } /* * Set socket options. We try to make the port * reusable and have it close as fast as possible * without waiting in unnecessary wait states on * close. */ setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)); linger.l_onoff = 1; linger.l_linger = 5; setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); debug("Bind to port %s on %s.", strport, ntop); /* Bind the socket to the desired port. */ if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { error("Bind to port %s on %s failed: %.200s.", strport, ntop, strerror(errno)); close(listen_sock); continue; } listen_socks[num_listen_socks] = listen_sock; num_listen_socks++; /* Start listening on the port. */ log("Server listening on %s port %s.", ntop, strport); if (listen(listen_sock, 5) < 0) fatal("listen: %.100s", strerror(errno)); } freeaddrinfo(options.listen_addrs); if (!num_listen_socks) fatal("Cannot bind any address."); if (!debug_flag) { /* * Record our pid in /etc/sshd_pid to make it easier * to kill the correct sshd. We don\'t want to do * this before the bind above because the bind will * fail if there already is a daemon, and this will * overwrite any old pid in the file. */ f = fopen(options.pid_file, "w"); if (f) { fprintf(f, "%u\n", (unsigned int) getpid()); fclose(f); } } if (options.protocol & SSH_PROTO_1) { public_key = RSA_new(); sensitive_data.private_key = RSA_new(); log("Generating %d bit RSA key.", options.server_key_bits); rsa_generate_key(sensitive_data.private_key, public_key, options.server_key_bits); arc4random_stir(); log("RSA key generation complete."); /* Schedule server key regeneration alarm. */ signal(SIGALRM, key_regeneration_alarm); alarm(options.key_regeneration_time); } /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ signal(SIGHUP, sighup_handler); signal(SIGTERM, sigterm_handler); signal(SIGQUIT, sigterm_handler); /* Arrange SIGCHLD to be caught. */ signal(SIGCHLD, main_sigchld_handler); /* setup fd set for listen */ maxfd = 0; for (i = 0; i < num_listen_socks; i++) if (listen_socks[i] > maxfd) maxfd = listen_socks[i]; fdsetsz = howmany(maxfd, NFDBITS) * sizeof(fd_mask); fdset = (fd_set *)xmalloc(fdsetsz); - /* Initialize the magic_connections table. It's magical! */ - magic_connections = calloc(MAGIC_CONNECTIONS_SIZE, - sizeof(struct magic_connection)); - if (magic_connections == NULL) - fatal("calloc: %s", strerror(errno)); + ratelim_init(); /* * Stay listening for connections until the system crashes or * the daemon is killed with a signal. */ for (;;) { if (received_sighup) sighup_restart(); /* Wait in select until there is a connection. */ memset(fdset, 0, fdsetsz); for (i = 0; i < num_listen_socks; i++) FD_SET(listen_socks[i], fdset); if (select(maxfd + 1, fdset, NULL, NULL, NULL) < 0) { if (errno != EINTR) error("select: %.100s", strerror(errno)); continue; } for (i = 0; i < num_listen_socks; i++) { if (!FD_ISSET(listen_socks[i], fdset)) continue; fromlen = sizeof(from); newsock = accept(listen_socks[i], (struct sockaddr *)&from, &fromlen); if (newsock < 0) { if (errno != EINTR && errno != EWOULDBLOCK) error("accept: %.100s", strerror(errno)); continue; } if (fcntl(newsock, F_SETFL, 0) < 0) { error("newsock del O_NONBLOCK: %s", strerror(errno)); continue; } if (options.connections_per_period != 0) { struct timeval diff, connections_end; - struct magic_connection *mc; + struct ratelim_connection *rc; (void)gettimeofday(&connections_end, NULL); - mc = &magic_connections[magic_hash((struct sockaddr *)0)]; - diff = timevaldiff(&mc->connections_begin, &connections_end); + rc = &ratelim_connections[i]; + diff = timevaldiff(&rc->connections_begin, + &connections_end); if (diff.tv_sec >= options.connections_period) { /* - * Slide the window forward only after completely - * leaving it. + * Slide the window forward only after + * completely leaving it. */ - mc->connections_begin = connections_end; - mc->connections_this_period = 1; + rc->connections_begin = connections_end; + rc->connections_this_period = 1; } else { - if (++mc->connections_this_period > + if (++rc->connections_this_period > options.connections_per_period) - connections_per_period_exceeded = 1; + ratelim_exceeded = 1; } } /* * Got connection. Fork a child to handle it unless * we are in debugging mode or the maximum number of * connections per period has been exceeded. */ if (debug_flag) { /* * In debugging mode. Close the listening * socket, and start processing the * connection without forking. */ debug("Server will not fork when running in debugging mode."); close_listen_socks(); sock_in = newsock; sock_out = newsock; pid = getpid(); break; - } else if (connections_per_period_exceeded) { - log("Connection rate limit of %u/%us has been exceeded; " - "dropping connection from %s.", - options.connections_per_period, options.connections_period, - ntop); - connections_per_period_exceeded = 0; + } else if (ratelim_exceeded) { + const char *myaddr; + + myaddr = get_ipaddr(newsock); + log("rate limit (%u/%u) on %s port %d " + "exceeded by %s", + options.connections_per_period, + options.connections_period, myaddr, + get_sock_port(newsock, 1), ntop); + free((void *)myaddr); + ratelim_exceeded = 0; + continue; } else { /* * Normal production daemon. Fork, and have * the child process the connection. The * parent continues listening. */ if ((pid = fork()) == 0) { /* * Child. Close the listening socket, and start using the * accepted socket. Reinitialize logging (since our pid has * changed). We break out of the loop to handle the connection. */ close_listen_socks(); sock_in = newsock; sock_out = newsock; log_init(av0, options.log_level, options.log_facility, log_stderr); break; } } /* Parent. Stay in the loop. */ if (pid < 0) error("fork: %.100s", strerror(errno)); else debug("Forked child %d.", pid); /* Mark that the key has been used (it was "given" to the child). */ key_used = 1; arc4random_stir(); /* Close the new socket (the child is now taking care of it). */ close(newsock); } /* for (i = 0; i < num_listen_socks; i++) */ /* child process check (or debug mode) */ if (num_listen_socks < 0) break; } } /* This is the child processing a new connection. */ /* * Disable the key regeneration alarm. We will not regenerate the * key since we are no longer in a position to give it to anyone. We * will not restart on SIGHUP since it no longer makes sense. */ alarm(0); signal(SIGALRM, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGCHLD, SIG_DFL); /* * Set socket options for the connection. We want the socket to * close as fast as possible without waiting for anything. If the * connection is not a socket, these will do nothing. */ /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ linger.l_onoff = 1; linger.l_linger = 5; setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); /* * Register our connection. This turns encryption off because we do * not have a key. */ packet_set_connection(sock_in, sock_out); remote_port = get_remote_port(); remote_ip = get_remote_ipaddr(); /* Check whether logins are denied from this host. */ #ifdef LIBWRAP { struct request_info req; request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); fromhost(&req); if (!hosts_access(&req)) { close(sock_in); close(sock_out); refuse(&req); } verbose("Connection from %.500s port %d", eval_client(&req), remote_port); } #endif /* LIBWRAP */ /* Log the connection. */ verbose("Connection from %.500s port %d", remote_ip, remote_port); /* * We don\'t want to listen forever unless the other side * successfully authenticates itself. So we set up an alarm which is * cleared after successful authentication. A limit of zero * indicates no limit. Note that we don\'t set the alarm in debugging * mode; it is just annoying to have the server exit just when you * are about to discover the bug. */ signal(SIGALRM, grace_alarm_handler); if (!debug_flag) alarm(options.login_grace_time); sshd_exchange_identification(sock_in, sock_out); /* * Check that the connection comes from a privileged port. Rhosts- * and Rhosts-RSA-Authentication only make sense from priviledged * programs. Of course, if the intruder has root access on his local * machine, he can connect from any port. So do not use these * authentication methods from machines that you do not trust. */ if (remote_port >= IPPORT_RESERVED || remote_port < IPPORT_RESERVED / 2) { options.rhosts_authentication = 0; options.rhosts_rsa_authentication = 0; } #ifdef KRB4 if (!packet_connection_is_ipv4() && options.krb4_authentication) { debug("Kerberos Authentication disabled, only available for IPv4."); options.krb4_authentication = 0; } #endif /* KRB4 */ packet_set_nonblocking(); /* perform the key exchange */ /* authenticate user and start session */ if (compat20) { do_ssh2_kex(); do_authentication2(); } else { do_ssh1_kex(); do_authentication(); } #ifdef KRB4 /* Cleanup user's ticket cache file. */ if (options.krb4_ticket_cleanup) (void) dest_tkt(); #endif /* KRB4 */ /* The connection has been terminated. */ verbose("Closing connection to %.100s", remote_ip); packet_close(); exit(0); } /* * SSH1 key exchange */ void do_ssh1_kex() { int i, len; int plen, slen; BIGNUM *session_key_int; unsigned char session_key[SSH_SESSION_KEY_LENGTH]; unsigned char cookie[8]; unsigned int cipher_type, auth_mask, protocol_flags; u_int32_t rand = 0; /* * Generate check bytes that the client must send back in the user * packet in order for it to be accepted; this is used to defy ip * spoofing attacks. Note that this only works against somebody * doing IP spoofing from a remote machine; any machine on the local * network can still see outgoing packets and catch the random * cookie. This only affects rhosts authentication, and this is one * of the reasons why it is inherently insecure. */ for (i = 0; i < 8; i++) { if (i % 4 == 0) rand = arc4random(); cookie[i] = rand & 0xff; rand >>= 8; } /* * Send our public key. We include in the packet 64 bits of random * data that must be matched in the reply in order to prevent IP * spoofing. */ packet_start(SSH_SMSG_PUBLIC_KEY); for (i = 0; i < 8; i++) packet_put_char(cookie[i]); /* Store our public server RSA key. */ packet_put_int(BN_num_bits(public_key->n)); packet_put_bignum(public_key->e); packet_put_bignum(public_key->n); /* Store our public host RSA key. */ packet_put_int(BN_num_bits(sensitive_data.host_key->n)); packet_put_bignum(sensitive_data.host_key->e); packet_put_bignum(sensitive_data.host_key->n); /* Put protocol flags. */ packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); /* Declare which ciphers we support. */ packet_put_int(cipher_mask1()); /* Declare supported authentication types. */ auth_mask = 0; if (options.rhosts_authentication) auth_mask |= 1 << SSH_AUTH_RHOSTS; if (options.rhosts_rsa_authentication) auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; if (options.rsa_authentication) auth_mask |= 1 << SSH_AUTH_RSA; #ifdef KRB4 if (options.krb4_authentication) auth_mask |= 1 << SSH_AUTH_KRB4; #endif #ifdef KRB5 if (options.krb5_authentication) { auth_mask |= 1 << SSH_AUTH_KRB5; /* compatibility with MetaCentre ssh */ auth_mask |= 1 << SSH_AUTH_KRB4; } if (options.krb5_tgt_passing) auth_mask |= 1 << SSH_PASS_KRB5_TGT; #endif /* KRB5 */ #ifdef AFS if (options.krb4_tgt_passing) auth_mask |= 1 << SSH_PASS_KRB4_TGT; if (options.afs_token_passing) auth_mask |= 1 << SSH_PASS_AFS_TOKEN; #endif #ifdef SKEY if (options.skey_authentication == 1) auth_mask |= 1 << SSH_AUTH_TIS; #endif if (options.password_authentication) auth_mask |= 1 << SSH_AUTH_PASSWORD; packet_put_int(auth_mask); /* Send the packet and wait for it to be sent. */ packet_send(); packet_write_wait(); debug("Sent %d bit public key and %d bit host key.", BN_num_bits(public_key->n), BN_num_bits(sensitive_data.host_key->n)); /* Read clients reply (cipher type and session key). */ packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); /* Get cipher type and check whether we accept this. */ cipher_type = packet_get_char(); if (!(cipher_mask() & (1 << cipher_type))) packet_disconnect("Warning: client selects unsupported cipher."); /* Get check bytes from the packet. These must match those we sent earlier with the public key packet. */ for (i = 0; i < 8; i++) if (cookie[i] != packet_get_char()) packet_disconnect("IP Spoofing check bytes do not match."); debug("Encryption type: %.200s", cipher_name(cipher_type)); /* Get the encrypted integer. */ session_key_int = BN_new(); packet_get_bignum(session_key_int, &slen); protocol_flags = packet_get_int(); packet_set_protocol_flags(protocol_flags); packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); /* * Decrypt it using our private server key and private host key (key * with larger modulus first). */ if (BN_cmp(sensitive_data.private_key->n, sensitive_data.host_key->n) > 0) { /* Private key has bigger modulus. */ if (BN_num_bits(sensitive_data.private_key->n) < BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { fatal("do_connection: %s: private_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", get_remote_ipaddr(), BN_num_bits(sensitive_data.private_key->n), BN_num_bits(sensitive_data.host_key->n), SSH_KEY_BITS_RESERVED); } rsa_private_decrypt(session_key_int, session_key_int, sensitive_data.private_key); rsa_private_decrypt(session_key_int, session_key_int, sensitive_data.host_key); } else { /* Host key has bigger modulus (or they are equal). */ if (BN_num_bits(sensitive_data.host_key->n) < BN_num_bits(sensitive_data.private_key->n) + SSH_KEY_BITS_RESERVED) { fatal("do_connection: %s: host_key %d < private_key %d + SSH_KEY_BITS_RESERVED %d", get_remote_ipaddr(), BN_num_bits(sensitive_data.host_key->n), BN_num_bits(sensitive_data.private_key->n), SSH_KEY_BITS_RESERVED); } rsa_private_decrypt(session_key_int, session_key_int, sensitive_data.host_key); rsa_private_decrypt(session_key_int, session_key_int, sensitive_data.private_key); } compute_session_id(session_id, cookie, sensitive_data.host_key->n, sensitive_data.private_key->n); /* Destroy the private and public keys. They will no longer be needed. */ destroy_sensitive_data(); /* * Extract session key from the decrypted integer. The key is in the * least significant 256 bits of the integer; the first byte of the * key is in the highest bits. */ BN_mask_bits(session_key_int, sizeof(session_key) * 8); len = BN_num_bytes(session_key_int); if (len < 0 || len > sizeof(session_key)) fatal("do_connection: bad len from %s: session_key_int %d > sizeof(session_key) %d", get_remote_ipaddr(), len, sizeof(session_key)); memset(session_key, 0, sizeof(session_key)); BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); /* Destroy the decrypted integer. It is no longer needed. */ BN_clear_free(session_key_int); /* Xor the first 16 bytes of the session key with the session id. */ for (i = 0; i < 16; i++) session_key[i] ^= session_id[i]; /* Set the session key. From this on all communications will be encrypted. */ packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); /* Destroy our copy of the session key. It is no longer needed. */ memset(session_key, 0, sizeof(session_key)); debug("Received session key; encryption turned on."); /* Send an acknowledgement packet. Note that this packet is sent encrypted. */ packet_start(SSH_SMSG_SUCCESS); packet_send(); packet_write_wait(); } /* * SSH2 key exchange: diffie-hellman-group1-sha1 */ void do_ssh2_kex() { Buffer *server_kexinit; Buffer *client_kexinit; int payload_len, dlen; int slen; unsigned int klen, kout; unsigned char *signature = NULL; unsigned char *server_host_key_blob = NULL; unsigned int sbloblen; DH *dh; BIGNUM *dh_client_pub = 0; BIGNUM *shared_secret = 0; int i; unsigned char *kbuf; unsigned char *hash; Kex *kex; char *cprop[PROPOSAL_MAX]; /* KEXINIT */ if (options.ciphers != NULL) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; } server_kexinit = kex_init(myproposal); client_kexinit = xmalloc(sizeof(*client_kexinit)); buffer_init(client_kexinit); /* algorithm negotiation */ kex_exchange_kexinit(server_kexinit, client_kexinit, cprop); kex = kex_choose_conf(cprop, myproposal, 1); for (i = 0; i < PROPOSAL_MAX; i++) xfree(cprop[i]); /* KEXDH */ debug("Wait SSH2_MSG_KEXDH_INIT."); packet_read_expect(&payload_len, SSH2_MSG_KEXDH_INIT); /* key, cert */ dh_client_pub = BN_new(); if (dh_client_pub == NULL) fatal("dh_client_pub == NULL"); packet_get_bignum2(dh_client_pub, &dlen); #ifdef DEBUG_KEXDH fprintf(stderr, "\ndh_client_pub= "); bignum_print(dh_client_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_client_pub)); #endif /* generate DH key */ dh = dh_new_group1(); /* XXX depends on 'kex' */ #ifdef DEBUG_KEXDH fprintf(stderr, "\np= "); bignum_print(dh->p); fprintf(stderr, "\ng= "); bignum_print(dh->g); fprintf(stderr, "\npub= "); bignum_print(dh->pub_key); fprintf(stderr, "\n"); #endif if (!dh_pub_is_valid(dh, dh_client_pub)) packet_disconnect("bad client public DH value"); klen = DH_size(dh); kbuf = xmalloc(klen); kout = DH_compute_key(kbuf, dh_client_pub, dh); #ifdef DEBUG_KEXDH debug("shared secret: len %d/%d", klen, kout); fprintf(stderr, "shared secret == "); for (i = 0; i< kout; i++) fprintf(stderr, "%02x", (kbuf[i])&0xff); fprintf(stderr, "\n"); #endif shared_secret = BN_new(); BN_bin2bn(kbuf, kout, shared_secret); memset(kbuf, 0, klen); xfree(kbuf); /* XXX precompute? */ dsa_make_key_blob(sensitive_data.dsa_host_key, &server_host_key_blob, &sbloblen); /* calc H */ /* XXX depends on 'kex' */ hash = kex_hash( client_version_string, server_version_string, buffer_ptr(client_kexinit), buffer_len(client_kexinit), buffer_ptr(server_kexinit), buffer_len(server_kexinit), (char *)server_host_key_blob, sbloblen, dh_client_pub, dh->pub_key, shared_secret ); buffer_free(client_kexinit); buffer_free(server_kexinit); xfree(client_kexinit); xfree(server_kexinit); #ifdef DEBUG_KEXDH fprintf(stderr, "hash == "); for (i = 0; i< 20; i++) fprintf(stderr, "%02x", (hash[i])&0xff); fprintf(stderr, "\n"); #endif /* save session id := H */ /* XXX hashlen depends on KEX */ session_id2_len = 20; session_id2 = xmalloc(session_id2_len); memcpy(session_id2, hash, session_id2_len); /* sign H */ /* XXX hashlen depends on KEX */ dsa_sign(sensitive_data.dsa_host_key, &signature, &slen, hash, 20); destroy_sensitive_data(); /* send server hostkey, DH pubkey 'f' and singed H */ packet_start(SSH2_MSG_KEXDH_REPLY); packet_put_string((char *)server_host_key_blob, sbloblen); packet_put_bignum2(dh->pub_key); /* f */ packet_put_string((char *)signature, slen); packet_send(); xfree(signature); xfree(server_host_key_blob); packet_write_wait(); kex_derive_keys(kex, hash, shared_secret); packet_set_kex(kex); /* have keys, free DH */ DH_free(dh); debug("send SSH2_MSG_NEWKEYS."); packet_start(SSH2_MSG_NEWKEYS); packet_send(); packet_write_wait(); debug("done: send SSH2_MSG_NEWKEYS."); debug("Wait SSH2_MSG_NEWKEYS."); packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS); debug("GOT SSH2_MSG_NEWKEYS."); #ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ packet_start(SSH2_MSG_IGNORE); packet_put_cstring("markus"); packet_send(); packet_write_wait(); #endif debug("done: KEX2."); }