Attached (and inline) is a patch to add the following config options: ControlBindMask ControlAllowUsers ControlAllowGroups ControlDenyUsers ControlDenyGroups It pulls the peer credential check from client_process_control() in ssh.c, and expounds upon it in a new function, client_control_grant(). Supplemental groups are not checked in this patch. I didn't feel comfortable taking a shot at defining the semantics of how supplemental groups would work. groupaccess.c probably has all the code necessary to employ supplemental group checking. The patch includes documentation additions, as well. The diff is against Portable OpenSSH 4.2. That's what we have in our tree at the moment. Please send any corrections or complaints my way and I'll refactor the patch as necessary (e.g. if the patch doesn't apply to 4.3 or against OpenBSD trunk). For posterity, let it be known that this work was funded by Barracuda Networks, Inc. - Bill Index: scp.0 ==================================================================--- scp.0 (revision 15802) +++ scp.0 (revision 15803) @@ -70,6 +70,11 @@ ConnectTimeout ControlMaster ControlPath + ControlBindMask + ControlAllowUsers + ControlAllowGroups + ControlDenyUsers + ControlDenyGroups GlobalKnownHostsFile GSSAPIAuthentication GSSAPIDelegateCredentials Index: scp.1 ==================================================================--- scp.1 (revision 15802) +++ scp.1 (revision 15803) @@ -130,6 +130,11 @@ .It ConnectTimeout .It ControlMaster .It ControlPath +.It ControlBindMask +.It ControlAllowUsers +.It ControlAllowGroups +.It ControlDenyUsers +.It ControlDenyGroups .It GlobalKnownHostsFile .It GSSAPIAuthentication .It GSSAPIDelegateCredentials Index: ssh.0 ==================================================================--- ssh.0 (revision 15802) +++ ssh.0 (revision 15803) @@ -392,6 +392,11 @@ ConnectTimeout ControlMaster ControlPath + ControlBindMask + ControlAllowUsers + ControlAllowGroups + ControlDenyUsers + ControlDenyGroups DynamicForward EscapeChar ForwardAgent Index: ssh.1 ==================================================================--- ssh.1 (revision 15802) +++ ssh.1 (revision 15803) @@ -691,6 +691,11 @@ .It ConnectTimeout .It ControlMaster .It ControlPath +.It ControlBindMask +.It ControlAllowUsers +.It ControlAllowGroups +.It ControlDenyUsers +.It ControlDenyGroups .It DynamicForward .It EscapeChar .It ForwardAgent Index: sftp.0 ==================================================================--- sftp.0 (revision 15802) +++ sftp.0 (revision 15803) @@ -74,6 +74,11 @@ ConnectTimeout ControlMaster ControlPath + ControlBindMask + ControlAllowUsers + ControlAllowGroups + ControlDenyUsers + ControlDenyGroups GlobalKnownHostsFile GSSAPIAuthentication GSSAPIDelegateCredentials Index: sftp.1 ==================================================================--- sftp.1 (revision 15802) +++ sftp.1 (revision 15803) @@ -158,6 +158,11 @@ .It ConnectTimeout .It ControlMaster .It ControlPath +.It ControlBindMask +.It ControlAllowUsers +.It ControlAllowGroups +.It ControlDenyUsers +.It ControlDenyGroups .It GlobalKnownHostsFile .It GSSAPIAuthentication .It GSSAPIDelegateCredentials Index: ssh.c ==================================================================--- ssh.c (revision 15802) +++ ssh.c (revision 15803) @@ -1012,7 +1012,7 @@ if ((control_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) fatal("%s socket(): %s\n", __func__, strerror(errno)); - old_umask = umask(0177); + old_umask = umask(options.control_bind_mask); if (bind(control_fd, (struct sockaddr*)&addr, addr_len) == -1) { control_fd = -1; if (errno == EINVAL || errno == EADDRINUSE) Index: clientloop.c ==================================================================--- clientloop.c (revision 15802) +++ clientloop.c (revision 15803) @@ -675,6 +675,84 @@ xfree(cctx); } + +static int +client_control_grant(int client_fd) +{ + struct passwd *epw = 0; + struct group *egr = 0; + char euidstr[48]; /* Sufficient for 2^128 in decimal ascii */ + char egidstr[48]; /* Sufficient for 2^128 in decimal ascii */ + uid_t euid; + gid_t egid; + u_int i; + + if (getpeereid(client_fd, &euid, &egid) < 0) { + error("%s getpeereid failed: %s", __func__, strerror(errno)); + return -1; + } + + if ((euid == 0) || (getuid() == euid)) + return 1; /* Short circuit. */ + + if ((int)sizeof euidstr <= snprintf(euidstr, sizeof euidstr, "%lu", (u_long)euid)) { + error("%s uid too high", __func__); + return -1; + } + + if ((int)sizeof egidstr <= snprintf(egidstr, sizeof egidstr, "%lu", (u_long)egid)) { + error("%s gid too high", __func__); + return -1; + } + + if ((options.num_control_allow_users || options.num_control_deny_users) + && !(epw = getpwuid(euid))) { + error("%s getpwuid failed: %s", __func__, strerror(errno)); + return -1; /* Fail, otherwise we might miss a deny pattern. */ + } + + if ((options.num_control_allow_groups || options.num_control_deny_groups) + && !(egr = getgrgid(euid))) { + error("%s getgrgid failed: %s", __func__, strerror(errno)); + return -1; /* Fail, otherwise we might miss a deny pattern. */ + } + + for (i = 0; i < options.num_control_deny_users; i++) { + if (match_pattern(euidstr,options.control_deny_users[i]) + || (epw && match_pattern(epw->pw_name,options.control_deny_users[i]))) { + error("%s control mode uid denied: %s", __func__, options.control_deny_users[i]); + return 0; + } + } + + for (i = 0; i < options.num_control_deny_groups; i++) { + if (match_pattern(egidstr,options.control_deny_groups[i]) + || (egr && match_pattern(egr->gr_name,options.control_deny_groups[i]))) { + error("%s control mode gid denied: %s", __func__, options.control_deny_groups[i]); + return 0; + } + } + + for (i = 0; i < options.num_control_allow_users; i++) { + if (match_pattern(euidstr,options.control_allow_users[i]) + || (epw && match_pattern(epw->pw_name,options.control_allow_users[i]))) { + return 1; + } + } + + for (i = 0; i < options.num_control_allow_groups; i++) { + if (match_pattern(egidstr,options.control_allow_groups[i]) + || (egr && match_pattern(egr->gr_name,options.control_allow_groups[i]))) { + return 1; + } + } + + error("%s control mode uid/gid denied: %s/%s", __func__, euidstr, egidstr); + + return 0; /* Deny by default. */ +} + + static void client_process_control(fd_set * readset) { @@ -686,8 +764,6 @@ struct confirm_ctx *cctx; char *cmd; u_int i, len, env_len, command, flags; - uid_t euid; - gid_t egid; /* * Accept connection on control socket @@ -703,16 +779,21 @@ return; } - if (getpeereid(client_fd, &euid, &egid) < 0) { - error("%s getpeereid failed: %s", __func__, strerror(errno)); + switch(client_control_grant(client_fd)) { + case 1: /* ALLOWED */ + break; + case 0: /* DENIED */ + error("control access denied!"); close(client_fd); return; - } - if ((euid != 0) && (getuid() != euid)) { - error("control mode uid mismatch: peer euid %u != uid %u", - (u_int) euid, (u_int) getuid()); + case -1: /* SYSERR! */ + error("error granting access"); close(client_fd); return; + default: /* OOPS! */ + error("unknown error granting access"); + close(client_fd); + return; } unset_nonblock(client_fd); Index: readconf.c ==================================================================--- readconf.c (revision 15802) +++ readconf.c (revision 15803) @@ -106,8 +106,9 @@ oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssDelegateCreds, oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, - oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, - oDeprecated, oUnsupported + oSendEnv, oControlBindMask, oControlPath, oControlMaster, + oControlAllowUsers, oControlDenyUsers, oControlAllowGroups, + oControlDenyGroups, oHashKnownHosts, oDeprecated, oUnsupported } OpCodes; /* Textual representations of the tokens. */ @@ -195,8 +196,13 @@ { "serveraliveinterval", oServerAliveInterval }, { "serveralivecountmax", oServerAliveCountMax }, { "sendenv", oSendEnv }, + { "controlbindmask", oControlBindMask }, { "controlpath", oControlPath }, { "controlmaster", oControlMaster }, + { "controlallowusers", oControlAllowUsers }, + { "controldenyusers", oControlDenyUsers }, + { "controlallowgroups", oControlAllowUsers }, + { "controldenygroups", oControlDenyGroups }, { "hashknownhosts", oHashKnownHosts }, { NULL, oBadOption } }; @@ -790,6 +796,17 @@ } break; + case oControlBindMask: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing ControlBindMask argument.", + filename, linenum); + value = strtol(arg, &endofnumber, 0); + if (*endofnumber != '\0' || value < 0 || value > 0777) + fatal("%.200s line %d: Bad mask.", filename, linenum); + options->control_bind_mask = value; + break; + case oControlPath: charptr = &options->control_path; goto parse_string; @@ -818,6 +835,46 @@ *intptr = value; break; + case oControlAllowUsers: + while ((arg = strdelim(&s)) && *arg != '\0') { + if (options->num_control_allow_users >= MAX_CONTROL_ALLOW_USERS) + fatal("%s line %d: too many control allow users.", + filename, linenum); + options->control_allow_users[options->num_control_allow_users++] + xstrdup(arg); + } + break; + + case oControlDenyUsers: + while ((arg = strdelim(&s)) && *arg != '\0') { + if (options->num_control_deny_users >= MAX_CONTROL_DENY_USERS) + fatal("%s line %d: too many control deny users.", + filename, linenum); + options->control_deny_users[options->num_control_deny_users++] + xstrdup(arg); + } + break; + + case oControlAllowGroups: + while ((arg = strdelim(&s)) && *arg != '\0') { + if (options->num_control_allow_groups >= MAX_CONTROL_ALLOW_GROUPS) + fatal("%s line %d: too many control allow groups.", + filename, linenum); + options->control_allow_groups[options->num_control_allow_groups++] + xstrdup(arg); + } + break; + + case oControlDenyGroups: + while ((arg = strdelim(&s)) && *arg != '\0') { + if (options->num_control_deny_groups >= MAX_CONTROL_DENY_GROUPS) + fatal("%s line %d: too many control deny groups.", + filename, linenum); + options->control_deny_groups[options->num_control_deny_groups++] + xstrdup(arg); + } + break; + case oHashKnownHosts: intptr = &options->hash_known_hosts; goto parse_flag; @@ -963,8 +1020,13 @@ options->server_alive_interval = -1; options->server_alive_count_max = -1; options->num_send_env = 0; + options->control_bind_mask = 0177; options->control_path = NULL; options->control_master = -1; + options->num_control_allow_users = 0; + options->num_control_deny_users = 0; + options->num_control_allow_groups = 0; + options->num_control_deny_groups = 0; options->hash_known_hosts = -1; } Index: readconf.h ==================================================================--- readconf.h (revision 15802) +++ readconf.h (revision 15803) @@ -28,8 +28,13 @@ } Forward; /* Data structure for representing option data. */ -#define MAX_SEND_ENV 256 +#define MAX_SEND_ENV 256 +#define MAX_CONTROL_ALLOW_USERS 256 +#define MAX_CONTROL_DENY_USERS 256 +#define MAX_CONTROL_ALLOW_GROUPS 256 +#define MAX_CONTROL_DENY_GROUPS 256 + typedef struct { int forward_agent; /* Forward authentication agent. */ int forward_x11; /* Forward X11 display. */ @@ -110,8 +115,17 @@ int num_send_env; char *send_env[MAX_SEND_ENV]; + mode_t control_bind_mask; char *control_path; int control_master; + u_int num_control_allow_users; + char *control_allow_users[MAX_CONTROL_ALLOW_USERS]; + u_int num_control_deny_users; + char *control_deny_users[MAX_CONTROL_DENY_USERS]; + u_int num_control_allow_groups; + char *control_allow_groups[MAX_CONTROL_ALLOW_GROUPS]; + u_int num_control_deny_groups; + char *control_deny_groups[MAX_CONTROL_ALLOW_USERS]; int hash_known_hosts; } Options; Index: ssh_config.0 ==================================================================--- ssh_config.0 (revision 15802) +++ ssh_config.0 (revision 15803) @@ -158,6 +158,38 @@ three of these escape sequences. This ensures that shared con- nections are uniquely identified. + ControlBindMask + Specify the umask to use when creating/binding the control + socket. + + ControlAllowUsers + Specify a list of uid and/or user name patterns, separated by + spaces. control socket access is granted for non-process owner + users only if the requesting process has an effective uid of 0, + or if the effective uid/user name matches here or the effective + gid/group name matches ControlAllowGroups. + + ControlAllowGroups + Specify a list of gid and/or group name patterns, separated by + spaces. control socket access is granted for non-process owner + users only if the requesting process has an effective uid of 0, + or if the effective gid/group name matches here or the + effective uid/user name matches ControlAllowUsers. + + ControlDenyUsers + Unless the requesting process has an effective uid of 0 or an + effective uid which matches the uid of the control master, + control socket access is denied if the effective uid/user name + matches here or the effective gid/group name matches + ControlDenyGroups. + + ControlDenyGroups + Unless the requesting process has an effective uid of 0 or an + effective uid which matches the uid of the control master, + control socket access is denied if the effective gid/group name + matches here or the effective uid/user name matches + ControlDenyUsers. + DynamicForward Specifies that a TCP/IP port on the local machine be forwarded over the secure channel, and the application protocol is then Index: ssh_config.5 ==================================================================--- ssh_config.5 (revision 15802) +++ ssh_config.5 (revision 15803) @@ -315,6 +315,36 @@ used for opportunistic connection sharing include all three of these escape sequences. This ensures that shared connections are uniquely identified. +.It Cm ControlBindMask +Specify the umask to use when creating/binding the control socket. +.It Cm ControlAllowUsers +Specify a list of uid and/or user name patterns, separated by spaces. +control socket access is granted for non-process owner users only if the +requesting process has an effective uid of 0, or if the effective uid/user +name matches here or the effective gid/group name matches +.Cm ControlAllowGroups +. +.It Cm ControlAllowGroups +Specify a list of gid and/or group name patterns, separated by spaces. +control socket access is granted for non-process owner users only if the +requesting process has an effective uid of 0, or if the effective gid/group +name matches here or the effective uid/user name matches +.Cm ControlAllowUsers +. +.It Cm ControlDenyUsers +Unless the requesting process has an effective uid of 0 or an effective uid +which matches the uid of the control master, control socket access is denied +if the effective uid/user name matches here or the effective gid/group name +matches +.Cm ControlDenyGroups +. +.It Cm ControlDenyGroups +Unless the requesting process has an effective uid of 0 or an effective uid +which matches the uid of the control master, control socket access is denied +if the effective gid/group name matches here or the effective uid/user name +matches +.Cm ControlDenyUsers +. .It Cm DynamicForward Specifies that a TCP/IP port on the local machine be forwarded over the secure channel, and the application -------------- next part -------------- Index: scp.0 ==================================================================--- scp.0 (revision 15802) +++ scp.0 (revision 15803) @@ -70,6 +70,11 @@ ConnectTimeout ControlMaster ControlPath + ControlBindMask + ControlAllowUsers + ControlAllowGroups + ControlDenyUsers + ControlDenyGroups GlobalKnownHostsFile GSSAPIAuthentication GSSAPIDelegateCredentials Index: scp.1 ==================================================================--- scp.1 (revision 15802) +++ scp.1 (revision 15803) @@ -130,6 +130,11 @@ .It ConnectTimeout .It ControlMaster .It ControlPath +.It ControlBindMask +.It ControlAllowUsers +.It ControlAllowGroups +.It ControlDenyUsers +.It ControlDenyGroups .It GlobalKnownHostsFile .It GSSAPIAuthentication .It GSSAPIDelegateCredentials Index: ssh.0 ==================================================================--- ssh.0 (revision 15802) +++ ssh.0 (revision 15803) @@ -392,6 +392,11 @@ ConnectTimeout ControlMaster ControlPath + ControlBindMask + ControlAllowUsers + ControlAllowGroups + ControlDenyUsers + ControlDenyGroups DynamicForward EscapeChar ForwardAgent Index: ssh.1 ==================================================================--- ssh.1 (revision 15802) +++ ssh.1 (revision 15803) @@ -691,6 +691,11 @@ .It ConnectTimeout .It ControlMaster .It ControlPath +.It ControlBindMask +.It ControlAllowUsers +.It ControlAllowGroups +.It ControlDenyUsers +.It ControlDenyGroups .It DynamicForward .It EscapeChar .It ForwardAgent Index: sftp.0 ==================================================================--- sftp.0 (revision 15802) +++ sftp.0 (revision 15803) @@ -74,6 +74,11 @@ ConnectTimeout ControlMaster ControlPath + ControlBindMask + ControlAllowUsers + ControlAllowGroups + ControlDenyUsers + ControlDenyGroups GlobalKnownHostsFile GSSAPIAuthentication GSSAPIDelegateCredentials Index: sftp.1 ==================================================================--- sftp.1 (revision 15802) +++ sftp.1 (revision 15803) @@ -158,6 +158,11 @@ .It ConnectTimeout .It ControlMaster .It ControlPath +.It ControlBindMask +.It ControlAllowUsers +.It ControlAllowGroups +.It ControlDenyUsers +.It ControlDenyGroups .It GlobalKnownHostsFile .It GSSAPIAuthentication .It GSSAPIDelegateCredentials Index: ssh.c ==================================================================--- ssh.c (revision 15802) +++ ssh.c (revision 15803) @@ -1012,7 +1012,7 @@ if ((control_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) fatal("%s socket(): %s\n", __func__, strerror(errno)); - old_umask = umask(0177); + old_umask = umask(options.control_bind_mask); if (bind(control_fd, (struct sockaddr*)&addr, addr_len) == -1) { control_fd = -1; if (errno == EINVAL || errno == EADDRINUSE) Index: clientloop.c ==================================================================--- clientloop.c (revision 15802) +++ clientloop.c (revision 15803) @@ -675,6 +675,84 @@ xfree(cctx); } + +static int +client_control_grant(int client_fd) +{ + struct passwd *epw = 0; + struct group *egr = 0; + char euidstr[48]; /* Sufficient for 2^128 in decimal ascii */ + char egidstr[48]; /* Sufficient for 2^128 in decimal ascii */ + uid_t euid; + gid_t egid; + u_int i; + + if (getpeereid(client_fd, &euid, &egid) < 0) { + error("%s getpeereid failed: %s", __func__, strerror(errno)); + return -1; + } + + if ((euid == 0) || (getuid() == euid)) + return 1; /* Short circuit. */ + + if ((int)sizeof euidstr <= snprintf(euidstr, sizeof euidstr, "%lu", (u_long)euid)) { + error("%s uid too high", __func__); + return -1; + } + + if ((int)sizeof egidstr <= snprintf(egidstr, sizeof egidstr, "%lu", (u_long)egid)) { + error("%s gid too high", __func__); + return -1; + } + + if ((options.num_control_allow_users || options.num_control_deny_users) + && !(epw = getpwuid(euid))) { + error("%s getpwuid failed: %s", __func__, strerror(errno)); + return -1; /* Fail, otherwise we might miss a deny pattern. */ + } + + if ((options.num_control_allow_groups || options.num_control_deny_groups) + && !(egr = getgrgid(euid))) { + error("%s getgrgid failed: %s", __func__, strerror(errno)); + return -1; /* Fail, otherwise we might miss a deny pattern. */ + } + + for (i = 0; i < options.num_control_deny_users; i++) { + if (match_pattern(euidstr,options.control_deny_users[i]) + || (epw && match_pattern(epw->pw_name,options.control_deny_users[i]))) { + error("%s control mode uid denied: %s", __func__, options.control_deny_users[i]); + return 0; + } + } + + for (i = 0; i < options.num_control_deny_groups; i++) { + if (match_pattern(egidstr,options.control_deny_groups[i]) + || (egr && match_pattern(egr->gr_name,options.control_deny_groups[i]))) { + error("%s control mode gid denied: %s", __func__, options.control_deny_groups[i]); + return 0; + } + } + + for (i = 0; i < options.num_control_allow_users; i++) { + if (match_pattern(euidstr,options.control_allow_users[i]) + || (epw && match_pattern(epw->pw_name,options.control_allow_users[i]))) { + return 1; + } + } + + for (i = 0; i < options.num_control_allow_groups; i++) { + if (match_pattern(egidstr,options.control_allow_groups[i]) + || (egr && match_pattern(egr->gr_name,options.control_allow_groups[i]))) { + return 1; + } + } + + error("%s control mode uid/gid denied: %s/%s", __func__, euidstr, egidstr); + + return 0; /* Deny by default. */ +} + + static void client_process_control(fd_set * readset) { @@ -686,8 +764,6 @@ struct confirm_ctx *cctx; char *cmd; u_int i, len, env_len, command, flags; - uid_t euid; - gid_t egid; /* * Accept connection on control socket @@ -703,16 +779,21 @@ return; } - if (getpeereid(client_fd, &euid, &egid) < 0) { - error("%s getpeereid failed: %s", __func__, strerror(errno)); + switch(client_control_grant(client_fd)) { + case 1: /* ALLOWED */ + break; + case 0: /* DENIED */ + error("control access denied!"); close(client_fd); return; - } - if ((euid != 0) && (getuid() != euid)) { - error("control mode uid mismatch: peer euid %u != uid %u", - (u_int) euid, (u_int) getuid()); + case -1: /* SYSERR! */ + error("error granting access"); close(client_fd); return; + default: /* OOPS! */ + error("unknown error granting access"); + close(client_fd); + return; } unset_nonblock(client_fd); Index: readconf.c ==================================================================--- readconf.c (revision 15802) +++ readconf.c (revision 15803) @@ -106,8 +106,9 @@ oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssDelegateCreds, oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, - oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, - oDeprecated, oUnsupported + oSendEnv, oControlBindMask, oControlPath, oControlMaster, + oControlAllowUsers, oControlDenyUsers, oControlAllowGroups, + oControlDenyGroups, oHashKnownHosts, oDeprecated, oUnsupported } OpCodes; /* Textual representations of the tokens. */ @@ -195,8 +196,13 @@ { "serveraliveinterval", oServerAliveInterval }, { "serveralivecountmax", oServerAliveCountMax }, { "sendenv", oSendEnv }, + { "controlbindmask", oControlBindMask }, { "controlpath", oControlPath }, { "controlmaster", oControlMaster }, + { "controlallowusers", oControlAllowUsers }, + { "controldenyusers", oControlDenyUsers }, + { "controlallowgroups", oControlAllowUsers }, + { "controldenygroups", oControlDenyGroups }, { "hashknownhosts", oHashKnownHosts }, { NULL, oBadOption } }; @@ -790,6 +796,17 @@ } break; + case oControlBindMask: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing ControlBindMask argument.", + filename, linenum); + value = strtol(arg, &endofnumber, 0); + if (*endofnumber != '\0' || value < 0 || value > 0777) + fatal("%.200s line %d: Bad mask.", filename, linenum); + options->control_bind_mask = value; + break; + case oControlPath: charptr = &options->control_path; goto parse_string; @@ -818,6 +835,46 @@ *intptr = value; break; + case oControlAllowUsers: + while ((arg = strdelim(&s)) && *arg != '\0') { + if (options->num_control_allow_users >= MAX_CONTROL_ALLOW_USERS) + fatal("%s line %d: too many control allow users.", + filename, linenum); + options->control_allow_users[options->num_control_allow_users++] + xstrdup(arg); + } + break; + + case oControlDenyUsers: + while ((arg = strdelim(&s)) && *arg != '\0') { + if (options->num_control_deny_users >= MAX_CONTROL_DENY_USERS) + fatal("%s line %d: too many control deny users.", + filename, linenum); + options->control_deny_users[options->num_control_deny_users++] + xstrdup(arg); + } + break; + + case oControlAllowGroups: + while ((arg = strdelim(&s)) && *arg != '\0') { + if (options->num_control_allow_groups >= MAX_CONTROL_ALLOW_GROUPS) + fatal("%s line %d: too many control allow groups.", + filename, linenum); + options->control_allow_groups[options->num_control_allow_groups++] + xstrdup(arg); + } + break; + + case oControlDenyGroups: + while ((arg = strdelim(&s)) && *arg != '\0') { + if (options->num_control_deny_groups >= MAX_CONTROL_DENY_GROUPS) + fatal("%s line %d: too many control deny groups.", + filename, linenum); + options->control_deny_groups[options->num_control_deny_groups++] + xstrdup(arg); + } + break; + case oHashKnownHosts: intptr = &options->hash_known_hosts; goto parse_flag; @@ -963,8 +1020,13 @@ options->server_alive_interval = -1; options->server_alive_count_max = -1; options->num_send_env = 0; + options->control_bind_mask = 0177; options->control_path = NULL; options->control_master = -1; + options->num_control_allow_users = 0; + options->num_control_deny_users = 0; + options->num_control_allow_groups = 0; + options->num_control_deny_groups = 0; options->hash_known_hosts = -1; } Index: readconf.h ==================================================================--- readconf.h (revision 15802) +++ readconf.h (revision 15803) @@ -28,8 +28,13 @@ } Forward; /* Data structure for representing option data. */ -#define MAX_SEND_ENV 256 +#define MAX_SEND_ENV 256 +#define MAX_CONTROL_ALLOW_USERS 256 +#define MAX_CONTROL_DENY_USERS 256 +#define MAX_CONTROL_ALLOW_GROUPS 256 +#define MAX_CONTROL_DENY_GROUPS 256 + typedef struct { int forward_agent; /* Forward authentication agent. */ int forward_x11; /* Forward X11 display. */ @@ -110,8 +115,17 @@ int num_send_env; char *send_env[MAX_SEND_ENV]; + mode_t control_bind_mask; char *control_path; int control_master; + u_int num_control_allow_users; + char *control_allow_users[MAX_CONTROL_ALLOW_USERS]; + u_int num_control_deny_users; + char *control_deny_users[MAX_CONTROL_DENY_USERS]; + u_int num_control_allow_groups; + char *control_allow_groups[MAX_CONTROL_ALLOW_GROUPS]; + u_int num_control_deny_groups; + char *control_deny_groups[MAX_CONTROL_ALLOW_USERS]; int hash_known_hosts; } Options; Index: ssh_config.0 ==================================================================--- ssh_config.0 (revision 15802) +++ ssh_config.0 (revision 15803) @@ -158,6 +158,38 @@ three of these escape sequences. This ensures that shared con- nections are uniquely identified. + ControlBindMask + Specify the umask to use when creating/binding the control + socket. + + ControlAllowUsers + Specify a list of uid and/or user name patterns, separated by + spaces. control socket access is granted for non-process owner + users only if the requesting process has an effective uid of 0, + or if the effective uid/user name matches here or the effective + gid/group name matches ControlAllowGroups. + + ControlAllowGroups + Specify a list of gid and/or group name patterns, separated by + spaces. control socket access is granted for non-process owner + users only if the requesting process has an effective uid of 0, + or if the effective gid/group name matches here or the + effective uid/user name matches ControlAllowUsers. + + ControlDenyUsers + Unless the requesting process has an effective uid of 0 or an + effective uid which matches the uid of the control master, + control socket access is denied if the effective uid/user name + matches here or the effective gid/group name matches + ControlDenyGroups. + + ControlDenyGroups + Unless the requesting process has an effective uid of 0 or an + effective uid which matches the uid of the control master, + control socket access is denied if the effective gid/group name + matches here or the effective uid/user name matches + ControlDenyUsers. + DynamicForward Specifies that a TCP/IP port on the local machine be forwarded over the secure channel, and the application protocol is then Index: ssh_config.5 ==================================================================--- ssh_config.5 (revision 15802) +++ ssh_config.5 (revision 15803) @@ -315,6 +315,36 @@ used for opportunistic connection sharing include all three of these escape sequences. This ensures that shared connections are uniquely identified. +.It Cm ControlBindMask +Specify the umask to use when creating/binding the control socket. +.It Cm ControlAllowUsers +Specify a list of uid and/or user name patterns, separated by spaces. +control socket access is granted for non-process owner users only if the +requesting process has an effective uid of 0, or if the effective uid/user +name matches here or the effective gid/group name matches +.Cm ControlAllowGroups +. +.It Cm ControlAllowGroups +Specify a list of gid and/or group name patterns, separated by spaces. +control socket access is granted for non-process owner users only if the +requesting process has an effective uid of 0, or if the effective gid/group +name matches here or the effective uid/user name matches +.Cm ControlAllowUsers +. +.It Cm ControlDenyUsers +Unless the requesting process has an effective uid of 0 or an effective uid +which matches the uid of the control master, control socket access is denied +if the effective uid/user name matches here or the effective gid/group name +matches +.Cm ControlDenyGroups +. +.It Cm ControlDenyGroups +Unless the requesting process has an effective uid of 0 or an effective uid +which matches the uid of the control master, control socket access is denied +if the effective gid/group name matches here or the effective uid/user name +matches +.Cm ControlDenyUsers +. .It Cm DynamicForward Specifies that a TCP/IP port on the local machine be forwarded over the secure channel, and the application