diff --git a/.skipped-commit-ids b/.skipped-commit-ids index f4c45722..9febbfbc 100644 --- a/.skipped-commit-ids +++ b/.skipped-commit-ids @@ -17,3 +17,5 @@ f6ae971186ba68d066cd102e57d5b0b2c211a5ee systrace is dead. fe5b31f69a60d47171836911f144acff77810217 Makefile.inc bits 5781670c0578fe89663c9085ed3ba477cf7e7913 Delete sshconnect1.c ea80f445e819719ccdcb237022cacfac990fdc5c Makefile.inc warning flags +b92c93266d8234d493857bb822260dacf4366157 moduli-gen.sh tweak +b25bf747544265b39af74fe0716dc8d9f5b63b95 Updated moduli diff --git a/INSTALL b/INSTALL index 92106bf0..e4865bbb 100644 --- a/INSTALL +++ b/INSTALL @@ -99,7 +99,7 @@ http://www.gnu.org/software/autoconf/ Basic Security Module (BSM): -Native BSM support is know to exist in Solaris from at least 2.5.1, +Native BSM support is known to exist in Solaris from at least 2.5.1, FreeBSD 6.1 and OS X. Alternatively, you may use the OpenBSM implementation (http://www.openbsm.org). diff --git a/auth.c b/auth.c index 2f18cf88..07c3d8ec 100644 --- a/auth.c +++ b/auth.c @@ -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. * @@ -268,21 +268,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 @@ -291,7 +311,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; @@ -310,6 +331,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, @@ -318,10 +344,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 && diff --git a/auth.h b/auth.h index a5cd7504..805318e2 100644 --- a/auth.h +++ b/auth.h @@ -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. @@ -44,6 +44,7 @@ struct ssh; struct sshkey; +struct sshbuf; typedef struct Authctxt Authctxt; typedef struct Authmethod Authmethod; @@ -62,13 +63,17 @@ 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; @@ -76,14 +81,23 @@ struct Authctxt { 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; + + /* 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 */ #ifdef WINDOWS void *auth_token; #endif - struct sshkey **prev_userkeys; - u_int nprev_userkeys; }; + /* * Every authentication method has to handle authentication requests for * non-existing users, or for users that are not allowed to login. In this @@ -122,10 +136,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, @@ -152,9 +174,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 *); diff --git a/auth2-gss.c b/auth2-gss.c index 680d5e71..589283b7 100644 --- a/auth2-gss.c +++ b/auth2-gss.c @@ -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. @@ -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"); @@ -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); @@ -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"); @@ -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); diff --git a/auth2-hostbased.c b/auth2-hostbased.c index 63fe9ae6..92758b38 100644 --- a/auth2-hostbased.c +++ b/auth2-hostbased.c @@ -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. * @@ -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 */ @@ -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); diff --git a/auth2-pubkey.c b/auth2-pubkey.c index db440367..2fc318bb 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -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. * @@ -138,7 +138,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; } @@ -195,7 +195,6 @@ 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; @@ -209,13 +208,10 @@ userauth_pubkey(struct ssh *ssh) sshbuf_len(b), ssh->compat)) == 0) { #endif 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); @@ -245,8 +241,7 @@ 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); @@ -254,44 +249,6 @@ userauth_pubkey(struct ssh *ssh) 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 @@ -1161,36 +1118,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, diff --git a/auth2.c b/auth2.c index cb4c2fd5..862e0996 100644 --- a/auth2.c +++ b/auth2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2.c,v 1.142 2017/05/31 07:00:13 markus Exp $ */ +/* $OpenBSD: auth2.c,v 1.143 2017/06/24 06:34:38 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -55,6 +56,7 @@ #include "ssh-gss.h" #endif #include "monitor_wrap.h" +#include "ssherr.h" /* import */ extern ServerOptions options; @@ -277,6 +279,7 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh) ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); #endif + auth2_authctxt_reset_info(authctxt); authctxt->postponed = 0; authctxt->server_caused_failure = 0; @@ -327,6 +330,10 @@ userauth_finish(struct ssh *ssh, int authenticated, const char *method, /* Log before sending the reply */ auth_log(authctxt, authenticated, partial, method, submethod); + /* Update information exposed to session */ + if (authenticated || partial) + auth2_update_session_info(authctxt, method, submethod); + if (authctxt->postponed) return; @@ -624,4 +631,128 @@ auth2_update_methods_lists(Authctxt *authctxt, const char *method, return 0; } +/* Reset method-specific information */ +void auth2_authctxt_reset_info(Authctxt *authctxt) +{ + sshkey_free(authctxt->auth_method_key); + free(authctxt->auth_method_info); + authctxt->auth_method_key = NULL; + authctxt->auth_method_info = NULL; +} + +/* Record auth method-specific information for logs */ +void +auth2_record_info(Authctxt *authctxt, const char *fmt, ...) +{ + va_list ap; + int i; + + free(authctxt->auth_method_info); + authctxt->auth_method_info = NULL; + + va_start(ap, fmt); + i = vasprintf(&authctxt->auth_method_info, fmt, ap); + va_end(ap); + + if (i < 0 || authctxt->auth_method_info == NULL) + fatal("%s: vasprintf failed", __func__); +} + +/* + * Records a public key used in authentication. This is used for logging + * and to ensure that the same key is not subsequently accepted again for + * multiple authentication. + */ +void +auth2_record_key(Authctxt *authctxt, int authenticated, + const struct sshkey *key) +{ + struct sshkey **tmp, *dup; + int r; + + if ((r = sshkey_demote(key, &dup)) != 0) + fatal("%s: copy key: %s", __func__, ssh_err(r)); + sshkey_free(authctxt->auth_method_key); + authctxt->auth_method_key = dup; + + if (!authenticated) + return; + + /* If authenticated, make sure we don't accept this key again */ + if ((r = sshkey_demote(key, &dup)) != 0) + fatal("%s: copy key: %s", __func__, ssh_err(r)); + if (authctxt->nprev_keys >= INT_MAX || + (tmp = recallocarray(authctxt->prev_keys, authctxt->nprev_keys, + authctxt->nprev_keys + 1, sizeof(*authctxt->prev_keys))) == NULL) + fatal("%s: reallocarray failed", __func__); + authctxt->prev_keys = tmp; + authctxt->prev_keys[authctxt->nprev_keys] = dup; + authctxt->nprev_keys++; + +} + +/* Checks whether a key has already been previously used for authentication */ +int +auth2_key_already_used(Authctxt *authctxt, const struct sshkey *key) +{ + u_int i; + char *fp; + + for (i = 0; i < authctxt->nprev_keys; i++) { + if (sshkey_equal_public(key, authctxt->prev_keys[i])) { + fp = sshkey_fingerprint(authctxt->prev_keys[i], + options.fingerprint_hash, SSH_FP_DEFAULT); + debug3("%s: key already used: %s %s", __func__, + sshkey_type(authctxt->prev_keys[i]), + fp == NULL ? "UNKNOWN" : fp); + free(fp); + return 1; + } + } + return 0; +} + +/* + * Updates authctxt->session_info with details of authentication. Should be + * whenever an authentication method succeeds. + */ +void +auth2_update_session_info(Authctxt *authctxt, const char *method, + const char *submethod) +{ + int r; + + if (authctxt->session_info == NULL) { + if ((authctxt->session_info = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new", __func__); + } + + /* Append method[/submethod] */ + if ((r = sshbuf_putf(authctxt->session_info, "%s%s%s", + method, submethod == NULL ? "" : "/", + submethod == NULL ? "" : submethod)) != 0) + fatal("%s: append method: %s", __func__, ssh_err(r)); + + /* Append key if present */ + if (authctxt->auth_method_key != NULL) { + if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 || + (r = sshkey_format_text(authctxt->auth_method_key, + authctxt->session_info)) != 0) + fatal("%s: append key: %s", __func__, ssh_err(r)); + } + + if (authctxt->auth_method_info != NULL) { + /* Ensure no ambiguity here */ + if (strchr(authctxt->auth_method_info, '\n') != NULL) + fatal("%s: auth_method_info contains \\n", __func__); + if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 || + (r = sshbuf_putf(authctxt->session_info, "%s", + authctxt->auth_method_info)) != 0) { + fatal("%s: append method info: %s", + __func__, ssh_err(r)); + } + } + if ((r = sshbuf_put_u8(authctxt->session_info, '\n')) != 0) + fatal("%s: append: %s", __func__, ssh_err(r)); +} diff --git a/authfd.c b/authfd.c index ddd28be6..2e94140f 100644 --- a/authfd.c +++ b/authfd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.c,v 1.103 2017/05/05 10:42:49 naddy Exp $ */ +/* $OpenBSD: authfd.c,v 1.104 2017/06/28 01:09:22 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -331,7 +331,7 @@ ssh_free_identitylist(struct ssh_identitylist *idl) /* encode signature algoritm in flag bits, so we can keep the msg format */ static u_int -agent_encode_alg(struct sshkey *key, const char *alg) +agent_encode_alg(const struct sshkey *key, const char *alg) { if (alg != NULL && key->type == KEY_RSA) { if (strcmp(alg, "rsa-sha2-256") == 0) @@ -344,7 +344,7 @@ agent_encode_alg(struct sshkey *key, const char *alg) /* ask agent to sign data, returns err.h code on error, 0 on success */ int -ssh_agent_sign(int sock, struct sshkey *key, +ssh_agent_sign(int sock, const struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, const char *alg, u_int compat) { diff --git a/authfd.h b/authfd.h index 5804f18d..97cdc5de 100644 --- a/authfd.h +++ b/authfd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.h,v 1.40 2017/05/05 10:42:49 naddy Exp $ */ +/* $OpenBSD: authfd.h,v 1.41 2017/06/28 01:09:22 djm Exp $ */ /* * Author: Tatu Ylonen @@ -38,7 +38,7 @@ int ssh_remove_all_identities(int sock, int version); int ssh_decrypt_challenge(int sock, struct sshkey* key, BIGNUM *challenge, u_char session_id[16], u_char response[16]); -int ssh_agent_sign(int sock, struct sshkey *key, +int ssh_agent_sign(int sock, const struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, const char *alg, u_int compat); diff --git a/clientloop.c b/clientloop.c index 284b08e6..c623131a 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.299 2017/05/31 09:15:42 deraadt Exp $ */ +/* $OpenBSD: clientloop.c,v 1.300 2017/06/23 07:24:48 mestre Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1259,7 +1259,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) } else { debug("pledge: network"); - if (pledge("stdio unix inet dns tty", NULL) == -1) + if (pledge("stdio unix inet dns proc tty", NULL) == -1) fatal("%s pledge(): %s", __func__, strerror(errno)); } diff --git a/configure.ac b/configure.ac index 46f7d495..18079acb 100644 --- a/configure.ac +++ b/configure.ac @@ -3814,6 +3814,8 @@ OSSH_CHECK_HEADER_FOR_FIELD([ut_time], [utmpx.h], [HAVE_TIME_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_tv], [utmpx.h], [HAVE_TV_IN_UTMPX]) AC_CHECK_MEMBERS([struct stat.st_blksize]) +AC_CHECK_MEMBERS([struct stat.st_mtim]) +AC_CHECK_MEMBERS([struct stat.st_mtime]) AC_CHECK_MEMBERS([struct passwd.pw_gecos, struct passwd.pw_class, struct passwd.pw_change, struct passwd.pw_expire], [], [], [[ diff --git a/contrib/aix/README b/contrib/aix/README index 2a299350..4a11ae70 100644 --- a/contrib/aix/README +++ b/contrib/aix/README @@ -35,7 +35,7 @@ The script treats all packages as USR packages (not ROOT+USR when appropriate). It seems to work, though...... If there are any patches to this that have not yet been integrated they -may be found at http://www.zip.com.au/~dtucker/openssh/. +may be found at http://www.dtucker.net/openssh/. Disclaimer: diff --git a/contrib/win32/openssh/OpenSSHUtils.psm1 b/contrib/win32/openssh/OpenSSHUtils.psm1 index b32f096e..a68fd852 100644 --- a/contrib/win32/openssh/OpenSSHUtils.psm1 +++ b/contrib/win32/openssh/OpenSSHUtils.psm1 @@ -342,7 +342,14 @@ function Repair-FilePermissionInternal { foreach($a in $acl.Access) { - $IdentityReferenceSid = Get-UserSid -User $a.IdentityReference + if ($a.IdentityReference -is [System.Security.Principal.SecurityIdentifier]) + { + $IdentityReferenceSid = $a.IdentityReference + } + Else + { + $IdentityReferenceSid = Get-UserSid -User $a.IdentityReference + } if($IdentityReferenceSid -eq $null) { $idRefShortValue = ($a.IdentityReference.Value).split('\')[-1] diff --git a/contrib/win32/openssh/install-sshd.ps1 b/contrib/win32/openssh/install-sshd.ps1 index 5ecc6bae..f7b0df18 100644 --- a/contrib/win32/openssh/install-sshd.ps1 +++ b/contrib/win32/openssh/install-sshd.ps1 @@ -1,6 +1,7 @@ # @manojampalam - authored initial script # @friism - Fixed issue with invalid SDDL on Set-Acl # @manojampalam - removed ntrights.exe dependency +# @bingbing8 - removed secedit.exe dependency $scriptpath = $MyInvocation.MyCommand.Path $scriptdir = Split-Path $scriptpath @@ -10,68 +11,240 @@ $sshagentpath = Join-Path $scriptdir "ssh-agent.exe" $logsdir = Join-Path $scriptdir "logs" $sshdAccount = "NT SERVICE\SSHD" +$sshdSid = "S-1-5-80-3847866527-469524349-687026318-516638107-1125189541" -#Idea borrowed from http://sqldbamusings.blogspot.com/2012/03/powershell-adding-accounts-to-local.html -function Add-Privilege +#Idea borrowed from https://gallery.technet.microsoft.com/scriptcenter/Grant-Revoke-Query-user-26e259b0 +$definition = @' +using System; + +namespace MyLsaWrapper { - param( - [string] $Account, - - [ValidateSet("SeAssignPrimaryTokenPrivilege", "SeServiceLogonRight")] - [string] $Privilege - ) + using System.Runtime.InteropServices; + using System.Security; + using System.ComponentModel; + using System.Security.Principal; + + using LSA_HANDLE = IntPtr; - #Get $Account SID - $account_sid = $null - try + [StructLayout(LayoutKind.Sequential)] + struct LSA_OBJECT_ATTRIBUTES { - $ntprincipal = new-object System.Security.Principal.NTAccount "$Account" - $sid = $ntprincipal.Translate([System.Security.Principal.SecurityIdentifier]) - $account_sid = $sid.Value.ToString() - } - catch + internal int Length; + internal IntPtr RootDirectory; + internal IntPtr ObjectName; + internal int Attributes; + internal IntPtr SecurityDescriptor; + internal IntPtr SecurityQualityOfService; + } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + struct LSA_UNICODE_STRING { - Throw 'Unable to resolve '+ $Account + internal ushort Length; + internal ushort MaximumLength; + [MarshalAs(UnmanagedType.LPWStr)] + internal string Buffer; } + sealed class Win32Sec + { + [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern uint LsaOpenPolicy( + LSA_UNICODE_STRING[] SystemName, + ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, + int AccessMask, + out IntPtr PolicyHandle + ); - #Prepare policy settings file to be applied - $settings_to_export = [System.IO.Path]::GetTempFileName() - "[Unicode]" | Set-Content $settings_to_export -Encoding Unicode - "Unicode=yes" | Add-Content $settings_to_export -Force -WhatIf:$false - "[Version]" | Add-Content $settings_to_export -Force -WhatIf:$false - "signature=`"`$CHICAGO`$`"" | Add-Content $settings_to_export -Force -WhatIf:$false - "Revision=1" | Add-Content $settings_to_export -Force -WhatIf:$false - "[Privilege Rights]" | Add-Content $settings_to_export -Force -WhatIf:$false - - #Get Current policy settings - $imported_settings = [System.IO.Path]::GetTempFileName() - secedit.exe /export /areas USER_RIGHTS /cfg "$($imported_settings)" > $null - - if (-not(Test-Path $imported_settings)) { - Throw "Unable to import current security policy settings" + [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern uint LsaAddAccountRights( + LSA_HANDLE PolicyHandle, + IntPtr pSID, + LSA_UNICODE_STRING[] UserRights, + int CountOfRights + ); + + [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern uint LsaRemoveAccountRights( + LSA_HANDLE PolicyHandle, + IntPtr pSID, + bool AllRights, + LSA_UNICODE_STRING[] UserRights, + int CountOfRights + ); + + [DllImport("advapi32")] + internal static extern int LsaNtStatusToWinError(int NTSTATUS); + + [DllImport("advapi32")] + internal static extern int LsaClose(IntPtr PolicyHandle); } - #find current assigned accounts to $Privilege and add it to $settings_to_export - $current_settings = Get-Content $imported_settings -Encoding Unicode - $existing_setting = $null - foreach ($setting in $current_settings) { - if ($setting -like "$Privilege`*") { - $existing_setting = $setting - } + internal sealed class Sid : IDisposable + { + public IntPtr pSid = IntPtr.Zero; + public System.Security.Principal.SecurityIdentifier sid = null; + + public Sid(string account) + { + try { sid = new SecurityIdentifier(account); } + catch { sid = (SecurityIdentifier)(new NTAccount(account)).Translate(typeof(SecurityIdentifier)); } + Byte[] buffer = new Byte[sid.BinaryLength]; + sid.GetBinaryForm(buffer, 0); + + pSid = Marshal.AllocHGlobal(sid.BinaryLength); + Marshal.Copy(buffer, 0, pSid, sid.BinaryLength); + } + + public void Dispose() + { + if (pSid != IntPtr.Zero) + { + Marshal.FreeHGlobal(pSid); + pSid = IntPtr.Zero; + } + GC.SuppressFinalize(this); + } + ~Sid() { Dispose(); } } - #Add $account_sid to list - if ($existing_setting -eq $null) { - $Privilege + " = *" + $account_sid | Add-Content $settings_to_export -Force -WhatIf:$false + public sealed class LsaWrapper : IDisposable + { + enum Access : int + { + POLICY_READ = 0x20006, + POLICY_ALL_ACCESS = 0x00F0FFF, + POLICY_EXECUTE = 0X20801, + POLICY_WRITE = 0X207F8 + } + const uint STATUS_ACCESS_DENIED = 0xc0000022; + const uint STATUS_INSUFFICIENT_RESOURCES = 0xc000009a; + const uint STATUS_NO_MEMORY = 0xc0000017; + const uint STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034; + const uint STATUS_NO_MORE_ENTRIES = 0x8000001a; + + IntPtr lsaHandle; + + public LsaWrapper() : this(null) { } // local system if systemName is null + public LsaWrapper(string systemName) + { + LSA_OBJECT_ATTRIBUTES lsaAttr; + lsaAttr.RootDirectory = IntPtr.Zero; + lsaAttr.ObjectName = IntPtr.Zero; + lsaAttr.Attributes = 0; + lsaAttr.SecurityDescriptor = IntPtr.Zero; + lsaAttr.SecurityQualityOfService = IntPtr.Zero; + lsaAttr.Length = Marshal.SizeOf(typeof(LSA_OBJECT_ATTRIBUTES)); + lsaHandle = IntPtr.Zero; + LSA_UNICODE_STRING[] system = null; + if (systemName != null) + { + system = new LSA_UNICODE_STRING[1]; + system[0] = InitLsaString(systemName); + } + + uint ret = Win32Sec.LsaOpenPolicy(system, ref lsaAttr, (int)Access.POLICY_ALL_ACCESS, out lsaHandle); + if (ret == 0) return; + if (ret == STATUS_ACCESS_DENIED) throw new UnauthorizedAccessException(); + if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) throw new OutOfMemoryException(); + throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret)); + } + + public void AddPrivilege(string account, string privilege) + { + uint ret = 0; + using (Sid sid = new Sid(account)) + { + LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1]; + privileges[0] = InitLsaString(privilege); + ret = Win32Sec.LsaAddAccountRights(lsaHandle, sid.pSid, privileges, 1); + } + if (ret == 0) return; + if (ret == STATUS_ACCESS_DENIED) throw new UnauthorizedAccessException(); + if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) throw new OutOfMemoryException(); + throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret)); + } + + public void RemovePrivilege(string account, string privilege) + { + uint ret = 0; + using (Sid sid = new Sid(account)) + { + LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1]; + privileges[0] = InitLsaString(privilege); + ret = Win32Sec.LsaRemoveAccountRights(lsaHandle, sid.pSid, false, privileges, 1); + } + if (ret == 0) return; + if (ret == STATUS_ACCESS_DENIED) throw new UnauthorizedAccessException(); + if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) throw new OutOfMemoryException(); + throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret)); + } + + public void Dispose() + { + if (lsaHandle != IntPtr.Zero) + { + Win32Sec.LsaClose(lsaHandle); + lsaHandle = IntPtr.Zero; + } + GC.SuppressFinalize(this); + } + ~LsaWrapper() { Dispose(); } + + // helper functions: + static LSA_UNICODE_STRING InitLsaString(string s) + { + // Unicode strings max. 32KB + if (s.Length > 0x7ffe) throw new ArgumentException("String too long"); + LSA_UNICODE_STRING lus = new LSA_UNICODE_STRING(); + lus.Buffer = s; + lus.Length = (ushort)(s.Length * sizeof(char)); + lus.MaximumLength = (ushort)(lus.Length + sizeof(char)); + return lus; + } } - else + public class LsaWrapperCaller { - $existing_setting + ",*" + $account_sid | Add-Content $settings_to_export -Force -WhatIf:$false + public static void AddPrivilege(string account, string privilege) + { + using (LsaWrapper lsaWrapper = new LsaWrapper()) + { + lsaWrapper.AddPrivilege(account, privilege); + } + } + public static void RemovePrivilege(string account, string privilege) + { + using (LsaWrapper lsaWrapper = new LsaWrapper()) + { + lsaWrapper.RemovePrivilege(account, privilege); + } + } } +} +'@ +$references = @() +if(($psversiontable.Containskey("psedition")) -and ($psversiontable.PSEdition -ieq "core")) +{ + $references = "System.Security.Principal.Windows", "Microsoft.Win32.Primitives" +} + +try { + $null = [MyLsaWrapper.LsaWrapperCaller] +} +catch { + $types = Add-Type $definition -ref $references -WarningAction SilentlyContinue -ErrorAction SilentlyContinue +} + - #export - secedit.exe /configure /db "secedit.sdb" /cfg "$($settings_to_export)" /areas USER_RIGHTS > $null +function Add-Privilege +{ + param( + [ValidateNotNullOrEmpty()] + [string] $Account, + + [ValidateSet("SeAssignPrimaryTokenPrivilege", "SeServiceLogonRight")] + [string] $Privilege + ) + [MyLsaWrapper.LsaWrapperCaller]::AddPrivilege($Account, $Privilege) } @@ -98,7 +271,7 @@ New-Service -Name sshd -BinaryPathName $sshdpath -Description "SSH Daemon" -Star sc.exe config sshd obj= $sshdAccount sc.exe privs sshd SeAssignPrimaryTokenPrivilege -Add-Privilege -Account $sshdAccount -Privilege SeAssignPrimaryTokenPrivilege +Add-Privilege -Account $sshdSid -Privilege SeAssignPrimaryTokenPrivilege if(-not (test-path $logsdir -PathType Container)) { diff --git a/contrib/win32/win32compat/ssh-agent/agentconfig.c b/contrib/win32/win32compat/ssh-agent/agentconfig.c index 954e4e06..dddada98 100644 --- a/contrib/win32/win32compat/ssh-agent/agentconfig.c +++ b/contrib/win32/win32compat/ssh-agent/agentconfig.c @@ -56,6 +56,19 @@ struct passwd *privsep_pw = NULL; static char *config_file_name = _PATH_SERVER_CONFIG_FILE; int auth_sock = -1; +int +auth2_key_already_used(Authctxt *authctxt, const struct sshkey *key) +{ + return 0; +} + +void +auth2_record_key(Authctxt *authctxt, int authenticated, + const struct sshkey *key) +{ + return; +} + int auth2_methods_valid(const char * c, int i) { return 1; diff --git a/contrib/win32/win32compat/ssh-agent/keyagent-request.c b/contrib/win32/win32compat/ssh-agent/keyagent-request.c index affcbc9e..18c05069 100644 --- a/contrib/win32/win32compat/ssh-agent/keyagent-request.c +++ b/contrib/win32/win32compat/ssh-agent/keyagent-request.c @@ -332,7 +332,7 @@ process_remove_all(struct sshbuf* request, struct sshbuf* response, struct agent int r = 0; if (get_user_root(con, &user_root) != 0 || - RegOpenKeyExW(user_root, SSH_ROOT, 0, + RegOpenKeyExW(user_root, SSH_AGENT_ROOT, 0, DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_WOW64_64KEY, &root) != 0) { goto done; } diff --git a/defines.h b/defines.h index 4c1b6a8d..1d0ad318 100644 --- a/defines.h +++ b/defines.h @@ -519,6 +519,13 @@ struct winsize { } #endif +#ifndef timespeccmp +#define timespeccmp(tsp, usp, cmp) \ + (((tsp)->tv_sec == (usp)->tv_sec) ? \ + ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ + ((tsp)->tv_sec cmp (usp)->tv_sec)) +#endif + #ifndef __P # define __P(x) x #endif diff --git a/gss-serv.c b/gss-serv.c index 53993d67..6cae720e 100644 --- a/gss-serv.c +++ b/gss-serv.c @@ -1,4 +1,4 @@ -/* $OpenBSD: gss-serv.c,v 1.29 2015/05/22 03:50:02 djm Exp $ */ +/* $OpenBSD: gss-serv.c,v 1.30 2017/06/24 06:34:38 djm Exp $ */ /* * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. @@ -393,4 +393,13 @@ ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) return (ctx->major); } +/* Privileged */ +const char *ssh_gssapi_displayname(void) +{ + if (gssapi_client.displayname.length == 0 || + gssapi_client.displayname.value == NULL) + return NULL; + return (char *)gssapi_client.displayname.value; +} + #endif diff --git a/includes.h b/includes.h index 497a038b..0fd71792 100644 --- a/includes.h +++ b/includes.h @@ -93,6 +93,9 @@ #ifdef HAVE_SYS_SYSMACROS_H # include /* For MIN, MAX, etc */ #endif +#ifdef HAVE_SYS_TIME_H +# include /* for timespeccmp if present */ +#endif #ifdef HAVE_SYS_MMAN_H #include /* for MAP_ANONYMOUS */ #endif diff --git a/kex.c b/kex.c index cf44fbc0..d5d5a9da 100644 --- a/kex.c +++ b/kex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.c,v 1.133 2017/05/30 14:23:52 markus Exp $ */ +/* $OpenBSD: kex.c,v 1.134 2017/06/13 12:13:59 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * @@ -378,7 +378,9 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) { struct kex *kex = ssh->kex; u_int32_t i, ninfo; - char *name, *val, *found; + char *name, *found; + u_char *val; + size_t vlen; int r; debug("SSH2_MSG_EXT_INFO received"); @@ -388,12 +390,17 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) for (i = 0; i < ninfo; i++) { if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) return r; - if ((r = sshpkt_get_cstring(ssh, &val, NULL)) != 0) { + if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) { free(name); return r; } - debug("%s: %s=<%s>", __func__, name, val); if (strcmp(name, "server-sig-algs") == 0) { + /* Ensure no \0 lurking in value */ + if (memchr(val, '\0', vlen) != NULL) { + error("%s: nul byte in %s", __func__, name); + return SSH_ERR_INVALID_FORMAT; + } + debug("%s: %s=<%s>", __func__, name, val); found = match_list("rsa-sha2-256", val, NULL); if (found) { kex->rsa_sha2 = 256; @@ -404,7 +411,8 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) kex->rsa_sha2 = 512; free(found); } - } + } else + debug("%s: %s (unrecognised)", __func__, name); free(name); free(val); } diff --git a/monitor.c b/monitor.c index 8897f6a8..8a7897bd 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.171 2017/05/31 10:04:29 markus Exp $ */ +/* $OpenBSD: monitor.c,v 1.172 2017/06/24 06:34:38 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -308,6 +308,8 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) partial = 0; auth_method = "unknown"; auth_submethod = NULL; + auth2_authctxt_reset_info(authctxt); + authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1); /* Special handling for multiple required authentications */ @@ -347,6 +349,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) auth_method, auth_submethod); if (!partial && !authenticated) authctxt->failures++; + if (authenticated || partial) { + auth2_update_session_info(authctxt, + auth_method, auth_submethod); + } } } @@ -1147,12 +1153,11 @@ mm_answer_keyallowed(int sock, Buffer *m) switch (type) { case MM_USERKEY: allowed = options.pubkey_authentication && - !auth2_userkey_already_used(authctxt, key) && + !auth2_key_already_used(authctxt, key) && match_pattern_list(sshkey_ssh_name(key), options.pubkey_key_types, 0) == 1 && user_key_allowed(authctxt->pw, key, pubkey_auth_attempt); - pubkey_auth_info(authctxt, key, NULL); auth_method = "publickey"; if (options.pubkey_authentication && (!pubkey_auth_attempt || allowed != 1)) @@ -1160,11 +1165,12 @@ mm_answer_keyallowed(int sock, Buffer *m) break; case MM_HOSTKEY: allowed = options.hostbased_authentication && + !auth2_key_already_used(authctxt, key) && match_pattern_list(sshkey_ssh_name(key), options.hostbased_key_types, 0) == 1 && hostbased_key_allowed(authctxt->pw, cuser, chost, key); - pubkey_auth_info(authctxt, key, + auth2_record_info(authctxt, "client user \"%.100s\", client host \"%.100s\"", cuser, chost); auth_method = "hostbased"; @@ -1175,11 +1181,10 @@ mm_answer_keyallowed(int sock, Buffer *m) } } - debug3("%s: key %p is %s", - __func__, key, allowed ? "allowed" : "not allowed"); + debug3("%s: key is %s", __func__, allowed ? "allowed" : "not allowed"); - if (key != NULL) - key_free(key); + auth2_record_key(authctxt, 0, key); + sshkey_free(key); /* clear temporarily storage (used by verify) */ monitor_reset_key_state(); @@ -1353,10 +1358,12 @@ mm_answer_keyverify(int sock, struct sshbuf *m) switch (key_blobtype) { case MM_USERKEY: valid_data = monitor_valid_userblob(data, datalen); + auth_method = "publickey"; break; case MM_HOSTKEY: valid_data = monitor_valid_hostbasedblob(data, datalen, hostbased_cuser, hostbased_chost); + auth_method = "hostbased"; break; default: valid_data = 0; @@ -1367,23 +1374,17 @@ mm_answer_keyverify(int sock, struct sshbuf *m) ret = sshkey_verify(key, signature, signaturelen, data, datalen, active_state->compat); - debug3("%s: key %p signature %s", - __func__, key, (ret == 0) ? "verified" : "unverified"); - - /* If auth was successful then record key to ensure it isn't reused */ - if (ret == 0 && key_blobtype == MM_USERKEY) - auth2_record_userkey(authctxt, key); - else - sshkey_free(key); + debug3("%s: %s %p signature %s", __func__, auth_method, key, + (ret == 0) ? "verified" : "unverified"); + auth2_record_key(authctxt, ret == 0, key); free(blob); free(signature); free(data); - auth_method = key_blobtype == MM_USERKEY ? "publickey" : "hostbased"; - monitor_reset_key_state(); + sshkey_free(key); sshbuf_reset(m); /* encode ret != 0 as positive integer, since we're sending u32 */ @@ -1799,6 +1800,7 @@ int mm_answer_gss_userok(int sock, Buffer *m) { int authenticated; + const char *displayname; if (!options.gss_authentication) fatal("%s: GSSAPI authentication not enabled", __func__); @@ -1813,6 +1815,9 @@ mm_answer_gss_userok(int sock, Buffer *m) auth_method = "gssapi-with-mic"; + if ((displayname = ssh_gssapi_displayname()) != NULL) + auth2_record_info(authctxt, "%s", displayname); + /* Monitor loop will terminate if authenticated */ return (authenticated); } diff --git a/mux.c b/mux.c index 2d6639c5..3dde4da4 100644 --- a/mux.c +++ b/mux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mux.c,v 1.64 2017/01/21 11:32:04 guenther Exp $ */ +/* $OpenBSD: mux.c,v 1.65 2017/06/09 06:47:13 djm Exp $ */ /* * Copyright (c) 2002-2008 Damien Miller * @@ -1570,31 +1570,38 @@ mux_client_hello_exchange(int fd) { Buffer m; u_int type, ver; + int ret = -1; buffer_init(&m); buffer_put_int(&m, MUX_MSG_HELLO); buffer_put_int(&m, SSHMUX_VER); /* no extensions */ - if (mux_client_write_packet(fd, &m) != 0) - fatal("%s: write packet: %s", __func__, strerror(errno)); + if (mux_client_write_packet(fd, &m) != 0) { + debug("%s: write packet: %s", __func__, strerror(errno)); + goto out; + } buffer_clear(&m); /* Read their HELLO */ if (mux_client_read_packet(fd, &m) != 0) { - buffer_free(&m); - return -1; + debug("%s: read packet failed", __func__); + goto out; } type = buffer_get_int(&m); - if (type != MUX_MSG_HELLO) - fatal("%s: expected HELLO (%u) received %u", + if (type != MUX_MSG_HELLO) { + error("%s: expected HELLO (%u) received %u", __func__, MUX_MSG_HELLO, type); + goto out; + } ver = buffer_get_int(&m); - if (ver != SSHMUX_VER) - fatal("Unsupported multiplexing protocol version %d " + if (ver != SSHMUX_VER) { + error("Unsupported multiplexing protocol version %d " "(expected %d)", ver, SSHMUX_VER); + goto out; + } debug2("%s: master version %u", __func__, ver); /* No extensions are presently defined */ while (buffer_len(&m) > 0) { @@ -1605,8 +1612,11 @@ mux_client_hello_exchange(int fd) free(name); free(value); } + /* success */ + ret = 0; + out: buffer_free(&m); - return 0; + return ret; } static u_int diff --git a/openbsd-compat/recallocarray.c b/openbsd-compat/recallocarray.c index c281f75e..3e1156ce 100644 --- a/openbsd-compat/recallocarray.c +++ b/openbsd-compat/recallocarray.c @@ -22,7 +22,9 @@ #include #include +#ifdef HAVE_STDINT_H #include +#endif #include #include diff --git a/packet.c b/packet.c index 7c748688..9458ffdb 100644 --- a/packet.c +++ b/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.260 2017/06/06 09:12:17 dtucker Exp $ */ +/* $OpenBSD: packet.c,v 1.262 2017/06/24 06:38:11 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -901,6 +901,7 @@ ssh_set_newkeys(struct ssh *ssh, int mode) /* * The 2^(blocksize*2) limit is too expensive for 3DES, * so enforce a 1GB limit for small blocksizes. + * See RFC4344 section 3.2. */ if (enc->block_size >= 16) *max_blocks = (u_int64_t)1 << (enc->block_size*2); @@ -944,7 +945,10 @@ ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len) (int64_t)state->rekey_time + state->rekey_interval <= monotime()) return 1; - /* Always rekey when MAX_PACKETS sent in either direction */ + /* + * Always rekey when MAX_PACKETS sent in either direction + * As per RFC4344 section 3.1 we do this after 2^31 packets. + */ if (state->p_send.packets > MAX_PACKETS || state->p_read.packets > MAX_PACKETS) return 1; @@ -2218,9 +2222,7 @@ newkeys_to_blob(struct sshbuf *m, struct ssh *ssh, int mode) return r; if ((b = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; - /* The cipher struct is constant and shared, you export pointer */ if ((r = sshbuf_put_cstring(b, enc->name)) != 0 || - (r = sshbuf_put(b, &enc->cipher, sizeof(enc->cipher))) != 0 || (r = sshbuf_put_u32(b, enc->enabled)) != 0 || (r = sshbuf_put_u32(b, enc->block_size)) != 0 || (r = sshbuf_put_string(b, enc->key, enc->key_len)) != 0 || @@ -2294,12 +2296,15 @@ newkeys_from_blob(struct sshbuf *m, struct ssh *ssh, int mode) comp = &newkey->comp; if ((r = sshbuf_get_cstring(b, &enc->name, NULL)) != 0 || - (r = sshbuf_get(b, &enc->cipher, sizeof(enc->cipher))) != 0 || (r = sshbuf_get_u32(b, (u_int *)&enc->enabled)) != 0 || (r = sshbuf_get_u32(b, &enc->block_size)) != 0 || (r = sshbuf_get_string(b, &enc->key, &keylen)) != 0 || (r = sshbuf_get_string(b, &enc->iv, &ivlen)) != 0) goto out; + if ((enc->cipher = cipher_by_name(enc->name)) == NULL) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } if (cipher_authlen(enc->cipher) == 0) { if ((r = sshbuf_get_cstring(b, &mac->name, NULL)) != 0) goto out; @@ -2317,11 +2322,6 @@ newkeys_from_blob(struct sshbuf *m, struct ssh *ssh, int mode) if ((r = sshbuf_get_u32(b, &comp->type)) != 0 || (r = sshbuf_get_cstring(b, &comp->name, NULL)) != 0) goto out; - if (enc->name == NULL || - cipher_by_name(enc->name) != enc->cipher) { - r = SSH_ERR_INVALID_FORMAT; - goto out; - } if (sshbuf_len(b) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; diff --git a/regress/Makefile b/regress/Makefile index f968c416..7d50f9cf 100644 --- a/regress/Makefile +++ b/regress/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.94 2016/12/16 03:51:19 dtucker Exp $ +# $OpenBSD: Makefile,v 1.95 2017/06/24 06:35:24 djm Exp $ REGRESS_TARGETS= unit t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 t-exec tests: prep $(REGRESS_TARGETS) @@ -79,7 +79,8 @@ LTESTS= connect \ principals-command \ cert-file \ cfginclude \ - allow-deny-users + allow-deny-users \ + authinfo # dhgex \ diff --git a/regress/authinfo.sh b/regress/authinfo.sh new file mode 100644 index 00000000..e725296c --- /dev/null +++ b/regress/authinfo.sh @@ -0,0 +1,17 @@ +# $OpenBSD: authinfo.sh,v 1.1 2017/06/24 06:35:24 djm Exp $ +# Placed in the Public Domain. + +tid="authinfo" + +# Ensure the environment variable doesn't leak when ExposeAuthInfo=no. +verbose "ExposeAuthInfo=no" +env SSH_USER_AUTH=blah ${SSH} -F $OBJ/ssh_proxy x \ + 'test -z "$SSH_USER_AUTH"' || fail "SSH_USER_AUTH present" + +verbose "ExposeAuthInfo=yes" +echo ExposeAuthInfo=yes >> $OBJ/sshd_proxy +${SSH} -F $OBJ/ssh_proxy x \ + 'grep ^publickey "$SSH_USER_AUTH" /dev/null >/dev/null' || + fail "ssh with ExposeAuthInfo failed" + +# XXX test multiple auth and key contents diff --git a/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 b/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 index d1f43fed..41069399 100644 --- a/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 +++ b/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 @@ -146,7 +146,7 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "^Permission denied" + $matches = Get-Content $filePath | Select-String -pattern "Permission denied" $matches.Count | Should BeGreaterThan 2 #Cleanup @@ -165,7 +165,7 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "^Permission denied" + $matches = Get-Content $filePath | Select-String -pattern "Permission denied" $matches.Count | Should BeGreaterThan 2 #Cleanup @@ -181,7 +181,7 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow ssh -p $port -E $FilePath -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "^Permission denied" + $matches = Get-Content $filePath | Select-String -pattern "Permission denied" $matches.Count | Should BeGreaterThan 2 #Cleanup @@ -197,7 +197,7 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "^Permission denied" + $matches = Get-Content $filePath | Select-String -pattern "Permission denied" $matches.Count | Should BeGreaterThan 2 #Cleanup diff --git a/regress/pesterTests/SCP.Tests.ps1 b/regress/pesterTests/SCP.Tests.ps1 index b2d9ff62..030fad11 100644 --- a/regress/pesterTests/SCP.Tests.ps1 +++ b/regress/pesterTests/SCP.Tests.ps1 @@ -37,38 +37,35 @@ Describe "Tests for scp command" -Tags "CI" { Title = 'Simple copy local file to local file' Source = $SourceFilePath Destination = $DestinationFilePath - Options = "-P $port " }, @{ Title = 'Simple copy local file to remote file' Source = $SourceFilePath Destination = "test_target:$DestinationFilePath" - Options = "-P $port -S $sshcmd" + Options = "-S '$sshcmd'" }, @{ Title = 'Simple copy remote file to local file' Source = "test_target:$SourceFilePath" Destination = $DestinationFilePath - Options = "-P $port -p -c aes128-ctr -C" + Options = "-p -c aes128-ctr -C" }, @{ Title = 'Simple copy local file to local dir' Source = $SourceFilePath Destination = $DestinationDir - Options = "-P $port " }, @{ Title = 'simple copy local file to remote dir' Source = $SourceFilePath Destination = "test_target:$DestinationDir" - Options = "-P $port -C -q" - }<#, + Options = "-C -q" + }, @{ Title = 'simple copy remote file to local dir' Source = "test_target:$SourceFilePath" Destination = $DestinationDir - Options = "-P $port " - }#> + } ) $testData1 = @( @@ -76,7 +73,7 @@ Describe "Tests for scp command" -Tags "CI" { Title = 'copy from local dir to remote dir' Source = $sourceDir Destination = "test_target:$DestinationDir" - Options = "-P $port -r -p -c aes128-ctr" + Options = "-r -p -c aes128-ctr" }, @{ Title = 'copy from local dir to local dir' @@ -88,7 +85,7 @@ Describe "Tests for scp command" -Tags "CI" { Title = 'copy from remote dir to local dir' Source = "test_target:$sourceDir" Destination = $DestinationDir - Options = "-P $port -C -r -q" + Options = "-C -r -q" } ) @@ -150,7 +147,7 @@ Describe "Tests for scp command" -Tags "CI" { It 'File copy: ' -TestCases:$testData { - param([string]$Title, $Source, $Destination, $Options) + param([string]$Title, $Source, $Destination, [string]$Options) iex "scp $Options $Source $Destination" $LASTEXITCODE | Should Be 0 @@ -168,7 +165,7 @@ Describe "Tests for scp command" -Tags "CI" { } It 'Directory recursive copy: <Title> ' -TestCases:$testData1 { - param([string]$Title, $Source, $Destination, $Options) + param([string]$Title, $Source, $Destination, [string]$Options) iex "scp $Options $Source $Destination" $LASTEXITCODE | Should Be 0 diff --git a/sandbox-solaris.c b/sandbox-solaris.c index 343a0102..56ddb9a9 100644 --- a/sandbox-solaris.c +++ b/sandbox-solaris.c @@ -61,6 +61,12 @@ ssh_sandbox_init(struct monitor *monitor) if (priv_delset(box->pset, PRIV_FILE_LINK_ANY) != 0 || #ifdef PRIV_NET_ACCESS priv_delset(box->pset, PRIV_NET_ACCESS) != 0 || +#endif +#ifdef PRIV_DAX_ACCESS + priv_delset(box->pset, PRIV_DAX_ACCESS) != 0 || +#endif +#ifdef PRIV_SYS_IB_INFO + priv_delset(box->pset, PRIV_SYS_IB_INFO) != 0 || #endif priv_delset(box->pset, PRIV_PROC_EXEC) != 0 || priv_delset(box->pset, PRIV_PROC_FORK) != 0 || diff --git a/servconf.c b/servconf.c index 32d6ea38..6907bf5e 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.308 2017/05/17 01:24:17 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.309 2017/06/24 06:34:38 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved @@ -164,6 +164,7 @@ initialize_server_options(ServerOptions *options) options->version_addendum = NULL; options->fingerprint_hash = -1; options->disable_forwarding = -1; + options->expose_userauth_info = -1; } /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */ @@ -333,6 +334,8 @@ fill_default_server_options(ServerOptions *options) options->fingerprint_hash = SSH_FP_HASH_DEFAULT; if (options->disable_forwarding == -1) options->disable_forwarding = 0; + if (options->expose_userauth_info == -1) + options->expose_userauth_info = 0; assemble_algorithms(options); @@ -418,6 +421,7 @@ typedef enum { sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, sStreamLocalBindMask, sStreamLocalBindUnlink, sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding, + sExposeAuthInfo, sDeprecated, sIgnore, sUnsupported } ServerOpCodes; @@ -561,6 +565,7 @@ static struct { { "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL }, { "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL }, { "disableforwarding", sDisableForwarding, SSHCFG_ALL }, + { "exposeauthinfo", sExposeAuthInfo, SSHCFG_ALL }, { NULL, sBadOption, 0 } }; @@ -1841,6 +1846,10 @@ process_server_config_line(ServerOptions *options, char *line, options->fingerprint_hash = value; break; + case sExposeAuthInfo: + intptr = &options->expose_userauth_info; + goto parse_flag; + case sDeprecated: case sIgnore: case sUnsupported: @@ -1979,6 +1988,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) M_CP_INTOPT(allow_streamlocal_forwarding); M_CP_INTOPT(allow_agent_forwarding); M_CP_INTOPT(disable_forwarding); + M_CP_INTOPT(expose_userauth_info); M_CP_INTOPT(permit_tun); M_CP_INTOPT(fwd_opts.gateway_ports); M_CP_INTOPT(fwd_opts.streamlocal_bind_unlink); @@ -2278,6 +2288,7 @@ dump_config(ServerOptions *o) dump_cfg_fmtint(sAllowStreamLocalForwarding, o->allow_streamlocal_forwarding); dump_cfg_fmtint(sStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink); dump_cfg_fmtint(sFingerprintHash, o->fingerprint_hash); + dump_cfg_fmtint(sExposeAuthInfo, o->expose_userauth_info); /* string arguments */ dump_cfg_string(sPidFile, o->pid_file); diff --git a/servconf.h b/servconf.h index 5853a974..c2848a76 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.123 2016/11/30 03:00:05 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.124 2017/06/24 06:34:38 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> @@ -189,6 +189,7 @@ typedef struct { char *auth_methods[MAX_AUTH_METHODS]; int fingerprint_hash; + int expose_userauth_info; } ServerOptions; /* Information about the incoming connection as used by Match */ diff --git a/session.c b/session.c index 1b40cb94..eed17067 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.288 2017/05/31 09:15:42 deraadt Exp $ */ +/* $OpenBSD: session.c,v 1.290 2017/06/24 06:34:38 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved @@ -94,6 +94,7 @@ #include "kex.h" #include "monitor_wrap.h" #include "sftp.h" +#include "atomicio.h" #if defined(KRB5) && defined(USE_AFS) #include <kafs.h> @@ -134,7 +135,6 @@ static int session_pty_req(Session *); /* import */ extern ServerOptions options; extern char *__progname; -extern int log_stderr; extern int debug_flag; extern u_int utmp_len; extern int startup_pipe; @@ -161,6 +161,9 @@ login_cap_t *lc; static int is_child = 0; static int in_chroot = 0; +/* File containing userauth info, if ExposeAuthInfo set */ +static char *auth_info_file = NULL; + /* Name and directory of socket for authentication agent forwarding. */ static char *auth_sock_name = NULL; static char *auth_sock_dir = NULL; @@ -255,6 +258,40 @@ display_loginmsg(void) } } +static void +prepare_auth_info_file(struct passwd *pw, struct sshbuf *info) +{ + int fd = -1, success = 0; + + if (!options.expose_userauth_info || info == NULL) + return; + + temporarily_use_uid(pw); + auth_info_file = xstrdup("/tmp/sshauth.XXXXXXXXXXXXXXX"); + if ((fd = mkstemp(auth_info_file)) == -1) { + error("%s: mkstemp: %s", __func__, strerror(errno)); + goto out; + } + if (atomicio(vwrite, fd, sshbuf_mutable_ptr(info), + sshbuf_len(info)) != sshbuf_len(info)) { + error("%s: write: %s", __func__, strerror(errno)); + goto out; + } + if (close(fd) != 0) { + error("%s: close: %s", __func__, strerror(errno)); + goto out; + } + success = 1; + out: + if (!success) { + if (fd != -1) + close(fd); + free(auth_info_file); + auth_info_file = NULL; + } + restore_uid(); +} + void do_authenticated(Authctxt *authctxt) { @@ -270,7 +307,10 @@ do_authenticated(Authctxt *authctxt) auth_debug_send(); + prepare_auth_info_file(authctxt->pw, authctxt->session_info); + do_authenticated2(authctxt); + do_cleanup(authctxt); } @@ -641,10 +681,6 @@ do_exec_no_pty(Session *s, const char *command) case 0: is_child = 1; - /* Child. Reinitialize the log since the pid has changed. */ - log_init(__progname, options.log_level, - options.log_facility, log_stderr); - /* * Create a new session and process group since the 4.4BSD * setlogin() affects the entire process group. @@ -799,9 +835,6 @@ do_exec_pty(Session *s, const char *command) close(fdout); close(ptymaster); - /* Child. Reinitialize the log because the pid has changed. */ - log_init(__progname, options.log_level, - options.log_facility, log_stderr); /* Close the master side of the pseudo tty. */ close(ptyfd); @@ -1363,6 +1396,8 @@ do_setup_env(Session *s, const char *shell) free(laddr); child_set_env(&env, &envsize, "SSH_CONNECTION", buf); + if (auth_info_file != NULL) + child_set_env(&env, &envsize, "SSH_USER_AUTH", auth_info_file); if (s->ttyfd != -1) child_set_env(&env, &envsize, "SSH_TTY", s->tty); if (s->term) @@ -2840,6 +2875,15 @@ do_cleanup(Authctxt *authctxt) /* remove agent socket */ auth_sock_cleanup_proc(authctxt->pw); + /* remove userauth info */ + if (auth_info_file != NULL) { + temporarily_use_uid(authctxt->pw); + unlink(auth_info_file); + restore_uid(); + free(auth_info_file); + auth_info_file = NULL; + } + /* * Cleanup ptys/utmp only if privsep is disabled, * or if running in monitor. diff --git a/sftp-common.c b/sftp-common.c index 3a70c52d..13a7f5be 100644 --- a/sftp-common.c +++ b/sftp-common.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-common.c,v 1.29 2016/09/12 01:22:38 deraadt Exp $ */ +/* $OpenBSD: sftp-common.c,v 1.30 2017/06/10 06:36:46 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Damien Miller. All rights reserved. @@ -216,22 +216,21 @@ ls_file(const char *name, const struct stat *st, int remote, int si_units) int ulen, glen, sz = 0; struct tm *ltime = localtime(&st->st_mtime); char *user, *group; - char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; + char buf[1024], lc[8], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; char sbuf[FMT_SCALED_STRSIZE]; time_t now; strmode(st->st_mode, mode); - if (!remote) { - user = user_from_uid(st->st_uid, 0); - } else { + if (remote) { snprintf(ubuf, sizeof ubuf, "%u", (u_int)st->st_uid); user = ubuf; - } - if (!remote) { - group = group_from_gid(st->st_gid, 0); - } else { snprintf(gbuf, sizeof gbuf, "%u", (u_int)st->st_gid); group = gbuf; + strlcpy(lc, "?", sizeof(lc)); + } else { + user = user_from_uid(st->st_uid, 0); + group = group_from_gid(st->st_gid, 0); + snprintf(lc, sizeof(lc), "%u", (u_int)st->st_nlink); } if (ltime != NULL) { now = time(NULL); @@ -247,12 +246,12 @@ ls_file(const char *name, const struct stat *st, int remote, int si_units) glen = MAXIMUM(strlen(group), 8); if (si_units) { fmt_scaled((long long)st->st_size, sbuf); - snprintf(buf, sizeof buf, "%s %3u %-*s %-*s %8s %s %s", mode, - (u_int)st->st_nlink, ulen, user, glen, group, + snprintf(buf, sizeof buf, "%s %3s %-*s %-*s %8s %s %s", + mode, lc, ulen, user, glen, group, sbuf, tbuf, name); } else { - snprintf(buf, sizeof buf, "%s %3u %-*s %-*s %8llu %s %s", mode, - (u_int)st->st_nlink, ulen, user, glen, group, + snprintf(buf, sizeof buf, "%s %3s %-*s %-*s %8llu %s %s", + mode, lc, ulen, user, glen, group, (unsigned long long)st->st_size, tbuf, name); } return xstrdup(buf); diff --git a/sftp.c b/sftp.c index 856de710..b89ae9df 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.179 2017/05/02 08:54:19 djm Exp $ */ +/* $OpenBSD: sftp.c,v 1.180 2017/06/10 06:33:34 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * @@ -106,6 +106,7 @@ volatile sig_atomic_t interrupted = 0; /* I wish qsort() took a separate ctx for the comparison function...*/ int sort_flag; +glob_t *sort_glob; /* Context used for commandline completion */ struct complete_ctx { @@ -927,6 +928,34 @@ do_ls_dir(struct sftp_conn *conn, const char *path, return (0); } +static int +sglob_comp(const void *aa, const void *bb) +{ + u_int a = *(const u_int *)aa; + u_int b = *(const u_int *)bb; + const char *ap = sort_glob->gl_pathv[a]; + const char *bp = sort_glob->gl_pathv[b]; + const struct stat *as = sort_glob->gl_statv[a]; + const struct stat *bs = sort_glob->gl_statv[b]; + int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; + +#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) + if (sort_flag & LS_NAME_SORT) + return (rmul * strcmp(ap, bp)); + else if (sort_flag & LS_TIME_SORT) { +#if defined(HAVE_STRUCT_STAT_ST_MTIM) + return (rmul * timespeccmp(&as->st_mtim, &bs->st_mtim, <)); +#elif defined(HAVE_STRUCT_STAT_ST_MTIME) + return (rmul * NCMP(as->st_mtime, bs->st_mtime)); +#else + return rmul * 1; +#endif + } else if (sort_flag & LS_SIZE_SORT) + return (rmul * NCMP(as->st_size, bs->st_size)); + + fatal("Unknown ls sort type"); +} + /* sftp ls.1 replacement which handles path globs */ static int do_globbed_ls(struct sftp_conn *conn, const char *path, @@ -936,7 +965,8 @@ do_globbed_ls(struct sftp_conn *conn, const char *path, glob_t g; int err, r; struct winsize ws; - u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80; + u_int i, j, nentries, *indices = NULL, c = 1; + u_int colspace = 0, columns = 1, m = 0, width = 80; memset(&g, 0, sizeof(g)); @@ -981,7 +1011,26 @@ do_globbed_ls(struct sftp_conn *conn, const char *path, colspace = width / columns; } - for (i = 0; g.gl_pathv[i] && !interrupted; i++) { + /* + * Sorting: rather than mess with the contents of glob_t, prepare + * an array of indices into it and sort that. For the usual + * unsorted case, the indices are just the identity 1=1, 2=2, etc. + */ + for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++) + ; /* count entries */ + indices = calloc(nentries, sizeof(*indices)); + for (i = 0; i < nentries; i++) + indices[i] = i; + + if (lflag & SORT_FLAGS) { + sort_glob = &g; + sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); + qsort(indices, nentries, sizeof(*indices), sglob_comp); + sort_glob = NULL; + } + + for (j = 0; j < nentries && !interrupted; j++) { + i = indices[j]; fname = path_strip(g.gl_pathv[i], strip_path); if (lflag & LS_LONG_VIEW) { if (g.gl_statv[i] == NULL) { @@ -1009,6 +1058,7 @@ do_globbed_ls(struct sftp_conn *conn, const char *path, out: if (g.gl_pathc) globfree(&g); + free(indices); return 0; } diff --git a/ssh-gss.h b/ssh-gss.h index a99d7f08..6593e422 100644 --- a/ssh-gss.h +++ b/ssh-gss.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-gss.h,v 1.11 2014/02/26 20:28:44 djm Exp $ */ +/* $OpenBSD: ssh-gss.h,v 1.12 2017/06/24 06:34:38 djm Exp $ */ /* * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. * @@ -128,6 +128,7 @@ 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); void ssh_gssapi_storecreds(void); +const char *ssh_gssapi_displayname(void); #endif /* GSSAPI */ diff --git a/ssh-keygen.1 b/ssh-keygen.1 index 786d37d5..66f8321c 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-keygen.1,v 1.141 2017/05/05 10:41:58 naddy Exp $ +.\" $OpenBSD: ssh-keygen.1,v 1.142 2017/06/28 01:09:22 djm Exp $ .\" .\" Author: Tatu Ylonen <ylo@cs.hut.fi> .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -35,7 +35,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: May 5 2017 $ +.Dd $Mdocdate: June 28 2017 $ .Dt SSH-KEYGEN 1 .Os .Sh NAME @@ -114,6 +114,8 @@ .Fl s Ar ca_key .Fl I Ar certificate_identity .Op Fl h +.Op Fl U +.Op Fl D Ar pkcs11_provider .Op Fl n Ar principals .Op Fl O Ar option .Op Fl V Ar validity_interval @@ -558,6 +560,14 @@ The possible values are .Dq ed25519 , or .Dq rsa . +.It Fl U +When used in combination with +.Fl s , +this option indicates that a CA key resides in a +.Xr ssh-agent 1 . +See the +.Sx CERTIFICATES +section for more information. .It Fl u Update a KRL. When specified with @@ -705,6 +715,14 @@ to .Pp .Dl $ ssh-keygen -s ca_key.pub -D libpkcs11.so -I key_id user_key.pub .Pp +Similarly, it is possible for the CA key to be hosted in a +.Xr ssh-agent 1 . +This is indicated by the +.Fl U +flag and, again, the CA key must be identified by its public half. +.Pp +.Dl $ ssh-keygen -Us ca_key.pub -I key_id user_key.pub +.Pp In all cases, .Ar key_id is a "key identifier" that is logged by the server when the certificate diff --git a/ssh-keygen.c b/ssh-keygen.c index d73f5ecc..ea6765ea 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.304 2017/05/30 14:16:41 markus Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.305 2017/06/28 01:09:22 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -59,6 +59,7 @@ #include "krl.h" #include "digest.h" #include "utf8.h" +#include "authfd.h" #include "sshfileperm.h" #ifdef WITH_OPENSSL @@ -122,6 +123,9 @@ char *identity_comment = NULL; /* Path to CA key when certifying keys. */ char *ca_key_path = NULL; +/* Prefer to use agent keys for CA signing */ +int prefer_agent = 0; + /* Certificate serial number */ unsigned long long cert_serial = 0; @@ -1615,24 +1619,66 @@ load_pkcs11_key(char *path) #endif /* ENABLE_PKCS11 */ } +/* Signer for sshkey_certify_custom that uses the agent */ +static int +agent_signer(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, + const char *alg, u_int compat, void *ctx) +{ + int *agent_fdp = (int *)ctx; + + return ssh_agent_sign(*agent_fdp, key, sigp, lenp, + data, datalen, alg, compat); +} + static void do_ca_sign(struct passwd *pw, int argc, char **argv) { - int r, i, fd; + int r, i, fd, found, agent_fd = -1; u_int n; struct sshkey *ca, *public; char valid[64], *otmp, *tmp, *cp, *out, *comment, **plist = NULL; FILE *f; + struct ssh_identitylist *agent_ids; + size_t j; #ifdef ENABLE_PKCS11 pkcs11_init(1); #endif tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); if (pkcs11provider != NULL) { + /* If a PKCS#11 token was specified then try to use it */ if ((ca = load_pkcs11_key(tmp)) == NULL) fatal("No PKCS#11 key matching %s found", ca_key_path); - } else + } else if (prefer_agent) { + /* + * Agent signature requested. Try to use agent after making + * sure the public key specified is actually present in the + * agent. + */ + if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0) + fatal("Cannot load CA public key %s: %s", + tmp, ssh_err(r)); + if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) + fatal("Cannot use public key for CA signature: %s", + ssh_err(r)); + if ((r = ssh_fetch_identitylist(agent_fd, &agent_ids)) != 0) + fatal("Retrieve agent key list: %s", ssh_err(r)); + found = 0; + for (j = 0; j < agent_ids->nkeys; j++) { + if (sshkey_equal(ca, agent_ids->keys[j])) { + found = 1; + break; + } + } + if (!found) + fatal("CA key %s not found in agent", tmp); + ssh_free_identitylist(agent_ids); + ca->flags |= SSHKEY_FLAG_EXT; + } else { + /* CA key is assumed to be a private key on the filesystem */ ca = load_identity(tmp); + } free(tmp); if (key_type_name != NULL && @@ -1682,8 +1728,16 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) &public->cert->signature_key)) != 0) fatal("sshkey_from_private (ca key): %s", ssh_err(r)); - if ((r = sshkey_certify(public, ca, key_type_name)) != 0) - fatal("Couldn't certify key %s: %s", tmp, ssh_err(r)); + if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) { + if ((r = sshkey_certify_custom(public, ca, + key_type_name, agent_signer, &agent_fd)) != 0) + fatal("Couldn't certify key %s via agent: %s", + tmp, ssh_err(r)); + } else { + if ((sshkey_certify(public, ca, key_type_name)) != 0) + fatal("Couldn't certify key %s: %s", + tmp, ssh_err(r)); + } if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0) *cp = '\0'; @@ -2279,8 +2333,9 @@ usage(void) " ssh-keygen -T output_file -f input_file [-v] [-a rounds] [-J num_lines]\n" " [-j start_line] [-K checkpt] [-W generator]\n" #endif - " ssh-keygen -s ca_key -I certificate_identity [-h] [-n principals]\n" - " [-O option] [-V validity_interval] [-z serial_number] file ...\n" + " ssh-keygen -s ca_key -I certificate_identity [-h] [-U]\n" + " [-D pkcs11_provider] [-n principals] [-O option]\n" + " [-V validity_interval] [-z serial_number] file ...\n" " ssh-keygen -L [-f input_keyfile]\n" " ssh-keygen -A\n" " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" @@ -2338,8 +2393,8 @@ main(int argc, char **argv) if (gethostname(hostname, sizeof(hostname)) < 0) fatal("gethostname: %s", strerror(errno)); - /* Remaining characters: UYdw */ - while ((opt = getopt(argc, argv, "ABHLQXceghiklopquvxy" + /* Remaining characters: Ydw */ + while ((opt = getopt(argc, argv, "ABHLQUXceghiklopquvxy" "C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:" "a:b:f:g:j:m:n:r:s:t:z:")) != -1) { switch (opt) { @@ -2466,6 +2521,9 @@ main(int argc, char **argv) case 'D': pkcs11provider = optarg; break; + case 'U': + prefer_agent = 1; + break; case 'u': update_krl = 1; break; diff --git a/ssh-keyscan.c b/ssh-keyscan.c index 0c863e47..8fd8cc8f 100644 --- a/ssh-keyscan.c +++ b/ssh-keyscan.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keyscan.c,v 1.114 2017/05/31 07:00:13 markus Exp $ */ +/* $OpenBSD: ssh-keyscan.c,v 1.115 2017/06/30 04:17:23 dtucker Exp $ */ /* * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>. * @@ -395,7 +395,6 @@ confree(int s) { if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) fatal("confree: attempt to free bad fdno %d", s); - free(fdcon[s].c_namebase); free(fdcon[s].c_output_name); if (fdcon[s].c_status == CS_KEYS) diff --git a/ssh.1 b/ssh.1 index 47cd0211..3aacec41 100644 --- a/ssh.1 +++ b/ssh.1 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh.1,v 1.382 2017/05/30 18:58:37 bluhm Exp $ -.Dd $Mdocdate: May 30 2017 $ +.\" $OpenBSD: ssh.1,v 1.383 2017/06/09 06:43:01 djm Exp $ +.Dd $Mdocdate: June 9 2017 $ .Dt SSH 1 .Os .Sh NAME @@ -846,6 +846,17 @@ The client proves that it has access to the private key and the server checks that the corresponding public key is authorized to accept the account. .Pp +The server may inform the client of errors that prevented public key +authentication from succeeding after authentication completes using a +different method. +These may be viewed by increasing the +.Cm LogLevel +to +.Cm DEBUG +or higher (e.g. by using the +.Fl v +flag). +.Pp The user creates his/her key pair by running .Xr ssh-keygen 1 . This stores the private key in diff --git a/ssh_config.5 b/ssh_config.5 index 4277f9ea..1cbfe040 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.250 2017/05/30 19:38:17 jmc Exp $ -.Dd $Mdocdate: May 30 2017 $ +.\" $OpenBSD: ssh_config.5,v 1.251 2017/06/24 05:35:05 djm Exp $ +.Dd $Mdocdate: June 24 2017 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -809,7 +809,7 @@ The list of available key types may also be obtained using .It Cm HostKeyAlias Specifies an alias that should be used instead of the real host name when looking up or saving the host key -in the host key database files. +in the host key database files and when validating host certificates. This option is useful for tunneling SSH connections or for multiple servers running on a single host. .It Cm HostName diff --git a/sshconnect.c b/sshconnect.c index 11c848a5..796e89ca 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.280 2017/05/30 14:13:40 markus Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.282 2017/06/24 05:37:44 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -34,6 +34,9 @@ #include <paths.h> #endif #include <pwd.h> +#ifdef HAVE_POLL_H +#include <poll.h> +#endif #include <signal.h> #include <stdarg.h> #include <stdio.h> @@ -344,87 +347,71 @@ ssh_create_socket(int privileged, struct addrinfo *ai) return sock; } +/* + * Wait up to *timeoutp milliseconds for fd to be readable. Updates + * *timeoutp with time remaining. + * Returns 0 if fd ready or -1 on timeout or error (see errno). + */ static int -timeout_connect(int sockfd, const struct sockaddr *serv_addr, - socklen_t addrlen, int *timeoutp) +waitrfd(int fd, int *timeoutp) { - fd_set *fdset; - struct timeval tv, t_start; - socklen_t optlen; - int optval, rc, result = -1; + struct pollfd pfd; + struct timeval t_start; + int oerrno, r; gettimeofday(&t_start, NULL); - - if (*timeoutp <= 0) { - result = connect(sockfd, serv_addr, addrlen); - goto done; - } - - set_nonblock(sockfd); - rc = connect(sockfd, serv_addr, addrlen); - if (rc == 0) { - unset_nonblock(sockfd); - result = 0; - goto done; - } - if (errno != EINPROGRESS) { - result = -1; - goto done; + pfd.fd = fd; + pfd.events = POLLIN; + for (; *timeoutp >= 0;) { + r = poll(&pfd, 1, *timeoutp); + oerrno = errno; + ms_subtract_diff(&t_start, timeoutp); + errno = oerrno; + if (r > 0) + return 0; + else if (r == -1 && errno != EAGAIN) + return -1; + else if (r == 0) + break; } + /* timeout */ + errno = ETIMEDOUT; + return -1; +} - fdset = xcalloc(howmany(sockfd + 1, NFDBITS), - sizeof(fd_mask)); - FD_SET(sockfd, fdset); - ms_to_timeval(&tv, *timeoutp); +static int +timeout_connect(int sockfd, const struct sockaddr *serv_addr, + socklen_t addrlen, int *timeoutp) +{ + int optval = 0; + socklen_t optlen = sizeof(optval); - for (;;) { - rc = select(sockfd + 1, NULL, fdset, NULL, &tv); - if (rc != -1 || errno != EINTR) - break; - } + /* No timeout: just do a blocking connect() */ + if (*timeoutp <= 0) + return connect(sockfd, serv_addr, addrlen); - switch (rc) { - case 0: - /* Timed out */ - errno = ETIMEDOUT; - break; - case -1: - /* Select error */ - debug("select: %s", strerror(errno)); - break; - case 1: - /* Completed or failed */ - optval = 0; - optlen = sizeof(optval); - if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, - &optlen) == -1) { - debug("getsockopt: %s", strerror(errno)); - break; - } - if (optval != 0) { - errno = optval; - break; - } - result = 0; + set_nonblock(sockfd); + if (connect(sockfd, serv_addr, addrlen) == 0) { + /* Succeeded already? */ unset_nonblock(sockfd); - break; - default: - /* Should not occur */ - fatal("Bogus return (%d) from select()", rc); - } + return 0; + } else if (errno != EINPROGRESS) + return -1; - free(fdset); + if (waitrfd(sockfd, timeoutp) == -1) + return -1; - done: - if (result == 0 && *timeoutp > 0) { - ms_subtract_diff(&t_start, timeoutp); - if (*timeoutp <= 0) { - errno = ETIMEDOUT; - result = -1; - } + /* Completed or failed */ + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { + debug("getsockopt: %s", strerror(errno)); + return -1; } - - return (result); + if (optval != 0) { + errno = optval; + return -1; + } + unset_nonblock(sockfd); + return 0; } /* @@ -562,42 +549,25 @@ ssh_exchange_identification(int timeout_ms) int connection_out = packet_get_connection_out(); u_int i, n; size_t len; - int fdsetsz, remaining, rc; - struct timeval t_start, t_remaining; - fd_set *readfds; - fd_set *exceptfds; - - fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); - readfds = xcalloc(1, fdsetsz); - exceptfds = xcalloc(1, fdsetsz); + int rc; send_client_banner(connection_out, 0); /* Read other side's version identification. */ - remaining = timeout_ms; for (n = 0;;) { for (i = 0; i < sizeof(buf) - 1; i++) { if (timeout_ms > 0) { - gettimeofday(&t_start, NULL); - ms_to_timeval(&t_remaining, remaining); - FD_SET(connection_in, readfds); - FD_SET(connection_in, exceptfds); - rc = select(connection_in + 1, readfds, NULL, - exceptfds, &t_remaining); - ms_subtract_diff(&t_start, &remaining); - if (rc == 0 || remaining <= 0) + rc = waitrfd(connection_in, &timeout_ms); + if (rc == -1 && errno == ETIMEDOUT) { fatal("Connection timed out during " "banner exchange"); - if (rc == -1) { - if (errno == EINTR) - continue; - fatal("ssh_exchange_identification: " - "select: %s", strerror(errno)); + } else if (rc == -1) { + fatal("%s: %s", + __func__, strerror(errno)); } } len = atomicio(read, connection_in, &buf[i], 1); - if (len != 1 && errno == EPIPE) fatal("ssh_exchange_identification: " "Connection closed by remote host"); @@ -623,8 +593,6 @@ ssh_exchange_identification(int timeout_ms) debug("ssh_exchange_identification: %s", buf); } server_version_string = xstrdup(buf); - free(readfds); - free(exceptfds); /* * Check that the versions match. In future this might accept @@ -883,7 +851,9 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, host, type, want_cert ? "certificate" : "key"); debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", host_found->file, host_found->line); - if (want_cert && !check_host_cert(hostname, host_key)) + if (want_cert && + !check_host_cert(options.host_key_alias == NULL ? + hostname : options.host_key_alias, host_key)) goto fail; if (options.check_host_ip && ip_status == HOST_NEW) { if (readonly || want_cert) diff --git a/sshconnect2.c b/sshconnect2.c index ad4f3daf..b8dff518 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.263 2017/05/31 07:00:13 markus Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.264 2017/06/14 00:31:38 dtucker Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -474,7 +474,8 @@ userauth(Authctxt *authctxt, char *authlist) for (;;) { Authmethod *method = authmethod_get(authlist); if (method == NULL) - fatal("Permission denied (%s).", authlist); + fatal("%s@%s: Permission denied (%s).", + authctxt->server_user, authctxt->host, authlist); authctxt->method = method; /* reset the per method handler */ diff --git a/sshd.8 b/sshd.8 index 05368f94..a4201146 100644 --- a/sshd.8 +++ b/sshd.8 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd.8,v 1.289 2017/05/07 23:12:57 djm Exp $ -.Dd $Mdocdate: May 7 2017 $ +.\" $OpenBSD: sshd.8,v 1.291 2017/06/24 06:28:50 jmc Exp $ +.Dd $Mdocdate: June 24 2017 $ .Dt SSHD 8 .Os .Sh NAME @@ -652,9 +652,23 @@ Hostnames is a comma-separated list of patterns and .Ql \&? act as -wildcards); each pattern in turn is matched against the canonical host -name (when authenticating a client) or against the user-supplied -name (when authenticating a server). +wildcards); each pattern in turn is matched against the host name. +When +.Nm sshd +is authenticating a client, such as when using +.Cm HostbasedAuthentication , +this will be the canonical client host name. +When +.Xr ssh 1 +is authenticating a server, this will be the host name +given by the user, the value of the +.Xr ssh 1 +.Cm HostkeyAlias +if it was specified, or the canonical server hostname if the +.Xr ssh 1 +.Cm CanonicalizeHostname +option was used. +.Pp A pattern may also be preceded by .Ql \&! to indicate negation: if the host name matches a negated diff --git a/sshd_config.5 b/sshd_config.5 index 7b4cb1d9..d1262983 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.245 2017/05/17 01:24:17 djm Exp $ -.Dd $Mdocdate: May 17 2017 $ +.\" $OpenBSD: sshd_config.5,v 1.248 2017/06/24 07:08:57 djm Exp $ +.Dd $Mdocdate: June 24 2017 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -564,6 +564,12 @@ Disables all forwarding features, including X11, TCP and StreamLocal. This option overrides all other forwarding-related options and may simplify restricted configurations. +.It Cm ExposeAuthInfo +Enables writing a file containing a list of authentication methods and +public credentials (e.g. keys) used to authenticate the user. +The location of the file is exposed to the user session through the +.Ev SSH_USER_AUTH +environment variable. .It Cm FingerprintHash Specifies the hash algorithm used when logging key fingerprints. Valid options are: diff --git a/sshkey.c b/sshkey.c index 9a3f0be5..acc6e3f2 100644 --- a/sshkey.c +++ b/sshkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.c,v 1.51 2017/05/31 09:15:42 deraadt Exp $ */ +/* $OpenBSD: sshkey.c,v 1.53 2017/06/28 01:09:22 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved. @@ -1331,7 +1331,7 @@ sshkey_to_base64(const struct sshkey *key, char **b64p) return r; } -static int +int sshkey_format_text(const struct sshkey *key, struct sshbuf *b) { int r = SSH_ERR_INTERNAL_ERROR; @@ -2253,7 +2253,8 @@ sshkey_drop_cert(struct sshkey *k) /* Sign a certified key, (re-)generating the signed certblob. */ int -sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg) +sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg, + sshkey_certify_signer *signer, void *signer_ctx) { struct sshbuf *principals = NULL; u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32]; @@ -2342,8 +2343,8 @@ sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg) goto out; /* Sign the whole mess */ - if ((ret = sshkey_sign(ca, &sig_blob, &sig_len, sshbuf_ptr(cert), - sshbuf_len(cert), alg, 0)) != 0) + if ((ret = signer(ca, &sig_blob, &sig_len, sshbuf_ptr(cert), + sshbuf_len(cert), alg, 0, signer_ctx)) != 0) goto out; /* Append signature and we are done */ @@ -2359,6 +2360,22 @@ sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg) return ret; } +static int +default_key_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, + const char *alg, u_int compat, void *ctx) +{ + if (ctx != NULL) + return SSH_ERR_INVALID_ARGUMENT; + return sshkey_sign(key, sigp, lenp, data, datalen, alg, compat); +} + +int +sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg) +{ + return sshkey_certify_custom(k, ca, alg, default_key_sign, NULL); +} + int sshkey_cert_check_authority(const struct sshkey *k, int want_host, int require_principal, @@ -3365,6 +3382,64 @@ sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, #ifdef WITH_OPENSSL +static int +translate_libcrypto_error(unsigned long pem_err) +{ + int pem_reason = ERR_GET_REASON(pem_err); + + switch (ERR_GET_LIB(pem_err)) { + case ERR_LIB_PEM: + switch (pem_reason) { + case PEM_R_BAD_PASSWORD_READ: + case PEM_R_PROBLEMS_GETTING_PASSWORD: + case PEM_R_BAD_DECRYPT: + return SSH_ERR_KEY_WRONG_PASSPHRASE; + default: + return SSH_ERR_INVALID_FORMAT; + } + case ERR_LIB_EVP: + switch (pem_reason) { + case EVP_R_BAD_DECRYPT: + return SSH_ERR_KEY_WRONG_PASSPHRASE; + case EVP_R_BN_DECODE_ERROR: + case EVP_R_DECODE_ERROR: +#ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR + case EVP_R_PRIVATE_KEY_DECODE_ERROR: +#endif + return SSH_ERR_INVALID_FORMAT; + default: + return SSH_ERR_LIBCRYPTO_ERROR; + } + case ERR_LIB_ASN1: + return SSH_ERR_INVALID_FORMAT; + } + return SSH_ERR_LIBCRYPTO_ERROR; +} + +static void +clear_libcrypto_errors(void) +{ + while (ERR_get_error() != 0) + ; +} + +/* + * Translate OpenSSL error codes to determine whether + * passphrase is required/incorrect. + */ +static int +convert_libcrypto_error(void) +{ + /* + * Some password errors are reported at the beginning + * of the error queue. + */ + if (translate_libcrypto_error(ERR_peek_error()) == + SSH_ERR_KEY_WRONG_PASSPHRASE) + return SSH_ERR_KEY_WRONG_PASSPHRASE; + return translate_libcrypto_error(ERR_peek_last_error()); +} + static int sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type, const char *passphrase, struct sshkey **keyp) @@ -3385,48 +3460,10 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type, goto out; } + clear_libcrypto_errors(); if ((pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, (char *)passphrase)) == NULL) { - unsigned long pem_err = ERR_peek_last_error(); - int pem_reason = ERR_GET_REASON(pem_err); - - /* - * Translate OpenSSL error codes to determine whether - * passphrase is required/incorrect. - */ - switch (ERR_GET_LIB(pem_err)) { - case ERR_LIB_PEM: - switch (pem_reason) { - case PEM_R_BAD_PASSWORD_READ: - case PEM_R_PROBLEMS_GETTING_PASSWORD: - case PEM_R_BAD_DECRYPT: - r = SSH_ERR_KEY_WRONG_PASSPHRASE; - goto out; - default: - r = SSH_ERR_INVALID_FORMAT; - goto out; - } - case ERR_LIB_EVP: - switch (pem_reason) { - case EVP_R_BAD_DECRYPT: - r = SSH_ERR_KEY_WRONG_PASSPHRASE; - goto out; - case EVP_R_BN_DECODE_ERROR: - case EVP_R_DECODE_ERROR: -#ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR - case EVP_R_PRIVATE_KEY_DECODE_ERROR: -#endif - r = SSH_ERR_INVALID_FORMAT; - goto out; - default: - r = SSH_ERR_LIBCRYPTO_ERROR; - goto out; - } - case ERR_LIB_ASN1: - r = SSH_ERR_INVALID_FORMAT; - goto out; - } - r = SSH_ERR_LIBCRYPTO_ERROR; + r = convert_libcrypto_error(); goto out; } if (pk->type == EVP_PKEY_RSA && diff --git a/sshkey.h b/sshkey.h index b0b5b274..d8346a57 100644 --- a/sshkey.h +++ b/sshkey.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.h,v 1.18 2017/05/07 23:15:59 djm Exp $ */ +/* $OpenBSD: sshkey.h,v 1.20 2017/06/28 01:09:22 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -124,6 +124,7 @@ int sshkey_fingerprint_raw(const struct sshkey *k, int, u_char **retp, size_t *lenp); const char *sshkey_type(const struct sshkey *); const char *sshkey_cert_type(const struct sshkey *); +int sshkey_format_text(const struct sshkey *, struct sshbuf *); int sshkey_write(const struct sshkey *, FILE *); int sshkey_read(struct sshkey *, char **); u_int sshkey_size(const struct sshkey *); @@ -136,13 +137,19 @@ int sshkey_type_is_cert(int); int sshkey_type_plain(int); int sshkey_to_certified(struct sshkey *); int sshkey_drop_cert(struct sshkey *); -int sshkey_certify(struct sshkey *, struct sshkey *, const char *); int sshkey_cert_copy(const struct sshkey *, struct sshkey *); int sshkey_cert_check_authority(const struct sshkey *, int, int, const char *, const char **); size_t sshkey_format_cert_validity(const struct sshkey_cert *, char *, size_t) __attribute__((__bounded__(__string__, 2, 3))); +int sshkey_certify(struct sshkey *, struct sshkey *, const char *); +/* Variant allowing use of a custom signature function (e.g. for ssh-agent) */ +typedef int sshkey_certify_signer(const struct sshkey *, u_char **, size_t *, + const u_char *, size_t, const char *, u_int, void *); +int sshkey_certify_custom(struct sshkey *, struct sshkey *, const char *, + sshkey_certify_signer *, void *); + int sshkey_ecdsa_nid_from_name(const char *); int sshkey_curve_name_to_nid(const char *); const char * sshkey_curve_nid_to_name(int);