summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2014-02-09 16:09:58 +0000
committerColin Watson <cjwatson@debian.org>2014-02-09 23:43:40 +0000
commit2bb37315c1e077bc176e703fbf0028a1f6315d37 (patch)
tree8c0fc9e5af8b442fbfa6688bfa03ca05fc9f0c19
parent05609b1cb381eafb999214bf4a95138e63abdbf2 (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.c5
-rw-r--r--ssh.12
-rw-r--r--ssh_config.52
8 files changed, 82 insertions, 29 deletions
diff --git a/auth-rhosts.c b/auth-rhosts.c
index 06ae7f0b9..f20278797 100644
--- a/auth-rhosts.c
+++ b/auth-rhosts.c
@@ -256,8 +256,7 @@ auth_rhosts2_raw(struct passwd *pw, const char *client_user, const char *hostnam
256 return 0; 256 return 0;
257 } 257 }
258 if (options.strict_modes && 258 if (options.strict_modes &&
259 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 259 !secure_permissions(&st, pw->pw_uid)) {
260 (st.st_mode & 022) != 0)) {
261 logit("Rhosts authentication refused for %.100s: " 260 logit("Rhosts authentication refused for %.100s: "
262 "bad ownership or modes for home directory.", pw->pw_name); 261 "bad ownership or modes for home directory.", pw->pw_name);
263 auth_debug_add("Rhosts authentication refused for %.100s: " 262 auth_debug_add("Rhosts authentication refused for %.100s: "
@@ -283,8 +282,7 @@ auth_rhosts2_raw(struct passwd *pw, const char *client_user, const char *hostnam
283 * allowing access to their account by anyone. 282 * allowing access to their account by anyone.
284 */ 283 */
285 if (options.strict_modes && 284 if (options.strict_modes &&
286 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 285 !secure_permissions(&st, pw->pw_uid)) {
287 (st.st_mode & 022) != 0)) {
288 logit("Rhosts authentication refused for %.100s: bad modes for %.200s", 286 logit("Rhosts authentication refused for %.100s: bad modes for %.200s",
289 pw->pw_name, buf); 287 pw->pw_name, buf);
290 auth_debug_add("Bad file modes for %.200s", buf); 288 auth_debug_add("Bad file modes for %.200s", buf);
diff --git a/auth.c b/auth.c
index 9a36f1dac..0c45f0954 100644
--- a/auth.c
+++ b/auth.c
@@ -407,8 +407,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
407 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); 407 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
408 if (options.strict_modes && 408 if (options.strict_modes &&
409 (stat(user_hostfile, &st) == 0) && 409 (stat(user_hostfile, &st) == 0) &&
410 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 410 !secure_permissions(&st, pw->pw_uid)) {
411 (st.st_mode & 022) != 0)) {
412 logit("Authentication refused for %.100s: " 411 logit("Authentication refused for %.100s: "
413 "bad owner or modes for %.200s", 412 "bad owner or modes for %.200s",
414 pw->pw_name, user_hostfile); 413 pw->pw_name, user_hostfile);
@@ -470,8 +469,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
470 snprintf(err, errlen, "%s is not a regular file", buf); 469 snprintf(err, errlen, "%s is not a regular file", buf);
471 return -1; 470 return -1;
472 } 471 }
473 if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || 472 if (!secure_permissions(stp, uid)) {
474 (stp->st_mode & 022) != 0) {
475 snprintf(err, errlen, "bad ownership or modes for file %s", 473 snprintf(err, errlen, "bad ownership or modes for file %s",
476 buf); 474 buf);
477 return -1; 475 return -1;
@@ -486,8 +484,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
486 strlcpy(buf, cp, sizeof(buf)); 484 strlcpy(buf, cp, sizeof(buf));
487 485
488 if (stat(buf, &st) < 0 || 486 if (stat(buf, &st) < 0 ||
489 (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || 487 !secure_permissions(&st, uid)) {
490 (st.st_mode & 022) != 0) {
491 snprintf(err, errlen, 488 snprintf(err, errlen,
492 "bad ownership or modes for directory %s", buf); 489 "bad ownership or modes for directory %s", buf);
493 return -1; 490 return -1;
diff --git a/misc.c b/misc.c
index c3c809943..eb57bfc1b 100644
--- a/misc.c
+++ b/misc.c
@@ -48,8 +48,9 @@
48#include <netdb.h> 48#include <netdb.h>
49#ifdef HAVE_PATHS_H 49#ifdef HAVE_PATHS_H
50# include <paths.h> 50# include <paths.h>
51#include <pwd.h>
52#endif 51#endif
52#include <pwd.h>
53#include <grp.h>
53#ifdef SSH_TUN_OPENBSD 54#ifdef SSH_TUN_OPENBSD
54#include <net/if.h> 55#include <net/if.h>
55#endif 56#endif
@@ -58,6 +59,7 @@
58#include "misc.h" 59#include "misc.h"
59#include "log.h" 60#include "log.h"
60#include "ssh.h" 61#include "ssh.h"
62#include "platform.h"
61 63
62/* remove newline at end of string */ 64/* remove newline at end of string */
63char * 65char *
@@ -642,6 +644,71 @@ read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
642 return -1; 644 return -1;
643} 645}
644 646
647/*
648 * return 1 if the specified uid is a uid that may own a system directory
649 * otherwise 0.
650 */
651int
652platform_sys_dir_uid(uid_t uid)
653{
654 if (uid == 0)
655 return 1;
656#ifdef PLATFORM_SYS_DIR_UID
657 if (uid == PLATFORM_SYS_DIR_UID)
658 return 1;
659#endif
660 return 0;
661}
662
663int
664secure_permissions(struct stat *st, uid_t uid)
665{
666 if (!platform_sys_dir_uid(st->st_uid) && st->st_uid != uid)
667 return 0;
668 if ((st->st_mode & 002) != 0)
669 return 0;
670 if ((st->st_mode & 020) != 0) {
671 /* If the file is group-writable, the group in question must
672 * have exactly one member, namely the file's owner.
673 * (Zero-member groups are typically used by setgid
674 * binaries, and are unlikely to be suitable.)
675 */
676 struct passwd *pw;
677 struct group *gr;
678 int members = 0;
679
680 gr = getgrgid(st->st_gid);
681 if (!gr)
682 return 0;
683
684 /* Check primary group memberships. */
685 while ((pw = getpwent()) != NULL) {
686 if (pw->pw_gid == gr->gr_gid) {
687 ++members;
688 if (pw->pw_uid != uid)
689 return 0;
690 }
691 }
692 endpwent();
693
694 pw = getpwuid(st->st_uid);
695 if (!pw)
696 return 0;
697
698 /* Check supplementary group memberships. */
699 if (gr->gr_mem[0]) {
700 ++members;
701 if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
702 gr->gr_mem[1])
703 return 0;
704 }
705
706 if (!members)
707 return 0;
708 }
709 return 1;
710}
711
645int 712int
646tun_open(int tun, int mode) 713tun_open(int tun, int mode)
647{ 714{
diff --git a/misc.h b/misc.h
index fceb30655..51ba182e1 100644
--- a/misc.h
+++ b/misc.h
@@ -104,4 +104,6 @@ char *read_passphrase(const char *, int);
104int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); 104int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
105int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *); 105int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
106 106
107int secure_permissions(struct stat *st, uid_t uid);
108
107#endif /* _MISC_H */ 109#endif /* _MISC_H */
diff --git a/platform.c b/platform.c
index a962f15b5..0b3bee147 100644
--- a/platform.c
+++ b/platform.c
@@ -194,19 +194,3 @@ platform_krb5_get_principal_name(const char *pw_name)
194 return NULL; 194 return NULL;
195#endif 195#endif
196} 196}
197
198/*
199 * return 1 if the specified uid is a uid that may own a system directory
200 * otherwise 0.
201 */
202int
203platform_sys_dir_uid(uid_t uid)
204{
205 if (uid == 0)
206 return 1;
207#ifdef PLATFORM_SYS_DIR_UID
208 if (uid == PLATFORM_SYS_DIR_UID)
209 return 1;
210#endif
211 return 0;
212}
diff --git a/readconf.c b/readconf.c
index dab7963d6..c74193424 100644
--- a/readconf.c
+++ b/readconf.c
@@ -30,6 +30,8 @@
30#include <stdio.h> 30#include <stdio.h>
31#include <string.h> 31#include <string.h>
32#include <unistd.h> 32#include <unistd.h>
33#include <pwd.h>
34#include <grp.h>
33#ifdef HAVE_UTIL_H 35#ifdef HAVE_UTIL_H
34#include <util.h> 36#include <util.h>
35#endif 37#endif
@@ -1155,8 +1157,7 @@ read_config_file(const char *filename, const char *host, Options *options,
1155 1157
1156 if (fstat(fileno(f), &sb) == -1) 1158 if (fstat(fileno(f), &sb) == -1)
1157 fatal("fstat %s: %s", filename, strerror(errno)); 1159 fatal("fstat %s: %s", filename, strerror(errno));
1158 if (((sb.st_uid != 0 && sb.st_uid != getuid()) || 1160 if (!secure_permissions(&sb, getuid()))
1159 (sb.st_mode & 022) != 0))
1160 fatal("Bad owner or permissions on %s", filename); 1161 fatal("Bad owner or permissions on %s", filename);
1161 } 1162 }
1162 1163
diff --git a/ssh.1 b/ssh.1
index 62292cc09..05ae6ad8c 100644
--- a/ssh.1
+++ b/ssh.1
@@ -1338,6 +1338,8 @@ The file format and configuration options are described in
1338.Xr ssh_config 5 . 1338.Xr ssh_config 5 .
1339Because of the potential for abuse, this file must have strict permissions: 1339Because of the potential for abuse, this file must have strict permissions:
1340read/write for the user, and not writable by others. 1340read/write for the user, and not writable by others.
1341It may be group-writable provided that the group in question contains only
1342the user.
1341.Pp 1343.Pp
1342.It Pa ~/.ssh/environment 1344.It Pa ~/.ssh/environment
1343Contains additional definitions for environment variables; see 1345Contains additional definitions for environment variables; see
diff --git a/ssh_config.5 b/ssh_config.5
index 694868053..a1e18d286 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -1365,6 +1365,8 @@ The format of this file is described above.
1365This file is used by the SSH client. 1365This file is used by the SSH client.
1366Because of the potential for abuse, this file must have strict permissions: 1366Because of the potential for abuse, this file must have strict permissions:
1367read/write for the user, and not accessible by others. 1367read/write for the user, and not accessible by others.
1368It may be group-writable provided that the group in question contains only
1369the user.
1368.It Pa /etc/ssh/ssh_config 1370.It Pa /etc/ssh/ssh_config
1369Systemwide configuration file. 1371Systemwide configuration file.
1370This file provides defaults for those 1372This file provides defaults for those