1*874e263eSdtucker /* $OpenBSD: sshd-session.c,v 1.11 2025/01/16 06:37:10 dtucker Exp $ */ 271f11376Sdjm /* 371f11376Sdjm * SSH2 implementation: 471f11376Sdjm * Privilege Separation: 571f11376Sdjm * 671f11376Sdjm * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. 771f11376Sdjm * Copyright (c) 2002 Niels Provos. All rights reserved. 871f11376Sdjm * 971f11376Sdjm * Redistribution and use in source and binary forms, with or without 1071f11376Sdjm * modification, are permitted provided that the following conditions 1171f11376Sdjm * are met: 1271f11376Sdjm * 1. Redistributions of source code must retain the above copyright 1371f11376Sdjm * notice, this list of conditions and the following disclaimer. 1471f11376Sdjm * 2. Redistributions in binary form must reproduce the above copyright 1571f11376Sdjm * notice, this list of conditions and the following disclaimer in the 1671f11376Sdjm * documentation and/or other materials provided with the distribution. 1771f11376Sdjm * 1871f11376Sdjm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1971f11376Sdjm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2071f11376Sdjm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2171f11376Sdjm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2271f11376Sdjm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2371f11376Sdjm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2471f11376Sdjm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2571f11376Sdjm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2671f11376Sdjm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2771f11376Sdjm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2871f11376Sdjm */ 2971f11376Sdjm 3071f11376Sdjm #include <sys/types.h> 3171f11376Sdjm #include <sys/ioctl.h> 3271f11376Sdjm #include <sys/wait.h> 3371f11376Sdjm #include <sys/tree.h> 3471f11376Sdjm #include <sys/stat.h> 3571f11376Sdjm #include <sys/socket.h> 3671f11376Sdjm #include <sys/time.h> 3771f11376Sdjm #include <sys/queue.h> 3871f11376Sdjm 3971f11376Sdjm #include <errno.h> 4071f11376Sdjm #include <fcntl.h> 4171f11376Sdjm #include <netdb.h> 4271f11376Sdjm #include <paths.h> 4371f11376Sdjm #include <pwd.h> 4471f11376Sdjm #include <signal.h> 4571f11376Sdjm #include <stdio.h> 4671f11376Sdjm #include <stdlib.h> 4771f11376Sdjm #include <string.h> 4871f11376Sdjm #include <stdarg.h> 4971f11376Sdjm #include <unistd.h> 5071f11376Sdjm #include <limits.h> 5171f11376Sdjm 5271f11376Sdjm #ifdef WITH_OPENSSL 5371f11376Sdjm #include <openssl/bn.h> 5471f11376Sdjm #include <openssl/evp.h> 5571f11376Sdjm #endif 5671f11376Sdjm 5771f11376Sdjm #include "xmalloc.h" 5871f11376Sdjm #include "ssh.h" 5971f11376Sdjm #include "ssh2.h" 6071f11376Sdjm #include "sshpty.h" 6171f11376Sdjm #include "packet.h" 6271f11376Sdjm #include "log.h" 6371f11376Sdjm #include "sshbuf.h" 6471f11376Sdjm #include "misc.h" 6571f11376Sdjm #include "match.h" 6671f11376Sdjm #include "servconf.h" 6771f11376Sdjm #include "uidswap.h" 6871f11376Sdjm #include "compat.h" 6971f11376Sdjm #include "cipher.h" 7071f11376Sdjm #include "digest.h" 7171f11376Sdjm #include "sshkey.h" 7271f11376Sdjm #include "kex.h" 7371f11376Sdjm #include "authfile.h" 7471f11376Sdjm #include "pathnames.h" 7571f11376Sdjm #include "atomicio.h" 7671f11376Sdjm #include "canohost.h" 7771f11376Sdjm #include "hostfile.h" 7871f11376Sdjm #include "auth.h" 7971f11376Sdjm #include "authfd.h" 8071f11376Sdjm #include "msg.h" 8171f11376Sdjm #include "dispatch.h" 8271f11376Sdjm #include "channels.h" 8371f11376Sdjm #include "session.h" 8471f11376Sdjm #include "monitor.h" 8571f11376Sdjm #ifdef GSSAPI 8671f11376Sdjm #include "ssh-gss.h" 8771f11376Sdjm #endif 8871f11376Sdjm #include "monitor_wrap.h" 8971f11376Sdjm #include "auth-options.h" 9071f11376Sdjm #include "version.h" 9171f11376Sdjm #include "ssherr.h" 9271f11376Sdjm #include "sk-api.h" 9371f11376Sdjm #include "srclimit.h" 9471f11376Sdjm #include "dh.h" 9571f11376Sdjm 9671f11376Sdjm /* Re-exec fds */ 9771f11376Sdjm #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) 9871f11376Sdjm #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) 9971f11376Sdjm #define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3) 10071f11376Sdjm #define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4) 10171f11376Sdjm 102856b6ee8Sdjm /* Privsep fds */ 103856b6ee8Sdjm #define PRIVSEP_MONITOR_FD (STDERR_FILENO + 1) 104856b6ee8Sdjm #define PRIVSEP_LOG_FD (STDERR_FILENO + 2) 105856b6ee8Sdjm #define PRIVSEP_MIN_FREE_FD (STDERR_FILENO + 3) 106856b6ee8Sdjm 10771f11376Sdjm extern char *__progname; 10871f11376Sdjm 10971f11376Sdjm /* Server configuration options. */ 11071f11376Sdjm ServerOptions options; 11171f11376Sdjm 11271f11376Sdjm /* Name of the server configuration file. */ 11371f11376Sdjm char *config_file_name = _PATH_SERVER_CONFIG_FILE; 11471f11376Sdjm 11571f11376Sdjm /* 11671f11376Sdjm * Debug mode flag. This can be set on the command line. If debug 11771f11376Sdjm * mode is enabled, extra debugging output will be sent to the system 11871f11376Sdjm * log, the daemon will not go to background, and will exit after processing 11971f11376Sdjm * the first connection. 12071f11376Sdjm */ 12171f11376Sdjm int debug_flag = 0; 12271f11376Sdjm 12371f11376Sdjm /* Flag indicating that the daemon is being started from inetd. */ 12471f11376Sdjm static int inetd_flag = 0; 12571f11376Sdjm 12671f11376Sdjm /* debug goes to stderr unless inetd_flag is set */ 12771f11376Sdjm static int log_stderr = 0; 12871f11376Sdjm 12971f11376Sdjm /* Saved arguments to main(). */ 13071f11376Sdjm static char **saved_argv; 13171f11376Sdjm 13271f11376Sdjm /* Daemon's agent connection */ 13371f11376Sdjm int auth_sock = -1; 13471f11376Sdjm static int have_agent = 0; 13571f11376Sdjm 13671f11376Sdjm /* 13771f11376Sdjm * Any really sensitive data in the application is contained in this 13871f11376Sdjm * structure. The idea is that this structure could be locked into memory so 13971f11376Sdjm * that the pages do not get written into swap. However, there are some 14071f11376Sdjm * problems. The private key contains BIGNUMs, and we do not (in principle) 14171f11376Sdjm * have access to the internals of them, and locking just the structure is 14271f11376Sdjm * not very useful. Currently, memory locking is not implemented. 14371f11376Sdjm */ 14471f11376Sdjm struct { 14571f11376Sdjm u_int num_hostkeys; 14671f11376Sdjm struct sshkey **host_keys; /* all private host keys */ 14771f11376Sdjm struct sshkey **host_pubkeys; /* all public host keys */ 14871f11376Sdjm struct sshkey **host_certificates; /* all public host certificates */ 14971f11376Sdjm } sensitive_data; 15071f11376Sdjm 15171f11376Sdjm /* record remote hostname or ip */ 15271f11376Sdjm u_int utmp_len = HOST_NAME_MAX+1; 15371f11376Sdjm 15471f11376Sdjm static int startup_pipe = -1; /* in child */ 15571f11376Sdjm 15671f11376Sdjm /* variables used for privilege separation */ 15771f11376Sdjm struct monitor *pmonitor = NULL; 15871f11376Sdjm int privsep_is_preauth = 1; 15971f11376Sdjm 16071f11376Sdjm /* global connection state and authentication contexts */ 16171f11376Sdjm Authctxt *the_authctxt = NULL; 16271f11376Sdjm struct ssh *the_active_state; 16371f11376Sdjm 16471f11376Sdjm /* global key/cert auth options. XXX move to permanent ssh->authctxt? */ 16571f11376Sdjm struct sshauthopt *auth_opts = NULL; 16671f11376Sdjm 16771f11376Sdjm /* sshd_config buffer */ 16871f11376Sdjm struct sshbuf *cfg; 16971f11376Sdjm 17071f11376Sdjm /* Included files from the configuration file */ 17171f11376Sdjm struct include_list includes = TAILQ_HEAD_INITIALIZER(includes); 17271f11376Sdjm 17371f11376Sdjm /* message to be displayed after login */ 17471f11376Sdjm struct sshbuf *loginmsg; 17571f11376Sdjm 17671f11376Sdjm /* Prototypes for various functions defined later in this file. */ 17771f11376Sdjm void destroy_sensitive_data(void); 17871f11376Sdjm void demote_sensitive_data(void); 179856b6ee8Sdjm 180856b6ee8Sdjm /* XXX reduce to stub once postauth split */ 181856b6ee8Sdjm int 182856b6ee8Sdjm mm_is_monitor(void) 183856b6ee8Sdjm { 184856b6ee8Sdjm /* 185856b6ee8Sdjm * m_pid is only set in the privileged part, and 186856b6ee8Sdjm * points to the unprivileged child. 187856b6ee8Sdjm */ 188856b6ee8Sdjm return (pmonitor && pmonitor->m_pid > 0); 189856b6ee8Sdjm } 19071f11376Sdjm 19171f11376Sdjm /* 19271f11376Sdjm * Signal handler for the alarm after the login grace period has expired. 193bdb58b7fSderaadt * As usual, this may only take signal-safe actions, even though it is 194bdb58b7fSderaadt * terminal. 19571f11376Sdjm */ 19671f11376Sdjm static void 19771f11376Sdjm grace_alarm_handler(int sig) 19871f11376Sdjm { 19971f11376Sdjm /* 20071f11376Sdjm * Try to kill any processes that we have spawned, E.g. authorized 20171f11376Sdjm * keys command helpers or privsep children. 20271f11376Sdjm */ 20371f11376Sdjm if (getpgid(0) == getpid()) { 204bdb58b7fSderaadt struct sigaction sa; 205bdb58b7fSderaadt 206bdb58b7fSderaadt /* mask all other signals while in handler */ 207bdb58b7fSderaadt memset(&sa, 0, sizeof(sa)); 208bdb58b7fSderaadt sa.sa_handler = SIG_IGN; 209bdb58b7fSderaadt sigfillset(&sa.sa_mask); 210bdb58b7fSderaadt sa.sa_flags = SA_RESTART; 211bdb58b7fSderaadt (void)sigaction(SIGTERM, &sa, NULL); 21271f11376Sdjm kill(0, SIGTERM); 21371f11376Sdjm } 2147965d983Sdjm _exit(EXIT_LOGIN_GRACE); 21571f11376Sdjm } 21671f11376Sdjm 21771f11376Sdjm /* Destroy the host and server keys. They will no longer be needed. */ 21871f11376Sdjm void 21971f11376Sdjm destroy_sensitive_data(void) 22071f11376Sdjm { 22171f11376Sdjm u_int i; 22271f11376Sdjm 22371f11376Sdjm for (i = 0; i < options.num_host_key_files; i++) { 22471f11376Sdjm if (sensitive_data.host_keys[i]) { 22571f11376Sdjm sshkey_free(sensitive_data.host_keys[i]); 22671f11376Sdjm sensitive_data.host_keys[i] = NULL; 22771f11376Sdjm } 22871f11376Sdjm if (sensitive_data.host_certificates[i]) { 22971f11376Sdjm sshkey_free(sensitive_data.host_certificates[i]); 23071f11376Sdjm sensitive_data.host_certificates[i] = NULL; 23171f11376Sdjm } 23271f11376Sdjm } 23371f11376Sdjm } 23471f11376Sdjm 23571f11376Sdjm /* Demote private to public keys for network child */ 23671f11376Sdjm void 23771f11376Sdjm demote_sensitive_data(void) 23871f11376Sdjm { 23971f11376Sdjm struct sshkey *tmp; 24071f11376Sdjm u_int i; 24171f11376Sdjm int r; 24271f11376Sdjm 24371f11376Sdjm for (i = 0; i < options.num_host_key_files; i++) { 24471f11376Sdjm if (sensitive_data.host_keys[i]) { 24571f11376Sdjm if ((r = sshkey_from_private( 24671f11376Sdjm sensitive_data.host_keys[i], &tmp)) != 0) 24771f11376Sdjm fatal_r(r, "could not demote host %s key", 24871f11376Sdjm sshkey_type(sensitive_data.host_keys[i])); 24971f11376Sdjm sshkey_free(sensitive_data.host_keys[i]); 25071f11376Sdjm sensitive_data.host_keys[i] = tmp; 25171f11376Sdjm } 25271f11376Sdjm /* Certs do not need demotion */ 25371f11376Sdjm } 25471f11376Sdjm } 25571f11376Sdjm 256856b6ee8Sdjm struct sshbuf * 257856b6ee8Sdjm pack_hostkeys(void) 25871f11376Sdjm { 259856b6ee8Sdjm struct sshbuf *keybuf = NULL, *hostkeys = NULL; 260856b6ee8Sdjm int r; 261856b6ee8Sdjm u_int i; 26271f11376Sdjm 263856b6ee8Sdjm if ((hostkeys = sshbuf_new()) == NULL) 264856b6ee8Sdjm fatal_f("sshbuf_new failed"); 26571f11376Sdjm 266856b6ee8Sdjm /* pack hostkeys into a string. Empty key slots get empty strings */ 267856b6ee8Sdjm for (i = 0; i < options.num_host_key_files; i++) { 268856b6ee8Sdjm /* public key */ 269856b6ee8Sdjm if (sensitive_data.host_pubkeys[i] != NULL) { 270856b6ee8Sdjm if ((r = sshkey_puts(sensitive_data.host_pubkeys[i], 271856b6ee8Sdjm hostkeys)) != 0) 272856b6ee8Sdjm fatal_fr(r, "compose hostkey public"); 273856b6ee8Sdjm } else { 274856b6ee8Sdjm if ((r = sshbuf_put_string(hostkeys, NULL, 0)) != 0) 275856b6ee8Sdjm fatal_fr(r, "compose hostkey empty public"); 27671f11376Sdjm } 277856b6ee8Sdjm /* cert */ 278856b6ee8Sdjm if (sensitive_data.host_certificates[i] != NULL) { 279856b6ee8Sdjm if ((r = sshkey_puts( 280856b6ee8Sdjm sensitive_data.host_certificates[i], 281856b6ee8Sdjm hostkeys)) != 0) 282856b6ee8Sdjm fatal_fr(r, "compose host cert"); 283856b6ee8Sdjm } else { 284856b6ee8Sdjm if ((r = sshbuf_put_string(hostkeys, NULL, 0)) != 0) 285856b6ee8Sdjm fatal_fr(r, "compose host cert empty"); 286856b6ee8Sdjm } 287856b6ee8Sdjm } 288856b6ee8Sdjm 289856b6ee8Sdjm sshbuf_free(keybuf); 290856b6ee8Sdjm return hostkeys; 29171f11376Sdjm } 29271f11376Sdjm 29371f11376Sdjm static int 29471f11376Sdjm privsep_preauth(struct ssh *ssh) 29571f11376Sdjm { 29671f11376Sdjm int status, r; 29771f11376Sdjm pid_t pid; 29871f11376Sdjm 29971f11376Sdjm /* Set up unprivileged child process to deal with network data */ 30071f11376Sdjm pmonitor = monitor_init(); 30171f11376Sdjm /* Store a pointer to the kex for later rekeying */ 30271f11376Sdjm pmonitor->m_pkex = &ssh->kex; 30371f11376Sdjm 304856b6ee8Sdjm if ((pid = fork()) == -1) 30571f11376Sdjm fatal("fork of unprivileged child failed"); 306856b6ee8Sdjm else if (pid != 0) { 30771f11376Sdjm debug2("Network child is on pid %ld", (long)pid); 30871f11376Sdjm pmonitor->m_pid = pid; 30971f11376Sdjm if (have_agent) { 31071f11376Sdjm r = ssh_get_authentication_socket(&auth_sock); 31171f11376Sdjm if (r != 0) { 31271f11376Sdjm error_r(r, "Could not get agent socket"); 31371f11376Sdjm have_agent = 0; 31471f11376Sdjm } 31571f11376Sdjm } 31671f11376Sdjm monitor_child_preauth(ssh, pmonitor); 31771f11376Sdjm 31871f11376Sdjm /* Wait for the child's exit status */ 31971f11376Sdjm while (waitpid(pid, &status, 0) == -1) { 32071f11376Sdjm if (errno == EINTR) 32171f11376Sdjm continue; 32271f11376Sdjm pmonitor->m_pid = -1; 32371f11376Sdjm fatal_f("waitpid: %s", strerror(errno)); 32471f11376Sdjm } 32571f11376Sdjm privsep_is_preauth = 0; 32671f11376Sdjm pmonitor->m_pid = -1; 32771f11376Sdjm if (WIFEXITED(status)) { 32871f11376Sdjm if (WEXITSTATUS(status) != 0) 32971f11376Sdjm fatal_f("preauth child exited with status %d", 33071f11376Sdjm WEXITSTATUS(status)); 33171f11376Sdjm } else if (WIFSIGNALED(status)) 33271f11376Sdjm fatal_f("preauth child terminated by signal %d", 33371f11376Sdjm WTERMSIG(status)); 33471f11376Sdjm return 1; 33571f11376Sdjm } else { 33671f11376Sdjm /* child */ 33771f11376Sdjm close(pmonitor->m_sendfd); 33871f11376Sdjm close(pmonitor->m_log_recvfd); 33971f11376Sdjm 340856b6ee8Sdjm /* 341856b6ee8Sdjm * Arrange unpriv-preauth child process fds: 342856b6ee8Sdjm * 0, 1 network socket 343856b6ee8Sdjm * 2 optional stderr 344856b6ee8Sdjm * 3 reserved 345856b6ee8Sdjm * 4 monitor message socket 346856b6ee8Sdjm * 5 monitor logging socket 347856b6ee8Sdjm * 348856b6ee8Sdjm * We know that the monitor sockets will have fds > 4 because 349856b6ee8Sdjm * of the reserved fds in main() 350856b6ee8Sdjm */ 35171f11376Sdjm 352856b6ee8Sdjm if (ssh_packet_get_connection_in(ssh) != STDIN_FILENO && 353856b6ee8Sdjm dup2(ssh_packet_get_connection_in(ssh), STDIN_FILENO) == -1) 354856b6ee8Sdjm fatal("dup2 stdin failed: %s", strerror(errno)); 355856b6ee8Sdjm if (ssh_packet_get_connection_out(ssh) != STDOUT_FILENO && 356856b6ee8Sdjm dup2(ssh_packet_get_connection_out(ssh), 357856b6ee8Sdjm STDOUT_FILENO) == -1) 358856b6ee8Sdjm fatal("dup2 stdout failed: %s", strerror(errno)); 359856b6ee8Sdjm /* leave stderr as-is */ 360856b6ee8Sdjm log_redirect_stderr_to(NULL); /* dup can clobber log fd */ 361856b6ee8Sdjm if (pmonitor->m_recvfd != PRIVSEP_MONITOR_FD && 362856b6ee8Sdjm dup2(pmonitor->m_recvfd, PRIVSEP_MONITOR_FD) == -1) 363856b6ee8Sdjm fatal("dup2 monitor fd: %s", strerror(errno)); 364856b6ee8Sdjm if (pmonitor->m_log_sendfd != PRIVSEP_LOG_FD && 365856b6ee8Sdjm dup2(pmonitor->m_log_sendfd, PRIVSEP_LOG_FD) == -1) 366856b6ee8Sdjm fatal("dup2 log fd: %s", strerror(errno)); 367856b6ee8Sdjm closefrom(PRIVSEP_MIN_FREE_FD); 36871f11376Sdjm 369856b6ee8Sdjm saved_argv[0] = options.sshd_auth_path; 370856b6ee8Sdjm execv(options.sshd_auth_path, saved_argv); 371856b6ee8Sdjm 372856b6ee8Sdjm fatal_f("exec of %s failed: %s", 373856b6ee8Sdjm options.sshd_auth_path, strerror(errno)); 37471f11376Sdjm } 37571f11376Sdjm } 37671f11376Sdjm 37771f11376Sdjm static void 37871f11376Sdjm privsep_postauth(struct ssh *ssh, Authctxt *authctxt) 37971f11376Sdjm { 38071f11376Sdjm /* New socket pair */ 38171f11376Sdjm monitor_reinit(pmonitor); 38271f11376Sdjm 38371f11376Sdjm pmonitor->m_pid = fork(); 38471f11376Sdjm if (pmonitor->m_pid == -1) 38571f11376Sdjm fatal("fork of unprivileged child failed"); 38671f11376Sdjm else if (pmonitor->m_pid != 0) { 38771f11376Sdjm verbose("User child is on pid %ld", (long)pmonitor->m_pid); 38871f11376Sdjm sshbuf_reset(loginmsg); 38971f11376Sdjm monitor_clear_keystate(ssh, pmonitor); 39071f11376Sdjm monitor_child_postauth(ssh, pmonitor); 39171f11376Sdjm 39271f11376Sdjm /* NEVERREACHED */ 39371f11376Sdjm exit(0); 39471f11376Sdjm } 39571f11376Sdjm 39671f11376Sdjm /* child */ 39771f11376Sdjm 39871f11376Sdjm close(pmonitor->m_sendfd); 39971f11376Sdjm pmonitor->m_sendfd = -1; 40071f11376Sdjm 40171f11376Sdjm /* Demote the private keys to public keys. */ 40271f11376Sdjm demote_sensitive_data(); 40371f11376Sdjm 40471f11376Sdjm /* Drop privileges */ 40571f11376Sdjm do_setusercontext(authctxt->pw); 40671f11376Sdjm 40771f11376Sdjm /* It is safe now to apply the key state */ 40871f11376Sdjm monitor_apply_keystate(ssh, pmonitor); 40971f11376Sdjm 41071f11376Sdjm /* 41171f11376Sdjm * Tell the packet layer that authentication was successful, since 41271f11376Sdjm * this information is not part of the key state. 41371f11376Sdjm */ 41471f11376Sdjm ssh_packet_set_authenticated(ssh); 41571f11376Sdjm } 41671f11376Sdjm 41771f11376Sdjm static struct sshkey * 41871f11376Sdjm get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh) 41971f11376Sdjm { 42071f11376Sdjm u_int i; 42171f11376Sdjm struct sshkey *key; 42271f11376Sdjm 42371f11376Sdjm for (i = 0; i < options.num_host_key_files; i++) { 42471f11376Sdjm switch (type) { 42571f11376Sdjm case KEY_RSA_CERT: 42671f11376Sdjm case KEY_DSA_CERT: 42771f11376Sdjm case KEY_ECDSA_CERT: 42871f11376Sdjm case KEY_ED25519_CERT: 42971f11376Sdjm case KEY_ECDSA_SK_CERT: 43071f11376Sdjm case KEY_ED25519_SK_CERT: 43171f11376Sdjm case KEY_XMSS_CERT: 43271f11376Sdjm key = sensitive_data.host_certificates[i]; 43371f11376Sdjm break; 43471f11376Sdjm default: 43571f11376Sdjm key = sensitive_data.host_keys[i]; 43671f11376Sdjm if (key == NULL && !need_private) 43771f11376Sdjm key = sensitive_data.host_pubkeys[i]; 43871f11376Sdjm break; 43971f11376Sdjm } 44071f11376Sdjm if (key == NULL || key->type != type) 44171f11376Sdjm continue; 44271f11376Sdjm switch (type) { 44371f11376Sdjm case KEY_ECDSA: 44471f11376Sdjm case KEY_ECDSA_SK: 44571f11376Sdjm case KEY_ECDSA_CERT: 44671f11376Sdjm case KEY_ECDSA_SK_CERT: 44771f11376Sdjm if (key->ecdsa_nid != nid) 44871f11376Sdjm continue; 44971f11376Sdjm /* FALLTHROUGH */ 45071f11376Sdjm default: 45171f11376Sdjm return need_private ? 45271f11376Sdjm sensitive_data.host_keys[i] : key; 45371f11376Sdjm } 45471f11376Sdjm } 45571f11376Sdjm return NULL; 45671f11376Sdjm } 45771f11376Sdjm 45871f11376Sdjm struct sshkey * 45971f11376Sdjm get_hostkey_public_by_type(int type, int nid, struct ssh *ssh) 46071f11376Sdjm { 46171f11376Sdjm return get_hostkey_by_type(type, nid, 0, ssh); 46271f11376Sdjm } 46371f11376Sdjm 46471f11376Sdjm struct sshkey * 46571f11376Sdjm get_hostkey_private_by_type(int type, int nid, struct ssh *ssh) 46671f11376Sdjm { 46771f11376Sdjm return get_hostkey_by_type(type, nid, 1, ssh); 46871f11376Sdjm } 46971f11376Sdjm 47071f11376Sdjm struct sshkey * 47171f11376Sdjm get_hostkey_by_index(int ind) 47271f11376Sdjm { 47371f11376Sdjm if (ind < 0 || (u_int)ind >= options.num_host_key_files) 47471f11376Sdjm return (NULL); 47571f11376Sdjm return (sensitive_data.host_keys[ind]); 47671f11376Sdjm } 47771f11376Sdjm 47871f11376Sdjm struct sshkey * 47971f11376Sdjm get_hostkey_public_by_index(int ind, struct ssh *ssh) 48071f11376Sdjm { 48171f11376Sdjm if (ind < 0 || (u_int)ind >= options.num_host_key_files) 48271f11376Sdjm return (NULL); 48371f11376Sdjm return (sensitive_data.host_pubkeys[ind]); 48471f11376Sdjm } 48571f11376Sdjm 48671f11376Sdjm int 48771f11376Sdjm get_hostkey_index(struct sshkey *key, int compare, struct ssh *ssh) 48871f11376Sdjm { 48971f11376Sdjm u_int i; 49071f11376Sdjm 49171f11376Sdjm for (i = 0; i < options.num_host_key_files; i++) { 49271f11376Sdjm if (sshkey_is_cert(key)) { 49371f11376Sdjm if (key == sensitive_data.host_certificates[i] || 49471f11376Sdjm (compare && sensitive_data.host_certificates[i] && 49571f11376Sdjm sshkey_equal(key, 49671f11376Sdjm sensitive_data.host_certificates[i]))) 49771f11376Sdjm return (i); 49871f11376Sdjm } else { 49971f11376Sdjm if (key == sensitive_data.host_keys[i] || 50071f11376Sdjm (compare && sensitive_data.host_keys[i] && 50171f11376Sdjm sshkey_equal(key, sensitive_data.host_keys[i]))) 50271f11376Sdjm return (i); 50371f11376Sdjm if (key == sensitive_data.host_pubkeys[i] || 50471f11376Sdjm (compare && sensitive_data.host_pubkeys[i] && 50571f11376Sdjm sshkey_equal(key, sensitive_data.host_pubkeys[i]))) 50671f11376Sdjm return (i); 50771f11376Sdjm } 50871f11376Sdjm } 50971f11376Sdjm return (-1); 51071f11376Sdjm } 51171f11376Sdjm 51271f11376Sdjm /* Inform the client of all hostkeys */ 51371f11376Sdjm static void 51471f11376Sdjm notify_hostkeys(struct ssh *ssh) 51571f11376Sdjm { 51671f11376Sdjm struct sshbuf *buf; 51771f11376Sdjm struct sshkey *key; 51871f11376Sdjm u_int i, nkeys; 51971f11376Sdjm int r; 52071f11376Sdjm char *fp; 52171f11376Sdjm 52271f11376Sdjm /* Some clients cannot cope with the hostkeys message, skip those. */ 52371f11376Sdjm if (ssh->compat & SSH_BUG_HOSTKEYS) 52471f11376Sdjm return; 52571f11376Sdjm 52671f11376Sdjm if ((buf = sshbuf_new()) == NULL) 52771f11376Sdjm fatal_f("sshbuf_new"); 52871f11376Sdjm for (i = nkeys = 0; i < options.num_host_key_files; i++) { 52971f11376Sdjm key = get_hostkey_public_by_index(i, ssh); 53071f11376Sdjm if (key == NULL || key->type == KEY_UNSPEC || 53171f11376Sdjm sshkey_is_cert(key)) 53271f11376Sdjm continue; 53371f11376Sdjm fp = sshkey_fingerprint(key, options.fingerprint_hash, 53471f11376Sdjm SSH_FP_DEFAULT); 53571f11376Sdjm debug3_f("key %d: %s %s", i, sshkey_ssh_name(key), fp); 53671f11376Sdjm free(fp); 53771f11376Sdjm if (nkeys == 0) { 53871f11376Sdjm /* 53971f11376Sdjm * Start building the request when we find the 54071f11376Sdjm * first usable key. 54171f11376Sdjm */ 54271f11376Sdjm if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 54371f11376Sdjm (r = sshpkt_put_cstring(ssh, "hostkeys-00@openssh.com")) != 0 || 54471f11376Sdjm (r = sshpkt_put_u8(ssh, 0)) != 0) /* want reply */ 54571f11376Sdjm sshpkt_fatal(ssh, r, "%s: start request", __func__); 54671f11376Sdjm } 54771f11376Sdjm /* Append the key to the request */ 54871f11376Sdjm sshbuf_reset(buf); 54971f11376Sdjm if ((r = sshkey_putb(key, buf)) != 0) 55071f11376Sdjm fatal_fr(r, "couldn't put hostkey %d", i); 55171f11376Sdjm if ((r = sshpkt_put_stringb(ssh, buf)) != 0) 55271f11376Sdjm sshpkt_fatal(ssh, r, "%s: append key", __func__); 55371f11376Sdjm nkeys++; 55471f11376Sdjm } 55571f11376Sdjm debug3_f("sent %u hostkeys", nkeys); 55671f11376Sdjm if (nkeys == 0) 55771f11376Sdjm fatal_f("no hostkeys"); 55871f11376Sdjm if ((r = sshpkt_send(ssh)) != 0) 55971f11376Sdjm sshpkt_fatal(ssh, r, "%s: send", __func__); 56071f11376Sdjm sshbuf_free(buf); 56171f11376Sdjm } 56271f11376Sdjm 56371f11376Sdjm static void 56471f11376Sdjm usage(void) 56571f11376Sdjm { 56671f11376Sdjm fprintf(stderr, "%s, %s\n", SSH_VERSION, SSH_OPENSSL_VERSION); 56771f11376Sdjm fprintf(stderr, 56871f11376Sdjm "usage: sshd [-46DdeGiqTtV] [-C connection_spec] [-c host_cert_file]\n" 56971f11376Sdjm " [-E log_file] [-f config_file] [-g login_grace_time]\n" 57071f11376Sdjm " [-h host_key_file] [-o option] [-p port] [-u len]\n" 57171f11376Sdjm ); 57271f11376Sdjm exit(1); 57371f11376Sdjm } 57471f11376Sdjm 57571f11376Sdjm static void 57671f11376Sdjm parse_hostkeys(struct sshbuf *hostkeys) 57771f11376Sdjm { 57871f11376Sdjm int r; 57971f11376Sdjm u_int num_keys = 0; 58071f11376Sdjm struct sshkey *k; 58171f11376Sdjm struct sshbuf *kbuf; 58271f11376Sdjm const u_char *cp; 58371f11376Sdjm size_t len; 58471f11376Sdjm 58571f11376Sdjm while (sshbuf_len(hostkeys) != 0) { 58671f11376Sdjm if (num_keys > 2048) 58771f11376Sdjm fatal_f("too many hostkeys"); 58871f11376Sdjm sensitive_data.host_keys = xrecallocarray( 58971f11376Sdjm sensitive_data.host_keys, num_keys, num_keys + 1, 59071f11376Sdjm sizeof(*sensitive_data.host_pubkeys)); 59171f11376Sdjm sensitive_data.host_pubkeys = xrecallocarray( 59271f11376Sdjm sensitive_data.host_pubkeys, num_keys, num_keys + 1, 59371f11376Sdjm sizeof(*sensitive_data.host_pubkeys)); 59471f11376Sdjm sensitive_data.host_certificates = xrecallocarray( 59571f11376Sdjm sensitive_data.host_certificates, num_keys, num_keys + 1, 59671f11376Sdjm sizeof(*sensitive_data.host_certificates)); 59771f11376Sdjm /* private key */ 59871f11376Sdjm k = NULL; 59971f11376Sdjm if ((r = sshbuf_froms(hostkeys, &kbuf)) != 0) 60071f11376Sdjm fatal_fr(r, "extract privkey"); 60171f11376Sdjm if (sshbuf_len(kbuf) != 0 && 60271f11376Sdjm (r = sshkey_private_deserialize(kbuf, &k)) != 0) 60371f11376Sdjm fatal_fr(r, "parse pubkey"); 60471f11376Sdjm sensitive_data.host_keys[num_keys] = k; 60571f11376Sdjm sshbuf_free(kbuf); 60671f11376Sdjm if (k) 60771f11376Sdjm debug2_f("privkey %u: %s", num_keys, sshkey_ssh_name(k)); 60871f11376Sdjm /* public key */ 60971f11376Sdjm k = NULL; 61071f11376Sdjm if ((r = sshbuf_get_string_direct(hostkeys, &cp, &len)) != 0) 61171f11376Sdjm fatal_fr(r, "extract pubkey"); 61271f11376Sdjm if (len != 0 && (r = sshkey_from_blob(cp, len, &k)) != 0) 61371f11376Sdjm fatal_fr(r, "parse pubkey"); 61471f11376Sdjm sensitive_data.host_pubkeys[num_keys] = k; 61571f11376Sdjm if (k) 61671f11376Sdjm debug2_f("pubkey %u: %s", num_keys, sshkey_ssh_name(k)); 61771f11376Sdjm /* certificate */ 61871f11376Sdjm k = NULL; 61971f11376Sdjm if ((r = sshbuf_get_string_direct(hostkeys, &cp, &len)) != 0) 62071f11376Sdjm fatal_fr(r, "extract pubkey"); 62171f11376Sdjm if (len != 0 && (r = sshkey_from_blob(cp, len, &k)) != 0) 62271f11376Sdjm fatal_fr(r, "parse pubkey"); 62371f11376Sdjm sensitive_data.host_certificates[num_keys] = k; 62471f11376Sdjm if (k) 62571f11376Sdjm debug2_f("cert %u: %s", num_keys, sshkey_ssh_name(k)); 62671f11376Sdjm num_keys++; 62771f11376Sdjm } 62871f11376Sdjm sensitive_data.num_hostkeys = num_keys; 62971f11376Sdjm } 63071f11376Sdjm 63171f11376Sdjm static void 63271f11376Sdjm recv_rexec_state(int fd, struct sshbuf *conf, uint64_t *timing_secretp) 63371f11376Sdjm { 63471f11376Sdjm struct sshbuf *m, *inc, *hostkeys; 63571f11376Sdjm u_char *cp, ver; 63671f11376Sdjm size_t len; 63771f11376Sdjm int r; 63871f11376Sdjm struct include_item *item; 63971f11376Sdjm 64071f11376Sdjm debug3_f("entering fd = %d", fd); 64171f11376Sdjm 64271f11376Sdjm if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL) 64371f11376Sdjm fatal_f("sshbuf_new failed"); 64471f11376Sdjm if (ssh_msg_recv(fd, m) == -1) 64571f11376Sdjm fatal_f("ssh_msg_recv failed"); 64671f11376Sdjm if ((r = sshbuf_get_u8(m, &ver)) != 0) 64771f11376Sdjm fatal_fr(r, "parse version"); 64871f11376Sdjm if (ver != 0) 64971f11376Sdjm fatal_f("rexec version mismatch"); 65071f11376Sdjm if ((r = sshbuf_get_string(m, &cp, &len)) != 0 || /* XXX _direct */ 65171f11376Sdjm (r = sshbuf_get_u64(m, timing_secretp)) != 0 || 65271f11376Sdjm (r = sshbuf_froms(m, &hostkeys)) != 0 || 65371f11376Sdjm (r = sshbuf_get_stringb(m, inc)) != 0) 65471f11376Sdjm fatal_fr(r, "parse config"); 65571f11376Sdjm 65671f11376Sdjm if (conf != NULL && (r = sshbuf_put(conf, cp, len))) 65771f11376Sdjm fatal_fr(r, "sshbuf_put"); 65871f11376Sdjm 65971f11376Sdjm while (sshbuf_len(inc) != 0) { 66071f11376Sdjm item = xcalloc(1, sizeof(*item)); 66171f11376Sdjm if ((item->contents = sshbuf_new()) == NULL) 66271f11376Sdjm fatal_f("sshbuf_new failed"); 66371f11376Sdjm if ((r = sshbuf_get_cstring(inc, &item->selector, NULL)) != 0 || 66471f11376Sdjm (r = sshbuf_get_cstring(inc, &item->filename, NULL)) != 0 || 66571f11376Sdjm (r = sshbuf_get_stringb(inc, item->contents)) != 0) 66671f11376Sdjm fatal_fr(r, "parse includes"); 66771f11376Sdjm TAILQ_INSERT_TAIL(&includes, item, entry); 66871f11376Sdjm } 66971f11376Sdjm 67071f11376Sdjm parse_hostkeys(hostkeys); 67171f11376Sdjm 67271f11376Sdjm free(cp); 67371f11376Sdjm sshbuf_free(m); 67471f11376Sdjm sshbuf_free(hostkeys); 67571f11376Sdjm sshbuf_free(inc); 67671f11376Sdjm 67771f11376Sdjm debug3_f("done"); 67871f11376Sdjm } 67971f11376Sdjm 68071f11376Sdjm /* 68171f11376Sdjm * If IP options are supported, make sure there are none (log and 68271f11376Sdjm * return an error if any are found). Basically we are worried about 68371f11376Sdjm * source routing; it can be used to pretend you are somebody 68471f11376Sdjm * (ip-address) you are not. That itself may be "almost acceptable" 68571f11376Sdjm * under certain circumstances, but rhosts authentication is useless 68671f11376Sdjm * if source routing is accepted. Notice also that if we just dropped 68771f11376Sdjm * source routing here, the other side could use IP spoofing to do 68871f11376Sdjm * rest of the interaction and could still bypass security. So we 68971f11376Sdjm * exit here if we detect any IP options. 69071f11376Sdjm */ 69171f11376Sdjm static void 69271f11376Sdjm check_ip_options(struct ssh *ssh) 69371f11376Sdjm { 69471f11376Sdjm int sock_in = ssh_packet_get_connection_in(ssh); 69571f11376Sdjm struct sockaddr_storage from; 69671f11376Sdjm u_char opts[200]; 69771f11376Sdjm socklen_t i, option_size = sizeof(opts), fromlen = sizeof(from); 69871f11376Sdjm char text[sizeof(opts) * 3 + 1]; 69971f11376Sdjm 70071f11376Sdjm memset(&from, 0, sizeof(from)); 70171f11376Sdjm if (getpeername(sock_in, (struct sockaddr *)&from, 70271f11376Sdjm &fromlen) == -1) 70371f11376Sdjm return; 70471f11376Sdjm if (from.ss_family != AF_INET) 70571f11376Sdjm return; 70671f11376Sdjm /* XXX IPv6 options? */ 70771f11376Sdjm 70871f11376Sdjm if (getsockopt(sock_in, IPPROTO_IP, IP_OPTIONS, opts, 70971f11376Sdjm &option_size) >= 0 && option_size != 0) { 71071f11376Sdjm text[0] = '\0'; 71171f11376Sdjm for (i = 0; i < option_size; i++) 71271f11376Sdjm snprintf(text + i*3, sizeof(text) - i*3, 71371f11376Sdjm " %2.2x", opts[i]); 71471f11376Sdjm fatal("Connection from %.100s port %d with IP opts: %.800s", 71571f11376Sdjm ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), text); 71671f11376Sdjm } 71771f11376Sdjm } 71871f11376Sdjm 71971f11376Sdjm /* Set the routing domain for this process */ 72071f11376Sdjm static void 72171f11376Sdjm set_process_rdomain(struct ssh *ssh, const char *name) 72271f11376Sdjm { 72371f11376Sdjm int rtable, ortable = getrtable(); 72471f11376Sdjm const char *errstr; 72571f11376Sdjm 72671f11376Sdjm if (name == NULL) 72771f11376Sdjm return; /* default */ 72871f11376Sdjm 72971f11376Sdjm if (strcmp(name, "%D") == 0) { 73071f11376Sdjm /* "expands" to routing domain of connection */ 73171f11376Sdjm if ((name = ssh_packet_rdomain_in(ssh)) == NULL) 73271f11376Sdjm return; 73371f11376Sdjm } 73471f11376Sdjm 73571f11376Sdjm rtable = (int)strtonum(name, 0, 255, &errstr); 73671f11376Sdjm if (errstr != NULL) /* Shouldn't happen */ 73771f11376Sdjm fatal("Invalid routing domain \"%s\": %s", name, errstr); 73871f11376Sdjm if (rtable != ortable && setrtable(rtable) != 0) 73971f11376Sdjm fatal("Unable to set routing domain %d: %s", 74071f11376Sdjm rtable, strerror(errno)); 74171f11376Sdjm debug_f("set routing domain %d (was %d)", rtable, ortable); 74271f11376Sdjm } 74371f11376Sdjm 74471f11376Sdjm /* 74571f11376Sdjm * Main program for the daemon. 74671f11376Sdjm */ 74771f11376Sdjm int 74871f11376Sdjm main(int ac, char **av) 74971f11376Sdjm { 75071f11376Sdjm struct ssh *ssh = NULL; 75171f11376Sdjm extern char *optarg; 75271f11376Sdjm extern int optind; 753856b6ee8Sdjm int devnull, r, opt, on = 1, remote_port; 75471f11376Sdjm int sock_in = -1, sock_out = -1, rexeced_flag = 0, have_key = 0; 75571f11376Sdjm const char *remote_ip, *rdomain; 75671f11376Sdjm char *line, *laddr, *logfile = NULL; 75771f11376Sdjm u_int i; 75871f11376Sdjm u_int64_t ibytes, obytes; 75971f11376Sdjm mode_t new_umask; 76071f11376Sdjm Authctxt *authctxt; 76171f11376Sdjm struct connection_info *connection_info = NULL; 76271f11376Sdjm sigset_t sigmask; 76371f11376Sdjm uint64_t timing_secret = 0; 764873f0a37Sdlg struct itimerval itv; 76571f11376Sdjm 76671f11376Sdjm sigemptyset(&sigmask); 76771f11376Sdjm sigprocmask(SIG_SETMASK, &sigmask, NULL); 76871f11376Sdjm 76971f11376Sdjm /* Save argv. */ 77071f11376Sdjm saved_argv = av; 77171f11376Sdjm 77271f11376Sdjm /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 77371f11376Sdjm sanitise_stdfd(); 77471f11376Sdjm 77571f11376Sdjm /* Initialize configuration options to their default values. */ 77671f11376Sdjm initialize_server_options(&options); 77771f11376Sdjm 77871f11376Sdjm /* Parse command-line arguments. */ 77971f11376Sdjm while ((opt = getopt(ac, av, 78071f11376Sdjm "C:E:b:c:f:g:h:k:o:p:u:46DGQRTdeiqrtV")) != -1) { 78171f11376Sdjm switch (opt) { 78271f11376Sdjm case '4': 78371f11376Sdjm options.address_family = AF_INET; 78471f11376Sdjm break; 78571f11376Sdjm case '6': 78671f11376Sdjm options.address_family = AF_INET6; 78771f11376Sdjm break; 78871f11376Sdjm case 'f': 78971f11376Sdjm config_file_name = optarg; 79071f11376Sdjm break; 79171f11376Sdjm case 'c': 79271f11376Sdjm servconf_add_hostcert("[command-line]", 0, 79371f11376Sdjm &options, optarg); 79471f11376Sdjm break; 79571f11376Sdjm case 'd': 79671f11376Sdjm if (debug_flag == 0) { 79771f11376Sdjm debug_flag = 1; 79871f11376Sdjm options.log_level = SYSLOG_LEVEL_DEBUG1; 79971f11376Sdjm } else if (options.log_level < SYSLOG_LEVEL_DEBUG3) 80071f11376Sdjm options.log_level++; 80171f11376Sdjm break; 80271f11376Sdjm case 'D': 80371f11376Sdjm /* ignore */ 80471f11376Sdjm break; 80571f11376Sdjm case 'E': 80671f11376Sdjm logfile = optarg; 80771f11376Sdjm /* FALLTHROUGH */ 80871f11376Sdjm case 'e': 80971f11376Sdjm log_stderr = 1; 81071f11376Sdjm break; 81171f11376Sdjm case 'i': 81271f11376Sdjm inetd_flag = 1; 81371f11376Sdjm break; 81471f11376Sdjm case 'r': 81571f11376Sdjm /* ignore */ 81671f11376Sdjm break; 81771f11376Sdjm case 'R': 81871f11376Sdjm rexeced_flag = 1; 81971f11376Sdjm break; 82071f11376Sdjm case 'Q': 82171f11376Sdjm /* ignored */ 82271f11376Sdjm break; 82371f11376Sdjm case 'q': 82471f11376Sdjm options.log_level = SYSLOG_LEVEL_QUIET; 82571f11376Sdjm break; 82671f11376Sdjm case 'b': 82771f11376Sdjm /* protocol 1, ignored */ 82871f11376Sdjm break; 82971f11376Sdjm case 'p': 83071f11376Sdjm options.ports_from_cmdline = 1; 83171f11376Sdjm if (options.num_ports >= MAX_PORTS) { 83271f11376Sdjm fprintf(stderr, "too many ports.\n"); 83371f11376Sdjm exit(1); 83471f11376Sdjm } 83571f11376Sdjm options.ports[options.num_ports++] = a2port(optarg); 83671f11376Sdjm if (options.ports[options.num_ports-1] <= 0) { 83771f11376Sdjm fprintf(stderr, "Bad port number.\n"); 83871f11376Sdjm exit(1); 83971f11376Sdjm } 84071f11376Sdjm break; 84171f11376Sdjm case 'g': 84271f11376Sdjm if ((options.login_grace_time = convtime(optarg)) == -1) { 84371f11376Sdjm fprintf(stderr, "Invalid login grace time.\n"); 84471f11376Sdjm exit(1); 84571f11376Sdjm } 84671f11376Sdjm break; 84771f11376Sdjm case 'k': 84871f11376Sdjm /* protocol 1, ignored */ 84971f11376Sdjm break; 85071f11376Sdjm case 'h': 85171f11376Sdjm servconf_add_hostkey("[command-line]", 0, 85271f11376Sdjm &options, optarg, 1); 85371f11376Sdjm break; 85471f11376Sdjm case 't': 85571f11376Sdjm case 'T': 85671f11376Sdjm case 'G': 85771f11376Sdjm fatal("test/dump modes not supported"); 85871f11376Sdjm break; 85971f11376Sdjm case 'C': 86071f11376Sdjm connection_info = server_get_connection_info(ssh, 0, 0); 86171f11376Sdjm if (parse_server_match_testspec(connection_info, 86271f11376Sdjm optarg) == -1) 86371f11376Sdjm exit(1); 86471f11376Sdjm break; 86571f11376Sdjm case 'u': 86671f11376Sdjm utmp_len = (u_int)strtonum(optarg, 0, HOST_NAME_MAX+1+1, NULL); 86771f11376Sdjm if (utmp_len > HOST_NAME_MAX+1) { 86871f11376Sdjm fprintf(stderr, "Invalid utmp length.\n"); 86971f11376Sdjm exit(1); 87071f11376Sdjm } 87171f11376Sdjm break; 87271f11376Sdjm case 'o': 87371f11376Sdjm line = xstrdup(optarg); 87471f11376Sdjm if (process_server_config_line(&options, line, 87571f11376Sdjm "command-line", 0, NULL, NULL, &includes) != 0) 87671f11376Sdjm exit(1); 87771f11376Sdjm free(line); 87871f11376Sdjm break; 87971f11376Sdjm case 'V': 88071f11376Sdjm fprintf(stderr, "%s, %s\n", 88171f11376Sdjm SSH_VERSION, SSH_OPENSSL_VERSION); 88271f11376Sdjm exit(0); 88371f11376Sdjm default: 88471f11376Sdjm usage(); 88571f11376Sdjm break; 88671f11376Sdjm } 88771f11376Sdjm } 88871f11376Sdjm 88971f11376Sdjm /* Check that there are no remaining arguments. */ 89071f11376Sdjm if (optind < ac) { 89171f11376Sdjm fprintf(stderr, "Extra argument %s.\n", av[optind]); 89271f11376Sdjm exit(1); 89371f11376Sdjm } 89471f11376Sdjm 89571f11376Sdjm if (!rexeced_flag) 89671f11376Sdjm fatal("sshd-session should not be executed directly"); 89771f11376Sdjm 89871f11376Sdjm closefrom(REEXEC_MIN_FREE_FD); 89971f11376Sdjm 900856b6ee8Sdjm /* Reserve fds we'll need later for reexec things */ 901856b6ee8Sdjm if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) 902856b6ee8Sdjm fatal("open %s: %s", _PATH_DEVNULL, strerror(errno)); 903856b6ee8Sdjm while (devnull < PRIVSEP_MIN_FREE_FD) { 904856b6ee8Sdjm if ((devnull = dup(devnull)) == -1) 905856b6ee8Sdjm fatal("dup %s: %s", _PATH_DEVNULL, strerror(errno)); 906856b6ee8Sdjm } 907856b6ee8Sdjm 90871f11376Sdjm #ifdef WITH_OPENSSL 90971f11376Sdjm OpenSSL_add_all_algorithms(); 91071f11376Sdjm #endif 91171f11376Sdjm 91271f11376Sdjm /* If requested, redirect the logs to the specified logfile. */ 91371f11376Sdjm if (logfile != NULL) { 91471f11376Sdjm char *cp, pid_s[32]; 91571f11376Sdjm 91671f11376Sdjm snprintf(pid_s, sizeof(pid_s), "%ld", (unsigned long)getpid()); 91771f11376Sdjm cp = percent_expand(logfile, 91871f11376Sdjm "p", pid_s, 91971f11376Sdjm "P", "sshd-session", 92071f11376Sdjm (char *)NULL); 92171f11376Sdjm log_redirect_stderr_to(cp); 92271f11376Sdjm free(cp); 92371f11376Sdjm } 92471f11376Sdjm 92571f11376Sdjm /* 92671f11376Sdjm * Force logging to stderr until we have loaded the private host 92771f11376Sdjm * key (unless started from inetd) 92871f11376Sdjm */ 92971f11376Sdjm log_init(__progname, 93071f11376Sdjm options.log_level == SYSLOG_LEVEL_NOT_SET ? 93171f11376Sdjm SYSLOG_LEVEL_INFO : options.log_level, 93271f11376Sdjm options.log_facility == SYSLOG_FACILITY_NOT_SET ? 93371f11376Sdjm SYSLOG_FACILITY_AUTH : options.log_facility, 93471f11376Sdjm log_stderr || !inetd_flag || debug_flag); 93571f11376Sdjm 93671f11376Sdjm /* Fetch our configuration */ 93771f11376Sdjm if ((cfg = sshbuf_new()) == NULL) 93871f11376Sdjm fatal("sshbuf_new config buf failed"); 93971f11376Sdjm setproctitle("%s", "[rexeced]"); 94071f11376Sdjm recv_rexec_state(REEXEC_CONFIG_PASS_FD, cfg, &timing_secret); 941856b6ee8Sdjm /* close the fd, but keep the slot reserved */ 942856b6ee8Sdjm if (dup2(devnull, REEXEC_CONFIG_PASS_FD) == -1) 943856b6ee8Sdjm fatal("dup2 devnull->config fd: %s", strerror(errno)); 94471f11376Sdjm parse_server_config(&options, "rexec", cfg, &includes, NULL, 1); 94571f11376Sdjm /* Fill in default values for those options not explicitly set. */ 94671f11376Sdjm fill_default_server_options(&options); 94771f11376Sdjm options.timing_secret = timing_secret; 94871f11376Sdjm 949*874e263eSdtucker /* Reinit logging in case config set Level, Facility or Verbose. */ 950*874e263eSdtucker log_init(__progname, options.log_level, options.log_facility, 951*874e263eSdtucker log_stderr || !inetd_flag || debug_flag); 952*874e263eSdtucker 953*874e263eSdtucker debug("sshd-session version %s, %s", SSH_VERSION, SSH_OPENSSL_VERSION); 954*874e263eSdtucker 955856b6ee8Sdjm if (!debug_flag && !inetd_flag) { 956856b6ee8Sdjm if ((startup_pipe = dup(REEXEC_STARTUP_PIPE_FD)) == -1) 957856b6ee8Sdjm fatal("internal error: no startup pipe"); 958856b6ee8Sdjm /* close the fd, but keep the slot reserved */ 959856b6ee8Sdjm if (dup2(devnull, REEXEC_STARTUP_PIPE_FD) == -1) 960856b6ee8Sdjm fatal("dup2 devnull->startup fd: %s", strerror(errno)); 961856b6ee8Sdjm 96271f11376Sdjm /* 96371f11376Sdjm * Signal parent that this child is at a point where 96471f11376Sdjm * they can go away if they have a SIGHUP pending. 96571f11376Sdjm */ 96671f11376Sdjm (void)atomicio(vwrite, startup_pipe, "\0", 1); 96771f11376Sdjm } 96871f11376Sdjm 96971f11376Sdjm /* Check that options are sensible */ 97071f11376Sdjm if (options.authorized_keys_command_user == NULL && 97171f11376Sdjm (options.authorized_keys_command != NULL && 97271f11376Sdjm strcasecmp(options.authorized_keys_command, "none") != 0)) 97371f11376Sdjm fatal("AuthorizedKeysCommand set without " 97471f11376Sdjm "AuthorizedKeysCommandUser"); 97571f11376Sdjm if (options.authorized_principals_command_user == NULL && 97671f11376Sdjm (options.authorized_principals_command != NULL && 97771f11376Sdjm strcasecmp(options.authorized_principals_command, "none") != 0)) 97871f11376Sdjm fatal("AuthorizedPrincipalsCommand set without " 97971f11376Sdjm "AuthorizedPrincipalsCommandUser"); 98071f11376Sdjm 98171f11376Sdjm /* 98271f11376Sdjm * Check whether there is any path through configured auth methods. 98371f11376Sdjm * Unfortunately it is not possible to verify this generally before 98471f11376Sdjm * daemonisation in the presence of Match block, but this catches 98571f11376Sdjm * and warns for trivial misconfigurations that could break login. 98671f11376Sdjm */ 98771f11376Sdjm if (options.num_auth_methods != 0) { 98871f11376Sdjm for (i = 0; i < options.num_auth_methods; i++) { 98971f11376Sdjm if (auth2_methods_valid(options.auth_methods[i], 99071f11376Sdjm 1) == 0) 99171f11376Sdjm break; 99271f11376Sdjm } 99371f11376Sdjm if (i >= options.num_auth_methods) 99471f11376Sdjm fatal("AuthenticationMethods cannot be satisfied by " 99571f11376Sdjm "enabled authentication methods"); 99671f11376Sdjm } 99771f11376Sdjm 99871f11376Sdjm #ifdef WITH_OPENSSL 99971f11376Sdjm if (options.moduli_file != NULL) 100071f11376Sdjm dh_set_moduli_file(options.moduli_file); 100171f11376Sdjm #endif 100271f11376Sdjm 100371f11376Sdjm if (options.host_key_agent) { 100471f11376Sdjm if (strcmp(options.host_key_agent, SSH_AUTHSOCKET_ENV_NAME)) 100571f11376Sdjm setenv(SSH_AUTHSOCKET_ENV_NAME, 100671f11376Sdjm options.host_key_agent, 1); 100771f11376Sdjm if ((r = ssh_get_authentication_socket(NULL)) == 0) 100871f11376Sdjm have_agent = 1; 100971f11376Sdjm else 101071f11376Sdjm error_r(r, "Could not connect to agent \"%s\"", 101171f11376Sdjm options.host_key_agent); 101271f11376Sdjm } 101371f11376Sdjm 101471f11376Sdjm if (options.num_host_key_files != sensitive_data.num_hostkeys) { 101571f11376Sdjm fatal("internal error: hostkeys confused (config %u recvd %u)", 101671f11376Sdjm options.num_host_key_files, sensitive_data.num_hostkeys); 101771f11376Sdjm } 101871f11376Sdjm 101971f11376Sdjm for (i = 0; i < options.num_host_key_files; i++) { 102071f11376Sdjm if (sensitive_data.host_keys[i] != NULL || 102171f11376Sdjm (have_agent && sensitive_data.host_pubkeys[i] != NULL)) { 102271f11376Sdjm have_key = 1; 102371f11376Sdjm break; 102471f11376Sdjm } 102571f11376Sdjm } 102671f11376Sdjm if (!have_key) 10279574f1dcSjsg fatal("internal error: monitor received no hostkeys"); 102871f11376Sdjm 102971f11376Sdjm /* Ensure that umask disallows at least group and world write */ 103071f11376Sdjm new_umask = umask(0077) | 0022; 103171f11376Sdjm (void) umask(new_umask); 103271f11376Sdjm 103371f11376Sdjm /* Initialize the log (it is reinitialized below in case we forked). */ 103471f11376Sdjm if (debug_flag) 103571f11376Sdjm log_stderr = 1; 103671f11376Sdjm log_init(__progname, options.log_level, 103771f11376Sdjm options.log_facility, log_stderr); 103871f11376Sdjm for (i = 0; i < options.num_log_verbose; i++) 103971f11376Sdjm log_verbose_add(options.log_verbose[i]); 104071f11376Sdjm 104171f11376Sdjm /* Reinitialize the log (because of the fork above). */ 104271f11376Sdjm log_init(__progname, options.log_level, options.log_facility, log_stderr); 104371f11376Sdjm 104471f11376Sdjm /* 104571f11376Sdjm * Chdir to the root directory so that the current disk can be 104671f11376Sdjm * unmounted if desired. 104771f11376Sdjm */ 104871f11376Sdjm if (chdir("/") == -1) 104971f11376Sdjm error("chdir(\"/\"): %s", strerror(errno)); 105071f11376Sdjm 105171f11376Sdjm /* ignore SIGPIPE */ 105271f11376Sdjm ssh_signal(SIGPIPE, SIG_IGN); 105371f11376Sdjm 105471f11376Sdjm /* Get a connection, either from inetd or rexec */ 105571f11376Sdjm if (inetd_flag) { 105671f11376Sdjm /* 105771f11376Sdjm * NB. must be different fd numbers for the !socket case, 105871f11376Sdjm * as packet_connection_is_on_socket() depends on this. 105971f11376Sdjm */ 106071f11376Sdjm sock_in = dup(STDIN_FILENO); 106171f11376Sdjm sock_out = dup(STDOUT_FILENO); 106271f11376Sdjm } else { 106371f11376Sdjm /* rexec case; accept()ed socket in ancestor listener */ 106471f11376Sdjm sock_in = sock_out = dup(STDIN_FILENO); 106571f11376Sdjm } 106671f11376Sdjm 106771f11376Sdjm /* 106871f11376Sdjm * We intentionally do not close the descriptors 0, 1, and 2 106971f11376Sdjm * as our code for setting the descriptors won't work if 107071f11376Sdjm * ttyfd happens to be one of those. 107171f11376Sdjm */ 107271f11376Sdjm if (stdfd_devnull(1, 1, !log_stderr) == -1) 107371f11376Sdjm error("stdfd_devnull failed"); 107471f11376Sdjm debug("network sockets: %d, %d", sock_in, sock_out); 107571f11376Sdjm 107671f11376Sdjm /* This is the child processing a new connection. */ 107771f11376Sdjm setproctitle("%s", "[accepted]"); 107871f11376Sdjm 107971f11376Sdjm /* Executed child processes don't need these. */ 108071f11376Sdjm fcntl(sock_out, F_SETFD, FD_CLOEXEC); 108171f11376Sdjm fcntl(sock_in, F_SETFD, FD_CLOEXEC); 108271f11376Sdjm 108371f11376Sdjm /* We will not restart on SIGHUP since it no longer makes sense. */ 108471f11376Sdjm ssh_signal(SIGALRM, SIG_DFL); 108571f11376Sdjm ssh_signal(SIGHUP, SIG_DFL); 108671f11376Sdjm ssh_signal(SIGTERM, SIG_DFL); 108771f11376Sdjm ssh_signal(SIGQUIT, SIG_DFL); 108871f11376Sdjm ssh_signal(SIGCHLD, SIG_DFL); 108971f11376Sdjm 109071f11376Sdjm /* 109171f11376Sdjm * Register our connection. This turns encryption off because we do 109271f11376Sdjm * not have a key. 109371f11376Sdjm */ 109471f11376Sdjm if ((ssh = ssh_packet_set_connection(NULL, sock_in, sock_out)) == NULL) 109571f11376Sdjm fatal("Unable to create connection"); 109671f11376Sdjm the_active_state = ssh; 109771f11376Sdjm ssh_packet_set_server(ssh); 109871f11376Sdjm 109971f11376Sdjm check_ip_options(ssh); 110071f11376Sdjm 110171f11376Sdjm /* Prepare the channels layer */ 110271f11376Sdjm channel_init_channels(ssh); 110371f11376Sdjm channel_set_af(ssh, options.address_family); 110471f11376Sdjm server_process_channel_timeouts(ssh); 110571f11376Sdjm server_process_permitopen(ssh); 110671f11376Sdjm 110771f11376Sdjm /* Set SO_KEEPALIVE if requested. */ 110871f11376Sdjm if (options.tcp_keep_alive && ssh_packet_connection_is_on_socket(ssh) && 110971f11376Sdjm setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1) 111071f11376Sdjm error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 111171f11376Sdjm 111271f11376Sdjm if ((remote_port = ssh_remote_port(ssh)) < 0) { 111371f11376Sdjm debug("ssh_remote_port failed"); 111471f11376Sdjm cleanup_exit(255); 111571f11376Sdjm } 111671f11376Sdjm 111771f11376Sdjm /* 111871f11376Sdjm * The rest of the code depends on the fact that 111971f11376Sdjm * ssh_remote_ipaddr() caches the remote ip, even if 112071f11376Sdjm * the socket goes away. 112171f11376Sdjm */ 112271f11376Sdjm remote_ip = ssh_remote_ipaddr(ssh); 112371f11376Sdjm 112471f11376Sdjm rdomain = ssh_packet_rdomain_in(ssh); 112571f11376Sdjm 112671f11376Sdjm /* Log the connection. */ 112771f11376Sdjm laddr = get_local_ipaddr(sock_in); 112871f11376Sdjm verbose("Connection from %s port %d on %s port %d%s%s%s", 112971f11376Sdjm remote_ip, remote_port, laddr, ssh_local_port(ssh), 113071f11376Sdjm rdomain == NULL ? "" : " rdomain \"", 113171f11376Sdjm rdomain == NULL ? "" : rdomain, 113271f11376Sdjm rdomain == NULL ? "" : "\""); 113371f11376Sdjm free(laddr); 113471f11376Sdjm 113571f11376Sdjm /* 113671f11376Sdjm * We don't want to listen forever unless the other side 113771f11376Sdjm * successfully authenticates itself. So we set up an alarm which is 113871f11376Sdjm * cleared after successful authentication. A limit of zero 113971f11376Sdjm * indicates no limit. Note that we don't set the alarm in debugging 114071f11376Sdjm * mode; it is just annoying to have the server exit just when you 114171f11376Sdjm * are about to discover the bug. 114271f11376Sdjm */ 114371f11376Sdjm ssh_signal(SIGALRM, grace_alarm_handler); 1144873f0a37Sdlg if (!debug_flag && options.login_grace_time > 0) { 1145873f0a37Sdlg int ujitter = arc4random_uniform(4 * 1000000); 1146873f0a37Sdlg 1147873f0a37Sdlg timerclear(&itv.it_interval); 1148873f0a37Sdlg itv.it_value.tv_sec = options.login_grace_time; 1149873f0a37Sdlg itv.it_value.tv_sec += ujitter / 1000000; 1150873f0a37Sdlg itv.it_value.tv_usec = ujitter % 1000000; 1151873f0a37Sdlg 1152873f0a37Sdlg if (setitimer(ITIMER_REAL, &itv, NULL) == -1) 1153873f0a37Sdlg fatal("login grace time setitimer failed"); 1154873f0a37Sdlg } 115571f11376Sdjm 115671f11376Sdjm if ((r = kex_exchange_identification(ssh, -1, 115771f11376Sdjm options.version_addendum)) != 0) 115871f11376Sdjm sshpkt_fatal(ssh, r, "banner exchange"); 115971f11376Sdjm 116071f11376Sdjm ssh_packet_set_nonblocking(ssh); 116171f11376Sdjm 116271f11376Sdjm /* allocate authentication context */ 116371f11376Sdjm authctxt = xcalloc(1, sizeof(*authctxt)); 116471f11376Sdjm ssh->authctxt = authctxt; 116571f11376Sdjm 116671f11376Sdjm /* XXX global for cleanup, access from other modules */ 116771f11376Sdjm the_authctxt = authctxt; 116871f11376Sdjm 116971f11376Sdjm /* Set default key authentication options */ 117071f11376Sdjm if ((auth_opts = sshauthopt_new_with_keys_defaults()) == NULL) 117171f11376Sdjm fatal("allocation failed"); 117271f11376Sdjm 117371f11376Sdjm /* prepare buffer to collect messages to display to user after login */ 117471f11376Sdjm if ((loginmsg = sshbuf_new()) == NULL) 117571f11376Sdjm fatal("sshbuf_new loginmsg failed"); 117671f11376Sdjm auth_debug_reset(); 117771f11376Sdjm 1178856b6ee8Sdjm if (privsep_preauth(ssh) != 1) 1179856b6ee8Sdjm fatal("privsep_preauth failed"); 118071f11376Sdjm 1181856b6ee8Sdjm /* Now user is authenticated */ 118271f11376Sdjm 118371f11376Sdjm /* 118471f11376Sdjm * Cancel the alarm we set to limit the time taken for 118571f11376Sdjm * authentication. 118671f11376Sdjm */ 1187873f0a37Sdlg timerclear(&itv.it_interval); 1188873f0a37Sdlg timerclear(&itv.it_value); 1189873f0a37Sdlg if (setitimer(ITIMER_REAL, &itv, NULL) == -1) 1190873f0a37Sdlg fatal("login grace time clear failed"); 119171f11376Sdjm ssh_signal(SIGALRM, SIG_DFL); 119271f11376Sdjm authctxt->authenticated = 1; 119371f11376Sdjm if (startup_pipe != -1) { 11947965d983Sdjm /* signal listener that authentication completed successfully */ 11957965d983Sdjm (void)atomicio(vwrite, startup_pipe, "\001", 1); 119671f11376Sdjm close(startup_pipe); 119771f11376Sdjm startup_pipe = -1; 119871f11376Sdjm } 119971f11376Sdjm 120071f11376Sdjm if (options.routing_domain != NULL) 120171f11376Sdjm set_process_rdomain(ssh, options.routing_domain); 120271f11376Sdjm 120371f11376Sdjm /* 120471f11376Sdjm * In privilege separation, we fork another child and prepare 120571f11376Sdjm * file descriptor passing. 120671f11376Sdjm */ 120771f11376Sdjm privsep_postauth(ssh, authctxt); 120871f11376Sdjm /* the monitor process [priv] will not return */ 120971f11376Sdjm 121071f11376Sdjm ssh_packet_set_timeout(ssh, options.client_alive_interval, 121171f11376Sdjm options.client_alive_count_max); 121271f11376Sdjm 121371f11376Sdjm /* Try to send all our hostkeys to the client */ 121471f11376Sdjm notify_hostkeys(ssh); 121571f11376Sdjm 121671f11376Sdjm /* Start session. */ 121771f11376Sdjm do_authenticated(ssh, authctxt); 121871f11376Sdjm 121971f11376Sdjm /* The connection has been terminated. */ 122071f11376Sdjm ssh_packet_get_bytes(ssh, &ibytes, &obytes); 122171f11376Sdjm verbose("Transferred: sent %llu, received %llu bytes", 122271f11376Sdjm (unsigned long long)obytes, (unsigned long long)ibytes); 122371f11376Sdjm 122471f11376Sdjm verbose("Closing connection to %.500s port %d", remote_ip, remote_port); 122571f11376Sdjm ssh_packet_close(ssh); 122671f11376Sdjm 122771f11376Sdjm mm_terminate(); 122871f11376Sdjm 122971f11376Sdjm exit(0); 123071f11376Sdjm } 123171f11376Sdjm 123271f11376Sdjm int 123371f11376Sdjm sshd_hostkey_sign(struct ssh *ssh, struct sshkey *privkey, 123471f11376Sdjm struct sshkey *pubkey, u_char **signature, size_t *slenp, 123571f11376Sdjm const u_char *data, size_t dlen, const char *alg) 123671f11376Sdjm { 123771f11376Sdjm if (privkey) { 123871f11376Sdjm if (mm_sshkey_sign(ssh, privkey, signature, slenp, 123971f11376Sdjm data, dlen, alg, options.sk_provider, NULL, 124071f11376Sdjm ssh->compat) < 0) 124171f11376Sdjm fatal_f("privkey sign failed"); 124271f11376Sdjm } else { 124371f11376Sdjm if (mm_sshkey_sign(ssh, pubkey, signature, slenp, 124471f11376Sdjm data, dlen, alg, options.sk_provider, NULL, 124571f11376Sdjm ssh->compat) < 0) 124671f11376Sdjm fatal_f("pubkey sign failed"); 124771f11376Sdjm } 124871f11376Sdjm return 0; 124971f11376Sdjm } 125071f11376Sdjm 125171f11376Sdjm /* server specific fatal cleanup */ 125271f11376Sdjm void 125371f11376Sdjm cleanup_exit(int i) 125471f11376Sdjm { 12557965d983Sdjm extern int auth_attempted; /* monitor.c */ 12567965d983Sdjm 125771f11376Sdjm if (the_active_state != NULL && the_authctxt != NULL) { 125871f11376Sdjm do_cleanup(the_active_state, the_authctxt); 125971f11376Sdjm if (privsep_is_preauth && 126071f11376Sdjm pmonitor != NULL && pmonitor->m_pid > 1) { 126171f11376Sdjm debug("Killing privsep child %d", pmonitor->m_pid); 126271f11376Sdjm if (kill(pmonitor->m_pid, SIGKILL) != 0 && 126371f11376Sdjm errno != ESRCH) { 126471f11376Sdjm error_f("kill(%d): %s", pmonitor->m_pid, 126571f11376Sdjm strerror(errno)); 126671f11376Sdjm } 126771f11376Sdjm } 126871f11376Sdjm } 12697965d983Sdjm /* Override default fatal exit value when auth was attempted */ 12707965d983Sdjm if (i == 255 && auth_attempted) 12717965d983Sdjm _exit(EXIT_AUTH_ATTEMPTED); 127271f11376Sdjm _exit(i); 127371f11376Sdjm } 1274