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: 2022-02-23

Patch-Name: user-group-modes.patch

Gbp-Pq: Name user-group-modes.patch
This commit is contained in:
Colin Watson 2014-02-09 16:09:58 +00:00 committed by lixiuwen
parent 1029016475
commit f557b572cf
7 changed files with 62 additions and 13 deletions

View File

@ -266,8 +266,7 @@ auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname,
return 0; return 0;
} }
if (options.strict_modes && if (options.strict_modes &&
((st.st_uid != 0 && st.st_uid != pw->pw_uid) || !secure_permissions(&st, pw->pw_uid)) {
(st.st_mode & 022) != 0)) {
logit("Rhosts authentication refused for %.100s: " logit("Rhosts authentication refused for %.100s: "
"bad ownership or modes for home directory.", pw->pw_name); "bad ownership or modes for home directory.", pw->pw_name);
auth_debug_add("Rhosts authentication refused for %.100s: " auth_debug_add("Rhosts authentication refused for %.100s: "
@ -296,8 +295,7 @@ auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname,
* allowing access to their account by anyone. * allowing access to their account by anyone.
*/ */
if (options.strict_modes && if (options.strict_modes &&
((st.st_uid != 0 && st.st_uid != pw->pw_uid) || !secure_permissions(&st, pw->pw_uid)) {
(st.st_mode & 022) != 0)) {
logit("Rhosts authentication refused for %.100s: " logit("Rhosts authentication refused for %.100s: "
"bad modes for %.200s", pw->pw_name, path); "bad modes for %.200s", pw->pw_name, path);
auth_debug_add("Bad file modes for %.200s", path); auth_debug_add("Bad file modes for %.200s", path);

3
auth.c
View File

@ -431,8 +431,7 @@ check_key_in_hostfiles(struct passwd *pw, struct sshkey *key, const char *host,
user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
if (options.strict_modes && if (options.strict_modes &&
(stat(user_hostfile, &st) == 0) && (stat(user_hostfile, &st) == 0) &&
((st.st_uid != 0 && st.st_uid != pw->pw_uid) || !secure_permissions(&st, pw->pw_uid)) {
(st.st_mode & 022) != 0)) {
logit("Authentication refused for %.100s: " logit("Authentication refused for %.100s: "
"bad owner or modes for %.200s", "bad owner or modes for %.200s",
pw->pw_name, user_hostfile); pw->pw_name, user_hostfile);

57
misc.c
View File

@ -62,9 +62,9 @@
#include <netdb.h> #include <netdb.h>
#ifdef HAVE_PATHS_H #ifdef HAVE_PATHS_H
# include <paths.h> # include <paths.h>
#endif
#include <pwd.h> #include <pwd.h>
#include <grp.h> #include <grp.h>
#endif
#ifdef SSH_TUN_OPENBSD #ifdef SSH_TUN_OPENBSD
#include <net/if.h> #include <net/if.h>
#endif #endif
@ -1414,6 +1414,55 @@ percent_dollar_expand(const char *string, ...)
return ret; return ret;
} }
int
secure_permissions(struct stat *st, uid_t uid)
{
if (!platform_sys_dir_uid(st->st_uid) && st->st_uid != uid)
return 0;
if ((st->st_mode & 002) != 0)
return 0;
if ((st->st_mode & 020) != 0) {
/* If the file is group-writable, the group in question must
* have exactly one member, namely the file's owner.
* (Zero-member groups are typically used by setgid
* binaries, and are unlikely to be suitable.)
*/
struct passwd *pw;
struct group *gr;
int members = 0;
gr = getgrgid(st->st_gid);
if (!gr)
return 0;
/* Check primary group memberships. */
while ((pw = getpwent()) != NULL) {
if (pw->pw_gid == gr->gr_gid) {
++members;
if (pw->pw_uid != uid)
return 0;
}
}
endpwent();
pw = getpwuid(st->st_uid);
if (!pw)
return 0;
/* Check supplementary group memberships. */
if (gr->gr_mem[0]) {
++members;
if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
gr->gr_mem[1])
return 0;
}
if (!members)
return 0;
}
return 1;
}
int int
tun_open(int tun, int mode, char **ifname) tun_open(int tun, int mode, char **ifname)
{ {
@ -2223,8 +2272,7 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
snprintf(err, errlen, "%s is not a regular file", buf); snprintf(err, errlen, "%s is not a regular file", buf);
return -1; return -1;
} }
if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || if (!secure_permissions(stp, uid)) {
(stp->st_mode & 022) != 0) {
snprintf(err, errlen, "bad ownership or modes for file %s", snprintf(err, errlen, "bad ownership or modes for file %s",
buf); buf);
return -1; return -1;
@ -2239,8 +2287,7 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
strlcpy(buf, cp, sizeof(buf)); strlcpy(buf, cp, sizeof(buf));
if (stat(buf, &st) == -1 || if (stat(buf, &st) == -1 ||
(!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || !secure_permissions(&st, uid)) {
(st.st_mode & 022) != 0) {
snprintf(err, errlen, snprintf(err, errlen,
"bad ownership or modes for directory %s", buf); "bad ownership or modes for directory %s", buf);
return -1; return -1;

2
misc.h
View File

@ -237,6 +237,8 @@ struct notifier_ctx *notify_start(int, const char *, ...)
void notify_complete(struct notifier_ctx *, const char *, ...) void notify_complete(struct notifier_ctx *, const char *, ...)
__attribute__((format(printf, 2, 3))); __attribute__((format(printf, 2, 3)));
int secure_permissions(struct stat *st, uid_t uid);
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))

View File

@ -2481,8 +2481,7 @@ read_config_file_depth(const char *filename, struct passwd *pw,
if (fstat(fileno(f), &sb) == -1) if (fstat(fileno(f), &sb) == -1)
fatal("fstat %s: %s", filename, strerror(errno)); fatal("fstat %s: %s", filename, strerror(errno));
if (((sb.st_uid != 0 && sb.st_uid != getuid()) || if (!secure_permissions(&sb, getuid()))
(sb.st_mode & 022) != 0))
fatal("Bad owner or permissions on %s", filename); fatal("Bad owner or permissions on %s", filename);
} }

2
ssh.1
View File

@ -1577,6 +1577,8 @@ The file format and configuration options are described in
.Xr ssh_config 5 . .Xr ssh_config 5 .
Because of the potential for abuse, this file must have strict permissions: Because of the potential for abuse, this file must have strict permissions:
read/write for the user, and not writable by others. read/write for the user, and not writable by others.
It may be group-writable provided that the group in question contains only
the user.
.Pp .Pp
.It Pa ~/.ssh/environment .It Pa ~/.ssh/environment
Contains additional definitions for environment variables; see Contains additional definitions for environment variables; see

View File

@ -2395,6 +2395,8 @@ The format of this file is described above.
This file is used by the SSH client. This file is used by the SSH client.
Because of the potential for abuse, this file must have strict permissions: Because of the potential for abuse, this file must have strict permissions:
read/write for the user, and not writable by others. read/write for the user, and not writable by others.
It may be group-writable provided that the group in question contains only
the user.
.It Pa /etc/ssh/ssh_config .It Pa /etc/ssh/ssh_config
Systemwide configuration file. Systemwide configuration file.
This file provides defaults for those This file provides defaults for those