+ debug3("%s: done", __func__);
+}
+
+/* Accept a connection from inetd */
+static void
+server_accept_inetd(int *sock_in, int *sock_out)
+{
+ int fd;
+
+ startup_pipe = -1;
+ if (rexeced_flag) {
+ close(REEXEC_CONFIG_PASS_FD);
+ *sock_in = *sock_out = dup(STDIN_FILENO);
+ if (!debug_flag) {
+ startup_pipe = dup(REEXEC_STARTUP_PIPE_FD);
+ close(REEXEC_STARTUP_PIPE_FD);
+ }
+ } else {
+ *sock_in = dup(STDIN_FILENO);
+ *sock_out = dup(STDOUT_FILENO);
+ }
+ /*
+ * 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.
+ */
+ if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ if (fd > STDOUT_FILENO)
+ close(fd);
+ }
+ debug("inetd sockets after dupping: %d, %d", *sock_in, *sock_out);
+}
+
+/*
+ * Listen for TCP connections
+ */
+static void
+server_listen(void)
+{
+ int ret, listen_sock, on = 1;
+ struct addrinfo *ai;
+ char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+ int socksize;
+ int socksizelen = sizeof(int);
+
+ 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 ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen,
+ ntop, sizeof(ntop), strport, sizeof(strport),
+ NI_NUMERICHOST|NI_NUMERICSERV)) != 0) {
+ error("getnameinfo failed: %.100s",
+ ssh_gai_strerror(ret));
+ continue;
+ }
+ /* Create socket for listening. */
+ listen_sock = socket(ai->ai_family, ai->ai_socktype,
+ ai->ai_protocol);
+ if (listen_sock < 0) {
+ /* kernel may not support ipv6 */
+ verbose("socket: %.100s", strerror(errno));
+ continue;
+ }
+ if (set_nonblock(listen_sock) == -1) {
+ close(listen_sock);
+ continue;
+ }
+ /*
+ * Set socket options.
+ * Allow local port reuse in TIME_WAIT.
+ */
+ if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on)) == -1)
+ error("setsockopt SO_REUSEADDR: %s", strerror(errno));
+
+#ifdef IPV6_V6ONLY
+ /* Only communicate in IPv6 over AF_INET6 sockets. */
+ if (ai->ai_family == AF_INET6) {
+ if (setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY,
+ &on, sizeof(on)) == -1)
+ error("setsockopt IPV6_V6ONLY: %s",
+ strerror(errno));
+ }
+#endif
+
+ debug("Bind to port %s on %s.", strport, ntop);
+
+ getsockopt(listen_sock, SOL_SOCKET, SO_RCVBUF,
+ &socksize, &socksizelen);
+ debug("Server TCP RWIN socket size: %d", socksize);
+ debug("HPN Buffer Size: %d", options.hpn_buffer_size);
+
+ /* 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. */
+ if (listen(listen_sock, SSH_LISTEN_BACKLOG) < 0)
+ fatal("listen on [%s]:%s: %.100s",
+ ntop, strport, strerror(errno));
+ logit("Server listening on %s port %s.", ntop, strport);
+ }
+ freeaddrinfo(options.listen_addrs);
+
+ if (!num_listen_socks)
+ fatal("Cannot bind any address.");
+}
+
+/*
+ * The main TCP accept loop. Note that, for the non-debug case, returns
+ * from this function are in a forked subprocess.
+ */
+static void
+server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
+{
+ fd_set *fdset;
+ int i, j, ret, maxfd;
+ int key_used = 0, startups = 0;
+ int startup_p[2] = { -1 , -1 };
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+ pid_t pid;
+
+ /* setup fd set for accept */
+ fdset = NULL;
+ maxfd = 0;
+ for (i = 0; i < num_listen_socks; i++)
+ if (listen_socks[i] > maxfd)
+ maxfd = listen_socks[i];
+ /* pipes connected to unauthenticated childs */
+ startup_pipes = xcalloc(options.max_startups, sizeof(int));
+ for (i = 0; i < options.max_startups; i++)
+ startup_pipes[i] = -1;
+
+ /*
+ * Stay listening for connections until the system crashes or
+ * the daemon is killed with a signal.
+ */
+ for (;;) {
+ if (received_sighup)
+ sighup_restart();
+ if (fdset != NULL)
+ xfree(fdset);
+ fdset = (fd_set *)xcalloc(howmany(maxfd + 1, NFDBITS),
+ sizeof(fd_mask));
+
+ for (i = 0; i < num_listen_socks; i++)
+ FD_SET(listen_socks[i], fdset);
+ for (i = 0; i < options.max_startups; i++)
+ if (startup_pipes[i] != -1)
+ FD_SET(startup_pipes[i], fdset);
+
+ /* Wait in select until there is a connection. */
+ ret = select(maxfd+1, fdset, NULL, NULL, NULL);
+ if (ret < 0 && errno != EINTR)
+ error("select: %.100s", strerror(errno));
+ if (received_sigterm) {
+ logit("Received signal %d; terminating.",
+ (int) received_sigterm);
+ close_listen_socks();
+ unlink(options.pid_file);
+ exit(255);
+ }
+ if (key_used && key_do_regen) {
+ generate_ephemeral_server_key();
+ key_used = 0;
+ key_do_regen = 0;
+ }
+ if (ret < 0)
+ continue;
+
+ for (i = 0; i < options.max_startups; i++)
+ if (startup_pipes[i] != -1 &&
+ FD_ISSET(startup_pipes[i], fdset)) {
+ /*
+ * the read end of the pipe is ready
+ * if the child has closed the pipe
+ * after successful authentication
+ * or if the child has died
+ */
+ close(startup_pipes[i]);
+ startup_pipes[i] = -1;
+ startups--;
+ }
+ 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 != EAGAIN &&
+ errno != EWOULDBLOCK)
+ error("accept: %.100s", strerror(errno));
+ continue;
+ }
+ if (unset_nonblock(*newsock) == -1) {
+ close(*newsock);
+ continue;
+ }
+ if (drop_connection(startups) == 1) {
+ debug("drop connection #%d", startups);
+ close(*newsock);
+ continue;
+ }
+ if (pipe(startup_p) == -1) {
+ close(*newsock);
+ continue;
+ }
+
+ if (rexec_flag && socketpair(AF_UNIX,
+ SOCK_STREAM, 0, config_s) == -1) {
+ error("reexec socketpair: %s",
+ strerror(errno));
+ close(*newsock);
+ close(startup_p[0]);
+ close(startup_p[1]);
+ continue;
+ }
+
+ for (j = 0; j < options.max_startups; j++)
+ if (startup_pipes[j] == -1) {
+ startup_pipes[j] = startup_p[0];
+ if (maxfd < startup_p[0])
+ maxfd = startup_p[0];
+ startups++;
+ break;
+ }
+
+ /*
+ * Got connection. Fork a child to handle it, unless
+ * we are in debugging mode.
+ */
+ 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;
+ close(startup_p[0]);
+ close(startup_p[1]);
+ startup_pipe = -1;
+ pid = getpid();
+ if (rexec_flag) {
+ send_rexec_state(config_s[0],
+ &cfg);
+ close(config_s[0]);
+ }
+ break;
+ }
+
+ /*
+ * Normal production daemon. Fork, and have
+ * the child process the connection. The
+ * parent continues listening.
+ */
+ platform_pre_fork();
+ if ((pid = fork()) == 0) {
+ /*
+ * Child. Close the listening and
+ * max_startup sockets. Start using
+ * the accepted socket. Reinitialize
+ * logging (since our pid has changed).
+ * We break out of the loop to handle
+ * the connection.
+ */
+ platform_post_fork_child();
+ startup_pipe = startup_p[1];
+ close_startup_pipes();
+ close_listen_socks();
+ *sock_in = *newsock;
+ *sock_out = *newsock;
+ log_init(__progname,
+ options.log_level,
+ options.log_facility,
+ log_stderr);
+ if (rexec_flag)
+ close(config_s[0]);
+ break;
+ }
+
+ /* Parent. Stay in the loop. */
+ platform_post_fork_parent(pid);
+ if (pid < 0)
+ error("fork: %.100s", strerror(errno));
+ else
+ debug("Forked child %ld.", (long)pid);
+
+ close(startup_p[1]);
+
+ if (rexec_flag) {
+ send_rexec_state(config_s[0], &cfg);
+ close(config_s[0]);
+ close(config_s[1]);
+ }
+
+ /*
+ * Mark that the key has been used (it
+ * was "given" to the child).
+ */
+ if ((options.protocol & SSH_PROTO_1) &&
+ key_used == 0) {
+ /* Schedule server key regeneration alarm. */
+ signal(SIGALRM, key_regeneration_alarm);
+ alarm(options.key_regeneration_time);
+ key_used = 1;
+ }
+
+ close(*newsock);
+
+ /*
+ * Ensure that our random state differs
+ * from that of the child
+ */
+ arc4random_stir();
+ }
+
+ /* child process check (or debug mode) */
+ if (num_listen_socks < 0)
+ break;
+ }
+}
+
+
+/*
+ * Main program for the daemon.
+ */
+int
+main(int ac, char **av)
+{
+ extern char *optarg;
+ extern int optind;
+ int opt, i, on = 1;
+ int sock_in = -1, sock_out = -1, newsock = -1;
+ const char *remote_ip;
+ char *test_user = NULL, *test_host = NULL, *test_addr = NULL;
+ int remote_port;
+ char *line, *p, *cp;
+ int config_s[2] = { -1 , -1 };
+ u_int64_t ibytes, obytes;
+ mode_t new_umask;
+ Key *key;
+ Authctxt *authctxt;
+
+#ifdef HAVE_SECUREWARE
+ (void)set_auth_parameters(ac, av);
+#endif
+ __progname = ssh_get_progname(av[0]);
+ init_rng();
+
+ /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */
+ saved_argc = ac;
+ rexec_argc = ac;
+ saved_argv = xcalloc(ac + 1, sizeof(*saved_argv));
+ for (i = 0; i < ac; i++)
+ saved_argv[i] = xstrdup(av[i]);
+ saved_argv[i] = NULL;
+
+#ifndef HAVE_SETPROCTITLE
+ /* Prepare for later setproctitle emulation */
+ compat_init_setproctitle(ac, av);
+ av = saved_argv;
+#endif
+
+ if (geteuid() == 0 && setgroups(0, NULL) == -1)
+ debug("setgroups(): %.200s", strerror(errno));
+
+ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
+ sanitise_stdfd();
+
+ /* 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:u:o:C:dDeiqrtQRT46")) != -1) {
+ switch (opt) {
+ case '4':
+ options.address_family = AF_INET;
+ break;
+ case '6':
+ options.address_family = AF_INET6;
+ break;
+ case 'f':
+ config_file_name = optarg;
+ break;
+ case 'd':
+ if (debug_flag == 0) {
+ debug_flag = 1;
+ options.log_level = SYSLOG_LEVEL_DEBUG1;
+ } else if (options.log_level < SYSLOG_LEVEL_DEBUG3)
+ options.log_level++;
+ break;
+ case 'D':
+ no_daemon_flag = 1;
+ break;
+ case 'e':
+ log_stderr = 1;
+ break;
+ case 'i':
+ inetd_flag = 1;
+ break;
+ case 'r':
+ rexec_flag = 0;
+ break;
+ case 'R':
+ rexeced_flag = 1;
+ inetd_flag = 1;
+ break;
+ case 'Q':
+ /* ignored */
+ break;
+ case 'q':
+ options.log_level = SYSLOG_LEVEL_QUIET;
+ break;
+ case 'b':
+ options.server_key_bits = (int)strtonum(optarg, 256,
+ 32768, NULL);
+ break;
+ case 'p':
+ options.ports_from_cmdline = 1;
+ if (options.num_ports >= MAX_PORTS) {
+ fprintf(stderr, "too many ports.\n");
+ exit(1);
+ }
+ options.ports[options.num_ports++] = a2port(optarg);
+ if (options.ports[options.num_ports-1] <= 0) {
+ fprintf(stderr, "Bad port number.\n");
+ exit(1);
+ }
+ break;
+ case 'g':
+ if ((options.login_grace_time = convtime(optarg)) == -1) {
+ fprintf(stderr, "Invalid login grace time.\n");
+ exit(1);
+ }
+ break;
+ case 'k':
+ if ((options.key_regeneration_time = convtime(optarg)) == -1) {
+ fprintf(stderr, "Invalid key regeneration interval.\n");
+ exit(1);
+ }
+ break;
+ case 'h':
+ if (options.num_host_key_files >= MAX_HOSTKEYS) {
+ fprintf(stderr, "too many host keys.\n");
+ exit(1);
+ }
+ options.host_key_files[options.num_host_key_files++] = optarg;
+ break;
+ case 't':
+ test_flag = 1;
+ break;
+ case 'T':
+ test_flag = 2;
+ break;
+ case 'C':
+ cp = optarg;
+ while ((p = strsep(&cp, ",")) && *p != '\0') {
+ if (strncmp(p, "addr=", 5) == 0)
+ test_addr = xstrdup(p + 5);
+ else if (strncmp(p, "host=", 5) == 0)
+ test_host = xstrdup(p + 5);
+ else if (strncmp(p, "user=", 5) == 0)
+ test_user = xstrdup(p + 5);
+ else {
+ fprintf(stderr, "Invalid test "
+ "mode specification %s\n", p);
+ exit(1);
+ }
+ }
+ break;
+ case 'u':
+ utmp_len = (u_int)strtonum(optarg, 0, MAXHOSTNAMELEN+1, NULL);
+ if (utmp_len > MAXHOSTNAMELEN) {
+ fprintf(stderr, "Invalid utmp length.\n");
+ exit(1);
+ }
+ break;
+ case 'o':
+ line = xstrdup(optarg);
+ if (process_server_config_line(&options, line,
+ "command-line", 0, NULL, NULL, NULL, NULL) != 0)
+ exit(1);
+ xfree(line);
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+ if (rexeced_flag || inetd_flag)
+ rexec_flag = 0;
+ if (!test_flag && (rexec_flag && (av[0] == NULL || *av[0] != '/')))
+ fatal("sshd re-exec requires execution with an absolute path");
+ if (rexeced_flag)
+ closefrom(REEXEC_MIN_FREE_FD);
+ else
+ closefrom(REEXEC_DEVCRYPTO_RESERVED_FD);
+
+ SSLeay_add_all_algorithms();
+
+ /*
+ * Force logging to stderr until we have loaded the private host
+ * key (unless started from inetd)
+ */
+ log_init(__progname,
+ options.log_level == SYSLOG_LEVEL_NOT_SET ?
+ SYSLOG_LEVEL_INFO : options.log_level,
+ options.log_facility == SYSLOG_FACILITY_NOT_SET ?
+ SYSLOG_FACILITY_AUTH : options.log_facility,
+ log_stderr || !inetd_flag);
+
+ /*
+ * Unset KRB5CCNAME, otherwise the user's session may inherit it from
+ * root's environment
+ */
+ if (getenv("KRB5CCNAME") != NULL)
+ unsetenv("KRB5CCNAME");
+
+#ifdef _UNICOS
+ /* Cray can define user privs drop all privs now!
+ * Not needed on PRIV_SU systems!
+ */
+ drop_cray_privs();
+#endif
+
+ sensitive_data.server_key = NULL;
+ sensitive_data.ssh1_host_key = NULL;
+ sensitive_data.have_ssh1_key = 0;
+ sensitive_data.have_ssh2_key = 0;
+
+ /*
+ * If we're doing an extended config test, make sure we have all of
+ * the parameters we need. If we're not doing an extended test,
+ * do not silently ignore connection test params.
+ */
+ if (test_flag >= 2 &&
+ (test_user != NULL || test_host != NULL || test_addr != NULL)
+ && (test_user == NULL || test_host == NULL || test_addr == NULL))
+ fatal("user, host and addr are all required when testing "
+ "Match configs");
+ if (test_flag < 2 && (test_user != NULL || test_host != NULL ||
+ test_addr != NULL))
+ fatal("Config test connection parameter (-C) provided without "
+ "test mode (-T)");
+
+ /* Fetch our configuration */
+ buffer_init(&cfg);
+ if (rexeced_flag)
+ recv_rexec_state(REEXEC_CONFIG_PASS_FD, &cfg);
+ else
+ load_server_config(config_file_name, &cfg);
+
+ parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name,
+ &cfg, NULL, NULL, NULL);
+
+ seed_rng();