summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2014-02-09 16:09:58 +0000
committerColin Watson <cjwatson@debian.org>2020-06-07 10:25:35 +0100
commitcb72edd9757c469f3b5dc9cde374715ae8b54509 (patch)
tree951cb0005a70b702a50b17a59077ac697950264b
parentf2c3eb379d31f24de20dc9a2e0089ed84f52055b (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 687c57b42..aed3c13ac 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 554ceb0b1..75fe4dfea 100644
--- a/misc.c
+++ b/misc.c
@@ -61,8 +61,9 @@
61#include <netdb.h> 61#include <netdb.h>
62#ifdef HAVE_PATHS_H 62#ifdef HAVE_PATHS_H
63# include <paths.h> 63# include <paths.h>
64#include <pwd.h>
65#endif 64#endif
65#include <pwd.h>
66#include <grp.h>
66#ifdef SSH_TUN_OPENBSD 67#ifdef SSH_TUN_OPENBSD
67#include <net/if.h> 68#include <net/if.h>
68#endif 69#endif
@@ -1125,6 +1126,55 @@ percent_expand(const char *string, ...)
1125} 1126}
1126 1127
1127int 1128int
1129secure_permissions(struct stat *st, uid_t uid)
1130{
1131 if (!platform_sys_dir_uid(st->st_uid) && st->st_uid != uid)
1132 return 0;
1133 if ((st->st_mode & 002) != 0)
1134 return 0;
1135 if ((st->st_mode & 020) != 0) {
1136 /* If the file is group-writable, the group in question must
1137 * have exactly one member, namely the file's owner.
1138 * (Zero-member groups are typically used by setgid
1139 * binaries, and are unlikely to be suitable.)
1140 */
1141 struct passwd *pw;
1142 struct group *gr;
1143 int members = 0;
1144
1145 gr = getgrgid(st->st_gid);
1146 if (!gr)
1147 return 0;
1148
1149 /* Check primary group memberships. */
1150 while ((pw = getpwent()) != NULL) {
1151 if (pw->pw_gid == gr->gr_gid) {
1152 ++members;
1153 if (pw->pw_uid != uid)
1154 return 0;
1155 }
1156 }
1157 endpwent();
1158
1159 pw = getpwuid(st->st_uid);
1160 if (!pw)
1161 return 0;
1162
1163 /* Check supplementary group memberships. */
1164 if (gr->gr_mem[0]) {
1165 ++members;
1166 if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
1167 gr->gr_mem[1])
1168 return 0;
1169 }
1170
1171 if (!members)
1172 return 0;
1173 }
1174 return 1;
1175}
1176
1177int
1128tun_open(int tun, int mode, char **ifname) 1178tun_open(int tun, int mode, char **ifname)
1129{ 1179{
1130#if defined(CUSTOM_SYS_TUN_OPEN) 1180#if defined(CUSTOM_SYS_TUN_OPEN)
@@ -1909,8 +1959,7 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
1909 snprintf(err, errlen, "%s is not a regular file", buf); 1959 snprintf(err, errlen, "%s is not a regular file", buf);
1910 return -1; 1960 return -1;
1911 } 1961 }
1912 if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || 1962 if (!secure_permissions(stp, uid)) {
1913 (stp->st_mode & 022) != 0) {
1914 snprintf(err, errlen, "bad ownership or modes for file %s", 1963 snprintf(err, errlen, "bad ownership or modes for file %s",
1915 buf); 1964 buf);
1916 return -1; 1965 return -1;
@@ -1925,8 +1974,7 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
1925 strlcpy(buf, cp, sizeof(buf)); 1974 strlcpy(buf, cp, sizeof(buf));
1926 1975
1927 if (stat(buf, &st) == -1 || 1976 if (stat(buf, &st) == -1 ||
1928 (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || 1977 !secure_permissions(&st, uid)) {
1929 (st.st_mode & 022) != 0) {
1930 snprintf(err, errlen, 1978 snprintf(err, errlen,
1931 "bad ownership or modes for directory %s", buf); 1979 "bad ownership or modes for directory %s", buf);
1932 return -1; 1980 return -1;
diff --git a/misc.h b/misc.h
index 4a05db2da..5db594b91 100644
--- a/misc.h
+++ b/misc.h
@@ -188,6 +188,8 @@ struct notifier_ctx *notify_start(int, const char *, ...)
188 __attribute__((format(printf, 2, 3))); 188 __attribute__((format(printf, 2, 3)));
189void notify_complete(struct notifier_ctx *); 189void notify_complete(struct notifier_ctx *);
190 190
191int secure_permissions(struct stat *st, uid_t uid);
192
191#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 193#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
192#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) 194#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
193#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) 195#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
diff --git a/readconf.c b/readconf.c
index 431243193..5bf0afbb4 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1926,8 +1926,7 @@ read_config_file_depth(const char *filename, struct passwd *pw,
1926 1926
1927 if (fstat(fileno(f), &sb) == -1) 1927 if (fstat(fileno(f), &sb) == -1)
1928 fatal("fstat %s: %s", filename, strerror(errno)); 1928 fatal("fstat %s: %s", filename, strerror(errno));
1929 if (((sb.st_uid != 0 && sb.st_uid != getuid()) || 1929 if (!secure_permissions(&sb, getuid()))
1930 (sb.st_mode & 022) != 0))
1931 fatal("Bad owner or permissions on %s", filename); 1930 fatal("Bad owner or permissions on %s", filename);
1932 } 1931 }
1933 1932
diff --git a/ssh.1 b/ssh.1
index 7a3ba31ab..a80be8efe 100644
--- a/ssh.1
+++ b/ssh.1
@@ -1509,6 +1509,8 @@ The file format and configuration options are described in
1509.Xr ssh_config 5 . 1509.Xr ssh_config 5 .
1510Because of the potential for abuse, this file must have strict permissions: 1510Because of the potential for abuse, this file must have strict permissions:
1511read/write for the user, and not writable by others. 1511read/write for the user, and not writable by others.
1512It may be group-writable provided that the group in question contains only
1513the user.
1512.Pp 1514.Pp
1513.It Pa ~/.ssh/environment 1515.It Pa ~/.ssh/environment
1514Contains additional definitions for environment variables; see 1516Contains additional definitions for environment variables; see
diff --git a/ssh_config.5 b/ssh_config.5
index 85ab7447f..d814147d4 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -1957,6 +1957,8 @@ The format of this file is described above.
1957This file is used by the SSH client. 1957This file is used by the SSH client.
1958Because of the potential for abuse, this file must have strict permissions: 1958Because of the potential for abuse, this file must have strict permissions:
1959read/write for the user, and not writable by others. 1959read/write for the user, and not writable by others.
1960It may be group-writable provided that the group in question contains only
1961the user.
1960.It Pa /etc/ssh/ssh_config 1962.It Pa /etc/ssh/ssh_config
1961Systemwide configuration file. 1963Systemwide configuration file.
1962This file provides defaults for those 1964This file provides defaults for those