Skip to content

Commit

Permalink
upstream commit
Browse files Browse the repository at this point in the history
refactor authentication logging

optionally record successful auth methods and public credentials
used in a file accessible to user sessions

feedback and ok markus@

Upstream-ID: 090b93036967015717b9a54fd0467875ae9d32fb
  • Loading branch information
djmdjm committed Jun 24, 2017
1 parent e2004d4 commit 8f57495
Show file tree
Hide file tree
Showing 13 changed files with 338 additions and 140 deletions.
62 changes: 44 additions & 18 deletions auth.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* $OpenBSD: auth.c,v 1.121 2017/05/30 08:52:19 markus Exp $ */
/* $OpenBSD: auth.c,v 1.122 2017/06/24 06:34:38 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
Expand Down Expand Up @@ -267,21 +267,41 @@ allowed_user(struct passwd * pw)
return 1;
}

void
auth_info(Authctxt *authctxt, const char *fmt, ...)
/*
* Formats any key left in authctxt->auth_method_key for inclusion in
* auth_log()'s message. Also includes authxtct->auth_method_info if present.
*/
static char *
format_method_key(Authctxt *authctxt)
{
va_list ap;
int i;

free(authctxt->info);
authctxt->info = NULL;
const struct sshkey *key = authctxt->auth_method_key;
const char *methinfo = authctxt->auth_method_info;
char *fp, *ret = NULL;

va_start(ap, fmt);
i = vasprintf(&authctxt->info, fmt, ap);
va_end(ap);
if (key == NULL)
return NULL;

if (i < 0 || authctxt->info == NULL)
fatal("vasprintf failed");
if (key_is_cert(key)) {
fp = sshkey_fingerprint(key->cert->signature_key,
options.fingerprint_hash, SSH_FP_DEFAULT);
xasprintf(&ret, "%s ID %s (serial %llu) CA %s %s%s%s",
sshkey_type(key), key->cert->key_id,
(unsigned long long)key->cert->serial,
sshkey_type(key->cert->signature_key),
fp == NULL ? "(null)" : fp,
methinfo == NULL ? "" : ", ",
methinfo == NULL ? "" : methinfo);
free(fp);
} else {
fp = sshkey_fingerprint(key, options.fingerprint_hash,
SSH_FP_DEFAULT);
xasprintf(&ret, "%s %s%s%s", sshkey_type(key),
fp == NULL ? "(null)" : fp,
methinfo == NULL ? "" : ", ",
methinfo == NULL ? "" : methinfo);
free(fp);
}
return ret;
}

