From bb6cc07cf4e98d86b38a2011203c6bb724465135 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Mon, 17 Sep 2012 13:25:06 +1000 Subject: - dtucker@cvs.openbsd.org 2012/09/13 23:37:36 [servconf.c] Fix comment line length --- servconf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'servconf.c') diff --git a/servconf.c b/servconf.c index ee2e531a0..f4b7dd58b 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.229 2012/07/13 01:35:21 dtucker Exp $ */ +/* $OpenBSD: servconf.c,v 1.230 2012/09/13 23:37:36 dtucker Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -618,8 +618,9 @@ out: } /* - * All of the attributes on a single Match line are ANDed together, so we need to check every - * attribute and set the result to zero if any attribute does not match. + * All of the attributes on a single Match line are ANDed together, so we need + * to check every * attribute and set the result to zero if any attribute does + * not match. */ static int match_cfg_line(char **condition, int line, struct connection_info *ci) -- cgit v1.2.3 From 09d3e1251250dcf45e5434cd474430e4ec5e8639 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Wed, 31 Oct 2012 08:58:58 +1100 Subject: - djm@cvs.openbsd.org 2012/10/30 21:29:55 [auth-rsa.c auth.c auth.h auth2-pubkey.c servconf.c servconf.h] [sshd.c sshd_config sshd_config.5] new sshd_config option AuthorizedKeysCommand to support fetching authorized_keys from a command in addition to (or instead of) from the filesystem. The command is run as the target server user unless another specified via a new AuthorizedKeysCommandUser option. patch originally by jchadima AT redhat.com, reworked by me; feedback and ok markus@ --- ChangeLog | 10 +++ auth-rsa.c | 4 +- auth.c | 53 ++++++++++----- auth.h | 6 +- auth2-pubkey.c | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++------ servconf.c | 30 ++++++++- servconf.h | 4 +- sshd.c | 11 ++- sshd_config | 5 +- sshd_config.5 | 22 +++++- 10 files changed, 308 insertions(+), 43 deletions(-) (limited to 'servconf.c') diff --git a/ChangeLog b/ChangeLog index 3cd16e480..27ec898b8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,16 @@ - markus@cvs.openbsd.org 2012/10/05 12:34:39 [sftp.c] fix signed vs unsigned warning; feedback & ok: djm@ + - djm@cvs.openbsd.org 2012/10/30 21:29:55 + [auth-rsa.c auth.c auth.h auth2-pubkey.c servconf.c servconf.h] + [sshd.c sshd_config sshd_config.5] + new sshd_config option AuthorizedKeysCommand to support fetching + authorized_keys from a command in addition to (or instead of) from + the filesystem. The command is run as the target server user unless + another specified via a new AuthorizedKeysCommandUser option. + + patch originally by jchadima AT redhat.com, reworked by me; feedback + and ok markus@ 20121019 - (tim) [buildpkg.sh.in] Double up on some backslashes so they end up in diff --git a/auth-rsa.c b/auth-rsa.c index 4ab46cd51..2c8a7cb35 100644 --- a/auth-rsa.c +++ b/auth-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-rsa.c,v 1.80 2011/05/23 03:30:07 djm Exp $ */ +/* $OpenBSD: auth-rsa.c,v 1.81 2012/10/30 21:29:54 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -276,6 +276,8 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) temporarily_use_uid(pw); for (i = 0; !allowed && i < options.num_authkeys_files; i++) { + if (strcasecmp(options.authorized_keys_files[i], "none") == 0) + continue; file = expand_authorized_keys( options.authorized_keys_files[i], pw); allowed = rsa_key_allowed_in_file(pw, file, client_n, rkey); diff --git a/auth.c b/auth.c index a8cffd5c1..b5e1eefa0 100644 --- a/auth.c +++ b/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.96 2012/05/13 01:42:32 dtucker Exp $ */ +/* $OpenBSD: auth.c,v 1.97 2012/10/30 21:29:54 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -409,41 +409,42 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, return host_status; } - /* - * Check a given file for security. This is defined as all components + * Check a given path for security. This is defined as all components * of the path to the file must be owned by either the owner of * of the file or root and no directories must be group or world writable. * * XXX Should any specific check be done for sym links ? * - * Takes an open file descriptor, the file name, a uid and and + * Takes an the file name, its stat information (preferably from fstat() to + * avoid races), the uid of the expected owner, their home directory and an * error buffer plus max size as arguments. * * Returns 0 on success and -1 on failure */ -static int -secure_filename(FILE *f, const char *file, struct passwd *pw, - char *err, size_t errlen) +int +auth_secure_path(const char *name, struct stat *stp, const char *pw_dir, + uid_t uid, char *err, size_t errlen) { - uid_t uid = pw->pw_uid; char buf[MAXPATHLEN], homedir[MAXPATHLEN]; char *cp; int comparehome = 0; struct stat st; - if (realpath(file, buf) == NULL) { - snprintf(err, errlen, "realpath %s failed: %s", file, + if (realpath(name, buf) == NULL) { + snprintf(err, errlen, "realpath %s failed: %s", name, strerror(errno)); return -1; } - if (realpath(pw->pw_dir, homedir) != NULL) + if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL) comparehome = 1; - /* check the open file to avoid races */ - if (fstat(fileno(f), &st) < 0 || - (st.st_uid != 0 && st.st_uid != uid) || - (st.st_mode & 022) != 0) { + if (!S_ISREG(stp->st_mode)) { + snprintf(err, errlen, "%s is not a regular file", buf); + return -1; + } + if ((stp->st_uid != 0 && stp->st_uid != uid) || + (stp->st_mode & 022) != 0) { snprintf(err, errlen, "bad ownership or modes for file %s", buf); return -1; @@ -479,6 +480,28 @@ secure_filename(FILE *f, const char *file, struct passwd *pw, return 0; } +/* + * Version of secure_path() that accepts an open file descriptor to + * avoid races. + * + * Returns 0 on success and -1 on failure + */ +static int +secure_filename(FILE *f, const char *file, struct passwd *pw, + char *err, size_t errlen) +{ + char buf[MAXPATHLEN]; + struct stat st; + + /* check the open file to avoid races */ + if (fstat(fileno(f), &st) < 0) { + snprintf(err, errlen, "cannot stat file %s: %s", + buf, strerror(errno)); + return -1; + } + return auth_secure_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen); +} + static FILE * auth_openfile(const char *file, struct passwd *pw, int strict_modes, int log_missing, char *file_type) diff --git a/auth.h b/auth.h index 0d786c4d5..063404167 100644 --- a/auth.h +++ b/auth.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.h,v 1.69 2011/05/23 03:30:07 djm Exp $ */ +/* $OpenBSD: auth.h,v 1.70 2012/10/30 21:29:54 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -120,6 +120,10 @@ int auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, Key *); int hostbased_key_allowed(struct passwd *, const char *, char *, Key *); int user_key_allowed(struct passwd *, Key *); +struct stat; +int auth_secure_path(const char *, struct stat *, const char *, uid_t, + char *, size_t); + #ifdef KRB5 int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *); int auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt); diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 5bccb5d76..ec8f75d57 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.30 2011/09/25 05:44:47 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.31 2012/10/30 21:29:54 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -27,9 +27,13 @@ #include #include +#include +#include #include +#include #include +#include #include #include #include @@ -240,7 +244,7 @@ match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert) if (strcmp(cp, cert->principals[i]) == 0) { debug3("matched principal \"%.100s\" " "from file \"%s\" on line %lu", - cert->principals[i], file, linenum); + cert->principals[i], file, linenum); if (auth_parse_options(pw, line_opts, file, linenum) != 1) continue; @@ -253,31 +257,22 @@ match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert) fclose(f); restore_uid(); return 0; -} +} -/* return 1 if user allows given key */ +/* + * Checks whether key is allowed in authorized_keys-format file, + * returns 1 if the key is allowed or 0 otherwise. + */ static int -user_key_allowed2(struct passwd *pw, Key *key, char *file) +check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw) { char line[SSH_MAX_PUBKEY_BYTES]; const char *reason; int found_key = 0; - FILE *f; u_long linenum = 0; Key *found; char *fp; - /* Temporarily use the user's uid. */ - temporarily_use_uid(pw); - - debug("trying public key file %s", file); - f = auth_openkeyfile(file, pw, options.strict_modes); - - if (!f) { - restore_uid(); - return 0; - } - found_key = 0; found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type); @@ -370,8 +365,6 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) break; } } - restore_uid(); - fclose(f); key_free(found); if (!found_key) debug2("key not found"); @@ -433,7 +426,172 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) return ret; } -/* check whether given key is in .ssh/authorized_keys* */ +/* + * Checks whether key is allowed in file. + * returns 1 if the key is allowed or 0 otherwise. + */ +static int +user_key_allowed2(struct passwd *pw, Key *key, char *file) +{ + FILE *f; + int found_key = 0; + + /* Temporarily use the user's uid. */ + temporarily_use_uid(pw); + + debug("trying public key file %s", file); + if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) { + found_key = check_authkeys_file(f, file, key, pw); + fclose(f); + } + + restore_uid(); + return found_key; +} + +/* + * Checks whether key is allowed in output of command. + * returns 1 if the key is allowed or 0 otherwise. + */ +static int +user_key_command_allowed2(struct passwd *user_pw, Key *key) +{ + FILE *f; + int ok, found_key = 0; + struct passwd *pw; + struct stat st; + int status, devnull, p[2], i; + pid_t pid; + char errmsg[512]; + + if (options.authorized_keys_command == NULL || + options.authorized_keys_command[0] != '/') + return 0; + + /* If no user specified to run commands the default to target user */ + if (options.authorized_keys_command_user == NULL) + pw = user_pw; + else { + pw = getpwnam(options.authorized_keys_command_user); + if (pw == NULL) { + error("AuthorizedKeyCommandUser \"%s\" not found: %s", + options.authorized_keys_command, strerror(errno)); + return 0; + } + } + + temporarily_use_uid(pw); + + if (stat(options.authorized_keys_command, &st) < 0) { + error("Could not stat AuthorizedKeysCommand \"%s\": %s", + options.authorized_keys_command, strerror(errno)); + goto out; + } + if (auth_secure_path(options.authorized_keys_command, &st, NULL, 0, + errmsg, sizeof(errmsg)) != 0) { + error("Unsafe AuthorizedKeysCommand: %s", errmsg); + goto out; + } + + if (pipe(p) != 0) { + error("%s: pipe: %s", __func__, strerror(errno)); + goto out; + } + + debug3("Running AuthorizedKeysCommand: \"%s\" as \"%s\"", + options.authorized_keys_command, pw->pw_name); + + /* + * Don't want to call this in the child, where it can fatal() and + * run cleanup_exit() code. + */ + restore_uid(); + + switch ((pid = fork())) { + case -1: /* error */ + error("%s: fork: %s", __func__, strerror(errno)); + close(p[0]); + close(p[1]); + return 0; + case 0: /* child */ + for (i = 0; i < NSIG; i++) + signal(i, SIG_DFL); + + /* Don't use permanently_set_uid() here to avoid fatal() */ + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { + error("setresgid %u: %s", (u_int)pw->pw_gid, + strerror(errno)); + _exit(1); + } + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { + error("setresuid %u: %s", (u_int)pw->pw_uid, + strerror(errno)); + _exit(1); + } + + close(p[0]); + if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { + error("%s: open %s: %s", __func__, _PATH_DEVNULL, + strerror(errno)); + _exit(1); + } + if (dup2(devnull, STDIN_FILENO) == -1 || + dup2(p[1], STDOUT_FILENO) == -1 || + dup2(devnull, STDERR_FILENO) == -1) { + error("%s: dup2: %s", __func__, strerror(errno)); + _exit(1); + } + closefrom(STDERR_FILENO + 1); + + execl(options.authorized_keys_command, + options.authorized_keys_command, pw->pw_name, NULL); + + error("AuthorizedKeysCommand %s exec failed: %s", + options.authorized_keys_command, strerror(errno)); + _exit(127); + default: /* parent */ + break; + } + + temporarily_use_uid(pw); + + close(p[1]); + if ((f = fdopen(p[0], "r")) == NULL) { + error("%s: fdopen: %s", __func__, strerror(errno)); + close(p[0]); + /* Don't leave zombie child */ + kill(pid, SIGTERM); + while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) + ; + goto out; + } + ok = check_authkeys_file(f, options.authorized_keys_command, key, pw); + fclose(f); + + while (waitpid(pid, &status, 0) == -1) { + if (errno != EINTR) { + error("%s: waitpid: %s", __func__, strerror(errno)); + goto out; + } + } + if (WIFSIGNALED(status)) { + error("AuthorizedKeysCommand %s exited on signal %d", + options.authorized_keys_command, WTERMSIG(status)); + goto out; + } else if (WEXITSTATUS(status) != 0) { + error("AuthorizedKeysCommand %s returned status %d", + options.authorized_keys_command, WEXITSTATUS(status)); + goto out; + } + found_key = ok; + out: + restore_uid(); + return found_key; +} + +/* + * Check whether key authenticates and authorises the user. + */ int user_key_allowed(struct passwd *pw, Key *key) { @@ -449,9 +607,17 @@ user_key_allowed(struct passwd *pw, Key *key) if (success) return success; + success = user_key_command_allowed2(pw, key); + if (success > 0) + return success; + for (i = 0; !success && i < options.num_authkeys_files; i++) { + + if (strcasecmp(options.authorized_keys_files[i], "none") == 0) + continue; file = expand_authorized_keys( options.authorized_keys_files[i], pw); + success = user_key_allowed2(pw, key, file); xfree(file); } diff --git a/servconf.c b/servconf.c index f4b7dd58b..8e69ea5ce 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.230 2012/09/13 23:37:36 dtucker Exp $ */ +/* $OpenBSD: servconf.c,v 1.231 2012/10/30 21:29:54 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -135,6 +135,8 @@ initialize_server_options(ServerOptions *options) options->num_permitted_opens = -1; options->adm_forced_command = NULL; options->chroot_directory = NULL; + options->authorized_keys_command = NULL; + options->authorized_keys_command_user = NULL; options->zero_knowledge_password_authentication = -1; options->revoked_keys_file = NULL; options->trusted_user_ca_keys = NULL; @@ -329,6 +331,7 @@ typedef enum { sZeroKnowledgePasswordAuthentication, sHostCertificate, sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, sKexAlgorithms, sIPQoS, sVersionAddendum, + sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, sDeprecated, sUnsupported } ServerOpCodes; @@ -453,6 +456,8 @@ static struct { { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, { "ipqos", sIPQoS, SSHCFG_ALL }, + { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, + { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, { NULL, sBadOption, 0 } }; @@ -1498,6 +1503,25 @@ process_server_config_line(ServerOptions *options, char *line, } return 0; + case sAuthorizedKeysCommand: + len = strspn(cp, WHITESPACE); + if (*activep && options->authorized_keys_command == NULL) { + if (cp[len] != '/' && strcasecmp(cp + len, "none") != 0) + fatal("%.200s line %d: AuthorizedKeysCommand " + "must be an absolute path", + filename, linenum); + options->authorized_keys_command = xstrdup(cp + len); + } + return 0; + + case sAuthorizedKeysCommandUser: + charptr = &options->authorized_keys_command_user; + + arg = strdelim(&cp); + if (*activep && *charptr == NULL) + *charptr = xstrdup(arg); + break; + case sDeprecated: logit("%s line %d: Deprecated option %s", filename, linenum, arg); @@ -1648,6 +1672,8 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) M_CP_INTOPT(hostbased_uses_name_from_packet_only); M_CP_INTOPT(kbd_interactive_authentication); M_CP_INTOPT(zero_knowledge_password_authentication); + M_CP_STROPT(authorized_keys_command); + M_CP_STROPT(authorized_keys_command_user); M_CP_INTOPT(permit_root_login); M_CP_INTOPT(permit_empty_passwd); @@ -1908,6 +1934,8 @@ dump_config(ServerOptions *o) dump_cfg_string(sAuthorizedPrincipalsFile, o->authorized_principals_file); dump_cfg_string(sVersionAddendum, o->version_addendum); + dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command); + dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user); /* string arguments requiring a lookup */ dump_cfg_string(sLogLevel, log_level_name(o->log_level)); diff --git a/servconf.h b/servconf.h index 096d596d7..0064c9bc5 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.103 2012/07/10 02:19:15 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.104 2012/10/30 21:29:55 djm Exp $ */ /* * Author: Tatu Ylonen @@ -166,6 +166,8 @@ typedef struct { char *revoked_keys_file; char *trusted_user_ca_keys; char *authorized_principals_file; + char *authorized_keys_command; + char *authorized_keys_command_user; char *version_addendum; /* Appended to SSH banner */ } ServerOptions; diff --git a/sshd.c b/sshd.c index 9aff5e8af..eff0290b0 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.393 2012/07/10 02:19:15 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.394 2012/10/30 21:29:55 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -359,6 +359,15 @@ grace_alarm_handler(int sig) if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0) kill(pmonitor->m_pid, SIGALRM); + /* + * Try to kill any processes that we have spawned, E.g. authorized + * keys command helpers. + */ + if (getpgid(0) == getpid()) { + signal(SIGTERM, SIG_IGN); + killpg(0, SIGTERM); + } + /* Log error and exit. */ sigdie("Timeout before authentication for %s", get_remote_ipaddr()); } diff --git a/sshd_config b/sshd_config index 9424ee2c6..3d35bef02 100644 --- a/sshd_config +++ b/sshd_config @@ -1,4 +1,4 @@ -# $OpenBSD: sshd_config,v 1.87 2012/07/10 02:19:15 djm Exp $ +# $OpenBSD: sshd_config,v 1.88 2012/10/30 21:29:55 djm Exp $ # This is the sshd server system-wide configuration file. See # sshd_config(5) for more information. @@ -51,6 +51,9 @@ AuthorizedKeysFile .ssh/authorized_keys #AuthorizedPrincipalsFile none +#AuthorizedKeysCommand none +#AuthorizedKeysCommandUser nobody + # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts #RhostsRSAAuthentication no # similar for protocol version 2 diff --git a/sshd_config.5 b/sshd_config.5 index 987558ae8..de8f0f825 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.145 2012/10/04 13:21:50 markus Exp $ -.Dd $Mdocdate: October 4 2012 $ +.\" $OpenBSD: sshd_config.5,v 1.146 2012/10/30 21:29:55 djm Exp $ +.Dd $Mdocdate: October 30 2012 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -151,6 +151,22 @@ See in .Xr ssh_config 5 for more information on patterns. +.It Cm AuthorizedKeysCommand +Specifies a program to be used for lookup of the user's public keys. +The program will be invoked with a single argument of the username +being authenticated, and should produce on standard output zero or +more lines of authorized_keys output (see AUTHORIZED_KEYS in +.Xr sshd 8 ) +If a key supplied by AuthorizedKeysCommand does not successfully authenticate +and authorize the user then public key authentication continues using the usual +.Cm AuthorizedKeysFile +files. +By default, no AuthorizedKeysCommand is run. +.It Cm AuthorizedKeysCommandUser +Specifies the user under whose account the AuthorizedKeysCommand is run. +The default is the user being authenticated. +It is recommended to use a dedicated user that has no other role on the host +than running authorized keys commands. .It Cm AuthorizedKeysFile Specifies the file that contains the public keys that can be used for user authentication. @@ -712,6 +728,8 @@ Available keywords are .Cm AllowTcpForwarding , .Cm AllowUsers , .Cm AuthorizedKeysFile , +.Cm AuthorizedKeysCommand , +.Cm AuthorizedKeysCommandUser , .Cm AuthorizedPrincipalsFile , .Cm Banner , .Cm ChrootDirectory , -- cgit v1.2.3 From a6e3f01d1e230b8acfdd6b4cf3096459d2a325e0 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Sun, 4 Nov 2012 23:21:40 +1100 Subject: - djm@cvs.openbsd.org 2012/11/04 11:09:15 [auth.h auth1.c auth2.c monitor.c servconf.c servconf.h sshd.c] [sshd_config.5] Support multiple required authentication via an AuthenticationMethods option. This option lists one or more comma-separated lists of authentication method names. Successful completion of all the methods in any list is required for authentication to complete; feedback and ok markus@ --- ChangeLog | 8 +++ auth.h | 7 +- auth1.c | 7 +- auth2.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- monitor.c | 35 ++++++++-- servconf.c | 26 ++++++- servconf.h | 7 +- sshd.c | 23 ++++++- sshd_config.5 | 25 ++++++- 9 files changed, 328 insertions(+), 28 deletions(-) (limited to 'servconf.c') diff --git a/ChangeLog b/ChangeLog index 120c132af..85c92eb7f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,14 @@ [auth2-pubkey.c sshd.c sshd_config.5] Remove default of AuthorizedCommandUser. Administrators are now expected to explicitly specify a user. feedback and ok markus@ + - djm@cvs.openbsd.org 2012/11/04 11:09:15 + [auth.h auth1.c auth2.c monitor.c servconf.c servconf.h sshd.c] + [sshd_config.5] + Support multiple required authentication via an AuthenticationMethods + option. This option lists one or more comma-separated lists of + authentication method names. Successful completion of all the methods in + any list is required for authentication to complete; + feedback and ok markus@ 20121030 - (djm) OpenBSD CVS Sync diff --git a/auth.h b/auth.h index 063404167..8920c7dae 100644 --- a/auth.h +++ b/auth.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.h,v 1.70 2012/10/30 21:29:54 djm Exp $ */ +/* $OpenBSD: auth.h,v 1.71 2012/11/04 11:09:15 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -64,6 +64,8 @@ struct Authctxt { #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; @@ -152,6 +154,9 @@ void userauth_send_banner(const char *); int auth_root_allowed(char *); char *auth2_read_banner(void); +int auth2_methods_valid(const char *, int); +int auth2_update_methods_lists(Authctxt *, const char *); +int auth2_setup_methods_lists(Authctxt *); void privsep_challenge_enable(void); diff --git a/auth1.c b/auth1.c index cc85aec74..fb37fadfe 100644 --- a/auth1.c +++ b/auth1.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth1.c,v 1.75 2010/08/31 09:58:37 djm Exp $ */ +/* $OpenBSD: auth1.c,v 1.76 2012/11/04 11:09:15 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -406,6 +406,11 @@ do_authentication(Authctxt *authctxt) authctxt->pw = fakepw(); } + /* Configuration may have changed as a result of Match */ + if (options.num_auth_methods != 0) + fatal("AuthenticationMethods is not supported with SSH " + "protocol 1"); + setproctitle("%s%s", authctxt->valid ? user : "unknown", use_privsep ? " [net]" : ""); diff --git a/auth2.c b/auth2.c index b66bef64c..8114ec863 100644 --- a/auth2.c +++ b/auth2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2.c,v 1.124 2011/12/07 05:44:38 djm Exp $ */ +/* $OpenBSD: auth2.c,v 1.125 2012/11/04 11:09:15 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -96,8 +96,10 @@ static void input_service_request(int, u_int32_t, void *); static void input_userauth_request(int, u_int32_t, void *); /* helper */ -static Authmethod *authmethod_lookup(const char *); -static char *authmethods_get(void); +static Authmethod *authmethod_lookup(Authctxt *, const char *); +static char *authmethods_get(Authctxt *authctxt); +static int method_allowed(Authctxt *, const char *); +static int list_starts_with(const char *, const char *); char * auth2_read_banner(void) @@ -255,6 +257,8 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) if (use_privsep) mm_inform_authserv(service, style); userauth_banner(); + if (auth2_setup_methods_lists(authctxt) != 0) + packet_disconnect("no authentication methods enabled"); } else if (strcmp(user, authctxt->user) != 0 || strcmp(service, authctxt->service) != 0) { packet_disconnect("Change of username or service not allowed: " @@ -277,7 +281,7 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) authctxt->server_caused_failure = 0; /* try to authenticate user */ - m = authmethod_lookup(method); + m = authmethod_lookup(authctxt, method); if (m != NULL && authctxt->failures < options.max_authtries) { debug2("input_userauth_request: try method %s", method); authenticated = m->userauth(authctxt); @@ -293,6 +297,7 @@ void userauth_finish(Authctxt *authctxt, int authenticated, char *method) { char *methods; + int partial = 0; if (!authctxt->valid && authenticated) fatal("INTERNAL ERROR: authenticated invalid user %s", @@ -335,7 +340,13 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method) if (authctxt->postponed) return; - /* XXX todo: check if multiple auth methods are needed */ + if (authenticated && options.num_auth_methods != 0) { + if (!auth2_update_methods_lists(authctxt, method)) { + authenticated = 0; + partial = 1; + } + } + if (authenticated == 1) { /* turn off userauth */ dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); @@ -356,34 +367,61 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method) #endif packet_disconnect(AUTH_FAIL_MSG, authctxt->user); } - methods = authmethods_get(); + methods = authmethods_get(authctxt); + debug3("%s: failure partial=%d next methods=\"%s\"", __func__, + partial, methods); packet_start(SSH2_MSG_USERAUTH_FAILURE); packet_put_cstring(methods); - packet_put_char(0); /* XXX partial success, unused */ + packet_put_char(partial); packet_send(); packet_write_wait(); xfree(methods); } } +/* + * Checks whether method is allowed by at least one AuthenticationMethods + * methods list. Returns 1 if allowed, or no methods lists configured. + * 0 otherwise. + */ +static int +method_allowed(Authctxt *authctxt, const char *method) +{ + u_int i; + + /* + * NB. authctxt->num_auth_methods might be zero as a result of + * auth2_setup_methods_lists(), so check the configuration. + */ + if (options.num_auth_methods == 0) + return 1; + for (i = 0; i < authctxt->num_auth_methods; i++) { + if (list_starts_with(authctxt->auth_methods[i], method)) + return 1; + } + return 0; +} + static char * -authmethods_get(void) +authmethods_get(Authctxt *authctxt) { Buffer b; char *list; - int i; + u_int i; buffer_init(&b); for (i = 0; authmethods[i] != NULL; i++) { if (strcmp(authmethods[i]->name, "none") == 0) continue; - if (authmethods[i]->enabled != NULL && - *(authmethods[i]->enabled) != 0) { - if (buffer_len(&b) > 0) - buffer_append(&b, ",", 1); - buffer_append(&b, authmethods[i]->name, - strlen(authmethods[i]->name)); - } + if (authmethods[i]->enabled == NULL || + *(authmethods[i]->enabled) == 0) + continue; + if (!method_allowed(authctxt, authmethods[i]->name)) + continue; + if (buffer_len(&b) > 0) + buffer_append(&b, ",", 1); + buffer_append(&b, authmethods[i]->name, + strlen(authmethods[i]->name)); } buffer_append(&b, "\0", 1); list = xstrdup(buffer_ptr(&b)); @@ -392,7 +430,7 @@ authmethods_get(void) } static Authmethod * -authmethod_lookup(const char *name) +authmethod_lookup(Authctxt *authctxt, const char *name) { int i; @@ -400,10 +438,154 @@ authmethod_lookup(const char *name) for (i = 0; authmethods[i] != NULL; i++) if (authmethods[i]->enabled != NULL && *(authmethods[i]->enabled) != 0 && - strcmp(name, authmethods[i]->name) == 0) + strcmp(name, authmethods[i]->name) == 0 && + method_allowed(authctxt, authmethods[i]->name)) return authmethods[i]; debug2("Unrecognized authentication method name: %s", name ? name : "NULL"); return NULL; } +/* + * Check a comma-separated list of methods for validity. Is need_enable is + * non-zero, then also require that the methods are enabled. + * Returns 0 on success or -1 if the methods list is invalid. + */ +int +auth2_methods_valid(const char *_methods, int need_enable) +{ + char *methods, *omethods, *method; + u_int i, found; + int ret = -1; + + if (*_methods == '\0') { + error("empty authentication method list"); + return -1; + } + omethods = methods = xstrdup(_methods); + while ((method = strsep(&methods, ",")) != NULL) { + for (found = i = 0; !found && authmethods[i] != NULL; i++) { + if (strcmp(method, authmethods[i]->name) != 0) + continue; + if (need_enable) { + if (authmethods[i]->enabled == NULL || + *(authmethods[i]->enabled) == 0) { + error("Disabled method \"%s\" in " + "AuthenticationMethods list \"%s\"", + method, _methods); + goto out; + } + } + found = 1; + break; + } + if (!found) { + error("Unknown authentication method \"%s\" in list", + method); + goto out; + } + } + ret = 0; + out: + free(omethods); + return ret; +} + +/* + * Prune the AuthenticationMethods supplied in the configuration, removing + * any methods lists that include disabled methods. Note that this might + * leave authctxt->num_auth_methods == 0, even when multiple required auth + * has been requested. For this reason, all tests for whether multiple is + * enabled should consult options.num_auth_methods directly. + */ +int +auth2_setup_methods_lists(Authctxt *authctxt) +{ + u_int i; + + if (options.num_auth_methods == 0) + return 0; + debug3("%s: checking methods", __func__); + authctxt->auth_methods = xcalloc(options.num_auth_methods, + sizeof(*authctxt->auth_methods)); + authctxt->num_auth_methods = 0; + for (i = 0; i < options.num_auth_methods; i++) { + if (auth2_methods_valid(options.auth_methods[i], 1) != 0) { + logit("Authentication methods list \"%s\" contains " + "disabled method, skipping", + options.auth_methods[i]); + continue; + } + debug("authentication methods list %d: %s", + authctxt->num_auth_methods, options.auth_methods[i]); + authctxt->auth_methods[authctxt->num_auth_methods++] = + xstrdup(options.auth_methods[i]); + } + if (authctxt->num_auth_methods == 0) { + error("No AuthenticationMethods left after eliminating " + "disabled methods"); + return -1; + } + return 0; +} + +static int +list_starts_with(const char *methods, const char *method) +{ + size_t l = strlen(method); + + if (strncmp(methods, method, l) != 0) + return 0; + if (methods[l] != ',' && methods[l] != '\0') + return 0; + return 1; +} + +/* + * Remove method from the start of a comma-separated list of methods. + * Returns 0 if the list of methods did not start with that method or 1 + * if it did. + */ +static int +remove_method(char **methods, const char *method) +{ + char *omethods = *methods; + size_t l = strlen(method); + + if (!list_starts_with(omethods, method)) + return 0; + *methods = xstrdup(omethods + l + (omethods[l] == ',' ? 1 : 0)); + free(omethods); + return 1; +} + +/* + * Called after successful authentication. Will remove the successful method + * from the start of each list in which it occurs. If it was the last method + * in any list, then authentication is deemed successful. + * Returns 1 if the method completed any authentication list or 0 otherwise. + */ +int +auth2_update_methods_lists(Authctxt *authctxt, const char *method) +{ + u_int i, found = 0; + + debug3("%s: updating methods list after \"%s\"", __func__, method); + for (i = 0; i < authctxt->num_auth_methods; i++) { + if (!remove_method(&(authctxt->auth_methods[i]), method)) + continue; + found = 1; + if (*authctxt->auth_methods[i] == '\0') { + debug2("authentication methods list %d complete", i); + return 1; + } + debug3("authentication methods list %d remaining: \"%s\"", + i, authctxt->auth_methods[i]); + } + /* This should not happen, but would be bad if it did */ + if (!found) + fatal("%s: method not in AuthenticationMethods", __func__); + return 0; +} + + diff --git a/monitor.c b/monitor.c index e9802a3fd..0adbf3a65 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.117 2012/06/22 12:30:26 dtucker Exp $ */ +/* $OpenBSD: monitor.c,v 1.118 2012/11/04 11:09:15 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -381,6 +381,21 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) while (!authenticated) { auth_method = "unknown"; authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1); + + /* Special handling for multiple required authentications */ + if (options.num_auth_methods != 0) { + if (!compat20) + fatal("AuthenticationMethods is not supported" + "with SSH protocol 1"); + if (authenticated && + !auth2_update_methods_lists(authctxt, + auth_method)) { + debug3("%s: method %s: partial", __func__, + auth_method); + authenticated = 0; + } + } + if (authenticated) { if (!(ent->flags & MON_AUTHDECIDE)) fatal("%s: unexpected authentication from %d", @@ -401,7 +416,6 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) } #endif } - if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) { auth_log(authctxt, authenticated, auth_method, compat20 ? " ssh2" : ""); @@ -781,7 +795,17 @@ mm_answer_pwnamallow(int sock, Buffer *m) COPY_MATCH_STRING_OPTS(); #undef M_CP_STROPT #undef M_CP_STRARRAYOPT - + + /* Create valid auth method lists */ + if (compat20 && auth2_setup_methods_lists(authctxt) != 0) { + /* + * The monitor will continue long enough to let the child + * run to it's packet_disconnect(), but it must not allow any + * authentication to succeed. + */ + debug("%s: no valid authentication method lists", __func__); + } + debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed); mm_request_send(sock, MONITOR_ANS_PWNAM, m); @@ -918,7 +942,10 @@ mm_answer_bsdauthrespond(int sock, Buffer *m) debug3("%s: sending authenticated: %d", __func__, authok); mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m); - auth_method = "bsdauth"; + if (compat20) + auth_method = "keyboard-interactive"; + else + auth_method = "bsdauth"; return (authok != 0); } diff --git a/servconf.c b/servconf.c index 8e69ea5ce..b90dba63b 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.231 2012/10/30 21:29:54 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.232 2012/11/04 11:09:15 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -48,6 +48,8 @@ #include "groupaccess.h" #include "canohost.h" #include "packet.h" +#include "hostfile.h" +#include "auth.h" static void add_listen_addr(ServerOptions *, char *, int); static void add_one_listen_addr(ServerOptions *, char *, int); @@ -332,6 +334,7 @@ typedef enum { sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, sKexAlgorithms, sIPQoS, sVersionAddendum, sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, + sAuthenticationMethods, sDeprecated, sUnsupported } ServerOpCodes; @@ -459,6 +462,7 @@ static struct { { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, + { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL }, { NULL, sBadOption, 0 } }; @@ -1522,6 +1526,24 @@ process_server_config_line(ServerOptions *options, char *line, *charptr = xstrdup(arg); break; + case sAuthenticationMethods: + if (*activep && options->num_auth_methods == 0) { + while ((arg = strdelim(&cp)) && *arg != '\0') { + if (options->num_auth_methods >= + MAX_AUTH_METHODS) + fatal("%s line %d: " + "too many authentication methods.", + filename, linenum); + if (auth2_methods_valid(arg, 0) != 0) + fatal("%s line %d: invalid " + "authentication method list.", + filename, linenum); + options->auth_methods[ + options->num_auth_methods++] = xstrdup(arg); + } + } + return 0; + case sDeprecated: logit("%s line %d: Deprecated option %s", filename, linenum, arg); @@ -1953,6 +1975,8 @@ dump_config(ServerOptions *o) dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups); dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups); dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env); + dump_cfg_strarray_oneline(sAuthenticationMethods, + o->num_auth_methods, o->auth_methods); /* other arguments */ for (i = 0; i < o->num_subsystems; i++) diff --git a/servconf.h b/servconf.h index 0064c9bc5..68fcdb764 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.104 2012/10/30 21:29:55 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.105 2012/11/04 11:09:15 djm Exp $ */ /* * Author: Tatu Ylonen @@ -28,6 +28,7 @@ #define MAX_ACCEPT_ENV 256 /* Max # of env vars. */ #define MAX_MATCH_GROUPS 256 /* Max # of groups for Match. */ #define MAX_AUTHKEYS_FILES 256 /* Max # of authorized_keys files. */ +#define MAX_AUTH_METHODS 256 /* Max # of AuthenticationMethods. */ /* permit_root_login */ #define PERMIT_NOT_SET -1 @@ -170,6 +171,9 @@ typedef struct { char *authorized_keys_command_user; char *version_addendum; /* Appended to SSH banner */ + + u_int num_auth_methods; + char *auth_methods[MAX_AUTH_METHODS]; } ServerOptions; /* Information about the incoming connection as used by Match */ @@ -199,6 +203,7 @@ struct connection_info { M_CP_STRARRAYOPT(allow_groups, num_allow_groups); \ M_CP_STRARRAYOPT(deny_groups, num_deny_groups); \ M_CP_STRARRAYOPT(accept_env, num_accept_env); \ + M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \ } while (0) struct connection_info *get_connection_info(int, int); diff --git a/sshd.c b/sshd.c index 4ad1a4bd1..af7ff91ba 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.395 2012/11/04 10:38:43 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.396 2012/11/04 11:09:15 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1337,6 +1337,7 @@ main(int ac, char **av) int remote_port; char *line; int config_s[2] = { -1 , -1 }; + u_int n; u_int64_t ibytes, obytes; mode_t new_umask; Key *key; @@ -1566,6 +1567,26 @@ main(int ac, char **av) fatal("AuthorizedKeysCommand set without " "AuthorizedKeysCommandUser"); + /* + * Check whether there is any path through configured auth methods. + * Unfortunately it is not possible to verify this generally before + * daemonisation in the presence of Match block, but this catches + * and warns for trivial misconfigurations that could break login. + */ + if (options.num_auth_methods != 0) { + if ((options.protocol & SSH_PROTO_1)) + fatal("AuthenticationMethods is not supported with " + "SSH protocol 1"); + for (n = 0; n < options.num_auth_methods; n++) { + if (auth2_methods_valid(options.auth_methods[n], + 1) == 0) + break; + } + if (n >= options.num_auth_methods) + fatal("AuthenticationMethods cannot be satisfied by " + "enabled authentication methods"); + } + /* set default channel AF */ channel_set_af(options.address_family); diff --git a/sshd_config.5 b/sshd_config.5 index 0fb0b837d..05f3374fb 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -33,7 +33,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. .\" -.\" $OpenBSD: sshd_config.5,v 1.148 2012/11/04 10:38:43 djm Exp $ +.\" $OpenBSD: sshd_config.5,v 1.149 2012/11/04 11:09:15 djm Exp $ .Dd $Mdocdate: November 4 2012 $ .Dt SSHD_CONFIG 5 .Os @@ -151,6 +151,28 @@ See in .Xr ssh_config 5 for more information on patterns. +.It Cm AuthenticationMethods +Specifies the authentication methods that must be successfully completed +for a user to be granted access. +This option must be followed by one or more comma-separated lists of +authentication method names. +Successful authentication requires completion of every method in at least +one of these lists. +.Pp +For example, an argument of +.Dq publickey,password publickey,keyboard-interactive +would require the user to complete public key authentication, followed by +either password or keyboard interactive authentication. +Only methods that are next in one or more lists are offered at each stage, +so for this example, it would not be possible to attempt password or +keyboard-interactive authentication before public key. +.Pp +This option is only available for SSH protocol 2 and will yield a fatal +error if enabled if protocol 1 is also enabled. +Note that each authentication method listed should also be explicitly enabled +in the configuration. +The default is not to require multiple authentication; successful completion +of a single authentication method is sufficient. .It Cm AuthorizedKeysCommand Specifies a program to be used to look up the user's public keys. The program will be invoked with a single argument of the username @@ -728,6 +750,7 @@ Available keywords are .Cm AllowGroups , .Cm AllowTcpForwarding , .Cm AllowUsers , +.Cm AuthenticationMethods , .Cm AuthorizedKeysCommand , .Cm AuthorizedKeysCommandUser , .Cm AuthorizedKeysFile , -- cgit v1.2.3 From aa5b3f831417bac9538d2b6f21d55fef278e8926 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Mon, 3 Dec 2012 09:50:54 +1100 Subject: - djm@cvs.openbsd.org 2012/12/02 20:46:11 [auth-options.c channels.c servconf.c servconf.h serverloop.c session.c] [sshd_config.5] make AllowTcpForwarding accept "local" and "remote" in addition to its current "yes"/"no" to allow the server to specify whether just local or remote TCP forwarding is enabled. ok markus@ --- ChangeLog | 6 ++++++ auth-options.c | 4 ++-- channels.c | 12 +++++------- servconf.c | 18 ++++++++++++++---- servconf.h | 10 ++++++++-- serverloop.c | 19 +++++++++++++------ session.c | 9 ++++++--- sshd_config.5 | 16 ++++++++++++++-- 8 files changed, 68 insertions(+), 26 deletions(-) (limited to 'servconf.c') diff --git a/ChangeLog b/ChangeLog index 7cea00cb5..cee038727 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,12 @@ make deleting explicit keys "ssh-add -d" symmetric with adding keys - try to delete the corresponding certificate too and respect the -k option to allow deleting of the key only; feedback and ok markus@ + - djm@cvs.openbsd.org 2012/12/02 20:46:11 + [auth-options.c channels.c servconf.c servconf.h serverloop.c session.c] + [sshd_config.5] + make AllowTcpForwarding accept "local" and "remote" in addition to its + current "yes"/"no" to allow the server to specify whether just local or + remote TCP forwarding is enabled. ok markus@ 20121114 - (djm) OpenBSD CVS Sync diff --git a/auth-options.c b/auth-options.c index 0e67bd8c0..23d0423e1 100644 --- a/auth-options.c +++ b/auth-options.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.c,v 1.56 2011/10/18 04:58:26 djm Exp $ */ +/* $OpenBSD: auth-options.c,v 1.57 2012/12/02 20:46:11 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -349,7 +349,7 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) xfree(patterns); goto bad_option; } - if (options.allow_tcp_forwarding) + if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) channel_add_permitted_opens(host, port); xfree(patterns); goto next_option; diff --git a/channels.c b/channels.c index 7791febd7..9cf85a38d 100644 --- a/channels.c +++ b/channels.c @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.318 2012/04/23 08:18:17 djm Exp $ */ +/* $OpenBSD: channels.c,v 1.319 2012/12/02 20:46:11 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -3165,12 +3165,10 @@ channel_add_adm_permitted_opens(char *host, int port) void channel_disable_adm_local_opens(void) { - if (num_adm_permitted_opens == 0) { - permitted_adm_opens = xmalloc(sizeof(*permitted_adm_opens)); - permitted_adm_opens[num_adm_permitted_opens].host_to_connect - = NULL; - num_adm_permitted_opens = 1; - } + channel_clear_adm_permitted_opens(); + permitted_adm_opens = xmalloc(sizeof(*permitted_adm_opens)); + permitted_adm_opens[num_adm_permitted_opens].host_to_connect = NULL; + num_adm_permitted_opens = 1; } void diff --git a/servconf.c b/servconf.c index b90dba63b..21b371c84 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.232 2012/11/04 11:09:15 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.233 2012/12/02 20:46:11 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -250,7 +250,7 @@ fill_default_server_options(ServerOptions *options) if (options->compression == -1) options->compression = COMP_DELAYED; if (options->allow_tcp_forwarding == -1) - options->allow_tcp_forwarding = 1; + options->allow_tcp_forwarding = FORWARD_ALLOW; if (options->allow_agent_forwarding == -1) options->allow_agent_forwarding = 1; if (options->gateway_ports == -1) @@ -786,6 +786,14 @@ static const struct multistate multistate_privsep[] = { { "no", PRIVSEP_OFF }, { NULL, -1 } }; +static const struct multistate multistate_tcpfwd[] = { + { "yes", FORWARD_ALLOW }, + { "all", FORWARD_ALLOW }, + { "no", FORWARD_DENY }, + { "remote", FORWARD_REMOTE }, + { "local", FORWARD_LOCAL }, + { NULL, -1 } +}; int process_server_config_line(ServerOptions *options, char *line, @@ -1143,7 +1151,8 @@ process_server_config_line(ServerOptions *options, char *line, case sAllowTcpForwarding: intptr = &options->allow_tcp_forwarding; - goto parse_flag; + multistate_ptr = multistate_tcpfwd; + goto parse_multistate; case sAllowAgentForwarding: intptr = &options->allow_agent_forwarding; @@ -1423,7 +1432,6 @@ process_server_config_line(ServerOptions *options, char *line, } if (strcmp(arg, "none") == 0) { if (*activep && n == -1) { - channel_clear_adm_permitted_opens(); options->num_permitted_opens = 1; channel_disable_adm_local_opens(); } @@ -1780,6 +1788,8 @@ fmt_intarg(ServerOpCodes code, int val) return fmt_multistate_int(val, multistate_compression); case sUsePrivilegeSeparation: return fmt_multistate_int(val, multistate_privsep); + case sAllowTcpForwarding: + return fmt_multistate_int(val, multistate_tcpfwd); case sProtocol: switch (val) { case SSH_PROTO_1: diff --git a/servconf.h b/servconf.h index 68fcdb764..a23ef7f3a 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.105 2012/11/04 11:09:15 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.106 2012/12/02 20:46:11 djm Exp $ */ /* * Author: Tatu Ylonen @@ -42,6 +42,12 @@ #define PRIVSEP_ON 1 #define PRIVSEP_NOSANDBOX 2 +/* AllowTCPForwarding */ +#define FORWARD_DENY 0 +#define FORWARD_REMOTE (1) +#define FORWARD_LOCAL (1<<1) +#define FORWARD_ALLOW (FORWARD_REMOTE|FORWARD_LOCAL) + #define DEFAULT_AUTH_FAIL_MAX 6 /* Default for MaxAuthTries */ #define DEFAULT_SESSIONS_MAX 10 /* Default for MaxSessions */ @@ -116,7 +122,7 @@ typedef struct { int permit_user_env; /* If true, read ~/.ssh/environment */ int use_login; /* If true, login(1) is used */ int compression; /* If true, compression is allowed */ - int allow_tcp_forwarding; + int allow_tcp_forwarding; /* One of FORWARD_* */ int allow_agent_forwarding; u_int num_allow_users; char *allow_users[MAX_ALLOW_USERS]; diff --git a/serverloop.c b/serverloop.c index 741c5befb..14e60c6dc 100644 --- a/serverloop.c +++ b/serverloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: serverloop.c,v 1.162 2012/06/20 04:42:58 djm Exp $ */ +/* $OpenBSD: serverloop.c,v 1.163 2012/12/02 20:46:11 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -950,7 +950,7 @@ server_input_window_size(int type, u_int32_t seq, void *ctxt) static Channel * server_request_direct_tcpip(void) { - Channel *c; + Channel *c = NULL; char *target, *originator; u_short target_port, originator_port; @@ -963,9 +963,16 @@ server_request_direct_tcpip(void) debug("server_request_direct_tcpip: originator %s port %d, target %s " "port %d", originator, originator_port, target, target_port); - /* XXX check permission */ - c = channel_connect_to(target, target_port, - "direct-tcpip", "direct-tcpip"); + /* XXX fine grained permissions */ + if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 && + !no_port_forwarding_flag) { + c = channel_connect_to(target, target_port, + "direct-tcpip", "direct-tcpip"); + } else { + logit("refused local port forward: " + "originator %s port %d, target %s port %d", + originator, originator_port, target, target_port); + } xfree(originator); xfree(target); @@ -1126,7 +1133,7 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt) listen_address, listen_port); /* check permissions */ - if (!options.allow_tcp_forwarding || + if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 || no_port_forwarding_flag || (!want_reply && listen_port == 0) #ifndef NO_IPPORT_RESERVED_CONCEPT diff --git a/session.c b/session.c index 65bf28776..643e7fc59 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.260 2012/03/15 03:10:27 guenther Exp $ */ +/* $OpenBSD: session.c,v 1.261 2012/12/02 20:46:11 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -273,7 +273,10 @@ do_authenticated(Authctxt *authctxt) setproctitle("%s", authctxt->pw->pw_name); /* setup the channel layer */ - if (!no_port_forwarding_flag && options.allow_tcp_forwarding) + if (no_port_forwarding_flag || + (options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) + channel_disable_adm_local_opens(); + else channel_permit_all_opens(); auth_debug_send(); @@ -383,7 +386,7 @@ do_authenticated1(Authctxt *authctxt) debug("Port forwarding not permitted for this authentication."); break; } - if (!options.allow_tcp_forwarding) { + if (!(options.allow_tcp_forwarding & FORWARD_REMOTE)) { debug("Port forwarding not permitted."); break; } diff --git a/sshd_config.5 b/sshd_config.5 index 05f3374fb..d2c4db55b 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.149 2012/11/04 11:09:15 djm Exp $ -.Dd $Mdocdate: November 4 2012 $ +.\" $OpenBSD: sshd_config.5,v 1.150 2012/12/02 20:46:11 djm Exp $ +.Dd $Mdocdate: December 2 2012 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -124,6 +124,18 @@ in for more information on patterns. .It Cm AllowTcpForwarding Specifies whether TCP forwarding is permitted. +The available options are +.Dq yes +or +.Dq all +to allow TCP forwarding, +.Dq no +to prevent all TCP forwarding, +.Dq local +to allow local (from the perspective of +.Xr ssh 1 ) forwarding only or +.Dq remote +to allow remote forwarding only. The default is .Dq yes . Note that disabling TCP forwarding does not improve security unless -- cgit v1.2.3 From 1f583df8c3a7a3731a18b17e14c9ec09f31905ba Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Tue, 12 Feb 2013 11:02:08 +1100 Subject: - dtucker@cvs.openbsd.org 2013/02/06 00:20:42 [servconf.c sshd_config sshd_config.5] Change default of MaxStartups to 10:30:100 to start doing random early drop at 10 connections up to 100 connections. This will make it harder to DoS as CPUs have come a long way since the original value was set back in 2000. Prompted by nion at debian org, ok markus@ --- ChangeLog | 6 ++++++ servconf.c | 8 ++++---- sshd_config | 4 ++-- sshd_config.5 | 6 +++--- 4 files changed, 15 insertions(+), 9 deletions(-) (limited to 'servconf.c') diff --git a/ChangeLog b/ChangeLog index b028fef82..2d5f1d96b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,12 @@ - djm@cvs.openbsd.org 2013/01/27 10:06:12 [krl.c] actually use the xrealloc() return value; spotted by xi.wang AT gmail.com + - dtucker@cvs.openbsd.org 2013/02/06 00:20:42 + [servconf.c sshd_config sshd_config.5] + Change default of MaxStartups to 10:30:100 to start doing random early + drop at 10 connections up to 100 connections. This will make it harder + to DoS as CPUs have come a long way since the original value was set + back in 2000. Prompted by nion at debian org, ok markus@ 20130211 - (djm) [configure.ac openbsd-compat/openssl-compat.h] Repair build on old diff --git a/servconf.c b/servconf.c index 21b371c84..b2a60fd6c 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.233 2012/12/02 20:46:11 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.234 2013/02/06 00:20:42 dtucker Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -256,11 +256,11 @@ fill_default_server_options(ServerOptions *options) if (options->gateway_ports == -1) options->gateway_ports = 0; if (options->max_startups == -1) - options->max_startups = 10; + options->max_startups = 100; if (options->max_startups_rate == -1) - options->max_startups_rate = 100; /* 100% */ + options->max_startups_rate = 30; /* 30% */ if (options->max_startups_begin == -1) - options->max_startups_begin = options->max_startups; + options->max_startups_begin = 10; if (options->max_authtries == -1) options->max_authtries = DEFAULT_AUTH_FAIL_MAX; if (options->max_sessions == -1) diff --git a/sshd_config b/sshd_config index 3d35bef02..9cd2fddce 100644 --- a/sshd_config +++ b/sshd_config @@ -1,4 +1,4 @@ -# $OpenBSD: sshd_config,v 1.88 2012/10/30 21:29:55 djm Exp $ +# $OpenBSD: sshd_config,v 1.89 2013/02/06 00:20:42 dtucker Exp $ # This is the sshd server system-wide configuration file. See # sshd_config(5) for more information. @@ -109,7 +109,7 @@ UsePrivilegeSeparation sandbox # Default for new installations. #ClientAliveCountMax 3 #UseDNS yes #PidFile /var/run/sshd.pid -#MaxStartups 10 +#MaxStartups 10:30:100 #PermitTunnel no #ChrootDirectory none #VersionAddendum none diff --git a/sshd_config.5 b/sshd_config.5 index d2913c33f..cfa480697 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.155 2013/01/18 08:00:49 jmc Exp $ -.Dd $Mdocdate: January 18 2013 $ +.\" $OpenBSD: sshd_config.5,v 1.156 2013/02/06 00:20:42 dtucker Exp $ +.Dd $Mdocdate: February 6 2013 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -820,7 +820,7 @@ SSH daemon. Additional connections will be dropped until authentication succeeds or the .Cm LoginGraceTime expires for a connection. -The default is 10. +The default is 10:30:100. .Pp Alternatively, random early drop can be enabled by specifying the three colon separated values -- cgit v1.2.3