From 5260325f3150ad441f310d31239beeb765f716ed Mon Sep 17 00:00:00 2001 From: damien Date: Wed, 24 Nov 1999 13:26:21 +0000 Subject: [PATCH] - Merged very large OpenBSD source code reformat - OpenBSD CVS updates - [channels.c cipher.c compat.c log-client.c scp.c serverloop.c] [ssh.h sshd.8 sshd.c] syslog changes: * Unified Logmessage for all auth-types, for success and for failed * Standard connections get only ONE line in the LOG when level==LOG: Auth-attempts are logged only, if authentication is: a) successfull or b) with passwd or c) we had more than AUTH_FAIL_LOG failues * many log() became verbose() * old behaviour with level=VERBOSE - [readconf.c readconf.h ssh.1 ssh.h sshconnect.c sshd.c] tranfer s/key challenge/response data in SSH_SMSG_AUTH_TIS_CHALLENGE messages. allows use of s/key in windows (ttssh, securecrt) and ssh-1.2.27 clients without 'ssh -v', ok: niels@ - [sshd.8] -V, for fallback to openssh in SSH2 compatibility mode - [sshd.c] fix sigchld race; cjc5@po.cwru.edu --- ChangeLog | 23 + auth-krb4.c | 408 ++--- auth-passwd.c | 342 ++-- auth-rh-rsa.c | 172 +- auth-rhosts.c | 474 +++--- auth-rsa.c | 803 +++++---- auth-skey.c | 6 +- authfd.c | 919 +++++----- authfd.h | 85 +- authfile.c | 595 ++++--- bufaux.c | 226 +-- bufaux.h | 40 +- buffer.c | 177 +- buffer.h | 64 +- canohost.c | 369 ++-- channels.c | 2425 +++++++++++++------------- channels.h | 60 +- cipher.c | 407 ++--- cipher.h | 93 +- clientloop.c | 1522 ++++++++--------- compat.c | 11 +- compat.h | 2 +- compress.c | 231 ++- compress.h | 36 +- configure.in | 2 +- crc32.c | 137 +- crc32.h | 28 +- deattack.c | 236 ++- deattack.h | 7 +- fingerprint.c | 21 +- fingerprint.h | 2 +- getput.h | 29 +- hostfile.c | 455 +++-- includes.h | 28 +- log-client.c | 79 +- log-server.c | 236 ++- log.c | 219 ++- login.c | 191 +-- match.c | 123 +- mpaux.c | 66 +- mpaux.h | 38 +- nchan.c | 117 +- nchan.h | 24 +- packet.c | 857 +++++----- packet.h | 99 +- pty.c | 396 ++--- pty.h | 44 +- radix.c | 369 ++-- readconf.c | 1095 ++++++------ readconf.h | 166 +- readpass.c | 177 +- rsa.c | 229 ++- rsa.h | 39 +- scp.c | 633 ++++--- servconf.c | 933 +++++----- servconf.h | 140 +- serverloop.c | 1112 ++++++------ ssh-add.c | 513 +++--- ssh-agent.c | 1124 ++++++------ ssh-keygen.c | 973 +++++------ ssh.1 | 10 + ssh.c | 1385 ++++++++------- ssh.h | 347 ++-- sshconnect.c | 2849 ++++++++++++++++--------------- sshd.8 | 10 +- sshd.c | 4501 +++++++++++++++++++++++++------------------------ tildexpand.c | 112 +- ttymodes.c | 477 +++--- ttymodes.h | 186 +- uidswap.c | 113 +- uidswap.h | 50 +- xmalloc.c | 71 +- xmalloc.h | 38 +- 73 files changed, 15086 insertions(+), 15490 deletions(-) diff --git a/ChangeLog b/ChangeLog index 16072e6a..7b68e3fc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +19991124 + - Merged very large OpenBSD source code reformat + - OpenBSD CVS updates + - [channels.c cipher.c compat.c log-client.c scp.c serverloop.c] + [ssh.h sshd.8 sshd.c] + syslog changes: + * Unified Logmessage for all auth-types, for success and for failed + * Standard connections get only ONE line in the LOG when level==LOG: + Auth-attempts are logged only, if authentication is: + a) successfull or + b) with passwd or + c) we had more than AUTH_FAIL_LOG failues + * many log() became verbose() + * old behaviour with level=VERBOSE + - [readconf.c readconf.h ssh.1 ssh.h sshconnect.c sshd.c] + tranfer s/key challenge/response data in SSH_SMSG_AUTH_TIS_CHALLENGE + messages. allows use of s/key in windows (ttssh, securecrt) and + ssh-1.2.27 clients without 'ssh -v', ok: niels@ + - [sshd.8] + -V, for fallback to openssh in SSH2 compatibility mode + - [sshd.c] + fix sigchld race; cjc5@po.cwru.edu + 19991123 - Added SuSE package files from Chris Saia - Restructured package-related files under packages/* diff --git a/auth-krb4.c b/auth-krb4.c index 7bb83ea6..9f99533b 100644 --- a/auth-krb4.c +++ b/auth-krb4.c @@ -1,13 +1,7 @@ /* - - auth-kerberos.c - - Dug Song - - Kerberos v4 authentication and ticket-passing routines. - - $Id$ -*/ + * Dug Song + * Kerberos v4 authentication and ticket-passing routines. + */ #include "includes.h" #include "packet.h" @@ -20,216 +14,222 @@ char *ticket = NULL; void krb4_cleanup_proc(void *ignore) { - debug("krb4_cleanup_proc called"); - - if (ticket) { - (void) dest_tkt(); - xfree(ticket); - ticket = NULL; - } + debug("krb4_cleanup_proc called"); + if (ticket) { + (void) dest_tkt(); + xfree(ticket); + ticket = NULL; + } } -int krb4_init(uid_t uid) +int +krb4_init(uid_t uid) { - static int cleanup_registered = 0; - char *tkt_root = TKT_ROOT; - struct stat st; - int fd; - - if (!ticket) { - /* Set unique ticket string manually since we're still root. */ - ticket = xmalloc(MAXPATHLEN); + static int cleanup_registered = 0; + char *tkt_root = TKT_ROOT; + struct stat st; + int fd; + + if (!ticket) { + /* Set unique ticket string manually since we're still root. */ + ticket = xmalloc(MAXPATHLEN); #ifdef AFS - if (lstat("/ticket", &st) != -1) - tkt_root = "/ticket/"; + if (lstat("/ticket", &st) != -1) + tkt_root = "/ticket/"; #endif /* AFS */ - snprintf(ticket, MAXPATHLEN, "%s%d_%d", tkt_root, uid, getpid()); - (void) krb_set_tkt_string(ticket); - } - /* Register ticket cleanup in case of fatal error. */ - if (!cleanup_registered) { - fatal_add_cleanup(krb4_cleanup_proc, NULL); - cleanup_registered = 1; - } - /* Try to create our ticket file. */ - if ((fd = mkstemp(ticket)) != -1) { - close(fd); - return 1; - } - /* Ticket file exists - make sure user owns it (just passed ticket). */ - if (lstat(ticket, &st) != -1) { - if (st.st_mode == (S_IFREG|S_IRUSR|S_IWUSR) && st.st_uid == uid) - return 1; - } - /* Failure - cancel cleanup function, leaving bad ticket for inspection. */ - log("WARNING: bad ticket file %s", ticket); - fatal_remove_cleanup(krb4_cleanup_proc, NULL); - cleanup_registered = 0; - xfree(ticket); - ticket = NULL; - - return 0; + snprintf(ticket, MAXPATHLEN, "%s%d_%d", tkt_root, uid, getpid()); + (void) krb_set_tkt_string(ticket); + } + /* Register ticket cleanup in case of fatal error. */ + if (!cleanup_registered) { + fatal_add_cleanup(krb4_cleanup_proc, NULL); + cleanup_registered = 1; + } + /* Try to create our ticket file. */ + if ((fd = mkstemp(ticket)) != -1) { + close(fd); + return 1; + } + /* Ticket file exists - make sure user owns it (just passed ticket). */ + if (lstat(ticket, &st) != -1) { + if (st.st_mode == (S_IFREG | S_IRUSR | S_IWUSR) && + st.st_uid == uid) + return 1; + } + /* Failure - cancel cleanup function, leaving bad ticket for inspection. */ + log("WARNING: bad ticket file %s", ticket); + fatal_remove_cleanup(krb4_cleanup_proc, NULL); + cleanup_registered = 0; + xfree(ticket); + ticket = NULL; + + return 0; } -int auth_krb4(const char *server_user, KTEXT auth, char **client) +int +auth_krb4(const char *server_user, KTEXT auth, char **client) { - AUTH_DAT adat = { 0 }; - KTEXT_ST reply; - char instance[INST_SZ]; - int r, s; - u_int cksum; - Key_schedule schedule; - struct sockaddr_in local, foreign; - - s = packet_get_connection_in(); - - r = sizeof(local); - memset(&local, 0, sizeof(local)); - if (getsockname(s, (struct sockaddr *) &local, &r) < 0) - debug("getsockname failed: %.100s", strerror(errno)); - r = sizeof(foreign); - memset(&foreign, 0, sizeof(foreign)); - if (getpeername(s, (struct sockaddr *)&foreign, &r) < 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, ""))) { - packet_send_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, (char *)server_user) != KSUCCESS) { - packet_send_debug("Kerberos V4 .klogin authorization failed!"); - log("Kerberos V4 .klogin authorization failed for %s to account %s", - *client, server_user); - xfree(*client); - 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) { - packet_send_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)); - - packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE); - packet_put_string((char *) reply.dat, reply.length); - packet_send(); - packet_write_wait(); - return 1; + AUTH_DAT adat = {0}; + KTEXT_ST reply; + char instance[INST_SZ]; + int r, s; + u_int cksum; + Key_schedule schedule; + struct sockaddr_in local, foreign; + + s = packet_get_connection_in(); + + r = sizeof(local); + memset(&local, 0, sizeof(local)); + if (getsockname(s, (struct sockaddr *) & local, &r) < 0) + debug("getsockname failed: %.100s", strerror(errno)); + r = sizeof(foreign); + memset(&foreign, 0, sizeof(foreign)); + if (getpeername(s, (struct sockaddr *) & foreign, &r) < 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, ""))) { + packet_send_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, (char *) server_user) != KSUCCESS) { + packet_send_debug("Kerberos V4 .klogin authorization failed!"); + log("Kerberos V4 .klogin authorization failed for %s to account %s", + *client, server_user); + xfree(*client); + 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) { + packet_send_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)); + + packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE); + packet_put_string((char *) reply.dat, reply.length); + packet_send(); + packet_write_wait(); + return 1; } #endif /* KRB4 */ #ifdef AFS -int auth_kerberos_tgt(struct passwd *pw, const char *string) +int +auth_kerberos_tgt(struct passwd *pw, const char *string) { - CREDENTIALS creds; - - if (!radix_to_creds(string, &creds)) { - log("Protocol error decoding Kerberos V4 tgt"); - packet_send_debug("Protocol error decoding Kerberos V4 tgt"); - goto auth_kerberos_tgt_failure; - } - if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ - strlcpy(creds.service, "krbtgt", sizeof creds.service); - - if (strcmp(creds.service, "krbtgt")) { - log("Kerberos V4 tgt (%s%s%s@%s) rejected for %s", creds.pname, - creds.pinst[0] ? "." : "", creds.pinst, creds.realm, pw->pw_name); - packet_send_debug("Kerberos V4 tgt (%s%s%s@%s) rejected for %s", - creds.pname, creds.pinst[0] ? "." : "", creds.pinst, - creds.realm, pw->pw_name); - goto auth_kerberos_tgt_failure; - } - if (!krb4_init(pw->pw_uid)) - goto auth_kerberos_tgt_failure; - - if (in_tkt(creds.pname, creds.pinst) != KSUCCESS) - goto auth_kerberos_tgt_failure; - - if (save_credentials(creds.service, creds.instance, creds.realm, - creds.session, creds.lifetime, creds.kvno, - &creds.ticket_st, creds.issue_date) != KSUCCESS) { - packet_send_debug("Kerberos V4 tgt refused: couldn't save credentials"); - goto auth_kerberos_tgt_failure; - } - /* Successful authentication, passed all checks. */ - chown(tkt_string(), pw->pw_uid, pw->pw_gid); - - packet_send_debug("Kerberos V4 tgt accepted (%s.%s@%s, %s%s%s@%s)", - creds.service, creds.instance, creds.realm, creds.pname, - creds.pinst[0] ? "." : "", creds.pinst, creds.realm); - memset(&creds, 0, sizeof(creds)); - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); - return 1; - - auth_kerberos_tgt_failure: - krb4_cleanup_proc(NULL); - memset(&creds, 0, sizeof(creds)); - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - return 0; + CREDENTIALS creds; + + if (!radix_to_creds(string, &creds)) { + log("Protocol error decoding Kerberos V4 tgt"); + packet_send_debug("Protocol error decoding Kerberos V4 tgt"); + goto auth_kerberos_tgt_failure; + } + if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ + strlcpy(creds.service, "krbtgt", sizeof creds.service); + + if (strcmp(creds.service, "krbtgt")) { + log("Kerberos V4 tgt (%s%s%s@%s) rejected for %s", creds.pname, + creds.pinst[0] ? "." : "", creds.pinst, creds.realm, + pw->pw_name); + packet_send_debug("Kerberos V4 tgt (%s%s%s@%s) rejected for %s", + creds.pname, creds.pinst[0] ? "." : "", creds.pinst, + creds.realm, pw->pw_name); + goto auth_kerberos_tgt_failure; + } + if (!krb4_init(pw->pw_uid)) + goto auth_kerberos_tgt_failure; + + if (in_tkt(creds.pname, creds.pinst) != KSUCCESS) + goto auth_kerberos_tgt_failure; + + if (save_credentials(creds.service, creds.instance, creds.realm, + creds.session, creds.lifetime, creds.kvno, + &creds.ticket_st, creds.issue_date) != KSUCCESS) { + packet_send_debug("Kerberos V4 tgt refused: couldn't save credentials"); + goto auth_kerberos_tgt_failure; + } + /* Successful authentication, passed all checks. */ + chown(tkt_string(), pw->pw_uid, pw->pw_gid); + + packet_send_debug("Kerberos V4 tgt accepted (%s.%s@%s, %s%s%s@%s)", + creds.service, creds.instance, creds.realm, creds.pname, + creds.pinst[0] ? "." : "", creds.pinst, creds.realm); + memset(&creds, 0, sizeof(creds)); + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); + return 1; + +auth_kerberos_tgt_failure: + krb4_cleanup_proc(NULL); + memset(&creds, 0, sizeof(creds)); + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + return 0; } -int auth_afs_token(struct passwd *pw, const char *token_string) +int +auth_afs_token(struct passwd *pw, const char *token_string) { - CREDENTIALS creds; - uid_t uid = pw->pw_uid; - - if (!radix_to_creds(token_string, &creds)) { - log("Protocol error decoding AFS token"); - packet_send_debug("Protocol error decoding AFS token"); - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - return 0; - } - if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ - strlcpy(creds.service, "afs", sizeof creds.service); - - if (strncmp(creds.pname, "AFS ID ", 7) == 0) - uid = atoi(creds.pname + 7); - - if (kafs_settoken(creds.realm, uid, &creds)) { - log("AFS token (%s@%s) rejected for %s", creds.pname, creds.realm, - pw->pw_name); - packet_send_debug("AFS token (%s@%s) rejected for %s", creds.pname, - creds.realm, pw->pw_name); - memset(&creds, 0, sizeof(creds)); - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - return 0; - } - packet_send_debug("AFS token accepted (%s@%s, %s@%s)", creds.service, - creds.realm, creds.pname, creds.realm); - memset(&creds, 0, sizeof(creds)); - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); - return 1; + CREDENTIALS creds; + uid_t uid = pw->pw_uid; + + if (!radix_to_creds(token_string, &creds)) { + log("Protocol error decoding AFS token"); + packet_send_debug("Protocol error decoding AFS token"); + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + return 0; + } + if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ + strlcpy(creds.service, "afs", sizeof creds.service); + + if (strncmp(creds.pname, "AFS ID ", 7) == 0) + uid = atoi(creds.pname + 7); + + if (kafs_settoken(creds.realm, uid, &creds)) { + log("AFS token (%s@%s) rejected for %s", creds.pname, creds.realm, + pw->pw_name); + packet_send_debug("AFS token (%s@%s) rejected for %s", creds.pname, + creds.realm, pw->pw_name); + memset(&creds, 0, sizeof(creds)); + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + return 0; + } + packet_send_debug("AFS token accepted (%s@%s, %s@%s)", creds.service, + creds.realm, creds.pname, creds.realm); + memset(&creds, 0, sizeof(creds)); + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); + return 1; } #endif /* AFS */ diff --git a/auth-passwd.c b/auth-passwd.c index bebd6389..76325f02 100644 --- a/auth-passwd.c +++ b/auth-passwd.c @@ -1,18 +1,11 @@ /* - -auth-passwd.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sat Mar 18 05:11:38 1995 ylo - -Password authentication. This file contains the functions to check whether -the password is valid for the user. - -*/ + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Sat Mar 18 05:11:38 1995 ylo + * Password authentication. This file contains the functions to check whether + * the password is valid for the user. + */ #include "includes.h" @@ -24,7 +17,6 @@ RCSID("$Id$"); #include "ssh.h" #include "servconf.h" #include "xmalloc.h" -#include "config.h" #ifdef HAVE_SHADOW_H #include @@ -34,185 +26,189 @@ RCSID("$Id$"); #include "md5crypt.h" #endif -/* Don't need anything from here if we are using PAM */ - -/* Tries to authenticate the user using password. Returns true if - authentication succeeds. */ - -int auth_password(struct passwd *pw, const char *password) +/* + * Tries to authenticate the user using password. Returns true if + * authentication succeeds. + */ +int +auth_password(struct passwd * pw, const char *password) { - extern ServerOptions options; - char *encrypted_password; + extern ServerOptions options; + char *encrypted_password; #ifdef HAVE_SHADOW_H - struct spwd *spw; + struct spwd *spw; #endif - if (pw->pw_uid == 0 && options.permit_root_login == 2) - { - /*packet_send_debug("Server does not permit root login with password.");*/ - return 0; - } - - if (*password == '\0' && options.permit_empty_passwd == 0) - { - /*packet_send_debug("Server does not permit empty password login.");*/ - return 0; - } - - /* deny if no user. */ - if (pw == NULL) - return 0; + if (pw->pw_uid == 0 && options.permit_root_login == 2) { + /* Server does not permit root login with password */ + return 0; + } + if (*password == '\0' && options.permit_empty_passwd == 0) { + /* Server does not permit empty password login */ + return 0; + } + /* deny if no user. */ + if (pw == NULL) + return 0; #ifdef SKEY - if (options.skey_authentication == 1) { - if (strncasecmp(password, "s/key", 5) == 0) { - char *skeyinfo = skey_keyinfo(pw->pw_name); - if(skeyinfo == NULL){ - debug("generating fake skeyinfo for %.100s.", pw->pw_name); - skeyinfo = skey_fake_keyinfo(pw->pw_name); - } - if(skeyinfo != NULL) - packet_send_debug(skeyinfo); - /* Try again. */ - return 0; - } - else if (skey_haskey(pw->pw_name) == 0 && - skey_passcheck(pw->pw_name, (char *)password) != -1) { - /* Authentication succeeded. */ - return 1; - } - /* Fall back to ordinary passwd authentication. */ - } + if (options.skey_authentication == 1) { + if (strncasecmp(password, "s/key", 5) == 0) { + char *skeyinfo = skey_keyinfo(pw->pw_name); + if (skeyinfo == NULL) { + debug("generating fake skeyinfo for %.100s.", + pw->pw_name); + skeyinfo = skey_fake_keyinfo(pw->pw_name); + } + if (skeyinfo != NULL) + packet_send_debug(skeyinfo); + /* Try again. */ + return 0; + } else if (skey_haskey(pw->pw_name) == 0 && + skey_passcheck(pw->pw_name, (char *) password) != -1) { + /* Authentication succeeded. */ + return 1; + } + /* Fall back to ordinary passwd authentication. */ + } #endif #if defined(KRB4) - /* Support for Kerberos v4 authentication - Dug Song */ - if (options.kerberos_authentication) - { - AUTH_DAT adata; - KTEXT_ST tkt; - struct hostent *hp; - unsigned long faddr; - char localhost[MAXHOSTNAMELEN]; - char phost[INST_SZ]; - char realm[REALM_SZ]; - int r; - - /* Try Kerberos password authentication only for non-root - users and only if Kerberos is installed. */ - if (pw->pw_uid != 0 && krb_get_lrealm(realm, 1) == KSUCCESS) { - - /* Set up our ticket file. */ - if (!krb4_init(pw->pw_uid)) { - log("Couldn't initialize Kerberos ticket file for %s!", pw->pw_name); - goto kerberos_auth_failure; - } - /* Try to get TGT using our password. */ - r = krb_get_pw_in_tkt((char *)pw->pw_name, "", realm, "krbtgt", realm, - DEFAULT_TKT_LIFE, (char *)password); - if (r != INTK_OK) { - packet_send_debug("Kerberos V4 password authentication for %s " - "failed: %s", pw->pw_name, krb_err_txt[r]); - goto kerberos_auth_failure; + /* Support for Kerberos v4 authentication - Dug Song + */ + if (options.kerberos_authentication) { + AUTH_DAT adata; + KTEXT_ST tkt; + struct hostent *hp; + unsigned long faddr; + char localhost[MAXHOSTNAMELEN]; + char phost[INST_SZ]; + char realm[REALM_SZ]; + int r; + + /* Try Kerberos password authentication only for non-root + users and only if Kerberos is installed. */ + if (pw->pw_uid != 0 && krb_get_lrealm(realm, 1) == KSUCCESS) { + + /* Set up our ticket file. */ + if (!krb4_init(pw->pw_uid)) { + log("Couldn't initialize Kerberos ticket file for %s!", + pw->pw_name); + goto kerberos_auth_failure; + } + /* Try to get TGT using our password. */ + r = krb_get_pw_in_tkt((char *) pw->pw_name, "", + realm, "krbtgt", realm, + DEFAULT_TKT_LIFE, (char *) password); + if (r != INTK_OK) { + packet_send_debug("Kerberos V4 password " + "authentication for %s failed: %s", + pw->pw_name, krb_err_txt[r]); + goto kerberos_auth_failure; + } + /* Successful authentication. */ + chown(tkt_string(), pw->pw_uid, pw->pw_gid); + + /* + * Now that we have a TGT, try to get a local + * "rcmd" ticket to ensure that we are not talking + * to a bogus Kerberos server. + */ + (void) gethostname(localhost, sizeof(localhost)); + (void) strlcpy(phost, (char *) krb_get_phost(localhost), + INST_SZ); + r = krb_mk_req(&tkt, KRB4_SERVICE_NAME, phost, realm, 33); + + if (r == KSUCCESS) { + if (!(hp = gethostbyname(localhost))) { + log("Couldn't get local host address!"); + goto kerberos_auth_failure; + } + memmove((void *) &faddr, (void *) hp->h_addr, + sizeof(faddr)); + + /* Verify our "rcmd" ticket. */ + r = krb_rd_req(&tkt, KRB4_SERVICE_NAME, phost, + faddr, &adata, ""); + if (r == RD_AP_UNDEC) { + /* + * Probably didn't have a srvtab on + * localhost. Allow login. + */ + log("Kerberos V4 TGT for %s unverifiable, " + "no srvtab installed? krb_rd_req: %s", + pw->pw_name, krb_err_txt[r]); + } else if (r != KSUCCESS) { + log("Kerberos V4 %s ticket unverifiable: %s", + KRB4_SERVICE_NAME, krb_err_txt[r]); + goto kerberos_auth_failure; + } + } else if (r == KDC_PR_UNKNOWN) { + /* Allow login if no rcmd service exists, + but log the error. */ + log("Kerberos V4 TGT for %s unverifiable: %s; %s.%s " + "not registered, or srvtab is wrong?", pw->pw_name, + krb_err_txt[r], KRB4_SERVICE_NAME, phost); + } else { + /* TGT is bad, forget it. Possibly + spoofed! */ + packet_send_debug("WARNING: Kerberos V4 TGT " + "possibly spoofed for %s: %s", + pw->pw_name, krb_err_txt[r]); + goto kerberos_auth_failure; + } + + /* Authentication succeeded. */ + return 1; + + kerberos_auth_failure: + krb4_cleanup_proc(NULL); + + if (!options.kerberos_or_local_passwd) + return 0; + } else { + /* Logging in as root or no local Kerberos realm. */ + packet_send_debug("Unable to authenticate to Kerberos."); + } + /* Fall back to ordinary passwd authentication. */ } - /* Successful authentication. */ - chown(tkt_string(), pw->pw_uid, pw->pw_gid); - - /* Now that we have a TGT, try to get a local "rcmd" ticket to - ensure that we are not talking to a bogus Kerberos server. */ - (void) gethostname(localhost, sizeof(localhost)); - (void) strlcpy(phost, (char *)krb_get_phost(localhost), INST_SZ); - r = krb_mk_req(&tkt, KRB4_SERVICE_NAME, phost, realm, 33); - - if (r == KSUCCESS) { - if (!(hp = gethostbyname(localhost))) { - log("Couldn't get local host address!"); - goto kerberos_auth_failure; - } - memmove((void *)&faddr, (void *)hp->h_addr, sizeof(faddr)); - - /* Verify our "rcmd" ticket. */ - r = krb_rd_req(&tkt, KRB4_SERVICE_NAME, phost, faddr, &adata, ""); - if (r == RD_AP_UNDEC) { - /* Probably didn't have a srvtab on localhost. Allow login. */ - log("Kerberos V4 TGT for %s unverifiable, no srvtab installed? " - "krb_rd_req: %s", pw->pw_name, krb_err_txt[r]); - } - else if (r != KSUCCESS) { - log("Kerberos V4 %s ticket unverifiable: %s", - KRB4_SERVICE_NAME, krb_err_txt[r]); - goto kerberos_auth_failure; - } - } - else if (r == KDC_PR_UNKNOWN) { - /* Allow login if no rcmd service exists, but log the error. */ - log("Kerberos V4 TGT for %s unverifiable: %s; %s.%s " - "not registered, or srvtab is wrong?", pw->pw_name, - krb_err_txt[r], KRB4_SERVICE_NAME, phost); - } - else { - /* TGT is bad, forget it. Possibly spoofed! */ - packet_send_debug("WARNING: Kerberos V4 TGT possibly spoofed for" - "%s: %s", pw->pw_name, krb_err_txt[r]); - goto kerberos_auth_failure; +#endif /* KRB4 */ + + /* Check for users with no password. */ + if (strcmp(password, "") == 0 && strcmp(pw->pw_passwd, "") == 0) { + packet_send_debug("Login permitted without a password " + "because the account has no password."); + return 1; } - - /* Authentication succeeded. */ - return 1; - - kerberos_auth_failure: - krb4_cleanup_proc(NULL); - - if (!options.kerberos_or_local_passwd) - return 0; - } - else { - /* Logging in as root or no local Kerberos realm. */ - packet_send_debug("Unable to authenticate to Kerberos."); - } - /* Fall back to ordinary passwd authentication. */ - } -#endif /* KRB4 */ - - /* Check for users with no password. */ - if (strcmp(password, "") == 0 && strcmp(pw->pw_passwd, "") == 0) - { - packet_send_debug("Login permitted without a password because the account has no password."); - return 1; /* The user has no password and an empty password was tried. */ - } #ifdef HAVE_SHADOW_H - spw = getspnam(pw->pw_name); - if (spw == NULL) - return(0); + spw = getspnam(pw->pw_name); + if (spw == NULL) + return(0); - if ((spw->sp_namp == NULL) || (strcmp(pw->pw_name, spw->sp_namp) != 0)) - fatal("Shadow lookup returned garbage."); + if ((spw->sp_namp == NULL) || (strcmp(pw->pw_name, spw->sp_namp) != 0)) + fatal("Shadow lookup returned garbage."); - if (strlen(spw->sp_pwdp) < 3) - return(0); + if (strlen(spw->sp_pwdp) < 3) + return(0); - /* Encrypt the candidate password using the proper salt. */ + /* Encrypt the candidate password using the proper salt. */ #ifdef HAVE_MD5_PASSWORDS - if (is_md5_salt(spw->sp_pwdp)) - encrypted_password = md5_crypt(password, spw->sp_pwdp); - else - encrypted_password = crypt(password, spw->sp_pwdp); + if (is_md5_salt(spw->sp_pwdp)) + encrypted_password = md5_crypt(password, spw->sp_pwdp); + else + encrypted_password = crypt(password, spw->sp_pwdp); #else /* HAVE_MD5_PASSWORDS */ - encrypted_password = crypt(password, spw->sp_pwdp); + encrypted_password = crypt(password, spw->sp_pwdp); #endif /* HAVE_MD5_PASSWORDS */ - - /* Authentication is accepted if the encrypted passwords are identical. */ - return (strcmp(encrypted_password, spw->sp_pwdp) == 0); + /* Authentication is accepted if the encrypted passwords are identical. */ + return (strcmp(encrypted_password, spw->sp_pwdp) == 0); #else /* !HAVE_SHADOW_H */ + encrypted_password = crypt(password, + (pw->pw_passwd[0] && pw->pw_passwd[1]) ? pw->pw_passwd : "xx"); - /* Encrypt the candidate password using the proper salt. */ - encrypted_password = crypt(password, - (pw->pw_passwd[0] && pw->pw_passwd[1]) ? - pw->pw_passwd : "xx"); - /* Authentication is accepted if the encrypted passwords are identical. */ - return (strcmp(encrypted_password, pw->pw_passwd) == 0); + /* Authentication is accepted if the encrypted passwords are identical. */ + return (strcmp(encrypted_password, pw->pw_passwd) == 0); #endif /* !HAVE_SHADOW_H */ } - #endif /* !HAVE_PAM */ diff --git a/auth-rh-rsa.c b/auth-rh-rsa.c index cb03b839..0b8ea698 100644 --- a/auth-rh-rsa.c +++ b/auth-rh-rsa.c @@ -1,18 +1,18 @@ /* - -auth-rh-rsa.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sun May 7 03:08:06 1995 ylo - -Rhosts or /etc/hosts.equiv authentication combined with RSA host -authentication. - -*/ + * + * auth-rh-rsa.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Sun May 7 03:08:06 1995 ylo + * + * Rhosts or /etc/hosts.equiv authentication combined with RSA host + * authentication. + * + */ #include "includes.h" RCSID("$Id$"); @@ -26,78 +26,76 @@ RCSID("$Id$"); /* Tries to authenticate the user using the .rhosts file and the host using its host key. Returns true if authentication succeeds. */ -int auth_rhosts_rsa(struct passwd *pw, const char *client_user, - BIGNUM *client_host_key_e, BIGNUM *client_host_key_n) +int +auth_rhosts_rsa(struct passwd *pw, const char *client_user, + BIGNUM *client_host_key_e, BIGNUM *client_host_key_n) { - extern ServerOptions options; - const char *canonical_hostname; - HostStatus host_status; - BIGNUM *ke, *kn; - - debug("Trying rhosts with RSA host authentication for %.100s", client_user); - - /* Check if we would accept it using rhosts authentication. */ - if (!auth_rhosts(pw, client_user)) - return 0; - - canonical_hostname = get_canonical_hostname(); - - debug("Rhosts RSA authentication: canonical host %.900s", - canonical_hostname); - - /* Check if we know the host and its host key. */ - /* Check system-wide host file. */ - ke = BN_new(); - kn = BN_new(); - host_status = check_host_in_hostfile(SSH_SYSTEM_HOSTFILE, canonical_hostname, - client_host_key_e, client_host_key_n, ke, kn); - - /* Check user host file unless ignored. */ - if (host_status != HOST_OK && !options.ignore_user_known_hosts) { - struct stat st; - char *user_hostfile = tilde_expand_filename(SSH_USER_HOSTFILE, pw->pw_uid); - /* Check file permissions of SSH_USER_HOSTFILE, - auth_rsa() did already check pw->pw_dir, but there is a race XXX */ - if (options.strict_modes && - (stat(user_hostfile, &st) == 0) && - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || - (st.st_mode & 022) != 0)) { - log("Rhosts RSA authentication refused for %.100s: bad owner or modes for %.200s", - pw->pw_name, user_hostfile); - } else { - /* XXX race between stat and the following open() */ - temporarily_use_uid(pw->pw_uid); - host_status = check_host_in_hostfile(user_hostfile, canonical_hostname, - client_host_key_e, client_host_key_n, ke, kn); - restore_uid(); - } - xfree(user_hostfile); - } - BN_free(ke); - BN_free(kn); - - if (host_status != HOST_OK) { - /* The host key was not found. */ - debug("Rhosts with RSA host authentication denied: unknown or invalid host key"); - packet_send_debug("Your host key cannot be verified: unknown or invalid host key."); - return 0; - } - - /* A matching host key was found and is known. */ - - /* Perform the challenge-response dialog with the client for the host key. */ - if (!auth_rsa_challenge_dialog(client_host_key_e, client_host_key_n)) - { - log("Client on %.800s failed to respond correctly to host authentication.", - canonical_hostname); - return 0; - } - - /* We have authenticated the user using .rhosts or /etc/hosts.equiv, and - the host using RSA. We accept the authentication. */ - - log("Rhosts with RSA host authentication accepted for %.100s, %.100s on %.700s.", - pw->pw_name, client_user, canonical_hostname); - packet_send_debug("Rhosts with RSA host authentication accepted."); - return 1; + extern ServerOptions options; + const char *canonical_hostname; + HostStatus host_status; + BIGNUM *ke, *kn; + + debug("Trying rhosts with RSA host authentication for %.100s", client_user); + + /* Check if we would accept it using rhosts authentication. */ + if (!auth_rhosts(pw, client_user)) + return 0; + + canonical_hostname = get_canonical_hostname(); + + debug("Rhosts RSA authentication: canonical host %.900s", + canonical_hostname); + + /* Check if we know the host and its host key. */ + ke = BN_new(); + kn = BN_new(); + host_status = check_host_in_hostfile(SSH_SYSTEM_HOSTFILE, canonical_hostname, + client_host_key_e, client_host_key_n, + ke, kn); + + /* Check user host file unless ignored. */ + if (host_status != HOST_OK && !options.ignore_user_known_hosts) { + struct stat st; + char *user_hostfile = tilde_expand_filename(SSH_USER_HOSTFILE, pw->pw_uid); + /* Check file permissions of SSH_USER_HOSTFILE, auth_rsa() + did already check pw->pw_dir, but there is a race XXX */ + if (options.strict_modes && + (stat(user_hostfile, &st) == 0) && + ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0)) { + log("Rhosts RSA authentication refused for %.100s: bad owner or modes for %.200s", + pw->pw_name, user_hostfile); + } else { + /* XXX race between stat and the following open() */ + temporarily_use_uid(pw->pw_uid); + host_status = check_host_in_hostfile(user_hostfile, canonical_hostname, + client_host_key_e, client_host_key_n, + ke, kn); + restore_uid(); + } + xfree(user_hostfile); + } + BN_free(ke); + BN_free(kn); + + if (host_status != HOST_OK) { + debug("Rhosts with RSA host authentication denied: unknown or invalid host key"); + packet_send_debug("Your host key cannot be verified: unknown or invalid host key."); + return 0; + } + /* A matching host key was found and is known. */ + + /* Perform the challenge-response dialog with the client for the host key. */ + if (!auth_rsa_challenge_dialog(client_host_key_e, client_host_key_n)) { + log("Client on %.800s failed to respond correctly to host authentication.", + canonical_hostname); + return 0; + } + /* We have authenticated the user using .rhosts or /etc/hosts.equiv, and the host using RSA. + We accept the authentication. */ + + verbose("Rhosts with RSA host authentication accepted for %.100s, %.100s on %.700s.", + pw->pw_name, client_user, canonical_hostname); + packet_send_debug("Rhosts with RSA host authentication accepted."); + return 1; } diff --git a/auth-rhosts.c b/auth-rhosts.c index 8149be6b..e6071208 100644 --- a/auth-rhosts.c +++ b/auth-rhosts.c @@ -1,19 +1,19 @@ /* - -auth-rhosts.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Fri Mar 17 05:12:18 1995 ylo - -Rhosts authentication. This file contains code to check whether to admit -the login based on rhosts authentication. This file also processes -/etc/hosts.equiv. - -*/ + * + * auth-rhosts.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Fri Mar 17 05:12:18 1995 ylo + * + * Rhosts authentication. This file contains code to check whether to admit + * the login based on rhosts authentication. This file also processes + * /etc/hosts.equiv. + * + */ #include "includes.h" RCSID("$Id$"); @@ -28,256 +28,228 @@ RCSID("$Id$"); /etc/hosts.equiv). This returns true if authentication can be granted based on the file, and returns zero otherwise. */ -int check_rhosts_file(const char *filename, const char *hostname, - const char *ipaddr, const char *client_user, - const char *server_user) +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. */ - - /* Open the .rhosts file. */ - f = fopen(filename, "r"); - if (!f) - return 0; /* Cannot read the .rhosts - deny access. */ - - /* Go through the file, checking every entry. */ - 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, "%s %s %s", hostbuf, userbuf, dummy)) - { - case 0: - packet_send_debug("Found empty line in %.100s.", filename); - continue; /* Empty line? */ - case 1: - /* Host name only. */ - strlcpy(userbuf, server_user, sizeof(userbuf)); - break; - case 2: - /* Got both host and user name. */ - break; - case 3: - packet_send_debug("Found garbage in %.100s.", filename); - continue; /* Extra garbage */ - default: - continue; /* Weird... */ - } - - 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 '-'. */ - packet_send_debug("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; + FILE *f; + char buf[1024]; /* Must not be larger than host, user, dummy below. */ + + /* Open the .rhosts file, deny if unreadable */ + f = fopen(filename, "r"); + if (!f) + return 0; + + /* Go through the file, checking every entry. */ + 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, "%s %s %s", hostbuf, userbuf, dummy)) { + case 0: + packet_send_debug("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: + packet_send_debug("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 '-'. */ + packet_send_debug("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) { + packet_send_debug("Matched negative entry in %.100s.", + filename); + return 0; + } + /* Accept authentication. */ + return 1; } - 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) - { - packet_send_debug("Matched negative entry in %.100s.", - filename); - return 0; - } - - /* Accept authentication. */ - return 1; - } - - /* Authentication using this file denied. */ - fclose(f); - return 0; + /* Authentication using this file denied. */ + fclose(f); + return 0; } -/* Tries to authenticate the user using the .shosts or .rhosts file. +/* Tries to authenticate the user using the .shosts or .rhosts file. Returns true if authentication succeeds. If ignore_rhosts is true, only /etc/hosts.equiv will be considered (.rhosts and .shosts are ignored). */ -int auth_rhosts(struct passwd *pw, const char *client_user) +int +auth_rhosts(struct passwd *pw, const char *client_user) { - extern ServerOptions options; - char buf[1024]; - const char *hostname, *ipaddr; - struct stat st; - static const char *rhosts_files[] = { ".shosts", ".rhosts", NULL }; - unsigned int rhosts_file_index; - - /* Quick check: if the user has no .shosts or .rhosts files, return failure - immediately without doing costly lookups from name servers. */ - /* Switch to the user's uid. */ - temporarily_use_uid(pw->pw_uid); - for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; - rhosts_file_index++) - { - /* Check users .rhosts or .shosts. */ - snprintf(buf, sizeof buf, "%.500s/%.100s", - pw->pw_dir, rhosts_files[rhosts_file_index]); - if (stat(buf, &st) >= 0) - break; - } - /* Switch back to privileged uid. */ - restore_uid(); - - if (!rhosts_files[rhosts_file_index] && stat("/etc/hosts.equiv", &st) < 0 && - stat(SSH_HOSTS_EQUIV, &st) < 0) - return 0; /* The user has no .shosts or .rhosts file and there are no - system-wide files. */ - - /* Get the name, address, and port of the remote host. */ - hostname = get_canonical_hostname(); - ipaddr = get_remote_ipaddr(); - - /* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */ - if (pw->pw_uid != 0) - { - if (check_rhosts_file("/etc/hosts.equiv", hostname, ipaddr, client_user, - pw->pw_name)) - { - packet_send_debug("Accepted for %.100s [%.100s] by /etc/hosts.equiv.", - hostname, ipaddr); - return 1; + extern ServerOptions options; + char buf[1024]; + const char *hostname, *ipaddr; + struct stat st; + static const char *rhosts_files[] = {".shosts", ".rhosts", NULL}; + unsigned int rhosts_file_index; + + /* Quick check: if the user has no .shosts or .rhosts files, + return failure immediately without doing costly lookups from + name servers. */ + /* Switch to the user's uid. */ + temporarily_use_uid(pw->pw_uid); + for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; + rhosts_file_index++) { + /* Check users .rhosts or .shosts. */ + snprintf(buf, sizeof buf, "%.500s/%.100s", + pw->pw_dir, rhosts_files[rhosts_file_index]); + if (stat(buf, &st) >= 0) + break; } - if (check_rhosts_file(SSH_HOSTS_EQUIV, hostname, ipaddr, client_user, - pw->pw_name)) - { - packet_send_debug("Accepted for %.100s [%.100s] by %.100s.", - hostname, ipaddr, SSH_HOSTS_EQUIV); - return 1; + /* Switch back to privileged uid. */ + restore_uid(); + + /* Deny if The user has no .shosts or .rhosts file and there are no system-wide files. */ + if (!rhosts_files[rhosts_file_index] && + stat("/etc/hosts.equiv", &st) < 0 && + stat(SSH_HOSTS_EQUIV, &st) < 0) + return 0; + + /* Get the name, address, and port of the remote host. */ + hostname = get_canonical_hostname(); + ipaddr = get_remote_ipaddr(); + + /* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */ + if (pw->pw_uid != 0) { + if (check_rhosts_file("/etc/hosts.equiv", hostname, ipaddr, client_user, + pw->pw_name)) { + packet_send_debug("Accepted for %.100s [%.100s] by /etc/hosts.equiv.", + hostname, ipaddr); + return 1; + } + if (check_rhosts_file(SSH_HOSTS_EQUIV, hostname, ipaddr, client_user, + pw->pw_name)) { + packet_send_debug("Accepted for %.100s [%.100s] by %.100s.", + hostname, ipaddr, SSH_HOSTS_EQUIV); + return 1; + } } - } - - /* Check that the home directory is owned by root or the user, and is not - group or world writable. */ - if (stat(pw->pw_dir, &st) < 0) - { - log("Rhosts authentication refused for %.100s: no home directory %.200s", - pw->pw_name, pw->pw_dir); - packet_send_debug("Rhosts authentication refused for %.100: no home directory %.200s", - pw->pw_name, pw->pw_dir); - return 0; - } - if (options.strict_modes && - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || - (st.st_mode & 022) != 0)) - { - log("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", - pw->pw_name); - packet_send_debug("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", - pw->pw_name); - return 0; - } - - /* Check all .rhosts files (currently .shosts and .rhosts). */ - /* Temporarily use the user's uid. */ - temporarily_use_uid(pw->pw_uid); - for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; - rhosts_file_index++) - { - /* Check users .rhosts or .shosts. */ - snprintf(buf, sizeof buf, "%.500s/%.100s", - pw->pw_dir, rhosts_files[rhosts_file_index]); - if (stat(buf, &st) < 0) - continue; /* No such file. */ - - /* Make sure that the file is either owned by the user or by root, - and make sure it is not writable by anyone but the owner. This is - to help avoid novices accidentally allowing access to their account - by anyone. */ - if (options.strict_modes && - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || - (st.st_mode & 022) != 0)) - { - log("Rhosts authentication refused for %.100s: bad modes for %.200s", - pw->pw_name, buf); - packet_send_debug("Bad file modes for %.200s", buf); - continue; + /* Check that the home directory is owned by root or the user, and + is not group or world writable. */ + if (stat(pw->pw_dir, &st) < 0) { + log("Rhosts authentication refused for %.100s: no home directory %.200s", + pw->pw_name, pw->pw_dir); + packet_send_debug("Rhosts authentication refused for %.100: no home directory %.200s", + pw->pw_name, pw->pw_dir); + return 0; } - - /* Check if we have been configured to ignore .rhosts and .shosts - files. */ - if (options.ignore_rhosts) - { - packet_send_debug("Server has been configured to ignore %.100s.", - rhosts_files[rhosts_file_index]); - continue; + if (options.strict_modes && + ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0)) { + log("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", + pw->pw_name); + packet_send_debug("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", + pw->pw_name); + return 0; } - - /* Check if authentication is permitted by the file. */ - if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name)) - { - packet_send_debug("Accepted by %.100s.", - rhosts_files[rhosts_file_index]); - /* Restore the privileged uid. */ - restore_uid(); - return 1; + /* Temporarily use the user's uid. */ + temporarily_use_uid(pw->pw_uid); + + /* Check all .rhosts files (currently .shosts and .rhosts). */ + for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; + rhosts_file_index++) { + /* Check users .rhosts or .shosts. */ + snprintf(buf, sizeof buf, "%.500s/%.100s", + pw->pw_dir, rhosts_files[rhosts_file_index]); + if (stat(buf, &st) < 0) + continue; + + /* Make sure that the file is either owned by the user or + by root, and make sure it is not writable by anyone but + the owner. This is to help avoid novices accidentally + allowing access to their account by anyone. */ + if (options.strict_modes && + ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0)) { + log("Rhosts authentication refused for %.100s: bad modes for %.200s", + pw->pw_name, buf); + packet_send_debug("Bad file modes for %.200s", buf); + continue; + } + /* Check if we have been configured to ignore .rhosts and .shosts files. */ + if (options.ignore_rhosts) { + packet_send_debug("Server has been configured to ignore %.100s.", + rhosts_files[rhosts_file_index]); + continue; + } + /* Check if authentication is permitted by the file. */ + if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name)) { + packet_send_debug("Accepted by %.100s.", + rhosts_files[rhosts_file_index]); + /* Restore the privileged uid. */ + restore_uid(); + return 1; + } } - } - /* Rhosts authentication denied. */ - /* Restore the privileged uid. */ - restore_uid(); - return 0; + /* Restore the privileged uid. */ + restore_uid(); + return 0; } diff --git a/auth-rsa.c b/auth-rsa.c index 8a8eca98..d7af01f2 100644 --- a/auth-rsa.c +++ b/auth-rsa.c @@ -1,19 +1,19 @@ /* - -auth-rsa.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Mon Mar 27 01:46:52 1995 ylo - -RSA-based authentication. This code determines whether to admit a login -based on RSA authentication. This file also contains functions to check -validity of the host key. - -*/ + * + * auth-rsa.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Mon Mar 27 01:46:52 1995 ylo + * + * RSA-based authentication. This code determines whether to admit a login + * based on RSA authentication. This file also contains functions to check + * validity of the host key. + * + */ #include "includes.h" RCSID("$Id$"); @@ -50,7 +50,7 @@ extern unsigned char session_id[16]; /* The .ssh/authorized_keys file contains public keys, one per line, in the following format: options bits e n comment - where bits, e and n are decimal numbers, + where bits, e and n are decimal numbers, and comment is any string of characters up to newline. The maximum length of a line is 8000 characters. See the documentation for a description of the options. @@ -63,71 +63,69 @@ extern unsigned char session_id[16]; int auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n) { - BIGNUM *challenge, *encrypted_challenge, *aux; - RSA *pk; - BN_CTX *ctx = BN_CTX_new(); - unsigned char buf[32], mdbuf[16], response[16]; - MD5_CTX md; - unsigned int i; - int plen, len; - - encrypted_challenge = BN_new(); - challenge = BN_new(); - aux = BN_new(); - - /* Generate a random challenge. */ - BN_rand(challenge, 256, 0, 0); - BN_mod(challenge, challenge, n, ctx); - - /* Create the public key data structure. */ - pk = RSA_new(); - pk->e = BN_new(); - BN_copy(pk->e, e); - pk->n = BN_new(); - BN_copy(pk->n, n); - - /* Encrypt the challenge with the public key. */ - rsa_public_encrypt(encrypted_challenge, challenge, pk); - RSA_free(pk); - - /* Send the encrypted challenge to the client. */ - packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); - packet_put_bignum(encrypted_challenge); - packet_send(); - packet_write_wait(); - - /* The response is MD5 of decrypted challenge plus session id. */ - len = BN_num_bytes(challenge); - if (len <= 0 || len > 32) - fatal("auth_rsa_challenge_dialog: bad challenge length %d", len); - memset(buf, 0, 32); - BN_bn2bin(challenge, buf + 32 - len); - MD5_Init(&md); - MD5_Update(&md, buf, 32); - MD5_Update(&md, session_id, 16); - MD5_Final(mdbuf, &md); - - /* We will no longer need these. */ - BN_clear_free(encrypted_challenge); - BN_clear_free(challenge); - BN_clear_free(aux); - BN_CTX_free(ctx); - - /* Wait for a response. */ - packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE); - packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE); - for (i = 0; i < 16; i++) - response[i] = packet_get_char(); - - /* Verify that the response is the original challenge. */ - if (memcmp(response, mdbuf, 16) != 0) - { - /* Wrong answer. */ - return 0; - } - - /* Correct answer. */ - return 1; + BIGNUM *challenge, *encrypted_challenge, *aux; + RSA *pk; + BN_CTX *ctx = BN_CTX_new(); + unsigned char buf[32], mdbuf[16], response[16]; + MD5_CTX md; + unsigned int i; + int plen, len; + + encrypted_challenge = BN_new(); + challenge = BN_new(); + aux = BN_new(); + + /* Generate a random challenge. */ + BN_rand(challenge, 256, 0, 0); + BN_mod(challenge, challenge, n, ctx); + + /* Create the public key data structure. */ + pk = RSA_new(); + pk->e = BN_new(); + BN_copy(pk->e, e); + pk->n = BN_new(); + BN_copy(pk->n, n); + + /* Encrypt the challenge with the public key. */ + rsa_public_encrypt(encrypted_challenge, challenge, pk); + RSA_free(pk); + + /* Send the encrypted challenge to the client. */ + packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); + packet_put_bignum(encrypted_challenge); + packet_send(); + packet_write_wait(); + + /* The response is MD5 of decrypted challenge plus session id. */ + len = BN_num_bytes(challenge); + if (len <= 0 || len > 32) + fatal("auth_rsa_challenge_dialog: bad challenge length %d", len); + memset(buf, 0, 32); + BN_bn2bin(challenge, buf + 32 - len); + MD5_Init(&md); + MD5_Update(&md, buf, 32); + MD5_Update(&md, session_id, 16); + MD5_Final(mdbuf, &md); + + /* We will no longer need these. */ + BN_clear_free(encrypted_challenge); + BN_clear_free(challenge); + BN_clear_free(aux); + BN_CTX_free(ctx); + + /* Wait for a response. */ + packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE); + packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE); + for (i = 0; i < 16; i++) + response[i] = packet_get_char(); + + /* Verify that the response is the original challenge. */ + if (memcmp(response, mdbuf, 16) != 0) { + /* Wrong answer. */ + return 0; + } + /* Correct answer. */ + return 1; } /* Performs the RSA authentication dialog with the client. This returns @@ -137,357 +135,324 @@ auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n) int auth_rsa(struct passwd *pw, BIGNUM *client_n) { - extern ServerOptions options; - char line[8192], file[1024]; - int authenticated; - unsigned int bits; - FILE *f; - unsigned long linenum = 0; - struct stat st; - BIGNUM *e, *n; - - /* Temporarily use the user's uid. */ - temporarily_use_uid(pw->pw_uid); - - /* The authorized keys. */ - snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, - SSH_USER_PERMITTED_KEYS); - - /* Fail quietly if file does not exist */ - if (stat(file, &st) < 0) - { - /* Restore the privileged uid. */ - restore_uid(); - return 0; - } - - /* Open the file containing the authorized keys. */ - f = fopen(file, "r"); - if (!f) - { - /* Restore the privileged uid. */ - restore_uid(); - packet_send_debug("Could not open %.900s for reading.", file); - packet_send_debug("If your home is on an NFS volume, it may need to be world-readable."); - return 0; - } - - if (options.strict_modes) { - int fail=0; - char buf[1024]; - /* Check open file in order to avoid open/stat races */ - if (fstat(fileno(f), &st) < 0 || - (st.st_uid != 0 && st.st_uid != pw->pw_uid) || - (st.st_mode & 022) != 0) { - snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " - "bad ownership or modes for '%s'.", pw->pw_name, file); - fail=1; - }else{ - /* Check path to SSH_USER_PERMITTED_KEYS */ - int i; - static const char *check[] = { - "", SSH_USER_DIR, NULL - }; - for (i=0; check[i]; i++) { - snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]); - if (stat(line, &st) < 0 || - (st.st_uid != 0 && st.st_uid != pw->pw_uid) || - (st.st_mode & 022) != 0) { - snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " - "bad ownership or modes for '%s'.", pw->pw_name, line); - fail=1; - break; - } - } - } - if (fail) { - log(buf); - packet_send_debug(buf); - restore_uid(); - return 0; - } - } - - /* Flag indicating whether authentication has succeeded. */ - authenticated = 0; - - /* Initialize mp-int variables. */ - e = BN_new(); - n = BN_new(); - - /* Go though the accepted keys, looking for the current key. If found, - perform a challenge-response dialog to verify that the user really has - the corresponding private key. */ - while (fgets(line, sizeof(line), f)) - { - char *cp; - char *options; - - linenum++; - - /* Skip leading whitespace. */ - for (cp = line; *cp == ' ' || *cp == '\t'; cp++) - ; - - /* Skip empty and comment lines. */ - if (!*cp || *cp == '\n' || *cp == '#') - continue; - - /* Check if there are options for this key, and if so, save their - starting address and skip the option part for now. If there are no - options, set the starting address to NULL. */ - if (*cp < '0' || *cp > '9') - { - int quoted = 0; - options = cp; - for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) - { - if (*cp == '\\' && cp[1] == '"') - cp++; /* Skip both */ - else - if (*cp == '"') - quoted = !quoted; - } + extern ServerOptions options; + char line[8192], file[1024]; + int authenticated; + unsigned int bits; + FILE *f; + unsigned long linenum = 0; + struct stat st; + BIGNUM *e, *n; + + /* Temporarily use the user's uid. */ + temporarily_use_uid(pw->pw_uid); + + /* The authorized keys. */ + snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, + SSH_USER_PERMITTED_KEYS); + + /* Fail quietly if file does not exist */ + if (stat(file, &st) < 0) { + /* Restore the privileged uid. */ + restore_uid(); + return 0; } - else - options = NULL; - - /* Parse the key from the line. */ - if (!auth_rsa_read_key(&cp, &bits, e, n)) - { - debug("%.100s, line %lu: bad key syntax", - SSH_USER_PERMITTED_KEYS, linenum); - packet_send_debug("%.100s, line %lu: bad key syntax", - SSH_USER_PERMITTED_KEYS, linenum); - continue; + /* Open the file containing the authorized keys. */ + f = fopen(file, "r"); + if (!f) { + /* Restore the privileged uid. */ + restore_uid(); + packet_send_debug("Could not open %.900s for reading.", file); + packet_send_debug("If your home is on an NFS volume, it may need to be world-readable."); + return 0; } - /* cp now points to the comment part. */ - - /* check the real bits */ - if (bits != BN_num_bits(n)) - error("Warning: error in %s, line %ld: keysize mismatch: " - "actual size %d vs. announced %d.", - file, linenum, BN_num_bits(n), bits); - - /* Check if the we have found the desired key (identified by its - modulus). */ - if (BN_cmp(n, client_n) != 0) - continue; /* Wrong key. */ - - /* We have found the desired key. */ - - /* Perform the challenge-response dialog for this key. */ - if (!auth_rsa_challenge_dialog(e, n)) - { - /* Wrong response. */ - log("Wrong response to RSA authentication challenge."); - packet_send_debug("Wrong response to RSA authentication challenge."); - continue; - } - - /* Correct response. The client has been successfully authenticated. - Note that we have not yet processed the options; this will be reset - if the options cause the authentication to be rejected. */ - authenticated = 1; - - /* RSA part of authentication was accepted. Now process the options. */ - if (options) - { - while (*options && *options != ' ' && *options != '\t') - { - cp = "no-port-forwarding"; - if (strncmp(options, cp, strlen(cp)) == 0) - { - packet_send_debug("Port forwarding disabled."); - no_port_forwarding_flag = 1; - options += strlen(cp); - goto next_option; - } - cp = "no-agent-forwarding"; - if (strncmp(options, cp, strlen(cp)) == 0) - { - packet_send_debug("Agent forwarding disabled."); - no_agent_forwarding_flag = 1; - options += strlen(cp); - goto next_option; - } - cp = "no-X11-forwarding"; - if (strncmp(options, cp, strlen(cp)) == 0) - { - packet_send_debug("X11 forwarding disabled."); - no_x11_forwarding_flag = 1; - options += strlen(cp); - goto next_option; + if (options.strict_modes) { + int fail = 0; + char buf[1024]; + /* Check open file in order to avoid open/stat races */ + if (fstat(fileno(f), &st) < 0 || + (st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0) { + snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " + "bad ownership or modes for '%s'.", pw->pw_name, file); + fail = 1; + } else { + /* Check path to SSH_USER_PERMITTED_KEYS */ + int i; + static const char *check[] = { + "", SSH_USER_DIR, NULL + }; + for (i = 0; check[i]; i++) { + snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]); + if (stat(line, &st) < 0 || + (st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0) { + snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " + "bad ownership or modes for '%s'.", pw->pw_name, line); + fail = 1; + break; + } + } } - cp = "no-pty"; - if (strncmp(options, cp, strlen(cp)) == 0) - { - packet_send_debug("Pty allocation disabled."); - no_pty_flag = 1; - options += strlen(cp); - goto next_option; + if (fail) { + log(buf); + packet_send_debug(buf); + restore_uid(); + return 0; } - cp = "command=\""; - if (strncmp(options, cp, strlen(cp)) == 0) - { - int i; - options += strlen(cp); - forced_command = xmalloc(strlen(options) + 1); - i = 0; - while (*options) - { - if (*options == '"') - break; - if (*options == '\\' && options[1] == '"') - { - options += 2; - forced_command[i++] = '"'; - continue; + } + /* Flag indicating whether authentication has succeeded. */ + authenticated = 0; + + /* Initialize mp-int variables. */ + e = BN_new(); + n = BN_new(); + + /* Go though the accepted keys, looking for the current key. If + found, perform a challenge-response dialog to verify that the + user really has the corresponding private key. */ + while (fgets(line, sizeof(line), f)) { + char *cp; + char *options; + + linenum++; + + /* Skip leading whitespace. */ + for (cp = line; *cp == ' ' || *cp == '\t'; cp++); + + /* Skip empty and comment lines. */ + if (!*cp || *cp == '\n' || *cp == '#') + continue; + + /* Check if there are options for this key, and if so, + save their starting address and skip the option part + for now. If there are no options, set the starting + address to NULL. */ + if (*cp < '0' || *cp > '9') { + int quoted = 0; + options = cp; + for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { + if (*cp == '\\' && cp[1] == '"') + cp++; /* Skip both */ + else if (*cp == '"') + quoted = !quoted; } - forced_command[i++] = *options++; - } - if (!*options) - { - debug("%.100s, line %lu: missing end quote", - SSH_USER_PERMITTED_KEYS, linenum); - packet_send_debug("%.100s, line %lu: missing end quote", - SSH_USER_PERMITTED_KEYS, linenum); - continue; - } - forced_command[i] = 0; - packet_send_debug("Forced command: %.900s", forced_command); - options++; - goto next_option; + } else + options = NULL; + + /* Parse the key from the line. */ + if (!auth_rsa_read_key(&cp, &bits, e, n)) { + debug("%.100s, line %lu: bad key syntax", + SSH_USER_PERMITTED_KEYS, linenum); + packet_send_debug("%.100s, line %lu: bad key syntax", + SSH_USER_PERMITTED_KEYS, linenum); + continue; } - cp = "environment=\""; - if (strncmp(options, cp, strlen(cp)) == 0) - { - int i; - char *s; - struct envstring *new_envstring; - options += strlen(cp); - s = xmalloc(strlen(options) + 1); - i = 0; - while (*options) - { - if (*options == '"') - break; - if (*options == '\\' && options[1] == '"') - { - options += 2; - s[i++] = '"'; - continue; - } - s[i++] = *options++; - } - if (!*options) - { - debug("%.100s, line %lu: missing end quote", - SSH_USER_PERMITTED_KEYS, linenum); - packet_send_debug("%.100s, line %lu: missing end quote", - SSH_USER_PERMITTED_KEYS, linenum); - continue; - } - s[i] = 0; - packet_send_debug("Adding to environment: %.900s", s); - debug("Adding to environment: %.900s", s); - options++; - new_envstring = xmalloc(sizeof(struct envstring)); - new_envstring->s = s; - new_envstring->next = custom_environment; - custom_environment = new_envstring; - goto next_option; + /* cp now points to the comment part. */ + + /* check the real bits */ + if (bits != BN_num_bits(n)) + error("Warning: error in %s, line %ld: keysize mismatch: " + "actual size %d vs. announced %d.", + file, linenum, BN_num_bits(n), bits); + + /* Check if the we have found the desired key (identified by its modulus). */ + if (BN_cmp(n, client_n) != 0) + continue; /* Wrong key. */ + + /* We have found the desired key. */ + + /* Perform the challenge-response dialog for this key. */ + if (!auth_rsa_challenge_dialog(e, n)) { + /* Wrong response. */ + verbose("Wrong response to RSA authentication challenge."); + packet_send_debug("Wrong response to RSA authentication challenge."); + continue; } - cp = "from=\""; - if (strncmp(options, cp, strlen(cp)) == 0) - { - char *patterns = xmalloc(strlen(options) + 1); - int i; - options += strlen(cp); - i = 0; - while (*options) - { - if (*options == '"') - break; - if (*options == '\\' && options[1] == '"') - { - options += 2; - patterns[i++] = '"'; - continue; + /* Correct response. The client has been successfully + authenticated. Note that we have not yet processed the + options; this will be reset if the options cause the + authentication to be rejected. */ + authenticated = 1; + + /* RSA part of authentication was accepted. Now process the options. */ + if (options) { + while (*options && *options != ' ' && *options != '\t') { + cp = "no-port-forwarding"; + if (strncmp(options, cp, strlen(cp)) == 0) { + packet_send_debug("Port forwarding disabled."); + no_port_forwarding_flag = 1; + options += strlen(cp); + goto next_option; + } + cp = "no-agent-forwarding"; + if (strncmp(options, cp, strlen(cp)) == 0) { + packet_send_debug("Agent forwarding disabled."); + no_agent_forwarding_flag = 1; + options += strlen(cp); + goto next_option; + } + cp = "no-X11-forwarding"; + if (strncmp(options, cp, strlen(cp)) == 0) { + packet_send_debug("X11 forwarding disabled."); + no_x11_forwarding_flag = 1; + options += strlen(cp); + goto next_option; + } + cp = "no-pty"; + if (strncmp(options, cp, strlen(cp)) == 0) { + packet_send_debug("Pty allocation disabled."); + no_pty_flag = 1; + options += strlen(cp); + goto next_option; + } + cp = "command=\""; + if (strncmp(options, cp, strlen(cp)) == 0) { + int i; + options += strlen(cp); + forced_command = xmalloc(strlen(options) + 1); + i = 0; + while (*options) { + if (*options == '"') + break; + if (*options == '\\' && options[1] == '"') { + options += 2; + forced_command[i++] = '"'; + continue; + } + forced_command[i++] = *options++; + } + if (!*options) { + debug("%.100s, line %lu: missing end quote", + SSH_USER_PERMITTED_KEYS, linenum); + packet_send_debug("%.100s, line %lu: missing end quote", + SSH_USER_PERMITTED_KEYS, linenum); + continue; + } + forced_command[i] = 0; + packet_send_debug("Forced command: %.900s", forced_command); + options++; + goto next_option; + } + cp = "environment=\""; + if (strncmp(options, cp, strlen(cp)) == 0) { + int i; + char *s; + struct envstring *new_envstring; + options += strlen(cp); + s = xmalloc(strlen(options) + 1); + i = 0; + while (*options) { + if (*options == '"') + break; + if (*options == '\\' && options[1] == '"') { + options += 2; + s[i++] = '"'; + continue; + } + s[i++] = *options++; + } + if (!*options) { + debug("%.100s, line %lu: missing end quote", + SSH_USER_PERMITTED_KEYS, linenum); + packet_send_debug("%.100s, line %lu: missing end quote", + SSH_USER_PERMITTED_KEYS, linenum); + continue; + } + s[i] = 0; + packet_send_debug("Adding to environment: %.900s", s); + debug("Adding to environment: %.900s", s); + options++; + new_envstring = xmalloc(sizeof(struct envstring)); + new_envstring->s = s; + new_envstring->next = custom_environment; + custom_environment = new_envstring; + goto next_option; + } + cp = "from=\""; + if (strncmp(options, cp, strlen(cp)) == 0) { + char *patterns = xmalloc(strlen(options) + 1); + int i; + options += strlen(cp); + i = 0; + while (*options) { + if (*options == '"') + break; + if (*options == '\\' && options[1] == '"') { + options += 2; + patterns[i++] = '"'; + continue; + } + patterns[i++] = *options++; + } + if (!*options) { + debug("%.100s, line %lu: missing end quote", + SSH_USER_PERMITTED_KEYS, linenum); + packet_send_debug("%.100s, line %lu: missing end quote", + SSH_USER_PERMITTED_KEYS, linenum); + continue; + } + patterns[i] = 0; + options++; + if (!match_hostname(get_canonical_hostname(), patterns, + strlen(patterns)) && + !match_hostname(get_remote_ipaddr(), patterns, + strlen(patterns))) { + log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).", + pw->pw_name, get_canonical_hostname(), + get_remote_ipaddr()); + packet_send_debug("Your host '%.200s' is not permitted to use this key for login.", + get_canonical_hostname()); + xfree(patterns); + authenticated = 0; + break; + } + xfree(patterns); + /* Host name matches. */ + goto next_option; + } + bad_option: + /* Unknown option. */ + log("Bad options in %.100s file, line %lu: %.50s", + SSH_USER_PERMITTED_KEYS, linenum, options); + packet_send_debug("Bad options in %.100s file, line %lu: %.50s", + SSH_USER_PERMITTED_KEYS, linenum, options); + authenticated = 0; + break; + + next_option: + /* Skip the comma, and move to the next option + (or break out if there are no more). */ + if (!*options) + fatal("Bugs in auth-rsa.c option processing."); + if (*options == ' ' || *options == '\t') + break; /* End of options. */ + if (*options != ',') + goto bad_option; + options++; + /* Process the next option. */ + continue; } - patterns[i++] = *options++; - } - if (!*options) - { - debug("%.100s, line %lu: missing end quote", - SSH_USER_PERMITTED_KEYS, linenum); - packet_send_debug("%.100s, line %lu: missing end quote", - SSH_USER_PERMITTED_KEYS, linenum); - continue; - } - patterns[i] = 0; - options++; - if (!match_hostname(get_canonical_hostname(), patterns, - strlen(patterns)) && - !match_hostname(get_remote_ipaddr(), patterns, - strlen(patterns))) - { - log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).", - pw->pw_name, get_canonical_hostname(), - get_remote_ipaddr()); - packet_send_debug("Your host '%.200s' is not permitted to use this key for login.", - get_canonical_hostname()); - xfree(patterns); - authenticated = 0; - break; - } - xfree(patterns); - /* Host name matches. */ - goto next_option; } - bad_option: - /* Unknown option. */ - log("Bad options in %.100s file, line %lu: %.50s", - SSH_USER_PERMITTED_KEYS, linenum, options); - packet_send_debug("Bad options in %.100s file, line %lu: %.50s", - SSH_USER_PERMITTED_KEYS, linenum, options); - authenticated = 0; - break; - - next_option: - /* Skip the comma, and move to the next option (or break out - if there are no more). */ - if (!*options) - fatal("Bugs in auth-rsa.c option processing."); - if (*options == ' ' || *options == '\t') - break; /* End of options. */ - if (*options != ',') - goto bad_option; - options++; - /* Process the next option. */ - continue; - } + /* Break out of the loop if authentication was successful; + otherwise continue searching. */ + if (authenticated) + break; } - /* Break out of the loop if authentication was successful; otherwise - continue searching. */ - if (authenticated) - break; - } + /* Restore the privileged uid. */ + restore_uid(); - /* Restore the privileged uid. */ - restore_uid(); + /* Close the file. */ + fclose(f); - /* Close the file. */ - fclose(f); - - /* Clear any mp-int variables. */ - BN_clear_free(n); - BN_clear_free(e); + /* Clear any mp-int variables. */ + BN_clear_free(n); + BN_clear_free(e); - if (authenticated) - packet_send_debug("RSA authentication accepted."); + if (authenticated) + packet_send_debug("RSA authentication accepted."); - /* Return authentication result. */ - return authenticated; + /* Return authentication result. */ + return authenticated; } diff --git a/auth-skey.c b/auth-skey.c index 91bdda8f..d54f3d55 100644 --- a/auth-skey.c +++ b/auth-skey.c @@ -1,6 +1,7 @@ +#include "includes.h" + #ifdef SKEY -#include "includes.h" RCSID("$Id$"); #include "ssh.h" @@ -32,6 +33,7 @@ hash_collapse(s) return i; } + char * skey_fake_keyinfo(char *username) { @@ -150,4 +152,4 @@ skey_fake_keyinfo(char *username) return skeyprompt; } -#endif SKEY +#endif /* SKEY */ diff --git a/authfd.c b/authfd.c index f5851d28..040bfe4d 100644 --- a/authfd.c +++ b/authfd.c @@ -1,17 +1,17 @@ /* - -authfd.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Wed Mar 29 01:30:28 1995 ylo - -Functions for connecting the local authentication agent. - -*/ + * + * authfd.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Wed Mar 29 01:30:28 1995 ylo + * + * Functions for connecting the local authentication agent. + * + */ #include "includes.h" RCSID("$Id$"); @@ -36,45 +36,42 @@ RCSID("$Id$"); int ssh_get_authentication_socket() { - const char *authsocket; - int sock; - struct sockaddr_un sunaddr; - - authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); - if (!authsocket) - return -1; - - sunaddr.sun_family = AF_UNIX; - strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); - - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) - return -1; - - /* close on exec */ - if (fcntl(sock, F_SETFD, 1) == -1) - { - close(sock); - return -1; - } - - if (connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) - { - close(sock); - return -1; - } - - return sock; + const char *authsocket; + int sock; + struct sockaddr_un sunaddr; + + authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); + if (!authsocket) + return -1; + + sunaddr.sun_family = AF_UNIX; + strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + return -1; + + /* close on exec */ + if (fcntl(sock, F_SETFD, 1) == -1) { + close(sock); + return -1; + } + if (connect(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) { + close(sock); + return -1; + } + return sock; } /* Closes the agent socket if it should be closed (depends on how it was - obtained). The argument must have been returned by + obtained). The argument must have been returned by ssh_get_authentication_socket(). */ -void ssh_close_authentication_socket(int sock) +void +ssh_close_authentication_socket(int sock) { - if (getenv(SSH_AUTHSOCKET_ENV_NAME)) - close(sock); + if (getenv(SSH_AUTHSOCKET_ENV_NAME)) + close(sock); } /* Opens and connects a private socket for communication with the @@ -83,38 +80,39 @@ void ssh_close_authentication_socket(int sock) Returns NULL if an error occurred and the connection could not be opened. */ -AuthenticationConnection *ssh_get_authentication_connection() +AuthenticationConnection * +ssh_get_authentication_connection() { - AuthenticationConnection *auth; - int sock; - - sock = ssh_get_authentication_socket(); - - /* Fail if we couldn't obtain a connection. This happens if we exited - due to a timeout. */ - if (sock < 0) - return NULL; - - /* Applocate the connection structure and initialize it. */ - auth = xmalloc(sizeof(*auth)); - auth->fd = sock; - buffer_init(&auth->packet); - buffer_init(&auth->identities); - auth->howmany = 0; - - return auth; + AuthenticationConnection *auth; + int sock; + + sock = ssh_get_authentication_socket(); + + /* Fail if we couldn't obtain a connection. This happens if we + exited due to a timeout. */ + if (sock < 0) + return NULL; + + /* Applocate the connection structure and initialize it. */ + auth = xmalloc(sizeof(*auth)); + auth->fd = sock; + buffer_init(&auth->packet); + buffer_init(&auth->identities); + auth->howmany = 0; + + return auth; } /* Closes the connection to the authentication agent and frees any associated memory. */ -void ssh_close_authentication_connection(AuthenticationConnection *ac) +void +ssh_close_authentication_connection(AuthenticationConnection *ac) { - buffer_free(&ac->packet); - buffer_free(&ac->identities); - close(ac->fd); - /* Free the connection data structure. */ - xfree(ac); + buffer_free(&ac->packet); + buffer_free(&ac->identities); + close(ac->fd); + xfree(ac); } /* Returns the first authentication identity held by the agent. @@ -126,67 +124,62 @@ int ssh_get_first_identity(AuthenticationConnection *auth, BIGNUM *e, BIGNUM *n, char **comment) { - unsigned char msg[8192]; - int len, l; - - /* Send a message to the agent requesting for a list of the identities - it can represent. */ - msg[0] = 0; - msg[1] = 0; - msg[2] = 0; - msg[3] = 1; - msg[4] = SSH_AGENTC_REQUEST_RSA_IDENTITIES; - if (write(auth->fd, msg, 5) != 5) - { - error("write auth->fd: %.100s", strerror(errno)); - return 0; - } - - /* Read the length of the response. XXX implement timeouts here. */ - len = 4; - while (len > 0) - { - l = read(auth->fd, msg + 4 - len, len); - if (l <= 0) - { - error("read auth->fd: %.100s", strerror(errno)); - return 0; + unsigned char msg[8192]; + int len, l; + + /* Send a message to the agent requesting for a list of the + identities it can represent. */ + msg[0] = 0; + msg[1] = 0; + msg[2] = 0; + msg[3] = 1; + msg[4] = SSH_AGENTC_REQUEST_RSA_IDENTITIES; + if (write(auth->fd, msg, 5) != 5) { + error("write auth->fd: %.100s", strerror(errno)); + return 0; + } + /* Read the length of the response. XXX implement timeouts here. */ + len = 4; + while (len > 0) { + l = read(auth->fd, msg + 4 - len, len); + if (l <= 0) { + error("read auth->fd: %.100s", strerror(errno)); + return 0; + } + len -= l; + } + + /* Extract the length, and check it for sanity. (We cannot trust + authentication agents). */ + len = GET_32BIT(msg); + if (len < 1 || len > 256 * 1024) + fatal("Authentication reply message too long: %d\n", len); + + /* Read the packet itself. */ + buffer_clear(&auth->identities); + while (len > 0) { + l = len; + if (l > sizeof(msg)) + l = sizeof(msg); + l = read(auth->fd, msg, l); + if (l <= 0) + fatal("Incomplete authentication reply."); + buffer_append(&auth->identities, (char *) msg, l); + len -= l; } - len -= l; - } - - /* Extract the length, and check it for sanity. (We cannot trust - authentication agents). */ - len = GET_32BIT(msg); - if (len < 1 || len > 256*1024) - fatal("Authentication reply message too long: %d\n", len); - - /* Read the packet itself. */ - buffer_clear(&auth->identities); - while (len > 0) - { - l = len; - if (l > sizeof(msg)) - l = sizeof(msg); - l = read(auth->fd, msg, l); - if (l <= 0) - fatal("Incomplete authentication reply."); - buffer_append(&auth->identities, (char *)msg, l); - len -= l; - } - - /* Get message type, and verify that we got a proper answer. */ - buffer_get(&auth->identities, (char *)msg, 1); - if (msg[0] != SSH_AGENT_RSA_IDENTITIES_ANSWER) - fatal("Bad authentication reply message type: %d", msg[0]); - - /* Get the number of entries in the response and check it for sanity. */ - auth->howmany = buffer_get_int(&auth->identities); - if (auth->howmany > 1024) - fatal("Too many identities in authentication reply: %d\n", auth->howmany); - - /* Return the first entry (if any). */ - return ssh_get_next_identity(auth, e, n, comment); + + /* Get message type, and verify that we got a proper answer. */ + buffer_get(&auth->identities, (char *) msg, 1); + if (msg[0] != SSH_AGENT_RSA_IDENTITIES_ANSWER) + fatal("Bad authentication reply message type: %d", msg[0]); + + /* Get the number of entries in the response and check it for sanity. */ + auth->howmany = buffer_get_int(&auth->identities); + if (auth->howmany > 1024) + fatal("Too many identities in authentication reply: %d\n", auth->howmany); + + /* Return the first entry (if any). */ + return ssh_get_next_identity(auth, e, n, comment); } /* Returns the next authentication identity for the agent. Other functions @@ -198,27 +191,27 @@ int ssh_get_next_identity(AuthenticationConnection *auth, BIGNUM *e, BIGNUM *n, char **comment) { - unsigned int bits; + unsigned int bits; - /* Return failure if no more entries. */ - if (auth->howmany <= 0) - return 0; + /* Return failure if no more entries. */ + if (auth->howmany <= 0) + return 0; - /* Get the next entry from the packet. These will abort with a fatal - error if the packet is too short or contains corrupt data. */ - bits = buffer_get_int(&auth->identities); - buffer_get_bignum(&auth->identities, e); - buffer_get_bignum(&auth->identities, n); - *comment = buffer_get_string(&auth->identities, NULL); + /* Get the next entry from the packet. These will abort with a + fatal error if the packet is too short or contains corrupt data. */ + bits = buffer_get_int(&auth->identities); + buffer_get_bignum(&auth->identities, e); + buffer_get_bignum(&auth->identities, n); + *comment = buffer_get_string(&auth->identities, NULL); - if (bits != BN_num_bits(n)) - error("Warning: keysize mismatch: actual %d, announced %u", - BN_num_bits(n), bits); + if (bits != BN_num_bits(n)) + error("Warning: keysize mismatch: actual %d, announced %u", + BN_num_bits(n), bits); - /* Decrement the number of remaining entries. */ - auth->howmany--; + /* Decrement the number of remaining entries. */ + auth->howmany--; - return 1; + return 1; } /* Generates a random challenge, sends it to the agent, and waits for response @@ -229,355 +222,329 @@ ssh_get_next_identity(AuthenticationConnection *auth, int ssh_decrypt_challenge(AuthenticationConnection *auth, - BIGNUM *e, BIGNUM *n, BIGNUM *challenge, + BIGNUM* e, BIGNUM *n, BIGNUM *challenge, unsigned char session_id[16], unsigned int response_type, unsigned char response[16]) { - Buffer buffer; - unsigned char buf[8192]; - int len, l, i; - - /* Response type 0 is no longer supported. */ - if (response_type == 0) - fatal("Compatibility with ssh protocol version 1.0 no longer supported."); - - /* Format a message to the agent. */ - buf[0] = SSH_AGENTC_RSA_CHALLENGE; - buffer_init(&buffer); - buffer_append(&buffer, (char *)buf, 1); - buffer_put_int(&buffer, BN_num_bits(n)); - buffer_put_bignum(&buffer, e); - buffer_put_bignum(&buffer, n); - buffer_put_bignum(&buffer, challenge); - buffer_append(&buffer, (char *)session_id, 16); - buffer_put_int(&buffer, response_type); - - /* Get the length of the message, and format it in the buffer. */ - len = buffer_len(&buffer); - PUT_32BIT(buf, len); - - /* Send the length and then the packet to the agent. */ - if (write(auth->fd, buf, 4) != 4 || - write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != - buffer_len(&buffer)) - { - error("Error writing to authentication socket."); - error_cleanup: - buffer_free(&buffer); - return 0; - } - - /* Wait for response from the agent. First read the length of the - response packet. */ - len = 4; - while (len > 0) - { - l = read(auth->fd, buf + 4 - len, len); - if (l <= 0) - { - error("Error reading response length from authentication socket."); - goto error_cleanup; + Buffer buffer; + unsigned char buf[8192]; + int len, l, i; + + /* Response type 0 is no longer supported. */ + if (response_type == 0) + fatal("Compatibility with ssh protocol version 1.0 no longer supported."); + + /* Format a message to the agent. */ + buf[0] = SSH_AGENTC_RSA_CHALLENGE; + buffer_init(&buffer); + buffer_append(&buffer, (char *) buf, 1); + buffer_put_int(&buffer, BN_num_bits(n)); + buffer_put_bignum(&buffer, e); + buffer_put_bignum(&buffer, n); + buffer_put_bignum(&buffer, challenge); + buffer_append(&buffer, (char *) session_id, 16); + buffer_put_int(&buffer, response_type); + + /* Get the length of the message, and format it in the buffer. */ + len = buffer_len(&buffer); + PUT_32BIT(buf, len); + + /* Send the length and then the packet to the agent. */ + if (write(auth->fd, buf, 4) != 4 || + write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != + buffer_len(&buffer)) { + error("Error writing to authentication socket."); +error_cleanup: + buffer_free(&buffer); + return 0; + } + /* Wait for response from the agent. First read the length of the + response packet. */ + len = 4; + while (len > 0) { + l = read(auth->fd, buf + 4 - len, len); + if (l <= 0) { + error("Error reading response length from authentication socket."); + goto error_cleanup; + } + len -= l; + } + + /* Extract the length, and check it for sanity. */ + len = GET_32BIT(buf); + if (len > 256 * 1024) + fatal("Authentication response too long: %d", len); + + /* Read the rest of the response in tothe buffer. */ + buffer_clear(&buffer); + while (len > 0) { + l = len; + if (l > sizeof(buf)) + l = sizeof(buf); + l = read(auth->fd, buf, l); + if (l <= 0) { + error("Error reading response from authentication socket."); + goto error_cleanup; + } + buffer_append(&buffer, (char *) buf, l); + len -= l; } - len -= l; - } - - /* Extract the length, and check it for sanity. */ - len = GET_32BIT(buf); - if (len > 256*1024) - fatal("Authentication response too long: %d", len); - - /* Read the rest of the response in tothe buffer. */ - buffer_clear(&buffer); - while (len > 0) - { - l = len; - if (l > sizeof(buf)) - l = sizeof(buf); - l = read(auth->fd, buf, l); - if (l <= 0) - { - error("Error reading response from authentication socket."); - goto error_cleanup; + + /* Get the type of the packet. */ + buffer_get(&buffer, (char *) buf, 1); + + /* Check for agent failure message. */ + if (buf[0] == SSH_AGENT_FAILURE) { + log("Agent admitted failure to authenticate using the key."); + goto error_cleanup; } - buffer_append(&buffer, (char *)buf, l); - len -= l; - } - - /* Get the type of the packet. */ - buffer_get(&buffer, (char *)buf, 1); - - /* Check for agent failure message. */ - if (buf[0] == SSH_AGENT_FAILURE) - { - log("Agent admitted failure to authenticate using the key."); - goto error_cleanup; - } - - /* Now it must be an authentication response packet. */ - if (buf[0] != SSH_AGENT_RSA_RESPONSE) - fatal("Bad authentication response: %d", buf[0]); - - /* Get the response from the packet. This will abort with a fatal error - if the packet is corrupt. */ - for (i = 0; i < 16; i++) - response[i] = buffer_get_char(&buffer); - - /* The buffer containing the packet is no longer needed. */ - buffer_free(&buffer); - - /* Correct answer. */ - return 1; -} + /* Now it must be an authentication response packet. */ + if (buf[0] != SSH_AGENT_RSA_RESPONSE) + fatal("Bad authentication response: %d", buf[0]); + + /* Get the response from the packet. This will abort with a fatal + error if the packet is corrupt. */ + for (i = 0; i < 16; i++) + response[i] = buffer_get_char(&buffer); + + /* The buffer containing the packet is no longer needed. */ + buffer_free(&buffer); + + /* Correct answer. */ + return 1; +} /* Adds an identity to the authentication server. This call is not meant to be used by normal applications. */ -int ssh_add_identity(AuthenticationConnection *auth, - RSA *key, const char *comment) +int +ssh_add_identity(AuthenticationConnection *auth, + RSA * key, const char *comment) { - Buffer buffer; - unsigned char buf[8192]; - int len, l, type; - - /* Format a message to the agent. */ - buffer_init(&buffer); - buffer_put_char(&buffer, SSH_AGENTC_ADD_RSA_IDENTITY); - buffer_put_int(&buffer, BN_num_bits(key->n)); - buffer_put_bignum(&buffer, key->n); - buffer_put_bignum(&buffer, key->e); - buffer_put_bignum(&buffer, key->d); - /* To keep within the protocol: p < q for ssh. in SSL p > q */ - buffer_put_bignum(&buffer, key->iqmp); /* ssh key->u */ - buffer_put_bignum(&buffer, key->q); /* ssh key->p, SSL key->q */ - buffer_put_bignum(&buffer, key->p); /* ssh key->q, SSL key->p */ - buffer_put_string(&buffer, comment, strlen(comment)); - - /* Get the length of the message, and format it in the buffer. */ - len = buffer_len(&buffer); - PUT_32BIT(buf, len); - - /* Send the length and then the packet to the agent. */ - if (write(auth->fd, buf, 4) != 4 || - write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != - buffer_len(&buffer)) - { - error("Error writing to authentication socket."); - error_cleanup: - buffer_free(&buffer); - return 0; - } - - /* Wait for response from the agent. First read the length of the - response packet. */ - len = 4; - while (len > 0) - { - l = read(auth->fd, buf + 4 - len, len); - if (l <= 0) - { - error("Error reading response length from authentication socket."); - goto error_cleanup; + Buffer buffer; + unsigned char buf[8192]; + int len, l, type; + + /* Format a message to the agent. */ + buffer_init(&buffer); + buffer_put_char(&buffer, SSH_AGENTC_ADD_RSA_IDENTITY); + buffer_put_int(&buffer, BN_num_bits(key->n)); + buffer_put_bignum(&buffer, key->n); + buffer_put_bignum(&buffer, key->e); + buffer_put_bignum(&buffer, key->d); + /* To keep within the protocol: p < q for ssh. in SSL p > q */ + buffer_put_bignum(&buffer, key->iqmp); /* ssh key->u */ + buffer_put_bignum(&buffer, key->q); /* ssh key->p, SSL key->q */ + buffer_put_bignum(&buffer, key->p); /* ssh key->q, SSL key->p */ + buffer_put_string(&buffer, comment, strlen(comment)); + + /* Get the length of the message, and format it in the buffer. */ + len = buffer_len(&buffer); + PUT_32BIT(buf, len); + + /* Send the length and then the packet to the agent. */ + if (write(auth->fd, buf, 4) != 4 || + write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != + buffer_len(&buffer)) { + error("Error writing to authentication socket."); +error_cleanup: + buffer_free(&buffer); + return 0; + } + /* Wait for response from the agent. First read the length of the + response packet. */ + len = 4; + while (len > 0) { + l = read(auth->fd, buf + 4 - len, len); + if (l <= 0) { + error("Error reading response length from authentication socket."); + goto error_cleanup; + } + len -= l; + } + + /* Extract the length, and check it for sanity. */ + len = GET_32BIT(buf); + if (len > 256 * 1024) + fatal("Add identity response too long: %d", len); + + /* Read the rest of the response in tothe buffer. */ + buffer_clear(&buffer); + while (len > 0) { + l = len; + if (l > sizeof(buf)) + l = sizeof(buf); + l = read(auth->fd, buf, l); + if (l <= 0) { + error("Error reading response from authentication socket."); + goto error_cleanup; + } + buffer_append(&buffer, (char *) buf, l); + len -= l; } - len -= l; - } - - /* Extract the length, and check it for sanity. */ - len = GET_32BIT(buf); - if (len > 256*1024) - fatal("Add identity response too long: %d", len); - - /* Read the rest of the response in tothe buffer. */ - buffer_clear(&buffer); - while (len > 0) - { - l = len; - if (l > sizeof(buf)) - l = sizeof(buf); - l = read(auth->fd, buf, l); - if (l <= 0) - { - error("Error reading response from authentication socket."); - goto error_cleanup; + + /* Get the type of the packet. */ + type = buffer_get_char(&buffer); + switch (type) { + case SSH_AGENT_FAILURE: + buffer_free(&buffer); + return 0; + case SSH_AGENT_SUCCESS: + buffer_free(&buffer); + return 1; + default: + fatal("Bad response to add identity from authentication agent: %d", + type); } - buffer_append(&buffer, (char *)buf, l); - len -= l; - } - - /* Get the type of the packet. */ - type = buffer_get_char(&buffer); - switch (type) - { - case SSH_AGENT_FAILURE: - buffer_free(&buffer); - return 0; - case SSH_AGENT_SUCCESS: - buffer_free(&buffer); - return 1; - default: - fatal("Bad response to add identity from authentication agent: %d", - type); - } - /*NOTREACHED*/ - return 0; -} - -/* Removes an identity from the authentication server. This call is not meant + /* NOTREACHED */ + return 0; +} + +/* Removes an identity from the authentication server. This call is not meant to be used by normal applications. */ -int ssh_remove_identity(AuthenticationConnection *auth, RSA *key) +int +ssh_remove_identity(AuthenticationConnection *auth, RSA *key) { - Buffer buffer; - unsigned char buf[8192]; - int len, l, type; - - /* Format a message to the agent. */ - buffer_init(&buffer); - buffer_put_char(&buffer, SSH_AGENTC_REMOVE_RSA_IDENTITY); - buffer_put_int(&buffer, BN_num_bits(key->n)); - buffer_put_bignum(&buffer, key->e); - buffer_put_bignum(&buffer, key->n); - - /* Get the length of the message, and format it in the buffer. */ - len = buffer_len(&buffer); - PUT_32BIT(buf, len); - - /* Send the length and then the packet to the agent. */ - if (write(auth->fd, buf, 4) != 4 || - write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != - buffer_len(&buffer)) - { - error("Error writing to authentication socket."); - error_cleanup: - buffer_free(&buffer); - return 0; - } - - /* Wait for response from the agent. First read the length of the - response packet. */ - len = 4; - while (len > 0) - { - l = read(auth->fd, buf + 4 - len, len); - if (l <= 0) - { - error("Error reading response length from authentication socket."); - goto error_cleanup; + Buffer buffer; + unsigned char buf[8192]; + int len, l, type; + + /* Format a message to the agent. */ + buffer_init(&buffer); + buffer_put_char(&buffer, SSH_AGENTC_REMOVE_RSA_IDENTITY); + buffer_put_int(&buffer, BN_num_bits(key->n)); + buffer_put_bignum(&buffer, key->e); + buffer_put_bignum(&buffer, key->n); + + /* Get the length of the message, and format it in the buffer. */ + len = buffer_len(&buffer); + PUT_32BIT(buf, len); + + /* Send the length and then the packet to the agent. */ + if (write(auth->fd, buf, 4) != 4 || + write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != + buffer_len(&buffer)) { + error("Error writing to authentication socket."); +error_cleanup: + buffer_free(&buffer); + return 0; + } + /* Wait for response from the agent. First read the length of the + response packet. */ + len = 4; + while (len > 0) { + l = read(auth->fd, buf + 4 - len, len); + if (l <= 0) { + error("Error reading response length from authentication socket."); + goto error_cleanup; + } + len -= l; + } + + /* Extract the length, and check it for sanity. */ + len = GET_32BIT(buf); + if (len > 256 * 1024) + fatal("Remove identity response too long: %d", len); + + /* Read the rest of the response in tothe buffer. */ + buffer_clear(&buffer); + while (len > 0) { + l = len; + if (l > sizeof(buf)) + l = sizeof(buf); + l = read(auth->fd, buf, l); + if (l <= 0) { + error("Error reading response from authentication socket."); + goto error_cleanup; + } + buffer_append(&buffer, (char *) buf, l); + len -= l; } - len -= l; - } - - /* Extract the length, and check it for sanity. */ - len = GET_32BIT(buf); - if (len > 256*1024) - fatal("Remove identity response too long: %d", len); - - /* Read the rest of the response in tothe buffer. */ - buffer_clear(&buffer); - while (len > 0) - { - l = len; - if (l > sizeof(buf)) - l = sizeof(buf); - l = read(auth->fd, buf, l); - if (l <= 0) - { - error("Error reading response from authentication socket."); - goto error_cleanup; + + /* Get the type of the packet. */ + type = buffer_get_char(&buffer); + switch (type) { + case SSH_AGENT_FAILURE: + buffer_free(&buffer); + return 0; + case SSH_AGENT_SUCCESS: + buffer_free(&buffer); + return 1; + default: + fatal("Bad response to remove identity from authentication agent: %d", + type); } - buffer_append(&buffer, (char *)buf, l); - len -= l; - } - - /* Get the type of the packet. */ - type = buffer_get_char(&buffer); - switch (type) - { - case SSH_AGENT_FAILURE: - buffer_free(&buffer); - return 0; - case SSH_AGENT_SUCCESS: - buffer_free(&buffer); - return 1; - default: - fatal("Bad response to remove identity from authentication agent: %d", - type); - } - /*NOTREACHED*/ - return 0; -} - -/* Removes all identities from the agent. This call is not meant + /* NOTREACHED */ + return 0; +} + +/* Removes all identities from the agent. This call is not meant to be used by normal applications. */ -int ssh_remove_all_identities(AuthenticationConnection *auth) +int +ssh_remove_all_identities(AuthenticationConnection *auth) { - Buffer buffer; - unsigned char buf[8192]; - int len, l, type; - - /* Get the length of the message, and format it in the buffer. */ - PUT_32BIT(buf, 1); - buf[4] = SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES; - - /* Send the length and then the packet to the agent. */ - if (write(auth->fd, buf, 5) != 5) - { - error("Error writing to authentication socket."); - return 0; - } - - /* Wait for response from the agent. First read the length of the - response packet. */ - len = 4; - while (len > 0) - { - l = read(auth->fd, buf + 4 - len, len); - if (l <= 0) - { - error("Error reading response length from authentication socket."); - return 0; + Buffer buffer; + unsigned char buf[8192]; + int len, l, type; + + /* Get the length of the message, and format it in the buffer. */ + PUT_32BIT(buf, 1); + buf[4] = SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES; + + /* Send the length and then the packet to the agent. */ + if (write(auth->fd, buf, 5) != 5) { + error("Error writing to authentication socket."); + return 0; + } + /* Wait for response from the agent. First read the length of the + response packet. */ + len = 4; + while (len > 0) { + l = read(auth->fd, buf + 4 - len, len); + if (l <= 0) { + error("Error reading response length from authentication socket."); + return 0; + } + len -= l; + } + + /* Extract the length, and check it for sanity. */ + len = GET_32BIT(buf); + if (len > 256 * 1024) + fatal("Remove identity response too long: %d", len); + + /* Read the rest of the response into the buffer. */ + buffer_init(&buffer); + while (len > 0) { + l = len; + if (l > sizeof(buf)) + l = sizeof(buf); + l = read(auth->fd, buf, l); + if (l <= 0) { + error("Error reading response from authentication socket."); + buffer_free(&buffer); + return 0; + } + buffer_append(&buffer, (char *) buf, l); + len -= l; } - len -= l; - } - - /* Extract the length, and check it for sanity. */ - len = GET_32BIT(buf); - if (len > 256*1024) - fatal("Remove identity response too long: %d", len); - - /* Read the rest of the response into the buffer. */ - buffer_init(&buffer); - while (len > 0) - { - l = len; - if (l > sizeof(buf)) - l = sizeof(buf); - l = read(auth->fd, buf, l); - if (l <= 0) - { - error("Error reading response from authentication socket."); - buffer_free(&buffer); - return 0; + + /* Get the type of the packet. */ + type = buffer_get_char(&buffer); + switch (type) { + case SSH_AGENT_FAILURE: + buffer_free(&buffer); + return 0; + case SSH_AGENT_SUCCESS: + buffer_free(&buffer); + return 1; + default: + fatal("Bad response to remove identity from authentication agent: %d", + type); } - buffer_append(&buffer, (char *)buf, l); - len -= l; - } - - /* Get the type of the packet. */ - type = buffer_get_char(&buffer); - switch (type) - { - case SSH_AGENT_FAILURE: - buffer_free(&buffer); - return 0; - case SSH_AGENT_SUCCESS: - buffer_free(&buffer); - return 1; - default: - fatal("Bad response to remove identity from authentication agent: %d", - type); - } - /*NOTREACHED*/ - return 0; -} + /* NOTREACHED */ + return 0; +} diff --git a/authfd.h b/authfd.h index 7b54d639..74011ef4 100644 --- a/authfd.h +++ b/authfd.h @@ -1,17 +1,17 @@ /* - -authfd.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Wed Mar 29 01:17:41 1995 ylo - -Functions to interface with the SSH_AUTHENTICATION_FD socket. - -*/ + * + * authfd.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Wed Mar 29 01:17:41 1995 ylo + * + * Functions to interface with the SSH_AUTHENTICATION_FD socket. + * + */ /* RCSID("$Id$"); */ @@ -31,72 +31,73 @@ Functions to interface with the SSH_AUTHENTICATION_FD socket. #define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 #define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9 -typedef struct -{ - int fd; - Buffer packet; - Buffer identities; - int howmany; -} AuthenticationConnection; - +typedef struct { + int fd; + Buffer packet; + Buffer identities; + int howmany; +} AuthenticationConnection; /* Returns the number of the authentication fd, or -1 if there is none. */ -int ssh_get_authentication_socket(); +int ssh_get_authentication_socket(); -/* This should be called for any descriptor returned by +/* This should be called for any descriptor returned by ssh_get_authentication_socket(). Depending on the way the descriptor was obtained, this may close the descriptor. */ -void ssh_close_authentication_socket(int authfd); +void ssh_close_authentication_socket(int authfd); /* Opens and connects a private socket for communication with the - authentication agent. Returns NULL if an error occurred and the + authentication agent. Returns NULL if an error occurred and the connection could not be opened. The connection should be closed by the caller by calling ssh_close_authentication_connection(). */ AuthenticationConnection *ssh_get_authentication_connection(); /* Closes the connection to the authentication agent and frees any associated memory. */ -void ssh_close_authentication_connection(AuthenticationConnection *ac); +void ssh_close_authentication_connection(AuthenticationConnection * ac); /* Returns the first authentication identity held by the agent. Returns true if an identity is available, 0 otherwise. The caller must initialize the integers before the call, and free the comment after a successful call (before calling ssh_get_next_identity). */ -int ssh_get_first_identity(AuthenticationConnection *connection, - BIGNUM *e, BIGNUM *n, char **comment); +int +ssh_get_first_identity(AuthenticationConnection * connection, + BIGNUM * e, BIGNUM * n, char **comment); /* Returns the next authentication identity for the agent. Other functions can be called between this and ssh_get_first_identity or two calls of this function. This returns 0 if there are no more identities. The caller must free comment after a successful return. */ -int ssh_get_next_identity(AuthenticationConnection *connection, - BIGNUM *e, BIGNUM *n, char **comment); +int +ssh_get_next_identity(AuthenticationConnection * connection, + BIGNUM * e, BIGNUM * n, char **comment); /* Requests the agent to decrypt the given challenge. Returns true if the agent claims it was able to decrypt it. */ -int ssh_decrypt_challenge(AuthenticationConnection *auth, - BIGNUM *e, BIGNUM *n, BIGNUM *challenge, - unsigned char session_id[16], - unsigned int response_type, - unsigned char response[16]); +int +ssh_decrypt_challenge(AuthenticationConnection * auth, + BIGNUM * e, BIGNUM * n, BIGNUM * challenge, + unsigned char session_id[16], + unsigned int response_type, + unsigned char response[16]); /* Adds an identity to the authentication server. This call is not meant to be used by normal applications. This returns true if the identity was successfully added. */ -int ssh_add_identity(AuthenticationConnection *connection, - RSA *key, const char *comment); + int ssh_add_identity(AuthenticationConnection * connection, + RSA * key, const char *comment); /* Removes the identity from the authentication server. This call is not meant to be used by normal applications. This returns true if the identity was successfully added. */ -int ssh_remove_identity(AuthenticationConnection *connection, - RSA *key); + int ssh_remove_identity(AuthenticationConnection * connection, + RSA * key); /* Removes all identities from the authentication agent. This call is not meant to be used by normal applications. This returns true if the operation was successful. */ -int ssh_remove_all_identities(AuthenticationConnection *connection); + int ssh_remove_all_identities(AuthenticationConnection * connection); /* Closes the connection to the authentication agent. */ -void ssh_close_authentication(AuthenticationConnection *connection); + void ssh_close_authentication(AuthenticationConnection * connection); -#endif /* AUTHFD_H */ +#endif /* AUTHFD_H */ diff --git a/authfile.c b/authfile.c index 1649b605..653fd90a 100644 --- a/authfile.c +++ b/authfile.c @@ -1,18 +1,18 @@ /* - -authfile.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Mon Mar 27 03:52:05 1995 ylo - -This file contains functions for reading and writing identity files, and -for reading the passphrase from the user. - -*/ + * + * authfile.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Mon Mar 27 03:52:05 1995 ylo + * + * This file contains functions for reading and writing identity files, and + * for reading the passphrase from the user. + * + */ #include "includes.h" RCSID("$Id$"); @@ -42,93 +42,93 @@ int save_private_key(const char *filename, const char *passphrase, RSA *key, const char *comment) { - Buffer buffer, encrypted; - char buf[100], *cp; - int f, i; - CipherContext cipher; - int cipher_type; - u_int32_t rand; - - /* If the passphrase is empty, use SSH_CIPHER_NONE to ease converting to - another cipher; otherwise use SSH_AUTHFILE_CIPHER. */ - if (strcmp(passphrase, "") == 0) - cipher_type = SSH_CIPHER_NONE; - else - cipher_type = SSH_AUTHFILE_CIPHER; - - /* This buffer is used to built the secret part of the private key. */ - buffer_init(&buffer); - - /* Put checkbytes for checking passphrase validity. */ - rand = arc4random(); - buf[0] = rand & 0xff; - buf[1] = (rand >> 8) & 0xff; - buf[2] = buf[0]; - buf[3] = buf[1]; - buffer_append(&buffer, buf, 4); - - /* Store the private key (n and e will not be stored because they will - be stored in plain text, and storing them also in encrypted format - would just give known plaintext). */ - buffer_put_bignum(&buffer, key->d); - buffer_put_bignum(&buffer, key->iqmp); - buffer_put_bignum(&buffer, key->q); /* reverse from SSL p */ - buffer_put_bignum(&buffer, key->p); /* reverse from SSL q */ - - /* Pad the part to be encrypted until its size is a multiple of 8. */ - while (buffer_len(&buffer) % 8 != 0) - buffer_put_char(&buffer, 0); - - /* This buffer will be used to contain the data in the file. */ - buffer_init(&encrypted); - - /* First store keyfile id string. */ - cp = AUTHFILE_ID_STRING; - for (i = 0; cp[i]; i++) - buffer_put_char(&encrypted, cp[i]); - buffer_put_char(&encrypted, 0); - - /* Store cipher type. */ - buffer_put_char(&encrypted, cipher_type); - buffer_put_int(&encrypted, 0); /* For future extension */ - - /* Store public key. This will be in plain text. */ - buffer_put_int(&encrypted, BN_num_bits(key->n)); - buffer_put_bignum(&encrypted, key->n); - buffer_put_bignum(&encrypted, key->e); - buffer_put_string(&encrypted, comment, strlen(comment)); - - /* Allocate space for the private part of the key in the buffer. */ - buffer_append_space(&encrypted, &cp, buffer_len(&buffer)); - - cipher_set_key_string(&cipher, cipher_type, passphrase, 1); - cipher_encrypt(&cipher, (unsigned char *)cp, - (unsigned char *)buffer_ptr(&buffer), - buffer_len(&buffer)); - memset(&cipher, 0, sizeof(cipher)); - - /* Destroy temporary data. */ - memset(buf, 0, sizeof(buf)); - buffer_free(&buffer); - - /* Write to a file. */ - f = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0600); - if (f < 0) - return 0; - - if (write(f, buffer_ptr(&encrypted), buffer_len(&encrypted)) != - buffer_len(&encrypted)) - { - debug("Write to key file %.200s failed: %.100s", filename, - strerror(errno)); - buffer_free(&encrypted); - close(f); - remove(filename); - return 0; - } - close(f); - buffer_free(&encrypted); - return 1; + Buffer buffer, encrypted; + char buf[100], *cp; + int f, i; + CipherContext cipher; + int cipher_type; + u_int32_t rand; + + /* If the passphrase is empty, use SSH_CIPHER_NONE to ease + converting to another cipher; otherwise use + SSH_AUTHFILE_CIPHER. */ + if (strcmp(passphrase, "") == 0) + cipher_type = SSH_CIPHER_NONE; + else + cipher_type = SSH_AUTHFILE_CIPHER; + + /* This buffer is used to built the secret part of the private key. */ + buffer_init(&buffer); + + /* Put checkbytes for checking passphrase validity. */ + rand = arc4random(); + buf[0] = rand & 0xff; + buf[1] = (rand >> 8) & 0xff; + buf[2] = buf[0]; + buf[3] = buf[1]; + buffer_append(&buffer, buf, 4); + + /* Store the private key (n and e will not be stored because they + will be stored in plain text, and storing them also in + encrypted format would just give known plaintext). */ + buffer_put_bignum(&buffer, key->d); + buffer_put_bignum(&buffer, key->iqmp); + buffer_put_bignum(&buffer, key->q); /* reverse from SSL p */ + buffer_put_bignum(&buffer, key->p); /* reverse from SSL q */ + + /* Pad the part to be encrypted until its size is a multiple of 8. */ + while (buffer_len(&buffer) % 8 != 0) + buffer_put_char(&buffer, 0); + + /* This buffer will be used to contain the data in the file. */ + buffer_init(&encrypted); + + /* First store keyfile id string. */ + cp = AUTHFILE_ID_STRING; + for (i = 0; cp[i]; i++) + buffer_put_char(&encrypted, cp[i]); + buffer_put_char(&encrypted, 0); + + /* Store cipher type. */ + buffer_put_char(&encrypted, cipher_type); + buffer_put_int(&encrypted, 0); /* For future extension */ + + /* Store public key. This will be in plain text. */ + buffer_put_int(&encrypted, BN_num_bits(key->n)); + buffer_put_bignum(&encrypted, key->n); + buffer_put_bignum(&encrypted, key->e); + buffer_put_string(&encrypted, comment, strlen(comment)); + + /* Allocate space for the private part of the key in the buffer. */ + buffer_append_space(&encrypted, &cp, buffer_len(&buffer)); + + cipher_set_key_string(&cipher, cipher_type, passphrase, 1); + cipher_encrypt(&cipher, (unsigned char *) cp, + (unsigned char *) buffer_ptr(&buffer), + buffer_len(&buffer)); + memset(&cipher, 0, sizeof(cipher)); + + /* Destroy temporary data. */ + memset(buf, 0, sizeof(buf)); + buffer_free(&buffer); + + /* Write to a file. */ + f = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (f < 0) + return 0; + + if (write(f, buffer_ptr(&encrypted), buffer_len(&encrypted)) != + buffer_len(&encrypted)) { + debug("Write to key file %.200s failed: %.100s", filename, + strerror(errno)); + buffer_free(&encrypted); + close(f); + remove(filename); + return 0; + } + close(f); + buffer_free(&encrypted); + return 1; } /* Loads the public part of the key file. Returns 0 if an error @@ -136,70 +136,65 @@ save_private_key(const char *filename, const char *passphrase, non-zero otherwise. */ int -load_public_key(const char *filename, RSA *pub, +load_public_key(const char *filename, RSA * pub, char **comment_return) { - int f, i; - off_t len; - Buffer buffer; - char *cp; - - /* Read data from the file into the buffer. */ - f = open(filename, O_RDONLY); - if (f < 0) - return 0; - - len = lseek(f, (off_t)0, SEEK_END); - lseek(f, (off_t)0, SEEK_SET); - - buffer_init(&buffer); - buffer_append_space(&buffer, &cp, len); - - if (read(f, cp, (size_t)len) != (size_t)len) - { - debug("Read from key file %.200s failed: %.100s", filename, - strerror(errno)); - buffer_free(&buffer); - close(f); - return 0; - } - close(f); - - /* Check that it is at least big enought to contain the ID string. */ - if (len < strlen(AUTHFILE_ID_STRING) + 1) - { - debug("Bad key file %.200s.", filename); - buffer_free(&buffer); - return 0; - } - - /* Make sure it begins with the id string. Consume the id string from - the buffer. */ - for (i = 0; i < (unsigned int)strlen(AUTHFILE_ID_STRING) + 1; i++) - if (buffer_get_char(&buffer) != (unsigned char)AUTHFILE_ID_STRING[i]) - { - debug("Bad key file %.200s.", filename); + int f, i; + off_t len; + Buffer buffer; + char *cp; + + /* Read data from the file into the buffer. */ + f = open(filename, O_RDONLY); + if (f < 0) + return 0; + + len = lseek(f, (off_t) 0, SEEK_END); + lseek(f, (off_t) 0, SEEK_SET); + + buffer_init(&buffer); + buffer_append_space(&buffer, &cp, len); + + if (read(f, cp, (size_t) len) != (size_t) len) { + debug("Read from key file %.200s failed: %.100s", filename, + strerror(errno)); + buffer_free(&buffer); + close(f); + return 0; + } + close(f); + + /* Check that it is at least big enought to contain the ID string. */ + if (len < strlen(AUTHFILE_ID_STRING) + 1) { + debug("Bad key file %.200s.", filename); + buffer_free(&buffer); + return 0; + } + /* Make sure it begins with the id string. Consume the id string + from the buffer. */ + for (i = 0; i < (unsigned int) strlen(AUTHFILE_ID_STRING) + 1; i++) + if (buffer_get_char(&buffer) != (unsigned char) AUTHFILE_ID_STRING[i]) { + debug("Bad key file %.200s.", filename); + buffer_free(&buffer); + return 0; + } + /* Skip cipher type and reserved data. */ + (void) buffer_get_char(&buffer); /* cipher type */ + (void) buffer_get_int(&buffer); /* reserved */ + + /* Read the public key from the buffer. */ + buffer_get_int(&buffer); + pub->n = BN_new(); + buffer_get_bignum(&buffer, pub->n); + pub->e = BN_new(); + buffer_get_bignum(&buffer, pub->e); + if (comment_return) + *comment_return = buffer_get_string(&buffer, NULL); + /* The encrypted private part is not parsed by this function. */ + buffer_free(&buffer); - return 0; - } - - /* Skip cipher type and reserved data. */ - (void)buffer_get_char(&buffer); /* cipher type */ - (void)buffer_get_int(&buffer); /* reserved */ - - /* Read the public key from the buffer. */ - buffer_get_int(&buffer); - pub->n = BN_new(); - buffer_get_bignum(&buffer, pub->n); - pub->e = BN_new(); - buffer_get_bignum(&buffer, pub->e); - if (comment_return) - *comment_return = buffer_get_string(&buffer, NULL); - /* The encrypted private part is not parsed by this function. */ - - buffer_free(&buffer); - - return 1; + + return 1; } /* Loads the private key from the file. Returns 0 if an error is encountered @@ -208,149 +203,139 @@ load_public_key(const char *filename, RSA *pub, int load_private_key(const char *filename, const char *passphrase, - RSA *prv, char **comment_return) + RSA * prv, char **comment_return) { - int f, i, check1, check2, cipher_type; - off_t len; - Buffer buffer, decrypted; - char *cp; - CipherContext cipher; - BN_CTX *ctx; - BIGNUM *aux; - struct stat st; - - /* Read the file into the buffer. */ - f = open(filename, O_RDONLY); - if (f < 0) - return 0; - - /* We assume we are called under uid of the owner of the file */ - if (fstat(f, &st) < 0 || - (st.st_uid != 0 && st.st_uid != getuid()) || - (st.st_mode & 077) != 0) { - error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); - error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); - error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); - error("Bad ownership or mode(0%3.3o) for '%s'.", - st.st_mode & 0777, filename); - error("It is recommended that your private key files are NOT accessible by others."); - return 0; - } - - len = lseek(f, (off_t)0, SEEK_END); - lseek(f, (off_t)0, SEEK_SET); - - buffer_init(&buffer); - buffer_append_space(&buffer, &cp, len); - - if (read(f, cp, (size_t)len) != (size_t)len) - { - debug("Read from key file %.200s failed: %.100s", filename, - strerror(errno)); - buffer_free(&buffer); - close(f); - return 0; - } - close(f); - - /* Check that it is at least big enought to contain the ID string. */ - if (len < strlen(AUTHFILE_ID_STRING) + 1) - { - debug("Bad key file %.200s.", filename); - buffer_free(&buffer); - return 0; - } - - /* Make sure it begins with the id string. Consume the id string from - the buffer. */ - for (i = 0; i < (unsigned int)strlen(AUTHFILE_ID_STRING) + 1; i++) - if (buffer_get_char(&buffer) != (unsigned char)AUTHFILE_ID_STRING[i]) - { - debug("Bad key file %.200s.", filename); + int f, i, check1, check2, cipher_type; + off_t len; + Buffer buffer, decrypted; + char *cp; + CipherContext cipher; + BN_CTX *ctx; + BIGNUM *aux; + struct stat st; + + /* Read the file into the buffer. */ + f = open(filename, O_RDONLY); + if (f < 0) + return 0; + + /* We assume we are called under uid of the owner of the file */ + if (fstat(f, &st) < 0 || + (st.st_uid != 0 && st.st_uid != getuid()) || + (st.st_mode & 077) != 0) { + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("Bad ownership or mode(0%3.3o) for '%s'.", + st.st_mode & 0777, filename); + error("It is recommended that your private key files are NOT accessible by others."); + return 0; + } + len = lseek(f, (off_t) 0, SEEK_END); + lseek(f, (off_t) 0, SEEK_SET); + + buffer_init(&buffer); + buffer_append_space(&buffer, &cp, len); + + if (read(f, cp, (size_t) len) != (size_t) len) { + debug("Read from key file %.200s failed: %.100s", filename, + strerror(errno)); + buffer_free(&buffer); + close(f); + return 0; + } + close(f); + + /* Check that it is at least big enought to contain the ID string. */ + if (len < strlen(AUTHFILE_ID_STRING) + 1) { + debug("Bad key file %.200s.", filename); + buffer_free(&buffer); + return 0; + } + /* Make sure it begins with the id string. Consume the id string + from the buffer. */ + for (i = 0; i < (unsigned int) strlen(AUTHFILE_ID_STRING) + 1; i++) + if (buffer_get_char(&buffer) != (unsigned char) AUTHFILE_ID_STRING[i]) { + debug("Bad key file %.200s.", filename); + buffer_free(&buffer); + return 0; + } + /* Read cipher type. */ + cipher_type = buffer_get_char(&buffer); + (void) buffer_get_int(&buffer); /* Reserved data. */ + + /* Read the public key from the buffer. */ + buffer_get_int(&buffer); + prv->n = BN_new(); + buffer_get_bignum(&buffer, prv->n); + prv->e = BN_new(); + buffer_get_bignum(&buffer, prv->e); + if (comment_return) + *comment_return = buffer_get_string(&buffer, NULL); + else + xfree(buffer_get_string(&buffer, NULL)); + + /* Check that it is a supported cipher. */ + if (((cipher_mask() | SSH_CIPHER_NONE | SSH_AUTHFILE_CIPHER) & + (1 << cipher_type)) == 0) { + debug("Unsupported cipher %.100s used in key file %.200s.", + cipher_name(cipher_type), filename); + buffer_free(&buffer); + goto fail; + } + /* Initialize space for decrypted data. */ + buffer_init(&decrypted); + buffer_append_space(&decrypted, &cp, buffer_len(&buffer)); + + /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ + cipher_set_key_string(&cipher, cipher_type, passphrase, 0); + cipher_decrypt(&cipher, (unsigned char *) cp, + (unsigned char *) buffer_ptr(&buffer), + buffer_len(&buffer)); + buffer_free(&buffer); - return 0; - } - - /* Read cipher type. */ - cipher_type = buffer_get_char(&buffer); - (void)buffer_get_int(&buffer); /* Reserved data. */ - - /* Read the public key from the buffer. */ - buffer_get_int(&buffer); - prv->n = BN_new(); - buffer_get_bignum(&buffer, prv->n); - prv->e = BN_new(); - buffer_get_bignum(&buffer, prv->e); - if (comment_return) - *comment_return = buffer_get_string(&buffer, NULL); - else - xfree(buffer_get_string(&buffer, NULL)); - - /* Check that it is a supported cipher. */ - if (((cipher_mask() | SSH_CIPHER_NONE | SSH_AUTHFILE_CIPHER) & - (1 << cipher_type)) == 0) - { - debug("Unsupported cipher %.100s used in key file %.200s.", - cipher_name(cipher_type), filename); - buffer_free(&buffer); - goto fail; - } - - /* Initialize space for decrypted data. */ - buffer_init(&decrypted); - buffer_append_space(&decrypted, &cp, buffer_len(&buffer)); - - /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ - cipher_set_key_string(&cipher, cipher_type, passphrase, 0); - cipher_decrypt(&cipher, (unsigned char *)cp, - (unsigned char *)buffer_ptr(&buffer), - buffer_len(&buffer)); - - buffer_free(&buffer); - - check1 = buffer_get_char(&decrypted); - check2 = buffer_get_char(&decrypted); - if (check1 != buffer_get_char(&decrypted) || - check2 != buffer_get_char(&decrypted)) - { - if (strcmp(passphrase, "") != 0) - debug("Bad passphrase supplied for key file %.200s.", filename); - /* Bad passphrase. */ - buffer_free(&decrypted); - fail: - BN_clear_free(prv->n); - BN_clear_free(prv->e); - if (comment_return) - xfree(*comment_return); - return 0; - } - - /* Read the rest of the private key. */ - prv->d = BN_new(); - buffer_get_bignum(&decrypted, prv->d); - prv->iqmp = BN_new(); - buffer_get_bignum(&decrypted, prv->iqmp); /* u */ - /* in SSL and SSH p and q are exchanged */ - prv->q = BN_new(); - buffer_get_bignum(&decrypted, prv->q); /* p */ - prv->p = BN_new(); - buffer_get_bignum(&decrypted, prv->p); /* q */ - - ctx = BN_CTX_new(); - aux = BN_new(); - - BN_sub(aux, prv->q, BN_value_one()); - prv->dmq1 = BN_new(); - BN_mod(prv->dmq1, prv->d, aux, ctx); - - BN_sub(aux, prv->p, BN_value_one()); - prv->dmp1 = BN_new(); - BN_mod(prv->dmp1, prv->d, aux, ctx); - - BN_clear_free(aux); - BN_CTX_free(ctx); - - buffer_free(&decrypted); - - return 1; + + check1 = buffer_get_char(&decrypted); + check2 = buffer_get_char(&decrypted); + if (check1 != buffer_get_char(&decrypted) || + check2 != buffer_get_char(&decrypted)) { + if (strcmp(passphrase, "") != 0) + debug("Bad passphrase supplied for key file %.200s.", filename); + /* Bad passphrase. */ + buffer_free(&decrypted); +fail: + BN_clear_free(prv->n); + BN_clear_free(prv->e); + if (comment_return) + xfree(*comment_return); + return 0; + } + /* Read the rest of the private key. */ + prv->d = BN_new(); + buffer_get_bignum(&decrypted, prv->d); + prv->iqmp = BN_new(); + buffer_get_bignum(&decrypted, prv->iqmp); /* u */ + /* in SSL and SSH p and q are exchanged */ + prv->q = BN_new(); + buffer_get_bignum(&decrypted, prv->q); /* p */ + prv->p = BN_new(); + buffer_get_bignum(&decrypted, prv->p); /* q */ + + ctx = BN_CTX_new(); + aux = BN_new(); + + BN_sub(aux, prv->q, BN_value_one()); + prv->dmq1 = BN_new(); + BN_mod(prv->dmq1, prv->d, aux, ctx); + + BN_sub(aux, prv->p, BN_value_one()); + prv->dmp1 = BN_new(); + BN_mod(prv->dmp1, prv->d, aux, ctx); + + BN_clear_free(aux); + BN_CTX_free(ctx); + + buffer_free(&decrypted); + + return 1; } diff --git a/bufaux.c b/bufaux.c index 4e8e0eec..265a28fb 100644 --- a/bufaux.c +++ b/bufaux.c @@ -1,18 +1,18 @@ /* - -bufaux.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Wed Mar 29 02:24:47 1995 ylo - -Auxiliary functions for storing and retrieving various data types to/from -Buffers. - -*/ + * + * bufaux.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Wed Mar 29 02:24:47 1995 ylo + * + * Auxiliary functions for storing and retrieving various data types to/from + * Buffers. + * + */ #include "includes.h" RCSID("$Id$"); @@ -30,122 +30,136 @@ RCSID("$Id$"); #include "xmalloc.h" #include "getput.h" -/* Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed - by (bits+7)/8 bytes of binary data, msb first. */ - +/* + * Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed + * by (bits+7)/8 bytes of binary data, msb first. + */ void buffer_put_bignum(Buffer *buffer, BIGNUM *value) { - int bits = BN_num_bits(value); - int bin_size = (bits + 7) / 8; - char *buf = xmalloc(bin_size); - int oi; - char msg[2]; - - /* Get the value of in binary */ - oi = BN_bn2bin(value, buf); - if (oi != bin_size) - fatal("buffer_put_bignum: BN_bn2bin() failed: oi %d != bin_size %d", - oi, bin_size); - - /* Store the number of bits in the buffer in two bytes, msb first. */ - PUT_16BIT(msg, bits); - buffer_append(buffer, msg, 2); - /* Store the binary data. */ - buffer_append(buffer, buf, oi); - /* Clear the temporary data. */ - memset(buf, 0, bin_size); - xfree(buf); + int bits = BN_num_bits(value); + int bin_size = (bits + 7) / 8; + char *buf = xmalloc(bin_size); + int oi; + char msg[2]; + + /* Get the value of in binary */ + oi = BN_bn2bin(value, buf); + if (oi != bin_size) + fatal("buffer_put_bignum: BN_bn2bin() failed: oi %d != bin_size %d", + oi, bin_size); + + /* Store the number of bits in the buffer in two bytes, msb first. */ + PUT_16BIT(msg, bits); + buffer_append(buffer, msg, 2); + /* Store the binary data. */ + buffer_append(buffer, buf, oi); + /* Clear the temporary data. */ + memset(buf, 0, bin_size); + xfree(buf); } -/* Retrieves an BIGNUM from the buffer. */ - +/* + * Retrieves an BIGNUM from the buffer. + */ int buffer_get_bignum(Buffer *buffer, BIGNUM *value) { - int bits, bytes; - unsigned char buf[2], *bin; - - /* Get the number for bits. */ - buffer_get(buffer, (char *)buf, 2); - bits = GET_16BIT(buf); - /* Compute the number of binary bytes that follow. */ - bytes = (bits + 7) / 8; - if (buffer_len(buffer) < bytes) - fatal("buffer_get_bignum: input buffer too small"); - bin = buffer_ptr(buffer); - BN_bin2bn(bin, bytes, value); - buffer_consume(buffer, bytes); - - return 2 + bytes; + int bits, bytes; + unsigned char buf[2], *bin; + + /* Get the number for bits. */ + buffer_get(buffer, (char *) buf, 2); + bits = GET_16BIT(buf); + /* Compute the number of binary bytes that follow. */ + bytes = (bits + 7) / 8; + if (buffer_len(buffer) < bytes) + fatal("buffer_get_bignum: input buffer too small"); + bin = buffer_ptr(buffer); + BN_bin2bn(bin, bytes, value); + buffer_consume(buffer, bytes); + + return 2 + bytes; } -/* Returns an integer from the buffer (4 bytes, msb first). */ - -unsigned int buffer_get_int(Buffer *buffer) +/* + * Returns an integer from the buffer (4 bytes, msb first). + */ +unsigned int +buffer_get_int(Buffer *buffer) { - unsigned char buf[4]; - buffer_get(buffer, (char *)buf, 4); - return GET_32BIT(buf); + unsigned char buf[4]; + buffer_get(buffer, (char *) buf, 4); + return GET_32BIT(buf); } -/* Stores an integer in the buffer in 4 bytes, msb first. */ - -void buffer_put_int(Buffer *buffer, unsigned int value) +/* + * Stores an integer in the buffer in 4 bytes, msb first. + */ +void +buffer_put_int(Buffer *buffer, unsigned int value) { - char buf[4]; - PUT_32BIT(buf, value); - buffer_append(buffer, buf, 4); + char buf[4]; + PUT_32BIT(buf, value); + buffer_append(buffer, buf, 4); } -/* Returns an arbitrary binary string from the buffer. The string cannot - be longer than 256k. The returned value points to memory allocated - with xmalloc; it is the responsibility of the calling function to free - the data. If length_ptr is non-NULL, the length of the returned data - will be stored there. A null character will be automatically appended - to the returned string, and is not counted in length. */ - -char *buffer_get_string(Buffer *buffer, unsigned int *length_ptr) +/* + * Returns an arbitrary binary string from the buffer. The string cannot + * be longer than 256k. The returned value points to memory allocated + * with xmalloc; it is the responsibility of the calling function to free + * the data. If length_ptr is non-NULL, the length of the returned data + * will be stored there. A null character will be automatically appended + * to the returned string, and is not counted in length. + */ +char * +buffer_get_string(Buffer *buffer, unsigned int *length_ptr) { - unsigned int len; - char *value; - /* Get the length. */ - len = buffer_get_int(buffer); - if (len > 256*1024) - fatal("Received packet with bad string length %d", len); - /* Allocate space for the string. Add one byte for a null character. */ - value = xmalloc(len + 1); - /* Get the string. */ - buffer_get(buffer, value, len); - /* Append a null character to make processing easier. */ - value[len] = 0; - /* Optionally return the length of the string. */ - if (length_ptr) - *length_ptr = len; - return value; + unsigned int len; + char *value; + /* Get the length. */ + len = buffer_get_int(buffer); + if (len > 256 * 1024) + fatal("Received packet with bad string length %d", len); + /* Allocate space for the string. Add one byte for a null character. */ + value = xmalloc(len + 1); + /* Get the string. */ + buffer_get(buffer, value, len); + /* Append a null character to make processing easier. */ + value[len] = 0; + /* Optionally return the length of the string. */ + if (length_ptr) + *length_ptr = len; + return value; } -/* Stores and arbitrary binary string in the buffer. */ - -void buffer_put_string(Buffer *buffer, const void *buf, unsigned int len) +/* + * Stores and arbitrary binary string in the buffer. + */ +void +buffer_put_string(Buffer *buffer, const void *buf, unsigned int len) { - buffer_put_int(buffer, len); - buffer_append(buffer, buf, len); + buffer_put_int(buffer, len); + buffer_append(buffer, buf, len); } -/* Returns a character from the buffer (0 - 255). */ - -int buffer_get_char(Buffer *buffer) +/* + * Returns a character from the buffer (0 - 255). + */ +int +buffer_get_char(Buffer *buffer) { - char ch; - buffer_get(buffer, &ch, 1); - return (unsigned char)ch; + char ch; + buffer_get(buffer, &ch, 1); + return (unsigned char) ch; } -/* Stores a character in the buffer. */ - -void buffer_put_char(Buffer *buffer, int value) +/* + * Stores a character in the buffer. + */ +void +buffer_put_char(Buffer *buffer, int value) { - char ch = value; - buffer_append(buffer, &ch, 1); + char ch = value; + buffer_append(buffer, &ch, 1); } diff --git a/bufaux.h b/bufaux.h index 17cd871a..faf0cbe8 100644 --- a/bufaux.h +++ b/bufaux.h @@ -1,15 +1,15 @@ /* - -bufaux.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Wed Mar 29 02:18:23 1995 ylo - -*/ + * + * bufaux.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Wed Mar 29 02:18:23 1995 ylo + * + */ /* RCSID("$Id$"); */ @@ -20,22 +20,22 @@ Created: Wed Mar 29 02:18:23 1995 ylo /* Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed by (bits+7)/8 bytes of binary data, msb first. */ -void buffer_put_bignum(Buffer *buffer, BIGNUM *value); +void buffer_put_bignum(Buffer * buffer, BIGNUM * value); /* Retrieves an BIGNUM from the buffer. */ -int buffer_get_bignum(Buffer *buffer, BIGNUM *value); +int buffer_get_bignum(Buffer * buffer, BIGNUM * value); /* Returns an integer from the buffer (4 bytes, msb first). */ -unsigned int buffer_get_int(Buffer *buffer); +unsigned int buffer_get_int(Buffer * buffer); /* Stores an integer in the buffer in 4 bytes, msb first. */ -void buffer_put_int(Buffer *buffer, unsigned int value); +void buffer_put_int(Buffer * buffer, unsigned int value); /* Returns a character from the buffer (0 - 255). */ -int buffer_get_char(Buffer *buffer); +int buffer_get_char(Buffer * buffer); /* Stores a character in the buffer. */ -void buffer_put_char(Buffer *buffer, int value); +void buffer_put_char(Buffer * buffer, int value); /* Returns an arbitrary binary string from the buffer. The string cannot be longer than 256k. The returned value points to memory allocated @@ -43,9 +43,9 @@ void buffer_put_char(Buffer *buffer, int value); the data. If length_ptr is non-NULL, the length of the returned data will be stored there. A null character will be automatically appended to the returned string, and is not counted in length. */ -char *buffer_get_string(Buffer *buffer, unsigned int *length_ptr); +char *buffer_get_string(Buffer * buffer, unsigned int *length_ptr); /* Stores and arbitrary binary string in the buffer. */ -void buffer_put_string(Buffer *buffer, const void *buf, unsigned int len); +void buffer_put_string(Buffer * buffer, const void *buf, unsigned int len); -#endif /* BUFAUX_H */ +#endif /* BUFAUX_H */ diff --git a/buffer.c b/buffer.c index cf5d9e1f..4cab175b 100644 --- a/buffer.c +++ b/buffer.c @@ -1,17 +1,17 @@ /* - -buffer.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sat Mar 18 04:15:33 1995 ylo - -Functions for manipulating fifo buffers (that can grow if needed). - -*/ + * + * buffer.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Sat Mar 18 04:15:33 1995 ylo + * + * Functions for manipulating fifo buffers (that can grow if needed). + * + */ #include "includes.h" RCSID("$Id$"); @@ -22,129 +22,134 @@ RCSID("$Id$"); /* Initializes the buffer structure. */ -void buffer_init(Buffer *buffer) +void +buffer_init(Buffer *buffer) { - buffer->alloc = 4096; - buffer->buf = xmalloc(buffer->alloc); - buffer->offset = 0; - buffer->end = 0; + buffer->alloc = 4096; + buffer->buf = xmalloc(buffer->alloc); + buffer->offset = 0; + buffer->end = 0; } /* Frees any memory used for the buffer. */ -void buffer_free(Buffer *buffer) +void +buffer_free(Buffer *buffer) { - memset(buffer->buf, 0, buffer->alloc); - xfree(buffer->buf); + memset(buffer->buf, 0, buffer->alloc); + xfree(buffer->buf); } /* Clears any data from the buffer, making it empty. This does not actually zero the memory. */ -void buffer_clear(Buffer *buffer) +void +buffer_clear(Buffer *buffer) { - buffer->offset = 0; - buffer->end = 0; + buffer->offset = 0; + buffer->end = 0; } /* Appends data to the buffer, expanding it if necessary. */ -void buffer_append(Buffer *buffer, const char *data, unsigned int len) +void +buffer_append(Buffer *buffer, const char *data, unsigned int len) { - char *cp; - buffer_append_space(buffer, &cp, len); - memcpy(cp, data, len); + char *cp; + buffer_append_space(buffer, &cp, len); + memcpy(cp, data, len); } /* Appends space to the buffer, expanding the buffer if necessary. This does not actually copy the data into the buffer, but instead returns a pointer to the allocated region. */ -void buffer_append_space(Buffer *buffer, char **datap, unsigned int len) +void +buffer_append_space(Buffer *buffer, char **datap, unsigned int len) { - /* If the buffer is empty, start using it from the beginning. */ - if (buffer->offset == buffer->end) - { - buffer->offset = 0; - buffer->end = 0; - } - - restart: - /* If there is enough space to store all data, store it now. */ - if (buffer->end + len < buffer->alloc) - { - *datap = buffer->buf + buffer->end; - buffer->end += len; - return; - } - - /* If the buffer is quite empty, but all data is at the end, move the - data to the beginning and retry. */ - if (buffer->offset > buffer->alloc / 2) - { - memmove(buffer->buf, buffer->buf + buffer->offset, - buffer->end - buffer->offset); - buffer->end -= buffer->offset; - buffer->offset = 0; - goto restart; - } - - /* Increase the size of the buffer and retry. */ - buffer->alloc += len + 32768; - buffer->buf = xrealloc(buffer->buf, buffer->alloc); - goto restart; + /* If the buffer is empty, start using it from the beginning. */ + if (buffer->offset == buffer->end) { + buffer->offset = 0; + buffer->end = 0; + } +restart: + /* If there is enough space to store all data, store it now. */ + if (buffer->end + len < buffer->alloc) { + *datap = buffer->buf + buffer->end; + buffer->end += len; + return; + } + /* If the buffer is quite empty, but all data is at the end, move + the data to the beginning and retry. */ + if (buffer->offset > buffer->alloc / 2) { + memmove(buffer->buf, buffer->buf + buffer->offset, + buffer->end - buffer->offset); + buffer->end -= buffer->offset; + buffer->offset = 0; + goto restart; + } + /* Increase the size of the buffer and retry. */ + buffer->alloc += len + 32768; + buffer->buf = xrealloc(buffer->buf, buffer->alloc); + goto restart; } /* Returns the number of bytes of data in the buffer. */ -unsigned int buffer_len(Buffer *buffer) +unsigned int +buffer_len(Buffer *buffer) { - return buffer->end - buffer->offset; + return buffer->end - buffer->offset; } /* Gets data from the beginning of the buffer. */ -void buffer_get(Buffer *buffer, char *buf, unsigned int len) +void +buffer_get(Buffer *buffer, char *buf, unsigned int len) { - if (len > buffer->end - buffer->offset) - fatal("buffer_get trying to get more bytes than in buffer"); - memcpy(buf, buffer->buf + buffer->offset, len); - buffer->offset += len; + if (len > buffer->end - buffer->offset) + fatal("buffer_get trying to get more bytes than in buffer"); + memcpy(buf, buffer->buf + buffer->offset, len); + buffer->offset += len; } /* Consumes the given number of bytes from the beginning of the buffer. */ -void buffer_consume(Buffer *buffer, unsigned int bytes) +void +buffer_consume(Buffer *buffer, unsigned int bytes) { - if (bytes > buffer->end - buffer->offset) - fatal("buffer_get trying to get more bytes than in buffer"); - buffer->offset += bytes; -} + if (bytes > buffer->end - buffer->offset) + fatal("buffer_get trying to get more bytes than in buffer"); + buffer->offset += bytes; +} /* Consumes the given number of bytes from the end of the buffer. */ -void buffer_consume_end(Buffer *buffer, unsigned int bytes) +void +buffer_consume_end(Buffer *buffer, unsigned int bytes) { - if (bytes > buffer->end - buffer->offset) - fatal("buffer_get trying to get more bytes than in buffer"); - buffer->end -= bytes; -} + if (bytes > buffer->end - buffer->offset) + fatal("buffer_get trying to get more bytes than in buffer"); + buffer->end -= bytes; +} /* Returns a pointer to the first used byte in the buffer. */ -char *buffer_ptr(Buffer *buffer) +char * +buffer_ptr(Buffer *buffer) { - return buffer->buf + buffer->offset; + return buffer->buf + buffer->offset; } /* Dumps the contents of the buffer to stderr. */ -void buffer_dump(Buffer *buffer) +void +buffer_dump(Buffer *buffer) { - int i; - unsigned char *ucp = (unsigned char *)buffer->buf; - - for (i = buffer->offset; i < buffer->end; i++) - fprintf(stderr, " %02x", ucp[i]); - fprintf(stderr, "\n"); + int i; + unsigned char *ucp = (unsigned char *) buffer->buf; + + for (i = buffer->offset; i < buffer->end; i++) + fprintf(stderr, " %02x", ucp[i]); + fprintf(stderr, "\n"); } diff --git a/buffer.h b/buffer.h index 681ad4ef..3a8f14c5 100644 --- a/buffer.h +++ b/buffer.h @@ -1,66 +1,64 @@ /* - -buffer.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sat Mar 18 04:12:25 1995 ylo - -Code for manipulating FIFO buffers. - -*/ + * + * buffer.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Sat Mar 18 04:12:25 1995 ylo + * + * Code for manipulating FIFO buffers. + * + */ /* RCSID("$Id$"); */ #ifndef BUFFER_H #define BUFFER_H -typedef struct -{ - char *buf; /* Buffer for data. */ - unsigned int alloc; /* Number of bytes allocated for data. */ - unsigned int offset; /* Offset of first byte containing data. */ - unsigned int end; /* Offset of last byte containing data. */ -} Buffer; - +typedef struct { + char *buf; /* Buffer for data. */ + unsigned int alloc; /* Number of bytes allocated for data. */ + unsigned int offset; /* Offset of first byte containing data. */ + unsigned int end; /* Offset of last byte containing data. */ +} Buffer; /* Initializes the buffer structure. */ -void buffer_init(Buffer *buffer); +void buffer_init(Buffer * buffer); /* Frees any memory used for the buffer. */ -void buffer_free(Buffer *buffer); +void buffer_free(Buffer * buffer); /* Clears any data from the buffer, making it empty. This does not actually zero the memory. */ -void buffer_clear(Buffer *buffer); +void buffer_clear(Buffer * buffer); /* Appends data to the buffer, expanding it if necessary. */ -void buffer_append(Buffer *buffer, const char *data, unsigned int len); +void buffer_append(Buffer * buffer, const char *data, unsigned int len); /* Appends space to the buffer, expanding the buffer if necessary. This does not actually copy the data into the buffer, but instead returns a pointer to the allocated region. */ -void buffer_append_space(Buffer *buffer, char **datap, unsigned int len); +void buffer_append_space(Buffer * buffer, char **datap, unsigned int len); /* Returns the number of bytes of data in the buffer. */ -unsigned int buffer_len(Buffer *buffer); +unsigned int buffer_len(Buffer * buffer); /* Gets data from the beginning of the buffer. */ -void buffer_get(Buffer *buffer, char *buf, unsigned int len); +void buffer_get(Buffer * buffer, char *buf, unsigned int len); /* Consumes the given number of bytes from the beginning of the buffer. */ -void buffer_consume(Buffer *buffer, unsigned int bytes); +void buffer_consume(Buffer * buffer, unsigned int bytes); /* Consumes the given number of bytes from the end of the buffer. */ -void buffer_consume_end(Buffer *buffer, unsigned int bytes); +void buffer_consume_end(Buffer * buffer, unsigned int bytes); /* Returns a pointer to the first used byte in the buffer. */ -char *buffer_ptr(Buffer *buffer); +char *buffer_ptr(Buffer * buffer); /* Dumps the contents of the buffer to stderr in hex. This intended for debugging purposes only. */ -void buffer_dump(Buffer *buffer); +void buffer_dump(Buffer * buffer); -#endif /* BUFFER_H */ +#endif /* BUFFER_H */ diff --git a/canohost.c b/canohost.c index 009b5253..4aa6c40d 100644 --- a/canohost.c +++ b/canohost.c @@ -1,17 +1,17 @@ /* - -canohost.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sun Jul 2 17:52:22 1995 ylo - -Functions for returning the canonical host name of the remote site. - -*/ + * + * canohost.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Sun Jul 2 17:52:22 1995 ylo + * + * Functions for returning the canonical host name of the remote site. + * + */ #include "includes.h" RCSID("$Id$"); @@ -20,214 +20,209 @@ RCSID("$Id$"); #include "xmalloc.h" #include "ssh.h" -/* Return the canonical name of the host at the other end of the socket. +/* Return the canonical name of the host at the other end of the socket. The caller should free the returned string with xfree. */ -char *get_remote_hostname(int socket) +char * +get_remote_hostname(int socket) { - struct sockaddr_in from; - int fromlen, i; - struct hostent *hp; - char name[MAXHOSTNAMELEN]; - - /* Get IP address of client. */ - fromlen = sizeof(from); - memset(&from, 0, sizeof(from)); - if (getpeername(socket, (struct sockaddr *)&from, &fromlen) < 0) - { - debug("getpeername failed: %.100s", strerror(errno)); - fatal_cleanup(); - } - - /* Map the IP address to a host name. */ - hp = gethostbyaddr((char *)&from.sin_addr, sizeof(struct in_addr), - from.sin_family); - if (hp) - { - /* Got host name, find canonic host name. */ - if (strchr(hp->h_name, '.') != 0) - strlcpy(name, hp->h_name, sizeof(name)); - else if (hp->h_aliases != 0 - && hp->h_aliases[0] != 0 - && strchr(hp->h_aliases[0], '.') != 0) - strlcpy(name, hp->h_aliases[0], sizeof(name)); - else - strlcpy(name, hp->h_name, sizeof(name)); - - /* Convert it to all lowercase (which is expected by the rest of this - software). */ - for (i = 0; name[i]; i++) - if (isupper(name[i])) - name[i] = tolower(name[i]); - - /* Map it back to an IP address and check that the given address actually - is an address of this host. This is necessary because anyone with - access to a name server can define arbitrary names for an IP address. - Mapping from name to IP address can be trusted better (but can still - be fooled if the intruder has access to the name server of the - domain). */ - hp = gethostbyname(name); - if (!hp) - { - log("reverse mapping checking gethostbyname for %.700s failed - POSSIBLE BREAKIN ATTEMPT!", name); - strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); - goto check_ip_options; + struct sockaddr_in from; + int fromlen, i; + struct hostent *hp; + char name[MAXHOSTNAMELEN]; + + /* Get IP address of client. */ + fromlen = sizeof(from); + memset(&from, 0, sizeof(from)); + if (getpeername(socket, (struct sockaddr *) & from, &fromlen) < 0) { + debug("getpeername failed: %.100s", strerror(errno)); + fatal_cleanup(); + } + /* Map the IP address to a host name. */ + hp = gethostbyaddr((char *) &from.sin_addr, sizeof(struct in_addr), + from.sin_family); + if (hp) { + /* Got host name, find canonic host name. */ + if (strchr(hp->h_name, '.') != 0) + strlcpy(name, hp->h_name, sizeof(name)); + else if (hp->h_aliases != 0 + && hp->h_aliases[0] != 0 + && strchr(hp->h_aliases[0], '.') != 0) + strlcpy(name, hp->h_aliases[0], sizeof(name)); + else + strlcpy(name, hp->h_name, sizeof(name)); + + /* Convert it to all lowercase (which is expected by the + rest of this software). */ + for (i = 0; name[i]; i++) + if (isupper(name[i])) + name[i] = tolower(name[i]); + + /* Map it back to an IP address and check that the given + address actually is an address of this host. This is + necessary because anyone with access to a name server + can define arbitrary names for an IP address. Mapping + from name to IP address can be trusted better (but can + still be fooled if the intruder has access to the name + server of the domain). */ + hp = gethostbyname(name); + if (!hp) { + log("reverse mapping checking gethostbyname for %.700s failed - POSSIBLE BREAKIN ATTEMPT!", name); + strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); + goto check_ip_options; + } + /* Look for the address from the list of addresses. */ + for (i = 0; hp->h_addr_list[i]; i++) + if (memcmp(hp->h_addr_list[i], &from.sin_addr, sizeof(from.sin_addr)) + == 0) + break; + /* If we reached the end of the list, the address was not + there. */ + if (!hp->h_addr_list[i]) { + /* Address not found for the host name. */ + log("Address %.100s maps to %.600s, but this does not map back to the address - POSSIBLE BREAKIN ATTEMPT!", + inet_ntoa(from.sin_addr), name); + strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); + goto check_ip_options; + } + /* Address was found for the host name. We accept the host name. */ + } else { + /* Host name not found. Use ascii representation of the address. */ + strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); + log("Could not reverse map address %.100s.", name); } - /* Look for the address from the list of addresses. */ - for (i = 0; hp->h_addr_list[i]; i++) - if (memcmp(hp->h_addr_list[i], &from.sin_addr, sizeof(from.sin_addr)) - == 0) - break; - /* If we reached the end of the list, the address was not there. */ - if (!hp->h_addr_list[i]) + +check_ip_options: + + /* If IP options are supported, make sure there are none (log and + disconnect them if any are found). Basically we are worried + about source routing; it can be used to pretend you are + somebody (ip-address) you are not. That itself may be "almost + acceptable" under certain circumstances, but rhosts + autentication is useless if source routing is accepted. Notice + also that if we just dropped source routing here, the other + side could use IP spoofing to do rest of the interaction and + could still bypass security. So we exit here if we detect any + IP options. */ { - /* Address not found for the host name. */ - log("Address %.100s maps to %.600s, but this does not map back to the address - POSSIBLE BREAKIN ATTEMPT!", - inet_ntoa(from.sin_addr), name); - strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); - goto check_ip_options; + unsigned char options[200], *ucp; + char text[1024], *cp; + int option_size, ipproto; + struct protoent *ip; + + if ((ip = getprotobyname("ip")) != NULL) + ipproto = ip->p_proto; + else + ipproto = IPPROTO_IP; + option_size = sizeof(options); + if (getsockopt(0, ipproto, IP_OPTIONS, (char *) options, + &option_size) >= 0 && option_size != 0) { + cp = text; + /* Note: "text" buffer must be at least 3x as big as options. */ + for (ucp = options; option_size > 0; ucp++, option_size--, cp += 3) + sprintf(cp, " %2.2x", *ucp); + log("Connection from %.100s with IP options:%.800s", + inet_ntoa(from.sin_addr), text); + packet_disconnect("Connection from %.100s with IP options:%.800s", + inet_ntoa(from.sin_addr), text); + } } - /* Address was found for the host name. We accept the host name. */ - } - else - { - /* Host name not found. Use ascii representation of the address. */ - strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); - log("Could not reverse map address %.100s.", name); - } - - check_ip_options: - - /* If IP options are supported, make sure there are none (log and disconnect - them if any are found). Basically we are worried about source routing; - it can be used to pretend you are somebody (ip-address) you are not. - That itself may be "almost acceptable" under certain circumstances, - but rhosts autentication is useless if source routing is accepted. - Notice also that if we just dropped source routing here, the other - side could use IP spoofing to do rest of the interaction and could still - bypass security. So we exit here if we detect any IP options. */ - { - unsigned char options[200], *ucp; - char text[1024], *cp; - int option_size, ipproto; - struct protoent *ip; - - if ((ip = getprotobyname("ip")) != NULL) - ipproto = ip->p_proto; - else - ipproto = IPPROTO_IP; - option_size = sizeof(options); - if (getsockopt(0, ipproto, IP_OPTIONS, (char *)options, - &option_size) >= 0 && option_size != 0) - { - cp = text; - /* Note: "text" buffer must be at least 3x as big as options. */ - for (ucp = options; option_size > 0; ucp++, option_size--, cp += 3) - sprintf(cp, " %2.2x", *ucp); - log("Connection from %.100s with IP options:%.800s", - inet_ntoa(from.sin_addr), text); - packet_disconnect("Connection from %.100s with IP options:%.800s", - inet_ntoa(from.sin_addr), text); - } - } - - return xstrdup(name); + + return xstrdup(name); } static char *canonical_host_name = NULL; static char *canonical_host_ip = NULL; /* Return the canonical name of the host in the other side of the current - connection. The host name is cached, so it is efficient to call this + connection. The host name is cached, so it is efficient to call this several times. */ -const char *get_canonical_hostname() +const char * +get_canonical_hostname() { - /* Check if we have previously retrieved this same name. */ - if (canonical_host_name != NULL) - return canonical_host_name; + /* Check if we have previously retrieved this same name. */ + if (canonical_host_name != NULL) + return canonical_host_name; - /* Get the real hostname if socket; otherwise return UNKNOWN. */ - if (packet_get_connection_in() == packet_get_connection_out()) - canonical_host_name = get_remote_hostname(packet_get_connection_in()); - else - canonical_host_name = xstrdup("UNKNOWN"); + /* Get the real hostname if socket; otherwise return UNKNOWN. */ + if (packet_get_connection_in() == packet_get_connection_out()) + canonical_host_name = get_remote_hostname(packet_get_connection_in()); + else + canonical_host_name = xstrdup("UNKNOWN"); - return canonical_host_name; + return canonical_host_name; } /* Returns the IP-address of the remote host as a string. The returned string need not be freed. */ -const char *get_remote_ipaddr() +const char * +get_remote_ipaddr() { - struct sockaddr_in from; - int fromlen, socket; - - /* Check if we have previously retrieved this same name. */ - if (canonical_host_ip != NULL) - return canonical_host_ip; - - /* If not a socket, return UNKNOWN. */ - if (packet_get_connection_in() != packet_get_connection_out()) - { - canonical_host_ip = xstrdup("UNKNOWN"); - return canonical_host_ip; - } - - /* Get client socket. */ - socket = packet_get_connection_in(); - - /* Get IP address of client. */ - fromlen = sizeof(from); - memset(&from, 0, sizeof(from)); - if (getpeername(socket, (struct sockaddr *)&from, &fromlen) < 0) - { - debug("getpeername failed: %.100s", strerror(errno)); - fatal_cleanup(); - } - - /* Get the IP address in ascii. */ - canonical_host_ip = xstrdup(inet_ntoa(from.sin_addr)); - - /* Return ip address string. */ - return canonical_host_ip; + struct sockaddr_in from; + int fromlen, socket; + + /* Check if we have previously retrieved this same name. */ + if (canonical_host_ip != NULL) + return canonical_host_ip; + + /* If not a socket, return UNKNOWN. */ + if (packet_get_connection_in() != packet_get_connection_out()) { + canonical_host_ip = xstrdup("UNKNOWN"); + return canonical_host_ip; + } + /* Get client socket. */ + socket = packet_get_connection_in(); + + /* Get IP address of client. */ + fromlen = sizeof(from); + memset(&from, 0, sizeof(from)); + if (getpeername(socket, (struct sockaddr *) & from, &fromlen) < 0) { + debug("getpeername failed: %.100s", strerror(errno)); + fatal_cleanup(); + } + /* Get the IP address in ascii. */ + canonical_host_ip = xstrdup(inet_ntoa(from.sin_addr)); + + /* Return ip address string. */ + return canonical_host_ip; } /* Returns the port of the peer of the socket. */ -int get_peer_port(int sock) +int +get_peer_port(int sock) { - struct sockaddr_in from; - int fromlen; - - /* Get IP address of client. */ - fromlen = sizeof(from); - memset(&from, 0, sizeof(from)); - if (getpeername(sock, (struct sockaddr *)&from, &fromlen) < 0) - { - debug("getpeername failed: %.100s", strerror(errno)); - fatal_cleanup(); - } - - /* Return port number. */ - return ntohs(from.sin_port); + struct sockaddr_in from; + int fromlen; + + /* Get IP address of client. */ + fromlen = sizeof(from); + memset(&from, 0, sizeof(from)); + if (getpeername(sock, (struct sockaddr *) & from, &fromlen) < 0) { + debug("getpeername failed: %.100s", strerror(errno)); + fatal_cleanup(); + } + /* Return port number. */ + return ntohs(from.sin_port); } /* Returns the port number of the remote host. */ -int get_remote_port() +int +get_remote_port() { - int socket; + int socket; - /* If the connection is not a socket, return 65535. This is intentionally - chosen to be an unprivileged port number. */ - if (packet_get_connection_in() != packet_get_connection_out()) - return 65535; + /* If the connection is not a socket, return 65535. This is + intentionally chosen to be an unprivileged port number. */ + if (packet_get_connection_in() != packet_get_connection_out()) + return 65535; - /* Get client socket. */ - socket = packet_get_connection_in(); + /* Get client socket. */ + socket = packet_get_connection_in(); - /* Get and return the peer port number. */ - return get_peer_port(socket); + /* Get and return the peer port number. */ + return get_peer_port(socket); } diff --git a/channels.c b/channels.c index 60a6216d..04c5f71e 100644 --- a/channels.c +++ b/channels.c @@ -1,19 +1,19 @@ /* - -channels.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Fri Mar 24 16:35:24 1995 ylo - -This file contains functions for generic socket connection forwarding. -There is also code for initiating connection forwarding for X11 connections, -arbitrary tcp/ip connections, and the authentication agent connection. - -*/ + * + * channels.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Fri Mar 24 16:35:24 1995 ylo + * + * This file contains functions for generic socket connection forwarding. + * There is also code for initiating connection forwarding for X11 connections, + * arbitrary tcp/ip connections, and the authentication agent connection. + * + */ #include "includes.h" RCSID("$Id$"); @@ -52,7 +52,7 @@ static int channel_max_fd_value = 0; /* Name and directory of socket for authentication agent forwarding. */ static char *channel_forwarded_auth_socket_name = NULL; -static char *channel_forwarded_auth_socket_dir = NULL; +static char *channel_forwarded_auth_socket_dir = NULL; /* Saved X11 authentication protocol name. */ char *x11_saved_proto = NULL; @@ -70,10 +70,9 @@ unsigned int x11_fake_data_len; The local sides of any remote forwards are stored in this array to prevent a corrupt remote server from accessing arbitrary TCP/IP ports on our local network (which might be behind a firewall). */ -typedef struct -{ - char *host; /* Host name. */ - int port; /* Port number. */ +typedef struct { + char *host; /* Host name. */ + int port; /* Port number. */ } ForwardPermission; /* List of all permitted host/port pairs to connect. */ @@ -90,1056 +89,1015 @@ static int have_hostname_in_open = 0; /* Sets specific protocol options. */ -void channel_set_options(int hostname_in_open) +void +channel_set_options(int hostname_in_open) { - have_hostname_in_open = hostname_in_open; + have_hostname_in_open = hostname_in_open; } /* Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually called by the server, because the user could connect to any port anyway, and the server has no way to know but to trust the client anyway. */ -void channel_permit_all_opens() +void +channel_permit_all_opens() { - all_opens_permitted = 1; + all_opens_permitted = 1; } -/* Allocate a new channel object and set its type and socket. +/* Allocate a new channel object and set its type and socket. This will cause remote_name to be freed. */ -int channel_allocate(int type, int sock, char *remote_name) +int +channel_allocate(int type, int sock, char *remote_name) { - int i, found; - Channel *c; - - /* Update the maximum file descriptor value. */ - if (sock > channel_max_fd_value) - channel_max_fd_value = sock; - - /* Do initial allocation if this is the first call. */ - if (channels_alloc == 0) - { - channels_alloc = 10; - channels = xmalloc(channels_alloc * sizeof(Channel)); - for (i = 0; i < channels_alloc; i++) - channels[i].type = SSH_CHANNEL_FREE; - - /* Kludge: arrange a call to channel_stop_listening if we terminate - with fatal(). */ - fatal_add_cleanup((void (*)(void *))channel_stop_listening, NULL); - } - - /* Try to find a free slot where to put the new channel. */ - for (found = -1, i = 0; i < channels_alloc; i++) - if (channels[i].type == SSH_CHANNEL_FREE) - { - /* Found a free slot. */ - found = i; - break; - } - - if (found == -1) - { - /* There are no free slots. Take last+1 slot and expand the array. */ - found = channels_alloc; - channels_alloc += 10; - debug("channel: expanding %d", channels_alloc); - channels = xrealloc(channels, channels_alloc * sizeof(Channel)); - for (i = found; i < channels_alloc; i++) - channels[i].type = SSH_CHANNEL_FREE; - } - - /* Initialize and return new channel number. */ - c=&channels[found]; - buffer_init(&c->input); - buffer_init(&c->output); - chan_init_iostates(c); - c->self = found; - c->type = type; - c->sock = sock; - c->remote_id = -1; - c->remote_name = remote_name; - debug("channel %d: new [%s]", found, remote_name); - return found; + int i, found; + Channel *c; + + /* Update the maximum file descriptor value. */ + if (sock > channel_max_fd_value) + channel_max_fd_value = sock; + + /* Do initial allocation if this is the first call. */ + if (channels_alloc == 0) { + channels_alloc = 10; + channels = xmalloc(channels_alloc * sizeof(Channel)); + for (i = 0; i < channels_alloc; i++) + channels[i].type = SSH_CHANNEL_FREE; + + /* Kludge: arrange a call to channel_stop_listening if we + terminate with fatal(). */ + fatal_add_cleanup((void (*) (void *)) channel_stop_listening, NULL); + } + /* Try to find a free slot where to put the new channel. */ + for (found = -1, i = 0; i < channels_alloc; i++) + if (channels[i].type == SSH_CHANNEL_FREE) { + /* Found a free slot. */ + found = i; + break; + } + if (found == -1) { + /* There are no free slots. Take last+1 slot and expand + the array. */ + found = channels_alloc; + channels_alloc += 10; + debug("channel: expanding %d", channels_alloc); + channels = xrealloc(channels, channels_alloc * sizeof(Channel)); + for (i = found; i < channels_alloc; i++) + channels[i].type = SSH_CHANNEL_FREE; + } + /* Initialize and return new channel number. */ + c = &channels[found]; + buffer_init(&c->input); + buffer_init(&c->output); + chan_init_iostates(c); + c->self = found; + c->type = type; + c->sock = sock; + c->remote_id = -1; + c->remote_name = remote_name; + debug("channel %d: new [%s]", found, remote_name); + return found; } /* Free the channel and close its socket. */ -void channel_free(int channel) +void +channel_free(int channel) { - if (channel < 0 || channel >= channels_alloc || - channels[channel].type == SSH_CHANNEL_FREE) - packet_disconnect("channel free: bad local channel %d", channel); - - if(compat13) - shutdown(channels[channel].sock, SHUT_RDWR); - close(channels[channel].sock); - buffer_free(&channels[channel].input); - buffer_free(&channels[channel].output); - channels[channel].type = SSH_CHANNEL_FREE; - if (channels[channel].remote_name) - { - xfree(channels[channel].remote_name); - channels[channel].remote_name = NULL; - } + if (channel < 0 || channel >= channels_alloc || + channels[channel].type == SSH_CHANNEL_FREE) + packet_disconnect("channel free: bad local channel %d", channel); + + if (compat13) + shutdown(channels[channel].sock, SHUT_RDWR); + close(channels[channel].sock); + buffer_free(&channels[channel].input); + buffer_free(&channels[channel].output); + channels[channel].type = SSH_CHANNEL_FREE; + if (channels[channel].remote_name) { + xfree(channels[channel].remote_name); + channels[channel].remote_name = NULL; + } } /* This is called just before select() to add any bits relevant to channels in the select bitmasks. */ -void channel_prepare_select(fd_set *readset, fd_set *writeset) +void +channel_prepare_select(fd_set * readset, fd_set * writeset) { - int i; - Channel *ch; - unsigned char *ucp; - unsigned int proto_len, data_len; - - for (i = 0; i < channels_alloc; i++) - { - ch = &channels[i]; - redo: - switch (ch->type) - { - case SSH_CHANNEL_X11_LISTENER: - case SSH_CHANNEL_PORT_LISTENER: - case SSH_CHANNEL_AUTH_SOCKET: - FD_SET(ch->sock, readset); - break; - - case SSH_CHANNEL_OPEN: - if(compat13){ - if (buffer_len(&ch->input) < packet_get_maxsize()) - FD_SET(ch->sock, readset); - if (buffer_len(&ch->output) > 0) - FD_SET(ch->sock, writeset); - break; - } - /* test whether sockets are 'alive' for read/write */ - if (ch->istate == CHAN_INPUT_OPEN) - if (buffer_len(&ch->input) < packet_get_maxsize()) - FD_SET(ch->sock, readset); - if (ch->ostate == CHAN_OUTPUT_OPEN || ch->ostate == CHAN_OUTPUT_WAIT_DRAIN){ - if (buffer_len(&ch->output) > 0){ - FD_SET(ch->sock, writeset); - }else if(ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) { - chan_obuf_empty(ch); - } - } - break; - - case SSH_CHANNEL_INPUT_DRAINING: - if (!compat13) - fatal("cannot happen: IN_DRAIN"); - if (buffer_len(&ch->input) == 0) - { - packet_start(SSH_MSG_CHANNEL_CLOSE); - packet_put_int(ch->remote_id); - packet_send(); - ch->type = SSH_CHANNEL_CLOSED; - debug("Closing channel %d after input drain.", i); - break; - } - break; - - case SSH_CHANNEL_OUTPUT_DRAINING: - if (!compat13) - fatal("cannot happen: OUT_DRAIN"); - if (buffer_len(&ch->output) == 0) - { - /* debug("Freeing channel %d after output drain.", i); */ - channel_free(i); - break; - } - FD_SET(ch->sock, writeset); - break; - - case SSH_CHANNEL_X11_OPEN: - /* This is a special state for X11 authentication spoofing. An - opened X11 connection (when authentication spoofing is being - done) remains in this state until the first packet has been - completely read. The authentication data in that packet is - then substituted by the real data if it matches the fake data, - and the channel is put into normal mode. */ - - /* Check if the fixed size part of the packet is in buffer. */ - if (buffer_len(&ch->output) < 12) - break; - - /* Parse the lengths of variable-length fields. */ - ucp = (unsigned char *)buffer_ptr(&ch->output); - if (ucp[0] == 0x42) - { /* Byte order MSB first. */ - proto_len = 256 * ucp[6] + ucp[7]; - data_len = 256 * ucp[8] + ucp[9]; - } - else - if (ucp[0] == 0x6c) - { /* Byte order LSB first. */ - proto_len = ucp[6] + 256 * ucp[7]; - data_len = ucp[8] + 256 * ucp[9]; - } - else - { - debug("Initial X11 packet contains bad byte order byte: 0x%x", - ucp[0]); - ch->type = SSH_CHANNEL_OPEN; - goto reject; - } - - /* Check if the whole packet is in buffer. */ - if (buffer_len(&ch->output) < - 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) - break; - - /* Check if authentication protocol matches. */ - if (proto_len != strlen(x11_saved_proto) || - memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) - { - debug("X11 connection uses different authentication protocol."); - ch->type = SSH_CHANNEL_OPEN; - goto reject; - } - - /* Check if authentication data matches our fake data. */ - if (data_len != x11_fake_data_len || - memcmp(ucp + 12 + ((proto_len + 3) & ~3), - x11_fake_data, x11_fake_data_len) != 0) - { - debug("X11 auth data does not match fake data."); - ch->type = SSH_CHANNEL_OPEN; - goto reject; - } - - /* Check fake data length */ - if (x11_fake_data_len != x11_saved_data_len) - { - error("X11 fake_data_len %d != saved_data_len %d", - x11_fake_data_len, x11_saved_data_len); - ch->type = SSH_CHANNEL_OPEN; - goto reject; - } - - /* Received authentication protocol and data match our fake data. - Substitute the fake data with real data. */ - memcpy(ucp + 12 + ((proto_len + 3) & ~3), - x11_saved_data, x11_saved_data_len); - - /* Start normal processing for the channel. */ - ch->type = SSH_CHANNEL_OPEN; - goto redo; - + int i; + Channel *ch; + unsigned char *ucp; + unsigned int proto_len, data_len; + + for (i = 0; i < channels_alloc; i++) { + ch = &channels[i]; +redo: + switch (ch->type) { + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_AUTH_SOCKET: + FD_SET(ch->sock, readset); + break; + + case SSH_CHANNEL_OPEN: + if (compat13) { + if (buffer_len(&ch->input) < packet_get_maxsize()) + FD_SET(ch->sock, readset); + if (buffer_len(&ch->output) > 0) + FD_SET(ch->sock, writeset); + break; + } + /* test whether sockets are 'alive' for read/write */ + if (ch->istate == CHAN_INPUT_OPEN) + if (buffer_len(&ch->input) < packet_get_maxsize()) + FD_SET(ch->sock, readset); + if (ch->ostate == CHAN_OUTPUT_OPEN || + ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + if (buffer_len(&ch->output) > 0) { + FD_SET(ch->sock, writeset); + } else if (ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + chan_obuf_empty(ch); + } + } + break; + + case SSH_CHANNEL_INPUT_DRAINING: + if (!compat13) + fatal("cannot happen: IN_DRAIN"); + if (buffer_len(&ch->input) == 0) { + packet_start(SSH_MSG_CHANNEL_CLOSE); + packet_put_int(ch->remote_id); + packet_send(); + ch->type = SSH_CHANNEL_CLOSED; + debug("Closing channel %d after input drain.", i); + break; + } + break; + + case SSH_CHANNEL_OUTPUT_DRAINING: + if (!compat13) + fatal("cannot happen: OUT_DRAIN"); + if (buffer_len(&ch->output) == 0) { + channel_free(i); + break; + } + FD_SET(ch->sock, writeset); + break; + + case SSH_CHANNEL_X11_OPEN: + /* This is a special state for X11 authentication + spoofing. An opened X11 connection (when + authentication spoofing is being done) remains + in this state until the first packet has been + completely read. The authentication data in + that packet is then substituted by the real + data if it matches the fake data, and the + channel is put into normal mode. */ + + /* Check if the fixed size part of the packet is in buffer. */ + if (buffer_len(&ch->output) < 12) + break; + + /* Parse the lengths of variable-length fields. */ + ucp = (unsigned char *) buffer_ptr(&ch->output); + if (ucp[0] == 0x42) { /* Byte order MSB first. */ + proto_len = 256 * ucp[6] + ucp[7]; + data_len = 256 * ucp[8] + ucp[9]; + } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ + proto_len = ucp[6] + 256 * ucp[7]; + data_len = ucp[8] + 256 * ucp[9]; + } else { + debug("Initial X11 packet contains bad byte order byte: 0x%x", + ucp[0]); + ch->type = SSH_CHANNEL_OPEN; + goto reject; + } + + /* Check if the whole packet is in buffer. */ + if (buffer_len(&ch->output) < + 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) + break; + + /* Check if authentication protocol matches. */ + if (proto_len != strlen(x11_saved_proto) || + memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { + debug("X11 connection uses different authentication protocol."); + ch->type = SSH_CHANNEL_OPEN; + goto reject; + } + /* Check if authentication data matches our fake data. */ + if (data_len != x11_fake_data_len || + memcmp(ucp + 12 + ((proto_len + 3) & ~3), + x11_fake_data, x11_fake_data_len) != 0) { + debug("X11 auth data does not match fake data."); + ch->type = SSH_CHANNEL_OPEN; + goto reject; + } + /* Check fake data length */ + if (x11_fake_data_len != x11_saved_data_len) { + error("X11 fake_data_len %d != saved_data_len %d", + x11_fake_data_len, x11_saved_data_len); + ch->type = SSH_CHANNEL_OPEN; + goto reject; + } + /* Received authentication protocol and data match + our fake data. Substitute the fake data with + real data. */ + memcpy(ucp + 12 + ((proto_len + 3) & ~3), + x11_saved_data, x11_saved_data_len); + + /* Start normal processing for the channel. */ + ch->type = SSH_CHANNEL_OPEN; + goto redo; + reject: - /* We have received an X11 connection that has bad authentication - information. */ - log("X11 connection rejected because of wrong authentication.\r\n"); - buffer_clear(&ch->input); - buffer_clear(&ch->output); - if (compat13) { - close(ch->sock); - ch->sock = -1; - ch->type = SSH_CHANNEL_CLOSED; - packet_start(SSH_MSG_CHANNEL_CLOSE); - packet_put_int(ch->remote_id); - packet_send(); - }else{ - debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate); - chan_read_failed(ch); - chan_write_failed(ch); - debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate); - } - break; - - case SSH_CHANNEL_FREE: - default: - continue; + /* We have received an X11 connection that has bad + authentication information. */ + log("X11 connection rejected because of wrong authentication.\r\n"); + buffer_clear(&ch->input); + buffer_clear(&ch->output); + if (compat13) { + close(ch->sock); + ch->sock = -1; + ch->type = SSH_CHANNEL_CLOSED; + packet_start(SSH_MSG_CHANNEL_CLOSE); + packet_put_int(ch->remote_id); + packet_send(); + } else { + debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate); + chan_read_failed(ch); + chan_write_failed(ch); + debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate); + } + break; + + case SSH_CHANNEL_FREE: + default: + continue; + } } - } } /* After select, perform any appropriate operations for channels which have events pending. */ -void channel_after_select(fd_set *readset, fd_set *writeset) +void +channel_after_select(fd_set * readset, fd_set * writeset) { - struct sockaddr addr; - int addrlen, newsock, i, newch, len; - Channel *ch; - char buf[16384], *remote_hostname; - - /* Loop over all channels... */ - for (i = 0; i < channels_alloc; i++) - { - ch = &channels[i]; - switch (ch->type) - { - case SSH_CHANNEL_X11_LISTENER: - /* This is our fake X11 server socket. */ - if (FD_ISSET(ch->sock, readset)) - { - debug("X11 connection requested."); - addrlen = sizeof(addr); - newsock = accept(ch->sock, &addr, &addrlen); - if (newsock < 0) - { - error("accept: %.100s", strerror(errno)); - break; - } - remote_hostname = get_remote_hostname(newsock); - snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", - remote_hostname, get_peer_port(newsock)); - xfree(remote_hostname); - newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, - xstrdup(buf)); - packet_start(SSH_SMSG_X11_OPEN); - packet_put_int(newch); - if (have_hostname_in_open) - packet_put_string(buf, strlen(buf)); - packet_send(); - } - break; - - case SSH_CHANNEL_PORT_LISTENER: - /* This socket is listening for connections to a forwarded TCP/IP - port. */ - if (FD_ISSET(ch->sock, readset)) - { - debug("Connection to port %d forwarding to %.100s:%d requested.", - ch->listening_port, ch->path, ch->host_port); - addrlen = sizeof(addr); - newsock = accept(ch->sock, &addr, &addrlen); - if (newsock < 0) - { - error("accept: %.100s", strerror(errno)); - break; - } - remote_hostname = get_remote_hostname(newsock); - snprintf(buf, sizeof buf, "listen port %d:%.100s:%d, connect from %.200s:%d", - ch->listening_port, ch->path, ch->host_port, - remote_hostname, get_peer_port(newsock)); - xfree(remote_hostname); - newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, - xstrdup(buf)); - packet_start(SSH_MSG_PORT_OPEN); - packet_put_int(newch); - packet_put_string(ch->path, strlen(ch->path)); - packet_put_int(ch->host_port); - if (have_hostname_in_open) - packet_put_string(buf, strlen(buf)); - packet_send(); - } - break; - - case SSH_CHANNEL_AUTH_SOCKET: - /* This is the authentication agent socket listening for connections - from clients. */ - if (FD_ISSET(ch->sock, readset)) - { - int nchan; - len = sizeof(addr); - newsock = accept(ch->sock, &addr, &len); - if (newsock < 0) - { - error("accept from auth socket: %.100s", strerror(errno)); - break; + struct sockaddr addr; + int addrlen, newsock, i, newch, len; + Channel *ch; + char buf[16384], *remote_hostname; + + /* Loop over all channels... */ + for (i = 0; i < channels_alloc; i++) { + ch = &channels[i]; + switch (ch->type) { + case SSH_CHANNEL_X11_LISTENER: + /* This is our fake X11 server socket. */ + if (FD_ISSET(ch->sock, readset)) { + debug("X11 connection requested."); + addrlen = sizeof(addr); + newsock = accept(ch->sock, &addr, &addrlen); + if (newsock < 0) { + error("accept: %.100s", strerror(errno)); + break; + } + remote_hostname = get_remote_hostname(newsock); + snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", + remote_hostname, get_peer_port(newsock)); + xfree(remote_hostname); + newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, + xstrdup(buf)); + packet_start(SSH_SMSG_X11_OPEN); + packet_put_int(newch); + if (have_hostname_in_open) + packet_put_string(buf, strlen(buf)); + packet_send(); + } + break; + + case SSH_CHANNEL_PORT_LISTENER: + /* This socket is listening for connections to a + forwarded TCP/IP port. */ + if (FD_ISSET(ch->sock, readset)) { + debug("Connection to port %d forwarding to %.100s:%d requested.", + ch->listening_port, ch->path, ch->host_port); + addrlen = sizeof(addr); + newsock = accept(ch->sock, &addr, &addrlen); + if (newsock < 0) { + error("accept: %.100s", strerror(errno)); + break; + } + remote_hostname = get_remote_hostname(newsock); + snprintf(buf, sizeof buf, "listen port %d:%.100s:%d, connect from %.200s:%d", + ch->listening_port, ch->path, ch->host_port, + remote_hostname, get_peer_port(newsock)); + xfree(remote_hostname); + newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, + xstrdup(buf)); + packet_start(SSH_MSG_PORT_OPEN); + packet_put_int(newch); + packet_put_string(ch->path, strlen(ch->path)); + packet_put_int(ch->host_port); + if (have_hostname_in_open) + packet_put_string(buf, strlen(buf)); + packet_send(); + } + break; + + case SSH_CHANNEL_AUTH_SOCKET: + /* This is the authentication agent socket + listening for connections from clients. */ + if (FD_ISSET(ch->sock, readset)) { + int nchan; + len = sizeof(addr); + newsock = accept(ch->sock, &addr, &len); + if (newsock < 0) { + error("accept from auth socket: %.100s", strerror(errno)); + break; + } + nchan = channel_allocate(SSH_CHANNEL_OPENING, newsock, + xstrdup("accepted auth socket")); + packet_start(SSH_SMSG_AGENT_OPEN); + packet_put_int(nchan); + packet_send(); + } + break; + + case SSH_CHANNEL_OPEN: + /* This is an open two-way communication channel. + It is not of interest to us at this point what + kind of data is being transmitted. */ + + /* Read available incoming data and append it to + buffer; shutdown socket, if read or write + failes */ + if (FD_ISSET(ch->sock, readset)) { + len = read(ch->sock, buf, sizeof(buf)); + if (len <= 0) { + if (compat13) { + buffer_consume(&ch->output, buffer_len(&ch->output)); + ch->type = SSH_CHANNEL_INPUT_DRAINING; + debug("Channel %d status set to input draining.", i); + } else { + chan_read_failed(ch); + } + break; + } + buffer_append(&ch->input, buf, len); + } + /* Send buffered output data to the socket. */ + if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) { + len = write(ch->sock, buffer_ptr(&ch->output), + buffer_len(&ch->output)); + if (len <= 0) { + if (compat13) { + buffer_consume(&ch->output, buffer_len(&ch->output)); + debug("Channel %d status set to input draining.", i); + ch->type = SSH_CHANNEL_INPUT_DRAINING; + } else { + chan_write_failed(ch); + } + break; + } + buffer_consume(&ch->output, len); + } + break; + + case SSH_CHANNEL_OUTPUT_DRAINING: + if (!compat13) + fatal("cannot happen: OUT_DRAIN"); + /* Send buffered output data to the socket. */ + if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) { + len = write(ch->sock, buffer_ptr(&ch->output), + buffer_len(&ch->output)); + if (len <= 0) + buffer_consume(&ch->output, buffer_len(&ch->output)); + else + buffer_consume(&ch->output, len); + } + break; + + case SSH_CHANNEL_X11_OPEN: + case SSH_CHANNEL_FREE: + default: + continue; } - - nchan = channel_allocate(SSH_CHANNEL_OPENING, newsock, - xstrdup("accepted auth socket")); - packet_start(SSH_SMSG_AGENT_OPEN); - packet_put_int(nchan); - packet_send(); - } - break; - - case SSH_CHANNEL_OPEN: - /* This is an open two-way communication channel. It is not of - interest to us at this point what kind of data is being - transmitted. */ - - /* Read available incoming data and append it to buffer; - shutdown socket, if read or write failes */ - if (FD_ISSET(ch->sock, readset)) - { - len = read(ch->sock, buf, sizeof(buf)); - if (len <= 0) - { - if (compat13) { - buffer_consume(&ch->output, buffer_len(&ch->output)); - ch->type = SSH_CHANNEL_INPUT_DRAINING; - debug("Channel %d status set to input draining.", i); - }else{ - chan_read_failed(ch); - } - break; - } - buffer_append(&ch->input, buf, len); - } - /* Send buffered output data to the socket. */ - if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) - { - len = write(ch->sock, buffer_ptr(&ch->output), - buffer_len(&ch->output)); - if (len <= 0) - { - if (compat13) { - buffer_consume(&ch->output, buffer_len(&ch->output)); - debug("Channel %d status set to input draining.", i); - ch->type = SSH_CHANNEL_INPUT_DRAINING; - }else{ - chan_write_failed(ch); - } - break; - } - buffer_consume(&ch->output, len); - } - break; - - case SSH_CHANNEL_OUTPUT_DRAINING: - if (!compat13) - fatal("cannot happen: OUT_DRAIN"); - /* Send buffered output data to the socket. */ - if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) - { - len = write(ch->sock, buffer_ptr(&ch->output), - buffer_len(&ch->output)); - if (len <= 0) - buffer_consume(&ch->output, buffer_len(&ch->output)); - else - buffer_consume(&ch->output, len); - } - break; - - case SSH_CHANNEL_X11_OPEN: - case SSH_CHANNEL_FREE: - default: - continue; } - } } /* If there is data to send to the connection, send some of it now. */ -void channel_output_poll() +void +channel_output_poll() { - int len, i; - Channel *ch; - - for (i = 0; i < channels_alloc; i++) - { - ch = &channels[i]; - /* We are only interested in channels that can have buffered incoming - data. */ - if (ch->type != SSH_CHANNEL_OPEN && - ch->type != SSH_CHANNEL_INPUT_DRAINING) - continue; - - /* Get the amount of buffered data for this channel. */ - len = buffer_len(&ch->input); - if (len > 0) - { - /* Send some data for the other side over the secure connection. */ - if (packet_is_interactive()) - { - if (len > 1024) - len = 512; - } - else - { - if (len > 16384) - len = 16384; /* Keep the packets at reasonable size. */ - } - packet_start(SSH_MSG_CHANNEL_DATA); - packet_put_int(ch->remote_id); - packet_put_string(buffer_ptr(&ch->input), len); - packet_send(); - buffer_consume(&ch->input, len); + int len, i; + Channel *ch; + + for (i = 0; i < channels_alloc; i++) { + ch = &channels[i]; + /* We are only interested in channels that can have + buffered incoming data. */ + if (ch->type != SSH_CHANNEL_OPEN && + ch->type != SSH_CHANNEL_INPUT_DRAINING) + continue; + + /* Get the amount of buffered data for this channel. */ + len = buffer_len(&ch->input); + if (len > 0) { + /* Send some data for the other side over the + secure connection. */ + if (packet_is_interactive()) { + if (len > 1024) + len = 512; + } else { + /* Keep the packets at reasonable size. */ + if (len > 16384) + len = 16384; + } + packet_start(SSH_MSG_CHANNEL_DATA); + packet_put_int(ch->remote_id); + packet_put_string(buffer_ptr(&ch->input), len); + packet_send(); + buffer_consume(&ch->input, len); + } else if (ch->istate == CHAN_INPUT_WAIT_DRAIN) { + if (compat13) + fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); + /* input-buffer is empty and read-socket shutdown: + tell peer, that we will not send more data: + send IEOF */ + chan_ibuf_empty(ch); + } } - else if(ch->istate == CHAN_INPUT_WAIT_DRAIN) - { - if (compat13) - fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); - /* input-buffer is empty and read-socket shutdown: - tell peer, that we will not send more data: send IEOF */ - chan_ibuf_empty(ch); - } - } } /* This is called when a packet of type CHANNEL_DATA has just been received. The message type has already been consumed, but channel number and data is still there. */ -void channel_input_data(int payload_len) +void +channel_input_data(int payload_len) { - int channel; - char *data; - unsigned int data_len; - - /* Get the channel number and verify it. */ - channel = packet_get_int(); - if (channel < 0 || channel >= channels_alloc || - channels[channel].type == SSH_CHANNEL_FREE) - packet_disconnect("Received data for nonexistent channel %d.", channel); - - /* Ignore any data for non-open channels (might happen on close) */ - if (channels[channel].type != SSH_CHANNEL_OPEN && - channels[channel].type != SSH_CHANNEL_X11_OPEN) - return; - - /* Get the data. */ - data = packet_get_string(&data_len); - packet_integrity_check(payload_len, 4 + 4+data_len, SSH_MSG_CHANNEL_DATA); - buffer_append(&channels[channel].output, data, data_len); - xfree(data); + int channel; + char *data; + unsigned int data_len; + + /* Get the channel number and verify it. */ + channel = packet_get_int(); + if (channel < 0 || channel >= channels_alloc || + channels[channel].type == SSH_CHANNEL_FREE) + packet_disconnect("Received data for nonexistent channel %d.", channel); + + /* Ignore any data for non-open channels (might happen on close) */ + if (channels[channel].type != SSH_CHANNEL_OPEN && + channels[channel].type != SSH_CHANNEL_X11_OPEN) + return; + + /* Get the data. */ + data = packet_get_string(&data_len); + packet_integrity_check(payload_len, 4 + 4 + data_len, SSH_MSG_CHANNEL_DATA); + buffer_append(&channels[channel].output, data, data_len); + xfree(data); } /* Returns true if no channel has too much buffered data, and false if one or more channel is overfull. */ -int channel_not_very_much_buffered_data() +int +channel_not_very_much_buffered_data() { - unsigned int i; - Channel *ch; - - for (i = 0; i < channels_alloc; i++) - { - ch = &channels[i]; - switch (ch->type) - { - case SSH_CHANNEL_X11_LISTENER: - case SSH_CHANNEL_PORT_LISTENER: - case SSH_CHANNEL_AUTH_SOCKET: - continue; - case SSH_CHANNEL_OPEN: - if (buffer_len(&ch->input) > packet_get_maxsize()) - return 0; - if (buffer_len(&ch->output) > packet_get_maxsize()) - return 0; - continue; - case SSH_CHANNEL_INPUT_DRAINING: - case SSH_CHANNEL_OUTPUT_DRAINING: - case SSH_CHANNEL_X11_OPEN: - case SSH_CHANNEL_FREE: - default: - continue; + unsigned int i; + Channel *ch; + + for (i = 0; i < channels_alloc; i++) { + ch = &channels[i]; + switch (ch->type) { + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_AUTH_SOCKET: + continue; + case SSH_CHANNEL_OPEN: + if (buffer_len(&ch->input) > packet_get_maxsize()) + return 0; + if (buffer_len(&ch->output) > packet_get_maxsize()) + return 0; + continue; + case SSH_CHANNEL_INPUT_DRAINING: + case SSH_CHANNEL_OUTPUT_DRAINING: + case SSH_CHANNEL_X11_OPEN: + case SSH_CHANNEL_FREE: + default: + continue; + } } - } - return 1; + return 1; } /* This is called after receiving CHANNEL_CLOSE/IEOF. */ -void channel_input_close() +void +channel_input_close() { - int channel; - - /* Get the channel number and verify it. */ - channel = packet_get_int(); - if (channel < 0 || channel >= channels_alloc || - channels[channel].type == SSH_CHANNEL_FREE) - packet_disconnect("Received data for nonexistent channel %d.", channel); - - if(!compat13){ - /* proto version 1.5 overloads CLOSE with IEOF */ - chan_rcvd_ieof(&channels[channel]); - return; - } - - /* Send a confirmation that we have closed the channel and no more data is - coming for it. */ - packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); - packet_put_int(channels[channel].remote_id); - packet_send(); - - /* If the channel is in closed state, we have sent a close request, and - the other side will eventually respond with a confirmation. Thus, - we cannot free the channel here, because then there would be no-one to - receive the confirmation. The channel gets freed when the confirmation - arrives. */ - if (channels[channel].type != SSH_CHANNEL_CLOSED) - { - /* Not a closed channel - mark it as draining, which will cause it to - be freed later. */ - buffer_consume(&channels[channel].input, - buffer_len(&channels[channel].input)); - channels[channel].type = SSH_CHANNEL_OUTPUT_DRAINING; - /* debug("Setting status to output draining; output len = %d", - buffer_len(&channels[channel].output)); */ - } + int channel; + + /* Get the channel number and verify it. */ + channel = packet_get_int(); + if (channel < 0 || channel >= channels_alloc || + channels[channel].type == SSH_CHANNEL_FREE) + packet_disconnect("Received data for nonexistent channel %d.", channel); + + if (!compat13) { + /* proto version 1.5 overloads CLOSE with IEOF */ + chan_rcvd_ieof(&channels[channel]); + return; + } + /* Send a confirmation that we have closed the channel and no more + data is coming for it. */ + packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); + packet_put_int(channels[channel].remote_id); + packet_send(); + + /* If the channel is in closed state, we have sent a close + request, and the other side will eventually respond with a + confirmation. Thus, we cannot free the channel here, because + then there would be no-one to receive the confirmation. The + channel gets freed when the confirmation arrives. */ + if (channels[channel].type != SSH_CHANNEL_CLOSED) { + /* Not a closed channel - mark it as draining, which will + cause it to be freed later. */ + buffer_consume(&channels[channel].input, + buffer_len(&channels[channel].input)); + channels[channel].type = SSH_CHANNEL_OUTPUT_DRAINING; + } } /* This is called after receiving CHANNEL_CLOSE_CONFIRMATION/OCLOSE. */ -void channel_input_close_confirmation() +void +channel_input_close_confirmation() { - int channel; - - /* Get the channel number and verify it. */ - channel = packet_get_int(); - if (channel < 0 || channel >= channels_alloc) - packet_disconnect("Received close confirmation for out-of-range channel %d.", - channel); - - if(!compat13){ - /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ - chan_rcvd_oclose(&channels[channel]); - return; - } - - if (channels[channel].type != SSH_CHANNEL_CLOSED) - packet_disconnect("Received close confirmation for non-closed channel %d (type %d).", - channel, channels[channel].type); - - /* Free the channel. */ - channel_free(channel); + int channel; + + /* Get the channel number and verify it. */ + channel = packet_get_int(); + if (channel < 0 || channel >= channels_alloc) + packet_disconnect("Received close confirmation for out-of-range channel %d.", + channel); + + if (!compat13) { + /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ + chan_rcvd_oclose(&channels[channel]); + return; + } + if (channels[channel].type != SSH_CHANNEL_CLOSED) + packet_disconnect("Received close confirmation for non-closed channel %d (type %d).", + channel, channels[channel].type); + + /* Free the channel. */ + channel_free(channel); } /* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */ -void channel_input_open_confirmation() +void +channel_input_open_confirmation() { - int channel, remote_channel; - - /* Get the channel number and verify it. */ - channel = packet_get_int(); - if (channel < 0 || channel >= channels_alloc || - channels[channel].type != SSH_CHANNEL_OPENING) - packet_disconnect("Received open confirmation for non-opening channel %d.", - channel); - - /* Get remote side's id for this channel. */ - remote_channel = packet_get_int(); - - /* Record the remote channel number and mark that the channel is now open. */ - channels[channel].remote_id = remote_channel; - channels[channel].type = SSH_CHANNEL_OPEN; + int channel, remote_channel; + + /* Get the channel number and verify it. */ + channel = packet_get_int(); + if (channel < 0 || channel >= channels_alloc || + channels[channel].type != SSH_CHANNEL_OPENING) + packet_disconnect("Received open confirmation for non-opening channel %d.", + channel); + + /* Get remote side's id for this channel. */ + remote_channel = packet_get_int(); + + /* Record the remote channel number and mark that the channel is + now open. */ + channels[channel].remote_id = remote_channel; + channels[channel].type = SSH_CHANNEL_OPEN; } /* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */ -void channel_input_open_failure() +void +channel_input_open_failure() { - int channel; - - /* Get the channel number and verify it. */ - channel = packet_get_int(); - if (channel < 0 || channel >= channels_alloc || - channels[channel].type != SSH_CHANNEL_OPENING) - packet_disconnect("Received open failure for non-opening channel %d.", - channel); - - /* Free the channel. This will also close the socket. */ - channel_free(channel); + int channel; + + /* Get the channel number and verify it. */ + channel = packet_get_int(); + if (channel < 0 || channel >= channels_alloc || + channels[channel].type != SSH_CHANNEL_OPENING) + packet_disconnect("Received open failure for non-opening channel %d.", + channel); + + /* Free the channel. This will also close the socket. */ + channel_free(channel); } /* Stops listening for channels, and removes any unix domain sockets that we might have. */ -void channel_stop_listening() +void +channel_stop_listening() { - int i; - for (i = 0; i < channels_alloc; i++) - { - switch (channels[i].type) - { - case SSH_CHANNEL_AUTH_SOCKET: - close(channels[i].sock); - remove(channels[i].path); - channel_free(i); - break; - case SSH_CHANNEL_PORT_LISTENER: - case SSH_CHANNEL_X11_LISTENER: - close(channels[i].sock); - channel_free(i); - break; - default: - break; + int i; + for (i = 0; i < channels_alloc; i++) { + switch (channels[i].type) { + case SSH_CHANNEL_AUTH_SOCKET: + close(channels[i].sock); + remove(channels[i].path); + channel_free(i); + break; + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_X11_LISTENER: + close(channels[i].sock); + channel_free(i); + break; + default: + break; + } } - } } /* Closes the sockets of all channels. This is used to close extra file descriptors after a fork. */ -void channel_close_all() +void +channel_close_all() { - int i; - for (i = 0; i < channels_alloc; i++) - { - if (channels[i].type != SSH_CHANNEL_FREE) - close(channels[i].sock); - } + int i; + for (i = 0; i < channels_alloc; i++) { + if (channels[i].type != SSH_CHANNEL_FREE) + close(channels[i].sock); + } } /* Returns the maximum file descriptor number used by the channels. */ -int channel_max_fd() +int +channel_max_fd() { - return channel_max_fd_value; + return channel_max_fd_value; } /* Returns true if any channel is still open. */ -int channel_still_open() +int +channel_still_open() { - unsigned int i; - for (i = 0; i < channels_alloc; i++) - switch (channels[i].type) - { - case SSH_CHANNEL_FREE: - case SSH_CHANNEL_X11_LISTENER: - case SSH_CHANNEL_PORT_LISTENER: - case SSH_CHANNEL_CLOSED: - case SSH_CHANNEL_AUTH_SOCKET: - continue; - case SSH_CHANNEL_OPENING: - case SSH_CHANNEL_OPEN: - case SSH_CHANNEL_X11_OPEN: - return 1; - case SSH_CHANNEL_INPUT_DRAINING: - case SSH_CHANNEL_OUTPUT_DRAINING: - if (!compat13) - fatal("cannot happen: OUT_DRAIN"); - return 1; - default: - fatal("channel_still_open: bad channel type %d", channels[i].type); - /*NOTREACHED*/ - } - return 0; + unsigned int i; + for (i = 0; i < channels_alloc; i++) + switch (channels[i].type) { + case SSH_CHANNEL_FREE: + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_CLOSED: + case SSH_CHANNEL_AUTH_SOCKET: + continue; + case SSH_CHANNEL_OPENING: + case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_X11_OPEN: + return 1; + case SSH_CHANNEL_INPUT_DRAINING: + case SSH_CHANNEL_OUTPUT_DRAINING: + if (!compat13) + fatal("cannot happen: OUT_DRAIN"); + return 1; + default: + fatal("channel_still_open: bad channel type %d", channels[i].type); + /* NOTREACHED */ + } + return 0; } /* Returns a message describing the currently open forwarded connections, suitable for sending to the client. The message contains crlf pairs for newlines. */ -char *channel_open_message() +char * +channel_open_message() { - Buffer buffer; - int i; - char buf[512], *cp; - - buffer_init(&buffer); - snprintf(buf, sizeof buf, "The following connections are open:\r\n"); - buffer_append(&buffer, buf, strlen(buf)); - for (i = 0; i < channels_alloc; i++){ - Channel *c=&channels[i]; - switch (c->type) - { - case SSH_CHANNEL_FREE: - case SSH_CHANNEL_X11_LISTENER: - case SSH_CHANNEL_PORT_LISTENER: - case SSH_CHANNEL_CLOSED: - case SSH_CHANNEL_AUTH_SOCKET: - continue; - case SSH_CHANNEL_OPENING: - case SSH_CHANNEL_OPEN: - case SSH_CHANNEL_X11_OPEN: - case SSH_CHANNEL_INPUT_DRAINING: - case SSH_CHANNEL_OUTPUT_DRAINING: - snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d o%d)\r\n", - c->self,c->remote_name, - c->type,c->remote_id, c->istate,c->ostate); + Buffer buffer; + int i; + char buf[512], *cp; + + buffer_init(&buffer); + snprintf(buf, sizeof buf, "The following connections are open:\r\n"); buffer_append(&buffer, buf, strlen(buf)); - continue; - default: - fatal("channel_still_open: bad channel type %d", c->type); - /*NOTREACHED*/ - } - } - buffer_append(&buffer, "\0", 1); - cp = xstrdup(buffer_ptr(&buffer)); - buffer_free(&buffer); - return cp; + for (i = 0; i < channels_alloc; i++) { + Channel *c = &channels[i]; + switch (c->type) { + case SSH_CHANNEL_FREE: + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_CLOSED: + case SSH_CHANNEL_AUTH_SOCKET: + continue; + case SSH_CHANNEL_OPENING: + case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_X11_OPEN: + case SSH_CHANNEL_INPUT_DRAINING: + case SSH_CHANNEL_OUTPUT_DRAINING: + snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d o%d)\r\n", + c->self, c->remote_name, + c->type, c->remote_id, c->istate, c->ostate); + buffer_append(&buffer, buf, strlen(buf)); + continue; + default: + fatal("channel_still_open: bad channel type %d", c->type); + /* NOTREACHED */ + } + } + buffer_append(&buffer, "\0", 1); + cp = xstrdup(buffer_ptr(&buffer)); + buffer_free(&buffer); + return cp; } /* Initiate forwarding of connections to local port "port" through the secure channel to host:port from remote side. */ -void channel_request_local_forwarding(int port, const char *host, - int host_port) +void +channel_request_local_forwarding(int port, const char *host, + int host_port) { - int ch, sock; - struct sockaddr_in sin; - extern Options options; - - if (strlen(host) > sizeof(channels[0].path) - 1) - packet_disconnect("Forward host name too long."); - - /* Create a port to listen for the host. */ - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) - packet_disconnect("socket: %.100s", strerror(errno)); - - /* Initialize socket address. */ - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - if (options.gateway_ports == 1) - sin.sin_addr.s_addr = htonl(INADDR_ANY); - else - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - sin.sin_port = htons(port); - - /* Bind the socket to the address. */ - if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) - packet_disconnect("bind: %.100s", strerror(errno)); - - /* Start listening for connections on the socket. */ - if (listen(sock, 5) < 0) - packet_disconnect("listen: %.100s", strerror(errno)); - - /* Allocate a channel number for the socket. */ - ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock, - xstrdup("port listener")); - strcpy(channels[ch].path, host); /* note: host name stored here */ - channels[ch].host_port = host_port; /* port on host to connect to */ - channels[ch].listening_port = port; /* port being listened */ -} + int ch, sock; + struct sockaddr_in sin; + extern Options options; + + if (strlen(host) > sizeof(channels[0].path) - 1) + packet_disconnect("Forward host name too long."); + + /* Create a port to listen for the host. */ + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + packet_disconnect("socket: %.100s", strerror(errno)); + + /* Initialize socket address. */ + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + if (options.gateway_ports == 1) + sin.sin_addr.s_addr = htonl(INADDR_ANY); + else + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sin.sin_port = htons(port); + + /* Bind the socket to the address. */ + if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) + packet_disconnect("bind: %.100s", strerror(errno)); + + /* Start listening for connections on the socket. */ + if (listen(sock, 5) < 0) + packet_disconnect("listen: %.100s", strerror(errno)); + + /* Allocate a channel number for the socket. */ + ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock, + xstrdup("port listener")); + strcpy(channels[ch].path, host); + channels[ch].host_port = host_port; + channels[ch].listening_port = port; +} /* Initiate forwarding of connections to port "port" on remote host through the secure channel to host:port from local side. */ -void channel_request_remote_forwarding(int port, const char *host, - int remote_port) +void +channel_request_remote_forwarding(int port, const char *host, + int remote_port) { - int payload_len; - /* Record locally that connection to this host/port is permitted. */ - if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) - fatal("channel_request_remote_forwarding: too many forwards"); - permitted_opens[num_permitted_opens].host = xstrdup(host); - permitted_opens[num_permitted_opens].port = remote_port; - num_permitted_opens++; - - /* Send the forward request to the remote side. */ - packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); - packet_put_int(port); - packet_put_string(host, strlen(host)); - packet_put_int(remote_port); - packet_send(); - packet_write_wait(); - - /* Wait for response from the remote side. It will send a disconnect - message on failure, and we will never see it here. */ - packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); + int payload_len; + /* Record locally that connection to this host/port is permitted. */ + if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) + fatal("channel_request_remote_forwarding: too many forwards"); + + permitted_opens[num_permitted_opens].host = xstrdup(host); + permitted_opens[num_permitted_opens].port = remote_port; + num_permitted_opens++; + + /* Send the forward request to the remote side. */ + packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); + packet_put_int(port); + packet_put_string(host, strlen(host)); + packet_put_int(remote_port); + packet_send(); + packet_write_wait(); + + /* Wait for response from the remote side. It will send a + disconnect message on failure, and we will never see it here. */ + packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); } /* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates listening for the port, and sends back a success reply (or disconnect - message if there was an error). This never returns if there was an + message if there was an error). This never returns if there was an error. */ -void channel_input_port_forward_request(int is_root) +void +channel_input_port_forward_request(int is_root) { - int port, host_port; - char *hostname; - - /* Get arguments from the packet. */ - port = packet_get_int(); - hostname = packet_get_string(NULL); - host_port = packet_get_int(); - - /* Port numbers are 16 bit quantities. */ - if ((port & 0xffff) != port) - packet_disconnect("Requested forwarding of nonexistent port %d.", port); - - /* Check that an unprivileged user is not trying to forward a privileged - port. */ - if (port < IPPORT_RESERVED && !is_root) - packet_disconnect("Requested forwarding of port %d but user is not root.", - port); - - /* Initiate forwarding. */ - channel_request_local_forwarding(port, hostname, host_port); - - /* Free the argument string. */ - xfree(hostname); + int port, host_port; + char *hostname; + + /* Get arguments from the packet. */ + port = packet_get_int(); + hostname = packet_get_string(NULL); + host_port = packet_get_int(); + + /* Port numbers are 16 bit quantities. */ + if ((port & 0xffff) != port) + packet_disconnect("Requested forwarding of nonexistent port %d.", port); + + /* Check that an unprivileged user is not trying to forward a + privileged port. */ + if (port < IPPORT_RESERVED && !is_root) + packet_disconnect("Requested forwarding of port %d but user is not root.", + port); + + /* Initiate forwarding. */ + channel_request_local_forwarding(port, hostname, host_port); + + /* Free the argument string. */ + xfree(hostname); } /* This is called after receiving PORT_OPEN message. This attempts to connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION or CHANNEL_OPEN_FAILURE. */ -void channel_input_port_open(int payload_len) +void +channel_input_port_open(int payload_len) { - int remote_channel, sock, newch, host_port, i; - struct sockaddr_in sin; - char *host, *originator_string; - struct hostent *hp; - int host_len, originator_len; - - /* Get remote channel number. */ - remote_channel = packet_get_int(); - - /* Get host name to connect to. */ - host = packet_get_string(&host_len); - - /* Get port to connect to. */ - host_port = packet_get_int(); - - /* Get remote originator name. */ - if (have_hostname_in_open) - originator_string = packet_get_string(&originator_len); - else - originator_string = xstrdup("unknown (remote did not supply name)"); - - packet_integrity_check(payload_len, - 4 + 4 + host_len + 4 + 4 + originator_len, - SSH_MSG_PORT_OPEN); - - /* Check if opening that port is permitted. */ - if (!all_opens_permitted) - { - /* Go trough all permitted ports. */ - for (i = 0; i < num_permitted_opens; i++) - if (permitted_opens[i].port == host_port && - strcmp(permitted_opens[i].host, host) == 0) - break; - - /* Check if we found the requested port among those permitted. */ - if (i >= num_permitted_opens) - { - /* The port is not permitted. */ - log("Received request to connect to %.100s:%d, but the request was denied.", - host, host_port); - packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(remote_channel); - packet_send(); + int remote_channel, sock, newch, host_port, i; + struct sockaddr_in sin; + char *host, *originator_string; + struct hostent *hp; + int host_len, originator_len; + + /* Get remote channel number. */ + remote_channel = packet_get_int(); + + /* Get host name to connect to. */ + host = packet_get_string(&host_len); + + /* Get port to connect to. */ + host_port = packet_get_int(); + + /* Get remote originator name. */ + if (have_hostname_in_open) + originator_string = packet_get_string(&originator_len); + else + originator_string = xstrdup("unknown (remote did not supply name)"); + + packet_integrity_check(payload_len, + 4 + 4 + host_len + 4 + 4 + originator_len, + SSH_MSG_PORT_OPEN); + + /* Check if opening that port is permitted. */ + if (!all_opens_permitted) { + /* Go trough all permitted ports. */ + for (i = 0; i < num_permitted_opens; i++) + if (permitted_opens[i].port == host_port && + strcmp(permitted_opens[i].host, host) == 0) + break; + + /* Check if we found the requested port among those permitted. */ + if (i >= num_permitted_opens) { + /* The port is not permitted. */ + log("Received request to connect to %.100s:%d, but the request was denied.", + host, host_port); + packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(remote_channel); + packet_send(); + } } - } - - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = inet_addr(host); - if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) - { - /* It was a valid numeric host address. */ - sin.sin_family = AF_INET; - } - else - { - /* Look up the host address from the name servers. */ - hp = gethostbyname(host); - if (!hp) - { - error("%.100s: unknown host.", host); - goto fail; + memset(&sin, 0, sizeof(sin)); + sin.sin_addr.s_addr = inet_addr(host); + if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) { + /* It was a valid numeric host address. */ + sin.sin_family = AF_INET; + } else { + /* Look up the host address from the name servers. */ + hp = gethostbyname(host); + if (!hp) { + error("%.100s: unknown host.", host); + goto fail; + } + if (!hp->h_addr_list[0]) { + error("%.100s: host has no IP address.", host); + goto fail; + } + sin.sin_family = hp->h_addrtype; + memcpy(&sin.sin_addr, hp->h_addr_list[0], + sizeof(sin.sin_addr)); } - if (!hp->h_addr_list[0]) - { - error("%.100s: host has no IP address.", host); - goto fail; + sin.sin_port = htons(host_port); + + /* Create the socket. */ + sock = socket(sin.sin_family, SOCK_STREAM, 0); + if (sock < 0) { + error("socket: %.100s", strerror(errno)); + goto fail; + } + /* Connect to the host/port. */ + if (connect(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) { + error("connect %.100s:%d: %.100s", host, host_port, + strerror(errno)); + close(sock); + goto fail; } - sin.sin_family = hp->h_addrtype; - memcpy(&sin.sin_addr, hp->h_addr_list[0], - sizeof(sin.sin_addr)); - } - sin.sin_port = htons(host_port); - - /* Create the socket. */ - sock = socket(sin.sin_family, SOCK_STREAM, 0); - if (sock < 0) - { - error("socket: %.100s", strerror(errno)); - goto fail; - } - - /* Connect to the host/port. */ - if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) - { - error("connect %.100s:%d: %.100s", host, host_port, - strerror(errno)); - close(sock); - goto fail; - } - - /* Successful connection. */ - - /* Allocate a channel for this connection. */ - newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); - channels[newch].remote_id = remote_channel; - - /* Send a confirmation to the remote host. */ - packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); - packet_put_int(remote_channel); - packet_put_int(newch); - packet_send(); - - /* Free the argument string. */ - xfree(host); - - return; - - fail: - /* Free the argument string. */ - xfree(host); - - /* Send refusal to the remote host. */ - packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(remote_channel); - packet_send(); + /* Successful connection. */ + + /* Allocate a channel for this connection. */ + newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); + channels[newch].remote_id = remote_channel; + + /* Send a confirmation to the remote host. */ + packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(remote_channel); + packet_put_int(newch); + packet_send(); + + /* Free the argument string. */ + xfree(host); + + return; + +fail: + /* Free the argument string. */ + xfree(host); + + /* Send refusal to the remote host. */ + packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(remote_channel); + packet_send(); } -/* Creates an internet domain socket for listening for X11 connections. +/* Creates an internet domain socket for listening for X11 connections. Returns a suitable value for the DISPLAY variable, or NULL if an error occurs. */ -char *x11_create_display_inet(int screen_number) +char * +x11_create_display_inet(int screen_number) { - extern ServerOptions options; - int display_number, port, sock; - struct sockaddr_in sin; - char buf[512]; - char hostname[MAXHOSTNAMELEN]; - - for (display_number = options.x11_display_offset; display_number < MAX_DISPLAYS; display_number++) - { - port = 6000 + display_number; - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_port = htons(port); - - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) - { - error("socket: %.100s", strerror(errno)); - return NULL; + extern ServerOptions options; + int display_number, port, sock; + struct sockaddr_in sin; + char buf[512]; + char hostname[MAXHOSTNAMELEN]; + + for (display_number = options.x11_display_offset; + display_number < MAX_DISPLAYS; + display_number++) { + port = 6000 + display_number; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(port); + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + error("socket: %.100s", strerror(errno)); + return NULL; + } + if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) { + debug("bind port %d: %.100s", port, strerror(errno)); + shutdown(sock, SHUT_RDWR); + close(sock); + continue; + } + break; + } + if (display_number >= MAX_DISPLAYS) { + error("Failed to allocate internet-domain X11 display socket."); + return NULL; } - - if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) - { - debug("bind port %d: %.100s", port, strerror(errno)); - shutdown(sock, SHUT_RDWR); - close(sock); - continue; + /* Start listening for connections on the socket. */ + if (listen(sock, 5) < 0) { + error("listen: %.100s", strerror(errno)); + shutdown(sock, SHUT_RDWR); + close(sock); + return NULL; } - break; - } - if (display_number >= MAX_DISPLAYS) - { - error("Failed to allocate internet-domain X11 display socket."); - return NULL; - } - - /* Start listening for connections on the socket. */ - if (listen(sock, 5) < 0) - { - error("listen: %.100s", strerror(errno)); - shutdown(sock, SHUT_RDWR); - close(sock); - return NULL; - } - - /* Set up a suitable value for the DISPLAY variable. */ - if (gethostname(hostname, sizeof(hostname)) < 0) - fatal("gethostname: %.100s", strerror(errno)); - snprintf(buf, sizeof buf, "%.400s:%d.%d", hostname, - display_number, screen_number); - - /* Allocate a channel for the socket. */ - (void)channel_allocate(SSH_CHANNEL_X11_LISTENER, sock, - xstrdup("X11 inet listener")); - - /* Return a suitable value for the DISPLAY environment variable. */ - return xstrdup(buf); + /* Set up a suitable value for the DISPLAY variable. */ + if (gethostname(hostname, sizeof(hostname)) < 0) + fatal("gethostname: %.100s", strerror(errno)); + snprintf(buf, sizeof buf, "%.400s:%d.%d", hostname, + display_number, screen_number); + + /* Allocate a channel for the socket. */ + (void) channel_allocate(SSH_CHANNEL_X11_LISTENER, sock, + xstrdup("X11 inet listener")); + + /* Return a suitable value for the DISPLAY environment variable. */ + return xstrdup(buf); } #ifndef X_UNIX_PATH @@ -1150,30 +1108,29 @@ static int connect_local_xsocket(unsigned dnr) { - static const char *const x_sockets[] = { - X_UNIX_PATH "%u", - "/var/X/.X11-unix/X" "%u", - "/usr/spool/sockets/X11/" "%u", - NULL - }; - int sock; - struct sockaddr_un addr; - const char *const *path; - - for (path = x_sockets; *path; ++path) - { - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) - error("socket: %.100s", strerror(errno)); - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr); - if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) - return sock; - close(sock); - } - error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); - return -1; + static const char *const x_sockets[] = { + X_UNIX_PATH "%u", + "/var/X/.X11-unix/X" "%u", + "/usr/spool/sockets/X11/" "%u", + NULL + }; + int sock; + struct sockaddr_un addr; + const char *const * path; + + for (path = x_sockets; *path; ++path) { + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + error("socket: %.100s", strerror(errno)); + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr); + if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0) + return sock; + close(sock); + } + error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); + return -1; } @@ -1181,327 +1138,317 @@ connect_local_xsocket(unsigned dnr) the remote channel number. We should do whatever we want, and respond with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */ -void x11_input_open(int payload_len) +void +x11_input_open(int payload_len) { - int remote_channel, display_number, sock, newch; - const char *display; - struct sockaddr_in sin; - char buf[1024], *cp, *remote_host; - struct hostent *hp; - int remote_len; - - /* Get remote channel number. */ - remote_channel = packet_get_int(); - - /* Get remote originator name. */ - if (have_hostname_in_open) - remote_host = packet_get_string(&remote_len); - else - remote_host = xstrdup("unknown (remote did not supply name)"); - - debug("Received X11 open request."); - packet_integrity_check(payload_len, 4 + 4+remote_len, SSH_SMSG_X11_OPEN); - - /* Try to open a socket for the local X server. */ - display = getenv("DISPLAY"); - if (!display) - { - error("DISPLAY not set."); - goto fail; - } - - /* Now we decode the value of the DISPLAY variable and make a connection - to the real X server. */ - - /* Check if it is a unix domain socket. Unix domain displays are in one - of the following formats: unix:d[.s], :d[.s], ::d[.s] */ - if (strncmp(display, "unix:", 5) == 0 || - display[0] == ':') - { - /* Connect to the unix domain socket. */ - if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) - { - error("Could not parse display number from DISPLAY: %.100s", - display); - goto fail; + int remote_channel, display_number, sock, newch; + const char *display; + struct sockaddr_in sin; + char buf[1024], *cp, *remote_host; + struct hostent *hp; + int remote_len; + + /* Get remote channel number. */ + remote_channel = packet_get_int(); + + /* Get remote originator name. */ + if (have_hostname_in_open) + remote_host = packet_get_string(&remote_len); + else + remote_host = xstrdup("unknown (remote did not supply name)"); + + debug("Received X11 open request."); + packet_integrity_check(payload_len, 4 + 4 + remote_len, SSH_SMSG_X11_OPEN); + + /* Try to open a socket for the local X server. */ + display = getenv("DISPLAY"); + if (!display) { + error("DISPLAY not set."); + goto fail; + } + /* Now we decode the value of the DISPLAY variable and make a + connection to the real X server. */ + + /* Check if it is a unix domain socket. Unix domain displays are + in one of the following formats: unix:d[.s], :d[.s], ::d[.s] */ + if (strncmp(display, "unix:", 5) == 0 || + display[0] == ':') { + /* Connect to the unix domain socket. */ + if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { + error("Could not parse display number from DISPLAY: %.100s", + display); + goto fail; + } + /* Create a socket. */ + sock = connect_local_xsocket(display_number); + if (sock < 0) + goto fail; + + /* OK, we now have a connection to the display. */ + goto success; + } + /* Connect to an inet socket. The DISPLAY value is supposedly + hostname:d[.s], where hostname may also be numeric IP address. */ + strncpy(buf, display, sizeof(buf)); + buf[sizeof(buf) - 1] = 0; + cp = strchr(buf, ':'); + if (!cp) { + error("Could not find ':' in DISPLAY: %.100s", display); + goto fail; + } + *cp = 0; + /* buf now contains the host name. But first we parse the display + number. */ + if (sscanf(cp + 1, "%d", &display_number) != 1) { + error("Could not parse display number from DISPLAY: %.100s", + display); + goto fail; + } + /* Try to parse the host name as a numeric IP address. */ + memset(&sin, 0, sizeof(sin)); + sin.sin_addr.s_addr = inet_addr(buf); + if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) { + /* It was a valid numeric host address. */ + sin.sin_family = AF_INET; + } else { + /* Not a numeric IP address. */ + /* Look up the host address from the name servers. */ + hp = gethostbyname(buf); + if (!hp) { + error("%.100s: unknown host.", buf); + goto fail; + } + if (!hp->h_addr_list[0]) { + error("%.100s: host has no IP address.", buf); + goto fail; + } + sin.sin_family = hp->h_addrtype; + memcpy(&sin.sin_addr, hp->h_addr_list[0], + sizeof(sin.sin_addr)); } - /* Create a socket. */ - sock = connect_local_xsocket(display_number); - if (sock < 0) - goto fail; - - /* OK, we now have a connection to the display. */ - goto success; - } - - /* Connect to an inet socket. The DISPLAY value is supposedly - hostname:d[.s], where hostname may also be numeric IP address. */ - strncpy(buf, display, sizeof(buf)); - buf[sizeof(buf) - 1] = 0; - cp = strchr(buf, ':'); - if (!cp) - { - error("Could not find ':' in DISPLAY: %.100s", display); - goto fail; - } - *cp = 0; - /* buf now contains the host name. But first we parse the display number. */ - if (sscanf(cp + 1, "%d", &display_number) != 1) - { - error("Could not parse display number from DISPLAY: %.100s", - display); - goto fail; - } - - /* Try to parse the host name as a numeric IP address. */ - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = inet_addr(buf); - if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) - { - /* It was a valid numeric host address. */ - sin.sin_family = AF_INET; - } - else - { - /* Not a numeric IP address. */ - /* Look up the host address from the name servers. */ - hp = gethostbyname(buf); - if (!hp) - { - error("%.100s: unknown host.", buf); - goto fail; + /* Set port number. */ + sin.sin_port = htons(6000 + display_number); + + /* Create a socket. */ + sock = socket(sin.sin_family, SOCK_STREAM, 0); + if (sock < 0) { + error("socket: %.100s", strerror(errno)); + goto fail; } - if (!hp->h_addr_list[0]) - { - error("%.100s: host has no IP address.", buf); - goto fail; + /* Connect it to the display. */ + if (connect(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) { + error("connect %.100s:%d: %.100s", buf, 6000 + display_number, + strerror(errno)); + close(sock); + goto fail; } - sin.sin_family = hp->h_addrtype; - memcpy(&sin.sin_addr, hp->h_addr_list[0], - sizeof(sin.sin_addr)); - } - /* Set port number. */ - sin.sin_port = htons(6000 + display_number); - - /* Create a socket. */ - sock = socket(sin.sin_family, SOCK_STREAM, 0); - if (sock < 0) - { - error("socket: %.100s", strerror(errno)); - goto fail; - } - /* Connect it to the display. */ - if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) - { - error("connect %.100s:%d: %.100s", buf, 6000 + display_number, - strerror(errno)); - close(sock); - goto fail; - } - - success: - /* We have successfully obtained a connection to the real X display. */ - - /* Allocate a channel for this connection. */ - if (x11_saved_proto == NULL) - newch = channel_allocate(SSH_CHANNEL_OPEN, sock, remote_host); - else - newch = channel_allocate(SSH_CHANNEL_X11_OPEN, sock, remote_host); - channels[newch].remote_id = remote_channel; - - /* Send a confirmation to the remote host. */ - packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); - packet_put_int(remote_channel); - packet_put_int(newch); - packet_send(); - - return; - - fail: - /* Send refusal to the remote host. */ - packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(remote_channel); - packet_send(); +success: + /* We have successfully obtained a connection to the real X display. */ + + /* Allocate a channel for this connection. */ + if (x11_saved_proto == NULL) + newch = channel_allocate(SSH_CHANNEL_OPEN, sock, remote_host); + else + newch = channel_allocate(SSH_CHANNEL_X11_OPEN, sock, remote_host); + channels[newch].remote_id = remote_channel; + + /* Send a confirmation to the remote host. */ + packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(remote_channel); + packet_put_int(newch); + packet_send(); + + return; + +fail: + /* Send refusal to the remote host. */ + packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(remote_channel); + packet_send(); } /* Requests forwarding of X11 connections, generates fake authentication data, and enables authentication spoofing. */ -void x11_request_forwarding_with_spoofing(const char *proto, const char *data) +void +x11_request_forwarding_with_spoofing(const char *proto, const char *data) { - unsigned int data_len = (unsigned int)strlen(data) / 2; - unsigned int i, value; - char *new_data; - int screen_number; - const char *cp; - u_int32_t rand = 0; - - cp = getenv("DISPLAY"); - if (cp) - cp = strchr(cp, ':'); - if (cp) - cp = strchr(cp, '.'); - if (cp) - screen_number = atoi(cp + 1); - else - screen_number = 0; - - /* Save protocol name. */ - x11_saved_proto = xstrdup(proto); - - /* Extract real authentication data and generate fake data of the same - length. */ - x11_saved_data = xmalloc(data_len); - x11_fake_data = xmalloc(data_len); - for (i = 0; i < data_len; i++) - { - if (sscanf(data + 2 * i, "%2x", &value) != 1) - fatal("x11_request_forwarding: bad authentication data: %.100s", data); - if (i % 4 == 0) - rand = arc4random(); - x11_saved_data[i] = value; - x11_fake_data[i] = rand & 0xff; - rand >>= 8; - } - x11_saved_data_len = data_len; - x11_fake_data_len = data_len; - - /* Convert the fake data into hex. */ - new_data = xmalloc(2 * data_len + 1); - for (i = 0; i < data_len; i++) - sprintf(new_data + 2 * i, "%02x", (unsigned char)x11_fake_data[i]); - - /* Send the request packet. */ - packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); - packet_put_string(proto, strlen(proto)); - packet_put_string(new_data, strlen(new_data)); - packet_put_int(screen_number); - packet_send(); - packet_write_wait(); - xfree(new_data); + unsigned int data_len = (unsigned int) strlen(data) / 2; + unsigned int i, value; + char *new_data; + int screen_number; + const char *cp; + u_int32_t rand = 0; + + cp = getenv("DISPLAY"); + if (cp) + cp = strchr(cp, ':'); + if (cp) + cp = strchr(cp, '.'); + if (cp) + screen_number = atoi(cp + 1); + else + screen_number = 0; + + /* Save protocol name. */ + x11_saved_proto = xstrdup(proto); + + /* Extract real authentication data and generate fake data of the + same length. */ + x11_saved_data = xmalloc(data_len); + x11_fake_data = xmalloc(data_len); + for (i = 0; i < data_len; i++) { + if (sscanf(data + 2 * i, "%2x", &value) != 1) + fatal("x11_request_forwarding: bad authentication data: %.100s", data); + if (i % 4 == 0) + rand = arc4random(); + x11_saved_data[i] = value; + x11_fake_data[i] = rand & 0xff; + rand >>= 8; + } + x11_saved_data_len = data_len; + x11_fake_data_len = data_len; + + /* Convert the fake data into hex. */ + new_data = xmalloc(2 * data_len + 1); + for (i = 0; i < data_len; i++) + sprintf(new_data + 2 * i, "%02x", (unsigned char) x11_fake_data[i]); + + /* Send the request packet. */ + packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); + packet_put_string(proto, strlen(proto)); + packet_put_string(new_data, strlen(new_data)); + packet_put_int(screen_number); + packet_send(); + packet_write_wait(); + xfree(new_data); } /* Sends a message to the server to request authentication fd forwarding. */ -void auth_request_forwarding() +void +auth_request_forwarding() { - packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); - packet_send(); - packet_write_wait(); + packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); + packet_send(); + packet_write_wait(); } /* Returns the name of the forwarded authentication socket. Returns NULL if there is no forwarded authentication socket. The returned value points to a static buffer. */ -char *auth_get_socket_name() +char * +auth_get_socket_name() { - return channel_forwarded_auth_socket_name; + return channel_forwarded_auth_socket_name; } /* removes the agent forwarding socket */ -void cleanup_socket(void) { - remove(channel_forwarded_auth_socket_name); - rmdir(channel_forwarded_auth_socket_dir); +void +cleanup_socket(void) +{ + remove(channel_forwarded_auth_socket_name); + rmdir(channel_forwarded_auth_socket_dir); } /* This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. This starts forwarding authentication requests. */ -void auth_input_request_forwarding(struct passwd *pw) +void +auth_input_request_forwarding(struct passwd * pw) { - int sock, newch; - struct sockaddr_un sunaddr; - - if (auth_get_socket_name() != NULL) - fatal("Protocol error: authentication forwarding requested twice."); - - /* Temporarily drop privileged uid for mkdir/bind. */ - temporarily_use_uid(pw->pw_uid); - - /* Allocate a buffer for the socket name, and format the name. */ - channel_forwarded_auth_socket_name = xmalloc(MAX_SOCKET_NAME); - channel_forwarded_auth_socket_dir = xmalloc(MAX_SOCKET_NAME); - strlcpy(channel_forwarded_auth_socket_dir, "/tmp/ssh-XXXXXXXX", MAX_SOCKET_NAME); - - /* Create private directory for socket */ - if (mkdtemp(channel_forwarded_auth_socket_dir) == NULL) - packet_disconnect("mkdtemp: %.100s", strerror(errno)); - snprintf(channel_forwarded_auth_socket_name, MAX_SOCKET_NAME, - "%s/agent.%d", channel_forwarded_auth_socket_dir, (int)getpid()); - - if (atexit(cleanup_socket) < 0) { - int saved=errno; - cleanup_socket(); - packet_disconnect("socket: %.100s", strerror(saved)); - } - - /* Create the socket. */ - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) - packet_disconnect("socket: %.100s", strerror(errno)); - - /* Bind it to the name. */ - memset(&sunaddr, 0, sizeof(sunaddr)); - sunaddr.sun_family = AF_UNIX; - strncpy(sunaddr.sun_path, channel_forwarded_auth_socket_name, - sizeof(sunaddr.sun_path)); - - if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) - packet_disconnect("bind: %.100s", strerror(errno)); - - /* Restore the privileged uid. */ - restore_uid(); - - /* Start listening on the socket. */ - if (listen(sock, 5) < 0) - packet_disconnect("listen: %.100s", strerror(errno)); - - /* Allocate a channel for the authentication agent socket. */ - newch = channel_allocate(SSH_CHANNEL_AUTH_SOCKET, sock, - xstrdup("auth socket")); - strcpy(channels[newch].path, channel_forwarded_auth_socket_name); + int sock, newch; + struct sockaddr_un sunaddr; + + if (auth_get_socket_name() != NULL) + fatal("Protocol error: authentication forwarding requested twice."); + + /* Temporarily drop privileged uid for mkdir/bind. */ + temporarily_use_uid(pw->pw_uid); + + /* Allocate a buffer for the socket name, and format the name. */ + channel_forwarded_auth_socket_name = xmalloc(MAX_SOCKET_NAME); + channel_forwarded_auth_socket_dir = xmalloc(MAX_SOCKET_NAME); + strlcpy(channel_forwarded_auth_socket_dir, "/tmp/ssh-XXXXXXXX", MAX_SOCKET_NAME); + + /* Create private directory for socket */ + if (mkdtemp(channel_forwarded_auth_socket_dir) == NULL) + packet_disconnect("mkdtemp: %.100s", strerror(errno)); + snprintf(channel_forwarded_auth_socket_name, MAX_SOCKET_NAME, "%s/agent.%d", + channel_forwarded_auth_socket_dir, (int) getpid()); + + if (atexit(cleanup_socket) < 0) { + int saved = errno; + cleanup_socket(); + packet_disconnect("socket: %.100s", strerror(saved)); + } + /* Create the socket. */ + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + packet_disconnect("socket: %.100s", strerror(errno)); + + /* Bind it to the name. */ + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + strncpy(sunaddr.sun_path, channel_forwarded_auth_socket_name, + sizeof(sunaddr.sun_path)); + + if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) + packet_disconnect("bind: %.100s", strerror(errno)); + + /* Restore the privileged uid. */ + restore_uid(); + + /* Start listening on the socket. */ + if (listen(sock, 5) < 0) + packet_disconnect("listen: %.100s", strerror(errno)); + + /* Allocate a channel for the authentication agent socket. */ + newch = channel_allocate(SSH_CHANNEL_AUTH_SOCKET, sock, + xstrdup("auth socket")); + strcpy(channels[newch].path, channel_forwarded_auth_socket_name); } /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ -void auth_input_open_request() +void +auth_input_open_request() { - int remch, sock, newch; - char *dummyname; - - /* Read the remote channel number from the message. */ - remch = packet_get_int(); - - /* Get a connection to the local authentication agent (this may again get - forwarded). */ - sock = ssh_get_authentication_socket(); - - /* If we could not connect the agent, send an error message back to - the server. This should never happen unless the agent - dies, because authentication forwarding is only enabled if we have an - agent. */ - if (sock < 0){ - packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(remch); - packet_send(); - return; - } - - debug("Forwarding authentication connection."); - - /* Dummy host name. This will be freed when the channel is freed; it will - still be valid in the packet_put_string below since the channel cannot - yet be freed at that point. */ - dummyname = xstrdup("authentication agent connection"); - - newch = channel_allocate(SSH_CHANNEL_OPEN, sock, dummyname); - channels[newch].remote_id = remch; - - /* Send a confirmation to the remote host. */ - packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); - packet_put_int(remch); - packet_put_int(newch); - packet_send(); + int remch, sock, newch; + char *dummyname; + + /* Read the remote channel number from the message. */ + remch = packet_get_int(); + + /* Get a connection to the local authentication agent (this may + again get forwarded). */ + sock = ssh_get_authentication_socket(); + + /* If we could not connect the agent, send an error message back + to the server. This should never happen unless the agent dies, + because authentication forwarding is only enabled if we have an + agent. */ + if (sock < 0) { + packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(remch); + packet_send(); + return; + } + debug("Forwarding authentication connection."); + + /* Dummy host name. This will be freed when the channel is freed; + it will still be valid in the packet_put_string below since the + channel cannot yet be freed at that point. */ + dummyname = xstrdup("authentication agent connection"); + + newch = channel_allocate(SSH_CHANNEL_OPEN, sock, dummyname); + channels[newch].remote_id = remch; + + /* Send a confirmation to the remote host. */ + packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(remch); + packet_put_int(newch); + packet_send(); } diff --git a/channels.h b/channels.h index d30f1635..28ff2084 100644 --- a/channels.h +++ b/channels.h @@ -4,37 +4,43 @@ #define CHANNELS_H /* Definitions for channel types. */ -#define SSH_CHANNEL_FREE 0 /* This channel is free (unused). */ -#define SSH_CHANNEL_X11_LISTENER 1 /* Listening for inet X11 conn. */ -#define SSH_CHANNEL_PORT_LISTENER 2 /* Listening on a port. */ -#define SSH_CHANNEL_OPENING 3 /* waiting for confirmation */ -#define SSH_CHANNEL_OPEN 4 /* normal open two-way channel */ -#define SSH_CHANNEL_CLOSED 5 /* waiting for close confirmation */ +#define SSH_CHANNEL_FREE 0 /* This channel is free + * (unused). */ +#define SSH_CHANNEL_X11_LISTENER 1 /* Listening for inet X11 + * conn. */ +#define SSH_CHANNEL_PORT_LISTENER 2 /* Listening on a port. */ +#define SSH_CHANNEL_OPENING 3 /* waiting for confirmation */ +#define SSH_CHANNEL_OPEN 4 /* normal open two-way channel */ +#define SSH_CHANNEL_CLOSED 5 /* waiting for close + * confirmation */ /* SSH_CHANNEL_AUTH_FD 6 authentication fd */ -#define SSH_CHANNEL_AUTH_SOCKET 7 /* authentication socket */ +#define SSH_CHANNEL_AUTH_SOCKET 7 /* authentication socket */ /* SSH_CHANNEL_AUTH_SOCKET_FD 8 connection to auth socket */ -#define SSH_CHANNEL_X11_OPEN 9 /* reading first X11 packet */ -#define SSH_CHANNEL_INPUT_DRAINING 10 /* sending remaining data to conn */ -#define SSH_CHANNEL_OUTPUT_DRAINING 11 /* sending remaining data to app */ +#define SSH_CHANNEL_X11_OPEN 9 /* reading first X11 packet */ +#define SSH_CHANNEL_INPUT_DRAINING 10 /* sending remaining data to + * conn */ +#define SSH_CHANNEL_OUTPUT_DRAINING 11 /* sending remaining data to + * app */ /* Data structure for channel data. This is iniailized in channel_allocate and cleared in channel_free. */ -typedef struct Channel -{ - int type; /* channel type/state */ - int self; /* my own channel identifier */ - int remote_id; /* channel identifier for remote peer */ - /* peer can be reached over encrypted connection, via packet-sent */ - int istate; /* input from channel (state of receive half) */ - int ostate; /* output to channel (state of transmit half) */ - int sock; /* data socket, linked to this channel */ - Buffer input; /* data read from socket, to be sent over encrypted connection */ - Buffer output; /* data received over encrypted connection for send on socket */ - char path[200]; /* path for unix domain sockets, or host name for forwards */ - int listening_port; /* port being listened for forwards */ - int host_port; /* remote port to connect for forwards */ - char *remote_name; /* remote hostname */ -} Channel; - +typedef struct Channel { + int type; /* channel type/state */ + int self; /* my own channel identifier */ + int remote_id; /* channel identifier for remote peer */ + /* peer can be reached over encrypted connection, via packet-sent */ + int istate; /* input from channel (state of receive half) */ + int ostate; /* output to channel (state of transmit half) */ + int sock; /* data socket, linked to this channel */ + Buffer input; /* data read from socket, to be sent over + * encrypted connection */ + Buffer output; /* data received over encrypted connection for + * send on socket */ + char path[200]; /* path for unix domain sockets, or host name + * for forwards */ + int listening_port; /* port being listened for forwards */ + int host_port; /* remote port to connect for forwards */ + char *remote_name; /* remote hostname */ +} Channel; #endif diff --git a/cipher.c b/cipher.c index f0da856e..dd474c25 100644 --- a/cipher.c +++ b/cipher.c @@ -1,15 +1,15 @@ /* - -cipher.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Wed Apr 19 17:41:39 1995 ylo - -*/ + * + * cipher.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Wed Apr 19 17:41:39 1995 ylo + * + */ #include "includes.h" RCSID("$Id$"); @@ -38,124 +38,124 @@ RCSID("$Id$"); */ void SSH_3CBC_ENCRYPT(des_key_schedule ks1, - des_key_schedule ks2, des_cblock *iv2, - des_key_schedule ks3, des_cblock *iv3, + des_key_schedule ks2, des_cblock * iv2, + des_key_schedule ks3, des_cblock * iv3, void *dest, void *src, unsigned int len) { - des_cblock iv1; + des_cblock iv1; - memcpy(&iv1, iv2, 8); + memcpy(&iv1, iv2, 8); - des_cbc_encrypt(src, dest, len, ks1, &iv1, DES_ENCRYPT); - memcpy(&iv1, dest + len - 8, 8); + des_cbc_encrypt(src, dest, len, ks1, &iv1, DES_ENCRYPT); + memcpy(&iv1, dest + len - 8, 8); - des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_DECRYPT); - memcpy(iv2, &iv1, 8); /* Note how iv1 == iv2 on entry and exit. */ + des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_DECRYPT); + memcpy(iv2, &iv1, 8); /* Note how iv1 == iv2 on entry and exit. */ - des_cbc_encrypt(dest, dest, len, ks3, iv3, DES_ENCRYPT); - memcpy(iv3, dest + len - 8, 8); + des_cbc_encrypt(dest, dest, len, ks3, iv3, DES_ENCRYPT); + memcpy(iv3, dest + len - 8, 8); } void SSH_3CBC_DECRYPT(des_key_schedule ks1, - des_key_schedule ks2, des_cblock *iv2, - des_key_schedule ks3, des_cblock *iv3, + des_key_schedule ks2, des_cblock * iv2, + des_key_schedule ks3, des_cblock * iv3, void *dest, void *src, unsigned int len) { - des_cblock iv1; + des_cblock iv1; - memcpy(&iv1, iv2, 8); + memcpy(&iv1, iv2, 8); - des_cbc_encrypt(src, dest, len, ks3, iv3, DES_DECRYPT); - memcpy(iv3, src + len - 8, 8); + des_cbc_encrypt(src, dest, len, ks3, iv3, DES_DECRYPT); + memcpy(iv3, src + len - 8, 8); - des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_ENCRYPT); - memcpy(iv2, dest + len - 8, 8); + des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_ENCRYPT); + memcpy(iv2, dest + len - 8, 8); - des_cbc_encrypt(dest, dest, len, ks1, &iv1, DES_DECRYPT); - /* memcpy(&iv1, iv2, 8); */ /* Note how iv1 == iv2 on entry and exit. */ + des_cbc_encrypt(dest, dest, len, ks1, &iv1, DES_DECRYPT); + /* memcpy(&iv1, iv2, 8); */ + /* Note how iv1 == iv2 on entry and exit. */ } /* * SSH uses a variation on Blowfish, all bytes must be swapped before * and after encryption/decryption. Thus the swap_bytes stuff (yuk). */ -static -void +static void swap_bytes(const unsigned char *src, unsigned char *dst_, int n) { - u_int32_t *dst = (u_int32_t *)dst_; /* dst must be properly aligned. */ - union { - u_int32_t i; - char c[4]; - } t; - - /* Process 8 bytes every lap. */ - for (n = n / 8; n > 0; n--) - { - t.c[3] = *src++; - t.c[2] = *src++; - t.c[1] = *src++; - t.c[0] = *src++; - *dst++ = t.i; - - t.c[3] = *src++; - t.c[2] = *src++; - t.c[1] = *src++; - t.c[0] = *src++; - *dst++ = t.i; - } + /* dst must be properly aligned. */ + u_int32_t *dst = (u_int32_t *) dst_; + union { + u_int32_t i; + char c[4]; + } t; + + /* Process 8 bytes every lap. */ + for (n = n / 8; n > 0; n--) { + t.c[3] = *src++; + t.c[2] = *src++; + t.c[1] = *src++; + t.c[0] = *src++; + *dst++ = t.i; + + t.c[3] = *src++; + t.c[2] = *src++; + t.c[1] = *src++; + t.c[0] = *src++; + *dst++ = t.i; + } } -void (*cipher_attack_detected)(const char *fmt, ...) = fatal; +void (*cipher_attack_detected) (const char *fmt,...) = fatal; -static inline -void +static inline void detect_cbc_attack(const unsigned char *src, unsigned int len) { - return; - - log("CRC-32 CBC insertion attack detected"); - cipher_attack_detected("CRC-32 CBC insertion attack detected"); + return; + + log("CRC-32 CBC insertion attack detected"); + cipher_attack_detected("CRC-32 CBC insertion attack detected"); } /* Names of all encryption algorithms. These must match the numbers defined int cipher.h. */ static char *cipher_names[] = { - "none", - "idea", - "des", - "3des", - "tss", - "rc4", - "blowfish" + "none", + "idea", + "des", + "3des", + "tss", + "rc4", + "blowfish" }; /* Returns a bit mask indicating which ciphers are supported by this implementation. The bit mask has the corresponding bit set of each supported cipher. */ -unsigned int cipher_mask() +unsigned int +cipher_mask() { - unsigned int mask = 0; - mask |= 1 << SSH_CIPHER_3DES; /* Mandatory */ - mask |= 1 << SSH_CIPHER_BLOWFISH; - return mask; + unsigned int mask = 0; + mask |= 1 << SSH_CIPHER_3DES; /* Mandatory */ + mask |= 1 << SSH_CIPHER_BLOWFISH; + return mask; } /* Returns the name of the cipher. */ -const -char *cipher_name(int cipher) +const char * +cipher_name(int cipher) { - if (cipher < 0 || cipher >= sizeof(cipher_names) / sizeof(cipher_names[0]) || - cipher_names[cipher] == NULL) - fatal("cipher_name: bad cipher number: %d", cipher); - return cipher_names[cipher]; + if (cipher < 0 || cipher >= sizeof(cipher_names) / sizeof(cipher_names[0]) || + cipher_names[cipher] == NULL) + fatal("cipher_name: bad cipher number: %d", cipher); + return cipher_names[cipher]; } /* Parses the name of the cipher. Returns the number of the corresponding @@ -164,146 +164,151 @@ char *cipher_name(int cipher) int cipher_number(const char *name) { - int i; - for (i = 0; i < sizeof(cipher_names) / sizeof(cipher_names[0]); i++) - if (strcmp(cipher_names[i], name) == 0 && - (cipher_mask() & (1 << i))) - return i; - return -1; + int i; + for (i = 0; i < sizeof(cipher_names) / sizeof(cipher_names[0]); i++) + if (strcmp(cipher_names[i], name) == 0 && + (cipher_mask() & (1 << i))) + return i; + return -1; } /* Selects the cipher, and keys if by computing the MD5 checksum of the passphrase and using the resulting 16 bytes as the key. */ -void cipher_set_key_string(CipherContext *context, int cipher, - const char *passphrase, int for_encryption) +void +cipher_set_key_string(CipherContext *context, int cipher, + const char *passphrase, int for_encryption) { - MD5_CTX md; - unsigned char digest[16]; - - MD5_Init(&md); - MD5_Update(&md, (const unsigned char *)passphrase, strlen(passphrase)); - MD5_Final(digest, &md); - - cipher_set_key(context, cipher, digest, 16, for_encryption); - - memset(digest, 0, sizeof(digest)); - memset(&md, 0, sizeof(md)); + MD5_CTX md; + unsigned char digest[16]; + + MD5_Init(&md); + MD5_Update(&md, (const unsigned char *) passphrase, strlen(passphrase)); + MD5_Final(digest, &md); + + cipher_set_key(context, cipher, digest, 16, for_encryption); + + memset(digest, 0, sizeof(digest)); + memset(&md, 0, sizeof(md)); } /* Selects the cipher to use and sets the key. */ -void cipher_set_key(CipherContext *context, int cipher, - const unsigned char *key, int keylen, int for_encryption) +void +cipher_set_key(CipherContext *context, int cipher, + const unsigned char *key, int keylen, int for_encryption) { - unsigned char padded[32]; - - /* Set cipher type. */ - context->type = cipher; - - /* Get 32 bytes of key data. Pad if necessary. (So that code below does - not need to worry about key size). */ - memset(padded, 0, sizeof(padded)); - memcpy(padded, key, keylen < sizeof(padded) ? keylen : sizeof(padded)); - - /* Initialize the initialization vector. */ - switch (cipher) - { - case SSH_CIPHER_NONE: - /* Has to stay for authfile saving of private key with no passphrase */ - break; - - case SSH_CIPHER_3DES: - /* Note: the least significant bit of each byte of key is parity, - and must be ignored by the implementation. 16 bytes of key are - used (first and last keys are the same). */ - if (keylen < 16) - error("Key length %d is insufficient for 3DES.", keylen); - des_set_key((void*)padded, context->u.des3.key1); - des_set_key((void*)(padded + 8), context->u.des3.key2); - if (keylen <= 16) - des_set_key((void*)padded, context->u.des3.key3); - else - des_set_key((void*)(padded + 16), context->u.des3.key3); - memset(context->u.des3.iv2, 0, sizeof(context->u.des3.iv2)); - memset(context->u.des3.iv3, 0, sizeof(context->u.des3.iv3)); - break; - - case SSH_CIPHER_BLOWFISH: - BF_set_key(&context->u.bf.key, keylen, padded); - memset(context->u.bf.iv, 0, 8); - break; - - default: - fatal("cipher_set_key: unknown cipher: %s", cipher_name(cipher)); - } - memset(padded, 0, sizeof(padded)); + unsigned char padded[32]; + + /* Set cipher type. */ + context->type = cipher; + + /* Get 32 bytes of key data. Pad if necessary. (So that code + below does not need to worry about key size). */ + memset(padded, 0, sizeof(padded)); + memcpy(padded, key, keylen < sizeof(padded) ? keylen : sizeof(padded)); + + /* Initialize the initialization vector. */ + switch (cipher) { + case SSH_CIPHER_NONE: + /* Has to stay for authfile saving of private key with + no passphrase */ + break; + + case SSH_CIPHER_3DES: + /* Note: the least significant bit of each byte of key is + parity, and must be ignored by the implementation. 16 + bytes of key are used (first and last keys are the + same). */ + if (keylen < 16) + error("Key length %d is insufficient for 3DES.", keylen); + des_set_key((void *) padded, context->u.des3.key1); + des_set_key((void *) (padded + 8), context->u.des3.key2); + if (keylen <= 16) + des_set_key((void *) padded, context->u.des3.key3); + else + des_set_key((void *) (padded + 16), context->u.des3.key3); + memset(context->u.des3.iv2, 0, sizeof(context->u.des3.iv2)); + memset(context->u.des3.iv3, 0, sizeof(context->u.des3.iv3)); + break; + + case SSH_CIPHER_BLOWFISH: + BF_set_key(&context->u.bf.key, keylen, padded); + memset(context->u.bf.iv, 0, 8); + break; + + default: + fatal("cipher_set_key: unknown cipher: %s", cipher_name(cipher)); + } + memset(padded, 0, sizeof(padded)); } /* Encrypts data using the cipher. */ -void cipher_encrypt(CipherContext *context, unsigned char *dest, - const unsigned char *src, unsigned int len) +void +cipher_encrypt(CipherContext *context, unsigned char *dest, + const unsigned char *src, unsigned int len) { - if ((len & 7) != 0) - fatal("cipher_encrypt: bad plaintext length %d", len); - - switch (context->type) - { - case SSH_CIPHER_NONE: - memcpy(dest, src, len); - break; - - case SSH_CIPHER_3DES: - SSH_3CBC_ENCRYPT(context->u.des3.key1, - context->u.des3.key2, &context->u.des3.iv2, - context->u.des3.key3, &context->u.des3.iv3, - dest, (void*)src, len); - break; - - case SSH_CIPHER_BLOWFISH: - swap_bytes(src, dest, len); - BF_cbc_encrypt(dest, dest, len, - &context->u.bf.key, context->u.bf.iv, BF_ENCRYPT); - swap_bytes(dest, dest, len); - break; - - default: - fatal("cipher_encrypt: unknown cipher: %d", context->type); - } + if ((len & 7) != 0) + fatal("cipher_encrypt: bad plaintext length %d", len); + + switch (context->type) { + case SSH_CIPHER_NONE: + memcpy(dest, src, len); + break; + + case SSH_CIPHER_3DES: + SSH_3CBC_ENCRYPT(context->u.des3.key1, + context->u.des3.key2, &context->u.des3.iv2, + context->u.des3.key3, &context->u.des3.iv3, + dest, (void *) src, len); + break; + + case SSH_CIPHER_BLOWFISH: + swap_bytes(src, dest, len); + BF_cbc_encrypt(dest, dest, len, + &context->u.bf.key, context->u.bf.iv, + BF_ENCRYPT); + swap_bytes(dest, dest, len); + break; + + default: + fatal("cipher_encrypt: unknown cipher: %s", cipher_name(context->type)); + } } - + /* Decrypts data using the cipher. */ -void cipher_decrypt(CipherContext *context, unsigned char *dest, - const unsigned char *src, unsigned int len) +void +cipher_decrypt(CipherContext *context, unsigned char *dest, + const unsigned char *src, unsigned int len) { - if ((len & 7) != 0) - fatal("cipher_decrypt: bad ciphertext length %d", len); - - switch (context->type) - { - case SSH_CIPHER_NONE: - memcpy(dest, src, len); - break; - - case SSH_CIPHER_3DES: - /* CRC-32 attack? */ - SSH_3CBC_DECRYPT(context->u.des3.key1, - context->u.des3.key2, &context->u.des3.iv2, - context->u.des3.key3, &context->u.des3.iv3, - dest, (void*)src, len); - break; - - case SSH_CIPHER_BLOWFISH: - detect_cbc_attack(src, len); - swap_bytes(src, dest, len); - BF_cbc_encrypt((void*)dest, dest, len, - &context->u.bf.key, context->u.bf.iv, BF_DECRYPT); - swap_bytes(dest, dest, len); - break; - - default: - fatal("cipher_decrypt: unknown cipher: %d", context->type); - } + if ((len & 7) != 0) + fatal("cipher_decrypt: bad ciphertext length %d", len); + + switch (context->type) { + case SSH_CIPHER_NONE: + memcpy(dest, src, len); + break; + + case SSH_CIPHER_3DES: + /* CRC-32 attack? */ + SSH_3CBC_DECRYPT(context->u.des3.key1, + context->u.des3.key2, &context->u.des3.iv2, + context->u.des3.key3, &context->u.des3.iv3, + dest, (void *) src, len); + break; + + case SSH_CIPHER_BLOWFISH: + detect_cbc_attack(src, len); + swap_bytes(src, dest, len); + BF_cbc_encrypt((void *) dest, dest, len, + &context->u.bf.key, context->u.bf.iv, + BF_DECRYPT); + swap_bytes(dest, dest, len); + break; + + default: + fatal("cipher_decrypt: unknown cipher: %s", cipher_name(context->type)); + } } diff --git a/cipher.h b/cipher.h index 90b02c11..5a7078df 100644 --- a/cipher.h +++ b/cipher.h @@ -1,15 +1,15 @@ /* - -cipher.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Wed Apr 19 16:50:42 1995 ylo - -*/ + * + * cipher.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Wed Apr 19 16:50:42 1995 ylo + * + */ /* RCSID("$Id$"); */ @@ -29,32 +29,31 @@ Created: Wed Apr 19 16:50:42 1995 ylo /* Cipher types. New types can be added, but old types should not be removed for compatibility. The maximum allowed value is 31. */ -#define SSH_CIPHER_NOT_SET -1 /* None selected (invalid number). */ -#define SSH_CIPHER_NONE 0 /* no encryption */ -#define SSH_CIPHER_IDEA 1 /* IDEA CFB */ -#define SSH_CIPHER_DES 2 /* DES CBC */ -#define SSH_CIPHER_3DES 3 /* 3DES CBC */ -#define SSH_CIPHER_BROKEN_TSS 4 /* TRI's Simple Stream encryption CBC */ -#define SSH_CIPHER_BROKEN_RC4 5 /* Alleged RC4 */ +#define SSH_CIPHER_NOT_SET -1 /* None selected (invalid number). */ +#define SSH_CIPHER_NONE 0 /* no encryption */ +#define SSH_CIPHER_IDEA 1 /* IDEA CFB */ +#define SSH_CIPHER_DES 2 /* DES CBC */ +#define SSH_CIPHER_3DES 3 /* 3DES CBC */ +#define SSH_CIPHER_BROKEN_TSS 4 /* TRI's Simple Stream encryption CBC */ +#define SSH_CIPHER_BROKEN_RC4 5 /* Alleged RC4 */ #define SSH_CIPHER_BLOWFISH 6 typedef struct { - unsigned int type; - union { - struct { - des_key_schedule key1; - des_key_schedule key2; - des_cblock iv2; - des_key_schedule key3; - des_cblock iv3; - } des3; - struct { - struct bf_key_st key; - unsigned char iv[8]; - } bf; - } u; -} CipherContext; - + unsigned int type; + union { + struct { + des_key_schedule key1; + des_key_schedule key2; + des_cblock iv2; + des_key_schedule key3; + des_cblock iv3; + } des3; + struct { + struct bf_key_st key; + unsigned char iv[8]; + } bf; + } u; +} CipherContext; /* Returns a bit mask indicating which ciphers are supported by this implementation. The bit mask has the corresponding bit set of each supported cipher. */ @@ -65,28 +64,32 @@ const char *cipher_name(int cipher); /* Parses the name of the cipher. Returns the number of the corresponding cipher, or -1 on error. */ -int cipher_number(const char *name); +int cipher_number(const char *name); /* Selects the cipher to use and sets the key. If for_encryption is true, the key is setup for encryption; otherwise it is setup for decryption. */ -void cipher_set_key(CipherContext *context, int cipher, - const unsigned char *key, int keylen, int for_encryption); +void +cipher_set_key(CipherContext * context, int cipher, + const unsigned char *key, int keylen, int for_encryption); /* Sets key for the cipher by computing the MD5 checksum of the passphrase, and using the resulting 16 bytes as the key. */ -void cipher_set_key_string(CipherContext *context, int cipher, - const char *passphrase, int for_encryption); +void +cipher_set_key_string(CipherContext * context, int cipher, + const char *passphrase, int for_encryption); /* Encrypts data using the cipher. */ -void cipher_encrypt(CipherContext *context, unsigned char *dest, - const unsigned char *src, unsigned int len); +void +cipher_encrypt(CipherContext * context, unsigned char *dest, + const unsigned char *src, unsigned int len); /* Decrypts data using the cipher. */ -void cipher_decrypt(CipherContext *context, unsigned char *dest, - const unsigned char *src, unsigned int len); +void +cipher_decrypt(CipherContext * context, unsigned char *dest, + const unsigned char *src, unsigned int len); /* If and CRC-32 attack is detected this function is called. Defaults * to fatal, changed to packet_disconnect in sshd and ssh. */ -extern void (*cipher_attack_detected)(const char *fmt, ...); +extern void (*cipher_attack_detected) (const char *fmt,...); -#endif /* CIPHER_H */ +#endif /* CIPHER_H */ diff --git a/clientloop.c b/clientloop.c index 4d1728d7..90fd4665 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,18 +1,18 @@ /* - -clientloop.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - - -Created: Sat Sep 23 12:23:57 1995 ylo - -The main loop for the interactive session (client side). - -*/ + * + * clientloop.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * + * Created: Sat Sep 23 12:23:57 1995 ylo + * + * The main loop for the interactive session (client side). + * + */ #include "includes.h" RCSID("$Id$"); @@ -49,292 +49,294 @@ static int in_raw_mode = 0; static int in_non_blocking_mode = 0; /* Common data for the client loop code. */ -static int escape_pending; /* Last character was the escape character */ -static int last_was_cr; /* Last character was a newline. */ -static int exit_status; /* Used to store the exit status of the command. */ -static int stdin_eof; /* EOF has been encountered on standard error. */ -static Buffer stdin_buffer; /* Buffer for stdin data. */ -static Buffer stdout_buffer; /* Buffer for stdout data. */ -static Buffer stderr_buffer; /* Buffer for stderr data. */ -static unsigned int buffer_high; /* Soft max buffer size. */ -static int max_fd; /* Maximum file descriptor number in select(). */ -static int connection_in; /* Connection to server (input). */ -static int connection_out; /* Connection to server (output). */ +static int escape_pending; /* Last character was the escape character */ +static int last_was_cr; /* Last character was a newline. */ +static int exit_status; /* Used to store the exit status of the command. */ +static int stdin_eof; /* EOF has been encountered on standard error. */ +static Buffer stdin_buffer; /* Buffer for stdin data. */ +static Buffer stdout_buffer; /* Buffer for stdout data. */ +static Buffer stderr_buffer; /* Buffer for stderr data. */ +static unsigned int buffer_high;/* Soft max buffer size. */ +static int max_fd; /* Maximum file descriptor number in select(). */ +static int connection_in; /* Connection to server (input). */ +static int connection_out; /* Connection to server (output). */ static unsigned long stdin_bytes, stdout_bytes, stderr_bytes; -static int quit_pending; /* Set to non-zero to quit the client loop. */ -static int escape_char; /* Escape character. */ +static int quit_pending; /* Set to non-zero to quit the client loop. */ +static int escape_char; /* Escape character. */ -/* Returns the user\'s terminal to normal mode if it had been put in raw +/* Returns the user\'s terminal to normal mode if it had been put in raw mode. */ -void leave_raw_mode() +void +leave_raw_mode() { - if (!in_raw_mode) - return; - in_raw_mode = 0; - if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0) - perror("tcsetattr"); + if (!in_raw_mode) + return; + in_raw_mode = 0; + if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0) + perror("tcsetattr"); - fatal_remove_cleanup((void (*)(void *))leave_raw_mode, NULL); + fatal_remove_cleanup((void (*) (void *)) leave_raw_mode, NULL); } /* Puts the user\'s terminal in raw mode. */ -void enter_raw_mode() +void +enter_raw_mode() { - struct termios tio; - - if (tcgetattr(fileno(stdin), &tio) < 0) - perror("tcgetattr"); - saved_tio = tio; - tio.c_iflag |= IGNPAR; - tio.c_iflag &= ~(ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXANY|IXOFF); - tio.c_lflag &= ~(ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHONL); + struct termios tio; + + if (tcgetattr(fileno(stdin), &tio) < 0) + perror("tcgetattr"); + saved_tio = tio; + tio.c_iflag |= IGNPAR; + tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); + tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); #ifdef IEXTEN - tio.c_lflag &= ~IEXTEN; -#endif /* IEXTEN */ - tio.c_oflag &= ~OPOST; - tio.c_cc[VMIN] = 1; - tio.c_cc[VTIME] = 0; - if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) - perror("tcsetattr"); - in_raw_mode = 1; - - fatal_add_cleanup((void (*)(void *))leave_raw_mode, NULL); -} - -/* Puts stdin terminal in non-blocking mode. */ + tio.c_lflag &= ~IEXTEN; +#endif /* IEXTEN */ + tio.c_oflag &= ~OPOST; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) + perror("tcsetattr"); + in_raw_mode = 1; + + fatal_add_cleanup((void (*) (void *)) leave_raw_mode, NULL); +} /* Restores stdin to blocking mode. */ -void leave_non_blocking() +void +leave_non_blocking() { - if (in_non_blocking_mode) - { - (void)fcntl(fileno(stdin), F_SETFL, 0); - in_non_blocking_mode = 0; - fatal_remove_cleanup((void (*)(void *))leave_non_blocking, NULL); - } + if (in_non_blocking_mode) { + (void) fcntl(fileno(stdin), F_SETFL, 0); + in_non_blocking_mode = 0; + fatal_remove_cleanup((void (*) (void *)) leave_non_blocking, NULL); + } } -void enter_non_blocking() +/* Puts stdin terminal in non-blocking mode. */ + +void +enter_non_blocking() { - in_non_blocking_mode = 1; - (void)fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); - fatal_add_cleanup((void (*)(void *))leave_non_blocking, NULL); + in_non_blocking_mode = 1; + (void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); + fatal_add_cleanup((void (*) (void *)) leave_non_blocking, NULL); } /* Signal handler for the window change signal (SIGWINCH). This just sets a flag indicating that the window has changed. */ -void window_change_handler(int sig) +void +window_change_handler(int sig) { - received_window_change_signal = 1; - signal(SIGWINCH, window_change_handler); + received_window_change_signal = 1; + signal(SIGWINCH, window_change_handler); } /* Signal handler for signals that cause the program to terminate. These signals must be trapped to restore terminal modes. */ -void signal_handler(int sig) +void +signal_handler(int sig) { - if (in_raw_mode) - leave_raw_mode(); - if (in_non_blocking_mode) - leave_non_blocking(); - channel_stop_listening(); - packet_close(); - fatal("Killed by signal %d.", sig); + if (in_raw_mode) + leave_raw_mode(); + if (in_non_blocking_mode) + leave_non_blocking(); + channel_stop_listening(); + packet_close(); + fatal("Killed by signal %d.", sig); } /* Returns current time in seconds from Jan 1, 1970 with the maximum available resolution. */ -double get_current_time() +double +get_current_time() { - struct timeval tv; - gettimeofday(&tv, NULL); - return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; + struct timeval tv; + gettimeofday(&tv, NULL); + return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; } /* This is called when the interactive is entered. This checks if there is an EOF coming on stdin. We must check this explicitly, as select() does not appear to wake up when redirecting from /dev/null. */ -void client_check_initial_eof_on_stdin() +void +client_check_initial_eof_on_stdin() { - int len; - char buf[1]; - - /* If standard input is to be "redirected from /dev/null", we simply - mark that we have seen an EOF and send an EOF message to the server. - Otherwise, we try to read a single character; it appears that for some - files, such /dev/null, select() never wakes up for read for this - descriptor, which means that we never get EOF. This way we will get - the EOF if stdin comes from /dev/null or similar. */ - if (stdin_null_flag) - { - /* Fake EOF on stdin. */ - debug("Sending eof."); - stdin_eof = 1; - packet_start(SSH_CMSG_EOF); - packet_send(); - } - else - { - /* Enter non-blocking mode for stdin. */ - enter_non_blocking(); - - /* Check for immediate EOF on stdin. */ - len = read(fileno(stdin), buf, 1); - if (len == 0) - { - /* EOF. Record that we have seen it and send EOF to server. */ - debug("Sending eof."); - stdin_eof = 1; - packet_start(SSH_CMSG_EOF); - packet_send(); + int len; + char buf[1]; + + /* If standard input is to be "redirected from /dev/null", we + simply mark that we have seen an EOF and send an EOF message to + the server. Otherwise, we try to read a single character; it + appears that for some files, such /dev/null, select() never + wakes up for read for this descriptor, which means that we + never get EOF. This way we will get the EOF if stdin comes + from /dev/null or similar. */ + if (stdin_null_flag) { + /* Fake EOF on stdin. */ + debug("Sending eof."); + stdin_eof = 1; + packet_start(SSH_CMSG_EOF); + packet_send(); + } else { + /* Enter non-blocking mode for stdin. */ + enter_non_blocking(); + + /* Check for immediate EOF on stdin. */ + len = read(fileno(stdin), buf, 1); + if (len == 0) { + /* EOF. Record that we have seen it and send EOF + to server. */ + debug("Sending eof."); + stdin_eof = 1; + packet_start(SSH_CMSG_EOF); + packet_send(); + } else if (len > 0) { + /* Got data. We must store the data in the + buffer, and also process it as an escape + character if appropriate. */ + if ((unsigned char) buf[0] == escape_char) + escape_pending = 1; + else { + buffer_append(&stdin_buffer, buf, 1); + stdin_bytes += 1; + } + } + /* Leave non-blocking mode. */ + leave_non_blocking(); } - else - if (len > 0) - { - /* Got data. We must store the data in the buffer, and also - process it as an escape character if appropriate. */ - if ((unsigned char)buf[0] == escape_char) - escape_pending = 1; - else - { - buffer_append(&stdin_buffer, buf, 1); - stdin_bytes += 1; - } - } - - /* Leave non-blocking mode. */ - leave_non_blocking(); - } } /* Get packets from the connection input buffer, and process them as long as there are packets available. */ -void client_process_buffered_input_packets() +void +client_process_buffered_input_packets() { - int type; - char *data; - unsigned int data_len; - int payload_len; - - /* Process any buffered packets from the server. */ - while (!quit_pending && (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) - { - switch (type) - { - - case SSH_SMSG_STDOUT_DATA: - data = packet_get_string(&data_len); - packet_integrity_check(payload_len, 4 + data_len, type); - buffer_append(&stdout_buffer, data, data_len); - stdout_bytes += data_len; - memset(data, 0, data_len); - xfree(data); - break; - - case SSH_SMSG_STDERR_DATA: - data = packet_get_string(&data_len); - packet_integrity_check(payload_len, 4 + data_len, type); - buffer_append(&stderr_buffer, data, data_len); - stdout_bytes += data_len; - memset(data, 0, data_len); - xfree(data); - break; - - case SSH_SMSG_EXITSTATUS: - packet_integrity_check(payload_len, 4, type); - exit_status = packet_get_int(); - /* Acknowledge the exit. */ - packet_start(SSH_CMSG_EXIT_CONFIRMATION); - packet_send(); - /* Must wait for packet to be sent since we are exiting the - loop. */ - packet_write_wait(); - /* Flag that we want to exit. */ - quit_pending = 1; - break; - - case SSH_SMSG_X11_OPEN: - x11_input_open(payload_len); - break; - - case SSH_MSG_PORT_OPEN: - channel_input_port_open(payload_len); - break; - - case SSH_SMSG_AGENT_OPEN: - packet_integrity_check(payload_len, 4, type); - auth_input_open_request(); - break; - - case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: - packet_integrity_check(payload_len, 4 + 4, type); - channel_input_open_confirmation(); - break; - - case SSH_MSG_CHANNEL_OPEN_FAILURE: - packet_integrity_check(payload_len, 4, type); - channel_input_open_failure(); - break; - - case SSH_MSG_CHANNEL_DATA: - channel_input_data(payload_len); - break; - - case SSH_MSG_CHANNEL_CLOSE: - packet_integrity_check(payload_len, 4, type); - channel_input_close(); - break; - - case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: - packet_integrity_check(payload_len, 4, type); - channel_input_close_confirmation(); - break; - - default: - /* Any unknown packets received during the actual session - cause the session to terminate. This is intended to make - debugging easier since no confirmations are sent. Any - compatible protocol extensions must be negotiated during - the preparatory phase. */ - packet_disconnect("Protocol error during session: type %d", - type); + int type; + char *data; + unsigned int data_len; + int payload_len; + + /* Process any buffered packets from the server. */ + while (!quit_pending && + (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) { + switch (type) { + + case SSH_SMSG_STDOUT_DATA: + data = packet_get_string(&data_len); + packet_integrity_check(payload_len, 4 + data_len, type); + buffer_append(&stdout_buffer, data, data_len); + stdout_bytes += data_len; + memset(data, 0, data_len); + xfree(data); + break; + + case SSH_SMSG_STDERR_DATA: + data = packet_get_string(&data_len); + packet_integrity_check(payload_len, 4 + data_len, type); + buffer_append(&stderr_buffer, data, data_len); + stdout_bytes += data_len; + memset(data, 0, data_len); + xfree(data); + break; + + case SSH_SMSG_EXITSTATUS: + packet_integrity_check(payload_len, 4, type); + exit_status = packet_get_int(); + /* Acknowledge the exit. */ + packet_start(SSH_CMSG_EXIT_CONFIRMATION); + packet_send(); + /* Must wait for packet to be sent since we are + exiting the loop. */ + packet_write_wait(); + /* Flag that we want to exit. */ + quit_pending = 1; + break; + + case SSH_SMSG_X11_OPEN: + x11_input_open(payload_len); + break; + + case SSH_MSG_PORT_OPEN: + channel_input_port_open(payload_len); + break; + + case SSH_SMSG_AGENT_OPEN: + packet_integrity_check(payload_len, 4, type); + auth_input_open_request(); + break; + + case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: + packet_integrity_check(payload_len, 4 + 4, type); + channel_input_open_confirmation(); + break; + + case SSH_MSG_CHANNEL_OPEN_FAILURE: + packet_integrity_check(payload_len, 4, type); + channel_input_open_failure(); + break; + + case SSH_MSG_CHANNEL_DATA: + channel_input_data(payload_len); + break; + + case SSH_MSG_CHANNEL_CLOSE: + packet_integrity_check(payload_len, 4, type); + channel_input_close(); + break; + + case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: + packet_integrity_check(payload_len, 4, type); + channel_input_close_confirmation(); + break; + + default: + /* Any unknown packets received during the actual + session cause the session to terminate. This + is intended to make debugging easier since no + confirmations are sent. Any compatible + protocol extensions must be negotiated during + the preparatory phase. */ + packet_disconnect("Protocol error during session: type %d", + type); + } } - } } /* Make packets from buffered stdin data, and buffer them for sending to the connection. */ -void client_make_packets_from_stdin_data() +void +client_make_packets_from_stdin_data() { - unsigned int len; - - /* Send buffered stdin data to the server. */ - while (buffer_len(&stdin_buffer) > 0 && - packet_not_very_much_data_to_write()) - { - len = buffer_len(&stdin_buffer); - if (len > packet_get_maxsize()) - len = packet_get_maxsize(); /* Keep the packets at reasonable size. */ - packet_start(SSH_CMSG_STDIN_DATA); - packet_put_string(buffer_ptr(&stdin_buffer), len); - packet_send(); - buffer_consume(&stdin_buffer, len); - /* If we have a pending EOF, send it now. */ - if (stdin_eof && buffer_len(&stdin_buffer) == 0) - { - packet_start(SSH_CMSG_EOF); - packet_send(); + unsigned int len; + + /* Send buffered stdin data to the server. */ + while (buffer_len(&stdin_buffer) > 0 && + packet_not_very_much_data_to_write()) { + len = buffer_len(&stdin_buffer); + /* Keep the packets at reasonable size. */ + if (len > packet_get_maxsize()) + len = packet_get_maxsize(); + packet_start(SSH_CMSG_STDIN_DATA); + packet_put_string(buffer_ptr(&stdin_buffer), len); + packet_send(); + buffer_consume(&stdin_buffer, len); + /* If we have a pending EOF, send it now. */ + if (stdin_eof && buffer_len(&stdin_buffer) == 0) { + packet_start(SSH_CMSG_EOF); + packet_send(); + } } - } } /* Checks if the client window has changed, and sends a packet about it to @@ -342,303 +344,286 @@ void client_make_packets_from_stdin_data() interrupt on Unix); this just checks the flag and sends a message if appropriate. */ -void client_check_window_change() +void +client_check_window_change() { - /* Send possible window change message to the server. */ - if (received_window_change_signal) - { - struct winsize ws; - - /* Clear the window change indicator. */ - received_window_change_signal = 0; - - /* Read new window size. */ - if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) - { - /* Successful, send the packet now. */ - packet_start(SSH_CMSG_WINDOW_SIZE); - packet_put_int(ws.ws_row); - packet_put_int(ws.ws_col); - packet_put_int(ws.ws_xpixel); - packet_put_int(ws.ws_ypixel); - packet_send(); + /* Send possible window change message to the server. */ + if (received_window_change_signal) { + struct winsize ws; + + /* Clear the window change indicator. */ + received_window_change_signal = 0; + + /* Read new window size. */ + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) { + /* Successful, send the packet now. */ + packet_start(SSH_CMSG_WINDOW_SIZE); + packet_put_int(ws.ws_row); + packet_put_int(ws.ws_col); + packet_put_int(ws.ws_xpixel); + packet_put_int(ws.ws_ypixel); + packet_send(); + } } - } } /* Waits until the client can do something (some data becomes available on one of the file descriptors). */ -void client_wait_until_can_do_something(fd_set *readset, fd_set *writeset) +void +client_wait_until_can_do_something(fd_set * readset, fd_set * writeset) { - /* Initialize select masks. */ - FD_ZERO(readset); - - /* Read from the connection, unless our buffers are full. */ - if (buffer_len(&stdout_buffer) < buffer_high && - buffer_len(&stderr_buffer) < buffer_high && - channel_not_very_much_buffered_data()) - FD_SET(connection_in, readset); - - /* Read from stdin, unless we have seen EOF or have very much buffered - data to send to the server. */ - if (!stdin_eof && packet_not_very_much_data_to_write()) - FD_SET(fileno(stdin), readset); - - FD_ZERO(writeset); - - /* Add any selections by the channel mechanism. */ - channel_prepare_select(readset, writeset); - - /* Select server connection if have data to write to the server. */ - if (packet_have_data_to_write()) - FD_SET(connection_out, writeset); - - /* Select stdout if have data in buffer. */ - if (buffer_len(&stdout_buffer) > 0) - FD_SET(fileno(stdout), writeset); - - /* Select stderr if have data in buffer. */ - if (buffer_len(&stderr_buffer) > 0) - FD_SET(fileno(stderr), writeset); - - /* Update maximum file descriptor number, if appropriate. */ - if (channel_max_fd() > max_fd) - max_fd = channel_max_fd(); - - /* Wait for something to happen. This will suspend the process until - some selected descriptor can be read, written, or has some other - event pending. Note: if you want to implement SSH_MSG_IGNORE - messages to fool traffic analysis, this might be the place to do - it: just have a random timeout for the select, and send a random - SSH_MSG_IGNORE packet when the timeout expires. */ - if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) - { - char buf[100]; - /* Some systems fail to clear these automatically. */ - FD_ZERO(readset); - FD_ZERO(writeset); - if (errno == EINTR) - return; - /* Note: we might still have data in the buffers. */ - snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); - buffer_append(&stderr_buffer, buf, strlen(buf)); - stderr_bytes += strlen(buf); - quit_pending = 1; - } + /* Initialize select masks. */ + FD_ZERO(readset); + + /* Read from the connection, unless our buffers are full. */ + if (buffer_len(&stdout_buffer) < buffer_high && + buffer_len(&stderr_buffer) < buffer_high && + channel_not_very_much_buffered_data()) + FD_SET(connection_in, readset); + + /* Read from stdin, unless we have seen EOF or have very much + buffered data to send to the server. */ + if (!stdin_eof && packet_not_very_much_data_to_write()) + FD_SET(fileno(stdin), readset); + + FD_ZERO(writeset); + + /* Add any selections by the channel mechanism. */ + channel_prepare_select(readset, writeset); + + /* Select server connection if have data to write to the server. */ + if (packet_have_data_to_write()) + FD_SET(connection_out, writeset); + + /* Select stdout if have data in buffer. */ + if (buffer_len(&stdout_buffer) > 0) + FD_SET(fileno(stdout), writeset); + + /* Select stderr if have data in buffer. */ + if (buffer_len(&stderr_buffer) > 0) + FD_SET(fileno(stderr), writeset); + + /* Update maximum file descriptor number, if appropriate. */ + if (channel_max_fd() > max_fd) + max_fd = channel_max_fd(); + + /* Wait for something to happen. This will suspend the process + until some selected descriptor can be read, written, or has + some other event pending. + Note: if you want to implement SSH_MSG_IGNORE messages to fool + traffic analysis, this might be the place to do it: + just have a random timeout for the select, and send a random + SSH_MSG_IGNORE packet when the timeout expires. */ + + if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) { + char buf[100]; + /* Some systems fail to clear these automatically. */ + FD_ZERO(readset); + FD_ZERO(writeset); + if (errno == EINTR) + return; + /* Note: we might still have data in the buffers. */ + snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); + buffer_append(&stderr_buffer, buf, strlen(buf)); + stderr_bytes += strlen(buf); + quit_pending = 1; + } } -void client_suspend_self() +void +client_suspend_self() { - struct winsize oldws, newws; - - /* Flush stdout and stderr buffers. */ - if (buffer_len(&stdout_buffer) > 0) - write(fileno(stdout), - buffer_ptr(&stdout_buffer), - buffer_len(&stdout_buffer)); - if (buffer_len(&stderr_buffer) > 0) - write(fileno(stderr), - buffer_ptr(&stderr_buffer), - buffer_len(&stderr_buffer)); - - /* Leave raw mode. */ - leave_raw_mode(); - - /* Free (and clear) the buffer to reduce the - amount of data that gets written to swap. */ - buffer_free(&stdin_buffer); - buffer_free(&stdout_buffer); - buffer_free(&stderr_buffer); - - /* Save old window size. */ - ioctl(fileno(stdin), TIOCGWINSZ, &oldws); - - /* Send the suspend signal to the program - itself. */ - kill(getpid(), SIGTSTP); - - /* Check if the window size has changed. */ - if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 && - (oldws.ws_row != newws.ws_row || oldws.ws_col != newws.ws_col || - oldws.ws_xpixel != newws.ws_xpixel || - oldws.ws_ypixel != newws.ws_ypixel)) - received_window_change_signal = 1; - - /* OK, we have been continued by the user. - Reinitialize buffers. */ - buffer_init(&stdin_buffer); - buffer_init(&stdout_buffer); - buffer_init(&stderr_buffer); - - /* Re-enter raw mode. */ - enter_raw_mode(); + struct winsize oldws, newws; + + /* Flush stdout and stderr buffers. */ + if (buffer_len(&stdout_buffer) > 0) + write(fileno(stdout), + buffer_ptr(&stdout_buffer), + buffer_len(&stdout_buffer)); + if (buffer_len(&stderr_buffer) > 0) + write(fileno(stderr), + buffer_ptr(&stderr_buffer), + buffer_len(&stderr_buffer)); + + /* Leave raw mode. */ + leave_raw_mode(); + + /* Free (and clear) the buffer to reduce the amount of data that + gets written to swap. */ + buffer_free(&stdin_buffer); + buffer_free(&stdout_buffer); + buffer_free(&stderr_buffer); + + /* Save old window size. */ + ioctl(fileno(stdin), TIOCGWINSZ, &oldws); + + /* Send the suspend signal to the program itself. */ + kill(getpid(), SIGTSTP); + + /* Check if the window size has changed. */ + if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 && + (oldws.ws_row != newws.ws_row || + oldws.ws_col != newws.ws_col || + oldws.ws_xpixel != newws.ws_xpixel || + oldws.ws_ypixel != newws.ws_ypixel)) + received_window_change_signal = 1; + + /* OK, we have been continued by the user. Reinitialize buffers. */ + buffer_init(&stdin_buffer); + buffer_init(&stdout_buffer); + buffer_init(&stderr_buffer); + + /* Re-enter raw mode. */ + enter_raw_mode(); } -void client_process_input(fd_set *readset) +void +client_process_input(fd_set * readset) { - int len, pid; - char buf[8192], *s; - - /* Read input from the server, and add any such data to the buffer of the - packet subsystem. */ - if (FD_ISSET(connection_in, readset)) - { - /* Read as much as possible. */ - len = read(connection_in, buf, sizeof(buf)); - if (len == 0) - { - /* Received EOF. The remote host has closed the connection. */ - snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", - host); - buffer_append(&stderr_buffer, buf, strlen(buf)); - stderr_bytes += strlen(buf); - quit_pending = 1; - return; - } - - /* There is a kernel bug on Solaris that causes select to sometimes - wake up even though there is no data available. */ - if (len < 0 && errno == EAGAIN) - len = 0; - - if (len < 0) - { - /* An error has encountered. Perhaps there is a network - problem. */ - snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", - host, strerror(errno)); - buffer_append(&stderr_buffer, buf, strlen(buf)); - stderr_bytes += strlen(buf); - quit_pending = 1; - return; - } - packet_process_incoming(buf, len); - } - - /* Read input from stdin. */ - if (FD_ISSET(fileno(stdin), readset)) - { - /* Read as much as possible. */ - len = read(fileno(stdin), buf, sizeof(buf)); - if (len <= 0) - { - /* Received EOF or error. They are treated similarly, - except that an error message is printed if it was - an error condition. */ - if (len < 0) - { - snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); - buffer_append(&stderr_buffer, buf, strlen(buf)); - stderr_bytes += strlen(buf); - } - /* Mark that we have seen EOF. */ - stdin_eof = 1; - /* Send an EOF message to the server unless there is data - in the buffer. If there is data in the buffer, no message - will be sent now. Code elsewhere will send the EOF - when the buffer becomes empty if stdin_eof is set. */ - if (buffer_len(&stdin_buffer) == 0) - { - packet_start(SSH_CMSG_EOF); - packet_send(); - } - } - else - if (escape_char == -1) - { - /* Normal successful read, and no escape character. Just - append the data to buffer. */ - buffer_append(&stdin_buffer, buf, len); - stdin_bytes += len; - } - else - { - /* Normal, successful read. But we have an escape character - and have to process the characters one by one. */ - unsigned int i; - for (i = 0; i < len; i++) - { - unsigned char ch; - /* Get one character at a time. */ - ch = buf[i]; - - /* Check if we have a pending escape character. */ - if (escape_pending) - { - /* We have previously seen an escape character. */ - /* Clear the flag now. */ - escape_pending = 0; - /* Process the escaped character. */ - switch (ch) - { - case '.': - /* Terminate the connection. */ - snprintf(buf, sizeof buf, "%c.\r\n", escape_char); + int len, pid; + char buf[8192], *s; + + /* Read input from the server, and add any such data to the buffer + of the packet subsystem. */ + if (FD_ISSET(connection_in, readset)) { + /* Read as much as possible. */ + len = read(connection_in, buf, sizeof(buf)); + if (len == 0) { + /* Received EOF. The remote host has closed the connection. */ + snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", + host); buffer_append(&stderr_buffer, buf, strlen(buf)); stderr_bytes += strlen(buf); quit_pending = 1; return; - - case 'Z' - 64: - /* Suspend the program. */ - /* Print a message to that effect to the user. */ - snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char); - buffer_append(&stderr_buffer, buf, strlen(buf)); - stderr_bytes += strlen(buf); - - /* Restore terminal modes and suspend. */ - client_suspend_self(); - - /* We have been continued. */ - continue; - - case '&': - /* Detach the program (continue to serve connections, - but put in background and no more new - connections). */ - if (!stdin_eof) - { - /* Sending SSH_CMSG_EOF alone does not always - appear to be enough. So we try to send an - EOF character first. */ - packet_start(SSH_CMSG_STDIN_DATA); - packet_put_string("\004", 1); - packet_send(); - /* Close stdin. */ - stdin_eof = 1; - if (buffer_len(&stdin_buffer) == 0) - { + } + /* There is a kernel bug on Solaris that causes select to + sometimes wake up even though there is no data + available. */ + if (len < 0 && errno == EAGAIN) + len = 0; + + if (len < 0) { + /* An error has encountered. Perhaps there is a network problem. */ + snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", + host, strerror(errno)); + buffer_append(&stderr_buffer, buf, strlen(buf)); + stderr_bytes += strlen(buf); + quit_pending = 1; + return; + } + packet_process_incoming(buf, len); + } + /* Read input from stdin. */ + if (FD_ISSET(fileno(stdin), readset)) { + /* Read as much as possible. */ + len = read(fileno(stdin), buf, sizeof(buf)); + if (len <= 0) { + /* Received EOF or error. They are treated + similarly, except that an error message is + printed if it was an error condition. */ + if (len < 0) { + snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); + buffer_append(&stderr_buffer, buf, strlen(buf)); + stderr_bytes += strlen(buf); + } + /* Mark that we have seen EOF. */ + stdin_eof = 1; + /* Send an EOF message to the server unless there + is data in the buffer. If there is data in the + buffer, no message will be sent now. Code + elsewhere will send the EOF when the buffer + becomes empty if stdin_eof is set. */ + if (buffer_len(&stdin_buffer) == 0) { packet_start(SSH_CMSG_EOF); packet_send(); - } - } - /* Restore tty modes. */ - leave_raw_mode(); - - /* Stop listening for new connections. */ - channel_stop_listening(); - - printf("%c& [backgrounded]\n", escape_char); - - /* Fork into background. */ - pid = fork(); - if (pid < 0) - { - error("fork: %.100s", strerror(errno)); - continue; - } - if (pid != 0) - { /* This is the parent. */ - /* The parent just exits. */ - exit(0); - } - - /* The child continues serving connections. */ - continue; - - case '?': - snprintf(buf, sizeof buf, "%c?\r\n\ + } + } else if (escape_char == -1) { + /* Normal successful read, and no escape + character. Just append the data to buffer. */ + buffer_append(&stdin_buffer, buf, len); + stdin_bytes += len; + } else { + /* Normal, successful read. But we have an escape + character and have to process the characters + one by one. */ + unsigned int i; + for (i = 0; i < len; i++) { + unsigned char ch; + /* Get one character at a time. */ + ch = buf[i]; + + /* Check if we have a pending escape + character. */ + if (escape_pending) { + /* We have previously seen an escape character. */ + /* Clear the flag now. */ + escape_pending = 0; + /* Process the escaped character. */ + switch (ch) { + case '.': + /* Terminate the connection. */ + snprintf(buf, sizeof buf, "%c.\r\n", escape_char); + buffer_append(&stderr_buffer, buf, strlen(buf)); + stderr_bytes += strlen(buf); + quit_pending = 1; + return; + + case 'Z' - 64: + /* Suspend the program. */ + /* Print a message to that effect to the user. */ + snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char); + buffer_append(&stderr_buffer, buf, strlen(buf)); + stderr_bytes += strlen(buf); + + /* Restore terminal modes and suspend. */ + client_suspend_self(); + + /* We have been continued. */ + continue; + + case '&': + /* Detach the program (continue to serve connections, + but put in background and no more new connections). */ + if (!stdin_eof) { + /* Sending SSH_CMSG_EOF alone does not always appear + to be enough. So we try to send an EOF character + first. */ + packet_start(SSH_CMSG_STDIN_DATA); + packet_put_string("\004", 1); + packet_send(); + /* Close stdin. */ + stdin_eof = 1; + if (buffer_len(&stdin_buffer) == 0) { + packet_start(SSH_CMSG_EOF); + packet_send(); + } + } + /* Restore tty modes. */ + leave_raw_mode(); + + /* Stop listening for new connections. */ + channel_stop_listening(); + + printf("%c& [backgrounded]\n", escape_char); + + /* Fork into background. */ + pid = fork(); + if (pid < 0) { + error("fork: %.100s", strerror(errno)); + continue; + } + if (pid != 0) { /* This is the parent. */ + /* The parent just exits. */ + exit(0); + } + /* The child continues serving connections. */ + continue; + + case '?': + snprintf(buf, sizeof buf, +"%c?\r\n\ Supported escape sequences:\r\n\ ~. - terminate connection\r\n\ ~^Z - suspend ssh\r\n\ @@ -647,110 +632,100 @@ Supported escape sequences:\r\n\ ~? - this message\r\n\ ~~ - send the escape character by typing it twice\r\n\ (Note that escapes are only recognized immediately after newline.)\r\n", - escape_char); - buffer_append(&stderr_buffer, buf, strlen(buf)); - continue; - - case '#': - snprintf(buf, sizeof buf, "%c#\r\n", escape_char); - buffer_append(&stderr_buffer, buf, strlen(buf)); - s = channel_open_message(); - buffer_append(&stderr_buffer, s, strlen(s)); - xfree(s); - continue; - - default: - if (ch != escape_char) - { - /* Escape character followed by non-special - character. Append both to the input - buffer. */ - buf[0] = escape_char; - buf[1] = ch; - buffer_append(&stdin_buffer, buf, 2); - stdin_bytes += 2; - continue; - } - /* Note that escape character typed twice falls through - here; the latter gets processed as a normal - character below. */ - break; - } - } - else - { - /* The previous character was not an escape char. - Check if this is an escape. */ - if (last_was_cr && ch == escape_char) - { - /* It is. Set the flag and continue to next - character. */ - escape_pending = 1; - continue; - } - } - - /* Normal character. Record whether it was a newline, - and append it to the buffer. */ - last_was_cr = (ch == '\r' || ch == '\n'); - buf[0] = ch; - buffer_append(&stdin_buffer, buf, 1); - stdin_bytes += 1; - continue; - } - } - } + escape_char); + buffer_append(&stderr_buffer, buf, strlen(buf)); + continue; + + case '#': + snprintf(buf, sizeof buf, "%c#\r\n", escape_char); + buffer_append(&stderr_buffer, buf, strlen(buf)); + s = channel_open_message(); + buffer_append(&stderr_buffer, s, strlen(s)); + xfree(s); + continue; + + default: + if (ch != escape_char) { + /* Escape character followed by non-special character. + Append both to the input buffer. */ + buf[0] = escape_char; + buf[1] = ch; + buffer_append(&stdin_buffer, buf, 2); + stdin_bytes += 2; + continue; + } + /* Note that escape character typed twice + falls through here; the latter gets processed + as a normal character below. */ + break; + } + } else { + /* The previous character was not an escape char. Check if this + is an escape. */ + if (last_was_cr && ch == escape_char) { + /* It is. Set the flag and continue to next character. */ + escape_pending = 1; + continue; + } + } + + /* Normal character. Record whether it was a newline, and append it to the + buffer. */ + last_was_cr = (ch == '\r' || ch == '\n'); + buf[0] = ch; + buffer_append(&stdin_buffer, buf, 1); + stdin_bytes += 1; + continue; + } + } + } } -void client_process_output(fd_set *writeset) +void +client_process_output(fd_set * writeset) { - int len; - char buf[100]; - - /* Write buffered output to stdout. */ - if (FD_ISSET(fileno(stdout), writeset)) - { - /* Write as much data as possible. */ - len = write(fileno(stdout), buffer_ptr(&stdout_buffer), - buffer_len(&stdout_buffer)); - if (len <= 0) - { - if (errno == EAGAIN) - len = 0; - else - { - /* An error or EOF was encountered. Put an error message - to stderr buffer. */ - snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); - buffer_append(&stderr_buffer, buf, strlen(buf)); - stderr_bytes += strlen(buf); - quit_pending = 1; - return; - } + int len; + char buf[100]; + + /* Write buffered output to stdout. */ + if (FD_ISSET(fileno(stdout), writeset)) { + /* Write as much data as possible. */ + len = write(fileno(stdout), buffer_ptr(&stdout_buffer), + buffer_len(&stdout_buffer)); + if (len <= 0) { + if (errno == EAGAIN) + len = 0; + else { + /* An error or EOF was encountered. Put + an error message to stderr buffer. */ + snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); + buffer_append(&stderr_buffer, buf, strlen(buf)); + stderr_bytes += strlen(buf); + quit_pending = 1; + return; + } + } + /* Consume printed data from the buffer. */ + buffer_consume(&stdout_buffer, len); + } + /* Write buffered output to stderr. */ + if (FD_ISSET(fileno(stderr), writeset)) { + /* Write as much data as possible. */ + len = write(fileno(stderr), buffer_ptr(&stderr_buffer), + buffer_len(&stderr_buffer)); + if (len <= 0) { + if (errno == EAGAIN) + len = 0; + else { + /* EOF or error, but can't even print + error message. */ + quit_pending = 1; + return; + } + } + /* Consume printed characters from the buffer. */ + buffer_consume(&stderr_buffer, len); } - /* Consume printed data from the buffer. */ - buffer_consume(&stdout_buffer, len); - } - - /* Write buffered output to stderr. */ - if (FD_ISSET(fileno(stderr), writeset)) - { - /* Write as much data as possible. */ - len = write(fileno(stderr), buffer_ptr(&stderr_buffer), - buffer_len(&stderr_buffer)); - if (len <= 0) { - if (errno == EAGAIN) - len = 0; - else - { - /* EOF or error, but can't even print error message. */ - quit_pending = 1; - return; - } - } - /* Consume printed characters from the buffer. */ - buffer_consume(&stderr_buffer, len); - } } /* Implements the interactive session with the server. This is called @@ -759,165 +734,160 @@ void client_process_output(fd_set *writeset) used as an escape character for terminating or suspending the session. */ -int client_loop(int have_pty, int escape_char_arg) +int +client_loop(int have_pty, int escape_char_arg) { - extern Options options; - double start_time, total_time; - int len; - char buf[100]; - - debug("Entering interactive session."); - - start_time = get_current_time(); - - /* Initialize variables. */ - escape_pending = 0; - last_was_cr = 1; - exit_status = -1; - stdin_eof = 0; - buffer_high = 64 * 1024; - connection_in = packet_get_connection_in(); - connection_out = packet_get_connection_out(); - max_fd = connection_in; - if (connection_out > max_fd) - max_fd = connection_out; - stdin_bytes = 0; - stdout_bytes = 0; - stderr_bytes = 0; - quit_pending = 0; - escape_char = escape_char_arg; - - /* Initialize buffers. */ - buffer_init(&stdin_buffer); - buffer_init(&stdout_buffer); - buffer_init(&stderr_buffer); - - /* Set signal handlers to restore non-blocking mode. */ - signal(SIGINT, signal_handler); - signal(SIGQUIT, signal_handler); - signal(SIGTERM, signal_handler); - signal(SIGPIPE, SIG_IGN); - if (have_pty) - signal(SIGWINCH, window_change_handler); - - /* Enter raw mode if have a pseudo terminal. */ - if (have_pty) - enter_raw_mode(); - - /* Check if we should immediately send of on stdin. */ - client_check_initial_eof_on_stdin(); - - /* Main loop of the client for the interactive session mode. */ - while (!quit_pending) - { - fd_set readset, writeset; - - /* Precess buffered packets sent by the server. */ - client_process_buffered_input_packets(); - - /* Make packets of buffered stdin data, and buffer them for sending - to the server. */ - client_make_packets_from_stdin_data(); - - /* Make packets from buffered channel data, and buffer them for sending - to the server. */ - if (packet_not_very_much_data_to_write()) - channel_output_poll(); - - /* Check if the window size has changed, and buffer a message about - it to the server if so. */ - client_check_window_change(); - - if (quit_pending) - break; - - /* Wait until we have something to do (something becomes available - on one of the descriptors). */ - client_wait_until_can_do_something(&readset, &writeset); - - if (quit_pending) - break; - - /* Do channel operations. */ - channel_after_select(&readset, &writeset); - - /* Process input from the connection and from stdin. Buffer any data - that is available. */ - client_process_input(&readset); - - /* Process output to stdout and stderr. Output to the connection - is processed elsewhere (above). */ - client_process_output(&writeset); - - /* Send as much buffered packet data as possible to the sender. */ - if (FD_ISSET(connection_out, &writeset)) - packet_write_poll(); - } - - /* Terminate the session. */ - - /* Stop watching for window change. */ - if (have_pty) - signal(SIGWINCH, SIG_DFL); - - /* Stop listening for connections. */ - channel_stop_listening(); - - /* In interactive mode (with pseudo tty) display a message indicating that - the connection has been closed. */ - if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) - { - snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); - buffer_append(&stderr_buffer, buf, strlen(buf)); - stderr_bytes += strlen(buf); - } - - /* Output any buffered data for stdout. */ - while (buffer_len(&stdout_buffer) > 0) - { - len = write(fileno(stdout), buffer_ptr(&stdout_buffer), - buffer_len(&stdout_buffer)); - if (len <= 0) - { - error("Write failed flushing stdout buffer."); - break; + extern Options options; + double start_time, total_time; + int len; + char buf[100]; + + debug("Entering interactive session."); + + start_time = get_current_time(); + + /* Initialize variables. */ + escape_pending = 0; + last_was_cr = 1; + exit_status = -1; + stdin_eof = 0; + buffer_high = 64 * 1024; + connection_in = packet_get_connection_in(); + connection_out = packet_get_connection_out(); + max_fd = connection_in; + if (connection_out > max_fd) + max_fd = connection_out; + stdin_bytes = 0; + stdout_bytes = 0; + stderr_bytes = 0; + quit_pending = 0; + escape_char = escape_char_arg; + + /* Initialize buffers. */ + buffer_init(&stdin_buffer); + buffer_init(&stdout_buffer); + buffer_init(&stderr_buffer); + + /* Set signal handlers to restore non-blocking mode. */ + signal(SIGINT, signal_handler); + signal(SIGQUIT, signal_handler); + signal(SIGTERM, signal_handler); + signal(SIGPIPE, SIG_IGN); + if (have_pty) + signal(SIGWINCH, window_change_handler); + + /* Enter raw mode if have a pseudo terminal. */ + if (have_pty) + enter_raw_mode(); + + /* Check if we should immediately send of on stdin. */ + client_check_initial_eof_on_stdin(); + + /* Main loop of the client for the interactive session mode. */ + while (!quit_pending) { + fd_set readset, writeset; + + /* Precess buffered packets sent by the server. */ + client_process_buffered_input_packets(); + + /* Make packets of buffered stdin data, and buffer them + for sending to the server. */ + client_make_packets_from_stdin_data(); + + /* Make packets from buffered channel data, and buffer + them for sending to the server. */ + if (packet_not_very_much_data_to_write()) + channel_output_poll(); + + /* Check if the window size has changed, and buffer a + message about it to the server if so. */ + client_check_window_change(); + + if (quit_pending) + break; + + /* Wait until we have something to do (something becomes + available on one of the descriptors). */ + client_wait_until_can_do_something(&readset, &writeset); + + if (quit_pending) + break; + + /* Do channel operations. */ + channel_after_select(&readset, &writeset); + + /* Process input from the connection and from stdin. + Buffer any data that is available. */ + client_process_input(&readset); + + /* Process output to stdout and stderr. Output to the + connection is processed elsewhere (above). */ + client_process_output(&writeset); + + /* Send as much buffered packet data as possible to the + sender. */ + if (FD_ISSET(connection_out, &writeset)) + packet_write_poll(); + } + + /* Terminate the session. */ + + /* Stop watching for window change. */ + if (have_pty) + signal(SIGWINCH, SIG_DFL); + + /* Stop listening for connections. */ + channel_stop_listening(); + + /* In interactive mode (with pseudo tty) display a message + indicating that the connection has been closed. */ + if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { + snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); + buffer_append(&stderr_buffer, buf, strlen(buf)); + stderr_bytes += strlen(buf); } - buffer_consume(&stdout_buffer, len); - } - - /* Output any buffered data for stderr. */ - while (buffer_len(&stderr_buffer) > 0) - { - len = write(fileno(stderr), buffer_ptr(&stderr_buffer), - buffer_len(&stderr_buffer)); - if (len <= 0) - { - error("Write failed flushing stderr buffer."); - break; + /* Output any buffered data for stdout. */ + while (buffer_len(&stdout_buffer) > 0) { + len = write(fileno(stdout), buffer_ptr(&stdout_buffer), + buffer_len(&stdout_buffer)); + if (len <= 0) { + error("Write failed flushing stdout buffer."); + break; + } + buffer_consume(&stdout_buffer, len); } - buffer_consume(&stderr_buffer, len); - } - - /* Leave raw mode. */ - if (have_pty) - leave_raw_mode(); - - /* Clear and free any buffers. */ - memset(buf, 0, sizeof(buf)); - buffer_free(&stdin_buffer); - buffer_free(&stdout_buffer); - buffer_free(&stderr_buffer); - - /* Report bytes transferred, and transfer rates. */ - total_time = get_current_time() - start_time; - debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds", - stdin_bytes, stdout_bytes, stderr_bytes, total_time); - if (total_time > 0) - debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", - stdin_bytes / total_time, stdout_bytes / total_time, - stderr_bytes / total_time); - - /* Return the exit status of the program. */ - debug("Exit status %d", exit_status); - return exit_status; + + /* Output any buffered data for stderr. */ + while (buffer_len(&stderr_buffer) > 0) { + len = write(fileno(stderr), buffer_ptr(&stderr_buffer), + buffer_len(&stderr_buffer)); + if (len <= 0) { + error("Write failed flushing stderr buffer."); + break; + } + buffer_consume(&stderr_buffer, len); + } + + /* Leave raw mode. */ + if (have_pty) + leave_raw_mode(); + + /* Clear and free any buffers. */ + memset(buf, 0, sizeof(buf)); + buffer_free(&stdin_buffer); + buffer_free(&stdout_buffer); + buffer_free(&stderr_buffer); + + /* Report bytes transferred, and transfer rates. */ + total_time = get_current_time() - start_time; + debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds", + stdin_bytes, stdout_bytes, stderr_bytes, total_time); + if (total_time > 0) + debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", + stdin_bytes / total_time, stdout_bytes / total_time, + stderr_bytes / total_time); + + /* Return the exit status of the program. */ + debug("Exit status %d", exit_status); + return exit_status; } diff --git a/compat.c b/compat.c index bf4897e1..669acf51 100644 --- a/compat.c +++ b/compat.c @@ -3,8 +3,11 @@ RCSID("$Id$"); #include "ssh.h" -int compat13=0; -void enable_compat13(void){ - log("Enabling compatibility mode for protocol 1.3"); - compat13=1; +int compat13 = 0; + +void +enable_compat13(void) +{ + verbose("Enabling compatibility mode for protocol 1.3"); + compat13 = 1; } diff --git a/compat.h b/compat.h index 6d6283af..76cd898b 100644 --- a/compat.h +++ b/compat.h @@ -2,6 +2,6 @@ #ifndef COMPAT_H #define COMPAT_H -void enable_compat13(void); +void enable_compat13(void); extern int compat13; #endif diff --git a/compress.c b/compress.c index 7ab23e19..def9e75a 100644 --- a/compress.c +++ b/compress.c @@ -1,17 +1,17 @@ /* - -compress.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Wed Oct 25 22:12:46 1995 ylo - -Interface to packet compression for ssh. - -*/ + * + * compress.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Wed Oct 25 22:12:46 1995 ylo + * + * Interface to packet compression for ssh. + * + */ #include "includes.h" RCSID("$Id$"); @@ -23,32 +23,34 @@ RCSID("$Id$"); static z_stream incoming_stream; static z_stream outgoing_stream; -/* Initializes compression; level is compression level from 1 to 9 (as in - gzip). */ +/* Initializes compression; level is compression level from 1 to 9 + (as in gzip). */ -void buffer_compress_init(int level) +void +buffer_compress_init(int level) { - debug("Enabling compression at level %d.", level); - if (level < 1 || level > 9) - fatal("Bad compression level %d.", level); - inflateInit(&incoming_stream); - deflateInit(&outgoing_stream, level); + debug("Enabling compression at level %d.", level); + if (level < 1 || level > 9) + fatal("Bad compression level %d.", level); + inflateInit(&incoming_stream); + deflateInit(&outgoing_stream, level); } /* Frees any data structures allocated for compression. */ -void buffer_compress_uninit() +void +buffer_compress_uninit() { - debug("compress outgoing: raw data %lu, compressed %lu, factor %.2f", - outgoing_stream.total_in, outgoing_stream.total_out, - outgoing_stream.total_in == 0 ? 0.0 : - (double)outgoing_stream.total_out / outgoing_stream.total_in); - debug("compress incoming: raw data %lu, compressed %lu, factor %.2f", - incoming_stream.total_out, incoming_stream.total_in, - incoming_stream.total_out == 0 ? 0.0 : - (double)incoming_stream.total_in / incoming_stream.total_out); - inflateEnd(&incoming_stream); - deflateEnd(&outgoing_stream); + debug("compress outgoing: raw data %lu, compressed %lu, factor %.2f", + outgoing_stream.total_in, outgoing_stream.total_out, + outgoing_stream.total_in == 0 ? 0.0 : + (double) outgoing_stream.total_out / outgoing_stream.total_in); + debug("compress incoming: raw data %lu, compressed %lu, factor %.2f", + incoming_stream.total_out, incoming_stream.total_in, + incoming_stream.total_out == 0 ? 0.0 : + (double) incoming_stream.total_in / incoming_stream.total_out); + inflateEnd(&incoming_stream); + deflateEnd(&outgoing_stream); } /* Compresses the contents of input_buffer into output_buffer. All @@ -59,50 +61,49 @@ void buffer_compress_uninit() form a single compression stream) by the receiver. This appends the compressed data to the output buffer. */ -void buffer_compress(Buffer *input_buffer, Buffer *output_buffer) +void +buffer_compress(Buffer * input_buffer, Buffer * output_buffer) { - char buf[4096]; - int status; - - /* This case is not handled below. */ - if (buffer_len(input_buffer) == 0) - return; - - /* Input is the contents of the input buffer. */ - outgoing_stream.next_in = buffer_ptr(input_buffer); - outgoing_stream.avail_in = buffer_len(input_buffer); - - /* Loop compressing until deflate() returns with avail_out != 0. */ - do - { - /* Set up fixed-size output buffer. */ - outgoing_stream.next_out = buf; - outgoing_stream.avail_out = sizeof(buf); - - /* Compress as much data into the buffer as possible. */ - status = deflate(&outgoing_stream, Z_PARTIAL_FLUSH); - switch (status) - { - case Z_OK: - /* Append compressed data to output_buffer. */ - buffer_append(output_buffer, buf, - sizeof(buf) - outgoing_stream.avail_out); - break; - case Z_STREAM_END: - fatal("buffer_compress: deflate returned Z_STREAM_END"); - /*NOTREACHED*/ - case Z_STREAM_ERROR: - fatal("buffer_compress: deflate returned Z_STREAM_ERROR"); - /*NOTREACHED*/ - case Z_BUF_ERROR: - fatal("buffer_compress: deflate returned Z_BUF_ERROR"); - /*NOTREACHED*/ - default: - fatal("buffer_compress: deflate returned %d", status); - /*NOTREACHED*/ + char buf[4096]; + int status; + + /* This case is not handled below. */ + if (buffer_len(input_buffer) == 0) + return; + + /* Input is the contents of the input buffer. */ + outgoing_stream.next_in = buffer_ptr(input_buffer); + outgoing_stream.avail_in = buffer_len(input_buffer); + + /* Loop compressing until deflate() returns with avail_out != 0. */ + do { + /* Set up fixed-size output buffer. */ + outgoing_stream.next_out = buf; + outgoing_stream.avail_out = sizeof(buf); + + /* Compress as much data into the buffer as possible. */ + status = deflate(&outgoing_stream, Z_PARTIAL_FLUSH); + switch (status) { + case Z_OK: + /* Append compressed data to output_buffer. */ + buffer_append(output_buffer, buf, + sizeof(buf) - outgoing_stream.avail_out); + break; + case Z_STREAM_END: + fatal("buffer_compress: deflate returned Z_STREAM_END"); + /* NOTREACHED */ + case Z_STREAM_ERROR: + fatal("buffer_compress: deflate returned Z_STREAM_ERROR"); + /* NOTREACHED */ + case Z_BUF_ERROR: + fatal("buffer_compress: deflate returned Z_BUF_ERROR"); + /* NOTREACHED */ + default: + fatal("buffer_compress: deflate returned %d", status); + /* NOTREACHED */ + } } - } - while (outgoing_stream.avail_out == 0); + while (outgoing_stream.avail_out == 0); } /* Uncompresses the contents of input_buffer into output_buffer. All @@ -113,48 +114,46 @@ void buffer_compress(Buffer *input_buffer, Buffer *output_buffer) same order that buffers compressed with that. This appends the uncompressed data to the output buffer. */ -void buffer_uncompress(Buffer *input_buffer, Buffer *output_buffer) +void +buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer) { - char buf[4096]; - int status; - - incoming_stream.next_in = buffer_ptr(input_buffer); - incoming_stream.avail_in = buffer_len(input_buffer); - - incoming_stream.next_out = buf; - incoming_stream.avail_out = sizeof(buf); - - for (;;) - { - status = inflate(&incoming_stream, Z_PARTIAL_FLUSH); - switch (status) - { - case Z_OK: - buffer_append(output_buffer, buf, - sizeof(buf) - incoming_stream.avail_out); - incoming_stream.next_out = buf; - incoming_stream.avail_out = sizeof(buf); - break; - case Z_STREAM_END: - fatal("buffer_uncompress: inflate returned Z_STREAM_END"); - /*NOTREACHED*/ - case Z_DATA_ERROR: - fatal("buffer_uncompress: inflate returned Z_DATA_ERROR"); - /*NOTREACHED*/ - case Z_STREAM_ERROR: - fatal("buffer_uncompress: inflate returned Z_STREAM_ERROR"); - /*NOTREACHED*/ - case Z_BUF_ERROR: - /* Comments in zlib.h say that we should keep calling inflate() - until we get an error. This appears to be the error that we - get. */ - return; - case Z_MEM_ERROR: - fatal("buffer_uncompress: inflate returned Z_MEM_ERROR"); - /*NOTREACHED*/ - default: - fatal("buffer_uncompress: inflate returned %d", status); + char buf[4096]; + int status; + + incoming_stream.next_in = buffer_ptr(input_buffer); + incoming_stream.avail_in = buffer_len(input_buffer); + + incoming_stream.next_out = buf; + incoming_stream.avail_out = sizeof(buf); + + for (;;) { + status = inflate(&incoming_stream, Z_PARTIAL_FLUSH); + switch (status) { + case Z_OK: + buffer_append(output_buffer, buf, + sizeof(buf) - incoming_stream.avail_out); + incoming_stream.next_out = buf; + incoming_stream.avail_out = sizeof(buf); + break; + case Z_STREAM_END: + fatal("buffer_uncompress: inflate returned Z_STREAM_END"); + /* NOTREACHED */ + case Z_DATA_ERROR: + fatal("buffer_uncompress: inflate returned Z_DATA_ERROR"); + /* NOTREACHED */ + case Z_STREAM_ERROR: + fatal("buffer_uncompress: inflate returned Z_STREAM_ERROR"); + /* NOTREACHED */ + case Z_BUF_ERROR: + /* Comments in zlib.h say that we should keep + calling inflate() until we get an error. This + appears to be the error that we get. */ + return; + case Z_MEM_ERROR: + fatal("buffer_uncompress: inflate returned Z_MEM_ERROR"); + /* NOTREACHED */ + default: + fatal("buffer_uncompress: inflate returned %d", status); + } } - } } - diff --git a/compress.h b/compress.h index 11cfc54d..2618b988 100644 --- a/compress.h +++ b/compress.h @@ -1,17 +1,17 @@ /* - -compress.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Wed Oct 25 22:12:46 1995 ylo - -Interface to packet compression for ssh. - -*/ + * + * compress.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Wed Oct 25 22:12:46 1995 ylo + * + * Interface to packet compression for ssh. + * + */ /* RCSID("$Id$"); */ @@ -20,10 +20,10 @@ Interface to packet compression for ssh. /* Initializes compression; level is compression level from 1 to 9 (as in gzip). */ -void buffer_compress_init(int level); +void buffer_compress_init(int level); /* Frees any data structures allocated by buffer_compress_init. */ -void buffer_compress_uninit(); +void buffer_compress_uninit(); /* Compresses the contents of input_buffer into output_buffer. All packets compressed using this function will form a single @@ -32,7 +32,7 @@ void buffer_compress_uninit(); independently (but in the appropriate order since they together form a single compression stream) by the receiver. This appends the compressed data to the output buffer. */ -void buffer_compress(Buffer *input_buffer, Buffer *output_buffer); +void buffer_compress(Buffer * input_buffer, Buffer * output_buffer); /* Uncompresses the contents of input_buffer into output_buffer. All packets uncompressed using this function will form a single @@ -41,6 +41,6 @@ void buffer_compress(Buffer *input_buffer, Buffer *output_buffer); same size units that the buffer_compress was called, and in the same order that buffers compressed with that. This appends the uncompressed data to the output buffer. */ -void buffer_uncompress(Buffer *input_buffer, Buffer *output_buffer); +void buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer); -#endif /* COMPRESS_H */ +#endif /* COMPRESS_H */ diff --git a/configure.in b/configure.in index 73635272..9f545d73 100644 --- a/configure.in +++ b/configure.in @@ -55,7 +55,7 @@ AC_CHECK_LIB(dl, dlopen, , ) AC_CHECK_LIB(pam, pam_authenticate, , ) dnl Checks for header files. -AC_CHECK_HEADERS(pty.h endian.h paths.h lastlog.h shadow.h netgroup.h maillock.h utmp.h sys/select.h sys/time.h) +AC_CHECK_HEADERS(pty.h endian.h paths.h lastlog.h shadow.h netgroup.h maillock.h utmp.h util.h sys/select.h sys/time.h) dnl Checks for library functions. AC_CHECK_FUNCS(openpty strlcpy strlcat mkdtemp arc4random setproctitle setlogin setenv) diff --git a/crc32.c b/crc32.c index 1d69496a..8a2f2a7d 100644 --- a/crc32.c +++ b/crc32.c @@ -1,6 +1,9 @@ -/* The implementation here was originally done by Gary S. Brown. I have - borrowed the tables directly, and made some minor changes to the - crc32-function (including changing the interface). //ylo */ +/* + * The implementation here was originally done by Gary S. Brown. + * I have borrowed the tables directly, and made some minor changes + * to the crc32-function (including changing the interface). + * //ylo + */ #include "includes.h" RCSID("$Id$"); @@ -48,73 +51,71 @@ RCSID("$Id$"); /* -------------------------------------------------------------------- */ static unsigned int crc32_tab[] = { - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, - 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, - 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, - 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, - 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, - 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, - 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, - 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, - 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, - 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, - 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, - 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, - 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, - 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, - 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, - 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, - 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, - 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, - 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, - 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, - 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, - 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, - 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, - 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, - 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, - 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, - 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, - 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, - 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, - 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, - 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, - 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, - 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, - 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, - 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, - 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, - 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, - 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, - 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, - 0x2d02ef8dL - }; + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; /* Return a 32-bit CRC of the contents of the buffer. */ -unsigned int crc32(const unsigned char *s, unsigned int len) +unsigned int +crc32(const unsigned char *s, unsigned int len) { - unsigned int i; - unsigned int crc32val; - - crc32val = 0; - for (i = 0; i < len; i ++) - { - crc32val = - crc32_tab[(crc32val ^ s[i]) & 0xff] ^ - (crc32val >> 8); - } - return crc32val; + unsigned int i; + unsigned int crc32val; + + crc32val = 0; + for (i = 0; i < len; i ++) { + crc32val = crc32_tab[(crc32val ^ s[i]) & 0xff] ^ (crc32val >> 8); + } + return crc32val; } diff --git a/crc32.h b/crc32.h index 80bcf6ee..e48d903d 100644 --- a/crc32.h +++ b/crc32.h @@ -1,17 +1,17 @@ /* - -crc32.h - -Author: Tatu Ylonen - -Copyright (c) 1992 Tatu Ylonen, Espoo, Finland - All rights reserved - -Created: Tue Feb 11 14:37:27 1992 ylo - -Functions for computing 32-bit CRC. - -*/ + * + * crc32.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1992 Tatu Ylonen, Espoo, Finland + * All rights reserved + * + * Created: Tue Feb 11 14:37:27 1992 ylo + * + * Functions for computing 32-bit CRC. + * + */ /* RCSID("$Id$"); */ @@ -22,4 +22,4 @@ Functions for computing 32-bit CRC. CRC. The polynomial used is 0xedb88320. */ unsigned int crc32(const unsigned char *buf, unsigned int len); -#endif /* CRC32_H */ +#endif /* CRC32_H */ diff --git a/deattack.c b/deattack.c index 9bdbc3ec..27b80f3a 100644 --- a/deattack.c +++ b/deattack.c @@ -15,7 +15,8 @@ * SOFTWARE. * * Ariel Futoransky - * */ + * + */ #include "includes.h" #include "deattack.h" @@ -25,157 +26,130 @@ #include "xmalloc.h" /* SSH Constants */ -#define SSH_MAXBLOCKS (32 * 1024) -#define SSH_BLOCKSIZE (8) +#define SSH_MAXBLOCKS (32 * 1024) +#define SSH_BLOCKSIZE (8) /* Hashing constants */ -#define HASH_MINSIZE (8 * 1024) -#define HASH_ENTRYSIZE (2) -#define HASH_FACTOR(x) ((x)*3/2) -#define HASH_UNUSEDCHAR (0xff) -#define HASH_UNUSED (0xffff) -#define HASH_IV (0xfffe) +#define HASH_MINSIZE (8 * 1024) +#define HASH_ENTRYSIZE (2) +#define HASH_FACTOR(x) ((x)*3/2) +#define HASH_UNUSEDCHAR (0xff) +#define HASH_UNUSED (0xffff) +#define HASH_IV (0xfffe) -#define HASH_MINBLOCKS (7*SSH_BLOCKSIZE) +#define HASH_MINBLOCKS (7*SSH_BLOCKSIZE) /* Hash function (Input keys are cipher results) */ -#define HASH(x) GET_32BIT(x) +#define HASH(x) GET_32BIT(x) -#define CMP(a,b) (memcmp(a, b, SSH_BLOCKSIZE)) +#define CMP(a,b) (memcmp(a, b, SSH_BLOCKSIZE)) void -crc_update(u_int32_t * a, u_int32_t b) +crc_update(u_int32_t *a, u_int32_t b) { - b ^= *a; - *a = crc32((unsigned char *) &b, sizeof(b)); + b ^= *a; + *a = crc32((unsigned char *) &b, sizeof(b)); } -/* - check_crc - detects if a block is used in a particular pattern - */ - +/* detect if a block is used in a particular pattern */ int -check_crc(unsigned char *S, unsigned char *buf, u_int32_t len, unsigned char *IV) +check_crc(unsigned char *S, unsigned char *buf, u_int32_t len, + unsigned char *IV) { - u_int32_t crc; - unsigned char *c; - - crc = 0; - if (IV && !CMP(S, IV)) - { - crc_update(&crc, 1); - crc_update(&crc, 0); - } - for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) - { - if (!CMP(S, c)) - { - crc_update(&crc, 1); - crc_update(&crc, 0); - } else - { - crc_update(&crc, 0); - crc_update(&crc, 0); - } - } - - return (crc == 0); + u_int32_t crc; + unsigned char *c; + + crc = 0; + if (IV && !CMP(S, IV)) { + crc_update(&crc, 1); + crc_update(&crc, 0); + } + for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) { + if (!CMP(S, c)) { + crc_update(&crc, 1); + crc_update(&crc, 0); + } else { + crc_update(&crc, 0); + crc_update(&crc, 0); + } + } + return (crc == 0); } -/* - detect_attack - Detects a crc32 compensation attack on a packet - */ +/* Detect a crc32 compensation attack on a packet */ int detect_attack(unsigned char *buf, u_int32_t len, unsigned char *IV) { - static u_int16_t *h = (u_int16_t *) NULL; - static u_int16_t n = HASH_MINSIZE / HASH_ENTRYSIZE; - register u_int32_t i, j; - u_int32_t l; - register unsigned char *c; - unsigned char *d; - - if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) || - len % SSH_BLOCKSIZE != 0) { - fatal("detect_attack: bad length %d", len); - } - - for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2); - - if (h == NULL) - { - debug("Installing crc compensation attack detector."); - n = l; - h = (u_int16_t *) xmalloc(n * HASH_ENTRYSIZE); - } else - { - if (l > n) - { - n = l; - h = (u_int16_t *) xrealloc(h, n * HASH_ENTRYSIZE); - } - } - - - if (len <= HASH_MINBLOCKS) - { - for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) - { - if (IV && (!CMP(c, IV))) - { - if ((check_crc(c, buf, len, IV))) - return (DEATTACK_DETECTED); - else - break; - } - for (d = buf; d < c; d += SSH_BLOCKSIZE) - { - if (!CMP(c, d)) - { - if ((check_crc(c, buf, len, IV))) - return (DEATTACK_DETECTED); - else - break; + static u_int16_t *h = (u_int16_t *) NULL; + static u_int16_t n = HASH_MINSIZE / HASH_ENTRYSIZE; + register u_int32_t i, j; + u_int32_t l; + register unsigned char *c; + unsigned char *d; + + if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) || + len % SSH_BLOCKSIZE != 0) { + fatal("detect_attack: bad length %d", len); + } + for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2) + ; + + if (h == NULL) { + debug("Installing crc compensation attack detector."); + n = l; + h = (u_int16_t *) xmalloc(n * HASH_ENTRYSIZE); + } else { + if (l > n) { + n = l; + h = (u_int16_t *) xrealloc(h, n * HASH_ENTRYSIZE); + } + } + + if (len <= HASH_MINBLOCKS) { + for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) { + if (IV && (!CMP(c, IV))) { + if ((check_crc(c, buf, len, IV))) + return (DEATTACK_DETECTED); + else + break; + } + for (d = buf; d < c; d += SSH_BLOCKSIZE) { + if (!CMP(c, d)) { + if ((check_crc(c, buf, len, IV))) + return (DEATTACK_DETECTED); + else + break; + } + } + } + return (DEATTACK_OK); } - } - } - return (DEATTACK_OK); - } - memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE); - - if (IV) - h[HASH(IV) & (n - 1)] = HASH_IV; - - - for (c = buf, j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) - { - for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED; - i = (i + 1) & (n - 1)) - { - if (h[i] == HASH_IV) - { - if (!CMP(c, IV)) - { - if (check_crc(c, buf, len, IV)) - return (DEATTACK_DETECTED); - else - break; + memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE); + + if (IV) + h[HASH(IV) & (n - 1)] = HASH_IV; + + for (c = buf, j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) { + for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED; + i = (i + 1) & (n - 1)) { + if (h[i] == HASH_IV) { + if (!CMP(c, IV)) { + if (check_crc(c, buf, len, IV)) + return (DEATTACK_DETECTED); + else + break; + } + } else if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE)) { + if (check_crc(c, buf, len, IV)) + return (DEATTACK_DETECTED); + else + break; + } + } + h[i] = j; } - } else if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE)) - { - if (check_crc(c, buf, len, IV)) - return (DEATTACK_DETECTED); - else - break; - } - } - h[i] = j; - } - - return (DEATTACK_OK); + return (DEATTACK_OK); } diff --git a/deattack.h b/deattack.h index c04b56b5..6ce54ded 100644 --- a/deattack.h +++ b/deattack.h @@ -1,4 +1,4 @@ -/* $Id$ +/* * Cryptographic attack detector for ssh - Header file * * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. @@ -14,7 +14,8 @@ * SOFTWARE. * * Ariel Futoransky - * */ + * + */ #ifndef _DEATTACK_H #define _DEATTACK_H @@ -23,5 +24,5 @@ #define DEATTACK_OK 0 #define DEATTACK_DETECTED 1 -int detect_attack(unsigned char *buf, u_int32_t len, unsigned char IV[8]); +int detect_attack(unsigned char *buf, u_int32_t len, unsigned char IV[8]); #endif diff --git a/fingerprint.c b/fingerprint.c index 1d0c9bee..2ed56acf 100644 --- a/fingerprint.c +++ b/fingerprint.c @@ -13,17 +13,18 @@ RCSID("$Id$"); #define FPRINT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x" -/* Generate key fingerprint in ascii format. - Based on ideas and code from Bjoern Groenvall */ - +/* + * Generate key fingerprint in ascii format. + * Based on ideas and code from Bjoern Groenvall + */ char * fingerprint(BIGNUM *e, BIGNUM *n) { - static char retval[80]; - MD5_CTX md; - unsigned char d[16]; - char *buf; - int nlen, elen; + static char retval[80]; + MD5_CTX md; + unsigned char d[16]; + char *buf; + int nlen, elen; nlen = BN_num_bytes(n); elen = BN_num_bytes(e); @@ -37,8 +38,8 @@ fingerprint(BIGNUM *e, BIGNUM *n) MD5_Update(&md, buf, nlen + elen); MD5_Final(d, &md); snprintf(retval, sizeof(retval), FPRINT, - d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], - d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], + d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); memset(buf, 0, nlen + elen); xfree(buf); return retval; diff --git a/fingerprint.h b/fingerprint.h index 0dd5cb62..8c5474ca 100644 --- a/fingerprint.h +++ b/fingerprint.h @@ -2,5 +2,5 @@ #ifndef FINGERPRINT_H #define FINGERPRINT_H -char * fingerprint(BIGNUM *e, BIGNUM *n); +char *fingerprint(BIGNUM * e, BIGNUM * n); #endif diff --git a/getput.h b/getput.h index 38b7d88f..ac564c00 100644 --- a/getput.h +++ b/getput.h @@ -1,17 +1,17 @@ /* - -getput.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Wed Jun 28 22:36:30 1995 ylo - -Macros for storing and retrieving data in msb first and lsb first order. - -*/ + * + * getput.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Wed Jun 28 22:36:30 1995 ylo + * + * Macros for storing and retrieving data in msb first and lsb first order. + * + */ /* RCSID("$Id$"); */ @@ -60,5 +60,4 @@ Macros for storing and retrieving data in msb first and lsb first order. (cp)[0] = (value); \ (cp)[1] = (value) >> 8; } while (0) -#endif /* GETPUT_H */ - +#endif /* GETPUT_H */ diff --git a/hostfile.c b/hostfile.c index d81bce96..6f2cc9d4 100644 --- a/hostfile.c +++ b/hostfile.c @@ -1,17 +1,17 @@ /* - -hostfile.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Thu Jun 29 07:10:56 1995 ylo - -Functions for manipulating the known hosts files. - -*/ + * + * hostfile.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Thu Jun 29 07:10:56 1995 ylo + * + * Functions for manipulating the known hosts files. + * + */ #include "includes.h" RCSID("$Id$"); @@ -26,140 +26,136 @@ RCSID("$Id$"); modify the buffer containing the number. */ int -auth_rsa_read_bignum(char **cpp, BIGNUM *value) +auth_rsa_read_bignum(char **cpp, BIGNUM * value) { - char *cp = *cpp; - int len, old; + char *cp = *cpp; + int len, old; + + /* Skip any leading whitespace. */ + for (; *cp == ' ' || *cp == '\t'; cp++); - /* Skip any leading whitespace. */ - for (; *cp == ' ' || *cp == '\t'; cp++) - ; + /* Check that it begins with a hex digit. */ + if (*cp < '0' || *cp > '9') + return 0; - /* Check that it begins with a hex digit. */ - if (*cp < '0' || *cp > '9') - return 0; + /* Save starting position. */ + *cpp = cp; - /* Save starting position. */ - *cpp = cp; + /* Move forward until all hex digits skipped. */ + for (; *cp >= '0' && *cp <= '9'; cp++); - /* Move forward until all hex digits skipped. */ - for (; *cp >= '0' && *cp <= '9'; cp++) - ; + /* Compute the length of the hex number. */ + len = cp - *cpp; - /* Compute the length of the hex number. */ - len = cp - *cpp; + /* Save the old terminating character, and replace it by \0. */ + old = *cp; + *cp = 0; - /* Save the old terminating character, and replace it by \0. */ - old = *cp; - *cp = 0; - - /* Parse the number. */ - if (BN_dec2bn(&value, *cpp) == 0) - return 0; + /* Parse the number. */ + if (BN_dec2bn(&value, *cpp) == 0) + return 0; - /* Restore old terminating character. */ - *cp = old; + /* Restore old terminating character. */ + *cp = old; - /* Move beyond the number and return success. */ - *cpp = cp; - return 1; + /* Move beyond the number and return success. */ + *cpp = cp; + return 1; } /* Parses an RSA key (number of bits, e, n) from a string. Moves the pointer over the key. Skips any whitespace at the beginning and at end. */ int -auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM *e, BIGNUM *n) +auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n) { - unsigned int bits; - char *cp; - - /* Skip leading whitespace. */ - for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) - ; - - /* Get number of bits. */ - if (*cp < '0' || *cp > '9') - return 0; /* Bad bit count... */ - for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) - bits = 10 * bits + *cp - '0'; - - /* Get public exponent. */ - if (!auth_rsa_read_bignum(&cp, e)) - return 0; - - /* Get public modulus. */ - if (!auth_rsa_read_bignum(&cp, n)) - return 0; - - /* Skip trailing whitespace. */ - for (; *cp == ' ' || *cp == '\t'; cp++) - ; - - /* Return results. */ - *cpp = cp; - *bitsp = bits; - return 1; + unsigned int bits; + char *cp; + + /* Skip leading whitespace. */ + for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++); + + /* Get number of bits. */ + if (*cp < '0' || *cp > '9') + return 0; /* Bad bit count... */ + for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) + bits = 10 * bits + *cp - '0'; + + /* Get public exponent. */ + if (!auth_rsa_read_bignum(&cp, e)) + return 0; + + /* Get public modulus. */ + if (!auth_rsa_read_bignum(&cp, n)) + return 0; + + /* Skip trailing whitespace. */ + for (; *cp == ' ' || *cp == '\t'; cp++); + + /* Return results. */ + *cpp = cp; + *bitsp = bits; + return 1; } /* Tries to match the host name (which must be in all lowercase) against the - comma-separated sequence of subpatterns (each possibly preceded by ! to + comma-separated sequence of subpatterns (each possibly preceded by ! to indicate negation). Returns true if there is a positive match; zero otherwise. */ int match_hostname(const char *host, const char *pattern, unsigned int len) { - char sub[1024]; - int negated; - int got_positive; - unsigned int i, subi; - - got_positive = 0; - for (i = 0; i < len;) - { - /* Check if the subpattern is negated. */ - if (pattern[i] == '!') - { - negated = 1; - i++; + char sub[1024]; + int negated; + int got_positive; + unsigned int i, subi; + + got_positive = 0; + for (i = 0; i < len;) { + /* Check if the subpattern is negated. */ + if (pattern[i] == '!') { + negated = 1; + i++; + } else + negated = 0; + + /* Extract the subpattern up to a comma or end. Convert + the subpattern to lowercase. */ + for (subi = 0; + i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; + subi++, i++) + sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i]; + /* If subpattern too long, return failure (no match). */ + if (subi >= sizeof(sub) - 1) + return 0; + + /* If the subpattern was terminated by a comma, skip the + comma. */ + if (i < len && pattern[i] == ',') + i++; + + /* Null-terminate the subpattern. */ + sub[subi] = '\0'; + + /* Try to match the subpattern against the host name. */ + if (match_pattern(host, sub)) { + if (negated) + return 0; /* Fail if host matches + any negated subpattern. */ + else + got_positive = 1; + } } - else - negated = 0; - - /* Extract the subpattern up to a comma or end. Convert the subpattern - to lowercase. */ - for (subi = 0; - i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; - subi++, i++) - sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i]; - /* If subpattern too long, return failure (no match). */ - if (subi >= sizeof(sub) - 1) - return 0; - - /* If the subpattern was terminated by a comma, skip the comma. */ - if (i < len && pattern[i] == ',') - i++; - - /* Null-terminate the subpattern. */ - sub[subi] = '\0'; - - /* Try to match the subpattern against the host name. */ - if (match_pattern(host, sub)) { - if (negated) - return 0; /* Fail if host matches any negated subpattern. */ - else - got_positive = 1; - } - } - - /* Return success if got a positive match. If there was a negative match, - we have already returned zero and never get here. */ - return got_positive; + + /* Return success if got a positive match. If there was a + negative match, we have already returned zero and never get + here. */ + return got_positive; } -/* Checks whether the given host (which must be in all lowercase) is +/* Checks whether the given host (which must be in all lowercase) is already in the list of our known hosts. Returns HOST_OK if the host is known and has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED if the host is known @@ -167,87 +163,82 @@ match_hostname(const char *host, const char *pattern, unsigned int len) HostStatus check_host_in_hostfile(const char *filename, const char *host, - BIGNUM *e, BIGNUM *n, BIGNUM *ke, BIGNUM *kn) + BIGNUM * e, BIGNUM * n, BIGNUM * ke, BIGNUM * kn) { - FILE *f; - char line[8192]; - int linenum = 0; - unsigned int bits, kbits, hostlen; - char *cp, *cp2; - HostStatus end_return; - - /* Open the file containing the list of known hosts. */ - f = fopen(filename, "r"); - if (!f) - return HOST_NEW; - - /* Cache the length of the host name. */ - hostlen = strlen(host); - - /* Return value when the loop terminates. This is set to HOST_CHANGED if - we have seen a different key for the host and have not found the proper - one. */ - end_return = HOST_NEW; - - /* size of modulus 'n' */ - bits = BN_num_bits(n); - - /* Go trough the file. */ - while (fgets(line, sizeof(line), f)) - { - cp = line; - linenum++; - - /* Skip any leading whitespace. */ - for (; *cp == ' ' || *cp == '\t'; cp++) - ; - - /* Ignore comment lines and empty lines. */ - if (!*cp || *cp == '#' || *cp == '\n') - continue; - - /* Find the end of the host name portion. */ - for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) - ; - - /* Check if the host name matches. */ - if (!match_hostname(host, cp, (unsigned int)(cp2 - cp))) - continue; - - /* Got a match. Skip host name. */ - cp = cp2; - - /* Extract the key from the line. This will skip any leading - whitespace. Ignore badly formatted lines. */ - if (!auth_rsa_read_key(&cp, &kbits, ke, kn)) - continue; - - if (kbits != BN_num_bits(kn)) { - error("Warning: error in %s, line %d: keysize mismatch for host %s: " - "actual size %d vs. announced %d.", - filename, linenum, host, BN_num_bits(kn), kbits); - error("Warning: replace %d with %d in %s, line %d.", - kbits, BN_num_bits(kn), filename, linenum); - } - - /* Check if the current key is the same as the given key. */ - if (BN_cmp(ke, e) == 0 && BN_cmp(kn, n) == 0) - { - /* Ok, they match. */ - fclose(f); - return HOST_OK; + FILE *f; + char line[8192]; + int linenum = 0; + unsigned int bits, kbits, hostlen; + char *cp, *cp2; + HostStatus end_return; + + /* Open the file containing the list of known hosts. */ + f = fopen(filename, "r"); + if (!f) + return HOST_NEW; + + /* Cache the length of the host name. */ + hostlen = strlen(host); + + /* Return value when the loop terminates. This is set to + HOST_CHANGED if we have seen a different key for the host and + have not found the proper one. */ + end_return = HOST_NEW; + + /* size of modulus 'n' */ + bits = BN_num_bits(n); + + /* Go trough the file. */ + while (fgets(line, sizeof(line), f)) { + cp = line; + linenum++; + + /* Skip any leading whitespace. */ + for (; *cp == ' ' || *cp == '\t'; cp++); + + /* Ignore comment lines and empty lines. */ + if (!*cp || *cp == '#' || *cp == '\n') + continue; + + /* Find the end of the host name portion. */ + for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++); + + /* Check if the host name matches. */ + if (!match_hostname(host, cp, (unsigned int) (cp2 - cp))) + continue; + + /* Got a match. Skip host name. */ + cp = cp2; + + /* Extract the key from the line. This will skip any + leading whitespace. Ignore badly formatted lines. */ + if (!auth_rsa_read_key(&cp, &kbits, ke, kn)) + continue; + + if (kbits != BN_num_bits(kn)) { + error("Warning: error in %s, line %d: keysize mismatch for host %s: " + "actual size %d vs. announced %d.", + filename, linenum, host, BN_num_bits(kn), kbits); + error("Warning: replace %d with %d in %s, line %d.", + kbits, BN_num_bits(kn), filename, linenum); + } + /* Check if the current key is the same as the given key. */ + if (BN_cmp(ke, e) == 0 && BN_cmp(kn, n) == 0) { + /* Ok, they match. */ + fclose(f); + return HOST_OK; + } + /* They do not match. We will continue to go through the + file; however, we note that we will not return that it + is new. */ + end_return = HOST_CHANGED; } - - /* They do not match. We will continue to go through the file; however, - we note that we will not return that it is new. */ - end_return = HOST_CHANGED; - } - /* Clear variables and close the file. */ - fclose(f); - - /* Return either HOST_NEW or HOST_CHANGED, depending on whether we saw a - different key for the host. */ - return end_return; + /* Clear variables and close the file. */ + fclose(f); + + /* Return either HOST_NEW or HOST_CHANGED, depending on whether we + saw a different key for the host. */ + return end_return; } /* Appends an entry to the host file. Returns false if the entry @@ -255,40 +246,40 @@ check_host_in_hostfile(const char *filename, const char *host, int add_host_to_hostfile(const char *filename, const char *host, - BIGNUM *e, BIGNUM *n) + BIGNUM * e, BIGNUM * n) { - FILE *f; - char *buf; - unsigned int bits; - - /* Open the file for appending. */ - f = fopen(filename, "a"); - if (!f) - return 0; - - /* size of modulus 'n' */ - bits = BN_num_bits(n); - - /* Print the host name and key to the file. */ - fprintf(f, "%s %u ", host, bits); - buf = BN_bn2dec(e); - if (buf == NULL) { - error("add_host_to_hostfile: BN_bn2dec(e) failed"); - fclose(f); - return 0; - } - fprintf(f, "%s ", buf); - free (buf); - buf = BN_bn2dec(n); - if (buf == NULL) { - error("add_host_to_hostfile: BN_bn2dec(n) failed"); - fclose(f); - return 0; - } - fprintf(f, "%s\n", buf); - free (buf); - - /* Close the file. */ - fclose(f); - return 1; + FILE *f; + char *buf; + unsigned int bits; + + /* Open the file for appending. */ + f = fopen(filename, "a"); + if (!f) + return 0; + + /* size of modulus 'n' */ + bits = BN_num_bits(n); + + /* Print the host name and key to the file. */ + fprintf(f, "%s %u ", host, bits); + buf = BN_bn2dec(e); + if (buf == NULL) { + error("add_host_to_hostfile: BN_bn2dec(e) failed"); + fclose(f); + return 0; + } + fprintf(f, "%s ", buf); + free(buf); + buf = BN_bn2dec(n); + if (buf == NULL) { + error("add_host_to_hostfile: BN_bn2dec(n) failed"); + fclose(f); + return 0; + } + fprintf(f, "%s\n", buf); + free(buf); + + /* Close the file. */ + fclose(f); + return 1; } diff --git a/includes.h b/includes.h index 722c6b3b..4a253969 100644 --- a/includes.h +++ b/includes.h @@ -1,17 +1,17 @@ /* - -includes.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Thu Mar 23 16:29:37 1995 ylo - -This file includes most of the needed system headers. - -*/ + * + * includes.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Thu Mar 23 16:29:37 1995 ylo + * + * This file includes most of the needed system headers. + * + */ #ifndef INCLUDES_H #define INCLUDES_H @@ -91,4 +91,4 @@ static /**/const char *const rcsid[] = { (char *)rcsid, "\100(#)" msg } client program. Socketpairs do not seem to work on all systems. */ #define USE_PIPES 1 -#endif /* INCLUDES_H */ +#endif /* INCLUDES_H */ diff --git a/log-client.c b/log-client.c index 18700cdb..09d96202 100644 --- a/log-client.c +++ b/log-client.c @@ -1,18 +1,18 @@ /* - -log-client.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Mon Mar 20 21:13:40 1995 ylo - -Client-side versions of debug(), log(), etc. These print to stderr. -This is a stripped down version of log-server.c. - -*/ + * + * log-client.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Mon Mar 20 21:13:40 1995 ylo + * + * Client-side versions of debug(), log(), etc. These print to stderr. + * This is a stripped down version of log-server.c. + * + */ #include "includes.h" RCSID("$Id$"); @@ -23,27 +23,26 @@ RCSID("$Id$"); static LogLevel log_level = SYSLOG_LEVEL_INFO; /* Initialize the log. - av0 program name (should be argv[0]) - level logging level - */ + * av0 program name (should be argv[0]) + * level logging level + */ void log_init(char *av0, LogLevel level, SyslogFacility ignored1, int ignored2) { - switch (level) - { - case SYSLOG_LEVEL_QUIET: - case SYSLOG_LEVEL_ERROR: - case SYSLOG_LEVEL_FATAL: - case SYSLOG_LEVEL_INFO: - case SYSLOG_LEVEL_CHAT: - case SYSLOG_LEVEL_DEBUG: - log_level = level; - break; - default: - /* unchanged */ - break; - } + switch (level) { + case SYSLOG_LEVEL_QUIET: + case SYSLOG_LEVEL_ERROR: + case SYSLOG_LEVEL_FATAL: + case SYSLOG_LEVEL_INFO: + case SYSLOG_LEVEL_VERBOSE: + case SYSLOG_LEVEL_DEBUG: + log_level = level; + break; + default: + /* unchanged */ + break; + } } #define MSGBUFSIZE 1024 @@ -51,13 +50,13 @@ log_init(char *av0, LogLevel level, SyslogFacility ignored1, int ignored2) void do_log(LogLevel level, const char *fmt, va_list args) { - char msgbuf[MSGBUFSIZE]; - - if (level > log_level) - return; - if (level == SYSLOG_LEVEL_DEBUG) - fprintf(stderr, "debug: "); - vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); - fprintf(stderr, "%s", msgbuf); - fprintf(stderr, "\r\n"); + char msgbuf[MSGBUFSIZE]; + + if (level > log_level) + return; + if (level == SYSLOG_LEVEL_DEBUG) + fprintf(stderr, "debug: "); + vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); + fprintf(stderr, "%s", msgbuf); + fprintf(stderr, "\r\n"); } diff --git a/log-server.c b/log-server.c index 8ed65b15..e60d8c7a 100644 --- a/log-server.c +++ b/log-server.c @@ -1,18 +1,18 @@ /* - -log-server.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Mon Mar 20 21:19:30 1995 ylo - -Server-side versions of debug(), log(), etc. These normally send the output -to the system log. - -*/ + * + * log-server.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Mon Mar 20 21:19:30 1995 ylo + * + * Server-side versions of debug(), log(), etc. These normally send the output + * to the system log. + * + */ #include "includes.h" RCSID("$Id$"); @@ -33,72 +33,68 @@ static int log_on_stderr = 0; static int log_facility = LOG_AUTH; /* Initialize the log. - av0 program name (should be argv[0]) - on_stderr print also on stderr - level logging level - */ + * av0 program name (should be argv[0]) + * on_stderr print also on stderr + * level logging level + */ -void log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr) +void +log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr) { - - switch (level) - { - case SYSLOG_LEVEL_QUIET: - case SYSLOG_LEVEL_ERROR: - case SYSLOG_LEVEL_FATAL: - case SYSLOG_LEVEL_INFO: - case SYSLOG_LEVEL_CHAT: - case SYSLOG_LEVEL_DEBUG: - log_level = level; - break; - default: - fprintf(stderr, "Unrecognized internal syslog level code %d\n", - (int)level); - exit(1); - } - - switch (facility) - { - case SYSLOG_FACILITY_DAEMON: - log_facility = LOG_DAEMON; - break; - case SYSLOG_FACILITY_USER: - log_facility = LOG_USER; - break; - case SYSLOG_FACILITY_AUTH: - log_facility = LOG_AUTH; - break; - case SYSLOG_FACILITY_LOCAL0: - log_facility = LOG_LOCAL0; - break; - case SYSLOG_FACILITY_LOCAL1: - log_facility = LOG_LOCAL1; - break; - case SYSLOG_FACILITY_LOCAL2: - log_facility = LOG_LOCAL2; - break; - case SYSLOG_FACILITY_LOCAL3: - log_facility = LOG_LOCAL3; - break; - case SYSLOG_FACILITY_LOCAL4: - log_facility = LOG_LOCAL4; - break; - case SYSLOG_FACILITY_LOCAL5: - log_facility = LOG_LOCAL5; - break; - case SYSLOG_FACILITY_LOCAL6: - log_facility = LOG_LOCAL6; - break; - case SYSLOG_FACILITY_LOCAL7: - log_facility = LOG_LOCAL7; - break; - default: - fprintf(stderr, "Unrecognized internal syslog facility code %d\n", - (int)facility); - exit(1); - } - - log_on_stderr = on_stderr; + switch (level) { + case SYSLOG_LEVEL_QUIET: + case SYSLOG_LEVEL_ERROR: + case SYSLOG_LEVEL_FATAL: + case SYSLOG_LEVEL_INFO: + case SYSLOG_LEVEL_VERBOSE: + case SYSLOG_LEVEL_DEBUG: + log_level = level; + break; + default: + fprintf(stderr, "Unrecognized internal syslog level code %d\n", + (int) level); + exit(1); + } + switch (facility) { + case SYSLOG_FACILITY_DAEMON: + log_facility = LOG_DAEMON; + break; + case SYSLOG_FACILITY_USER: + log_facility = LOG_USER; + break; + case SYSLOG_FACILITY_AUTH: + log_facility = LOG_AUTH; + break; + case SYSLOG_FACILITY_LOCAL0: + log_facility = LOG_LOCAL0; + break; + case SYSLOG_FACILITY_LOCAL1: + log_facility = LOG_LOCAL1; + break; + case SYSLOG_FACILITY_LOCAL2: + log_facility = LOG_LOCAL2; + break; + case SYSLOG_FACILITY_LOCAL3: + log_facility = LOG_LOCAL3; + break; + case SYSLOG_FACILITY_LOCAL4: + log_facility = LOG_LOCAL4; + break; + case SYSLOG_FACILITY_LOCAL5: + log_facility = LOG_LOCAL5; + break; + case SYSLOG_FACILITY_LOCAL6: + log_facility = LOG_LOCAL6; + break; + case SYSLOG_FACILITY_LOCAL7: + log_facility = LOG_LOCAL7; + break; + default: + fprintf(stderr, "Unrecognized internal syslog facility code %d\n", + (int) facility); + exit(1); + } + log_on_stderr = on_stderr; } #define MSGBUFSIZE 1024 @@ -106,48 +102,44 @@ void log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr) void do_log(LogLevel level, const char *fmt, va_list args) { - char msgbuf[MSGBUFSIZE]; - char fmtbuf[MSGBUFSIZE]; - char *txt = NULL; - int pri = LOG_INFO; - - if (level > log_level) - return; - switch (level) - { - case SYSLOG_LEVEL_ERROR: - txt = "error"; - pri = LOG_ERR; - break; - case SYSLOG_LEVEL_FATAL: - txt = "fatal"; - pri = LOG_ERR; - break; - case SYSLOG_LEVEL_INFO: - pri = LOG_INFO; - break; - case SYSLOG_LEVEL_CHAT: - pri = LOG_INFO; - break; - case SYSLOG_LEVEL_DEBUG: - txt = "debug"; - pri = LOG_DEBUG; - break; - default: - txt = "internal error"; - pri = LOG_ERR; - break; - } - - if (txt != NULL) { - snprintf(fmtbuf, sizeof(fmtbuf), "%s: %s", txt, fmt); - vsnprintf(msgbuf, sizeof(msgbuf), fmtbuf, args); - }else{ - vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); - } - if (log_on_stderr) - fprintf(stderr, "%s\n", msgbuf); - openlog(__progname, LOG_PID, log_facility); - syslog(pri, "%.500s", msgbuf); - closelog(); + char msgbuf[MSGBUFSIZE]; + char fmtbuf[MSGBUFSIZE]; + char *txt = NULL; + int pri = LOG_INFO; + + if (level > log_level) + return; + switch (level) { + case SYSLOG_LEVEL_ERROR: + txt = "error"; + pri = LOG_ERR; + break; + case SYSLOG_LEVEL_FATAL: + txt = "fatal"; + pri = LOG_ERR; + break; + case SYSLOG_LEVEL_INFO: + case SYSLOG_LEVEL_VERBOSE: + pri = LOG_INFO; + break; + case SYSLOG_LEVEL_DEBUG: + txt = "debug"; + pri = LOG_DEBUG; + break; + default: + txt = "internal error"; + pri = LOG_ERR; + break; + } + if (txt != NULL) { + snprintf(fmtbuf, sizeof(fmtbuf), "%s: %s", txt, fmt); + vsnprintf(msgbuf, sizeof(msgbuf), fmtbuf, args); + } else { + vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); + } + if (log_on_stderr) + fprintf(stderr, "%s\n", msgbuf); + openlog(__progname, LOG_PID, log_facility); + syslog(pri, "%.500s", msgbuf); + closelog(); } diff --git a/log.c b/log.c index 1ce534ea..e7052115 100644 --- a/log.c +++ b/log.c @@ -1,11 +1,11 @@ /* - -Shared versions of debug(), log(), etc. - + * + * Shared versions of debug(), log(), etc. + * */ #include "includes.h" -RCSID("$OpenBSD: log.c,v 1.2 1999/11/19 16:04:17 markus Exp $"); +RCSID("$OpenBSD: log.c,v 1.5 1999/11/24 00:26:02 deraadt Exp $"); #include "ssh.h" #include "xmalloc.h" @@ -13,66 +13,65 @@ RCSID("$OpenBSD: log.c,v 1.2 1999/11/19 16:04:17 markus Exp $"); /* Fatal messages. This function never returns. */ void -fatal(const char *fmt, ...) +fatal(const char *fmt,...) { - va_list args; - va_start(args, fmt); - do_log(SYSLOG_LEVEL_FATAL, fmt, args); - va_end(args); - fatal_cleanup(); + va_list args; + va_start(args, fmt); + do_log(SYSLOG_LEVEL_FATAL, fmt, args); + va_end(args); + fatal_cleanup(); } /* Error messages that should be logged. */ void -error(const char *fmt, ...) +error(const char *fmt,...) { - va_list args; - va_start(args, fmt); - do_log(SYSLOG_LEVEL_ERROR, fmt, args); - va_end(args); + va_list args; + va_start(args, fmt); + do_log(SYSLOG_LEVEL_ERROR, fmt, args); + va_end(args); } /* Log this message (information that usually should go to the log). */ void -log(const char *fmt, ...) +log(const char *fmt,...) { - va_list args; - va_start(args, fmt); - do_log(SYSLOG_LEVEL_INFO, fmt, args); - va_end(args); + va_list args; + va_start(args, fmt); + do_log(SYSLOG_LEVEL_INFO, fmt, args); + va_end(args); } /* More detailed messages (information that does not need to go to the log). */ void -chat(const char *fmt, ...) +verbose(const char *fmt,...) { - va_list args; - va_start(args, fmt); - do_log(SYSLOG_LEVEL_CHAT, fmt, args); - va_end(args); + va_list args; + va_start(args, fmt); + do_log(SYSLOG_LEVEL_VERBOSE, fmt, args); + va_end(args); } /* Debugging messages that should not be logged during normal operation. */ void -debug(const char *fmt, ...) +debug(const char *fmt,...) { - va_list args; - va_start(args, fmt); - do_log(SYSLOG_LEVEL_DEBUG, fmt, args); - va_end(args); + va_list args; + va_start(args, fmt); + do_log(SYSLOG_LEVEL_DEBUG, fmt, args); + va_end(args); } /* Fatal cleanup */ -struct fatal_cleanup -{ - struct fatal_cleanup *next; - void (*proc)(void *); - void *context; +struct fatal_cleanup { + struct fatal_cleanup *next; + void (*proc) (void *); + void *context; }; static struct fatal_cleanup *fatal_cleanups = NULL; @@ -80,116 +79,108 @@ static struct fatal_cleanup *fatal_cleanups = NULL; /* Registers a cleanup function to be called by fatal() before exiting. */ void -fatal_add_cleanup(void (*proc)(void *), void *context) +fatal_add_cleanup(void (*proc) (void *), void *context) { - struct fatal_cleanup *cu; + struct fatal_cleanup *cu; - cu = xmalloc(sizeof(*cu)); - cu->proc = proc; - cu->context = context; - cu->next = fatal_cleanups; - fatal_cleanups = cu; + cu = xmalloc(sizeof(*cu)); + cu->proc = proc; + cu->context = context; + cu->next = fatal_cleanups; + fatal_cleanups = cu; } /* Removes a cleanup frunction to be called at fatal(). */ void -fatal_remove_cleanup(void (*proc)(void *context), void *context) +fatal_remove_cleanup(void (*proc) (void *context), void *context) { - struct fatal_cleanup **cup, *cu; - - for (cup = &fatal_cleanups; *cup; cup = &cu->next) - { - cu = *cup; - if (cu->proc == proc && cu->context == context) - { - *cup = cu->next; - xfree(cu); - return; + struct fatal_cleanup **cup, *cu; + + for (cup = &fatal_cleanups; *cup; cup = &cu->next) { + cu = *cup; + if (cu->proc == proc && cu->context == context) { + *cup = cu->next; + xfree(cu); + return; + } } - } - fatal("fatal_remove_cleanup: no such cleanup function: 0x%lx 0x%lx\n", - (unsigned long)proc, (unsigned long)context); + fatal("fatal_remove_cleanup: no such cleanup function: 0x%lx 0x%lx\n", + (unsigned long) proc, (unsigned long) context); } /* Cleanup and exit */ void fatal_cleanup(void) { - struct fatal_cleanup *cu, *next_cu; - static int called = 0; - if (called) - exit(255); - called = 1; - - /* Call cleanup functions. */ - for (cu = fatal_cleanups; cu; cu = next_cu) - { - next_cu = cu->next; - debug("Calling cleanup 0x%lx(0x%lx)", - (unsigned long)cu->proc, (unsigned long)cu->context); - (*cu->proc)(cu->context); - } - - exit(255); + struct fatal_cleanup *cu, *next_cu; + static int called = 0; + + if (called) + exit(255); + called = 1; + /* Call cleanup functions. */ + for (cu = fatal_cleanups; cu; cu = next_cu) { + next_cu = cu->next; + debug("Calling cleanup 0x%lx(0x%lx)", + (unsigned long) cu->proc, (unsigned long) cu->context); + (*cu->proc) (cu->context); + } + exit(255); } /* textual representation of log-facilities/levels */ - -static struct -{ - const char *name; - SyslogFacility val; -} log_facilities[] = -{ - { "DAEMON", SYSLOG_FACILITY_DAEMON }, - { "USER", SYSLOG_FACILITY_USER }, - { "AUTH", SYSLOG_FACILITY_AUTH }, - { "LOCAL0", SYSLOG_FACILITY_LOCAL0 }, - { "LOCAL1", SYSLOG_FACILITY_LOCAL1 }, - { "LOCAL2", SYSLOG_FACILITY_LOCAL2 }, - { "LOCAL3", SYSLOG_FACILITY_LOCAL3 }, - { "LOCAL4", SYSLOG_FACILITY_LOCAL4 }, - { "LOCAL5", SYSLOG_FACILITY_LOCAL5 }, - { "LOCAL6", SYSLOG_FACILITY_LOCAL6 }, - { "LOCAL7", SYSLOG_FACILITY_LOCAL7 }, - { NULL, 0 } +static struct { + const char *name; + SyslogFacility val; +} log_facilities[] = { + { "DAEMON", SYSLOG_FACILITY_DAEMON }, + { "USER", SYSLOG_FACILITY_USER }, + { "AUTH", SYSLOG_FACILITY_AUTH }, + { "LOCAL0", SYSLOG_FACILITY_LOCAL0 }, + { "LOCAL1", SYSLOG_FACILITY_LOCAL1 }, + { "LOCAL2", SYSLOG_FACILITY_LOCAL2 }, + { "LOCAL3", SYSLOG_FACILITY_LOCAL3 }, + { "LOCAL4", SYSLOG_FACILITY_LOCAL4 }, + { "LOCAL5", SYSLOG_FACILITY_LOCAL5 }, + { "LOCAL6", SYSLOG_FACILITY_LOCAL6 }, + { "LOCAL7", SYSLOG_FACILITY_LOCAL7 }, + { NULL, 0 } }; -static struct -{ - const char *name; - LogLevel val; +static struct { + const char *name; + LogLevel val; } log_levels[] = { - { "QUIET", SYSLOG_LEVEL_QUIET }, - { "FATAL", SYSLOG_LEVEL_FATAL }, - { "ERROR", SYSLOG_LEVEL_ERROR }, - { "INFO", SYSLOG_LEVEL_INFO }, - { "CHAT", SYSLOG_LEVEL_CHAT }, - { "DEBUG", SYSLOG_LEVEL_DEBUG }, - { NULL, 0 } + { "QUIET", SYSLOG_LEVEL_QUIET }, + { "FATAL", SYSLOG_LEVEL_FATAL }, + { "ERROR", SYSLOG_LEVEL_ERROR }, + { "INFO", SYSLOG_LEVEL_INFO }, + { "VERBOSE", SYSLOG_LEVEL_VERBOSE }, + { "DEBUG", SYSLOG_LEVEL_DEBUG }, + { NULL, 0 } }; SyslogFacility log_facility_number(char *name) { - int i; - if (name != NULL) - for (i = 0; log_facilities[i].name; i++) - if (strcasecmp(log_facilities[i].name, name) == 0) - return log_facilities[i].val; - return (SyslogFacility)-1; + int i; + if (name != NULL) + for (i = 0; log_facilities[i].name; i++) + if (strcasecmp(log_facilities[i].name, name) == 0) + return log_facilities[i].val; + return (SyslogFacility) - 1; } LogLevel log_level_number(char *name) { - int i; - if (name != NULL) - for (i = 0; log_levels[i].name; i++) - if (strcasecmp(log_levels[i].name, name) == 0) - return log_levels[i].val; - return (LogLevel)-1; + int i; + if (name != NULL) + for (i = 0; log_levels[i].name; i++) + if (strcasecmp(log_levels[i].name, name) == 0) + return log_levels[i].val; + return (LogLevel) - 1; } diff --git a/login.c b/login.c index 5bec8237..6541909a 100644 --- a/login.c +++ b/login.c @@ -1,128 +1,129 @@ /* - -login.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Fri Mar 24 14:51:08 1995 ylo - -This file performs some of the things login(1) normally does. We cannot -easily use something like login -p -h host -f user, because there are -several different logins around, and it is hard to determined what kind of -login the current system has. Also, we want to be able to execute commands -on a tty. - -*/ + * + * login.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Fri Mar 24 14:51:08 1995 ylo + * + * This file performs some of the things login(1) normally does. We cannot + * easily use something like login -p -h host -f user, because there are + * several different logins around, and it is hard to determined what kind of + * login the current system has. Also, we want to be able to execute commands + * on a tty. + * + */ #include "includes.h" RCSID("$Id$"); #include +#include "ssh.h" +#ifdef HAVE_UTIL_H +# include +#endif #ifdef HAVE_LASTLOG_H # include #endif -#include "ssh.h" - -/* Returns the time when the user last logged in. Returns 0 if the - information is not available. This must be called before record_login. +/* Returns the time when the user last logged in. Returns 0 if the + information is not available. This must be called before record_login. The host the user logged in from will be returned in buf. */ /* Returns the time when the user last logged in (or 0 if no previous login is found). The name of the host used last time is returned in buf. */ -unsigned long get_last_login_time(uid_t uid, const char *logname, - char *buf, unsigned int bufsize) +unsigned long +get_last_login_time(uid_t uid, const char *logname, + char *buf, unsigned int bufsize) { - struct lastlog ll; - char *lastlog; - int fd; - - lastlog = _PATH_LASTLOG; - - buf[0] = '\0'; - - fd = open(lastlog, O_RDONLY); - if (fd < 0) - return 0; - lseek(fd, (off_t)((long)uid * sizeof(ll)), SEEK_SET); - if (read(fd, &ll, sizeof(ll)) != sizeof(ll)) - { - close(fd); - return 0; - } - close(fd); - if (bufsize > sizeof(ll.ll_host) + 1) - bufsize = sizeof(ll.ll_host) + 1; - strncpy(buf, ll.ll_host, bufsize - 1); - buf[bufsize - 1] = 0; - return ll.ll_time; + struct lastlog ll; + char *lastlog; + int fd; + + lastlog = _PATH_LASTLOG; + buf[0] = '\0'; + + fd = open(lastlog, O_RDONLY); + if (fd < 0) + return 0; + lseek(fd, (off_t) ((long) uid * sizeof(ll)), SEEK_SET); + if (read(fd, &ll, sizeof(ll)) != sizeof(ll)) { + close(fd); + return 0; + } + close(fd); + if (bufsize > sizeof(ll.ll_host) + 1) + bufsize = sizeof(ll.ll_host) + 1; + strncpy(buf, ll.ll_host, bufsize - 1); + buf[bufsize - 1] = 0; + return ll.ll_time; } /* Records that the user has logged in. I these parts of operating systems were more standardized. */ -void record_login(int pid, const char *ttyname, const char *user, uid_t uid, - const char *host, struct sockaddr_in *addr) +void +record_login(int pid, const char *ttyname, const char *user, uid_t uid, + const char *host, struct sockaddr_in * addr) { - int fd; - struct lastlog ll; - char *lastlog; - - struct utmp u; - const char *utmp, *wtmp; - - /* Construct an utmp/wtmp entry. */ - memset(&u, 0, sizeof(u)); - strncpy(u.ut_line, ttyname + 5, sizeof(u.ut_line)); - u.ut_time = time(NULL); - strncpy(u.ut_name, user, sizeof(u.ut_name)); + int fd; + struct lastlog ll; + char *lastlog; + struct utmp u; + const char *utmp, *wtmp; + + /* Construct an utmp/wtmp entry. */ + memset(&u, 0, sizeof(u)); + strncpy(u.ut_line, ttyname + 5, sizeof(u.ut_line)); + u.ut_time = time(NULL); + strncpy(u.ut_name, user, sizeof(u.ut_name)); #ifdef HAVE_HOST_IN_UTMP - strncpy(u.ut_host, host, sizeof(u.ut_host)); + strncpy(u.ut_host, host, sizeof(u.ut_host)); #endif - /* Figure out the file names. */ - utmp = _PATH_UTMP; - wtmp = _PATH_WTMP; - - login(&u); - - lastlog = _PATH_LASTLOG; - - /* Update lastlog unless actually recording a logout. */ - if (strcmp(user, "") != 0) - { - /* It is safer to bzero the lastlog structure first because some - systems might have some extra fields in it (e.g. SGI) */ - memset(&ll, 0, sizeof(ll)); - - /* Update lastlog. */ - ll.ll_time = time(NULL); - strncpy(ll.ll_line, ttyname + 5, sizeof(ll.ll_line)); - strncpy(ll.ll_host, host, sizeof(ll.ll_host)); - fd = open(lastlog, O_RDWR); - if (fd >= 0) - { - lseek(fd, (off_t)((long)uid * sizeof(ll)), SEEK_SET); - if (write(fd, &ll, sizeof(ll)) != sizeof(ll)) - log("Could not write %.100s: %.100s", lastlog, strerror(errno)); - close(fd); + /* Figure out the file names. */ + utmp = _PATH_UTMP; + wtmp = _PATH_WTMP; + + login(&u); + lastlog = _PATH_LASTLOG; + + /* Update lastlog unless actually recording a logout. */ + if (strcmp(user, "") != 0) { + /* It is safer to bzero the lastlog structure first + because some systems might have some extra fields in it + (e.g. SGI) */ + memset(&ll, 0, sizeof(ll)); + + /* Update lastlog. */ + ll.ll_time = time(NULL); + strncpy(ll.ll_line, ttyname + 5, sizeof(ll.ll_line)); + strncpy(ll.ll_host, host, sizeof(ll.ll_host)); + fd = open(lastlog, O_RDWR); + if (fd >= 0) { + lseek(fd, (off_t) ((long) uid * sizeof(ll)), SEEK_SET); + if (write(fd, &ll, sizeof(ll)) != sizeof(ll)) + log("Could not write %.100s: %.100s", lastlog, strerror(errno)); + close(fd); + } } - } } - -void record_logout(int pid, const char *ttyname) + +/* Records that the user has logged out. */ + +void +record_logout(int pid, const char *ttyname) { #ifdef HAVE_LIBUTIL_LOGIN - const char *line = ttyname + 5; /* /dev/ttyq8 -> ttyq8 */ - if (logout(line)) - logwtmp(line, "", ""); + const char *line = ttyname + 5; /* /dev/ttyq8 -> ttyq8 */ + if (logout(line)) + logwtmp(line, "", ""); #else /* HAVE_LIBUTIL_LOGIN */ - record_login(pid, ttyname, "", -1, "", NULL); + record_login(pid, ttyname, "", -1, "", NULL); #endif /* HAVE_LIBUTIL_LOGIN */ } - diff --git a/match.c b/match.c index b8ceea9f..06b3b478 100644 --- a/match.c +++ b/match.c @@ -1,17 +1,17 @@ /* - -match.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Thu Jun 22 01:17:50 1995 ylo - -Simple pattern matching, with '*' and '?' as wildcards. - -*/ + * + * match.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Thu Jun 22 01:17:50 1995 ylo + * + * Simple pattern matching, with '*' and '?' as wildcards. + * + */ #include "includes.h" RCSID("$Id$"); @@ -20,59 +20,58 @@ RCSID("$Id$"); /* Returns true if the given string matches the pattern (which may contain ? and * as wildcards), and zero if it does not match. */ - -int match_pattern(const char *s, const char *pattern) -{ - while (1) - { - /* If at end of pattern, accept if also at end of string. */ - if (!*pattern) - return !*s; - /* Process '*'. */ - if (*pattern == '*') - { - /* Skip the asterisk. */ - pattern++; +int +match_pattern(const char *s, const char *pattern) +{ + for (;;) { + /* If at end of pattern, accept if also at end of string. */ + if (!*pattern) + return !*s; - /* If at end of pattern, accept immediately. */ - if (!*pattern) - return 1; + /* Process '*'. */ + if (*pattern == '*') { + /* Skip the asterisk. */ + pattern++; - /* If next character in pattern is known, optimize. */ - if (*pattern != '?' && *pattern != '*') - { - /* Look instances of the next character in pattern, and try - to match starting from those. */ - for (; *s; s++) - if (*s == *pattern && - match_pattern(s + 1, pattern + 1)) - return 1; - /* Failed. */ - return 0; - } + /* If at end of pattern, accept immediately. */ + if (!*pattern) + return 1; - /* Move ahead one character at a time and try to match at each - position. */ - for (; *s; s++) - if (match_pattern(s, pattern)) - return 1; - /* Failed. */ - return 0; - } + /* If next character in pattern is known, optimize. */ + if (*pattern != '?' && *pattern != '*') { + /* Look instances of the next character in + pattern, and try to match starting from + those. */ + for (; *s; s++) + if (*s == *pattern && + match_pattern(s + 1, pattern + 1)) + return 1; + /* Failed. */ + return 0; + } + /* Move ahead one character at a time and try to + match at each position. */ + for (; *s; s++) + if (match_pattern(s, pattern)) + return 1; + /* Failed. */ + return 0; + } + /* There must be at least one more character in the + string. If we are at the end, fail. */ + if (!*s) + return 0; - /* There must be at least one more character in the string. If we are - at the end, fail. */ - if (!*s) - return 0; + /* Check if the next character of the string is + acceptable. */ + if (*pattern != '?' && *pattern != *s) + return 0; - /* Check if the next character of the string is acceptable. */ - if (*pattern != '?' && *pattern != *s) - return 0; - - /* Move to the next character, both in string and in pattern. */ - s++; - pattern++; - } - /*NOTREACHED*/ + /* Move to the next character, both in string and in + pattern. */ + s++; + pattern++; + } + /* NOTREACHED */ } diff --git a/mpaux.c b/mpaux.c index fbc986cf..331089ae 100644 --- a/mpaux.c +++ b/mpaux.c @@ -1,22 +1,25 @@ /* - -mpaux.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sun Jul 16 04:29:30 1995 ylo - -This file contains various auxiliary functions related to multiple -precision integers. - + * + * mpaux.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Sun Jul 16 04:29:30 1995 ylo + * + * This file contains various auxiliary functions related to multiple + * precision integers. + * */ #include "includes.h" RCSID("$Id$"); +#include "getput.h" +#include "xmalloc.h" + #ifdef HAVE_OPENSSL #include #include @@ -26,29 +29,24 @@ RCSID("$Id$"); #include #endif -#include "getput.h" -#include "xmalloc.h" - - void compute_session_id(unsigned char session_id[16], unsigned char cookie[8], - BIGNUM *host_key_n, - BIGNUM *session_key_n) + BIGNUM* host_key_n, + BIGNUM* session_key_n) { - unsigned int host_key_bits = BN_num_bits(host_key_n); - unsigned int session_key_bits = BN_num_bits(session_key_n); - unsigned int bytes = (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8 + 8; - unsigned char *buf = xmalloc(bytes); - MD5_CTX md; - - BN_bn2bin(host_key_n, buf); - BN_bn2bin(session_key_n, buf + (host_key_bits + 7 ) / 8); - memcpy(buf + (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8, - cookie, 8); - MD5_Init(&md); - MD5_Update(&md, buf, bytes); - MD5_Final(session_id, &md); - memset(buf, 0, bytes); - xfree(buf); + unsigned int host_key_bits = BN_num_bits(host_key_n); + unsigned int session_key_bits = BN_num_bits(session_key_n); + unsigned int bytes = (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8 + 8; + unsigned char *buf = xmalloc(bytes); + MD5_CTX md; + + BN_bn2bin(host_key_n, buf); + BN_bn2bin(session_key_n, buf + (host_key_bits + 7) / 8); + memcpy(buf + (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8, cookie, 8); + MD5_Init(&md); + MD5_Update(&md, buf, bytes); + MD5_Final(session_id, &md); + memset(buf, 0, bytes); + xfree(buf); } diff --git a/mpaux.h b/mpaux.h index 4d50688b..79f72ba1 100644 --- a/mpaux.h +++ b/mpaux.h @@ -1,18 +1,17 @@ /* - -mpaux.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sun Jul 16 04:29:30 1995 ylo - -This file contains various auxiliary functions related to multiple -precision integers. - -*/ + * + * mpaux.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Sun Jul 16 04:29:30 1995 ylo + * + * This file contains various auxiliary functions related to multiple + * precision integers. + */ /* RCSID("$Id$"); */ @@ -22,9 +21,10 @@ precision integers. /* Computes a 16-byte session id in the global variable session_id. The session id is computed by concatenating the linearized, msb first representations of host_key_n, session_key_n, and the cookie. */ -void compute_session_id(unsigned char session_id[16], - unsigned char cookie[8], - BIGNUM *host_key_n, - BIGNUM *session_key_n); +void +compute_session_id(unsigned char session_id[16], + unsigned char cookie[8], + BIGNUM * host_key_n, + BIGNUM * session_key_n); -#endif /* MPAUX_H */ +#endif /* MPAUX_H */ diff --git a/nchan.c b/nchan.c index 227e52f7..97101e36 100644 --- a/nchan.c +++ b/nchan.c @@ -15,122 +15,131 @@ static void chan_shutdown_read(Channel *c); static void chan_delele_if_full_closed(Channel *c); /* - * EVENTS: update channel input/output states - * execute ACTIONS + * EVENTS update channel input/output states execute ACTIONS */ + /* events concerning the INPUT from socket for channel (istate) */ void -chan_rcvd_oclose(Channel *c){ - switch(c->istate){ +chan_rcvd_oclose(Channel *c) +{ + switch (c->istate) { case CHAN_INPUT_WAIT_OCLOSE: debug("channel %d: INPUT_WAIT_OCLOSE -> INPUT_CLOSED [rcvd OCLOSE]", c->self); - c->istate=CHAN_INPUT_CLOSED; + c->istate = CHAN_INPUT_CLOSED; chan_delele_if_full_closed(c); break; case CHAN_INPUT_OPEN: debug("channel %d: INPUT_OPEN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self); chan_shutdown_read(c); chan_send_ieof(c); - c->istate=CHAN_INPUT_CLOSED; + c->istate = CHAN_INPUT_CLOSED; chan_delele_if_full_closed(c); break; default: - debug("protocol error: chan_rcvd_oclose %d for istate %d",c->self,c->istate); + debug("protocol error: chan_rcvd_oclose %d for istate %d", c->self, c->istate); break; } } void -chan_read_failed(Channel *c){ - switch(c->istate){ +chan_read_failed(Channel *c) +{ + switch (c->istate) { case CHAN_INPUT_OPEN: debug("channel %d: INPUT_OPEN -> INPUT_WAIT_DRAIN [read failed]", c->self); chan_shutdown_read(c); - c->istate=CHAN_INPUT_WAIT_DRAIN; + c->istate = CHAN_INPUT_WAIT_DRAIN; break; default: debug("internal error: we do not read, but chan_read_failed %d for istate %d", - c->self,c->istate); + c->self, c->istate); break; } } void -chan_ibuf_empty(Channel *c){ - if(buffer_len(&c->input)){ - debug("internal error: chan_ibuf_empty %d for non empty buffer",c->self); +chan_ibuf_empty(Channel *c) +{ + if (buffer_len(&c->input)) { + debug("internal error: chan_ibuf_empty %d for non empty buffer", c->self); return; } - switch(c->istate){ + switch (c->istate) { case CHAN_INPUT_WAIT_DRAIN: debug("channel %d: INPUT_WAIT_DRAIN -> INPUT_WAIT_OCLOSE [inbuf empty, send IEOF]", c->self); chan_send_ieof(c); - c->istate=CHAN_INPUT_WAIT_OCLOSE; + c->istate = CHAN_INPUT_WAIT_OCLOSE; break; default: - debug("internal error: chan_ibuf_empty %d for istate %d",c->self,c->istate); + debug("internal error: chan_ibuf_empty %d for istate %d", c->self, c->istate); break; } } + /* events concerning the OUTPUT from channel for socket (ostate) */ void -chan_rcvd_ieof(Channel *c){ - switch(c->ostate){ +chan_rcvd_ieof(Channel *c) +{ + switch (c->ostate) { case CHAN_OUTPUT_OPEN: debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_DRAIN [rvcd IEOF]", c->self); - c->ostate=CHAN_OUTPUT_WAIT_DRAIN; + c->ostate = CHAN_OUTPUT_WAIT_DRAIN; break; case CHAN_OUTPUT_WAIT_IEOF: debug("channel %d: OUTPUT_WAIT_IEOF -> OUTPUT_CLOSED [rvcd IEOF]", c->self); - c->ostate=CHAN_OUTPUT_CLOSED; + c->ostate = CHAN_OUTPUT_CLOSED; chan_delele_if_full_closed(c); break; default: - debug("protocol error: chan_rcvd_ieof %d for ostate %d", c->self,c->ostate); + debug("protocol error: chan_rcvd_ieof %d for ostate %d", c->self, c->ostate); break; } } void -chan_write_failed(Channel *c){ - switch(c->ostate){ +chan_write_failed(Channel *c) +{ + switch (c->ostate) { case CHAN_OUTPUT_OPEN: debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_IEOF [write failed]", c->self); chan_send_oclose(c); - c->ostate=CHAN_OUTPUT_WAIT_IEOF; + c->ostate = CHAN_OUTPUT_WAIT_IEOF; break; case CHAN_OUTPUT_WAIT_DRAIN: debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [write failed]", c->self); chan_send_oclose(c); - c->ostate=CHAN_OUTPUT_CLOSED; + c->ostate = CHAN_OUTPUT_CLOSED; chan_delele_if_full_closed(c); break; default: - debug("internal error: chan_write_failed %d for ostate %d",c->self,c->ostate); + debug("internal error: chan_write_failed %d for ostate %d", c->self, c->ostate); break; } } void -chan_obuf_empty(Channel *c){ - if(buffer_len(&c->output)){ - debug("internal error: chan_obuf_empty %d for non empty buffer",c->self); +chan_obuf_empty(Channel *c) +{ + if (buffer_len(&c->output)) { + debug("internal error: chan_obuf_empty %d for non empty buffer", c->self); return; } - switch(c->ostate){ + switch (c->ostate) { case CHAN_OUTPUT_WAIT_DRAIN: debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [obuf empty, send OCLOSE]", c->self); chan_send_oclose(c); - c->ostate=CHAN_OUTPUT_CLOSED; + c->ostate = CHAN_OUTPUT_CLOSED; chan_delele_if_full_closed(c); break; default: - debug("internal error: chan_obuf_empty %d for ostate %d",c->self,c->ostate); + debug("internal error: chan_obuf_empty %d for ostate %d", c->self, c->ostate); break; } } + /* - * ACTIONS: should never update c->istate or c->ostate + * ACTIONS: should never update the channel states: c->istate or c->ostate */ static void -chan_send_ieof(Channel *c){ - switch(c->istate){ +chan_send_ieof(Channel *c) +{ + switch (c->istate) { case CHAN_INPUT_OPEN: case CHAN_INPUT_WAIT_DRAIN: packet_start(SSH_MSG_CHANNEL_INPUT_EOF); @@ -138,13 +147,14 @@ chan_send_ieof(Channel *c){ packet_send(); break; default: - debug("internal error: channel %d: cannot send IEOF for istate %d",c->self,c->istate); + debug("internal error: channel %d: cannot send IEOF for istate %d", c->self, c->istate); break; } } static void -chan_send_oclose(Channel *c){ - switch(c->ostate){ +chan_send_oclose(Channel *c) +{ + switch (c->ostate) { case CHAN_OUTPUT_OPEN: case CHAN_OUTPUT_WAIT_DRAIN: chan_shutdown_write(c); @@ -154,34 +164,39 @@ chan_send_oclose(Channel *c){ packet_send(); break; default: - debug("internal error: channel %d: cannot send OCLOSE for ostate %d",c->self,c->istate); + debug("internal error: channel %d: cannot send OCLOSE for ostate %d", c->self, c->istate); break; } } + /* helper */ static void -chan_shutdown_write(Channel *c){ +chan_shutdown_write(Channel *c) +{ debug("channel %d: shutdown_write", c->self); - if(shutdown(c->sock, SHUT_WR)<0) + if (shutdown(c->sock, SHUT_WR) < 0) error("chan_shutdown_write failed for #%d/fd%d: %.100s", - c->self, c->sock, strerror(errno)); + c->self, c->sock, strerror(errno)); } static void -chan_shutdown_read(Channel *c){ +chan_shutdown_read(Channel *c) +{ debug("channel %d: shutdown_read", c->self); - if(shutdown(c->sock, SHUT_RD)<0) + if (shutdown(c->sock, SHUT_RD) < 0) error("chan_shutdown_read failed for #%d/fd%d: %.100s", - c->self, c->sock, strerror(errno)); + c->self, c->sock, strerror(errno)); } static void -chan_delele_if_full_closed(Channel *c){ - if(c->istate==CHAN_INPUT_CLOSED && c->ostate==CHAN_OUTPUT_CLOSED){ +chan_delele_if_full_closed(Channel *c) +{ + if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) { debug("channel %d: closing", c->self); channel_free(c->self); } } void -chan_init_iostates(Channel *c){ - c->ostate=CHAN_OUTPUT_OPEN; - c->istate=CHAN_INPUT_OPEN; +chan_init_iostates(Channel *c) +{ + c->ostate = CHAN_OUTPUT_OPEN; + c->istate = CHAN_INPUT_OPEN; } diff --git a/nchan.h b/nchan.h index 4e3a129b..0e70c5ac 100644 --- a/nchan.h +++ b/nchan.h @@ -7,24 +7,24 @@ * SSH Protocol 1.5 aka New Channel Protocol * Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored. * Written by Markus Friedl in October 1999 - * + * * Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the * tear down of channels: - * + * * 1.3: strict request-ack-protocol: * CLOSE -> * <- CLOSE_CONFIRM - * + * * 1.5: uses variations of: * IEOF -> * <- OCLOSE * <- IEOF * OCLOSE -> * i.e. both sides have to close the channel - * + * * See the debugging output from 'ssh -v' and 'sshd -d' of * ssh-1.2.27 as an example. - * + * */ /* ssh-proto-1.5 overloads prot-1.3-message-types */ @@ -44,14 +44,14 @@ #define CHAN_OUTPUT_CLOSED 0x80 /* EVENTS for the input state */ -void chan_rcvd_oclose(Channel *c); -void chan_read_failed(Channel *c); -void chan_ibuf_empty(Channel *c); +void chan_rcvd_oclose(Channel * c); +void chan_read_failed(Channel * c); +void chan_ibuf_empty(Channel * c); /* EVENTS for the output state */ -void chan_rcvd_ieof(Channel *c); -void chan_write_failed(Channel *c); -void chan_obuf_empty(Channel *c); +void chan_rcvd_ieof(Channel * c); +void chan_write_failed(Channel * c); +void chan_obuf_empty(Channel * c); -void chan_init_iostates(Channel *c); +void chan_init_iostates(Channel * c); #endif diff --git a/packet.c b/packet.c index 6b861916..e96cfa78 100644 --- a/packet.c +++ b/packet.c @@ -1,18 +1,18 @@ /* - -packet.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sat Mar 18 02:40:40 1995 ylo - -This file contains code implementing the packet protocol and communication -with the other side. This same code is used both on client and server side. - -*/ + * + * packet.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Sat Mar 18 02:40:40 1995 ylo + * + * This file contains code implementing the packet protocol and communication + * with the other side. This same code is used both on client and server side. + * + */ #include "includes.h" RCSID("$Id$"); @@ -45,7 +45,8 @@ static unsigned int remote_protocol_flags = 0; /* Encryption context for receiving data. This is only used for decryption. */ static CipherContext receive_context; -/* Encryption coontext for sending data. This is only used for encryption. */ + +/* Encryption context for sending data. This is only used for encryption. */ static CipherContext send_context; /* Buffer for raw input data from the socket. */ @@ -81,22 +82,20 @@ static int interactive_mode = 0; void packet_set_connection(int fd_in, int fd_out) { - connection_in = fd_in; - connection_out = fd_out; - cipher_type = SSH_CIPHER_NONE; - cipher_set_key(&send_context, SSH_CIPHER_NONE, (unsigned char *)"", 0, 1); - cipher_set_key(&receive_context, SSH_CIPHER_NONE, (unsigned char *)"", 0, 0); - if (!initialized) - { - initialized = 1; - buffer_init(&input); - buffer_init(&output); - buffer_init(&outgoing_packet); - buffer_init(&incoming_packet); - } - - /* Kludge: arrange the close function to be called from fatal(). */ - fatal_add_cleanup((void (*)(void *))packet_close, NULL); + connection_in = fd_in; + connection_out = fd_out; + cipher_type = SSH_CIPHER_NONE; + cipher_set_key(&send_context, SSH_CIPHER_NONE, (unsigned char *) "", 0, 1); + cipher_set_key(&receive_context, SSH_CIPHER_NONE, (unsigned char *) "", 0, 0); + if (!initialized) { + initialized = 1; + buffer_init(&input); + buffer_init(&output); + buffer_init(&outgoing_packet); + buffer_init(&incoming_packet); + } + /* Kludge: arrange the close function to be called from fatal(). */ + fatal_add_cleanup((void (*) (void *)) packet_close, NULL); } /* Sets the connection into non-blocking mode. */ @@ -104,15 +103,14 @@ packet_set_connection(int fd_in, int fd_out) void packet_set_nonblocking() { - /* Set the socket into non-blocking mode. */ - if (fcntl(connection_in, F_SETFL, O_NONBLOCK) < 0) - error("fcntl O_NONBLOCK: %.100s", strerror(errno)); + /* Set the socket into non-blocking mode. */ + if (fcntl(connection_in, F_SETFL, O_NONBLOCK) < 0) + error("fcntl O_NONBLOCK: %.100s", strerror(errno)); - if (connection_out != connection_in) - { - if (fcntl(connection_out, F_SETFL, O_NONBLOCK) < 0) - error("fcntl O_NONBLOCK: %.100s", strerror(errno)); - } + if (connection_out != connection_in) { + if (fcntl(connection_out, F_SETFL, O_NONBLOCK) < 0) + error("fcntl O_NONBLOCK: %.100s", strerror(errno)); + } } /* Returns the socket used for reading. */ @@ -120,7 +118,7 @@ packet_set_nonblocking() int packet_get_connection_in() { - return connection_in; + return connection_in; } /* Returns the descriptor used for writing. */ @@ -128,7 +126,7 @@ packet_get_connection_in() int packet_get_connection_out() { - return connection_out; + return connection_out; } /* Closes the connection and clears and frees internal data structures. */ @@ -136,28 +134,24 @@ packet_get_connection_out() void packet_close() { - if (!initialized) - return; - initialized = 0; - if (connection_in == connection_out) - { - shutdown(connection_out, SHUT_RDWR); - close(connection_out); - } - else - { - close(connection_in); - close(connection_out); - } - buffer_free(&input); - buffer_free(&output); - buffer_free(&outgoing_packet); - buffer_free(&incoming_packet); - if (packet_compression) - { - buffer_free(&compression_buffer); - buffer_compress_uninit(); - } + if (!initialized) + return; + initialized = 0; + if (connection_in == connection_out) { + shutdown(connection_out, SHUT_RDWR); + close(connection_out); + } else { + close(connection_in); + close(connection_out); + } + buffer_free(&input); + buffer_free(&output); + buffer_free(&outgoing_packet); + buffer_free(&incoming_packet); + if (packet_compression) { + buffer_free(&compression_buffer); + buffer_compress_uninit(); + } } /* Sets remote side protocol flags. */ @@ -165,8 +159,8 @@ packet_close() void packet_set_protocol_flags(unsigned int protocol_flags) { - remote_protocol_flags = protocol_flags; - channel_set_options((protocol_flags & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) != 0); + remote_protocol_flags = protocol_flags; + channel_set_options((protocol_flags & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) != 0); } /* Returns the remote protocol flags set earlier by the above function. */ @@ -174,63 +168,60 @@ packet_set_protocol_flags(unsigned int protocol_flags) unsigned int packet_get_protocol_flags() { - return remote_protocol_flags; + return remote_protocol_flags; } -/* Starts packet compression from the next packet on in both directions. +/* Starts packet compression from the next packet on in both directions. Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. */ void packet_start_compression(int level) { - if (packet_compression) - fatal("Compression already enabled."); - packet_compression = 1; - buffer_init(&compression_buffer); - buffer_compress_init(level); + if (packet_compression) + fatal("Compression already enabled."); + packet_compression = 1; + buffer_init(&compression_buffer); + buffer_compress_init(level); } /* Encrypts the given number of bytes, copying from src to dest. bytes is known to be a multiple of 8. */ void -packet_encrypt(CipherContext *cc, void *dest, void *src, +packet_encrypt(CipherContext * cc, void *dest, void *src, unsigned int bytes) { - cipher_encrypt(cc, dest, src, bytes); + cipher_encrypt(cc, dest, src, bytes); } /* Decrypts the given number of bytes, copying from src to dest. bytes is known to be a multiple of 8. */ void -packet_decrypt(CipherContext *cc, void *dest, void *src, +packet_decrypt(CipherContext * cc, void *dest, void *src, unsigned int bytes) { - int i; - - if ((bytes % 8) != 0) - fatal("packet_decrypt: bad ciphertext length %d", bytes); - - /* - Cryptographic attack detector for ssh - Modifications for packet.c - (C)1998 CORE-SDI, Buenos Aires Argentina - Ariel Futoransky(futo@core-sdi.com) - */ - switch (cc->type) - { - case SSH_CIPHER_NONE: - i = DEATTACK_OK; - break; - default: - i = detect_attack(src, bytes, NULL); - break; - } - - if (i == DEATTACK_DETECTED) - packet_disconnect("crc32 compensation attack: network attack detected"); - - cipher_decrypt(cc, dest, src, bytes); + int i; + + if ((bytes % 8) != 0) + fatal("packet_decrypt: bad ciphertext length %d", bytes); + + /* Cryptographic attack detector for ssh - Modifications for packet.c + (C)1998 CORE-SDI, Buenos Aires Argentina Ariel Futoransky(futo@core-sdi.com) */ + + switch (cc->type) { + case SSH_CIPHER_NONE: + i = DEATTACK_OK; + break; + default: + i = detect_attack(src, bytes, NULL); + break; + } + + if (i == DEATTACK_DETECTED) + packet_disconnect("crc32 compensation attack: network attack detected"); + + cipher_decrypt(cc, dest, src, bytes); } /* Causes any further packets to be encrypted using the given key. The same @@ -241,9 +232,9 @@ void packet_set_encryption_key(const unsigned char *key, unsigned int keylen, int cipher) { - /* All other ciphers use the same key in both directions for now. */ - cipher_set_key(&receive_context, cipher, key, keylen, 0); - cipher_set_key(&send_context, cipher, key, keylen, 1); + /* All other ciphers use the same key in both directions for now. */ + cipher_set_key(&receive_context, cipher, key, keylen, 0); + cipher_set_key(&send_context, cipher, key, keylen, 1); } /* Starts constructing a packet to send. */ @@ -251,12 +242,12 @@ packet_set_encryption_key(const unsigned char *key, unsigned int keylen, void packet_start(int type) { - char buf[9]; + char buf[9]; - buffer_clear(&outgoing_packet); - memset(buf, 0, 8); - buf[8] = type; - buffer_append(&outgoing_packet, buf, 9); + buffer_clear(&outgoing_packet); + memset(buf, 0, 8); + buf[8] = type; + buffer_append(&outgoing_packet, buf, 9); } /* Appends a character to the packet data. */ @@ -264,8 +255,8 @@ packet_start(int type) void packet_put_char(int value) { - char ch = value; - buffer_append(&outgoing_packet, &ch, 1); + char ch = value; + buffer_append(&outgoing_packet, &ch, 1); } /* Appends an integer to the packet data. */ @@ -273,7 +264,7 @@ packet_put_char(int value) void packet_put_int(unsigned int value) { - buffer_put_int(&outgoing_packet, value); + buffer_put_int(&outgoing_packet, value); } /* Appends a string to packet data. */ @@ -281,84 +272,85 @@ packet_put_int(unsigned int value) void packet_put_string(const char *buf, unsigned int len) { - buffer_put_string(&outgoing_packet, buf, len); + buffer_put_string(&outgoing_packet, buf, len); } /* Appends an arbitrary precision integer to packet data. */ void -packet_put_bignum(BIGNUM *value) +packet_put_bignum(BIGNUM * value) { - buffer_put_bignum(&outgoing_packet, value); + buffer_put_bignum(&outgoing_packet, value); } /* Finalizes and sends the packet. If the encryption key has been set, encrypts the packet before sending. */ - + void packet_send() { - char buf[8], *cp; - int i, padding, len; - unsigned int checksum; - u_int32_t rand = 0; - - /* If using packet compression, compress the payload of the outgoing - packet. */ - if (packet_compression) - { - buffer_clear(&compression_buffer); - buffer_consume(&outgoing_packet, 8); /* Skip padding. */ - buffer_append(&compression_buffer, "\0\0\0\0\0\0\0\0", 8); /* padding */ - buffer_compress(&outgoing_packet, &compression_buffer); - buffer_clear(&outgoing_packet); - buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer), - buffer_len(&compression_buffer)); - } - - /* Compute packet length without padding (add checksum, remove padding). */ - len = buffer_len(&outgoing_packet) + 4 - 8; - - /* Insert padding. */ - padding = 8 - len % 8; - if (cipher_type != SSH_CIPHER_NONE) - { - cp = buffer_ptr(&outgoing_packet); - for (i = 0; i < padding; i++) { - if (i % 4 == 0) - rand = arc4random(); - cp[7 - i] = rand & 0xff; - rand >>= 8; - } - } - buffer_consume(&outgoing_packet, 8 - padding); - - /* Add check bytes. */ - checksum = crc32((unsigned char *)buffer_ptr(&outgoing_packet), - buffer_len(&outgoing_packet)); - PUT_32BIT(buf, checksum); - buffer_append(&outgoing_packet, buf, 4); + char buf[8], *cp; + int i, padding, len; + unsigned int checksum; + u_int32_t rand = 0; + + /* If using packet compression, compress the payload of the + outgoing packet. */ + if (packet_compression) { + buffer_clear(&compression_buffer); + /* Skip padding. */ + buffer_consume(&outgoing_packet, 8); + /* padding */ + buffer_append(&compression_buffer, "\0\0\0\0\0\0\0\0", 8); + buffer_compress(&outgoing_packet, &compression_buffer); + buffer_clear(&outgoing_packet); + buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer), + buffer_len(&compression_buffer)); + } + /* Compute packet length without padding (add checksum, remove padding). */ + len = buffer_len(&outgoing_packet) + 4 - 8; + + /* Insert padding. */ + padding = 8 - len % 8; + if (cipher_type != SSH_CIPHER_NONE) { + cp = buffer_ptr(&outgoing_packet); + for (i = 0; i < padding; i++) { + if (i % 4 == 0) + rand = arc4random(); + cp[7 - i] = rand & 0xff; + rand >>= 8; + } + } + buffer_consume(&outgoing_packet, 8 - padding); + + /* Add check bytes. */ + checksum = crc32((unsigned char *) buffer_ptr(&outgoing_packet), + buffer_len(&outgoing_packet)); + PUT_32BIT(buf, checksum); + buffer_append(&outgoing_packet, buf, 4); #ifdef PACKET_DEBUG - fprintf(stderr, "packet_send plain: "); - buffer_dump(&outgoing_packet); + fprintf(stderr, "packet_send plain: "); + buffer_dump(&outgoing_packet); #endif - /* Append to output. */ - PUT_32BIT(buf, len); - buffer_append(&output, buf, 4); - buffer_append_space(&output, &cp, buffer_len(&outgoing_packet)); - packet_encrypt(&send_context, cp, buffer_ptr(&outgoing_packet), - buffer_len(&outgoing_packet)); - + /* Append to output. */ + PUT_32BIT(buf, len); + buffer_append(&output, buf, 4); + buffer_append_space(&output, &cp, buffer_len(&outgoing_packet)); + packet_encrypt(&send_context, cp, buffer_ptr(&outgoing_packet), + buffer_len(&outgoing_packet)); + #ifdef PACKET_DEBUG - fprintf(stderr, "encrypted: "); buffer_dump(&output); + fprintf(stderr, "encrypted: "); + buffer_dump(&output); #endif - buffer_clear(&outgoing_packet); + buffer_clear(&outgoing_packet); - /* Note that the packet is now only buffered in output. It won\'t be - actually sent until packet_write_wait or packet_write_poll is called. */ + /* Note that the packet is now only buffered in output. It won\'t + be actually sent until packet_write_wait or packet_write_poll + is called. */ } /* Waits until a packet has been received, and returns its type. Note that @@ -368,42 +360,41 @@ packet_send() int packet_read(int *payload_len_ptr) { - int type, len; - fd_set set; - char buf[8192]; - - /* Since we are blocking, ensure that all written packets have been sent. */ - packet_write_wait(); - - /* Stay in the loop until we have received a complete packet. */ - for (;;) - { - /* Try to read a packet from the buffer. */ - type = packet_read_poll(payload_len_ptr); - if (type == SSH_SMSG_SUCCESS - || type == SSH_SMSG_FAILURE - || type == SSH_CMSG_EOF - || type == SSH_CMSG_EXIT_CONFIRMATION) - packet_integrity_check(*payload_len_ptr, 0, type); - /* If we got a packet, return it. */ - if (type != SSH_MSG_NONE) - return type; - /* Otherwise, wait for some data to arrive, add it to the buffer, - and try again. */ - FD_ZERO(&set); - FD_SET(connection_in, &set); - /* Wait for some data to arrive. */ - select(connection_in + 1, &set, NULL, NULL, NULL); - /* Read data from the socket. */ - len = read(connection_in, buf, sizeof(buf)); - if (len == 0) - fatal("Connection closed by remote host."); - if (len < 0) - fatal("Read from socket failed: %.100s", strerror(errno)); - /* Append it to the buffer. */ - packet_process_incoming(buf, len); - } - /*NOTREACHED*/ + int type, len; + fd_set set; + char buf[8192]; + + /* Since we are blocking, ensure that all written packets have been sent. */ + packet_write_wait(); + + /* Stay in the loop until we have received a complete packet. */ + for (;;) { + /* Try to read a packet from the buffer. */ + type = packet_read_poll(payload_len_ptr); + if (type == SSH_SMSG_SUCCESS + || type == SSH_SMSG_FAILURE + || type == SSH_CMSG_EOF + || type == SSH_CMSG_EXIT_CONFIRMATION) + packet_integrity_check(*payload_len_ptr, 0, type); + /* If we got a packet, return it. */ + if (type != SSH_MSG_NONE) + return type; + /* Otherwise, wait for some data to arrive, add it to the + buffer, and try again. */ + FD_ZERO(&set); + FD_SET(connection_in, &set); + /* Wait for some data to arrive. */ + select(connection_in + 1, &set, NULL, NULL, NULL); + /* Read data from the socket. */ + len = read(connection_in, buf, sizeof(buf)); + if (len == 0) + fatal("Connection closed by %.200s", get_remote_ipaddr()); + if (len < 0) + fatal("Read from socket failed: %.100s", strerror(errno)); + /* Append it to the buffer. */ + packet_process_incoming(buf, len); + } + /* NOTREACHED */ } /* Waits until a packet has been received, verifies that its type matches @@ -412,131 +403,126 @@ packet_read(int *payload_len_ptr) void packet_read_expect(int *payload_len_ptr, int expected_type) { - int type; + int type; - type = packet_read(payload_len_ptr); - if (type != expected_type) - packet_disconnect("Protocol error: expected packet type %d, got %d", - expected_type, type); + type = packet_read(payload_len_ptr); + if (type != expected_type) + packet_disconnect("Protocol error: expected packet type %d, got %d", + expected_type, type); } /* Checks if a full packet is available in the data received so far via - packet_process_incoming. If so, reads the packet; otherwise returns - SSH_MSG_NONE. This does not wait for data from the connection. - - SSH_MSG_DISCONNECT is handled specially here. Also, - SSH_MSG_IGNORE messages are skipped by this function and are never returned - to higher levels. - - The returned payload_len does include space consumed by: - Packet length - Padding - Packet type - Check bytes - - - */ + * packet_process_incoming. If so, reads the packet; otherwise returns + * SSH_MSG_NONE. This does not wait for data from the connection. + * + * SSH_MSG_DISCONNECT is handled specially here. Also, + * SSH_MSG_IGNORE messages are skipped by this function and are never returned + * to higher levels. + * + * The returned payload_len does include space consumed by: + * Packet length + * Padding + * Packet type + * Check bytes + */ int packet_read_poll(int *payload_len_ptr) { - unsigned int len, padded_len; - unsigned char *ucp; - char buf[8], *cp; - unsigned int checksum, stored_checksum; - - restart: - - /* Check if input size is less than minimum packet size. */ - if (buffer_len(&input) < 4 + 8) - return SSH_MSG_NONE; - /* Get length of incoming packet. */ - ucp = (unsigned char *)buffer_ptr(&input); - len = GET_32BIT(ucp); - if (len < 1 + 2 + 2 || len > 256*1024) - packet_disconnect("Bad packet length %d.", len); - padded_len = (len + 8) & ~7; - - /* Check if the packet has been entirely received. */ - if (buffer_len(&input) < 4 + padded_len) - return SSH_MSG_NONE; - - /* The entire packet is in buffer. */ - - /* Consume packet length. */ - buffer_consume(&input, 4); - - /* Copy data to incoming_packet. */ - buffer_clear(&incoming_packet); - buffer_append_space(&incoming_packet, &cp, padded_len); - packet_decrypt(&receive_context, cp, buffer_ptr(&input), padded_len); - buffer_consume(&input, padded_len); + unsigned int len, padded_len; + unsigned char *ucp; + char buf[8], *cp; + unsigned int checksum, stored_checksum; + +restart: + + /* Check if input size is less than minimum packet size. */ + if (buffer_len(&input) < 4 + 8) + return SSH_MSG_NONE; + /* Get length of incoming packet. */ + ucp = (unsigned char *) buffer_ptr(&input); + len = GET_32BIT(ucp); + if (len < 1 + 2 + 2 || len > 256 * 1024) + packet_disconnect("Bad packet length %d.", len); + padded_len = (len + 8) & ~7; + + /* Check if the packet has been entirely received. */ + if (buffer_len(&input) < 4 + padded_len) + return SSH_MSG_NONE; + + /* The entire packet is in buffer. */ + + /* Consume packet length. */ + buffer_consume(&input, 4); + + /* Copy data to incoming_packet. */ + buffer_clear(&incoming_packet); + buffer_append_space(&incoming_packet, &cp, padded_len); + packet_decrypt(&receive_context, cp, buffer_ptr(&input), padded_len); + buffer_consume(&input, padded_len); #ifdef PACKET_DEBUG - fprintf(stderr, "read_poll plain: "); buffer_dump(&incoming_packet); + fprintf(stderr, "read_poll plain: "); + buffer_dump(&incoming_packet); #endif - - /* Compute packet checksum. */ - checksum = crc32((unsigned char *)buffer_ptr(&incoming_packet), - buffer_len(&incoming_packet) - 4); - - /* Skip padding. */ - buffer_consume(&incoming_packet, 8 - len % 8); - - /* Test check bytes. */ - - if (len != buffer_len(&incoming_packet)) - packet_disconnect("packet_read_poll: len %d != buffer_len %d.", - len, buffer_len(&incoming_packet)); - - ucp = (unsigned char *)buffer_ptr(&incoming_packet) + len - 4; - stored_checksum = GET_32BIT(ucp); - if (checksum != stored_checksum) - packet_disconnect("Corrupted check bytes on input."); - buffer_consume_end(&incoming_packet, 4); - - /* If using packet compression, decompress the packet. */ - if (packet_compression) - { - buffer_clear(&compression_buffer); - buffer_uncompress(&incoming_packet, &compression_buffer); - buffer_clear(&incoming_packet); - buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), - buffer_len(&compression_buffer)); - } - - /* Get packet type. */ - buffer_get(&incoming_packet, &buf[0], 1); - - /* Return length of payload (without type field). */ - *payload_len_ptr = buffer_len(&incoming_packet); - - /* Handle disconnect message. */ - if ((unsigned char)buf[0] == SSH_MSG_DISCONNECT) - fatal("%.900s", packet_get_string(NULL)); - - /* Ignore ignore messages. */ - if ((unsigned char)buf[0] == SSH_MSG_IGNORE) - goto restart; - - /* Send debug messages as debugging output. */ - if ((unsigned char)buf[0] == SSH_MSG_DEBUG) - { - debug("Remote: %.900s", packet_get_string(NULL)); - goto restart; - } - - /* Return type. */ - return (unsigned char)buf[0]; -} - + + /* Compute packet checksum. */ + checksum = crc32((unsigned char *) buffer_ptr(&incoming_packet), + buffer_len(&incoming_packet) - 4); + + /* Skip padding. */ + buffer_consume(&incoming_packet, 8 - len % 8); + + /* Test check bytes. */ + + if (len != buffer_len(&incoming_packet)) + packet_disconnect("packet_read_poll: len %d != buffer_len %d.", + len, buffer_len(&incoming_packet)); + + ucp = (unsigned char *) buffer_ptr(&incoming_packet) + len - 4; + stored_checksum = GET_32BIT(ucp); + if (checksum != stored_checksum) + packet_disconnect("Corrupted check bytes on input."); + buffer_consume_end(&incoming_packet, 4); + + /* If using packet compression, decompress the packet. */ + if (packet_compression) { + buffer_clear(&compression_buffer); + buffer_uncompress(&incoming_packet, &compression_buffer); + buffer_clear(&incoming_packet); + buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), + buffer_len(&compression_buffer)); + } + /* Get packet type. */ + buffer_get(&incoming_packet, &buf[0], 1); + + /* Return length of payload (without type field). */ + *payload_len_ptr = buffer_len(&incoming_packet); + + /* Handle disconnect message. */ + if ((unsigned char) buf[0] == SSH_MSG_DISCONNECT) + fatal("Received disconnect: %.900s", packet_get_string(NULL)); + + /* Ignore ignore messages. */ + if ((unsigned char) buf[0] == SSH_MSG_IGNORE) + goto restart; + + /* Send debug messages as debugging output. */ + if ((unsigned char) buf[0] == SSH_MSG_DEBUG) { + debug("Remote: %.900s", packet_get_string(NULL)); + goto restart; + } + /* Return type. */ + return (unsigned char) buf[0]; +} + /* Buffers the given amount of input characters. This is intended to be used together with packet_read_poll. */ void packet_process_incoming(const char *buf, unsigned int len) { - buffer_append(&input, buf, len); + buffer_append(&input, buf, len); } /* Returns a character from the packet. */ @@ -544,9 +530,9 @@ packet_process_incoming(const char *buf, unsigned int len) unsigned int packet_get_char() { - char ch; - buffer_get(&incoming_packet, &ch, 1); - return (unsigned char)ch; + char ch; + buffer_get(&incoming_packet, &ch, 1); + return (unsigned char) ch; } /* Returns an integer from the packet data. */ @@ -554,16 +540,16 @@ packet_get_char() unsigned int packet_get_int() { - return buffer_get_int(&incoming_packet); + return buffer_get_int(&incoming_packet); } /* Returns an arbitrary precision integer from the packet data. The integer must have been initialized before this call. */ void -packet_get_bignum(BIGNUM *value, int *length_ptr) +packet_get_bignum(BIGNUM * value, int *length_ptr) { - *length_ptr = buffer_get_bignum(&incoming_packet, value); + *length_ptr = buffer_get_bignum(&incoming_packet, value); } /* Returns a string from the packet data. The string is allocated using @@ -572,9 +558,10 @@ packet_get_bignum(BIGNUM *value, int *length_ptr) integer into which the length of the string is stored. */ char -*packet_get_string(unsigned int *length_ptr) +* +packet_get_string(unsigned int *length_ptr) { - return buffer_get_string(&incoming_packet, length_ptr); + return buffer_get_string(&incoming_packet, length_ptr); } /* Sends a diagnostic message from the server to the client. This message @@ -586,19 +573,19 @@ char packet_write_wait. */ void -packet_send_debug(const char *fmt, ...) +packet_send_debug(const char *fmt,...) { - char buf[1024]; - va_list args; - - va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - - packet_start(SSH_MSG_DEBUG); - packet_put_string(buf, strlen(buf)); - packet_send(); - packet_write_wait(); + char buf[1024]; + va_list args; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + packet_start(SSH_MSG_DEBUG); + packet_put_string(buf, strlen(buf)); + packet_send(); + packet_write_wait(); } /* Logs the error plus constructs and sends a disconnect @@ -607,36 +594,35 @@ packet_send_debug(const char *fmt, ...) formatted message must not exceed 1024 bytes. */ void -packet_disconnect(const char *fmt, ...) -{ - char buf[1024]; - va_list args; - static int disconnecting = 0; - if (disconnecting) /* Guard against recursive invocations. */ - fatal("packet_disconnect called recursively."); - disconnecting = 1; - - /* Format the message. Note that the caller must make sure the message - is of limited size. */ - va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - - /* Send the disconnect message to the other side, and wait for it to get - sent. */ - packet_start(SSH_MSG_DISCONNECT); - packet_put_string(buf, strlen(buf)); - packet_send(); - packet_write_wait(); - - /* Stop listening for connections. */ - channel_stop_listening(); - - /* Close the connection. */ - packet_close(); - - /* Display the error locally and exit. */ - fatal("Local: %.100s", buf); +packet_disconnect(const char *fmt,...) +{ + char buf[1024]; + va_list args; + static int disconnecting = 0; + if (disconnecting) /* Guard against recursive invocations. */ + fatal("packet_disconnect called recursively."); + disconnecting = 1; + + /* Format the message. Note that the caller must make sure the + message is of limited size. */ + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + /* Send the disconnect message to the other side, and wait for it to get sent. */ + packet_start(SSH_MSG_DISCONNECT); + packet_put_string(buf, strlen(buf)); + packet_send(); + packet_write_wait(); + + /* Stop listening for connections. */ + channel_stop_listening(); + + /* Close the connection. */ + packet_close(); + + /* Display the error locally and exit. */ + fatal("Disconnecting: %.100s", buf); } /* Checks if there is any buffered output, and tries to write some of the @@ -645,18 +631,17 @@ packet_disconnect(const char *fmt, ...) void packet_write_poll() { - int len = buffer_len(&output); - if (len > 0) - { - len = write(connection_out, buffer_ptr(&output), len); - if (len <= 0) { - if (errno == EAGAIN) - return; - else - fatal("Write failed: %.100s", strerror(errno)); - } - buffer_consume(&output, len); - } + int len = buffer_len(&output); + if (len > 0) { + len = write(connection_out, buffer_ptr(&output), len); + if (len <= 0) { + if (errno == EAGAIN) + return; + else + fatal("Write failed: %.100s", strerror(errno)); + } + buffer_consume(&output, len); + } } /* Calls packet_write_poll repeatedly until all pending output data has @@ -665,15 +650,14 @@ packet_write_poll() void packet_write_wait() { - packet_write_poll(); - while (packet_have_data_to_write()) - { - fd_set set; - FD_ZERO(&set); - FD_SET(connection_out, &set); - select(connection_out + 1, NULL, &set, NULL, NULL); - packet_write_poll(); - } + packet_write_poll(); + while (packet_have_data_to_write()) { + fd_set set; + FD_ZERO(&set); + FD_SET(connection_out, &set); + select(connection_out + 1, NULL, &set, NULL, NULL); + packet_write_poll(); + } } /* Returns true if there is buffered data to write to the connection. */ @@ -681,7 +665,7 @@ packet_write_wait() int packet_have_data_to_write() { - return buffer_len(&output) != 0; + return buffer_len(&output) != 0; } /* Returns true if there is not too much data to write to the connection. */ @@ -689,10 +673,10 @@ packet_have_data_to_write() int packet_not_very_much_data_to_write() { - if (interactive_mode) - return buffer_len(&output) < 16384; - else - return buffer_len(&output) < 128*1024; + if (interactive_mode) + return buffer_len(&output) < 16384; + else + return buffer_len(&output) < 128 * 1024; } /* Informs that the current session is interactive. Sets IP flags for that. */ @@ -700,45 +684,40 @@ packet_not_very_much_data_to_write() void packet_set_interactive(int interactive, int keepalives) { - int on = 1; - - /* Record that we are in interactive mode. */ - interactive_mode = interactive; - - /* Only set socket options if using a socket (as indicated by the descriptors - being the same). */ - if (connection_in != connection_out) - return; - - if (keepalives) - { - /* Set keepalives if requested. */ - if (setsockopt(connection_in, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, - sizeof(on)) < 0) - error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); - } - - if (interactive) - { - /* Set IP options for an interactive connection. Use IPTOS_LOWDELAY - and TCP_NODELAY. */ - int lowdelay = IPTOS_LOWDELAY; - if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *)&lowdelay, - sizeof(lowdelay)) < 0) - error("setsockopt IPTOS_LOWDELAY: %.100s", strerror(errno)); - if (setsockopt(connection_in, IPPROTO_TCP, TCP_NODELAY, (void *)&on, - sizeof(on)) < 0) - error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); - } - else - { - /* Set IP options for a non-interactive connection. Use - IPTOS_THROUGHPUT. */ - int throughput = IPTOS_THROUGHPUT; - if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *)&throughput, - sizeof(throughput)) < 0) - error("setsockopt IPTOS_THROUGHPUT: %.100s", strerror(errno)); - } + int on = 1; + + /* Record that we are in interactive mode. */ + interactive_mode = interactive; + + /* Only set socket options if using a socket (as indicated by the + descriptors being the same). */ + if (connection_in != connection_out) + return; + + if (keepalives) { + /* Set keepalives if requested. */ + if (setsockopt(connection_in, SOL_SOCKET, SO_KEEPALIVE, (void *) &on, + sizeof(on)) < 0) + error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); + } + if (interactive) { + /* Set IP options for an interactive connection. Use + IPTOS_LOWDELAY and TCP_NODELAY. */ + int lowdelay = IPTOS_LOWDELAY; + if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *) &lowdelay, + sizeof(lowdelay)) < 0) + error("setsockopt IPTOS_LOWDELAY: %.100s", strerror(errno)); + if (setsockopt(connection_in, IPPROTO_TCP, TCP_NODELAY, (void *) &on, + sizeof(on)) < 0) + error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); + } else { + /* Set IP options for a non-interactive connection. Use + IPTOS_THROUGHPUT. */ + int throughput = IPTOS_THROUGHPUT; + if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *) &throughput, + sizeof(throughput)) < 0) + error("setsockopt IPTOS_THROUGHPUT: %.100s", strerror(errno)); + } } /* Returns true if the current connection is interactive. */ @@ -746,22 +725,22 @@ packet_set_interactive(int interactive, int keepalives) int packet_is_interactive() { - return interactive_mode; + return interactive_mode; } int packet_set_maxsize(int s) { - static int called = 0; - if (called) { - log("packet_set_maxsize: called twice: old %d new %d", max_packet_size, s); - return -1; - } - if (s < 4*1024 || s > 1024*1024) { - log("packet_set_maxsize: bad size %d", s); - return -1; - } - log("packet_set_maxsize: setting to %d", s); - max_packet_size = s; - return s; + static int called = 0; + if (called) { + log("packet_set_maxsize: called twice: old %d new %d", max_packet_size, s); + return -1; + } + if (s < 4 * 1024 || s > 1024 * 1024) { + log("packet_set_maxsize: bad size %d", s); + return -1; + } + log("packet_set_maxsize: setting to %d", s); + max_packet_size = s; + return s; } diff --git a/packet.h b/packet.h index d542e9ae..a15f6880 100644 --- a/packet.h +++ b/packet.h @@ -1,17 +1,17 @@ /* - -packet.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sat Mar 18 02:02:14 1995 ylo - -Interface for the packet protocol functions. - -*/ + * + * packet.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Sat Mar 18 02:02:14 1995 ylo + * + * Interface for the packet protocol functions. + * + */ /* RCSID("$Id$"); */ @@ -31,83 +31,84 @@ Interface for the packet protocol functions. packet_set_encryption_key is called. It is permissible that fd_in and fd_out are the same descriptor; in that case it is assumed to be a socket. */ -void packet_set_connection(int fd_in, int fd_out); +void packet_set_connection(int fd_in, int fd_out); /* Puts the connection file descriptors into non-blocking mode. */ -void packet_set_nonblocking(void); +void packet_set_nonblocking(void); /* Returns the file descriptor used for input. */ -int packet_get_connection_in(void); +int packet_get_connection_in(void); /* Returns the file descriptor used for output. */ -int packet_get_connection_out(void); +int packet_get_connection_out(void); /* Closes the connection (both descriptors) and clears and frees - internal data structures. */ -void packet_close(void); + internal data structures. */ +void packet_close(void); /* Causes any further packets to be encrypted using the given key. The same key is used for both sending and reception. However, both directions are encrypted independently of each other. Cipher types are defined in ssh.h. */ -void packet_set_encryption_key(const unsigned char *key, unsigned int keylen, - int cipher_type); +void +packet_set_encryption_key(const unsigned char *key, unsigned int keylen, + int cipher_type); /* Sets remote side protocol flags for the current connection. This can be called at any time. */ -void packet_set_protocol_flags(unsigned int flags); +void packet_set_protocol_flags(unsigned int flags); /* Returns the remote protocol flags set earlier by the above function. */ unsigned int packet_get_protocol_flags(void); /* Enables compression in both directions starting from the next packet. */ -void packet_start_compression(int level); +void packet_start_compression(int level); /* Informs that the current session is interactive. Sets IP flags for optimal performance in interactive use. */ -void packet_set_interactive(int interactive, int keepalives); +void packet_set_interactive(int interactive, int keepalives); /* Returns true if the current connection is interactive. */ -int packet_is_interactive(void); +int packet_is_interactive(void); /* Starts constructing a packet to send. */ -void packet_start(int type); +void packet_start(int type); /* Appends a character to the packet data. */ -void packet_put_char(int ch); +void packet_put_char(int ch); /* Appends an integer to the packet data. */ -void packet_put_int(unsigned int value); +void packet_put_int(unsigned int value); /* Appends an arbitrary precision integer to packet data. */ -void packet_put_bignum(BIGNUM *value); +void packet_put_bignum(BIGNUM * value); /* Appends a string to packet data. */ -void packet_put_string(const char *buf, unsigned int len); +void packet_put_string(const char *buf, unsigned int len); /* Finalizes and sends the packet. If the encryption key has been set, encrypts the packet before sending. */ -void packet_send(void); +void packet_send(void); /* Waits until a packet has been received, and returns its type. */ -int packet_read(int *payload_len_ptr); +int packet_read(int *payload_len_ptr); /* Waits until a packet has been received, verifies that its type matches that given, and gives a fatal error and exits if there is a mismatch. */ -void packet_read_expect(int *payload_len_ptr, int type); +void packet_read_expect(int *payload_len_ptr, int type); /* Checks if a full packet is available in the data received so far via packet_process_incoming. If so, reads the packet; otherwise returns - SSH_MSG_NONE. This does not wait for data from the connection. - + SSH_MSG_NONE. This does not wait for data from the connection. + SSH_MSG_DISCONNECT is handled specially here. Also, SSH_MSG_IGNORE messages are skipped by this function and are never returned to higher levels. */ -int packet_read_poll(int *packet_len_ptr); +int packet_read_poll(int *packet_len_ptr); /* Buffers the given amount of input characters. This is intended to be used together with packet_read_poll. */ -void packet_process_incoming(const char *buf, unsigned int len); +void packet_process_incoming(const char *buf, unsigned int len); /* Returns a character (0-255) from the packet data. */ unsigned int packet_get_char(void); @@ -117,19 +118,19 @@ unsigned int packet_get_int(void); /* Returns an arbitrary precision integer from the packet data. The integer must have been initialized before this call. */ -void packet_get_bignum(BIGNUM *value, int *length_ptr); +void packet_get_bignum(BIGNUM * value, int *length_ptr); /* Returns a string from the packet data. The string is allocated using xmalloc; it is the responsibility of the calling program to free it when no longer needed. The length_ptr argument may be NULL, or point to an integer into which the length of the string is stored. */ -char *packet_get_string(unsigned int *length_ptr); +char *packet_get_string(unsigned int *length_ptr); /* Logs the error in syslog using LOG_INFO, constructs and sends a disconnect packet, closes the connection, and exits. This function never returns. The error message should not contain a newline. The total length of the message must not exceed 1024 bytes. */ -void packet_disconnect(const char *fmt, ...); +void packet_disconnect(const char *fmt,...); /* Sends a diagnostic message to the other side. This message can be sent at any time (but not while constructing another message). @@ -139,31 +140,31 @@ void packet_disconnect(const char *fmt, ...); must not exceed 1024 bytes. This will automatically call packet_write_wait. If the remote side protocol flags do not indicate that it supports SSH_MSG_DEBUG, this will do nothing. */ -void packet_send_debug(const char *fmt, ...); +void packet_send_debug(const char *fmt,...); /* Checks if there is any buffered output, and tries to write some of the output. */ -void packet_write_poll(void); +void packet_write_poll(void); /* Waits until all pending output data has been written. */ -void packet_write_wait(void); +void packet_write_wait(void); /* Returns true if there is buffered data to write to the connection. */ -int packet_have_data_to_write(void); +int packet_have_data_to_write(void); /* Returns true if there is not too much data to write to the connection. */ -int packet_not_very_much_data_to_write(void); +int packet_not_very_much_data_to_write(void); /* maximum packet size, requested by client with SSH_CMSG_MAX_PACKET_SIZE */ extern int max_packet_size; -int packet_set_maxsize(int s); +int packet_set_maxsize(int s); #define packet_get_maxsize() max_packet_size /* Stores tty modes from the fd into current packet. */ -void tty_make_modes(int fd); +void tty_make_modes(int fd); /* Parses tty modes for the fd from the current packet. */ -void tty_parse_modes(int fd, int *n_bytes_ptr); +void tty_parse_modes(int fd, int *n_bytes_ptr); #define packet_integrity_check(payload_len, expected_len, type) \ do { \ @@ -175,4 +176,4 @@ do { \ } \ } while (0) -#endif /* PACKET_H */ +#endif /* PACKET_H */ diff --git a/pty.c b/pty.c index ad9c6d55..12cd9bd9 100644 --- a/pty.c +++ b/pty.c @@ -1,28 +1,28 @@ /* - -pty.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Fri Mar 17 04:37:25 1995 ylo - -Allocating a pseudo-terminal, and making it the controlling tty. - -*/ + * + * pty.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Fri Mar 17 04:37:25 1995 ylo + * + * Allocating a pseudo-terminal, and making it the controlling tty. + * + */ #include "includes.h" RCSID("$Id$"); +#include "pty.h" +#include "ssh.h" + #ifdef HAVE_PTY_H #include #endif /* HAVE_PTY_H */ -#include "pty.h" -#include "ssh.h" - /* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */ #if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY) #undef HAVE_DEV_PTMX @@ -34,235 +34,211 @@ RCSID("$Id$"); /* Allocates and opens a pty. Returns 0 if no pty could be allocated, or nonzero if a pty was successfully allocated. On success, open file - descriptors for the pty and tty sides and the name of the tty side are + descriptors for the pty and tty sides and the name of the tty side are returned (the buffer must be able to hold at least 64 characters). */ -int pty_allocate(int *ptyfd, int *ttyfd, char *namebuf) +int +pty_allocate(int *ptyfd, int *ttyfd, char *namebuf) { #ifdef HAVE_OPENPTY + /* openpty(3) exists in OSF/1 and some other os'es */ + int i; - /* openpty(3) exists in OSF/1 and some other os'es */ - - int i; - - i = openpty(ptyfd, ttyfd, namebuf, NULL, NULL); - - if (i < 0) - { - error("openpty: %.100s", strerror(errno)); - return 0; - } - - return 1; - + i = openpty(ptyfd, ttyfd, namebuf, NULL, NULL); + if (i < 0) { + error("openpty: %.100s", strerror(errno)); + return 0; + } + return 1; #else /* HAVE_OPENPTY */ #ifdef HAVE__GETPTY - - /* _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more - pty's automagically when needed */ - - char *slave; - - slave = _getpty(ptyfd, O_RDWR, 0622, 0); - if (slave == NULL) - { - error("_getpty: %.100s", strerror(errno)); - return 0; - } - strcpy(namebuf, slave); - /* Open the slave side. */ - *ttyfd = open(namebuf, O_RDWR|O_NOCTTY); - if (*ttyfd < 0) - { - error("%.200s: %.100s", namebuf, strerror(errno)); - close(*ptyfd); - return 0; - } - return 1; - + /* _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates + more pty's automagically when needed */ + char *slave; + + slave = _getpty(ptyfd, O_RDWR, 0622, 0); + if (slave == NULL) { + error("_getpty: %.100s", strerror(errno)); + return 0; + } + strcpy(namebuf, slave); + /* Open the slave side. */ + *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); + if (*ttyfd < 0) { + error("%.200s: %.100s", namebuf, strerror(errno)); + close(*ptyfd); + return 0; + } + return 1; #else /* HAVE__GETPTY */ #ifdef HAVE_DEV_PTMX - /* This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 also has - bsd-style ptys, but they simply do not work.) */ - - int ptm; - char *pts; - - ptm = open("/dev/ptmx", O_RDWR|O_NOCTTY); - if (ptm < 0) - { - error("/dev/ptmx: %.100s", strerror(errno)); - return 0; - } - if (grantpt(ptm) < 0) - { - error("grantpt: %.100s", strerror(errno)); - return 0; - } - if (unlockpt(ptm) < 0) - { - error("unlockpt: %.100s", strerror(errno)); - return 0; - } - pts = ptsname(ptm); - if (pts == NULL) - error("Slave pty side name could not be obtained."); - strcpy(namebuf, pts); - *ptyfd = ptm; - - /* Open the slave side. */ - *ttyfd = open(namebuf, O_RDWR|O_NOCTTY); - if (*ttyfd < 0) - { - error("%.100s: %.100s", namebuf, strerror(errno)); - close(*ptyfd); - return 0; - } - /* Push the appropriate streams modules, as described in Solaris pts(7). */ - if (ioctl(*ttyfd, I_PUSH, "ptem") < 0) - error("ioctl I_PUSH ptem: %.100s", strerror(errno)); - if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0) - error("ioctl I_PUSH ldterm: %.100s", strerror(errno)); - if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0) - error("ioctl I_PUSH ttcompat: %.100s", strerror(errno)); - return 1; - + /* This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 + also has bsd-style ptys, but they simply do not work.) */ + int ptm; + char *pts; + + ptm = open("/dev/ptmx", O_RDWR | O_NOCTTY); + if (ptm < 0) { + error("/dev/ptmx: %.100s", strerror(errno)); + return 0; + } + if (grantpt(ptm) < 0) { + error("grantpt: %.100s", strerror(errno)); + return 0; + } + if (unlockpt(ptm) < 0) { + error("unlockpt: %.100s", strerror(errno)); + return 0; + } + pts = ptsname(ptm); + if (pts == NULL) + error("Slave pty side name could not be obtained."); + strcpy(namebuf, pts); + *ptyfd = ptm; + + /* Open the slave side. */ + *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); + if (*ttyfd < 0) { + error("%.100s: %.100s", namebuf, strerror(errno)); + close(*ptyfd); + return 0; + } + /* Push the appropriate streams modules, as described in Solaris + pts(7). */ + if (ioctl(*ttyfd, I_PUSH, "ptem") < 0) + error("ioctl I_PUSH ptem: %.100s", strerror(errno)); + if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0) + error("ioctl I_PUSH ldterm: %.100s", strerror(errno)); + if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0) + error("ioctl I_PUSH ttcompat: %.100s", strerror(errno)); + return 1; #else /* HAVE_DEV_PTMX */ #ifdef HAVE_DEV_PTS_AND_PTC + /* AIX-style pty code. */ + const char *name; - /* AIX-style pty code. */ - - const char *name; - - *ptyfd = open("/dev/ptc", O_RDWR|O_NOCTTY); - if (*ptyfd < 0) - { - error("Could not open /dev/ptc: %.100s", strerror(errno)); - return 0; - } - name = ttyname(*ptyfd); - if (!name) - fatal("Open of /dev/ptc returns device for which ttyname fails."); - strcpy(namebuf, name); - *ttyfd = open(name, O_RDWR|O_NOCTTY); - if (*ttyfd < 0) - { - error("Could not open pty slave side %.100s: %.100s", - name, strerror(errno)); - close(*ptyfd); - return 0; - } - return 1; - + *ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY); + if (*ptyfd < 0) { + error("Could not open /dev/ptc: %.100s", strerror(errno)); + return 0; + } + name = ttyname(*ptyfd); + if (!name) + fatal("Open of /dev/ptc returns device for which ttyname fails."); + strcpy(namebuf, name); + *ttyfd = open(name, O_RDWR | O_NOCTTY); + if (*ttyfd < 0) { + error("Could not open pty slave side %.100s: %.100s", + name, strerror(errno)); + close(*ptyfd); + return 0; + } + return 1; #else /* HAVE_DEV_PTS_AND_PTC */ - /* BSD-style pty code. */ - - char buf[64]; - int i; - const char *ptymajors = - "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const char *ptyminors = "0123456789abcdef"; - int num_minors = strlen(ptyminors); - int num_ptys = strlen(ptymajors) * num_minors; - - for (i = 0; i < num_ptys; i++) - { - snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors], - ptyminors[i % num_minors]); - *ptyfd = open(buf, O_RDWR|O_NOCTTY); - if (*ptyfd < 0) - continue; - snprintf(namebuf, sizeof buf, "/dev/tty%c%c", ptymajors[i / num_minors], - ptyminors[i % num_minors]); - - /* Open the slave side. */ - *ttyfd = open(namebuf, O_RDWR|O_NOCTTY); - if (*ttyfd < 0) - { - error("%.100s: %.100s", namebuf, strerror(errno)); - close(*ptyfd); - return 0; + /* BSD-style pty code. */ + char buf[64]; + int i; + const char *ptymajors = + "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const char *ptyminors = "0123456789abcdef"; + int num_minors = strlen(ptyminors); + int num_ptys = strlen(ptymajors) * num_minors; + + for (i = 0; i < num_ptys; i++) { + snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors], + ptyminors[i % num_minors]); + *ptyfd = open(buf, O_RDWR | O_NOCTTY); + if (*ptyfd < 0) + continue; + snprintf(namebuf, sizeof buf, "/dev/tty%c%c", ptymajors[i / num_minors], + ptyminors[i % num_minors]); + + /* Open the slave side. */ + *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); + if (*ttyfd < 0) { + error("%.100s: %.100s", namebuf, strerror(errno)); + close(*ptyfd); + return 0; + } + return 1; } - return 1; - } - return 0; + return 0; #endif /* HAVE_DEV_PTS_AND_PTC */ #endif /* HAVE_DEV_PTMX */ #endif /* HAVE__GETPTY */ #endif /* HAVE_OPENPTY */ } -/* Releases the tty. Its ownership is returned to root, and permissions to - 0666. */ +/* Releases the tty. Its ownership is returned to root, and permissions to 0666. */ -void pty_release(const char *ttyname) +void +pty_release(const char *ttyname) { - if (chown(ttyname, (uid_t)0, (gid_t)0) < 0) - debug("chown %.100s 0 0 failed: %.100s", ttyname, strerror(errno)); - if (chmod(ttyname, (mode_t)0666) < 0) - debug("chmod %.100s 0666 failed: %.100s", ttyname, strerror(errno)); + if (chown(ttyname, (uid_t) 0, (gid_t) 0) < 0) + debug("chown %.100s 0 0 failed: %.100s", ttyname, strerror(errno)); + if (chmod(ttyname, (mode_t) 0666) < 0) + debug("chmod %.100s 0666 failed: %.100s", ttyname, strerror(errno)); } /* Makes the tty the processes controlling tty and sets it to sane modes. */ -void pty_make_controlling_tty(int *ttyfd, const char *ttyname) +void +pty_make_controlling_tty(int *ttyfd, const char *ttyname) { - int fd; + int fd; - /* First disconnect from the old controlling tty. */ + /* First disconnect from the old controlling tty. */ #ifdef TIOCNOTTY - fd = open("/dev/tty", O_RDWR|O_NOCTTY); - if (fd >= 0) - { - (void)ioctl(fd, TIOCNOTTY, NULL); - close(fd); - } + fd = open("/dev/tty", O_RDWR | O_NOCTTY); + if (fd >= 0) { + (void) ioctl(fd, TIOCNOTTY, NULL); + close(fd); + } #endif /* TIOCNOTTY */ - if (setsid() < 0) - error("setsid: %.100s", strerror(errno)); - - /* Verify that we are successfully disconnected from the controlling tty. */ - fd = open("/dev/tty", O_RDWR|O_NOCTTY); - if (fd >= 0) - { - error("Failed to disconnect from controlling tty."); - close(fd); - } - - /* Make it our controlling tty. */ + if (setsid() < 0) + error("setsid: %.100s", strerror(errno)); + + /* Verify that we are successfully disconnected from the + controlling tty. */ + fd = open("/dev/tty", O_RDWR | O_NOCTTY); + if (fd >= 0) { + error("Failed to disconnect from controlling tty."); + close(fd); + } + /* Make it our controlling tty. */ #ifdef TIOCSCTTY - debug("Setting controlling tty using TIOCSCTTY."); - /* We ignore errors from this, because HPSUX defines TIOCSCTTY, but returns - EINVAL with these arguments, and there is absolutely no documentation. */ - ioctl(*ttyfd, TIOCSCTTY, NULL); + debug("Setting controlling tty using TIOCSCTTY."); + /* We ignore errors from this, because HPSUX defines TIOCSCTTY, + but returns EINVAL with these arguments, and there is + absolutely no documentation. */ + ioctl(*ttyfd, TIOCSCTTY, NULL); #endif /* TIOCSCTTY */ - fd = open(ttyname, O_RDWR); - if (fd < 0) - error("%.100s: %.100s", ttyname, strerror(errno)); - else - close(fd); - - /* Verify that we now have a controlling tty. */ - fd = open("/dev/tty", O_WRONLY); - if (fd < 0) - error("open /dev/tty failed - could not set controlling tty: %.100s", - strerror(errno)); - else - { - close(fd); - } + fd = open(ttyname, O_RDWR); + if (fd < 0) + error("%.100s: %.100s", ttyname, strerror(errno)); + else + close(fd); + + /* Verify that we now have a controlling tty. */ + fd = open("/dev/tty", O_WRONLY); + if (fd < 0) + error("open /dev/tty failed - could not set controlling tty: %.100s", + strerror(errno)); + else { + close(fd); + } } /* Changes the window size associated with the pty. */ -void pty_change_window_size(int ptyfd, int row, int col, - int xpixel, int ypixel) +void +pty_change_window_size(int ptyfd, int row, int col, + int xpixel, int ypixel) { - struct winsize w; - w.ws_row = row; - w.ws_col = col; - w.ws_xpixel = xpixel; - w.ws_ypixel = ypixel; - (void)ioctl(ptyfd, TIOCSWINSZ, &w); + struct winsize w; + w.ws_row = row; + w.ws_col = col; + w.ws_xpixel = xpixel; + w.ws_ypixel = ypixel; + (void) ioctl(ptyfd, TIOCSWINSZ, &w); } - diff --git a/pty.h b/pty.h index c6750581..1ade7174 100644 --- a/pty.h +++ b/pty.h @@ -1,18 +1,17 @@ /* - -pty.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Fri Mar 17 05:03:28 1995 ylo - -Functions for allocating a pseudo-terminal and making it the controlling -tty. - -*/ + * + * pty.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Fri Mar 17 05:03:28 1995 ylo + * + * Functions for allocating a pseudo-terminal and making it the controlling + * tty. + */ /* RCSID("$Id$"); */ @@ -21,20 +20,21 @@ tty. /* Allocates and opens a pty. Returns 0 if no pty could be allocated, or nonzero if a pty was successfully allocated. On success, open file - descriptors for the pty and tty sides and the name of the tty side are + descriptors for the pty and tty sides and the name of the tty side are returned (the buffer must be able to hold at least 64 characters). */ -int pty_allocate(int *ptyfd, int *ttyfd, char *ttyname); +int pty_allocate(int *ptyfd, int *ttyfd, char *ttyname); /* Releases the tty. Its ownership is returned to root, and permissions to 0666. */ -void pty_release(const char *ttyname); +void pty_release(const char *ttyname); -/* Makes the tty the processes controlling tty and sets it to sane modes. +/* Makes the tty the processes controlling tty and sets it to sane modes. This may need to reopen the tty to get rid of possible eavesdroppers. */ -void pty_make_controlling_tty(int *ttyfd, const char *ttyname); +void pty_make_controlling_tty(int *ttyfd, const char *ttyname); /* Changes the window size associated with the pty. */ -void pty_change_window_size(int ptyfd, int row, int col, - int xpixel, int ypixel); +void +pty_change_window_size(int ptyfd, int row, int col, + int xpixel, int ypixel); -#endif /* PTY_H */ +#endif /* PTY_H */ diff --git a/radix.c b/radix.c index 1c497945..6637b2fb 100644 --- a/radix.c +++ b/radix.c @@ -1,101 +1,105 @@ /* - radix.c + * radix.c + * + * base-64 encoding pinched from lynx2-7-2, who pinched it from rpem. + * Originally written by Mark Riordan 12 August 1990 and 17 Feb 1991 + * and placed in the public domain. + * + * Dug Song + */ - base-64 encoding pinched from lynx2-7-2, who pinched it from rpem. - Originally written by Mark Riordan 12 August 1990 and 17 Feb 1991 - and placed in the public domain. - - Dug Song -*/ - #include "includes.h" #ifdef AFS #include char six2pr[64] = { - 'A','B','C','D','E','F','G','H','I','J','K','L','M', - 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', - 'a','b','c','d','e','f','g','h','i','j','k','l','m', - 'n','o','p','q','r','s','t','u','v','w','x','y','z', - '0','1','2','3','4','5','6','7','8','9','+','/' + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; unsigned char pr2six[256]; -int uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded) +int +uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded) { - /* ENC is the basic 1 character encoding function to make a char printing */ + /* ENC is the basic 1 character encoding function to make a char printing */ #define ENC(c) six2pr[c] - - register char *outptr = bufcoded; - unsigned int i; - - for (i=0; i> 2); /* c1 */ - *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /*c2*/ - *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03));/*c3*/ - *(outptr++) = ENC(bufin[2] & 077); /* c4 */ - bufin += 3; - } - if (i == nbytes+1) { - outptr[-1] = '='; - } else if (i == nbytes+2) { - outptr[-1] = '='; - outptr[-2] = '='; - } - *outptr = '\0'; - return(outptr - bufcoded); + + register char *outptr = bufcoded; + unsigned int i; + + for (i = 0; i < nbytes; i += 3) { + *(outptr++) = ENC(*bufin >> 2); /* c1 */ + *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /* c2 */ + *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)); /* c3 */ + *(outptr++) = ENC(bufin[2] & 077); /* c4 */ + bufin += 3; + } + if (i == nbytes + 1) { + outptr[-1] = '='; + } else if (i == nbytes + 2) { + outptr[-1] = '='; + outptr[-2] = '='; + } + *outptr = '\0'; + return (outptr - bufcoded); } -int uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize) +int +uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize) { - /* single character decode */ + /* single character decode */ #define DEC(c) pr2six[(unsigned char)c] #define MAXVAL 63 - - static int first = 1; - int nbytesdecoded, j; - const char *bufin = bufcoded; - register unsigned char *bufout = bufplain; - register int nprbytes; - - /* If this is the first call, initialize the mapping table. */ - if (first) { - first = 0; - for(j=0; j<256; j++) pr2six[j] = MAXVAL+1; - for(j=0; j<64; j++) pr2six[(unsigned char)six2pr[j]] = (unsigned char)j; - } - - /* Strip leading whitespace. */ - while (*bufcoded==' ' || *bufcoded == '\t') bufcoded++; - - /* Figure out how many characters are in the input buffer. - If this would decode into more bytes than would fit into - the output buffer, adjust the number of input bytes downwards. */ - bufin = bufcoded; - while (DEC(*(bufin++)) <= MAXVAL); - nprbytes = bufin - bufcoded - 1; - nbytesdecoded = ((nprbytes+3)/4) * 3; - if (nbytesdecoded > outbufsize) - nprbytes = (outbufsize*4)/3; - - bufin = bufcoded; - - while (nprbytes > 0) { - *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4); - *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2); - *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3])); - bufin += 4; - nprbytes -= 4; - } - if (nprbytes & 03) { - if (DEC(bufin[-2]) > MAXVAL) - nbytesdecoded -= 2; - else - nbytesdecoded -= 1; - } - return(nbytesdecoded); + + static int first = 1; + int nbytesdecoded, j; + const char *bufin = bufcoded; + register unsigned char *bufout = bufplain; + register int nprbytes; + + /* If this is the first call, initialize the mapping table. */ + if (first) { + first = 0; + for (j = 0; j < 256; j++) + pr2six[j] = MAXVAL + 1; + for (j = 0; j < 64; j++) + pr2six[(unsigned char) six2pr[j]] = (unsigned char) j; + } + /* Strip leading whitespace. */ + while (*bufcoded == ' ' || *bufcoded == '\t') + bufcoded++; + + /* Figure out how many characters are in the input buffer. If this + would decode into more bytes than would fit into the output + buffer, adjust the number of input bytes downwards. */ + bufin = bufcoded; + while (DEC(*(bufin++)) <= MAXVAL); + nprbytes = bufin - bufcoded - 1; + nbytesdecoded = ((nprbytes + 3) / 4) * 3; + if (nbytesdecoded > outbufsize) + nprbytes = (outbufsize * 4) / 3; + + bufin = bufcoded; + + while (nprbytes > 0) { + *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4); + *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2); + *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3])); + bufin += 4; + nprbytes -= 4; + } + if (nprbytes & 03) { + if (DEC(bufin[-2]) > MAXVAL) + nbytesdecoded -= 2; + else + nbytesdecoded -= 1; + } + return (nbytesdecoded); } typedef unsigned char my_u_char; @@ -156,103 +160,124 @@ typedef unsigned short my_u_short; } -int creds_to_radix(CREDENTIALS *creds, unsigned char *buf) +int +creds_to_radix(CREDENTIALS *creds, unsigned char *buf) { - char *p, *s; - int len; - char temp[2048]; - - p = temp; - *p++ = 1; /* version */ - s = creds->service; while (*s) *p++ = *s++; *p++ = *s; - s = creds->instance; while (*s) *p++ = *s++; *p++ = *s; - s = creds->realm; while (*s) *p++ = *s++; *p++ = *s; - - s = creds->pname; while (*s) *p++ = *s++; *p++ = *s; - s = creds->pinst; while (*s) *p++ = *s++; *p++ = *s; - /* Null string to repeat the realm. */ - *p++ = '\0'; - - PUTLONG(creds->issue_date,p); - { - unsigned int endTime ; - endTime = (unsigned int)krb_life_to_time(creds->issue_date, - creds->lifetime); - PUTLONG(endTime,p); - } - - memcpy(p,&creds->session, sizeof(creds->session)); - p += sizeof(creds->session); - - PUTSHORT(creds->kvno,p); - PUTLONG(creds->ticket_st.length,p); - - memcpy(p,creds->ticket_st.dat, creds->ticket_st.length); - p += creds->ticket_st.length; - len = p - temp; - - return(uuencode(temp, len, buf)); + char *p, *s; + int len; + char temp[2048]; + + p = temp; + *p++ = 1; /* version */ + s = creds->service; + while (*s) + *p++ = *s++; + *p++ = *s; + s = creds->instance; + while (*s) + *p++ = *s++; + *p++ = *s; + s = creds->realm; + while (*s) + *p++ = *s++; + *p++ = *s; + + s = creds->pname; + while (*s) + *p++ = *s++; + *p++ = *s; + s = creds->pinst; + while (*s) + *p++ = *s++; + *p++ = *s; + /* Null string to repeat the realm. */ + *p++ = '\0'; + + PUTLONG(creds->issue_date, p); + { + unsigned int endTime; + endTime = (unsigned int) krb_life_to_time(creds->issue_date, + creds->lifetime); + PUTLONG(endTime, p); + } + + memcpy(p, &creds->session, sizeof(creds->session)); + p += sizeof(creds->session); + + PUTSHORT(creds->kvno, p); + PUTLONG(creds->ticket_st.length, p); + + memcpy(p, creds->ticket_st.dat, creds->ticket_st.length); + p += creds->ticket_st.length; + len = p - temp; + + return (uuencode(temp, len, buf)); } -int radix_to_creds(const char *buf, CREDENTIALS *creds) +int +radix_to_creds(const char *buf, CREDENTIALS *creds) { - char *p; - int len, tl; - char version; - char temp[2048]; - - if (!(len = uudecode(buf, temp, sizeof(temp)))) - return 0; - - p = temp; - - /* check version and length! */ - if (len < 1) return 0; - version = *p; p++; len--; - - GETSTRING(creds->service, p, len); - GETSTRING(creds->instance, p, len); - GETSTRING(creds->realm, p, len); - - GETSTRING(creds->pname, p, len); - GETSTRING(creds->pinst, p, len); - /* Ignore possibly different realm. */ - while (*p && len) p++, len--; - if (len == 0) return 0; - p++, len--; - - /* Enough space for remaining fixed-length parts? */ - if (len < (4 + 4 + sizeof(creds->session) + 2 + 4)) - return 0; - - GETLONG(creds->issue_date,p); - len -= 4; - { - unsigned int endTime; - GETLONG(endTime,p); - len -= 4; - creds->lifetime = krb_time_to_life(creds->issue_date, endTime); - } - - memcpy(&creds->session, p, sizeof(creds->session)); - p += sizeof(creds->session); - len -= sizeof(creds->session); - - GETSHORT(creds->kvno,p); - len -= 2; - GETLONG(creds->ticket_st.length,p); - len -= 4; - - tl = creds->ticket_st.length; - if (tl < 0 || tl > len || tl > sizeof(creds->ticket_st.dat)) - return 0; - - memcpy(creds->ticket_st.dat, p, tl); - p += tl; - len -= tl; - - return 1; -} + char *p; + int len, tl; + char version; + char temp[2048]; + + if (!(len = uudecode(buf, temp, sizeof(temp)))) + return 0; + + p = temp; + + /* check version and length! */ + if (len < 1) + return 0; + version = *p; + p++; + len--; + + GETSTRING(creds->service, p, len); + GETSTRING(creds->instance, p, len); + GETSTRING(creds->realm, p, len); + + GETSTRING(creds->pname, p, len); + GETSTRING(creds->pinst, p, len); + /* Ignore possibly different realm. */ + while (*p && len) + p++, len--; + if (len == 0) + return 0; + p++, len--; + /* Enough space for remaining fixed-length parts? */ + if (len < (4 + 4 + sizeof(creds->session) + 2 + 4)) + return 0; + + GETLONG(creds->issue_date, p); + len -= 4; + { + unsigned int endTime; + GETLONG(endTime, p); + len -= 4; + creds->lifetime = krb_time_to_life(creds->issue_date, endTime); + } + + memcpy(&creds->session, p, sizeof(creds->session)); + p += sizeof(creds->session); + len -= sizeof(creds->session); + + GETSHORT(creds->kvno, p); + len -= 2; + GETLONG(creds->ticket_st.length, p); + len -= 4; + + tl = creds->ticket_st.length; + if (tl < 0 || tl > len || tl > sizeof(creds->ticket_st.dat)) + return 0; + + memcpy(creds->ticket_st.dat, p, tl); + p += tl; + len -= tl; + + return 1; +} #endif /* AFS */ diff --git a/readconf.c b/readconf.c index 9e4bd5f9..f2387a31 100644 --- a/readconf.c +++ b/readconf.c @@ -1,17 +1,17 @@ /* - -readconf.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sat Apr 22 00:03:10 1995 ylo - -Functions for reading the configuration files. - -*/ + * + * readconf.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Sat Apr 22 00:03:10 1995 ylo + * + * Functions for reading the configuration files. + * + */ #include "includes.h" RCSID("$Id$"); @@ -86,73 +86,72 @@ RCSID("$Id$"); /* Keyword tokens. */ -typedef enum -{ - oBadOption, - oForwardAgent, oForwardX11, oGatewayPorts, oRhostsAuthentication, - oPasswordAuthentication, oRSAAuthentication, oFallBackToRsh, oUseRsh, +typedef enum { + oBadOption, + oForwardAgent, oForwardX11, oGatewayPorts, oRhostsAuthentication, + oPasswordAuthentication, oRSAAuthentication, oFallBackToRsh, oUseRsh, + oSkeyAuthentication, #ifdef KRB4 - oKerberosAuthentication, + oKerberosAuthentication, #endif /* KRB4 */ #ifdef AFS - oKerberosTgtPassing, oAFSTokenPassing, + oKerberosTgtPassing, oAFSTokenPassing, #endif - oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, - oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, - oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, - oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, - oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication, - oUsePrivilegedPort, oLogLevel + oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, + oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, + oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, + oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, + oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication, + oUsePrivilegedPort, oLogLevel } OpCodes; /* Textual representations of the tokens. */ -static struct -{ - const char *name; - OpCodes opcode; -} keywords[] = -{ - { "forwardagent", oForwardAgent }, - { "forwardx11", oForwardX11 }, - { "gatewayports", oGatewayPorts }, - { "useprivilegedport", oUsePrivilegedPort }, - { "rhostsauthentication", oRhostsAuthentication }, - { "passwordauthentication", oPasswordAuthentication }, - { "rsaauthentication", oRSAAuthentication }, +static struct { + const char *name; + OpCodes opcode; +} keywords[] = { + { "forwardagent", oForwardAgent }, + { "forwardx11", oForwardX11 }, + { "gatewayports", oGatewayPorts }, + { "useprivilegedport", oUsePrivilegedPort }, + { "rhostsauthentication", oRhostsAuthentication }, + { "passwordauthentication", oPasswordAuthentication }, + { "rsaauthentication", oRSAAuthentication }, + { "skeyauthentication", oSkeyAuthentication }, #ifdef KRB4 - { "kerberosauthentication", oKerberosAuthentication }, + { "kerberosauthentication", oKerberosAuthentication }, #endif /* KRB4 */ #ifdef AFS - { "kerberostgtpassing", oKerberosTgtPassing }, - { "afstokenpassing", oAFSTokenPassing }, + { "kerberostgtpassing", oKerberosTgtPassing }, + { "afstokenpassing", oAFSTokenPassing }, #endif - { "fallbacktorsh", oFallBackToRsh }, - { "usersh", oUseRsh }, - { "identityfile", oIdentityFile }, - { "hostname", oHostName }, - { "proxycommand", oProxyCommand }, - { "port", oPort }, - { "cipher", oCipher }, - { "remoteforward", oRemoteForward }, - { "localforward", oLocalForward }, - { "user", oUser }, - { "host", oHost }, - { "escapechar", oEscapeChar }, - { "rhostsrsaauthentication", oRhostsRSAAuthentication }, - { "globalknownhostsfile", oGlobalKnownHostsFile }, - { "userknownhostsfile", oUserKnownHostsFile }, - { "connectionattempts", oConnectionAttempts }, - { "batchmode", oBatchMode }, - { "checkhostip", oCheckHostIP }, - { "stricthostkeychecking", oStrictHostKeyChecking }, - { "compression", oCompression }, - { "compressionlevel", oCompressionLevel }, - { "keepalive", oKeepAlives }, - { "numberofpasswordprompts", oNumberOfPasswordPrompts }, - { "tisauthentication", oTISAuthentication }, - { "loglevel", oLogLevel }, - { NULL, 0 } + { "fallbacktorsh", oFallBackToRsh }, + { "usersh", oUseRsh }, + { "identityfile", oIdentityFile }, + { "hostname", oHostName }, + { "proxycommand", oProxyCommand }, + { "port", oPort }, + { "cipher", oCipher }, + { "remoteforward", oRemoteForward }, + { "localforward", oLocalForward }, + { "user", oUser }, + { "host", oHost }, + { "escapechar", oEscapeChar }, + { "rhostsrsaauthentication", oRhostsRSAAuthentication }, + { "globalknownhostsfile", oGlobalKnownHostsFile }, + { "userknownhostsfile", oUserKnownHostsFile }, + { "connectionattempts", oConnectionAttempts }, + { "batchmode", oBatchMode }, + { "checkhostip", oCheckHostIP }, + { "stricthostkeychecking", oStrictHostKeyChecking }, + { "compression", oCompression }, + { "compressionlevel", oCompressionLevel }, + { "keepalive", oKeepAlives }, + { "numberofpasswordprompts", oNumberOfPasswordPrompts }, + { "tisauthentication", oTISAuthentication }, + { "loglevel", oLogLevel }, + { NULL, 0 } }; /* Characters considered whitespace in strtok calls. */ @@ -162,53 +161,56 @@ static struct /* Adds a local TCP/IP port forward to options. Never returns if there is an error. */ -void add_local_forward(Options *options, int port, const char *host, - int host_port) +void +add_local_forward(Options *options, int port, const char *host, + int host_port) { - Forward *fwd; - extern uid_t original_real_uid; - if ((port & 0xffff) != port) - fatal("Requested forwarding of nonexistent port %d.", port); - if (port < IPPORT_RESERVED && original_real_uid != 0) - fatal("Privileged ports can only be forwarded by root.\n"); - if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) - fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION); - fwd = &options->local_forwards[options->num_local_forwards++]; - fwd->port = port; - fwd->host = xstrdup(host); - fwd->host_port = host_port; + Forward *fwd; + extern uid_t original_real_uid; + if ((port & 0xffff) != port) + fatal("Requested forwarding of nonexistent port %d.", port); + if (port < IPPORT_RESERVED && original_real_uid != 0) + fatal("Privileged ports can only be forwarded by root.\n"); + if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) + fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION); + fwd = &options->local_forwards[options->num_local_forwards++]; + fwd->port = port; + fwd->host = xstrdup(host); + fwd->host_port = host_port; } /* Adds a remote TCP/IP port forward to options. Never returns if there is an error. */ -void add_remote_forward(Options *options, int port, const char *host, - int host_port) +void +add_remote_forward(Options *options, int port, const char *host, + int host_port) { - Forward *fwd; - if (options->num_remote_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) - fatal("Too many remote forwards (max %d).", - SSH_MAX_FORWARDS_PER_DIRECTION); - fwd = &options->remote_forwards[options->num_remote_forwards++]; - fwd->port = port; - fwd->host = xstrdup(host); - fwd->host_port = host_port; + Forward *fwd; + if (options->num_remote_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) + fatal("Too many remote forwards (max %d).", + SSH_MAX_FORWARDS_PER_DIRECTION); + fwd = &options->remote_forwards[options->num_remote_forwards++]; + fwd->port = port; + fwd->host = xstrdup(host); + fwd->host_port = host_port; } /* Returns the number of the token pointed to by cp of length len. Never returns if the token is not known. */ -static OpCodes parse_token(const char *cp, const char *filename, int linenum) +static OpCodes +parse_token(const char *cp, const char *filename, int linenum) { - unsigned int i; + unsigned int i; - for (i = 0; keywords[i].name; i++) - if (strcmp(cp, keywords[i].name) == 0) - return keywords[i].opcode; + for (i = 0; keywords[i].name; i++) + if (strcmp(cp, keywords[i].name) == 0) + return keywords[i].opcode; - fprintf(stderr, "%s: line %d: Bad configuration option: %s\n", - filename, linenum, cp); - return oBadOption; + fprintf(stderr, "%s: line %d: Bad configuration option: %s\n", + filename, linenum, cp); + return oBadOption; } /* Processes a single option line as used in the configuration files. @@ -216,336 +218,329 @@ static OpCodes parse_token(const char *cp, const char *filename, int linenum) int process_config_line(Options *options, const char *host, - char *line, const char *filename, int linenum, - int *activep) + char *line, const char *filename, int linenum, + int *activep) { - char buf[256], *cp, *string, **charptr; - int opcode, *intptr, value, fwd_port, fwd_host_port; - - /* Skip leading whitespace. */ - cp = line + strspn(line, WHITESPACE); - if (!*cp || *cp == '\n' || *cp == '#') - return 0; - - /* Get the keyword. (Each line is supposed to begin with a keyword). */ - cp = strtok(cp, WHITESPACE); - { - char *t = cp; - for (; *t != 0; t++) - if ('A' <= *t && *t <= 'Z') - *t = *t - 'A' + 'a'; /* tolower */ - - } - opcode = parse_token(cp, filename, linenum); - - switch (opcode) - { - case oBadOption: - return -1; /* don't panic, but count bad options */ - /*NOTREACHED*/ - case oForwardAgent: - intptr = &options->forward_agent; - parse_flag: - cp = strtok(NULL, WHITESPACE); - if (!cp) - fatal("%.200s line %d: Missing yes/no argument.", filename, linenum); - value = 0; /* To avoid compiler warning... */ - if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0) - value = 1; - else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0) - value = 0; - else - fatal("%.200s line %d: Bad yes/no argument.", filename, linenum); - if (*activep && *intptr == -1) - *intptr = value; - break; - - case oForwardX11: - intptr = &options->forward_x11; - goto parse_flag; - - case oGatewayPorts: - intptr = &options->gateway_ports; - goto parse_flag; - - case oUsePrivilegedPort: - intptr = &options->use_privileged_port; - goto parse_flag; - - case oRhostsAuthentication: - intptr = &options->rhosts_authentication; - goto parse_flag; - - case oPasswordAuthentication: - intptr = &options->password_authentication; - goto parse_flag; - - case oRSAAuthentication: - intptr = &options->rsa_authentication; - goto parse_flag; - - case oRhostsRSAAuthentication: - intptr = &options->rhosts_rsa_authentication; - goto parse_flag; + char buf[256], *cp, *string, **charptr; + int opcode, *intptr, value, fwd_port, fwd_host_port; + + /* Skip leading whitespace. */ + cp = line + strspn(line, WHITESPACE); + if (!*cp || *cp == '\n' || *cp == '#') + return 0; + + /* Get the keyword. (Each line is supposed to begin with a + keyword). */ + cp = strtok(cp, WHITESPACE); + { + char *t = cp; + for (; *t != 0; t++) + if ('A' <= *t && *t <= 'Z') + *t = *t - 'A' + 'a'; /* tolower */ + + } + opcode = parse_token(cp, filename, linenum); + + switch (opcode) { + case oBadOption: + return -1; /* don't panic, but count bad options */ + /* NOTREACHED */ + case oForwardAgent: + intptr = &options->forward_agent; +parse_flag: + cp = strtok(NULL, WHITESPACE); + if (!cp) + fatal("%.200s line %d: Missing yes/no argument.", filename, linenum); + value = 0; /* To avoid compiler warning... */ + if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0) + value = 1; + else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0) + value = 0; + else + fatal("%.200s line %d: Bad yes/no argument.", filename, linenum); + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oForwardX11: + intptr = &options->forward_x11; + goto parse_flag; + + case oGatewayPorts: + intptr = &options->gateway_ports; + goto parse_flag; + + case oUsePrivilegedPort: + intptr = &options->use_privileged_port; + goto parse_flag; + + case oRhostsAuthentication: + intptr = &options->rhosts_authentication; + goto parse_flag; + + case oPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; + + case oRSAAuthentication: + intptr = &options->rsa_authentication; + goto parse_flag; + + case oRhostsRSAAuthentication: + intptr = &options->rhosts_rsa_authentication; + goto parse_flag; + + case oTISAuthentication: + /* fallthrough, there is no difference on the client side */ + case oSkeyAuthentication: + intptr = &options->skey_authentication; + goto parse_flag; #ifdef KRB4 - case oKerberosAuthentication: - intptr = &options->kerberos_authentication; - goto parse_flag; + case oKerberosAuthentication: + intptr = &options->kerberos_authentication; + goto parse_flag; #endif /* KRB4 */ #ifdef AFS - case oKerberosTgtPassing: - intptr = &options->kerberos_tgt_passing; - goto parse_flag; + case oKerberosTgtPassing: + intptr = &options->kerberos_tgt_passing; + goto parse_flag; - case oAFSTokenPassing: - intptr = &options->afs_token_passing; - goto parse_flag; + case oAFSTokenPassing: + intptr = &options->afs_token_passing; + goto parse_flag; #endif - - case oFallBackToRsh: - intptr = &options->fallback_to_rsh; - goto parse_flag; - - case oUseRsh: - intptr = &options->use_rsh; - goto parse_flag; - - case oBatchMode: - intptr = &options->batch_mode; - goto parse_flag; - - case oCheckHostIP: - intptr = &options->check_host_ip; - goto parse_flag; - - case oStrictHostKeyChecking: - intptr = &options->strict_host_key_checking; - cp = strtok(NULL, WHITESPACE); - if (!cp) - fatal("%.200s line %d: Missing yes/no argument.", - filename, linenum); - value = 0; /* To avoid compiler warning... */ - if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0) - value = 1; - else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0) - value = 0; - else if (strcmp(cp, "ask") == 0) - value = 2; - else - fatal("%.200s line %d: Bad yes/no/ask argument.", filename, linenum); - if (*activep && *intptr == -1) - *intptr = value; - break; - - case oCompression: - intptr = &options->compression; - goto parse_flag; - - case oKeepAlives: - intptr = &options->keepalives; - goto parse_flag; - - case oNumberOfPasswordPrompts: - intptr = &options->number_of_password_prompts; - goto parse_int; - - case oTISAuthentication: - cp = strtok(NULL, WHITESPACE); - if (cp != 0 && (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0)) - fprintf(stderr, - "%.99s line %d: Warning, TIS is not supported.\n", - filename, - linenum); - break; - - case oCompressionLevel: - intptr = &options->compression_level; - goto parse_int; - - case oIdentityFile: - cp = strtok(NULL, WHITESPACE); - if (!cp) - fatal("%.200s line %d: Missing argument.", filename, linenum); - if (*activep) - { - if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES) - fatal("%.200s line %d: Too many identity files specified (max %d).", - filename, linenum, SSH_MAX_IDENTITY_FILES); - options->identity_files[options->num_identity_files++] = xstrdup(cp); - } - break; - - case oUser: - charptr = &options->user; - parse_string: - cp = strtok(NULL, WHITESPACE); - if (!cp) - fatal("%.200s line %d: Missing argument.", filename, linenum); - if (*activep && *charptr == NULL) - *charptr = xstrdup(cp); - break; - - case oGlobalKnownHostsFile: - charptr = &options->system_hostfile; - goto parse_string; - - case oUserKnownHostsFile: - charptr = &options->user_hostfile; - goto parse_string; - - case oHostName: - charptr = &options->hostname; - goto parse_string; - - case oProxyCommand: - charptr = &options->proxy_command; - string = xstrdup(""); - while ((cp = strtok(NULL, WHITESPACE)) != NULL) - { - string = xrealloc(string, strlen(string) + strlen(cp) + 2); - strcat(string, " "); - strcat(string, cp); - } - if (*activep && *charptr == NULL) - *charptr = string; - else - xfree(string); - return 0; - - case oPort: - intptr = &options->port; - parse_int: - cp = strtok(NULL, WHITESPACE); - if (!cp) - fatal("%.200s line %d: Missing argument.", filename, linenum); - if (cp[0] < '0' || cp[0] > '9') - fatal("%.200s line %d: Bad number.", filename, linenum); + + case oFallBackToRsh: + intptr = &options->fallback_to_rsh; + goto parse_flag; + + case oUseRsh: + intptr = &options->use_rsh; + goto parse_flag; + + case oBatchMode: + intptr = &options->batch_mode; + goto parse_flag; + + case oCheckHostIP: + intptr = &options->check_host_ip; + goto parse_flag; + + case oStrictHostKeyChecking: + intptr = &options->strict_host_key_checking; + cp = strtok(NULL, WHITESPACE); + if (!cp) + fatal("%.200s line %d: Missing yes/no argument.", + filename, linenum); + value = 0; /* To avoid compiler warning... */ + if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0) + value = 1; + else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0) + value = 0; + else if (strcmp(cp, "ask") == 0) + value = 2; + else + fatal("%.200s line %d: Bad yes/no/ask argument.", filename, linenum); + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oCompression: + intptr = &options->compression; + goto parse_flag; + + case oKeepAlives: + intptr = &options->keepalives; + goto parse_flag; + + case oNumberOfPasswordPrompts: + intptr = &options->number_of_password_prompts; + goto parse_int; + + case oCompressionLevel: + intptr = &options->compression_level; + goto parse_int; + + case oIdentityFile: + cp = strtok(NULL, WHITESPACE); + if (!cp) + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (*activep) { + if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES) + fatal("%.200s line %d: Too many identity files specified (max %d).", + filename, linenum, SSH_MAX_IDENTITY_FILES); + options->identity_files[options->num_identity_files++] = xstrdup(cp); + } + break; + + case oUser: + charptr = &options->user; +parse_string: + cp = strtok(NULL, WHITESPACE); + if (!cp) + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (*activep && *charptr == NULL) + *charptr = xstrdup(cp); + break; + + case oGlobalKnownHostsFile: + charptr = &options->system_hostfile; + goto parse_string; + + case oUserKnownHostsFile: + charptr = &options->user_hostfile; + goto parse_string; + + case oHostName: + charptr = &options->hostname; + goto parse_string; + + case oProxyCommand: + charptr = &options->proxy_command; + string = xstrdup(""); + while ((cp = strtok(NULL, WHITESPACE)) != NULL) { + string = xrealloc(string, strlen(string) + strlen(cp) + 2); + strcat(string, " "); + strcat(string, cp); + } + if (*activep && *charptr == NULL) + *charptr = string; + else + xfree(string); + return 0; + + case oPort: + intptr = &options->port; +parse_int: + cp = strtok(NULL, WHITESPACE); + if (!cp) + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (cp[0] < '0' || cp[0] > '9') + fatal("%.200s line %d: Bad number.", filename, linenum); #if 0 - value = atoi(cp); + value = atoi(cp); #else - { - char *ptr; - value = strtol(cp, &ptr, 0); /* Octal, decimal, or hex format? */ - if (cp == ptr) - fatal("%.200s line %d: Bad number.", filename, linenum); - } + { + char *ptr; + value = strtol(cp, &ptr, 0); /* Octal, decimal, or + hex format? */ + if (cp == ptr) + fatal("%.200s line %d: Bad number.", filename, linenum); + } #endif - if (*activep && *intptr == -1) - *intptr = value; - break; - - case oConnectionAttempts: - intptr = &options->connection_attempts; - goto parse_int; - - case oCipher: - intptr = &options->cipher; - cp = strtok(NULL, WHITESPACE); - value = cipher_number(cp); - if (value == -1) - fatal("%.200s line %d: Bad cipher '%s'.", - filename, linenum, cp ? cp : ""); - if (*activep && *intptr == -1) - *intptr = value; - break; - - case oLogLevel: - intptr = (int *)&options->log_level; - cp = strtok(NULL, WHITESPACE); - value = log_level_number(cp); - if (value == (LogLevel)-1) - fatal("%.200s line %d: unsupported log level '%s'\n", - filename, linenum, cp ? cp : ""); - if (*activep && (LogLevel)*intptr == -1) - *intptr = (LogLevel)value; - break; - - case oRemoteForward: - cp = strtok(NULL, WHITESPACE); - if (!cp) - fatal("%.200s line %d: Missing argument.", filename, linenum); - if (cp[0] < '0' || cp[0] > '9') - fatal("%.200s line %d: Badly formatted port number.", - filename, linenum); - fwd_port = atoi(cp); - cp = strtok(NULL, WHITESPACE); - if (!cp) - fatal("%.200s line %d: Missing second argument.", - filename, linenum); - if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2) - fatal("%.200s line %d: Badly formatted host:port.", - filename, linenum); - if (*activep) - add_remote_forward(options, fwd_port, buf, fwd_host_port); - break; - - case oLocalForward: - cp = strtok(NULL, WHITESPACE); - if (!cp) - fatal("%.200s line %d: Missing argument.", filename, linenum); - if (cp[0] < '0' || cp[0] > '9') - fatal("%.200s line %d: Badly formatted port number.", - filename, linenum); - fwd_port = atoi(cp); - cp = strtok(NULL, WHITESPACE); - if (!cp) - fatal("%.200s line %d: Missing second argument.", - filename, linenum); - if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2) - fatal("%.200s line %d: Badly formatted host:port.", - filename, linenum); - if (*activep) - add_local_forward(options, fwd_port, buf, fwd_host_port); - break; - - case oHost: - *activep = 0; - while ((cp = strtok(NULL, WHITESPACE)) != NULL) - if (match_pattern(host, cp)) - { - debug("Applying options for %.100s", cp); - *activep = 1; - break; - } - /* Avoid garbage check below, as strtok already returned NULL. */ - return 0; - - case oEscapeChar: - intptr = &options->escape_char; - cp = strtok(NULL, WHITESPACE); - if (!cp) - fatal("%.200s line %d: Missing argument.", filename, linenum); - if (cp[0] == '^' && cp[2] == 0 && - (unsigned char)cp[1] >= 64 && (unsigned char)cp[1] < 128) - value = (unsigned char)cp[1] & 31; - else - if (strlen(cp) == 1) - value = (unsigned char)cp[0]; - else - if (strcmp(cp, "none") == 0) - value = -2; - else - { - fatal("%.200s line %d: Bad escape character.", - filename, linenum); - /*NOTREACHED*/ - value = 0; /* Avoid compiler warning. */ - } - if (*activep && *intptr == -1) - *intptr = value; - break; - - default: - fatal("process_config_line: Unimplemented opcode %d", opcode); - } - - /* Check that there is no garbage at end of line. */ - if (strtok(NULL, WHITESPACE) != NULL) - fatal("%.200s line %d: garbage at end of line.", - filename, linenum); - return 0; + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oConnectionAttempts: + intptr = &options->connection_attempts; + goto parse_int; + + case oCipher: + intptr = &options->cipher; + cp = strtok(NULL, WHITESPACE); + value = cipher_number(cp); + if (value == -1) + fatal("%.200s line %d: Bad cipher '%s'.", + filename, linenum, cp ? cp : ""); + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oLogLevel: + intptr = (int *) &options->log_level; + cp = strtok(NULL, WHITESPACE); + value = log_level_number(cp); + if (value == (LogLevel) - 1) + fatal("%.200s line %d: unsupported log level '%s'\n", + filename, linenum, cp ? cp : ""); + if (*activep && (LogLevel) * intptr == -1) + *intptr = (LogLevel) value; + break; + + case oRemoteForward: + cp = strtok(NULL, WHITESPACE); + if (!cp) + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (cp[0] < '0' || cp[0] > '9') + fatal("%.200s line %d: Badly formatted port number.", + filename, linenum); + fwd_port = atoi(cp); + cp = strtok(NULL, WHITESPACE); + if (!cp) + fatal("%.200s line %d: Missing second argument.", + filename, linenum); + if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2) + fatal("%.200s line %d: Badly formatted host:port.", + filename, linenum); + if (*activep) + add_remote_forward(options, fwd_port, buf, fwd_host_port); + break; + + case oLocalForward: + cp = strtok(NULL, WHITESPACE); + if (!cp) + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (cp[0] < '0' || cp[0] > '9') + fatal("%.200s line %d: Badly formatted port number.", + filename, linenum); + fwd_port = atoi(cp); + cp = strtok(NULL, WHITESPACE); + if (!cp) + fatal("%.200s line %d: Missing second argument.", + filename, linenum); + if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2) + fatal("%.200s line %d: Badly formatted host:port.", + filename, linenum); + if (*activep) + add_local_forward(options, fwd_port, buf, fwd_host_port); + break; + + case oHost: + *activep = 0; + while ((cp = strtok(NULL, WHITESPACE)) != NULL) + if (match_pattern(host, cp)) { + debug("Applying options for %.100s", cp); + *activep = 1; + break; + } + /* Avoid garbage check below, as strtok already returned + NULL. */ + return 0; + + case oEscapeChar: + intptr = &options->escape_char; + cp = strtok(NULL, WHITESPACE); + if (!cp) + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (cp[0] == '^' && cp[2] == 0 && + (unsigned char) cp[1] >= 64 && (unsigned char) cp[1] < 128) + value = (unsigned char) cp[1] & 31; + else if (strlen(cp) == 1) + value = (unsigned char) cp[0]; + else if (strcmp(cp, "none") == 0) + value = -2; + else { + fatal("%.200s line %d: Bad escape character.", + filename, linenum); + /* NOTREACHED */ + value = 0; /* Avoid compiler warning. */ + } + if (*activep && *intptr == -1) + *intptr = value; + break; + + default: + fatal("process_config_line: Unimplemented opcode %d", opcode); + } + + /* Check that there is no garbage at end of line. */ + if (strtok(NULL, WHITESPACE) != NULL) + fatal("%.200s line %d: garbage at end of line.", + filename, linenum); + return 0; } @@ -553,35 +548,35 @@ process_config_line(Options *options, const char *host, already be initialized before this call. This never returns if there is an error. If the file does not exist, this returns immediately. */ -void read_config_file(const char *filename, const char *host, Options *options) +void +read_config_file(const char *filename, const char *host, Options *options) { - FILE *f; - char line[1024]; - int active, linenum; - int bad_options = 0; - - /* Open the file. */ - f = fopen(filename, "r"); - if (!f) - return; - - debug("Reading configuration data %.200s", filename); - - /* Mark that we are now processing the options. This flag is turned on/off - by Host specifications. */ - active = 1; - linenum = 0; - while (fgets(line, sizeof(line), f)) - { - /* Update line number counter. */ - linenum++; - if (process_config_line(options, host, line, filename, linenum, &active) != 0) - bad_options++; - } - fclose(f); - if (bad_options > 0) - fatal("%s: terminating, %d bad configuration options\n", - filename, bad_options); + FILE *f; + char line[1024]; + int active, linenum; + int bad_options = 0; + + /* Open the file. */ + f = fopen(filename, "r"); + if (!f) + return; + + debug("Reading configuration data %.200s", filename); + + /* Mark that we are now processing the options. This flag is + turned on/off by Host specifications. */ + active = 1; + linenum = 0; + while (fgets(line, sizeof(line), f)) { + /* Update line number counter. */ + linenum++; + if (process_config_line(options, host, line, filename, linenum, &active) != 0) + bad_options++; + } + fclose(f); + if (bad_options > 0) + fatal("%s: terminating, %d bad configuration options\n", + filename, bad_options); } /* Initializes options to special values that indicate that they have not @@ -589,120 +584,124 @@ void read_config_file(const char *filename, const char *host, Options *options) Options are processed in the following order: command line, user config file, system config file. Last, fill_default_options is called. */ -void initialize_options(Options *options) +void +initialize_options(Options * options) { - memset(options, 'X', sizeof(*options)); - options->forward_agent = -1; - options->forward_x11 = -1; - options->gateway_ports = -1; - options->use_privileged_port = -1; - options->rhosts_authentication = -1; - options->rsa_authentication = -1; + memset(options, 'X', sizeof(*options)); + options->forward_agent = -1; + options->forward_x11 = -1; + options->gateway_ports = -1; + options->use_privileged_port = -1; + options->rhosts_authentication = -1; + options->rsa_authentication = -1; + options->skey_authentication = -1; #ifdef KRB4 - options->kerberos_authentication = -1; + options->kerberos_authentication = -1; #endif #ifdef AFS - options->kerberos_tgt_passing = -1; - options->afs_token_passing = -1; + options->kerberos_tgt_passing = -1; + options->afs_token_passing = -1; #endif - options->password_authentication = -1; - options->rhosts_rsa_authentication = -1; - options->fallback_to_rsh = -1; - options->use_rsh = -1; - options->batch_mode = -1; - options->check_host_ip = -1; - options->strict_host_key_checking = -1; - options->compression = -1; - options->keepalives = -1; - options->compression_level = -1; - options->port = -1; - options->connection_attempts = -1; - options->number_of_password_prompts = -1; - options->cipher = -1; - options->num_identity_files = 0; - options->hostname = NULL; - options->proxy_command = NULL; - options->user = NULL; - options->escape_char = -1; - options->system_hostfile = NULL; - options->user_hostfile = NULL; - options->num_local_forwards = 0; - options->num_remote_forwards = 0; - options->log_level = (LogLevel)-1; + options->password_authentication = -1; + options->rhosts_rsa_authentication = -1; + options->fallback_to_rsh = -1; + options->use_rsh = -1; + options->batch_mode = -1; + options->check_host_ip = -1; + options->strict_host_key_checking = -1; + options->compression = -1; + options->keepalives = -1; + options->compression_level = -1; + options->port = -1; + options->connection_attempts = -1; + options->number_of_password_prompts = -1; + options->cipher = -1; + options->num_identity_files = 0; + options->hostname = NULL; + options->proxy_command = NULL; + options->user = NULL; + options->escape_char = -1; + options->system_hostfile = NULL; + options->user_hostfile = NULL; + options->num_local_forwards = 0; + options->num_remote_forwards = 0; + options->log_level = (LogLevel) - 1; } /* Called after processing other sources of option data, this fills those options for which no value has been specified with their default values. */ -void fill_default_options(Options *options) +void +fill_default_options(Options * options) { - if (options->forward_agent == -1) - options->forward_agent = 1; - if (options->forward_x11 == -1) - options->forward_x11 = 1; - if (options->gateway_ports == -1) - options->gateway_ports = 0; - if (options->use_privileged_port == -1) - options->use_privileged_port = 1; - if (options->rhosts_authentication == -1) - options->rhosts_authentication = 1; - if (options->rsa_authentication == -1) - options->rsa_authentication = 1; + if (options->forward_agent == -1) + options->forward_agent = 1; + if (options->forward_x11 == -1) + options->forward_x11 = 1; + if (options->gateway_ports == -1) + options->gateway_ports = 0; + if (options->use_privileged_port == -1) + options->use_privileged_port = 1; + if (options->rhosts_authentication == -1) + options->rhosts_authentication = 1; + if (options->rsa_authentication == -1) + options->rsa_authentication = 1; + if (options->skey_authentication == -1) + options->skey_authentication = 0; #ifdef KRB4 - if (options->kerberos_authentication == -1) - options->kerberos_authentication = 1; + if (options->kerberos_authentication == -1) + options->kerberos_authentication = 1; #endif /* KRB4 */ #ifdef AFS - if (options->kerberos_tgt_passing == -1) - options->kerberos_tgt_passing = 1; - if (options->afs_token_passing == -1) - options->afs_token_passing = 1; + if (options->kerberos_tgt_passing == -1) + options->kerberos_tgt_passing = 1; + if (options->afs_token_passing == -1) + options->afs_token_passing = 1; #endif /* AFS */ - if (options->password_authentication == -1) - options->password_authentication = 1; - if (options->rhosts_rsa_authentication == -1) - options->rhosts_rsa_authentication = 1; - if (options->fallback_to_rsh == -1) - options->fallback_to_rsh = 1; - if (options->use_rsh == -1) - options->use_rsh = 0; - if (options->batch_mode == -1) - options->batch_mode = 0; - if (options->check_host_ip == -1) - options->check_host_ip = 1; - if (options->strict_host_key_checking == -1) - options->strict_host_key_checking = 2; /* 2 is default */ - if (options->compression == -1) - options->compression = 0; - if (options->keepalives == -1) - options->keepalives = 1; - if (options->compression_level == -1) - options->compression_level = 6; - if (options->port == -1) - options->port = 0; /* Filled in ssh_connect. */ - if (options->connection_attempts == -1) - options->connection_attempts = 4; - if (options->number_of_password_prompts == -1) - options->number_of_password_prompts = 3; - if (options->cipher == -1) - options->cipher = SSH_CIPHER_NOT_SET; /* Selected in ssh_login(). */ - if (options->num_identity_files == 0) - { - options->identity_files[0] = - xmalloc(2 + strlen(SSH_CLIENT_IDENTITY) + 1); - sprintf(options->identity_files[0], "~/%.100s", SSH_CLIENT_IDENTITY); - options->num_identity_files = 1; - } - if (options->escape_char == -1) - options->escape_char = '~'; - if (options->system_hostfile == NULL) - options->system_hostfile = SSH_SYSTEM_HOSTFILE; - if (options->user_hostfile == NULL) - options->user_hostfile = SSH_USER_HOSTFILE; - if (options->log_level == (LogLevel)-1) - options->log_level = SYSLOG_LEVEL_INFO; - /* options->proxy_command should not be set by default */ - /* options->user will be set in the main program if appropriate */ - /* options->hostname will be set in the main program if appropriate */ + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->rhosts_rsa_authentication == -1) + options->rhosts_rsa_authentication = 1; + if (options->fallback_to_rsh == -1) + options->fallback_to_rsh = 1; + if (options->use_rsh == -1) + options->use_rsh = 0; + if (options->batch_mode == -1) + options->batch_mode = 0; + if (options->check_host_ip == -1) + options->check_host_ip = 1; + if (options->strict_host_key_checking == -1) + options->strict_host_key_checking = 2; /* 2 is default */ + if (options->compression == -1) + options->compression = 0; + if (options->keepalives == -1) + options->keepalives = 1; + if (options->compression_level == -1) + options->compression_level = 6; + if (options->port == -1) + options->port = 0; /* Filled in ssh_connect. */ + if (options->connection_attempts == -1) + options->connection_attempts = 4; + if (options->number_of_password_prompts == -1) + options->number_of_password_prompts = 3; + /* Selected in ssh_login(). */ + if (options->cipher == -1) + options->cipher = SSH_CIPHER_NOT_SET; + if (options->num_identity_files == 0) { + options->identity_files[0] = + xmalloc(2 + strlen(SSH_CLIENT_IDENTITY) + 1); + sprintf(options->identity_files[0], "~/%.100s", SSH_CLIENT_IDENTITY); + options->num_identity_files = 1; + } + if (options->escape_char == -1) + options->escape_char = '~'; + if (options->system_hostfile == NULL) + options->system_hostfile = SSH_SYSTEM_HOSTFILE; + if (options->user_hostfile == NULL) + options->user_hostfile = SSH_USER_HOSTFILE; + if (options->log_level == (LogLevel) - 1) + options->log_level = SYSLOG_LEVEL_INFO; + /* options->proxy_command should not be set by default */ + /* options->user will be set in the main program if appropriate */ + /* options->hostname will be set in the main program if appropriate */ } - diff --git a/readconf.h b/readconf.h index adb39755..c58bd1aa 100644 --- a/readconf.h +++ b/readconf.h @@ -1,17 +1,17 @@ /* - -readconf.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sat Apr 22 00:25:29 1995 ylo - -Functions for reading the configuration file. - -*/ + * + * readconf.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Sat Apr 22 00:25:29 1995 ylo + * + * Functions for reading the configuration file. + * + */ /* RCSID("$Id$"); */ @@ -20,99 +20,107 @@ Functions for reading the configuration file. /* Data structure for representing a forwarding request. */ -typedef struct -{ - int port; /* Port to forward. */ - char *host; /* Host to connect. */ - int host_port; /* Port to connect on host. */ -} Forward; - +typedef struct { + int port; /* Port to forward. */ + char *host; /* Host to connect. */ + int host_port; /* Port to connect on host. */ +} Forward; /* Data structure for representing option data. */ -typedef struct -{ - int forward_agent; /* Forward authentication agent. */ - int forward_x11; /* Forward X11 display. */ - int gateway_ports; /* Allow remote connects to forwarded ports. */ - int use_privileged_port; /* Don't use privileged port if false. */ - int rhosts_authentication; /* Try rhosts authentication. */ - int rhosts_rsa_authentication;/* Try rhosts with RSA authentication. */ - int rsa_authentication; /* Try RSA authentication. */ +typedef struct { + int forward_agent; /* Forward authentication agent. */ + int forward_x11; /* Forward X11 display. */ + int gateway_ports; /* Allow remote connects to forwarded ports. */ + int use_privileged_port; /* Don't use privileged port if false. */ + int rhosts_authentication; /* Try rhosts authentication. */ + int rhosts_rsa_authentication; /* Try rhosts with RSA + * authentication. */ + int rsa_authentication; /* Try RSA authentication. */ + int skey_authentication; /* Try S/Key or TIS authentication. */ #ifdef KRB4 - int kerberos_authentication; /* Try Kerberos authentication. */ + int kerberos_authentication; /* Try Kerberos + * authentication. */ #endif #ifdef AFS - int kerberos_tgt_passing; /* Try Kerberos tgt passing. */ - int afs_token_passing; /* Try AFS token passing. */ + int kerberos_tgt_passing; /* Try Kerberos tgt passing. */ + int afs_token_passing; /* Try AFS token passing. */ #endif - int password_authentication; /* Try password authentication. */ - int fallback_to_rsh; /* Use rsh if cannot connect with ssh. */ - int use_rsh; /* Always use rsh (don\'t try ssh). */ - int batch_mode; /* Batch mode: do not ask for passwords. */ - int check_host_ip; /* Also keep track of keys for IP address */ - int strict_host_key_checking; /* Strict host key checking. */ - int compression; /* Compress packets in both directions. */ - int compression_level; /* Compression level 1 (fast) to 9 (best). */ - int keepalives; /* Set SO_KEEPALIVE. */ - LogLevel log_level; /* Level for logging. */ - - int port; /* Port to connect. */ - int connection_attempts; /* Max attempts (seconds) before giving up */ - int number_of_password_prompts; /* Max number of password prompts. */ - int cipher; /* Cipher to use. */ - char *hostname; /* Real host to connect. */ - char *proxy_command; /* Proxy command for connecting the host. */ - char *user; /* User to log in as. */ - int escape_char; /* Escape character; -2 = none */ - - char *system_hostfile; /* Path for /etc/ssh_known_hosts. */ - char *user_hostfile; /* Path for $HOME/.ssh/known_hosts. */ - - int num_identity_files; /* Number of files for RSA identities. */ - char *identity_files[SSH_MAX_IDENTITY_FILES]; - - /* Local TCP/IP forward requests. */ - int num_local_forwards; - Forward local_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; - - /* Remote TCP/IP forward requests. */ - int num_remote_forwards; - Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; -} Options; + int password_authentication; /* Try password + * authentication. */ + int fallback_to_rsh;/* Use rsh if cannot connect with ssh. */ + int use_rsh; /* Always use rsh (don\'t try ssh). */ + int batch_mode; /* Batch mode: do not ask for passwords. */ + int check_host_ip; /* Also keep track of keys for IP address */ + int strict_host_key_checking; /* Strict host key checking. */ + int compression; /* Compress packets in both directions. */ + int compression_level; /* Compression level 1 (fast) to 9 + * (best). */ + int keepalives; /* Set SO_KEEPALIVE. */ + LogLevel log_level; /* Level for logging. */ + + int port; /* Port to connect. */ + int connection_attempts; /* Max attempts (seconds) before + * giving up */ + int number_of_password_prompts; /* Max number of password + * prompts. */ + int cipher; /* Cipher to use. */ + char *hostname; /* Real host to connect. */ + char *proxy_command; /* Proxy command for connecting the host. */ + char *user; /* User to log in as. */ + int escape_char; /* Escape character; -2 = none */ + + char *system_hostfile;/* Path for /etc/ssh_known_hosts. */ + char *user_hostfile; /* Path for $HOME/.ssh/known_hosts. */ + + int num_identity_files; /* Number of files for RSA identities. */ + char *identity_files[SSH_MAX_IDENTITY_FILES]; + + /* Local TCP/IP forward requests. */ + int num_local_forwards; + Forward local_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; + + /* Remote TCP/IP forward requests. */ + int num_remote_forwards; + Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; +} Options; /* Initializes options to special values that indicate that they have not yet been set. Read_config_file will only set options with this value. Options are processed in the following order: command line, user config file, system config file. Last, fill_default_options is called. */ -void initialize_options(Options *options); +void initialize_options(Options * options); /* Called after processing other sources of option data, this fills those options for which no value has been specified with their default values. */ -void fill_default_options(Options *options); +void fill_default_options(Options * options); -/* Processes a single option line as used in the configuration files. +/* Processes a single option line as used in the configuration files. This only sets those values that have not already been set. Returns 0 for legal options */ -int process_config_line(Options *options, const char *host, - char *line, const char *filename, int linenum, - int *activep); +int +process_config_line(Options * options, const char *host, + char *line, const char *filename, int linenum, + int *activep); /* Reads the config file and modifies the options accordingly. Options should already be initialized before this call. This never returns if there is an error. If the file does not exist, this returns immediately. */ -void read_config_file(const char *filename, const char *host, - Options *options); +void +read_config_file(const char *filename, const char *host, + Options * options); /* Adds a local TCP/IP port forward to options. Never returns if there is an error. */ -void add_local_forward(Options *options, int port, const char *host, - int host_port); +void +add_local_forward(Options * options, int port, const char *host, + int host_port); /* Adds a remote TCP/IP port forward to options. Never returns if there is an error. */ -void add_remote_forward(Options *options, int port, const char *host, - int host_port); +void +add_remote_forward(Options * options, int port, const char *host, + int host_port); -#endif /* READCONF_H */ +#endif /* READCONF_H */ diff --git a/readpass.c b/readpass.c index 5c455755..f5b4802e 100644 --- a/readpass.c +++ b/readpass.c @@ -1,17 +1,17 @@ /* - -readpass.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Mon Jul 10 22:08:59 1995 ylo - -Functions for reading passphrases and passwords. - -*/ + * + * readpass.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Mon Jul 10 22:08:59 1995 ylo + * + * Functions for reading passphrases and passwords. + * + */ #include "includes.h" RCSID("$Id$"); @@ -23,92 +23,91 @@ RCSID("$Id$"); static struct termios saved_tio; /* Old interrupt signal handler for read_passphrase. */ -static void (*old_handler)(int sig) = NULL; +static void (*old_handler) (int sig) = NULL; /* Interrupt signal handler for read_passphrase. */ -void intr_handler(int sig) +void +intr_handler(int sig) { - /* Restore terminal modes. */ - tcsetattr(fileno(stdin), TCSANOW, &saved_tio); - /* Restore the old signal handler. */ - signal(sig, old_handler); - /* Resend the signal, with the old handler. */ - kill(getpid(), sig); + /* Restore terminal modes. */ + tcsetattr(fileno(stdin), TCSANOW, &saved_tio); + /* Restore the old signal handler. */ + signal(sig, old_handler); + /* Resend the signal, with the old handler. */ + kill(getpid(), sig); } -/* Reads a passphrase from /dev/tty with echo turned off. Returns the - passphrase (allocated with xmalloc). Exits if EOF is encountered. +/* Reads a passphrase from /dev/tty with echo turned off. Returns the + passphrase (allocated with xmalloc). Exits if EOF is encountered. The passphrase if read from stdin if from_stdin is true (as is the case with ssh-keygen). */ -char *read_passphrase(const char *prompt, int from_stdin) +char * +read_passphrase(const char *prompt, int from_stdin) { - char buf[1024], *cp; - struct termios tio; - FILE *f; - - if (from_stdin) - f = stdin; - else - { - /* Read the passphrase from /dev/tty to make it possible to ask it even - when stdin has been redirected. */ - f = fopen("/dev/tty", "r"); - if (!f) - { - /* No controlling terminal and no DISPLAY. Nowhere to read. */ - fprintf(stderr, "You have no controlling tty and no DISPLAY. Cannot read passphrase.\n"); - exit(1); + char buf[1024], *cp; + struct termios tio; + FILE *f; + + if (from_stdin) + f = stdin; + else { + /* Read the passphrase from /dev/tty to make it possible + to ask it even when stdin has been redirected. */ + f = fopen("/dev/tty", "r"); + if (!f) { + /* No controlling terminal and no DISPLAY. Nowhere to read. */ + fprintf(stderr, "You have no controlling tty and no DISPLAY. Cannot read passphrase.\n"); + exit(1); + } } - } - - /* Display the prompt (on stderr because stdout might be redirected). */ - fflush(stdout); - fprintf(stderr, "%s", prompt); - fflush(stderr); - - /* Get terminal modes. */ - tcgetattr(fileno(f), &tio); - saved_tio = tio; - /* Save signal handler and set the new handler. */ - old_handler = signal(SIGINT, intr_handler); - /* Set new terminal modes disabling all echo. */ - tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); - tcsetattr(fileno(f), TCSANOW, &tio); - - /* Read the passphrase from the terminal. */ - if (fgets(buf, sizeof(buf), f) == NULL) - { - /* Got EOF. Just exit. */ - /* Restore terminal modes. */ - tcsetattr(fileno(f), TCSANOW, &saved_tio); - /* Restore the signal handler. */ - signal(SIGINT, old_handler); - /* Print a newline (the prompt probably didn\'t have one). */ - fprintf(stderr, "\n"); - /* Close the file. */ - if (f != stdin) - fclose(f); - exit(1); - } - /* Restore terminal modes. */ - tcsetattr(fileno(f), TCSANOW, &saved_tio); - /* Restore the signal handler. */ - (void)signal(SIGINT, old_handler); - /* Remove newline from the passphrase. */ - if (strchr(buf, '\n')) - *strchr(buf, '\n') = 0; - /* Allocate a copy of the passphrase. */ - cp = xstrdup(buf); - /* Clear the buffer so we don\'t leave copies of the passphrase laying - around. */ - memset(buf, 0, sizeof(buf)); - /* Print a newline since the prompt probably didn\'t have one. */ - fprintf(stderr, "\n"); - /* Close the file. */ - if (f != stdin) - fclose(f); - return cp; + /* Display the prompt (on stderr because stdout might be redirected). */ + fflush(stdout); + fprintf(stderr, "%s", prompt); + fflush(stderr); + + /* Get terminal modes. */ + tcgetattr(fileno(f), &tio); + saved_tio = tio; + /* Save signal handler and set the new handler. */ + old_handler = signal(SIGINT, intr_handler); + + /* Set new terminal modes disabling all echo. */ + tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + tcsetattr(fileno(f), TCSANOW, &tio); + + /* Read the passphrase from the terminal. */ + if (fgets(buf, sizeof(buf), f) == NULL) { + /* Got EOF. Just exit. */ + /* Restore terminal modes. */ + tcsetattr(fileno(f), TCSANOW, &saved_tio); + /* Restore the signal handler. */ + signal(SIGINT, old_handler); + /* Print a newline (the prompt probably didn\'t have one). */ + fprintf(stderr, "\n"); + /* Close the file. */ + if (f != stdin) + fclose(f); + exit(1); + } + /* Restore terminal modes. */ + tcsetattr(fileno(f), TCSANOW, &saved_tio); + /* Restore the signal handler. */ + (void) signal(SIGINT, old_handler); + /* Remove newline from the passphrase. */ + if (strchr(buf, '\n')) + *strchr(buf, '\n') = 0; + /* Allocate a copy of the passphrase. */ + cp = xstrdup(buf); + /* Clear the buffer so we don\'t leave copies of the passphrase + laying around. */ + memset(buf, 0, sizeof(buf)); + /* Print a newline since the prompt probably didn\'t have one. */ + fprintf(stderr, "\n"); + /* Close the file. */ + if (f != stdin) + fclose(f); + return cp; } diff --git a/rsa.c b/rsa.c index def0ec18..a802e593 100644 --- a/rsa.c +++ b/rsa.c @@ -1,37 +1,37 @@ /* - -rsa.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Fri Mar 3 22:07:06 1995 ylo - -Description of the RSA algorithm can be found e.g. from the following sources: - - Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1994. - - Jennifer Seberry and Josed Pieprzyk: Cryptography: An Introduction to - Computer Security. Prentice-Hall, 1989. - - Man Young Rhee: Cryptography and Secure Data Communications. McGraw-Hill, - 1994. - - R. Rivest, A. Shamir, and L. M. Adleman: Cryptographic Communications - System and Method. US Patent 4,405,829, 1983. - - Hans Riesel: Prime Numbers and Computer Methods for Factorization. - Birkhauser, 1994. - - The RSA Frequently Asked Questions document by RSA Data Security, Inc., 1995. - - RSA in 3 lines of perl by Adam Back , 1995, as included - below: - - gone - had to be deleted - what a pity - + * + * rsa.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Fri Mar 3 22:07:06 1995 ylo + * + * Description of the RSA algorithm can be found e.g. from the following sources: + * + * Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1994. + * + * Jennifer Seberry and Josed Pieprzyk: Cryptography: An Introduction to + * Computer Security. Prentice-Hall, 1989. + * + * Man Young Rhee: Cryptography and Secure Data Communications. McGraw-Hill, + * 1994. + * + * R. Rivest, A. Shamir, and L. M. Adleman: Cryptographic Communications + * System and Method. US Patent 4,405,829, 1983. + * + * Hans Riesel: Prime Numbers and Computer Methods for Factorization. + * Birkhauser, 1994. + * + * The RSA Frequently Asked Questions document by RSA Data Security, Inc., 1995. + * + * RSA in 3 lines of perl by Adam Back , 1995, as included + * below: + * + * [gone - had to be deleted - what a pity] + * */ #include "includes.h" @@ -46,13 +46,13 @@ int rsa_verbose = 1; int rsa_alive() { - RSA *key; + RSA *key; - key = RSA_generate_key(32, 3, NULL, NULL); - if (key == NULL) - return (0); - RSA_free(key); - return (1); + key = RSA_generate_key(32, 3, NULL, NULL); + if (key == NULL) + return (0); + RSA_free(key); + return (1); } /* Generates RSA public and private keys. This initializes the data @@ -62,101 +62,100 @@ rsa_alive() void rsa_generate_key(RSA *prv, RSA *pub, unsigned int bits) { - RSA *key; - - if (rsa_verbose) { - printf("Generating RSA keys: "); - fflush(stdout); - } - - key = RSA_generate_key(bits, 35, NULL, NULL); - if (key == NULL) - fatal("rsa_generate_key: key generation failed."); - - /* Copy public key parameters */ - pub->n = BN_new(); - BN_copy(pub->n, key->n); - pub->e = BN_new(); - BN_copy(pub->e, key->e); - - /* Copy private key parameters */ - prv->n = BN_new(); - BN_copy(prv->n, key->n); - prv->e = BN_new(); - BN_copy(prv->e, key->e); - prv->d = BN_new(); - BN_copy(prv->d, key->d); - prv->p = BN_new(); - BN_copy(prv->p, key->p); - prv->q = BN_new(); - BN_copy(prv->q, key->q); - - prv->dmp1 = BN_new(); - BN_copy(prv->dmp1, key->dmp1); - - prv->dmq1 = BN_new(); - BN_copy(prv->dmq1, key->dmq1); - - prv->iqmp = BN_new(); - BN_copy(prv->iqmp, key->iqmp); - - RSA_free(key); - - if (rsa_verbose) - printf("Key generation complete.\n"); + RSA *key; + + if (rsa_verbose) { + printf("Generating RSA keys: "); + fflush(stdout); + } + key = RSA_generate_key(bits, 35, NULL, NULL); + if (key == NULL) + fatal("rsa_generate_key: key generation failed."); + + /* Copy public key parameters */ + pub->n = BN_new(); + BN_copy(pub->n, key->n); + pub->e = BN_new(); + BN_copy(pub->e, key->e); + + /* Copy private key parameters */ + prv->n = BN_new(); + BN_copy(prv->n, key->n); + prv->e = BN_new(); + BN_copy(prv->e, key->e); + prv->d = BN_new(); + BN_copy(prv->d, key->d); + prv->p = BN_new(); + BN_copy(prv->p, key->p); + prv->q = BN_new(); + BN_copy(prv->q, key->q); + + prv->dmp1 = BN_new(); + BN_copy(prv->dmp1, key->dmp1); + + prv->dmq1 = BN_new(); + BN_copy(prv->dmq1, key->dmq1); + + prv->iqmp = BN_new(); + BN_copy(prv->iqmp, key->iqmp); + + RSA_free(key); + + if (rsa_verbose) + printf("Key generation complete.\n"); } void -rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA* key) +rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA *key) { - char *inbuf, *outbuf; - int len, ilen, olen; + char *inbuf, *outbuf; + int len, ilen, olen; - if (BN_num_bits(key->e) < 2 || !BN_is_odd(key->e)) - fatal("rsa_public_encrypt() exponent too small or not odd"); + if (BN_num_bits(key->e) < 2 || !BN_is_odd(key->e)) + fatal("rsa_public_encrypt() exponent too small or not odd"); - olen = BN_num_bytes(key->n); - outbuf = xmalloc(olen); + olen = BN_num_bytes(key->n); + outbuf = xmalloc(olen); - ilen = BN_num_bytes(in); - inbuf = xmalloc(ilen); - BN_bn2bin(in, inbuf); + ilen = BN_num_bytes(in); + inbuf = xmalloc(ilen); + BN_bn2bin(in, inbuf); - if ((len = RSA_public_encrypt(ilen, inbuf, outbuf, key, - RSA_PKCS1_PADDING)) <= 0) - fatal("rsa_public_encrypt() failed"); + if ((len = RSA_public_encrypt(ilen, inbuf, outbuf, key, + RSA_PKCS1_PADDING)) <= 0) + fatal("rsa_public_encrypt() failed"); - BN_bin2bn(outbuf, len, out); + BN_bin2bn(outbuf, len, out); - memset(outbuf, 0, olen); - memset(inbuf, 0, ilen); - xfree(outbuf); - xfree(inbuf); + memset(outbuf, 0, olen); + memset(inbuf, 0, ilen); + xfree(outbuf); + xfree(inbuf); } void rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *key) { - char *inbuf, *outbuf; - int len, ilen, olen; + char *inbuf, *outbuf; + int len, ilen, olen; - olen = BN_num_bytes(key->n); - outbuf = xmalloc(olen); + olen = BN_num_bytes(key->n); + outbuf = xmalloc(olen); - ilen = BN_num_bytes(in); - inbuf = xmalloc(ilen); - BN_bn2bin(in, inbuf); + ilen = BN_num_bytes(in); + inbuf = xmalloc(ilen); + BN_bn2bin(in, inbuf); - if ((len = RSA_private_decrypt(ilen, inbuf, outbuf, key, - RSA_SSLV23_PADDING)) <= 0) - fatal("rsa_private_decrypt() failed"); + if ((len = RSA_private_decrypt(ilen, inbuf, outbuf, key, + RSA_SSLV23_PADDING)) <= 0) + fatal("rsa_private_decrypt() failed"); - BN_bin2bn(outbuf, len, out); + BN_bin2bn(outbuf, len, out); - memset(outbuf, 0, olen); - memset(inbuf, 0, ilen); - xfree(outbuf); - xfree(inbuf); + memset(outbuf, 0, olen); + memset(inbuf, 0, ilen); + xfree(outbuf); + xfree(inbuf); } /* Set whether to output verbose messages during key generation. */ @@ -164,5 +163,5 @@ rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *key) void rsa_set_verbose(int verbose) { - rsa_verbose = verbose; + rsa_verbose = verbose; } diff --git a/rsa.h b/rsa.h index 8cd74e46..300b1403 100644 --- a/rsa.h +++ b/rsa.h @@ -1,24 +1,25 @@ /* - -rsa.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Fri Mar 3 22:01:06 1995 ylo - -RSA key generation, encryption and decryption. - + * + * rsa.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Fri Mar 3 22:01:06 1995 ylo + * + * RSA key generation, encryption and decryption. + * */ /* RCSID("$Id$"); */ -#include "config.h" #ifndef RSA_H #define RSA_H +#include "config.h" + #ifdef HAVE_OPENSSL #include #include @@ -30,15 +31,15 @@ RSA key generation, encryption and decryption. #endif /* Calls SSL RSA_generate_key, only copies to prv and pub */ -void rsa_generate_key(RSA *prv, RSA *pub, unsigned int bits); +void rsa_generate_key(RSA * prv, RSA * pub, unsigned int bits); /* Indicates whether the rsa module is permitted to show messages on the terminal. */ -void rsa_set_verbose(int verbose); +void rsa_set_verbose __P((int verbose)); -int rsa_alive(void); +int rsa_alive __P((void)); -void rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA *prv); -void rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *prv); +void rsa_public_encrypt __P((BIGNUM * out, BIGNUM * in, RSA * prv)); +void rsa_private_decrypt __P((BIGNUM * out, BIGNUM * in, RSA * prv)); -#endif /* RSA_H */ +#endif /* RSA_H */ diff --git a/scp.c b/scp.c index 531dc6d1..aa522d96 100644 --- a/scp.c +++ b/scp.c @@ -1,13 +1,13 @@ /* - -scp - secure remote copy. This is basically patched BSD rcp which uses ssh -to do the data transfer (instead of using rcmd). - -NOTE: This version should NOT be suid root. (This uses ssh to do the transfer -and ssh has the necessary privileges.) - -1995 Timo Rinne , Tatu Ylonen - + * + * scp - secure remote copy. This is basically patched BSD rcp which uses ssh + * to do the data transfer (instead of using rcmd). + * + * NOTE: This version should NOT be suid root. (This uses ssh to do the transfer + * and ssh has the necessary privileges.) + * + * 1995 Timo Rinne , Tatu Ylonen + * */ /* @@ -42,7 +42,6 @@ and ssh has the necessary privileges.) * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ */ #include "includes.h" @@ -76,7 +75,7 @@ off_t totalbytes = 0; char *curfile; /* This is set to non-zero to enable verbose mode. */ -int verbose = 0; +int verbose_mode = 0; /* This is set to non-zero if compression is desired. */ int compress = 0; @@ -91,7 +90,7 @@ int batchmode = 0; /* This is set to the cipher type string if given on the command line. */ char *cipher = NULL; -/* This is set to the RSA authentication identity file name if given on +/* This is set to the RSA authentication identity file name if given on the command line. */ char *identity = NULL; @@ -102,98 +101,95 @@ char *port = NULL; host. This returns < 0 if execution fails, and >= 0 otherwise. This assigns the input and output file descriptors on success. */ -int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) +int +do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) { - int pin[2], pout[2], reserved[2]; - - if (verbose) - fprintf(stderr, "Executing: host %s, user %s, command %s\n", - host, remuser ? remuser : "(unspecified)", cmd); - - /* Reserve two descriptors so that the real pipes won't get descriptors - 0 and 1 because that will screw up dup2 below. */ - pipe(reserved); - - /* Create a socket pair for communicating with ssh. */ - if (pipe(pin) < 0) - fatal("pipe: %s", strerror(errno)); - if (pipe(pout) < 0) - fatal("pipe: %s", strerror(errno)); - - /* Free the reserved descriptors. */ - close(reserved[0]); - close(reserved[1]); - - /* For a child to execute the command on the remote host using ssh. */ - if (fork() == 0) - { - char *args[100]; - unsigned int i; - - /* Child. */ - close(pin[1]); - close(pout[0]); - dup2(pin[0], 0); - dup2(pout[1], 1); - close(pin[0]); - close(pout[1]); - - i = 0; - args[i++] = SSH_PROGRAM; - args[i++] = "-x"; - args[i++] = "-oFallBackToRsh no"; - if (verbose) - args[i++] = "-v"; - if (compress) - args[i++] = "-C"; - if (batchmode) - args[i++] = "-oBatchMode yes"; - if (cipher != NULL) - { - args[i++] = "-c"; - args[i++] = cipher; - } - if (identity != NULL) - { - args[i++] = "-i"; - args[i++] = identity; - } - if (port != NULL) - { - args[i++] = "-p"; - args[i++] = port; - } - if (remuser != NULL) - { - args[i++] = "-l"; - args[i++] = remuser; + int pin[2], pout[2], reserved[2]; + + if (verbose_mode) + fprintf(stderr, "Executing: host %s, user %s, command %s\n", + host, remuser ? remuser : "(unspecified)", cmd); + + /* Reserve two descriptors so that the real pipes won't get + descriptors 0 and 1 because that will screw up dup2 below. */ + pipe(reserved); + + /* Create a socket pair for communicating with ssh. */ + if (pipe(pin) < 0) + fatal("pipe: %s", strerror(errno)); + if (pipe(pout) < 0) + fatal("pipe: %s", strerror(errno)); + + /* Free the reserved descriptors. */ + close(reserved[0]); + close(reserved[1]); + + /* For a child to execute the command on the remote host using ssh. */ + if (fork() == 0) { + char *args[100]; + unsigned int i; + + /* Child. */ + close(pin[1]); + close(pout[0]); + dup2(pin[0], 0); + dup2(pout[1], 1); + close(pin[0]); + close(pout[1]); + + i = 0; + args[i++] = SSH_PROGRAM; + args[i++] = "-x"; + args[i++] = "-oFallBackToRsh no"; + if (verbose_mode) + args[i++] = "-v"; + if (compress) + args[i++] = "-C"; + if (batchmode) + args[i++] = "-oBatchMode yes"; + if (cipher != NULL) { + args[i++] = "-c"; + args[i++] = cipher; + } + if (identity != NULL) { + args[i++] = "-i"; + args[i++] = identity; + } + if (port != NULL) { + args[i++] = "-p"; + args[i++] = port; + } + if (remuser != NULL) { + args[i++] = "-l"; + args[i++] = remuser; + } + args[i++] = host; + args[i++] = cmd; + args[i++] = NULL; + + execvp(SSH_PROGRAM, args); + perror(SSH_PROGRAM); + exit(1); } - args[i++] = host; - args[i++] = cmd; - args[i++] = NULL; - - execvp(SSH_PROGRAM, args); - perror(SSH_PROGRAM); - exit(1); - } - /* Parent. Close the other side, and return the local side. */ - close(pin[0]); - *fdout = pin[1]; - close(pout[1]); - *fdin = pout[0]; - return 0; + /* Parent. Close the other side, and return the local side. */ + close(pin[0]); + *fdout = pin[1]; + close(pout[1]); + *fdin = pout[0]; + return 0; } -void fatal(const char *fmt, ...) +void +fatal(const char *fmt,...) { - va_list ap; - char buf[1024]; - - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - fprintf(stderr, "%s\n", buf); - exit(255); + va_list ap; + char buf[1024]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + fprintf(stderr, "%s\n", buf); + exit(255); } /* This stuff used to be in BSD rcp extern.h. */ @@ -205,31 +201,31 @@ typedef struct { extern int iamremote; -BUF *allocbuf(BUF *, int, int); -char *colon(char *); -void lostconn(int); -void nospace(void); -int okname(char *); -void run_err(const char *, ...); -void verifydir(char *); +BUF *allocbuf(BUF *, int, int); +char *colon(char *); +void lostconn(int); +void nospace(void); +int okname(char *); +void run_err(const char *,...); +void verifydir(char *); /* Stuff from BSD rcp.c continues. */ struct passwd *pwd; -uid_t userid; +uid_t userid; int errs, remin, remout; int pflag, iamremote, iamrecursive, targetshouldbedirectory; #define CMDNEEDS 64 char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ -int response(void); -void rsource(char *, struct stat *); -void sink(int, char *[]); -void source(int, char *[]); -void tolocal(int, char *[]); -void toremote(char *, int, char *[]); -void usage(void); +int response(void); +void rsource(char *, struct stat *); +void sink(int, char *[]); +void source(int, char *[]); +void tolocal(int, char *[]); +void toremote(char *, int, char *[]); +void usage(void); int main(argc, argv) @@ -242,47 +238,48 @@ main(argc, argv) extern int optind; fflag = tflag = 0; - while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q")) != EOF) - switch(ch) { /* User-visible flags. */ + while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q")) != EOF) + switch (ch) { + /* User-visible flags. */ case 'p': pflag = 1; break; case 'P': - port = optarg; - break; + port = optarg; + break; case 'r': iamrecursive = 1; break; - /* Server options. */ + /* Server options. */ case 'd': targetshouldbedirectory = 1; break; - case 'f': /* "from" */ + case 'f': /* "from" */ iamremote = 1; fflag = 1; break; - case 't': /* "to" */ + case 't': /* "to" */ iamremote = 1; tflag = 1; break; case 'c': cipher = optarg; - break; + break; case 'i': - identity = optarg; + identity = optarg; break; case 'v': - verbose = 1; - break; + verbose_mode = 1; + break; case 'B': - batchmode = 1; - break; + batchmode = 1; + break; case 'C': - compress = 1; - break; + compress = 1; + break; case 'q': - showprogress = 0; - break; + showprogress = 0; + break; case '?': default: usage(); @@ -291,25 +288,25 @@ main(argc, argv) argv += optind; if ((pwd = getpwuid(userid = getuid())) == NULL) - fatal("unknown user %d", (int)userid); + fatal("unknown user %d", (int) userid); - if (! isatty(STDERR_FILENO)) + if (!isatty(STDERR_FILENO)) showprogress = 0; remin = STDIN_FILENO; remout = STDOUT_FILENO; - if (fflag) { /* Follow "protocol", send data. */ - (void)response(); + if (fflag) { + /* Follow "protocol", send data. */ + (void) response(); source(argc, argv); exit(errs != 0); } - - if (tflag) { /* Receive data. */ + if (tflag) { + /* Receive data. */ sink(argc, argv); exit(errs != 0); } - if (argc < 2) usage(); if (argc > 2) @@ -317,16 +314,16 @@ main(argc, argv) remin = remout = -1; /* Command to be executed on remote system using "ssh". */ - (void)sprintf(cmd, "scp%s%s%s%s", verbose ? " -v" : "", - iamrecursive ? " -r" : "", pflag ? " -p" : "", - targetshouldbedirectory ? " -d" : ""); + (void) sprintf(cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "", + iamrecursive ? " -r" : "", pflag ? " -p" : "", + targetshouldbedirectory ? " -d" : ""); - (void)signal(SIGPIPE, lostconn); + (void) signal(SIGPIPE, lostconn); if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ toremote(targ, argc, argv); else { - tolocal(argc, argv); /* Dest is local host. */ + tolocal(argc, argv); /* Dest is local host. */ if (targetshouldbedirectory) verifydir(argv[argc - 1]); } @@ -360,15 +357,15 @@ toremote(targ, argc, argv) for (i = 0; i < argc - 1; i++) { src = colon(argv[i]); - if (src) { /* remote to remote */ + if (src) { /* remote to remote */ *src++ = 0; if (*src == 0) src = "."; host = strchr(argv[i], '@'); len = strlen(SSH_PROGRAM) + strlen(argv[i]) + - strlen(src) + (tuser ? strlen(tuser) : 0) + - strlen(thost) + strlen(targ) + CMDNEEDS + 32; - bp = xmalloc(len); + strlen(src) + (tuser ? strlen(tuser) : 0) + + strlen(thost) + strlen(targ) + CMDNEEDS + 32; + bp = xmalloc(len); if (host) { *host++ = 0; suser = argv[i]; @@ -376,37 +373,37 @@ toremote(targ, argc, argv) suser = pwd->pw_name; else if (!okname(suser)) continue; - (void)sprintf(bp, - "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'", - SSH_PROGRAM, verbose ? " -v" : "", - suser, host, cmd, src, - tuser ? tuser : "", tuser ? "@" : "", - thost, targ); + (void) sprintf(bp, + "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'", + SSH_PROGRAM, verbose_mode ? " -v" : "", + suser, host, cmd, src, + tuser ? tuser : "", tuser ? "@" : "", + thost, targ); } else - (void)sprintf(bp, - "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'", - SSH_PROGRAM, verbose ? " -v" : "", - argv[i], cmd, src, - tuser ? tuser : "", tuser ? "@" : "", - thost, targ); - if (verbose) - fprintf(stderr, "Executing: %s\n", bp); - (void)system(bp); - (void)xfree(bp); - } else { /* local to remote */ + (void) sprintf(bp, + "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'", + SSH_PROGRAM, verbose_mode ? " -v" : "", + argv[i], cmd, src, + tuser ? tuser : "", tuser ? "@" : "", + thost, targ); + if (verbose_mode) + fprintf(stderr, "Executing: %s\n", bp); + (void) system(bp); + (void) xfree(bp); + } else { /* local to remote */ if (remin == -1) { len = strlen(targ) + CMDNEEDS + 20; - bp = xmalloc(len); - (void)sprintf(bp, "%s -t %s", cmd, targ); + bp = xmalloc(len); + (void) sprintf(bp, "%s -t %s", cmd, targ); host = thost; - if (do_cmd(host, tuser, + if (do_cmd(host, tuser, bp, &remin, &remout) < 0) - exit(1); + exit(1); if (response() < 0) exit(1); - (void)xfree(bp); + (void) xfree(bp); } - source(1, argv+i); + source(1, argv + i); } } } @@ -420,18 +417,18 @@ tolocal(argc, argv) char *bp, *host, *src, *suser; for (i = 0; i < argc - 1; i++) { - if (!(src = colon(argv[i]))) { /* Local to local. */ + if (!(src = colon(argv[i]))) { /* Local to local. */ len = strlen(_PATH_CP) + strlen(argv[i]) + - strlen(argv[argc - 1]) + 20; + strlen(argv[argc - 1]) + 20; bp = xmalloc(len); - (void)sprintf(bp, "exec %s%s%s %s %s", _PATH_CP, - iamrecursive ? " -r" : "", pflag ? " -p" : "", - argv[i], argv[argc - 1]); - if (verbose) - fprintf(stderr, "Executing: %s\n", bp); + (void) sprintf(bp, "exec %s%s%s %s %s", _PATH_CP, + iamrecursive ? " -r" : "", pflag ? " -p" : "", + argv[i], argv[argc - 1]); + if (verbose_mode) + fprintf(stderr, "Executing: %s\n", bp); if (system(bp)) ++errs; - (void)xfree(bp); + (void) xfree(bp); continue; } *src++ = 0; @@ -449,16 +446,16 @@ tolocal(argc, argv) continue; } len = strlen(src) + CMDNEEDS + 20; - bp = xmalloc(len); - (void)sprintf(bp, "%s -f %s", cmd, src); - if (do_cmd(host, suser, bp, &remin, &remout) < 0) { - (void)xfree(bp); - ++errs; - continue; + bp = xmalloc(len); + (void) sprintf(bp, "%s -f %s", cmd, src); + if (do_cmd(host, suser, bp, &remin, &remout) < 0) { + (void) xfree(bp); + ++errs; + continue; } - xfree(bp); + xfree(bp); sink(1, argv + argc - 1); - (void)close(remin); + (void) close(remin); remin = remout = -1; } } @@ -476,7 +473,7 @@ source(argc, argv) char *last, *name, buf[2048]; for (indx = 0; indx < argc; ++indx) { - name = argv[indx]; + name = argv[indx]; statbytes = 0; if ((fd = open(name, O_RDONLY, 0)) < 0) goto syserr; @@ -507,36 +504,33 @@ syserr: run_err("%s: %s", name, strerror(errno)); * Make it compatible with possible future * versions expecting microseconds. */ - (void)sprintf(buf, "T%lu 0 %lu 0\n", - (unsigned long)stb.st_mtime, - (unsigned long)stb.st_atime); - (void)write(remout, buf, strlen(buf)); + (void) sprintf(buf, "T%lu 0 %lu 0\n", + (unsigned long) stb.st_mtime, + (unsigned long) stb.st_atime); + (void) write(remout, buf, strlen(buf)); if (response() < 0) goto next; } #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) - (void)sprintf(buf, "C%04o %lu %s\n", - (unsigned int)(stb.st_mode & FILEMODEMASK), - (unsigned long)stb.st_size, - last); - if (verbose) - { - fprintf(stderr, "Sending file modes: %s", buf); - fflush(stderr); - } - (void)write(remout, buf, strlen(buf)); + (void) sprintf(buf, "C%04o %lu %s\n", + (unsigned int) (stb.st_mode & FILEMODEMASK), + (unsigned long) stb.st_size, + last); + if (verbose_mode) { + fprintf(stderr, "Sending file modes: %s", buf); + fflush(stderr); + } + (void) write(remout, buf, strlen(buf)); if (response() < 0) goto next; if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { -next: (void)close(fd); +next: (void) close(fd); continue; } - if (showprogress) { totalbytes = stb.st_size; progressmeter(-1); } - /* Keep writing after an error so that we stay sync'd up. */ for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { amt = bp->cnt; @@ -548,7 +542,7 @@ next: (void)close(fd); haderr = result >= 0 ? EIO : errno; } if (haderr) - (void)write(remout, bp->buf, amt); + (void) write(remout, bp->buf, amt); else { result = write(remout, bp->buf, amt); if (result != amt) @@ -556,16 +550,16 @@ next: (void)close(fd); statbytes += result; } } - if(showprogress) + if (showprogress) progressmeter(1); if (close(fd) < 0 && !haderr) haderr = errno; if (!haderr) - (void)write(remout, "", 1); + (void) write(remout, "", 1); else run_err("%s: %s", name, strerror(haderr)); - (void)response(); + (void) response(); } } @@ -588,21 +582,21 @@ rsource(name, statp) else last++; if (pflag) { - (void)sprintf(path, "T%lu 0 %lu 0\n", - (unsigned long)statp->st_mtime, - (unsigned long)statp->st_atime); - (void)write(remout, path, strlen(path)); + (void) sprintf(path, "T%lu 0 %lu 0\n", + (unsigned long) statp->st_mtime, + (unsigned long) statp->st_atime); + (void) write(remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; } } - (void)sprintf(path, - "D%04o %d %.1024s\n", (unsigned int)(statp->st_mode & FILEMODEMASK), - 0, last); - if (verbose) - fprintf(stderr, "Entering directory: %s", path); - (void)write(remout, path, strlen(path)); + (void) sprintf(path, "D%04o %d %.1024s\n", + (unsigned int) (statp->st_mode & FILEMODEMASK), + 0, last); + if (verbose_mode) + fprintf(stderr, "Entering directory: %s", path); + (void) write(remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; @@ -616,13 +610,13 @@ rsource(name, statp) run_err("%s/%s: name too long", name, dp->d_name); continue; } - (void)sprintf(path, "%s/%s", name, dp->d_name); + (void) sprintf(path, "%s/%s", name, dp->d_name); vect[0] = path; source(1, vect); } - (void)closedir(dirp); - (void)write(remout, "E\n", 2); - (void)response(); + (void) closedir(dirp); + (void) write(remout, "E\n", 2); + (void) response(); } void @@ -632,21 +626,23 @@ sink(argc, argv) { static BUF buffer; struct stat stb; - enum { YES, NO, DISPLAYED } wrerr; + enum { + YES, NO, DISPLAYED + } wrerr; BUF *bp; off_t i, j; int amt, count, exists, first, mask, mode, ofd, omode; int setimes, size, targisdir, wrerrno = 0; char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; - struct utimbuf ut; - int dummy_usec; + struct utimbuf ut; + int dummy_usec; #define SCREWUP(str) { why = str; goto screwup; } setimes = targisdir = 0; mask = umask(0); if (!pflag) - (void)umask(mask); + (void) umask(mask); if (argc != 1) { run_err("ambiguous target"); exit(1); @@ -654,8 +650,8 @@ sink(argc, argv) targ = *argv; if (targetshouldbedirectory) verifydir(targ); - - (void)write(remout, "", 1); + + (void) write(remout, "", 1); if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) targisdir = 1; for (first = 1;; first = 0) { @@ -673,18 +669,17 @@ sink(argc, argv) if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) - (void)write(STDERR_FILENO, - buf + 1, strlen(buf + 1)); + (void) write(STDERR_FILENO, + buf + 1, strlen(buf + 1)); if (buf[0] == '\02') exit(1); ++errs; continue; } if (buf[0] == 'E') { - (void)write(remout, "", 1); + (void) write(remout, "", 1); return; } - if (ch == '\n') *--cp = 0; @@ -706,7 +701,7 @@ sink(argc, argv) getnum(dummy_usec); if (*cp++ != '\0') SCREWUP("atime.usec not delimited"); - (void)write(remout, "", 1); + (void) write(remout, "", 1); continue; } if (*cp != 'C' && *cp != 'D') { @@ -732,7 +727,7 @@ sink(argc, argv) if (*cp++ != ' ') SCREWUP("mode not delimited"); - for (size = 0; *cp >= '0' && *cp <= '9';) + for (size = 0; *cp >= '0' && *cp <= '9';) size = size * 10 + (*cp++ - '0'); if (*cp++ != ' ') SCREWUP("size not delimited"); @@ -743,9 +738,9 @@ sink(argc, argv) need = strlen(targ) + strlen(cp) + 250; if (need > cursize) - namebuf = xmalloc(need); - (void)sprintf(namebuf, "%s%s%s", targ, - *targ ? "/" : "", cp); + namebuf = xmalloc(need); + (void) sprintf(namebuf, "%s%s%s", targ, + *targ ? "/" : "", cp); np = namebuf; } else np = targ; @@ -759,9 +754,10 @@ sink(argc, argv) goto bad; } if (pflag) - (void)chmod(np, mode); + (void) chmod(np, mode); } else { - /* Handle copying from a read-only directory */ + /* Handle copying from a read-only + directory */ mod_flag = 1; if (mkdir(np, mode | S_IRWXU) < 0) goto bad; @@ -771,22 +767,22 @@ sink(argc, argv) if (setimes) { setimes = 0; if (utime(np, &ut) < 0) - run_err("%s: set times: %s", - np, strerror(errno)); + run_err("%s: set times: %s", + np, strerror(errno)); } if (mod_flag) - (void)chmod(np, mode); + (void) chmod(np, mode); continue; } omode = mode; mode |= S_IWRITE; - if ((ofd = open(np, O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0) { + if ((ofd = open(np, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) { bad: run_err("%s: %s", np, strerror(errno)); continue; } - (void)write(remout, "", 1); + (void) write(remout, "", 1); if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { - (void)close(ofd); + (void) close(ofd); continue; } cp = bp->buf; @@ -806,12 +802,12 @@ bad: run_err("%s: %s", np, strerror(errno)); j = read(remin, cp, amt); if (j <= 0) { run_err("%s", j ? strerror(errno) : - "dropped connection"); + "dropped connection"); exit(1); } amt -= j; cp += j; - statbytes += j; + statbytes += j; } while (amt > 0); if (count == bp->cnt) { /* Keep reading so we stay sync'd up. */ @@ -819,7 +815,7 @@ bad: run_err("%s: %s", np, strerror(errno)); j = write(ofd, bp->buf, count); if (j != count) { wrerr = YES; - wrerrno = j >= 0 ? EIO : errno; + wrerrno = j >= 0 ? EIO : errno; } } count = 0; @@ -831,7 +827,7 @@ bad: run_err("%s: %s", np, strerror(errno)); if (count != 0 && wrerr == NO && (j = write(ofd, bp->buf, count)) != count) { wrerr = YES; - wrerrno = j >= 0 ? EIO : errno; + wrerrno = j >= 0 ? EIO : errno; } #if 0 if (ftruncate(ofd, size)) { @@ -843,29 +839,29 @@ bad: run_err("%s: %s", np, strerror(errno)); if (exists || omode != mode) if (fchmod(ofd, omode)) run_err("%s: set mode: %s", - np, strerror(errno)); + np, strerror(errno)); } else { if (!exists && omode != mode) if (fchmod(ofd, omode & ~mask)) run_err("%s: set mode: %s", - np, strerror(errno)); + np, strerror(errno)); } - (void)close(ofd); - (void)response(); + (void) close(ofd); + (void) response(); if (setimes && wrerr == NO) { setimes = 0; if (utime(np, &ut) < 0) { run_err("%s: set times: %s", - np, strerror(errno)); + np, strerror(errno)); wrerr = DISPLAYED; } } - switch(wrerr) { + switch (wrerr) { case YES: run_err("%s: %s", np, strerror(wrerrno)); break; case NO: - (void)write(remout, "", 1); + (void) write(remout, "", 1); break; case DISPLAYED: break; @@ -885,14 +881,14 @@ response() lostconn(0); cp = rbuf; - switch(resp) { - case 0: /* ok */ + switch (resp) { + case 0: /* ok */ return (0); default: *cp++ = resp; /* FALLTHROUGH */ - case 1: /* error, followed by error msg */ - case 2: /* fatal error, "" */ + case 1: /* error, followed by error msg */ + case 2: /* fatal error, "" */ do { if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) lostconn(0); @@ -900,7 +896,7 @@ response() } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); if (!iamremote) - (void)write(STDERR_FILENO, rbuf, cp - rbuf); + (void) write(STDERR_FILENO, rbuf, cp - rbuf); ++errs; if (resp == 1) return (-1); @@ -912,13 +908,13 @@ response() void usage() { - (void)fprintf(stderr, - "usage: scp [-pqrvC] [-P port] [-c cipher] [-i identity] f1 f2; or:\n scp [options] f1 ... fn directory\n"); + (void) fprintf(stderr, + "usage: scp [-pqrvC] [-P port] [-c cipher] [-i identity] f1 f2; or:\n scp [options] f1 ... fn directory\n"); exit(1); } void -run_err(const char *fmt, ...) +run_err(const char *fmt,...) { static FILE *fp; va_list ap; @@ -927,18 +923,16 @@ run_err(const char *fmt, ...) ++errs; if (fp == NULL && !(fp = fdopen(remout, "w"))) return; - (void)fprintf(fp, "%c", 0x01); - (void)fprintf(fp, "scp: "); - (void)vfprintf(fp, fmt, ap); - (void)fprintf(fp, "\n"); - (void)fflush(fp); - - if (!iamremote) - { - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - } - + (void) fprintf(fp, "%c", 0x01); + (void) fprintf(fp, "scp: "); + (void) vfprintf(fp, fmt, ap); + (void) fprintf(fp, "\n"); + (void) fflush(fp); + + if (!iamremote) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } va_end(ap); } @@ -1043,17 +1037,17 @@ allocbuf(bp, fd, blksize) run_err("fstat: %s", strerror(errno)); return (0); } - if (stb.st_blksize == 0) - size = blksize; - else - size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % - stb.st_blksize; + if (stb.st_blksize == 0) + size = blksize; + else + size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % + stb.st_blksize; if (bp->cnt >= size) return (bp); - if (bp->buf == NULL) - bp->buf = xmalloc(size); - else - bp->buf = xrealloc(bp->buf, size); + if (bp->buf == NULL) + bp->buf = xmalloc(size); + else + bp->buf = xrealloc(bp->buf, size); bp->cnt = size; return (bp); } @@ -1072,16 +1066,16 @@ lostconn(signo) */ int atomicio(f, fd, s, n) -int (*f)(); -char *s; + int (*f) (); + char *s; { int res, pos = 0; - while (n>pos) { - res = (f)(fd, s+pos, n-pos); + while (n > pos) { + res = (f) (fd, s + pos, n - pos); switch (res) { case -1: - if (errno==EINTR || errno==EAGAIN) + if (errno == EINTR || errno == EAGAIN) continue; case 0: return (res); @@ -1095,12 +1089,12 @@ char *s; void alarmtimer(int wait) { - struct itimerval itv; + struct itimerval itv; - itv.it_value.tv_sec = wait; - itv.it_value.tv_usec = 0; - itv.it_interval = itv.it_value; - setitimer(ITIMER_REAL, &itv, NULL); + itv.it_value.tv_sec = wait; + itv.it_value.tv_usec = 0; + itv.it_interval = itv.it_value; + setitimer(ITIMER_REAL, &itv, NULL); } void @@ -1121,8 +1115,8 @@ foregroundproc() if (pgrp == -1) pgrp = getpgrp(); - return((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && - ctty_pgrp == pgrp)); + return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && + ctty_pgrp == pgrp)); } void @@ -1138,35 +1132,33 @@ progressmeter(int flag) char buf[256]; if (flag == -1) { - (void)gettimeofday(&start, (struct timezone *)0); + (void) gettimeofday(&start, (struct timezone *) 0); lastupdate = start; lastsize = 0; - } + } if (foregroundproc() == 0) return; - (void)gettimeofday(&now, (struct timezone *)0); + (void) gettimeofday(&now, (struct timezone *) 0); cursize = statbytes; if (totalbytes != 0) { ratio = cursize * 100.0 / totalbytes; ratio = MAX(ratio, 0); ratio = MIN(ratio, 100); - } - else + } else ratio = 100; - snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); + snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); barlength = getttywidth() - 51; if (barlength > 0) { i = barlength * ratio / 100; snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "|%.*s%*s|", i, -"*****************************************************************************" -"*****************************************************************************", - barlength - i, ""); + "|%.*s%*s|", i, + "*****************************************************************************" + "*****************************************************************************", + barlength - i, ""); } - i = 0; abbrevsize = cursize; while (abbrevsize >= 100000 && i < sizeof(prefixes)) { @@ -1174,8 +1166,8 @@ progressmeter(int flag) abbrevsize >>= 10; } snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5qd %c%c ", - (quad_t)abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : - 'B'); + (quad_t) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : + 'B'); timersub(&now, &lastupdate, &wait); if (cursize > lastsize) { @@ -1187,33 +1179,32 @@ progressmeter(int flag) } wait.tv_sec = 0; } - timersub(&now, &start, &td); elapsed = td.tv_sec + (td.tv_usec / 1000000.0); if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " --:-- ETA"); + " --:-- ETA"); } else if (wait.tv_sec >= STALLTIME) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " - stalled -"); + " - stalled -"); } else { - remaining = (int)(totalbytes / (statbytes / elapsed) - elapsed); + remaining = (int) (totalbytes / (statbytes / elapsed) - elapsed); i = remaining / 3600; if (i) snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "%2d:", i); + "%2d:", i); else snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " "); + " "); i = remaining % 3600; snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "%02d:%02d ETA", i / 60, i % 60); + "%02d:%02d ETA", i / 60, i % 60); } atomicio(write, fileno(stdout), buf, strlen(buf)); if (flag == -1) { - signal(SIGALRM, (void *)updateprogressmeter); + signal(SIGALRM, (void *) updateprogressmeter); alarmtimer(1); } else if (flag == 1) { alarmtimer(0); @@ -1228,9 +1219,7 @@ getttywidth(void) struct winsize winsize; if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) - return(winsize.ws_col ? winsize.ws_col : 80); + return (winsize.ws_col ? winsize.ws_col : 80); else - return(80); + return (80); } - - diff --git a/servconf.c b/servconf.c index e24aed8d..d0bdc1a8 100644 --- a/servconf.c +++ b/servconf.c @@ -1,15 +1,15 @@ /* - -servconf.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Mon Aug 21 15:48:58 1995 ylo - -*/ + * + * servconf.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Mon Aug 21 15:48:58 1995 ylo + * + */ #include "includes.h" RCSID("$Id$"); @@ -20,535 +20,514 @@ RCSID("$Id$"); /* Initializes the server options to their default values. */ -void initialize_server_options(ServerOptions *options) +void +initialize_server_options(ServerOptions *options) { - memset(options, 0, sizeof(*options)); - options->port = -1; - options->listen_addr.s_addr = htonl(INADDR_ANY); - options->host_key_file = NULL; - options->server_key_bits = -1; - options->login_grace_time = -1; - options->key_regeneration_time = -1; - options->permit_root_login = -1; - options->ignore_rhosts = -1; - options->ignore_user_known_hosts = -1; - options->print_motd = -1; - options->check_mail = -1; - options->x11_forwarding = -1; - options->x11_display_offset = -1; - options->strict_modes = -1; - options->keepalives = -1; - options->log_facility = (SyslogFacility)-1; - options->log_level = (LogLevel)-1; - options->rhosts_authentication = -1; - options->rhosts_rsa_authentication = -1; - options->rsa_authentication = -1; + memset(options, 0, sizeof(*options)); + options->port = -1; + options->listen_addr.s_addr = htonl(INADDR_ANY); + options->host_key_file = NULL; + options->server_key_bits = -1; + options->login_grace_time = -1; + options->key_regeneration_time = -1; + options->permit_root_login = -1; + options->ignore_rhosts = -1; + options->ignore_user_known_hosts = -1; + options->print_motd = -1; + options->check_mail = -1; + options->x11_forwarding = -1; + options->x11_display_offset = -1; + options->strict_modes = -1; + options->keepalives = -1; + options->log_facility = (SyslogFacility) - 1; + options->log_level = (LogLevel) - 1; + options->rhosts_authentication = -1; + options->rhosts_rsa_authentication = -1; + options->rsa_authentication = -1; #ifdef KRB4 - options->kerberos_authentication = -1; - options->kerberos_or_local_passwd = -1; - options->kerberos_ticket_cleanup = -1; + options->kerberos_authentication = -1; + options->kerberos_or_local_passwd = -1; + options->kerberos_ticket_cleanup = -1; #endif #ifdef AFS - options->kerberos_tgt_passing = -1; - options->afs_token_passing = -1; + options->kerberos_tgt_passing = -1; + options->afs_token_passing = -1; #endif - options->password_authentication = -1; + options->password_authentication = -1; #ifdef SKEY - options->skey_authentication = -1; + options->skey_authentication = -1; #endif - options->permit_empty_passwd = -1; - options->use_login = -1; - options->num_allow_users = 0; - options->num_deny_users = 0; - options->num_allow_groups = 0; - options->num_deny_groups = 0; + options->permit_empty_passwd = -1; + options->use_login = -1; + options->num_allow_users = 0; + options->num_deny_users = 0; + options->num_allow_groups = 0; + options->num_deny_groups = 0; } -void fill_default_server_options(ServerOptions *options) +void +fill_default_server_options(ServerOptions *options) { - if (options->port == -1) - { - struct servent *sp; - - sp = getservbyname(SSH_SERVICE_NAME, "tcp"); - if (sp) - options->port = ntohs(sp->s_port); - else - options->port = SSH_DEFAULT_PORT; - endservent(); - } - if (options->host_key_file == NULL) - options->host_key_file = HOST_KEY_FILE; - if (options->server_key_bits == -1) - options->server_key_bits = 768; - if (options->login_grace_time == -1) - options->login_grace_time = 600; - if (options->key_regeneration_time == -1) - options->key_regeneration_time = 3600; - if (options->permit_root_login == -1) - options->permit_root_login = 1; /* yes */ - if (options->ignore_rhosts == -1) - options->ignore_rhosts = 0; - if (options->ignore_user_known_hosts == -1) - options->ignore_user_known_hosts = 0; - if (options->check_mail == -1) - options->check_mail = 0; - if (options->print_motd == -1) - options->print_motd = 1; - if (options->x11_forwarding == -1) - options->x11_forwarding = 1; - if (options->x11_display_offset == -1) - options->x11_display_offset = 1; - if (options->strict_modes == -1) - options->strict_modes = 1; - if (options->keepalives == -1) - options->keepalives = 1; - if (options->log_facility == (SyslogFacility)(-1)) - options->log_facility = SYSLOG_FACILITY_AUTH; - if (options->log_level == (LogLevel)(-1)) - options->log_level = SYSLOG_LEVEL_INFO; - if (options->rhosts_authentication == -1) - options->rhosts_authentication = 0; - if (options->rhosts_rsa_authentication == -1) - options->rhosts_rsa_authentication = 1; - if (options->rsa_authentication == -1) - options->rsa_authentication = 1; + if (options->port == -1) { + struct servent *sp; + + sp = getservbyname(SSH_SERVICE_NAME, "tcp"); + if (sp) + options->port = ntohs(sp->s_port); + else + options->port = SSH_DEFAULT_PORT; + endservent(); + } + if (options->host_key_file == NULL) + options->host_key_file = HOST_KEY_FILE; + if (options->server_key_bits == -1) + options->server_key_bits = 768; + if (options->login_grace_time == -1) + options->login_grace_time = 600; + if (options->key_regeneration_time == -1) + options->key_regeneration_time = 3600; + if (options->permit_root_login == -1) + options->permit_root_login = 1; /* yes */ + if (options->ignore_rhosts == -1) + options->ignore_rhosts = 0; + if (options->ignore_user_known_hosts == -1) + options->ignore_user_known_hosts = 0; + if (options->check_mail == -1) + options->check_mail = 0; + if (options->print_motd == -1) + options->print_motd = 1; + if (options->x11_forwarding == -1) + options->x11_forwarding = 1; + if (options->x11_display_offset == -1) + options->x11_display_offset = 1; + if (options->strict_modes == -1) + options->strict_modes = 1; + if (options->keepalives == -1) + options->keepalives = 1; + if (options->log_facility == (SyslogFacility) (-1)) + options->log_facility = SYSLOG_FACILITY_AUTH; + if (options->log_level == (LogLevel) (-1)) + options->log_level = SYSLOG_LEVEL_INFO; + if (options->rhosts_authentication == -1) + options->rhosts_authentication = 0; + if (options->rhosts_rsa_authentication == -1) + options->rhosts_rsa_authentication = 1; + if (options->rsa_authentication == -1) + options->rsa_authentication = 1; #ifdef KRB4 - if (options->kerberos_authentication == -1) - options->kerberos_authentication = (access(KEYFILE, R_OK) == 0); - if (options->kerberos_or_local_passwd == -1) - options->kerberos_or_local_passwd = 1; - if (options->kerberos_ticket_cleanup == -1) - options->kerberos_ticket_cleanup = 1; + if (options->kerberos_authentication == -1) + options->kerberos_authentication = (access(KEYFILE, R_OK) == 0); + if (options->kerberos_or_local_passwd == -1) + options->kerberos_or_local_passwd = 1; + if (options->kerberos_ticket_cleanup == -1) + options->kerberos_ticket_cleanup = 1; #endif /* KRB4 */ #ifdef AFS - if (options->kerberos_tgt_passing == -1) - options->kerberos_tgt_passing = 0; - if (options->afs_token_passing == -1) - options->afs_token_passing = k_hasafs(); + if (options->kerberos_tgt_passing == -1) + options->kerberos_tgt_passing = 0; + if (options->afs_token_passing == -1) + options->afs_token_passing = k_hasafs(); #endif /* AFS */ - if (options->password_authentication == -1) - options->password_authentication = 1; + if (options->password_authentication == -1) + options->password_authentication = 1; #ifdef SKEY - if (options->skey_authentication == -1) - options->skey_authentication = 1; + if (options->skey_authentication == -1) + options->skey_authentication = 1; #endif - if (options->permit_empty_passwd == -1) - options->permit_empty_passwd = 1; - if (options->use_login == -1) - options->use_login = 0; + if (options->permit_empty_passwd == -1) + options->permit_empty_passwd = 1; + if (options->use_login == -1) + options->use_login = 0; } #define WHITESPACE " \t\r\n" /* Keyword tokens. */ -typedef enum -{ - sBadOption, /* == unknown option */ - sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime, - sPermitRootLogin, sLogFacility, sLogLevel, - sRhostsAuthentication, sRhostsRSAAuthentication, sRSAAuthentication, +typedef enum { + sBadOption, /* == unknown option */ + sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime, + sPermitRootLogin, sLogFacility, sLogLevel, + sRhostsAuthentication, sRhostsRSAAuthentication, sRSAAuthentication, #ifdef KRB4 - sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, + sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, #endif #ifdef AFS - sKerberosTgtPassing, sAFSTokenPassing, + sKerberosTgtPassing, sAFSTokenPassing, #endif #ifdef SKEY - sSkeyAuthentication, + sSkeyAuthentication, #endif - sPasswordAuthentication, sListenAddress, - sPrintMotd, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset, - sStrictModes, sEmptyPasswd, sRandomSeedFile, sKeepAlives, sCheckMail, - sUseLogin, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, - sIgnoreUserKnownHosts + sPasswordAuthentication, sListenAddress, + sPrintMotd, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset, + sStrictModes, sEmptyPasswd, sRandomSeedFile, sKeepAlives, sCheckMail, + sUseLogin, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, + sIgnoreUserKnownHosts } ServerOpCodes; /* Textual representation of the tokens. */ -static struct -{ - const char *name; - ServerOpCodes opcode; -} keywords[] = -{ - { "port", sPort }, - { "hostkey", sHostKeyFile }, - { "serverkeybits", sServerKeyBits }, - { "logingracetime", sLoginGraceTime }, - { "keyregenerationinterval", sKeyRegenerationTime }, - { "permitrootlogin", sPermitRootLogin }, - { "syslogfacility", sLogFacility }, - { "loglevel", sLogLevel }, - { "rhostsauthentication", sRhostsAuthentication }, - { "rhostsrsaauthentication", sRhostsRSAAuthentication }, - { "rsaauthentication", sRSAAuthentication }, +static struct { + const char *name; + ServerOpCodes opcode; +} keywords[] = { + { "port", sPort }, + { "hostkey", sHostKeyFile }, + { "serverkeybits", sServerKeyBits }, + { "logingracetime", sLoginGraceTime }, + { "keyregenerationinterval", sKeyRegenerationTime }, + { "permitrootlogin", sPermitRootLogin }, + { "syslogfacility", sLogFacility }, + { "loglevel", sLogLevel }, + { "rhostsauthentication", sRhostsAuthentication }, + { "rhostsrsaauthentication", sRhostsRSAAuthentication }, + { "rsaauthentication", sRSAAuthentication }, #ifdef KRB4 - { "kerberosauthentication", sKerberosAuthentication }, - { "kerberosorlocalpasswd", sKerberosOrLocalPasswd }, - { "kerberosticketcleanup", sKerberosTicketCleanup }, + { "kerberosauthentication", sKerberosAuthentication }, + { "kerberosorlocalpasswd", sKerberosOrLocalPasswd }, + { "kerberosticketcleanup", sKerberosTicketCleanup }, #endif #ifdef AFS - { "kerberostgtpassing", sKerberosTgtPassing }, - { "afstokenpassing", sAFSTokenPassing }, + { "kerberostgtpassing", sKerberosTgtPassing }, + { "afstokenpassing", sAFSTokenPassing }, #endif - { "passwordauthentication", sPasswordAuthentication }, + { "passwordauthentication", sPasswordAuthentication }, #ifdef SKEY - { "skeyauthentication", sSkeyAuthentication }, + { "skeyauthentication", sSkeyAuthentication }, #endif - { "checkmail", sCheckMail }, - { "listenaddress", sListenAddress }, - { "printmotd", sPrintMotd }, - { "ignorerhosts", sIgnoreRhosts }, - { "ignoreuserknownhosts", sIgnoreUserKnownHosts }, - { "x11forwarding", sX11Forwarding }, - { "x11displayoffset", sX11DisplayOffset }, - { "strictmodes", sStrictModes }, - { "permitemptypasswords", sEmptyPasswd }, - { "uselogin", sUseLogin }, - { "randomseed", sRandomSeedFile }, - { "keepalive", sKeepAlives }, - { "allowusers", sAllowUsers }, - { "denyusers", sDenyUsers }, - { "allowgroups", sAllowGroups }, - { "denygroups", sDenyGroups }, - { NULL, 0 } + { "checkmail", sCheckMail }, + { "listenaddress", sListenAddress }, + { "printmotd", sPrintMotd }, + { "ignorerhosts", sIgnoreRhosts }, + { "ignoreuserknownhosts", sIgnoreUserKnownHosts }, + { "x11forwarding", sX11Forwarding }, + { "x11displayoffset", sX11DisplayOffset }, + { "strictmodes", sStrictModes }, + { "permitemptypasswords", sEmptyPasswd }, + { "uselogin", sUseLogin }, + { "randomseed", sRandomSeedFile }, + { "keepalive", sKeepAlives }, + { "allowusers", sAllowUsers }, + { "denyusers", sDenyUsers }, + { "allowgroups", sAllowGroups }, + { "denygroups", sDenyGroups }, + { NULL, 0 } }; /* Returns the number of the token pointed to by cp of length len. Never returns if the token is not known. */ -static ServerOpCodes parse_token(const char *cp, const char *filename, - int linenum) +static ServerOpCodes +parse_token(const char *cp, const char *filename, + int linenum) { - unsigned int i; + unsigned int i; - for (i = 0; keywords[i].name; i++) - if (strcmp(cp, keywords[i].name) == 0) - return keywords[i].opcode; + for (i = 0; keywords[i].name; i++) + if (strcmp(cp, keywords[i].name) == 0) + return keywords[i].opcode; - fprintf(stderr, "%s: line %d: Bad configuration option: %s\n", - filename, linenum, cp); - return sBadOption; + fprintf(stderr, "%s: line %d: Bad configuration option: %s\n", + filename, linenum, cp); + return sBadOption; } /* Reads the server configuration file. */ -void read_server_config(ServerOptions *options, const char *filename) +void +read_server_config(ServerOptions *options, const char *filename) { - FILE *f; - char line[1024]; - char *cp, **charptr; - int linenum, *intptr, value; - int bad_options = 0; - ServerOpCodes opcode; - - f = fopen(filename, "r"); - if (!f) - { - perror(filename); - exit(1); - } - - linenum = 0; - while (fgets(line, sizeof(line), f)) - { - linenum++; - cp = line + strspn(line, WHITESPACE); - if (!*cp || *cp == '#') - continue; - cp = strtok(cp, WHITESPACE); - { - char *t = cp; - for (; *t != 0; t++) - if ('A' <= *t && *t <= 'Z') - *t = *t - 'A' + 'a'; /* tolower */ - - } - opcode = parse_token(cp, filename, linenum); - switch (opcode) - { - case sBadOption: - bad_options++; - continue; - case sPort: - intptr = &options->port; - parse_int: - cp = strtok(NULL, WHITESPACE); - if (!cp) - { - fprintf(stderr, "%s line %d: missing integer value.\n", - filename, linenum); - exit(1); - } - value = atoi(cp); - if (*intptr == -1) - *intptr = value; - break; - - case sServerKeyBits: - intptr = &options->server_key_bits; - goto parse_int; - - case sLoginGraceTime: - intptr = &options->login_grace_time; - goto parse_int; - - case sKeyRegenerationTime: - intptr = &options->key_regeneration_time; - goto parse_int; - - case sListenAddress: - cp = strtok(NULL, WHITESPACE); - if (!cp) - { - fprintf(stderr, "%s line %d: missing inet addr.\n", - filename, linenum); - exit(1); - } - options->listen_addr.s_addr = inet_addr(cp); - break; - - case sHostKeyFile: - charptr = &options->host_key_file; - cp = strtok(NULL, WHITESPACE); - if (!cp) - { - fprintf(stderr, "%s line %d: missing file name.\n", - filename, linenum); - exit(1); - } - if (*charptr == NULL) - *charptr = tilde_expand_filename(cp, getuid()); - break; - - case sRandomSeedFile: - fprintf(stderr, "%s line %d: \"randomseed\" option is obsolete.\n", - filename, linenum); - cp = strtok(NULL, WHITESPACE); - break; - - case sPermitRootLogin: - intptr = &options->permit_root_login; - cp = strtok(NULL, WHITESPACE); - if (!cp) - { - fprintf(stderr, "%s line %d: missing yes/without-password/no argument.\n", - filename, linenum); - exit(1); - } - if (strcmp(cp, "without-password") == 0) - value = 2; - else if (strcmp(cp, "yes") == 0) - value = 1; - else if (strcmp(cp, "no") == 0) - value = 0; - else - { - fprintf(stderr, "%s line %d: Bad yes/without-password/no argument: %s\n", - filename, linenum, cp); - exit(1); - } - if (*intptr == -1) - *intptr = value; - break; - - case sIgnoreRhosts: - intptr = &options->ignore_rhosts; - parse_flag: - cp = strtok(NULL, WHITESPACE); - if (!cp) - { - fprintf(stderr, "%s line %d: missing yes/no argument.\n", - filename, linenum); - exit(1); - } - if (strcmp(cp, "yes") == 0) - value = 1; - else - if (strcmp(cp, "no") == 0) - value = 0; - else - { - fprintf(stderr, "%s line %d: Bad yes/no argument: %s\n", - filename, linenum, cp); + FILE *f; + char line[1024]; + char *cp, **charptr; + int linenum, *intptr, value; + int bad_options = 0; + ServerOpCodes opcode; + + f = fopen(filename, "r"); + if (!f) { + perror(filename); exit(1); - } - if (*intptr == -1) - *intptr = value; - break; - - case sIgnoreUserKnownHosts: - intptr = &options->ignore_user_known_hosts; - goto parse_int; - - case sRhostsAuthentication: - intptr = &options->rhosts_authentication; - goto parse_flag; - - case sRhostsRSAAuthentication: - intptr = &options->rhosts_rsa_authentication; - goto parse_flag; - - case sRSAAuthentication: - intptr = &options->rsa_authentication; - goto parse_flag; - + } + linenum = 0; + while (fgets(line, sizeof(line), f)) { + linenum++; + cp = line + strspn(line, WHITESPACE); + if (!*cp || *cp == '#') + continue; + cp = strtok(cp, WHITESPACE); + { + char *t = cp; + for (; *t != 0; t++) + if ('A' <= *t && *t <= 'Z') + *t = *t - 'A' + 'a'; /* tolower */ + + } + opcode = parse_token(cp, filename, linenum); + switch (opcode) { + case sBadOption: + bad_options++; + continue; + case sPort: + intptr = &options->port; +parse_int: + cp = strtok(NULL, WHITESPACE); + if (!cp) { + fprintf(stderr, "%s line %d: missing integer value.\n", + filename, linenum); + exit(1); + } + value = atoi(cp); + if (*intptr == -1) + *intptr = value; + break; + + case sServerKeyBits: + intptr = &options->server_key_bits; + goto parse_int; + + case sLoginGraceTime: + intptr = &options->login_grace_time; + goto parse_int; + + case sKeyRegenerationTime: + intptr = &options->key_regeneration_time; + goto parse_int; + + case sListenAddress: + cp = strtok(NULL, WHITESPACE); + if (!cp) { + fprintf(stderr, "%s line %d: missing inet addr.\n", + filename, linenum); + exit(1); + } + options->listen_addr.s_addr = inet_addr(cp); + break; + + case sHostKeyFile: + charptr = &options->host_key_file; + cp = strtok(NULL, WHITESPACE); + if (!cp) { + fprintf(stderr, "%s line %d: missing file name.\n", + filename, linenum); + exit(1); + } + if (*charptr == NULL) + *charptr = tilde_expand_filename(cp, getuid()); + break; + + case sRandomSeedFile: + fprintf(stderr, "%s line %d: \"randomseed\" option is obsolete.\n", + filename, linenum); + cp = strtok(NULL, WHITESPACE); + break; + + case sPermitRootLogin: + intptr = &options->permit_root_login; + cp = strtok(NULL, WHITESPACE); + if (!cp) { + fprintf(stderr, "%s line %d: missing yes/without-password/no argument.\n", + filename, linenum); + exit(1); + } + if (strcmp(cp, "without-password") == 0) + value = 2; + else if (strcmp(cp, "yes") == 0) + value = 1; + else if (strcmp(cp, "no") == 0) + value = 0; + else { + fprintf(stderr, "%s line %d: Bad yes/without-password/no argument: %s\n", + filename, linenum, cp); + exit(1); + } + if (*intptr == -1) + *intptr = value; + break; + + case sIgnoreRhosts: + intptr = &options->ignore_rhosts; +parse_flag: + cp = strtok(NULL, WHITESPACE); + if (!cp) { + fprintf(stderr, "%s line %d: missing yes/no argument.\n", + filename, linenum); + exit(1); + } + if (strcmp(cp, "yes") == 0) + value = 1; + else if (strcmp(cp, "no") == 0) + value = 0; + else { + fprintf(stderr, "%s line %d: Bad yes/no argument: %s\n", + filename, linenum, cp); + exit(1); + } + if (*intptr == -1) + *intptr = value; + break; + + case sIgnoreUserKnownHosts: + intptr = &options->ignore_user_known_hosts; + goto parse_int; + + case sRhostsAuthentication: + intptr = &options->rhosts_authentication; + goto parse_flag; + + case sRhostsRSAAuthentication: + intptr = &options->rhosts_rsa_authentication; + goto parse_flag; + + case sRSAAuthentication: + intptr = &options->rsa_authentication; + goto parse_flag; + #ifdef KRB4 - case sKerberosAuthentication: - intptr = &options->kerberos_authentication; - goto parse_flag; - - case sKerberosOrLocalPasswd: - intptr = &options->kerberos_or_local_passwd; - goto parse_flag; - - case sKerberosTicketCleanup: - intptr = &options->kerberos_ticket_cleanup; - goto parse_flag; + case sKerberosAuthentication: + intptr = &options->kerberos_authentication; + goto parse_flag; + + case sKerberosOrLocalPasswd: + intptr = &options->kerberos_or_local_passwd; + goto parse_flag; + + case sKerberosTicketCleanup: + intptr = &options->kerberos_ticket_cleanup; + goto parse_flag; #endif - + #ifdef AFS - case sKerberosTgtPassing: - intptr = &options->kerberos_tgt_passing; - goto parse_flag; + case sKerberosTgtPassing: + intptr = &options->kerberos_tgt_passing; + goto parse_flag; - case sAFSTokenPassing: - intptr = &options->afs_token_passing; - goto parse_flag; + case sAFSTokenPassing: + intptr = &options->afs_token_passing; + goto parse_flag; #endif - case sPasswordAuthentication: - intptr = &options->password_authentication; - goto parse_flag; + case sPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; - case sCheckMail: - intptr = &options->check_mail; - goto parse_flag; + case sCheckMail: + intptr = &options->check_mail; + goto parse_flag; #ifdef SKEY - case sSkeyAuthentication: - intptr = &options->skey_authentication; - goto parse_flag; + case sSkeyAuthentication: + intptr = &options->skey_authentication; + goto parse_flag; #endif - case sPrintMotd: - intptr = &options->print_motd; - goto parse_flag; - - case sX11Forwarding: - intptr = &options->x11_forwarding; - goto parse_flag; - - case sX11DisplayOffset: - intptr = &options->x11_display_offset; - goto parse_int; - - case sStrictModes: - intptr = &options->strict_modes; - goto parse_flag; - - case sKeepAlives: - intptr = &options->keepalives; - goto parse_flag; - - case sEmptyPasswd: - intptr = &options->permit_empty_passwd; - goto parse_flag; - - case sUseLogin: - intptr = &options->use_login; - goto parse_flag; - - case sLogFacility: - intptr = (int *)&options->log_facility; - cp = strtok(NULL, WHITESPACE); - value = log_facility_number(cp); - if (value == (SyslogFacility)-1) - fatal("%.200s line %d: unsupported log facility '%s'\n", - filename, linenum, cp ? cp : ""); - if (*intptr == -1) - *intptr = (SyslogFacility)value; - break; - - case sLogLevel: - intptr = (int *)&options->log_level; - cp = strtok(NULL, WHITESPACE); - value = log_level_number(cp); - if (value == (LogLevel)-1) - fatal("%.200s line %d: unsupported log level '%s'\n", - filename, linenum, cp ? cp : ""); - if (*intptr == -1) - *intptr = (LogLevel)value; - break; - - case sAllowUsers: - while ((cp = strtok(NULL, WHITESPACE))) - { - if (options->num_allow_users >= MAX_ALLOW_USERS) - { - fprintf(stderr, "%s line %d: too many allow users.\n", - filename, linenum); - exit(1); + case sPrintMotd: + intptr = &options->print_motd; + goto parse_flag; + + case sX11Forwarding: + intptr = &options->x11_forwarding; + goto parse_flag; + + case sX11DisplayOffset: + intptr = &options->x11_display_offset; + goto parse_int; + + case sStrictModes: + intptr = &options->strict_modes; + goto parse_flag; + + case sKeepAlives: + intptr = &options->keepalives; + goto parse_flag; + + case sEmptyPasswd: + intptr = &options->permit_empty_passwd; + goto parse_flag; + + case sUseLogin: + intptr = &options->use_login; + goto parse_flag; + + case sLogFacility: + intptr = (int *) &options->log_facility; + cp = strtok(NULL, WHITESPACE); + value = log_facility_number(cp); + if (value == (SyslogFacility) - 1) + fatal("%.200s line %d: unsupported log facility '%s'\n", + filename, linenum, cp ? cp : ""); + if (*intptr == -1) + *intptr = (SyslogFacility) value; + break; + + case sLogLevel: + intptr = (int *) &options->log_level; + cp = strtok(NULL, WHITESPACE); + value = log_level_number(cp); + if (value == (LogLevel) - 1) + fatal("%.200s line %d: unsupported log level '%s'\n", + filename, linenum, cp ? cp : ""); + if (*intptr == -1) + *intptr = (LogLevel) value; + break; + + case sAllowUsers: + while ((cp = strtok(NULL, WHITESPACE))) { + if (options->num_allow_users >= MAX_ALLOW_USERS) { + fprintf(stderr, "%s line %d: too many allow users.\n", + filename, linenum); + exit(1); + } + options->allow_users[options->num_allow_users++] = xstrdup(cp); + } + break; + + case sDenyUsers: + while ((cp = strtok(NULL, WHITESPACE))) { + if (options->num_deny_users >= MAX_DENY_USERS) { + fprintf(stderr, "%s line %d: too many deny users.\n", + filename, linenum); + exit(1); + } + options->deny_users[options->num_deny_users++] = xstrdup(cp); + } + break; + + case sAllowGroups: + while ((cp = strtok(NULL, WHITESPACE))) { + if (options->num_allow_groups >= MAX_ALLOW_GROUPS) { + fprintf(stderr, "%s line %d: too many allow groups.\n", + filename, linenum); + exit(1); + } + options->allow_groups[options->num_allow_groups++] = xstrdup(cp); + } + break; + + case sDenyGroups: + while ((cp = strtok(NULL, WHITESPACE))) { + if (options->num_deny_groups >= MAX_DENY_GROUPS) { + fprintf(stderr, "%s line %d: too many deny groups.\n", + filename, linenum); + exit(1); + } + options->deny_groups[options->num_deny_groups++] = xstrdup(cp); + } + break; + + default: + fprintf(stderr, "%s line %d: Missing handler for opcode %s (%d)\n", + filename, linenum, cp, opcode); + exit(1); } - options->allow_users[options->num_allow_users++] = xstrdup(cp); - } - break; - - case sDenyUsers: - while ((cp = strtok(NULL, WHITESPACE))) - { - if (options->num_deny_users >= MAX_DENY_USERS) - { - fprintf(stderr, "%s line %d: too many deny users.\n", - filename, linenum); - exit(1); - } - options->deny_users[options->num_deny_users++] = xstrdup(cp); - } - break; - - case sAllowGroups: - while ((cp = strtok(NULL, WHITESPACE))) - { - if (options->num_allow_groups >= MAX_ALLOW_GROUPS) - { - fprintf(stderr, "%s line %d: too many allow groups.\n", - filename, linenum); - exit(1); + if (strtok(NULL, WHITESPACE) != NULL) { + fprintf(stderr, "%s line %d: garbage at end of line.\n", + filename, linenum); + exit(1); } - options->allow_groups[options->num_allow_groups++] = xstrdup(cp); - } - break; - - case sDenyGroups: - while ((cp = strtok(NULL, WHITESPACE))) - { - if (options->num_deny_groups >= MAX_DENY_GROUPS) - { - fprintf(stderr, "%s line %d: too many deny groups.\n", - filename, linenum); - exit(1); - } - options->deny_groups[options->num_deny_groups++] = xstrdup(cp); - } - break; - - default: - fprintf(stderr, "%s line %d: Missing handler for opcode %s (%d)\n", - filename, linenum, cp, opcode); - exit(1); } - if (strtok(NULL, WHITESPACE) != NULL) - { - fprintf(stderr, "%s line %d: garbage at end of line.\n", - filename, linenum); - exit(1); + fclose(f); + if (bad_options > 0) { + fprintf(stderr, "%s: terminating, %d bad configuration options\n", + filename, bad_options); + exit(1); } - } - fclose(f); - if (bad_options > 0) { - fprintf(stderr, "%s: terminating, %d bad configuration options\n", - filename, bad_options); - exit(1); - } } diff --git a/servconf.h b/servconf.h index 59420d55..e227ba13 100644 --- a/servconf.h +++ b/servconf.h @@ -1,86 +1,98 @@ /* - -servconf.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Mon Aug 21 15:35:03 1995 ylo - -Definitions for server configuration data and for the functions reading it. - -*/ + * + * servconf.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Mon Aug 21 15:35:03 1995 ylo + * + * Definitions for server configuration data and for the functions reading it. + * + */ /* RCSID("$Id$"); */ #ifndef SERVCONF_H #define SERVCONF_H -#define MAX_ALLOW_USERS 256 /* Max # users on allow list. */ -#define MAX_DENY_USERS 256 /* Max # users on deny list. */ -#define MAX_ALLOW_GROUPS 256 /* Max # groups on allow list. */ -#define MAX_DENY_GROUPS 256 /* Max # groups on deny list. */ +#define MAX_ALLOW_USERS 256 /* Max # users on allow list. */ +#define MAX_DENY_USERS 256 /* Max # users on deny list. */ +#define MAX_ALLOW_GROUPS 256 /* Max # groups on allow list. */ +#define MAX_DENY_GROUPS 256 /* Max # groups on deny list. */ -typedef struct -{ - int port; /* Port number to listen on. */ - struct in_addr listen_addr; /* Address on which the server listens. */ - char *host_key_file; /* File containing host key. */ - int server_key_bits; /* Size of the server key. */ - int login_grace_time; /* Disconnect if no auth in this time (sec). */ - int key_regeneration_time; /* Server key lifetime (seconds). */ - int permit_root_login; /* If true, permit root login. */ - int ignore_rhosts; /* Ignore .rhosts and .shosts. */ - int ignore_user_known_hosts; /* Ignore ~/.ssh/known_hosts for RhostsRsaAuth */ - int print_motd; /* If true, print /etc/motd. */ - int check_mail; /* If true, check for new mail. */ - int x11_forwarding; /* If true, permit inet (spoofing) X11 fwd. */ - int x11_display_offset; /* What DISPLAY number to start searching at */ - int strict_modes; /* If true, require string home dir modes. */ - int keepalives; /* If true, set SO_KEEPALIVE. */ - SyslogFacility log_facility; /* Facility for system logging. */ - LogLevel log_level; /* Level for system logging. */ - int rhosts_authentication; /* If true, permit rhosts authentication. */ - int rhosts_rsa_authentication;/* If true, permit rhosts RSA authentication.*/ - int rsa_authentication; /* If true, permit RSA authentication. */ +typedef struct { + int port; /* Port number to listen on. */ + struct in_addr listen_addr; /* Address on which the server + * listens. */ + char *host_key_file; /* File containing host key. */ + int server_key_bits;/* Size of the server key. */ + int login_grace_time; /* Disconnect if no auth in this time + * (sec). */ + int key_regeneration_time; /* Server key lifetime (seconds). */ + int permit_root_login; /* If true, permit root login. */ + int ignore_rhosts; /* Ignore .rhosts and .shosts. */ + int ignore_user_known_hosts; /* Ignore ~/.ssh/known_hosts + * for RhostsRsaAuth */ + int print_motd; /* If true, print /etc/motd. */ + int check_mail; /* If true, check for new mail. */ + int x11_forwarding; /* If true, permit inet (spoofing) X11 fwd. */ + int x11_display_offset; /* What DISPLAY number to start + * searching at */ + int strict_modes; /* If true, require string home dir modes. */ + int keepalives; /* If true, set SO_KEEPALIVE. */ + SyslogFacility log_facility; /* Facility for system logging. */ + LogLevel log_level; /* Level for system logging. */ + int rhosts_authentication; /* If true, permit rhosts + * authentication. */ + int rhosts_rsa_authentication; /* If true, permit rhosts RSA + * authentication. */ + int rsa_authentication; /* If true, permit RSA authentication. */ #ifdef KRB4 - int kerberos_authentication; /* If true, permit Kerberos authentication. */ - int kerberos_or_local_passwd; /* If true, permit kerberos and any other - password authentication mechanism, such - as SecurID or /etc/passwd */ - int kerberos_ticket_cleanup; /* If true, destroy ticket file on logout. */ + int kerberos_authentication; /* If true, permit Kerberos + * authentication. */ + int kerberos_or_local_passwd; /* If true, permit kerberos + * and any other password + * authentication mechanism, + * such as SecurID or + * /etc/passwd */ + int kerberos_ticket_cleanup; /* If true, destroy ticket + * file on logout. */ #endif #ifdef AFS - int kerberos_tgt_passing; /* If true, permit Kerberos tgt passing. */ - int afs_token_passing; /* If true, permit AFS token passing. */ + int kerberos_tgt_passing; /* If true, permit Kerberos tgt + * passing. */ + int afs_token_passing; /* If true, permit AFS token passing. */ #endif - int password_authentication; /* If true, permit password authentication. */ + int password_authentication; /* If true, permit password + * authentication. */ #ifdef SKEY - int skey_authentication; /* If true, permit s/key authentication. */ + int skey_authentication; /* If true, permit s/key + * authentication. */ #endif - int permit_empty_passwd; /* If false, do not permit empty passwords. */ - int use_login; /* If true, login(1) is used */ - unsigned int num_allow_users; - char *allow_users[MAX_ALLOW_USERS]; - unsigned int num_deny_users; - char *deny_users[MAX_DENY_USERS]; - unsigned int num_allow_groups; - char *allow_groups[MAX_ALLOW_GROUPS]; - unsigned int num_deny_groups; - char *deny_groups[MAX_DENY_GROUPS]; -} ServerOptions; - + int permit_empty_passwd; /* If false, do not permit empty + * passwords. */ + int use_login; /* If true, login(1) is used */ + unsigned int num_allow_users; + char *allow_users[MAX_ALLOW_USERS]; + unsigned int num_deny_users; + char *deny_users[MAX_DENY_USERS]; + unsigned int num_allow_groups; + char *allow_groups[MAX_ALLOW_GROUPS]; + unsigned int num_deny_groups; + char *deny_groups[MAX_DENY_GROUPS]; +} ServerOptions; /* Initializes the server options to special values that indicate that they have not yet been set. */ -void initialize_server_options(ServerOptions *options); +void initialize_server_options(ServerOptions * options); /* Reads the server configuration file. This only sets the values for those options that have the special value indicating they have not been set. */ -void read_server_config(ServerOptions *options, const char *filename); +void read_server_config(ServerOptions * options, const char *filename); /* Sets values for those values that have not yet been set. */ -void fill_default_server_options(ServerOptions *options); +void fill_default_server_options(ServerOptions * options); -#endif /* SERVCONF_H */ +#endif /* SERVCONF_H */ diff --git a/serverloop.c b/serverloop.c index 9961170a..fc959bae 100644 --- a/serverloop.c +++ b/serverloop.c @@ -1,17 +1,10 @@ /* - -serverloop.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sun Sep 10 00:30:37 1995 ylo - -Server main loop for handling the interactive session. - -*/ + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Sun Sep 10 00:30:37 1995 ylo + * Server main loop for handling the interactive session. + */ #include "includes.h" #include "xmalloc.h" @@ -43,603 +36,594 @@ static int max_fd; /* Max file descriptor number for select(). */ /* This SIGCHLD kludge is used to detect when the child exits. The server will exit after that, as soon as forwarded connections have terminated. */ -static int child_pid; /* Pid of the child. */ +static int child_pid; /* Pid of the child. */ static volatile int child_terminated; /* The child has terminated. */ static volatile int child_wait_status; /* Status from wait(). */ -void sigchld_handler(int sig) +void +sigchld_handler(int sig) { - int save_errno = errno; - int wait_pid; - debug("Received SIGCHLD."); - wait_pid = wait((int *)&child_wait_status); - if (wait_pid != -1) - { - if (wait_pid != child_pid) - error("Strange, got SIGCHLD and wait returned pid %d but child is %d", - wait_pid, child_pid); - if (WIFEXITED(child_wait_status) || - WIFSIGNALED(child_wait_status)) - child_terminated = 1; - } - signal(SIGCHLD, sigchld_handler); - errno = save_errno; + int save_errno = errno; + int wait_pid; + debug("Received SIGCHLD."); + wait_pid = wait((int *) &child_wait_status); + if (wait_pid != -1) { + if (wait_pid != child_pid) + error("Strange, got SIGCHLD and wait returned pid %d but child is %d", + wait_pid, child_pid); + if (WIFEXITED(child_wait_status) || + WIFSIGNALED(child_wait_status)) + child_terminated = 1; + } + signal(SIGCHLD, sigchld_handler); + errno = save_errno; } -/* Process any buffered packets that have been received from the client. */ - -void process_buffered_input_packets() +/* + * Process any buffered packets that have been received from the client. + */ +void +process_buffered_input_packets() { - int type; - char *data; - unsigned int data_len; - int row, col, xpixel, ypixel; - int payload_len; - - /* Process buffered packets from the client. */ - while ((type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) - { - switch (type) - { - case SSH_CMSG_STDIN_DATA: - /* Stdin data from the client. Append it to the buffer. */ - if (fdin == -1) - break; /* Ignore any data if the client has closed stdin. */ - data = packet_get_string(&data_len); - packet_integrity_check(payload_len, (4 + data_len), type); - buffer_append(&stdin_buffer, data, data_len); - memset(data, 0, data_len); - xfree(data); - break; - - case SSH_CMSG_EOF: - /* Eof from the client. The stdin descriptor to the program - will be closed when all buffered data has drained. */ - debug("EOF received for stdin."); - packet_integrity_check(payload_len, 0, type); - stdin_eof = 1; - break; - - case SSH_CMSG_WINDOW_SIZE: - debug("Window change received."); - packet_integrity_check(payload_len, 4*4, type); - row = packet_get_int(); - col = packet_get_int(); - xpixel = packet_get_int(); - ypixel = packet_get_int(); - if (fdin != -1) - pty_change_window_size(fdin, row, col, xpixel, ypixel); - break; - - case SSH_MSG_PORT_OPEN: - debug("Received port open request."); - channel_input_port_open(payload_len); - break; - - case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: - debug("Received channel open confirmation."); - packet_integrity_check(payload_len, 4 + 4, type); - channel_input_open_confirmation(); - break; - - case SSH_MSG_CHANNEL_OPEN_FAILURE: - debug("Received channel open failure."); - packet_integrity_check(payload_len, 4, type); - channel_input_open_failure(); - break; - - case SSH_MSG_CHANNEL_DATA: - channel_input_data(payload_len); - break; - - case SSH_MSG_CHANNEL_CLOSE: - debug("Received channel close."); - packet_integrity_check(payload_len, 4, type); - channel_input_close(); - break; - - case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: - debug("Received channel close confirmation."); - packet_integrity_check(payload_len, 4, type); - channel_input_close_confirmation(); - break; - - default: - /* In this phase, any unexpected messages cause a protocol - error. This is to ease debugging; also, since no - confirmations are sent messages, unprocessed unknown - messages could cause strange problems. Any compatible - protocol extensions must be negotiated before entering the - interactive session. */ - packet_disconnect("Protocol error during session: type %d", - type); + int type; + char *data; + unsigned int data_len; + int row, col, xpixel, ypixel; + int payload_len; + + /* Process buffered packets from the client. */ + while ((type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) { + switch (type) { + case SSH_CMSG_STDIN_DATA: + /* Stdin data from the client. Append it to the buffer. */ + /* Ignore any data if the client has closed stdin. */ + if (fdin == -1) + break; + data = packet_get_string(&data_len); + packet_integrity_check(payload_len, (4 + data_len), type); + buffer_append(&stdin_buffer, data, data_len); + memset(data, 0, data_len); + xfree(data); + break; + + case SSH_CMSG_EOF: + /* Eof from the client. The stdin descriptor to + the program will be closed when all buffered + data has drained. */ + debug("EOF received for stdin."); + packet_integrity_check(payload_len, 0, type); + stdin_eof = 1; + break; + + case SSH_CMSG_WINDOW_SIZE: + debug("Window change received."); + packet_integrity_check(payload_len, 4 * 4, type); + row = packet_get_int(); + col = packet_get_int(); + xpixel = packet_get_int(); + ypixel = packet_get_int(); + if (fdin != -1) + pty_change_window_size(fdin, row, col, xpixel, ypixel); + break; + + case SSH_MSG_PORT_OPEN: + debug("Received port open request."); + channel_input_port_open(payload_len); + break; + + case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: + debug("Received channel open confirmation."); + packet_integrity_check(payload_len, 4 + 4, type); + channel_input_open_confirmation(); + break; + + case SSH_MSG_CHANNEL_OPEN_FAILURE: + debug("Received channel open failure."); + packet_integrity_check(payload_len, 4, type); + channel_input_open_failure(); + break; + + case SSH_MSG_CHANNEL_DATA: + channel_input_data(payload_len); + break; + + case SSH_MSG_CHANNEL_CLOSE: + debug("Received channel close."); + packet_integrity_check(payload_len, 4, type); + channel_input_close(); + break; + + case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: + debug("Received channel close confirmation."); + packet_integrity_check(payload_len, 4, type); + channel_input_close_confirmation(); + break; + + default: + /* In this phase, any unexpected messages cause a + protocol error. This is to ease debugging; + also, since no confirmations are sent messages, + unprocessed unknown messages could cause + strange problems. Any compatible protocol + extensions must be negotiated before entering + the interactive session. */ + packet_disconnect("Protocol error during session: type %d", + type); + } } - } } -/* Make packets from buffered stderr data, and buffer it for sending - to the client. */ - -void make_packets_from_stderr_data() +/* + * Make packets from buffered stderr data, and buffer it for sending + * to the client. + */ +void +make_packets_from_stderr_data() { - int len; - - /* Send buffered stderr data to the client. */ - while (buffer_len(&stderr_buffer) > 0 && - packet_not_very_much_data_to_write()) - { - len = buffer_len(&stderr_buffer); - if (packet_is_interactive()) - { - if (len > 512) - len = 512; - } - else - { - if (len > packet_get_maxsize()) - len = packet_get_maxsize(); /* Keep the packets at reasonable size. */ + int len; + + /* Send buffered stderr data to the client. */ + while (buffer_len(&stderr_buffer) > 0 && + packet_not_very_much_data_to_write()) { + len = buffer_len(&stderr_buffer); + if (packet_is_interactive()) { + if (len > 512) + len = 512; + } else { + /* Keep the packets at reasonable size. */ + if (len > packet_get_maxsize()) + len = packet_get_maxsize(); + } + packet_start(SSH_SMSG_STDERR_DATA); + packet_put_string(buffer_ptr(&stderr_buffer), len); + packet_send(); + buffer_consume(&stderr_buffer, len); + stderr_bytes += len; } - packet_start(SSH_SMSG_STDERR_DATA); - packet_put_string(buffer_ptr(&stderr_buffer), len); - packet_send(); - buffer_consume(&stderr_buffer, len); - stderr_bytes += len; - } } -/* Make packets from buffered stdout data, and buffer it for sending to the - client. */ - -void make_packets_from_stdout_data() +/* + * Make packets from buffered stdout data, and buffer it for sending to the + * client. + */ +void +make_packets_from_stdout_data() { - int len; - - /* Send buffered stdout data to the client. */ - while (buffer_len(&stdout_buffer) > 0 && - packet_not_very_much_data_to_write()) - { - len = buffer_len(&stdout_buffer); - if (packet_is_interactive()) - { - if (len > 512) - len = 512; - } - else - { - if (len > packet_get_maxsize()) - len = packet_get_maxsize(); /* Keep the packets at reasonable size. */ + int len; + + /* Send buffered stdout data to the client. */ + while (buffer_len(&stdout_buffer) > 0 && + packet_not_very_much_data_to_write()) { + len = buffer_len(&stdout_buffer); + if (packet_is_interactive()) { + if (len > 512) + len = 512; + } else { + /* Keep the packets at reasonable size. */ + if (len > packet_get_maxsize()) + len = packet_get_maxsize(); + } + packet_start(SSH_SMSG_STDOUT_DATA); + packet_put_string(buffer_ptr(&stdout_buffer), len); + packet_send(); + buffer_consume(&stdout_buffer, len); + stdout_bytes += len; } - packet_start(SSH_SMSG_STDOUT_DATA); - packet_put_string(buffer_ptr(&stdout_buffer), len); - packet_send(); - buffer_consume(&stdout_buffer, len); - stdout_bytes += len; - } } -/* Sleep in select() until we can do something. This will initialize the - select masks. Upon return, the masks will indicate which descriptors - have data or can accept data. Optionally, a maximum time can be specified - for the duration of the wait (0 = infinite). */ - -void wait_until_can_do_something(fd_set *readset, fd_set *writeset, - unsigned int max_time_milliseconds) +/* + * Sleep in select() until we can do something. This will initialize the + * select masks. Upon return, the masks will indicate which descriptors + * have data or can accept data. Optionally, a maximum time can be specified + * for the duration of the wait (0 = infinite). + */ +void +wait_until_can_do_something(fd_set * readset, fd_set * writeset, + unsigned int max_time_milliseconds) { - struct timeval tv, *tvp; - int ret; + struct timeval tv, *tvp; + int ret; - /* When select fails we restart from here. */ + /* When select fails we restart from here. */ retry_select: - /* Initialize select() masks. */ - FD_ZERO(readset); - - /* Read packets from the client unless we have too much buffered stdin - or channel data. */ - if (buffer_len(&stdin_buffer) < 4096 && - channel_not_very_much_buffered_data()) - FD_SET(connection_in, readset); - - /* If there is not too much data already buffered going to the client, - try to get some more data from the program. */ - if (packet_not_very_much_data_to_write()) - { - if (!fdout_eof) - FD_SET(fdout, readset); - if (!fderr_eof) - FD_SET(fderr, readset); - } - - FD_ZERO(writeset); - - /* Set masks for channel descriptors. */ - channel_prepare_select(readset, writeset); - - /* If we have buffered packet data going to the client, mark that - descriptor. */ - if (packet_have_data_to_write()) - FD_SET(connection_out, writeset); - - /* If we have buffered data, try to write some of that data to the - program. */ - if (fdin != -1 && buffer_len(&stdin_buffer) > 0) - FD_SET(fdin, writeset); - - /* Update the maximum descriptor number if appropriate. */ - if (channel_max_fd() > max_fd) - max_fd = channel_max_fd(); - - /* If child has terminated and there is enough buffer space to read from - it, then read as much as is available and exit. */ - if (child_terminated && packet_not_very_much_data_to_write()) - if (max_time_milliseconds == 0) - max_time_milliseconds = 100; - - if (max_time_milliseconds == 0) - tvp = NULL; - else - { - tv.tv_sec = max_time_milliseconds / 1000; - tv.tv_usec = 1000 * (max_time_milliseconds % 1000); - tvp = &tv; - } - - /* Wait for something to happen, or the timeout to expire. */ - ret = select(max_fd + 1, readset, writeset, NULL, tvp); - - if (ret < 0) - { - if (errno != EINTR) - error("select: %.100s", strerror(errno)); - else - goto retry_select; - } -} + /* Initialize select() masks. */ + FD_ZERO(readset); + + /* Read packets from the client unless we have too much buffered + stdin or channel data. */ + if (buffer_len(&stdin_buffer) < 4096 && + channel_not_very_much_buffered_data()) + FD_SET(connection_in, readset); + + /* If there is not too much data already buffered going to the + client, try to get some more data from the program. */ + if (packet_not_very_much_data_to_write()) { + if (!fdout_eof) + FD_SET(fdout, readset); + if (!fderr_eof) + FD_SET(fderr, readset); + } + FD_ZERO(writeset); + + /* Set masks for channel descriptors. */ + channel_prepare_select(readset, writeset); + + /* If we have buffered packet data going to the client, mark that + descriptor. */ + if (packet_have_data_to_write()) + FD_SET(connection_out, writeset); + + /* If we have buffered data, try to write some of that data to the + program. */ + if (fdin != -1 && buffer_len(&stdin_buffer) > 0) + FD_SET(fdin, writeset); + + /* Update the maximum descriptor number if appropriate. */ + if (channel_max_fd() > max_fd) + max_fd = channel_max_fd(); + + /* If child has terminated and there is enough buffer space to + read from it, then read as much as is available and exit. */ + if (child_terminated && packet_not_very_much_data_to_write()) + if (max_time_milliseconds == 0) + max_time_milliseconds = 100; + + if (max_time_milliseconds == 0) + tvp = NULL; + else { + tv.tv_sec = max_time_milliseconds / 1000; + tv.tv_usec = 1000 * (max_time_milliseconds % 1000); + tvp = &tv; + } -/* Processes input from the client and the program. Input data is stored - in buffers and processed later. */ + /* Wait for something to happen, or the timeout to expire. */ + ret = select(max_fd + 1, readset, writeset, NULL, tvp); -void process_input(fd_set *readset) -{ - int len; - char buf[16384]; - - /* Read and buffer any input data from the client. */ - if (FD_ISSET(connection_in, readset)) - { - len = read(connection_in, buf, sizeof(buf)); - if (len == 0) - fatal("Connection closed by remote host."); - - /* There is a kernel bug on Solaris that causes select to sometimes - wake up even though there is no data available. */ - if (len < 0 && errno == EAGAIN) - len = 0; - - if (len < 0) - fatal("Read error from remote host: %.100s", strerror(errno)); - - /* Buffer any received data. */ - packet_process_incoming(buf, len); - } - - /* Read and buffer any available stdout data from the program. */ - if (!fdout_eof && FD_ISSET(fdout, readset)) - { - len = read(fdout, buf, sizeof(buf)); - if (len <= 0) - fdout_eof = 1; - else - { - buffer_append(&stdout_buffer, buf, len); - fdout_bytes += len; + if (ret < 0) { + if (errno != EINTR) + error("select: %.100s", strerror(errno)); + else + goto retry_select; } - } - - /* Read and buffer any available stderr data from the program. */ - if (!fderr_eof && FD_ISSET(fderr, readset)) - { - len = read(fderr, buf, sizeof(buf)); - if (len <= 0) - fderr_eof = 1; - else - buffer_append(&stderr_buffer, buf, len); - } } -/* Sends data from internal buffers to client program stdin. */ +/* + * Processes input from the client and the program. Input data is stored + * in buffers and processed later. + */ +void +process_input(fd_set * readset) +{ + int len; + char buf[16384]; + + /* Read and buffer any input data from the client. */ + if (FD_ISSET(connection_in, readset)) { + len = read(connection_in, buf, sizeof(buf)); + if (len == 0) { + verbose("Connection closed by remote host."); + fatal_cleanup(); + } + /* There is a kernel bug on Solaris that causes select to + sometimes wake up even though there is no data + available. */ + if (len < 0 && errno == EAGAIN) + len = 0; + + if (len < 0) { + verbose("Read error from remote host: %.100s", strerror(errno)); + fatal_cleanup(); + } + /* Buffer any received data. */ + packet_process_incoming(buf, len); + } + /* Read and buffer any available stdout data from the program. */ + if (!fdout_eof && FD_ISSET(fdout, readset)) { + len = read(fdout, buf, sizeof(buf)); + if (len <= 0) + fdout_eof = 1; + else { + buffer_append(&stdout_buffer, buf, len); + fdout_bytes += len; + } + } + /* Read and buffer any available stderr data from the program. */ + if (!fderr_eof && FD_ISSET(fderr, readset)) { + len = read(fderr, buf, sizeof(buf)); + if (len <= 0) + fderr_eof = 1; + else + buffer_append(&stderr_buffer, buf, len); + } +} -void process_output(fd_set *writeset) +/* + * Sends data from internal buffers to client program stdin. + */ +void +process_output(fd_set * writeset) { - int len; - - /* Write buffered data to program stdin. */ - if (fdin != -1 && FD_ISSET(fdin, writeset)) - { - len = write(fdin, buffer_ptr(&stdin_buffer), - buffer_len(&stdin_buffer)); - if (len <= 0) - { + int len; + + /* Write buffered data to program stdin. */ + if (fdin != -1 && FD_ISSET(fdin, writeset)) { + len = write(fdin, buffer_ptr(&stdin_buffer), + buffer_len(&stdin_buffer)); + if (len <= 0) { #ifdef USE_PIPES - close(fdin); + close(fdin); #else - if (fdout == -1) - close(fdin); - else - shutdown(fdin, SHUT_WR); /* We will no longer send. */ + if (fdout == -1) + close(fdin); + else + shutdown(fdin, SHUT_WR); /* We will no longer send. */ #endif - fdin = -1; - } - else - { - /* Successful write. Consume the data from the buffer. */ - buffer_consume(&stdin_buffer, len); - /* Update the count of bytes written to the program. */ - stdin_bytes += len; + fdin = -1; + } else { + /* Successful write. Consume the data from the buffer. */ + buffer_consume(&stdin_buffer, len); + /* Update the count of bytes written to the program. */ + stdin_bytes += len; + } } - } - - /* Send any buffered packet data to the client. */ - if (FD_ISSET(connection_out, writeset)) - packet_write_poll(); + /* Send any buffered packet data to the client. */ + if (FD_ISSET(connection_out, writeset)) + packet_write_poll(); } -/* Wait until all buffered output has been sent to the client. - This is used when the program terminates. */ - -void drain_output() +/* + * Wait until all buffered output has been sent to the client. + * This is used when the program terminates. + */ +void +drain_output() { - /* Send any buffered stdout data to the client. */ - if (buffer_len(&stdout_buffer) > 0) - { - packet_start(SSH_SMSG_STDOUT_DATA); - packet_put_string(buffer_ptr(&stdout_buffer), - buffer_len(&stdout_buffer)); - packet_send(); - /* Update the count of sent bytes. */ - stdout_bytes += buffer_len(&stdout_buffer); - } - - /* Send any buffered stderr data to the client. */ - if (buffer_len(&stderr_buffer) > 0) - { - packet_start(SSH_SMSG_STDERR_DATA); - packet_put_string(buffer_ptr(&stderr_buffer), - buffer_len(&stderr_buffer)); - packet_send(); - /* Update the count of sent bytes. */ - stderr_bytes += buffer_len(&stderr_buffer); - } - - /* Wait until all buffered data has been written to the client. */ - packet_write_wait(); + /* Send any buffered stdout data to the client. */ + if (buffer_len(&stdout_buffer) > 0) { + packet_start(SSH_SMSG_STDOUT_DATA); + packet_put_string(buffer_ptr(&stdout_buffer), + buffer_len(&stdout_buffer)); + packet_send(); + /* Update the count of sent bytes. */ + stdout_bytes += buffer_len(&stdout_buffer); + } + /* Send any buffered stderr data to the client. */ + if (buffer_len(&stderr_buffer) > 0) { + packet_start(SSH_SMSG_STDERR_DATA); + packet_put_string(buffer_ptr(&stderr_buffer), + buffer_len(&stderr_buffer)); + packet_send(); + /* Update the count of sent bytes. */ + stderr_bytes += buffer_len(&stderr_buffer); + } + /* Wait until all buffered data has been written to the client. */ + packet_write_wait(); } -/* 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(int pid, int fdin_arg, int fdout_arg, int fderr_arg) +/* + * 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(int pid, int fdin_arg, int fdout_arg, int fderr_arg) { - int wait_status, wait_pid; /* Status and pid returned by wait(). */ - int waiting_termination = 0; /* Have displayed waiting close message. */ - unsigned int max_time_milliseconds; - unsigned int previous_stdout_buffer_bytes; - unsigned int stdout_buffer_bytes; - int type; - - debug("Entering interactive session."); - - /* Initialize the SIGCHLD kludge. */ - child_pid = pid; - child_terminated = 0; - signal(SIGCHLD, sigchld_handler); - - /* Initialize our global variables. */ - fdin = fdin_arg; - fdout = fdout_arg; - fderr = fderr_arg; - connection_in = packet_get_connection_in(); - connection_out = packet_get_connection_out(); - - previous_stdout_buffer_bytes = 0; - - /* Set approximate I/O buffer size. */ - if (packet_is_interactive()) - buffer_high = 4096; - else - buffer_high = 64 * 1024; - - /* Initialize max_fd to the maximum of the known file descriptors. */ - max_fd = fdin; - if (fdout > max_fd) - max_fd = fdout; - if (fderr != -1 && fderr > max_fd) - max_fd = fderr; - if (connection_in > max_fd) - max_fd = connection_in; - if (connection_out > max_fd) - max_fd = connection_out; - - /* 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; - - /* Main loop of the server for the interactive session mode. */ - for (;;) - { - fd_set readset, writeset; - - /* 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) - { + int wait_status, wait_pid; /* Status and pid returned by wait(). */ + int waiting_termination = 0; /* Have displayed waiting close message. */ + unsigned int max_time_milliseconds; + unsigned int previous_stdout_buffer_bytes; + unsigned int stdout_buffer_bytes; + int type; + + debug("Entering interactive session."); + + /* Initialize the SIGCHLD kludge. */ + child_pid = pid; + child_terminated = 0; + signal(SIGCHLD, sigchld_handler); + + /* Initialize our global variables. */ + fdin = fdin_arg; + fdout = fdout_arg; + fderr = fderr_arg; + connection_in = packet_get_connection_in(); + connection_out = packet_get_connection_out(); + + previous_stdout_buffer_bytes = 0; + + /* Set approximate I/O buffer size. */ + if (packet_is_interactive()) + buffer_high = 4096; + else + buffer_high = 64 * 1024; + + /* Initialize max_fd to the maximum of the known file descriptors. */ + max_fd = fdin; + if (fdout > max_fd) + max_fd = fdout; + if (fderr != -1 && fderr > max_fd) + max_fd = fderr; + if (connection_in > max_fd) + max_fd = connection_in; + if (connection_out > max_fd) + max_fd = connection_out; + + /* 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; + + /* Main loop of the server for the interactive session mode. */ + for (;;) { + fd_set readset, writeset; + + /* 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) { #ifdef USE_PIPES - close(fdin); + close(fdin); #else - if (fdout == -1) - close(fdin); - else - shutdown(fdin, SHUT_WR); /* We will no longer send. */ + if (fdout == -1) + close(fdin); + else + shutdown(fdin, SHUT_WR); /* We will no longer send. */ #endif - fdin = -1; + 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()) + goto quit; + if (!waiting_termination) { + const char *s = "Waiting for forwarded connections to terminate...\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)); + xfree(cp); + } + } + /* Sleep in select() until we can do something. */ + wait_until_can_do_something(&readset, &writeset, + max_time_milliseconds); + + /* 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); } - /* 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) - max_time_milliseconds = 10; /* try again after a while */ - else - make_packets_from_stdout_data(); /* Send it now. */ - 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()) - goto quit; - if (!waiting_termination) - { - const char *s = - "Waiting for forwarded connections to terminate...\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)); - xfree(cp); - } - } +quit: + /* Cleanup and termination code. */ - /* Sleep in select() until we can do something. */ - wait_until_can_do_something(&readset, &writeset, - max_time_milliseconds); - - /* 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); - } - - quit: - /* 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; - - /* Stop listening for channels; this removes unix domain sockets. */ - channel_stop_listening(); - - /* Wait for the child to exit. Get its exit status. */ - wait_pid = wait(&wait_status); - if (wait_pid < 0) - { - /* It is possible that the wait was handled by SIGCHLD handler. This - may result in either: this call returning with EINTR, or: this - call returning ECHILD. */ - if (child_terminated) - wait_status = child_wait_status; - else - packet_disconnect("wait: %.100s", strerror(errno)); - } - else - { - /* Check if it matches the process we forked. */ - if (wait_pid != pid) - error("Strange, wait returned pid %d, expected %d", wait_pid, pid); - } - - /* We no longer want our SIGCHLD handler to be called. */ - signal(SIGCHLD, SIG_DFL); - - /* 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 - { - int plen; - type = packet_read(&plen); - } - while (type != SSH_CMSG_EXIT_CONFIRMATION); + /* Wait until all output has been sent to the client. */ + drain_output(); - debug("Received exit confirmation."); - return; - } + debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", + stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); - /* Check if the program terminated due to a signal. */ - if (WIFSIGNALED(wait_status)) - packet_disconnect("Command terminated on signal %d.", - WTERMSIG(wait_status)); + /* Free and clear the buffers. */ + buffer_free(&stdin_buffer); + buffer_free(&stdout_buffer); + buffer_free(&stderr_buffer); - /* Some weird exit cause. Just exit. */ - packet_disconnect("wait returned status %04x.", wait_status); - /*NOTREACHED*/ -} + /* 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; + + /* Stop listening for channels; this removes unix domain sockets. */ + channel_stop_listening(); + + /* Wait for the child to exit. Get its exit status. */ + wait_pid = wait(&wait_status); + if (wait_pid < 0) { + /* + * It is possible that the wait was handled by SIGCHLD + * handler. This may result in either: this call + * returning with EINTR, or: this call returning ECHILD. + */ + if (child_terminated) + wait_status = child_wait_status; + else + packet_disconnect("wait: %.100s", strerror(errno)); + } else { + /* Check if it matches the process we forked. */ + if (wait_pid != pid) + error("Strange, wait returned pid %d, expected %d", + wait_pid, pid); + } + /* We no longer want our SIGCHLD handler to be called. */ + signal(SIGCHLD, SIG_DFL); + + /* 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 { + int plen; + type = packet_read(&plen); + } + 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 */ +} diff --git a/ssh-add.c b/ssh-add.c index 0ab3a5b2..602c4b1d 100644 --- a/ssh-add.c +++ b/ssh-add.c @@ -1,17 +1,10 @@ /* - -ssh-add.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Thu Apr 6 00:52:24 1995 ylo - -Adds an identity to the authentication server, or removes an identity. - -*/ + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Thu Apr 6 00:52:24 1995 ylo + * Adds an identity to the authentication server, or removes an identity. + */ #include "includes.h" RCSID("$Id$"); @@ -35,291 +28,279 @@ const char *__progname = "ssh-add"; void delete_file(AuthenticationConnection *ac, const char *filename) { - RSA *key; - char *comment; - - key = RSA_new(); - if (!load_public_key(filename, key, &comment)) - { - printf("Bad key file %s: %s\n", filename, strerror(errno)); - return; - } - - if (ssh_remove_identity(ac, key)) - fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); - else - fprintf(stderr, "Could not remove identity: %s\n", filename); - RSA_free(key); - xfree(comment); + RSA *key; + char *comment; + + key = RSA_new(); + if (!load_public_key(filename, key, &comment)) { + printf("Bad key file %s: %s\n", filename, strerror(errno)); + return; + } + if (ssh_remove_identity(ac, key)) + fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); + else + fprintf(stderr, "Could not remove identity: %s\n", filename); + RSA_free(key); + xfree(comment); } void delete_all(AuthenticationConnection *ac) { - /* Send a request to remove all identities. */ - if (ssh_remove_all_identities(ac)) - fprintf(stderr, "All identities removed.\n"); - else - fprintf(stderr, "Failed to remove all identitities.\n"); + /* Send a request to remove all identities. */ + if (ssh_remove_all_identities(ac)) + fprintf(stderr, "All identities removed.\n"); + else + fprintf(stderr, "Failed to remove all identitities.\n"); } void add_file(AuthenticationConnection *ac, const char *filename) { - RSA *key; - RSA *public_key; - char *saved_comment, *comment; - int success; - - key = RSA_new(); - public_key = RSA_new(); - if (!load_public_key(filename, public_key, &saved_comment)) - { - printf("Bad key file %s: %s\n", filename, strerror(errno)); - return; - } - RSA_free(public_key); - - /* At first, try empty passphrase */ - success = load_private_key(filename, "", key, &comment); - if (!success) { - printf("Need passphrase for %s (%s).\n", filename, saved_comment); - if (!isatty(STDIN_FILENO)) { -#ifdef USE_EXTERNAL_ASKPASS - int prompts = 3; + RSA *key; + RSA *public_key; + char *saved_comment, *comment; + int success; + + key = RSA_new(); + public_key = RSA_new(); + if (!load_public_key(filename, public_key, &saved_comment)) { + printf("Bad key file %s: %s\n", filename, strerror(errno)); + return; + } + RSA_free(public_key); - while (prompts && !success) - { - success = askpass(filename, key, saved_comment, &comment); - prompts--; - } - if (!success) - { - xfree(saved_comment); - return; - } + /* At first, try empty passphrase */ + success = load_private_key(filename, "", key, &comment); + if (!success) { + printf("Need passphrase for %s (%s).\n", filename, saved_comment); + if (!isatty(STDIN_FILENO)) { +#ifdef USE_EXTERNAL_ASKPASS + int prompts = 3; + while (prompts && !success) { + success = askpass(filename, key, saved_comment, &comment); + prompts--; + } + if (!success) { + xfree(saved_comment); + return; + } #else /* !USE_EXTERNAL_ASKPASS */ - xfree(saved_comment); - return; + xfree(saved_comment); + return; #endif /* USE_EXTERNAL_ASKPASS */ - } - - while (!success) { - char *pass = read_passphrase("Enter passphrase: ", 1); - if (strcmp(pass, "") == 0){ - xfree(pass); - xfree(saved_comment); - return; - } - success = load_private_key(filename, pass, key, &comment); - memset(pass, 0, strlen(pass)); - xfree(pass); - if (success) - break; - printf("Bad passphrase.\n"); - } - } - xfree(saved_comment); - - if (ssh_add_identity(ac, key, comment)) - fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); - else - fprintf(stderr, "Could not add identity: %s\n", filename); - RSA_free(key); - xfree(comment); + } + + while (!success) { + char *pass = read_passphrase("Enter passphrase: ", 1); + if (strcmp(pass, "") == 0) { + xfree(pass); + xfree(saved_comment); + return; + } + success = load_private_key(filename, pass, key, &comment); + memset(pass, 0, strlen(pass)); + xfree(pass); + if (success) + break; + printf("Bad passphrase.\n"); + } + } + xfree(saved_comment); + + if (ssh_add_identity(ac, key, comment)) + fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); + else + fprintf(stderr, "Could not add identity: %s\n", filename); + RSA_free(key); + xfree(comment); } void list_identities(AuthenticationConnection *ac, int fp) { - BIGNUM *e, *n; - int status; - char *comment; - int had_identities; - - e = BN_new(); - n = BN_new(); - had_identities = 0; - for (status = ssh_get_first_identity(ac, e, n, &comment); - status; - status = ssh_get_next_identity(ac, e, n, &comment)) - { - unsigned int bits = BN_num_bits(n); - had_identities = 1; - if (fp) { - printf("%d %s %s\n", bits, fingerprint(e, n), comment); - } else { - char *ebuf, *nbuf; - ebuf = BN_bn2dec(e); - if (ebuf == NULL) { - error("list_identities: BN_bn2dec(e) failed."); - }else{ - nbuf = BN_bn2dec(n); - if (nbuf == NULL) { - error("list_identities: BN_bn2dec(n) failed."); - }else{ - printf("%d %s %s %s\n", bits, ebuf, nbuf, comment); - free(nbuf); - } - free(ebuf); + BIGNUM *e, *n; + int status; + char *comment; + int had_identities; + + e = BN_new(); + n = BN_new(); + had_identities = 0; + for (status = ssh_get_first_identity(ac, e, n, &comment); + status; + status = ssh_get_next_identity(ac, e, n, &comment)) { + unsigned int bits = BN_num_bits(n); + had_identities = 1; + if (fp) { + printf("%d %s %s\n", bits, fingerprint(e, n), comment); + } else { + char *ebuf, *nbuf; + ebuf = BN_bn2dec(e); + if (ebuf == NULL) { + error("list_identities: BN_bn2dec(e) failed."); + } else { + nbuf = BN_bn2dec(n); + if (nbuf == NULL) { + error("list_identities: BN_bn2dec(n) failed."); + } else { + printf("%d %s %s %s\n", bits, ebuf, nbuf, comment); + free(nbuf); + } + free(ebuf); + } + } + xfree(comment); } - } - xfree(comment); - } - BN_clear_free(e); - BN_clear_free(n); - if (!had_identities) - printf("The agent has no identities.\n"); + BN_clear_free(e); + BN_clear_free(n); + if (!had_identities) + printf("The agent has no identities.\n"); } int main(int argc, char **argv) { - AuthenticationConnection *ac = NULL; - struct passwd *pw; - char buf[1024]; - int no_files = 1; - int i; - int deleting = 0; - - /* check if RSA support exists */ - if (rsa_alive() == 0) { - fprintf(stderr, - "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", - __progname); - exit(1); - } - - /* At first, get a connection to the authentication agent. */ - ac = ssh_get_authentication_connection(); - if (ac == NULL) { - fprintf(stderr, "Could not open a connection to your authentication agent.\n"); - exit(1); - } - - for (i = 1; i < argc; i++) - { - if ((strcmp(argv[i], "-l") == 0) || - (strcmp(argv[i], "-L") == 0)) - { - list_identities(ac, argv[i][1] == 'l' ? 1 : 0); - no_files = 0; /* Don't default-add/delete if -l. */ - continue; + AuthenticationConnection *ac = NULL; + struct passwd *pw; + char buf[1024]; + int no_files = 1; + int i; + int deleting = 0; + + /* check if RSA support exists */ + if (rsa_alive() == 0) { + extern char *__progname; + + fprintf(stderr, + "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", + __progname); + exit(1); } - if (strcmp(argv[i], "-d") == 0) - { - deleting = 1; - continue; + /* At first, get a connection to the authentication agent. */ + ac = ssh_get_authentication_connection(); + if (ac == NULL) { + fprintf(stderr, "Could not open a connection to your authentication agent.\n"); + exit(1); } - if (strcmp(argv[i], "-D") == 0) - { - delete_all(ac); - no_files = 0; - continue; + for (i = 1; i < argc; i++) { + if ((strcmp(argv[i], "-l") == 0) || + (strcmp(argv[i], "-L") == 0)) { + list_identities(ac, argv[i][1] == 'l' ? 1 : 0); + /* Don't default-add/delete if -l. */ + no_files = 0; + continue; + } + if (strcmp(argv[i], "-d") == 0) { + deleting = 1; + continue; + } + if (strcmp(argv[i], "-D") == 0) { + delete_all(ac); + no_files = 0; + continue; + } + no_files = 0; + if (deleting) + delete_file(ac, argv[i]); + else + add_file(ac, argv[i]); } - no_files = 0; - if (deleting) - delete_file(ac, argv[i]); - else - add_file(ac, argv[i]); - } - if (no_files) - { - pw = getpwuid(getuid()); - if (!pw) - { - fprintf(stderr, "No user found with uid %d\n", (int)getuid()); - ssh_close_authentication_connection(ac); - exit(1); + if (no_files) { + pw = getpwuid(getuid()); + if (!pw) { + fprintf(stderr, "No user found with uid %d\n", (int) getuid()); + ssh_close_authentication_connection(ac); + exit(1); + } + snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); + if (deleting) + delete_file(ac, buf); + else + add_file(ac, buf); } - snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); - if (deleting) - delete_file(ac, buf); - else - add_file(ac, buf); - } - ssh_close_authentication_connection(ac); - exit(0); + ssh_close_authentication_connection(ac); + exit(0); } #ifdef USE_EXTERNAL_ASKPASS int askpass(const char *filename, RSA *key, const char *saved_comment, char **comment) { - int pipes[2]; - char buf[1024]; - int tmp; - pid_t child; - FILE *pipef; - - /* Check that we are X11-capable */ - if (getenv("DISPLAY") == NULL) - exit(1); - - if (pipe(pipes) == -1) { - fprintf(stderr, "Creating pipes failed: %s\n", strerror(errno)); - exit(1); - } - - if (fflush(NULL) == EOF) { - fprintf(stderr, "Cannot flush buffers: %s\n", strerror(errno)); - exit(1); - } - - child = fork(); - if (child == -1) { - fprintf(stderr, "Cannot fork: %s\n", strerror(errno)); - exit(1); - } - - if (child == 0) { - /* In child */ - - close(pipes[0]); - if (dup2(pipes[1], 1) ==-1) { - fprintf(stderr, "dup2 failed: %s\n", strerror(errno)); - exit(1); - } - - tmp = snprintf(buf, sizeof(buf), "Need passphrase for %s", saved_comment); - /* skip the prompt if it won't fit */ - if ((tmp < 0) || (tmp >= sizeof(buf))) - tmp = execlp(ASKPASS_PROGRAM, "ssh-askpass", 0); - else - tmp = execlp(ASKPASS_PROGRAM, "ssh-askpass", buf, 0); - - /* Shouldn't get this far */ - fprintf(stderr, "Executing ssh-askpass failed: %s\n", strerror(errno)); - exit(1); - } - - /* In parent */ - close(pipes[1]); - - if ((pipef = fdopen(pipes[0], "r")) == NULL) { - fprintf(stderr, "fdopen failed: %s\n", strerror(errno)); - exit(1); - } - - /* Read passphrase back from child, abort if none presented */ - if(fgets(buf, sizeof(buf), pipef) == NULL) - exit(1); - - fclose(pipef); - - if (strchr(buf, '\n')) - *strchr(buf, '\n') = 0; - - if (waitpid(child, NULL, 0) == -1) { - fprintf(stderr, "Waiting for child failed: %s\n", - strerror(errno)); - exit(1); - } - - /* Try password as it was presented */ - tmp = load_private_key(filename, buf, key, comment); - - memset(buf, 0, sizeof(buf)); - - return(tmp); + int pipes[2]; + char buf[1024]; + int tmp; + pid_t child; + FILE *pipef; + + /* Check that we are X11-capable */ + if (getenv("DISPLAY") == NULL) + exit(1); + + if (pipe(pipes) == -1) { + fprintf(stderr, "Creating pipes failed: %s\n", strerror(errno)); + exit(1); + } + + if (fflush(NULL) == EOF) { + fprintf(stderr, "Cannot flush buffers: %s\n", strerror(errno)); + exit(1); + } + + child = fork(); + if (child == -1) { + fprintf(stderr, "Cannot fork: %s\n", strerror(errno)); + exit(1); + } + + if (child == 0) { + /* In child */ + + close(pipes[0]); + if (dup2(pipes[1], 1) ==-1) { + fprintf(stderr, "dup2 failed: %s\n", strerror(errno)); + exit(1); + } + + tmp = snprintf(buf, sizeof(buf), "Need passphrase for %s", saved_comment); + /* skip the prompt if it won't fit */ + if ((tmp < 0) || (tmp >= sizeof(buf))) + tmp = execlp(ASKPASS_PROGRAM, "ssh-askpass", 0); + else + tmp = execlp(ASKPASS_PROGRAM, "ssh-askpass", buf, 0); + + /* Shouldn't get this far */ + fprintf(stderr, "Executing ssh-askpass failed: %s\n", strerror(errno)); + exit(1); + } + + /* In parent */ + close(pipes[1]); + + if ((pipef = fdopen(pipes[0], "r")) == NULL) { + fprintf(stderr, "fdopen failed: %s\n", strerror(errno)); + exit(1); + } + + /* Read passphrase back from child, abort if none presented */ + if(fgets(buf, sizeof(buf), pipef) == NULL) + exit(1); + + fclose(pipef); + + if (strchr(buf, '\n')) + *strchr(buf, '\n') = 0; + + if (waitpid(child, NULL, 0) == -1) { + fprintf(stderr, "Waiting for child failed: %s\n", + strerror(errno)); + exit(1); + } + + /* Try password as it was presented */ + tmp = load_private_key(filename, buf, key, comment); + + memset(buf, 0, sizeof(buf)); + + return(tmp); } #endif /* USE_EXTERNAL_ASKPASS */ diff --git a/ssh-agent.c b/ssh-agent.c index f1ceb569..70c2a7f6 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -1,22 +1,15 @@ -/* $OpenBSD: ssh-agent.c,v 1.20 1999/11/19 10:20:51 markus Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.22 1999/11/24 00:26:03 deraadt Exp $ */ /* - -ssh-agent.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Wed Mar 29 03:46:59 1995 ylo - -The authentication agent program. - -*/ + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Wed Mar 29 03:46:59 1995 ylo + * The authentication agent program. + */ #include "includes.h" -RCSID("$OpenBSD: ssh-agent.c,v 1.20 1999/11/19 10:20:51 markus Exp $"); +RCSID("$OpenBSD: ssh-agent.c,v 1.22 1999/11/24 00:26:03 deraadt Exp $"); #include "ssh.h" #include "rsa.h" @@ -35,27 +28,21 @@ RCSID("$OpenBSD: ssh-agent.c,v 1.20 1999/11/19 10:20:51 markus Exp $"); #include #endif -#ifdef HAVE___PROGNAME -extern char *__progname; -#else /* HAVE___PROGNAME */ -const char *__progname = "ssh-agent"; -#endif /* HAVE___PROGNAME */ - -typedef struct -{ - int fd; - enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } type; - Buffer input; - Buffer output; +typedef struct { + int fd; + enum { + AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION + } type; + Buffer input; + Buffer output; } SocketEntry; unsigned int sockets_alloc = 0; SocketEntry *sockets = NULL; -typedef struct -{ - RSA *key; - char *comment; +typedef struct { + RSA *key; + char *comment; } Identity; unsigned int num_identities = 0; @@ -70,640 +57,601 @@ int parent_pid = -1; char socket_name[1024]; char socket_dir[1024]; +#ifdef HAVE___PROGNAME +extern char *__progname; +#else /* HAVE___PROGNAME */ +const char *__progname = "ssh-agent"; +#endif /* HAVE___PROGNAME */ + void process_request_identity(SocketEntry *e) { - Buffer msg; - int i; - - buffer_init(&msg); - buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER); - buffer_put_int(&msg, num_identities); - for (i = 0; i < num_identities; i++) - { - buffer_put_int(&msg, BN_num_bits(identities[i].key->n)); - buffer_put_bignum(&msg, identities[i].key->e); - buffer_put_bignum(&msg, identities[i].key->n); - buffer_put_string(&msg, identities[i].comment, - strlen(identities[i].comment)); - } - buffer_put_int(&e->output, buffer_len(&msg)); - buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); - buffer_free(&msg); + Buffer msg; + int i; + + buffer_init(&msg); + buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER); + buffer_put_int(&msg, num_identities); + for (i = 0; i < num_identities; i++) { + buffer_put_int(&msg, BN_num_bits(identities[i].key->n)); + buffer_put_bignum(&msg, identities[i].key->e); + buffer_put_bignum(&msg, identities[i].key->n); + buffer_put_string(&msg, identities[i].comment, + strlen(identities[i].comment)); + } + buffer_put_int(&e->output, buffer_len(&msg)); + buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); + buffer_free(&msg); } void process_authentication_challenge(SocketEntry *e) { - int i, pub_bits, len; - BIGNUM *pub_e, *pub_n, *challenge; - Buffer msg; - MD5_CTX md; - unsigned char buf[32], mdbuf[16], session_id[16]; - unsigned int response_type; - - buffer_init(&msg); - pub_e = BN_new(); - pub_n = BN_new(); - challenge = BN_new(); - pub_bits = buffer_get_int(&e->input); - buffer_get_bignum(&e->input, pub_e); - buffer_get_bignum(&e->input, pub_n); - buffer_get_bignum(&e->input, challenge); - if (buffer_len(&e->input) == 0) - { - /* Compatibility code for old servers. */ - memset(session_id, 0, 16); - response_type = 0; - } - else - { - /* New code. */ - buffer_get(&e->input, (char *)session_id, 16); - response_type = buffer_get_int(&e->input); - } - for (i = 0; i < num_identities; i++) - if (pub_bits == BN_num_bits(identities[i].key->n) && - BN_cmp(pub_e, identities[i].key->e) == 0 && - BN_cmp(pub_n, identities[i].key->n) == 0) - { - /* Decrypt the challenge using the private key. */ - rsa_private_decrypt(challenge, challenge, identities[i].key); - - /* Compute the desired response. */ - switch (response_type) - { - case 0: /* As of protocol 1.0 */ - /* This response type is no longer supported. */ - log("Compatibility with ssh protocol 1.0 no longer supported."); - buffer_put_char(&msg, SSH_AGENT_FAILURE); - goto send; - - case 1: /* As of protocol 1.1 */ - /* The response is MD5 of decrypted challenge plus session id. */ - len = BN_num_bytes(challenge); - - if (len <= 0 || len > 32) { - fatal("process_authentication_challenge: " - "bad challenge length %d", len); - } - - memset(buf, 0, 32); - BN_bn2bin(challenge, buf + 32 - len); - MD5_Init(&md); - MD5_Update(&md, buf, 32); - MD5_Update(&md, session_id, 16); - MD5_Final(mdbuf, &md); - break; - - default: - fatal("process_authentication_challenge: bad response_type %d", - response_type); - break; - } - - /* Send the response. */ - buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE); - for (i = 0; i < 16; i++) - buffer_put_char(&msg, mdbuf[i]); - - goto send; - } - /* Unknown identity. Send failure. */ - buffer_put_char(&msg, SSH_AGENT_FAILURE); - send: - buffer_put_int(&e->output, buffer_len(&msg)); - buffer_append(&e->output, buffer_ptr(&msg), - buffer_len(&msg)); - buffer_free(&msg); - BN_clear_free(pub_e); - BN_clear_free(pub_n); - BN_clear_free(challenge); + int i, pub_bits, len; + BIGNUM *pub_e, *pub_n, *challenge; + Buffer msg; + MD5_CTX md; + unsigned char buf[32], mdbuf[16], session_id[16]; + unsigned int response_type; + + buffer_init(&msg); + pub_e = BN_new(); + pub_n = BN_new(); + challenge = BN_new(); + pub_bits = buffer_get_int(&e->input); + buffer_get_bignum(&e->input, pub_e); + buffer_get_bignum(&e->input, pub_n); + buffer_get_bignum(&e->input, challenge); + if (buffer_len(&e->input) == 0) { + /* Compatibility code for old servers. */ + memset(session_id, 0, 16); + response_type = 0; + } else { + /* New code. */ + buffer_get(&e->input, (char *) session_id, 16); + response_type = buffer_get_int(&e->input); + } + for (i = 0; i < num_identities; i++) + if (pub_bits == BN_num_bits(identities[i].key->n) && + BN_cmp(pub_e, identities[i].key->e) == 0 && + BN_cmp(pub_n, identities[i].key->n) == 0) { + /* Decrypt the challenge using the private key. */ + rsa_private_decrypt(challenge, challenge, identities[i].key); + + /* Compute the desired response. */ + switch (response_type) { + case 0:/* As of protocol 1.0 */ + /* This response type is no longer supported. */ + log("Compatibility with ssh protocol 1.0 no longer supported."); + buffer_put_char(&msg, SSH_AGENT_FAILURE); + goto send; + + case 1:/* As of protocol 1.1 */ + /* The response is MD5 of decrypted challenge plus session id. */ + len = BN_num_bytes(challenge); + + if (len <= 0 || len > 32) { + fatal("process_authentication_challenge: " + "bad challenge length %d", len); + } + memset(buf, 0, 32); + BN_bn2bin(challenge, buf + 32 - len); + MD5_Init(&md); + MD5_Update(&md, buf, 32); + MD5_Update(&md, session_id, 16); + MD5_Final(mdbuf, &md); + break; + + default: + fatal("process_authentication_challenge: bad response_type %d", + response_type); + break; + } + + /* Send the response. */ + buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE); + for (i = 0; i < 16; i++) + buffer_put_char(&msg, mdbuf[i]); + + goto send; + } + /* Unknown identity. Send failure. */ + buffer_put_char(&msg, SSH_AGENT_FAILURE); +send: + buffer_put_int(&e->output, buffer_len(&msg)); + buffer_append(&e->output, buffer_ptr(&msg), + buffer_len(&msg)); + buffer_free(&msg); + BN_clear_free(pub_e); + BN_clear_free(pub_n); + BN_clear_free(challenge); } void process_remove_identity(SocketEntry *e) { - unsigned int bits; - unsigned int i; - BIGNUM *dummy, *n; - - dummy = BN_new(); - n = BN_new(); - - /* Get the key from the packet. */ - bits = buffer_get_int(&e->input); - buffer_get_bignum(&e->input, dummy); - buffer_get_bignum(&e->input, n); - - if (bits != BN_num_bits(n)) - error("Warning: keysize mismatch: actual %d, announced %d", - BN_num_bits(n), bits); - - /* Check if we have the key. */ - for (i = 0; i < num_identities; i++) - if (BN_cmp(identities[i].key->n, n) == 0) - { - /* We have this key. Free the old key. Since we don\'t want to leave - empty slots in the middle of the array, we actually free the - key there and copy data from the last entry. */ - RSA_free(identities[i].key); - xfree(identities[i].comment); - if (i < num_identities - 1) - identities[i] = identities[num_identities - 1]; - num_identities--; - BN_clear_free(dummy); - BN_clear_free(n); - - /* Send success. */ + unsigned int bits; + unsigned int i; + BIGNUM *dummy, *n; + + dummy = BN_new(); + n = BN_new(); + + /* Get the key from the packet. */ + bits = buffer_get_int(&e->input); + buffer_get_bignum(&e->input, dummy); + buffer_get_bignum(&e->input, n); + + if (bits != BN_num_bits(n)) + error("Warning: keysize mismatch: actual %d, announced %d", + BN_num_bits(n), bits); + + /* Check if we have the key. */ + for (i = 0; i < num_identities; i++) + if (BN_cmp(identities[i].key->n, n) == 0) { + /* We have this key. Free the old key. Since we + don\'t want to leave empty slots in the middle + of the array, we actually free the key there + and copy data from the last entry. */ + RSA_free(identities[i].key); + xfree(identities[i].comment); + if (i < num_identities - 1) + identities[i] = identities[num_identities - 1]; + num_identities--; + BN_clear_free(dummy); + BN_clear_free(n); + + /* Send success. */ + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, SSH_AGENT_SUCCESS); + return; + } + /* We did not have the key. */ + BN_clear(dummy); + BN_clear(n); + + /* Send failure. */ buffer_put_int(&e->output, 1); - buffer_put_char(&e->output, SSH_AGENT_SUCCESS); - return; - } - /* We did not have the key. */ - BN_clear(dummy); - BN_clear(n); - - /* Send failure. */ - buffer_put_int(&e->output, 1); - buffer_put_char(&e->output, SSH_AGENT_FAILURE); + buffer_put_char(&e->output, SSH_AGENT_FAILURE); } -/* Removes all identities from the agent. */ - +/* + * Removes all identities from the agent. + */ void process_remove_all_identities(SocketEntry *e) { - unsigned int i; - - /* Loop over all identities and clear the keys. */ - for (i = 0; i < num_identities; i++) - { - RSA_free(identities[i].key); - xfree(identities[i].comment); - } - - /* Mark that there are no identities. */ - num_identities = 0; - - /* Send success. */ - buffer_put_int(&e->output, 1); - buffer_put_char(&e->output, SSH_AGENT_SUCCESS); - return; -} + unsigned int i; -/* Adds an identity to the agent. */ + /* Loop over all identities and clear the keys. */ + for (i = 0; i < num_identities; i++) { + RSA_free(identities[i].key); + xfree(identities[i].comment); + } -void -process_add_identity(SocketEntry *e) -{ - RSA *k; - int i; - BIGNUM *aux; - BN_CTX *ctx; - - if (num_identities == 0) - identities = xmalloc(sizeof(Identity)); - else - identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity)); - - identities[num_identities].key = RSA_new(); - k = identities[num_identities].key; - buffer_get_int(&e->input); /* bits */ - k->n = BN_new(); - buffer_get_bignum(&e->input, k->n); - k->e = BN_new(); - buffer_get_bignum(&e->input, k->e); - k->d = BN_new(); - buffer_get_bignum(&e->input, k->d); - k->iqmp = BN_new(); - buffer_get_bignum(&e->input, k->iqmp); - /* SSH and SSL have p and q swapped */ - k->q = BN_new(); - buffer_get_bignum(&e->input, k->q); /* p */ - k->p = BN_new(); - buffer_get_bignum(&e->input, k->p); /* q */ - - /* Generate additional parameters */ - aux = BN_new(); - ctx = BN_CTX_new(); - - BN_sub(aux, k->q, BN_value_one()); - k->dmq1 = BN_new(); - BN_mod(k->dmq1, k->d, aux, ctx); - - BN_sub(aux, k->p, BN_value_one()); - k->dmp1 = BN_new(); - BN_mod(k->dmp1, k->d, aux, ctx); - - BN_clear_free(aux); - BN_CTX_free(ctx); - - identities[num_identities].comment = buffer_get_string(&e->input, NULL); - - /* Check if we already have the key. */ - for (i = 0; i < num_identities; i++) - if (BN_cmp(identities[i].key->n, k->n) == 0) - { - /* We already have this key. Clear and free the new data and - return success. */ - RSA_free(k); - xfree(identities[num_identities].comment); + /* Mark that there are no identities. */ + num_identities = 0; /* Send success. */ buffer_put_int(&e->output, 1); buffer_put_char(&e->output, SSH_AGENT_SUCCESS); return; - } - - /* Increment the number of identities. */ - num_identities++; - - /* Send a success message. */ - buffer_put_int(&e->output, 1); - buffer_put_char(&e->output, SSH_AGENT_SUCCESS); +} + +/* + * Adds an identity to the agent. + */ +void +process_add_identity(SocketEntry *e) +{ + RSA *k; + int i; + BIGNUM *aux; + BN_CTX *ctx; + + if (num_identities == 0) + identities = xmalloc(sizeof(Identity)); + else + identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity)); + + identities[num_identities].key = RSA_new(); + k = identities[num_identities].key; + buffer_get_int(&e->input); /* bits */ + k->n = BN_new(); + buffer_get_bignum(&e->input, k->n); + k->e = BN_new(); + buffer_get_bignum(&e->input, k->e); + k->d = BN_new(); + buffer_get_bignum(&e->input, k->d); + k->iqmp = BN_new(); + buffer_get_bignum(&e->input, k->iqmp); + /* SSH and SSL have p and q swapped */ + k->q = BN_new(); + buffer_get_bignum(&e->input, k->q); /* p */ + k->p = BN_new(); + buffer_get_bignum(&e->input, k->p); /* q */ + + /* Generate additional parameters */ + aux = BN_new(); + ctx = BN_CTX_new(); + + BN_sub(aux, k->q, BN_value_one()); + k->dmq1 = BN_new(); + BN_mod(k->dmq1, k->d, aux, ctx); + + BN_sub(aux, k->p, BN_value_one()); + k->dmp1 = BN_new(); + BN_mod(k->dmp1, k->d, aux, ctx); + + BN_clear_free(aux); + BN_CTX_free(ctx); + + identities[num_identities].comment = buffer_get_string(&e->input, NULL); + + /* Check if we already have the key. */ + for (i = 0; i < num_identities; i++) + if (BN_cmp(identities[i].key->n, k->n) == 0) { + /* We already have this key. Clear and free the + new data and return success. */ + RSA_free(k); + xfree(identities[num_identities].comment); + + /* Send success. */ + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, SSH_AGENT_SUCCESS); + return; + } + /* Increment the number of identities. */ + num_identities++; + + /* Send a success message. */ + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, SSH_AGENT_SUCCESS); } void process_message(SocketEntry *e) { - unsigned int msg_len; - unsigned int type; - unsigned char *cp; - if (buffer_len(&e->input) < 5) - return; /* Incomplete message. */ - cp = (unsigned char *)buffer_ptr(&e->input); - msg_len = GET_32BIT(cp); - if (msg_len > 256 * 1024) - { - shutdown(e->fd, SHUT_RDWR); - close(e->fd); - e->type = AUTH_UNUSED; - return; - } - if (buffer_len(&e->input) < msg_len + 4) - return; - buffer_consume(&e->input, 4); - type = buffer_get_char(&e->input); - - switch (type) - { - case SSH_AGENTC_REQUEST_RSA_IDENTITIES: - process_request_identity(e); - break; - case SSH_AGENTC_RSA_CHALLENGE: - process_authentication_challenge(e); - break; - case SSH_AGENTC_ADD_RSA_IDENTITY: - process_add_identity(e); - break; - case SSH_AGENTC_REMOVE_RSA_IDENTITY: - process_remove_identity(e); - break; - case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: - process_remove_all_identities(e); - break; - default: - /* Unknown message. Respond with failure. */ - error("Unknown message %d", type); - buffer_clear(&e->input); - buffer_put_int(&e->output, 1); - buffer_put_char(&e->output, SSH_AGENT_FAILURE); - break; - } + unsigned int msg_len; + unsigned int type; + unsigned char *cp; + if (buffer_len(&e->input) < 5) + return; /* Incomplete message. */ + cp = (unsigned char *) buffer_ptr(&e->input); + msg_len = GET_32BIT(cp); + if (msg_len > 256 * 1024) { + shutdown(e->fd, SHUT_RDWR); + close(e->fd); + e->type = AUTH_UNUSED; + return; + } + if (buffer_len(&e->input) < msg_len + 4) + return; + buffer_consume(&e->input, 4); + type = buffer_get_char(&e->input); + + switch (type) { + case SSH_AGENTC_REQUEST_RSA_IDENTITIES: + process_request_identity(e); + break; + case SSH_AGENTC_RSA_CHALLENGE: + process_authentication_challenge(e); + break; + case SSH_AGENTC_ADD_RSA_IDENTITY: + process_add_identity(e); + break; + case SSH_AGENTC_REMOVE_RSA_IDENTITY: + process_remove_identity(e); + break; + case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: + process_remove_all_identities(e); + break; + default: + /* Unknown message. Respond with failure. */ + error("Unknown message %d", type); + buffer_clear(&e->input); + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, SSH_AGENT_FAILURE); + break; + } } void new_socket(int type, int fd) { - unsigned int i, old_alloc; - if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) - error("fcntl O_NONBLOCK: %s", strerror(errno)); - - if (fd > max_fd) - max_fd = fd; - - for (i = 0; i < sockets_alloc; i++) - if (sockets[i].type == AUTH_UNUSED) - { - sockets[i].fd = fd; - sockets[i].type = type; - buffer_init(&sockets[i].input); - buffer_init(&sockets[i].output); - return; - } - old_alloc = sockets_alloc; - sockets_alloc += 10; - if (sockets) - sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0])); - else - sockets = xmalloc(sockets_alloc * sizeof(sockets[0])); - for (i = old_alloc; i < sockets_alloc; i++) - sockets[i].type = AUTH_UNUSED; - sockets[old_alloc].type = type; - sockets[old_alloc].fd = fd; - buffer_init(&sockets[old_alloc].input); - buffer_init(&sockets[old_alloc].output); + unsigned int i, old_alloc; + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + error("fcntl O_NONBLOCK: %s", strerror(errno)); + + if (fd > max_fd) + max_fd = fd; + + for (i = 0; i < sockets_alloc; i++) + if (sockets[i].type == AUTH_UNUSED) { + sockets[i].fd = fd; + sockets[i].type = type; + buffer_init(&sockets[i].input); + buffer_init(&sockets[i].output); + return; + } + old_alloc = sockets_alloc; + sockets_alloc += 10; + if (sockets) + sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0])); + else + sockets = xmalloc(sockets_alloc * sizeof(sockets[0])); + for (i = old_alloc; i < sockets_alloc; i++) + sockets[i].type = AUTH_UNUSED; + sockets[old_alloc].type = type; + sockets[old_alloc].fd = fd; + buffer_init(&sockets[old_alloc].input); + buffer_init(&sockets[old_alloc].output); } void prepare_select(fd_set *readset, fd_set *writeset) { - unsigned int i; - for (i = 0; i < sockets_alloc; i++) - switch (sockets[i].type) - { - case AUTH_SOCKET: - case AUTH_CONNECTION: - FD_SET(sockets[i].fd, readset); - if (buffer_len(&sockets[i].output) > 0) - FD_SET(sockets[i].fd, writeset); - break; - case AUTH_UNUSED: - break; - default: - fatal("Unknown socket type %d", sockets[i].type); - break; - } + unsigned int i; + for (i = 0; i < sockets_alloc; i++) + switch (sockets[i].type) { + case AUTH_SOCKET: + case AUTH_CONNECTION: + FD_SET(sockets[i].fd, readset); + if (buffer_len(&sockets[i].output) > 0) + FD_SET(sockets[i].fd, writeset); + break; + case AUTH_UNUSED: + break; + default: + fatal("Unknown socket type %d", sockets[i].type); + break; + } } -void after_select(fd_set *readset, fd_set *writeset) +void +after_select(fd_set *readset, fd_set *writeset) { - unsigned int i; - int len, sock; - char buf[1024]; - struct sockaddr_un sunaddr; - - for (i = 0; i < sockets_alloc; i++) - switch (sockets[i].type) - { - case AUTH_UNUSED: - break; - case AUTH_SOCKET: - if (FD_ISSET(sockets[i].fd, readset)) - { - len = sizeof(sunaddr); - sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len); - if (sock < 0) - { - perror("accept from AUTH_SOCKET"); - break; - } - new_socket(AUTH_CONNECTION, sock); - } - break; - case AUTH_CONNECTION: - if (buffer_len(&sockets[i].output) > 0 && - FD_ISSET(sockets[i].fd, writeset)) - { - len = write(sockets[i].fd, buffer_ptr(&sockets[i].output), - buffer_len(&sockets[i].output)); - if (len <= 0) - { - shutdown(sockets[i].fd, SHUT_RDWR); - close(sockets[i].fd); - sockets[i].type = AUTH_UNUSED; - break; - } - buffer_consume(&sockets[i].output, len); - } - if (FD_ISSET(sockets[i].fd, readset)) - { - len = read(sockets[i].fd, buf, sizeof(buf)); - if (len <= 0) - { - shutdown(sockets[i].fd, SHUT_RDWR); - close(sockets[i].fd); - sockets[i].type = AUTH_UNUSED; - break; - } - buffer_append(&sockets[i].input, buf, len); - process_message(&sockets[i]); - } - break; - default: - fatal("Unknown type %d", sockets[i].type); - } + unsigned int i; + int len, sock; + char buf[1024]; + struct sockaddr_un sunaddr; + + for (i = 0; i < sockets_alloc; i++) + switch (sockets[i].type) { + case AUTH_UNUSED: + break; + case AUTH_SOCKET: + if (FD_ISSET(sockets[i].fd, readset)) { + len = sizeof(sunaddr); + sock = accept(sockets[i].fd, (struct sockaddr *) & sunaddr, &len); + if (sock < 0) { + perror("accept from AUTH_SOCKET"); + break; + } + new_socket(AUTH_CONNECTION, sock); + } + break; + case AUTH_CONNECTION: + if (buffer_len(&sockets[i].output) > 0 && + FD_ISSET(sockets[i].fd, writeset)) { + len = write(sockets[i].fd, buffer_ptr(&sockets[i].output), + buffer_len(&sockets[i].output)); + if (len <= 0) { + shutdown(sockets[i].fd, SHUT_RDWR); + close(sockets[i].fd); + sockets[i].type = AUTH_UNUSED; + break; + } + buffer_consume(&sockets[i].output, len); + } + if (FD_ISSET(sockets[i].fd, readset)) { + len = read(sockets[i].fd, buf, sizeof(buf)); + if (len <= 0) { + shutdown(sockets[i].fd, SHUT_RDWR); + close(sockets[i].fd); + sockets[i].type = AUTH_UNUSED; + break; + } + buffer_append(&sockets[i].input, buf, len); + process_message(&sockets[i]); + } + break; + default: + fatal("Unknown type %d", sockets[i].type); + } } void check_parent_exists(int sig) { - if (kill(parent_pid, 0) < 0) - { - /* printf("Parent has died - Authentication agent exiting.\n"); */ - exit(1); - } - signal(SIGALRM, check_parent_exists); - alarm(10); + if (kill(parent_pid, 0) < 0) { + /* printf("Parent has died - Authentication agent exiting.\n"); */ + exit(1); + } + signal(SIGALRM, check_parent_exists); + alarm(10); } void cleanup_socket(void) { - remove(socket_name); - rmdir(socket_dir); + remove(socket_name); + rmdir(socket_dir); } void cleanup_exit(int i) { - cleanup_socket(); - exit(i); + cleanup_socket(); + exit(i); } void usage() { - fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION); - fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n", - __progname); - exit(1); + fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION); + fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n", + __progname); + exit(1); } int main(int ac, char **av) { - fd_set readset, writeset; - int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch; - struct sockaddr_un sunaddr; - pid_t pid; - char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid]; - - /* check if RSA support exists */ - if (rsa_alive() == 0) { - fprintf(stderr, - "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", - __progname); - exit(1); - } - + fd_set readset, writeset; + int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch; + struct sockaddr_un sunaddr; + pid_t pid; + char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid]; + + /* check if RSA support exists */ + if (rsa_alive() == 0) { + fprintf(stderr, + "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", + __progname); + exit(1); + } #if defined(__GNU_LIBRARY__) - while ((ch = getopt(ac, av, "+cks")) != -1) + while ((ch = getopt(ac, av, "+cks")) != -1) { #else - while ((ch = getopt(ac, av, "cks")) != -1) + while ((ch = getopt(ac, av, "cks")) != -1) { #endif /* defined(__GNU_LIBRARY__) */ - { - switch (ch) - { - case 'c': - if (s_flag) - usage(); - c_flag++; - break; - case 'k': - k_flag++; - break; - case 's': - if (c_flag) - usage(); - s_flag++; - break; - default: - usage(); + switch (ch) { + case 'c': + if (s_flag) + usage(); + c_flag++; + break; + case 'k': + k_flag++; + break; + case 's': + if (c_flag) + usage(); + s_flag++; + break; + default: + usage(); + } } - } - ac -= optind; - av += optind; - - if (ac > 0 && (c_flag || k_flag || s_flag)) - usage(); - - if (ac == 0 && !c_flag && !k_flag && !s_flag) - { - shell = getenv("SHELL"); - if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0) - c_flag = 1; - } - - if (k_flag) - { - pidstr = getenv(SSH_AGENTPID_ENV_NAME); - if (pidstr == NULL) - { - fprintf(stderr, "%s not set, cannot kill agent\n", - SSH_AGENTPID_ENV_NAME); - exit(1); + ac -= optind; + av += optind; + + if (ac > 0 && (c_flag || k_flag || s_flag)) + usage(); + + if (ac == 0 && !c_flag && !k_flag && !s_flag) { + shell = getenv("SHELL"); + if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0) + c_flag = 1; } - pid = atoi(pidstr); - if (pid < 1) /* XXX PID_MAX check too */ - { - fprintf(stderr, "%s=\"%s\", which is not a good PID\n", - SSH_AGENTPID_ENV_NAME, pidstr); - exit(1); + if (k_flag) { + pidstr = getenv(SSH_AGENTPID_ENV_NAME); + if (pidstr == NULL) { + fprintf(stderr, "%s not set, cannot kill agent\n", + SSH_AGENTPID_ENV_NAME); + exit(1); + } + pid = atoi(pidstr); + if (pid < 1) { /* XXX PID_MAX check too */ + fprintf(stderr, "%s=\"%s\", which is not a good PID\n", + SSH_AGENTPID_ENV_NAME, pidstr); + exit(1); + } + if (kill(pid, SIGTERM) == -1) { + perror("kill"); + exit(1); + } + format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; + printf(format, SSH_AUTHSOCKET_ENV_NAME); + printf(format, SSH_AGENTPID_ENV_NAME); + printf("echo Agent pid %d killed;\n", pid); + exit(0); } - if (kill(pid, SIGTERM) == -1) - { - perror("kill"); - exit(1); + parent_pid = getpid(); + + /* Create private directory for agent socket */ + strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir); + if (mkdtemp(socket_dir) == NULL) { + perror("mkdtemp: private socket dir"); + exit(1); + } + snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir, + parent_pid); + + /* Create socket early so it will exist before command gets run + from the parent. */ + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + perror("socket"); + cleanup_exit(1); + } + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); + if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) { + perror("bind"); + cleanup_exit(1); + } + if (listen(sock, 5) < 0) { + perror("listen"); + cleanup_exit(1); } - format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; - printf(format, SSH_AUTHSOCKET_ENV_NAME); - printf(format, SSH_AGENTPID_ENV_NAME); - printf("echo Agent pid %d killed;\n", pid); - exit(0); - } - - parent_pid = getpid(); - - /* Create private directory for agent socket */ - strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir); - if (mkdtemp(socket_dir) == NULL) { - perror("mkdtemp: private socket dir"); - exit(1); - } - snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir, - parent_pid); - - /* Create socket early so it will exist before command gets run from - the parent. */ - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) - { - perror("socket"); - cleanup_exit(1); - } - memset(&sunaddr, 0, sizeof(sunaddr)); - sunaddr.sun_family = AF_UNIX; - strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); - if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) - { - perror("bind"); - cleanup_exit(1); - } - if (listen(sock, 5) < 0) - { - perror("listen"); - cleanup_exit(1); - } - - /* Fork, and have the parent execute the command, if any, or present the - socket data. The child continues as the authentication agent. */ - pid = fork(); - if (pid == -1) - { - perror("fork"); - exit(1); - } - if (pid != 0) - { /* Parent - execute the given command. */ - close(sock); - snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid); - if (ac == 0) - { - format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; - printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, - SSH_AUTHSOCKET_ENV_NAME); - printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, - SSH_AGENTPID_ENV_NAME); - printf("echo Agent pid %d;\n", pid); - exit(0); + /* Fork, and have the parent execute the command, if any, or + present the socket data. The child continues as the + authentication agent. */ + pid = fork(); + if (pid == -1) { + perror("fork"); + exit(1); } + if (pid != 0) { /* Parent - execute the given command. */ + close(sock); + snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid); + if (ac == 0) { + format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; + printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, + SSH_AUTHSOCKET_ENV_NAME); + printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, + SSH_AGENTPID_ENV_NAME); + printf("echo Agent pid %d;\n", pid); + exit(0); + } + setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1); + setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1); + execvp(av[0], av); + perror(av[0]); + exit(1); + } + close(0); + close(1); + close(2); - setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1); - setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1); - execvp(av[0], av); - perror(av[0]); - exit(1); - } - - close(0); - close(1); - close(2); - - if (setsid() == -1) - { - perror("setsid"); - cleanup_exit(1); - } - - if (atexit(cleanup_socket) < 0) - { - perror("atexit"); - cleanup_exit(1); - } - - new_socket(AUTH_SOCKET, sock); - if (ac > 0) - { - signal(SIGALRM, check_parent_exists); - alarm(10); - } - - signal(SIGINT, SIG_IGN); - signal(SIGPIPE, SIG_IGN); - while (1) - { - FD_ZERO(&readset); - FD_ZERO(&writeset); - prepare_select(&readset, &writeset); - if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0) - { - if (errno == EINTR) - continue; - exit(1); + if (setsid() == -1) { + perror("setsid"); + cleanup_exit(1); + } + if (atexit(cleanup_socket) < 0) { + perror("atexit"); + cleanup_exit(1); + } + new_socket(AUTH_SOCKET, sock); + if (ac > 0) { + signal(SIGALRM, check_parent_exists); + alarm(10); + } + signal(SIGINT, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + while (1) { + FD_ZERO(&readset); + FD_ZERO(&writeset); + prepare_select(&readset, &writeset); + if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + exit(1); + } + after_select(&readset, &writeset); } - after_select(&readset, &writeset); - } - /*NOTREACHED*/ + /* NOTREACHED */ } diff --git a/ssh-keygen.c b/ssh-keygen.c index 8a9da79d..2fb7bb86 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,17 +1,10 @@ /* - -ssh-keygen.c - -Author: Tatu Ylonen - -Copyright (c) 1994 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Mon Mar 27 02:26:40 1995 ylo - -Identity and host key generation and maintenance. - -*/ + * Author: Tatu Ylonen + * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Mon Mar 27 02:26:40 1995 ylo + * Identity and host key generation and maintenance. + */ #include "includes.h" RCSID("$Id$"); @@ -21,12 +14,6 @@ RCSID("$Id$"); #include "xmalloc.h" #include "fingerprint.h" -#ifdef HAVE___PROGNAME -extern char *__progname; -#else /* HAVE___PROGNAME */ -const char *__progname = "ssh-keygen"; -#endif /* HAVE___PROGNAME */ - /* Generated private key. */ RSA *private_key; @@ -64,533 +51,493 @@ char *identity_new_passphrase = NULL; char *identity_comment = NULL; /* argv0 */ +#ifdef HAVE___PROGNAME extern char *__progname; +#else /* HAVE___PROGNAME */ +const char *__progname = "ssh-keygen"; +#endif /* HAVE___PROGNAME */ void ask_filename(struct passwd *pw, const char *prompt) { - char buf[1024]; - snprintf(identity_file, sizeof(identity_file), "%s/%s", - pw->pw_dir, SSH_CLIENT_IDENTITY); - printf("%s (%s): ", prompt, identity_file); - fflush(stdout); - if (fgets(buf, sizeof(buf), stdin) == NULL) - exit(1); - if (strchr(buf, '\n')) - *strchr(buf, '\n') = 0; - if (strcmp(buf, "") != 0) - strlcpy(identity_file, buf, sizeof(identity_file)); - have_identity = 1; + char buf[1024]; + snprintf(identity_file, sizeof(identity_file), "%s/%s", + pw->pw_dir, SSH_CLIENT_IDENTITY); + printf("%s (%s): ", prompt, identity_file); + fflush(stdout); + if (fgets(buf, sizeof(buf), stdin) == NULL) + exit(1); + if (strchr(buf, '\n')) + *strchr(buf, '\n') = 0; + if (strcmp(buf, "") != 0) + strlcpy(identity_file, buf, sizeof(identity_file)); + have_identity = 1; } void do_fingerprint(struct passwd *pw) { - char *comment; - RSA *public_key; - struct stat st; - - if (!have_identity) - ask_filename(pw, "Enter file in which the key is"); - if (stat(identity_file, &st) < 0) - { - perror(identity_file); - exit(1); - } - public_key = RSA_new(); - if (!load_public_key(identity_file, public_key, &comment)) { - char *cp, line[1024]; - BIGNUM *e, *n; - int dummy, invalid = 0; - FILE *f = fopen(identity_file, "r"); - n = BN_new(); - e = BN_new(); - if (f && fgets(line, sizeof(line), f)) { - cp = line; - line[strlen(line)-1] = '\0'; - if (auth_rsa_read_key(&cp, &dummy, e, n)) { - public_key->e = e; - public_key->n = n; - comment = xstrdup(cp ? cp : "no comment"); - } else { - invalid = 1; - } - } else { - invalid = 1; - } - if (invalid) { - printf("%s is not a valid key file.\n", identity_file); - BN_free(e); - BN_free(n); - exit(1); - } - } - - printf("%d %s %s\n", BN_num_bits(public_key->n), - fingerprint(public_key->e, public_key->n), - comment); - RSA_free(public_key); - exit(0); + char *comment; + RSA *public_key; + struct stat st; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + public_key = RSA_new(); + if (!load_public_key(identity_file, public_key, &comment)) { + char *cp, line[1024]; + BIGNUM *e, *n; + int dummy, invalid = 0; + FILE *f = fopen(identity_file, "r"); + n = BN_new(); + e = BN_new(); + if (f && fgets(line, sizeof(line), f)) { + cp = line; + line[strlen(line) - 1] = '\0'; + if (auth_rsa_read_key(&cp, &dummy, e, n)) { + public_key->e = e; + public_key->n = n; + comment = xstrdup(cp ? cp : "no comment"); + } else { + invalid = 1; + } + } else { + invalid = 1; + } + if (invalid) { + printf("%s is not a valid key file.\n", identity_file); + BN_free(e); + BN_free(n); + exit(1); + } + } + printf("%d %s %s\n", BN_num_bits(public_key->n), + fingerprint(public_key->e, public_key->n), + comment); + RSA_free(public_key); + exit(0); } -/* Perform changing a passphrase. The argument is the passwd structure - for the current user. */ - +/* + * Perform changing a passphrase. The argument is the passwd structure + * for the current user. + */ void do_change_passphrase(struct passwd *pw) { - char *comment; - char *old_passphrase, *passphrase1, *passphrase2; - struct stat st; - RSA *private_key; - - if (!have_identity) - ask_filename(pw, "Enter file in which the key is"); - /* Check if the file exists. */ - if (stat(identity_file, &st) < 0) - { - perror(identity_file); - exit(1); - } - - /* Try to load the public key from the file the verify that it is - readable and of the proper format. */ - public_key = RSA_new(); - if (!load_public_key(identity_file, public_key, NULL)) - { - printf("%s is not a valid key file.\n", identity_file); - exit(1); - } - /* Clear the public key since we are just about to load the whole file. */ - RSA_free(public_key); - - /* Try to load the file with empty passphrase. */ - private_key = RSA_new(); - if (!load_private_key(identity_file, "", private_key, &comment)) { - /* Read passphrase from the user. */ - if (identity_passphrase) - old_passphrase = xstrdup(identity_passphrase); - else - old_passphrase = read_passphrase("Enter old passphrase: ", 1); - /* Try to load using the passphrase. */ - if (!load_private_key(identity_file, old_passphrase, private_key, &comment)) - { - memset(old_passphrase, 0, strlen(old_passphrase)); - xfree(old_passphrase); - printf("Bad passphrase.\n"); - exit(1); - } - /* Destroy the passphrase. */ - memset(old_passphrase, 0, strlen(old_passphrase)); - xfree(old_passphrase); - } - printf("Key has comment '%s'\n", comment); - - /* Ask the new passphrase (twice). */ - if (identity_new_passphrase) - { - passphrase1 = xstrdup(identity_new_passphrase); - passphrase2 = NULL; - } - else - { - passphrase1 = - read_passphrase("Enter new passphrase (empty for no passphrase): ", 1); - passphrase2 = read_passphrase("Enter same passphrase again: ", 1); - - /* Verify that they are the same. */ - if (strcmp(passphrase1, passphrase2) != 0) - { - memset(passphrase1, 0, strlen(passphrase1)); - memset(passphrase2, 0, strlen(passphrase2)); - xfree(passphrase1); - xfree(passphrase2); - printf("Pass phrases do not match. Try again.\n"); - exit(1); + char *comment; + char *old_passphrase, *passphrase1, *passphrase2; + struct stat st; + RSA *private_key; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + /* Check if the file exists. */ + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + /* Try to load the public key from the file the verify that it is + readable and of the proper format. */ + public_key = RSA_new(); + if (!load_public_key(identity_file, public_key, NULL)) { + printf("%s is not a valid key file.\n", identity_file); + exit(1); + } + /* Clear the public key since we are just about to load the whole file. */ + RSA_free(public_key); + + /* Try to load the file with empty passphrase. */ + private_key = RSA_new(); + if (!load_private_key(identity_file, "", private_key, &comment)) { + /* Read passphrase from the user. */ + if (identity_passphrase) + old_passphrase = xstrdup(identity_passphrase); + else + old_passphrase = read_passphrase("Enter old passphrase: ", 1); + /* Try to load using the passphrase. */ + if (!load_private_key(identity_file, old_passphrase, private_key, &comment)) { + memset(old_passphrase, 0, strlen(old_passphrase)); + xfree(old_passphrase); + printf("Bad passphrase.\n"); + exit(1); + } + /* Destroy the passphrase. */ + memset(old_passphrase, 0, strlen(old_passphrase)); + xfree(old_passphrase); + } + printf("Key has comment '%s'\n", comment); + + /* Ask the new passphrase (twice). */ + if (identity_new_passphrase) { + passphrase1 = xstrdup(identity_new_passphrase); + passphrase2 = NULL; + } else { + passphrase1 = + read_passphrase("Enter new passphrase (empty for no passphrase): ", 1); + passphrase2 = read_passphrase("Enter same passphrase again: ", 1); + + /* Verify that they are the same. */ + if (strcmp(passphrase1, passphrase2) != 0) { + memset(passphrase1, 0, strlen(passphrase1)); + memset(passphrase2, 0, strlen(passphrase2)); + xfree(passphrase1); + xfree(passphrase2); + printf("Pass phrases do not match. Try again.\n"); + exit(1); + } + /* Destroy the other copy. */ + memset(passphrase2, 0, strlen(passphrase2)); + xfree(passphrase2); } - /* Destroy the other copy. */ - memset(passphrase2, 0, strlen(passphrase2)); - xfree(passphrase2); - } - - /* Save the file using the new passphrase. */ - if (!save_private_key(identity_file, passphrase1, private_key, comment)) - { - printf("Saving the key failed: %s: %s.\n", - identity_file, strerror(errno)); - memset(passphrase1, 0, strlen(passphrase1)); - xfree(passphrase1); - RSA_free(private_key); - xfree(comment); - exit(1); - } - /* Destroy the passphrase and the copy of the key in memory. */ - memset(passphrase1, 0, strlen(passphrase1)); - xfree(passphrase1); - RSA_free(private_key); /* Destroys contents */ - xfree(comment); - - printf("Your identification has been saved with the new passphrase.\n"); - exit(0); -} -/* Change the comment of a private key file. */ + /* Save the file using the new passphrase. */ + if (!save_private_key(identity_file, passphrase1, private_key, comment)) { + printf("Saving the key failed: %s: %s.\n", + identity_file, strerror(errno)); + memset(passphrase1, 0, strlen(passphrase1)); + xfree(passphrase1); + RSA_free(private_key); + xfree(comment); + exit(1); + } + /* Destroy the passphrase and the copy of the key in memory. */ + memset(passphrase1, 0, strlen(passphrase1)); + xfree(passphrase1); + RSA_free(private_key); /* Destroys contents */ + xfree(comment); + + printf("Your identification has been saved with the new passphrase.\n"); + exit(0); +} +/* + * Change the comment of a private key file. + */ void do_change_comment(struct passwd *pw) { - char new_comment[1024], *comment; - RSA *private_key; - char *passphrase; - struct stat st; - FILE *f; - char *tmpbuf; - - if (!have_identity) - ask_filename(pw, "Enter file in which the key is"); - /* Check if the file exists. */ - if (stat(identity_file, &st) < 0) - { - perror(identity_file); - exit(1); - } - - /* Try to load the public key from the file the verify that it is - readable and of the proper format. */ - public_key = RSA_new(); - if (!load_public_key(identity_file, public_key, NULL)) - { - printf("%s is not a valid key file.\n", identity_file); - exit(1); - } - - private_key = RSA_new(); - /* Try to load the file with empty passphrase. */ - if (load_private_key(identity_file, "", private_key, &comment)) - passphrase = xstrdup(""); - else - { - /* Read passphrase from the user. */ - if (identity_passphrase) - passphrase = xstrdup(identity_passphrase); - else - if (identity_new_passphrase) - passphrase = xstrdup(identity_new_passphrase); - else - passphrase = read_passphrase("Enter passphrase: ", 1); - /* Try to load using the passphrase. */ - if (!load_private_key(identity_file, passphrase, private_key, &comment)) - { - memset(passphrase, 0, strlen(passphrase)); - xfree(passphrase); - printf("Bad passphrase.\n"); - exit(1); + char new_comment[1024], *comment; + RSA *private_key; + char *passphrase; + struct stat st; + FILE *f; + char *tmpbuf; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + /* Check if the file exists. */ + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + /* Try to load the public key from the file the verify that it is + readable and of the proper format. */ + public_key = RSA_new(); + if (!load_public_key(identity_file, public_key, NULL)) { + printf("%s is not a valid key file.\n", identity_file); + exit(1); } - } - printf("Key now has comment '%s'\n", comment); - - if (identity_comment) - { - strlcpy(new_comment, identity_comment, sizeof(new_comment)); - } - else - { - printf("Enter new comment: "); - fflush(stdout); - if (!fgets(new_comment, sizeof(new_comment), stdin)) - { - memset(passphrase, 0, strlen(passphrase)); - RSA_free(private_key); - exit(1); + private_key = RSA_new(); + /* Try to load the file with empty passphrase. */ + if (load_private_key(identity_file, "", private_key, &comment)) + passphrase = xstrdup(""); + else { + /* Read passphrase from the user. */ + if (identity_passphrase) + passphrase = xstrdup(identity_passphrase); + else if (identity_new_passphrase) + passphrase = xstrdup(identity_new_passphrase); + else + passphrase = read_passphrase("Enter passphrase: ", 1); + /* Try to load using the passphrase. */ + if (!load_private_key(identity_file, passphrase, private_key, &comment)) { + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + printf("Bad passphrase.\n"); + exit(1); + } + } + printf("Key now has comment '%s'\n", comment); + + if (identity_comment) { + strlcpy(new_comment, identity_comment, sizeof(new_comment)); + } else { + printf("Enter new comment: "); + fflush(stdout); + if (!fgets(new_comment, sizeof(new_comment), stdin)) { + memset(passphrase, 0, strlen(passphrase)); + RSA_free(private_key); + exit(1); + } + /* Remove terminating newline from comment. */ + if (strchr(new_comment, '\n')) + *strchr(new_comment, '\n') = 0; + } + + /* Save the file using the new passphrase. */ + if (!save_private_key(identity_file, passphrase, private_key, new_comment)) { + printf("Saving the key failed: %s: %s.\n", + identity_file, strerror(errno)); + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + RSA_free(private_key); + xfree(comment); + exit(1); } - - /* Remove terminating newline from comment. */ - if (strchr(new_comment, '\n')) - *strchr(new_comment, '\n') = 0; - } - - /* Save the file using the new passphrase. */ - if (!save_private_key(identity_file, passphrase, private_key, new_comment)) - { - printf("Saving the key failed: %s: %s.\n", - identity_file, strerror(errno)); - memset(passphrase, 0, strlen(passphrase)); - xfree(passphrase); - RSA_free(private_key); - xfree(comment); - exit(1); - } - - /* Destroy the passphrase and the private key in memory. */ - memset(passphrase, 0, strlen(passphrase)); - xfree(passphrase); - RSA_free(private_key); - - /* Save the public key in text format in a file with the same name but - .pub appended. */ - strlcat(identity_file, ".pub", sizeof(identity_file)); - f = fopen(identity_file, "w"); - if (!f) - { - printf("Could not save your public key in %s\n", identity_file); - exit(1); - } - fprintf(f, "%d ", BN_num_bits(public_key->n)); - tmpbuf = BN_bn2dec(public_key->e); - fprintf(f, "%s ", tmpbuf); - free (tmpbuf); - tmpbuf = BN_bn2dec(public_key->n); - fprintf(f, "%s %s\n", tmpbuf, new_comment); - free (tmpbuf); - fclose(f); - - xfree(comment); - - printf("The comment in your key file has been changed.\n"); - exit(0); + /* Destroy the passphrase and the private key in memory. */ + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + RSA_free(private_key); + + /* Save the public key in text format in a file with the same name + but .pub appended. */ + strlcat(identity_file, ".pub", sizeof(identity_file)); + f = fopen(identity_file, "w"); + if (!f) { + printf("Could not save your public key in %s\n", identity_file); + exit(1); + } + fprintf(f, "%d ", BN_num_bits(public_key->n)); + tmpbuf = BN_bn2dec(public_key->e); + fprintf(f, "%s ", tmpbuf); + free(tmpbuf); + tmpbuf = BN_bn2dec(public_key->n); + fprintf(f, "%s %s\n", tmpbuf, new_comment); + free(tmpbuf); + fclose(f); + + xfree(comment); + + printf("The comment in your key file has been changed.\n"); + exit(0); } void usage(void) { - printf("ssh-keygen version %s\n", SSH_VERSION); - printf("Usage: %s [-b bits] [-p] [-c] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname); - exit(1); + printf("ssh-keygen version %s\n", SSH_VERSION); + printf("Usage: %s [-b bits] [-p] [-c] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname); + exit(1); } -/* Main program for key management. */ - +/* + * Main program for key management. + */ int main(int ac, char **av) { - char dotsshdir[16*1024], comment[1024], *passphrase1, *passphrase2; - struct passwd *pw; - char *tmpbuf; - int opt; - struct stat st; - FILE *f; - char hostname[MAXHOSTNAMELEN]; - extern int optind; - extern char *optarg; - - /* check if RSA support exists */ - if (rsa_alive() == 0) { - extern char *__progname; - - fprintf(stderr, - "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", - __progname); - exit(1); - } - - /* Get user\'s passwd structure. We need this for the home directory. */ - pw = getpwuid(getuid()); - if (!pw) - { - printf("You don't exist, go away!\n"); - exit(1); - } - - /* Parse command line arguments. */ - while ((opt = getopt(ac, av, "qpclb:f:P:N:C:")) != EOF) - { - switch (opt) - { - case 'b': - bits = atoi(optarg); - if (bits < 512 || bits > 32768) - { - printf("Bits has bad value.\n"); - exit(1); - } - break; - - case 'l': - print_fingerprint = 1; - break; - - case 'p': - change_passphrase = 1; - break; - - case 'c': - change_comment = 1; - break; - - case 'f': - strlcpy(identity_file, optarg, sizeof(identity_file)); - have_identity = 1; - break; - - case 'P': - identity_passphrase = optarg; - break; - - case 'N': - identity_new_passphrase = optarg; - break; - - case 'C': - identity_comment = optarg; - break; - - case 'q': - quiet = 1; - break; - - case '?': - default: - usage(); + char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2; + struct passwd *pw; + char *tmpbuf; + int opt; + struct stat st; + FILE *f; + char hostname[MAXHOSTNAMELEN]; + extern int optind; + extern char *optarg; + + /* check if RSA support exists */ + if (rsa_alive() == 0) { + extern char *__progname; + + fprintf(stderr, + "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", + __progname); + exit(1); } - } - if (optind < ac) - { - printf("Too many arguments.\n"); - usage(); - } - if (change_passphrase && change_comment) - { - printf("Can only have one of -p and -c.\n"); - usage(); - } - - if (print_fingerprint) - do_fingerprint(pw); - - /* If the user requested to change the passphrase, do it now. This - function never returns. */ - if (change_passphrase) - do_change_passphrase(pw); - - /* If the user requested to change the comment, do it now. This function - never returns. */ - if (change_comment) - do_change_comment(pw); - - arc4random_stir(); - - if (quiet) - rsa_set_verbose(0); - - /* Generate the rsa key pair. */ - private_key = RSA_new(); - public_key = RSA_new(); - rsa_generate_key(private_key, public_key, bits); - - if (!have_identity) - ask_filename(pw, "Enter file in which to save the key"); - - /* Create ~/.ssh directory if it doesn\'t already exist. */ - snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, SSH_USER_DIR); - if (strstr(identity_file, dotsshdir) != NULL && - stat(dotsshdir, &st) < 0) { - if (mkdir(dotsshdir, 0755) < 0) - error("Could not create directory '%s'.", dotsshdir); - else if(!quiet) - printf("Created directory '%s'.\n", dotsshdir); - } - - /* If the file already exists, ask the user to confirm. */ - if (stat(identity_file, &st) >= 0) - { - char yesno[3]; - printf("%s already exists.\n", identity_file); - printf("Overwrite (y/n)? "); - fflush(stdout); - if (fgets(yesno, sizeof(yesno), stdin) == NULL) - exit(1); - if (yesno[0] != 'y' && yesno[0] != 'Y') - exit(1); - } - - /* Ask for a passphrase (twice). */ - if (identity_passphrase) - passphrase1 = xstrdup(identity_passphrase); - else - if (identity_new_passphrase) - passphrase1 = xstrdup(identity_new_passphrase); - else - { - passphrase_again: - passphrase1 = - read_passphrase("Enter passphrase (empty for no passphrase): ", 1); - passphrase2 = read_passphrase("Enter same passphrase again: ", 1); - if (strcmp(passphrase1, passphrase2) != 0) - { - /* The passphrases do not match. Clear them and retry. */ - memset(passphrase1, 0, strlen(passphrase1)); - memset(passphrase2, 0, strlen(passphrase2)); - xfree(passphrase1); - xfree(passphrase2); - printf("Passphrases do not match. Try again.\n"); - goto passphrase_again; - } - /* Clear the other copy of the passphrase. */ - memset(passphrase2, 0, strlen(passphrase2)); - xfree(passphrase2); - } - - /* Create default commend field for the passphrase. The user can later - edit this field. */ - if (identity_comment) - { - strlcpy(comment, identity_comment, sizeof(comment)); - } - else - { - if (gethostname(hostname, sizeof(hostname)) < 0) - { - perror("gethostname"); - exit(1); + /* Get user\'s passwd structure. We need this for the home + directory. */ + pw = getpwuid(getuid()); + if (!pw) { + printf("You don't exist, go away!\n"); + exit(1); + } + /* Parse command line arguments. */ + while ((opt = getopt(ac, av, "qpclb:f:P:N:C:")) != EOF) { + switch (opt) { + case 'b': + bits = atoi(optarg); + if (bits < 512 || bits > 32768) { + printf("Bits has bad value.\n"); + exit(1); + } + break; + + case 'l': + print_fingerprint = 1; + break; + + case 'p': + change_passphrase = 1; + break; + + case 'c': + change_comment = 1; + break; + + case 'f': + strlcpy(identity_file, optarg, sizeof(identity_file)); + have_identity = 1; + break; + + case 'P': + identity_passphrase = optarg; + break; + + case 'N': + identity_new_passphrase = optarg; + break; + + case 'C': + identity_comment = optarg; + break; + + case 'q': + quiet = 1; + break; + + case '?': + default: + usage(); + } + } + if (optind < ac) { + printf("Too many arguments.\n"); + usage(); + } + if (change_passphrase && change_comment) { + printf("Can only have one of -p and -c.\n"); + usage(); + } + if (print_fingerprint) + do_fingerprint(pw); + + /* If the user requested to change the passphrase, do it now. + This function never returns. */ + if (change_passphrase) + do_change_passphrase(pw); + + /* If the user requested to change the comment, do it now. This + function never returns. */ + if (change_comment) + do_change_comment(pw); + + arc4random_stir(); + + if (quiet) + rsa_set_verbose(0); + + /* Generate the rsa key pair. */ + private_key = RSA_new(); + public_key = RSA_new(); + rsa_generate_key(private_key, public_key, bits); + + if (!have_identity) + ask_filename(pw, "Enter file in which to save the key"); + + /* Create ~/.ssh directory if it doesn\'t already exist. */ + snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, SSH_USER_DIR); + if (strstr(identity_file, dotsshdir) != NULL && + stat(dotsshdir, &st) < 0) { + if (mkdir(dotsshdir, 0755) < 0) + error("Could not create directory '%s'.", dotsshdir); + else if (!quiet) + printf("Created directory '%s'.\n", dotsshdir); + } + /* If the file already exists, ask the user to confirm. */ + if (stat(identity_file, &st) >= 0) { + char yesno[3]; + printf("%s already exists.\n", identity_file); + printf("Overwrite (y/n)? "); + fflush(stdout); + if (fgets(yesno, sizeof(yesno), stdin) == NULL) + exit(1); + if (yesno[0] != 'y' && yesno[0] != 'Y') + exit(1); + } + /* Ask for a passphrase (twice). */ + if (identity_passphrase) + passphrase1 = xstrdup(identity_passphrase); + else if (identity_new_passphrase) + passphrase1 = xstrdup(identity_new_passphrase); + else { +passphrase_again: + passphrase1 = + read_passphrase("Enter passphrase (empty for no passphrase): ", 1); + passphrase2 = read_passphrase("Enter same passphrase again: ", 1); + if (strcmp(passphrase1, passphrase2) != 0) { + /* The passphrases do not match. Clear them and retry. */ + memset(passphrase1, 0, strlen(passphrase1)); + memset(passphrase2, 0, strlen(passphrase2)); + xfree(passphrase1); + xfree(passphrase2); + printf("Passphrases do not match. Try again.\n"); + goto passphrase_again; + } + /* Clear the other copy of the passphrase. */ + memset(passphrase2, 0, strlen(passphrase2)); + xfree(passphrase2); + } + + /* Create default commend field for the passphrase. The user can + later edit this field. */ + if (identity_comment) { + strlcpy(comment, identity_comment, sizeof(comment)); + } else { + if (gethostname(hostname, sizeof(hostname)) < 0) { + perror("gethostname"); + exit(1); + } + snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); + } + + /* Save the key with the given passphrase and comment. */ + if (!save_private_key(identity_file, passphrase1, private_key, comment)) { + printf("Saving the key failed: %s: %s.\n", + identity_file, strerror(errno)); + memset(passphrase1, 0, strlen(passphrase1)); + xfree(passphrase1); + exit(1); + } + /* Clear the passphrase. */ + memset(passphrase1, 0, strlen(passphrase1)); + xfree(passphrase1); + + /* Clear the private key and the random number generator. */ + RSA_free(private_key); + arc4random_stir(); + + if (!quiet) + printf("Your identification has been saved in %s.\n", identity_file); + + /* Save the public key in text format in a file with the same name + but .pub appended. */ + strlcat(identity_file, ".pub", sizeof(identity_file)); + f = fopen(identity_file, "w"); + if (!f) { + printf("Could not save your public key in %s\n", identity_file); + exit(1); + } + fprintf(f, "%d ", BN_num_bits(public_key->n)); + tmpbuf = BN_bn2dec(public_key->e); + fprintf(f, "%s ", tmpbuf); + free(tmpbuf); + tmpbuf = BN_bn2dec(public_key->n); + fprintf(f, "%s %s\n", tmpbuf, comment); + free(tmpbuf); + fclose(f); + + if (!quiet) { + printf("Your public key has been saved in %s.\n", identity_file); + printf("The key fingerprint is:\n"); + printf("%d %s %s\n", BN_num_bits(public_key->n), + fingerprint(public_key->e, public_key->n), + comment); } - snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); - } - - /* Save the key with the given passphrase and comment. */ - if (!save_private_key(identity_file, passphrase1, private_key, comment)) - { - printf("Saving the key failed: %s: %s.\n", - identity_file, strerror(errno)); - memset(passphrase1, 0, strlen(passphrase1)); - xfree(passphrase1); - exit(1); - } - /* Clear the passphrase. */ - memset(passphrase1, 0, strlen(passphrase1)); - xfree(passphrase1); - - /* Clear the private key and the random number generator. */ - RSA_free(private_key); - arc4random_stir(); - - if (!quiet) - printf("Your identification has been saved in %s.\n", identity_file); - - /* Save the public key in text format in a file with the same name but - .pub appended. */ - strlcat(identity_file, ".pub", sizeof(identity_file)); - f = fopen(identity_file, "w"); - if (!f) - { - printf("Could not save your public key in %s\n", identity_file); - exit(1); - } - fprintf(f, "%d ", BN_num_bits(public_key->n)); - tmpbuf = BN_bn2dec(public_key->e); - fprintf(f, "%s ", tmpbuf); - free(tmpbuf); - tmpbuf = BN_bn2dec(public_key->n); - fprintf(f, "%s %s\n", tmpbuf, comment); - free(tmpbuf); - fclose(f); - - if (!quiet) { - printf("Your public key has been saved in %s.\n", identity_file); - printf("The key fingerprint is:\n"); - printf("%d %s %s\n", BN_num_bits(public_key->n), - fingerprint(public_key->e, public_key->n), - comment); - } - - exit(0); + exit(0); } diff --git a/ssh.1 b/ssh.1 index a93fa1e3..08760ceb 100644 --- a/ssh.1 +++ b/ssh.1 @@ -662,6 +662,16 @@ or RSA authentication will only be attempted if the identity file exists, or an authentication agent is running. +.It Cm SkeyAuthentication +Specifies whether to use +.Xr skey 1 +authentication. The argument to +this keyword must be +.Dq yes +or +.Dq no . +The default is +.Dq no . .It Cm CheckHostIP If this flag is set to .Dq yes , diff --git a/ssh.c b/ssh.c index 28fa3290..aade7ba5 100644 --- a/ssh.c +++ b/ssh.c @@ -1,21 +1,14 @@ /* - -ssh.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sat Mar 18 16:36:11 1995 ylo - -Ssh client program. This program can be used to log into a remote machine. -The software supports strong authentication, encryption, and forwarding -of X11, TCP/IP, and authentication connections. - -Modified to work with SSL by Niels Provos in Canada. - -*/ + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Sat Mar 18 16:36:11 1995 ylo + * Ssh client program. This program can be used to log into a remote machine. + * The software supports strong authentication, encryption, and forwarding + * of X11, TCP/IP, and authentication connections. + * + * Modified to work with SSL by Niels Provos in Canada. + */ #include "includes.h" RCSID("$Id$"); @@ -34,13 +27,11 @@ extern char *__progname; const char *__progname = "ssh"; #endif /* HAVE___PROGNAME */ -/* Flag indicating whether debug mode is on. This can be set on the - command line. */ +/* Flag indicating whether debug mode is on. This can be set on the command line. */ int debug_flag = 0; -/* Flag indicating whether to allocate a pseudo tty. This can be set on the - command line, and is automatically set if no command is given on the command - line. */ +/* Flag indicating whether to allocate a pseudo tty. This can be set on the command + line, and is automatically set if no command is given on the command line. */ int tty_flag = 0; /* Flag indicating that nothing should be read from stdin. This can be set @@ -87,730 +78,680 @@ uid_t original_real_uid; void usage() { - fprintf(stderr, "Usage: %s [options] host [command]\n", av0); - fprintf(stderr, "Options:\n"); - fprintf(stderr, " -l user Log in using this user name.\n"); - fprintf(stderr, " -n Redirect input from /dev/null.\n"); - fprintf(stderr, " -a Disable authentication agent forwarding.\n"); + fprintf(stderr, "Usage: %s [options] host [command]\n", av0); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -l user Log in using this user name.\n"); + fprintf(stderr, " -n Redirect input from /dev/null.\n"); + fprintf(stderr, " -a Disable authentication agent forwarding.\n"); #ifdef AFS - fprintf(stderr, " -k Disable Kerberos ticket and AFS token forwarding.\n"); -#endif /* AFS */ - fprintf(stderr, " -x Disable X11 connection forwarding.\n"); - fprintf(stderr, " -i file Identity for RSA authentication (default: ~/.ssh/identity).\n"); - fprintf(stderr, " -t Tty; allocate a tty even if command is given.\n"); - fprintf(stderr, " -v Verbose; display verbose debugging messages.\n"); - fprintf(stderr, " -V Display version number only.\n"); - fprintf(stderr, " -P Don't allocate a privileged port.\n"); - fprintf(stderr, " -q Quiet; don't display any warning messages.\n"); - fprintf(stderr, " -f Fork into background after authentication.\n"); - fprintf(stderr, " -e char Set escape character; ``none'' = disable (default: ~).\n"); - - fprintf(stderr, " -c cipher Select encryption algorithm: " - "``3des'', " - "``blowfish''\n"); - fprintf(stderr, " -p port Connect to this port. Server must be on the same port.\n"); - fprintf(stderr, " -L listen-port:host:port Forward local port to remote address\n"); - fprintf(stderr, " -R listen-port:host:port Forward remote port to local address\n"); - fprintf(stderr, " These cause %s to listen for connections on a port, and\n", av0); - fprintf(stderr, " forward them to the other side by connecting to host:port.\n"); - fprintf(stderr, " -C Enable compression.\n"); - fprintf(stderr, " -g Allow remote hosts to connect to forwarded ports.\n"); - fprintf(stderr, " -o 'option' Process the option as if it was read from a configuration file.\n"); - exit(1); + fprintf(stderr, " -k Disable Kerberos ticket and AFS token forwarding.\n"); +#endif /* AFS */ + fprintf(stderr, " -x Disable X11 connection forwarding.\n"); + fprintf(stderr, " -i file Identity for RSA authentication (default: ~/.ssh/identity).\n"); + fprintf(stderr, " -t Tty; allocate a tty even if command is given.\n"); + fprintf(stderr, " -v Verbose; display verbose debugging messages.\n"); + fprintf(stderr, " -V Display version number only.\n"); + fprintf(stderr, " -P Don't allocate a privileged port.\n"); + fprintf(stderr, " -q Quiet; don't display any warning messages.\n"); + fprintf(stderr, " -f Fork into background after authentication.\n"); + fprintf(stderr, " -e char Set escape character; ``none'' = disable (default: ~).\n"); + + fprintf(stderr, " -c cipher Select encryption algorithm: " + "``3des'', " + "``blowfish''\n"); + fprintf(stderr, " -p port Connect to this port. Server must be on the same port.\n"); + fprintf(stderr, " -L listen-port:host:port Forward local port to remote address\n"); + fprintf(stderr, " -R listen-port:host:port Forward remote port to local address\n"); + fprintf(stderr, " These cause %s to listen for connections on a port, and\n", av0); + fprintf(stderr, " forward them to the other side by connecting to host:port.\n"); + fprintf(stderr, " -C Enable compression.\n"); + fprintf(stderr, " -g Allow remote hosts to connect to forwarded ports.\n"); + fprintf(stderr, " -o 'option' Process the option as if it was read from a configuration file.\n"); + exit(1); } -/* Connects to the given host using rsh (or prints an error message and exits - if rsh is not available). This function never returns. */ - +/* + * Connects to the given host using rsh (or prints an error message and exits + * if rsh is not available). This function never returns. + */ void -rsh_connect(char *host, char *user, Buffer *command) +rsh_connect(char *host, char *user, Buffer * command) { - char *args[10]; - int i; - - log("Using rsh. WARNING: Connection will not be encrypted."); - /* Build argument list for rsh. */ - i = 0; - args[i++] = _PATH_RSH; - args[i++] = host; /* may have to come after user on some systems */ - if (user) - { - args[i++] = "-l"; - args[i++] = user; - } - if (buffer_len(command) > 0) - { - buffer_append(command, "\0", 1); - args[i++] = buffer_ptr(command); - } - args[i++] = NULL; - if (debug_flag) - { - for (i = 0; args[i]; i++) - { - if (i != 0) - fprintf(stderr, " "); - fprintf(stderr, "%s", args[i]); + char *args[10]; + int i; + + log("Using rsh. WARNING: Connection will not be encrypted."); + /* Build argument list for rsh. */ + i = 0; + args[i++] = _PATH_RSH; + /* host may have to come after user on some systems */ + args[i++] = host; + if (user) { + args[i++] = "-l"; + args[i++] = user; + } + if (buffer_len(command) > 0) { + buffer_append(command, "\0", 1); + args[i++] = buffer_ptr(command); + } + args[i++] = NULL; + if (debug_flag) { + for (i = 0; args[i]; i++) { + if (i != 0) + fprintf(stderr, " "); + fprintf(stderr, "%s", args[i]); + } + fprintf(stderr, "\n"); } - fprintf(stderr, "\n"); - } - execv(_PATH_RSH, args); - perror(_PATH_RSH); - exit(1); + execv(_PATH_RSH, args); + perror(_PATH_RSH); + exit(1); } -/* Main program for the ssh client. */ - +/* + * Main program for the ssh client. + */ int main(int ac, char **av) { - int i, opt, optind, type, exit_status, ok, fwd_port, fwd_host_port, authfd; - char *optarg, *cp, buf[256]; - Buffer command; - struct winsize ws; - struct stat st; - struct passwd *pw, pwcopy; - int interactive = 0, dummy; - uid_t original_effective_uid; - int plen; - - /* Save the original real uid. It will be needed later (uid-swapping may - clobber the real uid). */ - original_real_uid = getuid(); - original_effective_uid = geteuid(); - - /* If we are installed setuid root be careful to not drop core. */ - if (original_real_uid != original_effective_uid) - { - struct rlimit rlim; - rlim.rlim_cur = rlim.rlim_max = 0; - if (setrlimit(RLIMIT_CORE, &rlim) < 0) - fatal("setrlimit failed: %.100s", strerror(errno)); - } - - /* Use uid-swapping to give up root privileges for the duration of option - processing. We will re-instantiate the rights when we are ready to - create the privileged port, and will permanently drop them when the - port has been created (actually, when the connection has been made, as - we may need to create the port several times). */ - temporarily_use_uid(original_real_uid); - - /* Set our umask to something reasonable, as some files are created with - the default umask. This will make them world-readable but writable - only by the owner, which is ok for all files for which we don't set - the modes explicitly. */ - umask(022); - - /* Save our own name. */ - av0 = av[0]; - - /* Initialize option structure to indicate that no values have been set. */ - initialize_options(&options); - - /* Parse command-line arguments. */ - host = NULL; - - /* If program name is not one of the standard names, use it as host name. */ - if (strchr(av0, '/')) - cp = strrchr(av0, '/') + 1; - else - cp = av0; - if (strcmp(cp, "rsh") != 0 && strcmp(cp, "ssh") != 0 && - strcmp(cp, "rlogin") != 0 && strcmp(cp, "slogin") != 0) - host = cp; - - for (optind = 1; optind < ac; optind++) - { - if (av[optind][0] != '-') - { - if (host) - break; - if ((cp = strchr(av[optind], '@'))) { - options.user = av[optind]; - *cp = '\0'; - host = ++cp; - } - else - host = av[optind]; - continue; + int i, opt, optind, type, exit_status, ok, fwd_port, fwd_host_port, + authfd; + char *optarg, *cp, buf[256]; + Buffer command; + struct winsize ws; + struct stat st; + struct passwd *pw, pwcopy; + int interactive = 0, dummy; + uid_t original_effective_uid; + int plen; + + /* Save the original real uid. It will be needed later + (uid-swapping may clobber the real uid). */ + original_real_uid = getuid(); + original_effective_uid = geteuid(); + + /* If we are installed setuid root be careful to not drop core. */ + if (original_real_uid != original_effective_uid) { + struct rlimit rlim; + rlim.rlim_cur = rlim.rlim_max = 0; + if (setrlimit(RLIMIT_CORE, &rlim) < 0) + fatal("setrlimit failed: %.100s", strerror(errno)); } - opt = av[optind][1]; - if (!opt) - usage(); - if (strchr("eilcpLRo", opt)) /* options with arguments */ - { - optarg = av[optind] + 2; - if (strcmp(optarg, "") == 0) - { - if (optind >= ac - 1) + /* Use uid-swapping to give up root privileges for the duration of + option processing. We will re-instantiate the rights when we + are ready to create the privileged port, and will permanently + drop them when the port has been created (actually, when the + connection has been made, as we may need to create the port + several times). */ + temporarily_use_uid(original_real_uid); + + /* Set our umask to something reasonable, as some files are + created with the default umask. This will make them + world-readable but writable only by the owner, which is ok for + all files for which we don't set the modes explicitly. */ + umask(022); + + /* Save our own name. */ + av0 = av[0]; + + /* Initialize option structure to indicate that no values have been set. */ + initialize_options(&options); + + /* Parse command-line arguments. */ + host = NULL; + + /* If program name is not one of the standard names, use it as host name. */ + if (strchr(av0, '/')) + cp = strrchr(av0, '/') + 1; + else + cp = av0; + if (strcmp(cp, "rsh") != 0 && strcmp(cp, "ssh") != 0 && + strcmp(cp, "rlogin") != 0 && strcmp(cp, "slogin") != 0) + host = cp; + + for (optind = 1; optind < ac; optind++) { + if (av[optind][0] != '-') { + if (host) + break; + if ((cp = strchr(av[optind], '@'))) { + options.user = av[optind]; + *cp = '\0'; + host = ++cp; + } else + host = av[optind]; + continue; + } + opt = av[optind][1]; + if (!opt) + usage(); + if (strchr("eilcpLRo", opt)) { /* options with arguments */ + optarg = av[optind] + 2; + if (strcmp(optarg, "") == 0) { + if (optind >= ac - 1) + usage(); + optarg = av[++optind]; + } + } else { + if (av[optind][2]) + usage(); + optarg = NULL; + } + switch (opt) { + case 'n': + stdin_null_flag = 1; + break; + + case 'f': + fork_after_authentication_flag = 1; + stdin_null_flag = 1; + break; + + case 'x': + options.forward_x11 = 0; + break; + + case 'X': + options.forward_x11 = 1; + break; + + case 'g': + options.gateway_ports = 1; + break; + + case 'P': + options.use_privileged_port = 0; + break; + + case 'a': + options.forward_agent = 0; + break; +#ifdef AFS + case 'k': + options.kerberos_tgt_passing = 0; + options.afs_token_passing = 0; + break; +#endif + case 'i': + if (stat(optarg, &st) < 0) { + fprintf(stderr, "Warning: Identity file %s does not exist.\n", + optarg); + break; + } + if (options.num_identity_files >= SSH_MAX_IDENTITY_FILES) + fatal("Too many identity files specified (max %d)", + SSH_MAX_IDENTITY_FILES); + options.identity_files[options.num_identity_files++] = + xstrdup(optarg); + break; + + case 't': + tty_flag = 1; + break; + + case 'v': + case 'V': + fprintf(stderr, "SSH Version %s, protocol version %d.%d.\n", + SSH_VERSION, PROTOCOL_MAJOR, PROTOCOL_MINOR); + fprintf(stderr, "Compiled with SSL.\n"); + if (opt == 'V') + exit(0); + debug_flag = 1; + options.log_level = SYSLOG_LEVEL_DEBUG; + break; + + case 'q': + options.log_level = SYSLOG_LEVEL_QUIET; + break; + + case 'e': + if (optarg[0] == '^' && optarg[2] == 0 && + (unsigned char) optarg[1] >= 64 && (unsigned char) optarg[1] < 128) + options.escape_char = (unsigned char) optarg[1] & 31; + else if (strlen(optarg) == 1) + options.escape_char = (unsigned char) optarg[0]; + else if (strcmp(optarg, "none") == 0) + options.escape_char = -2; + else { + fprintf(stderr, "Bad escape character '%s'.\n", optarg); + exit(1); + } + break; + + case 'c': + options.cipher = cipher_number(optarg); + if (options.cipher == -1) { + fprintf(stderr, "Unknown cipher type '%s'\n", optarg); + exit(1); + } + break; + + case 'p': + options.port = atoi(optarg); + if (options.port < 1 || options.port > 65535) { + fprintf(stderr, "Bad port %s.\n", optarg); + exit(1); + } + break; + + case 'l': + options.user = optarg; + break; + + case 'R': + if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf, + &fwd_host_port) != 3) { + fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg); + usage(); + /* NOTREACHED */ + } + add_remote_forward(&options, fwd_port, buf, fwd_host_port); + break; + + case 'L': + if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf, + &fwd_host_port) != 3) { + fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg); + usage(); + /* NOTREACHED */ + } + add_local_forward(&options, fwd_port, buf, fwd_host_port); + break; + + case 'C': + options.compression = 1; + break; + + case 'o': + dummy = 1; + if (process_config_line(&options, host ? host : "", optarg, + "command-line", 0, &dummy) != 0) + exit(1); + break; + + default: + usage(); + } + } + + /* Check that we got a host name. */ + if (!host) usage(); - optarg = av[++optind]; - } + + /* check if RSA support exists */ + if (rsa_alive() == 0) { + fprintf(stderr, + "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", + __progname); + exit(1); } - else - { - if (av[optind][2]) - usage(); - optarg = NULL; + /* Initialize the command to execute on remote host. */ + buffer_init(&command); + + /* Save the command to execute on the remote host in a buffer. + There is no limit on the length of the command, except by the + maximum packet size. Also sets the tty flag if there is no + command. */ + if (optind == ac) { + /* No command specified - execute shell on a tty. */ + tty_flag = 1; + } else { + /* A command has been specified. Store it into the + buffer. */ + for (i = optind; i < ac; i++) { + if (i > optind) + buffer_append(&command, " ", 1); + buffer_append(&command, av[i], strlen(av[i])); + } } - switch (opt) - { - case 'n': - stdin_null_flag = 1; - break; - - case 'f': - fork_after_authentication_flag = 1; - stdin_null_flag = 1; - break; - - case 'x': - options.forward_x11 = 0; - break; - - case 'X': - options.forward_x11 = 1; - break; - - case 'g': - options.gateway_ports = 1; - break; - - case 'P': - options.use_privileged_port = 0; - break; - - case 'a': - options.forward_agent = 0; - break; -#ifdef AFS - case 'k': - options.kerberos_tgt_passing = 0; - options.afs_token_passing = 0; - break; -#endif - case 'i': - if (stat(optarg, &st) < 0) - { - fprintf(stderr, "Warning: Identity file %s does not exist.\n", - optarg); - break; - } - if (options.num_identity_files >= SSH_MAX_IDENTITY_FILES) - fatal("Too many identity files specified (max %d)", - SSH_MAX_IDENTITY_FILES); - options.identity_files[options.num_identity_files++] = - xstrdup(optarg); - break; - - case 't': - tty_flag = 1; - break; - - case 'v': - case 'V': - fprintf(stderr, "SSH Version %s, protocol version %d.%d.\n", - SSH_VERSION, PROTOCOL_MAJOR, PROTOCOL_MINOR); - fprintf(stderr, "Compiled with SSL.\n"); - if (opt == 'V') - exit(0); - debug_flag = 1; - options.log_level = SYSLOG_LEVEL_DEBUG; - break; - - case 'q': - options.log_level = SYSLOG_LEVEL_QUIET; - break; - - case 'e': - if (optarg[0] == '^' && optarg[2] == 0 && - (unsigned char)optarg[1] >= 64 && (unsigned char)optarg[1] < 128) - options.escape_char = (unsigned char)optarg[1] & 31; - else - if (strlen(optarg) == 1) - options.escape_char = (unsigned char)optarg[0]; - else - if (strcmp(optarg, "none") == 0) - options.escape_char = -2; - else - { - fprintf(stderr, "Bad escape character '%s'.\n", optarg); - exit(1); + + /* Cannot fork to background if no command. */ + if (fork_after_authentication_flag && buffer_len(&command) == 0) + fatal("Cannot fork into background without a command to execute."); + + /* Allocate a tty by default if no command specified. */ + if (buffer_len(&command) == 0) + tty_flag = 1; + + /* Do not allocate a tty if stdin is not a tty. */ + if (!isatty(fileno(stdin))) { + if (tty_flag) + fprintf(stderr, "Pseudo-terminal will not be allocated because stdin is not a terminal.\n"); + tty_flag = 0; + } + /* Get user data. */ + pw = getpwuid(original_real_uid); + if (!pw) { + fprintf(stderr, "You don't exist, go away!\n"); + exit(1); + } + /* Take a copy of the returned structure. */ + memset(&pwcopy, 0, sizeof(pwcopy)); + pwcopy.pw_name = xstrdup(pw->pw_name); + pwcopy.pw_passwd = xstrdup(pw->pw_passwd); + pwcopy.pw_uid = pw->pw_uid; + pwcopy.pw_gid = pw->pw_gid; + pwcopy.pw_dir = xstrdup(pw->pw_dir); + pwcopy.pw_shell = xstrdup(pw->pw_shell); + pw = &pwcopy; + + /* Initialize "log" output. Since we are the client all output + actually goes to the terminal. */ + log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0); + + /* Read per-user configuration file. */ + snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_CONFFILE); + read_config_file(buf, host, &options); + + /* Read systemwide configuration file. */ + read_config_file(HOST_CONFIG_FILE, host, &options); + + /* Fill configuration defaults. */ + fill_default_options(&options); + + /* reinit */ + log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0); + + if (options.user == NULL) + options.user = xstrdup(pw->pw_name); + + if (options.hostname != NULL) + host = options.hostname; + + /* Find canonic host name. */ + if (strchr(host, '.') == 0) { + struct hostent *hp = gethostbyname(host); + if (hp != 0) { + if (strchr(hp->h_name, '.') != 0) + host = xstrdup(hp->h_name); + else if (hp->h_aliases != 0 + && hp->h_aliases[0] != 0 + && strchr(hp->h_aliases[0], '.') != 0) + host = xstrdup(hp->h_aliases[0]); } - break; - - case 'c': - options.cipher = cipher_number(optarg); - if (options.cipher == -1) - { - fprintf(stderr, "Unknown cipher type '%s'\n", optarg); - exit(1); - } - break; - - case 'p': - options.port = atoi(optarg); - if (options.port < 1 || options.port > 65535) - { - fprintf(stderr, "Bad port %s.\n", optarg); - exit(1); - } - break; - - case 'l': - options.user = optarg; - break; - - case 'R': - if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf, - &fwd_host_port) != 3) - { - fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg); - usage(); - /*NOTREACHED*/ - } - add_remote_forward(&options, fwd_port, buf, fwd_host_port); - break; - - case 'L': - if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf, - &fwd_host_port) != 3) - { - fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg); - usage(); - /*NOTREACHED*/ - } - add_local_forward(&options, fwd_port, buf, fwd_host_port); - break; - - case 'C': - options.compression = 1; - break; - - case 'o': - dummy = 1; - if (process_config_line(&options, host ? host : "", optarg, - "command-line", 0, &dummy) != 0) - exit(1); - break; - - default: - usage(); } - } - - /* Check that we got a host name. */ - if (!host) - usage(); - - /* check if RSA support exists */ - if (rsa_alive() == 0) { - - fprintf(stderr, - "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", - __progname); - exit(1); - } - - /* Initialize the command to execute on remote host. */ - buffer_init(&command); - - /* Save the command to execute on the remote host in a buffer. There is - no limit on the length of the command, except by the maximum packet - size. Also sets the tty flag if there is no command. */ - if (optind == ac) - { - /* No command specified - execute shell on a tty. */ - tty_flag = 1; - } - else - { - /* A command has been specified. Store it into the buffer. */ - for (i = optind; i < ac; i++) - { - if (i > optind) - buffer_append(&command, " ", 1); - buffer_append(&command, av[i], strlen(av[i])); + /* Disable rhosts authentication if not running as root. */ + if (original_effective_uid != 0 || !options.use_privileged_port) { + options.rhosts_authentication = 0; + options.rhosts_rsa_authentication = 0; + } + /* If using rsh has been selected, exec it now (without trying + anything else). Note that we must release privileges first. */ + if (options.use_rsh) { + /* Restore our superuser privileges. This must be done + before permanently setting the uid. */ + restore_uid(); + + /* Switch to the original uid permanently. */ + permanently_set_uid(original_real_uid); + + /* Execute rsh. */ + rsh_connect(host, options.user, &command); + fatal("rsh_connect returned"); } - } - - /* Cannot fork to background if no command. */ - if (fork_after_authentication_flag && buffer_len(&command) == 0) - fatal("Cannot fork into background without a command to execute."); - - /* Allocate a tty by default if no command specified. */ - if (buffer_len(&command) == 0) - tty_flag = 1; - - /* Do not allocate a tty if stdin is not a tty. */ - if (!isatty(fileno(stdin))) - { - if (tty_flag) - fprintf(stderr, "Pseudo-terminal will not be allocated because stdin is not a terminal.\n"); - tty_flag = 0; - } - - /* Get user data. */ - pw = getpwuid(original_real_uid); - if (!pw) - { - fprintf(stderr, "You don't exist, go away!\n"); - exit(1); - } - - /* Take a copy of the returned structure. */ - memset(&pwcopy, 0, sizeof(pwcopy)); - pwcopy.pw_name = xstrdup(pw->pw_name); - pwcopy.pw_passwd = xstrdup(pw->pw_passwd); - pwcopy.pw_uid = pw->pw_uid; - pwcopy.pw_gid = pw->pw_gid; - pwcopy.pw_dir = xstrdup(pw->pw_dir); - pwcopy.pw_shell = xstrdup(pw->pw_shell); - pw = &pwcopy; - - /* Initialize "log" output. Since we are the client all output actually - goes to the terminal. */ - log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0); - - /* Read per-user configuration file. */ - snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_CONFFILE); - read_config_file(buf, host, &options); - - /* Read systemwide configuration file. */ - read_config_file(HOST_CONFIG_FILE, host, &options); - - /* Fill configuration defaults. */ - fill_default_options(&options); - - /* reinit */ - log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0); - - if (options.user == NULL) - options.user = xstrdup(pw->pw_name); - - if (options.hostname != NULL) - host = options.hostname; - - /* Find canonic host name. */ - if (strchr(host, '.') == 0) - { - struct hostent *hp = gethostbyname(host); - if (hp != 0) - { - if (strchr(hp->h_name, '.') != 0) - host = xstrdup(hp->h_name); - else if (hp->h_aliases != 0 - && hp->h_aliases[0] != 0 - && strchr(hp->h_aliases[0], '.') != 0) - host = xstrdup(hp->h_aliases[0]); + /* Restore our superuser privileges. */ + restore_uid(); + + /* Open a connection to the remote host. This needs root + privileges if rhosts_{rsa_}authentication is enabled. */ + + ok = ssh_connect(host, &hostaddr, options.port, + options.connection_attempts, + !options.rhosts_authentication && + !options.rhosts_rsa_authentication, + original_real_uid, + options.proxy_command); + + /* If we successfully made the connection, load the host private + key in case we will need it later for combined rsa-rhosts + authentication. This must be done before releasing extra + privileges, because the file is only readable by root. */ + if (ok) { + host_private_key = RSA_new(); + if (load_private_key(HOST_KEY_FILE, "", host_private_key, NULL)) + host_private_key_loaded = 1; + } + /* Get rid of any extra privileges that we may have. We will no + longer need them. Also, extra privileges could make it very + hard to read identity files and other non-world-readable files + from the user's home directory if it happens to be on a NFS + volume where root is mapped to nobody. */ + + /* Note that some legacy systems need to postpone the following + call to permanently_set_uid() until the private hostkey is + destroyed with RSA_free(). Otherwise the calling user could + ptrace() the process, read the private hostkey and impersonate + the host. OpenBSD does not allow ptracing of setuid processes. */ + + permanently_set_uid(original_real_uid); + + /* Now that we are back to our own permissions, create ~/.ssh + directory if it doesn\'t already exist. */ + snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_DIR); + if (stat(buf, &st) < 0) + if (mkdir(buf, 0755) < 0) + error("Could not create directory '%.200s'.", buf); + + /* Check if the connection failed, and try "rsh" if appropriate. */ + if (!ok) { + if (options.port != 0) + log("Secure connection to %.100s on port %d refused%.100s.", + host, options.port, + options.fallback_to_rsh ? "; reverting to insecure method" : ""); + else + log("Secure connection to %.100s refused%.100s.", host, + options.fallback_to_rsh ? "; reverting to insecure method" : ""); + + if (options.fallback_to_rsh) { + rsh_connect(host, options.user, &command); + fatal("rsh_connect returned"); + } + exit(1); } - } - - /* Disable rhosts authentication if not running as root. */ - if (original_effective_uid != 0 || !options.use_privileged_port) - { - options.rhosts_authentication = 0; - options.rhosts_rsa_authentication = 0; - } - - /* If using rsh has been selected, exec it now (without trying anything - else). Note that we must release privileges first. */ - if (options.use_rsh) - { - /* Restore our superuser privileges. This must be done before - permanently setting the uid. */ - restore_uid(); - - /* Switch to the original uid permanently. */ - permanently_set_uid(original_real_uid); - - /* Execute rsh. */ - rsh_connect(host, options.user, &command); - fatal("rsh_connect returned"); - } - - /* Restore our superuser privileges. */ - restore_uid(); - - /* Open a connection to the remote host. This needs root privileges if - rhosts_{rsa_}authentication is enabled. */ - - ok = ssh_connect(host, &hostaddr, options.port, options.connection_attempts, - !options.rhosts_authentication && - !options.rhosts_rsa_authentication, - original_real_uid, options.proxy_command); - - /* If we successfully made the connection, load the host private key in - case we will need it later for combined rsa-rhosts authentication. - This must be done before releasing extra privileges, because the file - is only readable by root. */ - if (ok) - { - host_private_key = RSA_new(); - if (load_private_key(HOST_KEY_FILE, "", host_private_key, NULL)) - host_private_key_loaded = 1; - } - - /* Get rid of any extra privileges that we may have. We will no longer need - them. Also, extra privileges could make it very hard to read identity - files and other non-world-readable files from the user's home directory - if it happens to be on a NFS volume where root is mapped to nobody. */ - - /* Note that some legacy systems need to postpone the following call to - permanently_set_uid() until the private hostkey is destroyed with - RSA_free(). Otherwise the calling user could ptrace() the process, - read the private hostkey and impersonate the host. OpenBSD does not - allow ptracing of setuid processes. */ - - permanently_set_uid(original_real_uid); - - /* Now that we are back to our own permissions, create ~/.ssh directory - if it doesn\'t already exist. */ - snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_DIR); - if (stat(buf, &st) < 0) - if (mkdir(buf, 0755) < 0) - error("Could not create directory '%.200s'.", buf); - - /* Check if the connection failed, and try "rsh" if appropriate. */ - if (!ok) - { - if (options.port != 0) - log("Secure connection to %.100s on port %d refused%.100s.", - host, options.port, - options.fallback_to_rsh ? "; reverting to insecure method" : ""); - else - log("Secure connection to %.100s refused%.100s.", host, - options.fallback_to_rsh ? "; reverting to insecure method" : ""); - - if (options.fallback_to_rsh) - { - rsh_connect(host, options.user, &command); - fatal("rsh_connect returned"); + /* Expand ~ in options.identity_files. */ + for (i = 0; i < options.num_identity_files; i++) + options.identity_files[i] = + tilde_expand_filename(options.identity_files[i], original_real_uid); + + /* Expand ~ in known host file names. */ + options.system_hostfile = tilde_expand_filename(options.system_hostfile, + original_real_uid); + options.user_hostfile = tilde_expand_filename(options.user_hostfile, + original_real_uid); + + /* Log into the remote system. This never returns if the login fails. */ + ssh_login(host_private_key_loaded, host_private_key, + host, &hostaddr, original_real_uid); + + /* We no longer need the host private key. Clear it now. */ + if (host_private_key_loaded) + RSA_free(host_private_key); /* Destroys contents safely */ + + /* Close connection cleanly after attack. */ + cipher_attack_detected = packet_disconnect; + + /* If requested, fork and let ssh continue in the background. */ + if (fork_after_authentication_flag) { + int ret = fork(); + if (ret == -1) + fatal("fork failed: %.100s", strerror(errno)); + if (ret != 0) + exit(0); + setsid(); } - exit(1); - } - - /* Expand ~ in options.identity_files. */ - for (i = 0; i < options.num_identity_files; i++) - options.identity_files[i] = - tilde_expand_filename(options.identity_files[i], original_real_uid); - - /* Expand ~ in known host file names. */ - options.system_hostfile = tilde_expand_filename(options.system_hostfile, - original_real_uid); - options.user_hostfile = tilde_expand_filename(options.user_hostfile, - original_real_uid); - - /* Log into the remote system. This never returns if the login fails. */ - ssh_login(host_private_key_loaded, host_private_key, - host, &hostaddr, original_real_uid); - - /* We no longer need the host private key. Clear it now. */ - if (host_private_key_loaded) - RSA_free(host_private_key); /* Destroys contents safely */ - - /* Close connection cleanly after attack. */ - cipher_attack_detected = packet_disconnect; - - /* Enable compression if requested. */ - if (options.compression) - { - debug("Requesting compression at level %d.", options.compression_level); - - if (options.compression_level < 1 || options.compression_level > 9) - fatal("Compression level must be from 1 (fast) to 9 (slow, best)."); - - /* Send the request. */ - packet_start(SSH_CMSG_REQUEST_COMPRESSION); - packet_put_int(options.compression_level); - packet_send(); - packet_write_wait(); - type = packet_read(&plen); - if (type == SSH_SMSG_SUCCESS) - packet_start_compression(options.compression_level); - else if (type == SSH_SMSG_FAILURE) - log("Warning: Remote host refused compression."); - else - packet_disconnect("Protocol error waiting for compression response."); - } - - /* Allocate a pseudo tty if appropriate. */ - if (tty_flag) - { - debug("Requesting pty."); - - /* Start the packet. */ - packet_start(SSH_CMSG_REQUEST_PTY); - - /* Store TERM in the packet. There is no limit on the length of the - string. */ - cp = getenv("TERM"); - if (!cp) - cp = ""; - packet_put_string(cp, strlen(cp)); - - /* Store window size in the packet. */ - if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) - memset(&ws, 0, sizeof(ws)); - packet_put_int(ws.ws_row); - packet_put_int(ws.ws_col); - packet_put_int(ws.ws_xpixel); - packet_put_int(ws.ws_ypixel); - - /* Store tty modes in the packet. */ - tty_make_modes(fileno(stdin)); - - /* Send the packet, and wait for it to leave. */ - packet_send(); - packet_write_wait(); - - /* Read response from the server. */ - type = packet_read(&plen); - if (type == SSH_SMSG_SUCCESS) - interactive = 1; - else if (type == SSH_SMSG_FAILURE) - log("Warning: Remote host failed or refused to allocate a pseudo tty."); - else - packet_disconnect("Protocol error waiting for pty request response."); - } - - /* Request X11 forwarding if enabled and DISPLAY is set. */ - if (options.forward_x11 && getenv("DISPLAY") != NULL) - { - char line[512], proto[512], data[512]; - FILE *f; - int forwarded = 0, got_data = 0, i; + /* Enable compression if requested. */ + if (options.compression) { + debug("Requesting compression at level %d.", options.compression_level); + + if (options.compression_level < 1 || options.compression_level > 9) + fatal("Compression level must be from 1 (fast) to 9 (slow, best)."); + + /* Send the request. */ + packet_start(SSH_CMSG_REQUEST_COMPRESSION); + packet_put_int(options.compression_level); + packet_send(); + packet_write_wait(); + type = packet_read(&plen); + if (type == SSH_SMSG_SUCCESS) + packet_start_compression(options.compression_level); + else if (type == SSH_SMSG_FAILURE) + log("Warning: Remote host refused compression."); + else + packet_disconnect("Protocol error waiting for compression response."); + } + /* Allocate a pseudo tty if appropriate. */ + if (tty_flag) { + debug("Requesting pty."); + + /* Start the packet. */ + packet_start(SSH_CMSG_REQUEST_PTY); + + /* Store TERM in the packet. There is no limit on the + length of the string. */ + cp = getenv("TERM"); + if (!cp) + cp = ""; + packet_put_string(cp, strlen(cp)); + + /* Store window size in the packet. */ + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) + memset(&ws, 0, sizeof(ws)); + packet_put_int(ws.ws_row); + packet_put_int(ws.ws_col); + packet_put_int(ws.ws_xpixel); + packet_put_int(ws.ws_ypixel); + + /* Store tty modes in the packet. */ + tty_make_modes(fileno(stdin)); + + /* Send the packet, and wait for it to leave. */ + packet_send(); + packet_write_wait(); + + /* Read response from the server. */ + type = packet_read(&plen); + if (type == SSH_SMSG_SUCCESS) + interactive = 1; + else if (type == SSH_SMSG_FAILURE) + log("Warning: Remote host failed or refused to allocate a pseudo tty."); + else + packet_disconnect("Protocol error waiting for pty request response."); + } + /* Request X11 forwarding if enabled and DISPLAY is set. */ + if (options.forward_x11 && getenv("DISPLAY") != NULL) { + char line[512], proto[512], data[512]; + FILE *f; + int forwarded = 0, got_data = 0, i; #ifdef XAUTH_PATH - /* Try to get Xauthority information for the display. */ - snprintf(line, sizeof line, "%.100s list %.200s 2>/dev/null", - XAUTH_PATH, getenv("DISPLAY")); - f = popen(line, "r"); - if (f && fgets(line, sizeof(line), f) && - sscanf(line, "%*s %s %s", proto, data) == 2) - got_data = 1; - if (f) - pclose(f); + /* Try to get Xauthority information for the display. */ + snprintf(line, sizeof line, "%.100s list %.200s 2>/dev/null", + XAUTH_PATH, getenv("DISPLAY")); + f = popen(line, "r"); + if (f && fgets(line, sizeof(line), f) && + sscanf(line, "%*s %s %s", proto, data) == 2) + got_data = 1; + if (f) + pclose(f); #endif /* XAUTH_PATH */ - /* If we didn't get authentication data, just make up some data. The - forwarding code will check the validity of the response anyway, and - substitute this data. The X11 server, however, will ignore this - fake data and use whatever authentication mechanisms it was using - otherwise for the local connection. */ - if (!got_data) - { - u_int32_t rand = 0; - - strlcpy(proto, "MIT-MAGIC-COOKIE-1", sizeof proto); - for (i = 0; i < 16; i++) { - if (i % 4 == 0) - rand = arc4random(); - snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", rand & 0xff); - rand >>= 8; - } + /* If we didn't get authentication data, just make up some + data. The forwarding code will check the validity of + the response anyway, and substitute this data. The X11 + server, however, will ignore this fake data and use + whatever authentication mechanisms it was using + otherwise for the local connection. */ + if (!got_data) { + u_int32_t rand = 0; + + strlcpy(proto, "MIT-MAGIC-COOKIE-1", sizeof proto); + for (i = 0; i < 16; i++) { + if (i % 4 == 0) + rand = arc4random(); + snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", rand & 0xff); + rand >>= 8; + } + } + /* Got local authentication reasonable information. + Request forwarding with authentication spoofing. */ + debug("Requesting X11 forwarding with authentication spoofing."); + x11_request_forwarding_with_spoofing(proto, data); + + /* Read response from the server. */ + type = packet_read(&plen); + if (type == SSH_SMSG_SUCCESS) { + forwarded = 1; + interactive = 1; + } else if (type == SSH_SMSG_FAILURE) + log("Warning: Remote host denied X11 forwarding."); + else + packet_disconnect("Protocol error waiting for X11 forwarding"); + } + /* Tell the packet module whether this is an interactive session. */ + packet_set_interactive(interactive, options.keepalives); + + /* Clear agent forwarding if we don\'t have an agent. */ + authfd = ssh_get_authentication_socket(); + if (authfd < 0) + options.forward_agent = 0; + else + ssh_close_authentication_socket(authfd); + + /* Request authentication agent forwarding if appropriate. */ + if (options.forward_agent) { + debug("Requesting authentication agent forwarding."); + auth_request_forwarding(); + + /* Read response from the server. */ + type = packet_read(&plen); + packet_integrity_check(plen, 0, type); + if (type != SSH_SMSG_SUCCESS) + log("Warning: Remote host denied authentication agent forwarding."); + } + /* Initiate local TCP/IP port forwardings. */ + for (i = 0; i < options.num_local_forwards; i++) { + debug("Connections to local port %d forwarded to remote address %.200s:%d", + options.local_forwards[i].port, + options.local_forwards[i].host, + options.local_forwards[i].host_port); + channel_request_local_forwarding(options.local_forwards[i].port, + options.local_forwards[i].host, + options.local_forwards[i].host_port); } - /* Got local authentication reasonable information. Request forwarding - with authentication spoofing. */ - debug("Requesting X11 forwarding with authentication spoofing."); - x11_request_forwarding_with_spoofing(proto, data); - - /* Read response from the server. */ - type = packet_read(&plen); - if (type == SSH_SMSG_SUCCESS) - { - forwarded = 1; - interactive = 1; + /* Initiate remote TCP/IP port forwardings. */ + for (i = 0; i < options.num_remote_forwards; i++) { + debug("Connections to remote port %d forwarded to local address %.200s:%d", + options.remote_forwards[i].port, + options.remote_forwards[i].host, + options.remote_forwards[i].host_port); + channel_request_remote_forwarding(options.remote_forwards[i].port, + options.remote_forwards[i].host, + options.remote_forwards[i].host_port); } - else if (type == SSH_SMSG_FAILURE) - log("Warning: Remote host denied X11 forwarding."); - else - packet_disconnect("Protocol error waiting for X11 forwarding"); - } - - /* Tell the packet module whether this is an interactive session. */ - packet_set_interactive(interactive, options.keepalives); - - /* Clear agent forwarding if we don\'t have an agent. */ - authfd = ssh_get_authentication_socket(); - if (authfd < 0) - options.forward_agent = 0; - else - ssh_close_authentication_socket(authfd); - - /* Request authentication agent forwarding if appropriate. */ - if (options.forward_agent) - { - debug("Requesting authentication agent forwarding."); - auth_request_forwarding(); - - /* Read response from the server. */ - type = packet_read(&plen); - packet_integrity_check(plen, 0, type); - if (type != SSH_SMSG_SUCCESS) - log("Warning: Remote host denied authentication agent forwarding."); - } - - /* Initiate local TCP/IP port forwardings. */ - for (i = 0; i < options.num_local_forwards; i++) - { - debug("Connections to local port %d forwarded to remote address %.200s:%d", - options.local_forwards[i].port, options.local_forwards[i].host, - options.local_forwards[i].host_port); - channel_request_local_forwarding(options.local_forwards[i].port, - options.local_forwards[i].host, - options.local_forwards[i].host_port); - } - - /* Initiate remote TCP/IP port forwardings. */ - for (i = 0; i < options.num_remote_forwards; i++) - { - debug("Connections to remote port %d forwarded to local address %.200s:%d", - options.remote_forwards[i].port, options.remote_forwards[i].host, - options.remote_forwards[i].host_port); - channel_request_remote_forwarding(options.remote_forwards[i].port, - options.remote_forwards[i].host, - options.remote_forwards[i].host_port); - } - - /* If requested, fork and let ssh continue in the background. */ - if (fork_after_authentication_flag) - { - int ret = fork(); - if (ret == -1) - fatal("fork failed: %.100s", strerror(errno)); - if (ret != 0) - exit(0); - setsid(); - } - - /* If a command was specified on the command line, execute the command now. - Otherwise request the server to start a shell. */ - if (buffer_len(&command) > 0) - { - int len = buffer_len(&command); - if (len > 900) - len = 900; - debug("Sending command: %.*s", len, buffer_ptr(&command)); - packet_start(SSH_CMSG_EXEC_CMD); - packet_put_string(buffer_ptr(&command), buffer_len(&command)); - packet_send(); - packet_write_wait(); - } - else - { - debug("Requesting shell."); - packet_start(SSH_CMSG_EXEC_SHELL); - packet_send(); - packet_write_wait(); - } - - /* Enter the interactive session. */ - exit_status = client_loop(tty_flag, tty_flag ? options.escape_char : -1); - - /* Close the connection to the remote host. */ - packet_close(); - - /* Exit with the status returned by the program on the remote side. */ - exit(exit_status); + + /* If a command was specified on the command line, execute the + command now. Otherwise request the server to start a shell. */ + if (buffer_len(&command) > 0) { + int len = buffer_len(&command); + if (len > 900) + len = 900; + debug("Sending command: %.*s", len, buffer_ptr(&command)); + packet_start(SSH_CMSG_EXEC_CMD); + packet_put_string(buffer_ptr(&command), buffer_len(&command)); + packet_send(); + packet_write_wait(); + } else { + debug("Requesting shell."); + packet_start(SSH_CMSG_EXEC_SHELL); + packet_send(); + packet_write_wait(); + } + + /* Enter the interactive session. */ + exit_status = client_loop(tty_flag, tty_flag ? options.escape_char : -1); + + /* Close the connection to the remote host. */ + packet_close(); + + /* Exit with the status returned by the program on the remote side. */ + exit(exit_status); } diff --git a/ssh.h b/ssh.h index f5f7bd92..02f1226e 100644 --- a/ssh.h +++ b/ssh.h @@ -1,17 +1,17 @@ /* - -ssh.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Fri Mar 17 17:09:37 1995 ylo - -Generic header file for ssh. - -*/ + * + * ssh.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Fri Mar 17 17:09:37 1995 ylo + * + * Generic header file for ssh. + * + */ /* RCSID("$Id$"); */ @@ -25,7 +25,7 @@ Generic header file for ssh. #include "rsa.h" #include "cipher.h" -/* The default cipher used if IDEA is not supported by the remote host. +/* The default cipher used if IDEA is not supported by the remote host. It is recommended that this be one of the mandatory ciphers (DES, 3DES), though that is not required. */ #define SSH_FALLBACK_CIPHER SSH_CIPHER_3DES @@ -59,7 +59,9 @@ Generic header file for ssh. #define ETCDIR "/etc" #endif /* ETCDIR */ +#ifndef PIDDIR #define PIDDIR "/var/run" +#endif /* PIDDIR */ /* System-wide file containing host keys of known hosts. This file should be world-readable. */ @@ -76,15 +78,15 @@ only by root, whereas ssh_config should be world-readable. */ #define HOST_CONFIG_FILE ETCDIR "/ssh_config" #ifndef SSH_PROGRAM -#define SSH_PROGRAM "/usr/bin/ssh" +#define SSH_PROGRAM "/usr/bin/ssh" #endif /* SSH_PROGRAM */ #ifndef LOGIN_PROGRAM -#define LOGIN_PROGRAM "/usr/bin/login" +#define LOGIN_PROGRAM "/usr/bin/login" #endif /* LOGIN_PROGRAM */ #ifndef ASKPASS_PROGRAM -#define ASKPASS_PROGRAM "/usr/lib/ssh/ssh-askpass" +#define ASKPASS_PROGRAM "/usr/lib/ssh/ssh-askpass" #endif /* ASKPASS_PROGRAM */ /* The process id of the daemon listening for connections is saved @@ -100,7 +102,7 @@ only by root, whereas ssh_config should be world-readable. */ not contain anything particularly secret. */ #define SSH_USER_HOSTFILE "~/.ssh/known_hosts" -/* Name of the default file containing client-side authentication key. +/* Name of the default file containing client-side authentication key. This file should only be readable by the user him/herself. */ #define SSH_CLIENT_IDENTITY ".ssh/identity" @@ -116,7 +118,7 @@ only by root, whereas ssh_config should be world-readable. */ readable by anyone but the user him/herself, but does not contain anything particularly secret. If the user\'s home directory resides on an NFS volume where root is mapped to nobody, this may need to be - world-readable. (This file is read by the daemon which is running as + world-readable. (This file is read by the daemon which is running as root.) */ #define SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" @@ -130,7 +132,7 @@ only by root, whereas ssh_config should be world-readable. */ /* Ssh-only version of /etc/hosts.equiv. */ #define SSH_HOSTS_EQUIV ETCDIR "/shosts.equiv" -/* Additionally, the daemon may use ~/.rhosts and /etc/hosts.equiv if +/* Additionally, the daemon may use ~/.rhosts and /etc/hosts.equiv if rhosts authentication is enabled. */ /* Name of the environment variable containing the pathname of the @@ -145,7 +147,7 @@ only by root, whereas ssh_config should be world-readable. */ many bits. This is to make double encryption with rsaref work. */ #define SSH_KEY_BITS_RESERVED 128 -/* Length of the session key in bytes. (Specified as 256 bits in the +/* Length of the session key in bytes. (Specified as 256 bits in the protocol.) */ #define SSH_SESSION_KEY_LENGTH 32 @@ -158,21 +160,23 @@ only by root, whereas ssh_config should be world-readable. */ #define SSH_AUTH_RSA 2 #define SSH_AUTH_PASSWORD 3 #define SSH_AUTH_RHOSTS_RSA 4 - /* 5 is TIS */ +#define SSH_AUTH_TIS 5 #define SSH_AUTH_KERBEROS 6 #define SSH_PASS_KERBEROS_TGT 7 - /* 8 to 15 are reserved */ + /* 8 to 15 are reserved */ #define SSH_PASS_AFS_TOKEN 21 /* Protocol flags. These are bit masks. */ -#define SSH_PROTOFLAG_SCREEN_NUMBER 1 /* X11 forwarding includes screen */ -#define SSH_PROTOFLAG_HOST_IN_FWD_OPEN 2 /* forwarding opens contain host */ +#define SSH_PROTOFLAG_SCREEN_NUMBER 1 /* X11 forwarding includes + * screen */ +#define SSH_PROTOFLAG_HOST_IN_FWD_OPEN 2 /* forwarding opens contain + * host */ /* Definition of message types. New values can be added, but old values should not be removed or without careful consideration of the consequences for compatibility. The maximum value is 254; value 255 is reserved for future extension. */ -/* Message name */ /* msg code */ /* arguments */ + /* Message name *//* msg code *//* arguments */ #define SSH_MSG_NONE 0 /* no message */ #define SSH_MSG_DISCONNECT 1 /* cause (string) */ #define SSH_SMSG_PUBLIC_KEY 2 /* ck,msk,srvk,hostk */ @@ -212,10 +216,9 @@ only by root, whereas ssh_config should be world-readable. */ #define SSH_MSG_DEBUG 36 /* string */ #define SSH_CMSG_REQUEST_COMPRESSION 37 /* level 1-9 (int) */ #define SSH_CMSG_MAX_PACKET_SIZE 38 /* size 4k-1024k (int) */ -#define SSH_CMSG_AUTH_TIS 39 /* this is proto-1.5, but we ignore TIS */ -#define SSH_SMSG_AUTH_TIS_CHALLENGE 40 -#define SSH_CMSG_AUTH_TIS_RESPONSE 41 - +#define SSH_CMSG_AUTH_TIS 39 /* we use this for s/key */ +#define SSH_SMSG_AUTH_TIS_CHALLENGE 40 /* challenge (string) */ +#define SSH_CMSG_AUTH_TIS_RESPONSE 41 /* response (string) */ #define SSH_CMSG_AUTH_KERBEROS 42 /* (KTEXT) */ #define SSH_SMSG_AUTH_KERBEROS_RESPONSE 43 /* (KTEXT) */ #define SSH_CMSG_HAVE_KERBEROS_TGT 44 /* credentials (s) */ @@ -223,74 +226,79 @@ only by root, whereas ssh_config should be world-readable. */ /*------------ definitions for login.c -------------*/ -/* Returns the time when the user last logged in. Returns 0 if the - information is not available. This must be called before record_login. +/* Returns the time when the user last logged in. Returns 0 if the + information is not available. This must be called before record_login. The host from which the user logged in is stored in buf. */ -unsigned long get_last_login_time(uid_t uid, const char *logname, - char *buf, unsigned int bufsize); +unsigned long +get_last_login_time(uid_t uid, const char *logname, + char *buf, unsigned int bufsize); /* Records that the user has logged in. This does many things normally done by login(1). */ -void record_login(int pid, const char *ttyname, const char *user, uid_t uid, - const char *host, struct sockaddr_in *addr); +void +record_login(int pid, const char *ttyname, const char *user, uid_t uid, + const char *host, struct sockaddr_in * addr); /* Records that the user has logged out. This does many thigs normally done by login(1) or init. */ -void record_logout(int pid, const char *ttyname); +void record_logout(int pid, const char *ttyname); /*------------ definitions for sshconnect.c ----------*/ /* Opens a TCP/IP connection to the remote server on the given host. If port is 0, the default port will be used. If anonymous is zero, - a privileged port will be allocated to make the connection. - This requires super-user privileges if anonymous is false. + a privileged port will be allocated to make the connection. + This requires super-user privileges if anonymous is false. Connection_attempts specifies the maximum number of tries, one per second. This returns true on success, and zero on failure. If the connection is successful, this calls packet_set_connection for the connection. */ -int ssh_connect(const char *host, struct sockaddr_in *hostaddr, - int port, int connection_attempts, - int anonymous, uid_t original_real_uid, - const char *proxy_command); +int +ssh_connect(const char *host, struct sockaddr_in * hostaddr, + int port, int connection_attempts, + int anonymous, uid_t original_real_uid, + const char *proxy_command); /* Starts a dialog with the server, and authenticates the current user on the server. This does not need any extra privileges. The basic connection - to the server must already have been established before this is called. - If login fails, this function prints an error and never returns. + to the server must already have been established before this is called. + If login fails, this function prints an error and never returns. This initializes the random state, and leaves it initialized (it will also have references from the packet module). */ -void ssh_login(int host_key_valid, RSA *host_key, const char *host, - struct sockaddr_in *hostaddr, uid_t original_real_uid); +void +ssh_login(int host_key_valid, RSA * host_key, const char *host, + struct sockaddr_in * hostaddr, uid_t original_real_uid); /*------------ Definitions for various authentication methods. -------*/ /* Tries to authenticate the user using the .rhosts file. Returns true if authentication succeeds. If ignore_rhosts is non-zero, this will not consider .rhosts and .shosts (/etc/hosts.equiv will still be used). */ -int auth_rhosts(struct passwd *pw, const char *client_user); +int auth_rhosts(struct passwd * pw, const char *client_user); /* Tries to authenticate the user using the .rhosts file and the host using its host key. Returns true if authentication succeeds. */ -int auth_rhosts_rsa(struct passwd *pw, const char *client_user, - BIGNUM *client_host_key_e, BIGNUM *client_host_key_n); +int +auth_rhosts_rsa(struct passwd * pw, const char *client_user, + BIGNUM * client_host_key_e, BIGNUM * client_host_key_n); /* Tries to authenticate the user using password. Returns true if authentication succeeds. */ -int auth_password(struct passwd *pw, const char *password); +int auth_password(struct passwd * pw, const char *password); /* Performs the RSA authentication dialog with the client. This returns 0 if the client could not be authenticated, and 1 if authentication was successful. This may exit if there is a serious protocol violation. */ -int auth_rsa(struct passwd *pw, BIGNUM *client_n); +int auth_rsa(struct passwd * pw, BIGNUM * client_n); /* Parses an RSA key (number of bits, e, n) from a string. Moves the pointer over the key. Skips any whitespace at the beginning and at end. */ -int auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM *e, BIGNUM *n); +int auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n); /* Returns the name of the machine at the other end of the socket. The returned string should be freed by the caller. */ -char *get_remote_hostname(int socket); +char *get_remote_hostname(int socket); /* Return the canonical name of the host in the other side of the current connection (as returned by packet_get_connection). The host name is @@ -302,296 +310,301 @@ const char *get_canonical_hostname(void); const char *get_remote_ipaddr(void); /* Returns the port number of the peer of the socket. */ -int get_peer_port(int sock); +int get_peer_port(int sock); /* Returns the port number of the remote host. */ -int get_remote_port(void); +int get_remote_port(void); /* Tries to match the host name (which must be in all lowercase) against the - comma-separated sequence of subpatterns (each possibly preceded by ! to + comma-separated sequence of subpatterns (each possibly preceded by ! to indicate negation). Returns true if there is a positive match; zero otherwise. */ -int match_hostname(const char *host, const char *pattern, unsigned int len); +int match_hostname(const char *host, const char *pattern, unsigned int len); /* Checks whether the given host is already in the list of our known hosts. Returns HOST_OK if the host is known and has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED if the host is known but used to have a different host key. The host must be in all lowercase. */ -typedef enum { HOST_OK, HOST_NEW, HOST_CHANGED } HostStatus; -HostStatus check_host_in_hostfile(const char *filename, const char *host, - BIGNUM *e, BIGNUM *n, BIGNUM *ke, BIGNUM *kn); +typedef enum { + HOST_OK, HOST_NEW, HOST_CHANGED +} HostStatus; +HostStatus +check_host_in_hostfile(const char *filename, const char *host, + BIGNUM * e, BIGNUM * n, BIGNUM * ke, BIGNUM * kn); /* Appends an entry to the host file. Returns false if the entry could not be appended. */ -int add_host_to_hostfile(const char *filename, const char *host, - BIGNUM *e, BIGNUM *n); +int +add_host_to_hostfile(const char *filename, const char *host, + BIGNUM * e, BIGNUM * n); /* Performs the RSA authentication challenge-response dialog with the client, and returns true (non-zero) if the client gave the correct answer to our challenge; returns zero if the client gives a wrong answer. */ -int auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n); +int auth_rsa_challenge_dialog(BIGNUM * e, BIGNUM * n); -/* Reads a passphrase from /dev/tty with echo turned off. Returns the - passphrase (allocated with xmalloc). Exits if EOF is encountered. +/* Reads a passphrase from /dev/tty with echo turned off. Returns the + passphrase (allocated with xmalloc). Exits if EOF is encountered. If from_stdin is true, the passphrase will be read from stdin instead. */ -char *read_passphrase(const char *prompt, int from_stdin); +char *read_passphrase(const char *prompt, int from_stdin); /* Saves the authentication (private) key in a file, encrypting it with passphrase. The identification of the file (lowest 64 bits of n) will precede the key to provide identification of the key without needing a passphrase. */ -int save_private_key(const char *filename, const char *passphrase, - RSA *private_key, const char *comment); +int +save_private_key(const char *filename, const char *passphrase, + RSA * private_key, const char *comment); -/* Loads the public part of the key file (public key and comment). +/* Loads the public part of the key file (public key and comment). Returns 0 if an error occurred; zero if the public key was successfully read. The comment of the key is returned in comment_return if it is non-NULL; the caller must free the value with xfree. */ -int load_public_key(const char *filename, RSA *pub, - char **comment_return); +int +load_public_key(const char *filename, RSA * pub, + char **comment_return); /* Loads the private key from the file. Returns 0 if an error is encountered (file does not exist or is not readable, or passphrase is bad). - This initializes the private key. The comment of the key is returned - in comment_return if it is non-NULL; the caller must free the value + This initializes the private key. The comment of the key is returned + in comment_return if it is non-NULL; the caller must free the value with xfree. */ -int load_private_key(const char *filename, const char *passphrase, - RSA *private_key, char **comment_return); +int +load_private_key(const char *filename, const char *passphrase, + RSA * private_key, char **comment_return); /*------------ Definitions for logging. -----------------------*/ /* Supported syslog facilities and levels. */ -typedef enum -{ - SYSLOG_FACILITY_DAEMON, - SYSLOG_FACILITY_USER, - SYSLOG_FACILITY_AUTH, - SYSLOG_FACILITY_LOCAL0, - SYSLOG_FACILITY_LOCAL1, - SYSLOG_FACILITY_LOCAL2, - SYSLOG_FACILITY_LOCAL3, - SYSLOG_FACILITY_LOCAL4, - SYSLOG_FACILITY_LOCAL5, - SYSLOG_FACILITY_LOCAL6, - SYSLOG_FACILITY_LOCAL7 -} SyslogFacility; - -typedef enum -{ - SYSLOG_LEVEL_QUIET, - SYSLOG_LEVEL_FATAL, - SYSLOG_LEVEL_ERROR, - SYSLOG_LEVEL_INFO, - SYSLOG_LEVEL_CHAT, - SYSLOG_LEVEL_DEBUG -} LogLevel; - +typedef enum { + SYSLOG_FACILITY_DAEMON, + SYSLOG_FACILITY_USER, + SYSLOG_FACILITY_AUTH, + SYSLOG_FACILITY_LOCAL0, + SYSLOG_FACILITY_LOCAL1, + SYSLOG_FACILITY_LOCAL2, + SYSLOG_FACILITY_LOCAL3, + SYSLOG_FACILITY_LOCAL4, + SYSLOG_FACILITY_LOCAL5, + SYSLOG_FACILITY_LOCAL6, + SYSLOG_FACILITY_LOCAL7 +} SyslogFacility; + +typedef enum { + SYSLOG_LEVEL_QUIET, + SYSLOG_LEVEL_FATAL, + SYSLOG_LEVEL_ERROR, + SYSLOG_LEVEL_INFO, + SYSLOG_LEVEL_VERBOSE, + SYSLOG_LEVEL_DEBUG +} LogLevel; /* Initializes logging. */ -void log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr); +void log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr); /* Logging implementation, depending on server or client */ -void do_log(LogLevel level, const char *fmt, va_list args); +void do_log(LogLevel level, const char *fmt, va_list args); /* name to facility/level */ SyslogFacility log_facility_number(char *name); LogLevel log_level_number(char *name); /* Output a message to syslog or stderr */ -void fatal(const char *fmt, ...); -void error(const char *fmt, ...); -void log(const char *fmt, ...); -void chat(const char *fmt, ...); -void debug(const char *fmt, ...); +void fatal(const char *fmt,...) __attribute__((format(printf, 1, 2))); +void error(const char *fmt,...) __attribute__((format(printf, 1, 2))); +void log(const char *fmt,...) __attribute__((format(printf, 1, 2))); +void verbose(const char *fmt,...) __attribute__((format(printf, 1, 2))); +void debug(const char *fmt,...) __attribute__((format(printf, 1, 2))); /* same as fatal() but w/o logging */ -void fatal_cleanup(void); +void fatal_cleanup(void); -/* Registers a cleanup function to be called by fatal()/fatal_cleanup() before exiting. +/* Registers a cleanup function to be called by fatal()/fatal_cleanup() before exiting. It is permissible to call fatal_remove_cleanup for the function itself from the function. */ -void fatal_add_cleanup(void (*proc)(void *context), void *context); +void fatal_add_cleanup(void (*proc) (void *context), void *context); /* Removes a cleanup function to be called at fatal(). */ -void fatal_remove_cleanup(void (*proc)(void *context), void *context); +void fatal_remove_cleanup(void (*proc) (void *context), void *context); /*---------------- definitions for channels ------------------*/ /* Sets specific protocol options. */ -void channel_set_options(int hostname_in_open); +void channel_set_options(int hostname_in_open); /* Allocate a new channel object and set its type and socket. Remote_name must have been allocated with xmalloc; this will free it when the channel is freed. */ -int channel_allocate(int type, int sock, char *remote_name); +int channel_allocate(int type, int sock, char *remote_name); /* Free the channel and close its socket. */ -void channel_free(int channel); +void channel_free(int channel); /* Add any bits relevant to channels in select bitmasks. */ -void channel_prepare_select(fd_set *readset, fd_set *writeset); +void channel_prepare_select(fd_set * readset, fd_set * writeset); /* After select, perform any appropriate operations for channels which have events pending. */ -void channel_after_select(fd_set *readset, fd_set *writeset); +void channel_after_select(fd_set * readset, fd_set * writeset); /* If there is data to send to the connection, send some of it now. */ -void channel_output_poll(void); +void channel_output_poll(void); /* This is called when a packet of type CHANNEL_DATA has just been received. The message type has already been consumed, but channel number and data is still there. */ -void channel_input_data(int payload_len); +void channel_input_data(int payload_len); /* Returns true if no channel has too much buffered data. */ -int channel_not_very_much_buffered_data(void); +int channel_not_very_much_buffered_data(void); /* This is called after receiving CHANNEL_CLOSE. */ -void channel_input_close(void); +void channel_input_close(void); /* This is called after receiving CHANNEL_CLOSE_CONFIRMATION. */ -void channel_input_close_confirmation(void); +void channel_input_close_confirmation(void); /* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */ -void channel_input_open_confirmation(void); +void channel_input_open_confirmation(void); /* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */ -void channel_input_open_failure(void); +void channel_input_open_failure(void); /* This closes any sockets that are listening for connections; this removes any unix domain sockets. */ -void channel_stop_listening(void); +void channel_stop_listening(void); /* Closes the sockets of all channels. This is used to close extra file descriptors after a fork. */ -void channel_close_all(void); +void channel_close_all(void); /* Returns the maximum file descriptor number used by the channels. */ -int channel_max_fd(void); +int channel_max_fd(void); /* Returns true if there is still an open channel over the connection. */ -int channel_still_open(void); +int channel_still_open(void); /* Returns a string containing a list of all open channels. The list is suitable for displaying to the user. It uses crlf instead of newlines. The caller should free the string with xfree. */ -char *channel_open_message(void); +char *channel_open_message(void); /* Initiate forwarding of connections to local port "port" through the secure channel to host:port from remote side. This never returns if there was an error. */ -void channel_request_local_forwarding(int port, const char *host, - int remote_port); +void +channel_request_local_forwarding(int port, const char *host, + int remote_port); /* Initiate forwarding of connections to port "port" on remote host through the secure channel to host:port from local side. This never returns if there was an error. This registers that open requests for that port are permitted. */ -void channel_request_remote_forwarding(int port, const char *host, - int remote_port); +void +channel_request_remote_forwarding(int port, const char *host, + int remote_port); /* Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually called by the server, because the user could connect to any port anyway, and the server has no way to know but to trust the client anyway. */ -void channel_permit_all_opens(void); +void channel_permit_all_opens(void); /* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates listening for the port, and sends back a success reply (or disconnect - message if there was an error). This never returns if there was an + message if there was an error). This never returns if there was an error. */ -void channel_input_port_forward_request(int is_root); +void channel_input_port_forward_request(int is_root); /* This is called after receiving PORT_OPEN message. This attempts to connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION or CHANNEL_OPEN_FAILURE. */ -void channel_input_port_open(int payload_len); +void channel_input_port_open(int payload_len); /* Creates a port for X11 connections, and starts listening for it. Returns the display name, or NULL if an error was encountered. */ -char *x11_create_display(int screen); +char *x11_create_display(int screen); -/* Creates an internet domain socket for listening for X11 connections. +/* Creates an internet domain socket for listening for X11 connections. Returns a suitable value for the DISPLAY variable, or NULL if an error occurs. */ -char *x11_create_display_inet(int screen); +char *x11_create_display_inet(int screen); /* This is called when SSH_SMSG_X11_OPEN is received. The packet contains the remote channel number. We should do whatever we want, and respond with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */ -void x11_input_open(int payload_len); +void x11_input_open(int payload_len); -/* Requests forwarding of X11 connections. This should be called on the +/* Requests forwarding of X11 connections. This should be called on the client only. */ -void x11_request_forwarding(void); +void x11_request_forwarding(void); /* Requests forwarding for X11 connections, with authentication spoofing. This should be called in the client only. */ -void x11_request_forwarding_with_spoofing(const char *proto, const char *data); +void x11_request_forwarding_with_spoofing(const char *proto, const char *data); /* Sends a message to the server to request authentication fd forwarding. */ -void auth_request_forwarding(void); +void auth_request_forwarding(void); /* Returns the name of the forwarded authentication socket. Returns NULL if there is no forwarded authentication socket. The returned value points to a static buffer. */ -char *auth_get_socket_name(void); +char *auth_get_socket_name(void); /* This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. This starts forwarding authentication requests. */ -void auth_input_request_forwarding(struct passwd *pw); +void auth_input_request_forwarding(struct passwd * pw); /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ -void auth_input_open_request(void); +void auth_input_open_request(void); /* Returns true if the given string matches the pattern (which may contain ? and * as wildcards), and zero if it does not match. */ -int match_pattern(const char *s, const char *pattern); +int match_pattern(const char *s, const char *pattern); /* Expands tildes in the file name. Returns data allocated by xmalloc. Warning: this calls getpw*. */ -char *tilde_expand_filename(const char *filename, uid_t my_uid); +char *tilde_expand_filename(const char *filename, uid_t my_uid); /* 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(int pid, int fdin, int fdout, int fderr); +void server_loop(int pid, int fdin, int fdout, int fderr); /* Client side main loop for the interactive session. */ -int client_loop(int have_pty, int escape_char); +int client_loop(int have_pty, int escape_char); /* Linked list of custom environment strings (see auth-rsa.c). */ struct envstring { - struct envstring *next; - char *s; + struct envstring *next; + char *s; }; - #ifdef KRB4 #include /* Performs Kerberos v4 mutual authentication with the client. This returns 0 if the client could not be authenticated, and 1 if authentication was successful. This may exit if there is a serious protocol violation. */ -int auth_krb4(const char *server_user, KTEXT auth, char **client); -int krb4_init(uid_t uid); -void krb4_cleanup_proc(void *ignore); +int auth_krb4(const char *server_user, KTEXT auth, char **client); +int krb4_init(uid_t uid); +void krb4_cleanup_proc(void *ignore); #ifdef AFS #include /* Accept passed Kerberos v4 ticket-granting ticket and AFS tokens. */ -int auth_kerberos_tgt(struct passwd *pw, const char *string); -int auth_afs_token(struct passwd *pw, const char *token_string); +int auth_kerberos_tgt(struct passwd * pw, const char *string); +int auth_afs_token(struct passwd * pw, const char *token_string); -int creds_to_radix(CREDENTIALS *creds, unsigned char *buf); -int radix_to_creds(const char *buf, CREDENTIALS *creds); -#endif /* AFS */ +int creds_to_radix(CREDENTIALS * creds, unsigned char *buf); +int radix_to_creds(const char *buf, CREDENTIALS * creds); +#endif /* AFS */ -#endif /* KRB4 */ +#endif /* KRB4 */ #ifdef SKEY #include -char *skey_fake_keyinfo(char *username); -#endif /* SKEY */ +char *skey_fake_keyinfo(char *username); +#endif /* SKEY */ -#endif /* SSH_H */ +#endif /* SSH_H */ diff --git a/sshconnect.c b/sshconnect.c index 53495bca..ca9f0fe3 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,18 +1,11 @@ /* - -sshconnect.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sat Mar 18 22:15:47 1995 ylo - -Code to connect to a remote host, and to perform the client side of the -login (authentication) dialog. - -*/ + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Sat Mar 18 22:15:47 1995 ylo + * Code to connect to a remote host, and to perform the client side of the + * login (authentication) dialog. + */ #include "includes.h" RCSID("$Id$"); @@ -41,1497 +34,1495 @@ RCSID("$Id$"); /* Session id for the current session. */ unsigned char session_id[16]; -/* Connect to the given ssh server using a proxy command. */ - +/* + * Connect to the given ssh server using a proxy command. + */ int ssh_proxy_connect(const char *host, int port, uid_t original_real_uid, const char *proxy_command) { - Buffer command; - const char *cp; - char *command_string; - int pin[2], pout[2]; - int pid; - char portstring[100]; - - /* Convert the port number into a string. */ - snprintf(portstring, sizeof portstring, "%d", port); - - /* Build the final command string in the buffer by making the appropriate - substitutions to the given proxy command. */ - buffer_init(&command); - for (cp = proxy_command; *cp; cp++) - { - if (cp[0] == '%' && cp[1] == '%') - { - buffer_append(&command, "%", 1); - cp++; - continue; - } - if (cp[0] == '%' && cp[1] == 'h') - { - buffer_append(&command, host, strlen(host)); - cp++; - continue; - } - if (cp[0] == '%' && cp[1] == 'p') - { - buffer_append(&command, portstring, strlen(portstring)); - cp++; - continue; + Buffer command; + const char *cp; + char *command_string; + int pin[2], pout[2]; + int pid; + char portstring[100]; + + /* Convert the port number into a string. */ + snprintf(portstring, sizeof portstring, "%d", port); + + /* Build the final command string in the buffer by making the + appropriate substitutions to the given proxy command. */ + buffer_init(&command); + for (cp = proxy_command; *cp; cp++) { + if (cp[0] == '%' && cp[1] == '%') { + buffer_append(&command, "%", 1); + cp++; + continue; + } + if (cp[0] == '%' && cp[1] == 'h') { + buffer_append(&command, host, strlen(host)); + cp++; + continue; + } + if (cp[0] == '%' && cp[1] == 'p') { + buffer_append(&command, portstring, strlen(portstring)); + cp++; + continue; + } + buffer_append(&command, cp, 1); } - buffer_append(&command, cp, 1); - } - buffer_append(&command, "\0", 1); - - /* Get the final command string. */ - command_string = buffer_ptr(&command); - - /* Create pipes for communicating with the proxy. */ - if (pipe(pin) < 0 || pipe(pout) < 0) - fatal("Could not create pipes to communicate with the proxy: %.100s", - strerror(errno)); - - debug("Executing proxy command: %.500s", command_string); - - /* Fork and execute the proxy command. */ - if ((pid = fork()) == 0) - { - char *argv[10]; - - /* Child. Permanently give up superuser privileges. */ - permanently_set_uid(original_real_uid); - - /* Redirect stdin and stdout. */ - close(pin[1]); - if (pin[0] != 0) - { - if (dup2(pin[0], 0) < 0) - perror("dup2 stdin"); - close(pin[0]); + buffer_append(&command, "\0", 1); + + /* Get the final command string. */ + command_string = buffer_ptr(&command); + + /* Create pipes for communicating with the proxy. */ + if (pipe(pin) < 0 || pipe(pout) < 0) + fatal("Could not create pipes to communicate with the proxy: %.100s", + strerror(errno)); + + debug("Executing proxy command: %.500s", command_string); + + /* Fork and execute the proxy command. */ + if ((pid = fork()) == 0) { + char *argv[10]; + + /* Child. Permanently give up superuser privileges. */ + permanently_set_uid(original_real_uid); + + /* Redirect stdin and stdout. */ + close(pin[1]); + if (pin[0] != 0) { + if (dup2(pin[0], 0) < 0) + perror("dup2 stdin"); + close(pin[0]); + } + close(pout[0]); + if (dup2(pout[1], 1) < 0) + perror("dup2 stdout"); + /* Cannot be 1 because pin allocated two descriptors. */ + close(pout[1]); + + /* Stderr is left as it is so that error messages get + printed on the user's terminal. */ + argv[0] = "/bin/sh"; + argv[1] = "-c"; + argv[2] = command_string; + argv[3] = NULL; + + /* Execute the proxy command. Note that we gave up any + extra privileges above. */ + execv("/bin/sh", argv); + perror("/bin/sh"); + exit(1); } - close(pout[0]); - if (dup2(pout[1], 1) < 0) - perror("dup2 stdout"); - close(pout[1]); /* Cannot be 1 because pin allocated two descriptors. */ - - /* Stderr is left as it is so that error messages get printed on - the user's terminal. */ - argv[0] = "/bin/sh"; - argv[1] = "-c"; - argv[2] = command_string; - argv[3] = NULL; - - /* Execute the proxy command. Note that we gave up any extra - privileges above. */ - execv("/bin/sh", argv); - perror("/bin/sh"); - exit(1); - } - /* Parent. */ - if (pid < 0) - fatal("fork failed: %.100s", strerror(errno)); - - /* Close child side of the descriptors. */ - close(pin[0]); - close(pout[1]); - - /* Free the command name. */ - buffer_free(&command); - - /* Set the connection file descriptors. */ - packet_set_connection(pout[0], pin[1]); - - return 1; -} + /* Parent. */ + if (pid < 0) + fatal("fork failed: %.100s", strerror(errno)); + + /* Close child side of the descriptors. */ + close(pin[0]); + close(pout[1]); + + /* Free the command name. */ + buffer_free(&command); + + /* Set the connection file descriptors. */ + packet_set_connection(pout[0], pin[1]); -/* Creates a (possibly privileged) socket for use as the ssh connection. */ + return 1; +} -int ssh_create_socket(uid_t original_real_uid, int privileged) +/* + * Creates a (possibly privileged) socket for use as the ssh connection. + */ +int +ssh_create_socket(uid_t original_real_uid, int privileged) { - int sock; - - /* If we are running as root and want to connect to a privileged port, - bind our own socket to a privileged port. */ - if (privileged) - { - int p = IPPORT_RESERVED - 1; - - sock = rresvport(&p); - if (sock < 0) - fatal("rresvport: %.100s", strerror(errno)); - debug("Allocated local port %d.", p); - } - else - { - /* Just create an ordinary socket on arbitrary port. We use the - user's uid to create the socket. */ - temporarily_use_uid(original_real_uid); - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) - fatal("socket: %.100s", strerror(errno)); - restore_uid(); - } - return sock; + int sock; + + /* If we are running as root and want to connect to a privileged + port, bind our own socket to a privileged port. */ + if (privileged) { + int p = IPPORT_RESERVED - 1; + + sock = rresvport(&p); + if (sock < 0) + fatal("rresvport: %.100s", strerror(errno)); + debug("Allocated local port %d.", p); + } else { + /* Just create an ordinary socket on arbitrary port. We + use the user's uid to create the socket. */ + temporarily_use_uid(original_real_uid); + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + fatal("socket: %.100s", strerror(errno)); + restore_uid(); + } + return sock; } -/* Opens a TCP/IP connection to the remote server on the given host. If - port is 0, the default port will be used. If anonymous is zero, - a privileged port will be allocated to make the connection. - This requires super-user privileges if anonymous is false. - Connection_attempts specifies the maximum number of tries (one per - second). If proxy_command is non-NULL, it specifies the command (with %h - and %p substituted for host and port, respectively) to use to contact - the daemon. */ - -int ssh_connect(const char *host, struct sockaddr_in *hostaddr, - int port, int connection_attempts, - int anonymous, uid_t original_real_uid, - const char *proxy_command) +/* + * Opens a TCP/IP connection to the remote server on the given host. If + * port is 0, the default port will be used. If anonymous is zero, + * a privileged port will be allocated to make the connection. + * This requires super-user privileges if anonymous is false. + * Connection_attempts specifies the maximum number of tries (one per + * second). If proxy_command is non-NULL, it specifies the command (with %h + * and %p substituted for host and port, respectively) to use to contact + * the daemon. + */ +int +ssh_connect(const char *host, struct sockaddr_in * hostaddr, + int port, int connection_attempts, + int anonymous, uid_t original_real_uid, + const char *proxy_command) { - int sock = -1, attempt, i; - int on = 1; - struct servent *sp; - struct hostent *hp; - struct linger linger; - - debug("ssh_connect: getuid %d geteuid %d anon %d", - (int)getuid(), (int)geteuid(), anonymous); - - /* Get default port if port has not been set. */ - if (port == 0) - { - sp = getservbyname(SSH_SERVICE_NAME, "tcp"); - if (sp) - port = ntohs(sp->s_port); - else - port = SSH_DEFAULT_PORT; - } - - /* If a proxy command is given, connect using it. */ - if (proxy_command != NULL) - return ssh_proxy_connect(host, port, original_real_uid, proxy_command); - - /* No proxy command. */ - - /* No host lookup made yet. */ - hp = NULL; - - /* Try to connect several times. On some machines, the first time will - sometimes fail. In general socket code appears to behave quite - magically on many machines. */ - for (attempt = 0; attempt < connection_attempts; attempt++) - { - if (attempt > 0) - debug("Trying again..."); - - /* Try to parse the host name as a numeric inet address. */ - memset(hostaddr, 0, sizeof(hostaddr)); - hostaddr->sin_family = AF_INET; - hostaddr->sin_port = htons(port); - hostaddr->sin_addr.s_addr = inet_addr(host); - if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff) - { - /* Valid numeric IP address */ - debug("Connecting to %.100s port %d.", - inet_ntoa(hostaddr->sin_addr), port); - - /* Create a socket. */ - sock = ssh_create_socket(original_real_uid, - !anonymous && geteuid() == 0 && - port < IPPORT_RESERVED); - - /* Connect to the host. We use the user's uid in the hope that - it will help with the problems of tcp_wrappers showing the - remote uid as root. */ - temporarily_use_uid(original_real_uid); - if (connect(sock, (struct sockaddr *)hostaddr, sizeof(*hostaddr)) - >= 0) - { - /* Successful connect. */ - restore_uid(); - break; - } - debug("connect: %.100s", strerror(errno)); - restore_uid(); - - /* Destroy the failed socket. */ - shutdown(sock, SHUT_RDWR); - close(sock); + int sock = -1, attempt, i; + int on = 1; + struct servent *sp; + struct hostent *hp; + struct linger linger; + + debug("ssh_connect: getuid %d geteuid %d anon %d", + (int) getuid(), (int) geteuid(), anonymous); + + /* Get default port if port has not been set. */ + if (port == 0) { + sp = getservbyname(SSH_SERVICE_NAME, "tcp"); + if (sp) + port = ntohs(sp->s_port); + else + port = SSH_DEFAULT_PORT; } - else - { - /* Not a valid numeric inet address. */ - /* Map host name to an address. */ - if (!hp) - hp = gethostbyname(host); - if (!hp) - fatal("Bad host name: %.100s", host); - if (!hp->h_addr_list[0]) - fatal("Host does not have an IP address: %.100s", host); - - /* Loop through addresses for this host, and try each one in - sequence until the connection succeeds. */ - for (i = 0; hp->h_addr_list[i]; i++) - { - /* Set the address to connect to. */ - hostaddr->sin_family = hp->h_addrtype; - memcpy(&hostaddr->sin_addr, hp->h_addr_list[i], - sizeof(hostaddr->sin_addr)); - - debug("Connecting to %.200s [%.100s] port %d.", - host, inet_ntoa(hostaddr->sin_addr), port); - - /* Create a socket for connecting. */ - sock = ssh_create_socket(original_real_uid, - !anonymous && geteuid() == 0 && - port < IPPORT_RESERVED); - - /* Connect to the host. We use the user's uid in the hope that - it will help with tcp_wrappers showing the remote uid as - root. */ - temporarily_use_uid(original_real_uid); - if (connect(sock, (struct sockaddr *)hostaddr, - sizeof(*hostaddr)) >= 0) - { - /* Successful connection. */ - restore_uid(); - break; + /* If a proxy command is given, connect using it. */ + if (proxy_command != NULL) + return ssh_proxy_connect(host, port, original_real_uid, proxy_command); + + /* No proxy command. */ + + /* No host lookup made yet. */ + hp = NULL; + + /* Try to connect several times. On some machines, the first time + will sometimes fail. In general socket code appears to behave + quite magically on many machines. */ + for (attempt = 0; attempt < connection_attempts; attempt++) { + if (attempt > 0) + debug("Trying again..."); + + /* Try to parse the host name as a numeric inet address. */ + memset(hostaddr, 0, sizeof(hostaddr)); + hostaddr->sin_family = AF_INET; + hostaddr->sin_port = htons(port); + hostaddr->sin_addr.s_addr = inet_addr(host); + if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff) { + /* Valid numeric IP address */ + debug("Connecting to %.100s port %d.", + inet_ntoa(hostaddr->sin_addr), port); + + /* Create a socket. */ + sock = ssh_create_socket(original_real_uid, + !anonymous && geteuid() == 0 && + port < IPPORT_RESERVED); + + /* Connect to the host. We use the user's uid in + the hope that it will help with the problems of + tcp_wrappers showing the remote uid as root. */ + temporarily_use_uid(original_real_uid); + if (connect(sock, (struct sockaddr *) hostaddr, sizeof(*hostaddr)) + >= 0) { + /* Successful connect. */ + restore_uid(); + break; + } + debug("connect: %.100s", strerror(errno)); + restore_uid(); + + /* Destroy the failed socket. */ + shutdown(sock, SHUT_RDWR); + close(sock); + } else { + /* Not a valid numeric inet address. */ + /* Map host name to an address. */ + if (!hp) + hp = gethostbyname(host); + if (!hp) + fatal("Bad host name: %.100s", host); + if (!hp->h_addr_list[0]) + fatal("Host does not have an IP address: %.100s", host); + + /* Loop through addresses for this host, and try + each one in sequence until the connection + succeeds. */ + for (i = 0; hp->h_addr_list[i]; i++) { + /* Set the address to connect to. */ + hostaddr->sin_family = hp->h_addrtype; + memcpy(&hostaddr->sin_addr, hp->h_addr_list[i], + sizeof(hostaddr->sin_addr)); + + debug("Connecting to %.200s [%.100s] port %d.", + host, inet_ntoa(hostaddr->sin_addr), port); + + /* Create a socket for connecting. */ + sock = ssh_create_socket(original_real_uid, + !anonymous && geteuid() == 0 && + port < IPPORT_RESERVED); + + /* Connect to the host. We use the user's uid in the hope that + it will help with tcp_wrappers showing the remote uid as root. */ + temporarily_use_uid(original_real_uid); + if (connect(sock, (struct sockaddr *) hostaddr, + sizeof(*hostaddr)) >= 0) { + /* Successful connection. */ + restore_uid(); + break; + } + debug("connect: %.100s", strerror(errno)); + restore_uid(); + + /* Close the failed socket; there appear to be some problems when + reusing a socket for which connect() has already returned an error. */ + shutdown(sock, SHUT_RDWR); + close(sock); + } + if (hp->h_addr_list[i]) + break; /* Successful connection. */ } - debug("connect: %.100s", strerror(errno)); - restore_uid(); - - /* Close the failed socket; there appear to be some problems - when reusing a socket for which connect() has already - returned an error. */ - shutdown(sock, SHUT_RDWR); - close(sock); - } - if (hp->h_addr_list[i]) - break; /* Successful connection. */ - } - /* Sleep a moment before retrying. */ - sleep(1); - } - /* Return failure if we didn't get a successful connection. */ - if (attempt >= connection_attempts) - return 0; + /* Sleep a moment before retrying. */ + sleep(1); + } + /* Return failure if we didn't get a successful connection. */ + if (attempt >= connection_attempts) + return 0; - debug("Connection established."); + debug("Connection established."); - /* Set socket options. We would like the socket to disappear as soon as - it has been closed for whatever reason. */ - /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ - setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)); - linger.l_onoff = 1; - linger.l_linger = 5; - setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); + /* Set socket options. We would like the socket to disappear as + soon as it has been closed for whatever reason. */ + /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, + sizeof(on)); */ + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *) &on, sizeof(on)); + linger.l_onoff = 1; + linger.l_linger = 5; + setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); - /* Set the connection. */ - packet_set_connection(sock, sock); + /* Set the connection. */ + packet_set_connection(sock, sock); - return 1; + return 1; } -/* Checks if the user has an authentication agent, and if so, tries to - authenticate using the agent. */ - +/* + * Checks if the user has an authentication agent, and if so, tries to + * authenticate using the agent. + */ int try_agent_authentication() { - int status, type; - char *comment; - AuthenticationConnection *auth; - unsigned char response[16]; - unsigned int i; - BIGNUM *e, *n, *challenge; - - /* Get connection to the agent. */ - auth = ssh_get_authentication_connection(); - if (!auth) - return 0; - - e = BN_new(); - n = BN_new(); - challenge = BN_new(); - - /* Loop through identities served by the agent. */ - for (status = ssh_get_first_identity(auth, e, n, &comment); - status; - status = ssh_get_next_identity(auth, e, n, &comment)) - { - int plen, clen; - - /* Try this identity. */ - debug("Trying RSA authentication via agent with '%.100s'", comment); - xfree(comment); - - /* Tell the server that we are willing to authenticate using this key. */ - packet_start(SSH_CMSG_AUTH_RSA); - packet_put_bignum(n); - packet_send(); - packet_write_wait(); - - /* Wait for server's response. */ - type = packet_read(&plen); - - /* The server sends failure if it doesn\'t like our key or does not - support RSA authentication. */ - if (type == SSH_SMSG_FAILURE) - { - debug("Server refused our key."); - continue; - } - - /* Otherwise it should have sent a challenge. */ - if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) - packet_disconnect("Protocol error during RSA authentication: %d", - type); - - packet_get_bignum(challenge, &clen); - - packet_integrity_check(plen, clen, type); - - debug("Received RSA challenge from server."); - - /* Ask the agent to decrypt the challenge. */ - if (!ssh_decrypt_challenge(auth, e, n, challenge, - session_id, 1, response)) - { - /* The agent failed to authenticate this identifier although it - advertised it supports this. Just return a wrong value. */ - log("Authentication agent failed to decrypt challenge."); - memset(response, 0, sizeof(response)); - } - - debug("Sending response to RSA challenge."); - - /* Send the decrypted challenge back to the server. */ - packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); - for (i = 0; i < 16; i++) - packet_put_char(response[i]); - packet_send(); - packet_write_wait(); - - /* Wait for response from the server. */ - type = packet_read(&plen); - - /* The server returns success if it accepted the authentication. */ - if (type == SSH_SMSG_SUCCESS) - { - debug("RSA authentication accepted by server."); - BN_clear_free(e); - BN_clear_free(n); - BN_clear_free(challenge); - return 1; - } + int status, type; + char *comment; + AuthenticationConnection *auth; + unsigned char response[16]; + unsigned int i; + BIGNUM *e, *n, *challenge; + + /* Get connection to the agent. */ + auth = ssh_get_authentication_connection(); + if (!auth) + return 0; + + e = BN_new(); + n = BN_new(); + challenge = BN_new(); + + /* Loop through identities served by the agent. */ + for (status = ssh_get_first_identity(auth, e, n, &comment); + status; + status = ssh_get_next_identity(auth, e, n, &comment)) { + int plen, clen; + + /* Try this identity. */ + debug("Trying RSA authentication via agent with '%.100s'", comment); + xfree(comment); + + /* Tell the server that we are willing to authenticate using this key. */ + packet_start(SSH_CMSG_AUTH_RSA); + packet_put_bignum(n); + packet_send(); + packet_write_wait(); + + /* Wait for server's response. */ + type = packet_read(&plen); + + /* The server sends failure if it doesn\'t like our key or + does not support RSA authentication. */ + if (type == SSH_SMSG_FAILURE) { + debug("Server refused our key."); + continue; + } + /* Otherwise it should have sent a challenge. */ + if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) + packet_disconnect("Protocol error during RSA authentication: %d", + type); - /* Otherwise it should return failure. */ - if (type != SSH_SMSG_FAILURE) - packet_disconnect("Protocol error waiting RSA auth response: %d", - type); - } + packet_get_bignum(challenge, &clen); - BN_clear_free(e); - BN_clear_free(n); - BN_clear_free(challenge); + packet_integrity_check(plen, clen, type); - debug("RSA authentication using agent refused."); - return 0; -} + debug("Received RSA challenge from server."); + + /* Ask the agent to decrypt the challenge. */ + if (!ssh_decrypt_challenge(auth, e, n, challenge, + session_id, 1, response)) { + /* The agent failed to authenticate this identifier although it + advertised it supports this. Just return a wrong value. */ + log("Authentication agent failed to decrypt challenge."); + memset(response, 0, sizeof(response)); + } + debug("Sending response to RSA challenge."); + + /* Send the decrypted challenge back to the server. */ + packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); + for (i = 0; i < 16; i++) + packet_put_char(response[i]); + packet_send(); + packet_write_wait(); + + /* Wait for response from the server. */ + type = packet_read(&plen); + + /* The server returns success if it accepted the authentication. */ + if (type == SSH_SMSG_SUCCESS) { + debug("RSA authentication accepted by server."); + BN_clear_free(e); + BN_clear_free(n); + BN_clear_free(challenge); + return 1; + } + /* Otherwise it should return failure. */ + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error waiting RSA auth response: %d", + type); + } + + BN_clear_free(e); + BN_clear_free(n); + BN_clear_free(challenge); -/* Computes the proper response to a RSA challenge, and sends the response to - the server. */ + debug("RSA authentication using agent refused."); + return 0; +} +/* + * Computes the proper response to a RSA challenge, and sends the response to + * the server. + */ void -respond_to_rsa_challenge(BIGNUM *challenge, RSA *prv) +respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv) { - unsigned char buf[32], response[16]; - MD5_CTX md; - int i, len; - - /* Decrypt the challenge using the private key. */ - rsa_private_decrypt(challenge, challenge, prv); - - /* Compute the response. */ - /* The response is MD5 of decrypted challenge plus session id. */ - len = BN_num_bytes(challenge); - if (len <= 0 || len > sizeof(buf)) - packet_disconnect("respond_to_rsa_challenge: bad challenge length %d", - len); - - memset(buf, 0, sizeof(buf)); - BN_bn2bin(challenge, buf + sizeof(buf) - len); - MD5_Init(&md); - MD5_Update(&md, buf, 32); - MD5_Update(&md, session_id, 16); - MD5_Final(response, &md); - - debug("Sending response to host key RSA challenge."); - - /* Send the response back to the server. */ - packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); - for (i = 0; i < 16; i++) - packet_put_char(response[i]); - packet_send(); - packet_write_wait(); - - memset(buf, 0, sizeof(buf)); - memset(response, 0, sizeof(response)); - memset(&md, 0, sizeof(md)); -} + unsigned char buf[32], response[16]; + MD5_CTX md; + int i, len; + + /* Decrypt the challenge using the private key. */ + rsa_private_decrypt(challenge, challenge, prv); + + /* Compute the response. */ + /* The response is MD5 of decrypted challenge plus session id. */ + len = BN_num_bytes(challenge); + if (len <= 0 || len > sizeof(buf)) + packet_disconnect("respond_to_rsa_challenge: bad challenge length %d", + len); + + memset(buf, 0, sizeof(buf)); + BN_bn2bin(challenge, buf + sizeof(buf) - len); + MD5_Init(&md); + MD5_Update(&md, buf, 32); + MD5_Update(&md, session_id, 16); + MD5_Final(response, &md); + + debug("Sending response to host key RSA challenge."); + + /* Send the response back to the server. */ + packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); + for (i = 0; i < 16; i++) + packet_put_char(response[i]); + packet_send(); + packet_write_wait(); -/* Checks if the user has authentication file, and if so, tries to authenticate - the user using it. */ + memset(buf, 0, sizeof(buf)); + memset(response, 0, sizeof(response)); + memset(&md, 0, sizeof(md)); +} +/* + * Checks if the user has authentication file, and if so, tries to authenticate + * the user using it. + */ int -try_rsa_authentication(struct passwd *pw, const char *authfile) +try_rsa_authentication(struct passwd * pw, const char *authfile) { - extern Options options; - BIGNUM *challenge; - RSA *private_key; - RSA *public_key; - char *passphrase, *comment; - int type, i; - int plen, clen; - - /* Try to load identification for the authentication key. */ - public_key = RSA_new(); - if (!load_public_key(authfile, public_key, &comment)) { - RSA_free(public_key); - return 0; /* Could not load it. Fail. */ - } - - debug("Trying RSA authentication with key '%.100s'", comment); - - /* Tell the server that we are willing to authenticate using this key. */ - packet_start(SSH_CMSG_AUTH_RSA); - packet_put_bignum(public_key->n); - packet_send(); - packet_write_wait(); - - /* We no longer need the public key. */ - RSA_free(public_key); - - /* Wait for server's response. */ - type = packet_read(&plen); - - /* The server responds with failure if it doesn\'t like our key or doesn\'t - support RSA authentication. */ - if (type == SSH_SMSG_FAILURE) - { - debug("Server refused our key."); - xfree(comment); - return 0; /* Server refuses to authenticate with this key. */ - } - - /* Otherwise, the server should respond with a challenge. */ - if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) - packet_disconnect("Protocol error during RSA authentication: %d", type); - - /* Get the challenge from the packet. */ - challenge = BN_new(); - packet_get_bignum(challenge, &clen); - - packet_integrity_check(plen, clen, type); - - debug("Received RSA challenge from server."); - - private_key = RSA_new(); - /* Load the private key. Try first with empty passphrase; if it fails, - ask for a passphrase. */ - if (!load_private_key(authfile, "", private_key, NULL)) - { - char buf[300]; - /* Request passphrase from the user. We read from /dev/tty to make - this work even if stdin has been redirected. If running in - batch mode, we just use the empty passphrase, which will fail and - return. */ - snprintf(buf, sizeof buf, - "Enter passphrase for RSA key '%.100s': ", comment); - if (!options.batch_mode) - passphrase = read_passphrase(buf, 0); - else - { - debug("Will not query passphrase for %.100s in batch mode.", - comment); - passphrase = xstrdup(""); + extern Options options; + BIGNUM *challenge; + RSA *private_key; + RSA *public_key; + char *passphrase, *comment; + int type, i; + int plen, clen; + + /* Try to load identification for the authentication key. */ + public_key = RSA_new(); + if (!load_public_key(authfile, public_key, &comment)) { + RSA_free(public_key); + return 0; /* Could not load it. Fail. */ } - - /* Load the authentication file using the pasphrase. */ - if (!load_private_key(authfile, passphrase, private_key, NULL)) - { - memset(passphrase, 0, strlen(passphrase)); - xfree(passphrase); - error("Bad passphrase."); - - /* Send a dummy response packet to avoid protocol error. */ - packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); - for (i = 0; i < 16; i++) - packet_put_char(0); - packet_send(); - packet_write_wait(); - - /* Expect the server to reject it... */ - packet_read_expect(&plen, SSH_SMSG_FAILURE); - xfree(comment); - return 0; + debug("Trying RSA authentication with key '%.100s'", comment); + + /* Tell the server that we are willing to authenticate using this key. */ + packet_start(SSH_CMSG_AUTH_RSA); + packet_put_bignum(public_key->n); + packet_send(); + packet_write_wait(); + + /* We no longer need the public key. */ + RSA_free(public_key); + + /* Wait for server's response. */ + type = packet_read(&plen); + + /* The server responds with failure if it doesn\'t like our key or + doesn\'t support RSA authentication. */ + if (type == SSH_SMSG_FAILURE) { + debug("Server refused our key."); + xfree(comment); + return 0; /* Server refuses to authenticate with + this key. */ } + /* Otherwise, the server should respond with a challenge. */ + if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) + packet_disconnect("Protocol error during RSA authentication: %d", type); + + /* Get the challenge from the packet. */ + challenge = BN_new(); + packet_get_bignum(challenge, &clen); + + packet_integrity_check(plen, clen, type); + + debug("Received RSA challenge from server."); + + private_key = RSA_new(); + /* Load the private key. Try first with empty passphrase; if it + fails, ask for a passphrase. */ + if (!load_private_key(authfile, "", private_key, NULL)) { + char buf[300]; + snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ", + comment); + if (!options.batch_mode) + passphrase = read_passphrase(buf, 0); + else { + debug("Will not query passphrase for %.100s in batch mode.", + comment); + passphrase = xstrdup(""); + } - /* Destroy the passphrase. */ - memset(passphrase, 0, strlen(passphrase)); - xfree(passphrase); - } - - /* We no longer need the comment. */ - xfree(comment); - - /* Compute and send a response to the challenge. */ - respond_to_rsa_challenge(challenge, private_key); - - /* Destroy the private key. */ - RSA_free(private_key); - - /* We no longer need the challenge. */ - BN_clear_free(challenge); - - /* Wait for response from the server. */ - type = packet_read(&plen); - if (type == SSH_SMSG_SUCCESS) - { - debug("RSA authentication accepted by server."); - return 1; - } - if (type != SSH_SMSG_FAILURE) - packet_disconnect("Protocol error waiting RSA auth response: %d", type); - debug("RSA authentication refused."); - return 0; -} + /* Load the authentication file using the pasphrase. */ + if (!load_private_key(authfile, passphrase, private_key, NULL)) { + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + error("Bad passphrase."); + + /* Send a dummy response packet to avoid protocol error. */ + packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); + for (i = 0; i < 16; i++) + packet_put_char(0); + packet_send(); + packet_write_wait(); + + /* Expect the server to reject it... */ + packet_read_expect(&plen, SSH_SMSG_FAILURE); + xfree(comment); + return 0; + } + /* Destroy the passphrase. */ + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + } + /* We no longer need the comment. */ + xfree(comment); + + /* Compute and send a response to the challenge. */ + respond_to_rsa_challenge(challenge, private_key); + + /* Destroy the private key. */ + RSA_free(private_key); -/* Tries to authenticate the user using combined rhosts or /etc/hosts.equiv - authentication and RSA host authentication. */ + /* We no longer need the challenge. */ + BN_clear_free(challenge); + + /* Wait for response from the server. */ + type = packet_read(&plen); + if (type == SSH_SMSG_SUCCESS) { + debug("RSA authentication accepted by server."); + return 1; + } + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error waiting RSA auth response: %d", type); + debug("RSA authentication refused."); + return 0; +} +/* + * Tries to authenticate the user using combined rhosts or /etc/hosts.equiv + * authentication and RSA host authentication. + */ int -try_rhosts_rsa_authentication(const char *local_user, RSA *host_key) +try_rhosts_rsa_authentication(const char *local_user, RSA * host_key) { - int type; - BIGNUM *challenge; - int plen, clen; - - debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication."); - - /* Tell the server that we are willing to authenticate using this key. */ - packet_start(SSH_CMSG_AUTH_RHOSTS_RSA); - packet_put_string(local_user, strlen(local_user)); - packet_put_int(BN_num_bits(host_key->n)); - packet_put_bignum(host_key->e); - packet_put_bignum(host_key->n); - packet_send(); - packet_write_wait(); - - /* Wait for server's response. */ - type = packet_read(&plen); - - /* The server responds with failure if it doesn't admit our .rhosts - authentication or doesn't know our host key. */ - if (type == SSH_SMSG_FAILURE) - { - debug("Server refused our rhosts authentication or host key."); - return 0; /* Server refuses to authenticate us with this method. */ - } - - /* Otherwise, the server should respond with a challenge. */ - if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) - packet_disconnect("Protocol error during RSA authentication: %d", type); - - /* Get the challenge from the packet. */ - challenge = BN_new(); - packet_get_bignum(challenge, &clen); - - packet_integrity_check(plen, clen, type); - - debug("Received RSA challenge for host key from server."); - - /* Compute a response to the challenge. */ - respond_to_rsa_challenge(challenge, host_key); - - /* We no longer need the challenge. */ - BN_clear_free(challenge); - - /* Wait for response from the server. */ - type = packet_read(&plen); - if (type == SSH_SMSG_SUCCESS) - { - debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server."); - return 1; - } - if (type != SSH_SMSG_FAILURE) - packet_disconnect("Protocol error waiting RSA auth response: %d", type); - debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused."); - return 0; + int type; + BIGNUM *challenge; + int plen, clen; + + debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication."); + + /* Tell the server that we are willing to authenticate using this key. */ + packet_start(SSH_CMSG_AUTH_RHOSTS_RSA); + packet_put_string(local_user, strlen(local_user)); + packet_put_int(BN_num_bits(host_key->n)); + packet_put_bignum(host_key->e); + packet_put_bignum(host_key->n); + packet_send(); + packet_write_wait(); + + /* Wait for server's response. */ + type = packet_read(&plen); + + /* The server responds with failure if it doesn't admit our + .rhosts authentication or doesn't know our host key. */ + if (type == SSH_SMSG_FAILURE) { + debug("Server refused our rhosts authentication or host key."); + return 0; + } + /* Otherwise, the server should respond with a challenge. */ + if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) + packet_disconnect("Protocol error during RSA authentication: %d", type); + + /* Get the challenge from the packet. */ + challenge = BN_new(); + packet_get_bignum(challenge, &clen); + + packet_integrity_check(plen, clen, type); + + debug("Received RSA challenge for host key from server."); + + /* Compute a response to the challenge. */ + respond_to_rsa_challenge(challenge, host_key); + + /* We no longer need the challenge. */ + BN_clear_free(challenge); + + /* Wait for response from the server. */ + type = packet_read(&plen); + if (type == SSH_SMSG_SUCCESS) { + debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server."); + return 1; + } + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error waiting RSA auth response: %d", type); + debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused."); + return 0; } #ifdef KRB4 -int try_kerberos_authentication() +int +try_kerberos_authentication() { - KTEXT_ST auth; /* Kerberos data */ - char *reply; - char inst[INST_SZ]; - char *realm; - CREDENTIALS cred; - int r, type, plen; - 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; - - strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ); - - realm = (char *)krb_realmofhost(get_canonical_hostname()); - if (!realm) { - debug("Kerberos V4: no realm for %s", get_canonical_hostname()); - 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); - - r = sizeof(local); - memset(&local, 0, sizeof(local)); - if (getsockname(packet_get_connection_in(), - (struct sockaddr *) &local, &r) < 0) - debug("getsockname failed: %s", strerror(errno)); - - r = sizeof(foreign); - memset(&foreign, 0, sizeof(foreign)); - if (getpeername(packet_get_connection_in(), - (struct sockaddr *)&foreign, &r) < 0) { - debug("getpeername failed: %s", strerror(errno)); - fatal_cleanup(); - } - - /* Get server reply. */ - type = packet_read(&plen); - 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((unsigned int *)&auth.length); - memcpy(auth.dat, reply, auth.length); - xfree(reply); - - packet_integrity_check(plen, 4 + auth.length, type); - - /* 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, 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. */ - (void)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; + KTEXT_ST auth; /* Kerberos data */ + char *reply; + char inst[INST_SZ]; + char *realm; + CREDENTIALS cred; + int r, type, plen; + 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; + + strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ); + + realm = (char *) krb_realmofhost(get_canonical_hostname()); + if (!realm) { + debug("Kerberos V4: no realm for %s", get_canonical_hostname()); + 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); + + r = sizeof(local); + memset(&local, 0, sizeof(local)); + if (getsockname(packet_get_connection_in(), + (struct sockaddr *) & local, &r) < 0) + debug("getsockname failed: %s", strerror(errno)); + + r = sizeof(foreign); + memset(&foreign, 0, sizeof(foreign)); + if (getpeername(packet_get_connection_in(), + (struct sockaddr *) & foreign, &r) < 0) { + debug("getpeername failed: %s", strerror(errno)); + fatal_cleanup(); + } + /* Get server reply. */ + type = packet_read(&plen); + 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((unsigned int *) &auth.length); + memcpy(auth.dat, reply, auth.length); + xfree(reply); + + packet_integrity_check(plen, 4 + auth.length, type); + + /* 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, 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. */ + (void) 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; } + #endif /* KRB4 */ #ifdef AFS -int send_kerberos_tgt() +int +send_kerberos_tgt() { - CREDENTIALS *creds; - char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; - int r, type, plen; - unsigned char buffer[8192]; - struct stat st; - - /* Don't do anything if we don't have any tickets. */ - if (stat(tkt_string(), &st) < 0) return 0; - - creds = xmalloc(sizeof(*creds)); - - if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) { - debug("Kerberos V4 tf_fullname failed: %s",krb_err_txt[r]); - return 0; - } - if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) { - debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]); - return 0; - } - if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) { - debug("Kerberos V4 ticket expired: %s", TKT_FILE); - return 0; - } - - creds_to_radix(creds, buffer); - xfree(creds); - - packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); - packet_put_string((char *)buffer, strlen(buffer)); - packet_send(); - packet_write_wait(); - - type = packet_read(&plen); - - if (type == SSH_SMSG_FAILURE) - debug("Kerberos TGT for realm %s rejected.", prealm); - else if (type != SSH_SMSG_SUCCESS) - packet_disconnect("Protocol error on Kerberos TGT response: %d", type); - - return 1; + CREDENTIALS *creds; + char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; + int r, type, plen; + unsigned char buffer[8192]; + struct stat st; + + /* Don't do anything if we don't have any tickets. */ + if (stat(tkt_string(), &st) < 0) + return 0; + + creds = xmalloc(sizeof(*creds)); + + if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) { + debug("Kerberos V4 tf_fullname failed: %s", krb_err_txt[r]); + return 0; + } + if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) { + debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]); + return 0; + } + if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) { + debug("Kerberos V4 ticket expired: %s", TKT_FILE); + return 0; + } + creds_to_radix(creds, buffer); + xfree(creds); + + packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); + packet_put_string((char *) buffer, strlen(buffer)); + packet_send(); + packet_write_wait(); + + type = packet_read(&plen); + + if (type == SSH_SMSG_FAILURE) + debug("Kerberos TGT for realm %s rejected.", prealm); + else if (type != SSH_SMSG_SUCCESS) + packet_disconnect("Protocol error on Kerberos TGT response: %d", type); + + return 1; } -void send_afs_tokens(void) +void +send_afs_tokens(void) { - CREDENTIALS creds; - struct ViceIoctl parms; - struct ClearToken ct; - int i, type, len, plen; - char buf[2048], *p, *server_cell; - unsigned char buffer[8192]; - - /* Move over ktc_GetToken, here's something leaner. */ - for (i = 0; i < 100; i++) { /* just in case */ - parms.in = (char *)&i; - parms.in_size = sizeof(i); - parms.out = buf; - parms.out_size = sizeof(buf); - if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) break; - p = buf; - - /* Get secret token. */ - memcpy(&creds.ticket_st.length, p, sizeof(unsigned int)); - if (creds.ticket_st.length > MAX_KTXT_LEN) break; - p += sizeof(unsigned int); - memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); - p += creds.ticket_st.length; - - /* Get clear token. */ - memcpy(&len, p, sizeof(len)); - if (len != sizeof(struct ClearToken)) break; - p += sizeof(len); - memcpy(&ct, p, len); - p += len; - p += sizeof(len); /* primary flag */ - server_cell = p; - - /* Flesh out our credentials. */ - strlcpy(creds.service, "afs", sizeof creds.service); - creds.instance[0] = '\0'; - strlcpy(creds.realm, server_cell, REALM_SZ); - memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ); - creds.issue_date = ct.BeginTimestamp; - creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp); - creds.kvno = ct.AuthHandle; - snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId); - creds.pinst[0] = '\0'; - - /* Encode token, ship it off. */ - if (!creds_to_radix(&creds, buffer)) break; - packet_start(SSH_CMSG_HAVE_AFS_TOKEN); - packet_put_string((char *)buffer, strlen(buffer)); - packet_send(); - packet_write_wait(); - - /* Roger, Roger. Clearance, Clarence. What's your vector, Victor? */ - type = packet_read(&plen); - - if (type == SSH_SMSG_FAILURE) - debug("AFS token for cell %s rejected.", server_cell); - else if (type != SSH_SMSG_SUCCESS) - packet_disconnect("Protocol error on AFS token response: %d", type); - } + CREDENTIALS creds; + struct ViceIoctl parms; + struct ClearToken ct; + int i, type, len, plen; + char buf[2048], *p, *server_cell; + unsigned char buffer[8192]; + + /* Move over ktc_GetToken, here's something leaner. */ + for (i = 0; i < 100; i++) { /* just in case */ + parms.in = (char *) &i; + parms.in_size = sizeof(i); + parms.out = buf; + parms.out_size = sizeof(buf); + if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) + break; + p = buf; + + /* Get secret token. */ + memcpy(&creds.ticket_st.length, p, sizeof(unsigned int)); + if (creds.ticket_st.length > MAX_KTXT_LEN) + break; + p += sizeof(unsigned int); + memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); + p += creds.ticket_st.length; + + /* Get clear token. */ + memcpy(&len, p, sizeof(len)); + if (len != sizeof(struct ClearToken)) + break; + p += sizeof(len); + memcpy(&ct, p, len); + p += len; + p += sizeof(len); /* primary flag */ + server_cell = p; + + /* Flesh out our credentials. */ + strlcpy(creds.service, "afs", sizeof creds.service); + creds.instance[0] = '\0'; + strlcpy(creds.realm, server_cell, REALM_SZ); + memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ); + creds.issue_date = ct.BeginTimestamp; + creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp); + creds.kvno = ct.AuthHandle; + snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId); + creds.pinst[0] = '\0'; + + /* Encode token, ship it off. */ + if (!creds_to_radix(&creds, buffer)) + break; + packet_start(SSH_CMSG_HAVE_AFS_TOKEN); + packet_put_string((char *) buffer, strlen(buffer)); + packet_send(); + packet_write_wait(); + + /* Roger, Roger. Clearance, Clarence. What's your vector, + Victor? */ + type = packet_read(&plen); + + if (type == SSH_SMSG_FAILURE) + debug("AFS token for cell %s rejected.", server_cell); + else if (type != SSH_SMSG_SUCCESS) + packet_disconnect("Protocol error on AFS token response: %d", type); + } } -#endif /* AFS */ -/* Waits for the server identification string, and sends our own identification - string. */ +#endif /* AFS */ -void ssh_exchange_identification() +/* + * Waits for the server identification string, and sends our own + * identification string. + */ +void +ssh_exchange_identification() { - char buf[256], remote_version[256]; /* must be same size! */ - int remote_major, remote_minor, i; - int connection_in = packet_get_connection_in(); - int connection_out = packet_get_connection_out(); - extern Options options; - - /* Read other side\'s version identification. */ - for (i = 0; i < sizeof(buf) - 1; i++) - { - if (read(connection_in, &buf[i], 1) != 1) - fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); - if (buf[i] == '\r') - { - buf[i] = '\n'; - buf[i + 1] = 0; - break; + char buf[256], remote_version[256]; /* must be same size! */ + int remote_major, remote_minor, i; + int connection_in = packet_get_connection_in(); + int connection_out = packet_get_connection_out(); + extern Options options; + + /* Read other side\'s version identification. */ + for (i = 0; i < sizeof(buf) - 1; i++) { + if (read(connection_in, &buf[i], 1) != 1) + fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); + if (buf[i] == '\r') { + buf[i] = '\n'; + buf[i + 1] = 0; + break; + } + if (buf[i] == '\n') { + buf[i + 1] = 0; + break; + } } - if (buf[i] == '\n') - { - buf[i + 1] = 0; - break; + buf[sizeof(buf) - 1] = 0; + + /* Check that the versions match. In future this might accept + several versions and set appropriate flags to handle them. */ + if (sscanf(buf, "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); + + /* Check if the remote protocol version is too old. */ + if (remote_major == 1 && remote_minor < 3) + fatal("Remote machine has too old SSH software version."); + + /* We speak 1.3, too. */ + if (remote_major == 1 && remote_minor == 3) { + enable_compat13(); + if (options.forward_agent && strcmp(remote_version, SSH_VERSION) != 0) { + log("Agent forwarding disabled, remote version '%s' is not compatible.", + remote_version); + options.forward_agent = 0; + } } - } - buf[sizeof(buf) - 1] = 0; - - /* Check that the versions match. In future this might accept several - versions and set appropriate flags to handle them. */ - if (sscanf(buf, "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); - - /* Check if the remote protocol version is too old. */ - if (remote_major == 1 && remote_minor < 3) - fatal("Remote machine has too old SSH software version."); - - /* We speak 1.3, too. */ - if (remote_major == 1 && remote_minor == 3) { - enable_compat13(); - if (options.forward_agent && strcmp(remote_version, SSH_VERSION) != 0) { - log("Agent forwarding disabled, remote version '%s' is not compatible.", - remote_version); - options.forward_agent = 0; - } - } #if 0 - /* Removed for now, to permit compatibility with latter versions. The server - will reject our version and disconnect if it doesn't support it. */ - if (remote_major != PROTOCOL_MAJOR) - fatal("Protocol major versions differ: %d vs. %d", - PROTOCOL_MAJOR, remote_major); + /* Removed for now, to permit compatibility with latter versions. + The server will reject our version and disconnect if it doesn't + support it. */ + if (remote_major != PROTOCOL_MAJOR) + fatal("Protocol major versions differ: %d vs. %d", + PROTOCOL_MAJOR, remote_major); #endif - /* Send our own protocol version identification. */ - snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", - PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION); - if (write(connection_out, buf, strlen(buf)) != strlen(buf)) - fatal("write: %.100s", strerror(errno)); + /* Send our own protocol version identification. */ + snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", + PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION); + if (write(connection_out, buf, strlen(buf)) != strlen(buf)) + fatal("write: %.100s", strerror(errno)); } int ssh_cipher_default = SSH_CIPHER_3DES; -int read_yes_or_no(const char *prompt, int defval) +int +read_yes_or_no(const char *prompt, int defval) { - char buf[1024]; - FILE *f; - int retval = -1; - - if (isatty(0)) - f = stdin; - else - f = fopen("/dev/tty", "rw"); - - if (f == NULL) - return 0; - - fflush(stdout); - - while (1) - { - fprintf(stderr, "%s", prompt); - if (fgets(buf, sizeof(buf), f) == NULL) - { - /* Print a newline (the prompt probably didn\'t have one). */ - fprintf(stderr, "\n"); - strlcpy(buf, "no", sizeof buf); - } - /* Remove newline from response. */ - if (strchr(buf, '\n')) - *strchr(buf, '\n') = 0; - - if (buf[0] == 0) - retval = defval; - if (strcmp(buf, "yes") == 0) - retval = 1; - if (strcmp(buf, "no") == 0) - retval = 0; - - if (retval != -1) - { - if (f != stdin) - fclose(f); - return retval; + char buf[1024]; + FILE *f; + int retval = -1; + + if (isatty(0)) + f = stdin; + else + f = fopen("/dev/tty", "rw"); + + if (f == NULL) + return 0; + + fflush(stdout); + + while (1) { + fprintf(stderr, "%s", prompt); + if (fgets(buf, sizeof(buf), f) == NULL) { + /* Print a newline (the prompt probably didn\'t have one). */ + fprintf(stderr, "\n"); + strlcpy(buf, "no", sizeof buf); + } + /* Remove newline from response. */ + if (strchr(buf, '\n')) + *strchr(buf, '\n') = 0; + + if (buf[0] == 0) + retval = defval; + if (strcmp(buf, "yes") == 0) + retval = 1; + if (strcmp(buf, "no") == 0) + retval = 0; + + if (retval != -1) { + if (f != stdin) + fclose(f); + return retval; + } } - } } -/* Starts a dialog with the server, and authenticates the current user on the - server. This does not need any extra privileges. The basic connection - to the server must already have been established before this is called. - User is the remote user; if it is NULL, the current local user name will - be used. Anonymous indicates that no rhosts authentication will be used. - If login fails, this function prints an error and never returns. - This function does not require super-user privileges. */ - -void ssh_login(int host_key_valid, - RSA *own_host_key, - const char *orighost, - struct sockaddr_in *hostaddr, - uid_t original_real_uid) +/* + * Starts a dialog with the server, and authenticates the current user on the + * server. This does not need any extra privileges. The basic connection + * to the server must already have been established before this is called. + * User is the remote user; if it is NULL, the current local user name will + * be used. Anonymous indicates that no rhosts authentication will be used. + * If login fails, this function prints an error and never returns. + * This function does not require super-user privileges. + */ +void +ssh_login(int host_key_valid, + RSA *own_host_key, + const char *orighost, + struct sockaddr_in *hostaddr, + uid_t original_real_uid) { - extern Options options; - int i, type; - char *password; - struct passwd *pw; - BIGNUM *key; - RSA *host_key, *file_key; - RSA *public_key; - int bits, rbits; - unsigned char session_key[SSH_SESSION_KEY_LENGTH]; - const char *server_user, *local_user; - char *cp, *host, *ip = NULL; - unsigned char check_bytes[8]; - unsigned int supported_ciphers, supported_authentications, protocol_flags; - HostStatus host_status; - HostStatus ip_status; - int host_ip_differ = 0; - int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; - int payload_len, clen, sum_len = 0; - u_int32_t rand = 0; - - if (options.check_host_ip) - ip = xstrdup(inet_ntoa(hostaddr->sin_addr)); - - /* Convert the user-supplied hostname into all lowercase. */ - host = xstrdup(orighost); - for (cp = host; *cp; cp++) - if (isupper(*cp)) - *cp = tolower(*cp); - - /* Exchange protocol version identification strings with the server. */ - ssh_exchange_identification(); - - /* Put the connection into non-blocking mode. */ - packet_set_nonblocking(); - - /* Get local user name. Use it as server user if no user name - was given. */ - pw = getpwuid(original_real_uid); - if (!pw) - fatal("User id %d not found from user database.", original_real_uid); - local_user = xstrdup(pw->pw_name); - server_user = options.user ? options.user : local_user; - - debug("Waiting for server public key."); - - /* Wait for a public key packet from the server. */ - packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY); - - /* Get check bytes from the packet. */ - for (i = 0; i < 8; i++) - check_bytes[i] = packet_get_char(); - - /* Get the public key. */ - public_key = RSA_new(); - bits = packet_get_int(); /* bits */ - public_key->e = BN_new(); - packet_get_bignum(public_key->e, &clen); - sum_len += clen; - public_key->n = BN_new(); - packet_get_bignum(public_key->n, &clen); - sum_len += clen; - - rbits = BN_num_bits(public_key->n); - if (bits != rbits) { - log("Warning: Server lies about size of server public key: " - "actual size is %d bits vs. announced %d.", rbits, bits); - log("Warning: This may be due to an old implementation of ssh."); - } - - /* Get the host key. */ - host_key = RSA_new(); - bits = packet_get_int(); /* bits */ - host_key->e = BN_new(); - packet_get_bignum(host_key->e, &clen); - sum_len += clen; - host_key->n = BN_new(); - packet_get_bignum(host_key->n, &clen); - sum_len += clen; - - rbits = BN_num_bits(host_key->n); - if (bits != rbits) { - log("Warning: Server lies about size of server host key: " - "actual size is %d bits vs. announced %d.", rbits, bits); - log("Warning: This may be due to an old implementation of ssh."); - } - - /* Store the host key from the known host file in here - * so that we can compare it with the key for the IP - * address. */ - file_key = RSA_new(); - file_key->n = BN_new(); - file_key->e = BN_new(); - - /* Get protocol flags. */ - protocol_flags = packet_get_int(); - packet_set_protocol_flags(protocol_flags); - - /* Get supported cipher types. */ - supported_ciphers = packet_get_int(); - - /* Get supported authentication types. */ - supported_authentications = packet_get_int(); - - debug("Received server public key (%d bits) and host key (%d bits).", - BN_num_bits(public_key->n), BN_num_bits(host_key->n)); - - packet_integrity_check(payload_len, - 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, - SSH_SMSG_PUBLIC_KEY); - - /* Compute the session id. */ - compute_session_id(session_id, check_bytes, host_key->n, public_key->n); - - /* Check if the host key is present in the user\'s list of known hosts - or in the systemwide list. */ - host_status = check_host_in_hostfile(options.user_hostfile, host, - host_key->e, host_key->n, - file_key->e, file_key->n); - if (host_status == HOST_NEW) - host_status = check_host_in_hostfile(options.system_hostfile, host, - host_key->e, host_key->n, - file_key->e, file_key->n); - /* Force accepting of the host key for localhost and 127.0.0.1. - The problem is that if the home directory is NFS-mounted to multiple - machines, localhost will refer to a different machine in each of them, - and the user will get bogus HOST_CHANGED warnings. This essentially - disables host authentication for localhost; however, this is probably - not a real problem. */ - if (local) { - debug("Forcing accepting of host key for localhost."); - host_status = HOST_OK; - } - - /* Also perform check for the ip address, skip the check if we are - localhost or the hostname was an ip address to begin with */ - if (options.check_host_ip && !local && strcmp(host, ip)) { - RSA *ip_key = RSA_new(); - ip_key->n = BN_new(); - ip_key->e = BN_new(); - ip_status = check_host_in_hostfile(options.user_hostfile, ip, - host_key->e, host_key->n, - ip_key->e, ip_key->n); - - if (ip_status == HOST_NEW) - ip_status = check_host_in_hostfile(options.system_hostfile, ip, - host_key->e, host_key->n, - ip_key->e, ip_key->n); - if (host_status == HOST_CHANGED && - (ip_status != HOST_CHANGED || - (BN_cmp(ip_key->e, file_key->e) || BN_cmp(ip_key->n, file_key->n)))) - host_ip_differ = 1; - - RSA_free(ip_key); - } else - ip_status = host_status; - - RSA_free(file_key); - - switch (host_status) { - case HOST_OK: - /* The host is known and the key matches. */ - debug("Host '%.200s' is known and matches the host key.", host); - if (options.check_host_ip) { - if (ip_status == HOST_NEW) { - if (!add_host_to_hostfile(options.user_hostfile, ip, - host_key->e, host_key->n)) - log("Failed to add the host key for IP address '%.30s' to the list of known hosts (%.30s).", - ip, options.user_hostfile); - else - log("Warning: Permanently added host key for IP address '%.30s' to the list of known hosts.", - ip); - } else if (ip_status != HOST_OK) - log("Warning: the host key for '%.200s' differs from the key for the IP address '%.30s'", - host, ip); - } - - break; - case HOST_NEW: - { - char hostline[1000], *hostp = hostline; - /* The host is new. */ - if (options.strict_host_key_checking == 1) { - /* User has requested strict host key checking. We will not - add the host key automatically. The only alternative left - is to abort. */ - fatal("No host key is known for %.200s and you have requested strict checking.", host); - } else if (options.strict_host_key_checking == 2) { /* The default */ - char prompt[1024]; - char *fp = fingerprint(host_key->e, host_key->n); - snprintf(prompt, sizeof(prompt), - "The authenticity of host '%.200s' can't be established.\n" - "Key fingerprint is %d %s.\n" - "Are you sure you want to continue connecting (yes/no)? ", - host, BN_num_bits(host_key->n), fp); - if (!read_yes_or_no(prompt, -1)) - fatal("Aborted by user!\n"); - } - - if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) - snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); - else - hostp = host; - - /* If not in strict mode, add the key automatically to the local - known_hosts file. */ - if (!add_host_to_hostfile(options.user_hostfile, hostp, - host_key->e, host_key->n)) - log("Failed to add the host to the list of known hosts (%.500s).", - options.user_hostfile); - else - log("Warning: Permanently added '%.200s' to the list of known hosts.", - hostp); - break; - } - case HOST_CHANGED: - if (options.check_host_ip) { - if (host_ip_differ) { - char *msg; - if (ip_status == HOST_NEW) - msg = "is unknown"; - else if (ip_status == HOST_OK) - msg = "is unchanged"; - else - msg = "has a different value"; - error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); - error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); - error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); - error("The host key for %s has changed,", host); - error("and the key for the according IP address %s", ip); - error("%s. This could either mean that", msg); - error("DNS SPOOFING is happening or the IP address for the host"); - error("and its host key have changed at the same time"); - } - } - - /* The host key has changed. */ - error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); - error("@ WARNING: HOST IDENTIFICATION HAS CHANGED! @"); - error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); - error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); - error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); - error("It is also possible that the host key has just been changed."); - error("Please contact your system administrator."); - error("Add correct host key in %.100s to get rid of this message.", - options.user_hostfile); - - /* If strict host key checking is in use, the user will have to edit - the key manually and we can only abort. */ - if (options.strict_host_key_checking) - fatal("Host key for %.200s has changed and you have requested strict checking.", host); - - /* If strict host key checking has not been requested, allow the - connection but without password authentication or - agent forwarding. */ - if (options.password_authentication) { - error("Password authentication is disabled to avoid trojan horses."); - options.password_authentication = 0; - } - if (options.forward_agent) { - error("Agent forwarding is disabled to avoid trojan horses."); - options.forward_agent = 0; - } - /* XXX Should permit the user to change to use the new id. This could - be done by converting the host key to an identifying sentence, tell - that the host identifies itself by that sentence, and ask the user - if he/she whishes to accept the authentication. */ - break; - } - - if (options.check_host_ip) - xfree(ip); - - /* Generate a session key. */ - arc4random_stir(); - - /* Generate an encryption key for the session. The key is a 256 bit - random number, interpreted as a 32-byte key, with the least significant - 8 bits being the first byte of the key. */ - for (i = 0; i < 32; i++) { - if (i % 4 == 0) - rand = arc4random(); - session_key[i] = rand & 0xff; - rand >>= 8; - } - - /* According to the protocol spec, the first byte of the session key is - the highest byte of the integer. The session key is xored with the - first 16 bytes of the session id. */ - key = BN_new(); - BN_set_word(key, 0); - for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) - { - BN_lshift(key, key, 8); - if (i < 16) - BN_add_word(key, session_key[i] ^ session_id[i]); - else - BN_add_word(key, session_key[i]); - } - - /* Encrypt the integer using the public key and host key of the server - (key with smaller modulus first). */ - if (BN_cmp(public_key->n, host_key->n) < 0) - { - /* Public key has smaller modulus. */ - if (BN_num_bits(host_key->n) < - BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED) { - fatal("respond_to_rsa_challenge: host_key %d < public_key %d + " - "SSH_KEY_BITS_RESERVED %d", - BN_num_bits(host_key->n), - BN_num_bits(public_key->n), - SSH_KEY_BITS_RESERVED); - } - - rsa_public_encrypt(key, key, public_key); - rsa_public_encrypt(key, key, host_key); - } - else - { - /* Host key has smaller modulus (or they are equal). */ - if (BN_num_bits(public_key->n) < - BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED) { - fatal("respond_to_rsa_challenge: public_key %d < host_key %d + " - "SSH_KEY_BITS_RESERVED %d", - BN_num_bits(public_key->n), - BN_num_bits(host_key->n), - SSH_KEY_BITS_RESERVED); - } - - rsa_public_encrypt(key, key, host_key); - rsa_public_encrypt(key, key, public_key); - } - - if (options.cipher == SSH_CIPHER_NOT_SET) { - if (cipher_mask() & supported_ciphers & (1 << ssh_cipher_default)) - options.cipher = ssh_cipher_default; - else { - debug("Cipher %s not supported, using %.100s instead.", - cipher_name(ssh_cipher_default), - cipher_name(SSH_FALLBACK_CIPHER)); - options.cipher = SSH_FALLBACK_CIPHER; - } - } - - /* Check that the selected cipher is supported. */ - if (!(supported_ciphers & (1 << options.cipher))) - fatal("Selected cipher type %.100s not supported by server.", - cipher_name(options.cipher)); - - debug("Encryption type: %.100s", cipher_name(options.cipher)); - - /* Send the encrypted session key to the server. */ - packet_start(SSH_CMSG_SESSION_KEY); - packet_put_char(options.cipher); - - /* Send the check bytes back to the server. */ - for (i = 0; i < 8; i++) - packet_put_char(check_bytes[i]); - - /* Send the encrypted encryption key. */ - packet_put_bignum(key); - - /* Send protocol flags. */ - packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN); - - /* Send the packet now. */ - packet_send(); - packet_write_wait(); - - /* Destroy the session key integer and the public keys since we no longer - need them. */ - BN_clear_free(key); - RSA_free(public_key); - RSA_free(host_key); - - debug("Sent encrypted session key."); - - /* Set the encryption key. */ - packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, options.cipher); - - /* We will no longer need the session key here. Destroy any extra copies. */ - memset(session_key, 0, sizeof(session_key)); - - /* Expect a success message from the server. Note that this message will - be received in encrypted form. */ - packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); - - debug("Received encrypted confirmation."); - - /* Send the name of the user to log in as on the server. */ - packet_start(SSH_CMSG_USER); - packet_put_string(server_user, strlen(server_user)); - packet_send(); - packet_write_wait(); - - /* The server should respond with success if no authentication is needed - (the user has no password). Otherwise the server responds with - failure. */ - type = packet_read(&payload_len); - if (type == SSH_SMSG_SUCCESS) - return; /* Connection was accepted without authentication. */ - if (type != SSH_SMSG_FAILURE) - packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER", - type); - -#ifdef AFS - /* Try Kerberos tgt passing if the server supports it. */ - if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) && - options.kerberos_tgt_passing) - { - if (options.cipher == SSH_CIPHER_NONE) - log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!"); - (void)send_kerberos_tgt(); - } - - /* Try AFS token passing if the server supports it. */ - if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) && - options.afs_token_passing && k_hasafs()) { - if (options.cipher == SSH_CIPHER_NONE) - log("WARNING: Encryption is disabled! Token will be transmitted in the clear!"); - send_afs_tokens(); - } -#endif /* AFS */ - -#ifdef KRB4 - if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) && - options.kerberos_authentication) - { - debug("Trying Kerberos authentication."); - if (try_kerberos_authentication()) { - /* The server should respond with success or failure. */ - type = packet_read(&payload_len); - if (type == SSH_SMSG_SUCCESS) - return; /* Successful connection. */ - if (type != SSH_SMSG_FAILURE) - packet_disconnect("Protocol error: got %d in response to Kerberos auth", type); - } - } -#endif /* KRB4 */ - - /* Use rhosts authentication if running in privileged socket and we do not - wish to remain anonymous. */ - if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) && - options.rhosts_authentication) - { - debug("Trying rhosts authentication."); - packet_start(SSH_CMSG_AUTH_RHOSTS); - packet_put_string(local_user, strlen(local_user)); - packet_send(); - packet_write_wait(); - - /* The server should respond with success or failure. */ - type = packet_read(&payload_len); - if (type == SSH_SMSG_SUCCESS) - return; /* Successful connection. */ - if (type != SSH_SMSG_FAILURE) - packet_disconnect("Protocol error: got %d in response to rhosts auth", - type); - } - - /* Try .rhosts or /etc/hosts.equiv authentication with RSA host - authentication. */ - if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) && - options.rhosts_rsa_authentication && host_key_valid) - { - if (try_rhosts_rsa_authentication(local_user, own_host_key)) - return; /* Successful authentication. */ - } - - /* Try RSA authentication if the server supports it. */ - if ((supported_authentications & (1 << SSH_AUTH_RSA)) && - options.rsa_authentication) - { - /* Try RSA authentication using the authentication agent. The agent - is tried first because no passphrase is needed for it, whereas - identity files may require passphrases. */ - if (try_agent_authentication()) - return; /* Successful connection. */ - - /* Try RSA authentication for each identity. */ - for (i = 0; i < options.num_identity_files; i++) - if (try_rsa_authentication(pw, options.identity_files[i])) - return; /* Successful connection. */ - } - - /* Try password authentication if the server supports it. */ - if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) && - options.password_authentication && !options.batch_mode) - { - char prompt[80]; - snprintf(prompt, sizeof(prompt), "%.30s@%.30s's password: ", - server_user, host); - debug("Doing password authentication."); - if (options.cipher == SSH_CIPHER_NONE) - log("WARNING: Encryption is disabled! Password will be transmitted in clear text."); - for (i = 0; i < options.number_of_password_prompts; i++) { - if (i != 0) - error("Permission denied, please try again."); - password = read_passphrase(prompt, 0); - packet_start(SSH_CMSG_AUTH_PASSWORD); - packet_put_string(password, strlen(password)); - memset(password, 0, strlen(password)); - xfree(password); + extern Options options; + int i, type; + char *password; + struct passwd *pw; + BIGNUM *key; + RSA *host_key, *file_key; + RSA *public_key; + int bits, rbits; + unsigned char session_key[SSH_SESSION_KEY_LENGTH]; + const char *server_user, *local_user; + char *cp, *host, *ip = NULL; + char hostline[1000], *hostp; + unsigned char check_bytes[8]; + unsigned int supported_ciphers, supported_authentications, protocol_flags; + HostStatus host_status; + HostStatus ip_status; + int host_ip_differ = 0; + int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; + int payload_len, clen, sum_len = 0; + u_int32_t rand = 0; + + if (options.check_host_ip) + ip = xstrdup(inet_ntoa(hostaddr->sin_addr)); + + /* Convert the user-supplied hostname into all lowercase. */ + host = xstrdup(orighost); + for (cp = host; *cp; cp++) + if (isupper(*cp)) + *cp = tolower(*cp); + + /* Exchange protocol version identification strings with the server. */ + ssh_exchange_identification(); + + /* Put the connection into non-blocking mode. */ + packet_set_nonblocking(); + + /* Get local user name. Use it as server user if no user name was given. */ + pw = getpwuid(original_real_uid); + if (!pw) + fatal("User id %d not found from user database.", original_real_uid); + local_user = xstrdup(pw->pw_name); + server_user = options.user ? options.user : local_user; + + debug("Waiting for server public key."); + + /* Wait for a public key packet from the server. */ + packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY); + + /* Get check bytes from the packet. */ + for (i = 0; i < 8; i++) + check_bytes[i] = packet_get_char(); + + /* Get the public key. */ + public_key = RSA_new(); + bits = packet_get_int();/* bits */ + public_key->e = BN_new(); + packet_get_bignum(public_key->e, &clen); + sum_len += clen; + public_key->n = BN_new(); + packet_get_bignum(public_key->n, &clen); + sum_len += clen; + + rbits = BN_num_bits(public_key->n); + if (bits != rbits) { + log("Warning: Server lies about size of server public key: " + "actual size is %d bits vs. announced %d.", rbits, bits); + log("Warning: This may be due to an old implementation of ssh."); + } + /* Get the host key. */ + host_key = RSA_new(); + bits = packet_get_int();/* bits */ + host_key->e = BN_new(); + packet_get_bignum(host_key->e, &clen); + sum_len += clen; + host_key->n = BN_new(); + packet_get_bignum(host_key->n, &clen); + sum_len += clen; + + rbits = BN_num_bits(host_key->n); + if (bits != rbits) { + log("Warning: Server lies about size of server host key: " + "actual size is %d bits vs. announced %d.", rbits, bits); + log("Warning: This may be due to an old implementation of ssh."); + } + /* Store the host key from the known host file in here so that we + can compare it with the key for the IP address. */ + file_key = RSA_new(); + file_key->n = BN_new(); + file_key->e = BN_new(); + + /* Get protocol flags. */ + protocol_flags = packet_get_int(); + packet_set_protocol_flags(protocol_flags); + + /* Get supported cipher types. */ + supported_ciphers = packet_get_int(); + + /* Get supported authentication types. */ + supported_authentications = packet_get_int(); + + debug("Received server public key (%d bits) and host key (%d bits).", + BN_num_bits(public_key->n), BN_num_bits(host_key->n)); + + packet_integrity_check(payload_len, + 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, + SSH_SMSG_PUBLIC_KEY); + + /* Compute the session id. */ + compute_session_id(session_id, check_bytes, host_key->n, public_key->n); + + /* Check if the host key is present in the user\'s list of known + hosts or in the systemwide list. */ + host_status = check_host_in_hostfile(options.user_hostfile, host, + host_key->e, host_key->n, + file_key->e, file_key->n); + if (host_status == HOST_NEW) + host_status = check_host_in_hostfile(options.system_hostfile, host, + host_key->e, host_key->n, + file_key->e, file_key->n); + /* Force accepting of the host key for localhost and 127.0.0.1. + The problem is that if the home directory is NFS-mounted to + multiple machines, localhost will refer to a different machine + in each of them, and the user will get bogus HOST_CHANGED + warnings. This essentially disables host authentication for + localhost; however, this is probably not a real problem. */ + if (local) { + debug("Forcing accepting of host key for localhost."); + host_status = HOST_OK; + } + /* Also perform check for the ip address, skip the check if we are + localhost or the hostname was an ip address to begin with */ + if (options.check_host_ip && !local && strcmp(host, ip)) { + RSA *ip_key = RSA_new(); + ip_key->n = BN_new(); + ip_key->e = BN_new(); + ip_status = check_host_in_hostfile(options.user_hostfile, ip, + host_key->e, host_key->n, + ip_key->e, ip_key->n); + + if (ip_status == HOST_NEW) + ip_status = check_host_in_hostfile(options.system_hostfile, ip, + host_key->e, host_key->n, + ip_key->e, ip_key->n); + if (host_status == HOST_CHANGED && + (ip_status != HOST_CHANGED || + (BN_cmp(ip_key->e, file_key->e) || BN_cmp(ip_key->n, file_key->n)))) + host_ip_differ = 1; + + RSA_free(ip_key); + } else + ip_status = host_status; + + RSA_free(file_key); + + switch (host_status) { + case HOST_OK: + /* The host is known and the key matches. */ + debug("Host '%.200s' is known and matches the host key.", host); + if (options.check_host_ip) { + if (ip_status == HOST_NEW) { + if (!add_host_to_hostfile(options.user_hostfile, ip, + host_key->e, host_key->n)) + log("Failed to add the host key for IP address '%.30s' to the list of known hosts (%.30s).", + ip, options.user_hostfile); + else + log("Warning: Permanently added host key for IP address '%.30s' to the list of known hosts.", + ip); + } else if (ip_status != HOST_OK) + log("Warning: the host key for '%.200s' differs from the key for the IP address '%.30s'", + host, ip); + } + break; + case HOST_NEW: + /* The host is new. */ + if (options.strict_host_key_checking == 1) { + /* User has requested strict host key checking. We will not add the host key + automatically. The only alternative left is to abort. */ + fatal("No host key is known for %.200s and you have requested strict checking.", host); + } else if (options.strict_host_key_checking == 2) { + /* The default */ + char prompt[1024]; + char *fp = fingerprint(host_key->e, host_key->n); + snprintf(prompt, sizeof(prompt), + "The authenticity of host '%.200s' can't be established.\n" + "Key fingerprint is %d %s.\n" + "Are you sure you want to continue connecting (yes/no)? ", + host, BN_num_bits(host_key->n), fp); + if (!read_yes_or_no(prompt, -1)) + fatal("Aborted by user!\n"); + } + if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) { + snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); + hostp = hostline; + } else + hostp = host; + + /* If not in strict mode, add the key automatically to the local known_hosts file. */ + if (!add_host_to_hostfile(options.user_hostfile, hostp, + host_key->e, host_key->n)) + log("Failed to add the host to the list of known hosts (%.500s).", + options.user_hostfile); + else + log("Warning: Permanently added '%.200s' to the list of known hosts.", + hostp); + break; + case HOST_CHANGED: + if (options.check_host_ip && host_ip_differ) { + char *msg; + if (ip_status == HOST_NEW) + msg = "is unknown"; + else if (ip_status == HOST_OK) + msg = "is unchanged"; + else + msg = "has a different value"; + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("The host key for %s has changed,", host); + error("and the key for the according IP address %s", ip); + error("%s. This could either mean that", msg); + error("DNS SPOOFING is happening or the IP address for the host"); + error("and its host key have changed at the same time"); + } + /* The host key has changed. */ + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: HOST IDENTIFICATION HAS CHANGED! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); + error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); + error("It is also possible that the host key has just been changed."); + error("Please contact your system administrator."); + error("Add correct host key in %.100s to get rid of this message.", + options.user_hostfile); + + /* If strict host key checking is in use, the user will + have to edit the key manually and we can only abort. */ + if (options.strict_host_key_checking) + fatal("Host key for %.200s has changed and you have requested strict checking.", host); + + /* If strict host key checking has not been requested, allow the connection + but without password authentication or agent forwarding. */ + if (options.password_authentication) { + error("Password authentication is disabled to avoid trojan horses."); + options.password_authentication = 0; + } + if (options.forward_agent) { + error("Agent forwarding is disabled to avoid trojan horses."); + options.forward_agent = 0; + } + /* XXX Should permit the user to change to use the new id. + This could be done by converting the host key to an + identifying sentence, tell that the host identifies + itself by that sentence, and ask the user if he/she + whishes to accept the authentication. */ + break; + } + + if (options.check_host_ip) + xfree(ip); + + /* Generate a session key. */ + arc4random_stir(); + + /* Generate an encryption key for the session. The key is a 256 + bit random number, interpreted as a 32-byte key, with the least + significant 8 bits being the first byte of the key. */ + for (i = 0; i < 32; i++) { + if (i % 4 == 0) + rand = arc4random(); + session_key[i] = rand & 0xff; + rand >>= 8; + } + + /* According to the protocol spec, the first byte of the session + key is the highest byte of the integer. The session key is + xored with the first 16 bytes of the session id. */ + key = BN_new(); + BN_set_word(key, 0); + for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { + BN_lshift(key, key, 8); + if (i < 16) + BN_add_word(key, session_key[i] ^ session_id[i]); + else + BN_add_word(key, session_key[i]); + } + + /* Encrypt the integer using the public key and host key of the + server (key with smaller modulus first). */ + if (BN_cmp(public_key->n, host_key->n) < 0) { + /* Public key has smaller modulus. */ + if (BN_num_bits(host_key->n) < + BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED) { + fatal("respond_to_rsa_challenge: host_key %d < public_key %d + " + "SSH_KEY_BITS_RESERVED %d", + BN_num_bits(host_key->n), + BN_num_bits(public_key->n), + SSH_KEY_BITS_RESERVED); + } + rsa_public_encrypt(key, key, public_key); + rsa_public_encrypt(key, key, host_key); + } else { + /* Host key has smaller modulus (or they are equal). */ + if (BN_num_bits(public_key->n) < + BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED) { + fatal("respond_to_rsa_challenge: public_key %d < host_key %d + " + "SSH_KEY_BITS_RESERVED %d", + BN_num_bits(public_key->n), + BN_num_bits(host_key->n), + SSH_KEY_BITS_RESERVED); + } + rsa_public_encrypt(key, key, host_key); + rsa_public_encrypt(key, key, public_key); + } + + if (options.cipher == SSH_CIPHER_NOT_SET) { + if (cipher_mask() & supported_ciphers & (1 << ssh_cipher_default)) + options.cipher = ssh_cipher_default; + else { + debug("Cipher %s not supported, using %.100s instead.", + cipher_name(ssh_cipher_default), + cipher_name(SSH_FALLBACK_CIPHER)); + options.cipher = SSH_FALLBACK_CIPHER; + } + } + /* Check that the selected cipher is supported. */ + if (!(supported_ciphers & (1 << options.cipher))) + fatal("Selected cipher type %.100s not supported by server.", + cipher_name(options.cipher)); + + debug("Encryption type: %.100s", cipher_name(options.cipher)); + + /* Send the encrypted session key to the server. */ + packet_start(SSH_CMSG_SESSION_KEY); + packet_put_char(options.cipher); + + /* Send the check bytes back to the server. */ + for (i = 0; i < 8; i++) + packet_put_char(check_bytes[i]); + + /* Send the encrypted encryption key. */ + packet_put_bignum(key); + + /* Send protocol flags. */ + packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN); + + /* Send the packet now. */ packet_send(); packet_write_wait(); - + + /* Destroy the session key integer and the public keys since we no longer need them. */ + BN_clear_free(key); + RSA_free(public_key); + RSA_free(host_key); + + debug("Sent encrypted session key."); + + /* Set the encryption key. */ + packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, options.cipher); + + /* We will no longer need the session key here. Destroy any extra copies. */ + memset(session_key, 0, sizeof(session_key)); + + /* Expect a success message from the server. Note that this + message will be received in encrypted form. */ + packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); + + debug("Received encrypted confirmation."); + + /* Send the name of the user to log in as on the server. */ + packet_start(SSH_CMSG_USER); + packet_put_string(server_user, strlen(server_user)); + packet_send(); + packet_write_wait(); + + /* The server should respond with success if no authentication is + needed (the user has no password). Otherwise the server + responds with failure. */ type = packet_read(&payload_len); + + /* check whether the connection was accepted without authentication. */ if (type == SSH_SMSG_SUCCESS) - return; /* Successful connection. */ + return; if (type != SSH_SMSG_FAILURE) - packet_disconnect("Protocol error: got %d in response to passwd auth", type); - } - } + packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER", + type); + +#ifdef AFS + /* Try Kerberos tgt passing if the server supports it. */ + if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) && + options.kerberos_tgt_passing) { + if (options.cipher == SSH_CIPHER_NONE) + log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!"); + (void) send_kerberos_tgt(); + } + /* Try AFS token passing if the server supports it. */ + if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) && + options.afs_token_passing && k_hasafs()) { + if (options.cipher == SSH_CIPHER_NONE) + log("WARNING: Encryption is disabled! Token will be transmitted in the clear!"); + send_afs_tokens(); + } +#endif /* AFS */ - /* All authentication methods have failed. Exit with an error message. */ - fatal("Permission denied."); - /*NOTREACHED*/ +#ifdef KRB4 + if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) && + options.kerberos_authentication) { + debug("Trying Kerberos authentication."); + if (try_kerberos_authentication()) { + /* The server should respond with success or failure. */ + type = packet_read(&payload_len); + if (type == SSH_SMSG_SUCCESS) + return; + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error: got %d in response to Kerberos auth", type); + } + } +#endif /* KRB4 */ + + /* Use rhosts authentication if running in privileged socket and + we do not wish to remain anonymous. */ + if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) && + options.rhosts_authentication) { + debug("Trying rhosts authentication."); + packet_start(SSH_CMSG_AUTH_RHOSTS); + packet_put_string(local_user, strlen(local_user)); + packet_send(); + packet_write_wait(); + + /* The server should respond with success or failure. */ + type = packet_read(&payload_len); + if (type == SSH_SMSG_SUCCESS) + return; + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error: got %d in response to rhosts auth", + type); + } + /* Try .rhosts or /etc/hosts.equiv authentication with RSA host + authentication. */ + if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) && + options.rhosts_rsa_authentication && host_key_valid) { + if (try_rhosts_rsa_authentication(local_user, own_host_key)) + return; + } + /* Try RSA authentication if the server supports it. */ + if ((supported_authentications & (1 << SSH_AUTH_RSA)) && + options.rsa_authentication) { + /* Try RSA authentication using the authentication agent. + The agent is tried first because no passphrase is + needed for it, whereas identity files may require + passphrases. */ + if (try_agent_authentication()) + return; + + /* Try RSA authentication for each identity. */ + for (i = 0; i < options.num_identity_files; i++) + if (try_rsa_authentication(pw, options.identity_files[i])) + return; + } + /* Try skey authentication if the server supports it. */ + if ((supported_authentications & (1 << SSH_AUTH_TIS)) && + options.skey_authentication && !options.batch_mode) { + debug("Doing skey authentication."); + + /* request a challenge */ + packet_start(SSH_CMSG_AUTH_TIS); + packet_send(); + packet_write_wait(); + + type = packet_read(&payload_len); + if (type != SSH_SMSG_FAILURE && + type != SSH_SMSG_AUTH_TIS_CHALLENGE) { + packet_disconnect("Protocol error: got %d in response " + "to skey auth", type); + } + if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) { + debug("No challenge for skey authentication."); + } else { + char *challenge, *response; + challenge = packet_get_string(&payload_len); + if (options.cipher == SSH_CIPHER_NONE) + log("WARNING: Encryption is disabled! " + "Reponse will be transmitted in clear text."); + fprintf(stderr, "%s\n", challenge); + fflush(stderr); + for (i = 0; i < options.number_of_password_prompts; i++) { + if (i != 0) + error("Permission denied, please try again."); + response = read_passphrase("Response: ", 0); + packet_start(SSH_CMSG_AUTH_TIS_RESPONSE); + packet_put_string(response, strlen(response)); + memset(response, 0, strlen(response)); + xfree(response); + packet_send(); + packet_write_wait(); + type = packet_read(&payload_len); + if (type == SSH_SMSG_SUCCESS) + return; + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error: got %d in response " + "to skey auth", type); + } + } + } + /* Try password authentication if the server supports it. */ + if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) && + options.password_authentication && !options.batch_mode) { + char prompt[80]; + snprintf(prompt, sizeof(prompt), "%.30s@%.30s's password: ", + server_user, host); + debug("Doing password authentication."); + if (options.cipher == SSH_CIPHER_NONE) + log("WARNING: Encryption is disabled! Password will be transmitted in clear text."); + for (i = 0; i < options.number_of_password_prompts; i++) { + if (i != 0) + error("Permission denied, please try again."); + password = read_passphrase(prompt, 0); + packet_start(SSH_CMSG_AUTH_PASSWORD); + packet_put_string(password, strlen(password)); + memset(password, 0, strlen(password)); + xfree(password); + packet_send(); + packet_write_wait(); + + type = packet_read(&payload_len); + if (type == SSH_SMSG_SUCCESS) + return; + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error: got %d in response to passwd auth", type); + } + } + /* All authentication methods have failed. Exit with an error message. */ + fatal("Permission denied."); + /* NOTREACHED */ } diff --git a/sshd.8 b/sshd.8 index f8a46f1d..1267eda6 100644 --- a/sshd.8 +++ b/sshd.8 @@ -26,6 +26,7 @@ .Op Fl h Ar host_key_file .Op Fl k Ar key_gen_time .Op Fl p Ar port +.Op Fl V Ar client_protocol_id .Sh DESCRIPTION .Nm (Secure Shell Daemon) is the daemon program for @@ -165,6 +166,13 @@ Quiet mode. Nothing is sent to the system log. Normally the beginning, authentication, and termination of each connection is logged. .It Fl Q Do not print an error message if RSA support is missing. +.It Fl V Ar client_protocol_id +SSH2 compatibility mode. +When this options is specified +.Nm +assumes the client has sent the given version string +and skips the +Protocol Version Identification Exchange. .El .Sh CONFIGURATION FILE .Nm @@ -320,7 +328,7 @@ The default is 600 (seconds). Gives the verbosity level that is used when logging messages from .Nm sshd . The possible values are: -QUIET, FATAL, ERROR, INFO, CHAT and DEBUG. +QUIET, FATAL, ERROR, INFO, VERBOSE and DEBUG. The default is INFO. Logging with level DEBUG violates the privacy of users and is not recommended. diff --git a/sshd.c b/sshd.c index 6a8f7ebb..77065fe7 100644 --- a/sshd.c +++ b/sshd.c @@ -1,21 +1,14 @@ /* - -sshd.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Fri Mar 17 17:09:28 1995 ylo - -This program is the ssh daemon. It listens for connections from clients, and -performs authentication, executes use commands or shell, and forwards -information to/from the application to the user client over an encrypted -connection. This can also handle forwarding of X11, TCP/IP, and authentication -agent connections. - -*/ + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Fri Mar 17 17:09:28 1995 ylo + * This program is the ssh daemon. It listens for connections from clients, and + * performs authentication, executes use commands or shell, and forwards + * information to/from the application to the user client over an encrypted + * connection. This can also handle forwarding of X11, TCP/IP, and authentication + * agent connections. + */ #include "includes.h" RCSID("$Id$"); @@ -52,10 +45,12 @@ ServerOptions options; /* Name of the server configuration file. */ char *config_file_name = SERVER_CONFIG_FILE; -/* Debug mode flag. This can be set on the command line. If debug - mode is enabled, extra debugging output will be sent to the system - log, the daemon will not go to background, and will exit after processing - the first connection. */ +/* + * Debug mode flag. This can be set on the command line. If debug + * mode is enabled, extra debugging output will be sent to the system + * log, the daemon will not go to background, and will exit after processing + * the first connection. + */ int debug_flag = 0; /* Flag indicating that the daemon is being started from inetd. */ @@ -74,15 +69,21 @@ char **saved_argv; the SIGHUP signal handler. */ int listen_sock; -/* Flags set in auth-rsa from authorized_keys flags. These are set in - auth-rsa.c. */ +/* the client's version string, passed by sshd2 in compat mode. + if != NULL, sshd will skip the version-number exchange */ +char *client_version_string = NULL; + +/* Flags set in auth-rsa from authorized_keys flags. These are set in auth-rsa.c. */ int no_port_forwarding_flag = 0; int no_agent_forwarding_flag = 0; int no_x11_forwarding_flag = 0; int no_pty_flag = 0; -char *forced_command = NULL; /* RSA authentication "command=" option. */ -struct envstring *custom_environment = NULL; - /* RSA authentication "environment=" options. */ + +/* RSA authentication "command=" option. */ +char *forced_command = NULL; + +/* RSA authentication "environment=" options. */ +struct envstring *custom_environment = NULL; /* Session id for the current session. */ unsigned char session_id[16]; @@ -93,13 +94,9 @@ unsigned char session_id[16]; The private key contains BIGNUMs, and we do not (in principle) have access to the internals of them, and locking just the structure is not very useful. Currently, memory locking is not implemented. */ -struct -{ - /* Private part of server key. */ - RSA *private_key; - - /* Private part of host key. */ - RSA *host_key; +struct { + RSA *private_key; /* Private part of server key. */ + RSA *host_key; /* Private part of host key. */ } sensitive_data; /* Flag indicating whether the current session key has been used. This flag @@ -116,2454 +113,2458 @@ RSA *public_key; /* Prototypes for various functions defined later in this file. */ void do_connection(); void do_authentication(char *user); -void do_authloop(struct passwd *pw); +void do_authloop(struct passwd * pw); void do_fake_authloop(char *user); -void do_authenticated(struct passwd *pw); -void do_exec_pty(const char *command, int ptyfd, int ttyfd, - const char *ttyname, struct passwd *pw, const char *term, - const char *display, const char *auth_proto, - const char *auth_data); -void do_exec_no_pty(const char *command, struct passwd *pw, - const char *display, const char *auth_proto, - const char *auth_data); -void do_child(const char *command, struct passwd *pw, const char *term, +void do_authenticated(struct passwd * pw); +void do_exec_pty(const char *command, int ptyfd, int ttyfd, + const char *ttyname, struct passwd * pw, const char *term, + const char *display, const char *auth_proto, + const char *auth_data); +void do_exec_no_pty(const char *command, struct passwd * pw, + const char *display, const char *auth_proto, + const char *auth_data); +void do_child(const char *command, struct passwd * pw, const char *term, const char *display, const char *auth_proto, const char *auth_data, const char *ttyname); #ifdef HAVE_LIBPAM static int pamconv(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *appdata_ptr); + struct pam_response **resp, void *appdata_ptr); void do_pam_account_and_session(char *username, char *remote_user, - const char *remote_host); + const char *remote_host); void pam_cleanup_proc(void *context); static struct pam_conv conv = { - pamconv, - NULL + pamconv, + NULL }; struct pam_handle_t *pamh = NULL; const char *pampasswd = NULL; char *pamconv_msg = NULL; static int pamconv(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *appdata_ptr) + struct pam_response **resp, void *appdata_ptr) { - struct pam_response *reply; - int count; - size_t msg_len; - char *p; - - /* PAM will free this later */ - reply = malloc(num_msg * sizeof(*reply)); - if (reply == NULL) - return PAM_CONV_ERR; - - for(count = 0; count < num_msg; count++) - { - switch (msg[count]->msg_style) - { - case PAM_PROMPT_ECHO_OFF: - if (pampasswd == NULL) - { - free(reply); - return PAM_CONV_ERR; - } - reply[count].resp_retcode = PAM_SUCCESS; - reply[count].resp = xstrdup(pampasswd); - break; - - case PAM_TEXT_INFO: - reply[count].resp_retcode = PAM_SUCCESS; - reply[count].resp = xstrdup(""); - - if (msg[count]->msg == NULL) - break; - debug("Adding PAM message: %s", msg[count]->msg); - - msg_len = strlen(msg[count]->msg); - if (pamconv_msg) - { - size_t n = strlen(pamconv_msg); - pamconv_msg = xrealloc(pamconv_msg, n + msg_len + 2); - p = pamconv_msg + n; + struct pam_response *reply; + int count; + size_t msg_len; + char *p; + + /* PAM will free this later */ + reply = malloc(num_msg * sizeof(*reply)); + if (reply == NULL) + return PAM_CONV_ERR; + + for(count = 0; count < num_msg; count++) { + switch (msg[count]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + if (pampasswd == NULL) { + free(reply); + return PAM_CONV_ERR; + } + reply[count].resp_retcode = PAM_SUCCESS; + reply[count].resp = xstrdup(pampasswd); + break; + + case PAM_TEXT_INFO: + reply[count].resp_retcode = PAM_SUCCESS; + reply[count].resp = xstrdup(""); + + if (msg[count]->msg == NULL) + break; + + debug("Adding PAM message: %s", msg[count]->msg); + + msg_len = strlen(msg[count]->msg); + if (pamconv_msg) { + size_t n = strlen(pamconv_msg); + pamconv_msg = xrealloc(pamconv_msg, n + msg_len + 2); + p = pamconv_msg + n; + } else { + pamconv_msg = p = xmalloc(msg_len + 2); + } + memcpy(p, msg[count]->msg, msg_len); + p[msg_len] = '\n'; + p[msg_len + 1] = '\0'; + break; + + case PAM_PROMPT_ECHO_ON: + case PAM_ERROR_MSG: + default: + free(reply); + return PAM_CONV_ERR; + } } - else - pamconv_msg = p = xmalloc(msg_len + 2); - memcpy(p, msg[count]->msg, msg_len); - p[msg_len] = '\n'; - p[msg_len + 1] = '\0'; - break; - - case PAM_PROMPT_ECHO_ON: - case PAM_ERROR_MSG: - default: - free(reply); - return PAM_CONV_ERR; - } - } - - *resp = reply; - - return PAM_SUCCESS; + + *resp = reply; + + return PAM_SUCCESS; } void pam_cleanup_proc(void *context) { - int pam_retval; - - if (pamh != NULL) - { - pam_retval = pam_close_session((pam_handle_t *)pamh, 0); - if (pam_retval != PAM_SUCCESS) - { - log("Cannot close PAM session: %.200s", - PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - } - - pam_retval = pam_end((pam_handle_t *)pamh, pam_retval); - if (pam_retval != PAM_SUCCESS) - { - log("Cannot release PAM authentication: %.200s", - PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - } - } + int pam_retval; + + if (pamh != NULL) + { + pam_retval = pam_close_session((pam_handle_t *)pamh, 0); + if (pam_retval != PAM_SUCCESS) { + log("Cannot close PAM session: %.200s", + PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); + } + + pam_retval = pam_end((pam_handle_t *)pamh, pam_retval); + if (pam_retval != PAM_SUCCESS) { + log("Cannot release PAM authentication: %.200s", + PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); + } + } } void do_pam_account_and_session(char *username, char *remote_user, - const char *remote_host) + const char *remote_host) { - int pam_retval; - - if (remote_host != NULL) - { - debug("PAM setting rhost to \"%.200s\"", remote_host); - pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RHOST, remote_host); - if (pam_retval != PAM_SUCCESS) - { - log("PAM set rhost failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - do_fake_authloop(username); - } - } - - if (remote_user != NULL) - { - debug("PAM setting ruser to \"%.200s\"", remote_user); - pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RUSER, remote_user); - if (pam_retval != PAM_SUCCESS) - { - log("PAM set ruser failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - do_fake_authloop(username); - } - } - - pam_retval = pam_acct_mgmt((pam_handle_t *)pamh, 0); - if (pam_retval != PAM_SUCCESS) - { - log("PAM rejected by account configuration: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - do_fake_authloop(username); - } - - pam_retval = pam_open_session((pam_handle_t *)pamh, 0); - if (pam_retval != PAM_SUCCESS) - { - log("PAM session setup failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - do_fake_authloop(username); - } + int pam_retval; + + if (remote_host != NULL) { + debug("PAM setting rhost to \"%.200s\"", remote_host); + pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RHOST, remote_host); + if (pam_retval != PAM_SUCCESS) { + log("PAM set rhost failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); + do_fake_authloop(username); + } + } + + if (remote_user != NULL) { + debug("PAM setting ruser to \"%.200s\"", remote_user); + pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RUSER, remote_user); + if (pam_retval != PAM_SUCCESS) { + log("PAM set ruser failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); + do_fake_authloop(username); + } + } + + pam_retval = pam_acct_mgmt((pam_handle_t *)pamh, 0); + if (pam_retval != PAM_SUCCESS) { + log("PAM rejected by account configuration: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); + do_fake_authloop(username); + } + + pam_retval = pam_open_session((pam_handle_t *)pamh, 0); + if (pam_retval != PAM_SUCCESS) { + log("PAM session setup failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); + do_fake_authloop(username); + } } #endif /* HAVE_LIBPAM */ -/* Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; - the effect is to reread the configuration file (and to regenerate - the server key). */ - -void sighup_handler(int sig) +/* + * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; + * the effect is to reread the configuration file (and to regenerate + * the server key). + */ +void +sighup_handler(int sig) { - received_sighup = 1; - signal(SIGHUP, sighup_handler); + received_sighup = 1; + signal(SIGHUP, sighup_handler); } -/* Called from the main program after receiving SIGHUP. Restarts the - server. */ - -void sighup_restart() +/* + * Called from the main program after receiving SIGHUP. + * Restarts the server. + */ +void +sighup_restart() { - log("Received SIGHUP; restarting."); - close(listen_sock); - execv(saved_argv[0], saved_argv); - log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno)); - exit(1); + log("Received SIGHUP; restarting."); + close(listen_sock); + execv(saved_argv[0], saved_argv); + log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno)); + exit(1); } -/* Generic signal handler for terminating signals in the master daemon. - These close the listen socket; not closing it seems to cause "Address - already in use" problems on some machines, which is inconvenient. */ - -void sigterm_handler(int sig) +/* + * Generic signal handler for terminating signals in the master daemon. + * These close the listen socket; not closing it seems to cause "Address + * already in use" problems on some machines, which is inconvenient. + */ +void +sigterm_handler(int sig) { - log("Received signal %d; terminating.", sig); - close(listen_sock); - exit(255); + log("Received signal %d; terminating.", sig); + close(listen_sock); + exit(255); } -/* SIGCHLD handler. This is called whenever a child dies. This will then - reap any zombies left by exited c. */ - -void main_sigchld_handler(int sig) +/* + * SIGCHLD handler. This is called whenever a child dies. This will then + * reap any zombies left by exited c. + */ +void +main_sigchld_handler(int sig) { - int save_errno = errno; - int status; + int save_errno = errno; + int status; - while (waitpid(-1, &status, WNOHANG) > 0) - ; + while (waitpid(-1, &status, WNOHANG) > 0) + ; - signal(SIGCHLD, main_sigchld_handler); - errno = save_errno; + signal(SIGCHLD, main_sigchld_handler); + errno = save_errno; } -/* Signal handler for the alarm after the login grace period has expired. */ - -void grace_alarm_handler(int sig) +/* + * Signal handler for the alarm after the login grace period has expired. + */ +void +grace_alarm_handler(int sig) { - /* Close the connection. */ - packet_close(); - - /* Log error and exit. */ - fatal("Timeout before authentication."); -} + /* Close the connection. */ + packet_close(); -/* Signal handler for the key regeneration alarm. Note that this - alarm only occurs in the daemon waiting for connections, and it does not - do anything with the private key or random state before forking. Thus there - should be no concurrency control/asynchronous execution problems. */ + /* Log error and exit. */ + fatal("Timeout before authentication for %s.", get_remote_ipaddr()); +} -void key_regeneration_alarm(int sig) +/* + * convert ssh auth msg type into description + */ +char * +get_authname(int type) { - int save_errno = errno; - - /* Check if we should generate a new key. */ - if (key_used) - { - /* This should really be done in the background. */ - log("Generating new %d bit RSA key.", options.server_key_bits); - - if (sensitive_data.private_key != NULL) - RSA_free(sensitive_data.private_key); - sensitive_data.private_key = RSA_new(); - - if (public_key != NULL) - RSA_free(public_key); - public_key = RSA_new(); - - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - arc4random_stir(); - key_used = 0; - log("RSA key generation complete."); - } - - /* Reschedule the alarm. */ - signal(SIGALRM, key_regeneration_alarm); - alarm(options.key_regeneration_time); - errno = save_errno; + switch (type) { + case SSH_CMSG_AUTH_PASSWORD: + return "password"; + case SSH_CMSG_AUTH_RSA: + return "rsa"; + case SSH_CMSG_AUTH_RHOSTS_RSA: + return "rhosts-rsa"; + case SSH_CMSG_AUTH_RHOSTS: + return "rhosts"; +#ifdef KRB4 + case SSH_CMSG_AUTH_KERBEROS: + return "kerberos"; +#endif +#ifdef SKEY + case SSH_CMSG_AUTH_TIS_RESPONSE: + return "s/key"; +#endif + } + fatal("get_authname: unknown auth %d: internal error", type); + return NULL; } -/* Main program for the daemon. */ +/* + * Signal handler for the key regeneration alarm. Note that this + * alarm only occurs in the daemon waiting for connections, and it does not + * do anything with the private key or random state before forking. + * Thus there should be no concurrency control/asynchronous execution + * problems. + */ +void +key_regeneration_alarm(int sig) +{ + int save_errno = errno; + + /* Check if we should generate a new key. */ + if (key_used) { + /* This should really be done in the background. */ + log("Generating new %d bit RSA key.", options.server_key_bits); + + if (sensitive_data.private_key != NULL) + RSA_free(sensitive_data.private_key); + sensitive_data.private_key = RSA_new(); + + if (public_key != NULL) + RSA_free(public_key); + public_key = RSA_new(); + + rsa_generate_key(sensitive_data.private_key, public_key, + options.server_key_bits); + arc4random_stir(); + key_used = 0; + log("RSA key generation complete."); + } + /* Reschedule the alarm. */ + signal(SIGALRM, key_regeneration_alarm); + alarm(options.key_regeneration_time); + errno = save_errno; +} +/* + * Main program for the daemon. + */ int main(int ac, char **av) { - extern char *optarg; - extern int optind; - int opt, aux, sock_in, sock_out, newsock, i, pid, on = 1; - int remote_major, remote_minor; - int silentrsa = 0; - struct sockaddr_in sin; - char buf[100]; /* Must not be larger than remote_version. */ - char remote_version[100]; /* Must be at least as big as buf. */ - int remote_port; - char *comment; - FILE *f; - struct linger linger; - - /* Save argv[0]. */ - saved_argv = av; - if (strchr(av[0], '/')) - av0 = strrchr(av[0], '/') + 1; - else - av0 = av[0]; - - /* 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:diqQ")) != EOF) - { - switch (opt) - { - case 'f': - config_file_name = optarg; - break; - case 'd': - debug_flag = 1; - options.log_level = SYSLOG_LEVEL_DEBUG; - break; - case 'i': - inetd_flag = 1; - break; - case 'Q': - silentrsa = 1; - break; - case 'q': - options.log_level = SYSLOG_LEVEL_QUIET; - break; - case 'b': - options.server_key_bits = atoi(optarg); - break; - case 'p': - options.port = atoi(optarg); - break; - case 'g': - options.login_grace_time = atoi(optarg); - break; - case 'k': - options.key_regeneration_time = atoi(optarg); - break; - case 'h': - options.host_key_file = optarg; - break; - case '?': - default: - fprintf(stderr, "sshd version %s\n", SSH_VERSION); - fprintf(stderr, "Usage: %s [options]\n", av0); - fprintf(stderr, "Options:\n"); - fprintf(stderr, " -f file Configuration file (default %s/sshd_config)\n", ETCDIR); - fprintf(stderr, " -d Debugging mode\n"); - fprintf(stderr, " -i Started from inetd\n"); - fprintf(stderr, " -q Quiet (no logging)\n"); - fprintf(stderr, " -p port Listen on the specified port (default: 22)\n"); - fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n"); - fprintf(stderr, " -g seconds Grace period for authentication (default: 300)\n"); - fprintf(stderr, " -b bits Size of server RSA key (default: 768 bits)\n"); - fprintf(stderr, " -h file File from which to read host key (default: %s)\n", - HOST_KEY_FILE); - exit(1); + extern char *optarg; + extern int optind; + int opt, aux, sock_in, sock_out, newsock, i, pid, on = 1; + int remote_major, remote_minor; + int silentrsa = 0; + struct sockaddr_in sin; + char buf[100]; /* Must not be larger than remote_version. */ + char remote_version[100]; /* Must be at least as big as buf. */ + const char *remote_ip; + int remote_port; + char *comment; + FILE *f; + struct linger linger; + + /* Save argv[0]. */ + saved_argv = av; + if (strchr(av[0], '/')) + av0 = strrchr(av[0], '/') + 1; + else + av0 = av[0]; + + /* 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:V:diqQ")) != EOF) { + switch (opt) { + case 'f': + config_file_name = optarg; + break; + case 'd': + debug_flag = 1; + options.log_level = SYSLOG_LEVEL_DEBUG; + break; + case 'i': + inetd_flag = 1; + break; + case 'Q': + silentrsa = 1; + break; + case 'q': + options.log_level = SYSLOG_LEVEL_QUIET; + break; + case 'b': + options.server_key_bits = atoi(optarg); + break; + case 'p': + options.port = atoi(optarg); + break; + case 'g': + options.login_grace_time = atoi(optarg); + break; + case 'k': + options.key_regeneration_time = atoi(optarg); + break; + case 'h': + options.host_key_file = optarg; + break; + case 'V': + client_version_string = optarg; + /* only makes sense with inetd_flag, i.e. no listen() */ + inetd_flag = 1; + break; + case '?': + default: + fprintf(stderr, "sshd version %s\n", SSH_VERSION); + fprintf(stderr, "Usage: %s [options]\n", av0); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -f file Configuration file (default %s/sshd_config)\n", ETCDIR); + fprintf(stderr, " -d Debugging mode\n"); + fprintf(stderr, " -i Started from inetd\n"); + fprintf(stderr, " -q Quiet (no logging)\n"); + fprintf(stderr, " -p port Listen on the specified port (default: 22)\n"); + fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n"); + fprintf(stderr, " -g seconds Grace period for authentication (default: 300)\n"); + fprintf(stderr, " -b bits Size of server RSA key (default: 768 bits)\n"); + fprintf(stderr, " -h file File from which to read host key (default: %s)\n", + HOST_KEY_FILE); + exit(1); + } + } + + /* check if RSA support exists */ + if (rsa_alive() == 0) { + if (silentrsa == 0) + printf("sshd: no RSA support in libssl and libcrypto -- exiting. See ssl(8)\n"); + log("no RSA support in libssl and libcrypto -- exiting. See ssl(8)"); + exit(1); + } + /* Read server configuration options from the configuration file. */ + read_server_config(&options, config_file_name); + + /* Fill in default values for those options not explicitly set. */ + fill_default_server_options(&options); + + /* Check certain values for sanity. */ + if (options.server_key_bits < 512 || + options.server_key_bits > 32768) { + fprintf(stderr, "Bad server key size.\n"); + exit(1); + } + if (options.port < 1 || options.port > 65535) { + fprintf(stderr, "Bad port number.\n"); + exit(1); + } + /* Check that there are no remaining arguments. */ + if (optind < ac) { + fprintf(stderr, "Extra argument %s.\n", av[optind]); + exit(1); } - } - - /* check if RSA support exists */ - if (rsa_alive() == 0) { - if (silentrsa == 0) - printf("sshd: no RSA support in libssl and libcrypto -- exiting. See ssl(8)\n"); - log("no RSA support in libssl and libcrypto -- exiting. See ssl(8)"); - exit(1); - } - - /* Read server configuration options from the configuration file. */ - read_server_config(&options, config_file_name); - - /* Fill in default values for those options not explicitly set. */ - fill_default_server_options(&options); - - /* Check certain values for sanity. */ - if (options.server_key_bits < 512 || - options.server_key_bits > 32768) - { - fprintf(stderr, "Bad server key size.\n"); - exit(1); - } - if (options.port < 1 || options.port > 65535) - { - fprintf(stderr, "Bad port number.\n"); - exit(1); - } - - /* Check that there are no remaining arguments. */ - if (optind < ac) - { - fprintf(stderr, "Extra argument %s.\n", av[optind]); - exit(1); - } - - /* Force logging to stderr while loading the private host key - unless started from inetd */ - log_init(av0, options.log_level, options.log_facility, !inetd_flag); - - debug("sshd version %.100s", SSH_VERSION); - - sensitive_data.host_key = RSA_new(); - errno = 0; - /* Load the host key. It must have empty passphrase. */ - if (!load_private_key(options.host_key_file, "", - sensitive_data.host_key, &comment)) - { - error("Could not load host key: %.200s: %.100s", - options.host_key_file, strerror(errno)); - exit(1); - } - xfree(comment); - - /* Initialize the log (it is reinitialized below in case we forked). */ - if (debug_flag && !inetd_flag) - log_stderr = 1; - log_init(av0, 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) - { + /* Force logging to stderr while loading the private host key + unless started from inetd */ + log_init(av0, options.log_level, options.log_facility, !inetd_flag); + + debug("sshd version %.100s", SSH_VERSION); + + sensitive_data.host_key = RSA_new(); + errno = 0; + /* Load the host key. It must have empty passphrase. */ + if (!load_private_key(options.host_key_file, "", + sensitive_data.host_key, &comment)) { + error("Could not load host key: %.200s: %.100s", + options.host_key_file, strerror(errno)); + exit(1); + } + xfree(comment); + + /* Initialize the log (it is reinitialized below in case we + forked). */ + if (debug_flag && !inetd_flag) + log_stderr = 1; + log_init(av0, 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) { #ifdef TIOCNOTTY - int fd; + int fd; #endif /* TIOCNOTTY */ - if (daemon(0, 0) < 0) - fatal("daemon() failed: %.200s", strerror(errno)); - - /* Disconnect from the controlling tty. */ + if (daemon(0, 0) < 0) + fatal("daemon() failed: %.200s", strerror(errno)); + + /* Disconnect from the controlling tty. */ #ifdef TIOCNOTTY - fd = open("/dev/tty", O_RDWR|O_NOCTTY); - if (fd >= 0) - { - (void)ioctl(fd, TIOCNOTTY, NULL); - close(fd); - } + fd = open("/dev/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(av0, options.log_level, options.log_facility, log_stderr); - - /* 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.host_key->n) - SSH_KEY_BITS_RESERVED && - options.server_key_bits < - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) - { - options.server_key_bits = - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; - debug("Forcing server key to %d bits to make it differ from host key.", - options.server_key_bits); - } - - /* Do not display messages to stdout in RSA code. */ - rsa_set_verbose(0); - - /* Initialize the random number generator. */ - arc4random_stir(); - - /* Chdir to the root directory so that the current disk can be unmounted - if desired. */ - chdir("/"); - - /* Close connection cleanly after attack. */ - cipher_attack_detected = packet_disconnect; - - /* Start listening for a socket, unless started from inetd. */ - if (inetd_flag) - { - int s1, s2; - s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ - s2 = dup(s1); - sock_in = dup(0); - sock_out = dup(1); - /* 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. */ - debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); - - public_key = RSA_new(); - sensitive_data.private_key = RSA_new(); - /* Generate an rsa key. */ - log("Generating %d bit RSA key.", options.server_key_bits); - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - arc4random_stir(); - log("RSA key generation complete."); - } - else - { - /* Create socket for listening. */ - listen_sock = socket(AF_INET, SOCK_STREAM, 0); - if (listen_sock < 0) - fatal("socket: %.100s", strerror(errno)); - - /* Set socket options. We try to make the port reusable and have it - close as fast as possible without waiting in unnecessary wait states - on close. */ - setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, - sizeof(on)); - linger.l_onoff = 1; - linger.l_linger = 5; - setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (void *)&linger, - sizeof(linger)); - - /* Initialize the socket address. */ - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr = options.listen_addr; - sin.sin_port = htons(options.port); - - /* Bind the socket to the desired port. */ - if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) - { - error("bind: %.100s", strerror(errno)); - shutdown(listen_sock, SHUT_RDWR); - close(listen_sock); - fatal("Bind to port %d failed.", options.port); } - - if (!debug_flag) - { - /* Record our pid in /etc/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(SSH_DAEMON_PID_FILE, "w"); - if (f) - { - fprintf(f, "%u\n", (unsigned int)getpid()); - fclose(f); - } + /* Reinitialize the log (because of the fork above). */ + log_init(av0, options.log_level, options.log_facility, log_stderr); + + /* 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.host_key->n) - SSH_KEY_BITS_RESERVED && + options.server_key_bits < + BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { + options.server_key_bits = + BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; + debug("Forcing server key to %d bits to make it differ from host key.", + options.server_key_bits); } - - /* Start listening on the port. */ - log("Server listening on port %d.", options.port); - if (listen(listen_sock, 5) < 0) - fatal("listen: %.100s", strerror(errno)); - - public_key = RSA_new(); - sensitive_data.private_key = RSA_new(); - /* Generate an rsa key. */ - log("Generating %d bit RSA key.", options.server_key_bits); - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - arc4random_stir(); - log("RSA key generation complete."); - - /* Schedule server key regeneration alarm. */ - signal(SIGALRM, key_regeneration_alarm); - alarm(options.key_regeneration_time); - - /* 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); - - /* Stay listening for connections until the system crashes or the - daemon is killed with a signal. */ - for (;;) - { - if (received_sighup) - sighup_restart(); - /* Wait in accept until there is a connection. */ - aux = sizeof(sin); - newsock = accept(listen_sock, (struct sockaddr *)&sin, &aux); - if (received_sighup) - sighup_restart(); - if (newsock < 0) - { - if (errno == EINTR) - continue; - error("accept: %.100s", strerror(errno)); - continue; - } - - /* 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_sock); - sock_in = newsock; - sock_out = newsock; - pid = getpid(); - 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 socket, and start using - the accepted socket. Reinitialize logging (since our - pid has changed). We break out of the loop to handle - the connection. */ - close(listen_sock); - sock_in = newsock; - sock_out = newsock; - log_init(av0, options.log_level, options.log_facility, log_stderr); - break; + /* Do not display messages to stdout in RSA code. */ + rsa_set_verbose(0); + + /* Initialize the random number generator. */ + arc4random_stir(); + + /* Chdir to the root directory so that the current disk can be + unmounted if desired. */ + chdir("/"); + + /* Close connection cleanly after attack. */ + cipher_attack_detected = packet_disconnect; + + /* Start listening for a socket, unless started from inetd. */ + if (inetd_flag) { + int s1, s2; + s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ + s2 = dup(s1); + sock_in = dup(0); + sock_out = dup(1); + /* 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. */ + debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); + + public_key = RSA_new(); + sensitive_data.private_key = RSA_new(); + + log("Generating %d bit RSA key.", options.server_key_bits); + rsa_generate_key(sensitive_data.private_key, public_key, + options.server_key_bits); + arc4random_stir(); + log("RSA key generation complete."); + } else { + /* Create socket for listening. */ + listen_sock = socket(AF_INET, SOCK_STREAM, 0); + if (listen_sock < 0) + fatal("socket: %.100s", strerror(errno)); + + /* Set socket options. We try to make the port reusable + and have it close as fast as possible without waiting + in unnecessary wait states on close. */ + setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, + sizeof(on)); + linger.l_onoff = 1; + linger.l_linger = 5; + setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (void *) &linger, + sizeof(linger)); + + /* Initialize the socket address. */ + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr = options.listen_addr; + sin.sin_port = htons(options.port); + + /* Bind the socket to the desired port. */ + if (bind(listen_sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) { + error("bind: %.100s", strerror(errno)); + shutdown(listen_sock, SHUT_RDWR); + close(listen_sock); + fatal("Bind to port %d failed.", options.port); + } + if (!debug_flag) { + /* Record our pid in /etc/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(SSH_DAEMON_PID_FILE, "w"); + if (f) { + fprintf(f, "%u\n", (unsigned int) getpid()); + fclose(f); + } } - } - /* Parent. Stay in the loop. */ - if (pid < 0) - error("fork: %.100s", strerror(errno)); - else - debug("Forked child %d.", pid); + log("Server listening on port %d.", options.port); + if (listen(listen_sock, 5) < 0) + fatal("listen: %.100s", strerror(errno)); + + public_key = RSA_new(); + sensitive_data.private_key = RSA_new(); + + log("Generating %d bit RSA key.", options.server_key_bits); + rsa_generate_key(sensitive_data.private_key, public_key, + options.server_key_bits); + arc4random_stir(); + log("RSA key generation complete."); + + /* Schedule server key regeneration alarm. */ + signal(SIGALRM, key_regeneration_alarm); + alarm(options.key_regeneration_time); + + /* 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); + + /* Stay listening for connections until the system crashes + or the daemon is killed with a signal. */ + for (;;) { + if (received_sighup) + sighup_restart(); + /* Wait in accept until there is a connection. */ + aux = sizeof(sin); + newsock = accept(listen_sock, (struct sockaddr *) & sin, &aux); + if (received_sighup) + sighup_restart(); + if (newsock < 0) { + if (errno == EINTR) + continue; + error("accept: %.100s", strerror(errno)); + continue; + } + /* 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_sock); + sock_in = newsock; + sock_out = newsock; + pid = getpid(); + 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 + socket, and start using the + accepted socket. Reinitialize + logging (since our pid has + changed). We break out of the + loop to handle the connection. */ + close(listen_sock); + sock_in = newsock; + sock_out = newsock; + log_init(av0, options.log_level, options.log_facility, log_stderr); + break; + } + } + + /* Parent. Stay in the loop. */ + if (pid < 0) + error("fork: %.100s", strerror(errno)); + else + debug("Forked child %d.", pid); + + /* Mark that the key has been used (it was "given" to the child). */ + key_used = 1; + + arc4random_stir(); + + /* Close the new socket (the child is now taking care of it). */ + close(newsock); + } + } - /* Mark that the key has been used (it was "given" to the child). */ - key_used = 1; + /* This is the child processing a new connection. */ + + /* Disable the key regeneration alarm. We will not regenerate the + key since we are no longer in a position to give it to anyone. + We will not restart on SIGHUP since it no longer makes sense. */ + alarm(0); + signal(SIGALRM, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + + /* Set socket options for the connection. We want the socket to + close as fast as possible without waiting for anything. If the + connection is not a socket, these will do nothing. */ + /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, + sizeof(on)); */ + linger.l_onoff = 1; + linger.l_linger = 5; + setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); + + /* Register our connection. This turns encryption off because we + do not have a key. */ + packet_set_connection(sock_in, sock_out); + + remote_port = get_remote_port(); + remote_ip = get_remote_ipaddr(); + + /* Check whether logins are denied from this host. */ +#ifdef LIBWRAP + { + struct request_info req; - arc4random_stir(); + request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); + fromhost(&req); - /* Close the new socket (the child is now taking care of it). */ - close(newsock); + if (!hosts_access(&req)) { + close(sock_in); + close(sock_out); + refuse(&req); + } + verbose("Connection from %.500s port %d", eval_client(&req), remote_port); } - } - - /* This is the child processing a new connection. */ - - /* Disable the key regeneration alarm. We will not regenerate the key - since we are no longer in a position to give it to anyone. We will - not restart on SIGHUP since it no longer makes sense. */ - alarm(0); - signal(SIGALRM, SIG_DFL); - signal(SIGHUP, SIG_DFL); - signal(SIGTERM, SIG_DFL); - signal(SIGQUIT, SIG_DFL); - signal(SIGCHLD, SIG_DFL); - - /* Set socket options for the connection. We want the socket to close - as fast as possible without waiting for anything. If the connection - is not a socket, these will do nothing. */ - /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ - linger.l_onoff = 1; - linger.l_linger = 5; - setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); - - /* Register our connection. This turns encryption off because we do not - have a key. */ - packet_set_connection(sock_in, sock_out); - - remote_port = get_remote_port(); - - /* Check whether logins are denied from this host. */ -#ifdef LIBWRAP - { - struct request_info req; - - request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); - fromhost(&req); - - if (!hosts_access(&req)) { - close(sock_in); - close(sock_out); - refuse(&req); - } - log("Connection from %.500s port %d", eval_client(&req), remote_port); - } #else - /* Log the connection. */ - log("Connection from %.100s port %d", get_remote_ipaddr(), remote_port); + /* Log the connection. */ + verbose("Connection from %.500s port %d", remote_ip, remote_port); #endif /* LIBWRAP */ - /* We don\'t want to listen forever unless the other side successfully - authenticates itself. So we set up an alarm which is cleared after - successful authentication. A limit of zero indicates no limit. - Note that we don\'t set the alarm in debugging mode; it is just annoying - to have the server exit just when you are about to discover the bug. */ - signal(SIGALRM, grace_alarm_handler); - if (!debug_flag) - alarm(options.login_grace_time); - - /* Send our protocol version identification. */ - snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", - PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION); - if (write(sock_out, buf, strlen(buf)) != strlen(buf)) - fatal("Could not write ident string."); - - /* Read other side\'s version identification. */ - for (i = 0; i < sizeof(buf) - 1; i++) - { - if (read(sock_in, &buf[i], 1) != 1) - fatal("Did not receive ident string."); - if (buf[i] == '\r') - { - buf[i] = '\n'; - buf[i + 1] = 0; - break; + /* We don\'t want to listen forever unless the other side + successfully authenticates itself. So we set up an alarm which + is cleared after successful authentication. A limit of zero + indicates no limit. Note that we don\'t set the alarm in + debugging mode; it is just annoying to have the server exit + just when you are about to discover the bug. */ + signal(SIGALRM, grace_alarm_handler); + if (!debug_flag) + alarm(options.login_grace_time); + + if (client_version_string != NULL) { + /* we are exec'ed by sshd2, so skip exchange of protocol version */ + strlcpy(buf, client_version_string, sizeof(buf)); + } else { + /* Send our protocol version identification. */ + snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", + PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION); + if (write(sock_out, buf, strlen(buf)) != strlen(buf)) + fatal("Could not write ident string to %s.", get_remote_ipaddr()); + + /* Read other side\'s version identification. */ + for (i = 0; i < sizeof(buf) - 1; i++) { + if (read(sock_in, &buf[i], 1) != 1) + fatal("Did not receive ident string from %s.", get_remote_ipaddr()); + if (buf[i] == '\r') { + buf[i] = '\n'; + buf[i + 1] = 0; + break; + } + if (buf[i] == '\n') { + /* buf[i] == '\n' */ + buf[i + 1] = 0; + break; + } + } + buf[sizeof(buf) - 1] = 0; } - if (buf[i] == '\n') - { - /* buf[i] == '\n' */ - buf[i + 1] = 0; - break; + + /* Check that the versions match. In future this might accept + several versions and set appropriate flags to handle them. */ + if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, + remote_version) != 3) { + const char *s = "Protocol mismatch.\n"; + (void) write(sock_out, s, strlen(s)); + close(sock_in); + close(sock_out); + fatal("Bad protocol version identification '%.100s' from %s", + buf, get_remote_ipaddr()); + } + debug("Client protocol version %d.%d; client software version %.100s", + remote_major, remote_minor, remote_version); + if (remote_major != PROTOCOL_MAJOR) { + const char *s = "Protocol major versions differ.\n"; + (void) write(sock_out, s, strlen(s)); + close(sock_in); + close(sock_out); + fatal("Protocol major versions differ for %s: %d vs. %d", + get_remote_ipaddr(), + PROTOCOL_MAJOR, remote_major); } - } - buf[sizeof(buf) - 1] = 0; - - /* Check that the versions match. In future this might accept several - versions and set appropriate flags to handle them. */ - if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, - remote_version) != 3) - { - const char *s = "Protocol mismatch.\n"; - (void) write(sock_out, s, strlen(s)); - close(sock_in); - close(sock_out); - fatal("Bad protocol version identification: %.100s", buf); - } - debug("Client protocol version %d.%d; client software version %.100s", - remote_major, remote_minor, remote_version); - if (remote_major != PROTOCOL_MAJOR) - { - const char *s = "Protocol major versions differ.\n"; - (void) write(sock_out, s, strlen(s)); - close(sock_in); - close(sock_out); - fatal("Protocol major versions differ: %d vs. %d", - PROTOCOL_MAJOR, remote_major); - } - - /* Check that the client has sufficiently high software version. */ - if (remote_major == 1 && remote_minor < 3) - packet_disconnect("Your ssh version is too old and is no longer supported. Please install a newer version."); - - if (remote_major == 1 && remote_minor == 3) { - enable_compat13(); - if (strcmp(remote_version, "OpenSSH-1.1") != 0) { - debug("Agent forwarding disabled, remote version is not compatible."); - no_agent_forwarding_flag = 1; - } - } - - /* Check that the connection comes from a privileged port. - Rhosts- and Rhosts-RSA-Authentication only make sense - from priviledged programs. - Of course, if the intruder has root access on his local machine, - he can connect from any port. So do not use these authentication - methods from machines that you do not trust. */ - if (remote_port >= IPPORT_RESERVED || - remote_port < IPPORT_RESERVED / 2) - { - options.rhosts_authentication = 0; - options.rhosts_rsa_authentication = 0; - } - - packet_set_nonblocking(); - - /* Handle the connection. */ - do_connection(); + /* Check that the client has sufficiently high software version. */ + if (remote_major == 1 && remote_minor < 3) + packet_disconnect("Your ssh version is too old and is no longer supported. Please install a newer version."); + + if (remote_major == 1 && remote_minor == 3) { + enable_compat13(); + if (strcmp(remote_version, "OpenSSH-1.1") != 0) { + debug("Agent forwarding disabled, remote version is not compatible."); + no_agent_forwarding_flag = 1; + } + } + /* Check that the connection comes from a privileged port. Rhosts- + and Rhosts-RSA-Authentication only make sense from priviledged + programs. Of course, if the intruder has root access on his + local machine, he can connect from any port. So do not use + these authentication methods from machines that you do not trust. */ + if (remote_port >= IPPORT_RESERVED || + remote_port < IPPORT_RESERVED / 2) { + options.rhosts_authentication = 0; + options.rhosts_rsa_authentication = 0; + } + packet_set_nonblocking(); + + /* Handle the connection. */ + do_connection(); #ifdef KRB4 - /* Cleanup user's ticket cache file. */ - if (options.kerberos_ticket_cleanup) - (void) dest_tkt(); + /* Cleanup user's ticket cache file. */ + if (options.kerberos_ticket_cleanup) + (void) dest_tkt(); #endif /* KRB4 */ - /* Cleanup user's local Xauthority file. */ - if (xauthfile) unlink(xauthfile); + /* Cleanup user's local Xauthority file. */ + if (xauthfile) + unlink(xauthfile); - /* The connection has been terminated. */ - log("Closing connection to %.100s", inet_ntoa(sin.sin_addr)); + /* The connection has been terminated. */ + verbose("Closing connection to %.100s", remote_ip); #ifdef HAVE_LIBPAM - { - int retval; - - if (pamh != NULL) - { - debug("Closing PAM session."); - retval = pam_close_session((pam_handle_t *)pamh, 0); - - debug("Terminating PAM library."); - if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS) - log("Cannot release PAM authentication."); - - fatal_remove_cleanup(&pam_cleanup_proc, NULL); - } - } -#endif /* HAVE_LIBPAM */ + { + int retval; - packet_close(); + if (pamh != NULL) { + debug("Closing PAM session."); + retval = pam_close_session((pam_handle_t *)pamh, 0); - exit(0); -} + debug("Terminating PAM library."); + if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS) + log("Cannot release PAM authentication."); + + fatal_remove_cleanup(&pam_cleanup_proc, NULL); + } + } +#endif /* HAVE_LIBPAM */ -/* Process an incoming connection. Protocol version identifiers have already - been exchanged. This sends server key and performs the key exchange. - Server and host keys will no longer be needed after this functions. */ + packet_close(); + exit(0); +} +/* + * Process an incoming connection. Protocol version identifiers have already + * been exchanged. This sends server key and performs the key exchange. + * Server and host keys will no longer be needed after this functions. + */ void do_connection() { - int i, len; - BIGNUM *session_key_int; - unsigned char session_key[SSH_SESSION_KEY_LENGTH]; - unsigned char check_bytes[8]; - char *user; - unsigned int cipher_type, auth_mask, protocol_flags; - int plen, slen; - u_int32_t rand = 0; - - /* Generate check bytes that the client must send back in the user packet - in order for it to be accepted; this is used to defy ip spoofing - attacks. Note that this only works against somebody doing IP spoofing - from a remote machine; any machine on the local network can still see - outgoing packets and catch the random 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) - rand = arc4random(); - check_bytes[i] = rand & 0xff; - rand >>= 8; - } - - /* Send our public key. We include in the packet 64 bits of random - data that must be matched in the reply in order to prevent IP spoofing. */ - packet_start(SSH_SMSG_PUBLIC_KEY); - for (i = 0; i < 8; i++) - packet_put_char(check_bytes[i]); - - /* Store our public server RSA key. */ - packet_put_int(BN_num_bits(public_key->n)); - packet_put_bignum(public_key->e); - packet_put_bignum(public_key->n); - - /* Store our public host RSA key. */ - packet_put_int(BN_num_bits(sensitive_data.host_key->n)); - packet_put_bignum(sensitive_data.host_key->e); - packet_put_bignum(sensitive_data.host_key->n); - - /* Put protocol flags. */ - packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); - - /* Declare which ciphers we support. */ - packet_put_int(cipher_mask()); - - /* Declare supported authentication types. */ - auth_mask = 0; - if (options.rhosts_authentication) - auth_mask |= 1 << SSH_AUTH_RHOSTS; - if (options.rhosts_rsa_authentication) - auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; - if (options.rsa_authentication) - auth_mask |= 1 << SSH_AUTH_RSA; + int i, len; + BIGNUM *session_key_int; + unsigned char session_key[SSH_SESSION_KEY_LENGTH]; + unsigned char check_bytes[8]; + char *user; + unsigned int cipher_type, auth_mask, protocol_flags; + int plen, slen; + u_int32_t rand = 0; + + /* Generate check bytes that the client must send back in the user + packet in order for it to be accepted; this is used to defy ip + spoofing attacks. Note that this only works against somebody + doing IP spoofing from a remote machine; any machine on the + local network can still see outgoing packets and catch the + random 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) + rand = arc4random(); + check_bytes[i] = rand & 0xff; + rand >>= 8; + } + + /* Send our public key. We include in the packet 64 bits of + random data that must be matched in the reply in order to + prevent IP spoofing. */ + packet_start(SSH_SMSG_PUBLIC_KEY); + for (i = 0; i < 8; i++) + packet_put_char(check_bytes[i]); + + /* Store our public server RSA key. */ + packet_put_int(BN_num_bits(public_key->n)); + packet_put_bignum(public_key->e); + packet_put_bignum(public_key->n); + + /* Store our public host RSA key. */ + packet_put_int(BN_num_bits(sensitive_data.host_key->n)); + packet_put_bignum(sensitive_data.host_key->e); + packet_put_bignum(sensitive_data.host_key->n); + + /* Put protocol flags. */ + packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); + + /* Declare which ciphers we support. */ + packet_put_int(cipher_mask()); + + /* Declare supported authentication types. */ + auth_mask = 0; + if (options.rhosts_authentication) + auth_mask |= 1 << SSH_AUTH_RHOSTS; + if (options.rhosts_rsa_authentication) + auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; + if (options.rsa_authentication) + auth_mask |= 1 << SSH_AUTH_RSA; #ifdef KRB4 - if (options.kerberos_authentication) - auth_mask |= 1 << SSH_AUTH_KERBEROS; + if (options.kerberos_authentication) + auth_mask |= 1 << SSH_AUTH_KERBEROS; #endif #ifdef AFS - if (options.kerberos_tgt_passing) - auth_mask |= 1 << SSH_PASS_KERBEROS_TGT; - if (options.afs_token_passing) - auth_mask |= 1 << SSH_PASS_AFS_TOKEN; + if (options.kerberos_tgt_passing) + auth_mask |= 1 << SSH_PASS_KERBEROS_TGT; + if (options.afs_token_passing) + auth_mask |= 1 << SSH_PASS_AFS_TOKEN; #endif - if (options.password_authentication) - auth_mask |= 1 << SSH_AUTH_PASSWORD; - packet_put_int(auth_mask); - - /* Send the packet and wait for it to be sent. */ - packet_send(); - packet_write_wait(); - - debug("Sent %d bit public key and %d bit host key.", - BN_num_bits(public_key->n), BN_num_bits(sensitive_data.host_key->n)); - - /* Read clients reply (cipher type and session key). */ - packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); - - /* Get cipher type. */ - cipher_type = packet_get_char(); - - /* Get check bytes from the packet. These must match those we sent earlier - with the public key packet. */ - for (i = 0; i < 8; i++) - if (check_bytes[i] != packet_get_char()) - packet_disconnect("IP Spoofing check bytes do not match."); - - debug("Encryption type: %.200s", cipher_name(cipher_type)); - - /* Get the encrypted integer. */ - session_key_int = BN_new(); - packet_get_bignum(session_key_int, &slen); - - /* Get protocol flags. */ - protocol_flags = packet_get_int(); - packet_set_protocol_flags(protocol_flags); - - packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); - - /* Decrypt it using our private server key and private host key (key with - larger modulus first). */ - if (BN_cmp(sensitive_data.private_key->n, sensitive_data.host_key->n) > 0) - { - /* Private key has bigger modulus. */ - if (BN_num_bits(sensitive_data.private_key->n) < - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { - fatal("do_connection: private_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", - BN_num_bits(sensitive_data.private_key->n), - BN_num_bits(sensitive_data.host_key->n), - SSH_KEY_BITS_RESERVED); - } - - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.private_key); - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.host_key); - } - else - { - /* Host key has bigger modulus (or they are equal). */ - if (BN_num_bits(sensitive_data.host_key->n) < - BN_num_bits(sensitive_data.private_key->n) + SSH_KEY_BITS_RESERVED) { - fatal("do_connection: host_key %d < private_key %d + SSH_KEY_BITS_RESERVED %d", - BN_num_bits(sensitive_data.host_key->n), - BN_num_bits(sensitive_data.private_key->n), - SSH_KEY_BITS_RESERVED); - } - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.host_key); - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.private_key); - } - - /* Compute session id for this session. */ - compute_session_id(session_id, check_bytes, - sensitive_data.host_key->n, - sensitive_data.private_key->n); - - /* Extract session key from the decrypted integer. The key is in the - least significant 256 bits of the integer; the first byte of the - key is in the highest bits. */ - BN_mask_bits(session_key_int, sizeof(session_key) * 8); - len = BN_num_bytes(session_key_int); - if (len < 0 || len > sizeof(session_key)) - fatal("do_connection: bad len: session_key_int %d > sizeof(session_key) %d", - len, sizeof(session_key)); - memset(session_key, 0, sizeof(session_key)); - BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); - - /* Xor the first 16 bytes of the session key with the session id. */ - for (i = 0; i < 16; i++) - session_key[i] ^= session_id[i]; - - /* Destroy the decrypted integer. It is no longer needed. */ - BN_clear_free(session_key_int); - - /* Set the session key. From this on all communications will be - encrypted. */ - packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); - - /* Destroy our copy of the session key. It is no longer needed. */ - memset(session_key, 0, sizeof(session_key)); - - debug("Received session key; encryption turned on."); - - /* Send an acknowledgement packet. Note that this packet is sent - encrypted. */ - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); - - /* Get the name of the user that we wish to log in as. */ - packet_read_expect(&plen, SSH_CMSG_USER); - - /* Get the user name. */ - { - int ulen; - user = packet_get_string(&ulen); - packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); - } - - /* Destroy the private and public keys. They will no longer be needed. */ - RSA_free(public_key); - RSA_free(sensitive_data.private_key); - RSA_free(sensitive_data.host_key); - - setproctitle("%s", user); - /* Do the authentication. */ - do_authentication(user); -} +#ifdef SKEY + if (options.skey_authentication == 1) + auth_mask |= 1 << SSH_AUTH_TIS; +#endif + if (options.password_authentication) + auth_mask |= 1 << SSH_AUTH_PASSWORD; + packet_put_int(auth_mask); + + /* Send the packet and wait for it to be sent. */ + packet_send(); + packet_write_wait(); + + debug("Sent %d bit public key and %d bit host key.", + BN_num_bits(public_key->n), BN_num_bits(sensitive_data.host_key->n)); + + /* Read clients reply (cipher type and session key). */ + packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); + + /* Get cipher type. */ + cipher_type = packet_get_char(); + + /* Get check bytes from the packet. These must match those we + sent earlier with the public key packet. */ + for (i = 0; i < 8; i++) + if (check_bytes[i] != packet_get_char()) + packet_disconnect("IP Spoofing check bytes do not match."); + + debug("Encryption type: %.200s", cipher_name(cipher_type)); + + /* Get the encrypted integer. */ + session_key_int = BN_new(); + packet_get_bignum(session_key_int, &slen); + + /* Get protocol flags. */ + protocol_flags = packet_get_int(); + packet_set_protocol_flags(protocol_flags); + + packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); + + /* Decrypt it using our private server key and private host key + (key with larger modulus first). */ + if (BN_cmp(sensitive_data.private_key->n, sensitive_data.host_key->n) > 0) { + /* Private key has bigger modulus. */ + if (BN_num_bits(sensitive_data.private_key->n) < + BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { + fatal("do_connection: %s: private_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", + get_remote_ipaddr(), + BN_num_bits(sensitive_data.private_key->n), + BN_num_bits(sensitive_data.host_key->n), + SSH_KEY_BITS_RESERVED); + } + rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.private_key); + rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.host_key); + } else { + /* Host key has bigger modulus (or they are equal). */ + if (BN_num_bits(sensitive_data.host_key->n) < + BN_num_bits(sensitive_data.private_key->n) + SSH_KEY_BITS_RESERVED) { + fatal("do_connection: %s: host_key %d < private_key %d + SSH_KEY_BITS_RESERVED %d", + get_remote_ipaddr(), + BN_num_bits(sensitive_data.host_key->n), + BN_num_bits(sensitive_data.private_key->n), + SSH_KEY_BITS_RESERVED); + } + rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.host_key); + rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.private_key); + } + + /* Compute session id for this session. */ + compute_session_id(session_id, check_bytes, + sensitive_data.host_key->n, + sensitive_data.private_key->n); + + /* Extract session key from the decrypted integer. The key is in + the least significant 256 bits of the integer; the first byte + of the key is in the highest bits. */ + BN_mask_bits(session_key_int, sizeof(session_key) * 8); + len = BN_num_bytes(session_key_int); + if (len < 0 || len > sizeof(session_key)) + fatal("do_connection: bad len from %s: session_key_int %d > sizeof(session_key) %d", + get_remote_ipaddr(), + len, sizeof(session_key)); + memset(session_key, 0, sizeof(session_key)); + BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); + + /* Xor the first 16 bytes of the session key with the session id. */ + for (i = 0; i < 16; i++) + session_key[i] ^= session_id[i]; + + /* Destroy the decrypted integer. It is no longer needed. */ + BN_clear_free(session_key_int); + + /* Set the session key. From this on all communications will be encrypted. */ + packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); + + /* Destroy our copy of the session key. It is no longer needed. */ + memset(session_key, 0, sizeof(session_key)); + + debug("Received session key; encryption turned on."); + + /* Send an acknowledgement packet. Note that this packet is sent encrypted. */ + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); + + /* Get the name of the user that we wish to log in as. */ + packet_read_expect(&plen, SSH_CMSG_USER); + + /* Get the user name. */ + { + int ulen; + user = packet_get_string(&ulen); + packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); + } + + /* Destroy the private and public keys. They will no longer be needed. */ + RSA_free(public_key); + RSA_free(sensitive_data.private_key); + RSA_free(sensitive_data.host_key); -/* Check if the user is allowed to log in via ssh. If user is listed in - DenyUsers or user's primary group is listed in DenyGroups, false will - be returned. If AllowUsers isn't empty and user isn't listed there, or - if AllowGroups isn't empty and user isn't listed there, false will be - returned. Otherwise true is returned. - XXX This function should also check if user has a valid shell */ + setproctitle("%s", user); + /* Do the authentication. */ + do_authentication(user); +} +/* + * Check if the user is allowed to log in via ssh. If user is listed in + * DenyUsers or user's primary group is listed in DenyGroups, false will + * be returned. If AllowUsers isn't empty and user isn't listed there, or + * if AllowGroups isn't empty and user isn't listed there, false will be + * returned. Otherwise true is returned. + * XXX This function should also check if user has a valid shell + */ static int -allowed_user(struct passwd *pw) +allowed_user(struct passwd * pw) { - struct group *grp; - int i; - - /* Shouldn't be called if pw is NULL, but better safe than sorry... */ - if (!pw) - return 0; - - /* XXX Should check for valid login shell */ - - /* Return false if user is listed in DenyUsers */ - if (options.num_deny_users > 0) - { - if (!pw->pw_name) - return 0; - for (i = 0; i < options.num_deny_users; i++) - if (match_pattern(pw->pw_name, options.deny_users[i])) - return 0; - } - - /* Return false if AllowUsers isn't empty and user isn't listed there */ - if (options.num_allow_users > 0) - { - if (!pw->pw_name) - return 0; - for (i = 0; i < options.num_allow_users; i++) - if (match_pattern(pw->pw_name, options.allow_users[i])) - break; - /* i < options.num_allow_users iff we break for loop */ - if (i >= options.num_allow_users) - return 0; - } - - /* Get the primary group name if we need it. Return false if it fails */ - if (options.num_deny_groups > 0 || options.num_allow_groups > 0 ) - { - grp = getgrgid(pw->pw_gid); - if (!grp) - return 0; - - /* Return false if user's group is listed in DenyGroups */ - if (options.num_deny_groups > 0) - { - if (!grp->gr_name) - return 0; - for (i = 0; i < options.num_deny_groups; i++) - if (match_pattern(grp->gr_name, options.deny_groups[i])) - return 0; - } - - /* Return false if AllowGroups isn't empty and user's group isn't - listed there */ - if (options.num_allow_groups > 0) - { - if (!grp->gr_name) - return 0; - for (i = 0; i < options.num_allow_groups; i++) - if (match_pattern(grp->gr_name, options.allow_groups[i])) - break; - /* i < options.num_allow_groups iff we break for loop */ - if (i >= options.num_allow_groups) - return 0; - } - } - - /* We found no reason not to let this user try to log on... */ - return 1; + struct group *grp; + int i; + + /* Shouldn't be called if pw is NULL, but better safe than sorry... */ + if (!pw) + return 0; + + /* XXX Should check for valid login shell */ + + /* Return false if user is listed in DenyUsers */ + if (options.num_deny_users > 0) { + if (!pw->pw_name) + return 0; + for (i = 0; i < options.num_deny_users; i++) + if (match_pattern(pw->pw_name, options.deny_users[i])) + return 0; + } + /* Return false if AllowUsers isn't empty and user isn't listed + there */ + if (options.num_allow_users > 0) { + if (!pw->pw_name) + return 0; + for (i = 0; i < options.num_allow_users; i++) + if (match_pattern(pw->pw_name, options.allow_users[i])) + break; + /* i < options.num_allow_users iff we break for loop */ + if (i >= options.num_allow_users) + return 0; + } + /* Get the primary group name if we need it. Return false if it fails */ + if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { + grp = getgrgid(pw->pw_gid); + if (!grp) + return 0; + + /* Return false if user's group is listed in DenyGroups */ + if (options.num_deny_groups > 0) { + if (!grp->gr_name) + return 0; + for (i = 0; i < options.num_deny_groups; i++) + if (match_pattern(grp->gr_name, options.deny_groups[i])) + return 0; + } + /* Return false if AllowGroups isn't empty and user's + group isn't listed there */ + if (options.num_allow_groups > 0) { + if (!grp->gr_name) + return 0; + for (i = 0; i < options.num_allow_groups; i++) + if (match_pattern(grp->gr_name, options.allow_groups[i])) + break; + /* i < options.num_allow_groups iff we break for + loop */ + if (i >= options.num_allow_groups) + return 0; + } + } + /* We found no reason not to let this user try to log on... */ + return 1; } -/* Performs authentication of an incoming connection. Session key has already - been exchanged and encryption is enabled. User is the user name to log - in as (received from the client). */ - +/* + * Performs authentication of an incoming connection. Session key has already + * been exchanged and encryption is enabled. User is the user name to log + * in as (received from the client). + */ void do_authentication(char *user) { - struct passwd *pw, pwcopy; + struct passwd *pw, pwcopy; #ifdef AFS - /* If machine has AFS, set process authentication group. */ - if (k_hasafs()) { - k_setpag(); - k_unlog(); - } + /* If machine has AFS, set process authentication group. */ + if (k_hasafs()) { + k_setpag(); + k_unlog(); + } #endif /* AFS */ - - /* Verify that the user is a valid user. */ - pw = getpwnam(user); - if (!pw || !allowed_user(pw)) - do_fake_authloop(user); - - /* Take a copy of the returned structure. */ - memset(&pwcopy, 0, sizeof(pwcopy)); - pwcopy.pw_name = xstrdup(pw->pw_name); - pwcopy.pw_passwd = xstrdup(pw->pw_passwd); - pwcopy.pw_uid = pw->pw_uid; - pwcopy.pw_gid = pw->pw_gid; - pwcopy.pw_dir = xstrdup(pw->pw_dir); - pwcopy.pw_shell = xstrdup(pw->pw_shell); - pw = &pwcopy; + + /* Verify that the user is a valid user. */ + pw = getpwnam(user); + if (!pw || !allowed_user(pw)) + do_fake_authloop(user); + + /* Take a copy of the returned structure. */ + memset(&pwcopy, 0, sizeof(pwcopy)); + pwcopy.pw_name = xstrdup(pw->pw_name); + pwcopy.pw_passwd = xstrdup(pw->pw_passwd); + pwcopy.pw_uid = pw->pw_uid; + pwcopy.pw_gid = pw->pw_gid; + pwcopy.pw_dir = xstrdup(pw->pw_dir); + pwcopy.pw_shell = xstrdup(pw->pw_shell); + pw = &pwcopy; #ifdef HAVE_LIBPAM - { - int pam_retval; - - debug("Starting up PAM with username \"%.200s\"", pw->pw_name); + { + int pam_retval; + + debug("Starting up PAM with username \"%.200s\"", pw->pw_name); - pam_retval = pam_start("sshd", pw->pw_name, &conv, (pam_handle_t**)&pamh); - if (pam_retval != PAM_SUCCESS) - fatal("PAM initialisation failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); + pam_retval = pam_start("sshd", pw->pw_name, &conv, (pam_handle_t**)&pamh); + if (pam_retval != PAM_SUCCESS) + fatal("PAM initialisation failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - fatal_add_cleanup(&pam_cleanup_proc, NULL); - } + fatal_add_cleanup(&pam_cleanup_proc, NULL); + } #endif - /* If we are not running as root, the user must have the same uid as the - server. */ - if (getuid() != 0 && pw->pw_uid != getuid()) - packet_disconnect("Cannot change user when server not running as root."); + /* If we are not running as root, the user must have the same uid + as the server. */ + if (getuid() != 0 && pw->pw_uid != getuid()) + packet_disconnect("Cannot change user when server not running as root."); - debug("Attempting authentication for %.100s.", user); + debug("Attempting authentication for %.100s.", user); - /* If the user has no password, accept authentication immediately. */ - if (options.password_authentication && + /* If the user has no password, accept authentication immediately. */ + if (options.password_authentication && #ifdef KRB4 - (!options.kerberos_authentication || options.kerberos_or_local_passwd) && + (!options.kerberos_authentication || options.kerberos_or_local_passwd) && #endif /* KRB4 */ - auth_password(pw, "")) - { - /* Authentication with empty password succeeded. */ - debug("Login for user %.100s accepted without authentication.", user); - } else { - /* Loop until the user has been authenticated or the connection is closed, - do_authloop() returns only if authentication is successfull */ - do_authloop(pw); - } - - /* XXX log unified auth message */ - - /* Check if the user is logging in as root and root logins are disallowed. */ - if (pw->pw_uid == 0 && !options.permit_root_login) - { - if (forced_command) - log("Root login accepted for forced command."); - else - packet_disconnect("ROOT LOGIN REFUSED FROM %.200s", - get_canonical_hostname()); - } - - /* The user has been authenticated and accepted. */ - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); - - /* Perform session preparation. */ - do_authenticated(pw); + auth_password(pw, "")) { + /* Authentication with empty password succeeded. */ + log("Login for user %s from %.100s, accepted without authentication.", + pw->pw_name, get_remote_ipaddr()); + } else { + /* Loop until the user has been authenticated or the + connection is closed, do_authloop() returns only if + authentication is successfull */ + do_authloop(pw); + } + + /* Check if the user is logging in as root and root logins are disallowed. */ + if (pw->pw_uid == 0 && !options.permit_root_login) { + if (forced_command) + log("Root login accepted for forced command."); + else + packet_disconnect("ROOT LOGIN REFUSED FROM %.200s", + get_canonical_hostname()); + } + /* The user has been authenticated and accepted. */ + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); + + /* Perform session preparation. */ + do_authenticated(pw); } -#define MAX_AUTH_FAILURES 5 +#define AUTH_FAIL_MAX 6 +#define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2) +#define AUTH_FAIL_MSG "Too many authentication failures for %.100s" -/* read packets and try to authenticate local user *pw. - return if authentication is successfull */ +/* + * read packets and try to authenticate local user *pw. + * return if authentication is successfull + */ void -do_authloop(struct passwd *pw) +do_authloop(struct passwd * pw) { - int authentication_failures = 0; - unsigned int bits; - BIGNUM *client_host_key_e, *client_host_key_n; - BIGNUM *n; - char *client_user = NULL, *password = NULL; - int plen, dlen, nlen, ulen, elen; + int attempt = 0; + unsigned int bits; + BIGNUM *client_host_key_e, *client_host_key_n; + BIGNUM *n; + char *client_user = NULL, *password = NULL; + char user[1024]; + int plen, dlen, nlen, ulen, elen; + int type = 0; + void (*authlog) (const char *fmt,...) = verbose; #ifdef HAVE_LIBPAM - int pam_retval; + int pam_retval; #endif /* HAVE_LIBPAM */ - /* Indicate that authentication is needed. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); + /* Indicate that authentication is needed. */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); - for (;;) { - int authenticated = 0; + for (attempt = 1;; attempt++) { + int authenticated = 0; + strlcpy(user, "", sizeof user); - /* Get a packet from the client. */ - int type = packet_read(&plen); - - /* Process the packet. */ - switch (type) - { + /* Get a packet from the client. */ + type = packet_read(&plen); + + /* Process the packet. */ + switch (type) { #ifdef AFS - case SSH_CMSG_HAVE_KERBEROS_TGT: - if (!options.kerberos_tgt_passing) - { - /* packet_get_all(); */ - log("Kerberos tgt passing disabled."); - break; - } - else { - /* Accept Kerberos tgt. */ - char *tgt = packet_get_string(&dlen); - packet_integrity_check(plen, 4 + dlen, type); - if (!auth_kerberos_tgt(pw, tgt)) - debug("Kerberos tgt REFUSED for %s", pw->pw_name); - xfree(tgt); - } - continue; - - case SSH_CMSG_HAVE_AFS_TOKEN: - if (!options.afs_token_passing || !k_hasafs()) { - /* packet_get_all(); */ - log("AFS token passing disabled."); - break; - } - else { - /* Accept AFS token. */ - char *token_string = packet_get_string(&dlen); - packet_integrity_check(plen, 4 + dlen, type); - if (!auth_afs_token(pw, token_string)) - debug("AFS token REFUSED for %s", pw->pw_name); - xfree(token_string); - } - continue; + case SSH_CMSG_HAVE_KERBEROS_TGT: + if (!options.kerberos_tgt_passing) { + /* packet_get_all(); */ + verbose("Kerberos tgt passing disabled."); + break; + } else { + /* Accept Kerberos tgt. */ + char *tgt = packet_get_string(&dlen); + packet_integrity_check(plen, 4 + dlen, type); + if (!auth_kerberos_tgt(pw, tgt)) + verbose("Kerberos tgt REFUSED for %s", pw->pw_name); + xfree(tgt); + } + continue; + + case SSH_CMSG_HAVE_AFS_TOKEN: + if (!options.afs_token_passing || !k_hasafs()) { + /* packet_get_all(); */ + verbose("AFS token passing disabled."); + break; + } else { + /* Accept AFS token. */ + char *token_string = packet_get_string(&dlen); + packet_integrity_check(plen, 4 + dlen, type); + if (!auth_afs_token(pw, token_string)) + verbose("AFS token REFUSED for %s", pw->pw_name); + xfree(token_string); + } + continue; #endif /* AFS */ - #ifdef KRB4 - case SSH_CMSG_AUTH_KERBEROS: - if (!options.kerberos_authentication) - { - /* packet_get_all(); */ - log("Kerberos authentication disabled."); - break; - } - else { - /* Try Kerberos v4 authentication. */ - KTEXT_ST auth; - char *tkt_user = NULL; - char *kdata = packet_get_string((unsigned int *)&auth.length); - packet_integrity_check(plen, 4 + auth.length, type); - - if (auth.length < MAX_KTXT_LEN) - memcpy(auth.dat, kdata, auth.length); - xfree(kdata); - - authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user); - - log("Kerberos authentication %s%s for account %s from %s", - authenticated ? "accepted " : "failed", - tkt_user != NULL ? tkt_user : "", - pw->pw_name, get_canonical_hostname()); - if (authenticated) - xfree(tkt_user); - } - break; + case SSH_CMSG_AUTH_KERBEROS: + if (!options.kerberos_authentication) { + /* packet_get_all(); */ + verbose("Kerberos authentication disabled."); + break; + } else { + /* Try Kerberos v4 authentication. */ + KTEXT_ST auth; + char *tkt_user = NULL; + char *kdata = packet_get_string((unsigned int *) &auth.length); + packet_integrity_check(plen, 4 + auth.length, type); + + if (auth.length < MAX_KTXT_LEN) + memcpy(auth.dat, kdata, auth.length); + xfree(kdata); + + authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user); + + if (authenticated) { + snprintf(user, sizeof user, " tktuser %s", tkt_user); + xfree(tkt_user); + } + } + break; #endif /* KRB4 */ - - case SSH_CMSG_AUTH_RHOSTS: - if (!options.rhosts_authentication) - { - log("Rhosts authentication disabled."); - break; - } - - /* Get client user name. Note that we just have to trust the client; - this is one reason why rhosts authentication is insecure. - (Another is IP-spoofing on a local network.) */ - client_user = packet_get_string(&dlen); - packet_integrity_check(plen, 4 + dlen, type); - - /* Try to authenticate using /etc/hosts.equiv and .rhosts. */ - authenticated = auth_rhosts(pw, client_user); - - log("Rhosts authentication %s for %.100s, remote %.100s on %.700s.", - authenticated ? "accepted" : "failed", - pw->pw_name, client_user, get_canonical_hostname()); + + case SSH_CMSG_AUTH_RHOSTS: + if (!options.rhosts_authentication) { + verbose("Rhosts authentication disabled."); + break; + } + /* Get client user name. Note that we just have + to trust the client; this is one reason why + rhosts authentication is insecure. (Another is + IP-spoofing on a local network.) */ + client_user = packet_get_string(&ulen); + packet_integrity_check(plen, 4 + ulen, type); + + /* Try to authenticate using /etc/hosts.equiv and + .rhosts. */ + authenticated = auth_rhosts(pw, client_user); + + snprintf(user, sizeof user, " ruser %s", client_user); #ifndef HAVE_LIBPAM - xfree(client_user); + xfree(client_user); #endif /* HAVE_LIBPAM */ - break; - - case SSH_CMSG_AUTH_RHOSTS_RSA: - if (!options.rhosts_rsa_authentication) - { - log("Rhosts with RSA authentication disabled."); - break; - } - - /* Get client user name. Note that we just have to trust - the client; root on the client machine can claim to be - any user. */ - client_user = packet_get_string(&ulen); - - /* Get the client host key. */ - client_host_key_e = BN_new(); - client_host_key_n = BN_new(); - bits = packet_get_int(); - packet_get_bignum(client_host_key_e, &elen); - packet_get_bignum(client_host_key_n, &nlen); - - if (bits != BN_num_bits(client_host_key_n)) - error("Warning: keysize mismatch for client_host_key: " - "actual %d, announced %d", BN_num_bits(client_host_key_n), bits); - packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type); - - authenticated = auth_rhosts_rsa(pw, client_user, - client_host_key_e, client_host_key_n); - log("Rhosts authentication %s for %.100s, remote %.100s.", - authenticated ? "accepted" : "failed", - pw->pw_name, client_user); + break; + + case SSH_CMSG_AUTH_RHOSTS_RSA: + if (!options.rhosts_rsa_authentication) { + verbose("Rhosts with RSA authentication disabled."); + break; + } + /* Get client user name. Note that we just have + to trust the client; root on the client machine + can claim to be any user. */ + client_user = packet_get_string(&ulen); + + /* Get the client host key. */ + client_host_key_e = BN_new(); + client_host_key_n = BN_new(); + bits = packet_get_int(); + packet_get_bignum(client_host_key_e, &elen); + packet_get_bignum(client_host_key_n, &nlen); + + if (bits != BN_num_bits(client_host_key_n)) + error("Warning: keysize mismatch for client_host_key: " + "actual %d, announced %d", BN_num_bits(client_host_key_n), bits); + packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type); + + authenticated = auth_rhosts_rsa(pw, client_user, + client_host_key_e, client_host_key_n); + BN_clear_free(client_host_key_e); + BN_clear_free(client_host_key_n); + + snprintf(user, sizeof user, " ruser %s", client_user); #ifndef HAVE_LIBPAM - xfree(client_user); + xfree(client_user); #endif /* HAVE_LIBPAM */ - BN_clear_free(client_host_key_e); - BN_clear_free(client_host_key_n); - break; - - case SSH_CMSG_AUTH_RSA: - if (!options.rsa_authentication) - { - log("RSA authentication disabled."); - break; - } - - /* RSA authentication requested. */ - n = BN_new(); - packet_get_bignum(n, &nlen); - packet_integrity_check(plen, nlen, type); - - authenticated = auth_rsa(pw, n); - log("RSA authentication %s for %.100s.", - authenticated ? "accepted" : "failed", - pw->pw_name); - BN_clear_free(n); - break; - - case SSH_CMSG_AUTH_PASSWORD: - if (!options.password_authentication) - { - log("Password authentication disabled."); - break; - } - - /* Read user password. It is in plain text, but was transmitted - over the encrypted channel so it is not visible to an outside - observer. */ - password = packet_get_string(&dlen); - packet_integrity_check(plen, 4 + dlen, type); - + break; + + case SSH_CMSG_AUTH_RSA: + if (!options.rsa_authentication) { + verbose("RSA authentication disabled."); + break; + } + /* RSA authentication requested. */ + n = BN_new(); + packet_get_bignum(n, &nlen); + packet_integrity_check(plen, nlen, type); + authenticated = auth_rsa(pw, n); + BN_clear_free(n); + break; + + case SSH_CMSG_AUTH_PASSWORD: + if (!options.password_authentication) { + verbose("Password authentication disabled."); + break; + } + /* Read user password. It is in plain text, but + was transmitted over the encrypted channel so + it is not visible to an outside observer. */ + password = packet_get_string(&dlen); + packet_integrity_check(plen, 4 + dlen, type); + #ifdef HAVE_LIBPAM - /* Do PAM auth with password */ - pampasswd = password; - pam_retval = pam_authenticate((pam_handle_t *)pamh, 0); - if (pam_retval == PAM_SUCCESS) - { - log("PAM Password authentication accepted for user \"%.100s\"", pw->pw_name); - authenticated = 1; - break; - } - - log("PAM Password authentication for \"%.100s\" failed: %s", - pw->pw_name, PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - break; + /* Do PAM auth with password */ + pampasswd = password; + pam_retval = pam_authenticate((pam_handle_t *)pamh, 0); + if (pam_retval == PAM_SUCCESS) { + log("PAM Password authentication accepted for user \"%.100s\"", pw->pw_name); + authenticated = 1; + break; + } + + log("PAM Password authentication for \"%.100s\" failed: %s", + pw->pw_name, PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); + break; #else /* HAVE_LIBPAM */ - /* Try authentication with the password. */ - authenticated = auth_password(pw, password); - log("Password authentication %s for %.100s.", - authenticated ? "accepted" : "failed", - pw->pw_name); - - memset(password, 0, strlen(password)); - xfree(password); - break; + /* Try authentication with the password. */ + authenticated = auth_password(pw, password); + + memset(password, 0, strlen(password)); + xfree(password); + break; #endif /* HAVE_LIBPAM */ - - case SSH_CMSG_AUTH_TIS: - /* TIS Authentication is unsupported */ - log("TIS authentication disabled."); - break; - - default: - /* Any unknown messages will be ignored (and failure returned) - during authentication. */ - log("Unknown message during authentication: type %d", type); - break; /* Respond with a failure message. */ - } - - if (authenticated) - break; - if (++authentication_failures >= MAX_AUTH_FAILURES) - packet_disconnect("Too many authentication failures for %.100s from %.200s", - pw->pw_name, get_canonical_hostname()); - /* Send a message indicating that the authentication attempt failed. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - } + +#ifdef SKEY + case SSH_CMSG_AUTH_TIS: + debug("rcvd SSH_CMSG_AUTH_TIS"); + if (options.skey_authentication == 1) { + char *skeyinfo = skey_keyinfo(pw->pw_name); + if (skeyinfo == NULL) { + debug("generating fake skeyinfo for %.100s.", pw->pw_name); + skeyinfo = skey_fake_keyinfo(pw->pw_name); + } + if (skeyinfo != NULL) { + /* we send our s/key- in + tis-challenge messages */ + debug("sending challenge '%s'", skeyinfo); + packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); + packet_put_string(skeyinfo, strlen(skeyinfo)); + packet_send(); + packet_write_wait(); + continue; + } + } + break; + case SSH_CMSG_AUTH_TIS_RESPONSE: + debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE"); + if (options.skey_authentication == 1) { + char *response = packet_get_string(&dlen); + debug("skey response == '%s'", response); + packet_integrity_check(plen, 4 + dlen, type); + authenticated = (skey_haskey(pw->pw_name) == 0 && + skey_passcheck(pw->pw_name, response) != -1); + xfree(response); + } + break; +#else + case SSH_CMSG_AUTH_TIS: + /* TIS Authentication is unsupported */ + log("TIS authentication unsupported."); + break; +#endif + + default: + /* Any unknown messages will be ignored (and + failure returned) during authentication. */ + log("Unknown message during authentication: type %d", type); + break; + } + + /* Raise logging level */ + if (authenticated || + attempt == AUTH_FAIL_LOG || + type == SSH_CMSG_AUTH_PASSWORD) + authlog = log; + + authlog("%s %s for %.200s from %.200s port %d%s", + authenticated ? "Accepted" : "Failed", + get_authname(type), + pw->pw_uid == 0 ? "ROOT" : pw->pw_name, + get_remote_ipaddr(), + get_remote_port(), + user); #ifdef HAVE_LIBPAM - do_pam_account_and_session(pw->pw_name, client_user, get_canonical_hostname()); + do_pam_account_and_session(pw->pw_name, client_user, + get_canonical_hostname()); - /* Clean up */ - if (client_user != NULL) - xfree(client_user); + /* Clean up */ + if (client_user != NULL) + xfree(client_user); - if (password != NULL) - { - memset(password, 0, strlen(password)); - xfree(password); - } + if (password != NULL) { + memset(password, 0, strlen(password)); + xfree(password); + } #endif /* HAVE_LIBPAM */ + + if (authenticated) + return; + + if (attempt > AUTH_FAIL_MAX) + packet_disconnect(AUTH_FAIL_MSG, pw->pw_name); + + /* Send a message indicating that the authentication attempt failed. */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + } } -/* The user does not exist or access is denied, - but fake indication that authentication is needed. */ +/* + * The user does not exist or access is denied, + * but fake indication that authentication is needed. + */ void do_fake_authloop(char *user) { - int authentication_failures = 0; - - /* Indicate that authentication is needed. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - - /* Keep reading packets, and always respond with a failure. This is to - avoid disclosing whether such a user really exists. */ - for (;;) - { - /* Read a packet. This will not return if the client disconnects. */ - int plen; - int type = packet_read(&plen); + int attempt = 0; + + log("Faking authloop for illegal user %.200s from %.200s port %d", + user, + get_remote_ipaddr(), + get_remote_port()); + + /* Indicate that authentication is needed. */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + + /* Keep reading packets, and always respond with a failure. This + is to avoid disclosing whether such a user really exists. */ + for (attempt = 1;; attempt++) { + /* Read a packet. This will not return if the client + disconnects. */ + int plen; + int type = packet_read(&plen); #ifdef SKEY - int passw_len; - char *password, *skeyinfo; - if (options.password_authentication && - options.skey_authentication == 1 && - type == SSH_CMSG_AUTH_PASSWORD && - (password = packet_get_string(&passw_len)) != NULL && - passw_len == 5 && - strncasecmp(password, "s/key", 5) == 0 && - (skeyinfo = skey_fake_keyinfo(user)) != NULL ){ - /* Send a fake s/key challenge. */ - packet_send_debug(skeyinfo); - } + int dlen; + char *password, *skeyinfo; + if (options.password_authentication && + options.skey_authentication == 1 && + type == SSH_CMSG_AUTH_PASSWORD && + (password = packet_get_string(&dlen)) != NULL && + dlen == 5 && + strncasecmp(password, "s/key", 5) == 0 && + (skeyinfo = skey_fake_keyinfo(user)) != NULL) { + /* Send a fake s/key challenge. */ + packet_send_debug(skeyinfo); + } #endif - if (++authentication_failures >= MAX_AUTH_FAILURES) - packet_disconnect("Too many authentication failures for %.100s from %.200s", - user, get_canonical_hostname()); - /* Send failure. This should be indistinguishable from a failed - authentication. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - } - /*NOTREACHED*/ - abort(); + if (attempt > AUTH_FAIL_MAX) + packet_disconnect(AUTH_FAIL_MSG, user); + + /* Send failure. This should be indistinguishable from a + failed authentication. */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + } + /* NOTREACHED */ + abort(); } -/* Remove local Xauthority file. */ +/* + * Remove local Xauthority file. + */ static void xauthfile_cleanup_proc(void *ignore) { - debug("xauthfile_cleanup_proc called"); + debug("xauthfile_cleanup_proc called"); - if (xauthfile != NULL) { - unlink(xauthfile); - xfree(xauthfile); - xauthfile = NULL; - } + if (xauthfile != NULL) { + unlink(xauthfile); + xfree(xauthfile); + xauthfile = NULL; + } } -/* Prepares for an interactive session. This is called after the user has - been successfully authenticated. During this message exchange, pseudo - terminals are allocated, X11, TCP/IP, and authentication agent forwardings - are requested, etc. */ - -void do_authenticated(struct passwd *pw) +/* + * Prepares for an interactive session. This is called after the user has + * been successfully authenticated. During this message exchange, pseudo + * terminals are allocated, X11, TCP/IP, and authentication agent forwardings + * are requested, etc. + */ +void +do_authenticated(struct passwd * pw) { - int type; - int compression_level = 0, enable_compression_after_reply = 0; - int have_pty = 0, ptyfd = -1, ttyfd = -1, xauthfd = -1; - int row, col, xpixel, ypixel, screen; - char ttyname[64]; - char *command, *term = NULL, *display = NULL, *proto = NULL, *data = NULL; - struct group *grp; - gid_t tty_gid; - mode_t tty_mode; - int n_bytes; - - /* Cancel the alarm we set to limit the time taken for authentication. */ - alarm(0); - - /* Inform the channel mechanism that we are the server side and that - the client may request to connect to any port at all. (The user could - do it anyway, and we wouldn\'t know what is permitted except by the - client telling us, so we can equally well trust the client not to request - anything bogus.) */ - channel_permit_all_opens(); - - /* We stay in this loop until the client requests to execute a shell or a - command. */ - while (1) - { - int plen, dlen; - - /* Get a packet from the client. */ - type = packet_read(&plen); - - /* Process the packet. */ - switch (type) - { - case SSH_CMSG_REQUEST_COMPRESSION: - packet_integrity_check(plen, 4, type); - compression_level = packet_get_int(); - if (compression_level < 1 || compression_level > 9) - { - packet_send_debug("Received illegal compression level %d.", - compression_level); - goto fail; - } - /* Enable compression after we have responded with SUCCESS. */ - enable_compression_after_reply = 1; - break; - - case SSH_CMSG_REQUEST_PTY: - if (no_pty_flag) - { - debug("Allocating a pty not permitted for this authentication."); - goto fail; - } - if (have_pty) - packet_disconnect("Protocol error: you already have a pty."); - - debug("Allocating pty."); - - /* Allocate a pty and open it. */ - if (!pty_allocate(&ptyfd, &ttyfd, ttyname)) - { - error("Failed to allocate pty."); - goto fail; - } - - /* Determine the group to make the owner of the tty. */ - grp = getgrnam("tty"); - if (grp) - { - tty_gid = grp->gr_gid; - tty_mode = S_IRUSR|S_IWUSR|S_IWGRP; - } - else - { - tty_gid = pw->pw_gid; - tty_mode = S_IRUSR|S_IWUSR|S_IWGRP|S_IWOTH; - } - - /* Change ownership of the tty. */ - if (chown(ttyname, pw->pw_uid, tty_gid) < 0) - fatal("chown(%.100s, %d, %d) failed: %.100s", - ttyname, pw->pw_uid, tty_gid, strerror(errno)); - if (chmod(ttyname, tty_mode) < 0) - fatal("chmod(%.100s, 0%o) failed: %.100s", - ttyname, tty_mode, strerror(errno)); - - /* Get TERM from the packet. Note that the value may be of arbitrary - length. */ - - term = packet_get_string(&dlen); - packet_integrity_check(dlen, strlen(term), type); - /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */ - /* Remaining bytes */ - n_bytes = plen - (4 + dlen + 4*4); - - if (strcmp(term, "") == 0) - term = NULL; - - /* Get window size from the packet. */ - row = packet_get_int(); - col = packet_get_int(); - xpixel = packet_get_int(); - ypixel = packet_get_int(); - pty_change_window_size(ptyfd, row, col, xpixel, ypixel); - - /* Get tty modes from the packet. */ - tty_parse_modes(ttyfd, &n_bytes); - packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); - - /* Indicate that we now have a pty. */ - have_pty = 1; - break; - - case SSH_CMSG_X11_REQUEST_FORWARDING: - if (!options.x11_forwarding) - { - packet_send_debug("X11 forwarding disabled in server configuration file."); - goto fail; - } + int type; + int compression_level = 0, enable_compression_after_reply = 0; + int have_pty = 0, ptyfd = -1, ttyfd = -1, xauthfd = -1; + int row, col, xpixel, ypixel, screen; + char ttyname[64]; + char *command, *term = NULL, *display = NULL, *proto = NULL, + *data = NULL; + struct group *grp; + gid_t tty_gid; + mode_t tty_mode; + int n_bytes; + + /* Cancel the alarm we set to limit the time taken for + authentication. */ + alarm(0); + + /* Inform the channel mechanism that we are the server side and + that the client may request to connect to any port at all. + (The user could do it anyway, and we wouldn\'t know what is + permitted except by the client telling us, so we can equally + well trust the client not to request anything bogus.) */ + channel_permit_all_opens(); + + /* We stay in this loop until the client requests to execute a + shell or a command. */ + while (1) { + int plen, dlen; + + /* Get a packet from the client. */ + type = packet_read(&plen); + + /* Process the packet. */ + switch (type) { + case SSH_CMSG_REQUEST_COMPRESSION: + packet_integrity_check(plen, 4, type); + compression_level = packet_get_int(); + if (compression_level < 1 || compression_level > 9) { + packet_send_debug("Received illegal compression level %d.", + compression_level); + goto fail; + } + /* Enable compression after we have responded with SUCCESS. */ + enable_compression_after_reply = 1; + break; + + case SSH_CMSG_REQUEST_PTY: + if (no_pty_flag) { + debug("Allocating a pty not permitted for this authentication."); + goto fail; + } + if (have_pty) + packet_disconnect("Protocol error: you already have a pty."); + + debug("Allocating pty."); + + /* Allocate a pty and open it. */ + if (!pty_allocate(&ptyfd, &ttyfd, ttyname)) { + error("Failed to allocate pty."); + goto fail; + } + /* Determine the group to make the owner of the tty. */ + grp = getgrnam("tty"); + if (grp) { + tty_gid = grp->gr_gid; + tty_mode = S_IRUSR | S_IWUSR | S_IWGRP; + } else { + tty_gid = pw->pw_gid; + tty_mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH; + } + + /* Change ownership of the tty. */ + if (chown(ttyname, pw->pw_uid, tty_gid) < 0) + fatal("chown(%.100s, %d, %d) failed: %.100s", + ttyname, pw->pw_uid, tty_gid, strerror(errno)); + if (chmod(ttyname, tty_mode) < 0) + fatal("chmod(%.100s, 0%o) failed: %.100s", + ttyname, tty_mode, strerror(errno)); + + /* Get TERM from the packet. Note that the value may be of arbitrary length. */ + term = packet_get_string(&dlen); + packet_integrity_check(dlen, strlen(term), type); + /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */ + /* Remaining bytes */ + n_bytes = plen - (4 + dlen + 4 * 4); + + if (strcmp(term, "") == 0) + term = NULL; + + /* Get window size from the packet. */ + row = packet_get_int(); + col = packet_get_int(); + xpixel = packet_get_int(); + ypixel = packet_get_int(); + pty_change_window_size(ptyfd, row, col, xpixel, ypixel); + + /* Get tty modes from the packet. */ + tty_parse_modes(ttyfd, &n_bytes); + packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type); + + /* Indicate that we now have a pty. */ + have_pty = 1; + break; + + case SSH_CMSG_X11_REQUEST_FORWARDING: + if (!options.x11_forwarding) { + packet_send_debug("X11 forwarding disabled in server configuration file."); + goto fail; + } #ifdef XAUTH_PATH - if (no_x11_forwarding_flag) - { - packet_send_debug("X11 forwarding not permitted for this authentication."); - goto fail; - } - debug("Received request for X11 forwarding with auth spoofing."); - if (display) - packet_disconnect("Protocol error: X11 display already set."); - { - int proto_len, data_len; - proto = packet_get_string(&proto_len); - data = packet_get_string(&data_len); - packet_integrity_check(plen, 4+proto_len + 4+data_len + 4, type); - } - if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER) - screen = packet_get_int(); - else - screen = 0; - display = x11_create_display_inet(screen); - if (!display) - goto fail; - - /* Setup to always have a local .Xauthority. */ - xauthfile = xmalloc(MAXPATHLEN); - snprintf(xauthfile, MAXPATHLEN, "/tmp/XauthXXXXXX"); - - if ((xauthfd = mkstemp(xauthfile)) != -1) { - fchown(xauthfd, pw->pw_uid, pw->pw_gid); - close(xauthfd); - fatal_add_cleanup(xauthfile_cleanup_proc, NULL); - } - else { - xfree(xauthfile); - xauthfile = NULL; - } - break; + if (no_x11_forwarding_flag) { + packet_send_debug("X11 forwarding not permitted for this authentication."); + goto fail; + } + debug("Received request for X11 forwarding with auth spoofing."); + if (display) + packet_disconnect("Protocol error: X11 display already set."); + { + int proto_len, data_len; + proto = packet_get_string(&proto_len); + data = packet_get_string(&data_len); + packet_integrity_check(plen, 4 + proto_len + 4 + data_len + 4, type); + } + if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER) + screen = packet_get_int(); + else + screen = 0; + display = x11_create_display_inet(screen); + if (!display) + goto fail; + + /* Setup to always have a local .Xauthority. */ + xauthfile = xmalloc(MAXPATHLEN); + snprintf(xauthfile, MAXPATHLEN, "/tmp/XauthXXXXXX"); + + if ((xauthfd = mkstemp(xauthfile)) != -1) { + fchown(xauthfd, pw->pw_uid, pw->pw_gid); + close(xauthfd); + fatal_add_cleanup(xauthfile_cleanup_proc, NULL); + } else { + xfree(xauthfile); + xauthfile = NULL; + } + break; #else /* XAUTH_PATH */ - /* No xauth program; we won't accept forwarding with spoofing. */ - packet_send_debug("No xauth program; cannot forward with spoofing."); - goto fail; + packet_send_debug("No xauth program; cannot forward with spoofing."); + goto fail; #endif /* XAUTH_PATH */ - case SSH_CMSG_AGENT_REQUEST_FORWARDING: - if (no_agent_forwarding_flag) - { - debug("Authentication agent forwarding not permitted for this authentication."); - goto fail; - } - debug("Received authentication agent forwarding request."); - auth_input_request_forwarding(pw); - break; - - case SSH_CMSG_PORT_FORWARD_REQUEST: - if (no_port_forwarding_flag) - { - debug("Port forwarding not permitted for this authentication."); - goto fail; - } - debug("Received TCP/IP port forwarding request."); - channel_input_port_forward_request(pw->pw_uid == 0); - break; - - case SSH_CMSG_MAX_PACKET_SIZE: - if (packet_set_maxsize(packet_get_int()) < 0) - goto fail; - break; - - case SSH_CMSG_EXEC_SHELL: - /* Set interactive/non-interactive mode. */ - packet_set_interactive(have_pty || display != NULL, - options.keepalives); - - if (forced_command != NULL) - goto do_forced_command; - debug("Forking shell."); - packet_integrity_check(plen, 0, type); - if (have_pty) - do_exec_pty(NULL, ptyfd, ttyfd, ttyname, pw, term, display, proto, - data); - else - do_exec_no_pty(NULL, pw, display, proto, data); - return; - - case SSH_CMSG_EXEC_CMD: - /* Set interactive/non-interactive mode. */ - packet_set_interactive(have_pty || display != NULL, - options.keepalives); - - if (forced_command != NULL) - goto do_forced_command; - /* Get command from the packet. */ - { - int dlen; - command = packet_get_string(&dlen); - debug("Executing command '%.500s'", command); - packet_integrity_check(plen, 4 + dlen, type); - } - if (have_pty) - do_exec_pty(command, ptyfd, ttyfd, ttyname, pw, term, display, - proto, data); - else - do_exec_no_pty(command, pw, display, proto, data); - xfree(command); - return; - - default: - /* Any unknown messages in this phase are ignored, and a failure - message is returned. */ - log("Unknown packet type received after authentication: %d", type); - goto fail; - } + case SSH_CMSG_AGENT_REQUEST_FORWARDING: + if (no_agent_forwarding_flag) { + debug("Authentication agent forwarding not permitted for this authentication."); + goto fail; + } + debug("Received authentication agent forwarding request."); + auth_input_request_forwarding(pw); + break; + + case SSH_CMSG_PORT_FORWARD_REQUEST: + if (no_port_forwarding_flag) { + debug("Port forwarding not permitted for this authentication."); + goto fail; + } + debug("Received TCP/IP port forwarding request."); + channel_input_port_forward_request(pw->pw_uid == 0); + break; + + case SSH_CMSG_MAX_PACKET_SIZE: + if (packet_set_maxsize(packet_get_int()) < 0) + goto fail; + break; + + case SSH_CMSG_EXEC_SHELL: + /* Set interactive/non-interactive mode. */ + packet_set_interactive(have_pty || display != NULL, + options.keepalives); + + if (forced_command != NULL) + goto do_forced_command; + debug("Forking shell."); + packet_integrity_check(plen, 0, type); + if (have_pty) + do_exec_pty(NULL, ptyfd, ttyfd, ttyname, pw, term, display, proto, data); + else + do_exec_no_pty(NULL, pw, display, proto, data); + return; + + case SSH_CMSG_EXEC_CMD: + /* Set interactive/non-interactive mode. */ + packet_set_interactive(have_pty || display != NULL, + options.keepalives); + + if (forced_command != NULL) + goto do_forced_command; + /* Get command from the packet. */ + { + int dlen; + command = packet_get_string(&dlen); + debug("Executing command '%.500s'", command); + packet_integrity_check(plen, 4 + dlen, type); + } + if (have_pty) + do_exec_pty(command, ptyfd, ttyfd, ttyname, pw, term, display, proto, data); + else + do_exec_no_pty(command, pw, display, proto, data); + xfree(command); + return; + + default: + /* Any unknown messages in this phase are ignored, + and a failure message is returned. */ + log("Unknown packet type received after authentication: %d", type); + goto fail; + } - /* The request was successfully processed. */ - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); + /* The request was successfully processed. */ + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); - /* Enable compression now that we have replied if appropriate. */ - if (enable_compression_after_reply) - { - enable_compression_after_reply = 0; - packet_start_compression(compression_level); - } + /* Enable compression now that we have replied if appropriate. */ + if (enable_compression_after_reply) { + enable_compression_after_reply = 0; + packet_start_compression(compression_level); + } + continue; - continue; - - fail: - /* The request failed. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - continue; - - do_forced_command: - /* There is a forced command specified for this login. Execute it. */ - debug("Executing forced command: %.900s", forced_command); - if (have_pty) - do_exec_pty(forced_command, ptyfd, ttyfd, ttyname, pw, term, display, - proto, data); - else - do_exec_no_pty(forced_command, pw, display, proto, data); - return; - } -} +fail: + /* The request failed. */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + continue; -/* 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. */ +do_forced_command: + /* There is a forced command specified for this login. + Execute it. */ + debug("Executing forced command: %.900s", forced_command); + if (have_pty) + do_exec_pty(forced_command, ptyfd, ttyfd, ttyname, pw, term, display, proto, data); + else + do_exec_no_pty(forced_command, pw, display, proto, data); + return; + } +} -void do_exec_no_pty(const char *command, struct passwd *pw, - const char *display, const char *auth_proto, - const char *auth_data) -{ - int pid; +/* + * 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, struct passwd * pw, + const char *display, const char *auth_proto, + const char *auth_data) +{ + int pid; #ifdef USE_PIPES - int pin[2], pout[2], perr[2]; - /* Allocate pipes for communicating with the program. */ - if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) - packet_disconnect("Could not create pipes: %.100s", - strerror(errno)); + int pin[2], pout[2], perr[2]; + /* Allocate pipes for communicating with the program. */ + if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) + packet_disconnect("Could not create pipes: %.100s", + strerror(errno)); #else /* USE_PIPES */ - int inout[2], err[2]; - /* Uses socket pairs to communicate with the program. */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || - socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) - packet_disconnect("Could not create socket pairs: %.100s", - strerror(errno)); + int inout[2], err[2]; + /* Uses socket pairs to communicate with the program. */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || + socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) + packet_disconnect("Could not create socket pairs: %.100s", + strerror(errno)); #endif /* USE_PIPES */ - - setproctitle("%s@notty", pw->pw_name); - /* Fork the child. */ - if ((pid = fork()) == 0) - { - /* Child. Reinitialize the log since the pid has changed. */ - log_init(av0, options.log_level, options.log_facility, log_stderr); + setproctitle("%s@notty", pw->pw_name); - /* Create a new session and process group since the 4.4BSD setlogin() - affects the entire process group. */ - if (setsid() < 0) - error("setsid failed: %.100s", strerror(errno)); + /* Fork the child. */ + if ((pid = fork()) == 0) { + /* Child. Reinitialize the log since the pid has changed. */ + log_init(av0, options.log_level, options.log_facility, log_stderr); + + /* Create a new session and process group since the 4.4BSD + setlogin() affects the entire process group. */ + if (setsid() < 0) + error("setsid failed: %.100s", strerror(errno)); #ifdef USE_PIPES - /* Redirect stdin. We close the parent side of the socket pair, - and make the child side the standard input. */ - close(pin[1]); - if (dup2(pin[0], 0) < 0) - perror("dup2 stdin"); - close(pin[0]); - - /* Redirect stdout. */ - close(pout[0]); - if (dup2(pout[1], 1) < 0) - perror("dup2 stdout"); - close(pout[1]); - - /* Redirect stderr. */ - close(perr[0]); - if (dup2(perr[1], 2) < 0) - perror("dup2 stderr"); - close(perr[1]); + /* Redirect stdin. We close the parent side of the socket + pair, and make the child side the standard input. */ + close(pin[1]); + if (dup2(pin[0], 0) < 0) + perror("dup2 stdin"); + close(pin[0]); + + /* Redirect stdout. */ + close(pout[0]); + if (dup2(pout[1], 1) < 0) + perror("dup2 stdout"); + close(pout[1]); + + /* Redirect stderr. */ + close(perr[0]); + if (dup2(perr[1], 2) < 0) + perror("dup2 stderr"); + close(perr[1]); #else /* USE_PIPES */ - /* Redirect stdin, stdout, and stderr. Stdin and stdout will use the - same socket, as some programs (particularly rdist) seem to depend - on it. */ - close(inout[1]); - close(err[1]); - if (dup2(inout[0], 0) < 0) /* stdin */ - perror("dup2 stdin"); - if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ - perror("dup2 stdout"); - if (dup2(err[0], 2) < 0) /* stderr */ - perror("dup2 stderr"); + /* Redirect stdin, stdout, and stderr. Stdin and stdout + will use the same socket, as some programs + (particularly rdist) seem to depend on it. */ + close(inout[1]); + close(err[1]); + if (dup2(inout[0], 0) < 0) /* stdin */ + perror("dup2 stdin"); + if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ + perror("dup2 stdout"); + if (dup2(err[0], 2) < 0) /* stderr */ + perror("dup2 stderr"); #endif /* USE_PIPES */ - /* Do processing for the child (exec command etc). */ - do_child(command, pw, NULL, display, auth_proto, auth_data, NULL); - /*NOTREACHED*/ - } - if (pid < 0) - packet_disconnect("fork failed: %.100s", strerror(errno)); + /* Do processing for the child (exec command etc). */ + do_child(command, pw, NULL, display, auth_proto, auth_data, NULL); + /* NOTREACHED */ + } + if (pid < 0) + packet_disconnect("fork failed: %.100s", strerror(errno)); #ifdef USE_PIPES - /* We are the parent. Close the child sides of the pipes. */ - close(pin[0]); - close(pout[1]); - close(perr[1]); - - /* Enter the interactive session. */ - server_loop(pid, pin[1], pout[0], perr[0]); - /* server_loop has closed pin[1], pout[1], and perr[1]. */ + /* We are the parent. Close the child sides of the pipes. */ + close(pin[0]); + close(pout[1]); + close(perr[1]); + + /* Enter the interactive session. */ + server_loop(pid, pin[1], pout[0], perr[0]); + /* server_loop has closed pin[1], pout[1], and perr[1]. */ #else /* USE_PIPES */ - /* We are the parent. Close the child sides of the socket pairs. */ - close(inout[0]); - close(err[0]); - - /* Enter the interactive session. Note: server_loop must be able to handle - the case that fdin and fdout are the same. */ - server_loop(pid, inout[1], inout[1], err[1]); - /* server_loop has closed inout[1] and err[1]. */ + /* We are the parent. Close the child sides of the socket pairs. */ + close(inout[0]); + close(err[0]); + + /* Enter the interactive session. Note: server_loop must be able + to handle the case that fdin and fdout are the same. */ + server_loop(pid, inout[1], inout[1], err[1]); + /* server_loop has closed inout[1] and err[1]. */ #endif /* USE_PIPES */ } -struct pty_cleanup_context -{ - const char *ttyname; - int pid; +struct pty_cleanup_context { + const char *ttyname; + int pid; }; -/* Function to perform cleanup if we get aborted abnormally (e.g., due to a - dropped connection). */ - -void pty_cleanup_proc(void *context) +/* + * Function to perform cleanup if we get aborted abnormally (e.g., due to a + * dropped connection). + */ +void +pty_cleanup_proc(void *context) { - struct pty_cleanup_context *cu = context; + struct pty_cleanup_context *cu = context; - debug("pty_cleanup_proc called"); + debug("pty_cleanup_proc called"); - /* Record that the user has logged out. */ - record_logout(cu->pid, cu->ttyname); + /* Record that the user has logged out. */ + record_logout(cu->pid, cu->ttyname); - /* Release the pseudo-tty. */ - pty_release(cu->ttyname); + /* Release the pseudo-tty. */ + pty_release(cu->ttyname); } -/* This is called to fork and execute a command when we have a tty. This - will call do_child from the child, and server_loop from the parent after - setting up file descriptors, controlling tty, updating wtmp, utmp, - lastlog, and other such operations. */ - -void do_exec_pty(const char *command, int ptyfd, int ttyfd, - const char *ttyname, struct passwd *pw, const char *term, - const char *display, const char *auth_proto, - const char *auth_data) +/* + * This is called to fork and execute a command when we have a tty. This + * will call do_child from the child, and server_loop from the parent after + * setting up file descriptors, controlling tty, updating wtmp, utmp, + * lastlog, and other such operations. + */ +void +do_exec_pty(const char *command, int ptyfd, int ttyfd, + const char *ttyname, struct passwd * pw, const char *term, + const char *display, const char *auth_proto, + const char *auth_data) { - int pid, fdout; - const char *hostname; - time_t last_login_time; - char buf[100], *time_string; - FILE *f; - char line[256]; - struct stat st; - int quiet_login; - struct sockaddr_in from; - int fromlen; - struct pty_cleanup_context cleanup_context; - - /* Get remote host name. */ - hostname = get_canonical_hostname(); - - /* Get the time when the user last logged in. Buf will be set to contain - the hostname the last login was from. */ - if(!options.use_login) { - last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, - buf, sizeof(buf)); - } - - setproctitle("%s@%s", pw->pw_name, strrchr(ttyname, '/') + 1); - - /* Fork the child. */ - if ((pid = fork()) == 0) - { - pid = getpid(); - - /* Child. Reinitialize the log because the pid has changed. */ - log_init(av0, options.log_level, options.log_facility, log_stderr); - - /* Close the master side of the pseudo tty. */ - close(ptyfd); - - /* Make the pseudo tty our controlling tty. */ - pty_make_controlling_tty(&ttyfd, ttyname); - - /* Redirect stdin from the pseudo tty. */ - if (dup2(ttyfd, fileno(stdin)) < 0) - error("dup2 stdin failed: %.100s", strerror(errno)); - - /* Redirect stdout to the pseudo tty. */ - if (dup2(ttyfd, fileno(stdout)) < 0) - error("dup2 stdin failed: %.100s", strerror(errno)); - - /* Redirect stderr to the pseudo tty. */ - if (dup2(ttyfd, fileno(stderr)) < 0) - error("dup2 stdin failed: %.100s", strerror(errno)); - - /* Close the extra descriptor for the pseudo tty. */ - close(ttyfd); - - /* Get IP address of client. This is needed because we want to record - where the user logged in from. If the connection is not a socket, - let the ip address be 0.0.0.0. */ - memset(&from, 0, sizeof(from)); - if (packet_get_connection_in() == packet_get_connection_out()) - { - fromlen = sizeof(from); - if (getpeername(packet_get_connection_in(), - (struct sockaddr *)&from, &fromlen) < 0) { - debug("getpeername: %.100s", strerror(errno)); - fatal_cleanup(); - } + int pid, fdout; + const char *hostname; + time_t last_login_time; + char buf[100], *time_string; + FILE *f; + char line[256]; + struct stat st; + int quiet_login; + struct sockaddr_in from; + int fromlen; + struct pty_cleanup_context cleanup_context; + + /* Get remote host name. */ + hostname = get_canonical_hostname(); + + /* Get the time when the user last logged in. Buf will be set to + contain the hostname the last login was from. */ + if (!options.use_login) { + last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, + buf, sizeof(buf)); } + setproctitle("%s@%s", pw->pw_name, strrchr(ttyname, '/') + 1); + + /* Fork the child. */ + if ((pid = fork()) == 0) { + pid = getpid(); + + /* Child. Reinitialize the log because the pid has + changed. */ + log_init(av0, options.log_level, options.log_facility, log_stderr); + + /* Close the master side of the pseudo tty. */ + close(ptyfd); + + /* Make the pseudo tty our controlling tty. */ + pty_make_controlling_tty(&ttyfd, ttyname); + + /* Redirect stdin from the pseudo tty. */ + if (dup2(ttyfd, fileno(stdin)) < 0) + error("dup2 stdin failed: %.100s", strerror(errno)); + + /* Redirect stdout to the pseudo tty. */ + if (dup2(ttyfd, fileno(stdout)) < 0) + error("dup2 stdin failed: %.100s", strerror(errno)); + + /* Redirect stderr to the pseudo tty. */ + if (dup2(ttyfd, fileno(stderr)) < 0) + error("dup2 stdin failed: %.100s", strerror(errno)); + + /* Close the extra descriptor for the pseudo tty. */ + close(ttyfd); + + /* Get IP address of client. This is needed because we + want to record where the user logged in from. If the + connection is not a socket, let the ip address be 0.0.0.0. */ + memset(&from, 0, sizeof(from)); + if (packet_get_connection_in() == packet_get_connection_out()) { + 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 terminal. */ + record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname, + &from); - /* Record that there was a login on that terminal. */ - record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname, - &from); - - /* Check if .hushlogin exists. */ - snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir); - quiet_login = stat(line, &st) >= 0; + /* Check if .hushlogin exists. */ + snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir); + quiet_login = stat(line, &st) >= 0; #ifdef HAVE_LIBPAM - /* output the results of the pamconv() */ - if (!quiet_login && pamconv_msg != NULL) - fprintf(stderr, pamconv_msg); + /* output the results of the pamconv() */ + if (!quiet_login && pamconv_msg != NULL) + fprintf(stderr, pamconv_msg); #endif - - /* If the user has logged in before, display the time of last login. - However, don't display anything extra if a command has been - specified (so that ssh can be used to execute commands on a remote - machine without users knowing they are going to another machine). - Login(1) will do this for us as well, so check if login(1) is used */ - if (command == NULL && last_login_time != 0 && !quiet_login && - !options.use_login) - { - /* Convert the date to a string. */ - time_string = ctime(&last_login_time); - /* Remove the trailing newline. */ - if (strchr(time_string, '\n')) - *strchr(time_string, '\n') = 0; - /* Display the last login time. Host if displayed if known. */ - if (strcmp(buf, "") == 0) - printf("Last login: %s\r\n", time_string); - else - printf("Last login: %s from %s\r\n", time_string, buf); - } - /* Print /etc/motd unless a command was specified or printing it was - disabled in server options or login(1) will be used. Note that - some machines appear to print it in /etc/profile or similar. */ - if (command == NULL && options.print_motd && !quiet_login && - !options.use_login) - { - /* Print /etc/motd if it exists. */ - f = fopen("/etc/motd", "r"); - if (f) - { - while (fgets(line, sizeof(line), f)) - fputs(line, stdout); - fclose(f); - } + /* If the user has logged in before, display the time of + last login. However, don't display anything extra if a + command has been specified (so that ssh can be used to + execute commands on a remote machine without users + knowing they are going to another machine). Login(1) + will do this for us as well, so check if login(1) is used */ + if (command == NULL && last_login_time != 0 && !quiet_login && + !options.use_login) { + /* Convert the date to a string. */ + time_string = ctime(&last_login_time); + /* Remove the trailing newline. */ + if (strchr(time_string, '\n')) + *strchr(time_string, '\n') = 0; + /* Display the last login time. Host if displayed + if known. */ + if (strcmp(buf, "") == 0) + printf("Last login: %s\r\n", time_string); + else + printf("Last login: %s from %s\r\n", time_string, buf); + } + /* Print /etc/motd unless a command was specified or + printing it was disabled in server options or login(1) + will be used. Note that some machines appear to print + it in /etc/profile or similar. */ + if (command == NULL && options.print_motd && !quiet_login && + !options.use_login) { + /* Print /etc/motd if it exists. */ + f = fopen("/etc/motd", "r"); + if (f) { + while (fgets(line, sizeof(line), f)) + fputs(line, stdout); + fclose(f); + } + } + /* Do common processing for the child, such as execing the command. */ + do_child(command, pw, term, display, auth_proto, auth_data, ttyname); + /* NOTREACHED */ } - - /* Do common processing for the child, such as execing the command. */ - do_child(command, pw, term, display, auth_proto, auth_data, ttyname); - /*NOTREACHED*/ - } - if (pid < 0) - packet_disconnect("fork failed: %.100s", strerror(errno)); - /* Parent. Close the slave side of the pseudo tty. */ - close(ttyfd); - - /* Create another descriptor of the pty master side for use as the standard - input. We could use the original descriptor, but this simplifies code - in server_loop. The descriptor is bidirectional. */ - fdout = dup(ptyfd); - if (fdout < 0) - packet_disconnect("dup failed: %.100s", strerror(errno)); - - /* Add a cleanup function to clear the utmp entry and record logout time - in case we call fatal() (e.g., the connection gets closed). */ - cleanup_context.pid = pid; - cleanup_context.ttyname = ttyname; - fatal_add_cleanup(pty_cleanup_proc, (void *)&cleanup_context); - - /* Enter interactive session. */ - server_loop(pid, ptyfd, fdout, -1); - /* server_loop has not closed ptyfd and fdout. */ - - /* Cancel the cleanup function. */ - fatal_remove_cleanup(pty_cleanup_proc, (void *)&cleanup_context); - - /* Record that the user has logged out. */ - record_logout(pid, ttyname); - - /* Release the pseudo-tty. */ - pty_release(ttyname); - - /* Close the server side of the socket pairs. We must do this after the - pty cleanup, so that another process doesn't get this pty while we're - still cleaning up. */ - close(ptyfd); - close(fdout); + if (pid < 0) + packet_disconnect("fork failed: %.100s", strerror(errno)); + /* Parent. Close the slave side of the pseudo tty. */ + close(ttyfd); + + /* Create another descriptor of the pty master side for use as the + standard input. We could use the original descriptor, but this + simplifies code in server_loop. The descriptor is bidirectional. */ + fdout = dup(ptyfd); + if (fdout < 0) + packet_disconnect("dup failed: %.100s", strerror(errno)); + + /* Add a cleanup function to clear the utmp entry and record logout + time in case we call fatal() (e.g., the connection gets closed). */ + cleanup_context.pid = pid; + cleanup_context.ttyname = ttyname; + fatal_add_cleanup(pty_cleanup_proc, (void *) &cleanup_context); + + /* Enter interactive session. */ + server_loop(pid, ptyfd, fdout, -1); + /* server_loop has not closed ptyfd and fdout. */ + + /* Cancel the cleanup function. */ + fatal_remove_cleanup(pty_cleanup_proc, (void *) &cleanup_context); + + /* Record that the user has logged out. */ + record_logout(pid, ttyname); + + /* Release the pseudo-tty. */ + pty_release(ttyname); + + /* Close the server side of the socket pairs. We must do this + after the pty cleanup, so that another process doesn't get this + pty while we're still cleaning up. */ + close(ptyfd); + close(fdout); } -/* Sets the value of the given variable in the environment. If the variable - already exists, its value is overriden. */ - -void child_set_env(char ***envp, unsigned int *envsizep, const char *name, - const char *value) +/* + * Sets the value of the given variable in the environment. If the variable + * already exists, its value is overriden. + */ +void +child_set_env(char ***envp, unsigned int *envsizep, const char *name, + const char *value) { - unsigned int i, namelen; - char **env; - - /* Find the slot where the value should be stored. If the variable already - exists, we reuse the slot; otherwise we append a new slot at the end - of the array, expanding if necessary. */ - env = *envp; - namelen = strlen(name); - for (i = 0; env[i]; i++) - if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') - break; - if (env[i]) - { - /* Name already exists. Reuse the slot. */ - xfree(env[i]); - } - else - { - /* New variable. Expand the array if necessary. */ - if (i >= (*envsizep) - 1) - { - (*envsizep) += 50; - env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *)); + unsigned int i, namelen; + char **env; + + /* Find the slot where the value should be stored. If the + variable already exists, we reuse the slot; otherwise we append + a new slot at the end of the array, expanding if necessary. */ + env = *envp; + namelen = strlen(name); + for (i = 0; env[i]; i++) + if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') + break; + if (env[i]) { + /* Name already exists. Reuse the slot. */ + xfree(env[i]); + } else { + /* New variable. Expand the array if necessary. */ + if (i >= (*envsizep) - 1) { + (*envsizep) += 50; + env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *)); + } + /* Need to set the NULL pointer at end of array beyond the new slot. */ + env[i + 1] = NULL; } - /* Need to set the NULL pointer at end of array beyond the new - slot. */ - env[i + 1] = NULL; - } - - /* Allocate space and format the variable in the appropriate slot. */ - env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); - snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); + /* Allocate space and format the variable in the appropriate slot. */ + env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); + snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); } -/* Reads environment variables from the given file and adds/overrides them - into the environment. If the file does not exist, this does nothing. - Otherwise, it must consist of empty lines, comments (line starts with '#') - and assignments of the form name=value. No other forms are allowed. */ - -void read_environment_file(char ***env, unsigned int *envsize, - const char *filename) +/* + * Reads environment variables from the given file and adds/overrides them + * into the environment. If the file does not exist, this does nothing. + * Otherwise, it must consist of empty lines, comments (line starts with '#') + * and assignments of the form name=value. No other forms are allowed. + */ +void +read_environment_file(char ***env, unsigned int *envsize, + const char *filename) { - FILE *f; - char buf[4096]; - char *cp, *value; - - /* Open the environment file. */ - f = fopen(filename, "r"); - if (!f) - return; /* Not found. */ - - /* Process each line. */ - while (fgets(buf, sizeof(buf), f)) - { - /* Skip leading whitespace. */ - for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) - ; - - /* Ignore empty and comment lines. */ - if (!*cp || *cp == '#' || *cp == '\n') - continue; - - /* Remove newline. */ - if (strchr(cp, '\n')) - *strchr(cp, '\n') = '\0'; - - /* Find the equals sign. Its lack indicates badly formatted line. */ - value = strchr(cp, '='); - if (value == NULL) - { - fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf); - continue; - } + FILE *f; + char buf[4096]; + char *cp, *value; + + /* Open the environment file. */ + f = fopen(filename, "r"); + if (!f) + return; + + /* Process each line. */ + while (fgets(buf, sizeof(buf), f)) { + /* Skip leading whitespace. */ + for (cp = buf; *cp == ' ' || *cp == '\t'; cp++); + + /* Ignore empty and comment lines. */ + if (!*cp || *cp == '#' || *cp == '\n') + continue; + + /* Remove newline. */ + if (strchr(cp, '\n')) + *strchr(cp, '\n') = '\0'; + + /* Find the equals sign. Its lack indicates badly + formatted line. */ + value = strchr(cp, '='); + if (value == NULL) { + fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf); + continue; + } + /* Replace the equals sign by nul, and advance value to + the value string. */ + *value = '\0'; + value++; - /* Replace the equals sign by nul, and advance value to the value - string. */ - *value = '\0'; - value++; + /* Set the value in environment. */ + child_set_env(env, envsize, cp, value); + } - /* Set the value in environment. */ - child_set_env(env, envsize, cp, value); - } - - fclose(f); + fclose(f); } -/* Performs common processing for the child, such as setting up the - environment, closing extra file descriptors, setting the user and group - ids, and executing the command or shell. */ - -void do_child(const char *command, struct passwd *pw, const char *term, - const char *display, const char *auth_proto, - const char *auth_data, const char *ttyname) +/* + * Performs common processing for the child, such as setting up the + * environment, closing extra file descriptors, setting the user and group + * ids, and executing the command or shell. + */ +void +do_child(const char *command, struct passwd * pw, const char *term, + const char *display, const char *auth_proto, + const char *auth_data, const char *ttyname) { - const char *shell, *cp = NULL; - char buf[256]; - FILE *f; - unsigned int envsize, i; - char **env; - extern char **environ; - struct stat st; - char *argv[10]; + const char *shell, *cp = NULL; + char buf[256]; + FILE *f; + unsigned int envsize, i; + char **env; + extern char **environ; + struct stat st; + char *argv[10]; #ifndef HAVE_LIBPAM /* pam_nologin handles this */ - /* Check /etc/nologin. */ - f = fopen("/etc/nologin", "r"); - if (f) - { /* /etc/nologin exists. Print its contents and exit. */ - while (fgets(buf, sizeof(buf), f)) - fputs(buf, stderr); - fclose(f); - if (pw->pw_uid != 0) - exit(254); - } + /* Check /etc/nologin. */ + f = fopen("/etc/nologin", "r"); + if (f) { + /* /etc/nologin exists. Print its contents and exit. */ + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stderr); + fclose(f); + if (pw->pw_uid != 0) + exit(254); + } #endif /* HAVE_LIBPAM */ #ifdef HAVE_SETLOGIN - /* Set login name in the kernel. */ - if (setlogin(pw->pw_name) < 0) - error("setlogin failed: %s", strerror(errno)); + /* Set login name in the kernel. */ + if (setlogin(pw->pw_name) < 0) + error("setlogin failed: %s", strerror(errno)); #endif /* HAVE_SETLOGIN */ - /* Set uid, gid, and groups. */ - /* Login(1) does this as well, and it needs uid 0 for the "-h" switch, - so we let login(1) to this for us. */ - if(!options.use_login) { - if (getuid() == 0 || geteuid() == 0) - { - if (setgid(pw->pw_gid) < 0) - { - perror("setgid"); - exit(1); - } - /* Initialize the group list. */ - if (initgroups(pw->pw_name, pw->pw_gid) < 0) - { - perror("initgroups"); - exit(1); - } - endgrent(); - - /* Permanently switch to the desired uid. */ - permanently_set_uid(pw->pw_uid); - } - - if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) - fatal("Failed to set uids to %d.", (int)pw->pw_uid); - } - - /* Get the shell from the password data. An empty shell field is legal, - and means /bin/sh. */ - shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; + /* Set uid, gid, and groups. */ + /* Login(1) does this as well, and it needs uid 0 for the "-h" + switch, so we let login(1) to this for us. */ + if (!options.use_login) { + if (getuid() == 0 || geteuid() == 0) { + if (setgid(pw->pw_gid) < 0) { + perror("setgid"); + exit(1); + } + /* Initialize the group list. */ + if (initgroups(pw->pw_name, pw->pw_gid) < 0) { + perror("initgroups"); + exit(1); + } + endgrent(); + + /* Permanently switch to the desired uid. */ + permanently_set_uid(pw->pw_uid); + } + if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) + fatal("Failed to set uids to %d.", (int) pw->pw_uid); + } + /* Get the shell from the password data. An empty shell field is + legal, and means /bin/sh. */ + shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; #ifdef AFS - /* Try to get AFS tokens for the local cell. */ - if (k_hasafs()) { - char cell[64]; - - if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) - krb_afslog(cell, 0); - - krb_afslog(0, 0); - } + /* Try to get AFS tokens for the local cell. */ + if (k_hasafs()) { + char cell[64]; + + if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) + krb_afslog(cell, 0); + + krb_afslog(0, 0); + } #endif /* AFS */ - - /* Initialize the environment. In the first part we allocate space for - all environment variables. */ - envsize = 100; - env = xmalloc(envsize * sizeof(char *)); - env[0] = NULL; - - if(!options.use_login) { - /* Set basic environment. */ - child_set_env(&env, &envsize, "USER", pw->pw_name); - child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); - child_set_env(&env, &envsize, "HOME", pw->pw_dir); - child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); - - snprintf(buf, sizeof buf, "%.200s/%.50s", - _PATH_MAILDIR, pw->pw_name); - child_set_env(&env, &envsize, "MAIL", buf); - - /* Normal systems set SHELL by default. */ - child_set_env(&env, &envsize, "SHELL", shell); - } - - /* Let it inherit timezone if we have one. */ - if (getenv("TZ")) - child_set_env(&env, &envsize, "TZ", getenv("TZ")); - - /* Set custom environment options from RSA authentication. */ - while (custom_environment) - { - struct envstring *ce = custom_environment; - char *s = ce->s; - int i; - for (i = 0; s[i] != '=' && s[i]; i++) - ; - if (s[i] == '=') - { - s[i] = 0; - child_set_env(&env, &envsize, s, s + i + 1); + + /* Initialize the environment. In the first part we allocate + space for all environment variables. */ + envsize = 100; + env = xmalloc(envsize * sizeof(char *)); + env[0] = NULL; + + if (!options.use_login) { + /* Set basic environment. */ + child_set_env(&env, &envsize, "USER", pw->pw_name); + child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); + child_set_env(&env, &envsize, "HOME", pw->pw_dir); + child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); + + snprintf(buf, sizeof buf, "%.200s/%.50s", + _PATH_MAILDIR, pw->pw_name); + child_set_env(&env, &envsize, "MAIL", buf); + + /* Normal systems set SHELL by default. */ + child_set_env(&env, &envsize, "SHELL", shell); + } + /* Let it inherit timezone if we have one. */ + if (getenv("TZ")) + child_set_env(&env, &envsize, "TZ", getenv("TZ")); + + /* Set custom environment options from RSA authentication. */ + while (custom_environment) { + struct envstring *ce = custom_environment; + char *s = ce->s; + int i; + for (i = 0; s[i] != '=' && s[i]; i++); + if (s[i] == '=') { + s[i] = 0; + child_set_env(&env, &envsize, s, s + i + 1); + } + custom_environment = ce->next; + xfree(ce->s); + xfree(ce); } - custom_environment = ce->next; - xfree(ce->s); - xfree(ce); - } - /* Set SSH_CLIENT. */ - snprintf(buf, sizeof buf, "%.50s %d %d", - get_remote_ipaddr(), get_remote_port(), options.port); - child_set_env(&env, &envsize, "SSH_CLIENT", buf); + /* Set SSH_CLIENT. */ + snprintf(buf, sizeof buf, "%.50s %d %d", + get_remote_ipaddr(), get_remote_port(), options.port); + child_set_env(&env, &envsize, "SSH_CLIENT", buf); - /* Set SSH_TTY if we have a pty. */ - if (ttyname) - child_set_env(&env, &envsize, "SSH_TTY", ttyname); + /* Set SSH_TTY if we have a pty. */ + if (ttyname) + child_set_env(&env, &envsize, "SSH_TTY", ttyname); - /* Set TERM if we have a pty. */ - if (term) - child_set_env(&env, &envsize, "TERM", term); + /* Set TERM if we have a pty. */ + if (term) + child_set_env(&env, &envsize, "TERM", term); - /* Set DISPLAY if we have one. */ - if (display) - child_set_env(&env, &envsize, "DISPLAY", display); + /* Set DISPLAY if we have one. */ + if (display) + child_set_env(&env, &envsize, "DISPLAY", display); #ifdef KRB4 - { - extern char *ticket; - - if (ticket) - child_set_env(&env, &envsize, "KRBTKFILE", ticket); - } + { + extern char *ticket; + + if (ticket) + child_set_env(&env, &envsize, "KRBTKFILE", ticket); + } #endif /* KRB4 */ #ifdef HAVE_LIBPAM - /* Pull in any environment variables that may have been set by PAM. */ - { - char *equal_sign, var_name[256], var_val[256]; - long this_var; - char **pam_env = pam_getenvlist((pam_handle_t *)pamh); - for(this_var = 0; pam_env && pam_env[this_var]; this_var++) - { - if(strlen(pam_env[this_var]) < (sizeof(var_name) - 1)) - if((equal_sign = strstr(pam_env[this_var], "=")) != NULL) - { - memset(var_name, 0, sizeof(var_name)); - memset(var_val, 0, sizeof(var_val)); - strncpy(var_name, pam_env[this_var], - equal_sign - pam_env[this_var]); - strcpy(var_val, equal_sign + 1); - child_set_env(&env, &envsize, var_name, var_val); - } - } - } + /* Pull in any environment variables that may have been set by PAM. */ + { + char *equals, var_name[512], var_val[512]; + char **pam_env = pam_getenvlist((pam_handle_t *)pamh); + int i; + for(i = 0; pam_env && pam_env[i]; i++) { + equals = strstr(pam_env[i], "="); + if ((strlen(pam_env[i]) < (sizeof(var_name) - 1)) && (equals != NULL)) + { + memset(var_name, '\0', sizeof(var_name)); + memset(var_val, '\0', sizeof(var_val)); + strncpy(var_name, pam_env[i], equals - pam_env[i]); + strcpy(var_val, equals + 1); + child_set_env(&env, &envsize, var_name, var_val); + } + } + } #endif /* HAVE_LIBPAM */ - /* Set XAUTHORITY to always be a local file. */ - if (xauthfile) - child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); - - /* Set variable for forwarded authentication connection, if we have one. */ - if (auth_get_socket_name() != NULL) - child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, - auth_get_socket_name()); - - /* Read $HOME/.ssh/environment. */ - if(!options.use_login) { - snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir); - read_environment_file(&env, &envsize, buf); - } - - /* If debugging, dump the environment to stderr. */ - if (debug_flag) - { - fprintf(stderr, "Environment:\n"); - for (i = 0; env[i]; i++) - fprintf(stderr, " %.200s\n", env[i]); - } - - /* Close the connection descriptors; note that this is the child, and the - server will still have the socket open, and it is important that we - do not shutdown it. Note that the descriptors cannot be closed before - building the environment, as we call get_remote_ipaddr there. */ - 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()); - } - /* Close all descriptors related to channels. They will still remain - open in the parent. */ - channel_close_all(); - - /* Close any extra file descriptors. Note that there may still be - descriptors left by system functions. They will be closed later. */ - endpwent(); - endhostent(); - - /* Close any extra open file descriptors so that we don\'t have them - hanging around in clients. Note that we want to do this after - initgroups, because at least on Solaris 2.3 it leaves file descriptors - open. */ - for (i = 3; i < 64; i++) - close(i); - - /* Change current directory to the user\'s home directory. */ - if (chdir(pw->pw_dir) < 0) - fprintf(stderr, "Could not chdir to home directory %s: %s\n", - pw->pw_dir, strerror(errno)); - - /* Must take new environment into use so that .ssh/rc, /etc/sshrc and - xauth are run in the proper environment. */ - environ = env; - - /* Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first - in this order). */ - if(!options.use_login) { - if (stat(SSH_USER_RC, &st) >= 0) - { - if (debug_flag) - fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC); - - f = popen("/bin/sh " SSH_USER_RC, "w"); - if (f) - { - if (auth_proto != NULL && auth_data != NULL) - fprintf(f, "%s %s\n", auth_proto, auth_data); - pclose(f); - } - else - fprintf(stderr, "Could not run %s\n", SSH_USER_RC); - } - else - if (stat(SSH_SYSTEM_RC, &st) >= 0) - { - if (debug_flag) - fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC); - - f = popen("/bin/sh " SSH_SYSTEM_RC, "w"); - if (f) - { - if (auth_proto != NULL && auth_data != NULL) - fprintf(f, "%s %s\n", auth_proto, auth_data); - pclose(f); - } - else - fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC); - } + /* Set XAUTHORITY to always be a local file. */ + if (xauthfile) + child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); + + /* Set variable for forwarded authentication connection, if we + have one. */ + if (auth_get_socket_name() != NULL) + child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, + auth_get_socket_name()); + + /* Read $HOME/.ssh/environment. */ + if (!options.use_login) { + snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir); + read_environment_file(&env, &envsize, buf); + } + /* If debugging, dump the environment to stderr. */ + if (debug_flag) { + fprintf(stderr, "Environment:\n"); + for (i = 0; env[i]; i++) + fprintf(stderr, " %.200s\n", env[i]); + } + /* Close the connection descriptors; note that this is the child, + and the server will still have the socket open, and it is + important that we do not shutdown it. Note that the + descriptors cannot be closed before building the environment, + as we call get_remote_ipaddr there. */ + 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()); + } + /* Close all descriptors related to channels. They will still + remain open in the parent. */ + channel_close_all(); + + /* Close any extra file descriptors. Note that there may still be + descriptors left by system functions. They will be closed + later. */ + endpwent(); + endhostent(); + + /* Close any extra open file descriptors so that we don\'t have + them hanging around in clients. Note that we want to do this + after initgroups, because at least on Solaris 2.3 it leaves + file descriptors open. */ + for (i = 3; i < 64; i++) + close(i); + + /* Change current directory to the user\'s home directory. */ + if (chdir(pw->pw_dir) < 0) + fprintf(stderr, "Could not chdir to home directory %s: %s\n", + pw->pw_dir, strerror(errno)); + + /* Must take new environment into use so that .ssh/rc, /etc/sshrc + and xauth are run in the proper environment. */ + environ = env; + + /* Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found + first in this order). */ + if (!options.use_login) { + if (stat(SSH_USER_RC, &st) >= 0) { + if (debug_flag) + fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC); + + f = popen("/bin/sh " SSH_USER_RC, "w"); + if (f) { + if (auth_proto != NULL && auth_data != NULL) + fprintf(f, "%s %s\n", auth_proto, auth_data); + pclose(f); + } else + fprintf(stderr, "Could not run %s\n", SSH_USER_RC); + } else if (stat(SSH_SYSTEM_RC, &st) >= 0) { + if (debug_flag) + fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC); + + f = popen("/bin/sh " SSH_SYSTEM_RC, "w"); + if (f) { + if (auth_proto != NULL && auth_data != NULL) + fprintf(f, "%s %s\n", auth_proto, auth_data); + pclose(f); + } else + fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC); + } #ifdef XAUTH_PATH - else - { - /* Add authority data to .Xauthority if appropriate. */ - if (auth_proto != NULL && auth_data != NULL) - { - if (debug_flag) - fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n", - XAUTH_PATH, display, auth_proto, auth_data); - - f = popen(XAUTH_PATH " -q -", "w"); - if (f) - { - fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data); - fclose(f); - } - else - fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH); - } - } + else { + /* Add authority data to .Xauthority if + appropriate. */ + if (auth_proto != NULL && auth_data != NULL) { + if (debug_flag) + fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n", + XAUTH_PATH, display, auth_proto, auth_data); + + f = popen(XAUTH_PATH " -q -", "w"); + if (f) { + fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data); + fclose(f); + } else + fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH); + } + } #endif /* XAUTH_PATH */ - /* Get the last component of the shell name. */ - cp = strrchr(shell, '/'); - if (cp) - cp++; - else - cp = shell; - } - - /* If we have no command, execute the shell. In this case, the shell name - to be passed in argv[0] is preceded by '-' to indicate that this is - a login shell. */ - if (!command) - { - if(!options.use_login) { - char buf[256]; - - /* Check for mail if we have a tty and it was enabled in server options. */ - if (ttyname && options.check_mail) { - char *mailbox; - struct stat mailstat; - mailbox = getenv("MAIL"); - if(mailbox != NULL) { - if(stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0) { - printf("No mail.\n"); - } else if(mailstat.st_mtime < mailstat.st_atime) { - printf("You have mail.\n"); - } else { - printf("You have new mail.\n"); - } - } - } - /* Start the shell. Set initial character to '-'. */ - buf[0] = '-'; - strncpy(buf + 1, cp, sizeof(buf) - 1); - buf[sizeof(buf) - 1] = 0; - /* Execute the shell. */ - argv[0] = buf; - argv[1] = NULL; - execve(shell, argv, env); - /* Executing the shell failed. */ - perror(shell); - exit(1); - - } else { - /* Launch login(1). */ - - execl(LOGIN_PROGRAM, "login", "-h", get_remote_ipaddr(), "-p", "-f", "--", pw->pw_name, NULL); - - /* Login couldn't be executed, die. */ - - perror("login"); - exit(1); - } - } - - /* Execute the command using the user's shell. This uses the -c option - to execute the command. */ - argv[0] = (char *)cp; - argv[1] = "-c"; - argv[2] = (char *)command; - argv[3] = NULL; - execve(shell, argv, env); - perror(shell); - exit(1); + /* Get the last component of the shell name. */ + cp = strrchr(shell, '/'); + if (cp) + cp++; + else + cp = shell; + } + /* If we have no command, execute the shell. In this case, the + shell name to be passed in argv[0] is preceded by '-' to + indicate that this is a login shell. */ + if (!command) { + if (!options.use_login) { + char buf[256]; + + /* Check for mail if we have a tty and it was + enabled in server options. */ + if (ttyname && options.check_mail) { + char *mailbox; + struct stat mailstat; + mailbox = getenv("MAIL"); + if (mailbox != NULL) { + if (stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0) + printf("No mail.\n"); + else if (mailstat.st_mtime < mailstat.st_atime) + printf("You have mail.\n"); + else + printf("You have new mail.\n"); + } + } + /* Start the shell. Set initial character to '-'. */ + buf[0] = '-'; + strncpy(buf + 1, cp, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = 0; + + /* Execute the shell. */ + argv[0] = buf; + argv[1] = NULL; + execve(shell, argv, env); + + /* Executing the shell failed. */ + perror(shell); + exit(1); + + } else { + /* Launch login(1). */ + + execl(LOGIN_PROGRAM, "login", "-h", get_remote_ipaddr(), + "-p", "-f", "--", pw->pw_name, NULL); + + /* Login couldn't be executed, die. */ + + perror("login"); + exit(1); + } + } + /* Execute the command using the user's shell. This uses the -c + option to execute the command. */ + argv[0] = (char *) cp; + argv[1] = "-c"; + argv[2] = (char *) command; + argv[3] = NULL; + execve(shell, argv, env); + perror(shell); + exit(1); } diff --git a/tildexpand.c b/tildexpand.c index 32266674..ea7032e9 100644 --- a/tildexpand.c +++ b/tildexpand.c @@ -1,15 +1,9 @@ /* - -tildexpand.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Wed Jul 12 01:07:36 1995 ylo - -*/ + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Wed Jul 12 01:07:36 1995 ylo + */ #include "includes.h" RCSID("$Id$"); @@ -17,54 +11,52 @@ RCSID("$Id$"); #include "xmalloc.h" #include "ssh.h" -/* Expands tildes in the file name. Returns data allocated by xmalloc. - Warning: this calls getpw*. */ - -char *tilde_expand_filename(const char *filename, uid_t my_uid) +/* + * Expands tildes in the file name. Returns data allocated by xmalloc. + * Warning: this calls getpw*. + */ +char * +tilde_expand_filename(const char *filename, uid_t my_uid) { - const char *cp; - unsigned int userlen; - char *expanded; - struct passwd *pw; - char user[100]; - - /* Return immediately if no tilde. */ - if (filename[0] != '~') - return xstrdup(filename); - - /* Skip the tilde. */ - filename++; - - /* Find where the username ends. */ - cp = strchr(filename, '/'); - if (cp) - userlen = cp - filename; /* Have something after username. */ - else - userlen = strlen(filename); /* Nothign after username. */ - if (userlen == 0) - pw = getpwuid(my_uid); /* Own home directory. */ - else - { - /* Tilde refers to someone elses home directory. */ - if (userlen > sizeof(user) - 1) - fatal("User name after tilde too long."); - memcpy(user, filename, userlen); - user[userlen] = 0; - pw = getpwnam(user); - } - - /* Check that we found the user. */ - if (!pw) - fatal("Unknown user %100s.", user); - - /* If referring to someones home directory, return it now. */ - if (!cp) - { /* Only home directory specified */ - return xstrdup(pw->pw_dir); - } - - /* Build a path combining the specified directory and path. */ - expanded = xmalloc(strlen(pw->pw_dir) + strlen(cp + 1) + 2); - sprintf(expanded, "%s/%s", pw->pw_dir, cp + 1); - return expanded; + const char *cp; + unsigned int userlen; + char *expanded; + struct passwd *pw; + char user[100]; + + /* Return immediately if no tilde. */ + if (filename[0] != '~') + return xstrdup(filename); + + /* Skip the tilde. */ + filename++; + + /* Find where the username ends. */ + cp = strchr(filename, '/'); + if (cp) + userlen = cp - filename; /* Something after username. */ + else + userlen = strlen(filename); /* Nothing after username. */ + if (userlen == 0) + pw = getpwuid(my_uid); /* Own home directory. */ + else { + /* Tilde refers to someone elses home directory. */ + if (userlen > sizeof(user) - 1) + fatal("User name after tilde too long."); + memcpy(user, filename, userlen); + user[userlen] = 0; + pw = getpwnam(user); + } + /* Check that we found the user. */ + if (!pw) + fatal("Unknown user %100s.", user); + + /* If referring to someones home directory, return it now. */ + if (!cp) { /* Only home directory specified */ + return xstrdup(pw->pw_dir); + } + /* Build a path combining the specified directory and path. */ + expanded = xmalloc(strlen(pw->pw_dir) + strlen(cp + 1) + 2); + sprintf(expanded, "%s/%s", pw->pw_dir, cp + 1); + return expanded; } diff --git a/ttymodes.c b/ttymodes.c index 6124620f..aae9b516 100644 --- a/ttymodes.c +++ b/ttymodes.c @@ -1,20 +1,13 @@ /* - -ttymodes.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Tue Mar 21 15:59:15 1995 ylo - -Encoding and decoding of terminal modes in a portable way. -Much of the format is defined in ttymodes.h; it is included multiple times -into this file with the appropriate macro definitions to generate the -suitable code. - -*/ + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Tue Mar 21 15:59:15 1995 ylo + * Encoding and decoding of terminal modes in a portable way. + * Much of the format is defined in ttymodes.h; it is included multiple times + * into this file with the appropriate macro definitions to generate the + * suitable code. + */ #include "includes.h" RCSID("$Id$"); @@ -23,211 +16,214 @@ RCSID("$Id$"); #include "ssh.h" #define TTY_OP_END 0 -#define TTY_OP_ISPEED 192 /* int follows */ -#define TTY_OP_OSPEED 193 /* int follows */ +#define TTY_OP_ISPEED 192 /* int follows */ +#define TTY_OP_OSPEED 193 /* int follows */ -/* Converts POSIX speed_t to a baud rate. The values of the constants - for speed_t are not themselves portable. */ - -static int speed_to_baud(speed_t speed) +/* + * Converts POSIX speed_t to a baud rate. The values of the + * constants for speed_t are not themselves portable. + */ +static int +speed_to_baud(speed_t speed) { - switch (speed) - { - case B0: - return 0; - case B50: - return 50; - case B75: - return 75; - case B110: - return 110; - case B134: - return 134; - case B150: - return 150; - case B200: - return 200; - case B300: - return 300; - case B600: - return 600; - case B1200: - return 1200; - case B1800: - return 1800; - case B2400: - return 2400; - case B4800: - return 4800; - case B9600: - return 9600; + switch (speed) { + case B0: + return 0; + case B50: + return 50; + case B75: + return 75; + case B110: + return 110; + case B134: + return 134; + case B150: + return 150; + case B200: + return 200; + case B300: + return 300; + case B600: + return 600; + case B1200: + return 1200; + case B1800: + return 1800; + case B2400: + return 2400; + case B4800: + return 4800; + case B9600: + return 9600; #ifdef B19200 - case B19200: - return 19200; + case B19200: + return 19200; #else /* B19200 */ #ifdef EXTA - case EXTA: - return 19200; + case EXTA: + return 19200; #endif /* EXTA */ #endif /* B19200 */ #ifdef B38400 - case B38400: - return 38400; + case B38400: + return 38400; #else /* B38400 */ #ifdef EXTB - case EXTB: - return 38400; + case EXTB: + return 38400; #endif /* EXTB */ #endif /* B38400 */ #ifdef B7200 - case B7200: - return 7200; + case B7200: + return 7200; #endif /* B7200 */ #ifdef B14400 - case B14400: - return 14400; + case B14400: + return 14400; #endif /* B14400 */ #ifdef B28800 - case B28800: - return 28800; + case B28800: + return 28800; #endif /* B28800 */ #ifdef B57600 - case B57600: - return 57600; + case B57600: + return 57600; #endif /* B57600 */ #ifdef B76800 - case B76800: - return 76800; + case B76800: + return 76800; #endif /* B76800 */ #ifdef B115200 - case B115200: - return 115200; + case B115200: + return 115200; #endif /* B115200 */ #ifdef B230400 - case B230400: - return 230400; + case B230400: + return 230400; #endif /* B230400 */ - default: - return 9600; - } + default: + return 9600; + } } -/* Converts a numeric baud rate to a POSIX speed_t. */ - -static speed_t baud_to_speed(int baud) +/* + * Converts a numeric baud rate to a POSIX speed_t. + */ +static speed_t +baud_to_speed(int baud) { - switch (baud) - { - case 0: - return B0; - case 50: - return B50; - case 75: - return B75; - case 110: - return B110; - case 134: - return B134; - case 150: - return B150; - case 200: - return B200; - case 300: - return B300; - case 600: - return B600; - case 1200: - return B1200; - case 1800: - return B1800; - case 2400: - return B2400; - case 4800: - return B4800; - case 9600: - return B9600; + switch (baud) { + case 0: + return B0; + case 50: + return B50; + case 75: + return B75; + case 110: + return B110; + case 134: + return B134; + case 150: + return B150; + case 200: + return B200; + case 300: + return B300; + case 600: + return B600; + case 1200: + return B1200; + case 1800: + return B1800; + case 2400: + return B2400; + case 4800: + return B4800; + case 9600: + return B9600; #ifdef B19200 - case 19200: - return B19200; + case 19200: + return B19200; #else /* B19200 */ #ifdef EXTA - case 19200: - return EXTA; + case 19200: + return EXTA; #endif /* EXTA */ #endif /* B19200 */ #ifdef B38400 - case 38400: - return B38400; + case 38400: + return B38400; #else /* B38400 */ #ifdef EXTB - case 38400: - return EXTB; + case 38400: + return EXTB; #endif /* EXTB */ #endif /* B38400 */ #ifdef B7200 - case 7200: - return B7200; + case 7200: + return B7200; #endif /* B7200 */ #ifdef B14400 - case 14400: - return B14400; + case 14400: + return B14400; #endif /* B14400 */ #ifdef B28800 - case 28800: - return B28800; + case 28800: + return B28800; #endif /* B28800 */ #ifdef B57600 - case 57600: - return B57600; + case 57600: + return B57600; #endif /* B57600 */ #ifdef B76800 - case 76800: - return B76800; + case 76800: + return B76800; #endif /* B76800 */ #ifdef B115200 - case 115200: - return B115200; + case 115200: + return B115200; #endif /* B115200 */ #ifdef B230400 - case 230400: - return B230400; + case 230400: + return B230400; #endif /* B230400 */ - default: - return B9600; - } + default: + return B9600; + } } -/* Encodes terminal modes for the terminal referenced by fd in a portable - manner, and appends the modes to a packet being constructed. */ - -void tty_make_modes(int fd) +/* + * Encodes terminal modes for the terminal referenced by fd + * in a portable manner, and appends the modes to a packet + * being constructed. + */ +void +tty_make_modes(int fd) { - struct termios tio; - int baud; - - /* Get the modes. */ - if (tcgetattr(fd, &tio) < 0) - { - packet_put_char(TTY_OP_END); - log("tcgetattr: %.100s", strerror(errno)); - return; - } - - /* Store input and output baud rates. */ - baud = speed_to_baud(cfgetospeed(&tio)); - packet_put_char(TTY_OP_OSPEED); - packet_put_int(baud); - baud = speed_to_baud(cfgetispeed(&tio)); - packet_put_char(TTY_OP_ISPEED); - packet_put_int(baud); - - /* Store values of mode flags. */ + struct termios tio; + int baud; + + /* Get the modes. */ + if (tcgetattr(fd, &tio) < 0) { + packet_put_char(TTY_OP_END); + log("tcgetattr: %.100s", strerror(errno)); + return; + } + /* Store input and output baud rates. */ + baud = speed_to_baud(cfgetospeed(&tio)); + packet_put_char(TTY_OP_OSPEED); + packet_put_int(baud); + baud = speed_to_baud(cfgetispeed(&tio)); + packet_put_char(TTY_OP_ISPEED); + packet_put_int(baud); + + /* Store values of mode flags. */ #define TTYCHAR(NAME, OP) \ packet_put_char(OP); packet_put_char(tio.c_cc[NAME]); #define TTYMODE(NAME, FIELD, OP) \ @@ -244,48 +240,50 @@ void tty_make_modes(int fd) #undef SGTTYMODE #undef SGTTYMODEN - /* Mark end of mode data. */ - packet_put_char(TTY_OP_END); + /* Mark end of mode data. */ + packet_put_char(TTY_OP_END); } -/* Decodes terminal modes for the terminal referenced by fd in a portable - manner from a packet being read. */ - -void tty_parse_modes(int fd, int *n_bytes_ptr) +/* + * Decodes terminal modes for the terminal referenced by fd in a portable + * manner from a packet being read. + */ +void +tty_parse_modes(int fd, int *n_bytes_ptr) { - struct termios tio; - int opcode, baud; - int n_bytes = 0; - int failure = 0; - - /* Get old attributes for the terminal. We will modify these flags. - I am hoping that if there are any machine-specific modes, they will - initially have reasonable values. */ - if (tcgetattr(fd, &tio) < 0) - failure = -1; - - for (;;) - { - n_bytes += 1; - opcode = packet_get_char(); - switch (opcode) - { - case TTY_OP_END: - goto set; - - case TTY_OP_ISPEED: - n_bytes += 4; - baud = packet_get_int(); - if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) < 0) - error("cfsetispeed failed for %d", baud); - break; - - case TTY_OP_OSPEED: - n_bytes += 4; - baud = packet_get_int(); - if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) < 0) - error("cfsetospeed failed for %d", baud); - break; + struct termios tio; + int opcode, baud; + int n_bytes = 0; + int failure = 0; + + /* + * Get old attributes for the terminal. We will modify these + * flags. I am hoping that if there are any machine-specific + * modes, they will initially have reasonable values. + */ + if (tcgetattr(fd, &tio) < 0) + failure = -1; + + for (;;) { + n_bytes += 1; + opcode = packet_get_char(); + switch (opcode) { + case TTY_OP_END: + goto set; + + case TTY_OP_ISPEED: + n_bytes += 4; + baud = packet_get_int(); + if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) < 0) + error("cfsetispeed failed for %d", baud); + break; + + case TTY_OP_OSPEED: + n_bytes += 4; + baud = packet_get_int(); + if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) < 0) + error("cfsetospeed failed for %d", baud); + break; #define TTYCHAR(NAME, OP) \ case OP: \ @@ -312,48 +310,51 @@ void tty_parse_modes(int fd, int *n_bytes_ptr) #undef SGTTYMODE #undef SGTTYMODEN - default: - debug("Ignoring unsupported tty mode opcode %d (0x%x)", - opcode, opcode); - /* Opcodes 0 to 127 are defined to have a one-byte argument. */ - if (opcode >= 0 && opcode < 128) - { - n_bytes += 1; - (void)packet_get_char(); - break; - } - else - { - /* Opcodes 128 to 159 are defined to have an integer argument. */ - if (opcode >= 128 && opcode < 160) - { - n_bytes += 4; - (void)packet_get_int(); - break; + default: + debug("Ignoring unsupported tty mode opcode %d (0x%x)", + opcode, opcode); + /* + * Opcodes 0 to 127 are defined to have + * a one-byte argument. + */ + if (opcode >= 0 && opcode < 128) { + n_bytes += 1; + (void) packet_get_char(); + break; + } else { + /* + * Opcodes 128 to 159 are defined to have + * an integer argument. + */ + if (opcode >= 128 && opcode < 160) { + n_bytes += 4; + (void) packet_get_int(); + break; + } + } + /* + * It is a truly undefined opcode (160 to 255). + * We have no idea about its arguments. So we + * must stop parsing. Note that some data may be + * left in the packet; hopefully there is nothing + * more coming after the mode data. + */ + log("parse_tty_modes: unknown opcode %d", opcode); + packet_integrity_check(0, 1, SSH_CMSG_REQUEST_PTY); + goto set; } - } - /* It is a truly undefined opcode (160 to 255). We have no idea - about its arguments. So we must stop parsing. Note that some - data may be left in the packet; hopefully there is nothing more - coming after the mode data. */ - log("parse_tty_modes: unknown opcode %d", opcode); - packet_integrity_check(0, 1, SSH_CMSG_REQUEST_PTY); - goto set; } - } - set: - if (*n_bytes_ptr != n_bytes) - { - *n_bytes_ptr = n_bytes; - return; /* Don't process bytes passed */ - } +set: + if (*n_bytes_ptr != n_bytes) { + *n_bytes_ptr = n_bytes; + return; /* Don't process bytes passed */ + } + if (failure == -1) + return; /* Packet parsed ok but tty stuff failed */ - if (failure == -1) - return; /* Packet parsed ok but tty stuff failed */ - - /* Set the new modes for the terminal. */ - if (tcsetattr(fd, TCSANOW, &tio) < 0) - log("Setting tty modes failed: %.100s", strerror(errno)); - return; + /* Set the new modes for the terminal. */ + if (tcsetattr(fd, TCSANOW, &tio) < 0) + log("Setting tty modes failed: %.100s", strerror(errno)); + return; } diff --git a/ttymodes.h b/ttymodes.h index 3db5f33f..75f85b05 100644 --- a/ttymodes.h +++ b/ttymodes.h @@ -1,138 +1,140 @@ /* - -ttymodes.h - -Author: Tatu Ylonen - SGTTY stuff contributed by Janne Snabb - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Tue Mar 21 15:42:09 1995 ylo - -*/ + * + * ttymodes.h + * + * Author: Tatu Ylonen + * SGTTY stuff contributed by Janne Snabb + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Tue Mar 21 15:42:09 1995 ylo + * + */ /* RCSID("$Id$"); */ /* The tty mode description is a stream of bytes. The stream consists of - opcode-arguments pairs. It is terminated by opcode TTY_OP_END (0). - Opcodes 1-127 have one-byte arguments. Opcodes 128-159 have integer - arguments. Opcodes 160-255 are not yet defined, and cause parsing to - stop (they should only be used after any other data). + * opcode-arguments pairs. It is terminated by opcode TTY_OP_END (0). + * Opcodes 1-127 have one-byte arguments. Opcodes 128-159 have integer + * arguments. Opcodes 160-255 are not yet defined, and cause parsing to + * stop (they should only be used after any other data). + * + * The client puts in the stream any modes it knows about, and the + * server ignores any modes it does not know about. This allows some degree + * of machine-independence, at least between systems that use a posix-like + * tty interface. The protocol can support other systems as well, but might + * require reimplementing as mode names would likely be different. + */ - The client puts in the stream any modes it knows about, and the - server ignores any modes it does not know about. This allows some degree - of machine-independence, at least between systems that use a posix-like - tty interface. The protocol can support other systems as well, but might - require reimplementing as mode names would likely be different. */ - -/* Some constants and prototypes are defined in packet.h; this file - is only intended for including from ttymodes.h. */ +/* + * Some constants and prototypes are defined in packet.h; this file + * is only intended for including from ttymodes.h. + */ -/* termios macro */ /* sgtty macro */ + /* termios macro *//* sgtty macro */ /* name, op */ -TTYCHAR(VINTR, 1) SGTTYCHAR(tiotc.t_intrc, 1) -TTYCHAR(VQUIT, 2) SGTTYCHAR(tiotc.t_quitc, 2) -TTYCHAR(VERASE, 3) SGTTYCHAR(tio.sg_erase, 3) +TTYCHAR(VINTR, 1) SGTTYCHAR(tiotc.t_intrc, 1) +TTYCHAR(VQUIT, 2) SGTTYCHAR(tiotc.t_quitc, 2) +TTYCHAR(VERASE, 3) SGTTYCHAR(tio.sg_erase, 3) #if defined(VKILL) -TTYCHAR(VKILL, 4) SGTTYCHAR(tio.sg_kill, 4) -#endif /* VKILL */ -TTYCHAR(VEOF, 5) SGTTYCHAR(tiotc.t_eofc, 5) +TTYCHAR(VKILL, 4) SGTTYCHAR(tio.sg_kill, 4) +#endif /* VKILL */ +TTYCHAR(VEOF, 5) SGTTYCHAR(tiotc.t_eofc, 5) #if defined(VEOL) -TTYCHAR(VEOL, 6) SGTTYCHAR(tiotc.t_brkc, 6) -#endif /* VEOL */ +TTYCHAR(VEOL, 6) SGTTYCHAR(tiotc.t_brkc, 6) +#endif /* VEOL */ #ifdef VEOL2 /* n/a */ TTYCHAR(VEOL2, 7) -#endif /* VEOL2 */ -TTYCHAR(VSTART, 8) SGTTYCHAR(tiotc.t_startc, 8) -TTYCHAR(VSTOP, 9) SGTTYCHAR(tiotc.t_stopc, 9) +#endif /* VEOL2 */ +TTYCHAR(VSTART, 8) SGTTYCHAR(tiotc.t_startc, 8) +TTYCHAR(VSTOP, 9) SGTTYCHAR(tiotc.t_stopc, 9) #if defined(VSUSP) -TTYCHAR(VSUSP, 10) SGTTYCHAR(tioltc.t_suspc, 10) -#endif /* VSUSP */ +TTYCHAR(VSUSP, 10) SGTTYCHAR(tioltc.t_suspc, 10) +#endif /* VSUSP */ #if defined(VDSUSP) -TTYCHAR(VDSUSP, 11) SGTTYCHAR(tioltc.t_dsuspc, 11) -#endif /* VDSUSP */ +TTYCHAR(VDSUSP, 11) SGTTYCHAR(tioltc.t_dsuspc, 11) +#endif /* VDSUSP */ #if defined(VREPRINT) -TTYCHAR(VREPRINT, 12) SGTTYCHAR(tioltc.t_rprntc, 12) -#endif /* VREPRINT */ +TTYCHAR(VREPRINT, 12) SGTTYCHAR(tioltc.t_rprntc, 12) +#endif /* VREPRINT */ #if defined(VWERASE) -TTYCHAR(VWERASE, 13) SGTTYCHAR(tioltc.t_werasc, 13) -#endif /* VWERASE */ +TTYCHAR(VWERASE, 13) SGTTYCHAR(tioltc.t_werasc, 13) +#endif /* VWERASE */ #if defined(VLNEXT) -TTYCHAR(VLNEXT, 14) SGTTYCHAR(tioltc.t_lnextc, 14) -#endif /* VLNEXT */ +TTYCHAR(VLNEXT, 14) SGTTYCHAR(tioltc.t_lnextc, 14) +#endif /* VLNEXT */ #if defined(VFLUSH) -TTYCHAR(VFLUSH, 15) SGTTYCHAR(tioltc.t_flushc, 15) -#endif /* VFLUSH */ +TTYCHAR(VFLUSH, 15) SGTTYCHAR(tioltc.t_flushc, 15) +#endif /* VFLUSH */ #ifdef VSWTCH TTYCHAR(VSWTCH, 16) /* n/a */ -#endif /* VSWTCH */ +#endif /* VSWTCH */ #if defined(VSTATUS) -TTYCHAR(VSTATUS, 17) SGTTYCHAR(tiots.tc_statusc, 17) -#endif /* VSTATUS */ +TTYCHAR(VSTATUS, 17) SGTTYCHAR(tiots.tc_statusc, 17) +#endif /* VSTATUS */ #ifdef VDISCARD TTYCHAR(VDISCARD, 18) /* n/a */ -#endif /* VDISCARD */ +#endif /* VDISCARD */ /* name, field, op */ -TTYMODE(IGNPAR, c_iflag, 30) /* n/a */ -TTYMODE(PARMRK, c_iflag, 31) /* n/a */ -TTYMODE(INPCK, c_iflag, 32) SGTTYMODEN(ANYP, tio.sg_flags, 32) -TTYMODE(ISTRIP, c_iflag, 33) SGTTYMODEN(LPASS8, tiolm, 33) -TTYMODE(INLCR, c_iflag, 34) /* n/a */ -TTYMODE(IGNCR, c_iflag, 35) /* n/a */ -TTYMODE(ICRNL, c_iflag, 36) SGTTYMODE(CRMOD, tio.sg_flags, 36) +TTYMODE(IGNPAR, c_iflag, 30) /* n/a */ +TTYMODE(PARMRK, c_iflag, 31) /* n/a */ +TTYMODE(INPCK, c_iflag, 32) SGTTYMODEN(ANYP, tio.sg_flags, 32) +TTYMODE(ISTRIP, c_iflag, 33) SGTTYMODEN(LPASS8, tiolm, 33) +TTYMODE(INLCR, c_iflag, 34) /* n/a */ +TTYMODE(IGNCR, c_iflag, 35) /* n/a */ +TTYMODE(ICRNL, c_iflag, 36) SGTTYMODE(CRMOD, tio.sg_flags, 36) #if defined(IUCLC) -TTYMODE(IUCLC, c_iflag, 37) SGTTYMODE(LCASE, tio.sg_flags, 37) +TTYMODE(IUCLC, c_iflag, 37) SGTTYMODE(LCASE, tio.sg_flags, 37) #endif -TTYMODE(IXON, c_iflag, 38) /* n/a */ -TTYMODE(IXANY, c_iflag, 39) SGTTYMODEN(LDECCTQ, tiolm, 39) -TTYMODE(IXOFF, c_iflag, 40) SGTTYMODE(TANDEM, tio.sg_flags, 40) +TTYMODE(IXON, c_iflag, 38) /* n/a */ +TTYMODE(IXANY, c_iflag, 39) SGTTYMODEN(LDECCTQ, tiolm, 39) +TTYMODE(IXOFF, c_iflag, 40) SGTTYMODE(TANDEM, tio.sg_flags, 40) #ifdef IMAXBEL -TTYMODE(IMAXBEL,c_iflag, 41) /* n/a */ -#endif /* IMAXBEL */ +TTYMODE(IMAXBEL, c_iflag, 41) /* n/a */ +#endif /* IMAXBEL */ -TTYMODE(ISIG, c_lflag, 50) /* n/a */ -TTYMODE(ICANON, c_lflag, 51) SGTTYMODEN(CBREAK, tio.sg_flags, 51) +TTYMODE(ISIG, c_lflag, 50) /* n/a */ +TTYMODE(ICANON, c_lflag, 51) SGTTYMODEN(CBREAK, tio.sg_flags, 51) #ifdef XCASE -TTYMODE(XCASE, c_lflag, 52) /* n/a */ +TTYMODE(XCASE, c_lflag, 52) /* n/a */ #endif -TTYMODE(ECHO, c_lflag, 53) SGTTYMODE(ECHO, tio.sg_flags, 53) -TTYMODE(ECHOE, c_lflag, 54) SGTTYMODE(LCRTERA, tiolm, 54) -TTYMODE(ECHOK, c_lflag, 55) SGTTYMODE(LCRTKIL, tiolm, 55) -TTYMODE(ECHONL, c_lflag, 56) /* n/a */ -TTYMODE(NOFLSH, c_lflag, 57) SGTTYMODE(LNOFLSH, tiolm, 57) -TTYMODE(TOSTOP, c_lflag, 58) SGTTYMODE(LTOSTOP, tiolm, 58) +TTYMODE(ECHO, c_lflag, 53) SGTTYMODE(ECHO, tio.sg_flags, 53) +TTYMODE(ECHOE, c_lflag, 54) SGTTYMODE(LCRTERA, tiolm, 54) +TTYMODE(ECHOK, c_lflag, 55) SGTTYMODE(LCRTKIL, tiolm, 55) +TTYMODE(ECHONL, c_lflag, 56) /* n/a */ +TTYMODE(NOFLSH, c_lflag, 57) SGTTYMODE(LNOFLSH, tiolm, 57) +TTYMODE(TOSTOP, c_lflag, 58) SGTTYMODE(LTOSTOP, tiolm, 58) #ifdef IEXTEN TTYMODE(IEXTEN, c_lflag, 59) /* n/a */ -#endif /* IEXTEN */ +#endif /* IEXTEN */ #if defined(ECHOCTL) -TTYMODE(ECHOCTL,c_lflag, 60) SGTTYMODE(LCTLECH, tiolm, 60) -#endif /* ECHOCTL */ +TTYMODE(ECHOCTL, c_lflag, 60) SGTTYMODE(LCTLECH, tiolm, 60) +#endif /* ECHOCTL */ #ifdef ECHOKE -TTYMODE(ECHOKE, c_lflag, 61) /* n/a */ -#endif /* ECHOKE */ +TTYMODE(ECHOKE, c_lflag, 61) /* n/a */ +#endif /* ECHOKE */ #if defined(PENDIN) -TTYMODE(PENDIN, c_lflag, 62) SGTTYMODE(LPENDIN, tiolm, 62) -#endif /* PENDIN */ +TTYMODE(PENDIN, c_lflag, 62) SGTTYMODE(LPENDIN, tiolm, 62) +#endif /* PENDIN */ -TTYMODE(OPOST, c_oflag, 70) /* n/a */ +TTYMODE(OPOST, c_oflag, 70) /* n/a */ #if defined(OLCUC) -TTYMODE(OLCUC, c_oflag, 71) SGTTYMODE(LCASE, tio.sg_flags, 71) +TTYMODE(OLCUC, c_oflag, 71) SGTTYMODE(LCASE, tio.sg_flags, 71) #endif -TTYMODE(ONLCR, c_oflag, 72) SGTTYMODE(CRMOD, tio.sg_flags, 72) +TTYMODE(ONLCR, c_oflag, 72) SGTTYMODE(CRMOD, tio.sg_flags, 72) #ifdef OCRNL -TTYMODE(OCRNL, c_oflag, 73) /* n/a */ +TTYMODE(OCRNL, c_oflag, 73) /* n/a */ #endif #ifdef ONOCR -TTYMODE(ONOCR, c_oflag, 74) /* n/a */ +TTYMODE(ONOCR, c_oflag, 74) /* n/a */ #endif #ifdef ONLRET -TTYMODE(ONLRET, c_oflag, 75) /* n/a */ +TTYMODE(ONLRET, c_oflag, 75) /* n/a */ #endif -TTYMODE(CS7, c_cflag, 90) /* n/a */ -TTYMODE(CS8, c_cflag, 91) SGTTYMODE(LPASS8, tiolm, 91) -TTYMODE(PARENB, c_cflag, 92) /* n/a */ -TTYMODE(PARODD, c_cflag, 93) SGTTYMODE(ODDP, tio.sg_flags, 93) - +TTYMODE(CS7, c_cflag, 90) /* n/a */ +TTYMODE(CS8, c_cflag, 91) SGTTYMODE(LPASS8, tiolm, 91) +TTYMODE(PARENB, c_cflag, 92) /* n/a */ +TTYMODE(PARODD, c_cflag, 93) SGTTYMODE(ODDP, tio.sg_flags, 93) diff --git a/uidswap.c b/uidswap.c index de9e4625..95ff18d6 100644 --- a/uidswap.c +++ b/uidswap.c @@ -1,17 +1,10 @@ /* - -uidswap.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sat Sep 9 01:56:14 1995 ylo - -Code for uid-swapping. - -*/ + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Sat Sep 9 01:56:14 1995 ylo + * Code for uid-swapping. + */ #include "includes.h" RCSID("$Id$"); @@ -19,14 +12,14 @@ RCSID("$Id$"); #include "ssh.h" #include "uidswap.h" -/* Note: all these functions must work in all of the following cases: - - 1. euid=0, ruid=0 - 2. euid=0, ruid!=0 - 3. euid!=0, ruid!=0 - - Additionally, they must work regardless of whether the system has - POSIX saved uids or not. */ +/* + * Note: all these functions must work in all of the following cases: + * 1. euid=0, ruid=0 + * 2. euid=0, ruid!=0 + * 3. euid!=0, ruid!=0 + * Additionally, they must work regardless of whether the system has + * POSIX saved uids or not. + */ #ifdef _POSIX_SAVED_IDS /* Lets assume that posix saved ids also work with seteuid, even though that @@ -37,59 +30,57 @@ RCSID("$Id$"); /* Saved effective uid. */ static uid_t saved_euid = 0; -/* Temporarily changes to the given uid. If the effective user id is not - root, this does nothing. This call cannot be nested. */ - -void temporarily_use_uid(uid_t uid) +/* + * Temporarily changes to the given uid. If the effective user + * id is not root, this does nothing. This call cannot be nested. + */ +void +temporarily_use_uid(uid_t uid) { #ifdef SAVED_IDS_WORK_WITH_SETEUID + /* Save the current euid. */ + saved_euid = geteuid(); - /* Save the current euid. */ - saved_euid = geteuid(); - - /* Set the effective uid to the given (unprivileged) uid. */ - if (seteuid(uid) == -1) - debug("seteuid %d: %.100s", (int)uid, strerror(errno)); - + /* Set the effective uid to the given (unprivileged) uid. */ + if (seteuid(uid) == -1) + debug("seteuid %d: %.100s", (int) uid, strerror(errno)); #else /* SAVED_IDS_WORK_WITH_SETUID */ + /* Propagate the privileged uid to all of our uids. */ + if (setuid(geteuid()) < 0) + debug("setuid %d: %.100s", (int) geteuid(), strerror(errno)); - /* Propagate the privileged uid to all of our uids. */ - if (setuid(geteuid()) < 0) - debug("setuid %d: %.100s", (int)geteuid(), strerror(errno)); - - /* Set the effective uid to the given (unprivileged) uid. */ - if (seteuid(uid) == -1) - debug("seteuid %d: %.100s", (int)uid, strerror(errno)); - + /* Set the effective uid to the given (unprivileged) uid. */ + if (seteuid(uid) == -1) + debug("seteuid %d: %.100s", (int) uid, strerror(errno)); #endif /* SAVED_IDS_WORK_WITH_SETEUID */ - } -/* Restores to the original uid. */ - -void restore_uid() +/* + * Restores to the original uid. + */ +void +restore_uid() { #ifdef SAVED_IDS_WORK_WITH_SETEUID - - /* Set the effective uid back to the saved uid. */ - if (seteuid(saved_euid) < 0) - debug("seteuid %d: %.100s", (int)saved_euid, strerror(errno)); - + /* Set the effective uid back to the saved uid. */ + if (seteuid(saved_euid) < 0) + debug("seteuid %d: %.100s", (int) saved_euid, strerror(errno)); #else /* SAVED_IDS_WORK_WITH_SETEUID */ - - /* We are unable to restore the real uid to its unprivileged value. */ - /* Propagate the real uid (usually more privileged) to effective uid - as well. */ - setuid(getuid()); - + /* We are unable to restore the real uid to its unprivileged + value. */ + /* Propagate the real uid (usually more privileged) to effective + uid as well. */ + setuid(getuid()); #endif /* SAVED_IDS_WORK_WITH_SETEUID */ } -/* Permanently sets all uids to the given uid. This cannot be called while - temporarily_use_uid is effective. */ - -void permanently_set_uid(uid_t uid) +/* + * Permanently sets all uids to the given uid. This cannot be + * called while temporarily_use_uid is effective. + */ +void +permanently_set_uid(uid_t uid) { - if (setuid(uid) < 0) - debug("setuid %d: %.100s", (int)uid, strerror(errno)); + if (setuid(uid) < 0) + debug("setuid %d: %.100s", (int) uid, strerror(errno)); } diff --git a/uidswap.h b/uidswap.h index af4f924f..4755710d 100644 --- a/uidswap.h +++ b/uidswap.h @@ -1,30 +1,36 @@ /* - -uidswap.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Sat Sep 9 01:43:15 1995 ylo -Last modified: Sat Sep 9 02:34:04 1995 ylo - -*/ + * + * uidswap.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Sat Sep 9 01:43:15 1995 ylo + * Last modified: Sat Sep 9 02:34:04 1995 ylo + * + */ #ifndef UIDSWAP_H #define UIDSWAP_H -/* Temporarily changes to the given uid. If the effective user id is not - root, this does nothing. This call cannot be nested. */ -void temporarily_use_uid(uid_t uid); +/* + * Temporarily changes to the given uid. If the effective user id is not + * root, this does nothing. This call cannot be nested. + */ +void temporarily_use_uid(uid_t uid); -/* Restores the original effective user id after temporarily_use_uid(). - This should only be called while temporarily_use_uid is effective. */ -void restore_uid(); +/* + * Restores the original effective user id after temporarily_use_uid(). + * This should only be called while temporarily_use_uid is effective. + */ +void restore_uid(); -/* Permanently sets all uids to the given uid. This cannot be called while - temporarily_use_uid is effective. This must also clear any saved uids. */ -void permanently_set_uid(uid_t uid); +/* + * Permanently sets all uids to the given uid. This cannot be called while + * temporarily_use_uid is effective. This must also clear any saved uids. + */ +void permanently_set_uid(uid_t uid); -#endif /* UIDSWAP_H */ +#endif /* UIDSWAP_H */ diff --git a/xmalloc.c b/xmalloc.c index 4a8d190c..de1bbae1 100644 --- a/xmalloc.c +++ b/xmalloc.c @@ -1,56 +1,53 @@ /* - -xmalloc.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Mon Mar 20 21:23:10 1995 ylo - -Versions of malloc and friends that check their results, and never return -failure (they call fatal if they encounter an error). - -*/ + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Mon Mar 20 21:23:10 1995 ylo + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). + */ #include "includes.h" RCSID("$Id$"); #include "ssh.h" -void *xmalloc(size_t size) +void * +xmalloc(size_t size) { - void *ptr = malloc(size); - if (ptr == NULL) - fatal("xmalloc: out of memory (allocating %d bytes)", (int)size); - return ptr; + void *ptr = malloc(size); + if (ptr == NULL) + fatal("xmalloc: out of memory (allocating %d bytes)", (int) size); + return ptr; } -void *xrealloc(void *ptr, size_t new_size) +void * +xrealloc(void *ptr, size_t new_size) { - void *new_ptr; - - if (ptr == NULL) - fatal("xrealloc: NULL pointer given as argument"); - new_ptr = realloc(ptr, new_size); - if (new_ptr == NULL) - fatal("xrealloc: out of memory (new_size %d bytes)", (int)new_size); - return new_ptr; + void *new_ptr; + + if (ptr == NULL) + fatal("xrealloc: NULL pointer given as argument"); + new_ptr = realloc(ptr, new_size); + if (new_ptr == NULL) + fatal("xrealloc: out of memory (new_size %d bytes)", (int) new_size); + return new_ptr; } -void xfree(void *ptr) +void +xfree(void *ptr) { - if (ptr == NULL) - fatal("xfree: NULL pointer given as argument"); - free(ptr); + if (ptr == NULL) + fatal("xfree: NULL pointer given as argument"); + free(ptr); } -char *xstrdup(const char *str) +char * +xstrdup(const char *str) { - int len = strlen(str) + 1; + int len = strlen(str) + 1; - char *cp = xmalloc(len); - strlcpy(cp, str, len); - return cp; + char *cp = xmalloc(len); + strlcpy(cp, str, len); + return cp; } diff --git a/xmalloc.h b/xmalloc.h index 28d8852e..22cdafda 100644 --- a/xmalloc.h +++ b/xmalloc.h @@ -1,18 +1,18 @@ /* - -xmalloc.h - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Mon Mar 20 22:09:17 1995 ylo - -Versions of malloc and friends that check their results, and never return -failure (they call fatal if they encounter an error). - -*/ + * + * xmalloc.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Created: Mon Mar 20 22:09:17 1995 ylo + * + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). + * + */ /* RCSID("$Id$"); */ @@ -20,15 +20,15 @@ failure (they call fatal if they encounter an error). #define XMALLOC_H /* Like malloc, but calls fatal() if out of memory. */ -void *xmalloc(size_t size); +void *xmalloc(size_t size); /* Like realloc, but calls fatal() if out of memory. */ -void *xrealloc(void *ptr, size_t new_size); +void *xrealloc(void *ptr, size_t new_size); /* Frees memory allocated using xmalloc or xrealloc. */ -void xfree(void *ptr); +void xfree(void *ptr); /* Allocates memory using xmalloc, and copies the string into that memory. */ -char *xstrdup(const char *str); +char *xstrdup(const char *str); -#endif /* XMALLOC_H */ +#endif /* XMALLOC_H */ -- 2.45.1