diff options
-rw-r--r-- | auth-rhosts.c | 6 | ||||
-rw-r--r-- | auth.c | 9 | ||||
-rw-r--r-- | debian/changelog | 6 | ||||
-rw-r--r-- | debian/patches/debian-config.patch | 2 | ||||
-rw-r--r-- | debian/patches/user-group-modes.patch | 155 | ||||
-rw-r--r-- | misc.c | 27 | ||||
-rw-r--r-- | misc.h | 2 | ||||
-rw-r--r-- | readconf.c | 22 |
8 files changed, 165 insertions, 64 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); |
@@ -385,8 +385,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, | |||
385 | user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); | 385 | user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); |
386 | if (options.strict_modes && | 386 | if (options.strict_modes && |
387 | (stat(user_hostfile, &st) == 0) && | 387 | (stat(user_hostfile, &st) == 0) && |
388 | ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || | 388 | !secure_permissions(&st, pw->pw_uid)) { |
389 | (st.st_mode & 022) != 0)) { | ||
390 | logit("Authentication refused for %.100s: " | 389 | logit("Authentication refused for %.100s: " |
391 | "bad owner or modes for %.200s", | 390 | "bad owner or modes for %.200s", |
392 | pw->pw_name, user_hostfile); | 391 | pw->pw_name, user_hostfile); |
@@ -438,8 +437,7 @@ secure_filename(FILE *f, const char *file, struct passwd *pw, | |||
438 | 437 | ||
439 | /* check the open file to avoid races */ | 438 | /* check the open file to avoid races */ |
440 | if (fstat(fileno(f), &st) < 0 || | 439 | if (fstat(fileno(f), &st) < 0 || |
441 | (st.st_uid != 0 && st.st_uid != uid) || | 440 | !secure_permissions(&st, uid)) { |
442 | (st.st_mode & 022) != 0) { | ||
443 | snprintf(err, errlen, "bad ownership or modes for file %s", | 441 | snprintf(err, errlen, "bad ownership or modes for file %s", |
444 | buf); | 442 | buf); |
445 | return -1; | 443 | return -1; |
@@ -455,8 +453,7 @@ secure_filename(FILE *f, const char *file, struct passwd *pw, | |||
455 | 453 | ||
456 | debug3("secure_filename: checking '%s'", buf); | 454 | debug3("secure_filename: checking '%s'", buf); |
457 | if (stat(buf, &st) < 0 || | 455 | if (stat(buf, &st) < 0 || |
458 | (st.st_uid != 0 && st.st_uid != uid) || | 456 | !secure_permissions(&st, uid)) { |
459 | (st.st_mode & 022) != 0) { | ||
460 | snprintf(err, errlen, | 457 | snprintf(err, errlen, |
461 | "bad ownership or modes for directory %s", buf); | 458 | "bad ownership or modes for directory %s", buf); |
462 | return -1; | 459 | return -1; |
diff --git a/debian/changelog b/debian/changelog index 5790f4643..a27f3b57f 100644 --- a/debian/changelog +++ b/debian/changelog | |||
@@ -4,6 +4,12 @@ openssh (1:5.5p1-4) UNRELEASED; urgency=low | |||
4 | * Add powerpcspe to architecture list for libselinux1-dev build-dependency | 4 | * Add powerpcspe to architecture list for libselinux1-dev build-dependency |
5 | (closes: #579843). | 5 | (closes: #579843). |
6 | 6 | ||
7 | [ Colin Watson ] | ||
8 | * Allow ~/.ssh/authorized_keys and other secure files to be | ||
9 | group-writable, provided that the group in question contains only the | ||
10 | file's owner; this extends a patch previously applied to ~/.ssh/config | ||
11 | (closes: #581919). | ||
12 | |||
7 | -- Colin Watson <cjwatson@debian.org> Tue, 04 May 2010 13:32:17 +0100 | 13 | -- Colin Watson <cjwatson@debian.org> Tue, 04 May 2010 13:32:17 +0100 |
8 | 14 | ||
9 | openssh (1:5.5p1-3) unstable; urgency=low | 15 | openssh (1:5.5p1-3) unstable; urgency=low |
diff --git a/debian/patches/debian-config.patch b/debian/patches/debian-config.patch index a395d43a0..ac77919e6 100644 --- a/debian/patches/debian-config.patch +++ b/debian/patches/debian-config.patch | |||
@@ -24,7 +24,7 @@ Index: b/readconf.c | |||
24 | =================================================================== | 24 | =================================================================== |
25 | --- a/readconf.c | 25 | --- a/readconf.c |
26 | +++ b/readconf.c | 26 | +++ b/readconf.c |
27 | @@ -1152,7 +1152,7 @@ | 27 | @@ -1132,7 +1132,7 @@ |
28 | if (options->forward_x11 == -1) | 28 | if (options->forward_x11 == -1) |
29 | options->forward_x11 = 0; | 29 | options->forward_x11 = 0; |
30 | if (options->forward_x11_trusted == -1) | 30 | if (options->forward_x11_trusted == -1) |
diff --git a/debian/patches/user-group-modes.patch b/debian/patches/user-group-modes.patch index 4d7ebe566..8cf862049 100644 --- a/debian/patches/user-group-modes.patch +++ b/debian/patches/user-group-modes.patch | |||
@@ -1,10 +1,11 @@ | |||
1 | Description: Allow harmless group-writability | 1 | Description: Allow harmless group-writability |
2 | Allow ~/.ssh/config to be group-writable, provided that the group in | 2 | Allow secure files (~/.ssh/config, ~/.ssh/authorized_keys, etc.) to be |
3 | question contains only the file's owner. Rejected upstream for IMO | 3 | group-writable, provided that the group in question contains only the |
4 | incorrect reasons (e.g. a misunderstanding about the contents of | 4 | file's owner. Rejected upstream for IMO incorrect reasons (e.g. a |
5 | gr->gr_mem). Given that per-user groups and umask 002 are the default | 5 | misunderstanding about the contents of gr->gr_mem). Given that |
6 | setup in Debian (for good reasons - this makes operating in setgid | 6 | per-user groups and umask 002 are the default setup in Debian (for good |
7 | directories with other groups much easier), we need to permit this. | 7 | reasons - this makes operating in setgid directories with other groups |
8 | much easier), we need to permit this by default. | ||
8 | Author: Colin Watson <cjwatson@debian.org> | 9 | Author: Colin Watson <cjwatson@debian.org> |
9 | Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1060 | 10 | Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1060 |
10 | Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=314347 | 11 | Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=314347 |
@@ -23,36 +24,13 @@ Index: b/readconf.c | |||
23 | 24 | ||
24 | #include "xmalloc.h" | 25 | #include "xmalloc.h" |
25 | #include "ssh.h" | 26 | #include "ssh.h" |
26 | @@ -1000,11 +1002,30 @@ | 27 | @@ -1003,8 +1005,7 @@ |
27 | |||
28 | if (checkperm) { | ||
29 | struct stat sb; | ||
30 | + int bad_modes = 0; | ||
31 | 28 | ||
32 | if (fstat(fileno(f), &sb) == -1) | 29 | if (fstat(fileno(f), &sb) == -1) |
33 | fatal("fstat %s: %s", filename, strerror(errno)); | 30 | fatal("fstat %s: %s", filename, strerror(errno)); |
34 | - if (((sb.st_uid != 0 && sb.st_uid != getuid()) || | 31 | - if (((sb.st_uid != 0 && sb.st_uid != getuid()) || |
35 | - (sb.st_mode & 022) != 0)) | 32 | - (sb.st_mode & 022) != 0)) |
36 | + if (sb.st_uid != 0 && sb.st_uid != getuid()) | 33 | + if (!secure_permissions(&sb, getuid())) |
37 | + bad_modes = 1; | ||
38 | + if ((sb.st_mode & 020) != 0) { | ||
39 | + /* If the file is group-writable, the group in | ||
40 | + * question must have at most one member, namely the | ||
41 | + * file's owner. | ||
42 | + */ | ||
43 | + struct passwd *pw = getpwuid(sb.st_uid); | ||
44 | + struct group *gr = getgrgid(sb.st_gid); | ||
45 | + if (!pw || !gr) | ||
46 | + bad_modes = 1; | ||
47 | + else if (gr->gr_mem[0]) { | ||
48 | + if (strcmp(pw->pw_name, gr->gr_mem[0]) || | ||
49 | + gr->gr_mem[1]) | ||
50 | + bad_modes = 1; | ||
51 | + } | ||
52 | + } | ||
53 | + if ((sb.st_mode & 002) != 0) | ||
54 | + bad_modes = 1; | ||
55 | + if (bad_modes) | ||
56 | fatal("Bad owner or permissions on %s", filename); | 34 | fatal("Bad owner or permissions on %s", filename); |
57 | } | 35 | } |
58 | 36 | ||
@@ -82,3 +60,118 @@ Index: b/ssh_config.5 | |||
82 | .It Pa /etc/ssh/ssh_config | 60 | .It Pa /etc/ssh/ssh_config |
83 | Systemwide configuration file. | 61 | Systemwide configuration file. |
84 | This file provides defaults for those | 62 | This file provides defaults for those |
63 | Index: b/auth.c | ||
64 | =================================================================== | ||
65 | --- a/auth.c | ||
66 | +++ b/auth.c | ||
67 | @@ -385,8 +385,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 | @@ -438,8 +437,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 | @@ -455,8 +453,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 | @@ -45,8 +45,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 | @@ -638,6 +639,30 @@ | ||
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 & 020) != 0) { | ||
121 | + /* If the file is group-writable, the group in question must | ||
122 | + * have at most one member, namely the file's owner. | ||
123 | + */ | ||
124 | + struct passwd *pw = getpwuid(st->st_uid); | ||
125 | + struct group *gr = getgrgid(st->st_gid); | ||
126 | + if (!pw || !gr) | ||
127 | + return 0; | ||
128 | + else if (gr->gr_mem[0]) { | ||
129 | + if (strcmp(pw->pw_name, gr->gr_mem[0]) || | ||
130 | + gr->gr_mem[1]) | ||
131 | + return 0; | ||
132 | + } | ||
133 | + } | ||
134 | + if ((st->st_mode & 002) != 0) | ||
135 | + return 0; | ||
136 | + return 1; | ||
137 | +} | ||
138 | + | ||
139 | +int | ||
140 | tun_open(int tun, int mode) | ||
141 | { | ||
142 | #if defined(CUSTOM_SYS_TUN_OPEN) | ||
143 | Index: b/misc.h | ||
144 | =================================================================== | ||
145 | --- a/misc.h | ||
146 | +++ b/misc.h | ||
147 | @@ -91,4 +91,6 @@ | ||
148 | int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); | ||
149 | int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *); | ||
150 | |||
151 | +int secure_permissions(struct stat *st, uid_t uid); | ||
152 | + | ||
153 | #endif /* _MISC_H */ | ||
154 | Index: b/auth-rhosts.c | ||
155 | =================================================================== | ||
156 | --- a/auth-rhosts.c | ||
157 | +++ b/auth-rhosts.c | ||
158 | @@ -256,8 +256,7 @@ | ||
159 | return 0; | ||
160 | } | ||
161 | if (options.strict_modes && | ||
162 | - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
163 | - (st.st_mode & 022) != 0)) { | ||
164 | + !secure_permissions(&st, pw->pw_uid)) { | ||
165 | logit("Rhosts authentication refused for %.100s: " | ||
166 | "bad ownership or modes for home directory.", pw->pw_name); | ||
167 | auth_debug_add("Rhosts authentication refused for %.100s: " | ||
168 | @@ -283,8 +282,7 @@ | ||
169 | * allowing access to their account by anyone. | ||
170 | */ | ||
171 | if (options.strict_modes && | ||
172 | - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
173 | - (st.st_mode & 022) != 0)) { | ||
174 | + !secure_permissions(&st, pw->pw_uid)) { | ||
175 | logit("Rhosts authentication refused for %.100s: bad modes for %.200s", | ||
176 | pw->pw_name, buf); | ||
177 | auth_debug_add("Bad file modes for %.200s", buf); | ||
@@ -45,8 +45,9 @@ | |||
45 | #include <netdb.h> | 45 | #include <netdb.h> |
46 | #ifdef HAVE_PATHS_H | 46 | #ifdef HAVE_PATHS_H |
47 | # include <paths.h> | 47 | # include <paths.h> |
48 | #include <pwd.h> | ||
49 | #endif | 48 | #endif |
49 | #include <pwd.h> | ||
50 | #include <grp.h> | ||
50 | #ifdef SSH_TUN_OPENBSD | 51 | #ifdef SSH_TUN_OPENBSD |
51 | #include <net/if.h> | 52 | #include <net/if.h> |
52 | #endif | 53 | #endif |
@@ -638,6 +639,30 @@ read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, | |||
638 | } | 639 | } |
639 | 640 | ||
640 | int | 641 | int |
642 | secure_permissions(struct stat *st, uid_t uid) | ||
643 | { | ||
644 | if (st->st_uid != 0 && st->st_uid != uid) | ||
645 | return 0; | ||
646 | if ((st->st_mode & 020) != 0) { | ||
647 | /* If the file is group-writable, the group in question must | ||
648 | * have at most one member, namely the file's owner. | ||
649 | */ | ||
650 | struct passwd *pw = getpwuid(st->st_uid); | ||
651 | struct group *gr = getgrgid(st->st_gid); | ||
652 | if (!pw || !gr) | ||
653 | return 0; | ||
654 | else if (gr->gr_mem[0]) { | ||
655 | if (strcmp(pw->pw_name, gr->gr_mem[0]) || | ||
656 | gr->gr_mem[1]) | ||
657 | return 0; | ||
658 | } | ||
659 | } | ||
660 | if ((st->st_mode & 002) != 0) | ||
661 | return 0; | ||
662 | return 1; | ||
663 | } | ||
664 | |||
665 | int | ||
641 | tun_open(int tun, int mode) | 666 | tun_open(int tun, int mode) |
642 | { | 667 | { |
643 | #if defined(CUSTOM_SYS_TUN_OPEN) | 668 | #if defined(CUSTOM_SYS_TUN_OPEN) |
@@ -91,4 +91,6 @@ char *read_passphrase(const char *, int); | |||
91 | int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); | 91 | int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); |
92 | int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *); | 92 | int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *); |
93 | 93 | ||
94 | int secure_permissions(struct stat *st, uid_t uid); | ||
95 | |||
94 | #endif /* _MISC_H */ | 96 | #endif /* _MISC_H */ |
diff --git a/readconf.c b/readconf.c index 487c3399b..2a5a706ab 100644 --- a/readconf.c +++ b/readconf.c | |||
@@ -1002,30 +1002,10 @@ read_config_file(const char *filename, const char *host, Options *options, | |||
1002 | 1002 | ||
1003 | if (checkperm) { | 1003 | if (checkperm) { |
1004 | struct stat sb; | 1004 | struct stat sb; |
1005 | int bad_modes = 0; | ||
1006 | 1005 | ||
1007 | if (fstat(fileno(f), &sb) == -1) | 1006 | if (fstat(fileno(f), &sb) == -1) |
1008 | fatal("fstat %s: %s", filename, strerror(errno)); | 1007 | fatal("fstat %s: %s", filename, strerror(errno)); |
1009 | if (sb.st_uid != 0 && sb.st_uid != getuid()) | 1008 | if (!secure_permissions(&sb, getuid())) |
1010 | bad_modes = 1; | ||
1011 | if ((sb.st_mode & 020) != 0) { | ||
1012 | /* If the file is group-writable, the group in | ||
1013 | * question must have at most one member, namely the | ||
1014 | * file's owner. | ||
1015 | */ | ||
1016 | struct passwd *pw = getpwuid(sb.st_uid); | ||
1017 | struct group *gr = getgrgid(sb.st_gid); | ||
1018 | if (!pw || !gr) | ||
1019 | bad_modes = 1; | ||
1020 | else if (gr->gr_mem[0]) { | ||
1021 | if (strcmp(pw->pw_name, gr->gr_mem[0]) || | ||
1022 | gr->gr_mem[1]) | ||
1023 | bad_modes = 1; | ||
1024 | } | ||
1025 | } | ||
1026 | if ((sb.st_mode & 002) != 0) | ||
1027 | bad_modes = 1; | ||
1028 | if (bad_modes) | ||
1029 | fatal("Bad owner or permissions on %s", filename); | 1009 | fatal("Bad owner or permissions on %s", filename); |
1030 | } | 1010 | } |
1031 | 1011 | ||