static int timeout_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen, int *timeoutp) { int optval = 0; socklen_t optlen = sizeof(optval); /* No timeout: just do a blocking connect() */ if (*timeoutp <= 0) return connect(sockfd, serv_addr, addrlen); set_nonblock(sockfd); if (connect(sockfd, serv_addr, addrlen) == 0) { /* Succeeded already? */ unset_nonblock(sockfd); return 0; } else if (errno != EINPROGRESS) return -1; if (waitrfd(sockfd, timeoutp) == -1) return -1; /* Completed or failed */ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { debug("getsockopt: %s", strerror(errno)); return -1; } if (optval != 0) { errno = optval; return -1; } unset_nonblock(sockfd); return 0; }
static FILE * auth_openfile(const char *file, struct passwd *pw, int strict_modes, int log_missing, const char *file_type) { char line[1024]; struct stat st; int fd; FILE *f; if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) { if (log_missing || errno != ENOENT) debug("Could not open %s '%s': %s", file_type, file, strerror(errno)); return NULL; } if (fstat(fd, &st) < 0) { close(fd); return NULL; } if (!S_ISREG(st.st_mode)) { logit("User %s %s %s is not a regular file", pw->pw_name, file_type, file); close(fd); return NULL; } unset_nonblock(fd); if ((f = fdopen(fd, "r")) == NULL) { close(fd); return NULL; } if (strict_modes && safe_path_fd(fileno(f), file, pw, line, sizeof(line)) != 0) { fclose(f); logit("Authentication refused: %s", line); auth_debug_add("Ignored %s: %s", file_type, line); return NULL; } return f; }
FILE * auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes) { char line[1024]; struct stat st; int fd; FILE *f; /* * Open the file containing the authorized keys * Fail quietly if file does not exist */ if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) return NULL; if (fstat(fd, &st) < 0) { close(fd); return NULL; } if (!S_ISREG(st.st_mode)) { logit("User %s authorized keys %s is not a regular file", pw->pw_name, file); close(fd); return NULL; } unset_nonblock(fd); if ((f = fdopen(fd, "r")) == NULL) { close(fd); return NULL; } if (options.strict_modes && secure_filename(f, file, pw, line, sizeof(line)) != 0) { fclose(f); logit("Authentication refused: %s", line); return NULL; } return f; }
mongo_connection * mongo_connect (const char *host, int port) { struct addrinfo *res = NULL, *r; struct addrinfo hints; int e, fd = -1; gchar *port_s; mongo_connection *conn; if (!host) { errno = EINVAL; return NULL; } conn = g_new0 (mongo_connection, 1); memset (&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_STREAM; #ifdef __linux__ hints.ai_flags = AI_ADDRCONFIG; #endif port_s = g_strdup_printf ("%d", port); e = getaddrinfo (host, port_s, &hints, &res); if (e != 0) { int err = errno; g_free (conn); g_free (port_s); errno = err; return NULL; } g_free (port_s); for (r = res; r != NULL; r = r->ai_next) { fd = socket (r->ai_family, r->ai_socktype, r->ai_protocol); if (fd != -1 && connect (fd, r->ai_addr, r->ai_addrlen) == 0) break; if (fd != -1) { close (fd); fd = -1; } } freeaddrinfo (res); if (fd == -1) { g_free (conn); errno = EADDRNOTAVAIL; return NULL; } setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof (one)); if (unset_nonblock (fd)) { int err = errno; g_free (conn); close (fd); errno = err; return NULL; } conn->fd = fd; return conn; }
static int check_rhosts_file(const char *filename, const char *hostname, const char *ipaddr, const char *client_user, const char *server_user) { FILE *f; char buf[1024]; /* Must not be larger than host, user, dummy below. */ int fd; struct stat st; /* Open the .rhosts file, deny if unreadable */ if ((fd = open(filename, O_RDONLY|O_NONBLOCK)) == -1) return 0; if (fstat(fd, &st) == -1) { close(fd); return 0; } if (!S_ISREG(st.st_mode)) { logit("User %s hosts file %s is not a regular file", server_user, filename); close(fd); return 0; } unset_nonblock(fd); if ((f = fdopen(fd, "r")) == NULL) { close(fd); return 0; } while (fgets(buf, sizeof(buf), f)) { /* All three must be at least as big as buf to avoid overflows. */ char hostbuf[1024], userbuf[1024], dummy[1024], *host, *user, *cp; int negated; for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) ; if (*cp == '#' || *cp == '\n' || !*cp) continue; /* * NO_PLUS is supported at least on OSF/1. We skip it (we * don't ever support the plus syntax). */ if (strncmp(cp, "NO_PLUS", 7) == 0) continue; /* * This should be safe because each buffer is as big as the * whole string, and thus cannot be overwritten. */ switch (sscanf(buf, "%1023s %1023s %1023s", hostbuf, userbuf, dummy)) { case 0: auth_debug_add("Found empty line in %.100s.", filename); continue; case 1: /* Host name only. */ strlcpy(userbuf, server_user, sizeof(userbuf)); break; case 2: /* Got both host and user name. */ break; case 3: auth_debug_add("Found garbage in %.100s.", filename); continue; default: /* Weird... */ continue; } host = hostbuf; user = userbuf; negated = 0; /* Process negated host names, or positive netgroups. */ if (host[0] == '-') { negated = 1; host++; } else if (host[0] == '+') host++; if (user[0] == '-') { negated = 1; user++; } else if (user[0] == '+') user++; /* Check for empty host/user names (particularly '+'). */ if (!host[0] || !user[0]) { /* We come here if either was '+' or '-'. */ auth_debug_add("Ignoring wild host/user names in %.100s.", filename); continue; } /* Verify that host name matches. */ if (host[0] == '@') { if (!innetgr(host + 1, hostname, NULL, NULL) && !innetgr(host + 1, ipaddr, NULL, NULL)) continue; } else if (strcasecmp(host, hostname) && strcmp(host, ipaddr) != 0) continue; /* Different hostname. */ /* Verify that user name matches. */ if (user[0] == '@') { if (!innetgr(user + 1, NULL, client_user, NULL)) continue; } else if (strcmp(user, client_user) != 0) continue; /* Different username. */ /* Found the user and host. */ fclose(f); /* If the entry was negated, deny access. */ if (negated) { auth_debug_add("Matched negative entry in %.100s.", filename); return 0; } /* Accept authentication. */ return 1; } /* Authentication using this file denied. */ fclose(f); return 0; }
static int timeout_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen, int *timeoutp) { fd_set *fdset; struct timeval tv, t_start; socklen_t optlen; int optval, rc, result = -1; gettimeofday(&t_start, NULL); if (*timeoutp <= 0) { result = connect(sockfd, serv_addr, addrlen); goto done; } set_nonblock(sockfd); rc = connect(sockfd, serv_addr, addrlen); if (rc == 0) { unset_nonblock(sockfd); result = 0; goto done; } if (errno != EINPROGRESS) { result = -1; goto done; } #ifndef WIN32_FIXME fdset = xcalloc(howmany(sockfd + 1, NFDBITS), sizeof(fd_mask)); #else fdset = xmalloc(sizeof(fd_set)); FD_ZERO(fdset); #endif FD_SET(sockfd, fdset); ms_to_timeval(&tv, *timeoutp); for (;;) { rc = select(sockfd + 1, NULL, fdset, NULL, &tv); if (rc != -1 || errno != EINTR) break; } switch (rc) { case 0: /* Timed out */ errno = ETIMEDOUT; break; case -1: /* Select error */ debug("select: %s", strerror(errno)); break; case 1: /* Completed or failed */ optval = 0; optlen = sizeof(optval); if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { debug("getsockopt: %s", strerror(errno)); break; } if (optval != 0) { errno = optval; break; } result = 0; unset_nonblock(sockfd); break; default: /* Should not occur */ fatal("Bogus return (%d) from select()", rc); } free(fdset); done: if (result == 0 && *timeoutp > 0) { ms_subtract_diff(&t_start, timeoutp); if (*timeoutp <= 0) { errno = ETIMEDOUT; result = -1; } } return (result); }
void sink(int argc, char **argv) { static BUF buffer; struct stat stb; enum { YES, NO, DISPLAYED } wrerr; BUF *bp; off_t i; size_t j, count; int amt, exists, first, ofd; mode_t mode, omode, mask; off_t size, statbytes; unsigned long long ull; int setimes, targisdir, wrerrno = 0; char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; struct timeval tv[2]; #define atime tv[0] #define mtime tv[1] #define SCREWUP(str) { why = str; goto screwup; } setimes = targisdir = 0; mask = umask(0); if (!pflag) (void) umask(mask); if (argc != 1) { run_err("ambiguous target"); exit(1); } targ = *argv; if (targetshouldbedirectory) verifydir(targ); (void) atomicio(vwrite, remout, "", 1); if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) targisdir = 1; for (first = 1;; first = 0) { cp = buf; if (atomicio(read, remin, cp, 1) != 1) return; if (*cp++ == '\n') SCREWUP("unexpected <newline>"); do { if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) SCREWUP("lost connection"); *cp++ = ch; } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); *cp = 0; if (verbose_mode) fprintf(stderr, "Sink: %s", buf); if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) (void) atomicio(vwrite, STDERR_FILENO, buf + 1, strlen(buf + 1)); if (buf[0] == '\02') exit(1); ++errs; continue; } if (buf[0] == 'E') { (void) atomicio(vwrite, remout, "", 1); return; } if (ch == '\n') *--cp = 0; cp = buf; if (*cp == 'T') { setimes++; cp++; if (!isdigit((unsigned char)*cp)) SCREWUP("mtime.sec not present"); ull = strtoull(cp, &cp, 10); if (!cp || *cp++ != ' ') SCREWUP("mtime.sec not delimited"); if ((time_t)ull < 0 || (unsigned long long)(time_t)ull != ull) setimes = 0; /* out of range */ mtime.tv_sec = ull; mtime.tv_usec = strtol(cp, &cp, 10); if (!cp || *cp++ != ' ' || mtime.tv_usec < 0 || mtime.tv_usec > 999999) SCREWUP("mtime.usec not delimited"); if (!isdigit((unsigned char)*cp)) SCREWUP("atime.sec not present"); ull = strtoull(cp, &cp, 10); if (!cp || *cp++ != ' ') SCREWUP("atime.sec not delimited"); if ((time_t)ull < 0 || (unsigned long long)(time_t)ull != ull) setimes = 0; /* out of range */ atime.tv_sec = ull; atime.tv_usec = strtol(cp, &cp, 10); if (!cp || *cp++ != '\0' || atime.tv_usec < 0 || atime.tv_usec > 999999) SCREWUP("atime.usec not delimited"); (void) atomicio(vwrite, remout, "", 1); continue; } if (*cp != 'C' && *cp != 'D') { /* * Check for the case "rcp remote:foo\* local:bar". * In this case, the line "No match." can be returned * by the shell before the rcp command on the remote is * executed so the ^Aerror_message convention isn't * followed. */ if (first) { run_err("%s", cp); exit(1); } SCREWUP("expected control record"); } mode = 0; for (++cp; cp < buf + 5; cp++) { if (*cp < '0' || *cp > '7') SCREWUP("bad mode"); mode = (mode << 3) | (*cp - '0'); } if (*cp++ != ' ') SCREWUP("mode not delimited"); for (size = 0; isdigit((unsigned char)*cp);) size = size * 10 + (*cp++ - '0'); if (*cp++ != ' ') SCREWUP("size not delimited"); if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) { run_err("error: unexpected filename: %s", cp); exit(1); } if (targisdir) { static char *namebuf; static size_t cursize; size_t need; need = strlen(targ) + strlen(cp) + 250; if (need > cursize) { free(namebuf); namebuf = xmalloc(need); cursize = need; } (void) snprintf(namebuf, need, "%s%s%s", targ, strcmp(targ, "/") ? "/" : "", cp); np = namebuf; } else np = targ; curfile = cp; exists = stat(np, &stb) == 0; if (buf[0] == 'D') { int mod_flag = pflag; if (!iamrecursive) SCREWUP("received directory without -r"); if (exists) { if (!S_ISDIR(stb.st_mode)) { errno = ENOTDIR; goto bad; } if (pflag) (void) chmod(np, mode); } else { /* Handle copying from a read-only directory */ mod_flag = 1; if (mkdir(np, mode | S_IRWXU) < 0) goto bad; } vect[0] = xstrdup(np); sink(1, vect); if (setimes) { setimes = 0; if (utimes(vect[0], tv) < 0) run_err("%s: set times: %s", vect[0], strerror(errno)); } if (mod_flag) (void) chmod(vect[0], mode); free(vect[0]); continue; } omode = mode; mode |= S_IWUSR; if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { bad: run_err("%s: %s", np, strerror(errno)); continue; } (void) atomicio(vwrite, remout, "", 1); if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { (void) close(ofd); continue; } cp = bp->buf; wrerr = NO; statbytes = 0; if (showprogress) start_progress_meter(curfile, size, &statbytes); set_nonblock(remin); for (count = i = 0; i < size; i += bp->cnt) { amt = bp->cnt; if (i + amt > size) amt = size - i; count += amt; do { j = atomicio6(read, remin, cp, amt, scpio, &statbytes); if (j == 0) { run_err("%s", j != EPIPE ? strerror(errno) : "dropped connection"); exit(1); } amt -= j; cp += j; } while (amt > 0); if (count == bp->cnt) { /* Keep reading so we stay sync'd up. */ if (wrerr == NO) { if (atomicio(vwrite, ofd, bp->buf, count) != count) { wrerr = YES; wrerrno = errno; } } count = 0; cp = bp->buf; } } unset_nonblock(remin); if (showprogress) stop_progress_meter(); if (count != 0 && wrerr == NO && atomicio(vwrite, ofd, bp->buf, count) != count) { wrerr = YES; wrerrno = errno; } if (wrerr == NO && (!exists || S_ISREG(stb.st_mode)) && ftruncate(ofd, size) != 0) { run_err("%s: truncate: %s", np, strerror(errno)); wrerr = DISPLAYED; } if (pflag) { if (exists || omode != mode) #ifdef HAVE_FCHMOD if (fchmod(ofd, omode)) { #else /* HAVE_FCHMOD */ if (chmod(np, omode)) { #endif /* HAVE_FCHMOD */ run_err("%s: set mode: %s", np, strerror(errno)); wrerr = DISPLAYED; } } else { if (!exists && omode != mode) #ifdef HAVE_FCHMOD if (fchmod(ofd, omode & ~mask)) { #else /* HAVE_FCHMOD */ if (chmod(np, omode & ~mask)) { #endif /* HAVE_FCHMOD */ run_err("%s: set mode: %s", np, strerror(errno)); wrerr = DISPLAYED; } } if (close(ofd) == -1) { wrerr = YES; wrerrno = errno; } (void) response(); if (setimes && wrerr == NO) { setimes = 0; if (utimes(np, tv) < 0) { run_err("%s: set times: %s", np, strerror(errno)); wrerr = DISPLAYED; } } switch (wrerr) { case YES: run_err("%s: %s", np, strerror(wrerrno)); break; case NO: (void) atomicio(vwrite, remout, "", 1); break; case DISPLAYED: break; } } screwup: run_err("protocol error: %s", why); exit(1); } int response(void) { char ch, *cp, resp, rbuf[2048]; if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) lostconn(0); cp = rbuf; switch (resp) { case 0: /* ok */ return (0); default: *cp++ = resp; /* FALLTHROUGH */ case 1: /* error, followed by error msg */ case 2: /* fatal error, "" */ do { if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) lostconn(0); *cp++ = ch; } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); if (!iamremote) (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); ++errs; if (resp == 1) return (-1); exit(1); } /* NOTREACHED */ } void usage(void) { (void) fprintf(stderr, "usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" " [-l limit] [-o ssh_option] [-P port] [-S program]\n" " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); exit(1); }
void source(int argc, char **argv) { struct stat stb; static BUF buffer; BUF *bp; off_t i, statbytes; size_t amt, nr; int fd = -1, haderr, indx; char *last, *name, buf[2048], encname[MAXPATHLEN]; int len; for (indx = 0; indx < argc; ++indx) { name = argv[indx]; statbytes = 0; len = strlen(name); while (len > 1 && name[len-1] == '/') name[--len] = '\0'; if ((fd = open(name, O_RDONLY|O_NONBLOCK, 0)) < 0) goto syserr; if (strchr(name, '\n') != NULL) { strnvis(encname, name, sizeof(encname), VIS_NL); name = encname; } if (fstat(fd, &stb) < 0) { syserr: run_err("%s: %s", name, strerror(errno)); goto next; } if (stb.st_size < 0) { run_err("%s: %s", name, "Negative file size"); goto next; } unset_nonblock(fd); switch (stb.st_mode & S_IFMT) { case S_IFREG: break; case S_IFDIR: if (iamrecursive) { rsource(name, &stb); goto next; } /* FALLTHROUGH */ default: run_err("%s: not a regular file", name); goto next; } if ((last = strrchr(name, '/')) == NULL) last = name; else ++last; curfile = last; if (pflag) { if (do_times(remout, verbose_mode, &stb) < 0) goto next; } #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) snprintf(buf, sizeof buf, "C%04o %lld %s\n", (u_int) (stb.st_mode & FILEMODEMASK), (long long)stb.st_size, last); if (verbose_mode) { fprintf(stderr, "Sending file modes: %s", buf); } (void) atomicio(vwrite, remout, buf, strlen(buf)); if (response() < 0) goto next; if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { next: if (fd != -1) { (void) close(fd); fd = -1; } continue; } if (showprogress) start_progress_meter(curfile, stb.st_size, &statbytes); set_nonblock(remout); for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { amt = bp->cnt; if (i + (off_t)amt > stb.st_size) amt = stb.st_size - i; if (!haderr) { if ((nr = atomicio(read, fd, bp->buf, amt)) != amt) { haderr = errno; memset(bp->buf + nr, 0, amt - nr); } } /* Keep writing after error to retain sync */ if (haderr) { (void)atomicio(vwrite, remout, bp->buf, amt); memset(bp->buf, 0, amt); continue; } if (atomicio6(vwrite, remout, bp->buf, amt, scpio, &statbytes) != amt) haderr = errno; } unset_nonblock(remout); if (showprogress) stop_progress_meter(); if (fd != -1) { if (close(fd) < 0 && !haderr) haderr = errno; fd = -1; } if (!haderr) (void) atomicio(vwrite, remout, "", 1); else run_err("%s: %s", name, strerror(haderr)); (void) response(); } }
/* * Main program for the daemon. */ int main(int ac, char **av) { extern char *optarg; extern int optind; int opt, j, i, fdsetsz, on = 1; int sock_in = -1, sock_out = -1, newsock = -1; pid_t pid; socklen_t fromlen; fd_set *fdset; struct sockaddr_storage from; const char *remote_ip; int remote_port; FILE *f; struct addrinfo *ai; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; char *line; int listen_sock, maxfd; int startup_p[2], config_s[2]; int startups = 0; Key *key; Authctxt *authctxt; int ret, key_used = 0; Buffer cfg; char *pt; u_short ports[2] = {0,0}; /* Default MITM options */ memset(&mopt, 0x00, sizeof(mopt)); mopt.r_port = htons(22); mopt.resolve = 1; if (av[1] == NULL) usage(); /* Get route */ if ( (pt = strchr(av[1], ':')) != NULL) *pt++ = '\0'; if ( (long)(mopt.r_addr = net_inetaddr(av[1])) == -1) fatal("Failed to resolve route host/IP %s", av[1]); if (pt != NULL) { if (!ISPORT(atoi(pt))) fatal("Bad port number in route '%s'", pt); mopt.r_port = htons(atoi(pt)); } logit("Using static route to %s", net_sockstr_ip(mopt.r_addr, mopt.r_port, 0)); #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 = xmalloc(sizeof(*saved_argv) * (ac + 1)); 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)); /* Initialize configuration options to their default values. */ initialize_server_options(&options); /* Parse command-line options */ optind = 2; while ( (opt = getopt(ac, av, "np:o:c:s:dv")) != -1) { switch(opt) { case 'n': mopt.resolve = 0; break; case 'd': if (debug_flag == 0) { debug_flag = 1; options.log_level = SYSLOG_LEVEL_DEBUG1; } else if (options.log_level < SYSLOG_LEVEL_DEBUG4) options.log_level++; break; case 'o': options.passwdlog = optarg; break; case 'c': options.c_logdir = optarg; break; case 's': options.s_logdir = optarg; break; case 'v': options.log_level = SYSLOG_LEVEL_VERBOSE; 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; default: exit(EXIT_FAILURE); } } /* Default values */ IPv4or6 = AF_INET; no_daemon_flag = 1; log_stderr = 1; rexec_flag = 0; use_privsep = 0; IPv4or6 = AF_INET; SSLeay_add_all_algorithms(); channel_set_af(IPv4or6); /* * 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); //target_connect(net_inetaddr("10.0.0.1"), htons(22), 2, SSH_PROTO_2); //exit(1); #ifdef _AIX /* * Unset KRB5CCNAME, otherwise the user's session may inherit it from * root's environment */ unsetenv("KRB5CCNAME"); #endif /* _AIX */ #ifdef _UNICOS /* Cray can define user privs drop all privs now! * Not needed on PRIV_SU systems! */ drop_cray_privs(); #endif seed_rng(); sensitive_data.server_key = NULL; sensitive_data.ssh1_host_key = NULL; sensitive_data.have_ssh1_key = 0; sensitive_data.have_ssh2_key = 0; /* 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); if (!rexec_flag) buffer_free(&cfg); /* 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); /* load private host keys */ sensitive_data.host_keys = xmalloc(options.num_host_key_files * sizeof(Key *)); for (i = 0; i < options.num_host_key_files; i++) sensitive_data.host_keys[i] = NULL; for (i = 0; i < options.num_host_key_files; i++) { key = key_load_private(options.host_key_files[i], "", NULL); sensitive_data.host_keys[i] = key; if (key == NULL) { error("Could not load host key: %s", options.host_key_files[i]); sensitive_data.host_keys[i] = NULL; continue; } switch (key->type) { case KEY_RSA1: sensitive_data.ssh1_host_key = key; sensitive_data.have_ssh1_key = 1; break; case KEY_RSA: case KEY_DSA: sensitive_data.have_ssh2_key = 1; break; } debug("private host key: #%d type %d %s", i, key->type, key_type(key)); } if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) { logit("Disabling protocol version 1. Could not load host key"); options.protocol &= ~SSH_PROTO_1; } if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { logit("Disabling protocol version 2. Could not load host key"); options.protocol &= ~SSH_PROTO_2; } if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { logit("sshd: no hostkeys available -- exiting."); 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.ssh1_host_key->rsa->n) - SSH_KEY_BITS_RESERVED && options.server_key_bits < BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED) { options.server_key_bits = BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED; debug("Forcing server key to %d bits to make it differ from host key.", options.server_key_bits); } } if (use_privsep) { struct passwd *pw; struct stat st; if ((pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) fatal("Privilege separation user %s does not exist", SSH_PRIVSEP_USER); if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &st) == -1) || (S_ISDIR(st.st_mode) == 0)) fatal("Missing privilege separation directory: %s", _PATH_PRIVSEP_CHROOT_DIR); #ifdef HAVE_CYGWIN if (check_ntsec(_PATH_PRIVSEP_CHROOT_DIR) && (st.st_uid != getuid () || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0)) #else if (st.st_uid != 0 || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0) #endif fatal("%s must be owned by root and not group or " "world-writable.", _PATH_PRIVSEP_CHROOT_DIR); } /* Configuration looks good, so exit if in test mode. */ if (test_flag) exit(0); /* * Clear out any supplemental groups we may have inherited. This * prevents inadvertent creation of files with bad modes (in the * portable version at least, it's certainly possible for PAM * to create a file, and we can't control the code in every * module which might be used). */ if (setgroups(0, NULL) < 0) debug("setgroups() failed: %.200s", strerror(errno)); if (rexec_flag) { rexec_argv = xmalloc(sizeof(char *) * (rexec_argc + 2)); for (i = 0; i < rexec_argc; i++) { debug("rexec_argv[%d]='%s'", i, saved_argv[i]); rexec_argv[i] = saved_argv[i]; } rexec_argv[rexec_argc] = "-R"; rexec_argv[rexec_argc + 1] = NULL; } /* Initialize the log (it is reinitialized below in case we forked). */ if (debug_flag && !inetd_flag) log_stderr = 1; log_init(__progname, 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 || no_daemon_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(_PATH_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(__progname, options.log_level, options.log_facility, log_stderr); /* Initialize the random number generator. */ arc4random_stir(); /* Chdir to the root directory so that the current disk can be unmounted if desired. */ chdir("/"); /* ignore SIGPIPE */ signal(SIGPIPE, SIG_IGN); /* Start listening for a socket, unless started from inetd. */ if (inetd_flag) { 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); if ((options.protocol & SSH_PROTO_1) && sensitive_data.server_key == NULL) generate_ephemeral_server_key(); } 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, 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)); 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) { if (!ai->ai_next) 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. */ logit("SSH MITM Server listening on %s port %s.", ntop, strport); if (listen(listen_sock, SSH_LISTEN_BACKLOG) < 0) fatal("listen: %.100s", strerror(errno)); } freeaddrinfo(options.listen_addrs); if (!num_listen_socks) fatal("Cannot bind any address."); if (options.protocol & SSH_PROTO_1) generate_ephemeral_server_key(); /* * 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); /* Write out the pid file after the sigterm handler is setup */ if (!debug_flag) { /* * Record our pid in /var/run/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, "wb"); if (f == NULL) { error("Couldn't create pid file \"%s\": %s", options.pid_file, strerror(errno)); } else { fprintf(f, "%ld\n", (long) getpid()); fclose(f); } } /* setup fd set for listen */ 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 = xmalloc(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); fdsetsz = howmany(maxfd+1, NFDBITS) * sizeof(fd_mask); fdset = (fd_set *)xmalloc(fdsetsz); memset(fdset, 0, fdsetsz); 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); debug("Awaiting client"); newsock = accept(listen_socks[i], (struct sockaddr *)&from, &fromlen); if (newsock < 0) { if (errno != EINTR && 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; } else { /* * Normal production daemon. Fork, and have * the child process the connection. The * parent continues listening. */ 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. */ 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); close(config_s[0]); break; } } /* Parent. Stay in the loop. */ 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; } arc4random_stir(); /* Close the new socket (the child is now taking care of it). */ close(newsock); } /* child process check (or debug mode) */ if (num_listen_socks < 0) break; } } /* This is the child processing a new connection. */ setproctitle("%s", "[MITM]"); log_init("mitm-server", options.log_level, options.log_facility, log_stderr); alarm(0); signal(SIGALRM, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGINT, SIG_DFL); packet_set_connection(sock_in, sock_out); sshd_exchange_identification(sock_in, sock_out); packet_set_nonblocking(); /* perform the key exchange */ if (compat20) do_ssh2_kex(); else do_ssh1_kex(); mitm_ssh(sock_in); /* Unreached */ exit(1); }
/* * main entry pont of pcp worker child process */ void pcp_worker_main(int port) { sigjmp_buf local_sigjmp_buf; MemoryContext PCPMemoryContext; int authenticated = 0; char salt[4]; int random_salt = 0; struct timeval uptime; char tos; int rsize; char *buf = NULL; ereport(DEBUG1, (errmsg("I am PCP worker child with pid:%d",getpid()))); /* Identify myself via ps */ init_ps_display("", "", "", ""); gettimeofday(&uptime, NULL); srandom((unsigned int) (getpid() ^ uptime.tv_usec)); /* set up signal handlers */ signal(SIGTERM, die); signal(SIGINT, die); signal(SIGQUIT, die); signal(SIGCHLD, SIG_DFL); signal(SIGUSR2, wakeup_handler_child); signal(SIGUSR1, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGALRM, SIG_IGN); /* Create per loop iteration memory context */ PCPMemoryContext = AllocSetContextCreate(TopMemoryContext, "PCP_worker_main_loop", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); MemoryContextSwitchTo(TopMemoryContext); /* * install the call back for preparation of pcp worker child exit */ on_system_exit(pcp_worker_will_go_down, (Datum)NULL); /* Initialize my backend status */ pool_initialize_private_backend_status(); /* Initialize process context */ pool_init_process_context(); pcp_frontend = pcp_open(port); unset_nonblock(pcp_frontend->fd); if (sigsetjmp(local_sigjmp_buf, 1) != 0) { error_context_stack = NULL; EmitErrorReport(); MemoryContextSwitchTo(TopMemoryContext); FlushErrorState(); } /* We can now handle ereport(ERROR) */ PG_exception_stack = &local_sigjmp_buf; for(;;) { MemoryContextSwitchTo(PCPMemoryContext); MemoryContextResetAndDeleteChildren(PCPMemoryContext); errno = 0; /* read a PCP packet */ do_pcp_read(pcp_frontend, &tos, 1); do_pcp_read(pcp_frontend, &rsize, sizeof(int)); rsize = ntohl(rsize); if ((rsize - sizeof(int)) > 0) { buf = (char *)palloc(rsize - sizeof(int)); do_pcp_read(pcp_frontend, buf, rsize - sizeof(int)); } ereport(DEBUG1, (errmsg("received PCP packet"), errdetail("PCP packet type of service '%c'", tos))); if (tos == 'R') /* authentication */ { set_ps_display("PCP: processing authentication", false); process_authentication(pcp_frontend, buf,salt, &random_salt); authenticated = 1; continue; } if (tos == 'M') /* md5 salt */ { set_ps_display("PCP: processing authentication", false); send_md5salt(pcp_frontend, salt); random_salt = 1; continue; } /* is this connection authenticated? if not disconnect immediately*/ if (!authenticated) ereport(FATAL, (errmsg("authentication failed for new PCP connection"), errdetail("connection not authorized"))); /* process a request */ pcp_process_command(tos, buf, rsize); } exit(0); }