freebsd-skq/crypto/openssh/auth2.c

855 lines
22 KiB
C
Raw Normal View History

2000-05-15 04:37:24 +00:00
/*
* 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.
*/
2000-05-15 04:37:24 +00:00
#include "includes.h"
2001-05-04 04:14:23 +00:00
RCSID("$OpenBSD: auth2.c,v 1.56 2001/04/19 00:05:11 markus Exp $");
RCSID("$FreeBSD$");
2000-05-15 04:37:24 +00:00
#include <openssl/evp.h>
2001-05-04 04:14:23 +00:00
#include "ssh2.h"
2000-05-15 04:37:24 +00:00
#include "xmalloc.h"
#include "rsa.h"
2001-05-04 04:14:23 +00:00
#include "sshpty.h"
2000-05-15 04:37:24 +00:00
#include "packet.h"
#include "buffer.h"
2001-05-04 04:14:23 +00:00
#include "log.h"
2000-05-15 04:37:24 +00:00
#include "servconf.h"
#include "compat.h"
#include "channels.h"
#include "bufaux.h"
#include "auth.h"
#include "session.h"
#include "dispatch.h"
#include "key.h"
2001-05-04 04:14:23 +00:00
#include "cipher.h"
2000-05-15 04:37:24 +00:00
#include "kex.h"
2001-05-04 04:14:23 +00:00
#include "pathnames.h"
2000-05-15 04:37:24 +00:00
#include "uidswap.h"
#include "auth-options.h"
2001-05-04 04:14:23 +00:00
#include "misc.h"
#include "hostfile.h"
#include "canohost.h"
#include "tildexpand.h"
2000-05-15 04:37:24 +00:00
#ifdef HAVE_LOGIN_CAP
#include <login_cap.h>
#endif /* HAVE_LOGIN_CAP */
2000-05-15 04:37:24 +00:00
/* import */
extern ServerOptions options;
2001-05-04 04:14:23 +00:00
extern u_char *session_id2;
2000-05-15 04:37:24 +00:00
extern int session_id2_len;
static Authctxt *x_authctxt = NULL;
static int one = 1;
typedef struct Authmethod Authmethod;
struct Authmethod {
char *name;
int (*userauth)(Authctxt *authctxt);
int *enabled;
};
2000-05-15 04:37:24 +00:00
/* protocol */
void input_service_request(int type, int plen, void *ctxt);
void input_userauth_request(int type, int plen, void *ctxt);
void protocol_error(int type, int plen, void *ctxt);
2000-05-15 04:37:24 +00:00
/* helper */
Authmethod *authmethod_lookup(const char *name);
char *authmethods_get(void);
2001-05-04 04:14:23 +00:00
int user_key_allowed(struct passwd *pw, Key *key);
int
hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost,
Key *key);
2000-05-15 04:37:24 +00:00
/* auth */
2001-05-04 04:14:23 +00:00
void userauth_banner(void);
void userauth_reply(Authctxt *authctxt, int authenticated);
int userauth_none(Authctxt *authctxt);
int userauth_passwd(Authctxt *authctxt);
int userauth_pubkey(Authctxt *authctxt);
2001-05-04 04:14:23 +00:00
int userauth_hostbased(Authctxt *authctxt);
int userauth_kbdint(Authctxt *authctxt);
Authmethod authmethods[] = {
{"none",
userauth_none,
&one},
{"publickey",
userauth_pubkey,
2001-05-04 04:14:23 +00:00
&options.pubkey_authentication},
{"password",
userauth_passwd,
&options.password_authentication},
2001-05-04 04:14:23 +00:00
{"keyboard-interactive",
userauth_kbdint,
&options.kbd_interactive_authentication},
{"hostbased",
userauth_hostbased,
&options.hostbased_authentication},
{NULL, NULL, NULL}
2000-05-15 04:37:24 +00:00
};
/*
* loop until authctxt->success == TRUE
2000-05-15 04:37:24 +00:00
*/
void
do_authentication2()
{
2001-05-04 04:14:23 +00:00
Authctxt *authctxt = authctxt_new();
x_authctxt = authctxt; /*XXX*/
#if defined(KRB4) || defined(KRB5)
/* turn off kerberos, not supported by SSH2 */
options.kerberos_authentication = 0;
2000-05-15 04:37:24 +00:00
#endif
2001-05-04 04:14:23 +00:00
/* challenge-reponse is implemented via keyboard interactive */
if (options.challenge_reponse_authentication)
options.kbd_interactive_authentication = 1;
2000-05-15 04:37:24 +00:00
dispatch_init(&protocol_error);
dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
2001-05-04 04:14:23 +00:00
do_authenticated(authctxt);
2000-05-15 04:37:24 +00:00
}
void
protocol_error(int type, int plen, void *ctxt)
2000-05-15 04:37:24 +00:00
{
log("auth: protocol error: type %d plen %d", type, plen);
packet_start(SSH2_MSG_UNIMPLEMENTED);
packet_put_int(0);
packet_send();
packet_write_wait();
}
void
input_service_request(int type, int plen, void *ctxt)
2000-05-15 04:37:24 +00:00
{
Authctxt *authctxt = ctxt;
2001-05-04 04:14:23 +00:00
u_int len;
2000-05-15 04:37:24 +00:00
int accept = 0;
char *service = packet_get_string(&len);
packet_done();
if (authctxt == NULL)
fatal("input_service_request: no authctxt");
2000-05-15 04:37:24 +00:00
if (strcmp(service, "ssh-userauth") == 0) {
if (!authctxt->success) {
2000-05-15 04:37:24 +00:00
accept = 1;
/* now we can handle user-auth requests */
dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
}
}
/* XXX all other service requests are denied */
if (accept) {
packet_start(SSH2_MSG_SERVICE_ACCEPT);
packet_put_cstring(service);
packet_send();
packet_write_wait();
} else {
debug("bad service request %s", service);
packet_disconnect("bad service request %s", service);
}
xfree(service);
}
void
input_userauth_request(int type, int plen, void *ctxt)
2000-05-15 04:37:24 +00:00
{
Authctxt *authctxt = ctxt;
Authmethod *m = NULL;
2001-05-04 04:14:23 +00:00
char *user, *service, *method, *style = NULL;
2000-05-15 04:37:24 +00:00
int authenticated = 0;
#ifdef HAVE_LOGIN_CAP
login_cap_t *lc;
#endif /* HAVE_LOGIN_CAP */
#if defined(HAVE_LOGIN_CAP) || defined(LOGIN_ACCESS)
const char *from_host, *from_ip;
2001-05-04 04:14:23 +00:00
from_host = get_canonical_hostname(options.reverse_mapping_check);
from_ip = get_remote_ipaddr();
#endif /* HAVE_LOGIN_CAP || LOGIN_ACCESS */
2000-05-15 04:37:24 +00:00
if (authctxt == NULL)
fatal("input_userauth_request: no authctxt");
2000-05-15 04:37:24 +00:00
user = packet_get_string(NULL);
service = packet_get_string(NULL);
method = packet_get_string(NULL);
2000-05-15 04:37:24 +00:00
debug("userauth-request for user %s service %s method %s", user, service, method);
2001-05-04 04:14:23 +00:00
debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
if ((style = strchr(user, ':')) != NULL)
*style++ = 0;
2001-05-04 04:14:23 +00:00
if (authctxt->attempt++ == 0) {
/* setup auth context */
struct passwd *pw = NULL;
pw = getpwnam(user);
if (pw && allowed_user(pw) && strcmp(service, "ssh-connection")==0) {
authctxt->pw = pwcopy(pw);
authctxt->valid = 1;
debug2("input_userauth_request: setting up authctxt for %s", user);
#ifdef USE_PAM
start_pam(pw);
#endif
} else {
log("input_userauth_request: illegal user %s", user);
authctxt->pw = NULL;
}
2001-05-04 04:14:23 +00:00
setproctitle("%s", pw ? user : "unknown");
authctxt->user = xstrdup(user);
authctxt->service = xstrdup(service);
2001-05-04 04:14:23 +00:00
authctxt->style = style ? xstrdup(style) : NULL; /* currently unused */
} else if (authctxt->valid) {
if (strcmp(user, authctxt->user) != 0 ||
strcmp(service, authctxt->service) != 0) {
2001-05-04 04:14:23 +00:00
log("input_userauth_request: mismatch: (%s,%s)!=(%s,%s)",
user, service, authctxt->user, authctxt->service);
authctxt->valid = 0;
2000-05-15 04:37:24 +00:00
}
}
#ifdef HAVE_LOGIN_CAP
if (authctxt->pw != NULL) {
lc = login_getpwclass(authctxt->pw);
if (lc == NULL)
lc = login_getclassbyname(NULL, authctxt->pw);
if (!auth_hostok(lc, from_host, from_ip)) {
log("Denied connection for %.200s from %.200s [%.200s].",
authctxt->pw->pw_name, from_host, from_ip);
packet_disconnect("Sorry, you are not allowed to connect.");
}
if (!auth_timeok(lc, time(NULL))) {
log("LOGIN %.200s REFUSED (TIME) FROM %.200s",
authctxt->pw->pw_name, from_host);
packet_disconnect("Logins not available right now.");
}
login_close(lc);
lc = NULL;
}
#endif /* HAVE_LOGIN_CAP */
#ifdef LOGIN_ACCESS
if (authctxt->pw != NULL &&
!login_access(authctxt->pw->pw_name, from_host)) {
log("Denied connection for %.200s from %.200s [%.200s].",
authctxt->pw->pw_name, from_host, from_ip);
packet_disconnect("Sorry, you are not allowed to connect.");
}
#endif /* LOGIN_ACCESS */
2001-05-04 04:14:23 +00:00
/* reset state */
dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, &protocol_error);
authctxt->postponed = 0;
#ifdef BSD_AUTH
if (authctxt->as) {
auth_close(authctxt->as);
authctxt->as = NULL;
}
#endif
2001-05-04 04:14:23 +00:00
/* try to authenticate user */
m = authmethod_lookup(method);
if (m != NULL) {
debug2("input_userauth_request: try method %s", method);
authenticated = m->userauth(authctxt);
}
#ifdef USE_PAM
if (authenticated && authctxt->user && !do_pam_account(authctxt->user, NULL))
authenticated = 0;
#endif /* USE_PAM */
2001-05-04 04:14:23 +00:00
userauth_finish(authctxt, authenticated, method);
xfree(service);
xfree(user);
xfree(method);
}
void
2001-05-04 04:14:23 +00:00
userauth_finish(Authctxt *authctxt, int authenticated, char *method)
{
2001-05-04 04:14:23 +00:00
if (!authctxt->valid && authenticated)
fatal("INTERNAL ERROR: authenticated invalid user %s",
authctxt->user);
2001-05-04 04:14:23 +00:00
/* Special handling for root */
if (authenticated && authctxt->pw->pw_uid == 0 &&
!auth_root_allowed(method))
authenticated = 0;
2000-05-15 04:37:24 +00:00
2001-05-04 04:14:23 +00:00
/* Log before sending the reply */
auth_log(authctxt, authenticated, method, " ssh2");
2001-05-04 04:14:23 +00:00
if (!authctxt->postponed)
userauth_reply(authctxt, authenticated);
}
2001-05-04 04:14:23 +00:00
void
userauth_banner(void)
{
struct stat st;
char *banner = NULL;
off_t len, n;
int fd;
if (options.banner == NULL || (datafellows & SSH_BUG_BANNER))
return;
if ((fd = open(options.banner, O_RDONLY)) < 0)
return;
if (fstat(fd, &st) < 0)
goto done;
len = st.st_size;
banner = xmalloc(len + 1);
if ((n = read(fd, banner, len)) < 0)
goto done;
banner[n] = '\0';
packet_start(SSH2_MSG_USERAUTH_BANNER);
packet_put_cstring(banner);
packet_put_cstring(""); /* language, unused */
packet_send();
debug("userauth_banner: sent");
done:
if (banner)
xfree(banner);
close(fd);
return;
}
2000-05-15 04:37:24 +00:00
2001-05-04 04:14:23 +00:00
void
userauth_reply(Authctxt *authctxt, int authenticated)
{
2001-05-04 04:14:23 +00:00
char *methods;
2000-05-15 04:37:24 +00:00
/* XXX todo: check if multiple auth methods are needed */
if (authenticated == 1) {
/* turn off userauth */
dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error);
packet_start(SSH2_MSG_USERAUTH_SUCCESS);
packet_send();
packet_write_wait();
/* now we can break out */
authctxt->success = 1;
2001-05-04 04:14:23 +00:00
} else {
if (authctxt->failures++ > AUTH_FAIL_MAX)
packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
methods = authmethods_get();
2000-05-15 04:37:24 +00:00
packet_start(SSH2_MSG_USERAUTH_FAILURE);
packet_put_cstring(methods);
packet_put_char(0); /* XXX partial success, unused */
2000-05-15 04:37:24 +00:00
packet_send();
packet_write_wait();
xfree(methods);
2000-05-15 04:37:24 +00:00
}
}
int
userauth_none(Authctxt *authctxt)
2000-05-15 04:37:24 +00:00
{
/* disable method "none", only allowed one time */
Authmethod *m = authmethod_lookup("none");
if (m != NULL)
m->enabled = NULL;
2000-05-15 04:37:24 +00:00
packet_done();
2001-05-04 04:14:23 +00:00
userauth_banner();
#ifdef USE_PAM
2001-05-04 04:14:23 +00:00
return authctxt->valid ? auth_pam_password(authctxt, "") : 0;
#else /* !USE_PAM */
2001-05-04 04:14:23 +00:00
return authctxt->valid ? auth_password(authctxt, "") : 0;
#endif /* USE_PAM */
2000-05-15 04:37:24 +00:00
}
2000-05-15 04:37:24 +00:00
int
userauth_passwd(Authctxt *authctxt)
2000-05-15 04:37:24 +00:00
{
char *password;
int authenticated = 0;
int change;
2001-05-04 04:14:23 +00:00
u_int len;
2000-05-15 04:37:24 +00:00
change = packet_get_char();
if (change)
log("password change not supported");
password = packet_get_string(&len);
packet_done();
if (authctxt->valid &&
#ifdef USE_PAM
2001-05-04 04:14:23 +00:00
auth_pam_password(authctxt, password) == 1
#else
2001-05-04 04:14:23 +00:00
auth_password(authctxt, password) == 1
#endif
)
2000-05-15 04:37:24 +00:00
authenticated = 1;
memset(password, 0, len);
xfree(password);
return authenticated;
}
int
userauth_kbdint(Authctxt *authctxt)
{
int authenticated = 0;
char *lang = NULL;
char *devs = NULL;
lang = packet_get_string(NULL);
devs = packet_get_string(NULL);
packet_done();
debug("keyboard-interactive language %s devs %s", lang, devs);
2001-05-04 04:14:23 +00:00
if (options.challenge_reponse_authentication)
authenticated = auth2_challenge(authctxt, devs);
xfree(lang);
xfree(devs);
return authenticated;
}
2000-05-15 04:37:24 +00:00
int
userauth_pubkey(Authctxt *authctxt)
2000-05-15 04:37:24 +00:00
{
Buffer b;
Key *key;
char *pkalg, *pkblob, *sig;
2001-05-04 04:14:23 +00:00
u_int alen, blen, slen;
int have_sig, pktype;
2000-05-15 04:37:24 +00:00
int authenticated = 0;
if (!authctxt->valid) {
debug2("userauth_pubkey: disabled because of invalid user");
2000-05-15 04:37:24 +00:00
return 0;
}
have_sig = packet_get_char();
2001-05-04 04:14:23 +00:00
if (datafellows & SSH_BUG_PKAUTH) {
debug2("userauth_pubkey: SSH_BUG_PKAUTH");
/* no explicit pkalg given */
pkblob = packet_get_string(&blen);
buffer_init(&b);
buffer_append(&b, pkblob, blen);
/* so we have to extract the pkalg from the pkblob */
pkalg = buffer_get_string(&b, &alen);
buffer_free(&b);
} else {
pkalg = packet_get_string(&alen);
pkblob = packet_get_string(&blen);
}
pktype = key_type_from_name(pkalg);
if (pktype == KEY_UNSPEC) {
/* this is perfectly legal */
log("userauth_pubkey: unsupported public key algorithm: %s", pkalg);
xfree(pkalg);
2001-05-04 04:14:23 +00:00
xfree(pkblob);
2000-05-15 04:37:24 +00:00
return 0;
}
2001-05-04 04:14:23 +00:00
key = key_from_blob(pkblob, blen);
2000-05-15 04:37:24 +00:00
if (key != NULL) {
if (have_sig) {
sig = packet_get_string(&slen);
packet_done();
buffer_init(&b);
if (datafellows & SSH_OLD_SESSIONID) {
buffer_append(&b, session_id2, session_id2_len);
} else {
buffer_put_string(&b, session_id2, session_id2_len);
}
/* reconstruct packet */
2000-05-15 04:37:24 +00:00
buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
buffer_put_cstring(&b, authctxt->user);
buffer_put_cstring(&b,
2001-05-04 04:14:23 +00:00
datafellows & SSH_BUG_PKSERVICE ?
"ssh-userauth" :
authctxt->service);
2001-05-04 04:14:23 +00:00
if (datafellows & SSH_BUG_PKAUTH) {
buffer_put_char(&b, have_sig);
} else {
buffer_put_cstring(&b, "publickey");
buffer_put_char(&b, have_sig);
buffer_put_cstring(&b, pkalg);
}
buffer_put_string(&b, pkblob, blen);
2001-05-04 04:14:23 +00:00
#ifdef DEBUG_PK
2000-05-15 04:37:24 +00:00
buffer_dump(&b);
#endif
/* test for correct signature */
2001-05-04 04:14:23 +00:00
if (user_key_allowed(authctxt->pw, key) &&
key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
2000-05-15 04:37:24 +00:00
authenticated = 1;
buffer_clear(&b);
xfree(sig);
} else {
debug("test whether pkalg/pkblob are acceptable");
2000-05-15 04:37:24 +00:00
packet_done();
2000-05-15 04:37:24 +00:00
/* XXX fake reply and always send PK_OK ? */
/*
* XXX this allows testing whether a user is allowed
* to login: if you happen to have a valid pubkey this
* message is sent. the message is NEVER sent at all
* if a user is not allowed to login. is this an
* issue? -markus
*/
2001-05-04 04:14:23 +00:00
if (user_key_allowed(authctxt->pw, key)) {
2000-05-15 04:37:24 +00:00
packet_start(SSH2_MSG_USERAUTH_PK_OK);
packet_put_string(pkalg, alen);
packet_put_string(pkblob, blen);
packet_send();
packet_write_wait();
2001-05-04 04:14:23 +00:00
authctxt->postponed = 1;
2000-05-15 04:37:24 +00:00
}
}
if (authenticated != 1)
auth_clear_options();
2000-05-15 04:37:24 +00:00
key_free(key);
}
2001-05-04 04:14:23 +00:00
debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg);
xfree(pkalg);
xfree(pkblob);
return authenticated;
}
int
userauth_hostbased(Authctxt *authctxt)
{
Buffer b;
Key *key;
char *pkalg, *pkblob, *sig, *cuser, *chost, *service;
u_int alen, blen, slen;
int pktype;
int authenticated = 0;
if (!authctxt->valid) {
debug2("userauth_hostbased: disabled because of invalid user");
return 0;
}
pkalg = packet_get_string(&alen);
pkblob = packet_get_string(&blen);
chost = packet_get_string(NULL);
cuser = packet_get_string(NULL);
sig = packet_get_string(&slen);
debug("userauth_hostbased: cuser %s chost %s pkalg %s slen %d",
cuser, chost, pkalg, slen);
#ifdef DEBUG_PK
debug("signature:");
buffer_init(&b);
buffer_append(&b, sig, slen);
buffer_dump(&b);
buffer_free(&b);
#endif
pktype = key_type_from_name(pkalg);
if (pktype == KEY_UNSPEC) {
/* this is perfectly legal */
log("userauth_hostbased: unsupported "
"public key algorithm: %s", pkalg);
goto done;
}
key = key_from_blob(pkblob, blen);
if (key == NULL) {
debug("userauth_hostbased: cannot decode key: %s", pkalg);
goto done;
}
service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" :
authctxt->service;
buffer_init(&b);
buffer_put_string(&b, session_id2, session_id2_len);
/* reconstruct packet */
buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
buffer_put_cstring(&b, authctxt->user);
buffer_put_cstring(&b, service);
buffer_put_cstring(&b, "hostbased");
buffer_put_string(&b, pkalg, alen);
buffer_put_string(&b, pkblob, blen);
buffer_put_cstring(&b, chost);
buffer_put_cstring(&b, cuser);
#ifdef DEBUG_PK
buffer_dump(&b);
#endif
/* test for allowed key and correct signature */
if (hostbased_key_allowed(authctxt->pw, cuser, chost, key) &&
key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
authenticated = 1;
buffer_clear(&b);
key_free(key);
done:
debug2("userauth_hostbased: authenticated %d", authenticated);
2000-05-15 04:37:24 +00:00
xfree(pkalg);
xfree(pkblob);
2001-05-04 04:14:23 +00:00
xfree(cuser);
xfree(chost);
xfree(sig);
2000-05-15 04:37:24 +00:00
return authenticated;
}
/* get current user */
2000-05-15 04:37:24 +00:00
struct passwd*
auth_get_user(void)
{
return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL;
2000-05-15 04:37:24 +00:00
}
#define DELIM ","
char *
authmethods_get(void)
2000-05-15 04:37:24 +00:00
{
Authmethod *method = NULL;
2001-05-04 04:14:23 +00:00
u_int size = 0;
char *list;
for (method = authmethods; method->name != NULL; method++) {
if (strcmp(method->name, "none") == 0)
continue;
if (method->enabled != NULL && *(method->enabled) != 0) {
if (size != 0)
size += strlen(DELIM);
size += strlen(method->name);
2000-05-15 04:37:24 +00:00
}
}
size++; /* trailing '\0' */
list = xmalloc(size);
list[0] = '\0';
for (method = authmethods; method->name != NULL; method++) {
if (strcmp(method->name, "none") == 0)
continue;
if (method->enabled != NULL && *(method->enabled) != 0) {
if (list[0] != '\0')
strlcat(list, DELIM, size);
strlcat(list, method->name, size);
2000-05-15 04:37:24 +00:00
}
}
return list;
}
Authmethod *
authmethod_lookup(const char *name)
{
Authmethod *method = NULL;
if (name != NULL)
for (method = authmethods; method->name != NULL; method++)
if (method->enabled != NULL &&
*(method->enabled) != 0 &&
strcmp(name, method->name) == 0)
return method;
debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
return NULL;
2000-05-15 04:37:24 +00:00
}
/* return 1 if user allows given key */
int
2001-05-04 04:14:23 +00:00
user_key_allowed(struct passwd *pw, Key *key)
2000-05-15 04:37:24 +00:00
{
2001-05-04 04:14:23 +00:00
char line[8192], file[MAXPATHLEN];
2000-05-15 04:37:24 +00:00
int found_key = 0;
FILE *f;
2001-05-04 04:14:23 +00:00
u_long linenum = 0;
2000-05-15 04:37:24 +00:00
struct stat st;
Key *found;
if (pw == NULL)
return 0;
2000-05-15 04:37:24 +00:00
/* Temporarily use the user's uid. */
2001-05-04 04:14:23 +00:00
temporarily_use_uid(pw);
2000-05-15 04:37:24 +00:00
/* The authorized keys. */
snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
2001-05-04 04:14:23 +00:00
_PATH_SSH_USER_PERMITTED_KEYS2);
2000-05-15 04:37:24 +00:00
/* Fail quietly if file does not exist */
if (stat(file, &st) < 0) {
/* Restore the privileged uid. */
restore_uid();
return 0;
}
/* Open the file containing the authorized keys. */
f = fopen(file, "r");
if (!f) {
/* Restore the privileged uid. */
restore_uid();
return 0;
}
if (options.strict_modes) {
int fail = 0;
char buf[1024];
/* Check open file in order to avoid open/stat races */
if (fstat(fileno(f), &st) < 0 ||
(st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
(st.st_mode & 022) != 0) {
snprintf(buf, sizeof buf,
"%s authentication refused for %.100s: "
"bad ownership or modes for '%s'.",
key_type(key), pw->pw_name, file);
2000-05-15 04:37:24 +00:00
fail = 1;
} else {
2001-05-04 04:14:23 +00:00
/* Check path to _PATH_SSH_USER_PERMITTED_KEYS */
2000-05-15 04:37:24 +00:00
int i;
static const char *check[] = {
2001-05-04 04:14:23 +00:00
"", _PATH_SSH_USER_DIR, NULL
2000-05-15 04:37:24 +00:00
};
for (i = 0; check[i]; i++) {
snprintf(line, sizeof line, "%.500s/%.100s",
pw->pw_dir, check[i]);
if (stat(line, &st) < 0 ||
(st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
(st.st_mode & 022) != 0) {
snprintf(buf, sizeof buf,
"%s authentication refused for %.100s: "
2000-05-15 04:37:24 +00:00
"bad ownership or modes for '%s'.",
key_type(key), pw->pw_name, line);
2000-05-15 04:37:24 +00:00
fail = 1;
break;
}
}
}
if (fail) {
fclose(f);
2001-05-04 04:14:23 +00:00
log("%s", buf);
2000-05-15 04:37:24 +00:00
restore_uid();
return 0;
}
}
found_key = 0;
found = key_new(key->type);
2000-05-15 04:37:24 +00:00
while (fgets(line, sizeof(line), f)) {
char *cp, *options = NULL;
2000-05-15 04:37:24 +00:00
linenum++;
/* Skip leading whitespace, empty and comment lines. */
for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
;
if (!*cp || *cp == '\n' || *cp == '#')
continue;
2001-05-04 04:14:23 +00:00
if (key_read(found, &cp) == -1) {
/* no key? check if there are options for this key */
int quoted = 0;
2001-05-04 04:14:23 +00:00
debug2("user_key_allowed: check options: '%s'", cp);
options = cp;
for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
if (*cp == '\\' && cp[1] == '"')
cp++; /* Skip both */
else if (*cp == '"')
quoted = !quoted;
}
/* Skip remaining whitespace. */
for (; *cp == ' ' || *cp == '\t'; cp++)
;
2001-05-04 04:14:23 +00:00
if (key_read(found, &cp) == -1) {
debug2("user_key_allowed: advance: '%s'", cp);
/* still no key? advance to next line*/
continue;
}
}
if (key_equal(found, key) &&
2001-05-04 04:14:23 +00:00
auth_parse_options(pw, options, file, linenum) == 1) {
2000-05-15 04:37:24 +00:00
found_key = 1;
debug("matching key found: file %s, line %ld",
file, linenum);
break;
}
}
restore_uid();
fclose(f);
key_free(found);
2001-05-04 04:14:23 +00:00
if (!found_key)
debug2("key not found");
2000-05-15 04:37:24 +00:00
return found_key;
}
2001-05-04 04:14:23 +00:00
/* return 1 if given hostkey is allowed */
int
hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost,
Key *key)
{
2001-05-04 04:14:23 +00:00
Key *found;
const char *resolvedname, *ipaddr, *lookup;
struct stat st;
char *user_hostfile;
int host_status, len;
resolvedname = get_canonical_hostname(options.reverse_mapping_check);
ipaddr = get_remote_ipaddr();
debug2("userauth_hostbased: chost %s resolvedname %s ipaddr %s",
chost, resolvedname, ipaddr);
if (options.hostbased_uses_name_from_packet_only) {
if (auth_rhosts2(pw, cuser, chost, chost) == 0)
return 0;
lookup = chost;
} else {
if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') {
debug2("stripping trailing dot from chost %s", chost);
chost[len - 1] = '\0';
}
if (strcasecmp(resolvedname, chost) != 0)
log("userauth_hostbased mismatch: "
"client sends %s, but we resolve %s to %s",
chost, ipaddr, resolvedname);
if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0)
return 0;
lookup = resolvedname;
}
debug2("userauth_hostbased: access allowed by auth_rhosts2");
/* XXX this is copied from auth-rh-rsa.c and should be shared */
found = key_new(key->type);
host_status = check_host_in_hostfile(_PATH_SSH_SYSTEM_HOSTFILE2, lookup,
key, found, NULL);
if (host_status != HOST_OK && !options.ignore_user_known_hosts) {
user_hostfile = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE2,
pw->pw_uid);
if (options.strict_modes &&
(stat(user_hostfile, &st) == 0) &&
((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
(st.st_mode & 022) != 0)) {
log("Hostbased authentication refused for %.100s: "
"bad owner or modes for %.200s",
pw->pw_name, user_hostfile);
} else {
temporarily_use_uid(pw);
host_status = check_host_in_hostfile(user_hostfile,
lookup, key, found, NULL);
restore_uid();
}
xfree(user_hostfile);
}
key_free(found);
debug2("userauth_hostbased: key %s for %s", host_status == HOST_OK ?
"ok" : "not found", lookup);
return (host_status == HOST_OK);
}