summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2014-02-09 16:09:58 +0000
committerColin Watson <cjwatson@debian.org>2016-03-21 12:06:15 +0000
commitbf0d87583a842b9e8aaf2a9cd9dbc3e976df2af4 (patch)
tree9928e236b802580595a7abb0b2f2b0d2fab1b75c
parent2b2c5ff34efa305e141130466260ca97f3a429ff (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 ee9e827af..2ff2cffa9 100644
--- a/auth-rhosts.c
+++ b/auth-rhosts.c
@@ -271,8 +271,7 @@ auth_rhosts2_raw(struct passwd *pw, const char *client_user, const char *hostnam
271 return 0; 271 return 0;
272 } 272 }
273 if (options.strict_modes && 273 if (options.strict_modes &&
274 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 274 !secure_permissions(&st, pw->pw_uid)) {
275 (st.st_mode & 022) != 0)) {
276 logit("Rhosts authentication refused for %.100s: " 275 logit("Rhosts authentication refused for %.100s: "
277 "bad ownership or modes for home directory.", pw->pw_name); 276 "bad ownership or modes for home directory.", pw->pw_name);
278 auth_debug_add("Rhosts authentication refused for %.100s: " 277 auth_debug_add("Rhosts authentication refused for %.100s: "
@@ -298,8 +297,7 @@ auth_rhosts2_raw(struct passwd *pw, const char *client_user, const char *hostnam
298 * allowing access to their account by anyone. 297 * allowing access to their account by anyone.
299 */ 298 */
300 if (options.strict_modes && 299 if (options.strict_modes &&
301 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 300 !secure_permissions(&st, pw->pw_uid)) {
302 (st.st_mode & 022) != 0)) {
303 logit("Rhosts authentication refused for %.100s: bad modes for %.200s", 301 logit("Rhosts authentication refused for %.100s: bad modes for %.200s",
304 pw->pw_name, buf); 302 pw->pw_name, buf);
305 auth_debug_add("Bad file modes for %.200s", buf); 303 auth_debug_add("Bad file modes for %.200s", buf);
diff --git a/auth.c b/auth.c
index bd6a026a1..782b7f8fe 100644
--- a/auth.c
+++ b/auth.c
@@ -425,8 +425,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
425 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); 425 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
426 if (options.strict_modes && 426 if (options.strict_modes &&
427 (stat(user_hostfile, &st) == 0) && 427 (stat(user_hostfile, &st) == 0) &&
428 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 428 !secure_permissions(&st, pw->pw_uid)) {
429 (st.st_mode & 022) != 0)) {
430 logit("Authentication refused for %.100s: " 429 logit("Authentication refused for %.100s: "
431 "bad owner or modes for %.200s", 430 "bad owner or modes for %.200s",
432 pw->pw_name, user_hostfile); 431 pw->pw_name, user_hostfile);
@@ -488,8 +487,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
488 snprintf(err, errlen, "%s is not a regular file", buf); 487 snprintf(err, errlen, "%s is not a regular file", buf);
489 return -1; 488 return -1;
490 } 489 }
491 if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || 490 if (!secure_permissions(stp, uid)) {
492 (stp->st_mode & 022) != 0) {
493 snprintf(err, errlen, "bad ownership or modes for file %s", 491 snprintf(err, errlen, "bad ownership or modes for file %s",
494 buf); 492 buf);
495 return -1; 493 return -1;
@@ -504,8 +502,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
504 strlcpy(buf, cp, sizeof(buf)); 502 strlcpy(buf, cp, sizeof(buf));
505 503
506 if (stat(buf, &st) < 0 || 504 if (stat(buf, &st) < 0 ||
507 (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || 505 !secure_permissions(&st, uid)) {
508 (st.st_mode & 022) != 0) {
509 snprintf(err, errlen, 506 snprintf(err, errlen,
510 "bad ownership or modes for directory %s", buf); 507 "bad ownership or modes for directory %s", buf);
511 return -1; 508 return -1;
diff --git a/misc.c b/misc.c
index de7e1facd..5704fa6c4 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 *
@@ -647,6 +649,71 @@ read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
647 return -1; 649 return -1;
648} 650}
649 651
652/*
653 * return 1 if the specified uid is a uid that may own a system directory
654 * otherwise 0.
655 */
656int
657platform_sys_dir_uid(uid_t uid)
658{
659 if (uid == 0)
660 return 1;
661#ifdef PLATFORM_SYS_DIR_UID
662 if (uid == PLATFORM_SYS_DIR_UID)
663 return 1;
664#endif
665 return 0;
666}
667
668int
669secure_permissions(struct stat *st, uid_t uid)
670{
671 if (!platform_sys_dir_uid(st->st_uid) && st->st_uid != uid)
672 return 0;
673 if ((st->st_mode & 002) != 0)
674 return 0;
675 if ((st->st_mode & 020) != 0) {
676 /* If the file is group-writable, the group in question must
677 * have exactly one member, namely the file's owner.
678 * (Zero-member groups are typically used by setgid
679 * binaries, and are unlikely to be suitable.)
680 */
681 struct passwd *pw;
682 struct group *gr;
683 int members = 0;
684
685 gr = getgrgid(st->st_gid);
686 if (!gr)
687 return 0;
688
689 /* Check primary group memberships. */
690 while ((pw = getpwent()) != NULL) {
691 if (pw->pw_gid == gr->gr_gid) {
692 ++members;
693 if (pw->pw_uid != uid)
694 return 0;
695 }
696 }
697 endpwent();
698
699 pw = getpwuid(st->st_uid);
700 if (!pw)
701 return 0;
702
703 /* Check supplementary group memberships. */
704 if (gr->gr_mem[0]) {
705 ++members;
706 if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
707 gr->gr_mem[1])
708 return 0;
709 }
710
711 if (!members)
712 return 0;
713 }
714 return 1;
715}
716
650int 717int
651tun_open(int tun, int mode) 718tun_open(int tun, int mode)
652{ 719{
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 fde6b418a..cc1a6338d 100644
--- a/readconf.c
+++ b/readconf.c
@@ -39,6 +39,8 @@
39#include <stdio.h> 39#include <stdio.h>
40#include <string.h> 40#include <string.h>
41#include <unistd.h> 41#include <unistd.h>
42#include <pwd.h>
43#include <grp.h>
42#ifdef HAVE_UTIL_H 44#ifdef HAVE_UTIL_H
43#include <util.h> 45#include <util.h>
44#endif 46#endif
@@ -1626,8 +1628,7 @@ read_config_file(const char *filename, struct passwd *pw, const char *host,
1626 1628
1627 if (fstat(fileno(f), &sb) == -1) 1629 if (fstat(fileno(f), &sb) == -1)
1628 fatal("fstat %s: %s", filename, strerror(errno)); 1630 fatal("fstat %s: %s", filename, strerror(errno));
1629 if (((sb.st_uid != 0 && sb.st_uid != getuid()) || 1631 if (!secure_permissions(&sb, getuid()))
1630 (sb.st_mode & 022) != 0))
1631 fatal("Bad owner or permissions on %s", filename); 1632 fatal("Bad owner or permissions on %s", filename);
1632 } 1633 }
1633 1634
diff --git a/ssh.1 b/ssh.1
index cc5334338..feb0e8919 100644
--- a/ssh.1
+++ b/ssh.1
@@ -1459,6 +1459,8 @@ The file format and configuration options are described in
1459.Xr ssh_config 5 . 1459.Xr ssh_config 5 .
1460Because of the potential for abuse, this file must have strict permissions: 1460Because of the potential for abuse, this file must have strict permissions:
1461read/write for the user, and not writable by others. 1461read/write for the user, and not writable by others.
1462It may be group-writable provided that the group in question contains only
1463the user.
1462.Pp 1464.Pp
1463.It Pa ~/.ssh/environment 1465.It Pa ~/.ssh/environment
1464Contains additional definitions for environment variables; see 1466Contains additional definitions for environment variables; see
diff --git a/ssh_config.5 b/ssh_config.5
index bbf638b53..ab8f27179 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -1830,6 +1830,8 @@ The format of this file is described above.
1830This file is used by the SSH client. 1830This file is used by the SSH client.
1831Because of the potential for abuse, this file must have strict permissions: 1831Because of the potential for abuse, this file must have strict permissions:
1832read/write for the user, and not accessible by others. 1832read/write for the user, and not accessible by others.
1833It may be group-writable provided that the group in question contains only
1834the user.
1833.It Pa /etc/ssh/ssh_config 1835.It Pa /etc/ssh/ssh_config
1834Systemwide configuration file. 1836Systemwide configuration file.
1835This file provides defaults for those 1837This file provides defaults for those