openssh/auth1.c
Damien Miller d783435315 - deraadt@cvs.openbsd.org 2006/08/03 03:34:42
[OVERVIEW atomicio.c atomicio.h auth-bsdauth.c auth-chall.c auth-krb5.c]
     [auth-options.c auth-options.h auth-passwd.c auth-rh-rsa.c auth-rhosts.c]
     [auth-rsa.c auth-skey.c auth.c auth.h auth1.c auth2-chall.c auth2-gss.c]
     [auth2-hostbased.c auth2-kbdint.c auth2-none.c auth2-passwd.c ]
     [auth2-pubkey.c auth2.c authfd.c authfd.h authfile.c bufaux.c bufbn.c]
     [buffer.c buffer.h canohost.c channels.c channels.h cipher-3des1.c]
     [cipher-bf1.c cipher-ctr.c cipher.c cleanup.c clientloop.c compat.c]
     [compress.c deattack.c dh.c dispatch.c dns.c dns.h fatal.c groupaccess.c]
     [groupaccess.h gss-genr.c gss-serv-krb5.c gss-serv.c hostfile.c kex.c]
     [kex.h kexdh.c kexdhc.c kexdhs.c kexgex.c kexgexc.c kexgexs.c key.c]
     [key.h log.c log.h mac.c match.c md-sha256.c misc.c misc.h moduli.c]
     [monitor.c monitor_fdpass.c monitor_mm.c monitor_mm.h monitor_wrap.c]
     [monitor_wrap.h msg.c nchan.c packet.c progressmeter.c readconf.c]
     [readconf.h readpass.c rsa.c scard.c scard.h scp.c servconf.c servconf.h]
     [serverloop.c session.c session.h sftp-client.c sftp-common.c]
     [sftp-common.h sftp-glob.c sftp-server.c sftp.c ssh-add.c ssh-agent.c]
     [ssh-dss.c ssh-gss.h ssh-keygen.c ssh-keyscan.c ssh-keysign.c ssh-rsa.c]
     [ssh.c ssh.h sshconnect.c sshconnect.h sshconnect1.c sshconnect2.c]
     [sshd.c sshlogin.c sshlogin.h sshpty.c sshpty.h sshtty.c ttymodes.c]
     [uidswap.c uidswap.h uuencode.c uuencode.h xmalloc.c xmalloc.h]
     [loginrec.c loginrec.h openbsd-compat/port-aix.c openbsd-compat/port-tun.h]
     almost entirely get rid of the culture of ".h files that include .h files"
     ok djm, sort of ok stevesk
     makes the pain stop in one easy step
     NB. portable commit contains everything *except* removing includes.h, as
     that will take a fair bit more work as we move headers that are required
     for portability workarounds to defines.h. (also, this step wasn't "easy")
2006-08-05 12:39:39 +10:00

442 lines
10 KiB
C

/* $OpenBSD: auth1.c,v 1.70 2006/08/03 03:34:41 deraadt Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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".
*/
#include "includes.h"
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include "xmalloc.h"
#include "rsa.h"
#include "ssh1.h"
#include "packet.h"
#include "buffer.h"
#include "log.h"
#include "servconf.h"
#include "compat.h"
#include "key.h"
#include "hostfile.h"
#include "auth.h"
#include "channels.h"
#include "session.h"
#include "uidswap.h"
#ifdef GSSAPI
#include "ssh-gss.h"
#endif
#include "monitor_wrap.h"
#include "buffer.h"
/* import */
extern ServerOptions options;
extern Buffer loginmsg;
static int auth1_process_password(Authctxt *, char *, size_t);
static int auth1_process_rsa(Authctxt *, char *, size_t);
static int auth1_process_rhosts_rsa(Authctxt *, char *, size_t);
static int auth1_process_tis_challenge(Authctxt *, char *, size_t);
static int auth1_process_tis_response(Authctxt *, char *, size_t);
static char *client_user = NULL; /* Used to fill in remote user for PAM */
struct AuthMethod1 {
int type;
char *name;
int *enabled;
int (*method)(Authctxt *, char *, size_t);
};
const struct AuthMethod1 auth1_methods[] = {
{
SSH_CMSG_AUTH_PASSWORD, "password",
&options.password_authentication, auth1_process_password
},
{
SSH_CMSG_AUTH_RSA, "rsa",
&options.rsa_authentication, auth1_process_rsa
},
{
SSH_CMSG_AUTH_RHOSTS_RSA, "rhosts-rsa",
&options.rhosts_rsa_authentication, auth1_process_rhosts_rsa
},
{
SSH_CMSG_AUTH_TIS, "challenge-response",
&options.challenge_response_authentication,
auth1_process_tis_challenge
},
{
SSH_CMSG_AUTH_TIS_RESPONSE, "challenge-response",
&options.challenge_response_authentication,
auth1_process_tis_response
},
{ -1, NULL, NULL, NULL}
};
static const struct AuthMethod1
*lookup_authmethod1(int type)
{
int i;
for (i = 0; auth1_methods[i].name != NULL; i++)
if (auth1_methods[i].type == type)
return (&(auth1_methods[i]));
return (NULL);
}
static char *
get_authname(int type)
{
const struct AuthMethod1 *a;
static char buf[64];
if ((a = lookup_authmethod1(type)) != NULL)
return (a->name);
snprintf(buf, sizeof(buf), "bad-auth-msg-%d", type);
return (buf);
}
/*ARGSUSED*/
static int
auth1_process_password(Authctxt *authctxt, char *info, size_t infolen)
{
int authenticated = 0;
char *password;
u_int dlen;
/*
* Read user password. It is in plain text, but was
* transmitted over the encrypted channel so it is
* not visible to an outside observer.
*/
password = packet_get_string(&dlen);
packet_check_eom();
/* Try authentication with the password. */
authenticated = PRIVSEP(auth_password(authctxt, password));
memset(password, 0, dlen);
xfree(password);
return (authenticated);
}
/*ARGSUSED*/
static int
auth1_process_rsa(Authctxt *authctxt, char *info, size_t infolen)
{
int authenticated = 0;
BIGNUM *n;
/* RSA authentication requested. */
if ((n = BN_new()) == NULL)
fatal("do_authloop: BN_new failed");
packet_get_bignum(n);
packet_check_eom();
authenticated = auth_rsa(authctxt, n);
BN_clear_free(n);
return (authenticated);
}
/*ARGSUSED*/
static int
auth1_process_rhosts_rsa(Authctxt *authctxt, char *info, size_t infolen)
{
int keybits, authenticated = 0;
u_int bits;
Key *client_host_key;
u_int ulen;
/*
* Get client user name. Note that we just have to
* trust the client; root on the client machine can
* claim to be any user.
*/
client_user = packet_get_string(&ulen);
/* Get the client host key. */
client_host_key = key_new(KEY_RSA1);
bits = packet_get_int();
packet_get_bignum(client_host_key->rsa->e);
packet_get_bignum(client_host_key->rsa->n);
keybits = BN_num_bits(client_host_key->rsa->n);
if (keybits < 0 || bits != (u_int)keybits) {
verbose("Warning: keysize mismatch for client_host_key: "
"actual %d, announced %d",
BN_num_bits(client_host_key->rsa->n), bits);
}
packet_check_eom();
authenticated = auth_rhosts_rsa(authctxt, client_user,
client_host_key);
key_free(client_host_key);
snprintf(info, infolen, " ruser %.100s", client_user);
return (authenticated);
}
/*ARGSUSED*/
static int
auth1_process_tis_challenge(Authctxt *authctxt, char *info, size_t infolen)
{
char *challenge;
if ((challenge = get_challenge(authctxt)) == NULL)
return (0);
debug("sending challenge '%s'", challenge);
packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE);
packet_put_cstring(challenge);
xfree(challenge);
packet_send();
packet_write_wait();
return (-1);
}
/*ARGSUSED*/
static int
auth1_process_tis_response(Authctxt *authctxt, char *info, size_t infolen)
{
int authenticated = 0;
char *response;
u_int dlen;
response = packet_get_string(&dlen);
packet_check_eom();
authenticated = verify_response(authctxt, response);
memset(response, 'r', dlen);
xfree(response);
return (authenticated);
}
/*
* read packets, try to authenticate the user and
* return only if authentication is successful
*/
static void
do_authloop(Authctxt *authctxt)
{
int authenticated = 0;
char info[1024];
int prev = 0, type = 0;
const struct AuthMethod1 *meth;
debug("Attempting authentication for %s%.100s.",
authctxt->valid ? "" : "invalid user ", authctxt->user);
/* If the user has no password, accept authentication immediately. */
if (options.password_authentication &&
#ifdef KRB5
(!options.kerberos_authentication || options.kerberos_or_local_passwd) &&
#endif
PRIVSEP(auth_password(authctxt, ""))) {
#ifdef USE_PAM
if (options.use_pam && (PRIVSEP(do_pam_account())))
#endif
{
auth_log(authctxt, 1, "without authentication", "");
return;
}
}
/* Indicate that authentication is needed. */
packet_start(SSH_SMSG_FAILURE);
packet_send();
packet_write_wait();
for (;;) {
/* default to fail */
authenticated = 0;
info[0] = '\0';
/* Get a packet from the client. */
prev = type;
type = packet_read();
/*
* If we started challenge-response authentication but the
* next packet is not a response to our challenge, release
* the resources allocated by get_challenge() (which would
* normally have been released by verify_response() had we
* received such a response)
*/
if (prev == SSH_CMSG_AUTH_TIS &&
type != SSH_CMSG_AUTH_TIS_RESPONSE)
abandon_challenge_response(authctxt);
if ((meth = lookup_authmethod1(type)) == NULL) {
logit("Unknown message during authentication: "
"type %d", type);
goto skip;
}
if (!*(meth->enabled)) {
verbose("%s authentication disabled.", meth->name);
goto skip;
}
authenticated = meth->method(authctxt, info, sizeof(info));
if (authenticated == -1)
continue; /* "postponed" */
#ifdef BSD_AUTH
if (authctxt->as) {
auth_close(authctxt->as);
authctxt->as = NULL;
}
#endif
if (!authctxt->valid && authenticated)
fatal("INTERNAL ERROR: authenticated invalid user %s",
authctxt->user);
#ifdef _UNICOS
if (authenticated && cray_access_denied(authctxt->user)) {
authenticated = 0;
fatal("Access denied for user %s.",authctxt->user);
}
#endif /* _UNICOS */
#ifdef HAVE_CYGWIN
if (authenticated &&
!check_nt_auth(type == SSH_CMSG_AUTH_PASSWORD,
authctxt->pw)) {
packet_disconnect("Authentication rejected for uid %d.",
authctxt->pw == NULL ? -1 : authctxt->pw->pw_uid);
authenticated = 0;
}
#else
/* Special handling for root */
if (authenticated && authctxt->pw->pw_uid == 0 &&
!auth_root_allowed(meth->name)) {
authenticated = 0;
# ifdef SSH_AUDIT_EVENTS
PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED));
# endif
}
#endif
#ifdef USE_PAM
if (options.use_pam && authenticated &&
!PRIVSEP(do_pam_account())) {
char *msg;
size_t len;
error("Access denied for user %s by PAM account "
"configuration", authctxt->user);
len = buffer_len(&loginmsg);
buffer_append(&loginmsg, "\0", 1);
msg = buffer_ptr(&loginmsg);
/* strip trailing newlines */
if (len > 0)
while (len > 0 && msg[--len] == '\n')
msg[len] = '\0';
else
msg = "Access denied.";
packet_disconnect(msg);
}
#endif
skip:
/* Log before sending the reply */
auth_log(authctxt, authenticated, get_authname(type), info);
if (client_user != NULL) {
xfree(client_user);
client_user = NULL;
}
if (authenticated)
return;
if (authctxt->failures++ > options.max_authtries) {
#ifdef SSH_AUDIT_EVENTS
PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES));
#endif
packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
}
packet_start(SSH_SMSG_FAILURE);
packet_send();
packet_write_wait();
}
}
/*
* Performs authentication of an incoming connection. Session key has already
* been exchanged and encryption is enabled.
*/
void
do_authentication(Authctxt *authctxt)
{
u_int ulen;
char *user, *style = NULL;
/* Get the name of the user that we wish to log in as. */
packet_read_expect(SSH_CMSG_USER);
/* Get the user name. */
user = packet_get_string(&ulen);
packet_check_eom();
if ((style = strchr(user, ':')) != NULL)
*style++ = '\0';
authctxt->user = user;
authctxt->style = style;
/* Verify that the user is a valid user. */
if ((authctxt->pw = PRIVSEP(getpwnamallow(user))) != NULL)
authctxt->valid = 1;
else {
debug("do_authentication: invalid user %s", user);
authctxt->pw = fakepw();
}
setproctitle("%s%s", authctxt->valid ? user : "unknown",
use_privsep ? " [net]" : "");
#ifdef USE_PAM
if (options.use_pam)
PRIVSEP(start_pam(authctxt));
#endif
/*
* If we are not running as root, the user must have the same uid as
* the server.
*/
#ifndef HAVE_CYGWIN
if (!use_privsep && getuid() != 0 && authctxt->pw &&
authctxt->pw->pw_uid != getuid())
packet_disconnect("Cannot change user when server not running as root.");
#endif
/*
* Loop until the user has been authenticated or the connection is
* closed, do_authloop() returns only if authentication is successful
*/
do_authloop(authctxt);
/* The user has been authenticated and accepted. */
packet_start(SSH_SMSG_SUCCESS);
packet_send();
packet_write_wait();
}