summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2014-02-09 16:09:58 +0000
committerColin Watson <cjwatson@debian.org>2014-10-07 14:27:20 +0100
commit28ea747089f695e58a476a2849133402d4f86b92 (patch)
treefe010fa255dcd25e221fe76ef552aad3eb5da760
parentf51fe0c55e54c12db952624e980d18f39c41e581 (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 b5bedee8d..11fcca643 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 5e60682ce..18de51a29 100644
--- a/auth.c
+++ b/auth.c
@@ -421,8 +421,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
421 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); 421 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
422 if (options.strict_modes && 422 if (options.strict_modes &&
423 (stat(user_hostfile, &st) == 0) && 423 (stat(user_hostfile, &st) == 0) &&
424 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 424 !secure_permissions(&st, pw->pw_uid)) {
425 (st.st_mode & 022) != 0)) {
426 logit("Authentication refused for %.100s: " 425 logit("Authentication refused for %.100s: "
427 "bad owner or modes for %.200s", 426 "bad owner or modes for %.200s",
428 pw->pw_name, user_hostfile); 427 pw->pw_name, user_hostfile);
@@ -484,8 +483,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
484 snprintf(err, errlen, "%s is not a regular file", buf); 483 snprintf(err, errlen, "%s is not a regular file", buf);
485 return -1; 484 return -1;
486 } 485 }
487 if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || 486 if (!secure_permissions(stp, uid)) {
488 (stp->st_mode & 022) != 0) {
489 snprintf(err, errlen, "bad ownership or modes for file %s", 487 snprintf(err, errlen, "bad ownership or modes for file %s",
490 buf); 488 buf);
491 return -1; 489 return -1;
@@ -500,8 +498,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
500 strlcpy(buf, cp, sizeof(buf)); 498 strlcpy(buf, cp, sizeof(buf));
501 499
502 if (stat(buf, &st) < 0 || 500 if (stat(buf, &st) < 0 ||
503 (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || 501 !secure_permissions(&st, uid)) {
504 (st.st_mode & 022) != 0) {
505 snprintf(err, errlen, 502 snprintf(err, errlen,
506 "bad ownership or modes for directory %s", buf); 503 "bad ownership or modes for directory %s", buf);
507 return -1; 504 return -1;
diff --git a/misc.c b/misc.c
index 94b05b08e..c25ccd80e 100644
--- a/misc.c
+++ b/misc.c
@@ -50,8 +50,9 @@
50#include <netdb.h> 50#include <netdb.h>
51#ifdef HAVE_PATHS_H 51#ifdef HAVE_PATHS_H
52# include <paths.h> 52# include <paths.h>
53#include <pwd.h>
54#endif 53#endif
54#include <pwd.h>
55#include <grp.h>
55#ifdef SSH_TUN_OPENBSD 56#ifdef SSH_TUN_OPENBSD
56#include <net/if.h> 57#include <net/if.h>
57#endif 58#endif
@@ -60,6 +61,7 @@
60#include "misc.h" 61#include "misc.h"
61#include "log.h" 62#include "log.h"
62#include "ssh.h" 63#include "ssh.h"
64#include "platform.h"
63 65
64/* remove newline at end of string */ 66/* remove newline at end of string */
65char * 67char *
@@ -644,6 +646,71 @@ read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
644 return -1; 646 return -1;
645} 647}
646 648
649/*
650 * return 1 if the specified uid is a uid that may own a system directory
651 * otherwise 0.
652 */
653int
654platform_sys_dir_uid(uid_t uid)
655{
656 if (uid == 0)
657 return 1;
658#ifdef PLATFORM_SYS_DIR_UID
659 if (uid == PLATFORM_SYS_DIR_UID)
660 return 1;
661#endif
662 return 0;
663}
664
665int
666secure_permissions(struct stat *st, uid_t uid)
667{
668 if (!platform_sys_dir_uid(st->st_uid) && st->st_uid != uid)
669 return 0;
670 if ((st->st_mode & 002) != 0)
671 return 0;
672 if ((st->st_mode & 020) != 0) {
673 /* If the file is group-writable, the group in question must
674 * have exactly one member, namely the file's owner.
675 * (Zero-member groups are typically used by setgid
676 * binaries, and are unlikely to be suitable.)
677 */
678 struct passwd *pw;
679 struct group *gr;
680 int members = 0;
681
682 gr = getgrgid(st->st_gid);
683 if (!gr)
684 return 0;
685
686 /* Check primary group memberships. */
687 while ((pw = getpwent()) != NULL) {
688 if (pw->pw_gid == gr->gr_gid) {
689 ++members;
690 if (pw->pw_uid != uid)
691 return 0;
692 }
693 }
694 endpwent();
695
696 pw = getpwuid(st->st_uid);
697 if (!pw)
698 return 0;
699
700 /* Check supplementary group memberships. */
701 if (gr->gr_mem[0]) {
702 ++members;
703 if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
704 gr->gr_mem[1])
705 return 0;
706 }
707
708 if (!members)
709 return 0;
710 }
711 return 1;
712}
713
647int 714int
648tun_open(int tun, int mode) 715tun_open(int tun, int mode)
649{ 716{
diff --git a/misc.h b/misc.h
index 374c33ce1..89e1f75d3 100644
--- a/misc.h
+++ b/misc.h
@@ -135,4 +135,6 @@ char *read_passphrase(const char *, int);
135int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); 135int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
136int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *); 136int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
137 137
138int secure_permissions(struct stat *st, uid_t uid);
139
138#endif /* _MISC_H */ 140#endif /* _MISC_H */
diff --git a/platform.c b/platform.c
index f35ec39a8..9a23e6e3e 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 337818c63..0648867e8 100644
--- a/readconf.c
+++ b/readconf.c
@@ -38,6 +38,8 @@
38#include <stdio.h> 38#include <stdio.h>
39#include <string.h> 39#include <string.h>
40#include <unistd.h> 40#include <unistd.h>
41#include <pwd.h>
42#include <grp.h>
41#ifdef HAVE_UTIL_H 43#ifdef HAVE_UTIL_H
42#include <util.h> 44#include <util.h>
43#endif 45#endif
@@ -1516,8 +1518,7 @@ read_config_file(const char *filename, struct passwd *pw, const char *host,
1516 1518
1517 if (fstat(fileno(f), &sb) == -1) 1519 if (fstat(fileno(f), &sb) == -1)
1518 fatal("fstat %s: %s", filename, strerror(errno)); 1520 fatal("fstat %s: %s", filename, strerror(errno));
1519 if (((sb.st_uid != 0 && sb.st_uid != getuid()) || 1521 if (!secure_permissions(&sb, getuid()))
1520 (sb.st_mode & 022) != 0))
1521 fatal("Bad owner or permissions on %s", filename); 1522 fatal("Bad owner or permissions on %s", filename);
1522 } 1523 }
1523 1524
diff --git a/ssh.1 b/ssh.1
index fa5cfb2c2..7f6ab77eb 100644
--- a/ssh.1
+++ b/ssh.1
@@ -1342,6 +1342,8 @@ The file format and configuration options are described in
1342.Xr ssh_config 5 . 1342.Xr ssh_config 5 .
1343Because of the potential for abuse, this file must have strict permissions: 1343Because of the potential for abuse, this file must have strict permissions:
1344read/write for the user, and not writable by others. 1344read/write for the user, and not writable by others.
1345It may be group-writable provided that the group in question contains only
1346the user.
1345.Pp 1347.Pp
1346.It Pa ~/.ssh/environment 1348.It Pa ~/.ssh/environment
1347Contains additional definitions for environment variables; see 1349Contains additional definitions for environment variables; see
diff --git a/ssh_config.5 b/ssh_config.5
index ea92ea80d..d68b45a79 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -1587,6 +1587,8 @@ The format of this file is described above.
1587This file is used by the SSH client. 1587This file is used by the SSH client.
1588Because of the potential for abuse, this file must have strict permissions: 1588Because of the potential for abuse, this file must have strict permissions:
1589read/write for the user, and not accessible by others. 1589read/write for the user, and not accessible by others.
1590It may be group-writable provided that the group in question contains only
1591the user.
1590.It Pa /etc/ssh/ssh_config 1592.It Pa /etc/ssh/ssh_config
1591Systemwide configuration file. 1593Systemwide configuration file.
1592This file provides defaults for those 1594This file provides defaults for those