/* $Id: ssh_v1_auth_rsa_client.c,v 1.4.2.1 2001/02/11 05:40:16 tls Exp $ */ /* * Copyright (c) 2001 Jason R. Thorpe. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright 1999 RedBack Networks, Incorporated. * All rights reserved. * * This software is not in the public domain. It is distributed * under the terms of the license in the file LICENSE in the * same directory as this file. If you have received a copy of this * software without the LICENSE file (which means that whoever gave * you this software violated its license) you may obtain a copy from * http://www.panix.com/~tls/LICENSE.txt */ /* * SSHv1 RSA authentication: client-side routines. */ #include "options.h" #ifdef WITH_AUTH_RSA #include #include #include #include "sshd.h" #include "ssh_buffer.h" #include "ssh_event.h" #include "ssh_types.h" #include "ssh_util.h" #include "ssh_authagent.h" #include "ssh_parse.h" #include "ssh_ui.h" #include "crypto/ssh_auth.h" #include "crypto/ssh_rsakeys.h" #include "ssh_v1_messages.h" #include "ssh_v1_auth_rsa.h" int v1_user_try_auth_rsa(ssh_context_t *context, struct ssh_buf **bufp, size_t size, int msg_type, struct ssh_event *evp) { struct ssh_mpint mpi; ssh_RSA *key; struct ssh_buf *buf; char *keyname; size_t l; /* * If we are currently referencing a key, destroy it. */ if (context->v1_ctx.userkey != NULL) ssh_rsa_free(&context->v1_ctx.userkey); /* * If this is our first time through, authagent_session * will be NULL. Try to open a session with the * authentication agent, and get some keys. */ if (context->v1_ctx.authagent_session == NULL) { if ((context->v1_ctx.authagent_session = ssh_authagent_opensession(context)) == NULL) { if (context->client->Verbosity) fprintf(stderr, "%s: Unable to contact authentication " "agent for %s.\r\n", context->client->localhost, auth_name(context->v1_ctx.auth_tries)); goto try_rsa_keyfile; } if (ssh_authagent_v1_getcount(context, context->v1_ctx.authagent_session) <= 0) { SSH_ERROR("Unable to get number of " "identities from auth agent.\n"); ssh_authagent_closesession(context, context->v1_ctx.authagent_session); context->v1_ctx.authagent_session = NULL; goto try_rsa_keyfile; } } /* * Try and get a key from the agent. */ if (ssh_authagent_v1_getkey(context, context->v1_ctx.authagent_session, &key, &keyname) < 0) { void *keydata; off_t keysize; /* * No more keys available. Close the authentication * agent session. */ ssh_authagent_closesession(context, context->v1_ctx.authagent_session); context->v1_ctx.authagent_session = NULL; try_rsa_keyfile: keydata = ssh_sys_readin(context->client->PublicIdentityFile, &keysize); if (keydata == NULL) { /* Just skip to the next type. */ if (context->client->Verbosity) fprintf(stderr, "%s: unable to open public key " "file %s\r\n", context->client->localhost, context->client->PublicIdentityFile); return (RET_SKIP_STATE); } if (decode_public_keyfile(context, keydata, keysize, &key, &keyname) != 0) { /* Just skip to the next type. */ ssh_sys_readrelease(keydata, keysize); if (context->client->Verbosity) fprintf(stderr, "%s: unable to decode public key " "file %s\r\n", context->client->localhost, context->client->PublicIdentityFile); return (RET_SKIP_STATE); } ssh_sys_readrelease(keydata, keysize); } else { /* * Remember this key for the reply. */ context->v1_ctx.userkey = key; } if (context->client->Verbosity) fprintf(stderr, "%s: Doing %s authentication with key '%s'.\r\n", context->client->localhost, auth_name(context->v1_ctx.auth_tries), keyname); free(keyname); bignum_to_mpint(ssh_rsa_npart(key), &mpi); buf = buf_alloc(NULL, 4 + (l = bignum_num_bytes(ssh_rsa_npart(key)))); buf_put_mpint(buf, &mpi); EVT_BUILD_SEND(*evp, SSH_V1_CMSG_AUTH_RSA); *bufp = buf; /* * If we're using the keyfile, free the key; we'll read * it from the keyfile again later anyway. */ if (context->v1_ctx.userkey == NULL) ssh_rsa_free(&key); return (RET_OK); } int v1_auth_rsa_make_response(ssh_context_t *context, struct ssh_buf *msg, size_t msg_size) { FUNC_DECL(v1_auth_rsa_make_response); struct ssh_buf *buf; struct ssh_event ev; struct ssh_ev_send *evsend = NULL; ssh_BIGNUM *challenge_as_bn = NULL; const char *vmsg = NULL; char response[16]; if (context->client->Verbosity) fprintf(stderr, "%s: Received RSA challenge from server.\r\n", context->client->localhost); if (buf_get_bignum(msg, &challenge_as_bn) != 0) { vmsg = "Unable to read encrypted challenge"; goto bad; } /* * If we're not using the auth agent, extract the key from * the keyfile. */ if (context->v1_ctx.authagent_session == NULL) { char *challenge = NULL, *encr_chal = NULL, *keyname = NULL; void *keydata; ssh_MD5 md5ctx; off_t keysize; int l, cl; if ((keydata = ssh_sys_readin(context->client->IdentityFile, &keysize)) == NULL) { vmsg = "Unable to read private keyfile"; goto bad; } l = decode_keyfile(context, keydata, keysize, NULL, 0, &context->v1_ctx.userkey, &keyname, NULL); switch (l) { case DECODE_KEYFILE_OK: free(keyname); ssh_sys_readrelease(keydata, keysize); break; case DECODE_KEYFILE_PASSPHRASE: { char *prompt, *passphrase; /* * Encrypted key -- if we're the client, get the * passphrase from the user. */ l = 40 + strlen(keyname) + 1; prompt = malloc(40 + strlen(keyname) + 1); snprintf(prompt, l, "Enter passphrase for RSA key '%s': ", keyname); free(keyname); (void) ssh_ui_prompt(&passphrase, prompt, 0); free(prompt); l = decode_keyfile(context, keydata, keysize, passphrase, 0, &context->v1_ctx.userkey, NULL, NULL); ssh_sys_readrelease(keydata, keysize); memset(passphrase, 0, strlen(passphrase)); free(passphrase); if (l == DECODE_KEYFILE_OK) break; else if (l == DECODE_KEYFILE_PASSPHRASE) { fprintf(stderr, "Bad passphrase.\n"); /* * We have to send a dummy response in order * to appease the server; just zero out the * response buffer and send it as normal. */ memset(response, 0, sizeof(response)); goto send_response; } vmsg = "Unable to decode private keyfile"; break; } default: free(keyname); ssh_sys_readrelease(keydata, keysize); vmsg = "Unable to decode private keyfile"; goto bad; } l = bignum_num_bytes(challenge_as_bn); encr_chal = malloc(l); if (encr_chal == NULL) { vmsg = "Unable to allocate encrypted challenge buffer"; goto bad; } challenge = malloc(l); if (challenge == NULL) { free(encr_chal); vmsg = "Unable to allocate challenge buffer"; goto bad; } if ((cl = bignum_bn2bin(challenge_as_bn, encr_chal)) == 0) { free(challenge); free(encr_chal); vmsg = "Unable to convert challenge"; goto bad; } if (ssh_rsa_private_decrypt(cl, encr_chal, challenge, context->v1_ctx.userkey) == -1) { free(challenge); free(encr_chal); vmsg = "Unable to decrypt challenge"; goto bad; } ssh_md5_init(&md5ctx); ssh_md5_update(&md5ctx, challenge, 32); ssh_md5_update(&md5ctx, context->v1_ctx.session_id, sizeof(context->v1_ctx.session_id)); ssh_md5_final(response, &md5ctx); /* Zap the cleartext challenge. */ memset(challenge, 0, l); free(challenge); free(encr_chal); } else { if (ssh_authagent_v1_rsachallenge(context, context->v1_ctx.authagent_session, context->v1_ctx.session_id, context->v1_ctx.userkey, challenge_as_bn, response) == -1) { vmsg = "Agent failed to generate response"; goto bad; } } /* * We're finished with the key -- destroy it. */ ssh_rsa_free(&context->v1_ctx.userkey); if (context->client->Verbosity) fprintf(stderr, "%s: Sending response to host key RSA " "challenge.\r\n", context->client->localhost); send_response: ev.event_type = SSH_EVENT_SEND; evsend = (struct ssh_ev_send *) ev.event_data; SSH_DLOG(1, ("sending the response now")); buf = buf_alloc(NULL, 17); buf_put_int8(buf, SSH_V1_CMSG_AUTH_RSA_RESPONSE); buf_put_nbytes(buf, 16, response); memcpy(evsend->data, buf_alldata(buf), evsend->dlen = buf_alllen(buf)); buf_cleanup(buf); free(buf); if (ssh_sys_sendevent(context, &ev) < 0) { if (context->send_pid == -1) SSH_DLOG(1, ("sendThread exited. Exiting recvThread\n")); else SSH_ERROR("Unable to send event"); ssh_exit(context, 1, EXIT_NOW); } return (RET_OK); bad: if (context->v1_ctx.userkey != NULL) ssh_rsa_free(&context->v1_ctx.userkey); if (vmsg && context->client->Verbosity) fprintf(stderr, "%s: %s\r\n", context->client->localhost, vmsg); return (RET_FAIL); } #endif /* #ifdef WITH_AUTH_RSA */