From a306f2dd74b7488671decc385f82830c98f4a0d3 Mon Sep 17 00:00:00 2001 From: damien Date: Sat, 29 Apr 2000 13:57:08 +0000 Subject: [PATCH] - Merge big update to OpenSSH-2.0 from OpenBSD CVS [README.openssh2] - interop w/ F-secure windows client - sync documentation - ssh_host_dsa_key not ssh_dsa_key [auth-rsa.c] - missing fclose [auth.c authfile.c compat.c dsa.c dsa.h hostfile.c key.c key.h radix.c] [readconf.c readconf.h ssh-add.c ssh-keygen.c ssh.c ssh.h sshconnect.c] [sshd.c uuencode.c uuencode.h authfile.h] - add DSA pubkey auth and other SSH2 fixes. use ssh-keygen -[xX] for trading keys with the real and the original SSH, directly from the people who invented the SSH protocol. [auth.c auth.h authfile.c sshconnect.c auth1.c auth2.c sshconnect.h] [sshconnect1.c sshconnect2.c] - split auth/sshconnect in one file per protocol version [sshconnect2.c] - remove debug [uuencode.c] - add trailing = [version.h] - OpenSSH-2.0 [ssh-keygen.1 ssh-keygen.c] - add -R flag: exit code indicates if RSA is alive [sshd.c] - remove unused silent if -Q is specified [ssh.h] - host key becomes /etc/ssh_host_dsa_key [readconf.c servconf.c ] - ssh/sshd default to proto 1 and 2 [uuencode.c] - remove debug [auth2.c ssh-keygen.c sshconnect2.c sshd.c] - xfree DSA blobs [auth2.c serverloop.c session.c] - cleanup logging for sshd/2, respect PasswordAuth no [sshconnect2.c] - less debug, respect .ssh/config [README.openssh2 channels.c channels.h] - clientloop.c session.c ssh.c - support for x11-fwding, client+server --- ChangeLog | 44 ++ Makefile.in | 6 +- README.openssh2 | 11 +- auth-rsa.c | 1 + auth.c | 686 +----------------------- auth.h | 7 + auth1.c | 512 ++++++++++++++++++ auth2.c | 459 ++++++++++++++++ authfile.c | 204 ++++++- authfile.h | 36 ++ compat.c | 1 - dsa.c | 76 ++- dsa.h | 12 +- hostfile.c | 23 +- key.c | 107 ++-- key.h | 3 +- radix.c | 96 +--- readconf.c | 43 +- readconf.h | 4 + servconf.c | 2 +- serverloop.c | 2 +- session.c | 3 +- ssh-add.c | 35 +- ssh-keygen.1 | 6 + ssh-keygen.c | 348 +++++++++--- ssh.c | 63 ++- ssh.h | 35 +- sshconnect.c | 1356 ++--------------------------------------------- sshconnect.h | 16 + sshconnect1.c | 1020 +++++++++++++++++++++++++++++++++++ sshconnect2.c | 449 ++++++++++++++++ sshd.c | 221 +++++--- uuencode.c | 120 +++++ uuencode.h | 6 + version.h | 2 +- 35 files changed, 3530 insertions(+), 2485 deletions(-) create mode 100644 auth1.c create mode 100644 auth2.c create mode 100644 authfile.h create mode 100644 sshconnect.h create mode 100644 sshconnect1.c create mode 100644 sshconnect2.c create mode 100644 uuencode.c create mode 100644 uuencode.h diff --git a/ChangeLog b/ChangeLog index 98a5a9b3..9efe31fe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,47 @@ +20000429 + - Merge big update to OpenSSH-2.0 from OpenBSD CVS + [README.openssh2] + - interop w/ F-secure windows client + - sync documentation + - ssh_host_dsa_key not ssh_dsa_key + [auth-rsa.c] + - missing fclose + [auth.c authfile.c compat.c dsa.c dsa.h hostfile.c key.c key.h radix.c] + [readconf.c readconf.h ssh-add.c ssh-keygen.c ssh.c ssh.h sshconnect.c] + [sshd.c uuencode.c uuencode.h authfile.h] + - add DSA pubkey auth and other SSH2 fixes. use ssh-keygen -[xX] + for trading keys with the real and the original SSH, directly from the + people who invented the SSH protocol. + [auth.c auth.h authfile.c sshconnect.c auth1.c auth2.c sshconnect.h] + [sshconnect1.c sshconnect2.c] + - split auth/sshconnect in one file per protocol version + [sshconnect2.c] + - remove debug + [uuencode.c] + - add trailing = + [version.h] + - OpenSSH-2.0 + [ssh-keygen.1 ssh-keygen.c] + - add -R flag: exit code indicates if RSA is alive + [sshd.c] + - remove unused + silent if -Q is specified + [ssh.h] + - host key becomes /etc/ssh_host_dsa_key + [readconf.c servconf.c ] + - ssh/sshd default to proto 1 and 2 + [uuencode.c] + - remove debug + [auth2.c ssh-keygen.c sshconnect2.c sshd.c] + - xfree DSA blobs + [auth2.c serverloop.c session.c] + - cleanup logging for sshd/2, respect PasswordAuth no + [sshconnect2.c] + - less debug, respect .ssh/config + [README.openssh2 channels.c channels.h] + - clientloop.c session.c ssh.c + - support for x11-fwding, client+server + 20000421 - Merge fix from OpenBSD CVS [ssh-agent.c] diff --git a/Makefile.in b/Makefile.in index 196cc578..d5e3fde6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -31,11 +31,11 @@ LDFLAGS=-L. @LDFLAGS@ TARGETS=ssh sshd ssh-add ssh-keygen ssh-agent scp $(EXTRA_TARGETS) -LIBOBJS= atomicio.o authfd.o authfile.o bsd-bindresvport.o bsd-daemon.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o dsa.o fake-getaddrinfo.o fake-getnameinfo.o fingerprint.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o entropy.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o xmalloc.o +LIBOBJS= atomicio.o authfd.o authfile.o bsd-bindresvport.o bsd-daemon.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o dsa.o fake-getaddrinfo.o fake-getnameinfo.o fingerprint.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o entropy.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o uuencode.o xmalloc.o -SSHOBJS= ssh.o sshconnect.o log-client.o readconf.o clientloop.o +SSHOBJS= ssh.o sshconnect.o sshconnect1.o sshconnect2.o log-client.o readconf.o clientloop.o -SSHDOBJS= sshd.o auth-rhosts.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o servconf.o serverloop.o bsd-login.o md5crypt.o session.o auth.o +SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-rhosts.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o servconf.o serverloop.o bsd-login.o md5crypt.o session.o TROFFMAN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh.1 sshd.8 CATMAN = scp.0 ssh-add.0 ssh-agent.0 ssh-keygen.0 ssh.0 sshd.0 diff --git a/README.openssh2 b/README.openssh2 index 18722b3c..76a6879b 100644 --- a/README.openssh2 +++ b/README.openssh2 @@ -2,12 +2,15 @@ $Id$ howto: 1) generate server key: - $ umask 077 - $ openssl dsaparam 1024 -out dsa1024.pem - $ openssl gendsa -out /etc/ssh_dsa_key dsa1024.pem -rand /dev/arandom + $ 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) 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 @@ -22,7 +25,7 @@ works: key database in ~/.ssh/known_hosts with bits == 0 hack dss: signature works, keygen w/ openssl client interops w/ sshd2, lshd - server interops w/ ssh2, lsh, ssh.com's Windows client, SecureCRT + server interops w/ ssh2, lsh, ssh.com's Windows client, SecureCRT, F-Secure SSH Client 4.0 server supports multiple concurrent sessions (e.g. with SSH.com Windows client) todo: re-keying diff --git a/auth-rsa.c b/auth-rsa.c index 1e8c947f..63aa345b 100644 --- a/auth-rsa.c +++ b/auth-rsa.c @@ -185,6 +185,7 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n) } } if (fail) { + fclose(f); log(buf); packet_send_debug(buf); restore_uid(); diff --git a/auth.c b/auth.c index 4c6f32b0..3bfcfd8e 100644 --- a/auth.c +++ b/auth.c @@ -5,7 +5,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: auth.c,v 1.4 2000/04/14 10:30:29 markus Exp $"); +RCSID("$OpenBSD: auth.c,v 1.6 2000/04/26 21:28:31 markus Exp $"); #include "xmalloc.h" #include "rsa.h" @@ -40,7 +40,7 @@ extern char *forced_command; * If the user's shell is not executable, false will be returned. * Otherwise true is returned. */ -static int +int allowed_user(struct passwd * pw) { struct stat st; @@ -118,685 +118,3 @@ allowed_user(struct passwd * pw) /* We found no reason not to let this user try to log on... */ return 1; } - -/* - * 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; -} - -#define AUTH_FAIL_MAX 6 -#define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2) -#define AUTH_FAIL_MSG "Too many authentication failures for %.100s" - -/* - * 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()); - -#ifdef WITH_AIXAUTHENTICATE - if (strncmp(get_authname(type),"password", - strlen(get_authname(type))) == 0) - loginfailed(pw->pw_name,get_canonical_hostname(),"ssh"); -#endif /* WITH_AIXAUTHENTICATE */ - - /* 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; -#ifndef SKEY - (void)packet_read(&plen); -#else /* SKEY */ - int type = packet_read(&plen); - unsigned int dlen; - char *password, *skeyinfo; - /* Try to send a fake s/key challenge. */ - if (options.skey_authentication == 1 && - (skeyinfo = skey_fake_keyinfo(user)) != NULL) { - password = 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 = NULL, *password = NULL; - 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); - 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)) - error("Warning: keysize mismatch for client_host_key: " - "actual %d, announced %d", BN_num_bits(client_host_key->n), bits); - packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type); - - authenticated = auth_rhosts_rsa(pw, client_user, client_host_key); - RSA_free(client_host_key); - - snprintf(user, sizeof user, " ruser %s", 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); - -#ifdef USE_PAM - /* Do PAM auth with password */ - authenticated = auth_pam_password(pw, password); -#else /* USE_PAM */ - /* Try authentication with the password. */ - authenticated = auth_password(pw, password); -#endif /* USE_PAM */ - 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); - -#ifdef USE_PAM - if (authenticated) { - if (!do_pam_account(pw->pw_name, client_user)) { - if (client_user != NULL) { - xfree(client_user); - client_user = NULL; - } - do_fake_authloop1(pw->pw_name); - } - return; - } -#else /* USE_PAM */ - if (authenticated) { - return; - } -#endif /* USE_PAM */ - - if (client_user != NULL) { - xfree(client_user); - client_user = NULL; - } - - 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; -#ifdef WITH_AIXAUTHENTICATE - char *loginmsg; -#endif /* WITH_AIXAUTHENTICATE */ - - /* 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; - -#ifdef USE_PAM - start_pam(pw); -#endif - - /* - * If we are not running as root, the user must have the same uid as - * the server. - */ - if (getuid() != 0 && pw->pw_uid != getuid()) - packet_disconnect("Cannot change user when server not running as root."); - - 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 */ -#ifdef USE_PAM - auth_pam_password(pw, "")) { -#else /* USE_PAM */ - auth_password(pw, "")) { -#endif /* USE_PAM */ - /* 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. */ -#ifdef WITH_AIXAUTHENTICATE - loginsuccess(user,get_canonical_hostname(),"ssh",&loginmsg); -#endif /* WITH_AIXAUTHENTICATE */ - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); - - /* Perform session preparation. */ - do_authenticated(pw); -} - - -void input_service_request(int type, int plen); -void input_userauth_request(int type, int plen); -void ssh2_pty_cleanup(void); - -typedef struct Authctxt Authctxt; -struct Authctxt { - char *user; - char *service; - struct passwd pw; - int valid; -}; -static Authctxt *authctxt = NULL; -static int userauth_success = 0; - -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: bad user %s", u); - return NULL; - } -#ifdef USE_PAM - start_pam(pw); -#endif - 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(); -} - -static 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 int try = 0; - unsigned int len; - int c, authenticated = 0; - char *user, *service, *method; - struct passwd *pw; - - if (++try == AUTH_FAIL_MAX) - packet_disconnect("too many failed userauth_requests"); - - 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 && try == 1) { - packet_done(); -#ifdef USE_PAM - /* Do PAM auth with password */ - authenticated = auth_pam_password(pw, ""); -#else /* USE_PAM */ - /* Try authentication with the password. */ - authenticated = auth_password(pw, ""); -#endif /* USE_PAM */ - } else if (strcmp(method, "password") == 0) { - char *password; - c = packet_get_char(); - if (c) - debug("password change not supported"); - password = packet_get_string(&len); - packet_done(); -#ifdef USE_PAM - /* Do PAM auth with password */ - authenticated = auth_pam_password(pw, password); -#else /* USE_PAM */ - /* Try authentication with the password. */ - authenticated = auth_password(pw, password); -#endif /* USE_PAM */ - memset(password, 0, len); - xfree(password); - } else if (strcmp(method, "publickey") == 0) { - /* XXX TODO */ - char *pkalg, *pkblob, *sig; - int have_sig = packet_get_char(); - pkalg = packet_get_string(&len); - pkblob = packet_get_string(&len); - if (have_sig) { - sig = packet_get_string(&len); - /* test for correct signature */ - packet_done(); - xfree(sig); - } else { - packet_done(); - /* test whether pkalg/pkblob are acceptable */ - } - xfree(pkalg); - xfree(pkblob); - } - } - /* XXX check if other auth methods are needed */ - if (authenticated) { - /* turn off userauth */ - dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error); - packet_start(SSH2_MSG_USERAUTH_SUCCESS); - packet_send(); - packet_write_wait(); - log("userauth success for %s", user); - /* now we can break out */ - userauth_success = 1; - } else { - packet_start(SSH2_MSG_USERAUTH_FAILURE); - packet_put_cstring("password"); - packet_put_char(0); /* partial success */ - packet_send(); - packet_write_wait(); - } - xfree(service); - xfree(user); - xfree(method); -} -void -do_authentication2() -{ - dispatch_init(&protocol_error); - dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); - dispatch_run(DISPATCH_BLOCK, &userauth_success); - do_authenticated2(); -} diff --git a/auth.h b/auth.h index 3771e826..72126e09 100644 --- a/auth.h +++ b/auth.h @@ -7,4 +7,11 @@ 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/auth1.c b/auth1.c new file mode 100644 index 00000000..ae5f1cd8 --- /dev/null +++ b/auth1.c @@ -0,0 +1,512 @@ +/* + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + */ + +#include "includes.h" +RCSID("$OpenBSD: auth1.c,v 1.1 2000/04/26 21:28:32 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()); + +#ifdef WITH_AIXAUTHENTICATE + if (strncmp(get_authname(type),"password", + strlen(get_authname(type))) == 0) + loginfailed(pw->pw_name,get_canonical_hostname(),"ssh"); +#endif /* WITH_AIXAUTHENTICATE */ + + /* 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; +#ifndef SKEY + (void)packet_read(&plen); +#else /* SKEY */ + int type = packet_read(&plen); + 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 = NULL, *password = NULL; + 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); + 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)) + error("Warning: keysize mismatch for client_host_key: " + "actual %d, announced %d", BN_num_bits(client_host_key->n), bits); + packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type); + + authenticated = auth_rhosts_rsa(pw, client_user, client_host_key); + RSA_free(client_host_key); + + snprintf(user, sizeof user, " ruser %s", 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); + +#ifdef USE_PAM + /* Do PAM auth with password */ + authenticated = auth_pam_password(pw, password); +#else /* USE_PAM */ + /* Try authentication with the password. */ + authenticated = auth_password(pw, password); +#endif /* USE_PAM */ + + 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); + +#ifdef USE_PAM + if (authenticated) { + if (!do_pam_account(pw->pw_name, client_user)) { + if (client_user != NULL) { + xfree(client_user); + client_user = NULL; + } + do_fake_authloop1(pw->pw_name); + } + return; + } +#else /* USE_PAM */ + if (authenticated) { + return; + } +#endif /* USE_PAM */ + + if (client_user != NULL) { + xfree(client_user); + client_user = NULL; + } + + 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; +#ifdef WITH_AIXAUTHENTICATE + char *loginmsg; +#endif /* WITH_AIXAUTHENTICATE */ + + /* 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; + +#ifdef USE_PAM + start_pam(pw); +#endif + + /* + * If we are not running as root, the user must have the same uid as + * the server. + */ + if (getuid() != 0 && pw->pw_uid != getuid()) + packet_disconnect("Cannot change user when server not running as root."); + + 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 */ +#ifdef USE_PAM + auth_pam_password(pw, "")) { +#else /* USE_PAM */ + auth_password(pw, "")) { +#endif /* USE_PAM */ + /* 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. */ +#ifdef WITH_AIXAUTHENTICATE + loginsuccess(user,get_canonical_hostname(),"ssh",&loginmsg); +#endif /* WITH_AIXAUTHENTICATE */ + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); + + /* Perform session preparation. */ + do_authenticated(pw); +} diff --git a/auth2.c b/auth2.c new file mode 100644 index 00000000..9937ed67 --- /dev/null +++ b/auth2.c @@ -0,0 +1,459 @@ +/* + * 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.3 2000/04/27 15:23:02 markus Exp $"); + +#include +#include +#include + +#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() +{ + 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()); + } + +#ifdef USE_PAM + if (authenticated && !do_pam_account(pw->pw_name, NULL)) + authenticated = 0; +#endif /* USE_PAM */ + + /* XXX todo: check if multiple auth methods are needed */ + if (authenticated == 1) { + authmsg = "Accepted"; + /* 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) { + authmsg = "Failed"; + 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(); + } else { + authmsg = "Postponed"; + } + /* Raise logging level */ + if (authenticated == 1|| + attempt == AUTH_FAIL_LOG || + strcmp(method, "password") == 0) + authlog = log; + + 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()); + + xfree(service); + xfree(user); + xfree(method); +} + +int +ssh2_auth_none(struct passwd *pw) +{ + packet_done(); +#ifdef USE_PAM + return auth_pam_password(pw, ""); +#else /* USE_PAM */ + return auth_password(pw, ""); +#endif /* USE_PAM */ +} +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 && +#ifdef USE_PAM + auth_pam_password(pw, password) == 1) +#else /* USE_PAM */ + auth_password(pw, password) == 1) +#endif /* USE_PAM */ + 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.rsa_authentication == 0) { + debug("pubkey auth disabled"); + 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 ? */ + 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; + } +#ifdef USE_PAM + start_pam(pw); +#endif + 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/authfile.c b/authfile.c index 2998cc59..c284a646 100644 --- a/authfile.c +++ b/authfile.c @@ -18,11 +18,17 @@ RCSID("$Id$"); #include +#include +#include +#include +#include + #include "xmalloc.h" #include "buffer.h" #include "bufaux.h" #include "cipher.h" #include "ssh.h" +#include "key.h" /* Version identification string for identity files. */ #define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n" @@ -35,8 +41,8 @@ RCSID("$Id$"); */ int -save_private_key(const char *filename, const char *passphrase, - RSA *key, const char *comment) +save_private_key_rsa(const char *filename, const char *passphrase, + RSA *key, const char *comment) { Buffer buffer, encrypted; char buf[100], *cp; @@ -128,6 +134,63 @@ save_private_key(const char *filename, const char *passphrase, return 1; } +/* save DSA key in OpenSSL PEM format */ + +int +save_private_key_dsa(const char *filename, const char *passphrase, + DSA *dsa, const char *comment) +{ + FILE *fp; + int fd; + int success = 1; + int len = strlen(passphrase); + + if (len > 0 && len <= 4) { + error("passphrase too short: %d bytes", len); + errno = 0; + return 0; + } + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { + debug("open %s failed", filename); + return 0; + } + fp = fdopen(fd, "w"); + if (fp == NULL ) { + debug("fdopen %s failed", filename); + close(fd); + return 0; + } + if (len > 0) { + if (!PEM_write_DSAPrivateKey(fp, dsa, EVP_des_ede3_cbc(), + (char *)passphrase, strlen(passphrase), NULL, NULL)) + success = 0; + } else { + if (!PEM_write_DSAPrivateKey(fp, dsa, NULL, + NULL, 0, NULL, NULL)) + success = 0; + } + fclose(fp); + return success; +} + +int +save_private_key(const char *filename, const char *passphrase, Key *key, + const char *comment) +{ + switch (key->type) { + case KEY_RSA: + return save_private_key_rsa(filename, passphrase, key->rsa, comment); + break; + case KEY_DSA: + return save_private_key_dsa(filename, passphrase, key->dsa, comment); + break; + default: + break; + } + return 0; +} + /* * Loads the public part of the key file. Returns 0 if an error was * encountered (the file does not exist or is not readable), and non-zero @@ -135,8 +198,7 @@ save_private_key(const char *filename, const char *passphrase, */ int -load_public_key(const char *filename, RSA * pub, - char **comment_return) +load_public_key_rsa(const char *filename, RSA * pub, char **comment_return) { int fd, i; off_t len; @@ -154,7 +216,7 @@ load_public_key(const char *filename, RSA * pub, if (read(fd, cp, (size_t) len) != (size_t) len) { debug("Read from key file %.200s failed: %.100s", filename, - strerror(errno)); + strerror(errno)); buffer_free(&buffer); close(fd); return 0; @@ -183,9 +245,13 @@ load_public_key(const char *filename, RSA * pub, /* Read the public key from the buffer. */ buffer_get_int(&buffer); - pub->n = BN_new(); + /* XXX alloc */ + if (pub->n == NULL) + pub->n = BN_new(); buffer_get_bignum(&buffer, pub->n); - pub->e = BN_new(); + /* XXX alloc */ + if (pub->e == NULL) + pub->e = BN_new(); buffer_get_bignum(&buffer, pub->e); if (comment_return) *comment_return = buffer_get_string(&buffer, NULL); @@ -196,6 +262,20 @@ load_public_key(const char *filename, RSA * pub, return 1; } +int +load_public_key(const char *filename, Key * key, char **comment_return) +{ + switch (key->type) { + case KEY_RSA: + return load_public_key_rsa(filename, key->rsa, comment_return); + break; + case KEY_DSA: + default: + break; + } + return 0; +} + /* * 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 @@ -204,35 +284,17 @@ load_public_key(const char *filename, RSA * pub, */ int -load_private_key(const char *filename, const char *passphrase, - RSA * prv, char **comment_return) +load_private_key_rsa(int fd, const char *filename, + const char *passphrase, RSA * prv, char **comment_return) { - int fd, i, check1, check2, cipher_type; + int i, check1, check2, cipher_type; off_t len; Buffer buffer, decrypted; char *cp; CipherContext cipher; BN_CTX *ctx; BIGNUM *aux; - struct stat st; - - fd = open(filename, O_RDONLY); - if (fd < 0) - return 0; - /* check owner and modes */ - if (fstat(fd, &st) < 0 || - (st.st_uid != 0 && getuid() != 0 && st.st_uid != getuid()) || - (st.st_mode & 077) != 0) { - close(fd); - error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); - error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); - error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); - error("Bad ownership or mode(0%3.3o) for '%s'.", - st.st_mode & 0777, filename); - error("It is recommended that your private key files are NOT accessible by others."); - return 0; - } len = lseek(fd, (off_t) 0, SEEK_END); lseek(fd, (off_t) 0, SEEK_SET); @@ -309,7 +371,9 @@ load_private_key(const char *filename, const char *passphrase, buffer_free(&decrypted); fail: BN_clear_free(prv->n); + prv->n = NULL; BN_clear_free(prv->e); + prv->e = NULL; if (comment_return) xfree(*comment_return); return 0; @@ -343,3 +407,87 @@ fail: return 1; } + +int +load_private_key_dsa(int fd, const char *passphrase, Key *k, char **comment_return) +{ + DSA *dsa; + BIO *in; + FILE *fp; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + error("BIO_new failed"); + return 0; + } + fp = fdopen(fd, "r"); + if (fp == NULL) { + error("fdopen failed"); + return 0; + } + BIO_set_fp(in, fp, BIO_NOCLOSE); + dsa = PEM_read_bio_DSAPrivateKey(in, NULL, NULL, (char *)passphrase); + if (dsa == NULL) { + debug("PEM_read_bio_DSAPrivateKey failed"); + } else { + /* replace k->dsa with loaded key */ + DSA_free(k->dsa); + k->dsa = dsa; + } + BIO_free(in); + fclose(fp); + if (comment_return) + *comment_return = xstrdup("dsa w/o comment"); + debug("read DSA private key done"); +#ifdef DEBUG_DSS + DSA_print_fp(stderr, dsa, 8); +#endif + return dsa != NULL ? 1 : 0; +} + +int +load_private_key(const char *filename, const char *passphrase, Key *key, + char **comment_return) +{ + int fd; + int ret = 0; + struct stat st; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return 0; + + /* check owner and modes */ + if (fstat(fd, &st) < 0 || + (st.st_uid != 0 && st.st_uid != getuid()) || + (st.st_mode & 077) != 0) { + close(fd); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("Bad ownership or mode(0%3.3o) for '%s'.", + st.st_mode & 0777, filename); + error("It is recommended that your private key files are NOT accessible by others."); + return 0; + } + switch (key->type) { + case KEY_RSA: + if (key->rsa->e != NULL) { + BN_clear_free(key->rsa->e); + key->rsa->e = NULL; + } + if (key->rsa->n != NULL) { + BN_clear_free(key->rsa->n); + key->rsa->n = NULL; + } + ret = load_private_key_rsa(fd, filename, passphrase, + key->rsa, comment_return); + break; + case KEY_DSA: + ret = load_private_key_dsa(fd, passphrase, key, comment_return); + default: + break; + } + close(fd); + return ret; +} diff --git a/authfile.h b/authfile.h new file mode 100644 index 00000000..afec27d5 --- /dev/null +++ b/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/compat.c b/compat.c index e4abaa02..524e3f27 100644 --- a/compat.c +++ b/compat.c @@ -44,7 +44,6 @@ enable_compat20(void) { verbose("Enabling compatibility mode for protocol 2.0"); compat20 = 1; - packet_set_ssh2_format(); } void enable_compat13(void) diff --git a/dsa.c b/dsa.c index 2d4febe7..e1f23ec3 100644 --- a/dsa.c +++ b/dsa.c @@ -47,13 +47,14 @@ RCSID("$Id$"); #include #include "kex.h" #include "key.h" +#include "uuencode.h" #define INTBLOB_LEN 20 #define SIGBLOB_LEN (2*INTBLOB_LEN) Key * -dsa_serverkey_from_blob( - char *serverhostkey, int serverhostkeylen) +dsa_key_from_blob( + char *blob, int blen) { Buffer b; char *ktype; @@ -61,14 +62,17 @@ dsa_serverkey_from_blob( DSA *dsa; Key *key; +#ifdef DEBUG_DSS + dump_base64(blob, blen); +#endif /* fetch & parse DSA/DSS pubkey */ key = key_new(KEY_DSA); dsa = key->dsa; buffer_init(&b); - buffer_append(&b, serverhostkey, serverhostkeylen); + buffer_append(&b, blob, blen); ktype = buffer_get_string(&b, NULL); if (strcmp(KEX_DSS, ktype) != 0) { - error("dsa_serverkey_from_blob: cannot handle type %s", ktype); + error("dsa_key_from_blob: cannot handle type %s", ktype); key_free(key); return NULL; } @@ -78,7 +82,7 @@ dsa_serverkey_from_blob( buffer_get_bignum2(&b, dsa->pub_key); rlen = buffer_len(&b); if(rlen != 0) - error("dsa_serverkey_from_blob: remaining bytes in serverhostkey %d", rlen); + error("dsa_key_from_blob: remaining bytes in key blob %d", rlen); buffer_free(&b); debug("keytype %s", ktype); @@ -87,37 +91,8 @@ dsa_serverkey_from_blob( #endif return key; } -DSA * -dsa_load_private(char *filename) -{ - DSA *dsa; - BIO *in; - - in = BIO_new(BIO_s_file()); - if (in == NULL) - fatal("BIO_new failed"); - if (BIO_read_filename(in, filename) <= 0) - fatal("BIO_read failed %s: %s", filename, strerror(errno)); - fprintf(stderr, "read DSA private key\n"); - dsa = PEM_read_bio_DSAPrivateKey(in,NULL,NULL,NULL); - if (dsa == NULL) - fatal("PEM_read_bio_DSAPrivateKey failed %s", filename); - BIO_free(in); - return dsa; -} -Key * -dsa_get_serverkey(char *filename) -{ - Key *k = key_new(KEY_EMPTY); - k->type = KEY_DSA; - k->dsa = dsa_load_private(filename); -#ifdef DEBUG_DSS - DSA_print_fp(stderr, dsa, 8); -#endif - return k; -} int -dsa_make_serverkey_blob(Key *key, unsigned char **blobp, unsigned int *lenp) +dsa_make_key_blob(Key *key, unsigned char **blobp, unsigned int *lenp) { Buffer b; int len; @@ -146,7 +121,7 @@ int dsa_sign( Key *key, unsigned char **sigp, int *lenp, - unsigned char *hash, int hlen) + unsigned char *data, int datalen) { unsigned char *digest; unsigned char *ret; @@ -165,10 +140,13 @@ dsa_sign( } digest = xmalloc(evp_md->md_size); EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, hash, hlen); + 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); @@ -212,7 +190,7 @@ int dsa_verify( Key *key, unsigned char *signature, int signaturelen, - unsigned char *hash, int hlen) + unsigned char *data, int datalen) { Buffer b; unsigned char *digest; @@ -269,10 +247,10 @@ dsa_verify( xfree(sigblob); } - /* sha1 the signed data (== session_id == hash) */ + /* sha1 the data */ digest = xmalloc(evp_md->md_size); EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, hash, hlen); + EVP_DigestUpdate(&md, data, datalen); EVP_DigestFinal(&md, digest, NULL); ret = DSA_do_verify(digest, evp_md->md_size, sig, key->dsa); @@ -296,3 +274,21 @@ dsa_verify( 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/dsa.h b/dsa.h index 65e651d9..3cece7c1 100644 --- a/dsa.h +++ b/dsa.h @@ -1,20 +1,22 @@ #ifndef DSA_H #define DSA_H -Key *dsa_serverkey_from_blob(char *serverhostkey, int serverhostkeylen); -Key *dsa_get_serverkey(char *filename); -int dsa_make_serverkey_blob(Key *key, unsigned char **blobp, unsigned int *lenp); +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 *hash, int hlen); + unsigned char *data, int datalen); int dsa_verify( Key *key, unsigned char *signature, int signaturelen, - unsigned char *hash, int hlen); + unsigned char *data, int datalen); + +Key * +dsa_generate_key(unsigned int bits); #endif diff --git a/hostfile.c b/hostfile.c index 29efe565..e1c2429b 100644 --- a/hostfile.c +++ b/hostfile.c @@ -14,7 +14,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: hostfile.c,v 1.16 2000/04/14 10:30:31 markus Exp $"); +RCSID("$OpenBSD: hostfile.c,v 1.17 2000/04/26 20:56:29 markus Exp $"); #include "packet.h" #include "match.h" @@ -39,13 +39,8 @@ hostfile_read_key(char **cpp, unsigned int *bitsp, Key *ret) for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) ; - /* Get number of bits. */ - if (*cp < '0' || *cp > '9') - return 0; /* Bad bit count... */ - for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) - bits = 10 * bits + *cp - '0'; - - if (!key_read(ret, bits, &cp)) + bits = key_read(ret, &cp); + if (bits == 0) return 0; /* Skip trailing whitespace. */ @@ -182,24 +177,18 @@ add_host_to_hostfile(const char *filename, const char *host, Key *key) { FILE *f; int success = 0; - if (key == NULL) - return 1; - - /* Open the file for appending. */ + return 1; /* XXX ? */ f = fopen(filename, "a"); if (!f) return 0; - fprintf(f, "%s ", host); if (key_write(key, f)) { - fprintf(f, "\n"); success = 1; } else { - error("add_host_to_hostfile: saving key failed"); + error("add_host_to_hostfile: saving key in %s failed", filename); } - - /* Close the file. */ + fprintf(f, "\n"); fclose(f); return success; } diff --git a/key.c b/key.c index 872313ab..583c5290 100644 --- a/key.c +++ b/key.c @@ -38,6 +38,10 @@ #include #include "xmalloc.h" #include "key.h" +#include "dsa.h" +#include "uuencode.h" + +#define SSH_DSS "ssh-dss" Key * key_new(int type) @@ -47,6 +51,8 @@ key_new(int type) DSA *dsa; k = xmalloc(sizeof(*k)); k->type = type; + k->dsa = NULL; + k->rsa = NULL; switch (k->type) { case KEY_RSA: rsa = RSA_new(); @@ -63,8 +69,6 @@ key_new(int type) k->dsa = dsa; break; case KEY_EMPTY: - k->dsa = NULL; - k->rsa = NULL; break; default: fatal("key_new: bad key type %d", k->type); @@ -111,7 +115,7 @@ key_equal(Key *a, Key *b) BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0; break; default: - fatal("key_free: bad key type %d", a->type); + fatal("key_equal: bad key type %d", a->type); break; } return 0; @@ -127,46 +131,37 @@ char * key_fingerprint(Key *k) { static char retval[80]; - unsigned char *buf = NULL; + unsigned char *blob = NULL; int len = 0; - int nlen, elen, plen, qlen, glen, publen; + int nlen, elen; switch (k->type) { case KEY_RSA: nlen = BN_num_bytes(k->rsa->n); elen = BN_num_bytes(k->rsa->e); len = nlen + elen; - buf = xmalloc(len); - BN_bn2bin(k->rsa->n, buf); - BN_bn2bin(k->rsa->e, buf + nlen); + blob = xmalloc(len); + BN_bn2bin(k->rsa->n, blob); + BN_bn2bin(k->rsa->e, blob + nlen); break; case KEY_DSA: - plen = BN_num_bytes(k->dsa->p); - qlen = BN_num_bytes(k->dsa->q); - glen = BN_num_bytes(k->dsa->g); - publen = BN_num_bytes(k->dsa->pub_key); - len = qlen + qlen + glen + publen; - buf = xmalloc(len); - BN_bn2bin(k->dsa->p, buf); - BN_bn2bin(k->dsa->q, buf + plen); - BN_bn2bin(k->dsa->g, buf + plen + qlen); - BN_bn2bin(k->dsa->pub_key , buf + plen + qlen + glen); + dsa_make_key_blob(k, &blob, &len); break; default: fatal("key_fingerprint: bad key type %d", k->type); break; } - if (buf != NULL) { + if (blob != NULL) { unsigned char d[16]; EVP_MD_CTX md; EVP_DigestInit(&md, EVP_md5()); - EVP_DigestUpdate(&md, buf, len); + EVP_DigestUpdate(&md, blob, len); EVP_DigestFinal(&md, d, NULL); snprintf(retval, sizeof(retval), FPRINT, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); - memset(buf, 0, len); - xfree(buf); + memset(blob, 0, len); + xfree(blob); } return retval; } @@ -226,13 +221,27 @@ write_bignum(FILE *f, BIGNUM *num) free(buf); return 1; } -int -key_read(Key *ret, unsigned int bits, char **cpp) +unsigned int +key_read(Key *ret, char **cpp) { + Key *k; + unsigned int bits = 0; + char *cp; + int len, n; + unsigned char *blob; + + cp = *cpp; + switch(ret->type) { case KEY_RSA: + /* Get number of bits. */ + if (*cp < '0' || *cp > '9') + return 0; /* Bad bit count... */ + for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) + bits = 10 * bits + *cp - '0'; if (bits == 0) return 0; + *cpp = cp; /* Get public exponent, public modulus. */ if (!read_bignum(cpp, ret->rsa->e)) return 0; @@ -240,22 +249,32 @@ key_read(Key *ret, unsigned int bits, char **cpp) return 0; break; case KEY_DSA: - if (bits != 0) - return 0; - if (!read_bignum(cpp, ret->dsa->p)) + if (strncmp(cp, SSH_DSS " ", 7) != 0) return 0; - if (!read_bignum(cpp, ret->dsa->q)) - return 0; - if (!read_bignum(cpp, ret->dsa->g)) - return 0; - if (!read_bignum(cpp, ret->dsa->pub_key)) + cp += 7; + len = 2*strlen(cp); + blob = xmalloc(len); + n = uudecode(cp, blob, len); + k = dsa_key_from_blob(blob, n); + if (k == NULL) + return 0; + xfree(blob); + if (ret->dsa != NULL) + DSA_free(ret->dsa); + ret->dsa = k->dsa; + k->dsa = NULL; + key_free(k); + bits = BN_num_bits(ret->dsa->p); + cp = strchr(cp, '='); + if (cp == NULL) return 0; + *cpp = cp + 1; break; default: - fatal("bad key type: %d", ret->type); + fatal("key_read: bad key type: %d", ret->type); break; } - return 1; + return bits; } int key_write(Key *key, FILE *f) @@ -274,17 +293,15 @@ key_write(Key *key, FILE *f) error("key_write: failed for RSA key"); } } else if (key->type == KEY_DSA && key->dsa != NULL) { - /* bits == 0 means DSA key */ - bits = 0; - fprintf(f, "%u", bits); - if (write_bignum(f, key->dsa->p) && - write_bignum(f, key->dsa->q) && - write_bignum(f, key->dsa->g) && - write_bignum(f, key->dsa->pub_key)) { - success = 1; - } else { - error("key_write: failed for DSA key"); - } + int len, n; + unsigned char *blob, *uu; + dsa_make_key_blob(key, &blob, &len); + uu = xmalloc(2*len); + n = uuencode(blob, len, uu); + fprintf(f, "%s %s", SSH_DSS, uu); + xfree(blob); + xfree(uu); + success = 1; } return success; } diff --git a/key.h b/key.h index 70f0c518..d1bcf3b1 100644 --- a/key.h +++ b/key.h @@ -18,6 +18,7 @@ void key_free(Key *k); int key_equal(Key *a, Key *b); char *key_fingerprint(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/radix.c b/radix.c index 84e390fd..9d1c999a 100644 --- a/radix.c +++ b/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 */ #include "includes.h" +#include "uuencode.h" #ifdef AFS #include -char six2pr[64] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' -}; - -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; diff --git a/readconf.c b/readconf.c index 1e3476b8..6c00cd66 100644 --- a/readconf.c +++ b/readconf.c @@ -104,7 +104,8 @@ typedef enum { oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication, - oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol + oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oIdentityFile2, + oGlobalKnownHostsFile2, oUserKnownHostsFile2 } OpCodes; /* Textual representations of the tokens. */ @@ -131,6 +132,7 @@ static struct { { "fallbacktorsh", oFallBackToRsh }, { "usersh", oUseRsh }, { "identityfile", oIdentityFile }, + { "identityfile2", oIdentityFile2 }, { "hostname", oHostName }, { "proxycommand", oProxyCommand }, { "port", oPort }, @@ -145,6 +147,8 @@ static struct { { "rhostsrsaauthentication", oRhostsRSAAuthentication }, { "globalknownhostsfile", oGlobalKnownHostsFile }, { "userknownhostsfile", oUserKnownHostsFile }, + { "globalknownhostsfile2", oGlobalKnownHostsFile2 }, + { "userknownhostsfile2", oUserKnownHostsFile2 }, { "connectionattempts", oConnectionAttempts }, { "batchmode", oBatchMode }, { "checkhostip", oCheckHostIP }, @@ -368,14 +372,22 @@ parse_flag: goto parse_int; case oIdentityFile: + case oIdentityFile2: cp = strtok(NULL, WHITESPACE); if (!cp) fatal("%.200s line %d: Missing argument.", filename, linenum); if (*activep) { - if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES) + intptr = (opcode == oIdentityFile) ? + &options->num_identity_files : + &options->num_identity_files2; + if (*intptr >= SSH_MAX_IDENTITY_FILES) fatal("%.200s line %d: Too many identity files specified (max %d).", filename, linenum, SSH_MAX_IDENTITY_FILES); - options->identity_files[options->num_identity_files++] = xstrdup(cp); + charptr = (opcode == oIdentityFile) ? + &options->identity_files[*intptr] : + &options->identity_files2[*intptr]; + *charptr = xstrdup(cp); + *intptr = *intptr + 1; } break; @@ -397,6 +409,14 @@ parse_string: charptr = &options->user_hostfile; goto parse_string; + case oGlobalKnownHostsFile2: + charptr = &options->system_hostfile2; + goto parse_string; + + case oUserKnownHostsFile2: + charptr = &options->user_hostfile2; + goto parse_string; + case oHostName: charptr = &options->hostname; goto parse_string; @@ -642,12 +662,15 @@ initialize_options(Options * options) options->ciphers = NULL; options->protocol = SSH_PROTO_UNKNOWN; options->num_identity_files = 0; + options->num_identity_files2 = 0; options->hostname = NULL; options->proxy_command = NULL; options->user = NULL; options->escape_char = -1; options->system_hostfile = NULL; options->user_hostfile = NULL; + options->system_hostfile2 = NULL; + options->user_hostfile2 = NULL; options->num_local_forwards = 0; options->num_remote_forwards = 0; options->log_level = (LogLevel) - 1; @@ -715,19 +738,31 @@ fill_default_options(Options * options) if (options->cipher == -1) options->cipher = SSH_CIPHER_NOT_SET; if (options->protocol == SSH_PROTO_UNKNOWN) - options->protocol = SSH_PROTO_1; + options->protocol = SSH_PROTO_1|SSH_PROTO_2|SSH_PROTO_1_PREFERRED; if (options->num_identity_files == 0) { options->identity_files[0] = xmalloc(2 + strlen(SSH_CLIENT_IDENTITY) + 1); sprintf(options->identity_files[0], "~/%.100s", SSH_CLIENT_IDENTITY); options->num_identity_files = 1; } +#if 0 + if (options->num_identity_files2 == 0) { + options->identity_files2[0] = + xmalloc(2 + strlen(SSH2_CLIENT_IDENTITY) + 1); + sprintf(options->identity_files2[0], "~/%.100s", SSH2_CLIENT_IDENTITY); + options->num_identity_files2 = 1; + } +#endif if (options->escape_char == -1) options->escape_char = '~'; if (options->system_hostfile == NULL) options->system_hostfile = SSH_SYSTEM_HOSTFILE; if (options->user_hostfile == NULL) options->user_hostfile = SSH_USER_HOSTFILE; + if (options->system_hostfile2 == NULL) + options->system_hostfile2 = SSH_SYSTEM_HOSTFILE2; + if (options->user_hostfile2 == NULL) + options->user_hostfile2 = SSH_USER_HOSTFILE2; if (options->log_level == (LogLevel) - 1) options->log_level = SYSLOG_LEVEL_INFO; /* options->proxy_command should not be set by default */ diff --git a/readconf.h b/readconf.h index d65edab0..7a6dcc81 100644 --- a/readconf.h +++ b/readconf.h @@ -73,9 +73,13 @@ typedef struct { char *system_hostfile;/* Path for /etc/ssh_known_hosts. */ char *user_hostfile; /* Path for $HOME/.ssh/known_hosts. */ + char *system_hostfile2; + char *user_hostfile2; int num_identity_files; /* Number of files for RSA identities. */ + int num_identity_files2; /* DSA identities. */ char *identity_files[SSH_MAX_IDENTITY_FILES]; + char *identity_files2[SSH_MAX_IDENTITY_FILES]; /* Local TCP/IP forward requests. */ int num_local_forwards; diff --git a/servconf.c b/servconf.c index 5cf6416c..21123144 100644 --- a/servconf.c +++ b/servconf.c @@ -143,7 +143,7 @@ fill_default_server_options(ServerOptions *options) if (options->use_login == -1) options->use_login = 0; if (options->protocol == SSH_PROTO_UNKNOWN) - options->protocol = SSH_PROTO_1; + options->protocol = SSH_PROTO_1|SSH_PROTO_2; } #define WHITESPACE " \t\r\n" diff --git a/serverloop.c b/serverloop.c index 1a76b8da..1e031873 100644 --- a/serverloop.c +++ b/serverloop.c @@ -733,7 +733,7 @@ server_input_channel_open(int type, int plen) rwindow = packet_get_int(); rmaxpack = packet_get_int(); - log("channel_input_open: ctype %s rchan %d win %d max %d", + debug("channel_input_open: ctype %s rchan %d win %d max %d", ctype, rchan, rwindow, rmaxpack); if (strcmp(ctype, "session") == 0) { diff --git a/session.c b/session.c index 68a016e0..d5c53f11 100644 --- a/session.c +++ b/session.c @@ -8,7 +8,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.5 2000/04/19 09:24:39 markus Exp $"); +RCSID("$OpenBSD: session.c,v 1.6 2000/04/27 15:23:02 markus Exp $"); #include "xmalloc.h" #include "ssh.h" @@ -1474,6 +1474,5 @@ do_authenticated2(void) * authentication. */ alarm(0); - log("do_authenticated2"); server_loop2(); } diff --git a/ssh-add.c b/ssh-add.c index d81510e1..34713110 100644 --- a/ssh-add.c +++ b/ssh-add.c @@ -9,11 +9,16 @@ #include "includes.h" RCSID("$Id$"); +#include +#include + #include "rsa.h" #include "ssh.h" #include "xmalloc.h" #include "authfd.h" #include "fingerprint.h" +#include "key.h" +#include "authfile.h" #ifdef HAVE___PROGNAME extern char *__progname; @@ -24,19 +29,19 @@ const char *__progname = "ssh-add"; 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); } @@ -91,20 +96,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)) @@ -114,7 +118,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) { @@ -135,7 +140,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) @@ -145,11 +150,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/ssh-keygen.1 b/ssh-keygen.1 index 38ce251a..dfc40408 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 @@ -37,6 +37,8 @@ .Nm ssh-keygen .Fl l .Op Fl f Ar keyfile +.Nm ssh-keygen +.Fl R .Sh DESCRIPTION .Nm generates and manages authentication keys for @@ -112,6 +114,10 @@ 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. .El .Sh FILES .Bl -tag -width Ds diff --git a/ssh-keygen.c b/ssh-keygen.c index 1b5261d8..31352aa7 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -9,18 +9,21 @@ #include "includes.h" RCSID("$Id$"); -#include "rsa.h" +#include +#include +#include +#include + #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,6 +56,12 @@ 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 */ #ifdef HAVE___PROGNAME extern char *__progname; @@ -60,6 +69,8 @@ extern char *__progname; const char *__progname = "ssh-keygen"; #endif /* HAVE___PROGNAME */ +char hostname[MAXHOSTNAMELEN]; + void ask_filename(struct passwd *pw, const char *prompt) { @@ -77,12 +88,140 @@ 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; + 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 (strncmp(line, "----", 4) == 0 || + strstr(line, ": ") != NULL) { + fprintf(stderr, "ignore: %s", line); + continue; + } + if (!(p = strchr(line, '\n'))) { + fprintf(stderr, "input line too long.\n"); + exit(1); + } + *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; @@ -94,17 +233,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(); @@ -172,7 +310,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"); @@ -180,22 +320,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"); @@ -230,19 +374,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"); @@ -256,11 +400,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"); @@ -272,14 +416,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) @@ -289,7 +433,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"); @@ -305,7 +449,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')) @@ -313,18 +457,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"); @@ -332,13 +476,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); @@ -351,7 +492,7 @@ 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 [-b bits] [-p] [-c] [-l] [-x] [-X] [-y] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname); exit(1); } @@ -363,29 +504,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); @@ -428,6 +568,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(); @@ -441,22 +604,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"); @@ -509,17 +694,13 @@ passphrase_again: strlcpy(comment, identity_comment, sizeof(comment)); } else { /* Create default commend field for the passphrase. */ - if (gethostname(hostname, sizeof(hostname)) < 0) { - perror("gethostname"); - exit(1); - } snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); } /* Save the key with the given passphrase and comment. */ - if (!save_private_key(identity_file, passphrase1, private_key, comment)) { + 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); @@ -529,7 +710,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) @@ -541,21 +724,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/ssh.c b/ssh.c index 41733285..0d3e70e0 100644 --- a/ssh.c +++ b/ssh.c @@ -13,6 +13,10 @@ #include "includes.h" RCSID("$Id$"); +#include +#include +#include + #include "xmalloc.h" #include "ssh.h" #include "packet.h" @@ -24,6 +28,8 @@ RCSID("$Id$"); #include "ssh2.h" #include "compat.h" #include "channels.h" +#include "key.h" +#include "authfile.h" #ifdef HAVE___PROGNAME extern char *__progname; @@ -358,10 +364,16 @@ main(int ac, char **av) } break; case 'c': - options.cipher = cipher_number(optarg); - if (options.cipher == -1) { - fprintf(stderr, "Unknown cipher type '%s'\n", optarg); - exit(1); + if (ciphers_valid(optarg)) { + /* SSH2 only */ + options.ciphers = xstrdup(optarg); + } else { + /* SSH1 only */ + options.cipher = cipher_number(optarg); + if (options.cipher == -1) { + fprintf(stderr, "Unknown cipher type '%s'\n", optarg); + exit(1); + } } break; case 'p': @@ -417,16 +429,11 @@ main(int ac, char **av) if (!host) usage(); - /* check if RSA support exists */ - if (rsa_alive() == 0) { - fprintf(stderr, - "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", - __progname); - exit(1); - } /* Initialize the command to execute on remote host. */ buffer_init(&command); + OpenSSL_add_all_algorithms(); + /* * Save the command to execute on the remote host in a buffer. There * is no limit on the length of the command, except by the maximum @@ -496,6 +503,20 @@ main(int ac, char **av) /* reinit */ log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0); + /* check if RSA support exists */ + if ((options.protocol & SSH_PROTO_1) && + rsa_alive() == 0) { + log("%s: no RSA support in libssl and libcrypto. See ssl(8).", + __progname); + log("Disabling protocol version 1"); + options.protocol &= ~ (SSH_PROTO_1|SSH_PROTO_1_PREFERRED); + } + if (! options.protocol & (SSH_PROTO_1|SSH_PROTO_2)) { + fprintf(stderr, "%s: No protocol version available.\n", + __progname); + exit(1); + } + if (options.user == NULL) options.user = xstrdup(pw->pw_name); @@ -562,9 +583,12 @@ main(int ac, char **av) * authentication. This must be done before releasing extra * privileges, because the file is only readable by root. */ - if (ok) { + if (ok && (options.protocol & SSH_PROTO_1)) { + Key k; host_private_key = RSA_new(); - if (load_private_key(HOST_KEY_FILE, "", host_private_key, NULL)) + k.type = KEY_RSA; + k.rsa = host_private_key; + if (load_private_key(HOST_KEY_FILE, "", &k, NULL)) host_private_key_loaded = 1; } /* @@ -610,15 +634,22 @@ main(int ac, char **av) exit(1); } /* Expand ~ in options.identity_files. */ + /* XXX mem-leaks */ for (i = 0; i < options.num_identity_files; i++) options.identity_files[i] = tilde_expand_filename(options.identity_files[i], original_real_uid); - + for (i = 0; i < options.num_identity_files2; i++) + options.identity_files2[i] = + tilde_expand_filename(options.identity_files2[i], original_real_uid); /* Expand ~ in known host file names. */ options.system_hostfile = tilde_expand_filename(options.system_hostfile, - original_real_uid); + original_real_uid); options.user_hostfile = tilde_expand_filename(options.user_hostfile, - original_real_uid); + original_real_uid); + options.system_hostfile2 = tilde_expand_filename(options.system_hostfile2, + original_real_uid); + options.user_hostfile2 = tilde_expand_filename(options.user_hostfile2, + original_real_uid); /* Log into the remote system. This never returns if the login fails. */ ssh_login(host_private_key_loaded, host_private_key, diff --git a/ssh.h b/ssh.h index 4d1b26df..f9bdb96e 100644 --- a/ssh.h +++ b/ssh.h @@ -88,6 +88,7 @@ * world-readable. */ #define SSH_SYSTEM_HOSTFILE ETCDIR "/ssh_known_hosts" +#define SSH_SYSTEM_HOSTFILE2 ETCDIR "/ssh_known_hosts2" /* * Of these, ssh_host_key must be readable only by root, whereas ssh_config @@ -96,7 +97,7 @@ #define HOST_KEY_FILE ETCDIR "/ssh_host_key" #define SERVER_CONFIG_FILE ETCDIR "/sshd_config" #define HOST_CONFIG_FILE ETCDIR "/ssh_config" -#define DSA_KEY_FILE ETCDIR "/ssh_dsa_key" +#define DSA_KEY_FILE ETCDIR "/ssh_host_dsa_key" #ifndef SSH_PROGRAM #define SSH_PROGRAM "/usr/bin/ssh" @@ -128,6 +129,7 @@ * contain anything particularly secret. */ #define SSH_USER_HOSTFILE "~/.ssh/known_hosts" +#define SSH_USER_HOSTFILE2 "~/.ssh/known_hosts2" /* * Name of the default file containing client-side authentication key. This @@ -152,6 +154,7 @@ * running as root.) */ #define SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" +#define SSH_USER_PERMITTED_KEYS2 ".ssh/authorized_keys2" /* * Per-user and system-wide ssh "rc" files. These files are executed with @@ -407,36 +410,6 @@ int auth_rsa_challenge_dialog(RSA *pk); */ char *read_passphrase(const char *prompt, int from_stdin); -/* - * Saves the authentication (private) key in a file, encrypting it with - * passphrase. The identification of the file (lowest 64 bits of n) will - * precede the key to provide identification of the key without needing a - * passphrase. - */ -int -save_private_key(const char *filename, const char *passphrase, - RSA * private_key, const char *comment); - -/* - * Loads the public part of the key file (public key and comment). Returns 0 - * if an error occurred; zero if the public key was successfully read. The - * comment of the key is returned in comment_return if it is non-NULL; the - * caller must free the value with xfree. - */ -int -load_public_key(const char *filename, RSA * pub, - char **comment_return); - -/* - * 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, - RSA * private_key, char **comment_return); /*------------ Definitions for logging. -----------------------*/ diff --git a/sshconnect.c b/sshconnect.c index f58289e7..5554c064 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -5,48 +5,29 @@ * 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. - * - * SSH2 support added by Markus Friedl. */ #include "includes.h" -RCSID("$OpenBSD: sshconnect.c,v 1.69 2000/04/19 07:05:50 deraadt Exp $"); +RCSID("$OpenBSD: sshconnect.c,v 1.71 2000/04/26 21:28:33 markus Exp $"); #include +#include +#include + #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 "compat.h" #include "readconf.h" - -#include "bufaux.h" -#include -#include - -#include "ssh2.h" -#include -#include -#include -#include "kex.h" -#include "myproposal.h" #include "key.h" -#include "dsa.h" +#include "sshconnect.h" #include "hostfile.h" -/* Session id for the current session. */ -unsigned char session_id[16]; - -/* authentications supported by server */ -unsigned int supported_authentications; - -static char *client_version_string = NULL; -static char *server_version_string = NULL; +char *client_version_string = NULL; +char *server_version_string = NULL; extern Options options; extern char *__progname; @@ -316,653 +297,6 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, return 1; } -/* - * 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; - RSA *private_key; - RSA *public_key; - char *passphrase, *comment; - int type, i; - int plen, clen; - - /* Try to load identification for the authentication key. */ - public_key = RSA_new(); - if (!load_public_key(authfile, public_key, &comment)) { - RSA_free(public_key); - /* 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_key->n); - packet_send(); - packet_write_wait(); - - /* We no longer need the public key. */ - RSA_free(public_key); - - /* Wait for server's response. */ - type = packet_read(&plen); - - /* - * The server responds with failure if it doesn\'t like our key or - * doesn\'t support RSA authentication. - */ - if (type == SSH_SMSG_FAILURE) { - debug("Server refused our key."); - xfree(comment); - return 0; - } - /* Otherwise, the server should respond with a challenge. */ - if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) - packet_disconnect("Protocol error during RSA authentication: %d", type); - - /* Get the challenge from the packet. */ - challenge = BN_new(); - packet_get_bignum(challenge, &clen); - - packet_integrity_check(plen, clen, type); - - debug("Received RSA challenge from server."); - - private_key = RSA_new(); - /* - * Load the private key. Try first with empty passphrase; if it - * fails, ask for a passphrase. - */ - if (!load_private_key(authfile, "", private_key, NULL)) { - char buf[300]; - snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ", - comment); - if (!options.batch_mode) - passphrase = read_passphrase(buf, 0); - else { - debug("Will not query passphrase for %.100s in batch mode.", - comment); - passphrase = xstrdup(""); - } - - /* Load the authentication file using the pasphrase. */ - if (!load_private_key(authfile, passphrase, private_key, NULL)) { - memset(passphrase, 0, strlen(passphrase)); - xfree(passphrase); - error("Bad passphrase."); - - /* Send a dummy response packet to avoid protocol error. */ - packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); - for (i = 0; i < 16; i++) - packet_put_char(0); - packet_send(); - packet_write_wait(); - - /* Expect the server to reject it... */ - packet_read_expect(&plen, SSH_SMSG_FAILURE); - xfree(comment); - return 0; - } - /* Destroy the passphrase. */ - memset(passphrase, 0, strlen(passphrase)); - xfree(passphrase); - } - /* We no longer need the comment. */ - xfree(comment); - - /* Compute and send a response to the challenge. */ - respond_to_rsa_challenge(challenge, private_key); - - /* Destroy the private key. */ - RSA_free(private_key); - - /* 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); - 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)) - 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; -} - char * chop(char *s) { @@ -1060,7 +394,8 @@ ssh_exchange_identification() fatal("Protocol major versions differ: %d vs. %d", (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, remote_major); - + if (compat20) + packet_set_ssh2_format(); /* Send our own protocol version identification. */ snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, @@ -1122,7 +457,8 @@ read_yes_or_no(const char *prompt, int defval) */ void -check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) +check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, + const char *user_hostfile, const char *system_hostfile) { Key *file_key; char *ip = NULL; @@ -1141,6 +477,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) * essentially disables host authentication for localhost; however, * this is probably not a real problem. */ + /** hostaddr == 0! */ switch (hostaddr->sa_family) { case AF_INET: local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; @@ -1184,19 +521,19 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) * Check if the host key is present in the user\'s list of known * hosts or in the systemwide list. */ - host_status = check_host_in_hostfile(options.user_hostfile, host, host_key, file_key); + host_status = check_host_in_hostfile(user_hostfile, host, host_key, file_key); if (host_status == HOST_NEW) - host_status = check_host_in_hostfile(options.system_hostfile, host, host_key, file_key); + host_status = check_host_in_hostfile(system_hostfile, host, host_key, file_key); /* * Also perform check for the ip address, skip the check if we are * localhost or the hostname was an ip address to begin with */ if (options.check_host_ip && !local && strcmp(host, ip)) { Key *ip_key = key_new(host_key->type); - ip_status = check_host_in_hostfile(options.user_hostfile, ip, host_key, ip_key); + ip_status = check_host_in_hostfile(user_hostfile, ip, host_key, ip_key); if (ip_status == HOST_NEW) - ip_status = check_host_in_hostfile(options.system_hostfile, ip, host_key, ip_key); + ip_status = check_host_in_hostfile(system_hostfile, ip, host_key, ip_key); if (host_status == HOST_CHANGED && (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) host_ip_differ = 1; @@ -1213,9 +550,9 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) debug("Host '%.200s' is known and matches the host key.", host); if (options.check_host_ip) { if (ip_status == HOST_NEW) { - if (!add_host_to_hostfile(options.user_hostfile, ip, host_key)) + if (!add_host_to_hostfile(user_hostfile, ip, host_key)) log("Failed to add the host key for IP address '%.30s' to the list of known hosts (%.30s).", - ip, options.user_hostfile); + ip, user_hostfile); else log("Warning: Permanently added host key for IP address '%.30s' to the list of known hosts.", ip); @@ -1249,9 +586,9 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) hostp = host; /* If not in strict mode, add the key automatically to the local known_hosts file. */ - if (!add_host_to_hostfile(options.user_hostfile, hostp, host_key)) + if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) log("Failed to add the host to the list of known hosts (%.500s).", - options.user_hostfile); + user_hostfile); else log("Warning: Permanently added '%.200s' to the list of known hosts.", hostp); @@ -1283,7 +620,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) error("It is also possible that the host key has just been changed."); error("Please contact your system administrator."); error("Add correct host key in %.100s to get rid of this message.", - options.user_hostfile); + user_hostfile); /* * If strict host key checking is in use, the user will have @@ -1317,260 +654,22 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) if (options.check_host_ip) xfree(ip); } -void -check_rsa_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key) -{ - Key k; - k.type = KEY_RSA; - k.rsa = host_key; - check_host_key(host, hostaddr, &k); -} /* - * SSH2 key exchange - */ -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_ARCFOUR || - options.cipher == SSH_CIPHER_3DES_CBC || - options.cipher == SSH_CIPHER_CAST128_CBC || - options.cipher == SSH_CIPHER_BLOWFISH_CBC) { - myproposal[PROPOSAL_ENC_ALGS_CTOS] = - myproposal[PROPOSAL_ENC_ALGS_STOC] = cipher_name(options.cipher); - } - 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_serverkey_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); - - /* 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 - ); - 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 - dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20); - key_free(server_host_key); - - kex_derive_keys(kex, hash, shared_secret); - packet_set_kex(kex); - - /* have keys, free DH */ - DH_free(dh); - - 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 + * Starts a dialog with the server, and authenticates the current user on the + * server. This does not need any extra privileges. The basic connection + * to the server must already have been established before this is called. + * If login fails, this function prints an error and never returns. + * This function does not require super-user privileges. */ void -ssh_userauth2(int host_key_valid, RSA *own_host_key, - uid_t original_real_uid, char *host) +ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, + struct sockaddr *hostaddr, uid_t original_real_uid) { - int type; - int plen; - unsigned int dlen; - int partial; struct passwd *pw; - char prompt[80]; + char *host, *cp; char *server_user, *local_user; - char *auths; - char *password; - 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 ?? */ - log("buggy server: service_accept w/o service"); - } - packet_done(); - debug("got SSH2_MSG_SERVICE_ACCEPT"); - /*XX COMMONCODE: */ /* Get local user name. Use it as server user if no user name was given. */ pw = getpwuid(original_real_uid); if (!pw) @@ -1578,396 +677,6 @@ ssh_userauth2(int host_key_valid, RSA *own_host_key, local_user = xstrdup(pw->pw_name); server_user = options.user ? options.user : local_user; - /* 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 (;;) { - 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 (strstr(auths, "password") == NULL) - fatal("passwd auth not supported: %s", auths); - xfree(auths); - /* try passwd */ - 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(); - } - packet_done(); - debug("ssh-userauth2 successfull"); -} - -/* - * SSH1 key exchange - */ -void -ssh_kex(char *host, struct sockaddr *hostaddr) -{ - int i; - BIGNUM *key; - RSA *host_key; - RSA *public_key; - 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); - - check_rsa_host_key(host, hostaddr, host_key); - - 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_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(int host_key_valid, RSA *own_host_key, - uid_t original_real_uid, char *host) -{ - int i, type; - int payload_len; - struct passwd *pw; - const char *server_user, *local_user; - - /* Get local user name. Use it as server user if no user name was given. */ - pw = getpwuid(original_real_uid); - if (!pw) - fatal("User id %d not found from user database.", original_real_uid); - local_user = xstrdup(pw->pw_name); - server_user = options.user ? options.user : local_user; - - /* 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 */ -} -/* - * Starts a dialog with the server, and authenticates the current user on the - * server. This does not need any extra privileges. The basic connection - * to the server must already have been established before this is called. - * If login fails, this function prints an error and never returns. - * This function does not require super-user privileges. - */ -void -ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, - struct sockaddr *hostaddr, uid_t original_real_uid) -{ - char *host, *cp; - /* Convert the user-supplied hostname into all lowercase. */ host = xstrdup(orighost); for (cp = host; *cp; cp++) @@ -1984,12 +693,9 @@ ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, /* authenticate user */ if (compat20) { ssh_kex2(host, hostaddr); - ssh_userauth2(host_key_valid, own_host_key, original_real_uid, host); + ssh_userauth2(server_user, host); } else { - supported_authentications = 0; ssh_kex(host, hostaddr); - if (supported_authentications == 0) - fatal("supported_authentications == 0."); - ssh_userauth(host_key_valid, own_host_key, original_real_uid, host); + ssh_userauth(local_user, server_user, host, host_key_valid, own_host_key); } } diff --git a/sshconnect.h b/sshconnect.h new file mode 100644 index 00000000..13d395fd --- /dev/null +++ b/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/sshconnect1.c b/sshconnect1.c new file mode 100644 index 00000000..c5a76654 --- /dev/null +++ b/sshconnect1.c @@ -0,0 +1,1020 @@ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Sat Mar 18 22:15:47 1995 ylo + * Code to connect to a remote host, and to perform the client side of the + * login (authentication) dialog. + * + */ + +#include "includes.h" +RCSID("$OpenBSD: sshconnect1.c,v 1.1 2000/04/26 21:28:33 markus Exp $"); + +#include +#include +#include +#include + +#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); + 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)) + 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_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/sshconnect2.c b/sshconnect2.c new file mode 100644 index 00000000..31ef3084 --- /dev/null +++ b/sshconnect2.c @@ -0,0 +1,449 @@ +/* + * 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.4 2000/04/27 17:54:01 markus Exp $"); + +#include +#include +#include +#include +#include +#include + +#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_ARCFOUR || + options.cipher == SSH_CIPHER_3DES_CBC || + options.cipher == SSH_CIPHER_CAST128_CBC || + options.cipher == SSH_CIPHER_BLOWFISH_CBC) { + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = cipher_name(options.cipher); + } + 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) +{ + char prompt[80]; + char *password; + + 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; + + 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, 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); + + /* generate signature */ + dsa_sign(k, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); + key_free(k); +#ifdef DEBUG_DSS + buffer_dump(&b); +#endif + /* 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 ?? */ + log("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.rsa_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/sshd.c b/sshd.c index c1dcdd8e..fc2d1d20 100644 --- a/sshd.c +++ b/sshd.c @@ -14,7 +14,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.107 2000/04/19 07:05:50 deraadt Exp $"); +RCSID("$OpenBSD: sshd.c,v 1.111 2000/04/27 08:01:28 markus Exp $"); #include "xmalloc.h" #include "rsa.h" @@ -40,6 +40,7 @@ RCSID("$OpenBSD: sshd.c,v 1.107 2000/04/19 07:05:50 deraadt Exp $"); #include "auth.h" #include "myproposal.h" +#include "authfile.h" #ifdef LIBWRAP #include @@ -112,8 +113,9 @@ char *server_version_string = NULL; * not very useful. Currently, memory locking is not implemented. */ struct { - RSA *private_key; /* Private part of server key. */ + RSA *private_key; /* Private part of empheral server key. */ RSA *host_key; /* Private part of host key. */ + Key *dsa_host_key; /* Private DSA host key. */ } sensitive_data; /* @@ -132,6 +134,10 @@ RSA *public_key; /* session identifier, used by RSA-auth */ unsigned char session_id[16]; +/* same for ssh2 */ +unsigned char *session_id2 = NULL; +int session_id2_len = 0; + /* Prototypes for various functions defined later in this file. */ void do_ssh1_kex(); void do_ssh2_kex(); @@ -224,6 +230,7 @@ grace_alarm_handler(int sig) * Thus there should be no concurrency control/asynchronous execution * problems. */ +/* XXX do we really want this work to be done in a signal handler ? -m */ void key_regeneration_alarm(int sig) { @@ -344,6 +351,13 @@ sshd_exchange_identification(int sock_in, int sock_out) mismatch = 0; switch(remote_major) { case 1: + if (remote_minor == 99) { + if (options.protocol & SSH_PROTO_2) + enable_compat20(); + else + mismatch = 1; + break; + } if (!(options.protocol & SSH_PROTO_1)) { mismatch = 1; break; @@ -355,12 +369,6 @@ sshd_exchange_identification(int sock_in, int sock_out) /* note that this disables agent-forwarding */ enable_compat13(); } - if (remote_minor == 99) { - if (options.protocol & SSH_PROTO_2) - enable_compat20(); - else - mismatch = 1; - } break; case 2: if (options.protocol & SSH_PROTO_2) { @@ -386,6 +394,20 @@ sshd_exchange_identification(int sock_in, int sock_out) server_version_string, client_version_string); fatal_cleanup(); } + if (compat20) + packet_set_ssh2_format(); +} + + +void +destroy_sensitive_data(void) +{ + /* Destroy the private and public keys. They will no longer be needed. */ + RSA_free(public_key); + RSA_free(sensitive_data.private_key); + RSA_free(sensitive_data.host_key); + if (sensitive_data.dsa_host_key != NULL) + key_free(sensitive_data.dsa_host_key); } /* @@ -399,12 +421,11 @@ main(int ac, char **av) int opt, sock_in = 0, sock_out = 0, newsock, i, fdsetsz, on = 1; pid_t pid; socklen_t fromlen; - int silentrsa = 0; + int silent = 0; fd_set *fdset; struct sockaddr_storage from; const char *remote_ip; int remote_port; - char *comment; FILE *f; struct linger linger; struct addrinfo *ai; @@ -441,7 +462,7 @@ main(int ac, char **av) inetd_flag = 1; break; case 'Q': - silentrsa = 1; + silent = 1; break; case 'q': options.log_level = SYSLOG_LEVEL_QUIET; @@ -497,27 +518,14 @@ main(int ac, char **av) log_init(av0, options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility, - !inetd_flag); + !silent && !inetd_flag); - /* check if RSA support exists */ - if (rsa_alive() == 0) { - if (silentrsa == 0) - printf("sshd: no RSA support in libssl and libcrypto -- exiting. See ssl(8)\n"); - log("no RSA support in libssl and libcrypto -- exiting. See ssl(8)"); - exit(1); - } /* Read server configuration options from the configuration file. */ read_server_config(&options, config_file_name); /* Fill in default values for those options not explicitly set. */ fill_default_server_options(&options); - /* Check certain values for sanity. */ - if (options.server_key_bits < 512 || - options.server_key_bits > 32768) { - fprintf(stderr, "Bad server key size.\n"); - exit(1); - } /* Check that there are no remaining arguments. */ if (optind < ac) { fprintf(stderr, "Extra argument %s.\n", av[optind]); @@ -526,26 +534,79 @@ main(int ac, char **av) debug("sshd version %.100s", SSH_VERSION); - sensitive_data.host_key = RSA_new(); - errno = 0; - /* Load the host key. It must have empty passphrase. */ - if (!load_private_key(options.host_key_file, "", - sensitive_data.host_key, &comment)) { - error("Could not load host key: %.200s: %.100s", - options.host_key_file, strerror(errno)); + sensitive_data.dsa_host_key = NULL; + sensitive_data.host_key = NULL; + + /* check if RSA support exists */ + if ((options.protocol & SSH_PROTO_1) && + rsa_alive() == 0) { + log("no RSA support in libssl and libcrypto. See ssl(8)"); + log("Disabling protocol version 1"); + options.protocol &= ~SSH_PROTO_1; + } + /* Load the RSA/DSA host key. It must have empty passphrase. */ + if (options.protocol & SSH_PROTO_1) { + Key k; + sensitive_data.host_key = RSA_new(); + k.type = KEY_RSA; + k.rsa = sensitive_data.host_key; + errno = 0; + if (!load_private_key(options.host_key_file, "", &k, NULL)) { + error("Could not load host key: %.200s: %.100s", + options.host_key_file, strerror(errno)); + log("Disabling protocol version 1"); + options.protocol &= ~SSH_PROTO_1; + } + k.rsa = NULL; + } + if (options.protocol & SSH_PROTO_2) { + sensitive_data.dsa_host_key = key_new(KEY_DSA); + if (!load_private_key(options.dsa_key_file, "", sensitive_data.dsa_host_key, NULL)) { + error("Could not load DSA host key: %.200s", options.dsa_key_file); + log("Disabling protocol version 2"); + options.protocol &= ~SSH_PROTO_2; + } + } + if (! options.protocol & (SSH_PROTO_1|SSH_PROTO_2)) { + if (silent == 0) + fprintf(stderr, "sshd: no hostkeys available -- exiting.\n"); + log("sshd: no hostkeys available -- exiting.\n"); exit(1); } - xfree(comment); - /* Initialize the log (it is reinitialized below in case we - forked). */ + /* Check certain values for sanity. */ + if (options.protocol & SSH_PROTO_1) { + if (options.server_key_bits < 512 || + options.server_key_bits > 32768) { + fprintf(stderr, "Bad server key size.\n"); + exit(1); + } + /* + * Check that server and host key lengths differ sufficiently. This + * is necessary to make double encryption work with rsaref. Oh, I + * hate software patents. I dont know if this can go? Niels + */ + if (options.server_key_bits > + BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && + options.server_key_bits < + BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { + options.server_key_bits = + BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; + debug("Forcing server key to %d bits to make it differ from host key.", + options.server_key_bits); + } + } + + /* Initialize the log (it is reinitialized below in case we forked). */ if (debug_flag && !inetd_flag) log_stderr = 1; log_init(av0, options.log_level, options.log_facility, log_stderr); - /* If not in debugging mode, and not started from inetd, - disconnect from the controlling terminal, and fork. The - original process exits. */ + /* + * If not in debugging mode, and not started from inetd, disconnect + * from the controlling terminal, and fork. The original process + * exits. + */ if (!debug_flag && !inetd_flag) { #ifdef TIOCNOTTY int fd; @@ -565,18 +626,6 @@ main(int ac, char **av) /* Reinitialize the log (because of the fork above). */ log_init(av0, options.log_level, options.log_facility, log_stderr); - /* Check that server and host key lengths differ sufficiently. - This is necessary to make double encryption work with rsaref. - Oh, I hate software patents. I dont know if this can go? Niels */ - if (options.server_key_bits > - BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && - options.server_key_bits < - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { - options.server_key_bits = - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; - debug("Forcing server key to %d bits to make it differ from host key.", - options.server_key_bits); - } /* Do not display messages to stdout in RSA code. */ rsa_set_verbose(0); @@ -594,20 +643,22 @@ main(int ac, char **av) s2 = dup(s1); sock_in = dup(0); sock_out = dup(1); - /* We intentionally do not close the descriptors 0, 1, and 2 - as our code for setting the descriptors won\'t work - if ttyfd happens to be one of those. */ + /* + * We intentionally do not close the descriptors 0, 1, and 2 + * as our code for setting the descriptors won\'t work if + * ttyfd happens to be one of those. + */ debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); - public_key = RSA_new(); - sensitive_data.private_key = RSA_new(); - - /* XXX check options.protocol */ - log("Generating %d bit RSA key.", options.server_key_bits); - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - arc4random_stir(); - log("RSA key generation complete."); + if (options.protocol & SSH_PROTO_1) { + public_key = RSA_new(); + sensitive_data.private_key = RSA_new(); + log("Generating %d bit RSA key.", options.server_key_bits); + rsa_generate_key(sensitive_data.private_key, public_key, + options.server_key_bits); + arc4random_stir(); + log("RSA key generation complete."); + } } else { for (ai = options.listen_addrs; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) @@ -684,19 +735,20 @@ main(int ac, char **av) fclose(f); } } + if (options.protocol & SSH_PROTO_1) { + public_key = RSA_new(); + sensitive_data.private_key = RSA_new(); - public_key = RSA_new(); - sensitive_data.private_key = RSA_new(); - - log("Generating %d bit RSA key.", options.server_key_bits); - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - arc4random_stir(); - log("RSA key generation complete."); + log("Generating %d bit RSA key.", options.server_key_bits); + rsa_generate_key(sensitive_data.private_key, public_key, + options.server_key_bits); + arc4random_stir(); + log("RSA key generation complete."); - /* Schedule server key regeneration alarm. */ - signal(SIGALRM, key_regeneration_alarm); - alarm(options.key_regeneration_time); + /* Schedule server key regeneration alarm. */ + signal(SIGALRM, key_regeneration_alarm); + alarm(options.key_regeneration_time); + } /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ signal(SIGHUP, sighup_handler); @@ -1069,9 +1121,7 @@ do_ssh1_kex() sensitive_data.private_key->n); /* Destroy the private and public keys. They will no longer be needed. */ - RSA_free(public_key); - RSA_free(sensitive_data.private_key); - RSA_free(sensitive_data.host_key); + destroy_sensitive_data(); /* * Extract session key from the decrypted integer. The key is in the @@ -1130,7 +1180,6 @@ do_ssh2_kex() unsigned char *kbuf; unsigned char *hash; Kex *kex; - Key *server_host_key; char *cprop[PROPOSAL_MAX]; char *sprop[PROPOSAL_MAX]; @@ -1231,8 +1280,8 @@ do_ssh2_kex() memset(kbuf, 0, klen); xfree(kbuf); - server_host_key = dsa_get_serverkey(options.dsa_key_file); - dsa_make_serverkey_blob(server_host_key, &server_host_key_blob, &sbloblen); + /* XXX precompute? */ + dsa_make_key_blob(sensitive_data.dsa_host_key, &server_host_key_blob, &sbloblen); /* calc H */ /* XXX depends on 'kex' */ hash = kex_hash( @@ -1255,10 +1304,17 @@ do_ssh2_kex() fprintf(stderr, "%02x", (hash[i])&0xff); fprintf(stderr, "\n"); #endif + /* save session id := H */ + /* XXX hashlen depends on KEX */ + session_id2_len = 20; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, hash, session_id2_len); + /* sign H */ - dsa_sign(server_host_key, &signature, &slen, hash, 20); - /* hashlen depends on KEX */ - key_free(server_host_key); + /* XXX hashlen depends on KEX */ + dsa_sign(sensitive_data.dsa_host_key, &signature, &slen, hash, 20); + + destroy_sensitive_data(); /* send server hostkey, DH pubkey 'f' and singed H */ packet_start(SSH2_MSG_KEXDH_REPLY); @@ -1267,6 +1323,7 @@ do_ssh2_kex() packet_put_string((char *)signature, slen); packet_send(); xfree(signature); + xfree(server_host_key_blob); packet_write_wait(); kex_derive_keys(kex, hash, shared_secret); diff --git a/uuencode.c b/uuencode.c new file mode 100644 index 00000000..62689005 --- /dev/null +++ b/uuencode.c @@ -0,0 +1,120 @@ +/* + * base-64 encoding pinched from lynx2-7-2, who pinched it from rpem. + * Originally written by Mark Riordan 12 August 1990 and 17 Feb 1991 + * and placed in the public domain. + * + * Dug Song + */ + +#include "includes.h" +#include "xmalloc.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] = '='; + } else if (i == nbytes) { + *(outptr++) = '='; + } + *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); +} + +void +dump_base64(FILE *fp, unsigned char *data, int len) +{ + unsigned char *buf = xmalloc(2*len); + int i, n; + n = uuencode(data, len, buf); + 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/uuencode.h b/uuencode.h new file mode 100644 index 00000000..d3f44628 --- /dev/null +++ b/uuencode.h @@ -0,0 +1,6 @@ +#ifndef UUENCODE_H +#define UUENCODE_H +int uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded); +int uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize); +void dump_base64(FILE *fp, unsigned char *data, int len); +#endif diff --git a/version.h b/version.h index fe2e876e..5e7a38da 100644 --- a/version.h +++ b/version.h @@ -1 +1 @@ -#define SSH_VERSION "OpenSSH-1.2.3" +#define SSH_VERSION "OpenSSH-2.0" -- 2.45.1