X-Git-Url: http://andersk.mit.edu/gitweb/gssapi-openssh.git/blobdiff_plain/476861787f6e1b8a6c6af9307a15b9e57cb979dc..071d8001c928b4929a48dada8d855ebe014b3792:/openssh/sshd.c diff --git a/openssh/sshd.c b/openssh/sshd.c index 5dfc2b1..1805bde 100644 --- a/openssh/sshd.c +++ b/openssh/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.355 2008/02/14 13:10:31 mbalmer Exp $ */ +/* $OpenBSD: sshd.c,v 1.366 2009/01/22 10:02:34 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -54,6 +54,7 @@ # include #endif #include "openbsd-compat/sys-tree.h" +#include "openbsd-compat/sys-queue.h" #include #include @@ -116,9 +117,12 @@ #include "ssh-gss.h" #endif #include "monitor_wrap.h" -#include "monitor_fdpass.h" #include "version.h" +#ifdef USE_SECURITY_SESSION_API +#include +#endif + #ifdef LIBWRAP #include #include @@ -136,6 +140,9 @@ int deny_severity; #define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3) #define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4) +int myflag = 0; + + extern char *__progname; /* Server configuration options. */ @@ -368,9 +375,6 @@ grace_alarm_handler(int sig) static void generate_ephemeral_server_key(void) { - u_int32_t rnd = 0; - int i; - verbose("Generating %s%d bit RSA key.", sensitive_data.server_key ? "new " : "", options.server_key_bits); if (sensitive_data.server_key != NULL) @@ -379,12 +383,7 @@ generate_ephemeral_server_key(void) options.server_key_bits); verbose("RSA key generation complete."); - for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { - if (i % 4 == 0) - rnd = arc4random(); - sensitive_data.ssh1_cookie[i] = rnd & 0xff; - rnd >>= 8; - } + arc4random_buf(sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); arc4random_stir(); } @@ -406,7 +405,7 @@ sshd_exchange_identification(int sock_in, int sock_out) int mismatch; int remote_major, remote_minor; int major, minor; - char *s; + char *s, *newline = "\n"; char buf[256]; /* Must not be larger than remote_version. */ char remote_version[256]; /* Must be at least as big as buf. */ @@ -417,11 +416,13 @@ sshd_exchange_identification(int sock_in, int sock_out) } else if (options.protocol & SSH_PROTO_2) { major = PROTOCOL_MAJOR_2; minor = PROTOCOL_MINOR_2; + newline = "\r\n"; } else { major = PROTOCOL_MAJOR_1; minor = PROTOCOL_MINOR_1; } - snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION); + snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", major, minor, + SSH_RELEASE, newline); server_version_string = xstrdup(buf); /* Send our protocol version identification. */ @@ -472,6 +473,9 @@ sshd_exchange_identification(int sock_in, int sock_out) } debug("Client protocol version %d.%d; client software version %.100s", remote_major, remote_minor, remote_version); + logit("SSH: Server;Ltype: Version;Remote: %s-%d;Protocol: %d.%d;Client: %.100s", + get_remote_ipaddr(), get_remote_port(), + remote_major, remote_minor, remote_version); compat_datafellows(remote_version); @@ -583,16 +587,14 @@ demote_sensitive_data(void) static void privsep_preauth_child(void) { - u_int32_t rnd[256]; + u_int32_t rnd[256]; gid_t gidset[1]; - u_int i; /* Enable challenge-response authentication for privilege separation */ privsep_challenge_enable(); arc4random_stir(); - for (i = 0; i < 256; i++) - rnd[i] = arc4random(); + arc4random_buf(rnd, sizeof(rnd)); RAND_seed(rnd, sizeof(rnd)); /* Demote the private keys to public keys. */ @@ -666,7 +668,6 @@ static void privsep_postauth(Authctxt *authctxt) { u_int32_t rnd[256]; - u_int i; #ifdef DISABLE_FD_PASSING if (1) { @@ -685,7 +686,7 @@ privsep_postauth(Authctxt *authctxt) if (pmonitor->m_pid == -1) fatal("fork of unprivileged child failed"); else if (pmonitor->m_pid != 0) { - debug2("User child is on pid %ld", (long)pmonitor->m_pid); + verbose("User child is on pid %ld", (long)pmonitor->m_pid); close(pmonitor->m_recvfd); buffer_clear(&loginmsg); monitor_child_postauth(pmonitor); @@ -700,8 +701,7 @@ privsep_postauth(Authctxt *authctxt) demote_sensitive_data(); arc4random_stir(); - for (i = 0; i < 256; i++) - rnd[i] = arc4random(); + arc4random_buf(rnd, sizeof(rnd)); RAND_seed(rnd, sizeof(rnd)); /* Drop privileges */ @@ -803,7 +803,7 @@ drop_connection(int startups) p *= startups - options.max_startups_begin; p /= options.max_startups - options.max_startups_begin; p += options.max_startups_rate; - r = arc4random() % 100; + r = arc4random_uniform(100); debug("drop_connection: p %d, r %d", p, r); return (r < p) ? 1 : 0; @@ -815,8 +815,9 @@ usage(void) fprintf(stderr, "%s, %s\n", SSH_RELEASE, SSLeay_version(SSLEAY_VERSION)); fprintf(stderr, -"usage: sshd [-46Ddeiqt] [-b bits] [-f config_file] [-g login_grace_time]\n" -" [-h host_key_file] [-k key_gen_time] [-o option] [-p port] [-u len]\n" +"usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-f config_file]\n" +" [-g login_grace_time] [-h host_key_file] [-k key_gen_time]\n" +" [-o option] [-p port] [-u len]\n" ); exit(1); } @@ -953,6 +954,8 @@ 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) @@ -999,6 +1002,11 @@ server_listen(void) 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.", @@ -1104,7 +1112,8 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) *newsock = accept(listen_socks[i], (struct sockaddr *)&from, &fromlen); if (*newsock < 0) { - if (errno != EINTR && errno != EWOULDBLOCK) + if (errno != EINTR && errno != EAGAIN && + errno != EWOULDBLOCK) error("accept: %.100s", strerror(errno)); continue; } @@ -1251,9 +1260,12 @@ main(int ac, char **av) 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; + char *line, *p, *cp; int config_s[2] = { -1 , -1 }; + u_int64_t ibytes, obytes; + mode_t new_umask; Key *key; Authctxt *authctxt; @@ -1287,7 +1299,7 @@ main(int ac, char **av) initialize_server_options(&options); /* Parse command-line arguments. */ - while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:dDeiqrtQR46")) != -1) { + 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; @@ -1338,7 +1350,7 @@ main(int ac, char **av) exit(1); } options.ports[options.num_ports++] = a2port(optarg); - if (options.ports[options.num_ports-1] == 0) { + if (options.ports[options.num_ports-1] <= 0) { fprintf(stderr, "Bad port number.\n"); exit(1); } @@ -1365,6 +1377,25 @@ main(int ac, char **av) 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) { @@ -1426,6 +1457,21 @@ main(int ac, char **av) 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) @@ -1501,10 +1547,13 @@ main(int ac, char **av) logit("Disabling protocol version 1. Could not load host key"); options.protocol &= ~SSH_PROTO_1; } +#ifndef GSSAPI + /* The GSSAPI key exchange can run without a host key */ 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; } +#endif if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { logit("sshd: no hostkeys available -- exiting."); exit(1); @@ -1554,6 +1603,13 @@ main(int ac, char **av) "world-writable.", _PATH_PRIVSEP_CHROOT_DIR); } + if (test_flag > 1) { + if (test_user != NULL && test_addr != NULL && test_host != NULL) + parse_server_match_config(&options, test_user, + test_host, test_addr); + dump_config(&options); + } + /* Configuration looks good, so exit if in test mode. */ if (test_flag) exit(0); @@ -1578,6 +1634,10 @@ main(int ac, char **av) rexec_argv[rexec_argc + 1] = NULL; } + /* Ensure that umask disallows at least group and world write */ + new_umask = umask(0077) | 0022; + (void) umask(new_umask); + /* Initialize the log (it is reinitialized below in case we forked). */ if (debug_flag && (!inetd_flag || rexeced_flag)) log_stderr = 1; @@ -1740,6 +1800,9 @@ main(int ac, char **av) cleanup_exit(255); } + /* set the HPN options for the child */ + channel_set_hpn(options.hpn_disabled, options.hpn_buffer_size); + /* * We use get_canonical_hostname with usedns = 0 instead of * get_remote_ipaddr here so IP options will be checked. @@ -1777,6 +1840,60 @@ main(int ac, char **av) /* Log the connection. */ verbose("Connection from %.500s port %d", remote_ip, remote_port); +#ifdef USE_SECURITY_SESSION_API + /* + * Create a new security session for use by the new user login if + * the current session is the root session or we are not launched + * by inetd (eg: debugging mode or server mode). We do not + * necessarily need to create a session if we are launched from + * inetd because Panther xinetd will create a session for us. + * + * The only case where this logic will fail is if there is an + * inetd running in a non-root session which is not creating + * new sessions for us. Then all the users will end up in the + * same session (bad). + * + * When the client exits, the session will be destroyed for us + * automatically. + * + * We must create the session before any credentials are stored + * (including AFS pags, which happens a few lines below). + */ + { + OSStatus err = 0; + SecuritySessionId sid = 0; + SessionAttributeBits sattrs = 0; + + err = SessionGetInfo(callerSecuritySession, &sid, &sattrs); + if (err) + error("SessionGetInfo() failed with error %.8X", + (unsigned) err); + else + debug("Current Session ID is %.8X / Session Attributes are %.8X", + (unsigned) sid, (unsigned) sattrs); + + if (inetd_flag && !(sattrs & sessionIsRoot)) + debug("Running in inetd mode in a non-root session... " + "assuming inetd created the session for us."); + else { + debug("Creating new security session..."); + err = SessionCreate(0, sessionHasTTY | sessionIsRemote); + if (err) + error("SessionCreate() failed with error %.8X", + (unsigned) err); + + err = SessionGetInfo(callerSecuritySession, &sid, + &sattrs); + if (err) + error("SessionGetInfo() failed with error %.8X", + (unsigned) err); + else + debug("New Session ID is %.8X / Session Attributes are %.8X", + (unsigned) sid, (unsigned) sattrs); + } + } +#endif + /* * We don't want to listen forever unless the other side * successfully authenticates itself. So we set up an alarm which is @@ -1790,6 +1907,13 @@ main(int ac, char **av) alarm(options.login_grace_time); sshd_exchange_identification(sock_in, sock_out); +#if defined(AFS_KRB5) + /* If machine has AFS, set process authentication group. */ + if (k_hasafs()) { + k_setpag(); + k_unlog(); + } +#endif /* AFS || AFS_KRB5 */ /* In inetd mode, generate ephemeral key only for proto 1 connections */ if (!compat20 && inetd_flag && sensitive_data.server_key == NULL) @@ -1848,7 +1972,7 @@ main(int ac, char **av) #endif #ifdef GSSAPI - if (options.gss_authentication) { + if (options.gss_authentication && options.gss_deleg_creds) { temporarily_use_uid(authctxt->pw); ssh_gssapi_storecreds(); restore_uid(); @@ -1872,11 +1996,18 @@ main(int ac, char **av) destroy_sensitive_data(); } + packet_set_timeout(options.client_alive_interval, + options.client_alive_count_max); + /* Start session. */ do_authenticated(authctxt); /* The connection has been terminated. */ - verbose("Closing connection to %.100s", remote_ip); + packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); + packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); + verbose("Transferred: sent %llu, received %llu bytes", obytes, ibytes); + + verbose("Closing connection to %.500s port %d", remote_ip, remote_port); #ifdef USE_PAM if (options.use_pam) @@ -1956,7 +2087,6 @@ do_ssh1_kex(void) u_char session_key[SSH_SESSION_KEY_LENGTH]; u_char cookie[8]; u_int cipher_type, auth_mask, protocol_flags; - u_int32_t rnd = 0; /* * Generate check bytes that the client must send back in the user @@ -1967,12 +2097,7 @@ do_ssh1_kex(void) * cookie. This only affects rhosts authentication, and this is one * of the reasons why it is inherently insecure. */ - for (i = 0; i < 8; i++) { - if (i % 4 == 0) - rnd = arc4random(); - cookie[i] = rnd & 0xff; - rnd >>= 8; - } + arc4random_buf(cookie, sizeof(cookie)); /* * Send our public key. We include in the packet 64 bits of random @@ -2130,9 +2255,15 @@ do_ssh2_kex(void) { Kex *kex; + myflag++; + debug ("MYFLAG IS %d", myflag); if (options.ciphers != NULL) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; + } else if (options.none_enabled == 1) { + debug ("WARNING: None cipher enabled"); + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = KEX_ENCRYPT_INCLUDE_NONE; } myproposal[PROPOSAL_ENC_ALGS_CTOS] = compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); @@ -2153,12 +2284,61 @@ do_ssh2_kex(void) myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); +#ifdef GSSAPI + { + char *orig; + char *gss = NULL; + char *newstr = NULL; + orig = myproposal[PROPOSAL_KEX_ALGS]; + + /* + * If we don't have a host key, then there's no point advertising + * the other key exchange algorithms + */ + + if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) + orig = NULL; + + if (options.gss_keyex) + gss = ssh_gssapi_server_mechanisms(); + else + gss = NULL; + + if (gss && orig) + xasprintf(&newstr, "%s,%s", gss, orig); + else if (gss) + newstr = gss; + else if (orig) + newstr = orig; + + /* + * If we've got GSSAPI mechanisms, then we've got the 'null' host + * key alg, but we can't tell people about it unless its the only + * host key algorithm we support + */ + if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null"; + + if (newstr) + myproposal[PROPOSAL_KEX_ALGS] = newstr; + else + fatal("No supported key exchange algorithms"); + } +#endif + /* start key exchange */ kex = kex_setup(myproposal); kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; +#ifdef GSSAPI + if (options.gss_keyex) { + kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; + kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; + } +#endif kex->server = 1; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string;