summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2014-02-09 16:09:58 +0000
committerColin Watson <cjwatson@debian.org>2016-08-07 12:18:43 +0100
commit563974a78e937c4844e1198b5f6d79b8b2b5c600 (patch)
treeab2b821031f9b3d4dac7123b677dd6e8a93ac4d3
parent173d65e72989cba82502604da3f1336766c0cf0f (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: 2013-09-14 Patch-Name: user-group-modes.patch
-rw-r--r--auth-rhosts.c6
-rw-r--r--auth.c9
-rw-r--r--misc.c69
-rw-r--r--misc.h2
-rw-r--r--platform.c16
-rw-r--r--readconf.c3
-rw-r--r--ssh.12
-rw-r--r--ssh_config.52
8 files changed, 80 insertions, 29 deletions
diff --git a/auth-rhosts.c b/auth-rhosts.c
index 0ef344712..c17c13cc1 100644
--- a/auth-rhosts.c
+++ b/auth-rhosts.c
@@ -273,8 +273,7 @@ auth_rhosts2_raw(struct passwd *pw, const char *client_user, const char *hostnam
273 return 0; 273 return 0;
274 } 274 }
275 if (options.strict_modes && 275 if (options.strict_modes &&
276 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 276 !secure_permissions(&st, pw->pw_uid)) {
277 (st.st_mode & 022) != 0)) {
278 logit("Rhosts authentication refused for %.100s: " 277 logit("Rhosts authentication refused for %.100s: "
279 "bad ownership or modes for home directory.", pw->pw_name); 278 "bad ownership or modes for home directory.", pw->pw_name);
280 auth_debug_add("Rhosts authentication refused for %.100s: " 279 auth_debug_add("Rhosts authentication refused for %.100s: "
@@ -300,8 +299,7 @@ auth_rhosts2_raw(struct passwd *pw, const char *client_user, const char *hostnam
300 * allowing access to their account by anyone. 299 * allowing access to their account by anyone.
301 */ 300 */
302 if (options.strict_modes && 301 if (options.strict_modes &&
303 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 302 !secure_permissions(&st, pw->pw_uid)) {
304 (st.st_mode & 022) != 0)) {
305 logit("Rhosts authentication refused for %.100s: bad modes for %.200s", 303 logit("Rhosts authentication refused for %.100s: bad modes for %.200s",
306 pw->pw_name, buf); 304 pw->pw_name, buf);
307 auth_debug_add("Bad file modes for %.200s", buf); 305 auth_debug_add("Bad file modes for %.200s", buf);
diff --git a/auth.c b/auth.c
index f56dcc6cf..3f8b348fc 100644
--- a/auth.c
+++ b/auth.c
@@ -435,8 +435,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
435 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); 435 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
436 if (options.strict_modes && 436 if (options.strict_modes &&
437 (stat(user_hostfile, &st) == 0) && 437 (stat(user_hostfile, &st) == 0) &&
438 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 438 !secure_permissions(&st, pw->pw_uid)) {
439 (st.st_mode & 022) != 0)) {
440 logit("Authentication refused for %.100s: " 439 logit("Authentication refused for %.100s: "
441 "bad owner or modes for %.200s", 440 "bad owner or modes for %.200s",
442 pw->pw_name, user_hostfile); 441 pw->pw_name, user_hostfile);
@@ -498,8 +497,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
498 snprintf(err, errlen, "%s is not a regular file", buf); 497 snprintf(err, errlen, "%s is not a regular file", buf);
499 return -1; 498 return -1;
500 } 499 }
501 if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || 500 if (!secure_permissions(stp, uid)) {
502 (stp->st_mode & 022) != 0) {
503 snprintf(err, errlen, "bad ownership or modes for file %s", 501 snprintf(err, errlen, "bad ownership or modes for file %s",
504 buf); 502 buf);
505 return -1; 503 return -1;
@@ -514,8 +512,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
514 strlcpy(buf, cp, sizeof(buf)); 512 strlcpy(buf, cp, sizeof(buf));
515 513
516 if (stat(buf, &st) < 0 || 514 if (stat(buf, &st) < 0 ||
517 (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || 515 !secure_permissions(&st, uid)) {
518 (st.st_mode & 022) != 0) {
519 snprintf(err, errlen, 516 snprintf(err, errlen,
520 "bad ownership or modes for directory %s", buf); 517 "bad ownership or modes for directory %s", buf);
521 return -1; 518 return -1;
diff --git a/misc.c b/misc.c
index 9421b4d39..68efb2b38 100644
--- a/misc.c
+++ b/misc.c
@@ -51,8 +51,9 @@
51#include <netdb.h> 51#include <netdb.h>
52#ifdef HAVE_PATHS_H 52#ifdef HAVE_PATHS_H
53# include <paths.h> 53# include <paths.h>
54#include <pwd.h>
55#endif 54#endif
55#include <pwd.h>
56#include <grp.h>
56#ifdef SSH_TUN_OPENBSD 57#ifdef SSH_TUN_OPENBSD
57#include <net/if.h> 58#include <net/if.h>
58#endif 59#endif
@@ -61,6 +62,7 @@
61#include "misc.h" 62#include "misc.h"
62#include "log.h" 63#include "log.h"
63#include "ssh.h" 64#include "ssh.h"
65#include "platform.h"
64 66
65/* remove newline at end of string */ 67/* remove newline at end of string */
66char * 68char *
@@ -708,6 +710,71 @@ read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
708 return -1; 710 return -1;
709} 711}
710 712
713/*
714 * return 1 if the specified uid is a uid that may own a system directory
715 * otherwise 0.
716 */
717int
718platform_sys_dir_uid(uid_t uid)
719{
720 if (uid == 0)
721 return 1;
722#ifdef PLATFORM_SYS_DIR_UID
723 if (uid == PLATFORM_SYS_DIR_UID)
724 return 1;
725#endif
726 return 0;
727}
728
729int
730secure_permissions(struct stat *st, uid_t uid)
731{
732 if (!platform_sys_dir_uid(st->st_uid) && st->st_uid != uid)
733 return 0;
734 if ((st->st_mode & 002) != 0)
735 return 0;
736 if ((st->st_mode & 020) != 0) {
737 /* If the file is group-writable, the group in question must
738 * have exactly one member, namely the file's owner.
739 * (Zero-member groups are typically used by setgid
740 * binaries, and are unlikely to be suitable.)
741 */
742 struct passwd *pw;
743 struct group *gr;
744 int members = 0;
745
746 gr = getgrgid(st->st_gid);
747 if (!gr)
748 return 0;
749
750 /* Check primary group memberships. */
751 while ((pw = getpwent()) != NULL) {
752 if (pw->pw_gid == gr->gr_gid) {
753 ++members;
754 if (pw->pw_uid != uid)
755 return 0;
756 }
757 }
758 endpwent();
759
760 pw = getpwuid(st->st_uid);
761 if (!pw)
762 return 0;
763
764 /* Check supplementary group memberships. */
765 if (gr->gr_mem[0]) {
766 ++members;
767 if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
768 gr->gr_mem[1])
769 return 0;
770 }
771
772 if (!members)
773 return 0;
774 }
775 return 1;
776}
777
711int 778int
712tun_open(int tun, int mode) 779tun_open(int tun, int mode)
713{ 780{
diff --git a/misc.h b/misc.h
index 7c76a6a72..42cd95e42 100644
--- a/misc.h
+++ b/misc.h
@@ -139,4 +139,6 @@ char *read_passphrase(const char *, int);
139int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); 139int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
140int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *); 140int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
141 141
142int secure_permissions(struct stat *st, uid_t uid);
143
142#endif /* _MISC_H */ 144#endif /* _MISC_H */
diff --git a/platform.c b/platform.c
index 483170680..2ce4dbf81 100644
--- a/platform.c
+++ b/platform.c
@@ -199,19 +199,3 @@ platform_krb5_get_principal_name(const char *pw_name)
199 return NULL; 199 return NULL;
200#endif 200#endif
201} 201}
202
203/*
204 * return 1 if the specified uid is a uid that may own a system directory
205 * otherwise 0.
206 */
207int
208platform_sys_dir_uid(uid_t uid)
209{
210 if (uid == 0)
211 return 1;
212#ifdef PLATFORM_SYS_DIR_UID
213 if (uid == PLATFORM_SYS_DIR_UID)
214 return 1;
215#endif
216 return 0;
217}
diff --git a/readconf.c b/readconf.c
index 3a6c67ba5..f6b4c8f1e 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1753,8 +1753,7 @@ read_config_file_depth(const char *filename, struct passwd *pw,
1753 1753
1754 if (fstat(fileno(f), &sb) == -1) 1754 if (fstat(fileno(f), &sb) == -1)
1755 fatal("fstat %s: %s", filename, strerror(errno)); 1755 fatal("fstat %s: %s", filename, strerror(errno));
1756 if (((sb.st_uid != 0 && sb.st_uid != getuid()) || 1756 if (!secure_permissions(&sb, getuid()))
1757 (sb.st_mode & 022) != 0))
1758 fatal("Bad owner or permissions on %s", filename); 1757 fatal("Bad owner or permissions on %s", filename);
1759 } 1758 }
1760 1759
diff --git a/ssh.1 b/ssh.1
index 4011c65aa..feef81a54 100644
--- a/ssh.1
+++ b/ssh.1
@@ -1484,6 +1484,8 @@ The file format and configuration options are described in
1484.Xr ssh_config 5 . 1484.Xr ssh_config 5 .
1485Because of the potential for abuse, this file must have strict permissions: 1485Because of the potential for abuse, this file must have strict permissions:
1486read/write for the user, and not writable by others. 1486read/write for the user, and not writable by others.
1487It may be group-writable provided that the group in question contains only
1488the user.
1487.Pp 1489.Pp
1488.It Pa ~/.ssh/environment 1490.It Pa ~/.ssh/environment
1489Contains additional definitions for environment variables; see 1491Contains additional definitions for environment variables; see
diff --git a/ssh_config.5 b/ssh_config.5
index efc265ab7..5dd26bcaa 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -1903,6 +1903,8 @@ The format of this file is described above.
1903This file is used by the SSH client. 1903This file is used by the SSH client.
1904Because of the potential for abuse, this file must have strict permissions: 1904Because of the potential for abuse, this file must have strict permissions:
1905read/write for the user, and not accessible by others. 1905read/write for the user, and not accessible by others.
1906It may be group-writable provided that the group in question contains only
1907the user.
1906.It Pa /etc/ssh/ssh_config 1908.It Pa /etc/ssh/ssh_config
1907Systemwide configuration file. 1909Systemwide configuration file.
1908This file provides defaults for those 1910This file provides defaults for those