summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2000-04-29 23:57:08 +1000
committerDamien Miller <djm@mindrot.org>2000-04-29 23:57:08 +1000
commiteba71bab9bf01c0d688f829a8971f902732558df (patch)
treea9d5b50568bfc10cc50291fd3604debfaf3e3783
parent8117111a3c1360727e3c54aad31aa045e7a7871b (diff)
downloadopenssh-git-eba71bab9bf01c0d688f829a8971f902732558df.tar.gz
- 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
-rw-r--r--ChangeLog44
-rw-r--r--Makefile.in6
-rw-r--r--README.openssh215
-rw-r--r--auth-rsa.c3
-rw-r--r--auth.c686
-rw-r--r--auth.h7
-rw-r--r--auth1.c512
-rw-r--r--auth2.c459
-rw-r--r--authfile.c206
-rw-r--r--authfile.h36
-rw-r--r--compat.c3
-rw-r--r--dsa.c78
-rw-r--r--dsa.h12
-rw-r--r--hostfile.c23
-rw-r--r--key.c107
-rw-r--r--key.h3
-rw-r--r--radix.c96
-rw-r--r--readconf.c45
-rw-r--r--readconf.h6
-rw-r--r--servconf.c4
-rw-r--r--serverloop.c2
-rw-r--r--session.c3
-rw-r--r--ssh-add.c37
-rw-r--r--ssh-keygen.18
-rw-r--r--ssh-keygen.c350
-rw-r--r--ssh.c65
-rw-r--r--ssh.h37
-rw-r--r--sshconnect.c1356
-rw-r--r--sshconnect.h16
-rw-r--r--sshconnect1.c1020
-rw-r--r--sshconnect2.c449
-rw-r--r--sshd.c221
-rw-r--r--uuencode.c120
-rw-r--r--uuencode.h6
-rw-r--r--version.h2
35 files changed, 3544 insertions, 2499 deletions
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 bdf78bf5..fca3173a 100644
--- a/README.openssh2
+++ b/README.openssh2
@@ -1,13 +1,16 @@
-$Id: README.openssh2,v 1.3 2000/04/12 07:45:43 markus Exp $
+$Id: README.openssh2,v 1.6 2000/04/27 13:42:58 provos Exp $
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
@@ -38,4 +41,4 @@ todo:
sftp
-markus
-$Date: 2000/04/12 07:45:43 $
+$Date: 2000/04/27 13:42:58 $
diff --git a/auth-rsa.c b/auth-rsa.c
index aa8be2bb..c61eab29 100644
--- a/auth-rsa.c
+++ b/auth-rsa.c
@@ -16,7 +16,7 @@
*/
#include "includes.h"
-RCSID("$Id: auth-rsa.c,v 1.17 2000/04/16 02:31:49 damien Exp $");
+RCSID("$Id: auth-rsa.c,v 1.18 2000/04/29 13:57:09 damien Exp $");
#include "rsa.h"
#include "packet.h"
@@ -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 <ylo@cs.hut.fi>, 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 <openssl/dsa.h>
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+
+#include "xmalloc.h"
+#include "rsa.h"
+#include "ssh.h"
+#include "pty.h"
+#include "packet.h"
+#include "buffer.h"
+#include "cipher.h"
+#include "servconf.h"
+#include "compat.h"
+#include "channels.h"
+#include "bufaux.h"
+#include "ssh2.h"
+#include "auth.h"
+#include "session.h"
+#include "dispatch.h"
+#include "auth.h"
+#include "key.h"
+#include "kex.h"
+
+#include "dsa.h"
+#include "uidswap.h"
+
+/* import */
+extern ServerOptions options;
+extern unsigned char *session_id2;
+extern int session_id2_len;
+
+/* protocol */
+
+void input_service_request(int type, int plen);
+void input_userauth_request(int type, int plen);
+void protocol_error(int type, int plen);
+
+/* auth */
+int ssh2_auth_none(struct passwd *pw);
+int ssh2_auth_password(struct passwd *pw);
+int ssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen);
+
+/* helper */
+struct passwd* auth_set_user(char *u, char *s);
+int user_dsa_key_allowed(struct passwd *pw, Key *key);
+
+typedef struct Authctxt Authctxt;
+struct Authctxt {
+ char *user;
+ char *service;
+ struct passwd pw;
+ int valid;
+};
+static Authctxt *authctxt = NULL;
+static int userauth_success = 0;
+
+/*
+ * loop until userauth_success == TRUE
+ */
+
+void
+do_authentication2()
+{
+ 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 e17c6038..f93c9d47 100644
--- a/authfile.c
+++ b/authfile.c
@@ -15,14 +15,20 @@
*/
#include "includes.h"
-RCSID("$Id: authfile.c,v 1.11 2000/04/16 02:31:49 damien Exp $");
+RCSID("$Id: authfile.c,v 1.12 2000/04/29 13:57:10 damien Exp $");
#include <openssl/bn.h>
+#include <openssl/dsa.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+
#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: authfile.c,v 1.11 2000/04/16 02:31:49 damien Exp $");
*/
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 4673e3fb..3bf45c12 100644
--- a/compat.c
+++ b/compat.c
@@ -28,7 +28,7 @@
*/
#include "includes.h"
-RCSID("$Id: compat.c,v 1.8 2000/04/16 01:18:42 damien Exp $");
+RCSID("$Id: compat.c,v 1.9 2000/04/29 13:57:10 damien Exp $");
#include "ssh.h"
#include "packet.h"
@@ -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 1594c14f..a4f6d3e7 100644
--- a/dsa.c
+++ b/dsa.c
@@ -28,7 +28,7 @@
*/
#include "includes.h"
-RCSID("$Id: dsa.c,v 1.4 2000/04/14 10:30:31 markus Exp $");
+RCSID("$Id: dsa.c,v 1.5 2000/04/26 20:56:29 markus Exp $");
#include "ssh.h"
#include "xmalloc.h"
@@ -47,13 +47,14 @@ RCSID("$Id: dsa.c,v 1.4 2000/04/14 10:30:31 markus Exp $");
#include <openssl/hmac.h>
#include "kex.h"
#include "key.h"
+#include "uuencode.h"
#define INTBLOB_LEN 20
#define SIGBLOB_LEN (2*INTBLOB_LEN)
Key *
-dsa_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 <openssl/evp.h>
#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 <dugsong@UMICH.EDU>
*/
#include "includes.h"
+#include "uuencode.h"
#ifdef AFS
#include <krb.h>
-char six2pr[64] = {
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
- 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
- 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
-};
-
-unsigned char pr2six[256];
-
-int
-uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded)
-{
- /* ENC is the basic 1 character encoding function to make a char printing */
-#define ENC(c) six2pr[c]
-
- register char *outptr = bufcoded;
- unsigned int i;
-
- for (i = 0; i < nbytes; i += 3) {
- *(outptr++) = ENC(*bufin >> 2); /* c1 */
- *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /* c2 */
- *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)); /* c3 */
- *(outptr++) = ENC(bufin[2] & 077); /* c4 */
- bufin += 3;
- }
- if (i == nbytes + 1) {
- outptr[-1] = '=';
- } else if (i == nbytes + 2) {
- outptr[-1] = '=';
- outptr[-2] = '=';
- }
- *outptr = '\0';
- return (outptr - bufcoded);
-}
-
-int
-uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize)
-{
- /* single character decode */
-#define DEC(c) pr2six[(unsigned char)c]
-#define MAXVAL 63
-
- static int first = 1;
- int nbytesdecoded, j;
- const char *bufin = bufcoded;
- register unsigned char *bufout = bufplain;
- register int nprbytes;
-
- /* If this is the first call, initialize the mapping table. */
- if (first) {
- first = 0;
- for (j = 0; j < 256; j++)
- pr2six[j] = MAXVAL + 1;
- for (j = 0; j < 64; j++)
- pr2six[(unsigned char) six2pr[j]] = (unsigned char) j;
- }
- /* Strip leading whitespace. */
- while (*bufcoded == ' ' || *bufcoded == '\t')
- bufcoded++;
-
- /*
- * Figure out how many characters are in the input buffer. If this
- * would decode into more bytes than would fit into the output
- * buffer, adjust the number of input bytes downwards.
- */
- bufin = bufcoded;
- while (DEC(*(bufin++)) <= MAXVAL);
- nprbytes = bufin - bufcoded - 1;
- nbytesdecoded = ((nprbytes + 3) / 4) * 3;
- if (nbytesdecoded > outbufsize)
- nprbytes = (outbufsize * 4) / 3;
-
- bufin = bufcoded;
-
- while (nprbytes > 0) {
- *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4);
- *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2);
- *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3]));
- bufin += 4;
- nprbytes -= 4;
- }
- if (nprbytes & 03) {
- if (DEC(bufin[-2]) > MAXVAL)
- nbytesdecoded -= 2;
- else
- nbytesdecoded -= 1;
- }
- return (nbytesdecoded);
-}
-
typedef unsigned char my_u_char;
typedef unsigned int my_u_int32_t;
typedef unsigned short my_u_short;
diff --git a/readconf.c b/readconf.c
index 3b75290f..529f8039 100644
--- a/readconf.c
+++ b/readconf.c
@@ -14,7 +14,7 @@
*/
#include "includes.h"
-RCSID("$Id: readconf.c,v 1.11 2000/04/16 01:18:44 damien Exp $");
+RCSID("$Id: readconf.c,v 1.12 2000/04/29 13:57:11 damien Exp $");
#include "ssh.h"
#include "cipher.h"
@@ -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 0582a8f2..bbef923a 100644
--- a/readconf.h
+++ b/readconf.h
@@ -13,7 +13,7 @@
*
*/
-/* RCSID("$Id: readconf.h,v 1.8 2000/04/16 01:18:44 damien Exp $"); */
+/* RCSID("$Id: readconf.h,v 1.9 2000/04/29 13:57:11 damien Exp $"); */
#ifndef READCONF_H
#define 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 fe72d275..298fefbe 100644
--- a/servconf.c
+++ b/servconf.c
@@ -12,7 +12,7 @@
*/
#include "includes.h"
-RCSID("$Id: servconf.c,v 1.12 2000/04/16 01:18:45 damien Exp $");
+RCSID("$Id: servconf.c,v 1.13 2000/04/29 13:57:11 damien Exp $");
#include "ssh.h"
#include "servconf.h"
@@ -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 f01cca5c..0d388301 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -7,13 +7,18 @@
*/
#include "includes.h"
-RCSID("$Id: ssh-add.c,v 1.16 1999/12/06 00:47:29 damien Exp $");
+RCSID("$Id: ssh-add.c,v 1.17 2000/04/29 13:57:12 damien Exp $");
+
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
#include "rsa.h"
#include "ssh.h"
#include "xmalloc.h"
#include "authfd.h"
#include "fingerprint.h"
+#include "key.h"
+#include "authfile.h"
#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 b809a1a5..486de042 100644
--- a/ssh-keygen.1
+++ b/ssh-keygen.1
@@ -9,7 +9,7 @@
.\"
.\" Created: Sat Apr 22 23:55:14 1995 ylo
.\"
-.\" $Id: ssh-keygen.1,v 1.12 2000/04/20 13:27:27 damien Exp $
+.\" $Id: ssh-keygen.1,v 1.13 2000/04/29 13:57:12 damien Exp $
.\"
.Dd September 25, 1999
.Dt 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 f2484a4b..0155949f 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -7,20 +7,23 @@
*/
#include "includes.h"
-RCSID("$Id: ssh-keygen.c,v 1.13 2000/04/16 01:18:46 damien Exp $");
+RCSID("$Id: ssh-keygen.c,v 1.14 2000/04/29 13:57:12 damien Exp $");
+
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
-#include "rsa.h"
#include "ssh.h"
#include "xmalloc.h"
#include "fingerprint.h"
+#include "key.h"
+#include "rsa.h"
+#include "dsa.h"
+#include "authfile.h"
+#include "uuencode.h"
-/* Generated private key. */
-RSA *private_key;
-
-/* Generated public key. */
-RSA *public_key;
-
-/* Number of bits in the RSA key. This value can be changed on the command line. */
+/* Number of bits in the RSA/DSA key. This value can be changed on the command line. */
int bits = 1024;
/*
@@ -53,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 456570fc..bdf6180c 100644
--- a/ssh.c
+++ b/ssh.c
@@ -11,7 +11,11 @@
*/
#include "includes.h"
-RCSID("$Id: ssh.c,v 1.26 2000/04/16 01:18:46 damien Exp $");
+RCSID("$Id: ssh.c,v 1.27 2000/04/29 13:57:12 damien Exp $");
+
+#include <openssl/evp.h>
+#include <openssl/dsa.h>
+#include <openssl/rsa.h>
#include "xmalloc.h"
#include "ssh.h"
@@ -24,6 +28,8 @@ RCSID("$Id: ssh.c,v 1.26 2000/04/16 01:18:46 damien Exp $");
#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 87821398..57d78278 100644
--- a/ssh.h
+++ b/ssh.h
@@ -13,7 +13,7 @@
*
*/
-/* RCSID("$Id: ssh.h,v 1.34 2000/04/20 13:12:59 damien Exp $"); */
+/* RCSID("$Id: ssh.h,v 1.35 2000/04/29 13:57:12 damien Exp $"); */
#ifndef SSH_H
#define 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 <openssl/bn.h>
+#include <openssl/dsa.h>
+#include <openssl/rsa.h>
+
#include "xmalloc.h"
#include "rsa.h"
#include "ssh.h"
#include "buffer.h"
#include "packet.h"
-#include "authfd.h"
-#include "cipher.h"
-#include "mpaux.h"
#include "uidswap.h"
#include "compat.h"
#include "readconf.h"
-
-#include "bufaux.h"
-#include <openssl/rsa.h>
-#include <openssl/dsa.h>
-
-#include "ssh2.h"
-#include <openssl/md5.h>
-#include <openssl/dh.h>
-#include <openssl/hmac.h>
-#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 <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ * All rights reserved
+ * Created: Sat Mar 18 22:15:47 1995 ylo
+ * Code to connect to a remote host, and to perform the client side of the
+ * login (authentication) dialog.
+ *
+ */
+
+#include "includes.h"
+RCSID("$OpenBSD: sshconnect1.c,v 1.1 2000/04/26 21:28:33 markus Exp $");
+
+#include <openssl/bn.h>
+#include <openssl/dsa.h>
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+
+#include "xmalloc.h"
+#include "rsa.h"
+#include "ssh.h"
+#include "buffer.h"
+#include "packet.h"
+#include "authfd.h"
+#include "cipher.h"
+#include "mpaux.h"
+#include "uidswap.h"
+#include "readconf.h"
+#include "key.h"
+#include "sshconnect.h"
+#include "authfile.h"
+
+/* Session id for the current session. */
+unsigned char session_id[16];
+unsigned int supported_authentications = 0;
+
+extern Options options;
+extern char *__progname;
+
+/*
+ * Checks if the user has an authentication agent, and if so, tries to
+ * authenticate using the agent.
+ */
+int
+try_agent_authentication()
+{
+ int status, type;
+ char *comment;
+ AuthenticationConnection *auth;
+ unsigned char response[16];
+ unsigned int i;
+ BIGNUM *e, *n, *challenge;
+
+ /* Get connection to the agent. */
+ auth = ssh_get_authentication_connection();
+ if (!auth)
+ return 0;
+
+ e = BN_new();
+ n = BN_new();
+ challenge = BN_new();
+
+ /* Loop through identities served by the agent. */
+ for (status = ssh_get_first_identity(auth, e, n, &comment);
+ status;
+ status = ssh_get_next_identity(auth, e, n, &comment)) {
+ int plen, clen;
+
+ /* Try this identity. */
+ debug("Trying RSA authentication via agent with '%.100s'", comment);
+ xfree(comment);
+
+ /* Tell the server that we are willing to authenticate using this key. */
+ packet_start(SSH_CMSG_AUTH_RSA);
+ packet_put_bignum(n);
+ packet_send();
+ packet_write_wait();
+
+ /* Wait for server's response. */
+ type = packet_read(&plen);
+
+ /* The server sends failure if it doesn\'t like our key or
+ does not support RSA authentication. */
+ if (type == SSH_SMSG_FAILURE) {
+ debug("Server refused our key.");
+ continue;
+ }
+ /* Otherwise it should have sent a challenge. */
+ if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)
+ packet_disconnect("Protocol error during RSA authentication: %d",
+ type);
+
+ packet_get_bignum(challenge, &clen);
+
+ packet_integrity_check(plen, clen, type);
+
+ debug("Received RSA challenge from server.");
+
+ /* Ask the agent to decrypt the challenge. */
+ if (!ssh_decrypt_challenge(auth, e, n, challenge,
+ session_id, 1, response)) {
+ /* The agent failed to authenticate this identifier although it
+ advertised it supports this. Just return a wrong value. */
+ log("Authentication agent failed to decrypt challenge.");
+ memset(response, 0, sizeof(response));
+ }
+ debug("Sending response to RSA challenge.");
+
+ /* Send the decrypted challenge back to the server. */
+ packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);
+ for (i = 0; i < 16; i++)
+ packet_put_char(response[i]);
+ packet_send();
+ packet_write_wait();
+
+ /* Wait for response from the server. */
+ type = packet_read(&plen);
+
+ /* The server returns success if it accepted the authentication. */
+ if (type == SSH_SMSG_SUCCESS) {
+ debug("RSA authentication accepted by server.");
+ BN_clear_free(e);
+ BN_clear_free(n);
+ BN_clear_free(challenge);
+ return 1;
+ }
+ /* Otherwise it should return failure. */
+ if (type != SSH_SMSG_FAILURE)
+ packet_disconnect("Protocol error waiting RSA auth response: %d",
+ type);
+ }
+
+ BN_clear_free(e);
+ BN_clear_free(n);
+ BN_clear_free(challenge);
+
+ debug("RSA authentication using agent refused.");
+ return 0;
+}
+
+/*
+ * Computes the proper response to a RSA challenge, and sends the response to
+ * the server.
+ */
+void
+respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv)
+{
+ unsigned char buf[32], response[16];
+ MD5_CTX md;
+ int i, len;
+
+ /* Decrypt the challenge using the private key. */
+ rsa_private_decrypt(challenge, challenge, prv);
+
+ /* Compute the response. */
+ /* The response is MD5 of decrypted challenge plus session id. */
+ len = BN_num_bytes(challenge);
+ if (len <= 0 || len > sizeof(buf))
+ packet_disconnect("respond_to_rsa_challenge: bad challenge length %d",
+ len);
+
+ memset(buf, 0, sizeof(buf));
+ BN_bn2bin(challenge, buf + sizeof(buf) - len);
+ MD5_Init(&md);
+ MD5_Update(&md, buf, 32);
+ MD5_Update(&md, session_id, 16);
+ MD5_Final(response, &md);
+
+ debug("Sending response to host key RSA challenge.");
+
+ /* Send the response back to the server. */
+ packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);
+ for (i = 0; i < 16; i++)
+ packet_put_char(response[i]);
+ packet_send();
+ packet_write_wait();
+
+ memset(buf, 0, sizeof(buf));
+ memset(response, 0, sizeof(response));
+ memset(&md, 0, sizeof(md));
+}
+
+/*
+ * Checks if the user has authentication file, and if so, tries to authenticate
+ * the user using it.
+ */
+int
+try_rsa_authentication(const char *authfile)
+{
+ BIGNUM *challenge;
+ Key *public;
+ Key *private;
+ char *passphrase, *comment;
+ int type, i;
+ int plen, clen;
+
+ /* Try to load identification for the authentication key. */
+ public = key_new(KEY_RSA);
+ if (!load_public_key(authfile, public, &comment)) {
+ key_free(public);
+ /* Could not load it. Fail. */
+ return 0;
+ }
+ debug("Trying RSA authentication with key '%.100s'", comment);
+
+ /* Tell the server that we are willing to authenticate using this key. */
+ packet_start(SSH_CMSG_AUTH_RSA);
+ packet_put_bignum(public->rsa->n);
+ packet_send();
+ packet_write_wait();
+
+ /* We no longer need the public key. */
+ key_free(public);
+
+ /* Wait for server's response. */
+ type = packet_read(&plen);
+
+ /*
+ * The server responds with failure if it doesn\'t like our key or
+ * doesn\'t support RSA authentication.
+ */
+ if (type == SSH_SMSG_FAILURE) {
+ debug("Server refused our key.");
+ xfree(comment);
+ return 0;
+ }
+ /* Otherwise, the server should respond with a challenge. */
+ if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)
+ packet_disconnect("Protocol error during RSA authentication: %d", type);
+
+ /* Get the challenge from the packet. */
+ challenge = BN_new();
+ packet_get_bignum(challenge, &clen);
+
+ packet_integrity_check(plen, clen, type);
+
+ debug("Received RSA challenge from server.");
+
+ private = key_new(KEY_RSA);
+ /*
+ * Load the private key. Try first with empty passphrase; if it
+ * fails, ask for a passphrase.
+ */
+ if (!load_private_key(authfile, "", private, NULL)) {
+ char buf[300];
+ snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ",
+ comment);
+ if (!options.batch_mode)
+ passphrase = read_passphrase(buf, 0);
+ else {
+ debug("Will not query passphrase for %.100s in batch mode.",
+ comment);
+ passphrase = xstrdup("");
+ }
+
+ /* Load the authentication file using the pasphrase. */
+ if (!load_private_key(authfile, passphrase, private, NULL)) {
+ memset(passphrase, 0, strlen(passphrase));
+ xfree(passphrase);
+ error("Bad passphrase.");
+
+ /* Send a dummy response packet to avoid protocol error. */
+ packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);
+ for (i = 0; i < 16; i++)
+ packet_put_char(0);
+ packet_send();
+ packet_write_wait();
+
+ /* Expect the server to reject it... */
+ packet_read_expect(&plen, SSH_SMSG_FAILURE);
+ xfree(comment);
+ return 0;
+ }
+ /* Destroy the passphrase. */
+ memset(passphrase, 0, strlen(passphrase));
+ xfree(passphrase);
+ }
+ /* We no longer need the comment. */
+ xfree(comment);
+
+ /* Compute and send a response to the challenge. */
+ respond_to_rsa_challenge(challenge, private->rsa);
+
+ /* Destroy the private key. */
+ key_free(private);
+
+ /* We no longer need the challenge. */
+ BN_clear_free(challenge);
+
+ /* Wait for response from the server. */
+ type = packet_read(&plen);
+ if (type == SSH_SMSG_SUCCESS) {
+ debug("RSA authentication accepted by server.");
+ return 1;
+ }
+ if (type != SSH_SMSG_FAILURE)
+ packet_disconnect("Protocol error waiting RSA auth response: %d", type);
+ debug("RSA authentication refused.");
+ return 0;
+}
+
+/*
+ * Tries to authenticate the user using combined rhosts or /etc/hosts.equiv
+ * authentication and RSA host authentication.
+ */
+int
+try_rhosts_rsa_authentication(const char *local_user, RSA * host_key)
+{
+ int type;
+ BIGNUM *challenge;
+ int plen, clen;
+
+ debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication.");
+
+ /* Tell the server that we are willing to authenticate using this key. */
+ packet_start(SSH_CMSG_AUTH_RHOSTS_RSA);
+ packet_put_string(local_user, strlen(local_user));
+ packet_put_int(BN_num_bits(host_key->n));
+ packet_put_bignum(host_key->e);
+ packet_put_bignum(host_key->n);
+ packet_send();
+ packet_write_wait();
+
+ /* Wait for server's response. */
+ type = packet_read(&plen);
+
+ /* The server responds with failure if it doesn't admit our
+ .rhosts authentication or doesn't know our host key. */
+ if (type == SSH_SMSG_FAILURE) {
+ debug("Server refused our rhosts authentication or host key.");
+ return 0;
+ }
+ /* Otherwise, the server should respond with a challenge. */
+ if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)
+ packet_disconnect("Protocol error during RSA authentication: %d", type);
+
+ /* Get the challenge from the packet. */
+ challenge = BN_new();
+ packet_get_bignum(challenge, &clen);
+
+ packet_integrity_check(plen, clen, type);
+
+ debug("Received RSA challenge for host key from server.");
+
+ /* Compute a response to the challenge. */
+ respond_to_rsa_challenge(challenge, host_key);
+
+ /* We no longer need the challenge. */
+ BN_clear_free(challenge);
+
+ /* Wait for response from the server. */
+ type = packet_read(&plen);
+ if (type == SSH_SMSG_SUCCESS) {
+ debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server.");
+ return 1;
+ }
+ if (type != SSH_SMSG_FAILURE)
+ packet_disconnect("Protocol error waiting RSA auth response: %d", type);
+ debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused.");
+ return 0;
+}
+
+#ifdef KRB4
+int
+try_kerberos_authentication()
+{
+ KTEXT_ST auth; /* Kerberos data */
+ char *reply;
+ char inst[INST_SZ];
+ char *realm;
+ CREDENTIALS cred;
+ int r, type, plen;
+ socklen_t slen;
+ Key_schedule schedule;
+ u_long checksum, cksum;
+ MSG_DAT msg_data;
+ struct sockaddr_in local, foreign;
+ struct stat st;
+
+ /* Don't do anything if we don't have any tickets. */
+ if (stat(tkt_string(), &st) < 0)
+ return 0;
+
+ strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ);
+
+ realm = (char *) krb_realmofhost(get_canonical_hostname());
+ if (!realm) {
+ debug("Kerberos V4: no realm for %s", get_canonical_hostname());
+ return 0;
+ }
+ /* This can really be anything. */
+ checksum = (u_long) getpid();
+
+ r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum);
+ if (r != KSUCCESS) {
+ debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]);
+ return 0;
+ }
+ /* Get session key to decrypt the server's reply with. */
+ r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred);
+ if (r != KSUCCESS) {
+ debug("get_cred failed: %s", krb_err_txt[r]);
+ return 0;
+ }
+ des_key_sched((des_cblock *) cred.session, schedule);
+
+ /* Send authentication info to server. */
+ packet_start(SSH_CMSG_AUTH_KERBEROS);
+ packet_put_string((char *) auth.dat, auth.length);
+ packet_send();
+ packet_write_wait();
+
+ /* Zero the buffer. */
+ (void) memset(auth.dat, 0, MAX_KTXT_LEN);
+
+ slen = sizeof(local);
+ memset(&local, 0, sizeof(local));
+ if (getsockname(packet_get_connection_in(),
+ (struct sockaddr *) & local, &slen) < 0)
+ debug("getsockname failed: %s", strerror(errno));
+
+ slen = sizeof(foreign);
+ memset(&foreign, 0, sizeof(foreign));
+ if (getpeername(packet_get_connection_in(),
+ (struct sockaddr *) & foreign, &slen) < 0) {
+ debug("getpeername failed: %s", strerror(errno));
+ fatal_cleanup();
+ }
+ /* Get server reply. */
+ type = packet_read(&plen);
+ switch (type) {
+ case SSH_SMSG_FAILURE:
+ /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */
+ debug("Kerberos V4 authentication failed.");
+ return 0;
+ break;
+
+ case SSH_SMSG_AUTH_KERBEROS_RESPONSE:
+ /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */
+ debug("Kerberos V4 authentication accepted.");
+
+ /* Get server's response. */
+ reply = packet_get_string((unsigned int *) &auth.length);
+ memcpy(auth.dat, reply, auth.length);
+ xfree(reply);
+
+ packet_integrity_check(plen, 4 + auth.length, type);
+
+ /*
+ * If his response isn't properly encrypted with the session
+ * key, and the decrypted checksum fails to match, he's
+ * bogus. Bail out.
+ */
+ r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session,
+ &foreign, &local, &msg_data);
+ if (r != KSUCCESS) {
+ debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]);
+ packet_disconnect("Kerberos V4 challenge failed!");
+ }
+ /* Fetch the (incremented) checksum that we supplied in the request. */
+ (void) memcpy((char *) &cksum, (char *) msg_data.app_data, sizeof(cksum));
+ cksum = ntohl(cksum);
+
+ /* If it matches, we're golden. */
+ if (cksum == checksum + 1) {
+ debug("Kerberos V4 challenge successful.");
+ return 1;
+ } else
+ packet_disconnect("Kerberos V4 challenge failed!");
+ break;
+
+ default:
+ packet_disconnect("Protocol error on Kerberos V4 response: %d", type);
+ }
+ return 0;
+}
+
+#endif /* KRB4 */
+
+#ifdef AFS
+int
+send_kerberos_tgt()
+{
+ CREDENTIALS *creds;
+ char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ];
+ int r, type, plen;
+ char buffer[8192];
+ struct stat st;
+
+ /* Don't do anything if we don't have any tickets. */
+ if (stat(tkt_string(), &st) < 0)
+ return 0;
+
+ creds = xmalloc(sizeof(*creds));
+
+ if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) {
+ debug("Kerberos V4 tf_fullname failed: %s", krb_err_txt[r]);
+ return 0;
+ }
+ if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) {
+ debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]);
+ return 0;
+ }
+ if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) {
+ debug("Kerberos V4 ticket expired: %s", TKT_FILE);
+ return 0;
+ }
+ creds_to_radix(creds, (unsigned char *)buffer);
+ 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 <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <openssl/md5.h>
+#include <openssl/dh.h>
+#include <openssl/hmac.h>
+
+#include "ssh.h"
+#include "xmalloc.h"
+#include "rsa.h"
+#include "buffer.h"
+#include "packet.h"
+#include "cipher.h"
+#include "uidswap.h"
+#include "compat.h"
+#include "readconf.h"
+#include "bufaux.h"
+#include "ssh2.h"
+#include "kex.h"
+#include "myproposal.h"
+#include "key.h"
+#include "dsa.h"
+#include "sshconnect.h"
+#include "authfile.h"
+
+/* import */
+extern char *client_version_string;
+extern char *server_version_string;
+extern Options options;
+
+/*
+ * SSH2 key exchange
+ */
+
+unsigned char *session_id2 = NULL;
+int session_id2_len = 0;
+
+void
+ssh_kex2(char *host, struct sockaddr *hostaddr)
+{
+ Kex *kex;
+ char *cprop[PROPOSAL_MAX];
+ char *sprop[PROPOSAL_MAX];
+ Buffer *client_kexinit;
+ Buffer *server_kexinit;
+ int payload_len, dlen;
+ unsigned int klen, kout;
+ char *ptr;
+ char *signature = NULL;
+ unsigned int slen;
+ char *server_host_key_blob = NULL;
+ Key *server_host_key;
+ unsigned int sbloblen;
+ DH *dh;
+ BIGNUM *dh_server_pub = 0;
+ BIGNUM *shared_secret = 0;
+ int i;
+ unsigned char *kbuf;
+ unsigned char *hash;
+
+/* KEXINIT */
+
+ debug("Sending KEX init.");
+ if (options.ciphers != NULL) {
+ myproposal[PROPOSAL_ENC_ALGS_CTOS] =
+ myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
+ } else if (
+ options.cipher == SSH_CIPHER_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 <tcpd.h>
@@ -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 <dugsong@UMICH.EDU>
+ */
+
+#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"