From 7c1b2c4ea8a5d06908dda3f8e406b902b81fe905 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Sun, 15 May 2011 08:51:05 +1000 Subject: - djm@cvs.openbsd.org 2011/05/11 04:47:06 [auth.c auth.h auth2-pubkey.c pathnames.h servconf.c servconf.h] remove support for authorized_keys2; it is a relic from the early days of protocol v.2 support and has been undocumented for many years; ok markus@ --- servconf.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'servconf.h') diff --git a/servconf.h b/servconf.h index 5a058a416..3f04b8501 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.95 2010/11/13 23:27:50 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.96 2011/05/11 04:47:06 djm Exp $ */ /* * Author: Tatu Ylonen @@ -146,7 +146,6 @@ typedef struct { */ char *authorized_keys_file; /* File containing public keys */ - char *authorized_keys_file2; char *adm_forced_command; -- cgit v1.2.3 From f2e407e2dd83addc26b68b4218d8b6ceaceca54b Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 20 May 2011 19:04:14 +1000 Subject: - djm@cvs.openbsd.org 2011/05/20 03:25:45 [monitor.c monitor_wrap.c servconf.c servconf.h] use a macro to define which string options to copy between configs for Match. This avoids problems caused by forgetting to keep three code locations in perfect sync and ordering "this is at once beautiful and horrible" + ok dtucker@ --- ChangeLog | 7 +++++++ monitor.c | 13 ++++++++++--- monitor_wrap.c | 13 ++++++++++--- servconf.c | 9 +++------ servconf.h | 16 +++++++++++++++- 5 files changed, 45 insertions(+), 13 deletions(-) (limited to 'servconf.h') diff --git a/ChangeLog b/ChangeLog index ff81d0522..2cb2dce0c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23,6 +23,13 @@ - dtucker@cvs.openbsd.org 2011/05/20 02:00:19 [servconf.c] Add comment documenting what should be after the preauth check. ok djm + - djm@cvs.openbsd.org 2011/05/20 03:25:45 + [monitor.c monitor_wrap.c servconf.c servconf.h] + use a macro to define which string options to copy between configs + for Match. This avoids problems caused by forgetting to keep three + code locations in perfect sync and ordering + + "this is at once beautiful and horrible" + ok dtucker@ 20110515 - (djm) OpenBSD CVS Sync diff --git a/monitor.c b/monitor.c index 732cb365d..c3a418319 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.111 2011/05/15 08:09:01 djm Exp $ */ +/* $OpenBSD: monitor.c,v 1.112 2011/05/20 03:25:45 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -671,8 +671,15 @@ mm_answer_pwnamallow(int sock, Buffer *m) out: buffer_put_string(m, &options, sizeof(options)); - if (options.banner != NULL) - buffer_put_cstring(m, options.banner); + +#define M_CP_STROPT(x) do { \ + if (options.x != NULL) \ + buffer_put_cstring(m, options.x); \ + } while (0) + /* See comment in servconf.h */ + COPY_MATCH_STRING_OPTS(); +#undef M_CP_STROPT + debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed); mm_request_send(sock, MONITOR_ANS_PWNAM, m); diff --git a/monitor_wrap.c b/monitor_wrap.c index 1a5dda561..d3f274021 100644 --- a/monitor_wrap.c +++ b/monitor_wrap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.c,v 1.70 2010/08/31 11:54:45 djm Exp $ */ +/* $OpenBSD: monitor_wrap.c,v 1.71 2011/05/20 03:25:45 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -245,8 +245,15 @@ out: newopts = buffer_get_string(&m, &len); if (len != sizeof(*newopts)) fatal("%s: option block size mismatch", __func__); - if (newopts->banner != NULL) - newopts->banner = buffer_get_string(&m, NULL); + +#define M_CP_STROPT(x) do { \ + if (newopts->x != NULL) \ + newopts->x = buffer_get_string(&m, NULL); \ + } while (0) + /* See comment in servconf.h */ + COPY_MATCH_STRING_OPTS(); +#undef M_CP_STROPT + copy_set_server_options(&options, newopts, 1); xfree(newopts); diff --git a/servconf.c b/servconf.c index 04a32973e..daed26a66 100644 --- a/servconf.c +++ b/servconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.c,v 1.217 2011/05/20 02:00:19 dtucker Exp $ */ +/* $OpenBSD: servconf.c,v 1.218 2011/05/20 03:25:45 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -1499,11 +1499,8 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) M_CP_INTOPT(ip_qos_interactive); M_CP_INTOPT(ip_qos_bulk); - M_CP_STROPT(banner); - M_CP_STROPT(trusted_user_ca_keys); - M_CP_STROPT(revoked_keys_file); - M_CP_STROPT(authorized_keys_file); - M_CP_STROPT(authorized_principals_file); + /* See comment in servconf.h */ + COPY_MATCH_STRING_OPTS(); /* * The only things that should be below this point are string options diff --git a/servconf.h b/servconf.h index 3f04b8501..953ef8650 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.96 2011/05/11 04:47:06 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.97 2011/05/20 03:25:45 djm Exp $ */ /* * Author: Tatu Ylonen @@ -161,6 +161,20 @@ typedef struct { char *authorized_principals_file; } ServerOptions; +/* + * These are string config options that must be copied between the + * Match sub-config and the main config, and must be sent from the + * privsep slave to the privsep master. We use a macro to ensure all + * the options are copied and the copies are done in the correct order. + */ +#define COPY_MATCH_STRING_OPTS() do { \ + M_CP_STROPT(banner); \ + M_CP_STROPT(trusted_user_ca_keys); \ + M_CP_STROPT(revoked_keys_file); \ + M_CP_STROPT(authorized_keys_file); \ + M_CP_STROPT(authorized_principals_file); \ + } while (0) + void initialize_server_options(ServerOptions *); void fill_default_server_options(ServerOptions *); int process_server_config_line(ServerOptions *, char *, const char *, int, -- cgit v1.2.3 From d8478b6a9b32760d47c2419279c4a73f5f88fdb6 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Sun, 29 May 2011 21:39:36 +1000 Subject: OpenBSD CVS Sync - djm@cvs.openbsd.org 2011/05/23 03:30:07 [auth-rsa.c auth.c auth.h auth2-pubkey.c monitor.c monitor_wrap.c pathnames.h servconf.c servconf.h sshd.8 sshd_config sshd_config.5] allow AuthorizedKeysFile to specify multiple files, separated by spaces. Bring back authorized_keys2 as a default search path (to avoid breaking existing users of this file), but override this in sshd_config so it will be no longer used on fresh installs. Maybe in 2015 we can remove it entierly :) feedback and ok markus@ dtucker@ --- ChangeLog | 13 +++++++++++ auth-rsa.c | 70 ++++++++++++++++++++++++++++++++-------------------------- auth.c | 10 ++------- auth.h | 4 ++-- auth2-pubkey.c | 13 ++++++----- monitor.c | 9 ++++++-- monitor_wrap.c | 9 ++++++-- pathnames.h | 5 ++++- servconf.c | 54 ++++++++++++++++++++++++++++++++++++-------- servconf.h | 8 ++++--- sshd.8 | 12 +++++----- sshd_config | 7 ++++-- sshd_config.5 | 11 +++++---- 13 files changed, 151 insertions(+), 74 deletions(-) (limited to 'servconf.h') diff --git a/ChangeLog b/ChangeLog index 1aac69cbc..4e08289d6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +20110529 + - (djm) OpenBSD CVS Sync + - djm@cvs.openbsd.org 2011/05/23 03:30:07 + [auth-rsa.c auth.c auth.h auth2-pubkey.c monitor.c monitor_wrap.c] + [pathnames.h servconf.c servconf.h sshd.8 sshd_config sshd_config.5] + allow AuthorizedKeysFile to specify multiple files, separated by spaces. + Bring back authorized_keys2 as a default search path (to avoid breaking + existing users of this file), but override this in sshd_config so it will + be no longer used on fresh installs. Maybe in 2015 we can remove it + entierly :) + + feedback and ok markus@ dtucker@ + 20110520 - (djm) [session.c] call setexeccon() before executing passwd for pw changes; bz#1891 reported by jchadima AT redhat.com; ok dtucker@ diff --git a/auth-rsa.c b/auth-rsa.c index 4edaab056..4ab46cd51 100644 --- a/auth-rsa.c +++ b/auth-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-rsa.c,v 1.79 2010/12/03 23:55:27 djm Exp $ */ +/* $OpenBSD: auth-rsa.c,v 1.80 2011/05/23 03:30:07 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -160,44 +160,27 @@ auth_rsa_challenge_dialog(Key *key) return (success); } -/* - * check if there's user key matching client_n, - * return key if login is allowed, NULL otherwise - */ - -int -auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) +static int +rsa_key_allowed_in_file(struct passwd *pw, char *file, + const BIGNUM *client_n, Key **rkey) { - char line[SSH_MAX_PUBKEY_BYTES], *file; + char line[SSH_MAX_PUBKEY_BYTES]; int allowed = 0; u_int bits; FILE *f; u_long linenum = 0; Key *key; - /* Temporarily use the user's uid. */ - temporarily_use_uid(pw); - - /* The authorized keys. */ - file = authorized_keys_file(pw); debug("trying public RSA key file %s", file); - f = auth_openkeyfile(file, pw, options.strict_modes); - if (!f) { - xfree(file); - restore_uid(); - return (0); - } - - /* Flag indicating whether the key is allowed. */ - allowed = 0; - - key = key_new(KEY_RSA1); + if ((f = auth_openkeyfile(file, pw, options.strict_modes)) == NULL) + return 0; /* * Go though the accepted keys, looking for the current key. If * found, perform a challenge-response dialog to verify that the * user really has the corresponding private key. */ + key = key_new(KEY_RSA1); while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { char *cp; char *key_options; @@ -235,7 +218,10 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) } /* cp now points to the comment part. */ - /* Check if the we have found the desired key (identified by its modulus). */ + /* + * Check if the we have found the desired key (identified + * by its modulus). + */ if (BN_cmp(key->rsa->n, client_n) != 0) continue; @@ -264,11 +250,7 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) break; } - /* Restore the privileged uid. */ - restore_uid(); - /* Close the file. */ - xfree(file); fclose(f); /* return key if allowed */ @@ -276,7 +258,33 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) *rkey = key; else key_free(key); - return (allowed); + + return allowed; +} + +/* + * check if there's user key matching client_n, + * return key if login is allowed, NULL otherwise + */ + +int +auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) +{ + char *file; + u_int i, allowed = 0; + + temporarily_use_uid(pw); + + for (i = 0; !allowed && i < options.num_authkeys_files; i++) { + file = expand_authorized_keys( + options.authorized_keys_files[i], pw); + allowed = rsa_key_allowed_in_file(pw, file, client_n, rkey); + xfree(file); + } + + restore_uid(); + + return allowed; } /* diff --git a/auth.c b/auth.c index be78f1a28..cac12b2e7 100644 --- a/auth.c +++ b/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.92 2011/05/11 04:47:06 djm Exp $ */ +/* $OpenBSD: auth.c,v 1.93 2011/05/23 03:30:07 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -331,7 +331,7 @@ auth_root_allowed(char *method) * * This returns a buffer allocated by xmalloc. */ -static char * +char * expand_authorized_keys(const char *filename, struct passwd *pw) { char *file, ret[MAXPATHLEN]; @@ -354,12 +354,6 @@ expand_authorized_keys(const char *filename, struct passwd *pw) return (xstrdup(ret)); } -char * -authorized_keys_file(struct passwd *pw) -{ - return expand_authorized_keys(options.authorized_keys_file, pw); -} - char * authorized_principals_file(struct passwd *pw) { diff --git a/auth.h b/auth.h index 227395863..0d786c4d5 100644 --- a/auth.h +++ b/auth.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.h,v 1.68 2011/05/11 04:47:06 djm Exp $ */ +/* $OpenBSD: auth.h,v 1.69 2011/05/23 03:30:07 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -168,7 +168,7 @@ char *get_challenge(Authctxt *); int verify_response(Authctxt *, const char *); void abandon_challenge_response(Authctxt *); -char *authorized_keys_file(struct passwd *); +char *expand_authorized_keys(const char *, struct passwd *pw); char *authorized_principals_file(struct passwd *); FILE *auth_openkeyfile(const char *, struct passwd *, int); diff --git a/auth2-pubkey.c b/auth2-pubkey.c index a97509c28..137887ecd 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.28 2011/05/11 04:47:06 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.29 2011/05/23 03:30:07 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -436,7 +436,7 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) int user_key_allowed(struct passwd *pw, Key *key) { - int success; + u_int success, i; char *file; if (auth_key_is_revoked(key)) @@ -448,9 +448,12 @@ user_key_allowed(struct passwd *pw, Key *key) if (success) return success; - file = authorized_keys_file(pw); - success = user_key_allowed2(pw, key, file); - xfree(file); + for (i = 0; !success && i < options.num_authkeys_files; i++) { + file = expand_authorized_keys( + options.authorized_keys_files[i], pw); + success = user_key_allowed2(pw, key, file); + xfree(file); + } return success; } diff --git a/monitor.c b/monitor.c index c3a418319..4479e0a03 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.112 2011/05/20 03:25:45 djm Exp $ */ +/* $OpenBSD: monitor.c,v 1.113 2011/05/23 03:30:07 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -632,6 +632,7 @@ mm_answer_pwnamallow(int sock, Buffer *m) char *username; struct passwd *pwent; int allowed = 0; + u_int i; debug3("%s", __func__); @@ -676,9 +677,14 @@ mm_answer_pwnamallow(int sock, Buffer *m) if (options.x != NULL) \ buffer_put_cstring(m, options.x); \ } while (0) +#define M_CP_STRARRAYOPT(x, nx) do { \ + for (i = 0; i < options.nx; i++) \ + buffer_put_cstring(m, options.x[i]); \ + } while (0) /* See comment in servconf.h */ COPY_MATCH_STRING_OPTS(); #undef M_CP_STROPT +#undef M_CP_STRARRAYOPT debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed); mm_request_send(sock, MONITOR_ANS_PWNAM, m); @@ -691,7 +697,6 @@ mm_answer_pwnamallow(int sock, Buffer *m) monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1); monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1); } - #ifdef USE_PAM if (options.use_pam) monitor_permit(mon_dispatch, MONITOR_REQ_PAM_START, 1); diff --git a/monitor_wrap.c b/monitor_wrap.c index d3f274021..7a90b3ba3 100644 --- a/monitor_wrap.c +++ b/monitor_wrap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.c,v 1.71 2011/05/20 03:25:45 djm Exp $ */ +/* $OpenBSD: monitor_wrap.c,v 1.72 2011/05/23 03:30:07 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -211,7 +211,7 @@ mm_getpwnamallow(const char *username) { Buffer m; struct passwd *pw; - u_int len; + u_int len, i; ServerOptions *newopts; debug3("%s entering", __func__); @@ -250,9 +250,14 @@ out: if (newopts->x != NULL) \ newopts->x = buffer_get_string(&m, NULL); \ } while (0) +#define M_CP_STRARRAYOPT(x, nx) do { \ + for (i = 0; i < newopts->nx; i++) \ + newopts->x[i] = buffer_get_string(&m, NULL); \ + } while (0) /* See comment in servconf.h */ COPY_MATCH_STRING_OPTS(); #undef M_CP_STROPT +#undef M_CP_STRARRAYOPT copy_set_server_options(&options, newopts, 1); xfree(newopts); diff --git a/pathnames.h b/pathnames.h index 787bdb676..c3d9abff5 100644 --- a/pathnames.h +++ b/pathnames.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pathnames.h,v 1.21 2011/05/11 04:47:06 djm Exp $ */ +/* $OpenBSD: pathnames.h,v 1.22 2011/05/23 03:30:07 djm Exp $ */ /* * Author: Tatu Ylonen @@ -96,6 +96,9 @@ */ #define _PATH_SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" +/* backward compat for protocol v2 */ +#define _PATH_SSH_USER_PERMITTED_KEYS2 ".ssh/authorized_keys2" + /* * Per-user and system-wide ssh "rc" files. These files are executed with * /bin/sh before starting the shell or command if they exist. They will be diff --git a/servconf.c b/servconf.c index daed26a66..74710c41f 100644 --- a/servconf.c +++ b/servconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.c,v 1.218 2011/05/20 03:25:45 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.219 2011/05/23 03:30:07 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -126,7 +126,7 @@ initialize_server_options(ServerOptions *options) options->use_dns = -1; options->client_alive_interval = -1; options->client_alive_count_max = -1; - options->authorized_keys_file = NULL; + options->num_authkeys_files = 0; options->num_accept_env = 0; options->permit_tun = -1; options->num_permitted_opens = -1; @@ -263,8 +263,12 @@ fill_default_server_options(ServerOptions *options) options->client_alive_interval = 0; if (options->client_alive_count_max == -1) options->client_alive_count_max = 3; - if (options->authorized_keys_file == NULL) - options->authorized_keys_file = xstrdup(_PATH_SSH_USER_PERMITTED_KEYS); + if (options->num_authkeys_files == 0) { + options->authorized_keys_files[options->num_authkeys_files++] = + xstrdup(_PATH_SSH_USER_PERMITTED_KEYS); + options->authorized_keys_files[options->num_authkeys_files++] = + xstrdup(_PATH_SSH_USER_PERMITTED_KEYS2); + } if (options->permit_tun == -1) options->permit_tun = SSH_TUNMODE_NO; if (options->zero_knowledge_password_authentication == -1) @@ -430,6 +434,7 @@ static struct { { "clientaliveinterval", sClientAliveInterval, SSHCFG_GLOBAL }, { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL }, { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL }, + { "authorizedkeysfile2", sDeprecated, SSHCFG_ALL }, { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL}, { "acceptenv", sAcceptEnv, SSHCFG_GLOBAL }, { "permittunnel", sPermitTunnel, SSHCFG_ALL }, @@ -1241,11 +1246,22 @@ process_server_config_line(ServerOptions *options, char *line, * AuthorizedKeysFile /etc/ssh_keys/%u */ case sAuthorizedKeysFile: - charptr = &options->authorized_keys_file; - goto parse_tilde_filename; + if (*activep && options->num_authkeys_files == 0) { + while ((arg = strdelim(&cp)) && *arg != '\0') { + if (options->num_authkeys_files >= + MAX_AUTHKEYS_FILES) + fatal("%s line %d: " + "too many authorized keys files.", + filename, linenum); + options->authorized_keys_files[ + options->num_authkeys_files++] = + tilde_expand_filename(arg, getuid()); + } + } + return 0; + case sAuthorizedPrincipalsFile: charptr = &options->authorized_principals_file; - parse_tilde_filename: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing file name.", @@ -1464,6 +1480,12 @@ parse_server_match_config(ServerOptions *options, const char *user, dst->n = src->n; \ } \ } while(0) +#define M_CP_STRARRAYOPT(n, num_n) do {\ + if (src->num_n != 0) { \ + for (dst->num_n = 0; dst->num_n < src->num_n; dst->num_n++) \ + dst->n[dst->num_n] = xstrdup(src->n[dst->num_n]); \ + } \ +} while(0) /* * Copy any supported values that are set. @@ -1508,12 +1530,14 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) */ if (preauth) return; + M_CP_STROPT(adm_forced_command); M_CP_STROPT(chroot_directory); } #undef M_CP_INTOPT #undef M_CP_STROPT +#undef M_CP_STRARRAYOPT void parse_server_config(ServerOptions *options, const char *filename, Buffer *conf, @@ -1627,7 +1651,18 @@ dump_cfg_strarray(ServerOpCodes code, u_int count, char **vals) u_int i; for (i = 0; i < count; i++) - printf("%s %s\n", lookup_opcode_name(code), vals[i]); + printf("%s %s\n", lookup_opcode_name(code), vals[i]); +} + +static void +dump_cfg_strarray_oneline(ServerOpCodes code, u_int count, char **vals) +{ + u_int i; + + printf("%s", lookup_opcode_name(code)); + for (i = 0; i < count; i++) + printf(" %s", vals[i]); + printf("\n"); } void @@ -1725,7 +1760,6 @@ dump_config(ServerOptions *o) dump_cfg_string(sCiphers, o->ciphers); dump_cfg_string(sMacs, o->macs); dump_cfg_string(sBanner, o->banner); - dump_cfg_string(sAuthorizedKeysFile, o->authorized_keys_file); dump_cfg_string(sForceCommand, o->adm_forced_command); dump_cfg_string(sChrootDirectory, o->chroot_directory); dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); @@ -1738,6 +1772,8 @@ dump_config(ServerOptions *o) dump_cfg_string(sLogFacility, log_facility_name(o->log_facility)); /* string array arguments */ + dump_cfg_strarray_oneline(sAuthorizedKeysFile, o->num_authkeys_files, + o->authorized_keys_files); dump_cfg_strarray(sHostKeyFile, o->num_host_key_files, o->host_key_files); dump_cfg_strarray(sHostKeyFile, o->num_host_cert_files, diff --git a/servconf.h b/servconf.h index 953ef8650..31e621bde 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.97 2011/05/20 03:25:45 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.98 2011/05/23 03:30:07 djm Exp $ */ /* * Author: Tatu Ylonen @@ -27,6 +27,7 @@ #define MAX_HOSTCERTS 256 /* Max # host certificates. */ #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. */ /* permit_root_login */ #define PERMIT_NOT_SET -1 @@ -145,7 +146,8 @@ typedef struct { * disconnect the session */ - char *authorized_keys_file; /* File containing public keys */ + u_int num_authkeys_files; /* Files containing public keys */ + char *authorized_keys_files[MAX_AUTHKEYS_FILES]; char *adm_forced_command; @@ -171,8 +173,8 @@ typedef struct { M_CP_STROPT(banner); \ M_CP_STROPT(trusted_user_ca_keys); \ M_CP_STROPT(revoked_keys_file); \ - M_CP_STROPT(authorized_keys_file); \ M_CP_STROPT(authorized_principals_file); \ + M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files); \ } while (0) void initialize_server_options(ServerOptions *); diff --git a/sshd.8 b/sshd.8 index 5503b1331..8e007d100 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.260 2010/10/28 18:33:28 jmc Exp $ -.Dd $Mdocdate: October 28 2010 $ +.\" $OpenBSD: sshd.8,v 1.261 2011/05/23 03:30:07 djm Exp $ +.Dd $Mdocdate: May 23 2011 $ .Dt SSHD 8 .Os .Sh NAME @@ -462,10 +462,12 @@ is run, and if that does not exist either, xauth is used to add the cookie. .Sh AUTHORIZED_KEYS FILE FORMAT .Cm AuthorizedKeysFile -specifies the file containing public keys for +specifies the file or files containing public keys for public key authentication; -if none is specified, the default is -.Pa ~/.ssh/authorized_keys . +if none is specified, the default is both +.Pa ~/.ssh/authorized_keys +and +.Pa ~/.ssh/authorized_keys2 . Each line of the file contains one key (empty lines and lines starting with a .Ql # diff --git a/sshd_config b/sshd_config index 9b0d9fa20..473e86654 100644 --- a/sshd_config +++ b/sshd_config @@ -1,4 +1,4 @@ -# $OpenBSD: sshd_config,v 1.83 2011/05/06 01:03:35 dtucker Exp $ +# $OpenBSD: sshd_config,v 1.84 2011/05/23 03:30:07 djm Exp $ # This is the sshd server system-wide configuration file. See # sshd_config(5) for more information. @@ -44,7 +44,10 @@ #RSAAuthentication yes #PubkeyAuthentication yes -#AuthorizedKeysFile .ssh/authorized_keys + +# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 +# but this is overridden so installations will only check .ssh/authorized_keys +AuthorizedKeysFile .ssh/authorized_keys # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts #RhostsRSAAuthentication no diff --git a/sshd_config.5 b/sshd_config.5 index c3d6df30a..b23e0f789 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.131 2010/12/08 04:02:47 djm Exp $ -.Dd $Mdocdate: December 8 2010 $ +.\" $OpenBSD: sshd_config.5,v 1.132 2011/05/23 03:30:07 djm Exp $ +.Dd $Mdocdate: May 23 2011 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -168,8 +168,11 @@ After expansion, .Cm AuthorizedKeysFile is taken to be an absolute path or one relative to the user's home directory. -The default is -.Dq .ssh/authorized_keys . +The default is both +.Dq .ssh/authorized_keys +and +.Dq .ssh/authorized_keys2 . +Multiple files may be listed separated by whitespace. .It Cm AuthorizedPrincipalsFile Specifies a file that lists principal names that are accepted for certificate authentication. -- cgit v1.2.3 From 69ff1df952eebf0489b775a60ede094eaf596a05 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Thu, 23 Jun 2011 08:30:03 +1000 Subject: - djm@cvs.openbsd.org 2011/06/22 21:57:01 [servconf.c servconf.h sshd.c sshd_config.5 sandbox-rlimit.c] [sandbox-systrace.c sandbox.h configure.ac Makefile.in] introduce sandboxing of the pre-auth privsep child using systrace(4). This introduces a new "UsePrivilegeSeparation=sandbox" option for sshd_config that applies mandatory restrictions on the syscalls the privsep child can perform. This prevents a compromised privsep child from being used to attack other hosts (by opening sockets and proxying) or probing local kernel attack surface. The sandbox is implemented using systrace(4) in unsupervised "fast-path" mode, where a list of permitted syscalls is supplied. Any syscall not on the list results in SIGKILL being sent to the privsep child. Note that this requires a kernel with the new SYSTR_POLICY_KILL option. UsePrivilegeSeparation=sandbox will become the default in the future so please start testing it now. feedback dtucker@; ok markus@ --- ChangeLog | 20 ++++++ Makefile.in | 5 +- configure.ac | 43 +++++++++++- sandbox-rlimit.c | 92 ++++++++++++++++++++++++++ sandbox-systrace.c | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++++ sandbox.h | 23 +++++++ servconf.c | 15 ++++- servconf.h | 7 +- sshd.c | 30 +++++++-- sshd_config.5 | 10 ++- 10 files changed, 417 insertions(+), 15 deletions(-) create mode 100644 sandbox-rlimit.c create mode 100644 sandbox-systrace.c create mode 100755 sandbox.h (limited to 'servconf.h') diff --git a/ChangeLog b/ChangeLog index b8adb8a87..6a18e7193 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,26 @@ - djm@cvs.openbsd.org 2011/06/22 21:47:28 [servconf.c] reuse the multistate option arrays to pretty-print options for "sshd -T" + - djm@cvs.openbsd.org 2011/06/22 21:57:01 + [servconf.c servconf.h sshd.c sshd_config.5] + [configure.ac Makefile.in] + introduce sandboxing of the pre-auth privsep child using systrace(4). + + This introduces a new "UsePrivilegeSeparation=sandbox" option for + sshd_config that applies mandatory restrictions on the syscalls the + privsep child can perform. This prevents a compromised privsep child + from being used to attack other hosts (by opening sockets and proxying) + or probing local kernel attack surface. + + The sandbox is implemented using systrace(4) in unsupervised "fast-path" + mode, where a list of permitted syscalls is supplied. Any syscall not + on the list results in SIGKILL being sent to the privsep child. Note + that this requires a kernel with the new SYSTR_POLICY_KILL option. + + UsePrivilegeSeparation=sandbox will become the default in the future + so please start testing it now. + + feedback dtucker@; ok markus@ 20110620 - OpenBSD CVS Sync diff --git a/Makefile.in b/Makefile.in index f5b147619..f64aaac94 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,4 +1,4 @@ -# $Id: Makefile.in,v 1.322 2011/05/05 03:48:37 djm Exp $ +# $Id: Makefile.in,v 1.323 2011/06/22 22:30:03 djm Exp $ # uncomment if you run a non bourne compatable shell. Ie. csh #SHELL = @SH@ @@ -89,7 +89,8 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ auth2-gss.o gss-serv.o gss-serv-krb5.o \ loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ sftp-server.o sftp-common.o \ - roaming_common.o roaming_serv.o + roaming_common.o roaming_serv.o \ + sandbox-null.o sandbox-rlimit.o sandbox-systrace.o MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5 diff --git a/configure.ac b/configure.ac index 8f36338ff..380a8b949 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -# $Id: configure.ac,v 1.476 2011/06/03 02:11:38 djm Exp $ +# $Id: configure.ac,v 1.477 2011/06/22 22:30:03 djm Exp $ # # Copyright (c) 1999-2004 Damien Miller # @@ -15,7 +15,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_INIT([OpenSSH], [Portable], [openssh-unix-dev@mindrot.org]) -AC_REVISION($Revision: 1.476 $) +AC_REVISION($Revision: 1.477 $) AC_CONFIG_SRCDIR([ssh.c]) AC_LANG([C]) @@ -106,6 +106,16 @@ AC_SUBST([LD]) AC_C_INLINE AC_CHECK_DECL([LLONG_MAX], [have_llong_max=1], , [#include ]) +AC_CHECK_DECL([SYSTR_POLICY_KILL], [have_systr_policy_kill=1], , [ + #include + #include + #include +]) +AC_CHECK_DECL([RLIMIT_NPROC], + [AC_DEFINE([HAVE_RLIMIT_NPROC], [], [sys/resource.h has RLIMIT_NPROC])], , [ + #include + #include +]) use_stack_protector=1 AC_ARG_WITH([stackprotect], @@ -2461,6 +2471,34 @@ AC_DEFINE_UNQUOTED([SSH_PRIVSEP_USER], ["$SSH_PRIVSEP_USER"], [non-privileged user for privilege separation]) AC_SUBST([SSH_PRIVSEP_USER]) +# Decide which sandbox style to use +sandbox_arg="" +AC_ARG_WITH([sandbox], + [ --with-sandbox=style Specify privilege separation sandbox (no, rlimit, systrace)], + [ + if test "x$withval" = "xyes" ; then + sandbox_arg="" + else + sandbox_arg="$withval" + fi + ] +) +if test "x$sandbox_arg" = "xsystrace" || \ + ( test -z "$sandbox_arg" && test "x$have_systr_policy_kill" = "x1" ) ; then + SANDBOX_STYLE="systrace" + AC_DEFINE([SANDBOX_SYSTRACE], [1], [Sandbox using systrace(4)]) +elif test "x$sandbox_arg" = "xrlimit" || \ + ( test -z "$sandbox_arg" && test "x$ac_cv_func_setrlimit" = "xyes" ) ; then + SANDBOX_STYLE="rlimit" + AC_DEFINE([SANDBOX_RLIMIT], [1], [Sandbox using setrlimit(2)]) +elif test -z "$sandbox_arg" || test "x$sandbox_arg" = "xno" || \ + test "x$sandbox_arg" = "xnone" || test "x$sandbox_arg" = "xnull" ; then + SANDBOX_STYLE="none" + AC_DEFINE([SANDBOX_NULL], [1], [no privsep sandboxing]) +else + AC_MSG_ERROR([unsupported -with-sandbox]) +fi + # Cheap hack to ensure NEWS-OS libraries are arranged right. if test ! -z "$SONY" ; then LIBS="$LIBS -liberty"; @@ -4191,6 +4229,7 @@ echo " IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG" echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" echo " BSD Auth support: $BSD_AUTH_MSG" echo " Random number source: $RAND_MSG" +echo " Privsep sandbox style: $SANDBOX_STYLE" echo "" diff --git a/sandbox-rlimit.c b/sandbox-rlimit.c new file mode 100644 index 000000000..4d832fc3d --- /dev/null +++ b/sandbox-rlimit.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef SANDBOX_RLIMIT + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "sandbox.h" +#include "xmalloc.h" + +/* Minimal sandbox that sets zero nfiles, nprocs and filesize rlimits */ + +struct ssh_sandbox { + pid_t child_pid; +}; + +struct ssh_sandbox * +ssh_sandbox_init(void) +{ + struct ssh_sandbox *box; + + /* + * Strictly, we don't need to maintain any state here but we need + * to return non-NULL to satisfy the API. + */ + debug3("%s: preparing rlimit sandbox", __func__); + box = xcalloc(1, sizeof(*box)); + box->child_pid = 0; + + return box; +} + +void +ssh_sandbox_child(struct ssh_sandbox *box) +{ + struct rlimit rl_zero; + + rl_zero.rlim_cur = rl_zero.rlim_max = 0; + + if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s", + __func__, strerror(errno)); + if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s", + __func__, strerror(errno)); +#ifdef HAVE_RLIMIT_NPROC + if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s", + __func__, strerror(errno)); +#endif +} + +void +ssh_sandbox_parent_finish(struct ssh_sandbox *box) +{ + free(box); + debug3("%s: finished", __func__); +} + +void +ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) +{ + box->child_pid = child_pid; +} + +#endif /* SANDBOX_RLIMIT */ diff --git a/sandbox-systrace.c b/sandbox-systrace.c new file mode 100644 index 000000000..5d0b7fb86 --- /dev/null +++ b/sandbox-systrace.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef SANDBOX_SYSTRACE + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atomicio.h" +#include "log.h" +#include "sandbox.h" +#include "xmalloc.h" + +static const int preauth_policy[] = { + SYS___sysctl, + SYS_close, + SYS_exit, + SYS_getpid, + SYS_gettimeofday, + SYS_madvise, + SYS_mmap, + SYS_mprotect, + SYS_poll, + SYS_munmap, + SYS_read, + SYS_select, + SYS_sigprocmask, + SYS_write, + -1 +}; + +struct ssh_sandbox { + int child_sock; + int parent_sock; + int systrace_fd; + pid_t child_pid; + struct systrace_policy policy; +}; + +struct ssh_sandbox * +ssh_sandbox_init(void) +{ + struct ssh_sandbox *box; + int s[2]; + + debug3("%s: preparing systrace sandbox", __func__); + box = xcalloc(1, sizeof(*box)); + if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1) + fatal("%s: socketpair: %s", __func__, strerror(errno)); + box->child_sock = s[0]; + box->parent_sock = s[1]; + box->systrace_fd = -1; + box->child_pid = 0; + + return box; +} + +void +ssh_sandbox_child(struct ssh_sandbox *box) +{ + char whatever = 0; + + close(box->parent_sock); + /* Signal parent that we are ready */ + debug3("%s: ready", __func__); + if (atomicio(vwrite, box->child_sock, &whatever, 1) != 1) + fatal("%s: write: %s", __func__, strerror(errno)); + /* Wait for parent to signal for us to go */ + if (atomicio(read, box->child_sock, &whatever, 1) != 1) + fatal("%s: read: %s", __func__, strerror(errno)); + debug3("%s: started", __func__); + close(box->child_sock); +} + +static void +ssh_sandbox_parent(struct ssh_sandbox *box, pid_t child_pid, + const int *allowed_syscalls) +{ + int dev_systrace, i, j, found; + char whatever = 0; + + debug3("%s: wait for child %ld", __func__, (long)child_pid); + box->child_pid = child_pid; + close(box->child_sock); + /* Wait for child to signal that it is ready */ + if (atomicio(read, box->parent_sock, &whatever, 1) != 1) + fatal("%s: read: %s", __func__, strerror(errno)); + debug3("%s: child %ld ready", __func__, (long)child_pid); + + /* Set up systracing of child */ + if ((dev_systrace = open("/dev/systrace", O_RDONLY)) == -1) + fatal("%s: open(\"/dev/systrace\"): %s", __func__, + strerror(errno)); + if (ioctl(dev_systrace, STRIOCCLONE, &box->systrace_fd) == -1) + fatal("%s: ioctl(STRIOCCLONE, %d): %s", __func__, + dev_systrace, strerror(errno)); + close(dev_systrace); + debug3("%s: systrace attach, fd=%d", __func__, box->systrace_fd); + if (ioctl(box->systrace_fd, STRIOCATTACH, &child_pid) == -1) + fatal("%s: ioctl(%d, STRIOCATTACH, %d): %s", __func__, + box->systrace_fd, child_pid, strerror(errno)); + + /* Allocate and assign policy */ + bzero(&box->policy, sizeof(box->policy)); + box->policy.strp_op = SYSTR_POLICY_NEW; + box->policy.strp_maxents = SYS_MAXSYSCALL; + if (ioctl(box->systrace_fd, STRIOCPOLICY, &box->policy) == -1) + fatal("%s: ioctl(%d, STRIOCPOLICY (new)): %s", __func__, + box->systrace_fd, strerror(errno)); + + box->policy.strp_op = SYSTR_POLICY_ASSIGN; + box->policy.strp_pid = box->child_pid; + if (ioctl(box->systrace_fd, STRIOCPOLICY, &box->policy) == -1) + fatal("%s: ioctl(%d, STRIOCPOLICY (assign)): %s", + __func__, box->systrace_fd, strerror(errno)); + + /* Set per-syscall policy */ + for (i = 0; i < SYS_MAXSYSCALL; i++) { + for (j = found = 0; allowed_syscalls[j] != -1 && !found; j++) { + if (allowed_syscalls[j] == i) + found = 1; + } + box->policy.strp_op = SYSTR_POLICY_MODIFY; + box->policy.strp_code = i; + box->policy.strp_policy = found ? + SYSTR_POLICY_PERMIT : SYSTR_POLICY_KILL; + if (found) + debug3("%s: policy: enable syscall %d", __func__, i); + if (ioctl(box->systrace_fd, STRIOCPOLICY, + &box->policy) == -1) + fatal("%s: ioctl(%d, STRIOCPOLICY (modify)): %s", + __func__, box->systrace_fd, strerror(errno)); + } + + /* Signal the child to start running */ + debug3("%s: start child %ld", __func__, (long)child_pid); + if (atomicio(vwrite, box->parent_sock, &whatever, 1) != 1) + fatal("%s: write: %s", __func__, strerror(errno)); + close(box->parent_sock); +} + +void +ssh_sandbox_parent_finish(struct ssh_sandbox *box) +{ + /* Closing this before the child exits will terminate it */ + close(box->systrace_fd); + + free(box); + debug3("%s: finished", __func__); +} + +void +ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) +{ + ssh_sandbox_parent(box, child_pid, preauth_policy); +} + +#endif /* SANDBOX_SYSTRACE */ diff --git a/sandbox.h b/sandbox.h new file mode 100755 index 000000000..5fe30644d --- /dev/null +++ b/sandbox.h @@ -0,0 +1,23 @@ +/* $OpenBSD: sandbox.h,v 1.2 2011/06/22 22:14:05 djm Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct ssh_sandbox; + +struct ssh_sandbox *ssh_sandbox_init(void); +void ssh_sandbox_child(struct ssh_sandbox *); +void ssh_sandbox_parent_finish(struct ssh_sandbox *); +void ssh_sandbox_parent_preauth(struct ssh_sandbox *, pid_t); diff --git a/servconf.c b/servconf.c index 03b974617..91986e55d 100644 --- a/servconf.c +++ b/servconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.c,v 1.221 2011/06/22 21:47:28 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.222 2011/06/22 21:57:01 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -280,7 +280,7 @@ fill_default_server_options(ServerOptions *options) /* Turn privilege separation on by default */ if (use_privsep == -1) - use_privsep = 1; + use_privsep = PRIVSEP_ON; #ifndef HAVE_MMAP if (use_privsep && options->compression == 1) { @@ -701,6 +701,12 @@ static const struct multistate multistate_gatewayports[] = { { "no", 0 }, { NULL, -1 } }; +static const struct multistate multistate_privsep[] = { + { "sandbox", PRIVSEP_SANDBOX }, + { "yes", PRIVSEP_ON }, + { "no", PRIVSEP_OFF }, + { NULL, -1 } +}; int process_server_config_line(ServerOptions *options, char *line, @@ -1066,7 +1072,8 @@ process_server_config_line(ServerOptions *options, char *line, case sUsePrivilegeSeparation: intptr = &use_privsep; - goto parse_flag; + multistate_ptr = multistate_privsep; + goto parse_multistate; case sAllowUsers: while ((arg = strdelim(&cp)) && *arg != '\0') { @@ -1574,6 +1581,8 @@ fmt_intarg(ServerOpCodes code, int val) return fmt_multistate_int(val, multistate_gatewayports); case sCompression: return fmt_multistate_int(val, multistate_compression); + case sUsePrivilegeSeparation: + return fmt_multistate_int(val, multistate_privsep); case sProtocol: switch (val) { case SSH_PROTO_1: diff --git a/servconf.h b/servconf.h index 31e621bde..89f38e20f 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.98 2011/05/23 03:30:07 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.99 2011/06/22 21:57:01 djm Exp $ */ /* * Author: Tatu Ylonen @@ -36,6 +36,11 @@ #define PERMIT_NO_PASSWD 2 #define PERMIT_YES 3 +/* use_privsep */ +#define PRIVSEP_OFF 0 +#define PRIVSEP_ON 1 +#define PRIVSEP_SANDBOX 2 + #define DEFAULT_AUTH_FAIL_MAX 6 /* Default for MaxAuthTries */ #define DEFAULT_SESSIONS_MAX 10 /* Default for MaxSessions */ diff --git a/sshd.c b/sshd.c index 6e15522b3..bebcb9bf5 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.383 2011/06/17 21:44:31 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.384 2011/06/22 21:57:01 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -118,6 +118,7 @@ #endif #include "monitor_wrap.h" #include "roaming.h" +#include "sandbox.h" #include "version.h" #ifdef LIBWRAP @@ -624,18 +625,23 @@ privsep_preauth(Authctxt *authctxt) { int status; pid_t pid; + struct ssh_sandbox *box = NULL; /* Set up unprivileged child process to deal with network data */ pmonitor = monitor_init(); /* Store a pointer to the kex for later rekeying */ pmonitor->m_pkex = &xxx_kex; + if (use_privsep == PRIVSEP_SANDBOX) + box = ssh_sandbox_init(); pid = fork(); if (pid == -1) { fatal("fork of unprivileged child failed"); } else if (pid != 0) { debug2("Network child is on pid %ld", (long)pid); + if (box != NULL) + ssh_sandbox_parent_preauth(box, pid); pmonitor->m_pid = pid; monitor_child_preauth(authctxt, pmonitor); @@ -643,10 +649,21 @@ privsep_preauth(Authctxt *authctxt) monitor_sync(pmonitor); /* Wait for the child's exit status */ - while (waitpid(pid, &status, 0) < 0) + while (waitpid(pid, &status, 0) < 0) { if (errno != EINTR) - break; - return (1); + fatal("%s: waitpid: %s", __func__, + strerror(errno)); + } + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) + fatal("%s: preauth child exited with status %d", + __func__, WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) + fatal("%s: preauth child terminated by signal %d", + __func__, WTERMSIG(status)); + if (box != NULL) + ssh_sandbox_parent_finish(box); + return 1; } else { /* child */ close(pmonitor->m_sendfd); @@ -659,8 +676,11 @@ privsep_preauth(Authctxt *authctxt) if (getuid() == 0 || geteuid() == 0) privsep_preauth_child(); setproctitle("%s", "[net]"); + if (box != NULL) + ssh_sandbox_child(box); + + return 0; } - return (0); } static void diff --git a/sshd_config.5 b/sshd_config.5 index 70a53b3a9..f78452c85 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.133 2011/05/23 07:10:21 jmc Exp $ -.Dd $Mdocdate: May 23 2011 $ +.\" $OpenBSD: sshd_config.5,v 1.134 2011/06/22 21:57:01 djm Exp $ +.Dd $Mdocdate: June 22 2011 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -1071,6 +1071,12 @@ The goal of privilege separation is to prevent privilege escalation by containing any corruption within the unprivileged processes. The default is .Dq yes . +If +.Cm UsePrivilegeSeparation +is set to +.Dq sandbox +then the pre-authentication unprivileged process is subject to additional +restrictions. .It Cm X11DisplayOffset Specifies the first display number available for .Xr sshd 8 Ns 's -- cgit v1.2.3