Showing with 166 additions and 26 deletions.
  1. +5 −0 auth-pam.c
  2. +3 −0 auth.h
  3. +6 −0 auth2-gss.c
  4. +10 −4 auth2-hostbased.c
  5. +12 −22 auth2-pubkey.c
  6. +13 −0 auth2.c
  7. +10 −0 gss-serv.c
  8. +20 −0 monitor.c
  9. +20 −0 servconf.c
  10. +7 −0 servconf.h
  11. +14 −0 session.c
  12. +1 −0 ssh-gss.h
  13. +4 −0 ssh.1
  14. +15 −0 sshd_config.5
  15. +25 −0 sshkey.c
  16. +1 −0 sshkey.h
@@ -673,6 +673,11 @@ sshpam_init_ctx(Authctxt *authctxt)
return (NULL);
}

/* Notify PAM about any already successful auth methods */
if (options.expose_auth_methods >= EXPOSE_AUTHMETH_PAMONLY &&
authctxt->auth_details)
do_pam_putenv("SSH_USER_AUTH", authctxt->auth_details);

ctxt = xcalloc(1, sizeof *ctxt);

/* Start the authentication thread */
3 auth.h
@@ -81,6 +81,9 @@ struct Authctxt {

struct sshkey **prev_userkeys;
u_int nprev_userkeys;

char *last_details;
char *auth_details;
};
/*
* Every authentication method has to handle authentication requests for
@@ -240,6 +240,9 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)

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

if (authenticated)
authctxt->last_details = ssh_gssapi_get_displayname();

authctxt->postponed = 0;
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
@@ -278,6 +281,9 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
else
logit("GSSAPI MIC check failed");

if (authenticated)
authctxt->last_details = ssh_gssapi_get_displayname();

buffer_free(&b);
free(mic.value);

@@ -60,7 +60,7 @@ userauth_hostbased(Authctxt *authctxt)
{
Buffer b;
Key *key = NULL;
char *pkalg, *cuser, *chost, *service;
char *pkalg, *cuser, *chost, *service, *pubkey;
u_char *pkblob, *sig;
u_int alen, blen, slen;
int pktype;
@@ -132,15 +132,21 @@ userauth_hostbased(Authctxt *authctxt)
buffer_dump(&b);
#endif

pubkey_auth_info(authctxt, key,
"client user \"%.100s\", client host \"%.100s\"", cuser, chost);
pubkey = sshkey_format_oneline(key, options.fingerprint_hash);
auth_info(authctxt,
"%s, client user \"%.100s\", client host \"%.100s\"",
pubkey, cuser, chost);

/* test for allowed key and correct signature */
authenticated = 0;
if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) &&
PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
buffer_len(&b))) == 1)
buffer_len(&b))) == 1) {
authenticated = 1;
authctxt->last_details = pubkey;
} else {
free(pubkey);
}

buffer_free(&b);
done:
@@ -79,7 +79,7 @@ userauth_pubkey(Authctxt *authctxt)
{
Buffer b;
Key *key = NULL;
char *pkalg, *userstyle, *fp = NULL;
char *pkalg, *userstyle, *pubkey, *fp = NULL;
u_char *pkblob, *sig;
u_int alen, blen, slen;
int have_sig, pktype;
@@ -171,17 +171,21 @@ userauth_pubkey(Authctxt *authctxt)
#ifdef DEBUG_PK
buffer_dump(&b);
#endif
pubkey_auth_info(authctxt, key, NULL);
pubkey = sshkey_format_oneline(key, options.fingerprint_hash);
auth_info(authctxt, "%s", pubkey);

/* test for correct signature */
authenticated = 0;
if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) &&
PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
buffer_len(&b))) == 1) {
authenticated = 1;
authctxt->last_details = pubkey;
/* Record the successful key to prevent reuse */
auth2_record_userkey(authctxt, key);
key = NULL; /* Don't free below */
} else {
free(pubkey);
}
buffer_free(&b);
free(sig);
@@ -222,7 +226,7 @@ userauth_pubkey(Authctxt *authctxt)
void
pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...)
{
char *fp, *extra;
char *extra, *pubkey;
va_list ap;
int i;

@@ -232,27 +236,13 @@ pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...)
i = vasprintf(&extra, fmt, ap);
va_end(ap);
if (i < 0 || extra == NULL)
fatal("%s: vasprintf failed", __func__);
fatal("%s: vasprintf failed", __func__);
}

if (key_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",
key_type(key), key->cert->key_id,
(unsigned long long)key->cert->serial,
key_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", key_type(key),
fp == NULL ? "(null)" : fp,
extra == NULL ? "" : ", ", extra == NULL ? "" : extra);
free(fp);
}
pubkey = sshkey_format_oneline(key, options.fingerprint_hash);
auth_info(authctxt, "%s%s%s", pubkey, extra == NULL ? "" : ", ",
extra == NULL ? "" : extra);
free(pubkey);
free(extra);
}

