summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2014-02-09 16:09:58 +0000
committerColin Watson <cjwatson@debian.org>2018-08-24 17:49:07 +0100
commit840f43066f9cdf5f6bb07992aca1c5f43be8eb80 (patch)
treeeada7af8840576ed8b715b291101bc150914d004
parentd47fa6fefb418c6d8f5a6d3dd49fd9dc7fce0c74 (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: 2017-10-04 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 57296e1f6..546aa0495 100644
--- a/auth-rhosts.c
+++ b/auth-rhosts.c
@@ -261,8 +261,7 @@ auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname,
261 return 0; 261 return 0;
262 } 262 }
263 if (options.strict_modes && 263 if (options.strict_modes &&
264 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 264 !secure_permissions(&st, pw->pw_uid)) {
265 (st.st_mode & 022) != 0)) {
266 logit("Rhosts authentication refused for %.100s: " 265 logit("Rhosts authentication refused for %.100s: "
267 "bad ownership or modes for home directory.", pw->pw_name); 266 "bad ownership or modes for home directory.", pw->pw_name);
268 auth_debug_add("Rhosts authentication refused for %.100s: " 267 auth_debug_add("Rhosts authentication refused for %.100s: "
@@ -288,8 +287,7 @@ auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname,
288 * allowing access to their account by anyone. 287 * allowing access to their account by anyone.
289 */ 288 */
290 if (options.strict_modes && 289 if (options.strict_modes &&
291 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 290 !secure_permissions(&st, pw->pw_uid)) {
292 (st.st_mode & 022) != 0)) {
293 logit("Rhosts authentication refused for %.100s: bad modes for %.200s", 291 logit("Rhosts authentication refused for %.100s: bad modes for %.200s",
294 pw->pw_name, buf); 292 pw->pw_name, buf);
295 auth_debug_add("Bad file modes for %.200s", buf); 293 auth_debug_add("Bad file modes for %.200s", buf);
diff --git a/auth.c b/auth.c
index 80eb78c48..ad25631a5 100644
--- a/auth.c
+++ b/auth.c
@@ -469,8 +469,7 @@ check_key_in_hostfiles(struct passwd *pw, struct sshkey *key, const char *host,
469 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); 469 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
470 if (options.strict_modes && 470 if (options.strict_modes &&
471 (stat(user_hostfile, &st) == 0) && 471 (stat(user_hostfile, &st) == 0) &&
472 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 472 !secure_permissions(&st, pw->pw_uid)) {
473 (st.st_mode & 022) != 0)) {
474 logit("Authentication refused for %.100s: " 473 logit("Authentication refused for %.100s: "
475 "bad owner or modes for %.200s", 474 "bad owner or modes for %.200s",
476 pw->pw_name, user_hostfile); 475 pw->pw_name, user_hostfile);
diff --git a/misc.c b/misc.c
index ae4d29b84..2f3dbda0c 100644
--- a/misc.c
+++ b/misc.c
@@ -57,8 +57,9 @@
57#include <netdb.h> 57#include <netdb.h>
58#ifdef HAVE_PATHS_H 58#ifdef HAVE_PATHS_H
59# include <paths.h> 59# include <paths.h>
60#include <pwd.h>
61#endif 60#endif
61#include <pwd.h>
62#include <grp.h>
62#ifdef SSH_TUN_OPENBSD 63#ifdef SSH_TUN_OPENBSD
63#include <net/if.h> 64#include <net/if.h>
64#endif 65#endif
@@ -1025,6 +1026,55 @@ percent_expand(const char *string, ...)
1025} 1026}
1026 1027
1027int 1028int
1029secure_permissions(struct stat *st, uid_t uid)
1030{
1031 if (!platform_sys_dir_uid(st->st_uid) && st->st_uid != uid)
1032 return 0;
1033 if ((st->st_mode & 002) != 0)
1034 return 0;
1035 if ((st->st_mode & 020) != 0) {
1036 /* If the file is group-writable, the group in question must
1037 * have exactly one member, namely the file's owner.
1038 * (Zero-member groups are typically used by setgid
1039 * binaries, and are unlikely to be suitable.)
1040 */
1041 struct passwd *pw;
1042 struct group *gr;
1043 int members = 0;
1044
1045 gr = getgrgid(st->st_gid);
1046 if (!gr)
1047 return 0;
1048
1049 /* Check primary group memberships. */
1050 while ((pw = getpwent()) != NULL) {
1051 if (pw->pw_gid == gr->gr_gid) {
1052 ++members;
1053 if (pw->pw_uid != uid)
1054 return 0;
1055 }
1056 }
1057 endpwent();
1058
1059 pw = getpwuid(st->st_uid);
1060 if (!pw)
1061 return 0;
1062
1063 /* Check supplementary group memberships. */
1064 if (gr->gr_mem[0]) {
1065 ++members;
1066 if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
1067 gr->gr_mem[1])
1068 return 0;
1069 }
1070
1071 if (!members)
1072 return 0;
1073 }
1074 return 1;
1075}
1076
1077int
1028tun_open(int tun, int mode, char **ifname) 1078tun_open(int tun, int mode, char **ifname)
1029{ 1079{
1030#if defined(CUSTOM_SYS_TUN_OPEN) 1080#if defined(CUSTOM_SYS_TUN_OPEN)
@@ -1782,8 +1832,7 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
1782 snprintf(err, errlen, "%s is not a regular file", buf); 1832 snprintf(err, errlen, "%s is not a regular file", buf);
1783 return -1; 1833 return -1;
1784 } 1834 }
1785 if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || 1835 if (!secure_permissions(stp, uid)) {
1786 (stp->st_mode & 022) != 0) {
1787 snprintf(err, errlen, "bad ownership or modes for file %s", 1836 snprintf(err, errlen, "bad ownership or modes for file %s",
1788 buf); 1837 buf);
1789 return -1; 1838 return -1;
@@ -1798,8 +1847,7 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
1798 strlcpy(buf, cp, sizeof(buf)); 1847 strlcpy(buf, cp, sizeof(buf));
1799 1848
1800 if (stat(buf, &st) < 0 || 1849 if (stat(buf, &st) < 0 ||
1801 (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || 1850 !secure_permissions(&st, uid)) {
1802 (st.st_mode & 022) != 0) {
1803 snprintf(err, errlen, 1851 snprintf(err, errlen,
1804 "bad ownership or modes for directory %s", buf); 1852 "bad ownership or modes for directory %s", buf);
1805 return -1; 1853 return -1;
diff --git a/misc.h b/misc.h
index 6be289fd2..213c3abb7 100644
--- a/misc.h
+++ b/misc.h
@@ -167,6 +167,8 @@ int safe_path_fd(int, const char *, struct passwd *,
167char *read_passphrase(const char *, int); 167char *read_passphrase(const char *, int);
168int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); 168int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
169 169
170int secure_permissions(struct stat *st, uid_t uid);
171
170#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 172#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
171#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) 173#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
172#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) 174#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
diff --git a/readconf.c b/readconf.c
index 3fd0fe7b7..3ed6dfb54 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1795,8 +1795,7 @@ read_config_file_depth(const char *filename, struct passwd *pw,
1795 1795
1796 if (fstat(fileno(f), &sb) == -1) 1796 if (fstat(fileno(f), &sb) == -1)
1797 fatal("fstat %s: %s", filename, strerror(errno)); 1797 fatal("fstat %s: %s", filename, strerror(errno));
1798 if (((sb.st_uid != 0 && sb.st_uid != getuid()) || 1798 if (!secure_permissions(&sb, getuid()))
1799 (sb.st_mode & 022) != 0))
1800 fatal("Bad owner or permissions on %s", filename); 1799 fatal("Bad owner or permissions on %s", filename);
1801 } 1800 }
1802 1801
diff --git a/ssh.1 b/ssh.1
index b20908a5e..cb0fdd50e 100644
--- a/ssh.1
+++ b/ssh.1
@@ -1475,6 +1475,8 @@ The file format and configuration options are described in
1475.Xr ssh_config 5 . 1475.Xr ssh_config 5 .
1476Because of the potential for abuse, this file must have strict permissions: 1476Because of the potential for abuse, this file must have strict permissions:
1477read/write for the user, and not writable by others. 1477read/write for the user, and not writable by others.
1478It may be group-writable provided that the group in question contains only
1479the user.
1478.Pp 1480.Pp
1479.It Pa ~/.ssh/environment 1481.It Pa ~/.ssh/environment
1480Contains additional definitions for environment variables; see 1482Contains additional definitions for environment variables; see
diff --git a/ssh_config.5 b/ssh_config.5
index 86ada128e..03341a229 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -1813,6 +1813,8 @@ The format of this file is described above.
1813This file is used by the SSH client. 1813This file is used by the SSH client.
1814Because of the potential for abuse, this file must have strict permissions: 1814Because of the potential for abuse, this file must have strict permissions:
1815read/write for the user, and not accessible by others. 1815read/write for the user, and not accessible by others.
1816It may be group-writable provided that the group in question contains only
1817the user.
1816.It Pa /etc/ssh/ssh_config 1818.It Pa /etc/ssh/ssh_config
1817Systemwide configuration file. 1819Systemwide configuration file.
1818This file provides defaults for those 1820This file provides defaults for those