void
Expand All @@ -290,7 +310,8 @@ auth_log(Authctxt *authctxt, int authenticated, int partial,
{
struct ssh *ssh = active_state; /* XXX */
void (*authlog) (const char *fmt,...) = verbose;
char *authmsg;
const char *authmsg;
char *extra = NULL;

if (use_privsep && !mm_is_monitor() && !authctxt->postponed)
return;
Expand All @@ -309,6 +330,11 @@ auth_log(Authctxt *authctxt, int authenticated, int partial,
else
authmsg = authenticated ? "Accepted" : "Failed";

if ((extra = format_method_key(authctxt)) == NULL) {
if (authctxt->auth_method_info != NULL)
extra = xstrdup(authctxt->auth_method_info);
}

authlog("%s %s%s%s for %s%.100s from %.200s port %d ssh2%s%s",
authmsg,
method,
Expand All @@ -317,10 +343,10 @@ auth_log(Authctxt *authctxt, int authenticated, int partial,
authctxt->user,
ssh_remote_ipaddr(ssh),
ssh_remote_port(ssh),
authctxt->info != NULL ? ": " : "",
authctxt->info != NULL ? authctxt->info : "");
free(authctxt->info);
authctxt->info = NULL;
extra != NULL ? ": " : "",
extra != NULL ? extra : "");

free(extra);

#ifdef CUSTOM_FAILED_LOGIN
if (authenticated == 0 && !authctxt->postponed &&
Expand Down
48 changes: 33 additions & 15 deletions auth.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* $OpenBSD: auth.h,v 1.91 2017/05/30 14:29:59 markus Exp $ */
/* $OpenBSD: auth.h,v 1.92 2017/06/24 06:34:38 djm Exp $ */

/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
Expand Down Expand Up @@ -44,6 +44,7 @@

struct ssh;
struct sshkey;
struct sshbuf;

typedef struct Authctxt Authctxt;
typedef struct Authmethod Authmethod;
Expand All @@ -62,26 +63,38 @@ struct Authctxt {
char *service;
struct passwd *pw; /* set if 'valid' */
char *style;

/* Method lists for multiple authentication */
char **auth_methods; /* modified from server config */
u_int num_auth_methods;

/* Authentication method-specific data */
void *methoddata;
void *kbdintctxt;
char *info; /* Extra info for next auth_log */
#ifdef BSD_AUTH
auth_session_t *as;
#endif
char **auth_methods; /* modified from server config */
u_int num_auth_methods;
#ifdef KRB5
krb5_context krb5_ctx;
krb5_ccache krb5_fwd_ccache;
krb5_principal krb5_user;
char *krb5_ticket_file;
char *krb5_ccname;
#endif
Buffer *loginmsg;
void *methoddata;
struct sshbuf *loginmsg;

/* Authentication keys already used; these will be refused henceforth */
struct sshkey **prev_keys;
u_int nprev_keys;

struct sshkey **prev_userkeys;
u_int nprev_userkeys;
/* Last used key and ancilliary information from active auth method */
struct sshkey *auth_method_key;
char *auth_method_info;

/* Information exposed to session */
struct sshbuf *session_info; /* Auth info for environment */
};

/*
* Every authentication method has to handle authentication requests for
* non-existing users, or for users that are not allowed to login. In this
Expand Down Expand Up @@ -120,10 +133,18 @@ int auth_password(Authctxt *, const char *);
int hostbased_key_allowed(struct passwd *, const char *, char *,
struct sshkey *);
int user_key_allowed(struct passwd *, struct sshkey *, int);
void pubkey_auth_info(Authctxt *, const struct sshkey *, const char *, ...)
__attribute__((__format__ (printf, 3, 4)));
void auth2_record_userkey(Authctxt *, struct sshkey *);
int auth2_userkey_already_used(Authctxt *, struct sshkey *);
int auth2_key_already_used(Authctxt *, const struct sshkey *);

/*
* Handling auth method-specific information for logging and prevention
* of key reuse during multiple authentication.
*/
void auth2_authctxt_reset_info(Authctxt *);
void auth2_record_key(Authctxt *, int, const struct sshkey *);
void auth2_record_info(Authctxt *authctxt, const char *, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
void auth2_update_session_info(Authctxt *, const char *, const char *);

struct stat;
int auth_secure_path(const char *, struct stat *, const char *, uid_t,
Expand All @@ -150,9 +171,6 @@ void disable_forwarding(void);

void do_authentication2(Authctxt *);

void auth_info(Authctxt *authctxt, const char *, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
void auth_log(Authctxt *, int, int, const char *, const char *);
void auth_maxtries_exceeded(Authctxt *) __attribute__((noreturn));
void userauth_finish(struct ssh *, int, const char *, const char *);
Expand Down
12 changes: 11 additions & 1 deletion auth2-gss.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* $OpenBSD: auth2-gss.c,v 1.25 2017/05/30 14:29:59 markus Exp $ */
/* $OpenBSD: auth2-gss.c,v 1.26 2017/06/24 06:34:38 djm Exp $ */

/*
* Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
Expand Down Expand Up @@ -228,6 +228,7 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
{
Authctxt *authctxt = ssh->authctxt;
int authenticated;
const char *displayname;

if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
fatal("No authentication or GSSAPI context");
Expand All @@ -241,6 +242,10 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)

authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));

if ((!use_privsep || mm_is_monitor()) &&
(displayname = ssh_gssapi_displayname()) != NULL)
auth2_record_info(authctxt, "%s", displayname);

authctxt->postponed = 0;
ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
Expand All @@ -259,6 +264,7 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
Buffer b;
gss_buffer_desc mic, gssbuf;
u_int len;
const char *displayname;

if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
fatal("No authentication or GSSAPI context");
Expand All @@ -282,6 +288,10 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
buffer_free(&b);
free(mic.value);

if ((!use_privsep || mm_is_monitor()) &&
(displayname = ssh_gssapi_displayname()) != NULL)
auth2_record_info(authctxt, "%s", displayname);

authctxt->postponed = 0;
ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
Expand Down
8 changes: 4 additions & 4 deletions auth2-hostbased.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* $OpenBSD: auth2-hostbased.c,v 1.30 2017/05/30 14:29:59 markus Exp $ */
/* $OpenBSD: auth2-hostbased.c,v 1.31 2017/06/24 06:34:38 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
Expand Down Expand Up @@ -137,7 +137,7 @@ userauth_hostbased(struct ssh *ssh)
sshbuf_dump(b, stderr);
#endif

pubkey_auth_info(authctxt, key,
auth2_record_info(authctxt,
"client user \"%.100s\", client host \"%.100s\"", cuser, chost);

/* test for allowed key and correct signature */
Expand All @@ -147,11 +147,11 @@ userauth_hostbased(struct ssh *ssh)
sshbuf_ptr(b), sshbuf_len(b), ssh->compat)) == 0)
authenticated = 1;

auth2_record_key(authctxt, authenticated, key);
sshbuf_free(b);
done:
debug2("%s: authenticated %d", __func__, authenticated);
if (key != NULL)
sshkey_free(key);
sshkey_free(key);
free(pkalg);
free(pkblob);
free(cuser);
Expand Down
80 changes: 4 additions & 76 deletions auth2-pubkey.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* $OpenBSD: auth2-pubkey.c,v 1.67 2017/05/31 10:54:00 markus Exp $ */
/* $OpenBSD: auth2-pubkey.c,v 1.68 2017/06/24 06:34:38 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
Expand Down Expand Up @@ -137,7 +137,7 @@ userauth_pubkey(struct ssh *ssh)
goto done;
}
fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT);
if (auth2_userkey_already_used(authctxt, key)) {
if (auth2_key_already_used(authctxt, key)) {
logit("refusing previously-used %s key", sshkey_type(key));
goto done;
}
Expand Down Expand Up @@ -194,20 +194,17 @@ userauth_pubkey(struct ssh *ssh)
#ifdef DEBUG_PK
sshbuf_dump(b, stderr);
#endif
pubkey_auth_info(authctxt, key, NULL);

/* test for correct signature */
authenticated = 0;
if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) &&
PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b),
sshbuf_len(b), ssh->compat)) == 0) {
authenticated = 1;
/* Record the successful key to prevent reuse */
auth2_record_userkey(authctxt, key);
key = NULL; /* Don't free below */
}
sshbuf_free(b);
free(sig);
auth2_record_key(authctxt, authenticated, key);
} else {
debug("%s: test whether pkalg/pkblob are acceptable for %s %s",
__func__, sshkey_type(key), fp);
Expand Down Expand Up @@ -237,53 +234,14 @@ userauth_pubkey(struct ssh *ssh)
auth_clear_options();
done:
debug2("%s: authenticated %d pkalg %s", __func__, authenticated, pkalg);
if (key != NULL)
sshkey_free(key);
sshkey_free(key);
free(userstyle);
free(pkalg);
free(pkblob);
free(fp);
return authenticated;
}

