From 0fddf2967ac51d518e300408a0d7e6adf4cd2634 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 25 Nov 2019 00:52:46 +0000 Subject: upstream: Add a sshd_config PubkeyAuthOptions directive This directive has a single valid option "no-touch-required" that causes sshd to skip checking whether user presence was tested before a security key signature was made (usually by the user touching the key). ok markus@ OpenBSD-Commit-ID: 46e434a49802d4ed82bc0aa38cb985c198c407de --- servconf.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'servconf.h') diff --git a/servconf.h b/servconf.h index 5483da051..9f202260a 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.140 2019/04/18 18:56:16 dtucker Exp $ */ +/* $OpenBSD: servconf.h,v 1.141 2019/11/25 00:52:46 djm Exp $ */ /* * Author: Tatu Ylonen @@ -42,6 +42,9 @@ /* Magic name for internal sftp-server */ #define INTERNAL_SFTP_NAME "internal-sftp" +/* PubkeyAuthOptions flags */ +#define PUBKEYAUTH_TOUCH_REQUIRED 1 + struct ssh; struct fwd_perm_list; @@ -114,6 +117,7 @@ typedef struct { char *ca_sign_algorithms; /* Allowed CA signature algorithms */ int pubkey_authentication; /* If true, permit ssh2 pubkey authentication. */ char *pubkey_key_types; /* Key types allowed for public key */ + int pubkey_auth_options; /* -1 or mask of PUBKEYAUTH_* flags */ int kerberos_authentication; /* If true, permit Kerberos * authentication. */ int kerberos_or_local_passwd; /* If true, permit kerberos -- cgit v1.2.3 From 56584cce75f3d20aaa30befc7cbd331d922927f3 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Sun, 15 Dec 2019 18:57:30 +0000 Subject: upstream: allow security keys to act as host keys as well as user keys. Previously we didn't do this because we didn't want to expose the attack surface presented by USB and FIDO protocol handling, but now that this is insulated behind ssh-sk-helper there is less risk. ok markus@ OpenBSD-Commit-ID: 77b068dd133b8d87e0f010987bd5131e640ee64c --- monitor.c | 4 ++-- monitor_wrap.c | 4 +--- myproposal.h | 40 +++++++++++----------------------------- readconf.c | 4 ++-- servconf.c | 18 ++++++++++++++---- servconf.h | 3 ++- sshd.c | 42 +++++++++++++++++++++++++++++++++++------- 7 files changed, 67 insertions(+), 48 deletions(-) (limited to 'servconf.h') diff --git a/monitor.c b/monitor.c index 64eca98d6..6ee44204c 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.205 2019/11/25 10:23:36 djm Exp $ */ +/* $OpenBSD: monitor.c,v 1.206 2019/12/15 18:57:30 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -679,7 +679,7 @@ mm_answer_sign(struct ssh *ssh, int sock, struct sshbuf *m) if ((key = get_hostkey_by_index(keyid)) != NULL) { if ((r = sshkey_sign(key, &signature, &siglen, p, datlen, alg, - NULL, compat)) != 0) + options.sk_provider, compat)) != 0) fatal("%s: sshkey_sign failed: %s", __func__, ssh_err(r)); } else if ((key = get_hostkey_public_by_index(keyid, ssh)) != NULL && diff --git a/monitor_wrap.c b/monitor_wrap.c index 06599e3b1..001a8fa1c 100644 --- a/monitor_wrap.c +++ b/monitor_wrap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.c,v 1.116 2019/11/25 00:51:37 djm Exp $ */ +/* $OpenBSD: monitor_wrap.c,v 1.117 2019/12/15 18:57:30 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -224,8 +224,6 @@ mm_sshkey_sign(struct ssh *ssh, struct sshkey *key, u_char **sigp, size_t *lenp, int r; debug3("%s entering", __func__); - if (sk_provider != NULL) - fatal("%s: sk_provider != NULL", __func__); if ((m = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((r = sshbuf_put_u32(m, ndx)) != 0 || diff --git a/myproposal.h b/myproposal.h index b393db8b0..6688c3066 100644 --- a/myproposal.h +++ b/myproposal.h @@ -1,4 +1,4 @@ -/* $OpenBSD: myproposal.h,v 1.62 2019/12/10 22:43:19 djm Exp $ */ +/* $OpenBSD: myproposal.h,v 1.63 2019/12/15 18:57:30 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -39,32 +39,30 @@ # define HOSTKEY_ECDSA_CERT_METHODS \ "ecdsa-sha2-nistp256-cert-v01@openssh.com," \ "ecdsa-sha2-nistp384-cert-v01@openssh.com," \ - "ecdsa-sha2-nistp521-cert-v01@openssh.com," + "ecdsa-sha2-nistp521-cert-v01@openssh.com," \ + "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com," # define HOSTKEY_ECDSA_METHODS \ "ecdsa-sha2-nistp256," \ "ecdsa-sha2-nistp384," \ - "ecdsa-sha2-nistp521," + "ecdsa-sha2-nistp521," \ + "sk-ecdsa-sha2-nistp256@openssh.com," # else /* OPENSSL_HAS_NISTP521 */ # define KEX_ECDH_METHODS \ "ecdh-sha2-nistp256," \ "ecdh-sha2-nistp384," # define HOSTKEY_ECDSA_CERT_METHODS \ "ecdsa-sha2-nistp256-cert-v01@openssh.com," \ - "ecdsa-sha2-nistp384-cert-v01@openssh.com," + "ecdsa-sha2-nistp384-cert-v01@openssh.com," \ + "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com," # define HOSTKEY_ECDSA_METHODS \ "ecdsa-sha2-nistp256," \ - "ecdsa-sha2-nistp384," -# endif /* OPENSSL_HAS_NISTP521 */ -# define USERKEY_ECDSA_SK_CERT_METHODS \ - "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com," -# define USERKEY_ECDSA_SK_METHODS \ + "ecdsa-sha2-nistp384," \ "sk-ecdsa-sha2-nistp256@openssh.com," +# endif /* OPENSSL_HAS_NISTP521 */ #else /* OPENSSL_HAS_ECC */ # define KEX_ECDH_METHODS # define HOSTKEY_ECDSA_CERT_METHODS # define HOSTKEY_ECDSA_METHODS -# define USERKEY_ECDSA_SK_CERT_METHODS -# define USERKEY_ECDSA_SK_METHODS #endif /* OPENSSL_HAS_ECC */ #ifdef OPENSSL_HAVE_EVPGCM @@ -110,11 +108,13 @@ #define KEX_DEFAULT_PK_ALG \ HOSTKEY_ECDSA_CERT_METHODS \ "ssh-ed25519-cert-v01@openssh.com," \ + "sk-ssh-ed25519-cert-v01@openssh.com," \ "rsa-sha2-512-cert-v01@openssh.com," \ "rsa-sha2-256-cert-v01@openssh.com," \ "ssh-rsa-cert-v01@openssh.com," \ HOSTKEY_ECDSA_METHODS \ "ssh-ed25519," \ + "sk-ssh-ed25519@openssh.com," \ "rsa-sha2-512," \ "rsa-sha2-256," \ "ssh-rsa" @@ -145,29 +145,12 @@ /* Not a KEX value, but here so all the algorithm defaults are together */ #define SSH_ALLOWED_CA_SIGALGS \ HOSTKEY_ECDSA_METHODS \ - USERKEY_ECDSA_SK_METHODS \ "ssh-ed25519," \ "sk-ssh-ed25519@openssh.com," \ "rsa-sha2-512," \ "rsa-sha2-256," \ "ssh-rsa" -#define PUBKEY_DEFAULT_PK_ALG \ - USERKEY_ECDSA_SK_CERT_METHODS \ - HOSTKEY_ECDSA_CERT_METHODS \ - "sk-ssh-ed25519-cert-v01@openssh.com," \ - "ssh-ed25519-cert-v01@openssh.com," \ - "rsa-sha2-512-cert-v01@openssh.com," \ - "rsa-sha2-256-cert-v01@openssh.com," \ - "ssh-rsa-cert-v01@openssh.com," \ - USERKEY_ECDSA_SK_METHODS \ - HOSTKEY_ECDSA_METHODS \ - "sk-ssh-ed25519@openssh.com," \ - "ssh-ed25519," \ - "rsa-sha2-512," \ - "rsa-sha2-256," \ - "ssh-rsa" - #else /* WITH_OPENSSL */ #define KEX_SERVER_KEX \ @@ -176,7 +159,6 @@ #define KEX_DEFAULT_PK_ALG \ "ssh-ed25519-cert-v01@openssh.com," \ "ssh-ed25519" -#define PUBKEY_DEFAULT_PK_ALG KEX_DEFAULT_PK_ALG #define KEX_SERVER_ENCRYPT \ "chacha20-poly1305@openssh.com," \ "aes128-ctr,aes192-ctr,aes256-ctr" diff --git a/readconf.c b/readconf.c index c046e4dbf..4ea8ec566 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.314 2019/11/14 21:27:29 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.315 2019/12/15 18:57:30 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -2153,7 +2153,7 @@ fill_default_options(Options * options) ASSEMBLE(macs, KEX_CLIENT_MAC, all_mac); ASSEMBLE(kex_algorithms, KEX_CLIENT_KEX, all_kex); ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, all_key); - ASSEMBLE(pubkey_key_types, PUBKEY_DEFAULT_PK_ALG, all_key); + ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, all_key); ASSEMBLE(ca_sign_algorithms, SSH_ALLOWED_CA_SIGALGS, all_sig); #undef ASSEMBLE free(all_cipher); diff --git a/servconf.c b/servconf.c index 1f3beab4a..30cd59840 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.354 2019/11/25 00:52:46 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.355 2019/12/15 18:57:30 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -171,6 +171,7 @@ initialize_server_options(ServerOptions *options) options->authorized_keys_command = NULL; options->authorized_keys_command_user = NULL; options->revoked_keys_file = NULL; + options->sk_provider = NULL; options->trusted_user_ca_keys = NULL; options->authorized_principals_file = NULL; options->authorized_principals_command = NULL; @@ -211,7 +212,7 @@ assemble_algorithms(ServerOptions *o) ASSEMBLE(kex_algorithms, KEX_SERVER_KEX, all_kex); ASSEMBLE(hostkeyalgorithms, KEX_DEFAULT_PK_ALG, all_key); ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, all_key); - ASSEMBLE(pubkey_key_types, PUBKEY_DEFAULT_PK_ALG, all_key); + ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, all_key); ASSEMBLE(ca_sign_algorithms, SSH_ALLOWED_CA_SIGALGS, all_sig); #undef ASSEMBLE free(all_cipher); @@ -428,6 +429,8 @@ fill_default_server_options(ServerOptions *options) options->disable_forwarding = 0; if (options->expose_userauth_info == -1) options->expose_userauth_info = 0; + if (options->sk_provider == NULL) + options->sk_provider = xstrdup("internal"); assemble_algorithms(options); @@ -447,6 +450,7 @@ fill_default_server_options(ServerOptions *options) CLEAR_ON_NONE(options->banner); CLEAR_ON_NONE(options->trusted_user_ca_keys); CLEAR_ON_NONE(options->revoked_keys_file); + CLEAR_ON_NONE(options->sk_provider); CLEAR_ON_NONE(options->authorized_principals_file); CLEAR_ON_NONE(options->adm_forced_command); CLEAR_ON_NONE(options->chroot_directory); @@ -512,7 +516,7 @@ typedef enum { sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, sStreamLocalBindMask, sStreamLocalBindUnlink, sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding, - sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, + sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider, sDeprecated, sIgnore, sUnsupported } ServerOpCodes; @@ -662,6 +666,7 @@ static struct { { "exposeauthinfo", sExposeAuthInfo, SSHCFG_ALL }, { "rdomain", sRDomain, SSHCFG_ALL }, { "casignaturealgorithms", sCASignatureAlgorithms, SSHCFG_ALL }, + { "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL }, { NULL, sBadOption, 0 } }; @@ -2025,6 +2030,10 @@ process_server_config_line(ServerOptions *options, char *line, charptr = &options->revoked_keys_file; goto parse_filename; + case sSecurityKeyProvider: + charptr = &options->sk_provider; + goto parse_filename; + case sIPQoS: arg = strdelim(&cp); if ((value = parse_ipqos(arg)) == -1) @@ -2646,6 +2655,7 @@ dump_config(ServerOptions *o) dump_cfg_string(sChrootDirectory, o->chroot_directory); dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); dump_cfg_string(sRevokedKeys, o->revoked_keys_file); + dump_cfg_string(sSecurityKeyProvider, o->sk_provider); dump_cfg_string(sAuthorizedPrincipalsFile, o->authorized_principals_file); dump_cfg_string(sVersionAddendum, *o->version_addendum == '\0' @@ -2664,7 +2674,7 @@ dump_config(ServerOptions *o) dump_cfg_string(sHostKeyAlgorithms, o->hostkeyalgorithms ? o->hostkeyalgorithms : KEX_DEFAULT_PK_ALG); dump_cfg_string(sPubkeyAcceptedKeyTypes, o->pubkey_key_types ? - o->pubkey_key_types : PUBKEY_DEFAULT_PK_ALG); + o->pubkey_key_types : KEX_DEFAULT_PK_ALG); dump_cfg_string(sRDomain, o->routing_domain); /* string arguments requiring a lookup */ diff --git a/servconf.h b/servconf.h index 9f202260a..6fc1efb2c 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.141 2019/11/25 00:52:46 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.142 2019/12/15 18:57:30 djm Exp $ */ /* * Author: Tatu Ylonen @@ -215,6 +215,7 @@ typedef struct { int fingerprint_hash; int expose_userauth_info; u_int64_t timing_secret; + char *sk_provider; } ServerOptions; /* Information about the incoming connection as used by Match */ diff --git a/sshd.c b/sshd.c index 1e4d8a295..0cf13a741 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.541 2019/11/18 16:10:05 naddy Exp $ */ +/* $OpenBSD: sshd.c,v 1.542 2019/12/15 18:57:30 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -122,6 +122,7 @@ #include "auth-options.h" #include "version.h" #include "ssherr.h" +#include "sk-api.h" /* Re-exec fds */ #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) @@ -632,6 +633,8 @@ list_hostkey_types(void) case KEY_DSA: case KEY_ECDSA: case KEY_ED25519: + case KEY_ECDSA_SK: + case KEY_ED25519_SK: case KEY_XMSS: append_hostkey_type(b, sshkey_ssh_name(key)); break; @@ -651,6 +654,8 @@ list_hostkey_types(void) case KEY_DSA_CERT: case KEY_ECDSA_CERT: case KEY_ED25519_CERT: + case KEY_ECDSA_SK_CERT: + case KEY_ED25519_SK_CERT: case KEY_XMSS_CERT: append_hostkey_type(b, sshkey_ssh_name(key)); break; @@ -675,6 +680,8 @@ get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh) case KEY_DSA_CERT: case KEY_ECDSA_CERT: case KEY_ED25519_CERT: + case KEY_ECDSA_SK_CERT: + case KEY_ED25519_SK_CERT: case KEY_XMSS_CERT: key = sensitive_data.host_certificates[i]; break; @@ -684,10 +691,20 @@ get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh) key = sensitive_data.host_pubkeys[i]; break; } - if (key != NULL && key->type == type && - (key->type != KEY_ECDSA || key->ecdsa_nid == nid)) + if (key == NULL || key->type != type) + continue; + switch (type) { + case KEY_ECDSA: + case KEY_ECDSA_SK: + case KEY_ECDSA_CERT: + case KEY_ECDSA_SK_CERT: + if (key->ecdsa_nid != nid) + continue; + /* FALLTHROUGH */ + default: return need_private ? sensitive_data.host_keys[i] : key; + } } return NULL; } @@ -1723,7 +1740,14 @@ main(int ac, char **av) &key, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR) do_log2(ll, "Unable to load host key \"%s\": %s", options.host_key_files[i], ssh_err(r)); - if (r == 0 && (r = sshkey_shield_private(key)) != 0) { + if (sshkey_is_sk(key) && + key->sk_flags & SSH_SK_USER_PRESENCE_REQD) { + debug("host key %s requires user presence, ignoring", + options.host_key_files[i]); + key->sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; + } + if (r == 0 && key != NULL && + (r = sshkey_shield_private(key)) != 0) { do_log2(ll, "Unable to shield host key \"%s\": %s", options.host_key_files[i], ssh_err(r)); sshkey_free(key); @@ -1760,6 +1784,8 @@ main(int ac, char **av) case KEY_DSA: case KEY_ECDSA: case KEY_ED25519: + case KEY_ECDSA_SK: + case KEY_ED25519_SK: case KEY_XMSS: if (have_agent || key != NULL) sensitive_data.have_ssh2_key = 1; @@ -2212,17 +2238,19 @@ sshd_hostkey_sign(struct ssh *ssh, struct sshkey *privkey, if (use_privsep) { if (privkey) { if (mm_sshkey_sign(ssh, privkey, signature, slenp, - data, dlen, alg, NULL, ssh->compat) < 0) + data, dlen, alg, options.sk_provider, + ssh->compat) < 0) fatal("%s: privkey sign failed", __func__); } else { if (mm_sshkey_sign(ssh, pubkey, signature, slenp, - data, dlen, alg, NULL, ssh->compat) < 0) + data, dlen, alg, options.sk_provider, + ssh->compat) < 0) fatal("%s: pubkey sign failed", __func__); } } else { if (privkey) { if (sshkey_sign(privkey, signature, slenp, data, dlen, - alg, NULL, ssh->compat) < 0) + alg, options.sk_provider, ssh->compat) < 0) fatal("%s: privkey sign failed", __func__); } else { if ((r = ssh_agent_sign(auth_sock, pubkey, -- cgit v1.2.3 From c2bd7f74b0e0f3a3ee9d19ac549e6ba89013abaf Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Fri, 31 Jan 2020 22:42:45 +0000 Subject: upstream: Add a sshd_config "Include" directive to allow inclusion of files. This has sensible semantics wrt Match blocks and accepts glob(3) patterns to specify the included files. Based on patch by Jakub Jelen in bz2468; feedback and ok markus@ OpenBSD-Commit-ID: 36ed0e845b872e33f03355b936a4fff02d5794ff --- auth.c | 5 +- servconf.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++------- servconf.h | 20 +++++-- sshd.c | 65 +++++++++++++++++------ sshd_config.5 | 19 +++++-- 5 files changed, 232 insertions(+), 44 deletions(-) (limited to 'servconf.h') diff --git a/auth.c b/auth.c index b42d7e76c..086b8ebb1 100644 --- a/auth.c +++ b/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.145 2020/01/23 07:10:22 dtucker Exp $ */ +/* $OpenBSD: auth.c,v 1.146 2020/01/31 22:42:45 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -79,6 +79,7 @@ /* import */ extern ServerOptions options; +extern struct include_list includes; extern int use_privsep; extern struct sshbuf *loginmsg; extern struct passwd *privsep_pw; @@ -571,7 +572,7 @@ getpwnamallow(struct ssh *ssh, const char *user) ci = get_connection_info(ssh, 1, options.use_dns); ci->user = user; - parse_server_match_config(&options, ci); + parse_server_match_config(&options, &includes, ci); log_change_level(options.log_level); process_permitopen(ssh, &options); diff --git a/servconf.c b/servconf.c index 1e0718139..70f5f73f0 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.359 2020/01/23 10:24:29 dtucker Exp $ */ +/* $OpenBSD: servconf.c,v 1.360 2020/01/31 22:42:45 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -40,6 +40,11 @@ #ifdef HAVE_UTIL_H #include #endif +#ifdef USE_SYSTEM_GLOB +# include +#else +# include "openbsd-compat/glob.h" +#endif #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" @@ -69,6 +74,9 @@ static void add_listen_addr(ServerOptions *, const char *, const char *, int); static void add_one_listen_addr(ServerOptions *, const char *, const char *, int); +void parse_server_config_depth(ServerOptions *options, const char *filename, + struct sshbuf *conf, struct include_list *includes, + struct connection_info *connectinfo, int flags, int *activep, int depth); /* Use of privilege separation or not */ extern int use_privsep; @@ -526,7 +534,7 @@ typedef enum { sAcceptEnv, sSetEnv, sPermitTunnel, sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, sUsePrivilegeSeparation, sAllowAgentForwarding, - sHostCertificate, + sHostCertificate, sInclude, sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser, sKexAlgorithms, sCASignatureAlgorithms, sIPQoS, sVersionAddendum, @@ -538,9 +546,10 @@ typedef enum { sDeprecated, sIgnore, sUnsupported } ServerOpCodes; -#define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ -#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ -#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) +#define SSHCFG_GLOBAL 0x01 /* allowed in main section of config */ +#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ +#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) +#define SSHCFG_NEVERMATCH 0x04 /* Match never matches; internal only */ /* Textual representation of the tokens. */ static struct { @@ -669,6 +678,7 @@ static struct { { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, + { "include", sInclude, SSHCFG_ALL }, { "ipqos", sIPQoS, SSHCFG_ALL }, { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, @@ -1240,13 +1250,14 @@ static const struct multistate multistate_tcpfwd[] = { { NULL, -1 } }; -int -process_server_config_line(ServerOptions *options, char *line, +static int +process_server_config_line_depth(ServerOptions *options, char *line, const char *filename, int linenum, int *activep, - struct connection_info *connectinfo) + struct connection_info *connectinfo, int inc_flags, int depth, + struct include_list *includes) { char ch, *cp, ***chararrayptr, **charptr, *arg, *arg2, *p; - int cmdline = 0, *intptr, value, value2, n, port; + int cmdline = 0, *intptr, value, value2, n, port, oactive, r, found; SyslogFacility *log_facility_ptr; LogLevel *log_level_ptr; ServerOpCodes opcode; @@ -1255,6 +1266,8 @@ process_server_config_line(ServerOptions *options, char *line, long long val64; const struct multistate *multistate_ptr; const char *errstr; + struct include_item *item; + glob_t gbuf; /* Strip trailing whitespace. Allow \f (form feed) at EOL only */ if ((len = strlen(line)) == 0) @@ -1281,7 +1294,7 @@ process_server_config_line(ServerOptions *options, char *line, cmdline = 1; activep = &cmdline; } - if (*activep && opcode != sMatch) + if (*activep && opcode != sMatch && opcode != sInclude) debug3("%s:%d setting %s %s", filename, linenum, arg, cp); if (*activep == 0 && !(flags & SSHCFG_MATCH)) { if (connectinfo == NULL) { @@ -1954,6 +1967,96 @@ process_server_config_line(ServerOptions *options, char *line, *intptr = value; break; + case sInclude: + if (cmdline) { + fatal("Include directive not supported as a " + "command-line option"); + } + value = 0; + while ((arg2 = strdelim(&cp)) != NULL && *arg2 != '\0') { + value++; + found = 0; + if (*arg2 != '/' && *arg2 != '~') { + xasprintf(&arg, "%s/%s", SSHDIR, arg); + } else + arg = xstrdup(arg2); + + /* + * Don't let included files clobber the containing + * file's Match state. + */ + oactive = *activep; + + /* consult cache of include files */ + TAILQ_FOREACH(item, includes, entry) { + if (strcmp(item->selector, arg) != 0) + continue; + if (item->filename != NULL) { + parse_server_config_depth(options, + item->filename, item->contents, + includes, connectinfo, + (oactive ? 0 : SSHCFG_NEVERMATCH), + activep, depth + 1); + } + found = 1; + *activep = oactive; + } + if (found != 0) { + free(arg); + continue; + } + + /* requested glob was not in cache */ + debug2("%s line %d: new include %s", + filename, linenum, arg); + if ((r = glob(arg, 0, NULL, &gbuf)) != 0) { + if (r != GLOB_NOMATCH) { + fatal("%s line %d: include \"%s\" " + "glob failed", filename, + linenum, arg); + } + /* + * If no entry matched then record a + * placeholder to skip later glob calls. + */ + debug2("%s line %d: no match for %s", + filename, linenum, arg); + item = xcalloc(1, sizeof(*item)); + item->selector = strdup(arg); + TAILQ_INSERT_TAIL(includes, + item, entry); + } + if (gbuf.gl_pathc > INT_MAX) + fatal("%s: too many glob results", __func__); + for (n = 0; n < (int)gbuf.gl_pathc; n++) { + debug2("%s line %d: including %s", + filename, linenum, gbuf.gl_pathv[n]); + item = xcalloc(1, sizeof(*item)); + item->selector = strdup(arg); + item->filename = strdup(gbuf.gl_pathv[n]); + if ((item->contents = sshbuf_new()) == NULL) { + fatal("%s: sshbuf_new failed", + __func__); + } + load_server_config(item->filename, + item->contents); + parse_server_config_depth(options, + item->filename, item->contents, + includes, connectinfo, + (oactive ? 0 : SSHCFG_NEVERMATCH), + activep, depth + 1); + *activep = oactive; + TAILQ_INSERT_TAIL(includes, item, entry); + } + globfree(&gbuf); + free(arg); + } + if (value == 0) { + fatal("%s line %d: Include missing filename argument", + filename, linenum); + } + break; + case sMatch: if (cmdline) fatal("Match directive not supported as a command-line " @@ -1962,7 +2065,7 @@ process_server_config_line(ServerOptions *options, char *line, if (value < 0) fatal("%s line %d: Bad Match condition", filename, linenum); - *activep = value; + *activep = (inc_flags & SSHCFG_NEVERMATCH) ? 0 : value; break; case sPermitListen: @@ -2256,6 +2359,16 @@ process_server_config_line(ServerOptions *options, char *line, return 0; } +int +process_server_config_line(ServerOptions *options, char *line, + const char *filename, int linenum, int *activep, + struct connection_info *connectinfo, struct include_list *includes) +{ + return process_server_config_line_depth(options, line, filename, + linenum, activep, connectinfo, 0, 0, includes); +} + + /* Reads the server configuration file. */ void @@ -2294,12 +2407,13 @@ load_server_config(const char *filename, struct sshbuf *conf) void parse_server_match_config(ServerOptions *options, - struct connection_info *connectinfo) + struct include_list *includes, struct connection_info *connectinfo) { ServerOptions mo; initialize_server_options(&mo); - parse_server_config(&mo, "reprocess config", cfg, connectinfo); + parse_server_config(&mo, "reprocess config", cfg, includes, + connectinfo); copy_set_server_options(options, &mo, 0); } @@ -2443,22 +2557,27 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) #undef M_CP_STROPT #undef M_CP_STRARRAYOPT +#define SERVCONF_MAX_DEPTH 16 void -parse_server_config(ServerOptions *options, const char *filename, - struct sshbuf *conf, struct connection_info *connectinfo) +parse_server_config_depth(ServerOptions *options, const char *filename, + struct sshbuf *conf, struct include_list *includes, + struct connection_info *connectinfo, int flags, int *activep, int depth) { - int active, linenum, bad_options = 0; + int linenum, bad_options = 0; char *cp, *obuf, *cbuf; + if (depth < 0 || depth > SERVCONF_MAX_DEPTH) + fatal("Too many recursive configuration includes"); + debug2("%s: config %s len %zu", __func__, filename, sshbuf_len(conf)); if ((obuf = cbuf = sshbuf_dup_string(conf)) == NULL) fatal("%s: sshbuf_dup_string failed", __func__); - active = connectinfo ? 0 : 1; linenum = 1; while ((cp = strsep(&cbuf, "\n")) != NULL) { - if (process_server_config_line(options, cp, filename, - linenum++, &active, connectinfo) != 0) + if (process_server_config_line_depth(options, cp, + filename, linenum++, activep, connectinfo, flags, + depth, includes) != 0) bad_options++; } free(obuf); @@ -2468,6 +2587,16 @@ parse_server_config(ServerOptions *options, const char *filename, process_queued_listen_addrs(options); } +void +parse_server_config(ServerOptions *options, const char *filename, + struct sshbuf *conf, struct include_list *includes, + struct connection_info *connectinfo) +{ + int active = connectinfo ? 0 : 1; + parse_server_config_depth(options, filename, conf, includes, + connectinfo, 0, &active, 0); +} + static const char * fmt_multistate_int(int val, const struct multistate *m) { diff --git a/servconf.h b/servconf.h index 6fc1efb2c..deda09d93 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.142 2019/12/15 18:57:30 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.143 2020/01/31 22:42:45 djm Exp $ */ /* * Author: Tatu Ylonen @@ -16,6 +16,8 @@ #ifndef SERVCONF_H #define SERVCONF_H +#include + #define MAX_PORTS 256 /* Max # ports. */ #define MAX_SUBSYSTEMS 256 /* Max # subsystems. */ @@ -230,6 +232,15 @@ struct connection_info { * unspecified */ }; +/* List of included files for re-exec from the parsed configuration */ +struct include_item { + char *selector; + char *filename; + struct sshbuf *contents; + TAILQ_ENTRY(include_item) entry; +}; +TAILQ_HEAD(include_list, include_item); + /* * These are string config options that must be copied between the @@ -269,12 +280,13 @@ struct connection_info *get_connection_info(struct ssh *, int, int); void initialize_server_options(ServerOptions *); void fill_default_server_options(ServerOptions *); int process_server_config_line(ServerOptions *, char *, const char *, int, - int *, struct connection_info *); + int *, struct connection_info *, struct include_list *includes); void process_permitopen(struct ssh *ssh, ServerOptions *options); void load_server_config(const char *, struct sshbuf *); void parse_server_config(ServerOptions *, const char *, struct sshbuf *, - struct connection_info *); -void parse_server_match_config(ServerOptions *, struct connection_info *); + struct include_list *includes, struct connection_info *); +void parse_server_match_config(ServerOptions *, + struct include_list *includes, struct connection_info *); int parse_server_match_testspec(struct connection_info *, char *); int server_match_spec_complete(struct connection_info *); void copy_set_server_options(ServerOptions *, ServerOptions *, int); diff --git a/sshd.c b/sshd.c index 46fdf7ee3..57fab0425 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.545 2020/01/24 23:56:01 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.546 2020/01/31 22:42:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -251,6 +251,9 @@ struct sshauthopt *auth_opts = NULL; /* sshd_config buffer */ struct sshbuf *cfg; +/* Included files from the configuration file */ +struct include_list includes = TAILQ_HEAD_INITIALIZER(includes); + /* message to be displayed after login */ struct sshbuf *loginmsg; @@ -870,30 +873,45 @@ usage(void) static void send_rexec_state(int fd, struct sshbuf *conf) { - struct sshbuf *m; + struct sshbuf *m = NULL, *inc = NULL; + struct include_item *item = NULL; int r; debug3("%s: entering fd = %d config len %zu", __func__, fd, sshbuf_len(conf)); + if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + + /* pack includes into a string */ + TAILQ_FOREACH(item, &includes, entry) { + if ((r = sshbuf_put_cstring(inc, item->selector)) != 0 || + (r = sshbuf_put_cstring(inc, item->filename)) != 0 || + (r = sshbuf_put_stringb(inc, item->contents)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + } + /* * Protocol from reexec master to child: * string configuration - * string rngseed (only if OpenSSL is not self-seeded) + * string included_files[] { + * string selector + * string filename + * string contents + * } + * string rng_seed (if required) */ - if ((m = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); - if ((r = sshbuf_put_stringb(m, conf)) != 0) + if ((r = sshbuf_put_stringb(m, conf)) != 0 || + (r = sshbuf_put_stringb(m, inc)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); - #if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY) rexec_send_rng_seed(m); #endif - if (ssh_msg_send(fd, 0, m) == -1) fatal("%s: ssh_msg_send failed", __func__); sshbuf_free(m); + sshbuf_free(inc); debug3("%s: done", __func__); } @@ -901,14 +919,15 @@ send_rexec_state(int fd, struct sshbuf *conf) static void recv_rexec_state(int fd, struct sshbuf *conf) { - struct sshbuf *m; + struct sshbuf *m, *inc; u_char *cp, ver; size_t len; int r; + struct include_item *item; debug3("%s: entering fd = %d", __func__, fd); - if ((m = sshbuf_new()) == NULL) + if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if (ssh_msg_recv(fd, m) == -1) fatal("%s: ssh_msg_recv failed", __func__); @@ -916,14 +935,28 @@ recv_rexec_state(int fd, struct sshbuf *conf) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (ver != 0) fatal("%s: rexec version mismatch", __func__); - if ((r = sshbuf_get_string(m, &cp, &len)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); - if (conf != NULL && (r = sshbuf_put(conf, cp, len))) + if ((r = sshbuf_get_string(m, &cp, &len)) != 0 || + (r = sshbuf_get_stringb(m, inc)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); + #if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY) rexec_recv_rng_seed(m); #endif + if (conf != NULL && (r = sshbuf_put(conf, cp, len))) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + while (sshbuf_len(inc) != 0) { + item = xcalloc(1, sizeof(*item)); + if ((item->contents = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + if ((r = sshbuf_get_cstring(inc, &item->selector, NULL)) != 0 || + (r = sshbuf_get_cstring(inc, &item->filename, NULL)) != 0 || + (r = sshbuf_get_stringb(inc, item->contents)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + TAILQ_INSERT_TAIL(&includes, item, entry); + } + free(cp); sshbuf_free(m); @@ -1600,7 +1633,7 @@ main(int ac, char **av) case 'o': line = xstrdup(optarg); if (process_server_config_line(&options, line, - "command-line", 0, NULL, NULL) != 0) + "command-line", 0, NULL, NULL, &includes) != 0) exit(1); free(line); break; @@ -1669,7 +1702,7 @@ main(int ac, char **av) load_server_config(config_file_name, cfg); parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name, - cfg, NULL); + cfg, &includes, NULL); /* Fill in default values for those options not explicitly set. */ fill_default_server_options(&options); @@ -1895,7 +1928,7 @@ main(int ac, char **av) if (connection_info == NULL) connection_info = get_connection_info(ssh, 0, 0); connection_info->test = 1; - parse_server_match_config(&options, connection_info); + parse_server_match_config(&options, &includes, connection_info); dump_config(&options); } diff --git a/sshd_config.5 b/sshd_config.5 index 1395a5e6d..6c3b5e5e0 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.303 2020/01/28 01:49:36 djm Exp $ -.Dd $Mdocdate: January 28 2020 $ +.\" $OpenBSD: sshd_config.5,v 1.304 2020/01/31 22:42:45 djm Exp $ +.Dd $Mdocdate: January 31 2020 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -801,7 +801,20 @@ during and use only the system-wide known hosts file .Pa /etc/ssh/known_hosts . The default is -.Cm no . +.Dq no . +.It Cm Include +Include the specified configuration file(s). +Multiple path names may be specified and each pathname may contain +.Xr glob 7 +wildcards. +Files without absolute paths are assumed to be in +.Pa /etc/ssh . +A +.Cm Include +directive may appear inside a +.Cm Match +block +to perform conditional inclusion. .It Cm IPQoS Specifies the IPv4 type-of-service or DSCP class for the connection. Accepted values are -- cgit v1.2.3 From 92725d4d3fde675acc0ca040b48f3d0c7be73b7f Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Sat, 1 Feb 2020 17:25:09 +1100 Subject: Use sys-queue.h from compat library. Fixes build on platforms that don't have sys/queue.h (eg MUSL). --- servconf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'servconf.h') diff --git a/servconf.h b/servconf.h index deda09d93..4202a2d02 100644 --- a/servconf.h +++ b/servconf.h @@ -16,7 +16,7 @@ #ifndef SERVCONF_H #define SERVCONF_H -#include +#include #define MAX_PORTS 256 /* Max # ports. */ -- cgit v1.2.3