13 auth2.c
@@ -293,6 +293,7 @@ userauth_finish(Authctxt *authctxt, int authenticated, const char *method,
const char *submethod)
{
char *methods;
char *prev_auth_details;
int partial = 0;

if (!authctxt->valid && authenticated)
@@ -323,6 +324,18 @@ userauth_finish(Authctxt *authctxt, int authenticated, const char *method,
if (authctxt->postponed)
return;

if (authenticated || partial) {
prev_auth_details = authctxt->auth_details;
xasprintf(&authctxt->auth_details, "%s%s%s%s%s",
prev_auth_details ? prev_auth_details : "",
prev_auth_details ? ", " : "", method,
authctxt->last_details ? ": " : "",
authctxt->last_details ? authctxt->last_details : "");
free(prev_auth_details);
}
free(authctxt->last_details);
authctxt->last_details = NULL;

#ifdef USE_PAM
if (options.use_pam && authenticated) {
if (!PRIVSEP(do_pam_account())) {
@@ -383,6 +383,16 @@ ssh_gssapi_userok(char *user)
return (0);
}

/* Privileged */
char*
ssh_gssapi_get_displayname(void)
{
if (gssapi_client.displayname.length != 0 &&
gssapi_client.displayname.value != NULL)
return strdup((char *)gssapi_client.displayname.value);
return NULL;
}

/* Privileged */
OM_uint32
ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
@@ -335,6 +335,7 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
{
struct mon_table *ent;
int authenticated = 0, partial = 0;
char *prev_auth_details;

debug3("preauth child monitor started");

@@ -366,6 +367,18 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
auth_submethod = NULL;
authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1);

if (authenticated) {
prev_auth_details = authctxt->auth_details;
xasprintf(&authctxt->auth_details, "%s%s%s%s%s",
prev_auth_details ? prev_auth_details : "",
prev_auth_details ? ", " : "", auth_method,
authctxt->last_details ? ": " : "",
authctxt->last_details ? authctxt->last_details : "");
free(prev_auth_details);
}
free(authctxt->last_details);
authctxt->last_details = NULL;

/* Special handling for multiple required authentications */
if (options.num_auth_methods != 0) {
if (!compat20)
@@ -1450,6 +1463,10 @@ mm_answer_keyverify(int sock, Buffer *m)
debug3("%s: key %p signature %s",
__func__, key, (verified == 1) ? "verified" : "unverified");

if (verified == 1)
authctxt->last_details = sshkey_format_oneline(key,
options.fingerprint_hash);

/* If auth was successful then record key to ensure it isn't reused */
if (verified == 1 && key_blobtype == MM_USERKEY)
auth2_record_userkey(authctxt, key);
@@ -2060,6 +2077,9 @@ mm_answer_gss_userok(int sock, Buffer *m)

auth_method = "gssapi-with-mic";

if (authenticated)
authctxt->last_details = ssh_gssapi_get_displayname();

/* Monitor loop will terminate if authenticated */
return (authenticated);
}
@@ -169,6 +169,7 @@ initialize_server_options(ServerOptions *options)
options->ip_qos_bulk = -1;
options->version_addendum = NULL;
options->fingerprint_hash = -1;
options->expose_auth_methods = -1;
}

/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
@@ -353,6 +354,8 @@ fill_default_server_options(ServerOptions *options)
options->fwd_opts.streamlocal_bind_unlink = 0;
if (options->fingerprint_hash == -1)
options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
if (options->expose_auth_methods == -1)
options->expose_auth_methods = EXPOSE_AUTHMETH_NEVER;

assemble_algorithms(options);

@@ -438,6 +441,7 @@ typedef enum {
sAuthenticationMethods, sHostKeyAgent, sPermitUserRC,
sStreamLocalBindMask, sStreamLocalBindUnlink,
sAllowStreamLocalForwarding, sFingerprintHash,
sExposeAuthenticationMethods,
sDeprecated, sUnsupported
} ServerOpCodes;

@@ -580,6 +584,7 @@ static struct {
{ "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_ALL },
{ "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL },
{ "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL },
{ "exposeauthenticationmethods", sExposeAuthenticationMethods, SSHCFG_ALL },
{ NULL, sBadOption, 0 }
};

@@ -969,6 +974,12 @@ static const struct multistate multistate_tcpfwd[] = {
{ "local", FORWARD_LOCAL },
{ NULL, -1 }
};
static const struct multistate multistate_exposeauthmeth[] = {
{ "never", EXPOSE_AUTHMETH_NEVER },
{ "pam-only", EXPOSE_AUTHMETH_PAMONLY },
{ "pam-and-env", EXPOSE_AUTHMETH_PAMENV },
{ NULL, -1}
};

int
process_server_config_line(ServerOptions *options, char *line,
@@ -1879,6 +1890,11 @@ process_server_config_line(ServerOptions *options, char *line,
options->fingerprint_hash = value;
break;

case sExposeAuthenticationMethods:
intptr = &options->expose_auth_methods;
multistate_ptr = multistate_exposeauthmeth;
goto parse_multistate;

case sDeprecated:
logit("%s line %d: Deprecated option %s",
filename, linenum, arg);
@@ -2034,6 +2050,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
M_CP_INTOPT(ip_qos_bulk);
M_CP_INTOPT(rekey_limit);
M_CP_INTOPT(rekey_interval);
M_CP_INTOPT(expose_auth_methods);

/*
* The bind_mask is a mode_t that may be unsigned, so we can't use
@@ -2148,6 +2165,8 @@ fmt_intarg(ServerOpCodes code, int val)
return fmt_multistate_int(val, multistate_tcpfwd);
case sFingerprintHash:
return ssh_digest_alg_name(val);
case sExposeAuthenticationMethods:
return fmt_multistate_int(val, multistate_exposeauthmeth);
case sProtocol:
switch (val) {
case SSH_PROTO_1:
@@ -2337,6 +2356,7 @@ dump_config(ServerOptions *o)
dump_cfg_fmtint(sStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink);
dump_cfg_fmtint(sUsePrivilegeSeparation, use_privsep);
dump_cfg_fmtint(sFingerprintHash, o->fingerprint_hash);
dump_cfg_fmtint(sExposeAuthenticationMethods, o->expose_auth_methods);

/* string arguments */
dump_cfg_string(sPidFile, o->pid_file);
@@ -48,6 +48,11 @@
#define FORWARD_LOCAL (1<<1)
#define FORWARD_ALLOW (FORWARD_REMOTE|FORWARD_LOCAL)

/* Expose AuthenticationMethods */
#define EXPOSE_AUTHMETH_NEVER 0
#define EXPOSE_AUTHMETH_PAMONLY 1
#define EXPOSE_AUTHMETH_PAMENV 2

#define DEFAULT_AUTH_FAIL_MAX 6 /* Default for MaxAuthTries */
#define DEFAULT_SESSIONS_MAX 10 /* Default for MaxSessions */

@@ -195,6 +200,8 @@ typedef struct {
char *auth_methods[MAX_AUTH_METHODS];

int fingerprint_hash;

int expose_auth_methods; /* EXPOSE_AUTHMETH_* above */
} ServerOptions;

/* Information about the incoming connection as used by Match */
@@ -1154,6 +1154,12 @@ copy_environment(char **source, char ***env, u_int *envsize)
}
*var_val++ = '\0';

if (options.expose_auth_methods < EXPOSE_AUTHMETH_PAMENV &&
strcmp(var_name, "SSH_USER_AUTH") == 0) {
free(var_name);
continue;
}

debug3("Copy environment: %s=%s", var_name, var_val);
child_set_env(env, envsize, var_name, var_val);

@@ -1336,6 +1342,11 @@ do_setup_env(Session *s, const char *shell)
}
#endif /* USE_PAM */

if (options.expose_auth_methods >= EXPOSE_AUTHMETH_PAMENV &&
s->authctxt->auth_details)
child_set_env(&env, &envsize, "SSH_USER_AUTH",
s->authctxt->auth_details);

if (auth_sock_name != NULL)
child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
auth_sock_name);
@@ -2777,6 +2788,9 @@ do_cleanup(Authctxt *authctxt)
if (authctxt == NULL)
return;

free(authctxt->auth_details);
authctxt->auth_details = NULL;

#ifdef USE_PAM
if (options.use_pam) {
sshpam_cleanup();
@@ -124,6 +124,7 @@ int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *);
/* In the server */
OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
int ssh_gssapi_userok(char *name);
char* ssh_gssapi_get_displayname(void);
OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
void ssh_gssapi_do_child(char ***, u_int *);
void ssh_gssapi_cleanup_creds(void);
4 ssh.1
@@ -1396,6 +1396,10 @@ server IP address, and server port number.
This variable contains the original command line if a forced command
is executed.
It can be used to extract the original arguments.
.It Ev SSH_USER_AUTH
This variable contains, for SSH2 only, a comma-separated list of authentication
methods that were successfuly used to authenticate. When possible, these
methods are extended with detailed information on the credential used.
.It Ev SSH_TTY
This is set to the name of the tty (path to the device) associated
with the current shell or command.