void
pubkey_auth_info(Authctxt *authctxt, const struct sshkey *key,
const char *fmt, ...)
{
char *fp, *extra;
va_list ap;
int i;

extra = NULL;
if (fmt != NULL) {
va_start(ap, fmt);
i = vasprintf(&extra, fmt, ap);
va_end(ap);
if (i < 0 || extra == NULL)
fatal("%s: vasprintf failed", __func__);
}

if (sshkey_is_cert(key)) {
fp = sshkey_fingerprint(key->cert->signature_key,
options.fingerprint_hash, SSH_FP_DEFAULT);
auth_info(authctxt, "%s ID %s (serial %llu) CA %s %s%s%s",
sshkey_type(key), key->cert->key_id,
(unsigned long long)key->cert->serial,
sshkey_type(key->cert->signature_key),
fp == NULL ? "(null)" : fp,
extra == NULL ? "" : ", ", extra == NULL ? "" : extra);
free(fp);
} else {
fp = sshkey_fingerprint(key, options.fingerprint_hash,
SSH_FP_DEFAULT);
auth_info(authctxt, "%s %s%s%s", sshkey_type(key),
fp == NULL ? "(null)" : fp,
extra == NULL ? "" : ", ", extra == NULL ? "" : extra);
free(fp);
}
free(extra);
}

/*
* Splits 's' into an argument vector. Handles quoted string and basic
* escape characters (\\, \", \'). Caller must free the argument vector
Expand Down Expand Up @@ -1148,36 +1106,6 @@ user_key_allowed(struct passwd *pw, struct sshkey *key, int auth_attempt)
return success;
}

/* Records a public key in the list of previously-successful keys */
void
auth2_record_userkey(Authctxt *authctxt, struct sshkey *key)
{
struct sshkey **tmp;

if (authctxt->nprev_userkeys >= INT_MAX ||
(tmp = recallocarray(authctxt->prev_userkeys,
authctxt->nprev_userkeys, authctxt->nprev_userkeys + 1,
sizeof(*tmp))) == NULL)
fatal("%s: recallocarray failed", __func__);
authctxt->prev_userkeys = tmp;
authctxt->prev_userkeys[authctxt->nprev_userkeys] = key;
authctxt->nprev_userkeys++;
}

/* Checks whether a key has already been used successfully for authentication */
int
auth2_userkey_already_used(Authctxt *authctxt, struct sshkey *key)
{
u_int i;

for (i = 0; i < authctxt->nprev_userkeys; i++) {
if (sshkey_equal_public(key, authctxt->prev_userkeys[i])) {
return 1;
}
}
return 0;
}

Authmethod method_pubkey = {
"publickey",
userauth_pubkey,
Expand Down
Loading

0 comments on commit 8f57495

Please sign in to comment.