Index: head/crypto/openssh/rsa.c =================================================================== --- head/crypto/openssh/rsa.c (revision 72396) +++ head/crypto/openssh/rsa.c (revision 72397) @@ -1,195 +1,196 @@ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * * Copyright (c) 1999 Niels Provos. 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. * * 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. * * * Description of the RSA algorithm can be found e.g. from the following * sources: * * Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1994. * * Jennifer Seberry and Josed Pieprzyk: Cryptography: An Introduction to * Computer Security. Prentice-Hall, 1989. * * Man Young Rhee: Cryptography and Secure Data Communications. McGraw-Hill, * 1994. * * R. Rivest, A. Shamir, and L. M. Adleman: Cryptographic Communications * System and Method. US Patent 4,405,829, 1983. * * Hans Riesel: Prime Numbers and Computer Methods for Factorization. * Birkhauser, 1994. * * The RSA Frequently Asked Questions document by RSA Data Security, * Inc., 1995. * * RSA in 3 lines of perl by Adam Back , 1995, as * included below: * * [gone - had to be deleted - what a pity] */ #include "includes.h" RCSID("$OpenBSD: rsa.c,v 1.16 2000/09/07 20:27:53 deraadt Exp $"); RCSID("$FreeBSD$"); #include "rsa.h" #include "ssh.h" #include "xmalloc.h" int rsa_verbose = 1; int rsa_alive() { RSA *key; key = RSA_generate_key(32, 3, NULL, NULL); if (key == NULL) return (0); RSA_free(key); return (1); } /* * Generates RSA public and private keys. This initializes the data * structures; they should be freed with rsa_clear_private_key and * rsa_clear_public_key. */ void rsa_generate_key(RSA *prv, RSA *pub, unsigned int bits) { RSA *key; if (rsa_verbose) { printf("Generating RSA keys: "); fflush(stdout); } key = RSA_generate_key(bits, 35, NULL, NULL); if (key == NULL) fatal("rsa_generate_key: key generation failed."); /* Copy public key parameters */ pub->n = BN_new(); BN_copy(pub->n, key->n); pub->e = BN_new(); BN_copy(pub->e, key->e); /* Copy private key parameters */ prv->n = BN_new(); BN_copy(prv->n, key->n); prv->e = BN_new(); BN_copy(prv->e, key->e); prv->d = BN_new(); BN_copy(prv->d, key->d); prv->p = BN_new(); BN_copy(prv->p, key->p); prv->q = BN_new(); BN_copy(prv->q, key->q); prv->dmp1 = BN_new(); BN_copy(prv->dmp1, key->dmp1); prv->dmq1 = BN_new(); BN_copy(prv->dmq1, key->dmq1); prv->iqmp = BN_new(); BN_copy(prv->iqmp, key->iqmp); RSA_free(key); if (rsa_verbose) printf("Key generation complete.\n"); } void rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA *key) { unsigned char *inbuf, *outbuf; int len, ilen, olen; if (BN_num_bits(key->e) < 2 || !BN_is_odd(key->e)) fatal("rsa_public_encrypt() exponent too small or not odd"); olen = BN_num_bytes(key->n); outbuf = xmalloc(olen); ilen = BN_num_bytes(in); inbuf = xmalloc(ilen); BN_bn2bin(in, inbuf); if ((len = RSA_public_encrypt(ilen, inbuf, outbuf, key, RSA_PKCS1_PADDING)) <= 0) fatal("rsa_public_encrypt() failed."); BN_bin2bn(outbuf, len, out); memset(outbuf, 0, olen); memset(inbuf, 0, ilen); xfree(outbuf); xfree(inbuf); } -void +int rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *key) { unsigned char *inbuf, *outbuf; int len, ilen, olen; olen = BN_num_bytes(key->n); outbuf = xmalloc(olen); ilen = BN_num_bytes(in); inbuf = xmalloc(ilen); BN_bn2bin(in, inbuf); if ((len = RSA_private_decrypt(ilen, inbuf, outbuf, key, - RSA_PKCS1_PADDING)) <= 0) - fatal("rsa_private_decrypt() failed."); - - BN_bin2bn(outbuf, len, out); - + RSA_PKCS1_PADDING)) <= 0) { + error("rsa_private_decrypt() failed"); + } else { + BN_bin2bn(outbuf, len, out); + } memset(outbuf, 0, olen); memset(inbuf, 0, ilen); xfree(outbuf); xfree(inbuf); + return len; } /* Set whether to output verbose messages during key generation. */ void rsa_set_verbose(int verbose) { rsa_verbose = verbose; } Index: head/crypto/openssh/rsa.h =================================================================== --- head/crypto/openssh/rsa.h (revision 72396) +++ head/crypto/openssh/rsa.h (revision 72397) @@ -1,37 +1,37 @@ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * RSA key generation, encryption and decryption. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ /* RCSID("$OpenBSD: rsa.h,v 1.8 2000/09/07 20:27:53 deraadt Exp $"); */ /* $FreeBSD$ */ #ifndef RSA_H #define RSA_H #include #include /* Calls SSL RSA_generate_key, only copies to prv and pub */ void rsa_generate_key(RSA * prv, RSA * pub, unsigned int bits); /* * Indicates whether the rsa module is permitted to show messages on the * terminal. */ void rsa_set_verbose __P((int verbose)); int rsa_alive __P((void)); void rsa_public_encrypt __P((BIGNUM * out, BIGNUM * in, RSA * prv)); -void rsa_private_decrypt __P((BIGNUM * out, BIGNUM * in, RSA * prv)); +int rsa_private_decrypt __P((BIGNUM * out, BIGNUM * in, RSA * prv)); #endif /* RSA_H */ Index: head/crypto/openssh/ssh-agent.c =================================================================== --- head/crypto/openssh/ssh-agent.c (revision 72396) +++ head/crypto/openssh/ssh-agent.c (revision 72397) @@ -1,824 +1,825 @@ /* $OpenBSD: ssh-agent.c,v 1.37 2000/09/21 11:07:51 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * The authentication agent program. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 implementation, * 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. * * 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: ssh-agent.c,v 1.37 2000/09/21 11:07:51 markus Exp $"); RCSID("$FreeBSD$"); #include "ssh.h" #include "rsa.h" #include "buffer.h" #include "bufaux.h" #include "xmalloc.h" #include "packet.h" #include "getput.h" #include "mpaux.h" #include #include #include #include #include "key.h" #include "authfd.h" #include "dsa.h" #include "kex.h" #include "compat.h" typedef struct { int fd; enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } type; Buffer input; Buffer output; } SocketEntry; unsigned int sockets_alloc = 0; SocketEntry *sockets = NULL; typedef struct { Key *key; char *comment; } Identity; typedef struct { int nentries; Identity *identities; } Idtab; /* private key table, one per protocol version */ Idtab idtable[3]; int max_fd = 0; /* pid of shell == parent of agent */ pid_t parent_pid = -1; /* pathname and directory for AUTH_SOCKET */ char socket_name[1024]; char socket_dir[1024]; extern char *__progname; void idtab_init(void) { int i; for (i = 0; i <=2; i++){ idtable[i].identities = NULL; idtable[i].nentries = 0; } } /* return private key table for requested protocol version */ Idtab * idtab_lookup(int version) { if (version < 1 || version > 2) fatal("internal error, bad protocol version %d", version); return &idtable[version]; } /* return matching private key for given public key */ Key * lookup_private_key(Key *key, int *idx, int version) { int i; Idtab *tab = idtab_lookup(version); for (i = 0; i < tab->nentries; i++) { if (key_equal(key, tab->identities[i].key)) { if (idx != NULL) *idx = i; return tab->identities[i].key; } } return NULL; } /* send list of supported public keys to 'client' */ void process_request_identities(SocketEntry *e, int version) { Idtab *tab = idtab_lookup(version); Buffer msg; int i; buffer_init(&msg); buffer_put_char(&msg, (version == 1) ? SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER); buffer_put_int(&msg, tab->nentries); for (i = 0; i < tab->nentries; i++) { Identity *id = &tab->identities[i]; if (id->key->type == KEY_RSA) { buffer_put_int(&msg, BN_num_bits(id->key->rsa->n)); buffer_put_bignum(&msg, id->key->rsa->e); buffer_put_bignum(&msg, id->key->rsa->n); } else { unsigned char *blob; unsigned int blen; dsa_make_key_blob(id->key, &blob, &blen); buffer_put_string(&msg, blob, blen); xfree(blob); } buffer_put_cstring(&msg, id->comment); } buffer_put_int(&e->output, buffer_len(&msg)); buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); buffer_free(&msg); } /* ssh1 only */ void process_authentication_challenge1(SocketEntry *e) { Key *key, *private; BIGNUM *challenge; int i, len; Buffer msg; MD5_CTX md; unsigned char buf[32], mdbuf[16], session_id[16]; unsigned int response_type; buffer_init(&msg); key = key_new(KEY_RSA); challenge = BN_new(); buffer_get_int(&e->input); /* ignored */ buffer_get_bignum(&e->input, key->rsa->e); buffer_get_bignum(&e->input, key->rsa->n); buffer_get_bignum(&e->input, challenge); /* Only protocol 1.1 is supported */ if (buffer_len(&e->input) == 0) goto failure; buffer_get(&e->input, (char *) session_id, 16); response_type = buffer_get_int(&e->input); if (response_type != 1) goto failure; private = lookup_private_key(key, NULL, 1); if (private != NULL) { /* Decrypt the challenge using the private key. */ - rsa_private_decrypt(challenge, challenge, private->rsa); + if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0) + goto failure; /* The response is MD5 of decrypted challenge plus session id. */ len = BN_num_bytes(challenge); if (len <= 0 || len > 32) { log("process_authentication_challenge: bad challenge length %d", len); goto failure; } memset(buf, 0, 32); BN_bn2bin(challenge, buf + 32 - len); MD5_Init(&md); MD5_Update(&md, buf, 32); MD5_Update(&md, session_id, 16); MD5_Final(mdbuf, &md); /* Send the response. */ buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE); for (i = 0; i < 16; i++) buffer_put_char(&msg, mdbuf[i]); goto send; } failure: /* Unknown identity or protocol error. Send failure. */ buffer_put_char(&msg, SSH_AGENT_FAILURE); send: buffer_put_int(&e->output, buffer_len(&msg)); buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); key_free(key); BN_clear_free(challenge); buffer_free(&msg); } /* ssh2 only */ void process_sign_request2(SocketEntry *e) { extern int datafellows; Key *key, *private; unsigned char *blob, *data, *signature = NULL; unsigned int blen, dlen, slen = 0; int flags; Buffer msg; int ok = -1; datafellows = 0; blob = buffer_get_string(&e->input, &blen); data = buffer_get_string(&e->input, &dlen); flags = buffer_get_int(&e->input); if (flags & SSH_AGENT_OLD_SIGNATURE) datafellows = SSH_BUG_SIGBLOB; key = dsa_key_from_blob(blob, blen); if (key != NULL) { private = lookup_private_key(key, NULL, 2); if (private != NULL) ok = dsa_sign(private, &signature, &slen, data, dlen); } key_free(key); buffer_init(&msg); if (ok == 0) { buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE); buffer_put_string(&msg, signature, slen); } else { buffer_put_char(&msg, SSH_AGENT_FAILURE); } buffer_put_int(&e->output, buffer_len(&msg)); buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); buffer_free(&msg); xfree(data); xfree(blob); if (signature != NULL) xfree(signature); } /* shared */ void process_remove_identity(SocketEntry *e, int version) { Key *key = NULL, *private; unsigned char *blob; unsigned int blen; unsigned int bits; int success = 0; switch(version){ case 1: key = key_new(KEY_RSA); bits = buffer_get_int(&e->input); buffer_get_bignum(&e->input, key->rsa->e); buffer_get_bignum(&e->input, key->rsa->n); if (bits != key_size(key)) log("Warning: identity keysize mismatch: actual %d, announced %d", key_size(key), bits); break; case 2: blob = buffer_get_string(&e->input, &blen); key = dsa_key_from_blob(blob, blen); xfree(blob); break; } if (key != NULL) { int idx; private = lookup_private_key(key, &idx, version); if (private != NULL) { /* * We have this key. Free the old key. Since we * don\'t want to leave empty slots in the middle of * the array, we actually free the key there and copy * data from the last entry. */ Idtab *tab = idtab_lookup(version); key_free(tab->identities[idx].key); xfree(tab->identities[idx].comment); if (idx != tab->nentries) tab->identities[idx] = tab->identities[tab->nentries]; tab->nentries--; success = 1; } key_free(key); } buffer_put_int(&e->output, 1); buffer_put_char(&e->output, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); } void process_remove_all_identities(SocketEntry *e, int version) { unsigned int i; Idtab *tab = idtab_lookup(version); /* Loop over all identities and clear the keys. */ for (i = 0; i < tab->nentries; i++) { key_free(tab->identities[i].key); xfree(tab->identities[i].comment); } /* Mark that there are no identities. */ tab->nentries = 0; /* Send success. */ buffer_put_int(&e->output, 1); buffer_put_char(&e->output, SSH_AGENT_SUCCESS); return; } void process_add_identity(SocketEntry *e, int version) { Key *k = NULL; RSA *rsa; BIGNUM *aux; BN_CTX *ctx; char *type; char *comment; int success = 0; Idtab *tab = idtab_lookup(version); switch (version) { case 1: k = key_new(KEY_RSA); rsa = k->rsa; /* allocate mem for private key */ /* XXX rsa->n and rsa->e are already allocated */ rsa->d = BN_new(); rsa->iqmp = BN_new(); rsa->q = BN_new(); rsa->p = BN_new(); rsa->dmq1 = BN_new(); rsa->dmp1 = BN_new(); buffer_get_int(&e->input); /* ignored */ buffer_get_bignum(&e->input, rsa->n); buffer_get_bignum(&e->input, rsa->e); buffer_get_bignum(&e->input, rsa->d); buffer_get_bignum(&e->input, rsa->iqmp); /* SSH and SSL have p and q swapped */ buffer_get_bignum(&e->input, rsa->q); /* p */ buffer_get_bignum(&e->input, rsa->p); /* q */ /* Generate additional parameters */ aux = BN_new(); ctx = BN_CTX_new(); BN_sub(aux, rsa->q, BN_value_one()); BN_mod(rsa->dmq1, rsa->d, aux, ctx); BN_sub(aux, rsa->p, BN_value_one()); BN_mod(rsa->dmp1, rsa->d, aux, ctx); BN_clear_free(aux); BN_CTX_free(ctx); break; case 2: type = buffer_get_string(&e->input, NULL); if (strcmp(type, KEX_DSS)) { buffer_clear(&e->input); xfree(type); goto send; } xfree(type); k = key_new(KEY_DSA); /* allocate mem for private key */ k->dsa->priv_key = BN_new(); buffer_get_bignum2(&e->input, k->dsa->p); buffer_get_bignum2(&e->input, k->dsa->q); buffer_get_bignum2(&e->input, k->dsa->g); buffer_get_bignum2(&e->input, k->dsa->pub_key); buffer_get_bignum2(&e->input, k->dsa->priv_key); break; } comment = buffer_get_string(&e->input, NULL); if (k == NULL) { xfree(comment); goto send; } success = 1; if (lookup_private_key(k, NULL, version) == NULL) { if (tab->nentries == 0) tab->identities = xmalloc(sizeof(Identity)); else tab->identities = xrealloc(tab->identities, (tab->nentries + 1) * sizeof(Identity)); tab->identities[tab->nentries].key = k; tab->identities[tab->nentries].comment = comment; /* Increment the number of identities. */ tab->nentries++; } else { key_free(k); xfree(comment); } send: buffer_put_int(&e->output, 1); buffer_put_char(&e->output, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); } /* dispatch incoming messages */ void process_message(SocketEntry *e) { unsigned int msg_len; unsigned int type; unsigned char *cp; if (buffer_len(&e->input) < 5) return; /* Incomplete message. */ cp = (unsigned char *) buffer_ptr(&e->input); msg_len = GET_32BIT(cp); if (msg_len > 256 * 1024) { shutdown(e->fd, SHUT_RDWR); close(e->fd); e->type = AUTH_UNUSED; return; } if (buffer_len(&e->input) < msg_len + 4) return; buffer_consume(&e->input, 4); type = buffer_get_char(&e->input); switch (type) { /* ssh1 */ case SSH_AGENTC_RSA_CHALLENGE: process_authentication_challenge1(e); break; case SSH_AGENTC_REQUEST_RSA_IDENTITIES: process_request_identities(e, 1); break; case SSH_AGENTC_ADD_RSA_IDENTITY: process_add_identity(e, 1); break; case SSH_AGENTC_REMOVE_RSA_IDENTITY: process_remove_identity(e, 1); break; case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: process_remove_all_identities(e, 1); break; /* ssh2 */ case SSH2_AGENTC_SIGN_REQUEST: process_sign_request2(e); break; case SSH2_AGENTC_REQUEST_IDENTITIES: process_request_identities(e, 2); break; case SSH2_AGENTC_ADD_IDENTITY: process_add_identity(e, 2); break; case SSH2_AGENTC_REMOVE_IDENTITY: process_remove_identity(e, 2); break; case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: process_remove_all_identities(e, 2); break; default: /* Unknown message. Respond with failure. */ error("Unknown message %d", type); buffer_clear(&e->input); buffer_put_int(&e->output, 1); buffer_put_char(&e->output, SSH_AGENT_FAILURE); break; } } void new_socket(int type, int fd) { unsigned int i, old_alloc; if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) error("fcntl O_NONBLOCK: %s", strerror(errno)); if (fd > max_fd) max_fd = fd; for (i = 0; i < sockets_alloc; i++) if (sockets[i].type == AUTH_UNUSED) { sockets[i].fd = fd; sockets[i].type = type; buffer_init(&sockets[i].input); buffer_init(&sockets[i].output); return; } old_alloc = sockets_alloc; sockets_alloc += 10; if (sockets) sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0])); else sockets = xmalloc(sockets_alloc * sizeof(sockets[0])); for (i = old_alloc; i < sockets_alloc; i++) sockets[i].type = AUTH_UNUSED; sockets[old_alloc].type = type; sockets[old_alloc].fd = fd; buffer_init(&sockets[old_alloc].input); buffer_init(&sockets[old_alloc].output); } void prepare_select(fd_set *readset, fd_set *writeset) { unsigned int i; for (i = 0; i < sockets_alloc; i++) switch (sockets[i].type) { case AUTH_SOCKET: case AUTH_CONNECTION: FD_SET(sockets[i].fd, readset); if (buffer_len(&sockets[i].output) > 0) FD_SET(sockets[i].fd, writeset); break; case AUTH_UNUSED: break; default: fatal("Unknown socket type %d", sockets[i].type); break; } } void after_select(fd_set *readset, fd_set *writeset) { unsigned int i; int len, sock; socklen_t slen; char buf[1024]; struct sockaddr_un sunaddr; for (i = 0; i < sockets_alloc; i++) switch (sockets[i].type) { case AUTH_UNUSED: break; case AUTH_SOCKET: if (FD_ISSET(sockets[i].fd, readset)) { slen = sizeof(sunaddr); sock = accept(sockets[i].fd, (struct sockaddr *) & sunaddr, &slen); if (sock < 0) { perror("accept from AUTH_SOCKET"); break; } new_socket(AUTH_CONNECTION, sock); } break; case AUTH_CONNECTION: if (buffer_len(&sockets[i].output) > 0 && FD_ISSET(sockets[i].fd, writeset)) { len = write(sockets[i].fd, buffer_ptr(&sockets[i].output), buffer_len(&sockets[i].output)); if (len <= 0) { shutdown(sockets[i].fd, SHUT_RDWR); close(sockets[i].fd); sockets[i].type = AUTH_UNUSED; buffer_free(&sockets[i].input); buffer_free(&sockets[i].output); break; } buffer_consume(&sockets[i].output, len); } if (FD_ISSET(sockets[i].fd, readset)) { len = read(sockets[i].fd, buf, sizeof(buf)); if (len <= 0) { shutdown(sockets[i].fd, SHUT_RDWR); close(sockets[i].fd); sockets[i].type = AUTH_UNUSED; buffer_free(&sockets[i].input); buffer_free(&sockets[i].output); break; } buffer_append(&sockets[i].input, buf, len); process_message(&sockets[i]); } break; default: fatal("Unknown type %d", sockets[i].type); } } void check_parent_exists(int sig) { if (parent_pid != -1 && kill(parent_pid, 0) < 0) { /* printf("Parent has died - Authentication agent exiting.\n"); */ exit(1); } signal(SIGALRM, check_parent_exists); alarm(10); } void cleanup_socket(void) { remove(socket_name); rmdir(socket_dir); } void cleanup_exit(int i) { cleanup_socket(); exit(i); } void usage() { fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION); fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n", __progname); exit(1); } int main(int ac, char **av) { fd_set readset, writeset; int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch; struct sockaddr_un sunaddr; pid_t pid; char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid]; /* check if RSA support exists */ if (rsa_alive() == 0) { fprintf(stderr, "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", __progname); exit(1); } while ((ch = getopt(ac, av, "cks")) != -1) { switch (ch) { case 'c': if (s_flag) usage(); c_flag++; break; case 'k': k_flag++; break; case 's': if (c_flag) usage(); s_flag++; break; default: usage(); } } ac -= optind; av += optind; if (ac > 0 && (c_flag || k_flag || s_flag)) usage(); if (ac == 0 && !c_flag && !k_flag && !s_flag) { shell = getenv("SHELL"); if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0) c_flag = 1; } if (k_flag) { pidstr = getenv(SSH_AGENTPID_ENV_NAME); if (pidstr == NULL) { fprintf(stderr, "%s not set, cannot kill agent\n", SSH_AGENTPID_ENV_NAME); exit(1); } pid = atoi(pidstr); if (pid < 1) { /* XXX PID_MAX check too */ /* Yes, PID_MAX check please */ fprintf(stderr, "%s=\"%s\", which is not a good PID\n", SSH_AGENTPID_ENV_NAME, pidstr); exit(1); } if (kill(pid, SIGTERM) == -1) { perror("kill"); exit(1); } format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; printf(format, SSH_AUTHSOCKET_ENV_NAME); printf(format, SSH_AGENTPID_ENV_NAME); printf("echo Agent pid %d killed;\n", pid); exit(0); } parent_pid = getpid(); /* Create private directory for agent socket */ strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir); if (mkdtemp(socket_dir) == NULL) { perror("mkdtemp: private socket dir"); exit(1); } snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir, parent_pid); /* * Create socket early so it will exist before command gets run from * the parent. */ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); cleanup_exit(1); } memset(&sunaddr, 0, sizeof(sunaddr)); sunaddr.sun_family = AF_UNIX; strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); sunaddr.sun_len = SUN_LEN(&sunaddr) + 1; if (bind(sock, (struct sockaddr *)&sunaddr, sunaddr.sun_len) < 0) { perror("bind"); cleanup_exit(1); } if (listen(sock, 5) < 0) { perror("listen"); cleanup_exit(1); } /* * Fork, and have the parent execute the command, if any, or present * the socket data. The child continues as the authentication agent. */ pid = fork(); if (pid == -1) { perror("fork"); exit(1); } if (pid != 0) { /* Parent - execute the given command. */ close(sock); snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid); if (ac == 0) { format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, SSH_AUTHSOCKET_ENV_NAME); printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, SSH_AGENTPID_ENV_NAME); printf("echo Agent pid %d;\n", pid); exit(0); } if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 || setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) { perror("setenv"); exit(1); } execvp(av[0], av); perror(av[0]); exit(1); } close(0); close(1); close(2); if (setsid() == -1) { perror("setsid"); cleanup_exit(1); } if (atexit(cleanup_socket) < 0) { perror("atexit"); cleanup_exit(1); } new_socket(AUTH_SOCKET, sock); if (ac > 0) { signal(SIGALRM, check_parent_exists); alarm(10); } idtab_init(); signal(SIGINT, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, cleanup_exit); signal(SIGTERM, cleanup_exit); while (1) { FD_ZERO(&readset); FD_ZERO(&writeset); prepare_select(&readset, &writeset); if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0) { if (errno == EINTR) continue; exit(1); } after_select(&readset, &writeset); } /* NOTREACHED */ } Index: head/crypto/openssh/sshconnect1.c =================================================================== --- head/crypto/openssh/sshconnect1.c (revision 72396) +++ head/crypto/openssh/sshconnect1.c (revision 72397) @@ -1,1049 +1,1052 @@ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Code to connect to a remote host, and to perform the client side of the * login (authentication) dialog. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" RCSID("$OpenBSD: sshconnect1.c,v 1.8 2000/10/12 09:59:19 markus Exp $"); RCSID("$FreeBSD$"); #include #include #include #include #include "xmalloc.h" #include "rsa.h" #include "ssh.h" #include "buffer.h" #include "packet.h" #include "mpaux.h" #include "uidswap.h" #include "readconf.h" #include "key.h" #include "authfd.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 type; char *comment; AuthenticationConnection *auth; unsigned char response[16]; unsigned int i; int plen, clen; Key *key; BIGNUM *challenge; /* Get connection to the agent. */ auth = ssh_get_authentication_connection(); if (!auth) return 0; challenge = BN_new(); key = key_new(KEY_RSA); /* Loop through identities served by the agent. */ for (key = ssh_get_first_identity(auth, &comment, 1); key != NULL; key = ssh_get_next_identity(auth, &comment, 1)) { /* 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(key->rsa->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."); key_free(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, key, 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)); } key_free(key); 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) { BN_clear_free(challenge); debug("RSA authentication accepted by server."); 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(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); + /* XXX think about Bleichenbacher, too */ + if (rsa_private_decrypt(challenge, challenge, prv) <= 0) + packet_disconnect( + "respond_to_rsa_challenge: rsa_private_decrypt failed"); /* 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); + 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_krb4_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_KRB4); 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_KRB4_FAILURE */ debug("Kerberos V4 authentication failed."); return 0; break; case SSH_SMSG_AUTH_KRB4_RESPONSE: /* SSH_SMSG_AUTH_KRB4_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_krb4_tgt() { CREDENTIALS *creds; char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; int r, type, plen; char buffer[8192]; struct stat st; /* Don't do anything if we don't have any tickets. */ if (stat(tkt_string(), &st) < 0) return 0; creds = xmalloc(sizeof(*creds)); if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) { debug("Kerberos V4 tf_fullname failed: %s", krb_err_txt[r]); return 0; } if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) { debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]); return 0; } if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) { debug("Kerberos V4 ticket expired: %s", TKT_FILE); return 0; } creds_to_radix(creds, (unsigned char *)buffer, sizeof buffer); xfree(creds); packet_start(SSH_CMSG_HAVE_KRB4_TGT); packet_put_string(buffer, strlen(buffer)); packet_send(); packet_write_wait(); type = packet_read(&plen); if (type == SSH_SMSG_FAILURE) debug("Kerberos TGT for realm %s rejected.", prealm); else if (type != SSH_SMSG_SUCCESS) packet_disconnect("Protocol error on Kerberos TGT response: %d", type); return 1; } void send_afs_tokens(void) { CREDENTIALS creds; struct ViceIoctl parms; struct ClearToken ct; int i, type, len, plen; char buf[2048], *p, *server_cell; char buffer[8192]; /* Move over ktc_GetToken, here's something leaner. */ for (i = 0; i < 100; i++) { /* just in case */ parms.in = (char *) &i; parms.in_size = sizeof(i); parms.out = buf; parms.out_size = sizeof(buf); if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) break; p = buf; /* Get secret token. */ memcpy(&creds.ticket_st.length, p, sizeof(unsigned int)); if (creds.ticket_st.length > MAX_KTXT_LEN) break; p += sizeof(unsigned int); memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); p += creds.ticket_st.length; /* Get clear token. */ memcpy(&len, p, sizeof(len)); if (len != sizeof(struct ClearToken)) break; p += sizeof(len); memcpy(&ct, p, len); p += len; p += sizeof(len); /* primary flag */ server_cell = p; /* Flesh out our credentials. */ strlcpy(creds.service, "afs", sizeof creds.service); creds.instance[0] = '\0'; strlcpy(creds.realm, server_cell, REALM_SZ); memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ); creds.issue_date = ct.BeginTimestamp; creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp); creds.kvno = ct.AuthHandle; snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId); creds.pinst[0] = '\0'; /* Encode token, ship it off. */ if (creds_to_radix(&creds, (unsigned char*) buffer, sizeof buffer) <= 0) break; packet_start(SSH_CMSG_HAVE_AFS_TOKEN); packet_put_string(buffer, strlen(buffer)); packet_send(); packet_write_wait(); /* Roger, Roger. Clearance, Clarence. What's your vector, Victor? */ type = packet_read(&plen); if (type == SSH_SMSG_FAILURE) debug("AFS token for cell %s rejected.", server_cell); else if (type != SSH_SMSG_SUCCESS) packet_disconnect("Protocol error on AFS token response: %d", type); } } #endif /* AFS */ /* * Tries to authenticate with any string-based challenge/response system. * Note that the client code is not tied to s/key or TIS. */ int try_skey_authentication() { int type, i; int payload_len; unsigned int clen; char *challenge, *response; debug("Doing skey authentication."); /* request a challenge */ packet_start(SSH_CMSG_AUTH_TIS); packet_send(); packet_write_wait(); type = packet_read(&payload_len); if (type != SSH_SMSG_FAILURE && type != SSH_SMSG_AUTH_TIS_CHALLENGE) { packet_disconnect("Protocol error: got %d in response " "to skey-auth", type); } if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) { debug("No challenge for skey authentication."); return 0; } challenge = packet_get_string(&clen); packet_integrity_check(payload_len, (4 + clen), type); if (options.cipher == SSH_CIPHER_NONE) log("WARNING: Encryption is disabled! " "Reponse will be transmitted in clear text."); fprintf(stderr, "%s\n", challenge); xfree(challenge); fflush(stderr); for (i = 0; i < options.number_of_password_prompts; i++) { if (i != 0) error("Permission denied, please try again."); response = read_passphrase("Response: ", 0); packet_start(SSH_CMSG_AUTH_TIS_RESPONSE); packet_put_string(response, strlen(response)); memset(response, 0, strlen(response)); xfree(response); packet_send(); packet_write_wait(); type = packet_read(&payload_len); if (type == SSH_SMSG_SUCCESS) return 1; if (type != SSH_SMSG_FAILURE) packet_disconnect("Protocol error: got %d in response " "to skey-auth-reponse", type); } /* failure */ return 0; } /* * Tries to authenticate with plain passwd authentication. */ int try_password_authentication(char *prompt) { int type, i, payload_len; char *password; debug("Doing password authentication."); if (options.cipher == SSH_CIPHER_NONE) log("WARNING: Encryption is disabled! Password will be transmitted in clear text."); for (i = 0; i < options.number_of_password_prompts; i++) { if (i != 0) error("Permission denied, please try again."); password = read_passphrase(prompt, 0); packet_start(SSH_CMSG_AUTH_PASSWORD); packet_put_string(password, strlen(password)); memset(password, 0, strlen(password)); xfree(password); packet_send(); packet_write_wait(); type = packet_read(&payload_len); if (type == SSH_SMSG_SUCCESS) return 1; if (type != SSH_SMSG_FAILURE) packet_disconnect("Protocol error: got %d in response to passwd auth", type); } /* failure */ return 0; } /* * SSH1 key exchange */ void ssh_kex(char *host, struct sockaddr *hostaddr) { int i; BIGNUM *key; RSA *host_key; RSA *public_key; Key k; int bits, rbits; int ssh_cipher_default = SSH_CIPHER_3DES; unsigned char session_key[SSH_SESSION_KEY_LENGTH]; unsigned char cookie[8]; unsigned int supported_ciphers; unsigned int server_flags, client_flags; int payload_len, clen, sum_len = 0; u_int32_t rand = 0; debug("Waiting for server public key."); /* Wait for a public key packet from the server. */ packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY); /* Get cookie from the packet. */ for (i = 0; i < 8; i++) cookie[i] = packet_get_char(); /* Get the public key. */ public_key = RSA_new(); bits = packet_get_int();/* bits */ public_key->e = BN_new(); packet_get_bignum(public_key->e, &clen); sum_len += clen; public_key->n = BN_new(); packet_get_bignum(public_key->n, &clen); sum_len += clen; rbits = BN_num_bits(public_key->n); if (bits != rbits) { log("Warning: Server lies about size of server public key: " "actual size is %d bits vs. announced %d.", rbits, bits); log("Warning: This may be due to an old implementation of ssh."); } /* Get the host key. */ host_key = RSA_new(); bits = packet_get_int();/* bits */ host_key->e = BN_new(); packet_get_bignum(host_key->e, &clen); sum_len += clen; host_key->n = BN_new(); packet_get_bignum(host_key->n, &clen); sum_len += clen; rbits = BN_num_bits(host_key->n); if (bits != rbits) { log("Warning: Server lies about size of server host key: " "actual size is %d bits vs. announced %d.", rbits, bits); log("Warning: This may be due to an old implementation of ssh."); } /* Get protocol flags. */ server_flags = packet_get_int(); packet_set_protocol_flags(server_flags); supported_ciphers = packet_get_int(); supported_authentications = packet_get_int(); debug("Received server public key (%d bits) and host key (%d bits).", BN_num_bits(public_key->n), BN_num_bits(host_key->n)); packet_integrity_check(payload_len, 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, SSH_SMSG_PUBLIC_KEY); k.type = KEY_RSA; k.rsa = host_key; check_host_key(host, hostaddr, &k, options.user_hostfile, options.system_hostfile); client_flags = SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN; compute_session_id(session_id, cookie, host_key->n, public_key->n); /* Generate a session key. */ arc4random_stir(); /* * Generate an encryption key for the session. The key is a 256 bit * random number, interpreted as a 32-byte key, with the least * significant 8 bits being the first byte of the key. */ for (i = 0; i < 32; i++) { if (i % 4 == 0) rand = arc4random(); session_key[i] = rand & 0xff; rand >>= 8; } /* * According to the protocol spec, the first byte of the session key * is the highest byte of the integer. The session key is xored with * the first 16 bytes of the session id. */ key = BN_new(); BN_set_word(key, 0); for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { BN_lshift(key, key, 8); if (i < 16) BN_add_word(key, session_key[i] ^ session_id[i]); else BN_add_word(key, session_key[i]); } /* * Encrypt the integer using the public key and host key of the * server (key with smaller modulus first). */ if (BN_cmp(public_key->n, host_key->n) < 0) { /* Public key has smaller modulus. */ if (BN_num_bits(host_key->n) < BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED) { fatal("respond_to_rsa_challenge: host_key %d < public_key %d + " "SSH_KEY_BITS_RESERVED %d", BN_num_bits(host_key->n), BN_num_bits(public_key->n), SSH_KEY_BITS_RESERVED); } rsa_public_encrypt(key, key, public_key); rsa_public_encrypt(key, key, host_key); } else { /* Host key has smaller modulus (or they are equal). */ if (BN_num_bits(public_key->n) < BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED) { fatal("respond_to_rsa_challenge: public_key %d < host_key %d + " "SSH_KEY_BITS_RESERVED %d", BN_num_bits(public_key->n), BN_num_bits(host_key->n), SSH_KEY_BITS_RESERVED); } rsa_public_encrypt(key, key, host_key); rsa_public_encrypt(key, key, public_key); } /* Destroy the public keys since we no longer need them. */ RSA_free(public_key); RSA_free(host_key); if (options.cipher == SSH_CIPHER_ILLEGAL) { log("No valid SSH1 cipher, using %.100s instead.", cipher_name(ssh_cipher_default)); options.cipher = ssh_cipher_default; } else if (options.cipher == SSH_CIPHER_NOT_SET) { if (cipher_mask_ssh1(1) & supported_ciphers & (1 << ssh_cipher_default)) options.cipher = ssh_cipher_default; } /* 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_KRB4_TGT)) && options.krb4_tgt_passing) { if (options.cipher == SSH_CIPHER_NONE) log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!"); (void) send_krb4_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_KRB4)) && options.krb4_authentication) { debug("Trying Kerberos authentication."); if (try_krb4_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 */ #ifdef KRB5 if ((supported_authentications & (1 << SSH_AUTH_KRB5)) && options.krb5_authentication){ krb5_context ssh_context = NULL; krb5_auth_context auth_context = NULL; debug("Trying Kerberos V5 authentication."); if (try_krb5_authentication(&ssh_context, &auth_context)) { type = packet_read(&payload_len); if (type == SSH_SMSG_SUCCESS) { if ((supported_authentications & (1 << SSH_PASS_KRB5_TGT)) && options.krb5_tgt_passing) { if (options.cipher == SSH_CIPHER_NONE) log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!"); send_krb5_tgt(ssh_context, auth_context); } krb5_auth_con_free(ssh_context, auth_context); krb5_free_context(ssh_context); return; } if (type != SSH_SMSG_FAILURE) packet_disconnect("Protocol error: got %d in response to Kerberos5 auth", type); } } #endif /* KRB5 */ /* * 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 */ } Index: head/crypto/openssh/sshd.c =================================================================== --- head/crypto/openssh/sshd.c (revision 72396) +++ head/crypto/openssh/sshd.c (revision 72397) @@ -1,1625 +1,1645 @@ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * This program is the ssh daemon. It listens for connections from clients, * and performs authentication, executes use commands or shell, and forwards * information to/from the application to the user client over an encrypted * connection. This can also handle forwarding of X11, TCP/IP, and * authentication agent connections. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 implementation: * * 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. * * 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: sshd.c,v 1.132 2000/10/13 18:34:46 markus Exp $"); RCSID("$FreeBSD$"); #include "xmalloc.h" #include "rsa.h" #include "ssh.h" #include "pty.h" #include "packet.h" #include "mpaux.h" #include "servconf.h" #include "uidswap.h" #include "compat.h" #include "buffer.h" #include #include #include "ssh2.h" #include #include #include #include "kex.h" #include #include #include "key.h" #include "dsa.h" #include "dh.h" #include "auth.h" #include "myproposal.h" #include "authfile.h" #ifdef LIBWRAP #include #include int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; #endif /* LIBWRAP */ #ifndef O_NOCTTY #define O_NOCTTY 0 #endif #ifdef KRB5 #include #endif /* KRB5 */ /* Server configuration options. */ ServerOptions options; /* Name of the server configuration file. */ char *config_file_name = SERVER_CONFIG_FILE; /* * Flag indicating whether IPv4 or IPv6. This can be set on the command line. * Default value is AF_UNSPEC means both IPv4 and IPv6. */ int IPv4or6 = AF_UNSPEC; /* * Debug mode flag. This can be set on the command line. If debug * mode is enabled, extra debugging output will be sent to the system * log, the daemon will not go to background, and will exit after processing * the first connection. */ int debug_flag = 0; /* Flag indicating that the daemon is being started from inetd. */ int inetd_flag = 0; /* debug goes to stderr unless inetd_flag is set */ int log_stderr = 0; /* argv[0] without path. */ char *av0; /* Saved arguments to main(). */ char **saved_argv; /* * The sockets that the server is listening; this is used in the SIGHUP * signal handler. */ #define MAX_LISTEN_SOCKS 16 int listen_socks[MAX_LISTEN_SOCKS]; int num_listen_socks = 0; /* * the client's version string, passed by sshd2 in compat mode. if != NULL, * sshd will skip the version-number exchange */ char *client_version_string = NULL; char *server_version_string = NULL; /* * Any really sensitive data in the application is contained in this * structure. The idea is that this structure could be locked into memory so * that the pages do not get written into swap. However, there are some * problems. The private key contains BIGNUMs, and we do not (in principle) * have access to the internals of them, and locking just the structure is * not very useful. Currently, memory locking is not implemented. */ struct { 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; /* * Flag indicating whether the current session key has been used. This flag * is set whenever the key is used, and cleared when the key is regenerated. */ int key_used = 0; /* This is set to true when SIGHUP is received. */ int received_sighup = 0; /* Public side of the server key. This value is regenerated regularly with the private key. */ 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; /* record remote hostname or ip */ unsigned int utmp_len = MAXHOSTNAMELEN; /* Prototypes for various functions defined later in this file. */ void do_ssh1_kex(); void do_ssh2_kex(); void ssh_dh1_server(Kex *, Buffer *_kexinit, Buffer *); void ssh_dhgex_server(Kex *, Buffer *_kexinit, Buffer *); /* * Close all listening sockets */ void close_listen_socks(void) { int i; for (i = 0; i < num_listen_socks; i++) close(listen_socks[i]); num_listen_socks = -1; } /* * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; * the effect is to reread the configuration file (and to regenerate * the server key). */ void sighup_handler(int sig) { received_sighup = 1; signal(SIGHUP, sighup_handler); } /* * Called from the main program after receiving SIGHUP. * Restarts the server. */ void sighup_restart() { log("Received SIGHUP; restarting."); close_listen_socks(); execv(saved_argv[0], saved_argv); execv("/proc/curproc/file", saved_argv); log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno)); exit(1); } /* * Generic signal handler for terminating signals in the master daemon. * These close the listen socket; not closing it seems to cause "Address * already in use" problems on some machines, which is inconvenient. */ void sigterm_handler(int sig) { log("Received signal %d; terminating.", sig); close_listen_socks(); unlink(options.pid_file); exit(255); } /* * SIGCHLD handler. This is called whenever a child dies. This will then * reap any zombies left by exited c. */ void main_sigchld_handler(int sig) { int save_errno = errno; int status; while (waitpid(-1, &status, WNOHANG) > 0) ; signal(SIGCHLD, main_sigchld_handler); errno = save_errno; } /* * Signal handler for the alarm after the login grace period has expired. */ void grace_alarm_handler(int sig) { /* Close the connection. */ packet_close(); /* Log error and exit. */ fatal("Timeout before authentication for %s.", get_remote_ipaddr()); } /* * Signal handler for the key regeneration alarm. Note that this * alarm only occurs in the daemon waiting for connections, and it does not * do anything with the private key or random state before forking. * Thus there should be no concurrency control/asynchronous execution * problems. */ /* XXX do we really want this work to be done in a signal handler ? -m */ void key_regeneration_alarm(int sig) { int save_errno = errno; /* Check if we should generate a new key. */ if (key_used) { /* This should really be done in the background. */ log("Generating new %d bit RSA key.", options.server_key_bits); if (sensitive_data.private_key != NULL) RSA_free(sensitive_data.private_key); sensitive_data.private_key = RSA_new(); if (public_key != NULL) RSA_free(public_key); public_key = RSA_new(); rsa_generate_key(sensitive_data.private_key, public_key, options.server_key_bits); arc4random_stir(); key_used = 0; log("RSA key generation complete."); } /* Reschedule the alarm. */ signal(SIGALRM, key_regeneration_alarm); alarm(options.key_regeneration_time); errno = save_errno; } void sshd_exchange_identification(int sock_in, int sock_out) { int i, mismatch; int remote_major, remote_minor; int major, minor; char *s; char buf[256]; /* Must not be larger than remote_version. */ char remote_version[256]; /* Must be at least as big as buf. */ if ((options.protocol & SSH_PROTO_1) && (options.protocol & SSH_PROTO_2)) { major = PROTOCOL_MAJOR_1; minor = 99; } else if (options.protocol & SSH_PROTO_2) { major = PROTOCOL_MAJOR_2; minor = PROTOCOL_MINOR_2; } else { major = PROTOCOL_MAJOR_1; minor = PROTOCOL_MINOR_1; } snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION); server_version_string = xstrdup(buf); if (client_version_string == NULL) { /* Send our protocol version identification. */ if (atomicio(write, sock_out, server_version_string, strlen(server_version_string)) != strlen(server_version_string)) { log("Could not write ident string to %s.", get_remote_ipaddr()); fatal_cleanup(); } /* Read other side\'s version identification. */ for (i = 0; i < sizeof(buf) - 1; i++) { if (atomicio(read, sock_in, &buf[i], 1) != 1) { log("Did not receive ident string from %s.", get_remote_ipaddr()); fatal_cleanup(); } if (buf[i] == '\r') { buf[i] = '\n'; buf[i + 1] = 0; /* Kludge for F-Secure Macintosh < 1.0.2 */ if (i == 12 && strncmp(buf, "SSH-1.5-W1.0", 12) == 0) break; continue; } if (buf[i] == '\n') { /* buf[i] == '\n' */ buf[i + 1] = 0; break; } } buf[sizeof(buf) - 1] = 0; client_version_string = xstrdup(buf); } /* * Check that the versions match. In future this might accept * several versions and set appropriate flags to handle them. */ if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) != 3) { s = "Protocol mismatch.\n"; (void) atomicio(write, sock_out, s, strlen(s)); close(sock_in); close(sock_out); log("Bad protocol version identification '%.100s' from %s", client_version_string, get_remote_ipaddr()); fatal_cleanup(); } debug("Client protocol version %d.%d; client software version %.100s", remote_major, remote_minor, remote_version); compat_datafellows(remote_version); 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; } if (remote_minor < 3) { packet_disconnect("Your ssh version is too old and " "is no longer supported. Please install a newer version."); } else if (remote_minor == 3) { /* note that this disables agent-forwarding */ enable_compat13(); } break; case 2: if (options.protocol & SSH_PROTO_2) { enable_compat20(); break; } /* FALLTHROUGH */ default: mismatch = 1; break; } chop(server_version_string); chop(client_version_string); debug("Local version string %.200s", server_version_string); if (mismatch) { s = "Protocol major versions differ.\n"; (void) atomicio(write, sock_out, s, strlen(s)); close(sock_in); close(sock_out); log("Protocol major versions differ for %s: %.200s vs. %.200s", get_remote_ipaddr(), 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. */ if (public_key) RSA_free(public_key); if (sensitive_data.private_key) RSA_free(sensitive_data.private_key); if (sensitive_data.host_key) RSA_free(sensitive_data.host_key); if (sensitive_data.dsa_host_key != NULL) key_free(sensitive_data.dsa_host_key); } /* * returns 1 if connection should be dropped, 0 otherwise. * dropping starts at connection #max_startups_begin with a probability * of (max_startups_rate/100). the probability increases linearly until * all connections are dropped for startups > max_startups */ int drop_connection(int startups) { double p, r; if (startups < options.max_startups_begin) return 0; if (startups >= options.max_startups) return 1; if (options.max_startups_rate == 100) return 1; p = 100 - options.max_startups_rate; p *= startups - options.max_startups_begin; p /= (double) (options.max_startups - options.max_startups_begin); p += options.max_startups_rate; p /= 100.0; r = arc4random() / (double) UINT_MAX; debug("drop_connection: p %g, r %g", p, r); return (r < p) ? 1 : 0; } int *startup_pipes = NULL; /* options.max_startup sized array of fd ints */ int startup_pipe; /* in child */ /* * Main program for the daemon. */ int main(int ac, char **av) { extern char *optarg; extern int optind; int opt, sock_in = 0, sock_out = 0, newsock, j, i, fdsetsz, on = 1; pid_t pid; socklen_t fromlen; int silent = 0; fd_set *fdset; struct sockaddr_storage from; const char *remote_ip; int remote_port; FILE *f; struct linger linger; struct addrinfo *ai; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; int listen_sock, maxfd; int startup_p[2]; int startups = 0; /* Save argv[0]. */ saved_argv = av; if (strchr(av[0], '/')) av0 = strrchr(av[0], '/') + 1; else av0 = av[0]; /* Initialize configuration options to their default values. */ initialize_server_options(&options); /* Parse command-line arguments. */ while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:diqQ46")) != EOF) { switch (opt) { case '4': IPv4or6 = AF_INET; break; case '6': IPv4or6 = AF_INET6; break; case 'f': config_file_name = optarg; break; case 'd': if (0 == debug_flag) { debug_flag = 1; options.log_level = SYSLOG_LEVEL_DEBUG1; } else if (options.log_level < SYSLOG_LEVEL_DEBUG3) { options.log_level++; } else { fprintf(stderr, "Too high debugging level.\n"); exit(1); } break; case 'i': inetd_flag = 1; break; case 'Q': silent = 1; break; case 'q': options.log_level = SYSLOG_LEVEL_QUIET; break; case 'b': options.server_key_bits = atoi(optarg); break; case 'p': options.ports_from_cmdline = 1; if (options.num_ports >= MAX_PORTS) { fprintf(stderr, "too many ports.\n"); exit(1); } options.ports[options.num_ports++] = atoi(optarg); break; case 'g': options.login_grace_time = atoi(optarg); break; case 'k': options.key_regeneration_time = atoi(optarg); break; case 'h': options.host_key_file = optarg; break; case 'V': client_version_string = optarg; /* only makes sense with inetd_flag, i.e. no listen() */ inetd_flag = 1; break; case 'u': utmp_len = atoi(optarg); break; case '?': default: fprintf(stderr, "sshd version %s\n", SSH_VERSION); fprintf(stderr, "Usage: %s [options]\n", av0); fprintf(stderr, "Options:\n"); fprintf(stderr, " -f file Configuration file (default %s)\n", SERVER_CONFIG_FILE); fprintf(stderr, " -d Debugging mode (multiple -d means more debugging)\n"); fprintf(stderr, " -i Started from inetd\n"); fprintf(stderr, " -q Quiet (no logging)\n"); fprintf(stderr, " -p port Listen on the specified port (default: 22)\n"); fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n"); fprintf(stderr, " -g seconds Grace period for authentication (default: 300)\n"); fprintf(stderr, " -b bits Size of server RSA key (default: 768 bits)\n"); fprintf(stderr, " -h file File from which to read host key (default: %s)\n", HOST_KEY_FILE); fprintf(stderr, " -u len Maximum hostname length for utmp recording\n"); fprintf(stderr, " -4 Use IPv4 only\n"); fprintf(stderr, " -6 Use IPv6 only\n"); exit(1); } } /* * Force logging to stderr until we have loaded the private host * key (unless started from inetd) */ log_init(av0, options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility, !silent && !inetd_flag); /* 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 that there are no remaining arguments. */ if (optind < ac) { fprintf(stderr, "Extra argument %s.\n", av[optind]); exit(1); } debug("sshd version %.100s", SSH_VERSION); 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.host_dsa_key_file, "", sensitive_data.dsa_host_key, NULL)) { error("Could not load DSA host key: %.200s", options.host_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); } /* 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 (!debug_flag && !inetd_flag) { #ifdef TIOCNOTTY int fd; #endif /* TIOCNOTTY */ if (daemon(0, 0) < 0) fatal("daemon() failed: %.200s", strerror(errno)); /* Disconnect from the controlling tty. */ #ifdef TIOCNOTTY fd = open("/dev/tty", O_RDWR | O_NOCTTY); if (fd >= 0) { (void) ioctl(fd, TIOCNOTTY, NULL); close(fd); } #endif /* TIOCNOTTY */ } /* Reinitialize the log (because of the fork above). */ log_init(av0, options.log_level, options.log_facility, log_stderr); /* Do not display messages to stdout in RSA code. */ rsa_set_verbose(0); /* Initialize the random number generator. */ arc4random_stir(); /* Chdir to the root directory so that the current disk can be unmounted if desired. */ chdir("/"); /* Start listening for a socket, unless started from inetd. */ if (inetd_flag) { int s1, s2; s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ s2 = dup(s1); sock_in = dup(0); sock_out = dup(1); startup_pipe = -1; /* * We intentionally do not close the descriptors 0, 1, and 2 * as our code for setting the descriptors won\'t work if * ttyfd happens to be one of those. */ debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); 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) continue; if (num_listen_socks >= MAX_LISTEN_SOCKS) fatal("Too many listen sockets. " "Enlarge MAX_LISTEN_SOCKS"); if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error("getnameinfo failed"); continue; } /* Create socket for listening. */ listen_sock = socket(ai->ai_family, SOCK_STREAM, 0); if (listen_sock < 0) { /* kernel may not support ipv6 */ verbose("socket: %.100s", strerror(errno)); continue; } if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) < 0) { error("listen_sock O_NONBLOCK: %s", strerror(errno)); close(listen_sock); continue; } /* * Set socket options. We try to make the port * reusable and have it close as fast as possible * without waiting in unnecessary wait states on * close. */ setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)); linger.l_onoff = 1; linger.l_linger = 5; setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); debug("Bind to port %s on %s.", strport, ntop); /* Bind the socket to the desired port. */ if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { error("Bind to port %s on %s failed: %.200s.", strport, ntop, strerror(errno)); close(listen_sock); continue; } listen_socks[num_listen_socks] = listen_sock; num_listen_socks++; /* Start listening on the port. */ log("Server listening on %s port %s.", ntop, strport); if (listen(listen_sock, 5) < 0) fatal("listen: %.100s", strerror(errno)); } freeaddrinfo(options.listen_addrs); if (!num_listen_socks) fatal("Cannot bind any address."); if (!debug_flag) { /* * Record our pid in /etc/sshd_pid to make it easier * to kill the correct sshd. We don\'t want to do * this before the bind above because the bind will * fail if there already is a daemon, and this will * overwrite any old pid in the file. */ f = fopen(options.pid_file, "w"); if (f) { fprintf(f, "%u\n", (unsigned int) getpid()); fclose(f); } } 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."); /* Schedule server key regeneration alarm. */ signal(SIGALRM, key_regeneration_alarm); alarm(options.key_regeneration_time); } /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ signal(SIGHUP, sighup_handler); signal(SIGTERM, sigterm_handler); signal(SIGQUIT, sigterm_handler); /* Arrange SIGCHLD to be caught. */ signal(SIGCHLD, main_sigchld_handler); /* setup fd set for listen */ fdset = NULL; maxfd = 0; for (i = 0; i < num_listen_socks; i++) if (listen_socks[i] > maxfd) maxfd = listen_socks[i]; /* pipes connected to unauthenticated childs */ startup_pipes = xmalloc(options.max_startups * sizeof(int)); for (i = 0; i < options.max_startups; i++) startup_pipes[i] = -1; /* * Stay listening for connections until the system crashes or * the daemon is killed with a signal. */ for (;;) { if (received_sighup) sighup_restart(); if (fdset != NULL) xfree(fdset); fdsetsz = howmany(maxfd, NFDBITS) * sizeof(fd_mask); fdset = (fd_set *)xmalloc(fdsetsz); memset(fdset, 0, fdsetsz); for (i = 0; i < num_listen_socks; i++) FD_SET(listen_socks[i], fdset); for (i = 0; i < options.max_startups; i++) if (startup_pipes[i] != -1) FD_SET(startup_pipes[i], fdset); /* Wait in select until there is a connection. */ if (select(maxfd + 1, fdset, NULL, NULL, NULL) < 0) { if (errno != EINTR) error("select: %.100s", strerror(errno)); continue; } for (i = 0; i < options.max_startups; i++) if (startup_pipes[i] != -1 && FD_ISSET(startup_pipes[i], fdset)) { /* * the read end of the pipe is ready * if the child has closed the pipe * after successfull authentication * or if the child has died */ close(startup_pipes[i]); startup_pipes[i] = -1; startups--; } for (i = 0; i < num_listen_socks; i++) { if (!FD_ISSET(listen_socks[i], fdset)) continue; fromlen = sizeof(from); newsock = accept(listen_socks[i], (struct sockaddr *)&from, &fromlen); if (newsock < 0) { if (errno != EINTR && errno != EWOULDBLOCK) error("accept: %.100s", strerror(errno)); continue; } if (fcntl(newsock, F_SETFL, 0) < 0) { error("newsock del O_NONBLOCK: %s", strerror(errno)); continue; } if (drop_connection(startups) == 1) { debug("drop connection #%d", startups); close(newsock); continue; } if (pipe(startup_p) == -1) { close(newsock); continue; } for (j = 0; j < options.max_startups; j++) if (startup_pipes[j] == -1) { startup_pipes[j] = startup_p[0]; if (maxfd < startup_p[0]) maxfd = startup_p[0]; startups++; break; } /* * Got connection. Fork a child to handle it, unless * we are in debugging mode. */ if (debug_flag) { /* * In debugging mode. Close the listening * socket, and start processing the * connection without forking. */ debug("Server will not fork when running in debugging mode."); close_listen_socks(); sock_in = newsock; sock_out = newsock; startup_pipe = -1; pid = getpid(); break; } else { /* * Normal production daemon. Fork, and have * the child process the connection. The * parent continues listening. */ if ((pid = fork()) == 0) { /* * Child. Close the listening and max_startup * sockets. Start using the accepted socket. * Reinitialize logging (since our pid has * changed). We break out of the loop to handle * the connection. */ startup_pipe = startup_p[1]; for (j = 0; j < options.max_startups; j++) if (startup_pipes[j] != -1) close(startup_pipes[j]); close_listen_socks(); sock_in = newsock; sock_out = newsock; log_init(av0, options.log_level, options.log_facility, log_stderr); break; } } /* Parent. Stay in the loop. */ if (pid < 0) error("fork: %.100s", strerror(errno)); else debug("Forked child %d.", pid); close(startup_p[1]); /* Mark that the key has been used (it was "given" to the child). */ key_used = 1; arc4random_stir(); /* Close the new socket (the child is now taking care of it). */ close(newsock); } /* child process check (or debug mode) */ if (num_listen_socks < 0) break; } } /* This is the child processing a new connection. */ /* * Disable the key regeneration alarm. We will not regenerate the * key since we are no longer in a position to give it to anyone. We * will not restart on SIGHUP since it no longer makes sense. */ alarm(0); signal(SIGALRM, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGPIPE, SIG_IGN); /* * Set socket options for the connection. We want the socket to * close as fast as possible without waiting for anything. If the * connection is not a socket, these will do nothing. */ /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ linger.l_onoff = 1; linger.l_linger = 5; setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); /* * Register our connection. This turns encryption off because we do * not have a key. */ packet_set_connection(sock_in, sock_out); remote_port = get_remote_port(); remote_ip = get_remote_ipaddr(); /* Check whether logins are denied from this host. */ #ifdef LIBWRAP { struct request_info req; request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); fromhost(&req); if (!hosts_access(&req)) { close(sock_in); close(sock_out); refuse(&req); } verbose("Connection from %.500s port %d", eval_client(&req), remote_port); } #endif /* LIBWRAP */ /* Log the connection. */ verbose("Connection from %.500s port %d", remote_ip, remote_port); /* * We don\'t want to listen forever unless the other side * successfully authenticates itself. So we set up an alarm which is * cleared after successful authentication. A limit of zero * indicates no limit. Note that we don\'t set the alarm in debugging * mode; it is just annoying to have the server exit just when you * are about to discover the bug. */ signal(SIGALRM, grace_alarm_handler); if (!debug_flag) alarm(options.login_grace_time); sshd_exchange_identification(sock_in, sock_out); /* * Check that the connection comes from a privileged port. Rhosts- * and Rhosts-RSA-Authentication only make sense from priviledged * programs. Of course, if the intruder has root access on his local * machine, he can connect from any port. So do not use these * authentication methods from machines that you do not trust. */ if (remote_port >= IPPORT_RESERVED || remote_port < IPPORT_RESERVED / 2) { options.rhosts_authentication = 0; options.rhosts_rsa_authentication = 0; } #ifdef KRB4 if (!packet_connection_is_ipv4() && options.krb4_authentication) { debug("Kerberos Authentication disabled, only available for IPv4."); options.krb4_authentication = 0; } #endif /* KRB4 */ packet_set_nonblocking(); /* perform the key exchange */ /* authenticate user and start session */ if (compat20) { do_ssh2_kex(); do_authentication2(); } else { do_ssh1_kex(); do_authentication(); } #ifdef KRB4 /* Cleanup user's ticket cache file. */ if (options.krb4_ticket_cleanup) (void) dest_tkt(); #endif /* KRB4 */ /* The connection has been terminated. */ verbose("Closing connection to %.100s", remote_ip); #ifdef USE_PAM finish_pam(); #endif /* USE_PAM */ packet_close(); exit(0); } /* * SSH1 key exchange */ void do_ssh1_kex() { int i, len; int plen, slen; + int rsafail = 0; BIGNUM *session_key_int; unsigned char session_key[SSH_SESSION_KEY_LENGTH]; unsigned char cookie[8]; unsigned int cipher_type, auth_mask, protocol_flags; u_int32_t rand = 0; /* * Generate check bytes that the client must send back in the user * packet in order for it to be accepted; this is used to defy ip * spoofing attacks. Note that this only works against somebody * doing IP spoofing from a remote machine; any machine on the local * network can still see outgoing packets and catch the random * cookie. This only affects rhosts authentication, and this is one * of the reasons why it is inherently insecure. */ for (i = 0; i < 8; i++) { if (i % 4 == 0) rand = arc4random(); cookie[i] = rand & 0xff; rand >>= 8; } /* * Send our public key. We include in the packet 64 bits of random * data that must be matched in the reply in order to prevent IP * spoofing. */ packet_start(SSH_SMSG_PUBLIC_KEY); for (i = 0; i < 8; i++) packet_put_char(cookie[i]); /* Store our public server RSA key. */ packet_put_int(BN_num_bits(public_key->n)); packet_put_bignum(public_key->e); packet_put_bignum(public_key->n); /* Store our public host RSA key. */ packet_put_int(BN_num_bits(sensitive_data.host_key->n)); packet_put_bignum(sensitive_data.host_key->e); packet_put_bignum(sensitive_data.host_key->n); /* Put protocol flags. */ packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); /* Declare which ciphers we support. */ packet_put_int(cipher_mask_ssh1(0)); /* Declare supported authentication types. */ auth_mask = 0; if (options.rhosts_authentication) auth_mask |= 1 << SSH_AUTH_RHOSTS; if (options.rhosts_rsa_authentication) auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; if (options.rsa_authentication) auth_mask |= 1 << SSH_AUTH_RSA; #ifdef KRB4 if (options.krb4_authentication) auth_mask |= 1 << SSH_AUTH_KRB4; #endif #ifdef KRB5 if (options.krb5_authentication) { auth_mask |= 1 << SSH_AUTH_KRB5; /* compatibility with MetaCentre ssh */ auth_mask |= 1 << SSH_AUTH_KRB4; } if (options.krb5_tgt_passing) auth_mask |= 1 << SSH_PASS_KRB5_TGT; #endif /* KRB5 */ #ifdef AFS if (options.krb4_tgt_passing) auth_mask |= 1 << SSH_PASS_KRB4_TGT; if (options.afs_token_passing) auth_mask |= 1 << SSH_PASS_AFS_TOKEN; #endif #ifdef SKEY if (options.skey_authentication == 1) auth_mask |= 1 << SSH_AUTH_TIS; #endif if (options.password_authentication) auth_mask |= 1 << SSH_AUTH_PASSWORD; packet_put_int(auth_mask); /* Send the packet and wait for it to be sent. */ packet_send(); packet_write_wait(); debug("Sent %d bit public key and %d bit host key.", BN_num_bits(public_key->n), BN_num_bits(sensitive_data.host_key->n)); /* Read clients reply (cipher type and session key). */ packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); /* Get cipher type and check whether we accept this. */ cipher_type = packet_get_char(); if (!(cipher_mask_ssh1(0) & (1 << cipher_type))) packet_disconnect("Warning: client selects unsupported cipher."); /* Get check bytes from the packet. These must match those we sent earlier with the public key packet. */ for (i = 0; i < 8; i++) if (cookie[i] != packet_get_char()) packet_disconnect("IP Spoofing check bytes do not match."); debug("Encryption type: %.200s", cipher_name(cipher_type)); /* Get the encrypted integer. */ session_key_int = BN_new(); packet_get_bignum(session_key_int, &slen); protocol_flags = packet_get_int(); packet_set_protocol_flags(protocol_flags); packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); /* * Decrypt it using our private server key and private host key (key * with larger modulus first). */ if (BN_cmp(sensitive_data.private_key->n, sensitive_data.host_key->n) > 0) { - /* Private key has bigger modulus. */ + /* Server key has bigger modulus. */ if (BN_num_bits(sensitive_data.private_key->n) < BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { fatal("do_connection: %s: private_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", get_remote_ipaddr(), BN_num_bits(sensitive_data.private_key->n), BN_num_bits(sensitive_data.host_key->n), SSH_KEY_BITS_RESERVED); } - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.private_key); - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.host_key); + if (rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.private_key) <= 0) + rsafail++; + if (rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.host_key) <= 0) + rsafail++; } else { /* Host key has bigger modulus (or they are equal). */ if (BN_num_bits(sensitive_data.host_key->n) < BN_num_bits(sensitive_data.private_key->n) + SSH_KEY_BITS_RESERVED) { fatal("do_connection: %s: host_key %d < private_key %d + SSH_KEY_BITS_RESERVED %d", get_remote_ipaddr(), BN_num_bits(sensitive_data.host_key->n), BN_num_bits(sensitive_data.private_key->n), SSH_KEY_BITS_RESERVED); } - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.host_key); - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.private_key); + if (rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.host_key) < 0) + rsafail++; + if (rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.private_key) < 0) + rsafail++; } compute_session_id(session_id, cookie, sensitive_data.host_key->n, sensitive_data.private_key->n); /* Destroy the private and public keys. They will no longer be needed. */ destroy_sensitive_data(); /* * Extract session key from the decrypted integer. The key is in the * least significant 256 bits of the integer; the first byte of the * key is in the highest bits. */ - BN_mask_bits(session_key_int, sizeof(session_key) * 8); - len = BN_num_bytes(session_key_int); - if (len < 0 || len > sizeof(session_key)) - fatal("do_connection: bad len from %s: session_key_int %d > sizeof(session_key) %d", - get_remote_ipaddr(), - len, sizeof(session_key)); - memset(session_key, 0, sizeof(session_key)); - BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); + if (!rsafail) { + BN_mask_bits(session_key_int, sizeof(session_key) * 8); + len = BN_num_bytes(session_key_int); + if (len < 0 || len > sizeof(session_key)) { + error("do_connection: bad session key len from %s: " + "session_key_int %d > sizeof(session_key) %d", + get_remote_ipaddr(), len, sizeof(session_key)); + rsafail++; + } else { + memset(session_key, 0, sizeof(session_key)); + BN_bn2bin(session_key_int, + session_key + sizeof(session_key) - len); + } + } + if (rsafail) { + log("do_connection: generating a fake encryption key"); + for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { + if (i % 4 == 0) + rand = arc4random(); + session_key[i] = rand & 0xff; + rand >>= 8; + } + } /* Destroy the decrypted integer. It is no longer needed. */ BN_clear_free(session_key_int); /* Xor the first 16 bytes of the session key with the session id. */ for (i = 0; i < 16; i++) session_key[i] ^= session_id[i]; /* Set the session key. From this on all communications will be encrypted. */ packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); /* Destroy our copy of the session key. It is no longer needed. */ memset(session_key, 0, sizeof(session_key)); debug("Received session key; encryption turned on."); /* Send an acknowledgement packet. Note that this packet is sent encrypted. */ packet_start(SSH_SMSG_SUCCESS); packet_send(); packet_write_wait(); } /* * SSH2 key exchange: diffie-hellman-group1-sha1 */ void do_ssh2_kex() { Buffer *server_kexinit; Buffer *client_kexinit; int payload_len; int i; Kex *kex; char *cprop[PROPOSAL_MAX]; /* KEXINIT */ if (options.ciphers != NULL) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; } server_kexinit = kex_init(myproposal); client_kexinit = xmalloc(sizeof(*client_kexinit)); buffer_init(client_kexinit); /* algorithm negotiation */ kex_exchange_kexinit(server_kexinit, client_kexinit, cprop); kex = kex_choose_conf(cprop, myproposal, 1); for (i = 0; i < PROPOSAL_MAX; i++) xfree(cprop[i]); switch (kex->kex_type) { case DH_GRP1_SHA1: ssh_dh1_server(kex, client_kexinit, server_kexinit); break; case DH_GEX_SHA1: ssh_dhgex_server(kex, client_kexinit, server_kexinit); break; default: fatal("Unsupported key exchange %d", kex->kex_type); } debug("send SSH2_MSG_NEWKEYS."); packet_start(SSH2_MSG_NEWKEYS); packet_send(); packet_write_wait(); debug("done: send SSH2_MSG_NEWKEYS."); debug("Wait SSH2_MSG_NEWKEYS."); packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS); debug("GOT 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."); } /* * SSH2 key exchange */ /* diffie-hellman-group1-sha1 */ void ssh_dh1_server(Kex *kex, Buffer *client_kexinit, Buffer *server_kexinit) { #ifdef DEBUG_KEXDH int i; #endif int payload_len, dlen; int slen; unsigned char *signature = NULL; unsigned char *server_host_key_blob = NULL; unsigned int sbloblen; unsigned int klen, kout; unsigned char *kbuf; unsigned char *hash; BIGNUM *shared_secret = 0; DH *dh; BIGNUM *dh_client_pub = 0; /* KEXDH */ debug("Wait SSH2_MSG_KEXDH_INIT."); packet_read_expect(&payload_len, SSH2_MSG_KEXDH_INIT); /* key, cert */ dh_client_pub = BN_new(); if (dh_client_pub == NULL) fatal("dh_client_pub == NULL"); packet_get_bignum2(dh_client_pub, &dlen); #ifdef DEBUG_KEXDH fprintf(stderr, "\ndh_client_pub= "); BN_print_fp(stderr, dh_client_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_client_pub)); #endif /* generate DH key */ dh = dh_new_group1(); /* XXX depends on 'kex' */ #ifdef DEBUG_KEXDH fprintf(stderr, "\np= "); BN_print_fp(stderr, dh->p); fprintf(stderr, "\ng= "); bn_print(dh->g); fprintf(stderr, "\npub= "); BN_print_fp(stderr, dh->pub_key); fprintf(stderr, "\n"); DHparams_print_fp(stderr, dh); #endif if (!dh_pub_is_valid(dh, dh_client_pub)) packet_disconnect("bad client public DH value"); klen = DH_size(dh); kbuf = xmalloc(klen); kout = DH_compute_key(kbuf, dh_client_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); /* 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( client_version_string, server_version_string, buffer_ptr(client_kexinit), buffer_len(client_kexinit), buffer_ptr(server_kexinit), buffer_len(server_kexinit), (char *)server_host_key_blob, sbloblen, dh_client_pub, dh->pub_key, 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 /* 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 */ /* 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); packet_put_string((char *)server_host_key_blob, sbloblen); packet_put_bignum2(dh->pub_key); /* f */ 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); packet_set_kex(kex); /* have keys, free DH */ DH_free(dh); } /* diffie-hellman-group-exchange-sha1 */ void ssh_dhgex_server(Kex *kex, Buffer *client_kexinit, Buffer *server_kexinit) { #ifdef DEBUG_KEXDH int i; #endif int payload_len, dlen; int slen, nbits; unsigned char *signature = NULL; unsigned char *server_host_key_blob = NULL; unsigned int sbloblen; unsigned int klen, kout; unsigned char *kbuf; unsigned char *hash; BIGNUM *shared_secret = 0; DH *dh; BIGNUM *dh_client_pub = 0; /* KEXDHGEX */ debug("Wait SSH2_MSG_KEX_DH_GEX_REQUEST."); packet_read_expect(&payload_len, SSH2_MSG_KEX_DH_GEX_REQUEST); nbits = packet_get_int(); dh = choose_dh(nbits); debug("Sending SSH2_MSG_KEX_DH_GEX_GROUP."); packet_start(SSH2_MSG_KEX_DH_GEX_GROUP); packet_put_bignum2(dh->p); packet_put_bignum2(dh->g); packet_send(); packet_write_wait(); debug("Wait SSH2_MSG_KEX_DH_GEX_INIT."); packet_read_expect(&payload_len, SSH2_MSG_KEX_DH_GEX_INIT); /* key, cert */ dh_client_pub = BN_new(); if (dh_client_pub == NULL) fatal("dh_client_pub == NULL"); packet_get_bignum2(dh_client_pub, &dlen); #ifdef DEBUG_KEXDH fprintf(stderr, "\ndh_client_pub= "); BN_print_fp(stderr, dh_client_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_client_pub)); #endif #ifdef DEBUG_KEXDH fprintf(stderr, "\np= "); BN_print_fp(stderr, dh->p); fprintf(stderr, "\ng= "); bn_print(dh->g); fprintf(stderr, "\npub= "); BN_print_fp(stderr, dh->pub_key); fprintf(stderr, "\n"); DHparams_print_fp(stderr, dh); #endif if (!dh_pub_is_valid(dh, dh_client_pub)) packet_disconnect("bad client public DH value"); klen = DH_size(dh); kbuf = xmalloc(klen); kout = DH_compute_key(kbuf, dh_client_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); /* 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_gex( client_version_string, server_version_string, buffer_ptr(client_kexinit), buffer_len(client_kexinit), buffer_ptr(server_kexinit), buffer_len(server_kexinit), (char *)server_host_key_blob, sbloblen, nbits, dh->p, dh->g, dh_client_pub, dh->pub_key, 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 /* 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 */ /* 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_KEX_DH_GEX_REPLY); packet_put_string((char *)server_host_key_blob, sbloblen); packet_put_bignum2(dh->pub_key); /* f */ 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); packet_set_kex(kex); /* have keys, free DH */ DH_free(dh); }