diff options
70 files changed, 9125 insertions, 1547 deletions
diff --git a/crypto/openssh/README.openssh2 b/crypto/openssh/README.openssh2 new file mode 100644 index 0000000..12c90aa --- /dev/null +++ b/crypto/openssh/README.openssh2 @@ -0,0 +1,44 @@ +$Id: README.openssh2,v 1.8 2000/05/07 18:30:03 markus Exp $ + +howto: + 1) generate server key: + $ ssh-keygen -d -f /etc/ssh_host_dsa_key -N '' + 2) enable ssh2: + server: add 'Protocol 2,1' to /etc/sshd_config + client: ssh -o 'Protocol 2,1', or add to .ssh/config + 3) DSA authentication similar to RSA (add keys to ~/.ssh/authorized_keys2) + interop w/ ssh.com dsa-keys: + ssh-keygen -f /key/from/ssh.com -X >> ~/.ssh/authorized_keys2 + and vice versa + ssh-keygen -f /privatekey/from/openssh -x > ~/.ssh2/mykey.pub + echo Key mykey.pub >> ~/.ssh2/authorization + +works: + secsh-transport: works w/o rekey + proposal exchange, i.e. different enc/mac/comp per direction + encryption: blowfish-cbc, 3des-cbc, arcfour, cast128-cbc + mac: hmac-md5, hmac-sha1, (hmac-ripemd160) + compression: zlib, none + secsh-userauth: passwd and pubkey with DSA + secsh-connection: pty+shell or command, flow control works (window adjust) + tcp-forwarding: -L works, -R incomplete + x11-fwd + dss/dsa: host key database in ~/.ssh/known_hosts2 + client interops w/ sshd2, lshd + server interops w/ ssh2, lsh, ssh.com's Windows client, SecureCRT, F-Secure SSH Client 4.0, SecureFX (secure ftp) + server supports multiple concurrent sessions (e.g. with SSH.com Windows client) +todo: + re-keying + secsh-connection features: + tcp-forwarding, agent-fwd + auth other than passwd, and DSA-pubkey: + keyboard-interactive, (PGP-pubkey?) + config + server-auth w/ old host-keys + cleanup + advanced key storage? + keynote + sftp + +-markus +$Date: 2000/05/07 18:30:03 $ diff --git a/crypto/openssh/auth-rhosts.c b/crypto/openssh/auth-rhosts.c index 8432428..3393b86 100644 --- a/crypto/openssh/auth-rhosts.c +++ b/crypto/openssh/auth-rhosts.c @@ -1,22 +1,22 @@ /* - * + * * auth-rhosts.c - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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: auth-rhosts.c,v 1.12 1999/12/27 10:46:11 markus Exp $"); +RCSID("$Id: auth-rhosts.c,v 1.13 2000/04/14 10:30:29 markus Exp $"); #include "packet.h" #include "ssh.h" @@ -30,7 +30,7 @@ RCSID("$Id: auth-rhosts.c,v 1.12 1999/12/27 10:46:11 markus Exp $"); * based on the file, and returns zero otherwise. */ -int +int check_rhosts_file(const char *filename, const char *hostname, const char *ipaddr, const char *client_user, const char *server_user) @@ -146,7 +146,7 @@ check_rhosts_file(const char *filename, const char *hostname, * /etc/hosts.equiv will be considered (.rhosts and .shosts are ignored). */ -int +int auth_rhosts(struct passwd *pw, const char *client_user) { extern ServerOptions options; diff --git a/crypto/openssh/auth-skey.c b/crypto/openssh/auth-skey.c index 3610a67..5f3fe9e 100644 --- a/crypto/openssh/auth-skey.c +++ b/crypto/openssh/auth-skey.c @@ -1,16 +1,16 @@ #include "includes.h" -RCSID("$Id: auth-skey.c,v 1.5 1999/12/06 19:04:57 deraadt Exp $"); +RCSID("$Id: auth-skey.c,v 1.6 2000/04/14 10:30:29 markus Exp $"); #include "ssh.h" #include "packet.h" #include <sha1.h> -/* +/* * try skey authentication, - * return 1 on success, 0 on failure, -1 if skey is not available + * return 1 on success, 0 on failure, -1 if skey is not available */ -int +int auth_skey_password(struct passwd * pw, const char *password) { if (strncasecmp(password, "s/key", 5) == 0) { @@ -43,18 +43,18 @@ auth_skey_password(struct passwd * pw, const char *password) */ static u_int32_t hash_collapse(s) - u_char *s; + u_char *s; { - int len, target; + int len, target; u_int32_t i; if ((strlen(s) % sizeof(u_int32_t)) == 0) - target = strlen(s); /* Multiple of 4 */ + target = strlen(s); /* Multiple of 4 */ else target = strlen(s) - (strlen(s) % sizeof(u_int32_t)); - + for (i = 0, len = 0; len < target; len += 4) - i ^= ROUND(s + len); + i ^= ROUND(s + len); return i; } diff --git a/crypto/openssh/auth.c b/crypto/openssh/auth.c new file mode 100644 index 0000000..d3425a2 --- /dev/null +++ b/crypto/openssh/auth.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * Copyright (c) 2000 Markus Friedl. All rights reserved. + */ + +#include "includes.h" +RCSID("$OpenBSD: auth.c,v 1.6 2000/04/26 21:28:31 markus Exp $"); + +#include "xmalloc.h" +#include "rsa.h" +#include "ssh.h" +#include "pty.h" +#include "packet.h" +#include "buffer.h" +#include "cipher.h" +#include "mpaux.h" +#include "servconf.h" +#include "compat.h" +#include "channels.h" +#include "match.h" + +#include "bufaux.h" +#include "ssh2.h" +#include "auth.h" +#include "session.h" +#include "dispatch.h" + + +/* import */ +extern ServerOptions options; +extern char *forced_command; + +/* + * 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. + * If the user's shell is not executable, false will be returned. + * Otherwise true is returned. + */ +int +allowed_user(struct passwd * pw) +{ + struct stat st; + struct group *grp; + int i; + + /* Shouldn't be called if pw is NULL, but better safe than sorry... */ + if (!pw) + return 0; + + /* deny if shell does not exists or is not executable */ + if (stat(pw->pw_shell, &st) != 0) + return 0; + if (!((st.st_mode & S_IFREG) && (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)))) + return 0; + + /* 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; +} diff --git a/crypto/openssh/auth.h b/crypto/openssh/auth.h new file mode 100644 index 0000000..72126e0 --- /dev/null +++ b/crypto/openssh/auth.h @@ -0,0 +1,17 @@ +#ifndef AUTH_H +#define AUTH_H + +void do_authentication(void); +void do_authentication2(void); + +struct passwd * +auth_get_user(void); + +int allowed_user(struct passwd * pw);; + +#define AUTH_FAIL_MAX 6 +#define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2) +#define AUTH_FAIL_MSG "Too many authentication failures for %.100s" + +#endif + diff --git a/crypto/openssh/auth1.c b/crypto/openssh/auth1.c new file mode 100644 index 0000000..38114d8 --- /dev/null +++ b/crypto/openssh/auth1.c @@ -0,0 +1,468 @@ +/* + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + */ + +#include "includes.h" +RCSID("$OpenBSD: auth1.c,v 1.2 2000/04/29 18:11:52 markus Exp $"); + +#include "xmalloc.h" +#include "rsa.h" +#include "ssh.h" +#include "packet.h" +#include "buffer.h" +#include "cipher.h" +#include "mpaux.h" +#include "servconf.h" +#include "compat.h" +#include "auth.h" +#include "session.h" + +/* import */ +extern ServerOptions options; +extern char *forced_command; + +/* + * convert ssh auth msg type into description + */ +char * +get_authname(int type) +{ + static char buf[1024]; + 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 + } + snprintf(buf, sizeof buf, "bad-auth-msg-%d", type); + return buf; +} + +/* + * The user does not exist or access is denied, + * but fake indication that authentication is needed. + */ +void +do_fake_authloop1(char *user) +{ + 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 + unsigned int dlen; + char *password, *skeyinfo; + password = NULL; + /* Try to send a fake s/key challenge. */ + if (options.skey_authentication == 1 && + (skeyinfo = skey_fake_keyinfo(user)) != NULL) { + if (type == SSH_CMSG_AUTH_TIS) { + packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); + packet_put_string(skeyinfo, strlen(skeyinfo)); + packet_send(); + packet_write_wait(); + continue; + } else if (type == SSH_CMSG_AUTH_PASSWORD && + options.password_authentication && + (password = packet_get_string(&dlen)) != NULL && + dlen == 5 && + strncasecmp(password, "s/key", 5) == 0 ) { + packet_send_debug(skeyinfo); + } + } + if (password != NULL) + xfree(password); +#endif + 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(); +} + +/* + * read packets and try to authenticate local user *pw. + * return if authentication is successfull + */ +void +do_authloop(struct passwd * pw) +{ + int attempt = 0; + unsigned int bits; + RSA *client_host_key; + BIGNUM *n; + char *client_user, *password; + char user[1024]; + unsigned int dlen; + int plen, nlen, elen; + unsigned int ulen; + int type = 0; + void (*authlog) (const char *fmt,...) = verbose; + + /* Indicate that authentication is needed. */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + + for (attempt = 1;; attempt++) { + int authenticated = 0; + strlcpy(user, "", sizeof user); + + /* 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(); */ + 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(); */ + 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) { + 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); + xfree(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 = RSA_new(); + if (client_host_key == NULL) + fatal("RSA_new failed"); + client_host_key->e = BN_new(); + client_host_key->n = BN_new(); + if (client_host_key->e == NULL || client_host_key->n == NULL) + fatal("BN_new failed"); + 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)) + log("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); + RSA_free(client_host_key); + + snprintf(user, sizeof user, " ruser %s", client_user); + xfree(client_user); + 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); + + /* Try authentication with the password. */ + authenticated = auth_password(pw, password); + + memset(password, 0, strlen(password)); + xfree(password); + break; + +#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; + } + + /* + * Check if the user is logging in as root and root logins + * are disallowed. + * Note that root login is allowed for forced commands. + */ + if (authenticated && pw->pw_uid == 0 && !options.permit_root_login) { + if (forced_command) { + log("Root login accepted for forced command."); + } else { + authenticated = 0; + log("ROOT LOGIN REFUSED FROM %.200s", + get_canonical_hostname()); + } + } + + /* 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); + + 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(); + } +} + +/* + * Performs authentication of an incoming connection. Session key has already + * been exchanged and encryption is enabled. + */ +void +do_authentication() +{ + struct passwd *pw, pwcopy; + int plen; + unsigned int ulen; + char *user; + + /* Get the name of the user that we wish to log in as. */ + packet_read_expect(&plen, SSH_CMSG_USER); + + /* Get the user name. */ + user = packet_get_string(&ulen); + packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); + + setproctitle("%s", user); + +#ifdef AFS + /* 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_authloop1(user); + xfree(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; + + /* + * 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.", pw->pw_name); + + /* If the user has no password, accept authentication immediately. */ + if (options.password_authentication && +#ifdef KRB4 + (!options.kerberos_authentication || options.kerberos_or_local_passwd) && +#endif /* KRB4 */ + 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); + } + + /* The user has been authenticated and accepted. */ + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); + + /* Perform session preparation. */ + do_authenticated(pw); +} diff --git a/crypto/openssh/auth2.c b/crypto/openssh/auth2.c new file mode 100644 index 0000000..3f8c254 --- /dev/null +++ b/crypto/openssh/auth2.c @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "includes.h" +RCSID("$OpenBSD: auth2.c,v 1.8 2000/05/08 17:42:24 markus Exp $"); + +#include <openssl/dsa.h> +#include <openssl/rsa.h> +#include <openssl/evp.h> + +#include "xmalloc.h" +#include "rsa.h" +#include "ssh.h" +#include "pty.h" +#include "packet.h" +#include "buffer.h" +#include "cipher.h" +#include "servconf.h" +#include "compat.h" +#include "channels.h" +#include "bufaux.h" +#include "ssh2.h" +#include "auth.h" +#include "session.h" +#include "dispatch.h" +#include "auth.h" +#include "key.h" +#include "kex.h" + +#include "dsa.h" +#include "uidswap.h" + +/* import */ +extern ServerOptions options; +extern unsigned char *session_id2; +extern int session_id2_len; + +/* protocol */ + +void input_service_request(int type, int plen); +void input_userauth_request(int type, int plen); +void protocol_error(int type, int plen); + +/* auth */ +int ssh2_auth_none(struct passwd *pw); +int ssh2_auth_password(struct passwd *pw); +int ssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen); + +/* helper */ +struct passwd* auth_set_user(char *u, char *s); +int user_dsa_key_allowed(struct passwd *pw, Key *key); + +typedef struct Authctxt Authctxt; +struct Authctxt { + char *user; + char *service; + struct passwd pw; + int valid; +}; +static Authctxt *authctxt = NULL; +static int userauth_success = 0; + +/* + * loop until userauth_success == TRUE + */ + +void +do_authentication2() +{ + /* turn off skey/kerberos, not supported by SSH2 */ +#ifdef SKEY + options.skey_authentication = 0; +#endif +#ifdef KRB4 + options.kerberos_authentication = 0; +#endif + + dispatch_init(&protocol_error); + dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); + dispatch_run(DISPATCH_BLOCK, &userauth_success); + do_authenticated2(); +} + +void +protocol_error(int type, int plen) +{ + log("auth: protocol error: type %d plen %d", type, plen); + packet_start(SSH2_MSG_UNIMPLEMENTED); + packet_put_int(0); + packet_send(); + packet_write_wait(); +} + +void +input_service_request(int type, int plen) +{ + unsigned int len; + int accept = 0; + char *service = packet_get_string(&len); + packet_done(); + + if (strcmp(service, "ssh-userauth") == 0) { + if (!userauth_success) { + accept = 1; + /* now we can handle user-auth requests */ + dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); + } + } + /* XXX all other service requests are denied */ + + if (accept) { + packet_start(SSH2_MSG_SERVICE_ACCEPT); + packet_put_cstring(service); + packet_send(); + packet_write_wait(); + } else { + debug("bad service request %s", service); + packet_disconnect("bad service request %s", service); + } + xfree(service); +} + +void +input_userauth_request(int type, int plen) +{ + static void (*authlog) (const char *fmt,...) = verbose; + static int attempt = 0; + unsigned int len, rlen; + int authenticated = 0; + char *raw, *user, *service, *method, *authmsg = NULL; + struct passwd *pw; + + if (++attempt == AUTH_FAIL_MAX) + packet_disconnect("too many failed userauth_requests"); + + raw = packet_get_raw(&rlen); + if (plen != rlen) + fatal("plen != rlen"); + user = packet_get_string(&len); + service = packet_get_string(&len); + method = packet_get_string(&len); + debug("userauth-request for user %s service %s method %s", user, service, method); + + /* XXX we only allow the ssh-connection service */ + pw = auth_set_user(user, service); + if (pw && strcmp(service, "ssh-connection")==0) { + if (strcmp(method, "none") == 0) { + authenticated = ssh2_auth_none(pw); + } else if (strcmp(method, "password") == 0) { + authenticated = ssh2_auth_password(pw); + } else if (strcmp(method, "publickey") == 0) { + authenticated = ssh2_auth_pubkey(pw, raw, rlen); + } + } + if (authenticated && pw && pw->pw_uid == 0 && !options.permit_root_login) { + authenticated = 0; + log("ROOT LOGIN REFUSED FROM %.200s", + get_canonical_hostname()); + } + + /* Raise logging level */ + if (authenticated == 1 || + attempt == AUTH_FAIL_LOG || + strcmp(method, "password") == 0) + authlog = log; + + /* Log before sending the reply */ + if (authenticated == 1) { + authmsg = "Accepted"; + } else if (authenticated == 0) { + authmsg = "Failed"; + } else { + authmsg = "Postponed"; + } + authlog("%s %s for %.200s from %.200s port %d ssh2", + authmsg, + method, + pw && pw->pw_uid == 0 ? "ROOT" : user, + get_remote_ipaddr(), + get_remote_port()); + + /* XXX todo: check if multiple auth methods are needed */ + if (authenticated == 1) { + /* turn off userauth */ + dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error); + packet_start(SSH2_MSG_USERAUTH_SUCCESS); + packet_send(); + packet_write_wait(); + /* now we can break out */ + userauth_success = 1; + } else if (authenticated == 0) { + packet_start(SSH2_MSG_USERAUTH_FAILURE); + packet_put_cstring("publickey,password"); /* XXX dynamic */ + packet_put_char(0); /* XXX partial success, unused */ + packet_send(); + packet_write_wait(); + } + + xfree(service); + xfree(user); + xfree(method); +} + +int +ssh2_auth_none(struct passwd *pw) +{ + packet_done(); + return auth_password(pw, ""); +} +int +ssh2_auth_password(struct passwd *pw) +{ + char *password; + int authenticated = 0; + int change; + unsigned int len; + change = packet_get_char(); + if (change) + log("password change not supported"); + password = packet_get_string(&len); + packet_done(); + if (options.password_authentication && + auth_password(pw, password) == 1) + authenticated = 1; + memset(password, 0, len); + xfree(password); + return authenticated; +} +int +ssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen) +{ + Buffer b; + Key *key; + char *pkalg, *pkblob, *sig; + unsigned int alen, blen, slen; + int have_sig; + int authenticated = 0; + + if (options.dsa_authentication == 0) { + debug("pubkey auth disabled"); + return 0; + } + if (datafellows & SSH_BUG_PUBKEYAUTH) { + log("bug compatibility with ssh-2.0.13 pubkey not implemented"); + return 0; + } + have_sig = packet_get_char(); + pkalg = packet_get_string(&alen); + if (strcmp(pkalg, KEX_DSS) != 0) { + xfree(pkalg); + log("bad pkalg %s", pkalg); /*XXX*/ + return 0; + } + pkblob = packet_get_string(&blen); + key = dsa_key_from_blob(pkblob, blen); + if (key != NULL) { + if (have_sig) { + sig = packet_get_string(&slen); + packet_done(); + buffer_init(&b); + buffer_append(&b, session_id2, session_id2_len); + buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); + if (slen + 4 > rlen) + fatal("bad rlen/slen"); + buffer_append(&b, raw, rlen - slen - 4); +#ifdef DEBUG_DSS + buffer_dump(&b); +#endif + /* test for correct signature */ + if (user_dsa_key_allowed(pw, key) && + dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1) + authenticated = 1; + buffer_clear(&b); + xfree(sig); + } else { + packet_done(); + debug("test key..."); + /* test whether pkalg/pkblob are acceptable */ + /* XXX fake reply and always send PK_OK ? */ + /* + * XXX this allows testing whether a user is allowed + * to login: if you happen to have a valid pubkey this + * message is sent. the message is NEVER sent at all + * if a user is not allowed to login. is this an + * issue? -markus + */ + if (user_dsa_key_allowed(pw, key)) { + packet_start(SSH2_MSG_USERAUTH_PK_OK); + packet_put_string(pkalg, alen); + packet_put_string(pkblob, blen); + packet_send(); + packet_write_wait(); + authenticated = -1; + } + } + key_free(key); + } + xfree(pkalg); + xfree(pkblob); + return authenticated; +} + +/* set and get current user */ + +struct passwd* +auth_get_user(void) +{ + return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL; +} + +struct passwd* +auth_set_user(char *u, char *s) +{ + struct passwd *pw, *copy; + + if (authctxt == NULL) { + authctxt = xmalloc(sizeof(*authctxt)); + authctxt->valid = 0; + authctxt->user = xstrdup(u); + authctxt->service = xstrdup(s); + setproctitle("%s", u); + pw = getpwnam(u); + if (!pw || !allowed_user(pw)) { + log("auth_set_user: illegal user %s", u); + return NULL; + } + copy = &authctxt->pw; + memset(copy, 0, sizeof(*copy)); + copy->pw_name = xstrdup(pw->pw_name); + copy->pw_passwd = xstrdup(pw->pw_passwd); + copy->pw_uid = pw->pw_uid; + copy->pw_gid = pw->pw_gid; + copy->pw_dir = xstrdup(pw->pw_dir); + copy->pw_shell = xstrdup(pw->pw_shell); + authctxt->valid = 1; + } else { + if (strcmp(u, authctxt->user) != 0 || + strcmp(s, authctxt->service) != 0) { + log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)", + u, s, authctxt->user, authctxt->service); + return NULL; + } + } + return auth_get_user(); +} + +/* return 1 if user allows given key */ +int +user_dsa_key_allowed(struct passwd *pw, Key *key) +{ + char line[8192], file[1024]; + int found_key = 0; + unsigned int bits = -1; + FILE *f; + unsigned long linenum = 0; + struct stat st; + Key *found; + + /* 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_KEYS2); + + /* 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(); + 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, "DSA 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, + "DSA authentication refused for %.100s: " + "bad ownership or modes for '%s'.", + pw->pw_name, line); + fail = 1; + break; + } + } + } + if (fail) { + log(buf); + fclose(f); + restore_uid(); + return 0; + } + } + found_key = 0; + found = key_new(KEY_DSA); + + while (fgets(line, sizeof(line), f)) { + char *cp; + linenum++; + /* Skip leading whitespace, empty and comment lines. */ + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '\n' || *cp == '#') + continue; + bits = key_read(found, &cp); + if (bits == 0) + continue; + if (key_equal(found, key)) { + found_key = 1; + debug("matching key found: file %s, line %ld", + file, linenum); + break; + } + } + restore_uid(); + fclose(f); + key_free(found); + return found_key; +} diff --git a/crypto/openssh/authfd.h b/crypto/openssh/authfd.h index 2af4858..fbeea23 100644 --- a/crypto/openssh/authfd.h +++ b/crypto/openssh/authfd.h @@ -1,19 +1,19 @@ /* - * + * * authfd.h - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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: authfd.h,v 1.6 1999/11/24 19:53:44 markus Exp $"); */ +/* RCSID("$Id: authfd.h,v 1.7 2000/04/14 10:30:30 markus Exp $"); */ #ifndef AUTHFD_H #define AUTHFD_H @@ -67,7 +67,7 @@ void ssh_close_authentication_connection(AuthenticationConnection * ac); * integers before the call, and free the comment after a successful call * (before calling ssh_get_next_identity). */ -int +int ssh_get_first_identity(AuthenticationConnection * connection, BIGNUM * e, BIGNUM * n, char **comment); @@ -77,13 +77,13 @@ ssh_get_first_identity(AuthenticationConnection * connection, * function. This returns 0 if there are no more identities. The caller * must free comment after a successful return. */ -int +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 +int ssh_decrypt_challenge(AuthenticationConnection * auth, BIGNUM * e, BIGNUM * n, BIGNUM * challenge, unsigned char session_id[16], @@ -95,7 +95,7 @@ ssh_decrypt_challenge(AuthenticationConnection * auth, * be used by normal applications. This returns true if the identity was * successfully added. */ -int +int ssh_add_identity(AuthenticationConnection * connection, RSA * key, const char *comment); diff --git a/crypto/openssh/authfile.h b/crypto/openssh/authfile.h new file mode 100644 index 0000000..afec27d --- /dev/null +++ b/crypto/openssh/authfile.h @@ -0,0 +1,36 @@ +#ifndef AUTHFILE_H +#define AUTHFILE_H + +/* + * Saves the authentication (private) key in a file, encrypting it with + * passphrase. + * For RSA keys: 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, + Key * private_key, const char *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, Key * 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 with + * xfree. + */ +int +load_private_key(const char *filename, const char *passphrase, + Key * private_key, char **comment_return); + +#endif diff --git a/crypto/openssh/bufaux.h b/crypto/openssh/bufaux.h index 124a265..8ba92f8 100644 --- a/crypto/openssh/bufaux.h +++ b/crypto/openssh/bufaux.h @@ -1,17 +1,17 @@ /* - * + * * bufaux.h - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved - * + * * Created: Wed Mar 29 02:18:23 1995 ylo - * + * */ -/* RCSID("$Id: bufaux.h,v 1.4 1999/11/24 19:53:44 markus Exp $"); */ +/* RCSID("$Id: bufaux.h,v 1.6 2000/04/14 10:30:30 markus Exp $"); */ #ifndef BUFAUX_H #define BUFAUX_H @@ -23,9 +23,11 @@ * by (bits+7)/8 bytes of binary data, msb first. */ void buffer_put_bignum(Buffer * buffer, BIGNUM * value); +void buffer_put_bignum2(Buffer * buffer, BIGNUM * value); /* Retrieves an BIGNUM from the buffer. */ int buffer_get_bignum(Buffer * buffer, BIGNUM * value); +int buffer_get_bignum2(Buffer *buffer, BIGNUM * value); /* Returns an integer from the buffer (4 bytes, msb first). */ unsigned int buffer_get_int(Buffer * buffer); @@ -51,5 +53,6 @@ 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_cstring(Buffer *buffer, const char *s); #endif /* BUFAUX_H */ diff --git a/crypto/openssh/buffer.c b/crypto/openssh/buffer.c index 6557934..4d83433 100644 --- a/crypto/openssh/buffer.c +++ b/crypto/openssh/buffer.c @@ -1,20 +1,20 @@ /* - * + * * buffer.c - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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: buffer.c,v 1.4 1999/11/24 19:53:44 markus Exp $"); +RCSID("$Id: buffer.c,v 1.6 2000/04/14 10:30:30 markus Exp $"); #include "xmalloc.h" #include "buffer.h" @@ -22,7 +22,7 @@ RCSID("$Id: buffer.c,v 1.4 1999/11/24 19:53:44 markus Exp $"); /* Initializes the buffer structure. */ -void +void buffer_init(Buffer *buffer) { buffer->alloc = 4096; @@ -33,7 +33,7 @@ buffer_init(Buffer *buffer) /* Frees any memory used for the buffer. */ -void +void buffer_free(Buffer *buffer) { memset(buffer->buf, 0, buffer->alloc); @@ -45,7 +45,7 @@ buffer_free(Buffer *buffer) * zero the memory. */ -void +void buffer_clear(Buffer *buffer) { buffer->offset = 0; @@ -54,7 +54,7 @@ buffer_clear(Buffer *buffer) /* Appends data to the buffer, expanding it if necessary. */ -void +void buffer_append(Buffer *buffer, const char *data, unsigned int len) { char *cp; @@ -68,7 +68,7 @@ buffer_append(Buffer *buffer, const char *data, unsigned int len) * to the allocated region. */ -void +void buffer_append_space(Buffer *buffer, char **datap, unsigned int len) { /* If the buffer is empty, start using it from the beginning. */ @@ -102,7 +102,7 @@ restart: /* Returns the number of bytes of data in the buffer. */ -unsigned int +unsigned int buffer_len(Buffer *buffer) { return buffer->end - buffer->offset; @@ -110,32 +110,32 @@ buffer_len(Buffer *buffer) /* Gets data from the beginning of the buffer. */ -void +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"); + 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 +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"); + fatal("buffer_consume: trying to get more bytes than in buffer"); buffer->offset += bytes; } /* Consumes the given number of bytes from the end of the buffer. */ -void +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"); + fatal("buffer_consume_end: trying to get more bytes than in buffer"); buffer->end -= bytes; } @@ -149,7 +149,7 @@ buffer_ptr(Buffer *buffer) /* Dumps the contents of the buffer to stderr. */ -void +void buffer_dump(Buffer *buffer) { int i; diff --git a/crypto/openssh/buffer.h b/crypto/openssh/buffer.h index 04efc25..be4fdc3 100644 --- a/crypto/openssh/buffer.h +++ b/crypto/openssh/buffer.h @@ -1,19 +1,19 @@ /* - * + * * buffer.h - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved - * + * * Created: Sat Mar 18 04:12:25 1995 ylo - * + * * Code for manipulating FIFO buffers. - * + * */ -/* RCSID("$Id: buffer.h,v 1.3 1999/11/24 19:53:44 markus Exp $"); */ +/* RCSID("$Id: buffer.h,v 1.4 2000/04/14 10:30:30 markus Exp $"); */ #ifndef BUFFER_H #define BUFFER_H diff --git a/crypto/openssh/canohost.c b/crypto/openssh/canohost.c index fcf743c..a73f8d0 100644 --- a/crypto/openssh/canohost.c +++ b/crypto/openssh/canohost.c @@ -1,20 +1,20 @@ /* - * + * * canohost.c - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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: canohost.c,v 1.11 2000/01/04 13:41:32 markus Exp $"); +RCSID("$Id: canohost.c,v 1.12 2000/04/14 10:30:30 markus Exp $"); #include "packet.h" #include "xmalloc.h" @@ -241,7 +241,7 @@ get_sock_port(int sock, int local) /* Returns remote/local port number for the current connection. */ -int +int get_port(int local) { /* @@ -255,13 +255,13 @@ get_port(int local) return get_sock_port(packet_get_connection_in(), local); } -int +int get_peer_port(int sock) { return get_sock_port(sock, 0); } -int +int get_remote_port() { return get_port(0); diff --git a/crypto/openssh/channels.c b/crypto/openssh/channels.c index 62b6a22..5bf1e5b 100644 --- a/crypto/openssh/channels.c +++ b/crypto/openssh/channels.c @@ -1,22 +1,23 @@ /* - * + * * channels.c - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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. - * + * + * SSH2 support added by Markus Friedl. */ #include "includes.h" -RCSID("$Id: channels.c,v 1.39 2000/03/16 20:56:14 markus Exp $"); +RCSID("$Id: channels.c,v 1.57 2000/05/08 17:42:24 markus Exp $"); #include "ssh.h" #include "packet.h" @@ -31,12 +32,20 @@ RCSID("$Id: channels.c,v 1.39 2000/03/16 20:56:14 markus Exp $"); #include "nchan.h" #include "compat.h" +#include "ssh2.h" + /* Maximum number of fake X11 displays to try. */ #define MAX_DISPLAYS 1000 /* Max len of agent socket */ #define MAX_SOCKET_NAME 100 +/* default window/packet sizes for tcp/x11-fwd-channel */ +#define CHAN_TCP_WINDOW_DEFAULT (8*1024) +#define CHAN_TCP_PACKET_DEFAULT (CHAN_TCP_WINDOW_DEFAULT/2) +#define CHAN_X11_WINDOW_DEFAULT (4*1024) +#define CHAN_X11_PACKET_DEFAULT (CHAN_X11_WINDOW_DEFAULT/2) + /* * Pointer to an array containing all allocated channels. The array is * dynamically extended as needed. @@ -81,8 +90,9 @@ unsigned int x11_fake_data_len; * network (which might be behind a firewall). */ typedef struct { - char *host; /* Host name. */ - u_short port; /* Port number. */ + char *host_to_connect; /* Connect to 'host'. */ + u_short port_to_connect; /* Connect to 'port'. */ + u_short listen_port; /* Remote side should listen port number. */ } ForwardPermission; /* List of all permitted host/port pairs to connect. */ @@ -101,7 +111,7 @@ static int have_hostname_in_open = 0; /* Sets specific protocol options. */ -void +void channel_set_options(int hostname_in_open) { have_hostname_in_open = hostname_in_open; @@ -113,30 +123,92 @@ channel_set_options(int hostname_in_open) * and the server has no way to know but to trust the client anyway. */ -void +void channel_permit_all_opens() { all_opens_permitted = 1; } +/* lookup channel by id */ + +Channel * +channel_lookup(int id) +{ + Channel *c; + if (id < 0 && id > channels_alloc) { + log("channel_lookup: %d: bad id", id); + return NULL; + } + c = &channels[id]; + if (c->type == SSH_CHANNEL_FREE) { + log("channel_lookup: %d: bad id: channel free", id); + return NULL; + } + return c; +} + +void +set_nonblock(int fd) +{ + int val; + val = fcntl(fd, F_GETFL, 0); + if (val < 0) { + error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); + return; + } + if (val & O_NONBLOCK) + return; + debug("fd %d setting O_NONBLOCK", fd); + val |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, val) == -1) + error("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, strerror(errno)); +} + +/* + * Register filedescriptors for a channel, used when allocating a channel or + * when the channel consumer/producer is ready, e.g. shell exec'd + */ + +void +channel_register_fds(Channel *c, int rfd, int wfd, int efd, int extusage) +{ + /* Update the maximum file descriptor value. */ + if (rfd > channel_max_fd_value) + channel_max_fd_value = rfd; + if (wfd > channel_max_fd_value) + channel_max_fd_value = wfd; + if (efd > channel_max_fd_value) + channel_max_fd_value = efd; + /* XXX set close-on-exec -markus */ + + c->rfd = rfd; + c->wfd = wfd; + c->sock = (rfd == wfd) ? rfd : -1; + c->efd = efd; + c->extended_usage = extusage; + if (rfd != -1) + set_nonblock(rfd); + if (wfd != -1) + set_nonblock(wfd); + if (efd != -1) + set_nonblock(efd); +} + /* * 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_new(char *ctype, int type, int rfd, int wfd, int efd, + int window, int maxpack, int extusage, 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; - /* XXX set close-on-exec -markus */ - /* Do initial allocation if this is the first call. */ if (channels_alloc == 0) { + chan_init(); channels_alloc = 10; channels = xmalloc(channels_alloc * sizeof(Channel)); for (i = 0; i < channels_alloc; i++) @@ -167,411 +239,743 @@ channel_allocate(int type, int sock, char *remote_name) c = &channels[found]; buffer_init(&c->input); buffer_init(&c->output); + buffer_init(&c->extended); chan_init_iostates(c); + channel_register_fds(c, rfd, wfd, efd, extusage); c->self = found; c->type = type; - c->sock = sock; + c->ctype = ctype; + c->local_window = window; + c->local_window_max = window; + c->local_consumed = 0; + c->local_maxpacket = maxpack; c->remote_id = -1; c->remote_name = remote_name; + c->remote_window = 0; + c->remote_maxpacket = 0; + c->cb_fn = NULL; + c->cb_arg = NULL; + c->cb_event = 0; + c->dettach_user = NULL; debug("channel %d: new [%s]", found, remote_name); return found; } +/* old interface XXX */ +int +channel_allocate(int type, int sock, char *remote_name) +{ + return channel_new("", type, sock, sock, -1, 0, 0, 0, remote_name); +} -/* Free the channel and close its socket. */ -void -channel_free(int channel) +/* Close all channel fd/socket. */ + +void +channel_close_fds(Channel *c) { - if (channel < 0 || channel >= channels_alloc || - channels[channel].type == SSH_CHANNEL_FREE) - packet_disconnect("channel free: bad local channel %d", channel); + if (c->sock != -1) { + close(c->sock); + c->sock = -1; + } + if (c->rfd != -1) { + close(c->rfd); + c->rfd = -1; + } + if (c->wfd != -1) { + close(c->wfd); + c->wfd = -1; + } + if (c->efd != -1) { + close(c->efd); + c->efd = -1; + } +} + +/* Free the channel and close its fd/socket. */ - 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; +void +channel_free(int id) +{ + Channel *c = channel_lookup(id); + if (c == NULL) + packet_disconnect("channel free: bad local channel %d", id); + debug("channel_free: channel %d: status: %s", id, channel_open_message()); + if (c->dettach_user != NULL) { + debug("channel_free: channel %d: dettaching channel user", id); + c->dettach_user(c->self, NULL); + } + if (c->sock != -1) + shutdown(c->sock, SHUT_RDWR); + channel_close_fds(c); + buffer_free(&c->input); + buffer_free(&c->output); + buffer_free(&c->extended); + c->type = SSH_CHANNEL_FREE; + if (c->remote_name) { + xfree(c->remote_name); + c->remote_name = NULL; } } /* - * This is called just before select() to add any bits relevant to channels - * in the select bitmasks. + * 'channel_pre*' are called just before select() to add any bits relevant to + * channels in the select bitmasks. + */ +/* + * 'channel_post*': perform any appropriate operations for channels which + * have events pending. */ +typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset); +chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; +chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; -void -channel_prepare_select(fd_set * readset, fd_set * writeset) +void +channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset) { - int i; - Channel *ch; - unsigned char *ucp; - unsigned int proto_len, data_len; + FD_SET(c->sock, readset); +} - 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; +void +channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (buffer_len(&c->input) < packet_get_maxsize()) + FD_SET(c->sock, readset); + if (buffer_len(&c->output) > 0) + FD_SET(c->sock, writeset); +} - 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; +void +channel_pre_open_15(Channel *c, fd_set * readset, fd_set * writeset) +{ + /* test whether sockets are 'alive' for read/write */ + if (c->istate == CHAN_INPUT_OPEN) + if (buffer_len(&c->input) < packet_get_maxsize()) + FD_SET(c->sock, readset); + if (c->ostate == CHAN_OUTPUT_OPEN || + c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + if (buffer_len(&c->output) > 0) { + FD_SET(c->sock, writeset); + } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + chan_obuf_empty(c); + } + } +} - 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.", ch->self); - break; - } - break; +void +channel_pre_open_20(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (c->istate == CHAN_INPUT_OPEN && + c->remote_window > 0 && + buffer_len(&c->input) < c->remote_window) + FD_SET(c->rfd, readset); + if (c->ostate == CHAN_OUTPUT_OPEN || + c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + if (buffer_len(&c->output) > 0) { + FD_SET(c->wfd, writeset); + } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + chan_obuf_empty(c); + } + } + /** XXX check close conditions, too */ + if (c->efd != -1) { + if (c->extended_usage == CHAN_EXTENDED_WRITE && + buffer_len(&c->extended) > 0) + FD_SET(c->efd, writeset); + else if (c->extended_usage == CHAN_EXTENDED_READ && + buffer_len(&c->extended) < c->remote_window) + FD_SET(c->efd, readset); + } +} - 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; +void +channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (buffer_len(&c->input) == 0) { + packet_start(SSH_MSG_CHANNEL_CLOSE); + packet_put_int(c->remote_id); + packet_send(); + c->type = SSH_CHANNEL_CLOSED; + debug("Closing channel %d after input drain.", c->self); + } +} - 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; +void +channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (buffer_len(&c->output) == 0) + channel_free(c->self); + else + FD_SET(c->sock, writeset); +} - /* 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; - } +/* + * 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. + * XXX All this happens at the client side. + */ +int +x11_open_helper(Channel *c) +{ + unsigned char *ucp; + unsigned int proto_len, data_len; - /* 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 the fixed size part of the packet is in buffer. */ + if (buffer_len(&c->output) < 12) + return 0; + + /* Parse the lengths of variable-length fields. */ + ucp = (unsigned char *) buffer_ptr(&c->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]); + return -1; + } - /* 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); + /* Check if the whole packet is in buffer. */ + if (buffer_len(&c->output) < + 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) + return 0; - /* Start normal processing for the channel. */ - ch->type = SSH_CHANNEL_OPEN; - goto redo; + /* 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."); + return -1; + } + /* 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."); + return -1; + } + /* 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); + return -1; + } + /* + * 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); + return 1; +} - 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(); +void +channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) +{ + int ret = x11_open_helper(c); + if (ret == 1) { + /* Start normal processing for the channel. */ + c->type = SSH_CHANNEL_OPEN; + channel_pre_open_13(c, readset, writeset); + } else if (ret == -1) { + /* + * We have received an X11 connection that has bad + * authentication information. + */ + log("X11 connection rejected because of wrong authentication.\r\n"); + buffer_clear(&c->input); + buffer_clear(&c->output); + close(c->sock); + c->sock = -1; + c->type = SSH_CHANNEL_CLOSED; + packet_start(SSH_MSG_CHANNEL_CLOSE); + packet_put_int(c->remote_id); + packet_send(); + } +} + +void +channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) +{ + int ret = x11_open_helper(c); + if (ret == 1) { + c->type = SSH_CHANNEL_OPEN; + if (compat20) + channel_pre_open_20(c, readset, writeset); + else + channel_pre_open_15(c, readset, writeset); + } else if (ret == -1) { + debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); + chan_read_failed(c); /** force close? */ + chan_write_failed(c); + debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); + } +} + +/* This is our fake X11 server socket. */ +void +channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) +{ + struct sockaddr addr; + int newsock, newch; + socklen_t addrlen; + char buf[16384], *remote_hostname; + int remote_port; + + if (FD_ISSET(c->sock, readset)) { + debug("X11 connection requested."); + addrlen = sizeof(addr); + newsock = accept(c->sock, &addr, &addrlen); + if (newsock < 0) { + error("accept: %.100s", strerror(errno)); + return; + } + remote_hostname = get_remote_hostname(newsock); + remote_port = get_peer_port(newsock); + snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", + remote_hostname, remote_port); + + newch = channel_new("x11", + SSH_CHANNEL_OPENING, newsock, newsock, -1, + c->local_window_max, c->local_maxpacket, + 0, xstrdup(buf)); + if (compat20) { + packet_start(SSH2_MSG_CHANNEL_OPEN); + packet_put_cstring("x11"); + packet_put_int(newch); + packet_put_int(c->local_window_max); + packet_put_int(c->local_maxpacket); + /* originator host and port */ + packet_put_cstring(remote_hostname); + if (datafellows & SSH_BUG_X11FWD) { + debug("ssh2 x11 bug compat mode"); } 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); + packet_put_int(remote_port); } - break; - - case SSH_CHANNEL_FREE: - default: - continue; + packet_send(); + } else { + packet_start(SSH_SMSG_X11_OPEN); + packet_put_int(newch); + if (have_hostname_in_open) + packet_put_string(buf, strlen(buf)); + packet_send(); } + xfree(remote_hostname); } } /* - * After select, perform any appropriate operations for channels which have - * events pending. + * This socket is listening for connections to a forwarded TCP/IP port. */ - -void -channel_after_select(fd_set * readset, fd_set * writeset) +void +channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) { struct sockaddr addr; - int newsock, i, newch, len; + int newsock, newch; socklen_t addrlen; - 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(); + char buf[1024], *remote_hostname; + int remote_port; + + if (FD_ISSET(c->sock, readset)) { + debug("Connection to port %d forwarding " + "to %.100s port %d requested.", + c->listening_port, c->path, c->host_port); + addrlen = sizeof(addr); + newsock = accept(c->sock, &addr, &addrlen); + if (newsock < 0) { + error("accept: %.100s", strerror(errno)); + return; + } + remote_hostname = get_remote_hostname(newsock); + remote_port = get_peer_port(newsock); + snprintf(buf, sizeof buf, + "listen port %d for %.100s port %d, " + "connect from %.200s port %d", + c->listening_port, c->path, c->host_port, + remote_hostname, remote_port); + newch = channel_new("direct-tcpip", + SSH_CHANNEL_OPENING, newsock, newsock, -1, + c->local_window_max, c->local_maxpacket, + 0, xstrdup(buf)); + if (compat20) { + packet_start(SSH2_MSG_CHANNEL_OPEN); + packet_put_cstring("direct-tcpip"); + packet_put_int(newch); + packet_put_int(c->local_window_max); + packet_put_int(c->local_maxpacket); + /* target host and port */ + packet_put_string(c->path, strlen(c->path)); + packet_put_int(c->host_port); + /* originator host and port */ + packet_put_cstring(remote_hostname); + packet_put_int(remote_port); + packet_send(); + } else { + packet_start(SSH_MSG_PORT_OPEN); + packet_put_int(newch); + packet_put_string(c->path, strlen(c->path)); + packet_put_int(c->host_port); + if (have_hostname_in_open) { + packet_put_string(buf, strlen(buf)); } - break; + packet_send(); + } + xfree(remote_hostname); + } +} - 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 port %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 for %.100s port %d, connect from %.200s port %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; +/* + * This is the authentication agent socket listening for connections from + * clients. + */ +void +channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) +{ + struct sockaddr addr; + int newsock, newch; + socklen_t addrlen; - case SSH_CHANNEL_AUTH_SOCKET: - /* - * This is the authentication agent socket listening - * for connections from clients. - */ - if (FD_ISSET(ch->sock, readset)) { - addrlen = sizeof(addr); - newsock = accept(ch->sock, &addr, &addrlen); - if (newsock < 0) { - error("accept from auth socket: %.100s", strerror(errno)); - break; - } - newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, - xstrdup("accepted auth socket")); - packet_start(SSH_SMSG_AGENT_OPEN); - packet_put_int(newch); - packet_send(); - } - break; + if (FD_ISSET(c->sock, readset)) { + addrlen = sizeof(addr); + newsock = accept(c->sock, &addr, &addrlen); + if (newsock < 0) { + error("accept from auth socket: %.100s", strerror(errno)); + return; + } + newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, + xstrdup("accepted auth socket")); + packet_start(SSH_SMSG_AGENT_OPEN); + packet_put_int(newch); + packet_send(); + } +} - 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. - */ +int +channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) +{ + char buf[16*1024]; + int len; - /* - * 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); + if (c->rfd != -1 && + FD_ISSET(c->rfd, readset)) { + len = read(c->rfd, buf, sizeof(buf)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) + return 1; + if (len <= 0) { + debug("channel %d: read<=0 rfd %d len %d", + c->self, c->rfd, len); + if (compat13) { + buffer_consume(&c->output, buffer_len(&c->output)); + c->type = SSH_CHANNEL_INPUT_DRAINING; + debug("Channel %d status set to input draining.", c->self); + } else { + chan_read_failed(c); } - /* 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); + return -1; + } + buffer_append(&c->input, buf, len); + } + return 1; +} +int +channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) +{ + int len; + + /* Send buffered output data to the socket. */ + if (c->wfd != -1 && + FD_ISSET(c->wfd, writeset) && + buffer_len(&c->output) > 0) { + len = write(c->wfd, buffer_ptr(&c->output), + buffer_len(&c->output)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) + return 1; + if (len <= 0) { + if (compat13) { + buffer_consume(&c->output, buffer_len(&c->output)); + debug("Channel %d status set to input draining.", c->self); + c->type = SSH_CHANNEL_INPUT_DRAINING; + } else { + chan_write_failed(c); } - 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); + return -1; + } + buffer_consume(&c->output, len); + if (compat20 && len > 0) { + c->local_consumed += len; + } + } + return 1; +} +int +channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) +{ + char buf[16*1024]; + int len; + +/** XXX handle drain efd, too */ + if (c->efd != -1) { + if (c->extended_usage == CHAN_EXTENDED_WRITE && + FD_ISSET(c->efd, writeset) && + buffer_len(&c->extended) > 0) { + len = write(c->efd, buffer_ptr(&c->extended), + buffer_len(&c->extended)); + debug("channel %d: written %d to efd %d", + c->self, len, c->efd); + if (len > 0) { + buffer_consume(&c->extended, len); + c->local_consumed += len; } - break; + } else if (c->extended_usage == CHAN_EXTENDED_READ && + FD_ISSET(c->efd, readset)) { + len = read(c->efd, buf, sizeof(buf)); + debug("channel %d: read %d from efd %d", + c->self, len, c->efd); + if (len == 0) { + debug("channel %d: closing efd %d", + c->self, c->efd); + close(c->efd); + c->efd = -1; + } else if (len > 0) + buffer_append(&c->extended, buf, len); + } + } + return 1; +} +int +channel_check_window(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && + c->local_window < c->local_window_max/2 && + c->local_consumed > 0) { + packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); + packet_put_int(c->remote_id); + packet_put_int(c->local_consumed); + packet_send(); + debug("channel %d: window %d sent adjust %d", + c->self, c->local_window, + c->local_consumed); + c->local_window += c->local_consumed; + c->local_consumed = 0; + } + return 1; +} - case SSH_CHANNEL_X11_OPEN: - case SSH_CHANNEL_FREE: - default: +void +channel_post_open_1(Channel *c, fd_set * readset, fd_set * writeset) +{ + channel_handle_rfd(c, readset, writeset); + channel_handle_wfd(c, readset, writeset); +} + +void +channel_post_open_2(Channel *c, fd_set * readset, fd_set * writeset) +{ + channel_handle_rfd(c, readset, writeset); + channel_handle_wfd(c, readset, writeset); + channel_handle_efd(c, readset, writeset); + channel_check_window(c, readset, writeset); +} + +void +channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) +{ + int len; + /* Send buffered output data to the socket. */ + if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { + len = write(c->sock, buffer_ptr(&c->output), + buffer_len(&c->output)); + if (len <= 0) + buffer_consume(&c->output, buffer_len(&c->output)); + else + buffer_consume(&c->output, len); + } +} + +void +channel_handler_init_20(void) +{ + channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_20; + channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; + channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; + + channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_2; + channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; +} + +void +channel_handler_init_13(void) +{ + channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; + channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; + channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; + channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; + + channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1; + channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; + channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; + channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; +} + +void +channel_handler_init_15(void) +{ + channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_15; + channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; + channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; + + channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; + channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; + channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1; +} + +void +channel_handler_init(void) +{ + int i; + for(i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { + channel_pre[i] = NULL; + channel_post[i] = NULL; + } + if (compat20) + channel_handler_init_20(); + else if (compat13) + channel_handler_init_13(); + else + channel_handler_init_15(); +} + +void +channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) +{ + static int did_init = 0; + int i; + Channel *c; + + if (!did_init) { + channel_handler_init(); + did_init = 1; + } + for (i = 0; i < channels_alloc; i++) { + c = &channels[i]; + if (c->type == SSH_CHANNEL_FREE) continue; - } + if (ftab[c->type] == NULL) + continue; + (*ftab[c->type])(c, readset, writeset); + chan_delete_if_full_closed(c); } } +void +channel_prepare_select(fd_set * readset, fd_set * writeset) +{ + channel_handler(channel_pre, readset, writeset); +} + +void +channel_after_select(fd_set * readset, fd_set * writeset) +{ + channel_handler(channel_post, readset, writeset); +} + /* If there is data to send to the connection, send some of it now. */ -void +void channel_output_poll() { int len, i; - Channel *ch; + Channel *c; for (i = 0; i < channels_alloc; i++) { - ch = &channels[i]; + c = &channels[i]; /* We are only interested in channels that can have buffered incoming data. */ if (compat13) { - if (ch->type != SSH_CHANNEL_OPEN && - ch->type != SSH_CHANNEL_INPUT_DRAINING) + if (c->type != SSH_CHANNEL_OPEN && + c->type != SSH_CHANNEL_INPUT_DRAINING) continue; } else { - if (ch->type != SSH_CHANNEL_OPEN) + if (c->type != SSH_CHANNEL_OPEN) continue; - if (ch->istate != CHAN_INPUT_OPEN && - ch->istate != CHAN_INPUT_WAIT_DRAIN) + if (c->istate != CHAN_INPUT_OPEN && + c->istate != CHAN_INPUT_WAIT_DRAIN) continue; } + if (compat20 && + (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { + debug("channel: %d: no data after CLOSE", c->self); + continue; + } /* Get the amount of buffered data for this channel. */ - len = buffer_len(&ch->input); + len = buffer_len(&c->input); if (len > 0) { /* Send some data for the other side over the secure connection. */ - if (packet_is_interactive()) { - if (len > 1024) - len = 512; + if (compat20) { + if (len > c->remote_window) + len = c->remote_window; + if (len > c->remote_maxpacket) + len = c->remote_maxpacket; } else { - /* Keep the packets at reasonable size. */ - if (len > packet_get_maxsize()/2) - len = packet_get_maxsize()/2; + if (packet_is_interactive()) { + if (len > 1024) + len = 512; + } else { + /* Keep the packets at reasonable size. */ + if (len > packet_get_maxsize()/2) + len = packet_get_maxsize()/2; + } } - 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 (len > 0) { + packet_start(compat20 ? + SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); + packet_put_int(c->remote_id); + packet_put_string(buffer_ptr(&c->input), len); + packet_send(); + buffer_consume(&c->input, len); + c->remote_window -= len; + debug("channel %d: send data len %d", c->self, len); + } + } else if (c->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); + chan_ibuf_empty(c); + } + /* Send extended data, i.e. stderr */ + if (compat20 && + c->remote_window > 0 && + (len = buffer_len(&c->extended)) > 0 && + c->extended_usage == CHAN_EXTENDED_READ) { + if (len > c->remote_window) + len = c->remote_window; + if (len > c->remote_maxpacket) + len = c->remote_maxpacket; + packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); + packet_put_int(c->remote_id); + packet_put_int(SSH2_EXTENDED_DATA_STDERR); + packet_put_string(buffer_ptr(&c->extended), len); + packet_send(); + buffer_consume(&c->extended, len); + c->remote_window -= len; } } } @@ -582,87 +986,155 @@ channel_output_poll() * still there. */ -void -channel_input_data(int payload_len) +void +channel_input_data(int type, int plen) { int id; char *data; unsigned int data_len; - Channel *ch; + Channel *c; /* Get the channel number and verify it. */ id = packet_get_int(); - if (id < 0 || id >= channels_alloc) + c = channel_lookup(id); + if (c == NULL) packet_disconnect("Received data for nonexistent channel %d.", id); - ch = &channels[id]; - - if (ch->type == SSH_CHANNEL_FREE) - packet_disconnect("Received data for free channel %d.", ch->self); /* Ignore any data for non-open channels (might happen on close) */ - if (ch->type != SSH_CHANNEL_OPEN && - ch->type != SSH_CHANNEL_X11_OPEN) + if (c->type != SSH_CHANNEL_OPEN && + c->type != SSH_CHANNEL_X11_OPEN) return; /* same for protocol 1.5 if output end is no longer open */ - if (!compat13 && ch->ostate != CHAN_OUTPUT_OPEN) + if (!compat13 && c->ostate != CHAN_OUTPUT_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(&ch->output, data, data_len); + packet_done(); + + if (compat20){ + if (data_len > c->local_maxpacket) { + log("channel %d: rcvd big packet %d, maxpack %d", + c->self, data_len, c->local_maxpacket); + } + if (data_len > c->local_window) { + log("channel %d: rcvd too much data %d, win %d", + c->self, data_len, c->local_window); + xfree(data); + return; + } + c->local_window -= data_len; + }else{ + packet_integrity_check(plen, 4 + 4 + data_len, type); + } + buffer_append(&c->output, data, data_len); xfree(data); } +void +channel_input_extended_data(int type, int plen) +{ + int id; + int tcode; + char *data; + unsigned int data_len; + Channel *c; + + /* Get the channel number and verify it. */ + id = packet_get_int(); + c = channel_lookup(id); + + if (c == NULL) + packet_disconnect("Received extended_data for bad channel %d.", id); + if (c->type != SSH_CHANNEL_OPEN) { + log("channel %d: ext data for non open", id); + return; + } + tcode = packet_get_int(); + if (c->efd == -1 || + c->extended_usage != CHAN_EXTENDED_WRITE || + tcode != SSH2_EXTENDED_DATA_STDERR) { + log("channel %d: bad ext data", c->self); + return; + } + data = packet_get_string(&data_len); + packet_done(); + if (data_len > c->local_window) { + log("channel %d: rcvd too much extended_data %d, win %d", + c->self, data_len, c->local_window); + xfree(data); + return; + } + debug("channel %d: rcvd ext data %d", c->self, data_len); + c->local_window -= data_len; + buffer_append(&c->extended, 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 +int channel_not_very_much_buffered_data() { unsigned int i; - Channel *ch; + Channel *c; for (i = 0; i < channels_alloc; i++) { - ch = &channels[i]; - if (ch->type == SSH_CHANNEL_OPEN) { - if (buffer_len(&ch->input) > packet_get_maxsize()) + c = &channels[i]; + if (c->type == SSH_CHANNEL_OPEN) { + if (!compat20 && buffer_len(&c->input) > packet_get_maxsize()) { + debug("channel %d: big input buffer %d", + c->self, buffer_len(&c->input)); return 0; - if (buffer_len(&ch->output) > packet_get_maxsize()) + } + if (buffer_len(&c->output) > packet_get_maxsize()) { + debug("channel %d: big output buffer %d", + c->self, buffer_len(&c->output)); return 0; + } } } return 1; } -/* This is called after receiving CHANNEL_CLOSE/IEOF. */ +void +channel_input_ieof(int type, int plen) +{ + int id; + Channel *c; + + packet_integrity_check(plen, 4, type); + + id = packet_get_int(); + c = channel_lookup(id); + if (c == NULL) + packet_disconnect("Received ieof for nonexistent channel %d.", id); + chan_rcvd_ieof(c); +} -void -channel_input_close() +void +channel_input_close(int type, int plen) { - int channel; + int id; + Channel *c; - /* 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; - } + packet_integrity_check(plen, 4, type); + + id = packet_get_int(); + c = channel_lookup(id); + if (c == NULL) + packet_disconnect("Received close for nonexistent channel %d.", id); /* * 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_put_int(c->remote_id); packet_send(); /* @@ -672,81 +1144,153 @@ channel_input_close() * no-one to receive the confirmation. The channel gets freed when * the confirmation arrives. */ - if (channels[channel].type != SSH_CHANNEL_CLOSED) { + if (c->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; + buffer_consume(&c->input, buffer_len(&c->input)); + c->type = SSH_CHANNEL_OUTPUT_DRAINING; } } -/* This is called after receiving CHANNEL_CLOSE_CONFIRMATION/OCLOSE. */ - -void -channel_input_close_confirmation() +/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ +void +channel_input_oclose(int type, int plen) { - 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 id = packet_get_int(); + Channel *c = channel_lookup(id); + packet_integrity_check(plen, 4, type); + if (c == NULL) + packet_disconnect("Received oclose for nonexistent channel %d.", id); + chan_rcvd_oclose(c); } -/* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */ +void +channel_input_close_confirmation(int type, int plen) +{ + int id = packet_get_int(); + Channel *c = channel_lookup(id); + + packet_done(); + if (c == NULL) + packet_disconnect("Received close confirmation for " + "out-of-range channel %d.", id); + if (c->type != SSH_CHANNEL_CLOSED) + packet_disconnect("Received close confirmation for " + "non-closed channel %d (type %d).", id, c->type); + channel_free(c->self); +} -void -channel_input_open_confirmation() +void +channel_input_open_confirmation(int type, int plen) { - int channel, remote_channel; + int id, remote_id; + Channel *c; - /* 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); + if (!compat20) + packet_integrity_check(plen, 4 + 4, type); - /* Get remote side's id for this channel. */ - remote_channel = packet_get_int(); + id = packet_get_int(); + c = channel_lookup(id); + if (c==NULL || c->type != SSH_CHANNEL_OPENING) + packet_disconnect("Received open confirmation for " + "non-opening channel %d.", id); + remote_id = 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; + c->remote_id = remote_id; + c->type = SSH_CHANNEL_OPEN; + + if (compat20) { + c->remote_window = packet_get_int(); + c->remote_maxpacket = packet_get_int(); + packet_done(); + if (c->cb_fn != NULL && c->cb_event == type) { + debug("callback start"); + c->cb_fn(c->self, c->cb_arg); + debug("callback done"); + } + debug("channel %d: open confirm rwindow %d rmax %d", c->self, + c->remote_window, c->remote_maxpacket); + } +} + +void +channel_input_open_failure(int type, int plen) +{ + int id; + Channel *c; + + if (!compat20) + packet_integrity_check(plen, 4, type); + + id = packet_get_int(); + c = channel_lookup(id); + + if (c==NULL || c->type != SSH_CHANNEL_OPENING) + packet_disconnect("Received open failure for " + "non-opening channel %d.", id); + if (compat20) { + int reason = packet_get_int(); + char *msg = packet_get_string(NULL); + char *lang = packet_get_string(NULL); + log("channel_open_failure: %d: reason %d: %s", id, reason, msg); + packet_done(); + xfree(msg); + xfree(lang); + } + /* Free the channel. This will also close the socket. */ + channel_free(id); } -/* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */ +void +channel_input_channel_request(int type, int plen) +{ + int id; + Channel *c; + + id = packet_get_int(); + c = channel_lookup(id); + + if (c == NULL || + (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_LARVAL)) + packet_disconnect("Received request for " + "non-open channel %d.", id); + if (c->cb_fn != NULL && c->cb_event == type) { + debug("callback start"); + c->cb_fn(c->self, c->cb_arg); + debug("callback done"); + } else { + char *service = packet_get_string(NULL); + debug("channel: %d rcvd request for %s", c->self, service); +debug("cb_fn %p cb_event %d", c->cb_fn , c->cb_event); + xfree(service); + } +} -void -channel_input_open_failure() +void +channel_input_window_adjust(int type, int plen) { - int channel; + Channel *c; + int id, adjust; + + if (!compat20) + return; /* 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); + id = packet_get_int(); + c = channel_lookup(id); - /* Free the channel. This will also close the socket. */ - channel_free(channel); + if (c == NULL || c->type != SSH_CHANNEL_OPEN) { + log("Received window adjust for " + "non-open channel %d.", id); + return; + } + adjust = packet_get_int(); + packet_done(); + debug("channel %d: rcvd adjust %d", id, adjust); + c->remote_window += adjust; } /* @@ -754,7 +1298,7 @@ channel_input_open_failure() * might have. */ -void +void channel_stop_listening() { int i; @@ -777,23 +1321,22 @@ channel_stop_listening() } /* - * Closes the sockets of all channels. This is used to close extra file + * Closes the sockets/fds of all channels. This is used to close extra file * descriptors after a fork. */ -void +void channel_close_all() { int i; - for (i = 0; i < channels_alloc; i++) { + for (i = 0; i < channels_alloc; i++) if (channels[i].type != SSH_CHANNEL_FREE) - close(channels[i].sock); - } + channel_close_fds(&channels[i]); } /* Returns the maximum file descriptor number used by the channels. */ -int +int channel_max_fd() { return channel_max_fd_value; @@ -801,7 +1344,7 @@ channel_max_fd() /* Returns true if any channel is still open. */ -int +int channel_still_open() { unsigned int i; @@ -813,6 +1356,10 @@ channel_still_open() case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: continue; + case SSH_CHANNEL_LARVAL: + if (!compat20) + fatal("cannot happen: SSH_CHANNEL_LARVAL"); + continue; case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: @@ -854,20 +1401,22 @@ channel_open_message() case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: continue; + case SSH_CHANNEL_LARVAL: 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/%d o%d/%d)\r\n", + snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n", c->self, c->remote_name, c->type, c->remote_id, c->istate, buffer_len(&c->input), - c->ostate, buffer_len(&c->output)); + c->ostate, buffer_len(&c->output), + c->rfd, c->wfd); buffer_append(&buffer, buf, strlen(buf)); continue; default: - fatal("channel_still_open: bad channel type %d", c->type); + fatal("channel_open_message: bad channel type %d", c->type); /* NOTREACHED */ } } @@ -882,7 +1431,7 @@ channel_open_message() * channel to host:port from remote side. */ -void +void channel_request_local_forwarding(u_short port, const char *host, u_short host_port, int gateway_ports) { @@ -946,8 +1495,11 @@ channel_request_local_forwarding(u_short port, const char *host, continue; } /* Allocate a channel number for the socket. */ - ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock, - xstrdup("port listener")); + ch = channel_new( + "port listener", SSH_CHANNEL_PORT_LISTENER, + sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, + 0, xstrdup("port listener")); strlcpy(channels[ch].path, host, sizeof(channels[ch].path)); channels[ch].host_port = host_port; channels[ch].listening_port = port; @@ -963,32 +1515,41 @@ channel_request_local_forwarding(u_short port, const char *host, * the secure channel to host:port from local side. */ -void -channel_request_remote_forwarding(u_short port, const char *host, - u_short remote_port) +void +channel_request_remote_forwarding(u_short listen_port, const char *host_to_connect, + u_short port_to_connect) { 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; + permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); + permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; + permitted_opens[num_permitted_opens].listen_port = listen_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); + if (compat20) { + const char *address_to_bind = "0.0.0.0"; + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("tcpip-forward"); + packet_put_char(0); /* boolean: want reply */ + packet_put_cstring(address_to_bind); + packet_put_int(listen_port); + } else { + packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); + packet_put_int(listen_port); + packet_put_cstring(host_to_connect); + packet_put_int(port_to_connect); + 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); + } } /* @@ -997,8 +1558,8 @@ channel_request_remote_forwarding(u_short port, const char *host, * 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 gateway_ports) { u_short port, host_port; char *hostname; @@ -1017,69 +1578,21 @@ channel_input_port_forward_request(int is_root) port); /* * Initiate forwarding, - * bind port to localhost only (gateway ports == 0). */ - channel_request_local_forwarding(port, hostname, host_port, 0); + channel_request_local_forwarding(port, hostname, host_port, gateway_ports); /* 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) +/* XXX move to aux.c */ +int +channel_connect_to(const char *host, u_short host_port) { - int remote_channel, sock = 0, newch, i; - u_short host_port; - char *host, *originator_string; - unsigned int host_len, originator_len; struct addrinfo hints, *ai, *aitop; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; int gaierr; - - /* 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); - originator_len += 4; /* size of packet_int */ - } else { - originator_string = xstrdup("unknown (remote did not supply name)"); - originator_len = 0; /* no originator supplied */ - } - - packet_integrity_check(payload_len, - 4 + 4 + host_len + 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); - goto fail; - } - } + int sock = -1; memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; @@ -1087,15 +1600,14 @@ channel_input_port_open(int payload_len) snprintf(strport, sizeof strport, "%d", host_port); if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { error("%.100s: unknown host (%s)", host, gai_strerror(gaierr)); - goto fail; + return -1; } - for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { - error("channel_input_port_open: getnameinfo failed"); + error("channel_connect_to: getnameinfo failed"); continue; } /* Create the socket. */ @@ -1115,37 +1627,81 @@ channel_input_port_open(int payload_len) } freeaddrinfo(aitop); - if (!ai) { error("connect %.100s port %d: failed.", host, host_port); - goto fail; + return -1; } + /* success */ + return sock; +} +/* + * 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. + */ - /* Successful connection. */ +void +channel_input_port_open(int type, int plen) +{ + u_short host_port; + char *host, *originator_string; + int remote_channel, sock = -1, newch, i, denied; + unsigned int host_len, originator_len; - /* Allocate a channel for this connection. */ - newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); - channels[newch].remote_id = remote_channel; + /* Get remote channel number. */ + remote_channel = packet_get_int(); - /* 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(); + /* Get host name to connect to. */ + host = packet_get_string(&host_len); - /* Free the argument string. */ - xfree(host); + /* 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); + originator_len += 4; /* size of packet_int */ + } else { + originator_string = xstrdup("unknown (remote did not supply name)"); + originator_len = 0; /* no originator supplied */ + } - return; + packet_integrity_check(plen, + 4 + 4 + host_len + 4 + originator_len, SSH_MSG_PORT_OPEN); -fail: - /* Free the argument string. */ - xfree(host); + /* Check if opening that port is permitted. */ + denied = 0; + if (!all_opens_permitted) { + /* Go trough all permitted ports. */ + for (i = 0; i < num_permitted_opens; i++) + if (permitted_opens[i].port_to_connect == host_port && + strcmp(permitted_opens[i].host_to_connect, host) == 0) + break; - /* Send refusal to the remote host. */ - packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(remote_channel); - packet_send(); + /* 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); + denied = 1; + } + } + sock = denied ? -1 : channel_connect_to(host, host_port); + if (sock > 0) { + /* Allocate a channel for this connection. */ + newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); + channels[newch].remote_id = remote_channel; + + packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(remote_channel); + packet_put_int(newch); + packet_send(); + } else { + packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(remote_channel); + packet_send(); + } + xfree(host); } /* @@ -1230,8 +1786,10 @@ x11_create_display_inet(int screen_number, int x11_display_offset) /* Allocate a channel for each socket. */ for (n = 0; n < num_socks; n++) { sock = socks[n]; - (void) channel_allocate(SSH_CHANNEL_X11_LISTENER, sock, - xstrdup("X11 inet listener")); + (void) channel_new("x11 listener", + SSH_CHANNEL_X11_LISTENER, sock, sock, -1, + CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, + 0, xstrdup("X11 inet listener")); } /* Return a suitable value for the DISPLAY environment variable. */ @@ -1271,44 +1829,21 @@ connect_local_xsocket(unsigned int dnr) return -1; } - -/* - * 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) +int +x11_connect_display(void) { - int remote_channel, display_number, sock = 0, newch; + int display_number, sock = 0; const char *display; - char buf[1024], *cp, *remote_host; - unsigned int remote_len; + char buf[1024], *cp; struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr; - /* 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); - remote_len += 4; - } else { - remote_host = xstrdup("unknown (remote did not supply name)"); - remote_len = 0; - } - - debug("Received X11 open request."); - packet_integrity_check(payload_len, 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; + return -1; } /* * Now we decode the value of the DISPLAY variable and make a @@ -1325,15 +1860,15 @@ x11_input_open(int payload_len) if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { error("Could not parse display number from DISPLAY: %.100s", display); - goto fail; + return -1; } /* Create a socket. */ sock = connect_local_xsocket(display_number); if (sock < 0) - goto fail; + return -1; /* OK, we now have a connection to the display. */ - goto success; + return sock; } /* * Connect to an inet socket. The DISPLAY value is supposedly @@ -1344,14 +1879,14 @@ x11_input_open(int payload_len) cp = strchr(buf, ':'); if (!cp) { error("Could not find ':' in DISPLAY: %.100s", display); - goto fail; + return -1; } *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; + return -1; } /* Look up the host address */ @@ -1361,55 +1896,83 @@ x11_input_open(int payload_len) snprintf(strport, sizeof strport, "%d", 6000 + display_number); if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); - goto fail; + return -1; } for (ai = aitop; ai; ai = ai->ai_next) { /* Create a socket. */ sock = socket(ai->ai_family, SOCK_STREAM, 0); if (sock < 0) { debug("socket: %.100s", strerror(errno)); - continue; - } - /* Connect it to the display. */ - if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { - debug("connect %.100s port %d: %.100s", buf, 6000 + display_number, - strerror(errno)); - close(sock); - continue; + continue; + } + /* Connect it to the display. */ + if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + debug("connect %.100s port %d: %.100s", buf, + 6000 + display_number, strerror(errno)); + close(sock); + continue; + } + /* Success */ + break; } - /* Success */ - break; - - } /* (ai = aitop, ai; ai = ai->ai_next) */ freeaddrinfo(aitop); if (!ai) { - error("connect %.100s port %d: %.100s", buf, 6000 + display_number, + error("connect %.100s port %d: %.100s", buf, 6000 + display_number, strerror(errno)); - goto fail; + return -1; } -success: - /* We have successfully obtained a connection to the real X display. */ + return sock; +} - /* 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; +/* + * 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. + */ - /* 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(); +void +x11_input_open(int type, int plen) +{ + int remote_channel, sock = 0, newch; + char *remote_host; + unsigned int remote_len; - return; + /* Get remote channel number. */ + remote_channel = packet_get_int(); -fail: - /* Send refusal to the remote host. */ - packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(remote_channel); - packet_send(); + /* Get remote originator name. */ + if (have_hostname_in_open) { + remote_host = packet_get_string(&remote_len); + remote_len += 4; + } else { + remote_host = xstrdup("unknown (remote did not supply name)"); + remote_len = 0; + } + + debug("Received X11 open request."); + packet_integrity_check(plen, 4 + remote_len, SSH_SMSG_X11_OPEN); + + /* Obtain a connection to the real X display. */ + sock = x11_connect_display(); + if (sock == -1) { + /* Send refusal to the remote host. */ + packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(remote_channel); + packet_send(); + } else { + /* Allocate a channel for this connection. */ + newch = channel_allocate( + (x11_saved_proto == NULL) ? + SSH_CHANNEL_OPEN : 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(); + } } /* @@ -1417,8 +1980,9 @@ fail: * data, and enables authentication spoofing. */ -void -x11_request_forwarding_with_spoofing(const char *proto, const char *data) +void +x11_request_forwarding_with_spoofing(int client_session_id, + const char *proto, const char *data) { unsigned int data_len = (unsigned int) strlen(data) / 2; unsigned int i, value; @@ -1464,9 +2028,14 @@ x11_request_forwarding_with_spoofing(const char *proto, const char *data) 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)); + if (compat20) { + channel_request_start(client_session_id, "x11-req", 0); + packet_put_char(0); /* XXX bool single connection */ + } else { + packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); + } + packet_put_cstring(proto); + packet_put_cstring(new_data); packet_put_int(screen_number); packet_send(); packet_write_wait(); @@ -1475,7 +2044,7 @@ x11_request_forwarding_with_spoofing(const char *proto, const char *data) /* Sends a message to the server to request authentication fd forwarding. */ -void +void auth_request_forwarding() { packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); @@ -1497,7 +2066,7 @@ auth_get_socket_name() /* removes the agent forwarding socket */ -void +void cleanup_socket(void) { remove(channel_forwarded_auth_socket_name); @@ -1509,7 +2078,7 @@ cleanup_socket(void) * This starts forwarding authentication requests. */ -void +void auth_input_request_forwarding(struct passwd * pw) { int sock, newch; @@ -1567,12 +2136,14 @@ auth_input_request_forwarding(struct passwd * pw) /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ -void -auth_input_open_request() +void +auth_input_open_request(int type, int plen) { int remch, sock, newch; char *dummyname; + packet_integrity_check(plen, 4, type); + /* Read the remote channel number from the message. */ remch = packet_get_int(); @@ -1612,3 +2183,95 @@ auth_input_open_request() packet_put_int(newch); packet_send(); } + +void +channel_start_open(int id) +{ + Channel *c = channel_lookup(id); + if (c == NULL) { + log("channel_open: %d: bad id", id); + return; + } + debug("send channel open %d", id); + packet_start(SSH2_MSG_CHANNEL_OPEN); + packet_put_cstring(c->ctype); + packet_put_int(c->self); + packet_put_int(c->local_window); + packet_put_int(c->local_maxpacket); +} +void +channel_open(int id) +{ + /* XXX REMOVE ME */ + channel_start_open(id); + packet_send(); +} +void +channel_request(int id, char *service, int wantconfirm) +{ + channel_request_start(id, service, wantconfirm); + packet_send(); + debug("channel request %d: %s", id, service) ; +} +void +channel_request_start(int id, char *service, int wantconfirm) +{ + Channel *c = channel_lookup(id); + if (c == NULL) { + log("channel_request: %d: bad id", id); + return; + } + packet_start(SSH2_MSG_CHANNEL_REQUEST); + packet_put_int(c->remote_id); + packet_put_cstring(service); + packet_put_char(wantconfirm); +} +void +channel_register_callback(int id, int mtype, channel_callback_fn *fn, void *arg) +{ + Channel *c = channel_lookup(id); + if (c == NULL) { + log("channel_register_callback: %d: bad id", id); + return; + } + c->cb_event = mtype; + c->cb_fn = fn; + c->cb_arg = arg; +} +void +channel_register_cleanup(int id, channel_callback_fn *fn) +{ + Channel *c = channel_lookup(id); + if (c == NULL) { + log("channel_register_cleanup: %d: bad id", id); + return; + } + c->dettach_user = fn; +} +void +channel_cancel_cleanup(int id) +{ + Channel *c = channel_lookup(id); + if (c == NULL) { + log("channel_cancel_cleanup: %d: bad id", id); + return; + } + c->dettach_user = NULL; +} + +void +channel_set_fds(int id, int rfd, int wfd, int efd, int extusage) +{ + Channel *c = channel_lookup(id); + if (c == NULL || c->type != SSH_CHANNEL_LARVAL) + fatal("channel_activate for non-larval channel %d.", id); + + channel_register_fds(c, rfd, wfd, efd, extusage); + c->type = SSH_CHANNEL_OPEN; + /* XXX window size? */ + c->local_window = c->local_window_max = c->local_maxpacket/2; + packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); + packet_put_int(c->remote_id); + packet_put_int(c->local_window); + packet_send(); +} diff --git a/crypto/openssh/channels.h b/crypto/openssh/channels.h index 93a7b53..24ae2b82 100644 --- a/crypto/openssh/channels.h +++ b/crypto/openssh/channels.h @@ -1,4 +1,4 @@ -/* RCSID("$Id: channels.h,v 1.6 1999/11/24 19:53:45 markus Exp $"); */ +/* RCSID("$Id: channels.h,v 1.12 2000/05/03 18:03:06 markus Exp $"); */ #ifndef CHANNELS_H #define CHANNELS_H @@ -10,17 +10,18 @@ #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 */ -/* 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_AUTH_SOCKET 6 /* authentication socket */ +#define SSH_CHANNEL_X11_OPEN 7 /* reading first X11 packet */ +#define SSH_CHANNEL_INPUT_DRAINING 8 /* sending remaining data to conn */ +#define SSH_CHANNEL_OUTPUT_DRAINING 9 /* sending remaining data to app */ +#define SSH_CHANNEL_LARVAL 10 /* larval session */ +#define SSH_CHANNEL_MAX_TYPE 11 /* * Data structure for channel data. This is iniailized in channel_allocate * and cleared in channel_free. */ +typedef void channel_callback_fn(int id, void *arg); typedef struct Channel { int type; /* channel type/state */ @@ -29,15 +30,208 @@ typedef struct Channel { /* 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 */ + int flags; /* close sent/rcvd */ + int rfd; /* read fd */ + int wfd; /* write fd */ + int efd; /* extended fd */ + int sock; /* sock fd */ Buffer input; /* data read from socket, to be sent over * encrypted connection */ Buffer output; /* data received over encrypted connection for * send on socket */ + Buffer extended; 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 */ + + int remote_window; + int remote_maxpacket; + int local_window; + int local_window_max; + int local_consumed; + int local_maxpacket; + int extended_usage; + + char *ctype; /* type */ + + /* callback */ + channel_callback_fn *cb_fn; + void *cb_arg; + int cb_event; + channel_callback_fn *dettach_user; } Channel; + +#define CHAN_EXTENDED_IGNORE 0 +#define CHAN_EXTENDED_READ 1 +#define CHAN_EXTENDED_WRITE 2 + +void channel_set_fds(int id, int rfd, int wfd, int efd, int extusage); +void channel_open(int id); +void channel_request(int id, char *service, int wantconfirm); +void channel_request_start(int id, char *service, int wantconfirm); +void channel_register_callback(int id, int mtype, channel_callback_fn *fn, void *arg); +void channel_register_cleanup(int id, channel_callback_fn *fn); +void channel_cancel_cleanup(int id); +Channel *channel_lookup(int id); + +int +channel_new(char *ctype, int type, int rfd, int wfd, int efd, + int window, int maxpack, int extended_usage, char *remote_name); + +void channel_input_channel_request(int type, int plen); +void channel_input_close(int type, int plen); +void channel_input_close_confirmation(int type, int plen); +void channel_input_data(int type, int plen); +void channel_input_extended_data(int type, int plen); +void channel_input_ieof(int type, int plen); +void channel_input_oclose(int type, int plen); +void channel_input_open_confirmation(int type, int plen); +void channel_input_open_failure(int type, int plen); +void channel_input_port_open(int type, int plen); +void channel_input_window_adjust(int type, int plen); +void channel_input_open(int type, int plen); + +/* Sets specific protocol options. */ +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); + +/* Free the channel and close its socket. */ +void channel_free(int channel); + +/* Add any bits relevant to channels in select bitmasks. */ +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); + +/* If there is data to send to the connection, send some of it now. */ +void channel_output_poll(void); + +/* Returns true if no channel has too much buffered data. */ +int channel_not_very_much_buffered_data(void); + +/* This closes any sockets that are listening for connections; this removes + any unix domain sockets. */ +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); + +/* Returns the maximum file descriptor number used by the channels. */ +int channel_max_fd(void); + +/* Returns true if there is still an open channel over the connection. */ +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); + +/* + * 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(u_short port, const char *host, + u_short remote_port, int gateway_ports); + +/* + * 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(u_short port, const char *host, + u_short 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); + +/* + * 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 error. + */ +void channel_input_port_forward_request(int is_root, int gateway_ports); + +/* + * 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); + +/* + * 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, int x11_display_offset); + +/* + * 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 type, int plen); + +/* + * Requests forwarding of X11 connections. This should be called on the + * client only. + */ +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(int client_session_id, + const char *proto, const char *data); + +/* Sends a message to the server to request authentication fd forwarding. */ +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); + +/* + * 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); + +/* This is called to process an SSH_SMSG_AGENT_OPEN message. */ +void auth_input_open_request(int type, int plen); + +/* XXX */ +int channel_connect_to(const char *host, u_short host_port); +int x11_connect_display(void); + #endif diff --git a/crypto/openssh/clientloop.c b/crypto/openssh/clientloop.c index 59dad3d..b4c7b28 100644 --- a/crypto/openssh/clientloop.c +++ b/crypto/openssh/clientloop.c @@ -1,21 +1,22 @@ /* - * + * * clientloop.c - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved - * - * + * + * * Created: Sat Sep 23 12:23:57 1995 ylo - * + * * The main loop for the interactive session (client side). - * + * + * SSH2 support added by Markus Friedl. */ #include "includes.h" -RCSID("$Id: clientloop.c,v 1.14 1999/12/06 20:15:26 deraadt Exp $"); +RCSID("$Id: clientloop.c,v 1.26 2000/05/08 17:42:24 markus Exp $"); #include "xmalloc.h" #include "ssh.h" @@ -24,6 +25,12 @@ RCSID("$Id: clientloop.c,v 1.14 1999/12/06 20:15:26 deraadt Exp $"); #include "authfd.h" #include "readconf.h" +#include "ssh2.h" +#include "compat.h" +#include "channels.h" +#include "dispatch.h" + + /* Flag indicating that stdin should be redirected from /dev/null. */ extern int stdin_null_flag; @@ -70,9 +77,13 @@ 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. */ + +void client_init_dispatch(void); +int session_ident = -1; + /* Returns the user\'s terminal to normal mode if it had been put in raw mode. */ -void +void leave_raw_mode() { if (!in_raw_mode) @@ -86,7 +97,7 @@ leave_raw_mode() /* Puts the user\'s terminal in raw mode. */ -void +void enter_raw_mode() { struct termios tio; @@ -112,7 +123,7 @@ enter_raw_mode() /* Restores stdin to blocking mode. */ -void +void leave_non_blocking() { if (in_non_blocking_mode) { @@ -124,7 +135,7 @@ leave_non_blocking() /* Puts stdin terminal in non-blocking mode. */ -void +void enter_non_blocking() { in_non_blocking_mode = 1; @@ -137,7 +148,7 @@ enter_non_blocking() * flag indicating that the window has changed. */ -void +void window_change_handler(int sig) { received_window_change_signal = 1; @@ -149,7 +160,7 @@ window_change_handler(int sig) * signals must be trapped to restore terminal modes. */ -void +void signal_handler(int sig) { if (in_raw_mode) @@ -166,7 +177,7 @@ signal_handler(int sig) * available resolution. */ -double +double get_current_time() { struct timeval tv; @@ -180,7 +191,7 @@ get_current_time() * not appear to wake up when redirecting from /dev/null. */ -void +void client_check_initial_eof_on_stdin() { int len; @@ -228,115 +239,13 @@ client_check_initial_eof_on_stdin() } } -/* - * Get packets from the connection input buffer, and process them as long as - * there are packets available. - */ - -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); - } - } -} /* * Make packets from buffered stdin data, and buffer them for sending to the * connection. */ -void +void client_make_packets_from_stdin_data() { unsigned int len; @@ -367,26 +276,35 @@ client_make_packets_from_stdin_data() * appropriate. */ -void +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(); - } + struct winsize ws; + + if (! received_window_change_signal) + return; + /** XXX race */ + received_window_change_signal = 0; + + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) + return; + + debug("client_check_window_change: changed"); + + if (compat20) { + channel_request_start(session_ident, "window-change", 0); + packet_put_int(ws.ws_col); + packet_put_int(ws.ws_row); + packet_put_int(ws.ws_xpixel); + packet_put_int(ws.ws_ypixel); + packet_send(); + } else { + 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(); } } @@ -395,26 +313,36 @@ client_check_window_change() * one of the file descriptors). */ -void +void client_wait_until_can_do_something(fd_set * readset, fd_set * writeset) { + /*debug("client_wait_until_can_do_something"); */ + /* Initialize select masks. */ FD_ZERO(readset); + FD_ZERO(writeset); - /* 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()) + if (!compat20) { + /* 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); + + /* Select stdout/stderr if have data in buffer. */ + if (buffer_len(&stdout_buffer) > 0) + FD_SET(fileno(stdout), writeset); + if (buffer_len(&stderr_buffer) > 0) + FD_SET(fileno(stderr), writeset); + } else { 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); @@ -423,14 +351,7 @@ client_wait_until_can_do_something(fd_set * readset, fd_set * writeset) 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); - +/* move UP XXX */ /* Update maximum file descriptor number, if appropriate. */ if (channel_max_fd() > max_fd) max_fd = channel_max_fd(); @@ -459,7 +380,7 @@ client_wait_until_can_do_something(fd_set * readset, fd_set * writeset) } } -void +void client_suspend_self() { struct winsize oldws, newws; @@ -504,11 +425,11 @@ client_suspend_self() enter_raw_mode(); } -void -client_process_input(fd_set * readset) +void +client_process_net_input(fd_set * readset) { - int len, pid; - char buf[8192], *s; + int len; + char buf[8192]; /* * Read input from the server, and add any such data to the buffer of @@ -517,6 +438,7 @@ client_process_input(fd_set * readset) if (FD_ISSET(connection_in, readset)) { /* Read as much as possible. */ len = read(connection_in, buf, sizeof(buf)); +/*debug("read connection_in len %d", len); XXX */ 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", @@ -544,6 +466,15 @@ client_process_input(fd_set * readset) } packet_process_incoming(buf, len); } +} + +void +client_process_input(fd_set * readset) +{ + int len; + pid_t pid; + char buf[8192], *s; + /* Read input from stdin. */ if (FD_ISSET(fileno(stdin), readset)) { /* Read as much as possible. */ @@ -727,7 +658,7 @@ Supported escape sequences:\r\n\ } } -void +void client_process_output(fd_set * writeset) { int len; @@ -776,13 +707,31 @@ client_process_output(fd_set * writeset) } /* + * Get packets from the connection input buffer, and process them as long as + * there are packets available. + * + * 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. + */ + +void +client_process_buffered_input_packets() +{ + dispatch_run(DISPATCH_NONBLOCK, &quit_pending); +} + +/* * Implements the interactive session with the server. This is called after * the user has been authenticated, and a command has been started on the * remote host. If escape_char != -1, it is the character used as an escape * character for terminating or suspending the session. */ -int +int client_loop(int have_pty, int escape_char_arg) { extern Options options; @@ -816,6 +765,8 @@ client_loop(int have_pty, int escape_char_arg) buffer_init(&stdout_buffer); buffer_init(&stderr_buffer); + client_init_dispatch(); + /* Set signal handlers to restore non-blocking mode. */ signal(SIGINT, signal_handler); signal(SIGQUIT, signal_handler); @@ -828,7 +779,8 @@ client_loop(int have_pty, int escape_char_arg) enter_raw_mode(); /* Check if we should immediately send of on stdin. */ - client_check_initial_eof_on_stdin(); + if (!compat20) + client_check_initial_eof_on_stdin(); /* Main loop of the client for the interactive session mode. */ while (!quit_pending) { @@ -837,11 +789,17 @@ client_loop(int have_pty, int escape_char_arg) /* Process buffered packets sent by the server. */ client_process_buffered_input_packets(); + if (compat20 && !channel_still_open()) { + debug("!channel_still_open."); + break; + } + /* * Make packets of buffered stdin data, and buffer them for * sending to the server. */ - client_make_packets_from_stdin_data(); + if (!compat20) + client_make_packets_from_stdin_data(); /* * Make packets from buffered channel data, and buffer them @@ -871,17 +829,21 @@ client_loop(int have_pty, int escape_char_arg) /* 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); + /* Buffer input from the connection. */ + client_process_net_input(&readset); - /* - * Process output to stdout and stderr. Output to the - * connection is processed elsewhere (above). - */ - client_process_output(&writeset); + if (quit_pending) + break; + + if (!compat20) { + /* Buffer data from stdin */ + 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)) @@ -950,3 +912,206 @@ client_loop(int have_pty, int escape_char_arg) debug("Exit status %d", exit_status); return exit_status; } + +/*********/ + +void +client_input_stdout_data(int type, int plen) +{ + unsigned int data_len; + char *data = packet_get_string(&data_len); + packet_integrity_check(plen, 4 + data_len, type); + buffer_append(&stdout_buffer, data, data_len); + stdout_bytes += data_len; + memset(data, 0, data_len); + xfree(data); +} +void +client_input_stderr_data(int type, int plen) +{ + unsigned int data_len; + char *data = packet_get_string(&data_len); + packet_integrity_check(plen, 4 + data_len, type); + buffer_append(&stderr_buffer, data, data_len); + stdout_bytes += data_len; + memset(data, 0, data_len); + xfree(data); +} +void +client_input_exit_status(int type, int plen) +{ + packet_integrity_check(plen, 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; +} + +/* XXXX move to generic input handler */ +void +client_input_channel_open(int type, int plen) +{ + Channel *c = NULL; + char *ctype; + int id; + unsigned int len; + int rchan; + int rmaxpack; + int rwindow; + + ctype = packet_get_string(&len); + rchan = packet_get_int(); + rwindow = packet_get_int(); + rmaxpack = packet_get_int(); + + debug("client_input_channel_open: ctype %s rchan %d win %d max %d", + ctype, rchan, rwindow, rmaxpack); + + if (strcmp(ctype, "x11") == 0) { + int sock; + char *originator; + int originator_port; + originator = packet_get_string(NULL); + if (datafellows & SSH_BUG_X11FWD) { + debug("buggy server: x11 request w/o originator_port"); + originator_port = 0; + } else { + originator_port = packet_get_int(); + } + packet_done(); + /* XXX check permission */ + xfree(originator); + /* XXX move to channels.c */ + sock = x11_connect_display(); + if (sock >= 0) { + id = channel_new("x11", SSH_CHANNEL_X11_OPEN, + sock, sock, -1, 4*1024, 32*1024, 0, + xstrdup("x11")); + c = channel_lookup(id); + } + } +/* XXX duplicate : */ + if (c != NULL) { + debug("confirm %s", ctype); + c->remote_id = rchan; + c->remote_window = rwindow; + c->remote_maxpacket = rmaxpack; + + packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(c->remote_id); + packet_put_int(c->self); + packet_put_int(c->local_window); + packet_put_int(c->local_maxpacket); + packet_send(); + } else { + debug("failure %s", ctype); + packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(rchan); + packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); + packet_put_cstring("bla bla"); + packet_put_cstring(""); + packet_send(); + } + xfree(ctype); +} + +void +client_init_dispatch_20() +{ + dispatch_init(&dispatch_protocol_error); + dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); + dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); + dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); + dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request); + dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); +} +void +client_init_dispatch_13() +{ + dispatch_init(NULL); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); + dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); + dispatch_set(SSH_SMSG_AGENT_OPEN, &auth_input_open_request); + dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status); + dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data); + dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); + dispatch_set(SSH_SMSG_X11_OPEN, &x11_input_open); +} +void +client_init_dispatch_15() +{ + client_init_dispatch_13(); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); +} +void +client_init_dispatch() +{ + if (compat20) + client_init_dispatch_20(); + else if (compat13) + client_init_dispatch_13(); + else + client_init_dispatch_15(); +} + +void +client_input_channel_req(int id, void *arg) +{ + Channel *c = NULL; + unsigned int len; + int success = 0; + int reply; + char *rtype; + + rtype = packet_get_string(&len); + reply = packet_get_char(); + + debug("client_input_channel_req: rtype %s reply %d", rtype, reply); + + c = channel_lookup(id); + if (c == NULL) + fatal("session_input_channel_req: channel %d: bad channel", id); + + if (session_ident == -1) { + error("client_input_channel_req: no channel %d", id); + } else if (id != session_ident) { + error("client_input_channel_req: bad channel %d != %d", + id, session_ident); + } else if (strcmp(rtype, "exit-status") == 0) { + success = 1; + exit_status = packet_get_int(); + packet_done(); + } + if (reply) { + packet_start(success ? + SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); + packet_put_int(c->remote_id); + packet_send(); + } + xfree(rtype); +} + +void +client_set_session_ident(int id) +{ + debug("client_set_session_ident: id %d", id); + session_ident = id; + channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST, + client_input_channel_req, (void *)0); +} diff --git a/crypto/openssh/compat.c b/crypto/openssh/compat.c index 7e21fd0..33e509c 100644 --- a/crypto/openssh/compat.c +++ b/crypto/openssh/compat.c @@ -28,15 +28,77 @@ */ #include "includes.h" -RCSID("$Id: compat.c,v 1.5 1999/11/24 16:15:24 markus Exp $"); +RCSID("$Id: compat.c,v 1.13 2000/05/08 17:42:24 markus Exp $"); #include "ssh.h" +#include "packet.h" +#include "xmalloc.h" +#include "compat.h" int compat13 = 0; +int compat20 = 0; +int datafellows = 0; -void +void +enable_compat20(void) +{ + verbose("Enabling compatibility mode for protocol 2.0"); + compat20 = 1; +} +void enable_compat13(void) { verbose("Enabling compatibility mode for protocol 1.3"); compat13 = 1; } +/* datafellows bug compatibility */ +void +compat_datafellows(const char *version) +{ + int i; + size_t len; + struct { + char *version; + int bugs; + } check[] = { + {"2.1.0", SSH_BUG_SIGBLOB|SSH_BUG_HMAC}, + {"2.0.1", SSH_BUG_SIGBLOB|SSH_BUG_HMAC|SSH_BUG_PUBKEYAUTH|SSH_BUG_X11FWD}, + {NULL, 0} + }; + for (i = 0; check[i].version; i++) { + len = strlen(check[i].version); + if (strlen(version) >= len && + (strncmp(version, check[i].version, len) == 0)) { + verbose("datafellows: %.200s", version); + datafellows = check[i].bugs; + return; + } + } +} + +#define SEP "," +int +proto_spec(const char *spec) +{ + char *s = xstrdup(spec); + char *p; + int ret = SSH_PROTO_UNKNOWN; + + for ((p = strtok(s, SEP)); p; (p = strtok(NULL, SEP))) { + switch(atoi(p)) { + case 1: + if (ret == SSH_PROTO_UNKNOWN) + ret |= SSH_PROTO_1_PREFERRED; + ret |= SSH_PROTO_1; + break; + case 2: + ret |= SSH_PROTO_2; + break; + default: + log("ignoring bad proto spec: '%s'.", p); + break; + } + } + xfree(s); + return ret; +} diff --git a/crypto/openssh/compat.h b/crypto/openssh/compat.h index 68d5e2b..9308a6d 100644 --- a/crypto/openssh/compat.h +++ b/crypto/openssh/compat.h @@ -26,10 +26,26 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* RCSID("$Id: compat.h,v 1.4 1999/11/24 16:15:24 markus Exp $"); */ +/* RCSID("$Id: compat.h,v 1.7 2000/05/08 17:42:24 markus Exp $"); */ #ifndef COMPAT_H #define COMPAT_H + +#define SSH_PROTO_UNKNOWN 0x00 +#define SSH_PROTO_1 0x01 +#define SSH_PROTO_1_PREFERRED 0x02 +#define SSH_PROTO_2 0x04 + +#define SSH_BUG_SIGBLOB 0x01 +#define SSH_BUG_PUBKEYAUTH 0x02 +#define SSH_BUG_HMAC 0x04 +#define SSH_BUG_X11FWD 0x08 + void enable_compat13(void); +void enable_compat20(void); +void compat_datafellows(const char *s); +int proto_spec(const char *spec); extern int compat13; +extern int compat20; +extern int datafellows; #endif diff --git a/crypto/openssh/compress.c b/crypto/openssh/compress.c index 03e5081..86ccaa2 100644 --- a/crypto/openssh/compress.c +++ b/crypto/openssh/compress.c @@ -1,20 +1,20 @@ /* - * + * * compress.c - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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: compress.c,v 1.5 2000/03/16 20:56:14 markus Exp $"); +RCSID("$Id: compress.c,v 1.7 2000/04/14 10:30:31 markus Exp $"); #include "ssh.h" #include "buffer.h" @@ -28,7 +28,7 @@ static z_stream outgoing_stream; * (as in gzip). */ -void +void buffer_compress_init(int level) { debug("Enabling compression at level %d.", level); @@ -40,7 +40,7 @@ buffer_compress_init(int level) /* Frees any data structures allocated for compression. */ -void +void buffer_compress_uninit() { debug("compress outgoing: raw data %lu, compressed %lu, factor %.2f", @@ -64,7 +64,7 @@ buffer_compress_uninit() * receiver. This appends the compressed data to the output buffer. */ -void +void buffer_compress(Buffer * input_buffer, Buffer * output_buffer) { char buf[4096]; @@ -90,23 +90,13 @@ buffer_compress(Buffer * input_buffer, Buffer * output_buffer) case Z_OK: /* Append compressed data to output_buffer. */ buffer_append(output_buffer, buf, - sizeof(buf) - outgoing_stream.avail_out); + 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); } /* @@ -118,7 +108,7 @@ buffer_compress(Buffer * input_buffer, Buffer * output_buffer) * with that. This appends the uncompressed data to the output buffer. */ -void +void buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer) { char buf[4096]; @@ -127,27 +117,17 @@ buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer) incoming_stream.next_in = (unsigned char *) buffer_ptr(input_buffer); incoming_stream.avail_in = buffer_len(input_buffer); - incoming_stream.next_out = (unsigned char *) buf; - incoming_stream.avail_out = sizeof(buf); - for (;;) { + /* Set up fixed-size output buffer. */ + incoming_stream.next_out = (unsigned char *) buf; + incoming_stream.avail_out = sizeof(buf); + 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 = (unsigned char *) buf; - incoming_stream.avail_out = sizeof(buf); + sizeof(buf) - incoming_stream.avail_out); 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 @@ -155,11 +135,9 @@ buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer) * 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); + /* NOTREACHED */ } } } diff --git a/crypto/openssh/compress.h b/crypto/openssh/compress.h index 4e9c598..f975613 100644 --- a/crypto/openssh/compress.h +++ b/crypto/openssh/compress.h @@ -1,19 +1,19 @@ /* - * + * * compress.h - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved - * + * * Created: Wed Oct 25 22:12:46 1995 ylo - * + * * Interface to packet compression for ssh. - * + * */ -/* RCSID("$Id: compress.h,v 1.3 1999/11/24 19:53:46 markus Exp $"); */ +/* RCSID("$Id: compress.h,v 1.4 2000/04/14 10:30:31 markus Exp $"); */ #ifndef COMPRESS_H #define COMPRESS_H diff --git a/crypto/openssh/crc32.h b/crypto/openssh/crc32.h index 54299fc..4176e8a 100644 --- a/crypto/openssh/crc32.h +++ b/crypto/openssh/crc32.h @@ -1,19 +1,19 @@ /* - * + * * crc32.h - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * 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: crc32.h,v 1.4 1999/11/24 19:53:46 markus Exp $"); */ +/* RCSID("$Id: crc32.h,v 1.5 2000/04/14 10:30:31 markus Exp $"); */ #ifndef CRC32_H #define CRC32_H diff --git a/crypto/openssh/dispatch.c b/crypto/openssh/dispatch.c new file mode 100644 index 0000000..9b7def7 --- /dev/null +++ b/crypto/openssh/dispatch.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "includes.h" +RCSID("$Id: dispatch.c,v 1.2 2000/04/14 10:30:31 markus Exp $"); +#include "ssh.h" +#include "dispatch.h" +#include "packet.h" + +#define DISPATCH_MIN 0 +#define DISPATCH_MAX 255 + +dispatch_fn *dispatch[DISPATCH_MAX]; + +void +dispatch_protocol_error(int type, int plen) +{ + error("Hm, dispatch protocol error: type %d plen %d", type, plen); +} +void +dispatch_init(dispatch_fn *dflt) +{ + int i; + for (i = 0; i < DISPATCH_MAX; i++) + dispatch[i] = dflt; +} +void +dispatch_set(int type, dispatch_fn *fn) +{ + dispatch[type] = fn; +} +void +dispatch_run(int mode, int *done) +{ + for (;;) { + int plen; + int type; + + if (mode == DISPATCH_BLOCK) { + type = packet_read(&plen); + } else { + type = packet_read_poll(&plen); + if (type == SSH_MSG_NONE) + return; + } + if (type > 0 && type < DISPATCH_MAX && dispatch[type] != NULL) + (*dispatch[type])(type, plen); + else + packet_disconnect("protocol error: rcvd type %d", type); + if (done != NULL && *done) + return; + } +} diff --git a/crypto/openssh/dispatch.h b/crypto/openssh/dispatch.h new file mode 100644 index 0000000..12084aa --- /dev/null +++ b/crypto/openssh/dispatch.h @@ -0,0 +1,11 @@ +enum { + DISPATCH_BLOCK, + DISPATCH_NONBLOCK +}; + +typedef void dispatch_fn(int type, int plen); + +void dispatch_init(dispatch_fn *dflt); +void dispatch_set(int type, dispatch_fn *fn); +void dispatch_run(int mode, int *done); +void dispatch_protocol_error(int type, int plen); diff --git a/crypto/openssh/dsa.c b/crypto/openssh/dsa.c new file mode 100644 index 0000000..51d7ff2 --- /dev/null +++ b/crypto/openssh/dsa.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$Id: dsa.c,v 1.7 2000/05/08 17:42:24 markus Exp $"); + +#include "ssh.h" +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "compat.h" + +#include <openssl/bn.h> +#include <openssl/dh.h> +#include <openssl/rsa.h> +#include <openssl/dsa.h> +#include <openssl/evp.h> +#include <openssl/bio.h> +#include <openssl/pem.h> + +#include <openssl/hmac.h> +#include "kex.h" +#include "key.h" +#include "uuencode.h" + +#define INTBLOB_LEN 20 +#define SIGBLOB_LEN (2*INTBLOB_LEN) + +Key * +dsa_key_from_blob( + char *blob, int blen) +{ + Buffer b; + char *ktype; + int rlen; + DSA *dsa; + Key *key; + +#ifdef DEBUG_DSS + dump_base64(stderr, blob, blen); +#endif + /* fetch & parse DSA/DSS pubkey */ + key = key_new(KEY_DSA); + dsa = key->dsa; + buffer_init(&b); + buffer_append(&b, blob, blen); + ktype = buffer_get_string(&b, NULL); + if (strcmp(KEX_DSS, ktype) != 0) { + error("dsa_key_from_blob: cannot handle type %s", ktype); + key_free(key); + return NULL; + } + buffer_get_bignum2(&b, dsa->p); + buffer_get_bignum2(&b, dsa->q); + buffer_get_bignum2(&b, dsa->g); + buffer_get_bignum2(&b, dsa->pub_key); + rlen = buffer_len(&b); + if(rlen != 0) + error("dsa_key_from_blob: remaining bytes in key blob %d", rlen); + buffer_free(&b); + + debug("keytype %s", ktype); +#ifdef DEBUG_DSS + DSA_print_fp(stderr, dsa, 8); +#endif + return key; +} +int +dsa_make_key_blob(Key *key, unsigned char **blobp, unsigned int *lenp) +{ + Buffer b; + int len; + unsigned char *buf; + + if (key == NULL || key->type != KEY_DSA) + return 0; + buffer_init(&b); + buffer_put_cstring(&b, KEX_DSS); + buffer_put_bignum2(&b, key->dsa->p); + buffer_put_bignum2(&b, key->dsa->q); + buffer_put_bignum2(&b, key->dsa->g); + buffer_put_bignum2(&b, key->dsa->pub_key); + len = buffer_len(&b); + buf = xmalloc(len); + memcpy(buf, buffer_ptr(&b), len); + memset(buffer_ptr(&b), 0, len); + buffer_free(&b); + if (lenp != NULL) + *lenp = len; + if (blobp != NULL) + *blobp = buf; + return len; +} +int +dsa_sign( + Key *key, + unsigned char **sigp, int *lenp, + unsigned char *data, int datalen) +{ + unsigned char *digest; + unsigned char *ret; + DSA_SIG *sig; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + unsigned int rlen; + unsigned int slen; + unsigned int len; + unsigned char sigblob[SIGBLOB_LEN]; + Buffer b; + + if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { + error("dsa_sign: no DSA key"); + return -1; + } + digest = xmalloc(evp_md->md_size); + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, data, datalen); + EVP_DigestFinal(&md, digest, NULL); + + sig = DSA_do_sign(digest, evp_md->md_size, key->dsa); + if (sig == NULL) { + fatal("dsa_sign: cannot sign"); + } + + rlen = BN_num_bytes(sig->r); + slen = BN_num_bytes(sig->s); + if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { + error("bad sig size %d %d", rlen, slen); + DSA_SIG_free(sig); + return -1; + } + debug("sig size %d %d", rlen, slen); + + memset(sigblob, 0, SIGBLOB_LEN); + BN_bn2bin(sig->r, sigblob+ SIGBLOB_LEN - INTBLOB_LEN - rlen); + BN_bn2bin(sig->s, sigblob+ SIGBLOB_LEN - slen); + DSA_SIG_free(sig); + + if (datafellows & SSH_BUG_SIGBLOB) { + debug("datafellows"); + ret = xmalloc(SIGBLOB_LEN); + memcpy(ret, sigblob, SIGBLOB_LEN); + if (lenp != NULL) + *lenp = SIGBLOB_LEN; + if (sigp != NULL) + *sigp = ret; + } else { + /* ietf-drafts */ + buffer_init(&b); + buffer_put_cstring(&b, KEX_DSS); + buffer_put_string(&b, sigblob, SIGBLOB_LEN); + len = buffer_len(&b); + ret = xmalloc(len); + memcpy(ret, buffer_ptr(&b), len); + buffer_free(&b); + if (lenp != NULL) + *lenp = len; + if (sigp != NULL) + *sigp = ret; + } + return 0; +} +int +dsa_verify( + Key *key, + unsigned char *signature, int signaturelen, + unsigned char *data, int datalen) +{ + Buffer b; + unsigned char *digest; + DSA_SIG *sig; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + char *ktype; + unsigned char *sigblob; + char *txt; + unsigned int len; + int rlen; + int ret; + + if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { + error("dsa_verify: no DSA key"); + return -1; + } + + if (!(datafellows & SSH_BUG_SIGBLOB) && + signaturelen == SIGBLOB_LEN) { + datafellows |= ~SSH_BUG_SIGBLOB; + log("autodetect SSH_BUG_SIGBLOB"); + } else if ((datafellows & SSH_BUG_SIGBLOB) && + signaturelen != SIGBLOB_LEN) { + log("autoremove SSH_BUG_SIGBLOB"); + datafellows &= ~SSH_BUG_SIGBLOB; + } + + debug("len %d datafellows %d", signaturelen, datafellows); + + /* fetch signature */ + if (datafellows & SSH_BUG_SIGBLOB) { + sigblob = signature; + len = signaturelen; + } else { + /* ietf-drafts */ + buffer_init(&b); + buffer_append(&b, (char *) signature, signaturelen); + ktype = buffer_get_string(&b, NULL); + sigblob = (unsigned char *)buffer_get_string(&b, &len); + rlen = buffer_len(&b); + if(rlen != 0) + error("remaining bytes in signature %d", rlen); + buffer_free(&b); + } + + if (len != SIGBLOB_LEN) { + fatal("bad sigbloblen %d != SIGBLOB_LEN", len); + } + + /* parse signature */ + sig = DSA_SIG_new(); + sig->r = BN_new(); + sig->s = BN_new(); + BN_bin2bn(sigblob, INTBLOB_LEN, sig->r); + BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s); + + if (!(datafellows & SSH_BUG_SIGBLOB)) { + memset(sigblob, 0, len); + xfree(sigblob); + } + + /* sha1 the data */ + digest = xmalloc(evp_md->md_size); + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, data, datalen); + EVP_DigestFinal(&md, digest, NULL); + + ret = DSA_do_verify(digest, evp_md->md_size, sig, key->dsa); + + memset(digest, 0, evp_md->md_size); + xfree(digest); + DSA_SIG_free(sig); + + switch (ret) { + case 1: + txt = "correct"; + break; + case 0: + txt = "incorrect"; + break; + case -1: + default: + txt = "error"; + break; + } + debug("dsa_verify: signature %s", txt); + return ret; +} + +Key * +dsa_generate_key(unsigned int bits) +{ + DSA *dsa = DSA_generate_parameters(bits, NULL, 0, NULL, NULL, NULL, NULL); + Key *k; + if (dsa == NULL) { + fatal("DSA_generate_parameters failed"); + } + if (!DSA_generate_key(dsa)) { + fatal("DSA_generate_keys failed"); + } + + k = key_new(KEY_EMPTY); + k->type = KEY_DSA; + k->dsa = dsa; + return k; +} diff --git a/crypto/openssh/dsa.h b/crypto/openssh/dsa.h new file mode 100644 index 0000000..3cece7c --- /dev/null +++ b/crypto/openssh/dsa.h @@ -0,0 +1,22 @@ +#ifndef DSA_H +#define DSA_H + +Key *dsa_key_from_blob(char *blob, int blen); +int dsa_make_key_blob(Key *key, unsigned char **blobp, unsigned int *lenp); + +int +dsa_sign( + Key *key, + unsigned char **sigp, int *lenp, + unsigned char *data, int datalen); + +int +dsa_verify( + Key *key, + unsigned char *signature, int signaturelen, + unsigned char *data, int datalen); + +Key * +dsa_generate_key(unsigned int bits); + +#endif diff --git a/crypto/openssh/getput.h b/crypto/openssh/getput.h index d2e1380..e1aa9bb 100644 --- a/crypto/openssh/getput.h +++ b/crypto/openssh/getput.h @@ -1,19 +1,19 @@ /* - * + * * getput.h - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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: getput.h,v 1.2 1999/11/24 00:26:02 deraadt Exp $"); */ +/* RCSID("$Id: getput.h,v 1.3 2000/04/14 10:30:31 markus Exp $"); */ #ifndef GETPUT_H #define GETPUT_H @@ -21,7 +21,7 @@ /*------------ macros for storing/extracting msb first words -------------*/ #define GET_32BIT(cp) (((unsigned long)(unsigned char)(cp)[0] << 24) | \ - ((unsigned long)(unsigned char)(cp)[1] << 16) | \ + ((unsigned long)(unsigned char)(cp)[1] << 16) | \ ((unsigned long)(unsigned char)(cp)[2] << 8) | \ ((unsigned long)(unsigned char)(cp)[3])) diff --git a/crypto/openssh/hmac.c b/crypto/openssh/hmac.c new file mode 100644 index 0000000..fe53aa4 --- /dev/null +++ b/crypto/openssh/hmac.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$Id: hmac.c,v 1.2 2000/04/12 09:39:10 markus Exp $"); + +#include "xmalloc.h" +#include "ssh.h" +#include "getput.h" + +#include <openssl/hmac.h> + +unsigned char * +hmac( + EVP_MD *evp_md, + unsigned int seqno, + unsigned char *data, int datalen, + unsigned char *key, int keylen) +{ + HMAC_CTX c; + static unsigned char m[EVP_MAX_MD_SIZE]; + unsigned char b[4]; + + if (key == NULL) + fatal("hmac: no key"); + HMAC_Init(&c, key, keylen, evp_md); + PUT_32BIT(b, seqno); + HMAC_Update(&c, b, sizeof b); + HMAC_Update(&c, data, datalen); + HMAC_Final(&c, m, NULL); + HMAC_cleanup(&c); + return(m); +} diff --git a/crypto/openssh/hmac.h b/crypto/openssh/hmac.h new file mode 100644 index 0000000..fb68029 --- /dev/null +++ b/crypto/openssh/hmac.h @@ -0,0 +1,11 @@ +#ifndef HMAC_H +#define HMAC_H + +unsigned char * +hmac( + EVP_MD *evp_md, + unsigned int seqno, + unsigned char *data, int datalen, + unsigned char *key, int len); + +#endif diff --git a/crypto/openssh/hostfile.h b/crypto/openssh/hostfile.h index 64fe185..c9bdd7f 100644 --- a/crypto/openssh/hostfile.h +++ b/crypto/openssh/hostfile.h @@ -10,7 +10,7 @@ typedef enum { HOST_OK, HOST_NEW, HOST_CHANGED } HostStatus; -HostStatus +HostStatus check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *found); /* diff --git a/crypto/openssh/kex.c b/crypto/openssh/kex.c new file mode 100644 index 0000000..c10c77e --- /dev/null +++ b/crypto/openssh/kex.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$Id: kex.c,v 1.6 2000/05/08 17:42:25 markus Exp $"); + +#include "ssh.h" +#include "ssh2.h" +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "cipher.h" +#include "compat.h" + +#include <openssl/bn.h> +#include <openssl/dh.h> + +#include <openssl/crypto.h> +#include <openssl/bio.h> +#include <openssl/bn.h> +#include <openssl/dh.h> +#include <openssl/pem.h> + +#include "kex.h" + +Buffer * +kex_init(char *myproposal[PROPOSAL_MAX]) +{ + char c = 0; + unsigned char cookie[16]; + u_int32_t rand = 0; + int i; + Buffer *ki = xmalloc(sizeof(*ki)); + for (i = 0; i < 16; i++) { + if (i % 4 == 0) + rand = arc4random(); + cookie[i] = rand & 0xff; + rand >>= 8; + } + buffer_init(ki); + buffer_append(ki, (char *)cookie, sizeof cookie); + for (i = 0; i < PROPOSAL_MAX; i++) + buffer_put_cstring(ki, myproposal[i]); + buffer_append(ki, &c, 1); /* boolean first_kex_packet_follows */ + buffer_put_int(ki, 0); /* uint32 0 (reserved for future extension) */ + return ki; +} + +/* diffie-hellman-group1-sha1 */ + +int +dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) +{ + int i; + int n = BN_num_bits(dh_pub); + int bits_set = 0; + + /* we only accept g==2 */ + if (!BN_is_word(dh->g, 2)) { + log("invalid DH base != 2"); + return 0; + } + if (dh_pub->neg) { + log("invalid public DH value: negativ"); + return 0; + } + for (i = 0; i <= n; i++) + if (BN_is_bit_set(dh_pub, i)) + bits_set++; + debug("bits set: %d/%d", bits_set, BN_num_bits(dh->p)); + + /* if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial */ + if (bits_set > 1 && (BN_cmp(dh_pub, dh->p) == -1)) + return 1; + log("invalid public DH value (%d/%d)", bits_set, BN_num_bits(dh->p)); + return 0; +} + +DH * +dh_new_group1() +{ + static char *group1 = + "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" + "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" + "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381" + "FFFFFFFF" "FFFFFFFF"; + DH *dh; + int ret, tries = 0; + dh = DH_new(); + if(dh == NULL) + fatal("DH_new"); + ret = BN_hex2bn(&dh->p, group1); + if(ret<0) + fatal("BN_hex2bn"); + dh->g = BN_new(); + if(dh->g == NULL) + fatal("DH_new g"); + BN_set_word(dh->g, 2); + do { + if (DH_generate_key(dh) == 0) + fatal("DH_generate_key"); + if (tries++ > 10) + fatal("dh_new_group1: too many bad keys: giving up"); + } while (!dh_pub_is_valid(dh, dh->pub_key)); + return dh; +} + +void +bignum_print(BIGNUM *b) +{ + BN_print_fp(stderr,b); +} + +void +dump_digest(unsigned char *digest, int len) +{ + int i; + for (i = 0; i< len; i++){ + fprintf(stderr, "%02x", digest[i]); + if(i%2!=0) + fprintf(stderr, " "); + } + fprintf(stderr, "\n"); +} + +unsigned char * +kex_hash( + char *client_version_string, + char *server_version_string, + char *ckexinit, int ckexinitlen, + char *skexinit, int skexinitlen, + char *serverhostkeyblob, int sbloblen, + BIGNUM *client_dh_pub, + BIGNUM *server_dh_pub, + BIGNUM *shared_secret) +{ + Buffer b; + static unsigned char digest[EVP_MAX_MD_SIZE]; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + + buffer_init(&b); + buffer_put_string(&b, client_version_string, strlen(client_version_string)); + buffer_put_string(&b, server_version_string, strlen(server_version_string)); + + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + buffer_put_int(&b, ckexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, ckexinit, ckexinitlen); + buffer_put_int(&b, skexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, skexinit, skexinitlen); + + buffer_put_string(&b, serverhostkeyblob, sbloblen); + buffer_put_bignum2(&b, client_dh_pub); + buffer_put_bignum2(&b, server_dh_pub); + buffer_put_bignum2(&b, shared_secret); + +#ifdef DEBUG_KEX + buffer_dump(&b); +#endif + + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestFinal(&md, digest, NULL); + + buffer_free(&b); + +#ifdef DEBUG_KEX + dump_digest(digest, evp_md->md_size); +#endif + return digest; +} + +unsigned char * +derive_key(int id, int need, char unsigned *hash, BIGNUM *shared_secret) +{ + Buffer b; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + char c = id; + int have; + int mdsz = evp_md->md_size; + unsigned char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz); + + buffer_init(&b); + buffer_put_bignum2(&b, shared_secret); + + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); /* shared_secret K */ + EVP_DigestUpdate(&md, hash, mdsz); /* transport-06 */ + EVP_DigestUpdate(&md, &c, 1); /* key id */ + EVP_DigestUpdate(&md, hash, mdsz); /* session id */ + EVP_DigestFinal(&md, digest, NULL); + + /* expand */ + for (have = mdsz; need > have; have += mdsz) { + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestUpdate(&md, hash, mdsz); + EVP_DigestUpdate(&md, digest, have); + EVP_DigestFinal(&md, digest + have, NULL); + } + buffer_free(&b); +#ifdef DEBUG_KEX + fprintf(stderr, "Digest '%c'== ", c); + dump_digest(digest, need); +#endif + return digest; +} + +#define NKEYS 6 + +#define MAX_PROP 20 +#define SEP "," + +char * +get_match(char *client, char *server) +{ + char *sproposals[MAX_PROP]; + char *p; + int i, j, nproposals; + + for ((p = strtok(server, SEP)), i=0; p; (p = strtok(NULL, SEP)), i++) { + if (i < MAX_PROP) + sproposals[i] = p; + else + break; + } + nproposals = i; + + for ((p = strtok(client, SEP)), i=0; p; (p = strtok(NULL, SEP)), i++) { + for (j = 0; j < nproposals; j++) + if (strcmp(p, sproposals[j]) == 0) + return xstrdup(p); + } + return NULL; +} +void +choose_enc(Enc *enc, char *client, char *server) +{ + char *name = get_match(client, server); + if (name == NULL) + fatal("no matching cipher found: client %s server %s", client, server); + enc->type = cipher_number(name); + + switch (enc->type) { + case SSH_CIPHER_3DES_CBC: + enc->key_len = 24; + enc->iv_len = 8; + enc->block_size = 8; + break; + case SSH_CIPHER_BLOWFISH_CBC: + case SSH_CIPHER_CAST128_CBC: + enc->key_len = 16; + enc->iv_len = 8; + enc->block_size = 8; + break; + case SSH_CIPHER_ARCFOUR: + enc->key_len = 16; + enc->iv_len = 0; + enc->block_size = 8; + break; + default: + fatal("unsupported cipher %s", name); + } + enc->name = name; + enc->enabled = 0; + enc->iv = NULL; + enc->key = NULL; +} +void +choose_mac(Mac *mac, char *client, char *server) +{ + char *name = get_match(client, server); + if (name == NULL) + fatal("no matching mac found: client %s server %s", client, server); + if (strcmp(name, "hmac-md5") == 0) { + mac->md = EVP_md5(); + } else if (strcmp(name, "hmac-sha1") == 0) { + mac->md = EVP_sha1(); + } else if (strcmp(name, "hmac-ripemd160@openssh.com") == 0) { + mac->md = EVP_ripemd160(); + } else { + fatal("unsupported mac %s", name); + } + mac->name = name; + mac->mac_len = mac->md->md_size; + mac->key_len = (datafellows & SSH_BUG_HMAC) ? 16 : mac->mac_len; + mac->key = NULL; + mac->enabled = 0; +} +void +choose_comp(Comp *comp, char *client, char *server) +{ + char *name = get_match(client, server); + if (name == NULL) + fatal("no matching comp found: client %s server %s", client, server); + if (strcmp(name, "zlib") == 0) { + comp->type = 1; + } else if (strcmp(name, "none") == 0) { + comp->type = 0; + } else { + fatal("unsupported comp %s", name); + } + comp->name = name; +} +void +choose_kex(Kex *k, char *client, char *server) +{ + k->name = get_match(client, server); + if (k->name == NULL) + fatal("no kex alg"); + if (strcmp(k->name, KEX_DH1) != 0) + fatal("bad kex alg %s", k->name); +} +void +choose_hostkeyalg(Kex *k, char *client, char *server) +{ + k->hostkeyalg = get_match(client, server); + if (k->hostkeyalg == NULL) + fatal("no hostkey alg"); + if (strcmp(k->hostkeyalg, KEX_DSS) != 0) + fatal("bad hostkey alg %s", k->hostkeyalg); +} + +Kex * +kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server) +{ + int i; + int mode; + int ctos; /* direction: if true client-to-server */ + int need; + Kex *k; + + k = xmalloc(sizeof(*k)); + memset(k, 0, sizeof(*k)); + k->server = server; + + for (mode = 0; mode < MODE_MAX; mode++) { + int nenc, nmac, ncomp; + ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN); + nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; + nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; + ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; + choose_enc (&k->enc [mode], cprop[nenc], sprop[nenc]); + choose_mac (&k->mac [mode], cprop[nmac], sprop[nmac]); + choose_comp(&k->comp[mode], cprop[ncomp], sprop[ncomp]); + debug("kex: %s %s %s %s", + ctos ? "client->server" : "server->client", + k->enc[mode].name, + k->mac[mode].name, + k->comp[mode].name); + } + choose_kex(k, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]); + choose_hostkeyalg(k, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], + sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]); + for (i = 0; i < PROPOSAL_MAX; i++) { + xfree(cprop[i]); + xfree(sprop[i]); + } + need = 0; + for (mode = 0; mode < MODE_MAX; mode++) { + if (need < k->enc[mode].key_len) + need = k->enc[mode].key_len; + if (need < k->enc[mode].iv_len) + need = k->enc[mode].iv_len; + if (need < k->mac[mode].key_len) + need = k->mac[mode].key_len; + } + /* need runden? */ +#define WE_NEED 32 + k->we_need = WE_NEED; + k->we_need = need; + return k; +} + +int +kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret) +{ + int i; + int mode; + int ctos; + unsigned char *keys[NKEYS]; + + for (i = 0; i < NKEYS; i++) + keys[i] = derive_key('A'+i, k->we_need, hash, shared_secret); + + for (mode = 0; mode < MODE_MAX; mode++) { + ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN); + k->enc[mode].iv = keys[ctos ? 0 : 1]; + k->enc[mode].key = keys[ctos ? 2 : 3]; + k->mac[mode].key = keys[ctos ? 4 : 5]; + } + return 0; +} diff --git a/crypto/openssh/kex.h b/crypto/openssh/kex.h new file mode 100644 index 0000000..5395ebc --- /dev/null +++ b/crypto/openssh/kex.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef KEX_H +#define KEX_H + +#define KEX_DH1 "diffie-hellman-group1-sha1" +#define KEX_DSS "ssh-dss" + +enum kex_init_proposals { + PROPOSAL_KEX_ALGS, + PROPOSAL_SERVER_HOST_KEY_ALGS, + PROPOSAL_ENC_ALGS_CTOS, + PROPOSAL_ENC_ALGS_STOC, + PROPOSAL_MAC_ALGS_CTOS, + PROPOSAL_MAC_ALGS_STOC, + PROPOSAL_COMP_ALGS_CTOS, + PROPOSAL_COMP_ALGS_STOC, + PROPOSAL_LANG_CTOS, + PROPOSAL_LANG_STOC, + PROPOSAL_MAX +}; + +enum kex_modes { + MODE_IN, + MODE_OUT, + MODE_MAX +}; + +typedef struct Kex Kex; +typedef struct Mac Mac; +typedef struct Comp Comp; +typedef struct Enc Enc; + +struct Enc { + int type; + int enabled; + int block_size; + unsigned char *key; + unsigned char *iv; + int key_len; + int iv_len; + char *name; +}; +struct Mac { + EVP_MD *md; + int enabled; + int mac_len; + unsigned char *key; + int key_len; + char *name; +}; +struct Comp { + int type; + int enabled; + char *name; +}; +struct Kex { + Enc enc [MODE_MAX]; + Mac mac [MODE_MAX]; + Comp comp[MODE_MAX]; + int we_need; + int server; + char *name; + char *hostkeyalg; +}; + +Buffer *kex_init(char *myproposal[PROPOSAL_MAX]); +int dh_pub_is_valid(DH *dh, BIGNUM *dh_pub); +DH *dh_new_group1(); +Kex *kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server); +int kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret); +void bignum_print(BIGNUM *b); +void packet_set_kex(Kex *k); + +unsigned char * +kex_hash( + char *client_version_string, + char *server_version_string, + char *ckexinit, int ckexinitlen, + char *skexinit, int skexinitlen, + char *serverhostkeyblob, int sbloblen, + BIGNUM *client_dh_pub, + BIGNUM *server_dh_pub, + BIGNUM *shared_secret); + +#endif diff --git a/crypto/openssh/key.h b/crypto/openssh/key.h index 70f0c51..ed3f770 100644 --- a/crypto/openssh/key.h +++ b/crypto/openssh/key.h @@ -17,7 +17,9 @@ Key *key_new(int type); void key_free(Key *k); int key_equal(Key *a, Key *b); char *key_fingerprint(Key *k); +char *key_type(Key *k); int key_write(Key *key, FILE *f); -int key_read(Key *key, unsigned int bits, char **cpp); +unsigned int +key_read(Key *key, char **cpp); #endif diff --git a/crypto/openssh/lib/Makefile b/crypto/openssh/lib/Makefile index 4a9ce1c..35de105 100644 --- a/crypto/openssh/lib/Makefile +++ b/crypto/openssh/lib/Makefile @@ -5,7 +5,7 @@ SRCS= authfd.c authfile.c bufaux.c buffer.c canohost.c channels.c \ cipher.c compat.c compress.c crc32.c deattack.c fingerprint.c \ hostfile.c log.c match.c mpaux.c nchan.c packet.c readpass.c \ rsa.c tildexpand.c ttymodes.c uidswap.c xmalloc.c atomicio.c \ - key.c + key.c dispatch.c dsa.c kex.c hmac.c uuencode.c NOPROFILE= yes NOPIC= yes diff --git a/crypto/openssh/log-client.c b/crypto/openssh/log-client.c index 9e20a31..1e3c162 100644 --- a/crypto/openssh/log-client.c +++ b/crypto/openssh/log-client.c @@ -1,21 +1,21 @@ /* - * + * * log-client.c - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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: log-client.c,v 1.7 2000/02/27 18:50:09 deraadt Exp $"); +RCSID("$Id: log-client.c,v 1.8 2000/04/14 10:30:31 markus Exp $"); #include "xmalloc.h" #include "ssh.h" diff --git a/crypto/openssh/log-server.c b/crypto/openssh/log-server.c index 124d7fe..81ba673 100644 --- a/crypto/openssh/log-server.c +++ b/crypto/openssh/log-server.c @@ -1,21 +1,21 @@ /* - * + * * log-server.c - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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: log-server.c,v 1.12 2000/02/27 18:50:09 deraadt Exp $"); +RCSID("$Id: log-server.c,v 1.14 2000/04/14 10:30:31 markus Exp $"); #include <syslog.h> #include "packet.h" @@ -32,7 +32,7 @@ static int log_facility = LOG_AUTH; * level logging level */ -void +void log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr) { switch (level) { @@ -132,9 +132,11 @@ do_log(LogLevel level, const char *fmt, va_list args) } else { vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); } - if (log_on_stderr) + if (log_on_stderr) { fprintf(stderr, "%s\n", msgbuf); - openlog(__progname, LOG_PID, log_facility); - syslog(pri, "%.500s", msgbuf); - closelog(); + } else { + openlog(__progname, LOG_PID, log_facility); + syslog(pri, "%.500s", msgbuf); + closelog(); + } } diff --git a/crypto/openssh/match.c b/crypto/openssh/match.c index aadcfd6..00dff8a 100644 --- a/crypto/openssh/match.c +++ b/crypto/openssh/match.c @@ -1,20 +1,20 @@ /* - * + * * match.c - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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: match.c,v 1.5 2000/03/23 22:15:33 markus Exp $"); +RCSID("$Id: match.c,v 1.6 2000/04/14 10:30:31 markus Exp $"); #include "ssh.h" @@ -23,7 +23,7 @@ RCSID("$Id: match.c,v 1.5 2000/03/23 22:15:33 markus Exp $"); * and * as wildcards), and zero if it does not match. */ -int +int match_pattern(const char *s, const char *pattern) { for (;;) { diff --git a/crypto/openssh/mpaux.h b/crypto/openssh/mpaux.h index 6b30fe0..671fc51 100644 --- a/crypto/openssh/mpaux.h +++ b/crypto/openssh/mpaux.h @@ -1,19 +1,19 @@ /* - * + * * mpaux.h - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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: mpaux.h,v 1.5 1999/11/24 19:53:48 markus Exp $"); */ +/* RCSID("$Id: mpaux.h,v 1.6 2000/04/14 10:30:32 markus Exp $"); */ #ifndef MPAUX_H #define MPAUX_H @@ -23,7 +23,7 @@ * session id is computed by concatenating the linearized, msb first * representations of host_key_n, session_key_n, and the cookie. */ -void +void compute_session_id(unsigned char session_id[16], unsigned char cookie[8], BIGNUM * host_key_n, diff --git a/crypto/openssh/myproposal.h b/crypto/openssh/myproposal.h new file mode 100644 index 0000000..8b24179 --- /dev/null +++ b/crypto/openssh/myproposal.h @@ -0,0 +1,20 @@ +#define KEX_DEFAULT_KEX "diffie-hellman-group1-sha1" +#define KEX_DEFAULT_PK_ALG "ssh-dss" +#define KEX_DEFAULT_ENCRYPT "3des-cbc,blowfish-cbc,arcfour,cast128-cbc" +#define KEX_DEFAULT_MAC "hmac-sha1,hmac-md5,hmac-ripemd160@openssh.com" +#define KEX_DEFAULT_COMP "zlib,none" +#define KEX_DEFAULT_LANG "" + + +static const char *myproposal[PROPOSAL_MAX] = { + KEX_DEFAULT_KEX, + KEX_DEFAULT_PK_ALG, + KEX_DEFAULT_ENCRYPT, + KEX_DEFAULT_ENCRYPT, + KEX_DEFAULT_MAC, + KEX_DEFAULT_MAC, + KEX_DEFAULT_COMP, + KEX_DEFAULT_COMP, + KEX_DEFAULT_LANG, + KEX_DEFAULT_LANG +}; diff --git a/crypto/openssh/nchan.c b/crypto/openssh/nchan.c index 1f30a7c..fd92fe8 100644 --- a/crypto/openssh/nchan.c +++ b/crypto/openssh/nchan.c @@ -28,7 +28,7 @@ */ #include "includes.h" -RCSID("$Id: nchan.c,v 1.10 2000/01/10 10:15:28 markus Exp $"); +RCSID("$Id: nchan.c,v 1.17 2000/05/08 17:44:54 markus Exp $"); #include "ssh.h" @@ -37,143 +37,192 @@ RCSID("$Id: nchan.c,v 1.10 2000/01/10 10:15:28 markus Exp $"); #include "channels.h" #include "nchan.h" -static void chan_send_ieof(Channel *c); -static void chan_send_oclose(Channel *c); -static void chan_shutdown_write(Channel *c); -static void chan_shutdown_read(Channel *c); -static void chan_delete_if_full_closed(Channel *c); +#include "ssh2.h" +#include "compat.h" +/* functions manipulating channel states */ /* * EVENTS update channel input/output states execute ACTIONS */ - /* events concerning the INPUT from socket for channel (istate) */ -void -chan_rcvd_oclose(Channel *c) +chan_event_fn *chan_rcvd_oclose = NULL; +chan_event_fn *chan_read_failed = NULL; +chan_event_fn *chan_ibuf_empty = NULL; +/* events concerning the OUTPUT from channel for socket (ostate) */ +chan_event_fn *chan_rcvd_ieof = NULL; +chan_event_fn *chan_write_failed = NULL; +chan_event_fn *chan_obuf_empty = NULL; +/* + * ACTIONS: should never update the channel states + */ +static void chan_send_ieof1(Channel *c); +static void chan_send_oclose1(Channel *c); +static void chan_send_close2(Channel *c); +static void chan_send_eof2(Channel *c); + +/* channel cleanup */ +chan_event_fn *chan_delete_if_full_closed = NULL; + +/* helper */ +static void chan_shutdown_write(Channel *c); +static void chan_shutdown_read(Channel *c); + +/* + * SSH1 specific implementation of event functions + */ + +static void +chan_rcvd_oclose1(Channel *c) { + debug("channel %d: rcvd oclose", c->self); switch (c->istate) { case CHAN_INPUT_WAIT_OCLOSE: - debug("channel %d: INPUT_WAIT_OCLOSE -> INPUT_CLOSED [rcvd OCLOSE]", c->self); + debug("channel %d: input wait_oclose -> closed", c->self); c->istate = CHAN_INPUT_CLOSED; break; case CHAN_INPUT_OPEN: - debug("channel %d: INPUT_OPEN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self); + debug("channel %d: input open -> closed", c->self); chan_shutdown_read(c); - chan_send_ieof(c); + chan_send_ieof1(c); c->istate = CHAN_INPUT_CLOSED; break; case CHAN_INPUT_WAIT_DRAIN: /* both local read_failed and remote write_failed */ - log("channel %d: INPUT_WAIT_DRAIN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self); - debug("channel %d: INPUT_WAIT_DRAIN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self); - chan_send_ieof(c); + log("channel %d: input drain -> closed", c->self); + chan_send_ieof1(c); c->istate = CHAN_INPUT_CLOSED; break; default: - error("protocol error: chan_rcvd_oclose %d for istate %d", c->self, c->istate); + error("channel %d: protocol error: chan_rcvd_oclose for istate %d", + c->self, c->istate); return; } - chan_delete_if_full_closed(c); } -void -chan_read_failed(Channel *c) +static void +chan_read_failed_12(Channel *c) { + debug("channel %d: read failed", c->self); switch (c->istate) { case CHAN_INPUT_OPEN: - debug("channel %d: INPUT_OPEN -> INPUT_WAIT_DRAIN [read failed]", c->self); + debug("channel %d: input open -> drain", c->self); chan_shutdown_read(c); c->istate = CHAN_INPUT_WAIT_DRAIN; + if (buffer_len(&c->input) == 0) { + debug("channel %d: input: no drain shortcut", c->self); + chan_ibuf_empty(c); + } break; default: - error("internal error: we do not read, but chan_read_failed %d for istate %d", - c->self, c->istate); + error("channel %d: internal error: we do not read, but chan_read_failed for istate %d", + c->self, c->istate); break; } } -void -chan_ibuf_empty(Channel *c) +static void +chan_ibuf_empty1(Channel *c) { + debug("channel %d: ibuf empty", c->self); if (buffer_len(&c->input)) { - error("internal error: chan_ibuf_empty %d for non empty buffer", c->self); + error("channel %d: internal error: chan_ibuf_empty for non empty buffer", + c->self); return; } 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); + debug("channel %d: input drain -> wait_oclose", c->self); + chan_send_ieof1(c); c->istate = CHAN_INPUT_WAIT_OCLOSE; break; default: - error("internal error: chan_ibuf_empty %d for istate %d", c->self, c->istate); + error("channel %d: internal error: chan_ibuf_empty for istate %d", + c->self, c->istate); break; } } - -/* events concerning the OUTPUT from channel for socket (ostate) */ -void -chan_rcvd_ieof(Channel *c) +static void +chan_rcvd_ieof1(Channel *c) { + debug("channel %d: rcvd ieof", c->self); + if (c->type != SSH_CHANNEL_OPEN) { + debug("channel %d: non-open", c->self); + if (c->istate == CHAN_INPUT_OPEN) { + debug("channel %d: non-open: input open -> wait_oclose", c->self); + chan_shutdown_read(c); + chan_send_ieof1(c); + c->istate = CHAN_INPUT_WAIT_OCLOSE; + } else { + error("channel %d: istate %d != open", c->self, c->istate); + } + if (c->ostate == CHAN_OUTPUT_OPEN) { + debug("channel %d: non-open: output open -> closed", c->self); + chan_send_oclose1(c); + c->ostate = CHAN_OUTPUT_CLOSED; + } else { + error("channel %d: ostate %d != open", c->self, c->ostate); + } + return; + } switch (c->ostate) { case CHAN_OUTPUT_OPEN: - debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_DRAIN [rvcd IEOF]", c->self); + debug("channel %d: output open -> drain", c->self); c->ostate = CHAN_OUTPUT_WAIT_DRAIN; break; case CHAN_OUTPUT_WAIT_IEOF: - debug("channel %d: OUTPUT_WAIT_IEOF -> OUTPUT_CLOSED [rvcd IEOF]", c->self); + debug("channel %d: output wait_ieof -> closed", c->self); c->ostate = CHAN_OUTPUT_CLOSED; - chan_delete_if_full_closed(c); break; default: - error("protocol error: chan_rcvd_ieof %d for ostate %d", c->self, c->ostate); + error("channel %d: protocol error: chan_rcvd_ieof for ostate %d", + c->self, c->ostate); break; } } -void -chan_write_failed(Channel *c) +static void +chan_write_failed1(Channel *c) { + debug("channel %d: write failed", c->self); switch (c->ostate) { case CHAN_OUTPUT_OPEN: - debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_IEOF [write failed]", c->self); - chan_send_oclose(c); + debug("channel %d: output open -> wait_ieof", c->self); + chan_send_oclose1(c); 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); + debug("channel %d: output wait_drain -> closed", c->self); + chan_send_oclose1(c); c->ostate = CHAN_OUTPUT_CLOSED; - chan_delete_if_full_closed(c); break; default: - error("internal error: chan_write_failed %d for ostate %d", c->self, c->ostate); + error("channel %d: internal error: chan_write_failed for ostate %d", + c->self, c->ostate); break; } } -void -chan_obuf_empty(Channel *c) +static void +chan_obuf_empty1(Channel *c) { + debug("channel %d: obuf empty", c->self); if (buffer_len(&c->output)) { - debug("internal error: chan_obuf_empty %d for non empty buffer", c->self); + error("channel %d: internal error: chan_obuf_empty for non empty buffer", + c->self); return; } 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); + debug("channel %d: output drain -> closed", c->self); + chan_send_oclose1(c); c->ostate = CHAN_OUTPUT_CLOSED; - chan_delete_if_full_closed(c); break; default: - error("internal error: chan_obuf_empty %d for ostate %d", c->self, c->ostate); + error("channel %d: internal error: chan_obuf_empty for ostate %d", + c->self, c->ostate); break; } } - -/* - * ACTIONS: should never update the channel states: c->istate or c->ostate - */ static void -chan_send_ieof(Channel *c) +chan_send_ieof1(Channel *c) { + debug("channel %d: send ieof", c->self); switch (c->istate) { case CHAN_INPUT_OPEN: case CHAN_INPUT_WAIT_DRAIN: @@ -182,13 +231,15 @@ chan_send_ieof(Channel *c) packet_send(); break; default: - error("internal error: channel %d: cannot send IEOF for istate %d", c->self, c->istate); + error("channel %d: internal error: cannot send ieof for istate %d", + c->self, c->istate); break; } } static void -chan_send_oclose(Channel *c) +chan_send_oclose1(Channel *c) { + debug("channel %d: send oclose", c->self); switch (c->ostate) { case CHAN_OUTPUT_OPEN: case CHAN_OUTPUT_WAIT_DRAIN: @@ -199,40 +250,246 @@ chan_send_oclose(Channel *c) packet_send(); break; default: - error("internal error: channel %d: cannot send OCLOSE for ostate %d", c->self, c->istate); + error("channel %d: internal error: cannot send oclose for ostate %d", + c->self, c->ostate); break; } } +static void +chan_delete_if_full_closed1(Channel *c) +{ + if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) { + debug("channel %d: full closed", c->self); + channel_free(c->self); + } +} -/* helper */ +/* + * the same for SSH2 + */ static void -chan_shutdown_write(Channel *c) +chan_rcvd_oclose2(Channel *c) { - /* shutdown failure is allowed if write failed already */ - debug("channel %d: shutdown_write", c->self); - if (shutdown(c->sock, SHUT_WR) < 0) - debug("chan_shutdown_write failed for #%d/fd%d: %.100s", - c->self, c->sock, strerror(errno)); + debug("channel %d: rcvd close", c->self); + if (c->flags & CHAN_CLOSE_RCVD) + error("channel %d: protocol error: close rcvd twice", c->self); + c->flags |= CHAN_CLOSE_RCVD; + if (c->type == SSH_CHANNEL_LARVAL) { + /* tear down larval channels immediately */ + c->ostate = CHAN_OUTPUT_CLOSED; + c->istate = CHAN_INPUT_CLOSED; + return; + } + switch (c->ostate) { + case CHAN_OUTPUT_OPEN: + /* wait until a data from the channel is consumed if a CLOSE is received */ + debug("channel %d: output open -> drain", c->self); + c->ostate = CHAN_OUTPUT_WAIT_DRAIN; + break; + } + switch (c->istate) { + case CHAN_INPUT_OPEN: + debug("channel %d: input open -> closed", c->self); + chan_shutdown_read(c); + break; + case CHAN_INPUT_WAIT_DRAIN: + debug("channel %d: input drain -> closed", c->self); + chan_send_eof2(c); + break; + } + c->istate = CHAN_INPUT_CLOSED; } static void -chan_shutdown_read(Channel *c) +chan_ibuf_empty2(Channel *c) +{ + debug("channel %d: ibuf empty", c->self); + if (buffer_len(&c->input)) { + error("channel %d: internal error: chan_ibuf_empty for non empty buffer", + c->self); + return; + } + switch (c->istate) { + case CHAN_INPUT_WAIT_DRAIN: + debug("channel %d: input drain -> closed", c->self); + if (!(c->flags & CHAN_CLOSE_SENT)) + chan_send_eof2(c); + c->istate = CHAN_INPUT_CLOSED; + break; + default: + error("channel %d: internal error: chan_ibuf_empty for istate %d", + c->self, c->istate); + break; + } +} +static void +chan_rcvd_ieof2(Channel *c) +{ + debug("channel %d: rcvd eof", c->self); + if (c->ostate == CHAN_OUTPUT_OPEN) { + debug("channel %d: output open -> drain", c->self); + c->ostate = CHAN_OUTPUT_WAIT_DRAIN; + } +} +static void +chan_write_failed2(Channel *c) { - debug("channel %d: shutdown_read", c->self); - if (shutdown(c->sock, SHUT_RD) < 0) - error("chan_shutdown_read failed for #%d/fd%d [i%d o%d]: %.100s", - c->self, c->sock, c->istate, c->ostate, strerror(errno)); + debug("channel %d: write failed", c->self); + switch (c->ostate) { + case CHAN_OUTPUT_OPEN: + debug("channel %d: output open -> closed", c->self); + chan_shutdown_write(c); /* ?? */ + c->ostate = CHAN_OUTPUT_CLOSED; + break; + case CHAN_OUTPUT_WAIT_DRAIN: + debug("channel %d: output drain -> closed", c->self); + chan_shutdown_write(c); + c->ostate = CHAN_OUTPUT_CLOSED; + break; + default: + error("channel %d: internal error: chan_write_failed for ostate %d", + c->self, c->ostate); + break; + } } static void -chan_delete_if_full_closed(Channel *c) +chan_obuf_empty2(Channel *c) +{ + debug("channel %d: obuf empty", c->self); + if (buffer_len(&c->output)) { + error("internal error: chan_obuf_empty %d for non empty buffer", + c->self); + return; + } + switch (c->ostate) { + case CHAN_OUTPUT_WAIT_DRAIN: + debug("channel %d: output drain -> closed", c->self); + chan_shutdown_write(c); + c->ostate = CHAN_OUTPUT_CLOSED; + break; + default: + error("channel %d: internal error: chan_obuf_empty for ostate %d", + c->self, c->ostate); + break; + } +} +static void +chan_send_eof2(Channel *c) +{ + debug("channel %d: send eof", c->self); + switch (c->istate) { + case CHAN_INPUT_WAIT_DRAIN: + packet_start(SSH2_MSG_CHANNEL_EOF); + packet_put_int(c->remote_id); + packet_send(); + break; + default: + error("channel %d: internal error: cannot send eof for istate %d", + c->self, c->istate); + break; + } +} +static void +chan_send_close2(Channel *c) +{ + debug("channel %d: send close", c->self); + if (c->ostate != CHAN_OUTPUT_CLOSED || + c->istate != CHAN_INPUT_CLOSED) { + error("channel %d: internal error: cannot send close for istate/ostate %d/%d", + c->self, c->istate, c->ostate); + } else if (c->flags & CHAN_CLOSE_SENT) { + error("channel %d: internal error: already sent close", c->self); + } else { + packet_start(SSH2_MSG_CHANNEL_CLOSE); + packet_put_int(c->remote_id); + packet_send(); + c->flags |= CHAN_CLOSE_SENT; + } +} +static void +chan_delete_if_full_closed2(Channel *c) { if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) { - debug("channel %d: full closed", c->self); - channel_free(c->self); + if (!(c->flags & CHAN_CLOSE_SENT)) { + chan_send_close2(c); + } + if ((c->flags & CHAN_CLOSE_SENT) && + (c->flags & CHAN_CLOSE_RCVD)) { + debug("channel %d: full closed2", c->self); + channel_free(c->self); + } } } + +/* shared */ void chan_init_iostates(Channel *c) { c->ostate = CHAN_OUTPUT_OPEN; c->istate = CHAN_INPUT_OPEN; + c->flags = 0; +} + +/* init */ +void +chan_init(void) +{ + if (compat20) { + chan_rcvd_oclose = chan_rcvd_oclose2; + chan_read_failed = chan_read_failed_12; + chan_ibuf_empty = chan_ibuf_empty2; + + chan_rcvd_ieof = chan_rcvd_ieof2; + chan_write_failed = chan_write_failed2; + chan_obuf_empty = chan_obuf_empty2; + + chan_delete_if_full_closed = chan_delete_if_full_closed2; + } else { + chan_rcvd_oclose = chan_rcvd_oclose1; + chan_read_failed = chan_read_failed_12; + chan_ibuf_empty = chan_ibuf_empty1; + + chan_rcvd_ieof = chan_rcvd_ieof1; + chan_write_failed = chan_write_failed1; + chan_obuf_empty = chan_obuf_empty1; + + chan_delete_if_full_closed = chan_delete_if_full_closed1; + } +} + +/* helper */ +static void +chan_shutdown_write(Channel *c) +{ + buffer_consume(&c->output, buffer_len(&c->output)); + if (compat20 && c->type == SSH_CHANNEL_LARVAL) + return; + /* shutdown failure is allowed if write failed already */ + debug("channel %d: close_write", c->self); + if (c->sock != -1) { + if (shutdown(c->sock, SHUT_WR) < 0) + debug("channel %d: chan_shutdown_write: shutdown() failed for fd%d: %.100s", + c->self, c->sock, strerror(errno)); + } else { + if (close(c->wfd) < 0) + log("channel %d: chan_shutdown_write: close() failed for fd%d: %.100s", + c->self, c->wfd, strerror(errno)); + c->wfd = -1; + } +} +static void +chan_shutdown_read(Channel *c) +{ + if (compat20 && c->type == SSH_CHANNEL_LARVAL) + return; + debug("channel %d: close_read", c->self); + if (c->sock != -1) { + if (shutdown(c->sock, SHUT_RD) < 0) + error("channel %d: chan_shutdown_read: shutdown() failed for fd%d [i%d o%d]: %.100s", + c->self, c->sock, c->istate, c->ostate, strerror(errno)); + } else { + if (close(c->rfd) < 0) + log("channel %d: chan_shutdown_read: close() failed for fd%d: %.100s", + c->self, c->rfd, strerror(errno)); + c->rfd = -1; + } } diff --git a/crypto/openssh/nchan.h b/crypto/openssh/nchan.h index 3397be8..0a2cf35 100644 --- a/crypto/openssh/nchan.h +++ b/crypto/openssh/nchan.h @@ -27,7 +27,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* RCSID("$Id: nchan.h,v 1.5 1999/11/24 16:15:25 markus Exp $"); */ +/* RCSID("$Id: nchan.h,v 1.7 2000/04/03 07:07:15 markus Exp $"); */ #ifndef NCHAN_H #define NCHAN_H @@ -72,15 +72,25 @@ #define CHAN_OUTPUT_WAIT_IEOF 0x40 #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); +#define CHAN_CLOSE_SENT 0x01 +#define CHAN_CLOSE_RCVD 0x02 -/* EVENTS for the output state */ -void chan_rcvd_ieof(Channel * c); -void chan_write_failed(Channel * c); -void chan_obuf_empty(Channel * c); + +/* Channel EVENTS */ +typedef void chan_event_fn(Channel * c); + +/* for the input state */ +extern chan_event_fn *chan_rcvd_oclose; +extern chan_event_fn *chan_read_failed; +extern chan_event_fn *chan_ibuf_empty; + +/* for the output state */ +extern chan_event_fn *chan_rcvd_ieof; +extern chan_event_fn *chan_write_failed; +extern chan_event_fn *chan_obuf_empty; + +extern chan_event_fn *chan_delete_if_full_closed; void chan_init_iostates(Channel * c); +void chan_init(void); #endif diff --git a/crypto/openssh/nchan2.ms b/crypto/openssh/nchan2.ms new file mode 100644 index 0000000..1b119d1 --- /dev/null +++ b/crypto/openssh/nchan2.ms @@ -0,0 +1,64 @@ +.TL +OpenSSH Channel Close Protocol 2.0 Implementation +.SH +Channel Input State Diagram +.PS +reset +l=1 +s=1.2 +ellipsewid=s*ellipsewid +boxwid=s*boxwid +ellipseht=s*ellipseht +S1: ellipse "INPUT" "OPEN" +move right 2*l from last ellipse.e +S3: ellipse invis +move down l from last ellipse.s +S4: ellipse "INPUT" "CLOSED" +move down l from 1st ellipse.s +S2: ellipse "INPUT" "WAIT" "DRAIN" +arrow from S1.e to S4.n +box invis "rcvd CLOSE/" "shutdown_read" with .sw at last arrow.c +arrow "ibuf_empty ||" "rcvd CLOSE/" "send EOF" "" from S2.e to S4.w +arrow from S1.s to S2.n +box invis "read_failed/" "shutdown_read" with .e at last arrow.c +ellipse wid .9*ellipsewid ht .9*ellipseht at S4 +arrow "start" "" from S1.w+(-0.5,0) to S1.w +.PE +.SH +Channel Output State Diagram +.PS +S1: ellipse "OUTPUT" "OPEN" +move right 2*l from last ellipse.e +S3: ellipse invis +move down l from last ellipse.s +S4: ellipse "OUTPUT" "CLOSED" +move down l from 1st ellipse.s +S2: ellipse "OUTPUT" "WAIT" "DRAIN" +arrow from S1.e to S4.n +box invis "write_failed/" "shutdown_write" with .sw at last arrow.c +arrow "obuf_empty ||" "write_failed/" "shutdown_write" "" from S2.e to S4.w +arrow from S1.s to S2.n +box invis "rcvd EOF ||" "rcvd CLOSE/" "-" with .e at last arrow.c +ellipse wid .9*ellipsewid ht .9*ellipseht at S4 +arrow "start" "" from S1.w+(-0.5,0) to S1.w +.PE +.SH +Notes +.PP +The input buffer is filled with data from the socket +(the socket represents the local consumer/producer of the +forwarded channel). +The data is then sent over the INPUT-end (transmit-end) of the channel to the +remote peer. +Data sent by the peer is received on the OUTPUT-end (receive-end), +saved in the output buffer and written to the socket. +.PP +If the local protocol instance has forwarded all data on the +INPUT-end of the channel, it sends an EOF message to the peer. +.PP +A CLOSE message is sent to the peer if +both the INPUT- and the OUTOUT-half of the local +end of the channel are closed. +.PP +The channel can be deallocated by a protocol instance +if a CLOSE message he been both sent and received. diff --git a/crypto/openssh/packet.c b/crypto/openssh/packet.c index af54759..5fa76a3 100644 --- a/crypto/openssh/packet.c +++ b/crypto/openssh/packet.c @@ -1,21 +1,23 @@ /* - * + * * packet.c - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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. - * + * + * SSH2 packet format added by Markus Friedl. + * */ #include "includes.h" -RCSID("$Id: packet.c,v 1.22 2000/02/05 10:13:11 markus Exp $"); +RCSID("$Id: packet.c,v 1.32 2000/05/04 22:22:43 markus Exp $"); #include "xmalloc.h" #include "buffer.h" @@ -28,6 +30,23 @@ RCSID("$Id: packet.c,v 1.22 2000/02/05 10:13:11 markus Exp $"); #include "compress.h" #include "deattack.h" +#include "channels.h" + +#include "compat.h" +#include "ssh2.h" + +#include <openssl/bn.h> +#include <openssl/dh.h> +#include <openssl/hmac.h> +#include "buffer.h" +#include "kex.h" +#include "hmac.h" + +#ifdef PACKET_DEBUG +#define DBG(x) x +#else +#define DBG(x) +#endif /* * This variable contains the file descriptors used for communicating with @@ -80,19 +99,53 @@ static int initialized = 0; /* Set to true if the connection is interactive. */ static int interactive_mode = 0; +/* True if SSH2 packet format is used */ +int use_ssh2_packet_format = 0; + +/* Session key information for Encryption and MAC */ +Kex *kex = NULL; + +void +packet_set_kex(Kex *k) +{ + if( k->mac[MODE_IN ].key == NULL || + k->enc[MODE_IN ].key == NULL || + k->enc[MODE_IN ].iv == NULL || + k->mac[MODE_OUT].key == NULL || + k->enc[MODE_OUT].key == NULL || + k->enc[MODE_OUT].iv == NULL) + fatal("bad KEX"); + kex = k; +} +void +clear_enc_keys(Enc *enc, int len) +{ + memset(enc->iv, 0, len); + memset(enc->key, 0, len); + xfree(enc->iv); + xfree(enc->key); + enc->iv = NULL; + enc->key = NULL; +} +void +packet_set_ssh2_format(void) +{ + DBG(debug("use_ssh2_packet_format")); + use_ssh2_packet_format = 1; +} + /* * Sets the descriptors used for communication. Disables encryption until * packet_set_encryption_key is called. */ - 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); + cipher_set_key(&send_context, SSH_CIPHER_NONE, (unsigned char *) "", 0); + cipher_set_key(&receive_context, SSH_CIPHER_NONE, (unsigned char *) "", 0); if (!initialized) { initialized = 1; buffer_init(&input); @@ -224,6 +277,7 @@ packet_get_protocol_flags() * Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. */ +/*** XXXXX todo: kex means re-init */ void packet_start_compression(int level) { @@ -241,7 +295,7 @@ packet_start_compression(int level) void packet_encrypt(CipherContext * cc, void *dest, void *src, - unsigned int bytes) + unsigned int bytes) { cipher_encrypt(cc, dest, src, bytes); } @@ -253,7 +307,7 @@ packet_encrypt(CipherContext * cc, void *dest, void *src, void packet_decrypt(CipherContext * cc, void *dest, void *src, - unsigned int bytes) + unsigned int bytes) { int i; @@ -265,15 +319,11 @@ packet_decrypt(CipherContext * cc, void *dest, void *src, * (C)1998 CORE-SDI, Buenos Aires Argentina Ariel Futoransky(futo@core-sdi.com) */ - switch (cc->type) { - case SSH_CIPHER_NONE: + if (cc->type == SSH_CIPHER_NONE || compat20) { i = DEATTACK_OK; - break; - default: + } else { i = detect_attack(src, bytes, NULL); - break; } - if (i == DEATTACK_DETECTED) packet_disconnect("crc32 compensation attack: network attack detected"); @@ -288,17 +338,20 @@ packet_decrypt(CipherContext * cc, void *dest, void *src, void packet_set_encryption_key(const unsigned char *key, unsigned int keylen, - int cipher) + int cipher) { + if (keylen < 20) + fatal("keylen too small: %d", keylen); + /* 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); + cipher_set_key(&receive_context, cipher, key, keylen); + cipher_set_key(&send_context, cipher, key, keylen); } /* Starts constructing a packet to send. */ void -packet_start(int type) +packet_start1(int type) { char buf[9]; @@ -308,6 +361,29 @@ packet_start(int type) buffer_append(&outgoing_packet, buf, 9); } +void +packet_start2(int type) +{ + char buf[4+1+1]; + + buffer_clear(&outgoing_packet); + memset(buf, 0, sizeof buf); + /* buf[0..3] = payload_len; */ + /* buf[4] = pad_len; */ + buf[5] = type & 0xff; + buffer_append(&outgoing_packet, buf, sizeof buf); +} + +void +packet_start(int type) +{ + DBG(debug("packet_start[%d]",type)); + if (use_ssh2_packet_format) + packet_start2(type); + else + packet_start1(type); +} + /* Appends a character to the packet data. */ void @@ -332,6 +408,18 @@ packet_put_string(const char *buf, unsigned int len) { buffer_put_string(&outgoing_packet, buf, len); } +void +packet_put_cstring(const char *str) +{ + buffer_put_string(&outgoing_packet, str, strlen(str)); +} + +void +packet_put_raw(const char *buf, unsigned int len) +{ + buffer_append(&outgoing_packet, buf, len); +} + /* Appends an arbitrary precision integer to packet data. */ @@ -340,6 +428,11 @@ packet_put_bignum(BIGNUM * value) { buffer_put_bignum(&outgoing_packet, value); } +void +packet_put_bignum2(BIGNUM * value) +{ + buffer_put_bignum2(&outgoing_packet, value); +} /* * Finalizes and sends the packet. If the encryption key has been set, @@ -347,7 +440,7 @@ packet_put_bignum(BIGNUM * value) */ void -packet_send() +packet_send1() { char buf[8], *cp; int i, padding, len; @@ -372,7 +465,7 @@ packet_send() /* Compute packet length without padding (add checksum, remove padding). */ len = buffer_len(&outgoing_packet) + 4 - 8; - /* Insert padding. */ + /* Insert padding. Initialized to zero in packet_start1() */ padding = 8 - len % 8; if (cipher_type != SSH_CIPHER_NONE) { cp = buffer_ptr(&outgoing_packet); @@ -418,6 +511,136 @@ packet_send() } /* + * Finalize packet in SSH2 format (compress, mac, encrypt, enqueue) + */ +void +packet_send2() +{ + unsigned char *macbuf = NULL; + char *cp; + unsigned int packet_length = 0; + unsigned int i, padlen, len; + u_int32_t rand = 0; + static unsigned int seqnr = 0; + int type; + Enc *enc = NULL; + Mac *mac = NULL; + Comp *comp = NULL; + int block_size; + + if (kex != NULL) { + enc = &kex->enc[MODE_OUT]; + mac = &kex->mac[MODE_OUT]; + comp = &kex->comp[MODE_OUT]; + } + block_size = enc ? enc->block_size : 8; + + cp = buffer_ptr(&outgoing_packet); + type = cp[5] & 0xff; + +#ifdef PACKET_DEBUG + fprintf(stderr, "plain: "); + buffer_dump(&outgoing_packet); +#endif + + if (comp && comp->enabled) { + len = buffer_len(&outgoing_packet); + /* skip header, compress only payload */ + buffer_consume(&outgoing_packet, 5); + buffer_clear(&compression_buffer); + buffer_compress(&outgoing_packet, &compression_buffer); + buffer_clear(&outgoing_packet); + buffer_append(&outgoing_packet, "\0\0\0\0\0", 5); + buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer), + buffer_len(&compression_buffer)); + DBG(debug("compression: raw %d compressed %d", len, + buffer_len(&outgoing_packet))); + } + + /* sizeof (packet_len + pad_len + payload) */ + len = buffer_len(&outgoing_packet); + + /* + * calc size of padding, alloc space, get random data, + * minimum padding is 4 bytes + */ + padlen = block_size - (len % block_size); + if (padlen < 4) + padlen += block_size; + buffer_append_space(&outgoing_packet, &cp, padlen); + if (enc && enc->type != SSH_CIPHER_NONE) { + /* random padding */ + for (i = 0; i < padlen; i++) { + if (i % 4 == 0) + rand = arc4random(); + cp[i] = rand & 0xff; + rand <<= 8; + } + } else { + /* clear padding */ + memset(cp, 0, padlen); + } + /* packet_length includes payload, padding and padding length field */ + packet_length = buffer_len(&outgoing_packet) - 4; + cp = buffer_ptr(&outgoing_packet); + PUT_32BIT(cp, packet_length); + cp[4] = padlen & 0xff; + DBG(debug("send: len %d (includes padlen %d)", packet_length+4, padlen)); + + /* compute MAC over seqnr and packet(length fields, payload, padding) */ + if (mac && mac->enabled) { + macbuf = hmac( mac->md, seqnr, + (unsigned char *) buffer_ptr(&outgoing_packet), + buffer_len(&outgoing_packet), + mac->key, mac->key_len + ); + DBG(debug("done calc HMAC out #%d", seqnr)); + } + /* encrypt packet and append to output buffer. */ + buffer_append_space(&output, &cp, buffer_len(&outgoing_packet)); + packet_encrypt(&send_context, cp, buffer_ptr(&outgoing_packet), + buffer_len(&outgoing_packet)); + /* append unencrypted MAC */ + if (mac && mac->enabled) + buffer_append(&output, (char *)macbuf, mac->mac_len); +#ifdef PACKET_DEBUG + fprintf(stderr, "encrypted: "); + buffer_dump(&output); +#endif + /* increment sequence number for outgoing packets */ + if (++seqnr == 0) + log("outgoing seqnr wraps around"); + buffer_clear(&outgoing_packet); + + if (type == SSH2_MSG_NEWKEYS) { + if (kex==NULL || mac==NULL || enc==NULL || comp==NULL) + fatal("packet_send2: no KEX"); + if (mac->md != NULL) + mac->enabled = 1; + DBG(debug("cipher_set_key_iv send_context")); + cipher_set_key_iv(&send_context, enc->type, + enc->key, enc->key_len, + enc->iv, enc->iv_len); + clear_enc_keys(enc, kex->we_need); + if (comp->type != 0 && comp->enabled == 0) { + comp->enabled = 1; + if (! packet_compression) + packet_start_compression(6); + } + } +} + +void +packet_send() +{ + if (use_ssh2_packet_format) + packet_send2(); + else + packet_send1(); + DBG(debug("packet_send done")); +} + +/* * Waits until a packet has been received, and returns its type. Note that * no other data is processed until this returns, so this function should not * be used during the interactive session. @@ -429,6 +652,7 @@ packet_read(int *payload_len_ptr) int type, len; fd_set set; char buf[8192]; + DBG(debug("packet_read()")); /* Since we are blocking, ensure that all written packets have been sent. */ packet_write_wait(); @@ -437,10 +661,11 @@ packet_read(int *payload_len_ptr) for (;;) { /* Try to read a packet from the buffer. */ type = packet_read_poll(payload_len_ptr); - if (type == SSH_SMSG_SUCCESS + if (!use_ssh2_packet_format && ( + type == SSH_SMSG_SUCCESS || type == SSH_SMSG_FAILURE || type == SSH_CMSG_EOF - || type == SSH_CMSG_EXIT_CONFIRMATION) + || 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) @@ -482,7 +707,7 @@ packet_read_expect(int *payload_len_ptr, int expected_type) type = packet_read(payload_len_ptr); if (type != expected_type) packet_disconnect("Protocol error: expected packet type %d, got %d", - expected_type, type); + expected_type, type); } /* Checks if a full packet is available in the data received so far via @@ -501,15 +726,13 @@ packet_read_expect(int *payload_len_ptr, int expected_type) */ int -packet_read_poll(int *payload_len_ptr) +packet_read_poll1(int *payload_len_ptr) { unsigned int len, padded_len; unsigned char *ucp; - char buf[8], *cp, *msg; + 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; @@ -542,7 +765,7 @@ restart: /* Compute packet checksum. */ checksum = crc32((unsigned char *) buffer_ptr(&incoming_packet), - buffer_len(&incoming_packet) - 4); + buffer_len(&incoming_packet) - 4); /* Skip padding. */ buffer_consume(&incoming_packet, 8 - len % 8); @@ -551,7 +774,7 @@ restart: if (len != buffer_len(&incoming_packet)) packet_disconnect("packet_read_poll: len %d != buffer_len %d.", - len, buffer_len(&incoming_packet)); + len, buffer_len(&incoming_packet)); ucp = (unsigned char *) buffer_ptr(&incoming_packet) + len - 4; stored_checksum = GET_32BIT(ucp); @@ -565,7 +788,7 @@ restart: buffer_uncompress(&incoming_packet, &compression_buffer); buffer_clear(&incoming_packet); buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), - buffer_len(&compression_buffer)); + buffer_len(&compression_buffer)); } /* Get packet type. */ buffer_get(&incoming_packet, &buf[0], 1); @@ -573,29 +796,208 @@ restart: /* 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) { - msg = packet_get_string(NULL); - log("Received disconnect: %.900s", msg); - xfree(msg); - fatal_cleanup(); - } - - /* 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) { - msg = packet_get_string(NULL); - debug("Remote: %.900s", msg); - xfree(msg); - goto restart; - } /* Return type. */ return (unsigned char) buf[0]; } +int +packet_read_poll2(int *payload_len_ptr) +{ + unsigned int padlen, need; + unsigned char buf[8], *macbuf; + unsigned char *ucp; + char *cp; + static unsigned int packet_length = 0; + static unsigned int seqnr = 0; + int type; + int maclen, block_size; + Enc *enc = NULL; + Mac *mac = NULL; + Comp *comp = NULL; + + if (kex != NULL) { + enc = &kex->enc[MODE_IN]; + mac = &kex->mac[MODE_IN]; + comp = &kex->comp[MODE_IN]; + } + maclen = mac && mac->enabled ? mac->mac_len : 0; + block_size = enc ? enc->block_size : 8; + + if (packet_length == 0) { + /* + * check if input size is less than the cipher block size, + * decrypt first block and extract length of incoming packet + */ + if (buffer_len(&input) < block_size) + return SSH_MSG_NONE; + buffer_clear(&incoming_packet); + buffer_append_space(&incoming_packet, &cp, block_size); + packet_decrypt(&receive_context, cp, buffer_ptr(&input), + block_size); + ucp = (unsigned char *) buffer_ptr(&incoming_packet); + packet_length = GET_32BIT(ucp); + if (packet_length < 1 + 4 || packet_length > 256 * 1024) { + buffer_dump(&incoming_packet); + packet_disconnect("Bad packet length %d.", packet_length); + } + DBG(debug("input: packet len %d", packet_length+4)); + buffer_consume(&input, block_size); + } + /* we have a partial packet of block_size bytes */ + need = 4 + packet_length - block_size; + DBG(debug("partial packet %d, need %d, maclen %d", block_size, + need, maclen)); + if (need % block_size != 0) + fatal("padding error: need %d block %d mod %d", + need, block_size, need % block_size); + /* + * check if the entire packet has been received and + * decrypt into incoming_packet + */ + if (buffer_len(&input) < need + maclen) + return SSH_MSG_NONE; +#ifdef PACKET_DEBUG + fprintf(stderr, "read_poll enc/full: "); + buffer_dump(&input); +#endif + buffer_append_space(&incoming_packet, &cp, need); + packet_decrypt(&receive_context, cp, buffer_ptr(&input), need); + buffer_consume(&input, need); + /* + * compute MAC over seqnr and packet, + * increment sequence number for incoming packet + */ + if (mac && mac->enabled) { + macbuf = hmac( mac->md, seqnr, + (unsigned char *) buffer_ptr(&incoming_packet), + buffer_len(&incoming_packet), + mac->key, mac->key_len + ); + if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0) + packet_disconnect("Corrupted HMAC on input."); + DBG(debug("HMAC #%d ok", seqnr)); + buffer_consume(&input, mac->mac_len); + } + if (++seqnr == 0) + log("incoming seqnr wraps around"); + + /* get padlen */ + cp = buffer_ptr(&incoming_packet) + 4; + padlen = *cp & 0xff; + DBG(debug("input: padlen %d", padlen)); + if (padlen < 4) + packet_disconnect("Corrupted padlen %d on input.", padlen); + + /* skip packet size + padlen, discard padding */ + buffer_consume(&incoming_packet, 4 + 1); + buffer_consume_end(&incoming_packet, padlen); + + DBG(debug("input: len before de-compress %d", buffer_len(&incoming_packet))); + if (comp && comp->enabled) { + 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)); + DBG(debug("input: len after de-compress %d", buffer_len(&incoming_packet))); + } + /* + * get packet type, implies consume. + * return length of payload (without type field) + */ + buffer_get(&incoming_packet, (char *)&buf[0], 1); + *payload_len_ptr = buffer_len(&incoming_packet); + + /* reset for next packet */ + packet_length = 0; + + /* extract packet type */ + type = (unsigned char)buf[0]; + + if (type == SSH2_MSG_NEWKEYS) { + if (kex==NULL || mac==NULL || enc==NULL || comp==NULL) + fatal("packet_read_poll2: no KEX"); + if (mac->md != NULL) + mac->enabled = 1; + DBG(debug("cipher_set_key_iv receive_context")); + cipher_set_key_iv(&receive_context, enc->type, + enc->key, enc->key_len, + enc->iv, enc->iv_len); + clear_enc_keys(enc, kex->we_need); + if (comp->type != 0 && comp->enabled == 0) { + comp->enabled = 1; + if (! packet_compression) + packet_start_compression(6); + } + } + +#ifdef PACKET_DEBUG + fprintf(stderr, "read/plain[%d]:\r\n",type); + buffer_dump(&incoming_packet); +#endif + return (unsigned char)type; +} + +int +packet_read_poll(int *payload_len_ptr) +{ + char *msg; + for (;;) { + int type = use_ssh2_packet_format ? + packet_read_poll2(payload_len_ptr): + packet_read_poll1(payload_len_ptr); + + if(compat20) { + int reason; + if (type != 0) + DBG(debug("received packet type %d", type)); + switch(type) { + case SSH2_MSG_IGNORE: + break; + case SSH2_MSG_DEBUG: + packet_get_char(); + msg = packet_get_string(NULL); + debug("Remote: %.900s", msg); + xfree(msg); + msg = packet_get_string(NULL); + xfree(msg); + break; + case SSH2_MSG_DISCONNECT: + reason = packet_get_int(); + msg = packet_get_string(NULL); + log("Received disconnect: %d: %.900s", reason, msg); + xfree(msg); + fatal_cleanup(); + break; + default: + return type; + break; + } + } else { + switch(type) { + case SSH_MSG_IGNORE: + break; + case SSH_MSG_DEBUG: + msg = packet_get_string(NULL); + debug("Remote: %.900s", msg); + xfree(msg); + break; + case SSH_MSG_DISCONNECT: + msg = packet_get_string(NULL); + log("Received disconnect: %.900s", msg); + fatal_cleanup(); + xfree(msg); + break; + default: + if (type != 0) + DBG(debug("received packet type %d", type)); + return type; + break; + } + } + } +} + /* * Buffers the given amount of input characters. This is intended to be used * together with packet_read_poll. @@ -636,6 +1038,27 @@ packet_get_bignum(BIGNUM * value, int *length_ptr) *length_ptr = buffer_get_bignum(&incoming_packet, value); } +void +packet_get_bignum2(BIGNUM * value, int *length_ptr) +{ + *length_ptr = buffer_get_bignum2(&incoming_packet, value); +} + +char * +packet_get_raw(int *length_ptr) +{ + int bytes = buffer_len(&incoming_packet); + if (length_ptr != NULL) + *length_ptr = bytes; + return buffer_ptr(&incoming_packet); +} + +int +packet_remaining(void) +{ + return buffer_len(&incoming_packet); +} + /* * 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 @@ -668,8 +1091,15 @@ packet_send_debug(const char *fmt,...) vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); - packet_start(SSH_MSG_DEBUG); - packet_put_string(buf, strlen(buf)); + if (compat20) { + packet_start(SSH2_MSG_DEBUG); + packet_put_char(0); /* bool: always display */ + packet_put_cstring(buf); + packet_put_cstring(""); + } else { + packet_start(SSH_MSG_DEBUG); + packet_put_cstring(buf); + } packet_send(); packet_write_wait(); } @@ -700,8 +1130,15 @@ packet_disconnect(const char *fmt,...) 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)); + if (compat20) { + packet_start(SSH2_MSG_DISCONNECT); + packet_put_int(SSH2_DISCONNECT_PROTOCOL_ERROR); + packet_put_cstring(buf); + packet_put_cstring(""); + } else { + packet_start(SSH_MSG_DISCONNECT); + packet_put_string(buf, strlen(buf)); + } packet_send(); packet_write_wait(); @@ -832,7 +1269,8 @@ 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); + log("packet_set_maxsize: called twice: old %d new %d", + max_packet_size, s); return -1; } if (s < 4 * 1024 || s > 1024 * 1024) { diff --git a/crypto/openssh/pty.h b/crypto/openssh/pty.h index 7d8e09d..a5f9cb2 100644 --- a/crypto/openssh/pty.h +++ b/crypto/openssh/pty.h @@ -1,19 +1,19 @@ /* - * + * * pty.h - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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: pty.h,v 1.5 2000/02/15 16:52:58 markus Exp $"); */ +/* RCSID("$Id: pty.h,v 1.6 2000/04/14 10:30:32 markus Exp $"); */ #ifndef PTY_H #define PTY_H @@ -39,7 +39,7 @@ void pty_release(const char *ttyname); void pty_make_controlling_tty(int *ttyfd, const char *ttyname); /* Changes the window size associated with the pty. */ -void +void pty_change_window_size(int ptyfd, int row, int col, int xpixel, int ypixel); diff --git a/crypto/openssh/radix.c b/crypto/openssh/radix.c index ea7f5ba..0337733 100644 --- a/crypto/openssh/radix.c +++ b/crypto/openssh/radix.c @@ -1,109 +1,15 @@ /* * 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 <dugsong@UMICH.EDU> */ #include "includes.h" +#include "uuencode.h" #ifdef AFS #include <krb.h> -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', '+', '/' -}; - -unsigned char pr2six[256]; - -int -uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded) -{ - /* 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 < 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) -{ - /* 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); -} - typedef unsigned char my_u_char; typedef unsigned int my_u_int32_t; typedef unsigned short my_u_short; @@ -162,8 +68,8 @@ typedef unsigned short my_u_short; } -int -creds_to_radix(CREDENTIALS *creds, unsigned char *buf) +int +creds_to_radix(CREDENTIALS *creds, unsigned char *buf, size_t buflen) { char *p, *s; int len; @@ -213,10 +119,10 @@ creds_to_radix(CREDENTIALS *creds, unsigned char *buf) p += creds->ticket_st.length; len = p - temp; - return (uuencode((unsigned char *)temp, len, (char *)buf)); + return (uuencode((unsigned char *)temp, len, (char *)buf, buflen)); } -int +int radix_to_creds(const char *buf, CREDENTIALS *creds) { @@ -225,7 +131,8 @@ radix_to_creds(const char *buf, CREDENTIALS *creds) char version; char temp[2048]; - if (!(len = uudecode(buf, (unsigned char *)temp, sizeof(temp)))) + len = uudecode(buf, (unsigned char *)temp, sizeof(temp)); + if (len < 0) return 0; p = temp; diff --git a/crypto/openssh/readpass.c b/crypto/openssh/readpass.c index deb37cab6..85d88f8 100644 --- a/crypto/openssh/readpass.c +++ b/crypto/openssh/readpass.c @@ -32,7 +32,7 @@ */ #include "includes.h" -RCSID("$Id: readpass.c,v 1.9 2000/01/21 21:16:00 deraadt Exp $"); +RCSID("$Id: readpass.c,v 1.10 2000/04/14 10:30:32 markus Exp $"); #include "xmalloc.h" #include "ssh.h" @@ -58,7 +58,7 @@ read_passphrase(const char *prompt, int from_stdin) sigset_t oset, nset; struct sigaction sa, osa; int input, output, echo = 0; - + if (from_stdin) { input = STDIN_FILENO; output = STDERR_FILENO; diff --git a/crypto/openssh/scp.1 b/crypto/openssh/scp.1 index 8e93e23..51de6c5 100644 --- a/crypto/openssh/scp.1 +++ b/crypto/openssh/scp.1 @@ -9,7 +9,7 @@ .\" .\" Created: Sun May 7 00:14:37 1995 ylo .\" -.\" $Id: scp.1,v 1.6 2000/03/23 21:10:09 aaron Exp $ +.\" $Id: scp.1,v 1.7 2000/04/12 21:47:50 aaron Exp $ .\" .Dd September 25, 1999 .Dt SCP 1 @@ -36,7 +36,7 @@ .Ar host2 No : .Oc Ar file2 .Sm on -.Sh DESCRIPTION +.Sh DESCRIPTION .Nm copies files between hosts on a network. It uses @@ -74,7 +74,7 @@ Recursively copy entire directories. Verbose mode. Causes .Nm -and +and .Xr ssh 1 to print debugging messages about their progress. This is helpful in diff --git a/crypto/openssh/scp.c b/crypto/openssh/scp.c index 915ef97..491a789 100644 --- a/crypto/openssh/scp.c +++ b/crypto/openssh/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 <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> - * + * */ /* @@ -45,7 +45,7 @@ */ #include "includes.h" -RCSID("$Id: scp.c,v 1.26 2000/03/16 20:56:14 markus Exp $"); +RCSID("$Id: scp.c,v 1.30 2000/05/02 18:21:48 deraadt Exp $"); #include "ssh.h" #include "xmalloc.h" @@ -109,7 +109,7 @@ char *port = NULL; * assigns the input and output file descriptors on success. */ -int +int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) { int pin[2], pout[2], reserved[2]; @@ -194,7 +194,7 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) return 0; } -void +void fatal(const char *fmt,...) { va_list ap; @@ -257,10 +257,10 @@ main(argc, argv) switch (ch) { /* User-visible flags. */ case '4': - IPv4 = 1; + IPv4 = 1; break; case '6': - IPv6 = 1; + IPv6 = 1; break; case 'p': pflag = 1; @@ -543,7 +543,7 @@ syserr: run_err("%s: %s", name, strerror(errno)); (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) atomicio(write, remout, buf, strlen(buf)); if (response() < 0) goto next; } @@ -556,7 +556,7 @@ syserr: run_err("%s: %s", name, strerror(errno)); fprintf(stderr, "Sending file modes: %s", buf); fflush(stderr); } - (void) write(remout, buf, strlen(buf)); + (void) atomicio(write, remout, buf, strlen(buf)); if (response() < 0) goto next; if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { @@ -573,14 +573,14 @@ next: (void) close(fd); if (i + amt > stb.st_size) amt = stb.st_size - i; if (!haderr) { - result = read(fd, bp->buf, amt); + result = atomicio(read, fd, bp->buf, amt); if (result != amt) haderr = result >= 0 ? EIO : errno; } if (haderr) - (void) write(remout, bp->buf, amt); + (void) atomicio(write, remout, bp->buf, amt); else { - result = write(remout, bp->buf, amt); + result = atomicio(write, remout, bp->buf, amt); if (result != amt) haderr = result >= 0 ? EIO : errno; statbytes += result; @@ -592,7 +592,7 @@ next: (void) close(fd); if (close(fd) < 0 && !haderr) haderr = errno; if (!haderr) - (void) write(remout, "", 1); + (void) atomicio(write, remout, "", 1); else run_err("%s: %s", name, strerror(haderr)); (void) response(); @@ -621,7 +621,7 @@ rsource(name, statp) (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) atomicio(write, remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; @@ -632,7 +632,7 @@ rsource(name, statp) 0, last); if (verbose_mode) fprintf(stderr, "Entering directory: %s", path); - (void) write(remout, path, strlen(path)); + (void) atomicio(write, remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; @@ -651,7 +651,7 @@ rsource(name, statp) source(1, vect); } (void) closedir(dirp); - (void) write(remout, "E\n", 2); + (void) atomicio(write, remout, "E\n", 2); (void) response(); } @@ -687,17 +687,17 @@ sink(argc, argv) if (targetshouldbedirectory) verifydir(targ); - (void) write(remout, "", 1); + (void) atomicio(write, remout, "", 1); if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) targisdir = 1; for (first = 1;; first = 0) { cp = buf; - if (read(remin, cp, 1) <= 0) + if (atomicio(read, remin, cp, 1) <= 0) return; if (*cp++ == '\n') SCREWUP("unexpected <newline>"); do { - if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) + if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) SCREWUP("lost connection"); *cp++ = ch; } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); @@ -705,7 +705,7 @@ sink(argc, argv) if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) - (void) write(STDERR_FILENO, + (void) atomicio(write, STDERR_FILENO, buf + 1, strlen(buf + 1)); if (buf[0] == '\02') exit(1); @@ -713,7 +713,7 @@ sink(argc, argv) continue; } if (buf[0] == 'E') { - (void) write(remout, "", 1); + (void) atomicio(write, remout, "", 1); return; } if (ch == '\n') @@ -737,7 +737,7 @@ sink(argc, argv) getnum(dummy_usec); if (*cp++ != '\0') SCREWUP("atime.usec not delimited"); - (void) write(remout, "", 1); + (void) atomicio(write, remout, "", 1); continue; } if (*cp != 'C' && *cp != 'D') { @@ -816,7 +816,7 @@ sink(argc, argv) bad: run_err("%s: %s", np, strerror(errno)); continue; } - (void) write(remout, "", 1); + (void) atomicio(write, remout, "", 1); if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { (void) close(ofd); continue; @@ -835,7 +835,7 @@ bad: run_err("%s: %s", np, strerror(errno)); amt = size - i; count += amt; do { - j = read(remin, cp, amt); + j = atomicio(read, remin, cp, amt); if (j <= 0) { run_err("%s", j ? strerror(errno) : "dropped connection"); @@ -848,7 +848,7 @@ bad: run_err("%s: %s", np, strerror(errno)); if (count == bp->cnt) { /* Keep reading so we stay sync'd up. */ if (wrerr == NO) { - j = write(ofd, bp->buf, count); + j = atomicio(write, ofd, bp->buf, count); if (j != count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; @@ -861,7 +861,7 @@ bad: run_err("%s: %s", np, strerror(errno)); if (showprogress) progressmeter(1); if (count != 0 && wrerr == NO && - (j = write(ofd, bp->buf, count)) != count) { + (j = atomicio(write, ofd, bp->buf, count)) != count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; } @@ -897,7 +897,7 @@ bad: run_err("%s: %s", np, strerror(errno)); run_err("%s: %s", np, strerror(wrerrno)); break; case NO: - (void) write(remout, "", 1); + (void) atomicio(write, remout, "", 1); break; case DISPLAYED: break; @@ -913,7 +913,7 @@ response() { char ch, *cp, resp, rbuf[2048]; - if (read(remin, &resp, sizeof(resp)) != sizeof(resp)) + if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) lostconn(0); cp = rbuf; @@ -926,13 +926,13 @@ response() case 1: /* error, followed by error msg */ case 2: /* fatal error, "" */ do { - if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) + if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) lostconn(0); *cp++ = ch; } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); if (!iamremote) - (void) write(STDERR_FILENO, rbuf, cp - rbuf); + (void) atomicio(write, STDERR_FILENO, rbuf, cp - rbuf); ++errs; if (resp == 1) return (-1); @@ -1006,7 +1006,7 @@ run_err(const char *fmt,...) * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: scp.c,v 1.26 2000/03/16 20:56:14 markus Exp $ + * $Id: scp.c,v 1.30 2000/05/02 18:21:48 deraadt Exp $ */ char * @@ -1209,7 +1209,12 @@ progressmeter(int flag) snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " - stalled -"); } else { - remaining = (int) (totalbytes / (statbytes / elapsed) - elapsed); + if (flag != 1) + remaining = + (int)(totalbytes / (statbytes / elapsed) - elapsed); + else + remaining = elapsed; + i = remaining / 3600; if (i) snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), @@ -1219,7 +1224,8 @@ progressmeter(int flag) " "); i = remaining % 3600; snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "%02d:%02d ETA", i / 60, i % 60); + "%02d:%02d%s", i / 60, i % 60, + (flag != 1) ? " ETA" : " "); } atomicio(write, fileno(stdout), buf, strlen(buf)); @@ -1228,7 +1234,7 @@ progressmeter(int flag) alarmtimer(1); } else if (flag == 1) { alarmtimer(0); - write(fileno(stdout), "\n", 1); + atomicio(write, fileno(stdout), "\n", 1); statbytes = 0; } } diff --git a/crypto/openssh/serverloop.c b/crypto/openssh/serverloop.c index a5ecfe9..9bf3127 100644 --- a/crypto/openssh/serverloop.c +++ b/crypto/openssh/serverloop.c @@ -5,6 +5,10 @@ * Created: Sun Sep 10 00:30:37 1995 ylo * Server main loop for handling the interactive session. */ +/* + * SSH2 support by Markus Friedl. + * Copyright (c) 2000 Markus Friedl. All rights reserved. + */ #include "includes.h" #include "xmalloc.h" @@ -13,6 +17,12 @@ #include "buffer.h" #include "servconf.h" #include "pty.h" +#include "channels.h" + +#include "compat.h" +#include "ssh2.h" +#include "session.h" +#include "dispatch.h" static Buffer stdin_buffer; /* Buffer for stdin data. */ static Buffer stdout_buffer; /* Buffer for stdout data. */ @@ -38,15 +48,18 @@ static int max_fd; /* Max file descriptor number for select(). */ * will exit after that, as soon as forwarded connections have terminated. */ -static int child_pid; /* Pid of the child. */ +static pid_t 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 +void server_init_dispatch(void); + +void sigchld_handler(int sig) { int save_errno = errno; - int wait_pid; + pid_t wait_pid; + debug("Received SIGCHLD."); wait_pid = wait((int *) &child_wait_status); if (wait_pid != -1) { @@ -60,110 +73,21 @@ sigchld_handler(int sig) signal(SIGCHLD, sigchld_handler); errno = save_errno; } - -/* - * Process any buffered packets that have been received from the client. - */ -void -process_buffered_input_packets() +void +sigchld_handler2(int sig) { - 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); - } - } + int save_errno = errno; + debug("Received SIGCHLD."); + child_terminated = 1; + signal(SIGCHLD, sigchld_handler2); + errno = save_errno; } /* * Make packets from buffered stderr data, and buffer it for sending * to the client. */ -void +void make_packets_from_stderr_data() { int len; @@ -192,7 +116,7 @@ make_packets_from_stderr_data() * Make packets from buffered stdout data, and buffer it for sending to the * client. */ -void +void make_packets_from_stdout_data() { int len; @@ -223,7 +147,7 @@ make_packets_from_stdout_data() * have data or can accept data. Optionally, a maximum time can be specified * for the duration of the wait (0 = infinite). */ -void +void wait_until_can_do_something(fd_set * readset, fd_set * writeset, unsigned int max_time_milliseconds) { @@ -240,15 +164,21 @@ retry_select: * 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 (compat20) { + /* wrong: bad condition XXX */ + if (channel_not_very_much_buffered_data()) + FD_SET(connection_in, readset); + } else { + 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 (!compat20 && packet_not_very_much_data_to_write()) { if (!fdout_eof) FD_SET(fdout, readset); if (!fderr_eof) @@ -268,7 +198,7 @@ retry_select: /* If we have buffered data, try to write some of that data to the program. */ - if (fdin != -1 && buffer_len(&stdin_buffer) > 0) + if (!compat20 && fdin != -1 && buffer_len(&stdin_buffer) > 0) FD_SET(fdin, writeset); /* Update the maximum descriptor number if appropriate. */ @@ -290,6 +220,8 @@ retry_select: tv.tv_usec = 1000 * (max_time_milliseconds % 1000); tvp = &tv; } + if (tvp!=NULL) + debug("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds); /* Wait for something to happen, or the timeout to expire. */ ret = select(max_fd + 1, readset, writeset, NULL, tvp); @@ -306,7 +238,7 @@ retry_select: * Processes input from the client and the program. Input data is stored * in buffers and processed later. */ -void +void process_input(fd_set * readset) { int len; @@ -333,6 +265,9 @@ process_input(fd_set * readset) /* Buffer any received data. */ packet_process_incoming(buf, len); } + if (compat20) + return; + /* Read and buffer any available stdout data from the program. */ if (!fdout_eof && FD_ISSET(fdout, readset)) { len = read(fdout, buf, sizeof(buf)); @@ -356,20 +291,20 @@ process_input(fd_set * readset) /* * Sends data from internal buffers to client program stdin. */ -void +void process_output(fd_set * writeset) { int len; /* Write buffered data to program stdin. */ - if (fdin != -1 && FD_ISSET(fdin, writeset)) { + if (!compat20 && 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); #else - if (fdout == -1) + if (fdin != fdout) close(fdin); else shutdown(fdin, SHUT_WR); /* We will no longer send. */ @@ -391,7 +326,7 @@ process_output(fd_set * writeset) * Wait until all buffered output has been sent to the client. * This is used when the program terminates. */ -void +void drain_output() { /* Send any buffered stdout data to the client. */ @@ -416,6 +351,12 @@ drain_output() packet_write_wait(); } +void +process_buffered_input_packets() +{ + dispatch_run(DISPATCH_NONBLOCK, NULL); +} + /* * Performs the interactive session. This handles data transmission between * the client and the program. Note that the notion of stdin, stdout, and @@ -423,10 +364,11 @@ drain_output() * 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) +void +server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) { - int wait_status, wait_pid; /* Status and pid returned by wait(). */ + int wait_status; /* Status returned by wait(). */ + pid_t wait_pid; /* pid returned by wait(). */ int waiting_termination = 0; /* Have displayed waiting close message. */ unsigned int max_time_milliseconds; unsigned int previous_stdout_buffer_bytes; @@ -480,6 +422,8 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) if (fderr == -1) fderr_eof = 1; + server_init_dispatch(); + /* Main loop of the server for the interactive session mode. */ for (;;) { fd_set readset, writeset; @@ -495,7 +439,7 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) #ifdef USE_PIPES close(fdin); #else - if (fdout == -1) + if (fdin != fdout) close(fdin); else shutdown(fdin, SHUT_WR); /* We will no longer send. */ @@ -536,7 +480,7 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) 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; + break; if (!waiting_termination) { const char *s = "Waiting for forwarded connections to terminate...\r\n"; char *cp; @@ -563,7 +507,6 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) process_output(&writeset); } -quit: /* Cleanup and termination code. */ /* Wait until all output has been sent to the client. */ @@ -649,3 +592,233 @@ quit: packet_disconnect("wait returned status %04x.", wait_status); /* NOTREACHED */ } + +void +server_loop2(void) +{ + fd_set readset, writeset; + int had_channel = 0; + int status; + pid_t pid; + + debug("Entering interactive session for SSH2."); + + signal(SIGCHLD, sigchld_handler2); + child_terminated = 0; + 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; + server_init_dispatch(); + + for (;;) { + process_buffered_input_packets(); + if (!had_channel && channel_still_open()) + had_channel = 1; + if (had_channel && !channel_still_open()) { + debug("!channel_still_open."); + break; + } + if (packet_not_very_much_data_to_write()) + channel_output_poll(); + wait_until_can_do_something(&readset, &writeset, 0); + if (child_terminated) { + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + session_close_by_pid(pid, status); + child_terminated = 0; + } + channel_after_select(&readset, &writeset); + process_input(&readset); + process_output(&writeset); + } + signal(SIGCHLD, SIG_DFL); + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + session_close_by_pid(pid, status); + channel_stop_listening(); +} + +void +server_input_stdin_data(int type, int plen) +{ + char *data; + unsigned int data_len; + + /* Stdin data from the client. Append it to the buffer. */ + /* Ignore any data if the client has closed stdin. */ + if (fdin == -1) + return; + data = packet_get_string(&data_len); + packet_integrity_check(plen, (4 + data_len), type); + buffer_append(&stdin_buffer, data, data_len); + memset(data, 0, data_len); + xfree(data); +} + +void +server_input_eof(int type, int plen) +{ + /* + * 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(plen, 0, type); + stdin_eof = 1; +} + +void +server_input_window_size(int type, int plen) +{ + int row = packet_get_int(); + int col = packet_get_int(); + int xpixel = packet_get_int(); + int ypixel = packet_get_int(); + + debug("Window change received."); + packet_integrity_check(plen, 4 * 4, type); + if (fdin != -1) + pty_change_window_size(fdin, row, col, xpixel, ypixel); +} + +int +input_direct_tcpip(void) +{ + int sock; + char *target, *originator; + int target_port, originator_port; + + target = packet_get_string(NULL); + target_port = packet_get_int(); + originator = packet_get_string(NULL); + originator_port = packet_get_int(); + packet_done(); + /* XXX check permission */ + sock = channel_connect_to(target, target_port); + xfree(target); + xfree(originator); + if (sock < 0) + return -1; + return channel_new("direct-tcpip", SSH_CHANNEL_OPEN, + sock, sock, -1, 4*1024, 32*1024, 0, xstrdup("direct-tcpip")); +} + +void +server_input_channel_open(int type, int plen) +{ + Channel *c = NULL; + char *ctype; + int id; + unsigned int len; + int rchan; + int rmaxpack; + int rwindow; + + ctype = packet_get_string(&len); + rchan = packet_get_int(); + rwindow = packet_get_int(); + rmaxpack = packet_get_int(); + + debug("channel_input_open: ctype %s rchan %d win %d max %d", + ctype, rchan, rwindow, rmaxpack); + + if (strcmp(ctype, "session") == 0) { + debug("open session"); + packet_done(); + /* + * A server session has no fd to read or write + * until a CHANNEL_REQUEST for a shell is made, + * so we set the type to SSH_CHANNEL_LARVAL. + * Additionally, a callback for handling all + * CHANNEL_REQUEST messages is registered. + */ + id = channel_new(ctype, SSH_CHANNEL_LARVAL, + -1, -1, -1, 0, 32*1024, 0, xstrdup("server-session")); + if (session_open(id) == 1) { + channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST, + session_input_channel_req, (void *)0); + channel_register_cleanup(id, session_close_by_channel); + c = channel_lookup(id); + } else { + debug("session open failed, free channel %d", id); + channel_free(id); + } + } else if (strcmp(ctype, "direct-tcpip") == 0) { + debug("open direct-tcpip"); + id = input_direct_tcpip(); + if (id >= 0) + c = channel_lookup(id); + } + if (c != NULL) { + debug("confirm %s", ctype); + c->remote_id = rchan; + c->remote_window = rwindow; + c->remote_maxpacket = rmaxpack; + + packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(c->remote_id); + packet_put_int(c->self); + packet_put_int(c->local_window); + packet_put_int(c->local_maxpacket); + packet_send(); + } else { + debug("failure %s", ctype); + packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(rchan); + packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); + packet_put_cstring("bla bla"); + packet_put_cstring(""); + packet_send(); + } + xfree(ctype); +} + +void +server_init_dispatch_20() +{ + debug("server_init_dispatch_20"); + dispatch_init(&dispatch_protocol_error); + dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); + dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); + dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); + dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request); + dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); +} +void +server_init_dispatch_13() +{ + debug("server_init_dispatch_13"); + dispatch_init(NULL); + dispatch_set(SSH_CMSG_EOF, &server_input_eof); + dispatch_set(SSH_CMSG_STDIN_DATA, &server_input_stdin_data); + dispatch_set(SSH_CMSG_WINDOW_SIZE, &server_input_window_size); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); + dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); +} +void +server_init_dispatch_15() +{ + server_init_dispatch_13(); + debug("server_init_dispatch_15"); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose); +} +void +server_init_dispatch() +{ + if (compat20) + server_init_dispatch_20(); + else if (compat13) + server_init_dispatch_13(); + else + server_init_dispatch_15(); +} diff --git a/crypto/openssh/session.c b/crypto/openssh/session.c new file mode 100644 index 0000000..53d20c5 --- /dev/null +++ b/crypto/openssh/session.c @@ -0,0 +1,1516 @@ +/* + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + */ +/* + * SSH2 support by Markus Friedl. + * Copyright (c) 2000 Markus Friedl. All rights reserved. + */ + +#include "includes.h" +RCSID("$OpenBSD: session.c,v 1.12 2000/05/03 18:03:07 markus Exp $"); + +#include "xmalloc.h" +#include "ssh.h" +#include "pty.h" +#include "packet.h" +#include "buffer.h" +#include "cipher.h" +#include "mpaux.h" +#include "servconf.h" +#include "uidswap.h" +#include "compat.h" +#include "channels.h" +#include "nchan.h" + +#include "bufaux.h" +#include "ssh2.h" +#include "auth.h" + +/* types */ + +#define TTYSZ 64 +typedef struct Session Session; +struct Session { + int used; + int self; + int extended; + struct passwd *pw; + pid_t pid; + /* tty */ + char *term; + int ptyfd, ttyfd, ptymaster; + int row, col, xpixel, ypixel; + char tty[TTYSZ]; + /* X11 */ + char *display; + int screen; + char *auth_proto; + char *auth_data; + int single_connection; + /* proto 2 */ + int chanid; +}; + +/* func */ + +Session *session_new(void); +void session_set_fds(Session *s, int fdin, int fdout, int fderr); +void session_pty_cleanup(Session *s); +void session_proctitle(Session *s); +void do_exec_pty(Session *s, const char *command, struct passwd * pw); +void do_exec_no_pty(Session *s, const char *command, struct passwd * pw); + +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); + +/* import */ +extern ServerOptions options; +extern char *__progname; +extern int log_stderr; +extern int debug_flag; + +/* Local Xauthority file. */ +static char *xauthfile; + +/* data */ +#define MAX_SESSIONS 10 +Session sessions[MAX_SESSIONS]; + +/* 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; + +/* RSA authentication "command=" option. */ +char *forced_command = NULL; + +/* RSA authentication "environment=" options. */ +struct envstring *custom_environment = NULL; + +/* + * Remove local Xauthority file. + */ +void +xauthfile_cleanup_proc(void *ignore) +{ + debug("xauthfile_cleanup_proc called"); + + if (xauthfile != NULL) { + char *p; + unlink(xauthfile); + p = strrchr(xauthfile, '/'); + if (p != NULL) { + *p = '\0'; + rmdir(xauthfile); + } + xfree(xauthfile); + xauthfile = NULL; + } +} + +/* + * Function to perform cleanup if we get aborted abnormally (e.g., due to a + * dropped connection). + */ +void +pty_cleanup_proc(void *session) +{ + Session *s=session; + if (s == NULL) + fatal("pty_cleanup_proc: no session"); + debug("pty_cleanup_proc: %s", s->tty); + + if (s->pid != 0) { + /* Record that the user has logged out. */ + record_logout(s->pid, s->tty); + } + + /* Release the pseudo-tty. */ + pty_release(s->tty); +} + +/* + * 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) +{ + Session *s; + int type; + int compression_level = 0, enable_compression_after_reply = 0; + int have_pty = 0; + char *command; + int n_bytes; + int plen; + unsigned int proto_len, data_len, dlen; + + /* + * 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.) + */ + if (!no_port_forwarding_flag) + channel_permit_all_opens(); + + s = session_new(); + s->pw = pw; + + /* + * We stay in this loop until the client requests to execute a shell + * or a command. + */ + for (;;) { + int success = 0; + + /* 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); + break; + } + /* Enable compression after we have responded with SUCCESS. */ + enable_compression_after_reply = 1; + success = 1; + break; + + case SSH_CMSG_REQUEST_PTY: + if (no_pty_flag) { + debug("Allocating a pty not permitted for this authentication."); + break; + } + if (have_pty) + packet_disconnect("Protocol error: you already have a pty."); + + debug("Allocating pty."); + + /* Allocate a pty and open it. */ + if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, + sizeof(s->tty))) { + error("Failed to allocate pty."); + break; + } + fatal_add_cleanup(pty_cleanup_proc, (void *)s); + pty_setowner(pw, s->tty); + + /* Get TERM from the packet. Note that the value may be of arbitrary length. */ + s->term = packet_get_string(&dlen); + packet_integrity_check(dlen, strlen(s->term), type); + /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */ + /* Remaining bytes */ + n_bytes = plen - (4 + dlen + 4 * 4); + + if (strcmp(s->term, "") == 0) { + xfree(s->term); + s->term = NULL; + } + /* Get window size from the packet. */ + s->row = packet_get_int(); + s->col = packet_get_int(); + s->xpixel = packet_get_int(); + s->ypixel = packet_get_int(); + pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); + + /* Get tty modes from the packet. */ + tty_parse_modes(s->ttyfd, &n_bytes); + packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type); + + session_proctitle(s); + + /* Indicate that we now have a pty. */ + success = 1; + have_pty = 1; + break; + + case SSH_CMSG_X11_REQUEST_FORWARDING: + if (!options.x11_forwarding) { + packet_send_debug("X11 forwarding disabled in server configuration file."); + break; + } +#ifdef XAUTH_PATH + if (no_x11_forwarding_flag) { + packet_send_debug("X11 forwarding not permitted for this authentication."); + break; + } + debug("Received request for X11 forwarding with auth spoofing."); + if (s->display != NULL) + packet_disconnect("Protocol error: X11 display already set."); + + s->auth_proto = packet_get_string(&proto_len); + s->auth_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) + s->screen = packet_get_int(); + else + s->screen = 0; + s->display = x11_create_display_inet(s->screen, options.x11_display_offset); + + if (s->display == NULL) + break; + + /* Setup to always have a local .Xauthority. */ + xauthfile = xmalloc(MAXPATHLEN); + strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); + temporarily_use_uid(pw->pw_uid); + if (mkdtemp(xauthfile) == NULL) { + restore_uid(); + error("private X11 dir: mkdtemp %s failed: %s", + xauthfile, strerror(errno)); + xfree(xauthfile); + xauthfile = NULL; + /* XXXX remove listening channels */ + break; + } + strlcat(xauthfile, "/cookies", MAXPATHLEN); + open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + restore_uid(); + fatal_add_cleanup(xauthfile_cleanup_proc, NULL); + success = 1; + break; +#else /* XAUTH_PATH */ + packet_send_debug("No xauth program; cannot forward with spoofing."); + break; +#endif /* XAUTH_PATH */ + + case SSH_CMSG_AGENT_REQUEST_FORWARDING: + if (no_agent_forwarding_flag || compat13) { + debug("Authentication agent forwarding not permitted for this authentication."); + break; + } + debug("Received authentication agent forwarding request."); + auth_input_request_forwarding(pw); + success = 1; + break; + + case SSH_CMSG_PORT_FORWARD_REQUEST: + if (no_port_forwarding_flag) { + debug("Port forwarding not permitted for this authentication."); + break; + } + debug("Received TCP/IP port forwarding request."); + channel_input_port_forward_request(pw->pw_uid == 0, options.gateway_ports); + success = 1; + break; + + case SSH_CMSG_MAX_PACKET_SIZE: + if (packet_set_maxsize(packet_get_int()) > 0) + success = 1; + break; + + case SSH_CMSG_EXEC_SHELL: + case SSH_CMSG_EXEC_CMD: + /* Set interactive/non-interactive mode. */ + packet_set_interactive(have_pty || s->display != NULL, + options.keepalives); + + if (type == SSH_CMSG_EXEC_CMD) { + command = packet_get_string(&dlen); + debug("Exec command '%.500s'", command); + packet_integrity_check(plen, 4 + dlen, type); + } else { + command = NULL; + packet_integrity_check(plen, 0, type); + } + if (forced_command != NULL) { + command = forced_command; + debug("Forced command '%.500s'", forced_command); + } + if (have_pty) + do_exec_pty(s, command, pw); + else + do_exec_no_pty(s, command, pw); + + if (command != NULL) + xfree(command); + /* Cleanup user's local Xauthority file. */ + if (xauthfile) + xauthfile_cleanup_proc(NULL); + 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); + } + packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE); + 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); + } + } +} + +/* + * 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(Session *s, const char *command, struct passwd * pw) +{ + 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)); +#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)); +#endif /* USE_PIPES */ + if (s == NULL) + fatal("do_exec_no_pty: no session"); + + session_proctitle(s); + + /* Fork the child. */ + if ((pid = fork()) == 0) { + /* Child. Reinitialize the log since the pid has changed. */ + log_init(__progname, 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]); +#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"); +#endif /* USE_PIPES */ + + /* Do processing for the child (exec command etc). */ + do_child(command, pw, NULL, s->display, s->auth_proto, s->auth_data, NULL); + /* NOTREACHED */ + } + if (pid < 0) + packet_disconnect("fork failed: %.100s", strerror(errno)); + s->pid = pid; +#ifdef USE_PIPES + /* We are the parent. Close the child sides of the pipes. */ + close(pin[0]); + close(pout[1]); + close(perr[1]); + + if (compat20) { + session_set_fds(s, pin[1], pout[0], s->extended ? perr[0] : -1); + } else { + /* 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. + */ + if (compat20) { + session_set_fds(s, inout[1], inout[1], s->extended ? err[1] : -1); + } else { + server_loop(pid, inout[1], inout[1], err[1]); + /* server_loop has closed inout[1] and err[1]. */ + } +#endif /* USE_PIPES */ +} + +/* + * 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(Session *s, const char *command, struct passwd * pw) +{ + FILE *f; + char buf[100], *time_string; + char line[256]; + const char *hostname; + int fdout, ptyfd, ttyfd, ptymaster; + int quiet_login; + pid_t pid; + socklen_t fromlen; + struct sockaddr_storage from; + struct stat st; + time_t last_login_time; + + if (s == NULL) + fatal("do_exec_pty: no session"); + ptyfd = s->ptyfd; + ttyfd = s->ttyfd; + + /* 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)); + } + + /* Fork the child. */ + if ((pid = fork()) == 0) { + pid = getpid(); + + /* Child. Reinitialize the log because the pid has + changed. */ + log_init(__progname, 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, s->tty); + + /* 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); + +/* XXXX ? move to do_child() ??*/ + /* + * 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_connection_is_on_socket()) { + fromlen = sizeof(from); + if (getpeername(packet_get_connection_in(), + (struct sockaddr *) & from, &fromlen) < 0) { + debug("getpeername: %.100s", strerror(errno)); + fatal_cleanup(); + } + } + /* Record that there was a login on that terminal. */ + record_login(pid, s->tty, pw->pw_name, pw->pw_uid, hostname, + (struct sockaddr *)&from); + + /* Check if .hushlogin exists. */ + snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir); + quiet_login = stat(line, &st) >= 0; + + /* + * 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, s->term, s->display, s->auth_proto, s->auth_data, s->tty); + /* NOTREACHED */ + } + if (pid < 0) + packet_disconnect("fork failed: %.100s", strerror(errno)); + s->pid = pid; + + /* 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 #1 failed: %.100s", strerror(errno)); + + /* we keep a reference to the pty master */ + ptymaster = dup(ptyfd); + if (ptymaster < 0) + packet_disconnect("dup #2 failed: %.100s", strerror(errno)); + s->ptymaster = ptymaster; + + /* Enter interactive session. */ + if (compat20) { + session_set_fds(s, ptyfd, fdout, -1); + } else { + server_loop(pid, ptyfd, fdout, -1); + /* server_loop _has_ closed ptyfd and fdout. */ + session_pty_cleanup(s); + } +} + +/* + * 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]) { + /* Reuse the slot. */ + xfree(env[i]); + } else { + /* New variable. Expand 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; + } + + /* 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) +{ + FILE *f; + char buf[4096]; + char *cp, *value; + + f = fopen(filename, "r"); + if (!f) + return; + + while (fgets(buf, sizeof(buf), f)) { + for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '#' || *cp == '\n') + continue; + if (strchr(cp, '\n')) + *strchr(cp, '\n') = '\0'; + 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++; + child_set_env(env, envsize, cp, value); + } + 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) +{ + 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]; + + 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); + } + /* Set login name in the kernel. */ + if (setlogin(pw->pw_name) < 0) + error("setlogin failed: %s", strerror(errno)); + + /* 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); + } +#endif /* AFS */ + + /* Initialize the environment. */ + 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); + } + 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); + } + + snprintf(buf, sizeof buf, "%.50s %d %d", + get_remote_ipaddr(), get_remote_port(), get_local_port()); + child_set_env(&env, &envsize, "SSH_CLIENT", buf); + + if (ttyname) + child_set_env(&env, &envsize, "SSH_TTY", ttyname); + if (term) + child_set_env(&env, &envsize, "TERM", term); + if (display) + child_set_env(&env, &envsize, "DISPLAY", display); + +#ifdef KRB4 + { + extern char *ticket; + + if (ticket) + child_set_env(&env, &envsize, "KRBTKFILE", ticket); + } +#endif /* KRB4 */ + + if (xauthfile) + child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); + 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 (debug_flag) { + /* dump the environment */ + 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. + */ + /* XXX better use close-on-exec? -markus */ + 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(); + + /* + * 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); + pclose(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("/usr/bin/login", "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); +} + +Session * +session_new(void) +{ + int i; + static int did_init = 0; + if (!did_init) { + debug("session_new: init"); + for(i = 0; i < MAX_SESSIONS; i++) { + sessions[i].used = 0; + sessions[i].self = i; + } + did_init = 1; + } + for(i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + if (! s->used) { + s->pid = 0; + s->extended = 0; + s->chanid = -1; + s->ptyfd = -1; + s->ttyfd = -1; + s->term = NULL; + s->pw = NULL; + s->display = NULL; + s->screen = 0; + s->auth_data = NULL; + s->auth_proto = NULL; + s->used = 1; + s->pw = NULL; + debug("session_new: session %d", i); + return s; + } + } + return NULL; +} + +void +session_dump(void) +{ + int i; + for(i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + debug("dump: used %d session %d %p channel %d pid %d", + s->used, + s->self, + s, + s->chanid, + s->pid); + } +} + +int +session_open(int chanid) +{ + Session *s = session_new(); + debug("session_open: channel %d", chanid); + if (s == NULL) { + error("no more sessions"); + return 0; + } + s->pw = auth_get_user(); + if (s->pw == NULL) + fatal("no user for session %i", s->self); + debug("session_open: session %d: link with channel %d", s->self, chanid); + s->chanid = chanid; + return 1; +} + +Session * +session_by_channel(int id) +{ + int i; + for(i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + if (s->used && s->chanid == id) { + debug("session_by_channel: session %d channel %d", i, id); + return s; + } + } + debug("session_by_channel: unknown channel %d", id); + session_dump(); + return NULL; +} + +Session * +session_by_pid(pid_t pid) +{ + int i; + debug("session_by_pid: pid %d", pid); + for(i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + if (s->used && s->pid == pid) + return s; + } + error("session_by_pid: unknown pid %d", pid); + session_dump(); + return NULL; +} + +int +session_window_change_req(Session *s) +{ + s->col = packet_get_int(); + s->row = packet_get_int(); + s->xpixel = packet_get_int(); + s->ypixel = packet_get_int(); + packet_done(); + pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); + return 1; +} + +int +session_pty_req(Session *s) +{ + unsigned int len; + char *term_modes; /* encoded terminal modes */ + + if (s->ttyfd != -1) + return 0; + s->term = packet_get_string(&len); + s->col = packet_get_int(); + s->row = packet_get_int(); + s->xpixel = packet_get_int(); + s->ypixel = packet_get_int(); + term_modes = packet_get_string(&len); + packet_done(); + + if (strcmp(s->term, "") == 0) { + xfree(s->term); + s->term = NULL; + } + /* Allocate a pty and open it. */ + if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) { + xfree(s->term); + s->term = NULL; + s->ptyfd = -1; + s->ttyfd = -1; + error("session_pty_req: session %d alloc failed", s->self); + xfree(term_modes); + return 0; + } + debug("session_pty_req: session %d alloc %s", s->self, s->tty); + /* + * Add a cleanup function to clear the utmp entry and record logout + * time in case we call fatal() (e.g., the connection gets closed). + */ + fatal_add_cleanup(pty_cleanup_proc, (void *)s); + pty_setowner(s->pw, s->tty); + /* Get window size from the packet. */ + pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); + + session_proctitle(s); + + /* XXX parse and set terminal modes */ + xfree(term_modes); + return 1; +} + +int +session_subsystem_req(Session *s) +{ + unsigned int len; + int success = 0; + char *subsys = packet_get_string(&len); + + packet_done(); + log("subsystem request for %s", subsys); + + xfree(subsys); + return success; +} + +int +session_x11_req(Session *s) +{ + if (!options.x11_forwarding) { + debug("X11 forwarding disabled in server configuration file."); + return 0; + } + if (xauthfile != NULL) { + debug("X11 fwd already started."); + return 0; + } + + debug("Received request for X11 forwarding with auth spoofing."); + if (s->display != NULL) + packet_disconnect("Protocol error: X11 display already set."); + + s->single_connection = packet_get_char(); + s->auth_proto = packet_get_string(NULL); + s->auth_data = packet_get_string(NULL); + s->screen = packet_get_int(); + packet_done(); + + s->display = x11_create_display_inet(s->screen, options.x11_display_offset); + if (s->display == NULL) { + xfree(s->auth_proto); + xfree(s->auth_data); + return 0; + } + xauthfile = xmalloc(MAXPATHLEN); + strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); + temporarily_use_uid(s->pw->pw_uid); + if (mkdtemp(xauthfile) == NULL) { + restore_uid(); + error("private X11 dir: mkdtemp %s failed: %s", + xauthfile, strerror(errno)); + xfree(xauthfile); + xauthfile = NULL; + xfree(s->auth_proto); + xfree(s->auth_data); + /* XXXX remove listening channels */ + return 0; + } + strlcat(xauthfile, "/cookies", MAXPATHLEN); + open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + restore_uid(); + fatal_add_cleanup(xauthfile_cleanup_proc, s); + return 1; +} + +void +session_input_channel_req(int id, void *arg) +{ + unsigned int len; + int reply; + int success = 0; + char *rtype; + Session *s; + Channel *c; + + rtype = packet_get_string(&len); + reply = packet_get_char(); + + s = session_by_channel(id); + if (s == NULL) + fatal("session_input_channel_req: channel %d: no session", id); + c = channel_lookup(id); + if (c == NULL) + fatal("session_input_channel_req: channel %d: bad channel", id); + + debug("session_input_channel_req: session %d channel %d request %s reply %d", + s->self, id, rtype, reply); + + /* + * a session is in LARVAL state until a shell + * or programm is executed + */ + if (c->type == SSH_CHANNEL_LARVAL) { + if (strcmp(rtype, "shell") == 0) { + packet_done(); + s->extended = 1; + if (s->ttyfd == -1) + do_exec_no_pty(s, NULL, s->pw); + else + do_exec_pty(s, NULL, s->pw); + success = 1; + } else if (strcmp(rtype, "exec") == 0) { + char *command = packet_get_string(&len); + packet_done(); + s->extended = 1; + if (s->ttyfd == -1) + do_exec_no_pty(s, command, s->pw); + else + do_exec_pty(s, command, s->pw); + xfree(command); + success = 1; + } else if (strcmp(rtype, "pty-req") == 0) { + success = session_pty_req(s); + } else if (strcmp(rtype, "x11-req") == 0) { + success = session_x11_req(s); + } else if (strcmp(rtype, "subsystem") == 0) { + success = session_subsystem_req(s); + } + } + if (strcmp(rtype, "window-change") == 0) { + success = session_window_change_req(s); + } + + if (reply) { + packet_start(success ? + SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); + packet_put_int(c->remote_id); + packet_send(); + } + xfree(rtype); +} + +void +session_set_fds(Session *s, int fdin, int fdout, int fderr) +{ + if (!compat20) + fatal("session_set_fds: called for proto != 2.0"); + /* + * now that have a child and a pipe to the child, + * we can activate our channel and register the fd's + */ + if (s->chanid == -1) + fatal("no channel for session %d", s->self); + channel_set_fds(s->chanid, + fdout, fdin, fderr, + fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ); +} + +void +session_pty_cleanup(Session *s) +{ + if (s == NULL || s->ttyfd == -1) + return; + + debug("session_pty_cleanup: session %i release %s", s->self, s->tty); + + /* Cancel the cleanup function. */ + fatal_remove_cleanup(pty_cleanup_proc, (void *)s); + + /* Record that the user has logged out. */ + record_logout(s->pid, s->tty); + + /* Release the pseudo-tty. */ + pty_release(s->tty); + + /* + * 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. + */ + if (close(s->ptymaster) < 0) + error("close(s->ptymaster): %s", strerror(errno)); +} + +void +session_exit_message(Session *s, int status) +{ + Channel *c; + if (s == NULL) + fatal("session_close: no session"); + c = channel_lookup(s->chanid); + if (c == NULL) + fatal("session_close: session %d: no channel %d", + s->self, s->chanid); + debug("session_exit_message: session %d channel %d pid %d", + s->self, s->chanid, s->pid); + + if (WIFEXITED(status)) { + channel_request_start(s->chanid, + "exit-status", 0); + packet_put_int(WEXITSTATUS(status)); + packet_send(); + } else if (WIFSIGNALED(status)) { + channel_request_start(s->chanid, + "exit-signal", 0); + packet_put_int(WTERMSIG(status)); + packet_put_char(WCOREDUMP(status)); + packet_put_cstring(""); + packet_put_cstring(""); + packet_send(); + } else { + /* Some weird exit cause. Just exit. */ + packet_disconnect("wait returned status %04x.", status); + } + + /* disconnect channel */ + debug("session_exit_message: release channel %d", s->chanid); + channel_cancel_cleanup(s->chanid); + /* + * emulate a write failure with 'chan_write_failed', nobody will be + * interested in data we write. + * Note that we must not call 'chan_read_failed', since there could + * be some more data waiting in the pipe. + */ + if (c->ostate != CHAN_OUTPUT_CLOSED) + chan_write_failed(c); + s->chanid = -1; +} + +void +session_free(Session *s) +{ + debug("session_free: session %d pid %d", s->self, s->pid); + if (s->term) + xfree(s->term); + if (s->display) + xfree(s->display); + if (s->auth_data) + xfree(s->auth_data); + if (s->auth_proto) + xfree(s->auth_proto); + s->used = 0; +} + +void +session_close(Session *s) +{ + session_pty_cleanup(s); + session_free(s); + session_proctitle(s); +} + +void +session_close_by_pid(pid_t pid, int status) +{ + Session *s = session_by_pid(pid); + if (s == NULL) { + debug("session_close_by_pid: no session for pid %d", s->pid); + return; + } + if (s->chanid != -1) + session_exit_message(s, status); + session_close(s); +} + +/* + * this is called when a channel dies before + * the session 'child' itself dies + */ +void +session_close_by_channel(int id, void *arg) +{ + Session *s = session_by_channel(id); + if (s == NULL) { + debug("session_close_by_channel: no session for channel %d", id); + return; + } + /* disconnect channel */ + channel_cancel_cleanup(s->chanid); + s->chanid = -1; + + debug("session_close_by_channel: channel %d kill %d", id, s->pid); + if (s->pid == 0) { + /* close session immediately */ + session_close(s); + } else { + /* notify child, delay session cleanup */ + if (kill(s->pid, (s->ttyfd == -1) ? SIGTERM : SIGHUP) < 0) + error("session_close_by_channel: kill %d: %s", + s->pid, strerror(errno)); + } +} + +char * +session_tty_list(void) +{ + static char buf[1024]; + int i; + buf[0] = '\0'; + for(i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + if (s->used && s->ttyfd != -1) { + if (buf[0] != '\0') + strlcat(buf, ",", sizeof buf); + strlcat(buf, strrchr(s->tty, '/') + 1, sizeof buf); + } + } + if (buf[0] == '\0') + strlcpy(buf, "notty", sizeof buf); + return buf; +} + +void +session_proctitle(Session *s) +{ + if (s->pw == NULL) + error("no user for session %d", s->self); + else + setproctitle("%s@%s", s->pw->pw_name, session_tty_list()); +} + +void +do_authenticated2(void) +{ + /* + * Cancel the alarm we set to limit the time taken for + * authentication. + */ + alarm(0); + server_loop2(); + if (xauthfile) + xauthfile_cleanup_proc(NULL); +} diff --git a/crypto/openssh/session.h b/crypto/openssh/session.h new file mode 100644 index 0000000..a3427bc --- /dev/null +++ b/crypto/openssh/session.h @@ -0,0 +1,14 @@ +#ifndef SESSION_H +#define SESSION_H + +/* SSH1 */ +void do_authenticated(struct passwd * pw); + +/* SSH2 */ +void do_authenticated2(void); +int session_open(int id); +void session_input_channel_req(int id, void *arg); +void session_close_by_pid(pid_t pid, int status); +void session_close_by_channel(int id, void *arg); + +#endif diff --git a/crypto/openssh/ssh-add.1 b/crypto/openssh/ssh-add.1 index 8d1486b..1036395 100644 --- a/crypto/openssh/ssh-add.1 +++ b/crypto/openssh/ssh-add.1 @@ -9,28 +9,28 @@ .\" .\" Created: Sat Apr 22 23:55:14 1995 ylo .\" -.\" $Id: ssh-add.1,v 1.11 2000/03/23 21:11:38 aaron Exp $ +.\" $Id: ssh-add.1,v 1.13 2000/05/03 18:04:38 markus Exp $ .\" .Dd September 25, 1999 .Dt SSH-ADD 1 .Os .Sh NAME .Nm ssh-add -.Nd adds identities for the authentication agent +.Nd adds RSA identities for the authentication agent .Sh SYNOPSIS .Nm ssh-add .Op Fl lLdD .Op Ar -.Sh DESCRIPTION +.Sh DESCRIPTION .Nm -adds identities to the authentication agent, +adds RSA identities to the authentication agent, .Xr ssh-agent 1 . When run without arguments, it adds the file .Pa $HOME/.ssh/identity . Alternative file names can be given on the command line. If any file requires a passphrase, .Nm -asks for the passphrase from the user. +asks for the passphrase from the user. The Passphrase it is read from the user's tty. .Pp The authentication agent must be running and must be an ancestor of @@ -108,7 +108,7 @@ external libraries. .It has been updated to support ssh protocol 1.5. .It -contains added support for +contains added support for .Xr kerberos 8 authentication and ticket passing. .It diff --git a/crypto/openssh/ssh-add.c b/crypto/openssh/ssh-add.c index 78e1520..b7a385c 100644 --- a/crypto/openssh/ssh-add.c +++ b/crypto/openssh/ssh-add.c @@ -7,30 +7,35 @@ */ #include "includes.h" -RCSID("$Id: ssh-add.c,v 1.15 1999/12/02 20:05:40 markus Exp $"); +RCSID("$Id: ssh-add.c,v 1.16 2000/04/26 20:56:29 markus Exp $"); + +#include <openssl/rsa.h> +#include <openssl/dsa.h> #include "rsa.h" #include "ssh.h" #include "xmalloc.h" #include "authfd.h" #include "fingerprint.h" +#include "key.h" +#include "authfile.h" void delete_file(AuthenticationConnection *ac, const char *filename) { - RSA *key; + Key *public; char *comment; - key = RSA_new(); - if (!load_public_key(filename, key, &comment)) { + public = key_new(KEY_RSA); + if (!load_public_key(filename, public, &comment)) { printf("Bad key file %s: %s\n", filename, strerror(errno)); return; } - if (ssh_remove_identity(ac, key)) + if (ssh_remove_identity(ac, public->rsa)) fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); else fprintf(stderr, "Could not remove identity: %s\n", filename); - RSA_free(key); + key_free(public); xfree(comment); } @@ -85,20 +90,19 @@ ssh_askpass(char *askpass, char *msg) void add_file(AuthenticationConnection *ac, const char *filename) { - RSA *key; - RSA *public_key; + Key *public; + Key *private; char *saved_comment, *comment, *askpass = NULL; char buf[1024], msg[1024]; int success; int interactive = isatty(STDIN_FILENO); - key = RSA_new(); - public_key = RSA_new(); - if (!load_public_key(filename, public_key, &saved_comment)) { + public = key_new(KEY_RSA); + if (!load_public_key(filename, public, &saved_comment)) { printf("Bad key file %s: %s\n", filename, strerror(errno)); return; } - RSA_free(public_key); + key_free(public); if (!interactive && getenv("DISPLAY")) { if (getenv(SSH_ASKPASS_ENV)) @@ -108,7 +112,8 @@ add_file(AuthenticationConnection *ac, const char *filename) } /* At first, try empty passphrase */ - success = load_private_key(filename, "", key, &comment); + private = key_new(KEY_RSA); + success = load_private_key(filename, "", private, &comment); if (!success) { printf("Need passphrase for %.200s\n", filename); if (!interactive && askpass == NULL) { @@ -129,7 +134,7 @@ add_file(AuthenticationConnection *ac, const char *filename) xfree(saved_comment); return; } - success = load_private_key(filename, pass, key, &comment); + success = load_private_key(filename, pass, private, &comment); memset(pass, 0, strlen(pass)); xfree(pass); if (success) @@ -139,11 +144,11 @@ add_file(AuthenticationConnection *ac, const char *filename) } xfree(saved_comment); - if (ssh_add_identity(ac, key, comment)) + if (ssh_add_identity(ac, private->rsa, comment)) fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); else fprintf(stderr, "Could not add identity: %s\n", filename); - RSA_free(key); + key_free(private); xfree(comment); } diff --git a/crypto/openssh/ssh-agent.1 b/crypto/openssh/ssh-agent.1 index 7029b60..9f7299d 100644 --- a/crypto/openssh/ssh-agent.1 +++ b/crypto/openssh/ssh-agent.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-agent.1,v 1.10 2000/03/23 21:10:10 aaron Exp $ +.\" $OpenBSD: ssh-agent.1,v 1.12 2000/05/03 18:04:39 markus Exp $ .\" .\" -*- nroff -*- .\" @@ -18,16 +18,16 @@ .Nm ssh-agent .Nd authentication agent .Sh SYNOPSIS -.Nm ssh-agent +.Nm ssh-agent .Op Fl c Li | Fl s .Op Fl k .Oo .Ar command .Op Ar args ... .Oc -.Sh DESCRIPTION +.Sh DESCRIPTION .Nm -is a program to hold authentication private keys. +is a program to hold private keys used for RSA authentication. The idea is that .Nm is started in the beginning of an X-session or a login session, and @@ -64,12 +64,12 @@ When the command dies, so does the agent. The agent initially does not have any private keys. Keys are added using .Xr ssh-add 1 . -When executed without arguments, +When executed without arguments, .Xr ssh-add 1 -adds the +adds the .Pa $HOME/.ssh/identity file. -If the identity has a passphrase, +If the identity has a passphrase, .Xr ssh-add 1 asks for the passphrase (using a small X11 application if running under X11, or from the terminal if running without X). @@ -152,7 +152,7 @@ external libraries. .It has been updated to support ssh protocol 1.5. .It -contains added support for +contains added support for .Xr kerberos 8 authentication and ticket passing. .It diff --git a/crypto/openssh/ssh-keygen.1 b/crypto/openssh/ssh-keygen.1 index 9036164..ea81532 100644 --- a/crypto/openssh/ssh-keygen.1 +++ b/crypto/openssh/ssh-keygen.1 @@ -9,7 +9,7 @@ .\" .\" Created: Sat Apr 22 23:55:14 1995 ylo .\" -.\" $Id: ssh-keygen.1,v 1.12 2000/03/23 21:10:10 aaron Exp $ +.\" $Id: ssh-keygen.1,v 1.18 2000/05/08 17:26:04 hugh Exp $ .\" .Dd September 25, 1999 .Dt SSH-KEYGEN 1 @@ -19,7 +19,7 @@ .Nd authentication key generation .Sh SYNOPSIS .Nm ssh-keygen -.Op Fl q +.Op Fl dq .Op Fl b Ar bits .Op Fl N Ar new_passphrase .Op Fl C Ar comment @@ -30,6 +30,15 @@ .Op Fl N Ar new_passphrase .Op Fl f Ar keyfile .Nm ssh-keygen +.Fl x +.Op Fl f Ar keyfile +.Nm ssh-keygen +.Fl X +.Op Fl f Ar keyfile +.Nm ssh-keygen +.Fl y +.Op Fl f Ar keyfile +.Nm ssh-keygen .Fl c .Op Fl P Ar passphrase .Op Fl C Ar comment @@ -37,15 +46,27 @@ .Nm ssh-keygen .Fl l .Op Fl f Ar keyfile -.Sh DESCRIPTION +.Nm ssh-keygen +.Fl R +.Sh DESCRIPTION .Nm -generates and manages authentication keys for +generates and manages authentication keys for .Xr ssh 1 . +.Nm +defaults to generating an RSA key for use by protocols 1.3 and 1.5; +specifying the +.Fl d +flag will create a DSA key instead for use by protocol 2.0. +.Pp Normally each user wishing to use SSH -with RSA authentication runs this once to create the authentication +with RSA or DSA authentication runs this once to create the authentication key in -.Pa $HOME/.ssh/identity . -Additionally, the system administrator may use this to generate host keys. +.Pa $HOME/.ssh/identity +or +.Pa $HOME/.ssh/id_dsa . +Additionally, the system administrator may use this to generate host keys, +as seen in +.Pa /etc/rc . .Pp Normally this program generates the key and asks for a file in which to store the private key. @@ -69,7 +90,7 @@ If the passphrase is lost or forgotten, you will have to generate a new key and copy the corresponding public key to other machines. .Pp -There is also a comment field in the key file that is only for +For RSA, there is also a comment field in the key file that is only for convenience to the user to help identify the key. The comment can tell what the key is for, or whatever is useful. The comment is initialized to @@ -78,6 +99,9 @@ when the key is created, but can be changed using the .Fl c option. .Pp +After a key is generated, instructions below detail where the keys +should be placed to be activated. +.Pp The options are as follows: .Bl -tag -width Ds .It Fl b Ar bits @@ -112,6 +136,19 @@ Provides the new comment. Provides the new passphrase. .It Fl P Ar passphrase Provides the (old) passphrase. +.It Fl R +If RSA support is functional, immediately exits with code 0. If RSA +support is not functional, exits with code 1. This flag will be +removed once the RSA patent expires. +.It Fl x +This option will read a private +OpenSSH DSA format file and print a SSH2-compatible public key to stdout. +.It Fl X +This option will read a +SSH2-compatible public key file and print an OpenSSH DSA compatible public key to stdout. +.It Fl y +This option will read a private +OpenSSH DSA format file and print an OpenSSH DSA public key to stdout. .El .Sh FILES .Bl -tag -width Ds @@ -124,6 +161,8 @@ used to encrypt the private part of this file using 3DES. This file is not automatically accessed by .Nm but it is offered as the default file for the private key. +.Xr sshd 8 +will read this file when a login attempt is made. .It Pa $HOME/.ssh/identity.pub Contains the public key for authentication. The contents of this file should be added to @@ -131,6 +170,24 @@ The contents of this file should be added to on all machines where you wish to log in using RSA authentication. There is no need to keep the contents of this file secret. +.It Pa $HOME/.ssh/id_dsa +Contains the DSA authentication identity of the user. +This file should not be readable by anyone but the user. +It is possible to +specify a passphrase when generating the key; that passphrase will be +used to encrypt the private part of this file using 3DES. +This file is not automatically accessed by +.Nm +but it is offered as the default file for the private key. +.Xr sshd 8 +will read this file when a login attempt is made. +.It Pa $HOME/.ssh/id_dsa.pub +Contains the public key for authentication. +The contents of this file should be added to +.Pa $HOME/.ssh/authorized_keys2 +on all machines +where you wish to log in using DSA authentication. +There is no need to keep the contents of this file secret. .Sh AUTHOR Tatu Ylonen <ylo@cs.hut.fi> .Pp @@ -150,7 +207,7 @@ external libraries. .It has been updated to support ssh protocol 1.5. .It -contains added support for +contains added support for .Xr kerberos 8 authentication and ticket passing. .It diff --git a/crypto/openssh/ssh-keygen.c b/crypto/openssh/ssh-keygen.c index 29a967d..9129c70 100644 --- a/crypto/openssh/ssh-keygen.c +++ b/crypto/openssh/ssh-keygen.c @@ -7,20 +7,23 @@ */ #include "includes.h" -RCSID("$Id: ssh-keygen.c,v 1.17 2000/03/16 20:56:14 markus Exp $"); +RCSID("$Id: ssh-keygen.c,v 1.25 2000/05/08 18:23:07 markus Exp $"); + +#include <openssl/evp.h> +#include <openssl/pem.h> +#include <openssl/rsa.h> +#include <openssl/dsa.h> -#include "rsa.h" #include "ssh.h" #include "xmalloc.h" #include "fingerprint.h" +#include "key.h" +#include "rsa.h" +#include "dsa.h" +#include "authfile.h" +#include "uuencode.h" -/* Generated private key. */ -RSA *private_key; - -/* Generated public key. */ -RSA *public_key; - -/* Number of bits in the RSA key. This value can be changed on the command line. */ +/* Number of bits in the RSA/DSA key. This value can be changed on the command line. */ int bits = 1024; /* @@ -53,15 +56,24 @@ char *identity_new_passphrase = NULL; /* This is set to the new comment if given on the command line. */ char *identity_comment = NULL; +/* Dump public key file in format used by real and the original SSH 2 */ +int convert_to_ssh2 = 0; +int convert_from_ssh2 = 0; +int print_public = 0; +int dsa_mode = 0; + /* argv0 */ extern char *__progname; +char hostname[MAXHOSTNAMELEN]; + 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); + pw->pw_dir, + dsa_mode ? SSH_CLIENT_ID_DSA: SSH_CLIENT_IDENTITY); printf("%s (%s): ", prompt, identity_file); fflush(stdout); if (fgets(buf, sizeof(buf), stdin) == NULL) @@ -73,12 +85,148 @@ ask_filename(struct passwd *pw, const char *prompt) have_identity = 1; } +int +try_load_key(char *filename, Key *k) +{ + int success = 1; + if (!load_private_key(filename, "", k, NULL)) { + char *pass = read_passphrase("Enter passphrase: ", 1); + if (!load_private_key(filename, pass, k, NULL)) { + success = 0; + } + memset(pass, 0, strlen(pass)); + xfree(pass); + } + return success; +} + +#define SSH_COM_MAGIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" +#define SSH_COM_MAGIC_END "---- END SSH2 PUBLIC KEY ----" + +void +do_convert_to_ssh2(struct passwd *pw) +{ + Key *k; + int len; + unsigned char *blob; + 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); + } + k = key_new(KEY_DSA); + if (!try_load_key(identity_file, k)) { + fprintf(stderr, "load failed\n"); + exit(1); + } + dsa_make_key_blob(k, &blob, &len); + fprintf(stdout, SSH_COM_MAGIC_BEGIN "\n"); + fprintf(stdout, + "Comment: \"%d-bit DSA, converted from openssh by %s@%s\"\n", + BN_num_bits(k->dsa->p), + pw->pw_name, hostname); + dump_base64(stdout, blob, len); + fprintf(stdout, SSH_COM_MAGIC_END "\n"); + key_free(k); + xfree(blob); + exit(0); +} + +void +do_convert_from_ssh2(struct passwd *pw) +{ + Key *k; + int blen; + char line[1024], *p; + char blob[8096]; + char encoded[8096]; + struct stat st; + int escaped = 0; + FILE *fp; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + fp = fopen(identity_file, "r"); + if (fp == NULL) { + perror(identity_file); + exit(1); + } + encoded[0] = '\0'; + while (fgets(line, sizeof(line), fp)) { + if (!(p = strchr(line, '\n'))) { + fprintf(stderr, "input line too long.\n"); + exit(1); + } + if (p > line && p[-1] == '\\') + escaped++; + if (strncmp(line, "----", 4) == 0 || + strstr(line, ": ") != NULL) { + fprintf(stderr, "ignore: %s", line); + continue; + } + if (escaped) { + escaped--; + fprintf(stderr, "escaped: %s", line); + continue; + } + *p = '\0'; + strlcat(encoded, line, sizeof(encoded)); + } + blen = uudecode(encoded, (unsigned char *)blob, sizeof(blob)); + if (blen < 0) { + fprintf(stderr, "uudecode failed.\n"); + exit(1); + } + k = dsa_key_from_blob(blob, blen); + if (!key_write(k, stdout)) + fprintf(stderr, "key_write failed"); + key_free(k); + fprintf(stdout, "\n"); + fclose(fp); + exit(0); +} + +void +do_print_public(struct passwd *pw) +{ + Key *k; + int len; + unsigned char *blob; + 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); + } + k = key_new(KEY_DSA); + if (!try_load_key(identity_file, k)) { + fprintf(stderr, "load failed\n"); + exit(1); + } + dsa_make_key_blob(k, &blob, &len); + if (!key_write(k, stdout)) + fprintf(stderr, "key_write failed"); + key_free(k); + xfree(blob); + fprintf(stdout, "\n"); + exit(0); +} + void do_fingerprint(struct passwd *pw) { FILE *f; BIGNUM *e, *n; - RSA *public_key; + Key *public; char *comment = NULL, *cp, *ep, line[16*1024]; int i, skip = 0, num = 1, invalid = 1; unsigned int ignore; @@ -90,17 +238,16 @@ do_fingerprint(struct passwd *pw) perror(identity_file); exit(1); } - - public_key = RSA_new(); - if (load_public_key(identity_file, public_key, &comment)) { - printf("%d %s %s\n", BN_num_bits(public_key->n), - fingerprint(public_key->e, public_key->n), - comment); - RSA_free(public_key); + public = key_new(KEY_RSA); + if (load_public_key(identity_file, public, &comment)) { + printf("%d %s %s\n", BN_num_bits(public->rsa->n), + key_fingerprint(public), comment); + key_free(public); exit(0); } - RSA_free(public_key); + key_free(public); + /* XXX */ f = fopen(identity_file, "r"); if (f != NULL) { n = BN_new(); @@ -168,7 +315,9 @@ do_change_passphrase(struct passwd *pw) char *comment; char *old_passphrase, *passphrase1, *passphrase2; struct stat st; - RSA *private_key; + Key *private; + Key *public; + int type = dsa_mode ? KEY_DSA : KEY_RSA; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -176,22 +325,26 @@ do_change_passphrase(struct passwd *pw) perror(identity_file); exit(1); } - 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); + + if (type == KEY_RSA) { + /* XXX this works currently only for RSA */ + public = key_new(type); + if (!load_public_key(identity_file, public, 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. */ + key_free(public); } - /* 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)) { + private = key_new(type); + if (!load_private_key(identity_file, "", private, &comment)) { if (identity_passphrase) old_passphrase = xstrdup(identity_passphrase); else old_passphrase = read_passphrase("Enter old passphrase: ", 1); - if (!load_private_key(identity_file, old_passphrase, private_key, &comment)) { + if (!load_private_key(identity_file, old_passphrase, private, &comment)) { memset(old_passphrase, 0, strlen(old_passphrase)); xfree(old_passphrase); printf("Bad passphrase.\n"); @@ -226,19 +379,19 @@ do_change_passphrase(struct passwd *pw) } /* Save the file using the new passphrase. */ - if (!save_private_key(identity_file, passphrase1, private_key, comment)) { + if (!save_private_key(identity_file, passphrase1, private, comment)) { printf("Saving the key failed: %s: %s.\n", identity_file, strerror(errno)); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); - RSA_free(private_key); + key_free(private); 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 */ + key_free(private); /* Destroys contents */ xfree(comment); printf("Your identification has been saved with the new passphrase.\n"); @@ -252,11 +405,11 @@ void do_change_comment(struct passwd *pw) { char new_comment[1024], *comment; - RSA *private_key; + Key *private; + Key *public; char *passphrase; struct stat st; FILE *f; - char *tmpbuf; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -268,14 +421,14 @@ do_change_comment(struct passwd *pw) * 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)) { + public = key_new(KEY_RSA); + if (!load_public_key(identity_file, public, NULL)) { printf("%s is not a valid key file.\n", identity_file); exit(1); } - private_key = RSA_new(); - if (load_private_key(identity_file, "", private_key, &comment)) + private = key_new(KEY_RSA); + if (load_private_key(identity_file, "", private, &comment)) passphrase = xstrdup(""); else { if (identity_passphrase) @@ -285,7 +438,7 @@ do_change_comment(struct passwd *pw) else passphrase = read_passphrase("Enter passphrase: ", 1); /* Try to load using the passphrase. */ - if (!load_private_key(identity_file, passphrase, private_key, &comment)) { + if (!load_private_key(identity_file, passphrase, private, &comment)) { memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); printf("Bad passphrase.\n"); @@ -301,7 +454,7 @@ do_change_comment(struct passwd *pw) fflush(stdout); if (!fgets(new_comment, sizeof(new_comment), stdin)) { memset(passphrase, 0, strlen(passphrase)); - RSA_free(private_key); + key_free(private); exit(1); } if (strchr(new_comment, '\n')) @@ -309,18 +462,18 @@ do_change_comment(struct passwd *pw) } /* Save the file using the new passphrase. */ - if (!save_private_key(identity_file, passphrase, private_key, new_comment)) { + if (!save_private_key(identity_file, passphrase, private, 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); + key_free(private); xfree(comment); exit(1); } memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); - RSA_free(private_key); + key_free(private); strlcat(identity_file, ".pub", sizeof(identity_file)); f = fopen(identity_file, "w"); @@ -328,13 +481,10 @@ do_change_comment(struct passwd *pw) 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); + if (!key_write(public, f)) + fprintf(stderr, "write key failed"); + key_free(public); + fprintf(f, " %s\n", new_comment); fclose(f); xfree(comment); @@ -346,8 +496,7 @@ do_change_comment(struct passwd *pw) void usage(void) { - printf("ssh-keygen version %s\n", SSH_VERSION); - printf("Usage: %s [-b bits] [-p] [-c] [-l] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname); + printf("Usage: %s [-lpqxXydc] [-b bits] [-f file] [-C comment] [-N new-pass] [-P pass]\n", __progname); exit(1); } @@ -359,29 +508,28 @@ 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]; + Key *private; + Key *public; extern int optind; extern char *optarg; - /* 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); - } + OpenSSL_add_all_algorithms(); + /* we need this for the home * directory. */ pw = getpwuid(getuid()); if (!pw) { printf("You don't exist, go away!\n"); exit(1); } + if (gethostname(hostname, sizeof(hostname)) < 0) { + perror("gethostname"); + exit(1); + } - while ((opt = getopt(ac, av, "qpclb:f:P:N:C:")) != EOF) { + while ((opt = getopt(ac, av, "dqpclRxXyb:f:P:N:C:")) != EOF) { switch (opt) { case 'b': bits = atoi(optarg); @@ -424,6 +572,29 @@ main(int ac, char **av) quiet = 1; break; + case 'R': + if (rsa_alive() == 0) + exit(1); + else + exit(0); + break; + + case 'x': + convert_to_ssh2 = 1; + break; + + case 'X': + convert_from_ssh2 = 1; + break; + + case 'y': + print_public = 1; + break; + + case 'd': + dsa_mode = 1; + break; + case '?': default: usage(); @@ -437,22 +608,44 @@ main(int ac, char **av) printf("Can only have one of -p and -c.\n"); usage(); } + /* check if RSA support is needed and exists */ + if (dsa_mode == 0 && rsa_alive() == 0) { + fprintf(stderr, + "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", + __progname); + exit(1); + } if (print_fingerprint) do_fingerprint(pw); if (change_passphrase) do_change_passphrase(pw); if (change_comment) do_change_comment(pw); + if (convert_to_ssh2) + do_convert_to_ssh2(pw); + if (convert_from_ssh2) + do_convert_from_ssh2(pw); + if (print_public) + do_print_public(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 (dsa_mode != 0) { + if (!quiet) + printf("Generating DSA parameter and key.\n"); + public = private = dsa_generate_key(bits); + if (private == NULL) { + fprintf(stderr, "dsa_generate_keys failed"); + exit(1); + } + } else { + if (quiet) + rsa_set_verbose(0); + /* Generate the rsa key pair. */ + public = key_new(KEY_RSA); + private = key_new(KEY_RSA); + rsa_generate_key(private->rsa, public->rsa, bits); + } if (!have_identity) ask_filename(pw, "Enter file in which to save the key"); @@ -504,18 +697,14 @@ passphrase_again: if (identity_comment) { strlcpy(comment, identity_comment, sizeof(comment)); } else { - /* Create default commend field for the passphrase. */ - if (gethostname(hostname, sizeof(hostname)) < 0) { - perror("gethostname"); - exit(1); - } + /* Create default commend field for the passphrase. */ 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)) { + if (!save_private_key(identity_file, passphrase1, private, comment)) { printf("Saving the key failed: %s: %s.\n", - identity_file, strerror(errno)); + identity_file, strerror(errno)); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); exit(1); @@ -525,7 +714,9 @@ passphrase_again: xfree(passphrase1); /* Clear the private key and the random number generator. */ - RSA_free(private_key); + if (private != public) { + key_free(private); + } arc4random_stir(); if (!quiet) @@ -537,21 +728,18 @@ passphrase_again: 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); + if (!key_write(public, f)) + fprintf(stderr, "write key failed"); + fprintf(f, " %s\n", comment); fclose(f); if (!quiet) { - printf("Your public key has been saved in %s.\n", identity_file); + 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); + printf("%s %s\n", key_fingerprint(public), comment); } + + key_free(public); exit(0); } diff --git a/crypto/openssh/ssh/Makefile b/crypto/openssh/ssh/Makefile index b214455..a87d5dc 100644 --- a/crypto/openssh/ssh/Makefile +++ b/crypto/openssh/ssh/Makefile @@ -15,7 +15,8 @@ MAN= ssh.1 LINKS= ${BINDIR}/ssh ${BINDIR}/slogin MLINKS= ssh.1 slogin.1 -SRCS= ssh.c sshconnect.c log-client.c readconf.c clientloop.c +SRCS= ssh.c log-client.c readconf.c clientloop.c \ + sshconnect.c sshconnect1.c sshconnect2.c .include <bsd.own.mk> # for AFS diff --git a/crypto/openssh/ssh2.h b/crypto/openssh/ssh2.h new file mode 100644 index 0000000..cf684ba --- /dev/null +++ b/crypto/openssh/ssh2.h @@ -0,0 +1,106 @@ +/* + * draft-ietf-secsh-architecture-04.txt + * + * Transport layer protocol: + * + * 1-19 Transport layer generic (e.g. disconnect, ignore, debug, + * etc) + * 20-29 Algorithm negotiation + * 30-49 Key exchange method specific (numbers can be reused for + * different authentication methods) + * + * User authentication protocol: + * + * 50-59 User authentication generic + * 60-79 User authentication method specific (numbers can be reused + * for different authentication methods) + * + * Connection protocol: + * + * 80-89 Connection protocol generic + * 90-127 Channel related messages + * + * Reserved for client protocols: + * + * 128-191 Reserved + * + * Local extensions: + * + * 192-255 Local extensions + */ + +/* transport layer: generic */ + +#define SSH2_MSG_DISCONNECT 1 +#define SSH2_MSG_IGNORE 2 +#define SSH2_MSG_UNIMPLEMENTED 3 +#define SSH2_MSG_DEBUG 4 +#define SSH2_MSG_SERVICE_REQUEST 5 +#define SSH2_MSG_SERVICE_ACCEPT 6 + +/* transport layer: alg negotiation */ + +#define SSH2_MSG_KEXINIT 20 +#define SSH2_MSG_NEWKEYS 21 + +/* transport layer: kex specific messages, can be reused */ + +#define SSH2_MSG_KEXDH_INIT 30 +#define SSH2_MSG_KEXDH_REPLY 31 + +/* user authentication: generic */ + +#define SSH2_MSG_USERAUTH_REQUEST 50 +#define SSH2_MSG_USERAUTH_FAILURE 51 +#define SSH2_MSG_USERAUTH_SUCCESS 52 +#define SSH2_MSG_USERAUTH_BANNER 53 + +/* user authentication: method specific, can be reused */ + +#define SSH2_MSG_USERAUTH_PK_OK 60 +#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 +#define SSH2_MSG_USERAUTH_INFO_REQUEST 60 +#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 + +/* connection protocol: generic */ + +#define SSH2_MSG_GLOBAL_REQUEST 80 +#define SSH2_MSG_REQUEST_SUCCESS 81 +#define SSH2_MSG_REQUEST_FAILURE 82 + +/* channel related messages */ + +#define SSH2_MSG_CHANNEL_OPEN 90 +#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 +#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 +#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 +#define SSH2_MSG_CHANNEL_DATA 94 +#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 +#define SSH2_MSG_CHANNEL_EOF 96 +#define SSH2_MSG_CHANNEL_CLOSE 97 +#define SSH2_MSG_CHANNEL_REQUEST 98 +#define SSH2_MSG_CHANNEL_SUCCESS 99 +#define SSH2_MSG_CHANNEL_FAILURE 100 + +/* disconnect reason code */ + +#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 +#define SSH2_DISCONNECT_PROTOCOL_ERROR 2 +#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3 +#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4 +#define SSH2_DISCONNECT_MAC_ERROR 5 +#define SSH2_DISCONNECT_COMPRESSION_ERROR 6 +#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE 7 +#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 +#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 +#define SSH2_DISCONNECT_CONNECTION_LOST 10 +#define SSH2_DISCONNECT_BY_APPLICATION 11 + +/* misc */ + +#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 +#define SSH2_OPEN_CONNECT_FAILED 2 +#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3 +#define SSH2_OPEN_RESOURCE_SHORTAGE 4 + +#define SSH2_EXTENDED_DATA_STDERR 1 diff --git a/crypto/openssh/sshconnect.h b/crypto/openssh/sshconnect.h new file mode 100644 index 0000000..13d395f --- /dev/null +++ b/crypto/openssh/sshconnect.h @@ -0,0 +1,16 @@ +#ifndef SSHCONNECT_H +#define SSHCONNECT_H + +void +check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, + const char *user_hostfile, const char *system_hostfile); + +void ssh_kex(char *host, struct sockaddr *hostaddr); +void +ssh_userauth(const char* local_user, const char* server_user, char *host, + int host_key_valid, RSA *own_host_key); + +void ssh_kex2(char *host, struct sockaddr *hostaddr); +void ssh_userauth2(const char *server_user, char *host); + +#endif diff --git a/crypto/openssh/sshconnect1.c b/crypto/openssh/sshconnect1.c new file mode 100644 index 0000000..4360d72 --- /dev/null +++ b/crypto/openssh/sshconnect1.c @@ -0,0 +1,1024 @@ +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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("$OpenBSD: sshconnect1.c,v 1.3 2000/05/08 17:12:16 markus Exp $"); + +#include <openssl/bn.h> +#include <openssl/dsa.h> +#include <openssl/rsa.h> +#include <openssl/evp.h> + +#include "xmalloc.h" +#include "rsa.h" +#include "ssh.h" +#include "buffer.h" +#include "packet.h" +#include "authfd.h" +#include "cipher.h" +#include "mpaux.h" +#include "uidswap.h" +#include "readconf.h" +#include "key.h" +#include "sshconnect.h" +#include "authfile.h" + +/* Session id for the current session. */ +unsigned char session_id[16]; +unsigned int supported_authentications = 0; + +extern Options options; +extern char *__progname; + +/* + * 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; + } + /* 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); + + 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) +{ + 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)); +} + +/* + * Checks if the user has authentication file, and if so, tries to authenticate + * the user using it. + */ +int +try_rsa_authentication(const char *authfile) +{ + BIGNUM *challenge; + Key *public; + Key *private; + char *passphrase, *comment; + int type, i; + int plen, clen; + + /* Try to load identification for the authentication key. */ + public = key_new(KEY_RSA); + if (!load_public_key(authfile, public, &comment)) { + key_free(public); + /* Could not load it. Fail. */ + 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->rsa->n); + packet_send(); + packet_write_wait(); + + /* We no longer need the public key. */ + key_free(public); + + /* 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; + } + /* 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_new(KEY_RSA); + /* + * Load the private key. Try first with empty passphrase; if it + * fails, ask for a passphrase. + */ + if (!load_private_key(authfile, "", private, 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(""); + } + + /* Load the authentication file using the pasphrase. */ + if (!load_private_key(authfile, passphrase, private, 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->rsa); + + /* Destroy the private key. */ + key_free(private); + + /* 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) +{ + 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() +{ + KTEXT_ST auth; /* Kerberos data */ + char *reply; + char inst[INST_SZ]; + char *realm; + CREDENTIALS cred; + int r, type, plen; + socklen_t slen; + Key_schedule schedule; + u_long checksum, cksum; + MSG_DAT msg_data; + struct sockaddr_in local, foreign; + struct stat st; + + /* Don't do anything if we don't have any tickets. */ + if (stat(tkt_string(), &st) < 0) + return 0; + + 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); + + slen = sizeof(local); + memset(&local, 0, sizeof(local)); + if (getsockname(packet_get_connection_in(), + (struct sockaddr *) & local, &slen) < 0) + debug("getsockname failed: %s", strerror(errno)); + + slen = sizeof(foreign); + memset(&foreign, 0, sizeof(foreign)); + if (getpeername(packet_get_connection_in(), + (struct sockaddr *) & foreign, &slen) < 0) { + debug("getpeername failed: %s", strerror(errno)); + 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() +{ + CREDENTIALS *creds; + char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; + int r, type, plen; + 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, (unsigned char *)buffer, sizeof buffer); + xfree(creds); + + packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); + packet_put_string(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) +{ + CREDENTIALS creds; + struct ViceIoctl parms; + struct ClearToken ct; + int i, type, len, plen; + char buf[2048], *p, *server_cell; + 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, (unsigned char*) buffer, sizeof buffer) <= 0) + break; + packet_start(SSH_CMSG_HAVE_AFS_TOKEN); + packet_put_string(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 */ + +/* + * Tries to authenticate with any string-based challenge/response system. + * Note that the client code is not tied to s/key or TIS. + */ +int +try_skey_authentication() +{ + int type, i; + int payload_len; + unsigned int clen; + char *challenge, *response; + + 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."); + return 0; + } + challenge = packet_get_string(&clen); + packet_integrity_check(payload_len, (4 + clen), type); + if (options.cipher == SSH_CIPHER_NONE) + log("WARNING: Encryption is disabled! " + "Reponse will be transmitted in clear text."); + fprintf(stderr, "%s\n", challenge); + xfree(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 1; + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error: got %d in response " + "to skey-auth-reponse", type); + } + /* failure */ + return 0; +} + +/* + * Tries to authenticate with plain passwd authentication. + */ +int +try_password_authentication(char *prompt) +{ + int type, i, payload_len; + char *password; + + 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 1; + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error: got %d in response to passwd auth", type); + } + /* failure */ + return 0; +} + +/* + * SSH1 key exchange + */ +void +ssh_kex(char *host, struct sockaddr *hostaddr) +{ + int i; + BIGNUM *key; + RSA *host_key; + RSA *public_key; + Key k; + int bits, rbits; + int ssh_cipher_default = SSH_CIPHER_3DES; + unsigned char session_key[SSH_SESSION_KEY_LENGTH]; + unsigned char cookie[8]; + unsigned int supported_ciphers; + unsigned int server_flags, client_flags; + int payload_len, clen, sum_len = 0; + u_int32_t rand = 0; + + 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 cookie from the packet. */ + for (i = 0; i < 8; i++) + cookie[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."); + } + + /* Get protocol flags. */ + server_flags = packet_get_int(); + packet_set_protocol_flags(server_flags); + + supported_ciphers = packet_get_int(); + 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); + k.type = KEY_RSA; + k.rsa = host_key; + check_host_key(host, hostaddr, &k, + options.user_hostfile, options.system_hostfile); + + client_flags = SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN; + + compute_session_id(session_id, cookie, host_key->n, public_key->n); + + /* 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); + } + + /* Destroy the public keys since we no longer need them. */ + RSA_free(public_key); + RSA_free(host_key); + + if (options.cipher == SSH_CIPHER_ILLEGAL) { + log("No valid SSH1 cipher, using %.100s instead.", + cipher_name(SSH_FALLBACK_CIPHER)); + options.cipher = SSH_FALLBACK_CIPHER; + } else if (options.cipher == SSH_CIPHER_NOT_SET) { + if (cipher_mask1() & 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 cookie back to the server. */ + for (i = 0; i < 8; i++) + packet_put_char(cookie[i]); + + /* Send and destroy the encrypted encryption key integer. */ + packet_put_bignum(key); + BN_clear_free(key); + + /* Send protocol flags. */ + packet_put_int(client_flags); + + /* Send the packet now. */ + packet_send(); + packet_write_wait(); + + 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."); +} + +/* + * Authenticate user + */ +void +ssh_userauth( + const char* local_user, + const char* server_user, + char *host, + int host_key_valid, RSA *own_host_key) +{ + int i, type; + int payload_len; + + if (supported_authentications == 0) + fatal("ssh_userauth: server supports no auth methods"); + + /* 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; + 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; + 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(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) { + if (try_skey_authentication()) + return; + } + /* 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@%.40s's password: ", + server_user, host); + if (try_password_authentication(prompt)) + return; + } + /* All authentication methods have failed. Exit with an error message. */ + fatal("Permission denied."); + /* NOTREACHED */ +} diff --git a/crypto/openssh/sshconnect2.c b/crypto/openssh/sshconnect2.c new file mode 100644 index 0000000..99ffb2c --- /dev/null +++ b/crypto/openssh/sshconnect2.c @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$OpenBSD: sshconnect2.c,v 1.10 2000/05/08 17:42:25 markus Exp $"); + +#include <openssl/bn.h> +#include <openssl/rsa.h> +#include <openssl/dsa.h> +#include <openssl/md5.h> +#include <openssl/dh.h> +#include <openssl/hmac.h> + +#include "ssh.h" +#include "xmalloc.h" +#include "rsa.h" +#include "buffer.h" +#include "packet.h" +#include "cipher.h" +#include "uidswap.h" +#include "compat.h" +#include "readconf.h" +#include "bufaux.h" +#include "ssh2.h" +#include "kex.h" +#include "myproposal.h" +#include "key.h" +#include "dsa.h" +#include "sshconnect.h" +#include "authfile.h" + +/* import */ +extern char *client_version_string; +extern char *server_version_string; +extern Options options; + +/* + * SSH2 key exchange + */ + +unsigned char *session_id2 = NULL; +int session_id2_len = 0; + +void +ssh_kex2(char *host, struct sockaddr *hostaddr) +{ + Kex *kex; + char *cprop[PROPOSAL_MAX]; + char *sprop[PROPOSAL_MAX]; + Buffer *client_kexinit; + Buffer *server_kexinit; + int payload_len, dlen; + unsigned int klen, kout; + char *ptr; + char *signature = NULL; + unsigned int slen; + char *server_host_key_blob = NULL; + Key *server_host_key; + unsigned int sbloblen; + DH *dh; + BIGNUM *dh_server_pub = 0; + BIGNUM *shared_secret = 0; + int i; + unsigned char *kbuf; + unsigned char *hash; + +/* KEXINIT */ + + debug("Sending KEX init."); + if (options.ciphers != NULL) { + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; + } else if (options.cipher == SSH_CIPHER_3DES) { + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = + cipher_name(SSH_CIPHER_3DES_CBC); + } else if (options.cipher == SSH_CIPHER_BLOWFISH) { + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = + cipher_name(SSH_CIPHER_BLOWFISH_CBC); + } + if (options.compression) { + myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib"; + myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib"; + } else { + myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none"; + myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; + } + for (i = 0; i < PROPOSAL_MAX; i++) + cprop[i] = xstrdup(myproposal[i]); + + client_kexinit = kex_init(cprop); + packet_start(SSH2_MSG_KEXINIT); + packet_put_raw(buffer_ptr(client_kexinit), buffer_len(client_kexinit)); + packet_send(); + packet_write_wait(); + + debug("done"); + + packet_read_expect(&payload_len, SSH2_MSG_KEXINIT); + + /* save payload for session_id */ + server_kexinit = xmalloc(sizeof(*server_kexinit)); + buffer_init(server_kexinit); + ptr = packet_get_raw(&payload_len); + buffer_append(server_kexinit, ptr, payload_len); + + /* skip cookie */ + for (i = 0; i < 16; i++) + (void) packet_get_char(); + /* kex init proposal strings */ + for (i = 0; i < PROPOSAL_MAX; i++) { + sprop[i] = packet_get_string(NULL); + debug("got kexinit string: %s", sprop[i]); + } + i = (int) packet_get_char(); + debug("first kex follow == %d", i); + i = packet_get_int(); + debug("reserved == %d", i); + packet_done(); + + debug("done read kexinit"); + kex = kex_choose_conf(cprop, sprop, 0); + +/* KEXDH */ + + debug("Sending SSH2_MSG_KEXDH_INIT."); + + /* generate and send 'e', client DH public key */ + dh = dh_new_group1(); + packet_start(SSH2_MSG_KEXDH_INIT); + packet_put_bignum2(dh->pub_key); + packet_send(); + packet_write_wait(); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "\np= "); + bignum_print(dh->p); + fprintf(stderr, "\ng= "); + bignum_print(dh->g); + fprintf(stderr, "\npub= "); + bignum_print(dh->pub_key); + fprintf(stderr, "\n"); + DHparams_print_fp(stderr, dh); +#endif + + debug("Wait SSH2_MSG_KEXDH_REPLY."); + + packet_read_expect(&payload_len, SSH2_MSG_KEXDH_REPLY); + + debug("Got SSH2_MSG_KEXDH_REPLY."); + + /* key, cert */ + server_host_key_blob = packet_get_string(&sbloblen); + server_host_key = dsa_key_from_blob(server_host_key_blob, sbloblen); + if (server_host_key == NULL) + fatal("cannot decode server_host_key_blob"); + + check_host_key(host, hostaddr, server_host_key, + options.user_hostfile2, options.system_hostfile2); + + /* DH paramter f, server public DH key */ + dh_server_pub = BN_new(); + if (dh_server_pub == NULL) + fatal("dh_server_pub == NULL"); + packet_get_bignum2(dh_server_pub, &dlen); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "\ndh_server_pub= "); + bignum_print(dh_server_pub); + fprintf(stderr, "\n"); + debug("bits %d", BN_num_bits(dh_server_pub)); +#endif + + /* signed H */ + signature = packet_get_string(&slen); + packet_done(); + + if (!dh_pub_is_valid(dh, dh_server_pub)) + packet_disconnect("bad server public DH value"); + + klen = DH_size(dh); + kbuf = xmalloc(klen); + kout = DH_compute_key(kbuf, dh_server_pub, dh); +#ifdef DEBUG_KEXDH + debug("shared secret: len %d/%d", klen, kout); + fprintf(stderr, "shared secret == "); + for (i = 0; i< kout; i++) + fprintf(stderr, "%02x", (kbuf[i])&0xff); + fprintf(stderr, "\n"); +#endif + shared_secret = BN_new(); + + BN_bin2bn(kbuf, kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + /* calc and verify H */ + hash = kex_hash( + client_version_string, + server_version_string, + buffer_ptr(client_kexinit), buffer_len(client_kexinit), + buffer_ptr(server_kexinit), buffer_len(server_kexinit), + server_host_key_blob, sbloblen, + dh->pub_key, + dh_server_pub, + shared_secret + ); + xfree(server_host_key_blob); + buffer_free(client_kexinit); + buffer_free(server_kexinit); + xfree(client_kexinit); + xfree(server_kexinit); +#ifdef DEBUG_KEXDH + fprintf(stderr, "hash == "); + for (i = 0; i< 20; i++) + fprintf(stderr, "%02x", (hash[i])&0xff); + fprintf(stderr, "\n"); +#endif + if (dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1) + fatal("dsa_verify failed for server_host_key"); + key_free(server_host_key); + + kex_derive_keys(kex, hash, shared_secret); + packet_set_kex(kex); + + /* have keys, free DH */ + DH_free(dh); + + /* save session id */ + session_id2_len = 20; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, hash, session_id2_len); + + debug("Wait SSH2_MSG_NEWKEYS."); + packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS); + packet_done(); + debug("GOT SSH2_MSG_NEWKEYS."); + + debug("send SSH2_MSG_NEWKEYS."); + packet_start(SSH2_MSG_NEWKEYS); + packet_send(); + packet_write_wait(); + debug("done: send SSH2_MSG_NEWKEYS."); + +#ifdef DEBUG_KEXDH + /* send 1st encrypted/maced/compressed message */ + packet_start(SSH2_MSG_IGNORE); + packet_put_cstring("markus"); + packet_send(); + packet_write_wait(); +#endif + debug("done: KEX2."); +} +/* + * Authenticate user + */ +int +ssh2_try_passwd(const char *server_user, const char *host, const char *service) +{ + static int attempt = 0; + char prompt[80]; + char *password; + + if (attempt++ > options.number_of_password_prompts) + return 0; + + snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", + server_user, host); + password = read_passphrase(prompt, 0); + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_cstring(server_user); + packet_put_cstring(service); + packet_put_cstring("password"); + packet_put_char(0); + packet_put_cstring(password); + memset(password, 0, strlen(password)); + xfree(password); + packet_send(); + packet_write_wait(); + return 1; +} + +int +ssh2_try_pubkey(char *filename, + const char *server_user, const char *host, const char *service) +{ + Buffer b; + Key *k; + unsigned char *blob, *signature; + int bloblen, slen; + struct stat st; + + if (stat(filename, &st) != 0) { + debug("key does not exist: %s", filename); + return 0; + } + debug("try pubkey: %s", filename); + + k = key_new(KEY_DSA); + if (!load_private_key(filename, "", k, NULL)) { + int success = 0; + char *passphrase; + char prompt[300]; + snprintf(prompt, sizeof prompt, + "Enter passphrase for DSA key '%.100s': ", + filename); + passphrase = read_passphrase(prompt, 0); + success = load_private_key(filename, passphrase, k, NULL); + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + if (!success) + return 0; + } + dsa_make_key_blob(k, &blob, &bloblen); + + /* data to be signed */ + buffer_init(&b); + buffer_append(&b, session_id2, session_id2_len); + buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(&b, server_user); + buffer_put_cstring(&b, + datafellows & SSH_BUG_PUBKEYAUTH ? + "ssh-userauth" : + service); + buffer_put_cstring(&b, "publickey"); + buffer_put_char(&b, 1); + buffer_put_cstring(&b, KEX_DSS); + buffer_put_string(&b, blob, bloblen); + + /* generate signature */ + dsa_sign(k, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); + key_free(k); +#ifdef DEBUG_DSS + buffer_dump(&b); +#endif + if (datafellows & SSH_BUG_PUBKEYAUTH) { + /* e.g. ssh-2.0.13: data-to-be-signed != data-on-the-wire */ + buffer_clear(&b); + buffer_append(&b, session_id2, session_id2_len); + buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(&b, server_user); + buffer_put_cstring(&b, service); + buffer_put_cstring(&b, "publickey"); + buffer_put_char(&b, 1); + buffer_put_cstring(&b, KEX_DSS); + buffer_put_string(&b, blob, bloblen); + } + xfree(blob); + /* append signature */ + buffer_put_string(&b, signature, slen); + xfree(signature); + + /* skip session id and packet type */ + if (buffer_len(&b) < session_id2_len + 1) + fatal("ssh2_try_pubkey: internal error"); + buffer_consume(&b, session_id2_len + 1); + + /* put remaining data from buffer into packet */ + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_raw(buffer_ptr(&b), buffer_len(&b)); + buffer_free(&b); + + /* send */ + packet_send(); + packet_write_wait(); + return 1; +} + +void +ssh_userauth2(const char *server_user, char *host) +{ + int type; + int plen; + int sent; + unsigned int dlen; + int partial; + int i = 0; + char *auths; + char *service = "ssh-connection"; /* service name */ + + debug("send SSH2_MSG_SERVICE_REQUEST"); + packet_start(SSH2_MSG_SERVICE_REQUEST); + packet_put_cstring("ssh-userauth"); + packet_send(); + packet_write_wait(); + + type = packet_read(&plen); + if (type != SSH2_MSG_SERVICE_ACCEPT) { + fatal("denied SSH2_MSG_SERVICE_ACCEPT: %d", type); + } + if (packet_remaining() > 0) { + char *reply = packet_get_string(&plen); + debug("service_accept: %s", reply); + xfree(reply); + } else { + /* payload empty for ssh-2.0.13 ?? */ + debug("buggy server: service_accept w/o service"); + } + packet_done(); + debug("got SSH2_MSG_SERVICE_ACCEPT"); + + /* INITIAL request for auth */ + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_cstring(server_user); + packet_put_cstring(service); + packet_put_cstring("none"); + packet_send(); + packet_write_wait(); + + for (;;) { + sent = 0; + type = packet_read(&plen); + if (type == SSH2_MSG_USERAUTH_SUCCESS) + break; + if (type != SSH2_MSG_USERAUTH_FAILURE) + fatal("access denied: %d", type); + /* SSH2_MSG_USERAUTH_FAILURE means: try again */ + auths = packet_get_string(&dlen); + debug("authentications that can continue: %s", auths); + partial = packet_get_char(); + packet_done(); + if (partial) + debug("partial success"); + if (options.dsa_authentication && + strstr(auths, "publickey") != NULL) { + while (i < options.num_identity_files2) { + sent = ssh2_try_pubkey( + options.identity_files2[i++], + server_user, host, service); + if (sent) + break; + } + } + if (!sent) { + if (options.password_authentication && + !options.batch_mode && + strstr(auths, "password") != NULL) { + sent = ssh2_try_passwd(server_user, host, service); + } + } + if (!sent) + fatal("Permission denied (%s).", auths); + xfree(auths); + } + packet_done(); + debug("ssh-userauth2 successfull"); +} diff --git a/crypto/openssh/sshd/Makefile b/crypto/openssh/sshd/Makefile index 3815b5a..f74a032 100644 --- a/crypto/openssh/sshd/Makefile +++ b/crypto/openssh/sshd/Makefile @@ -7,7 +7,8 @@ BINDIR= /usr/sbin MAN= sshd.8 SRCS= sshd.c auth-rhosts.c auth-passwd.c auth-rsa.c auth-rh-rsa.c \ - pty.c log-server.c login.c servconf.c serverloop.c + pty.c log-server.c login.c servconf.c serverloop.c \ + auth.c auth1.c auth2.c session.c .include <bsd.own.mk> # for KERBEROS and AFS diff --git a/crypto/openssh/ttymodes.c b/crypto/openssh/ttymodes.c index 309885ef..25f5013 100644 --- a/crypto/openssh/ttymodes.c +++ b/crypto/openssh/ttymodes.c @@ -10,7 +10,7 @@ */ #include "includes.h" -RCSID("$Id: ttymodes.c,v 1.5 1999/11/24 19:53:54 markus Exp $"); +RCSID("$Id: ttymodes.c,v 1.6 2000/04/14 10:30:34 markus Exp $"); #include "packet.h" #include "ssh.h" @@ -23,7 +23,7 @@ RCSID("$Id: ttymodes.c,v 1.5 1999/11/24 19:53:54 markus Exp $"); * Converts POSIX speed_t to a baud rate. The values of the * constants for speed_t are not themselves portable. */ -static int +static int speed_to_baud(speed_t speed) { switch (speed) { @@ -112,7 +112,7 @@ speed_to_baud(speed_t speed) /* * Converts a numeric baud rate to a POSIX speed_t. */ -static speed_t +static speed_t baud_to_speed(int baud) { switch (baud) { @@ -203,7 +203,7 @@ baud_to_speed(int baud) * in a portable manner, and appends the modes to a packet * being constructed. */ -void +void tty_make_modes(int fd) { struct termios tio; @@ -247,7 +247,7 @@ tty_make_modes(int fd) * Decodes terminal modes for the terminal referenced by fd in a portable * manner from a packet being read. */ -void +void tty_parse_modes(int fd, int *n_bytes_ptr) { struct termios tio; diff --git a/crypto/openssh/ttymodes.h b/crypto/openssh/ttymodes.h index 433ff73..f8243f6 100644 --- a/crypto/openssh/ttymodes.h +++ b/crypto/openssh/ttymodes.h @@ -1,18 +1,18 @@ /* - * + * * ttymodes.h - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> * SGTTY stuff contributed by Janne Snabb <snabb@niksula.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved - * + * * Created: Tue Mar 21 15:42:09 1995 ylo - * + * */ -/* RCSID("$Id: ttymodes.h,v 1.6 1999/11/24 19:53:54 markus Exp $"); */ +/* RCSID("$Id: ttymodes.h,v 1.7 2000/04/14 10:30:34 markus Exp $"); */ /* 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). diff --git a/crypto/openssh/uidswap.c b/crypto/openssh/uidswap.c index aebff0c..20f04cf 100644 --- a/crypto/openssh/uidswap.c +++ b/crypto/openssh/uidswap.c @@ -7,7 +7,7 @@ */ #include "includes.h" -RCSID("$Id: uidswap.c,v 1.5 1999/11/24 19:53:54 markus Exp $"); +RCSID("$Id: uidswap.c,v 1.6 2000/04/14 10:30:34 markus Exp $"); #include "ssh.h" #include "uidswap.h" @@ -34,7 +34,7 @@ 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 +void temporarily_use_uid(uid_t uid) { #ifdef SAVED_IDS_WORK_WITH_SETEUID @@ -58,7 +58,7 @@ temporarily_use_uid(uid_t uid) /* * Restores to the original uid. */ -void +void restore_uid() { #ifdef SAVED_IDS_WORK_WITH_SETEUID @@ -79,7 +79,7 @@ restore_uid() * Permanently sets all uids to the given uid. This cannot be * called while temporarily_use_uid is effective. */ -void +void permanently_set_uid(uid_t uid) { if (setuid(uid) < 0) diff --git a/crypto/openssh/uidswap.h b/crypto/openssh/uidswap.h index 4755710..c08a370 100644 --- a/crypto/openssh/uidswap.h +++ b/crypto/openssh/uidswap.h @@ -1,15 +1,15 @@ /* - * + * * uidswap.h - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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 diff --git a/crypto/openssh/uuencode.c b/crypto/openssh/uuencode.c new file mode 100644 index 0000000..fc84d5a --- /dev/null +++ b/crypto/openssh/uuencode.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + */ +#include "includes.h" +#include "xmalloc.h" + +#include <resolv.h> + +int +uuencode(unsigned char *src, unsigned int srclength, + char *target, size_t targsize) +{ + return __b64_ntop(src, srclength, target, targsize); +} + +int +uudecode(const char *src, unsigned char *target, size_t targsize) +{ + int len; + char *encoded, *p; + + /* copy the 'readonly' source */ + encoded = xstrdup(src); + /* skip whitespace and data */ + for (p = encoded; *p == ' ' || *p == '\t'; p++) + ; + for (; *p != '\0' && *p != ' ' && *p != '\t'; p++) + ; + /* and remote trailing whitespace because __b64_pton needs this */ + *p = '\0'; + len = __b64_pton(encoded, target, targsize); + xfree(encoded); + return len; +} + +void +dump_base64(FILE *fp, unsigned char *data, int len) +{ + unsigned char *buf = xmalloc(2*len); + int i, n; + n = uuencode(data, len, buf, 2*len); + for (i = 0; i < n; i++) { + fprintf(fp, "%c", buf[i]); + if (i % 70 == 69) + fprintf(fp, "\n"); + } + if (i % 70 != 69) + fprintf(fp, "\n"); + xfree(buf); +} diff --git a/crypto/openssh/uuencode.h b/crypto/openssh/uuencode.h new file mode 100644 index 0000000..c92c627 --- /dev/null +++ b/crypto/openssh/uuencode.h @@ -0,0 +1,6 @@ +#ifndef UUENCODE_H +#define UUENCODE_H +int uuencode(unsigned char *src, unsigned int srclength, char *target, size_t targsize); +int uudecode(const char *src, unsigned char *target, size_t targsize); +void dump_base64(FILE *fp, unsigned char *data, int len); +#endif diff --git a/crypto/openssh/version.h b/crypto/openssh/version.h index fe2e876..d577644 100644 --- a/crypto/openssh/version.h +++ b/crypto/openssh/version.h @@ -1 +1 @@ -#define SSH_VERSION "OpenSSH-1.2.3" +#define SSH_VERSION "OpenSSH-2.1" diff --git a/crypto/openssh/xmalloc.c b/crypto/openssh/xmalloc.c index afcdbd9..3155099 100644 --- a/crypto/openssh/xmalloc.c +++ b/crypto/openssh/xmalloc.c @@ -8,7 +8,7 @@ */ #include "includes.h" -RCSID("$Id: xmalloc.c,v 1.5 1999/11/24 00:26:04 deraadt Exp $"); +RCSID("$Id: xmalloc.c,v 1.6 2000/04/14 10:30:34 markus Exp $"); #include "ssh.h" @@ -34,7 +34,7 @@ xrealloc(void *ptr, size_t new_size) return new_ptr; } -void +void xfree(void *ptr) { if (ptr == NULL) diff --git a/crypto/openssh/xmalloc.h b/crypto/openssh/xmalloc.h index ec49eb1..31291ea 100644 --- a/crypto/openssh/xmalloc.h +++ b/crypto/openssh/xmalloc.h @@ -1,20 +1,20 @@ /* - * + * * xmalloc.h - * + * * Author: Tatu Ylonen <ylo@cs.hut.fi> - * + * * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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: xmalloc.h,v 1.2 1999/11/24 00:26:04 deraadt Exp $"); */ +/* RCSID("$Id: xmalloc.h,v 1.3 2000/04/14 10:30:34 markus Exp $"); */ #ifndef XMALLOC_H #define XMALLOC_H |