1:5.8.1p1-3
[openssh.git] / debian / patches / user-group-modes.patch
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
13
14 Index: b/readconf.c
15 ===================================================================
16 --- a/readconf.c
17 +++ b/readconf.c
18 @@ -30,6 +30,8 @@
19  #include <stdio.h>
20  #include <string.h>
21  #include <unistd.h>
22 +#include <pwd.h>
23 +#include <grp.h>
24  
25  #include "xmalloc.h"
26  #include "ssh.h"
27 @@ -1085,8 +1087,7 @@
28  
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);
35         }
36  
37 Index: b/ssh.1
38 ===================================================================
39 --- a/ssh.1
40 +++ b/ssh.1
41 @@ -1293,6 +1293,8 @@
42  .Xr ssh_config 5 .
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
46 +the user.
47  .Pp
48  .It Pa ~/.ssh/environment
49  Contains additional definitions for environment variables; see
50 Index: b/ssh_config.5
51 ===================================================================
52 --- a/ssh_config.5
53 +++ b/ssh_config.5
54 @@ -1299,6 +1299,8 @@
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
59 +the user.
60  .It Pa /etc/ssh/ssh_config
61  Systemwide configuration file.
62  This file provides defaults for those
63 Index: b/auth.c
64 ===================================================================
65 --- a/auth.c
66 +++ b/auth.c
67 @@ -392,8 +392,7 @@
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);
77 @@ -454,8 +453,7 @@
78  
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",
85                     buf);
86                 return -1;
87 @@ -471,8 +469,7 @@
88  
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)) {
94                         snprintf(err, errlen,
95                             "bad ownership or modes for directory %s", buf);
96                         return -1;
97 Index: b/misc.c
98 ===================================================================
99 --- a/misc.c
100 +++ b/misc.c
101 @@ -48,8 +48,9 @@
102  #include <netdb.h>
103  #ifdef HAVE_PATHS_H
104  # include <paths.h>
105 -#include <pwd.h>
106  #endif
107 +#include <pwd.h>
108 +#include <grp.h>
109  #ifdef SSH_TUN_OPENBSD
110  #include <net/if.h>
111  #endif
112 @@ -642,6 +643,55 @@
113  }
114  
115  int
116 +secure_permissions(struct stat *st, uid_t uid)
117 +{
118 +       if (st->st_uid != 0 && st->st_uid != uid)
119 +               return 0;
120 +       if ((st->st_mode & 002) != 0)
121 +               return 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.)
127 +                */
128 +               struct passwd *pw;
129 +               struct group *gr;
130 +               int members = 0;
131 +
132 +               gr = getgrgid(st->st_gid);
133 +               if (!gr)
134 +                       return 0;
135 +
136 +               /* Check primary group memberships. */
137 +               while ((pw = getpwent()) != NULL) {
138 +                       if (pw->pw_gid == gr->gr_gid) {
139 +                               ++members;
140 +                               if (pw->pw_uid != uid)
141 +                                       return 0;
142 +                       }
143 +               }
144 +               endpwent();
145 +
146 +               pw = getpwuid(st->st_uid);
147 +               if (!pw)
148 +                       return 0;
149 +
150 +               /* Check supplementary group memberships. */
151 +               if (gr->gr_mem[0]) {
152 +                       ++members;
153 +                       if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
154 +                           gr->gr_mem[1])
155 +                               return 0;
156 +               }
157 +
158 +               if (!members)
159 +                       return 0;
160 +       }
161 +       return 1;
162 +}
163 +
164 +int
165  tun_open(int tun, int mode)
166  {
167  #if defined(CUSTOM_SYS_TUN_OPEN)
168 Index: b/misc.h
169 ===================================================================
170 --- a/misc.h
171 +++ b/misc.h
172 @@ -102,4 +102,6 @@
173  int     ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
174  int     read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
175  
176 +int     secure_permissions(struct stat *st, uid_t uid);
177 +
178  #endif /* _MISC_H */
179 Index: b/auth-rhosts.c
180 ===================================================================
181 --- a/auth-rhosts.c
182 +++ b/auth-rhosts.c
183 @@ -256,8 +256,7 @@
184                 return 0;
185         }
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: "
193 @@ -283,8 +282,7 @@
194                  * allowing access to their account by anyone.
195                  */
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",
201                             pw->pw_name, buf);
202                         auth_debug_add("Bad file modes for %.200s", buf);