summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2014-02-09 16:09:58 +0000
committerColin Watson <cjwatson@debian.org>2018-10-20 22:54:09 +0100
commit7b931d36ad36a93d2b1811858ca29408ec44ecae (patch)
tree169969b1dbe936bf31ea64d4335152968d56f8d5
parente755ec70d62bfb9b02159123f4e870b00010be77 (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 d8e6b4a3d..9d1d453f1 100644
--- a/auth.c
+++ b/auth.c
@@ -473,8 +473,7 @@ check_key_in_hostfiles(struct passwd *pw, struct sshkey *key, const char *host,
473 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); 473 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
474 if (options.strict_modes && 474 if (options.strict_modes &&
475 (stat(user_hostfile, &st) == 0) && 475 (stat(user_hostfile, &st) == 0) &&
476 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 476 !secure_permissions(&st, pw->pw_uid)) {
477 (st.st_mode & 022) != 0)) {
478 logit("Authentication refused for %.100s: " 477 logit("Authentication refused for %.100s: "
479 "bad owner or modes for %.200s", 478 "bad owner or modes for %.200s",
480 pw->pw_name, user_hostfile); 479 pw->pw_name, user_hostfile);
diff --git a/misc.c b/misc.c
index bdc06fdb3..5159e6692 100644
--- a/misc.c
+++ b/misc.c
@@ -58,8 +58,9 @@
58#include <netdb.h> 58#include <netdb.h>
59#ifdef HAVE_PATHS_H 59#ifdef HAVE_PATHS_H
60# include <paths.h> 60# include <paths.h>
61#include <pwd.h>
62#endif 61#endif
62#include <pwd.h>
63#include <grp.h>
63#ifdef SSH_TUN_OPENBSD 64#ifdef SSH_TUN_OPENBSD
64#include <net/if.h> 65#include <net/if.h>
65#endif 66#endif
@@ -1029,6 +1030,55 @@ percent_expand(const char *string, ...)
1029} 1030}
1030 1031
1031int 1032int
1033secure_permissions(struct stat *st, uid_t uid)
1034{
1035 if (!platform_sys_dir_uid(st->st_uid) && st->st_uid != uid)
1036 return 0;
1037 if ((st->st_mode & 002) != 0)
1038 return 0;
1039 if ((st->st_mode & 020) != 0) {
1040 /* If the file is group-writable, the group in question must
1041 * have exactly one member, namely the file's owner.
1042 * (Zero-member groups are typically used by setgid
1043 * binaries, and are unlikely to be suitable.)
1044 */
1045 struct passwd *pw;
1046 struct group *gr;
1047 int members = 0;
1048
1049 gr = getgrgid(st->st_gid);
1050 if (!gr)
1051 return 0;
1052
1053 /* Check primary group memberships. */
1054 while ((pw = getpwent()) != NULL) {
1055 if (pw->pw_gid == gr->gr_gid) {
1056 ++members;
1057 if (pw->pw_uid != uid)
1058 return 0;
1059 }
1060 }
1061 endpwent();
1062
1063 pw = getpwuid(st->st_uid);
1064 if (!pw)
1065 return 0;
1066
1067 /* Check supplementary group memberships. */
1068 if (gr->gr_mem[0]) {
1069 ++members;
1070 if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
1071 gr->gr_mem[1])
1072 return 0;
1073 }
1074
1075 if (!members)
1076 return 0;
1077 }
1078 return 1;
1079}
1080
1081int
1032tun_open(int tun, int mode, char **ifname) 1082tun_open(int tun, int mode, char **ifname)
1033{ 1083{
1034#if defined(CUSTOM_SYS_TUN_OPEN) 1084#if defined(CUSTOM_SYS_TUN_OPEN)
@@ -1786,8 +1836,7 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
1786 snprintf(err, errlen, "%s is not a regular file", buf); 1836 snprintf(err, errlen, "%s is not a regular file", buf);
1787 return -1; 1837 return -1;
1788 } 1838 }
1789 if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || 1839 if (!secure_permissions(stp, uid)) {
1790 (stp->st_mode & 022) != 0) {
1791 snprintf(err, errlen, "bad ownership or modes for file %s", 1840 snprintf(err, errlen, "bad ownership or modes for file %s",
1792 buf); 1841 buf);
1793 return -1; 1842 return -1;
@@ -1802,8 +1851,7 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
1802 strlcpy(buf, cp, sizeof(buf)); 1851 strlcpy(buf, cp, sizeof(buf));
1803 1852
1804 if (stat(buf, &st) < 0 || 1853 if (stat(buf, &st) < 0 ||
1805 (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || 1854 !secure_permissions(&st, uid)) {
1806 (st.st_mode & 022) != 0) {
1807 snprintf(err, errlen, 1855 snprintf(err, errlen,
1808 "bad ownership or modes for directory %s", buf); 1856 "bad ownership or modes for directory %s", buf);
1809 return -1; 1857 return -1;
diff --git a/misc.h b/misc.h
index 31b207a8d..aaf966e65 100644
--- a/misc.h
+++ b/misc.h
@@ -168,6 +168,8 @@ int safe_path_fd(int, const char *, struct passwd *,
168char *read_passphrase(const char *, int); 168char *read_passphrase(const char *, int);
169int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); 169int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
170 170
171int secure_permissions(struct stat *st, uid_t uid);
172
171#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 173#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
172#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) 174#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
173#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) 175#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
diff --git a/readconf.c b/readconf.c
index 052d4b1ac..6b01f20d2 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1820,8 +1820,7 @@ read_config_file_depth(const char *filename, struct passwd *pw,
1820 1820
1821 if (fstat(fileno(f), &sb) == -1) 1821 if (fstat(fileno(f), &sb) == -1)
1822 fatal("fstat %s: %s", filename, strerror(errno)); 1822 fatal("fstat %s: %s", filename, strerror(errno));
1823 if (((sb.st_uid != 0 && sb.st_uid != getuid()) || 1823 if (!secure_permissions(&sb, getuid()))
1824 (sb.st_mode & 022) != 0))
1825 fatal("Bad owner or permissions on %s", filename); 1824 fatal("Bad owner or permissions on %s", filename);
1826 } 1825 }
1827 1826
diff --git a/ssh.1 b/ssh.1
index 7760c3075..81f29af43 100644
--- a/ssh.1
+++ b/ssh.1
@@ -1485,6 +1485,8 @@ The file format and configuration options are described in
1485.Xr ssh_config 5 . 1485.Xr ssh_config 5 .
1486Because of the potential for abuse, this file must have strict permissions: 1486Because of the potential for abuse, this file must have strict permissions:
1487read/write for the user, and not writable by others. 1487read/write for the user, and not writable by others.
1488It may be group-writable provided that the group in question contains only
1489the user.
1488.Pp 1490.Pp
1489.It Pa ~/.ssh/environment 1491.It Pa ~/.ssh/environment
1490Contains additional definitions for environment variables; see 1492Contains additional definitions for environment variables; see
diff --git a/ssh_config.5 b/ssh_config.5
index 54e143c93..7d55fa820 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -1835,6 +1835,8 @@ The format of this file is described above.
1835This file is used by the SSH client. 1835This file is used by the SSH client.
1836Because of the potential for abuse, this file must have strict permissions: 1836Because of the potential for abuse, this file must have strict permissions:
1837read/write for the user, and not accessible by others. 1837read/write for the user, and not accessible by others.
1838It may be group-writable provided that the group in question contains only
1839the user.
1838.It Pa /etc/ssh/ssh_config 1840.It Pa /etc/ssh/ssh_config
1839Systemwide configuration file. 1841Systemwide configuration file.
1840This file provides defaults for those 1842This file provides defaults for those