1 Description: Allow harmless group-writability
2 Allow secure files (~/.ssh/config, ~/.ssh/authorized_keys, etc.) to be
3 group-writable, provided that the group in question contains only the
4 file's owner. Rejected upstream for IMO incorrect reasons (e.g. a
5 misunderstanding about the contents of gr->gr_mem). Given that
6 per-user groups and umask 002 are the default setup in Debian (for good
7 reasons - this makes operating in setgid directories with other groups
8 much easier), we need to permit this by default.
9 Author: Colin Watson <cjwatson@debian.org>
10 Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1060
11 Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=314347
12 Last-Update: 2010-02-27
15 ===================================================================
29 if (fstat(fileno(f), &sb) == -1)
30 fatal("fstat %s: %s", filename, strerror(errno));
31 - if (((sb.st_uid != 0 && sb.st_uid != getuid()) ||
32 - (sb.st_mode & 022) != 0))
33 + if (!secure_permissions(&sb, getuid()))
34 fatal("Bad owner or permissions on %s", filename);
38 ===================================================================
43 Because of the potential for abuse, this file must have strict permissions:
44 read/write for the user, and not accessible by others.
45 +It may be group-writable provided that the group in question contains only
48 .It Pa ~/.ssh/environment
49 Contains additional definitions for environment variables; see
51 ===================================================================
55 This file is used by the SSH client.
56 Because of the potential for abuse, this file must have strict permissions:
57 read/write for the user, and not accessible by others.
58 +It may be group-writable provided that the group in question contains only
60 .It Pa /etc/ssh/ssh_config
61 Systemwide configuration file.
62 This file provides defaults for those
64 ===================================================================
68 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
69 if (options.strict_modes &&
70 (stat(user_hostfile, &st) == 0) &&
71 - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
72 - (st.st_mode & 022) != 0)) {
73 + !secure_permissions(&st, pw->pw_uid)) {
74 logit("Authentication refused for %.100s: "
75 "bad owner or modes for %.200s",
76 pw->pw_name, user_hostfile);
79 /* check the open file to avoid races */
80 if (fstat(fileno(f), &st) < 0 ||
81 - (st.st_uid != 0 && st.st_uid != uid) ||
82 - (st.st_mode & 022) != 0) {
83 + !secure_permissions(&st, uid)) {
84 snprintf(err, errlen, "bad ownership or modes for file %s",
89 debug3("secure_filename: checking '%s'", buf);
90 if (stat(buf, &st) < 0 ||
91 - (st.st_uid != 0 && st.st_uid != uid) ||
92 - (st.st_mode & 022) != 0) {
93 + !secure_permissions(&st, uid)) {
95 "bad ownership or modes for directory %s", buf);
98 ===================================================================
109 #ifdef SSH_TUN_OPENBSD
116 +secure_permissions(struct stat *st, uid_t uid)
118 + if (st->st_uid != 0 && st->st_uid != uid)
120 + if ((st->st_mode & 002) != 0)
122 + if ((st->st_mode & 020) != 0) {
123 + /* If the file is group-writable, the group in question must
124 + * have exactly one member, namely the file's owner.
125 + * (Zero-member groups are typically used by setgid
126 + * binaries, and are unlikely to be suitable.)
132 + gr = getgrgid(st->st_gid);
136 + /* Check primary group memberships. */
137 + while ((pw = getpwent()) != NULL) {
138 + if (pw->pw_gid == gr->gr_gid) {
140 + if (pw->pw_uid != uid)
146 + pw = getpwuid(st->st_uid);
150 + /* Check supplementary group memberships. */
151 + if (gr->gr_mem[0]) {
153 + if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
165 tun_open(int tun, int mode)
167 #if defined(CUSTOM_SYS_TUN_OPEN)
169 ===================================================================
173 int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
174 int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
176 +int secure_permissions(struct stat *st, uid_t uid);
179 Index: b/auth-rhosts.c
180 ===================================================================
186 if (options.strict_modes &&
187 - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
188 - (st.st_mode & 022) != 0)) {
189 + !secure_permissions(&st, pw->pw_uid)) {
190 logit("Rhosts authentication refused for %.100s: "
191 "bad ownership or modes for home directory.", pw->pw_name);
192 auth_debug_add("Rhosts authentication refused for %.100s: "
194 * allowing access to their account by anyone.
196 if (options.strict_modes &&
197 - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
198 - (st.st_mode & 022) != 0)) {
199 + !secure_permissions(&st, pw->pw_uid)) {
200 logit("Rhosts authentication refused for %.100s: bad modes for %.200s",
202 auth_debug_add("Bad file modes for %.200s", buf);