summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2014-02-09 16:09:58 +0000
committerColin Watson <cjwatson@debian.org>2020-10-18 12:07:21 +0100
commitd08cd2b0cfbedf3ccd2ec3adaef850b8d9a87e85 (patch)
tree482ed3db805204bb6bc2e7d528d1b5669f6abc72
parent33a5f7aadea15899586710c615408045eaaecebd (diff)
Allow harmless group-writability
Allow secure files (~/.ssh/config, ~/.ssh/authorized_keys, etc.) to be group-writable, provided that the group in question contains only the file's owner. Rejected upstream for IMO incorrect reasons (e.g. a misunderstanding about the contents of gr->gr_mem). Given that per-user groups and umask 002 are the default setup in Debian (for good reasons - this makes operating in setgid directories with other groups much easier), we need to permit this by default. Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1060 Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=314347 Last-Update: 2019-10-09 Patch-Name: user-group-modes.patch
-rw-r--r--auth-rhosts.c6
-rw-r--r--auth.c3
-rw-r--r--misc.c58
-rw-r--r--misc.h2
-rw-r--r--readconf.c3
-rw-r--r--ssh.12
-rw-r--r--ssh_config.52
7 files changed, 63 insertions, 13 deletions
diff --git a/auth-rhosts.c b/auth-rhosts.c
index e81321b49..3bcc73766 100644
--- a/auth-rhosts.c
+++ b/auth-rhosts.c
@@ -260,8 +260,7 @@ auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname,
260 return 0; 260 return 0;
261 } 261 }
262 if (options.strict_modes && 262 if (options.strict_modes &&
263 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 263 !secure_permissions(&st, pw->pw_uid)) {
264 (st.st_mode & 022) != 0)) {
265 logit("Rhosts authentication refused for %.100s: " 264 logit("Rhosts authentication refused for %.100s: "
266 "bad ownership or modes for home directory.", pw->pw_name); 265 "bad ownership or modes for home directory.", pw->pw_name);
267 auth_debug_add("Rhosts authentication refused for %.100s: " 266 auth_debug_add("Rhosts authentication refused for %.100s: "
@@ -287,8 +286,7 @@ auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname,
287 * allowing access to their account by anyone. 286 * allowing access to their account by anyone.
288 */ 287 */
289 if (options.strict_modes && 288 if (options.strict_modes &&
290 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 289 !secure_permissions(&st, pw->pw_uid)) {
291 (st.st_mode & 022) != 0)) {
292 logit("Rhosts authentication refused for %.100s: bad modes for %.200s", 290 logit("Rhosts authentication refused for %.100s: bad modes for %.200s",
293 pw->pw_name, buf); 291 pw->pw_name, buf);
294 auth_debug_add("Bad file modes for %.200s", buf); 292 auth_debug_add("Bad file modes for %.200s", buf);
diff --git a/auth.c b/auth.c
index 3d31ec860..4152d9c44 100644
--- a/auth.c
+++ b/auth.c
@@ -474,8 +474,7 @@ check_key_in_hostfiles(struct passwd *pw, struct sshkey *key, const char *host,
474 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); 474 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
475 if (options.strict_modes && 475 if (options.strict_modes &&
476 (stat(user_hostfile, &st) == 0) && 476 (stat(user_hostfile, &st) == 0) &&
477 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 477 !secure_permissions(&st, pw->pw_uid)) {
478 (st.st_mode & 022) != 0)) {
479 logit("Authentication refused for %.100s: " 478 logit("Authentication refused for %.100s: "
480 "bad owner or modes for %.200s", 479 "bad owner or modes for %.200s",
481 pw->pw_name, user_hostfile); 480 pw->pw_name, user_hostfile);
diff --git a/misc.c b/misc.c
index 4623b5755..c75a795c2 100644
--- a/misc.c
+++ b/misc.c
@@ -55,8 +55,9 @@
55#include <netdb.h> 55#include <netdb.h>
56#ifdef HAVE_PATHS_H 56#ifdef HAVE_PATHS_H
57# include <paths.h> 57# include <paths.h>
58#include <pwd.h>
59#endif 58#endif
59#include <pwd.h>
60#include <grp.h>
60#ifdef SSH_TUN_OPENBSD 61#ifdef SSH_TUN_OPENBSD
61#include <net/if.h> 62#include <net/if.h>
62#endif 63#endif
@@ -1272,6 +1273,55 @@ percent_dollar_expand(const char *string, ...)
1272} 1273}
1273 1274
1274int 1275int
1276secure_permissions(struct stat *st, uid_t uid)
1277{
1278 if (!platform_sys_dir_uid(st->st_uid) && st->st_uid != uid)
1279 return 0;
1280 if ((st->st_mode & 002) != 0)
1281 return 0;
1282 if ((st->st_mode & 020) != 0) {
1283 /* If the file is group-writable, the group in question must
1284 * have exactly one member, namely the file's owner.
1285 * (Zero-member groups are typically used by setgid
1286 * binaries, and are unlikely to be suitable.)
1287 */
1288 struct passwd *pw;
1289 struct group *gr;
1290 int members = 0;
1291
1292 gr = getgrgid(st->st_gid);
1293 if (!gr)
1294 return 0;
1295
1296 /* Check primary group memberships. */
1297 while ((pw = getpwent()) != NULL) {
1298 if (pw->pw_gid == gr->gr_gid) {
1299 ++members;
1300 if (pw->pw_uid != uid)
1301 return 0;
1302 }
1303 }
1304 endpwent();
1305
1306 pw = getpwuid(st->st_uid);
1307 if (!pw)
1308 return 0;
1309
1310 /* Check supplementary group memberships. */
1311 if (gr->gr_mem[0]) {
1312 ++members;
1313 if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
1314 gr->gr_mem[1])
1315 return 0;
1316 }
1317
1318 if (!members)
1319 return 0;
1320 }
1321 return 1;
1322}
1323
1324int
1275tun_open(int tun, int mode, char **ifname) 1325tun_open(int tun, int mode, char **ifname)
1276{ 1326{
1277#if defined(CUSTOM_SYS_TUN_OPEN) 1327#if defined(CUSTOM_SYS_TUN_OPEN)
@@ -2056,8 +2106,7 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
2056 snprintf(err, errlen, "%s is not a regular file", buf); 2106 snprintf(err, errlen, "%s is not a regular file", buf);
2057 return -1; 2107 return -1;
2058 } 2108 }
2059 if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || 2109 if (!secure_permissions(stp, uid)) {
2060 (stp->st_mode & 022) != 0) {
2061 snprintf(err, errlen, "bad ownership or modes for file %s", 2110 snprintf(err, errlen, "bad ownership or modes for file %s",
2062 buf); 2111 buf);
2063 return -1; 2112 return -1;
@@ -2072,8 +2121,7 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
2072 strlcpy(buf, cp, sizeof(buf)); 2121 strlcpy(buf, cp, sizeof(buf));
2073 2122
2074 if (stat(buf, &st) == -1 || 2123 if (stat(buf, &st) == -1 ||
2075 (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || 2124 !secure_permissions(&st, uid)) {
2076 (st.st_mode & 022) != 0) {
2077 snprintf(err, errlen, 2125 snprintf(err, errlen,
2078 "bad ownership or modes for directory %s", buf); 2126 "bad ownership or modes for directory %s", buf);
2079 return -1; 2127 return -1;
diff --git a/misc.h b/misc.h
index ab94a79c0..b34c798e7 100644
--- a/misc.h
+++ b/misc.h
@@ -192,6 +192,8 @@ struct notifier_ctx *notify_start(int, const char *, ...)
192 __attribute__((format(printf, 2, 3))); 192 __attribute__((format(printf, 2, 3)));
193void notify_complete(struct notifier_ctx *); 193void notify_complete(struct notifier_ctx *);
194 194
195int secure_permissions(struct stat *st, uid_t uid);
196
195#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 197#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
196#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) 198#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
197#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) 199#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
diff --git a/readconf.c b/readconf.c
index 3d0a812b3..f4f273c96 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1967,8 +1967,7 @@ read_config_file_depth(const char *filename, struct passwd *pw,
1967 1967
1968 if (fstat(fileno(f), &sb) == -1) 1968 if (fstat(fileno(f), &sb) == -1)
1969 fatal("fstat %s: %s", filename, strerror(errno)); 1969 fatal("fstat %s: %s", filename, strerror(errno));
1970 if (((sb.st_uid != 0 && sb.st_uid != getuid()) || 1970 if (!secure_permissions(&sb, getuid()))
1971 (sb.st_mode & 022) != 0))
1972 fatal("Bad owner or permissions on %s", filename); 1971 fatal("Bad owner or permissions on %s", filename);
1973 } 1972 }
1974 1973
diff --git a/ssh.1 b/ssh.1
index be8e964f0..5d613076c 100644
--- a/ssh.1
+++ b/ssh.1
@@ -1528,6 +1528,8 @@ The file format and configuration options are described in
1528.Xr ssh_config 5 . 1528.Xr ssh_config 5 .
1529Because of the potential for abuse, this file must have strict permissions: 1529Because of the potential for abuse, this file must have strict permissions:
1530read/write for the user, and not writable by others. 1530read/write for the user, and not writable by others.
1531It may be group-writable provided that the group in question contains only
1532the user.
1531.Pp 1533.Pp
1532.It Pa ~/.ssh/environment 1534.It Pa ~/.ssh/environment
1533Contains additional definitions for environment variables; see 1535Contains additional definitions for environment variables; see
diff --git a/ssh_config.5 b/ssh_config.5
index 3ceb800ba..190e1d927 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -2010,6 +2010,8 @@ The format of this file is described above.
2010This file is used by the SSH client. 2010This file is used by the SSH client.
2011Because of the potential for abuse, this file must have strict permissions: 2011Because of the potential for abuse, this file must have strict permissions:
2012read/write for the user, and not writable by others. 2012read/write for the user, and not writable by others.
2013It may be group-writable provided that the group in question contains only
2014the user.
2013.It Pa /etc/ssh/ssh_config 2015.It Pa /etc/ssh/ssh_config
2014Systemwide configuration file. 2016Systemwide configuration file.
2015This file provides defaults for those 2017This file provides defaults for those