summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2014-02-09 16:09:58 +0000
committerColin Watson <cjwatson@debian.org>2017-03-29 01:39:47 +0100
commit0b9c0482cbff9ce16384e4247d955676d4d77df3 (patch)
treedcaaa33c770f1520a423e31d287ef10d827acc1b
parent980646a9f7f03b43b678272b2a56e30906c6ddec (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 ecf956f06..4dccd5e6a 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 c63906873..90390724d 100644
--- a/auth.c
+++ b/auth.c
@@ -444,8 +444,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
444 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); 444 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
445 if (options.strict_modes && 445 if (options.strict_modes &&
446 (stat(user_hostfile, &st) == 0) && 446 (stat(user_hostfile, &st) == 0) &&
447 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 447 !secure_permissions(&st, pw->pw_uid)) {
448 (st.st_mode & 022) != 0)) {
449 logit("Authentication refused for %.100s: " 448 logit("Authentication refused for %.100s: "
450 "bad owner or modes for %.200s", 449 "bad owner or modes for %.200s",
451 pw->pw_name, user_hostfile); 450 pw->pw_name, user_hostfile);
@@ -507,8 +506,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
507 snprintf(err, errlen, "%s is not a regular file", buf); 506 snprintf(err, errlen, "%s is not a regular file", buf);
508 return -1; 507 return -1;
509 } 508 }
510 if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || 509 if (!secure_permissions(stp, uid)) {
511 (stp->st_mode & 022) != 0) {
512 snprintf(err, errlen, "bad ownership or modes for file %s", 510 snprintf(err, errlen, "bad ownership or modes for file %s",
513 buf); 511 buf);
514 return -1; 512 return -1;
@@ -523,8 +521,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
523 strlcpy(buf, cp, sizeof(buf)); 521 strlcpy(buf, cp, sizeof(buf));
524 522
525 if (stat(buf, &st) < 0 || 523 if (stat(buf, &st) < 0 ||
526 (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || 524 !secure_permissions(&st, uid)) {
527 (st.st_mode & 022) != 0) {
528 snprintf(err, errlen, 525 snprintf(err, errlen,
529 "bad ownership or modes for directory %s", buf); 526 "bad ownership or modes for directory %s", buf);
530 return -1; 527 return -1;
diff --git a/misc.c b/misc.c
index cfd32729a..6e972f563 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 *
@@ -713,6 +715,71 @@ read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
713 return -1; 715 return -1;
714} 716}
715 717
718/*
719 * return 1 if the specified uid is a uid that may own a system directory
720 * otherwise 0.
721 */
722int
723platform_sys_dir_uid(uid_t uid)
724{
725 if (uid == 0)
726 return 1;
727#ifdef PLATFORM_SYS_DIR_UID
728 if (uid == PLATFORM_SYS_DIR_UID)
729 return 1;
730#endif
731 return 0;
732}
733
734int
735secure_permissions(struct stat *st, uid_t uid)
736{
737 if (!platform_sys_dir_uid(st->st_uid) && st->st_uid != uid)
738 return 0;
739 if ((st->st_mode & 002) != 0)
740 return 0;
741 if ((st->st_mode & 020) != 0) {
742 /* If the file is group-writable, the group in question must
743 * have exactly one member, namely the file's owner.
744 * (Zero-member groups are typically used by setgid
745 * binaries, and are unlikely to be suitable.)
746 */
747 struct passwd *pw;
748 struct group *gr;
749 int members = 0;
750
751 gr = getgrgid(st->st_gid);
752 if (!gr)
753 return 0;
754
755 /* Check primary group memberships. */
756 while ((pw = getpwent()) != NULL) {
757 if (pw->pw_gid == gr->gr_gid) {
758 ++members;
759 if (pw->pw_uid != uid)
760 return 0;
761 }
762 }
763 endpwent();
764
765 pw = getpwuid(st->st_uid);
766 if (!pw)
767 return 0;
768
769 /* Check supplementary group memberships. */
770 if (gr->gr_mem[0]) {
771 ++members;
772 if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
773 gr->gr_mem[1])
774 return 0;
775 }
776
777 if (!members)
778 return 0;
779 }
780 return 1;
781}
782
716int 783int
717tun_open(int tun, int mode) 784tun_open(int tun, int mode)
718{ 785{
diff --git a/misc.h b/misc.h
index c242f9011..8b223b55e 100644
--- a/misc.h
+++ b/misc.h
@@ -143,6 +143,8 @@ char *read_passphrase(const char *, int);
143int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); 143int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
144int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *); 144int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
145 145
146int secure_permissions(struct stat *st, uid_t uid);
147
146#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 148#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
147#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) 149#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
148#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) 150#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
diff --git a/platform.c b/platform.c
index cd7bf5665..380ee3a41 100644
--- a/platform.c
+++ b/platform.c
@@ -197,19 +197,3 @@ platform_krb5_get_principal_name(const char *pw_name)
197 return NULL; 197 return NULL;
198#endif 198#endif
199} 199}
200
201/*
202 * return 1 if the specified uid is a uid that may own a system directory
203 * otherwise 0.
204 */
205int
206platform_sys_dir_uid(uid_t uid)
207{
208 if (uid == 0)
209 return 1;
210#ifdef PLATFORM_SYS_DIR_UID
211 if (uid == PLATFORM_SYS_DIR_UID)
212 return 1;
213#endif
214 return 0;
215}
diff --git a/readconf.c b/readconf.c
index 0b1370a8e..70fac6824 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1773,8 +1773,7 @@ read_config_file_depth(const char *filename, struct passwd *pw,
1773 1773
1774 if (fstat(fileno(f), &sb) == -1) 1774 if (fstat(fileno(f), &sb) == -1)
1775 fatal("fstat %s: %s", filename, strerror(errno)); 1775 fatal("fstat %s: %s", filename, strerror(errno));
1776 if (((sb.st_uid != 0 && sb.st_uid != getuid()) || 1776 if (!secure_permissions(&sb, getuid()))
1777 (sb.st_mode & 022) != 0))
1778 fatal("Bad owner or permissions on %s", filename); 1777 fatal("Bad owner or permissions on %s", filename);
1779 } 1778 }
1780 1779
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 e4eaa5ae0..a04e5757b 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -1827,6 +1827,8 @@ The format of this file is described above.
1827This file is used by the SSH client. 1827This file is used by the SSH client.
1828Because of the potential for abuse, this file must have strict permissions: 1828Because of the potential for abuse, this file must have strict permissions:
1829read/write for the user, and not accessible by others. 1829read/write for the user, and not accessible by others.
1830It may be group-writable provided that the group in question contains only
1831the user.
1830.It Pa /etc/ssh/ssh_config 1832.It Pa /etc/ssh/ssh_config
1831Systemwide configuration file. 1833Systemwide configuration file.
1832This file provides defaults for those 1834This file provides defaults for those