static void mm_record_login(Session *s, struct passwd *pw) { struct ssh *ssh = active_state; /* XXX */ socklen_t fromlen; struct sockaddr_storage from; /* * Get IP address of client. If the connection is not a socket, let * the address be 0.0.0.0. */ memset(&from, 0, sizeof(from)); fromlen = sizeof(from); if (packet_connection_is_on_socket()) { if (getpeername(packet_get_connection_in(), (struct sockaddr *)&from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); cleanup_exit(255); } } /* Record that there was a login on that tty from the remote host. */ record_login(s->pid, s->tty, pw->pw_name, pw->pw_uid, session_get_remote_name_or_ip(ssh, utmp_len, options.use_dns), (struct sockaddr *)&from, fromlen); }
const char * get_canonical_hostname(int use_dns) { char *host; static char *canonical_host_name = NULL; static char *remote_ip = NULL; /* Check if we have previously retrieved name with same option. */ if (use_dns && canonical_host_name != NULL) return canonical_host_name; if (!use_dns && remote_ip != NULL) return remote_ip; /* Get the real hostname if socket; otherwise return UNKNOWN. */ if (packet_connection_is_on_socket()) host = get_remote_hostname(packet_get_connection_in(), use_dns); else host = "UNKNOWN"; if (use_dns) canonical_host_name = host; else remote_ip = host; return host; }
void server_loop2(Authctxt *authctxt) { fd_set *readset = NULL, *writeset = NULL; int rekeying = 0, max_fd, nalloc = 0; debug("Entering interactive session for SSH2."); mysignal(SIGCHLD, sigchld_handler); child_terminated = 0; connection_in = packet_get_connection_in(); connection_out = packet_get_connection_out(); notify_setup(); max_fd = MAX(connection_in, connection_out); max_fd = MAX(max_fd, notify_pipe[0]); xxx_authctxt = authctxt; server_init_dispatch(); for (;;) { process_buffered_input_packets(); rekeying = (xxx_kex != NULL && !xxx_kex->done); if (!rekeying && packet_not_very_much_data_to_write()) channel_output_poll(); wait_until_can_do_something(&readset, &writeset, &max_fd, &nalloc, 0); collect_children(); if (!rekeying) { channel_after_select(readset, writeset); if (packet_need_rekeying()) { debug("need rekeying"); xxx_kex->done = 0; kex_send_kexinit(xxx_kex); } } process_input(readset); if (connection_closed) break; process_output(writeset); } collect_children(); if (readset) xfree(readset); if (writeset) xfree(writeset); /* free all channels, no more reads and writes */ channel_free_all(); /* free remaining sessions, e.g. remove wtmp entries */ session_destroy_all(NULL); }
void blacklist_notify(int action) { if (blstate != NULL && packet_connection_is_on_socket()) (void)blacklist_r(blstate, action, packet_get_connection_in(), "ssh"); }
/* This is called to fork and execute a command when we have no tty. This will call do_child from the child, and server_loop from the parent after setting up file descriptors and such. */ void do_exec_no_pty(const char *command, char *pw, const char *display, const char *auth_proto, const char *auth_data) { ssh_init(); chdir(BBSHOME); dup2(packet_get_connection_in(), 0); bbs_entry(); exit(0); }
int get_recv_buf_size(void) { int fd = packet_get_connection_in(); int optval; socklen_t optvallen = sizeof(optval); if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &optval, &optvallen) != 0) optval = DEFAULT_ROAMBUF; return optval; }
static int get_port(int local) { /* * If the connection is not a socket, return 65535. This is * intentionally chosen to be an unprivileged port number. */ if (!packet_connection_is_on_socket()) return 65535; /* Get socket and return the port number. */ return get_sock_port(packet_get_connection_in(), local); }
void blacklist_notify(int action) { int fd; if (blstate == NULL) blacklist_init(); if (blstate == NULL) return; fd = packet_get_connection_in(); if (!packet_connection_is_on_socket()) { fprintf(stderr, "packet_connection_is_on_socket: false " "(fd = %d)\n", fd); } (void)blacklist_r(blstate, action, fd, "ssh"); }
const char * get_remote_ipaddr(void) { /* Check whether we have cached the ipaddr. */ if (canonical_host_ip == NULL) { if (packet_connection_is_on_socket()) { canonical_host_ip = get_peer_ipaddr(packet_get_connection_in()); if (canonical_host_ip == NULL) cleanup_exit(255); } else { /* If not on socket, return UNKNOWN. */ canonical_host_ip = xstrdup("UNKNOWN"); } } return canonical_host_ip; }
static void mm_record_login(Session *s, struct passwd *pw) { socklen_t fromlen; struct sockaddr_storage from; /* * Get IP address of client. If the connection is not a socket, let * the address be 0.0.0.0. */ memset(&from, 0, sizeof(from)); if (packet_connection_is_on_socket()) { fromlen = sizeof(from); if (getpeername(packet_get_connection_in(), (struct sockaddr *) & from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); fatal_cleanup(); } } /* Record that there was a login on that tty from the remote host. */ record_login(s->pid, s->tty, pw->pw_name, pw->pw_uid, get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping), (struct sockaddr *)&from); }
const char * get_canonical_hostname(int use_dns) { static char *canonical_host_name = NULL; static int use_dns_done = 0; /* Check if we have previously retrieved name with same option. */ if (canonical_host_name != NULL) { if (use_dns_done != use_dns) xfree(canonical_host_name); else return canonical_host_name; } /* Get the real hostname if socket; otherwise return UNKNOWN. */ if (packet_connection_is_on_socket()) canonical_host_name = get_remote_hostname( packet_get_connection_in(), use_dns); else canonical_host_name = xstrdup("UNKNOWN"); use_dns_done = use_dns; return canonical_host_name; }
static int sshpam_query(void *ctx, char **name, char **info, u_int *num, char ***prompts, u_int **echo_on) { Buffer buffer; struct pam_ctxt *ctxt = ctx; size_t plen; u_char type; char *msg; size_t len, mlen; debug3("PAM: %s entering", __func__); buffer_init(&buffer); *name = xstrdup(""); *info = xstrdup(""); *prompts = xmalloc(sizeof(char *)); **prompts = NULL; plen = 0; *echo_on = xmalloc(sizeof(u_int)); while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) { type = buffer_get_char(&buffer); msg = buffer_get_string(&buffer, NULL); mlen = strlen(msg); switch (type) { case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: *num = 1; len = plen + mlen + 1; **prompts = xrealloc(**prompts, 1, len); strlcpy(**prompts + plen, msg, len - plen); plen += mlen; **echo_on = (type == PAM_PROMPT_ECHO_ON); xfree(msg); return (0); case PAM_ERROR_MSG: case PAM_TEXT_INFO: /* accumulate messages */ len = plen + mlen + 2; **prompts = xrealloc(**prompts, 1, len); strlcpy(**prompts + plen, msg, len - plen); plen += mlen; strlcat(**prompts + plen, "\n", len - plen); plen++; xfree(msg); break; case PAM_ACCT_EXPIRED: sshpam_account_status = 0; /* FALLTHROUGH */ case PAM_AUTH_ERR: debug3("PAM: %s", pam_strerror(sshpam_handle, type)); if (**prompts != NULL && strlen(**prompts) != 0) { *info = **prompts; **prompts = NULL; *num = 0; **echo_on = 0; ctxt->pam_done = -1; xfree(msg); return 0; } /* FALLTHROUGH */ case PAM_SUCCESS: if (**prompts != NULL) { /* drain any accumulated messages */ debug("PAM: %s", **prompts); buffer_append(&loginmsg, **prompts, strlen(**prompts)); xfree(**prompts); **prompts = NULL; } if (type == PAM_SUCCESS) { if (!sshpam_authctxt->valid || (sshpam_authctxt->pw->pw_uid == 0 && options.permit_root_login != PERMIT_YES)) fatal("Internal error: PAM auth " "succeeded when it should have " "failed"); import_environments(&buffer); *num = 0; **echo_on = 0; ctxt->pam_done = 1; xfree(msg); return (0); } error("PAM: %s for %s%.100s from %.100s via %s", msg, sshpam_authctxt->valid ? "" : "illegal user ", sshpam_authctxt->user, get_remote_name_or_ip(utmp_len, options.use_dns), get_local_ipaddr(packet_get_connection_in())); /* FALLTHROUGH */ default: *num = 0; **echo_on = 0; xfree(msg); ctxt->pam_done = -1; return (-1); } } return (-1); }
/* * Try krb5 authentication. server_user is passed for logging purposes * only, in auth is received ticket, in client is returned principal * from the ticket */ int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client) { krb5_error_code problem; krb5_principal server; krb5_data reply; krb5_ticket *ticket; int fd, ret; ret = 0; server = NULL; ticket = NULL; reply.length = 0; problem = krb5_init(authctxt); if (problem) goto err; problem = krb5_auth_con_init(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx); if (problem) goto err; fd = packet_get_connection_in(); #ifdef HEIMDAL problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, &fd); #else problem = krb5_auth_con_genaddrs(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,fd, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR | KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR); #endif if (problem) goto err; problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL , KRB5_NT_SRV_HST, &server); if (problem) goto err; problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx, auth, server, NULL, NULL, &ticket); if (problem) goto err; #ifdef HEIMDAL problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client, &authctxt->krb5_user); #else problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->enc_part2->client, &authctxt->krb5_user); #endif if (problem) goto err; /* if client wants mutual auth */ problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, &reply); if (problem) goto err; /* Check .k5login authorization now. */ if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, authctxt->pw->pw_name)) goto err; if (client) krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, client); packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE); packet_put_string((char *) reply.data, reply.length); packet_send(); packet_write_wait(); ret = 1; err: if (server) krb5_free_principal(authctxt->krb5_ctx, server); if (ticket) krb5_free_ticket(authctxt->krb5_ctx, ticket); if (reply.length) xfree(reply.data); if (problem) { if (authctxt->krb5_ctx != NULL) debug("Kerberos v5 authentication failed: %s", krb5_get_err_text(authctxt->krb5_ctx, problem)); else debug("Kerberos v5 authentication failed: %d", problem); } return (ret); }
/* * Waits for the server identification string, and sends our own * identification string. */ void ssh_exchange_identification(int timeout_ms) { char buf[256], remote_version[256]; /* must be same size! */ int remote_major, remote_minor, mismatch; int connection_in = packet_get_connection_in(); int connection_out = packet_get_connection_out(); int minor1 = PROTOCOL_MINOR_1, client_banner_sent = 0; u_int i, n; size_t len; int fdsetsz, remaining, rc; struct timeval t_start, t_remaining; fd_set *fdset; fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); fdset = xcalloc(1, fdsetsz); /* * If we are SSH2-only then we can send the banner immediately and * save a round-trip. */ if (options.protocol == SSH_PROTO_2) { enable_compat20(); send_client_banner(connection_out, 0); client_banner_sent = 1; } /* Read other side's version identification. */ remaining = timeout_ms; for (n = 0;;) { for (i = 0; i < sizeof(buf) - 1; i++) { if (timeout_ms > 0) { gettimeofday(&t_start, NULL); ms_to_timeval(&t_remaining, remaining); FD_SET(connection_in, fdset); rc = select(connection_in + 1, fdset, NULL, fdset, &t_remaining); ms_subtract_diff(&t_start, &remaining); if (rc == 0 || remaining <= 0) fatal("Connection timed out during " "banner exchange"); if (rc == -1) { if (errno == EINTR) continue; fatal("ssh_exchange_identification: " "select: %s", strerror(errno)); } } len = roaming_atomicio(read, connection_in, &buf[i], 1); if (len != 1 && errno == EPIPE) fatal("ssh_exchange_identification: " "Connection closed by remote host"); else if (len != 1) fatal("ssh_exchange_identification: " "read: %.100s", strerror(errno)); if (buf[i] == '\r') { buf[i] = '\n'; buf[i + 1] = 0; continue; /**XXX wait for \n */ } if (buf[i] == '\n') { buf[i + 1] = 0; break; } if (++n > 65536) fatal("ssh_exchange_identification: " "No banner received"); } buf[sizeof(buf) - 1] = 0; if (strncmp(buf, "SSH-", 4) == 0) break; debug("ssh_exchange_identification: %s", buf); } server_version_string = xstrdup(buf); free(fdset); /* * Check that the versions match. In future this might accept * several versions and set appropriate flags to handle them. */ if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) != 3) fatal("Bad remote protocol version identification: '%.100s'", buf); debug("Remote protocol version %d.%d, remote software version %.100s", remote_major, remote_minor, remote_version); active_state->compat = compat_datafellows(remote_version); mismatch = 0; switch (remote_major) { case 1: if (remote_minor == 99 && (options.protocol & SSH_PROTO_2) && !(options.protocol & SSH_PROTO_1_PREFERRED)) { enable_compat20(); break; } if (!(options.protocol & SSH_PROTO_1)) { mismatch = 1; break; } if (remote_minor < 3) { fatal("Remote machine has too old SSH software version."); } else if (remote_minor == 3 || remote_minor == 4) { /* We speak 1.3, too. */ enable_compat13(); minor1 = 3; if (options.forward_agent) { logit("Agent forwarding disabled for protocol 1.3"); options.forward_agent = 0; } } break; case 2: if (options.protocol & SSH_PROTO_2) { enable_compat20(); break; } /* FALLTHROUGH */ default: mismatch = 1; break; } if (mismatch) fatal("Protocol major versions differ: %d vs. %d", (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, remote_major); if ((datafellows & SSH_BUG_DERIVEKEY) != 0) fatal("Server version \"%.100s\" uses unsafe key agreement; " "refusing connection", remote_version); if ((datafellows & SSH_BUG_RSASIGMD5) != 0) logit("Server version \"%.100s\" uses unsafe RSA signature " "scheme; disabling use of RSA keys", remote_version); if (!client_banner_sent) send_client_banner(connection_out, minor1); chop(server_version_string); }
static int try_krb4_authentication(void) { KTEXT_ST auth; /* Kerberos data */ char *reply; char inst[INST_SZ]; char *realm; CREDENTIALS cred; int r, type; socklen_t slen; Key_schedule schedule; u_long checksum, cksum; MSG_DAT msg_data; struct sockaddr_in local, foreign; struct stat st; /* Don't do anything if we don't have any tickets. */ if (stat(tkt_string(), &st) < 0) return 0; strlcpy(inst, (char *)krb_get_phost(get_canonical_hostname(1)), INST_SZ); realm = (char *)krb_realmofhost(get_canonical_hostname(1)); if (!realm) { debug("Kerberos v4: no realm for %s", get_canonical_hostname(1)); return 0; } /* This can really be anything. */ checksum = (u_long)getpid(); r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum); if (r != KSUCCESS) { debug("Kerberos v4 krb_mk_req failed: %s", krb_err_txt[r]); return 0; } /* Get session key to decrypt the server's reply with. */ r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred); if (r != KSUCCESS) { debug("get_cred failed: %s", krb_err_txt[r]); return 0; } des_key_sched((des_cblock *) cred.session, schedule); /* Send authentication info to server. */ packet_start(SSH_CMSG_AUTH_KERBEROS); packet_put_string((char *) auth.dat, auth.length); packet_send(); packet_write_wait(); /* Zero the buffer. */ (void) memset(auth.dat, 0, MAX_KTXT_LEN); slen = sizeof(local); memset(&local, 0, sizeof(local)); if (getsockname(packet_get_connection_in(), (struct sockaddr *)&local, &slen) < 0) debug("getsockname failed: %s", strerror(errno)); slen = sizeof(foreign); memset(&foreign, 0, sizeof(foreign)); if (getpeername(packet_get_connection_in(), (struct sockaddr *)&foreign, &slen) < 0) { debug("getpeername failed: %s", strerror(errno)); cleanup_exit(255); } /* Get server reply. */ type = packet_read(); switch (type) { case SSH_SMSG_FAILURE: /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */ debug("Kerberos v4 authentication failed."); return 0; break; case SSH_SMSG_AUTH_KERBEROS_RESPONSE: /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */ debug("Kerberos v4 authentication accepted."); /* Get server's response. */ reply = packet_get_string((u_int *) &auth.length); if (auth.length >= MAX_KTXT_LEN) fatal("Kerberos v4: Malformed response from server"); memcpy(auth.dat, reply, auth.length); free(reply); packet_check_eom(); /* * If his response isn't properly encrypted with the session * key, and the decrypted checksum fails to match, he's * bogus. Bail out. */ r = krb_rd_priv(auth.dat, auth.length, (void *)schedule, &cred.session, &foreign, &local, &msg_data); if (r != KSUCCESS) { debug("Kerberos v4 krb_rd_priv failed: %s", krb_err_txt[r]); packet_disconnect("Kerberos v4 challenge failed!"); } /* Fetch the (incremented) checksum that we supplied in the request. */ memcpy((char *)&cksum, (char *)msg_data.app_data, sizeof(cksum)); cksum = ntohl(cksum); /* If it matches, we're golden. */ if (cksum == checksum + 1) { debug("Kerberos v4 challenge successful."); return 1; } else packet_disconnect("Kerberos v4 challenge failed!"); break; default: packet_disconnect("Protocol error on Kerberos v4 response: %d", type); } return 0; }
static int roaming_resume(void) { u_int64_t recv_bytes; char *str = NULL, *kexlist = NULL, *c; int i, type; int timeout_ms = options.connection_timeout * 1000; u_int len; u_int32_t rnd = 0; resume_in_progress = 1; /* Exchange banners */ ssh_exchange_identification(timeout_ms); packet_set_nonblocking(); /* Send a kexinit message with [email protected] as only kex algo */ packet_start(SSH2_MSG_KEXINIT); for (i = 0; i < KEX_COOKIE_LEN; i++) { if (i % 4 == 0) rnd = arc4random(); packet_put_char(rnd & 0xff); rnd >>= 8; } packet_put_cstring(KEX_RESUME); for (i = 1; i < PROPOSAL_MAX; i++) { /* kex algorithm added so start with i=1 and not 0 */ packet_put_cstring(""); /* Not used when we resume */ } packet_put_char(1); /* first kex_packet follows */ packet_put_int(0); /* reserved */ packet_send(); /* Assume that [email protected] will be accepted */ packet_start(SSH2_MSG_KEX_ROAMING_RESUME); packet_put_int(roaming_id); packet_send(); /* Read the server's kexinit and check for [email protected] */ if ((type = packet_read()) != SSH2_MSG_KEXINIT) { debug("expected kexinit on resume, got %d", type); goto fail; } for (i = 0; i < KEX_COOKIE_LEN; i++) (void)packet_get_char(); kexlist = packet_get_string(&len); if (!kexlist || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) { debug("server doesn't allow resume"); goto fail; } free(str); for (i = 1; i < PROPOSAL_MAX; i++) { /* kex algorithm taken care of so start with i=1 and not 0 */ free(packet_get_string(&len)); } i = packet_get_char(); /* first_kex_packet_follows */ if (i && (c = strchr(kexlist, ','))) *c = 0; if (i && strcmp(kexlist, KEX_RESUME)) { debug("server's kex guess (%s) was wrong, skipping", kexlist); (void)packet_read(); /* Wrong guess - discard packet */ } /* * Read the ROAMING_AUTH_REQUIRED challenge from the server and * send ROAMING_AUTH */ if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) { debug("expected roaming_auth_required, got %d", type); goto fail; } roaming_auth_required(); /* Read ROAMING_AUTH_OK from the server */ if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) { debug("expected roaming_auth_ok, got %d", type); goto fail; } recv_bytes = packet_get_int64() ^ oldkey2; debug("Peer received %llu bytes", (unsigned long long)recv_bytes); resend_bytes(packet_get_connection_out(), &recv_bytes); resume_in_progress = 0; session_resumed = 1; /* Tell clientloop */ return 0; fail: free(kexlist); if (packet_get_connection_in() == packet_get_connection_out()) close(packet_get_connection_in()); else { close(packet_get_connection_in()); close(packet_get_connection_out()); } return 1; }
void server_loop2(Authctxt *authctxt) { fd_set *readset = NULL, *writeset = NULL; int rekeying = 0, max_fd, nalloc = 0; double start_time, total_time; debug("Entering interactive session for SSH2."); start_time = get_current_time(); mysignal(SIGCHLD, sigchld_handler); child_terminated = 0; connection_in = packet_get_connection_in(); connection_out = packet_get_connection_out(); if (!use_privsep) { signal(SIGTERM, sigterm_handler); signal(SIGINT, sigterm_handler); signal(SIGQUIT, sigterm_handler); } notify_setup(); max_fd = MAX(connection_in, connection_out); max_fd = MAX(max_fd, notify_pipe[0]); server_init_dispatch(); for (;;) { process_buffered_input_packets(); rekeying = (xxx_kex != NULL && !xxx_kex->done); if (!rekeying && packet_not_very_much_data_to_write()) channel_output_poll(); wait_until_can_do_something(&readset, &writeset, &max_fd, &nalloc, 0); if (received_sigterm) { logit("Exiting on signal %d", received_sigterm); /* Clean up sessions, utmp, etc. */ cleanup_exit(255); } collect_children(); if (!rekeying) { channel_after_select(readset, writeset); if (packet_need_rekeying()) { debug("need rekeying"); xxx_kex->done = 0; kex_send_kexinit(xxx_kex); } } process_input(readset); if (connection_closed) break; process_output(writeset); } collect_children(); if (readset) xfree(readset); if (writeset) xfree(writeset); /* free all channels, no more reads and writes */ channel_free_all(); /* free remaining sessions, e.g. remove wtmp entries */ session_destroy_all(NULL); total_time = get_current_time() - start_time; logit("SSH: Server;LType: Throughput;Remote: %s-%d;IN: %lu;OUT: %lu;Duration: %.1f;tPut_in: %.1f;tPut_out: %.1f", get_remote_ipaddr(), get_remote_port(), stdin_bytes, fdout_bytes, total_time, stdin_bytes / total_time, fdout_bytes / total_time); }
void record_failed_login(const char *username, const char *hostname, const char *ttyn) { int fd; struct utmp ut; struct sockaddr_storage from; socklen_t fromlen = sizeof(from); struct sockaddr_in *a4; struct sockaddr_in6 *a6; time_t t; struct stat fst; if (geteuid() != 0) return; if ((fd = open(_PATH_BTMP, O_WRONLY | O_APPEND)) < 0) { debug("Unable to open the btmp file %s: %s", _PATH_BTMP, strerror(errno)); return; } if (fstat(fd, &fst) < 0) { logit("%s: fstat of %s failed: %s", __func__, _PATH_BTMP, strerror(errno)); goto out; } if((fst.st_mode & (S_IXGRP | S_IRWXO)) || (fst.st_uid != 0)){ logit("Excess permission or bad ownership on file %s", _PATH_BTMP); goto out; } memset(&ut, 0, sizeof(ut)); /* strncpy because we don't necessarily want nul termination */ strncpy(ut.ut_user, username, sizeof(ut.ut_user)); strlcpy(ut.ut_line, "ssh:notty", sizeof(ut.ut_line)); time(&t); ut.ut_time = t; /* ut_time is not always a time_t */ ut.ut_type = LOGIN_PROCESS; ut.ut_pid = getpid(); /* strncpy because we don't necessarily want nul termination */ strncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); if (packet_connection_is_on_socket() && getpeername(packet_get_connection_in(), (struct sockaddr *)&from, &fromlen) == 0) { ipv64_normalise_mapped(&from, &fromlen); if (from.ss_family == AF_INET) { a4 = (struct sockaddr_in *)&from; memcpy(&ut.ut_addr, &(a4->sin_addr), MIN_SIZEOF(ut.ut_addr, a4->sin_addr)); } #ifdef HAVE_ADDR_V6_IN_UTMP if (from.ss_family == AF_INET6) { a6 = (struct sockaddr_in6 *)&from; memcpy(&ut.ut_addr_v6, &(a6->sin6_addr), MIN_SIZEOF(ut.ut_addr_v6, a6->sin6_addr)); } #endif } if (atomicio(vwrite, fd, &ut, sizeof(ut)) != sizeof(ut)) error("Failed to write to %s: %s", _PATH_BTMP, strerror(errno)); out: close(fd); }
int auth_krb4(Authctxt *authctxt, KTEXT auth, char **client, KTEXT reply) { AUTH_DAT adat = {0}; Key_schedule schedule; struct sockaddr_in local, foreign; char instance[INST_SZ]; socklen_t slen; u_int cksum; int r, s; s = packet_get_connection_in(); slen = sizeof(local); memset(&local, 0, sizeof(local)); if (getsockname(s, (struct sockaddr *) & local, &slen) < 0) debug("getsockname failed: %.100s", strerror(errno)); slen = sizeof(foreign); memset(&foreign, 0, sizeof(foreign)); if (getpeername(s, (struct sockaddr *) & foreign, &slen) < 0) { debug("getpeername failed: %.100s", strerror(errno)); fatal_cleanup(); } instance[0] = '*'; instance[1] = 0; /* Get the encrypted request, challenge, and session key. */ if ((r = krb_rd_req(auth, KRB4_SERVICE_NAME, instance, 0, &adat, ""))) { debug("Kerberos v4 krb_rd_req: %.100s", krb_err_txt[r]); return (0); } des_key_sched((des_cblock *) adat.session, schedule); *client = xmalloc(MAX_K_NAME_SZ); (void) snprintf(*client, MAX_K_NAME_SZ, "%s%s%s@%s", adat.pname, *adat.pinst ? "." : "", adat.pinst, adat.prealm); /* Check ~/.klogin authorization now. */ if (kuserok(&adat, authctxt->user) != KSUCCESS) { log("Kerberos v4 .klogin authorization failed for %s to " "account %s", *client, authctxt->user); xfree(*client); *client = NULL; return (0); } /* Increment the checksum, and return it encrypted with the session key. */ cksum = adat.checksum + 1; cksum = htonl(cksum); /* If we can't successfully encrypt the checksum, we send back an empty message, admitting our failure. */ if ((r = krb_mk_priv((u_char *) & cksum, reply->dat, sizeof(cksum) + 1, schedule, &adat.session, &local, &foreign)) < 0) { debug("Kerberos v4 mk_priv: (%d) %s", r, krb_err_txt[r]); reply->dat[0] = 0; reply->length = 0; } else reply->length = r; /* Clear session key. */ memset(&adat.session, 0, sizeof(&adat.session)); return (1); }
/* * Performs the interactive session. This handles data transmission between * the client and the program. Note that the notion of stdin, stdout, and * stderr in this function is sort of reversed: this function writes to * stdin (of the child program), and reads from stdout and stderr (of the * child program). */ void server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) { fd_set *readset = NULL, *writeset = NULL; int max_fd = 0; u_int nalloc = 0; int wait_status; /* Status returned by wait(). */ pid_t wait_pid; /* pid returned by wait(). */ int waiting_termination = 0; /* Have displayed waiting close message. */ u_int64_t max_time_milliseconds; u_int previous_stdout_buffer_bytes; u_int stdout_buffer_bytes; int type; debug("Entering interactive session."); /* Initialize the SIGCHLD kludge. */ child_terminated = 0; mysignal(SIGCHLD, sigchld_handler); if (!use_privsep) { signal(SIGTERM, sigterm_handler); signal(SIGINT, sigterm_handler); signal(SIGQUIT, sigterm_handler); } /* Initialize our global variables. */ fdin = fdin_arg; fdout = fdout_arg; fderr = fderr_arg; /* nonblocking IO */ set_nonblock(fdin); set_nonblock(fdout); /* we don't have stderr for interactive terminal sessions, see below */ if (fderr != -1) set_nonblock(fderr); if (!(datafellows & SSH_BUG_IGNOREMSG) && isatty(fdin)) fdin_is_tty = 1; connection_in = packet_get_connection_in(); connection_out = packet_get_connection_out(); notify_setup(); previous_stdout_buffer_bytes = 0; /* Set approximate I/O buffer size. */ if (packet_is_interactive()) buffer_high = 4096; else buffer_high = 64 * 1024; #if 0 /* Initialize max_fd to the maximum of the known file descriptors. */ max_fd = MAX(connection_in, connection_out); max_fd = MAX(max_fd, fdin); max_fd = MAX(max_fd, fdout); if (fderr != -1) max_fd = MAX(max_fd, fderr); #endif /* Initialize Initialize buffers. */ buffer_init(&stdin_buffer); buffer_init(&stdout_buffer); buffer_init(&stderr_buffer); /* * If we have no separate fderr (which is the case when we have a pty * - there we cannot make difference between data sent to stdout and * stderr), indicate that we have seen an EOF from stderr. This way * we don't need to check the descriptor everywhere. */ if (fderr == -1) fderr_eof = 1; server_init_dispatch(); /* Main loop of the server for the interactive session mode. */ for (;;) { /* Process buffered packets from the client. */ process_buffered_input_packets(); /* * If we have received eof, and there is no more pending * input data, cause a real eof by closing fdin. */ if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) { if (fdin != fdout) close(fdin); else shutdown(fdin, SHUT_WR); /* We will no longer send. */ fdin = -1; } /* Make packets from buffered stderr data to send to the client. */ make_packets_from_stderr_data(); /* * Make packets from buffered stdout data to send to the * client. If there is very little to send, this arranges to * not send them now, but to wait a short while to see if we * are getting more data. This is necessary, as some systems * wake up readers from a pty after each separate character. */ max_time_milliseconds = 0; stdout_buffer_bytes = buffer_len(&stdout_buffer); if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 && stdout_buffer_bytes != previous_stdout_buffer_bytes) { /* try again after a while */ max_time_milliseconds = 10; } else { /* Send it now. */ make_packets_from_stdout_data(); } previous_stdout_buffer_bytes = buffer_len(&stdout_buffer); /* Send channel data to the client. */ if (packet_not_very_much_data_to_write()) channel_output_poll(); /* * Bail out of the loop if the program has closed its output * descriptors, and we have no more data to send to the * client, and there is no pending buffered data. */ if (fdout_eof && fderr_eof && !packet_have_data_to_write() && buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) { if (!channel_still_open()) break; if (!waiting_termination) { const char *s = "Waiting for forwarded connections to terminate... (press ~& to background)\r\n"; char *cp; waiting_termination = 1; buffer_append(&stderr_buffer, s, strlen(s)); /* Display list of open channels. */ cp = channel_open_message(); buffer_append(&stderr_buffer, cp, strlen(cp)); free(cp); } } max_fd = MAX(connection_in, connection_out); max_fd = MAX(max_fd, fdin); max_fd = MAX(max_fd, fdout); max_fd = MAX(max_fd, fderr); max_fd = MAX(max_fd, notify_pipe[0]); /* Sleep in select() until we can do something. */ wait_until_can_do_something(&readset, &writeset, &max_fd, &nalloc, max_time_milliseconds); if (received_sigterm) { logit("Exiting on signal %d", (int)received_sigterm); /* Clean up sessions, utmp, etc. */ cleanup_exit(255); } /* Process any channel events. */ channel_after_select(readset, writeset); /* Process input from the client and from program stdout/stderr. */ process_input(readset); /* Process output to the client and to program stdin. */ process_output(writeset); } free(readset); free(writeset); /* Cleanup and termination code. */ /* Wait until all output has been sent to the client. */ drain_output(); debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); /* Free and clear the buffers. */ buffer_free(&stdin_buffer); buffer_free(&stdout_buffer); buffer_free(&stderr_buffer); /* Close the file descriptors. */ if (fdout != -1) close(fdout); fdout = -1; fdout_eof = 1; if (fderr != -1) close(fderr); fderr = -1; fderr_eof = 1; if (fdin != -1) close(fdin); fdin = -1; channel_free_all(); /* We no longer want our SIGCHLD handler to be called. */ mysignal(SIGCHLD, SIG_DFL); while ((wait_pid = waitpid(-1, &wait_status, 0)) < 0) if (errno != EINTR) packet_disconnect("wait: %.100s", strerror(errno)); if (wait_pid != pid) error("Strange, wait returned pid %ld, expected %ld", (long)wait_pid, (long)pid); /* Check if it exited normally. */ if (WIFEXITED(wait_status)) { /* Yes, normal exit. Get exit status and send it to the client. */ debug("Command exited with status %d.", WEXITSTATUS(wait_status)); packet_start(SSH_SMSG_EXITSTATUS); packet_put_int(WEXITSTATUS(wait_status)); packet_send(); packet_write_wait(); /* * Wait for exit confirmation. Note that there might be * other packets coming before it; however, the program has * already died so we just ignore them. The client is * supposed to respond with the confirmation when it receives * the exit status. */ do { type = packet_read(); } while (type != SSH_CMSG_EXIT_CONFIRMATION); debug("Received exit confirmation."); return; } /* Check if the program terminated due to a signal. */ if (WIFSIGNALED(wait_status)) packet_disconnect("Command terminated on signal %d.", WTERMSIG(wait_status)); /* Some weird exit cause. Just exit. */ packet_disconnect("wait returned status %04x.", wait_status); /* NOTREACHED */ }
/* * Try krb5 authentication. server_user is passed for logging purposes * only, in auth is received ticket, in client is returned principal * from the ticket */ int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *reply) { krb5_error_code problem; krb5_principal server; krb5_ticket *ticket; int fd, ret; const char *errtxt; ret = 0; server = NULL; ticket = NULL; reply->length = 0; problem = krb5_init(authctxt); if (problem) goto err; problem = krb5_auth_con_init(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx); if (problem) goto err; fd = packet_get_connection_in(); problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, &fd); if (problem) goto err; problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL, KRB5_NT_SRV_HST, &server); if (problem) goto err; problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx, auth, server, NULL, NULL, &ticket); if (problem) goto err; problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client, &authctxt->krb5_user); if (problem) goto err; /* if client wants mutual auth */ problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, reply); if (problem) goto err; /* Check .k5login authorization now. */ if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, authctxt->pw->pw_name)) goto err; if (client) krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, client); ret = 1; err: if (server) krb5_free_principal(authctxt->krb5_ctx, server); if (ticket) krb5_free_ticket(authctxt->krb5_ctx, ticket); if (!ret && reply->length) { free(reply->data); memset(reply, 0, sizeof(*reply)); } if (problem) { errtxt = NULL; if (authctxt->krb5_ctx != NULL) errtxt = krb5_get_error_message(authctxt->krb5_ctx, problem); if (errtxt != NULL) { debug("Kerberos v5 authentication failed: %s", errtxt); krb5_free_error_message(authctxt->krb5_ctx, errtxt); } else debug("Kerberos v5 authentication failed: %d", problem); } return (ret); }
void server_loop2(Authctxt *authctxt) { fd_set *readset = NULL, *writeset = NULL; int rekeying = 0, max_fd; u_int nalloc = 0; u_int64_t rekey_timeout_ms = 0; debug("Entering interactive session for SSH2."); mysignal(SIGCHLD, sigchld_handler); child_terminated = 0; connection_in = packet_get_connection_in(); connection_out = packet_get_connection_out(); if (!use_privsep) { signal(SIGTERM, sigterm_handler); signal(SIGINT, sigterm_handler); signal(SIGQUIT, sigterm_handler); } notify_setup(); max_fd = MAX(connection_in, connection_out); max_fd = MAX(max_fd, notify_pipe[0]); server_init_dispatch(); for (;;) { process_buffered_input_packets(); rekeying = (xxx_kex != NULL && !xxx_kex->done); if (!rekeying && packet_not_very_much_data_to_write()) channel_output_poll(); if (options.rekey_interval > 0 && compat20 && !rekeying) rekey_timeout_ms = packet_get_rekey_timeout() * 1000; else rekey_timeout_ms = 0; wait_until_can_do_something(&readset, &writeset, &max_fd, &nalloc, rekey_timeout_ms); if (received_sigterm) { logit("Exiting on signal %d", (int)received_sigterm); /* Clean up sessions, utmp, etc. */ cleanup_exit(255); } collect_children(); if (!rekeying) { channel_after_select(readset, writeset); if (packet_need_rekeying()) { debug("need rekeying"); xxx_kex->done = 0; kex_send_kexinit(xxx_kex); } } process_input(readset); if (connection_closed) break; process_output(writeset); } collect_children(); free(readset); free(writeset); /* free all channels, no more reads and writes */ channel_free_all(); /* free remaining sessions, e.g. remove wtmp entries */ session_destroy_all(NULL); }
/* * Waits for the server identification string, and sends our own * identification string. */ void ssh_exchange_identification(int timeout_ms) { char buf[256], remote_version[256]; /* must be same size! */ int remote_major, remote_minor, mismatch; int connection_in = packet_get_connection_in(); int connection_out = packet_get_connection_out(); u_int i, n; size_t len; int rc; send_client_banner(connection_out, 0); /* Read other side's version identification. */ for (n = 0;;) { for (i = 0; i < sizeof(buf) - 1; i++) { if (timeout_ms > 0) { rc = waitrfd(connection_in, &timeout_ms); if (rc == -1 && errno == ETIMEDOUT) { fatal("Connection timed out during " "banner exchange"); } else if (rc == -1) { fatal("%s: %s", __func__, strerror(errno)); } } len = atomicio(read, connection_in, &buf[i], 1); if (len != 1 && errno == EPIPE) fatal("ssh_exchange_identification: " "Connection closed by remote host"); else if (len != 1) fatal("ssh_exchange_identification: " "read: %.100s", strerror(errno)); if (buf[i] == '\r') { buf[i] = '\n'; buf[i + 1] = 0; continue; /**XXX wait for \n */ } if (buf[i] == '\n') { buf[i + 1] = 0; break; } if (++n > 65536) fatal("ssh_exchange_identification: " "No banner received"); } buf[sizeof(buf) - 1] = 0; if (strncmp(buf, "SSH-", 4) == 0) break; debug("ssh_exchange_identification: %s", buf); } server_version_string = xstrdup(buf); /* * Check that the versions match. In future this might accept * several versions and set appropriate flags to handle them. */ if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) != 3) fatal("Bad remote protocol version identification: '%.100s'", buf); debug("Remote protocol version %d.%d, remote software version %.100s", remote_major, remote_minor, remote_version); active_state->compat = compat_datafellows(remote_version); mismatch = 0; switch (remote_major) { case 2: break; case 1: if (remote_minor != 99) mismatch = 1; break; default: mismatch = 1; break; } if (mismatch) fatal("Protocol major versions differ: %d vs. %d", PROTOCOL_MAJOR_2, remote_major); if ((datafellows & SSH_BUG_DERIVEKEY) != 0) fatal("Server version \"%.100s\" uses unsafe key agreement; " "refusing connection", remote_version); if ((datafellows & SSH_BUG_RSASIGMD5) != 0) logit("Server version \"%.100s\" uses unsafe RSA signature " "scheme; disabling use of RSA keys", remote_version); chop(server_version_string); }