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 16:18:08 +0000
commit7016a7e8a6b854833132db253fd5e392984bd4ea (patch)
treee678b1f8ad646f806a81ca506c123ad4e106c543
parentcfae2bfa1e95cbb6c7a9799f13b82e8e804ca869 (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 6662e9a75..7f6c6c8ad 100644
--- a/auth.c
+++ b/auth.c
@@ -408,8 +408,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
408 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); 408 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
409 if (options.strict_modes && 409 if (options.strict_modes &&
410 (stat(user_hostfile, &st) == 0) && 410 (stat(user_hostfile, &st) == 0) &&
411 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 411 !secure_permissions(&st, pw->pw_uid)) {
412 (st.st_mode & 022) != 0)) {
413 logit("Authentication refused for %.100s: " 412 logit("Authentication refused for %.100s: "
414 "bad owner or modes for %.200s", 413 "bad owner or modes for %.200s",
415 pw->pw_name, user_hostfile); 414 pw->pw_name, user_hostfile);
@@ -471,8 +470,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
471 snprintf(err, errlen, "%s is not a regular file", buf); 470 snprintf(err, errlen, "%s is not a regular file", buf);
472 return -1; 471 return -1;
473 } 472 }
474 if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || 473 if (!secure_permissions(stp, uid)) {
475 (stp->st_mode & 022) != 0) {
476 snprintf(err, errlen, "bad ownership or modes for file %s", 474 snprintf(err, errlen, "bad ownership or modes for file %s",
477 buf); 475 buf);
478 return -1; 476 return -1;
@@ -487,8 +485,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
487 strlcpy(buf, cp, sizeof(buf)); 485 strlcpy(buf, cp, sizeof(buf));
488 486
489 if (stat(buf, &st) < 0 || 487 if (stat(buf, &st) < 0 ||
490 (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || 488 !secure_permissions(&st, uid)) {
491 (st.st_mode & 022) != 0) {
492 snprintf(err, errlen, 489 snprintf(err, errlen,
493 "bad ownership or modes for directory %s", buf); 490 "bad ownership or modes for directory %s", buf);
494 return -1; 491 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 2dcbf3187..389de7d60 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
@@ -1160,8 +1162,7 @@ read_config_file(const char *filename, const char *host, Options *options,
1160 1162
1161 if (fstat(fileno(f), &sb) == -1) 1163 if (fstat(fileno(f), &sb) == -1)
1162 fatal("fstat %s: %s", filename, strerror(errno)); 1164 fatal("fstat %s: %s", filename, strerror(errno));
1163 if (((sb.st_uid != 0 && sb.st_uid != getuid()) || 1165 if (!secure_permissions(&sb, getuid()))
1164 (sb.st_mode & 022) != 0))
1165 fatal("Bad owner or permissions on %s", filename); 1166 fatal("Bad owner or permissions on %s", filename);
1166 } 1167 }
1167 1168
diff --git a/ssh.1 b/ssh.1
index 66a7007d7..0b38ae188 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 135d83376..1497cfc11 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -1382,6 +1382,8 @@ The format of this file is described above.
1382This file is used by the SSH client. 1382This file is used by the SSH client.
1383Because of the potential for abuse, this file must have strict permissions: 1383Because of the potential for abuse, this file must have strict permissions:
1384read/write for the user, and not accessible by others. 1384read/write for the user, and not accessible by others.
1385It may be group-writable provided that the group in question contains only
1386the user.
1385.It Pa /etc/ssh/ssh_config 1387.It Pa /etc/ssh/ssh_config
1386Systemwide configuration file. 1388Systemwide configuration file.
1387This file provides defaults for those 1389This file provides defaults for those