/* $Id: ssh_v1_auth_client.c,v 1.9 2001/02/11 03:35: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 */ #include #include #include #include "options.h" #include "sshd.h" #include "ssh_buffer.h" #include "ssh_event.h" #include "ssh_v1_messages.h" #include "ssh_v1_proto.h" #include "ssh_util.h" #include "ssh_types.h" #include "ssh_auth.h" #ifdef WITH_AUTH_RHOSTS #include "ssh_v1_auth_rhosts.h" #endif #ifdef WITH_AUTH_RSA #include "ssh_v1_auth_rsa.h" #endif #ifdef WITH_AUTH_PASSWORD #include "ssh_v1_auth_passwd.h" #endif /* * Client routines for authenticating the user. */ /* * send_user: sends the desired user name from the client to the server */ int send_user(ssh_context_t * context, int msg_type) { FUNC_DECL(send_user); int ret, l; struct ssh_event ev; struct ssh_buf *buf = NULL; struct ssh_ev_send *evsend = NULL; SSH_DLOG(5, ("start\n")); if (msg_type != SSH_V1_SMSG_SUCCESS) { if (msg_type == SSH_V1_SMSG_FAILURE) { SSH_DLOG(1, ("send_user: server didn't like our session key\n")); EVT_V1_DISCONNECT(ev, context, "Session key no good eh?\n"); } else { SSH_DLOG(1, ("send_user: this server is confusing\n")); EVT_V1_DISCONNECT(ev, context, "Where are you from?\n"); } ret = RET_FATAL; } else { #ifdef SESSION_KEY_TIMING struct timeval scratch; gettimeofday(&scratch, NULL); if ((scratch.tv_usec -= tv_start.tv_usec) < 0) { scratch.tv_usec += 1000000; scratch.tv_sec --; } scratch.tv_sec -= tv_start.tv_sec; SSH_DLOG(1, ("server acknowledges session key: %ld.%06ld secs", scratch.tv_sec, scratch.tv_usec)); #endif /* SESSION_KEY_TIMING */ ev.event_type = SSH_EVENT_SEND; evsend = (struct ssh_ev_send *) ev.event_data; SSH_DLOG(1, ("sending the username now")); buf = buf_alloc(NULL, 1 + 4 + (l = strlen(context->username))); buf_put_int8(buf, SSH_V1_CMSG_USER); buf_put_binstr(buf, context->username, l); evsend->dlen = buf_alllen(buf); memcpy(evsend->data, buf_alldata(buf), buf_alllen(buf)); buf_cleanup(buf); free(buf); ret = RET_NEXT_STATE; } 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); } SSH_DLOG(5, ("finish\n")); return (ret); } int v1_user_try_auth(ssh_context_t *context, struct ssh_buf *msg, size_t size, int msg_type) { FUNC_DECL(v1_user_try_auth); struct ssh_event ev; struct ssh_ev_send *evsend = NULL; const char *method; struct ssh_buf *repl; int retval; /* success is caught in the caller */ if (msg_type != SSH_V1_SMSG_FAILURE && msg_type != SSH_V1_SMSG_AUTH_RSA_CHALLENGE) { SSH_ERROR("what's this? %d", msg_type); ssh_sys_exit(1); } if (msg_type == SSH_V1_SMSG_AUTH_RSA_CHALLENGE) return v1_auth_rsa_make_response(context, msg, size); if (msg_type == SSH_V1_SMSG_AUTH_TIS_CHALLENGE || msg_type == SSH_V1_SMSG_AUTH_KERBEROS_RESPONSE) { return RET_FAIL; } /* * part 1) a switch takes care of notifications, and advancing the * state. * part 2) a series of if statements takes care of advancing past * states we don't need (eg, compression or a tty). * part 3) another switch generates messages to request the things * we do need. */ /* part 1 -- what's next */ switch (context->v1_ctx.auth_tries) { case 0: SSH_DLOG(3, ("authentication cycle initiated")); context->v1_ctx.auth_tries = SSH_AUTH_RHOSTS; break; case SSH_AUTH_RHOSTS: if (context->client->Verbosity) fprintf(stderr, "%s: %s authentication failed.\r\n", context->client->localhost, auth_name(context->v1_ctx.auth_tries)); context->v1_ctx.auth_tries = SSH_AUTH_RSA; break; case SSH_AUTH_RSA: if (context->v1_ctx.authagent_session == NULL) { if (context->client->Verbosity) fprintf(stderr, "%s: %s authentication failed.\r\n", context->client->localhost, auth_name(context->v1_ctx.auth_tries)); context->v1_ctx.auth_tries = SSH_AUTH_PASSWORD; if (context->v1_ctx.userkey != NULL) ssh_rsa_free(&context->v1_ctx.userkey); } else SSH_DLOG(3, ("last rsa failed, trying next one")); break; case SSH_AUTH_PASSWORD: if (--context->client->NumberOfPasswordPrompts <= 0) { if (context->client->Verbosity) fprintf(stderr, "%s: %s authentication failed.\r\n", context->client->localhost, auth_name(context->v1_ctx.auth_tries)); context->v1_ctx.auth_tries = SSH_AUTH_RHOSTS_RSA; } break; case SSH_AUTH_RHOSTS_RSA: /* we can't advance any further */ if (context->client->Verbosity) fprintf(stderr, "%s: %s authentication failed.\r\n", context->client->localhost, auth_name(context->v1_ctx.auth_tries)); return RET_FAIL; break; } /* part 2 -- must support and be willing to try */ if (context->v1_ctx.auth_tries == SSH_AUTH_RHOSTS && (auth_is_supported(context, context->v1_ctx.auth_tries) == 0 || context->client->RhostsAuthentication == 0)) context->v1_ctx.auth_tries = SSH_AUTH_RSA; if (context->v1_ctx.auth_tries == SSH_AUTH_RSA && (auth_is_supported(context, context->v1_ctx.auth_tries) == 0 || context->client->RSAAuthentication == 0)) context->v1_ctx.auth_tries = SSH_AUTH_PASSWORD; if (context->v1_ctx.auth_tries == SSH_AUTH_PASSWORD && (auth_is_supported(context, context->v1_ctx.auth_tries) == 0 || context->client->PasswordAuthentication == 0)) context->v1_ctx.auth_tries = SSH_AUTH_RHOSTS_RSA; if (context->v1_ctx.auth_tries == SSH_AUTH_RHOSTS_RSA && (auth_is_supported(context, context->v1_ctx.auth_tries) == 0 || context->client->PasswordAuthentication == 0)) return (RET_FAIL); ev.event_type = SSH_EVENT_SEND; evsend = (struct ssh_ev_send *) ev.event_data; /* part 3 -- how do we do this anyway? */ switch (context->v1_ctx.auth_tries) { case SSH_AUTH_RHOSTS: method = "Rhosts"; retval = v1_user_try_auth_rhosts(context, &repl, size, msg_type, &ev); if (retval != RET_SKIP_STATE) break; break; case SSH_AUTH_RSA: method = "RSA"; retval = v1_user_try_auth_rsa(context, &repl, size, msg_type, &ev); if (retval != RET_SKIP_STATE) break; /* FALLTHROUGH */ case SSH_AUTH_PASSWORD: method = "Password"; retval = v1_user_try_auth_passwd(context, &repl, size, msg_type, &ev); if (retval != RET_SKIP_STATE) break; break; case SSH_AUTH_RHOSTS_RSA: method = "Rhosts/RSA"; EVT_BUILD_SEND(ev, SSH_V1_CMSG_AUTH_RHOSTS_RSA); retval = RET_OK; default: SSH_DLOG(3, ("Unknown authentication method %d\n", context->v1_ctx.auth_tries)); method = ""; retval = RET_FAIL; break; } switch (retval) { case RET_OK: SSH_DLOG(4, ("%s authentication attempted...let's see", method)); /* we got this far with no problem, pack it up for shipping */ memcpy(&evsend->data[evsend->dlen], buf_alldata(repl), buf_alllen(repl)); evsend->dlen += buf_alllen(repl); buf_cleanup(repl); free(repl); break; case RET_NEXT_STATE: SSH_DLOG(4, ("%s authentication succeeded\n", method)); break; case RET_FAIL: SSH_DLOG(2, ("all authentications failed\n", method)); if (context->disconnect_msg == NULL) context->disconnect_msg = "I can't get in!"; EVT_V1_DISCONNECT(ev, context, context->disconnect_msg); break; case RET_FATAL: SSH_DLOG(2, ("%s returned fatal error\n", method)); if (context->disconnect_msg == NULL) context->disconnect_msg = "Unknown FATAL error!"; EVT_V1_DISCONNECT(ev, context, context->disconnect_msg); break; default: SSH_ERROR("Invalid authentication return value"); ssh_exit(context, 1, EXIT_NOW); break; } 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 (retval); }