vBulletin Search Engine Optimization
| |||||||
| Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
| ||||
| Attached is more-or-less complete patch to OpenSSH 4.3p2 to implement unix domain socket forwarding. Basically, for forward and reverse forwardings anywhere you previously put a port number you can now put a path. The socket path should go between braces (`[' and `]'), and backslash escaping is honored within. Basically, any "port" which uses an escaping mechanism is considered a path instead. That way you can have socket paths like 1234 w/o ambiguity. -L[</path/to/socket>]:<host>:<port> -L<port>:[</path/to/socket>] -L[</path/to/socket>]:[</path/to/another_socket>] -R[</path/to/socket>]:<host>:<port> -R<port>:[</path/to/socket>] -R[</path/to/socket>]:[</path/to/another_socket>] I've added several new SSH messages: streamlocal-forward@openssh.com forarded-streamlocal@openssh.com cancel-streamlocal-forward@openssh.com direct-streamlocal@openssh.com streamlocal from "AF_LOCAL" and "SOCK_STREAM". Seemed to logically follow from the tcpip messages. Um, included in this patch, for now, is my previous patch which added options to specify a mask and access control lists for control sockets. I'll seperate them out when I port this over to the main OpenSSH source in OpenBSD. So, the following options are honored with this patch: Client: ControlBindMask Mask to use when binding a control socket ControlAllowUsers List of users and uids allowed to connect ControlAllowGroups List of groups and gids allows to connect ControlDenyUsers List of users and uids disallowed ControlDenyGroups List of groups and gids disallowed StreamLocalBindMask Mask to use when binding a forward socket StreamLocalBindUnlink Attempt an unlink before binding FakeStreamLocalForwards Fake a direct-streamlocal@openssh.com as direct-tcpip. This isn't currently honored; this always happens. See channel_post_port_listener(). Server: StreamLocalBindMask See above. StreamLocalBindUnlink See above. All regression tests pass, though I haven't yet added any for the new functionality. Please send suggestions and comments. It would be nice if this were included, or at least had the prospect of inclusion in the future. - Bill Index: scp.0 ================================================== ================= --- scp.0 (.../vendor/openssh/4.3p2) (revision 18230) +++ scp.0 (.../branches/b6698/common/openssh4) (revision 18230) @@ -70,6 +70,11 @@ ConnectTimeout ControlMaster ControlPath + ControlBindMask + ControlAllowUsers + ControlAllowGroups + ControlDenyUsers + ControlDenyGroups GlobalKnownHostsFile GSSAPIAuthentication GSSAPIDelegateCredentials Index: scp.1 ================================================== ================= --- scp.1 (.../vendor/openssh/4.3p2) (revision 18230) +++ scp.1 (.../branches/b6698/common/openssh4) (revision 18230) @@ -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 (.../vendor/openssh/4.3p2) (revision 18230) +++ ssh.0 (.../branches/b6698/common/openssh4) (revision 18230) @@ -223,6 +223,11 @@ ConnectTimeout ControlMaster ControlPath + ControlBindMask + ControlAllowUsers + ControlAllowGroups + ControlDenyUsers + ControlDenyGroups DynamicForward EscapeChar ForwardAgent Index: ssh.1 ================================================== ================= --- ssh.1 (.../vendor/openssh/4.3p2) (revision 18230) +++ ssh.1 (.../branches/b6698/common/openssh4) (revision 18230) @@ -446,6 +446,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 (.../vendor/openssh/4.3p2) (revision 18230) +++ sftp.0 (.../branches/b6698/common/openssh4) (revision 18230) @@ -74,6 +74,11 @@ ConnectTimeout ControlMaster ControlPath + ControlBindMask + ControlAllowUsers + ControlAllowGroups + ControlDenyUsers + ControlDenyGroups GlobalKnownHostsFile GSSAPIAuthentication GSSAPIDelegateCredentials Index: sftp.1 ================================================== ================= --- sftp.1 (.../vendor/openssh/4.3p2) (revision 18230) +++ sftp.1 (.../branches/b6698/common/openssh4) (revision 18230) @@ -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 (.../vendor/openssh/4.3p2) (revision 18230) +++ ssh.c (.../branches/b6698/common/openssh4) (revision 18230) @@ -777,41 +777,47 @@ { int success = 0; int i; + SocketName listen_on, connect_to; + ForwardOptions fwd_options; + forward_options_init_from_client(&fwd_options, &options); + /* Initiate local TCP/IP port forwardings. */ for (i = 0; i < options.num_local_forwards; i++) { - debug("Local connections to %.200s:%d forwarded to remote " - "address %.200s:%d", - (options.local_forwards[i].listen_host == NULL) ? - (options.gateway_ports ? "*" : "LOCALHOST") : - options.local_forwards[i].listen_host, - options.local_forwards[i].listen_port, - options.local_forwards[i].connect_host, - options.local_forwards[i].connect_port); + SOCKETNAME_INIT(&listen_on); + listen_on.host = options.local_forwards[i].listen_host; + listen_on.port = options.local_forwards[i].listen_port; + listen_on.path = options.local_forwards[i].listen_path; + SOCKETNAME_INIT(&connect_to); + connect_to.host = options.local_forwards[i].connect_host; + connect_to.port = options.local_forwards[i].connect_port; + connect_to.path = options.local_forwards[i].connect_path; + debug("Local connections to %.200s forwarded to remote " + "address %.200s", + SOCKETNAME_TOSTRING(&listen_on), + SOCKETNAME_TOSTRING(&connect_to)); success += channel_setup_local_fwd_listener( - options.local_forwards[i].listen_host, - options.local_forwards[i].listen_port, - options.local_forwards[i].connect_host, - options.local_forwards[i].connect_port, - options.gateway_ports); + &listen_on, &connect_to, &fwd_options); } if (i > 0 && success == 0) error("Could not request local forwarding."); /* Initiate remote TCP/IP port forwardings. */ for (i = 0; i < options.num_remote_forwards; i++) { - debug("Remote connections from %.200s:%d forwarded to " - "local address %.200s:%d", - (options.remote_forwards[i].listen_host == NULL) ? - "LOCALHOST" : options.remote_forwards[i].listen_host, - options.remote_forwards[i].listen_port, - options.remote_forwards[i].connect_host, - options.remote_forwards[i].connect_port); + SOCKETNAME_INIT(&listen_on); + listen_on.host = options.remote_forwards[i].listen_host; + listen_on.port = options.remote_forwards[i].listen_port; + listen_on.path = options.remote_forwards[i].listen_path; + SOCKETNAME_INIT(&connect_to); + connect_to.host = options.remote_forwards[i].connect_host; + connect_to.port = options.remote_forwards[i].connect_port; + connect_to.path = options.remote_forwards[i].connect_path; + debug("Remote connections from %.200s forwarded to " + "local address %.200s", + SOCKETNAME_TOSTRING(&listen_on), + SOCKETNAME_TOSTRING(&connect_to)); channel_request_remote_forwarding( - options.remote_forwards[i].listen_host, - options.remote_forwards[i].listen_port, - options.remote_forwards[i].connect_host, - options.remote_forwards[i].connect_port); + &listen_on, &connect_to); } } @@ -1023,7 +1029,7 @@ if ((control_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) fatal("%s socket(): %s", __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: session.c ================================================== ================= --- session.c (.../vendor/openssh/4.3p2) (revision 18230) +++ session.c (.../branches/b6698/common/openssh4) (revision 18230) @@ -235,6 +235,7 @@ int success, type, screen_flag; int enable_compression_after_reply = 0; u_int proto_len, data_len, dlen, compression_level = 0; + ForwardOptions fwd_opts; s = session_new(); if (s == NULL) { @@ -322,7 +323,8 @@ break; } debug("Received TCP/IP port forwarding request."); - channel_input_port_forward_request(s->pw->pw_uid == 0, options.gateway_ports); + forward_options_init_from_server(&fwd_opts, &options); + channel_input_port_forward_request(s->pw->pw_uid == 0, &fwd_opts); success = 1; break; Index: servconf.c ================================================== ================= --- servconf.c (.../vendor/openssh/4.3p2) (revision 18230) +++ servconf.c (.../branches/b6698/common/openssh4) (revision 18230) @@ -102,6 +102,8 @@ options->authorized_keys_file2 = NULL; options->num_accept_env = 0; options->permit_tun = -1; + options->streamlocal_bind_mask = 0177; + options->streamlocal_bind_unlink = -1; /* Needs to be accessable in many places */ use_privsep = -1; @@ -232,6 +234,8 @@ options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS; if (options->permit_tun == -1) options->permit_tun = SSH_TUNMODE_NO; + if (options->streamlocal_bind_unlink == -1) + options->streamlocal_bind_unlink = 0; /* Turn privilege separation on by default */ if (use_privsep == -1) @@ -274,6 +278,7 @@ sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, + sStreamLocalBindMask, sStreamLocalBindUnlink, sUsePrivilegeSeparation, sDeprecated, sUnsupported } ServerOpCodes; @@ -377,6 +382,8 @@ { "useprivilegeseparation", sUsePrivilegeSeparation}, { "acceptenv", sAcceptEnv }, { "permittunnel", sPermitTunnel }, + { "streamlocalbindmask", sStreamLocalBindMask }, + { "streamlocalbindunlink", sStreamLocalBindUnlink }, { NULL, sBadOption } }; @@ -441,7 +448,7 @@ process_server_config_line(ServerOptions *options, char *line, const char *filename, int linenum) { - char *cp, **charptr, *arg, *p; + char *cp, **charptr, *endofnumber, *arg, *p; int *intptr, value, n; ServerOpCodes opcode; u_short port; @@ -988,6 +995,21 @@ *intptr = value; break; + case sStreamLocalBindMask: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing StreamLocalBindMask argument.", + filename, linenum); + value = strtol(arg, &endofnumber, 0); + if (*endofnumber != '\0' || value < 0 || value > 0777) + fatal("%.200s line %d: Bad mask.", filename, linenum); + options->streamlocal_bind_mask = value; + break; + + case sStreamLocalBindUnlink: + intptr = &options->streamlocal_bind_unlink; + goto parse_flag; + case sDeprecated: logit("%s line %d: Deprecated option %s", filename, linenum, arg); Index: servconf.h ================================================== ================= --- servconf.h (.../vendor/openssh/4.3p2) (revision 18230) +++ servconf.h (.../branches/b6698/common/openssh4) (revision 18230) @@ -37,7 +37,7 @@ #define DEFAULT_AUTH_FAIL_MAX 6 /* Default for MaxAuthTries */ -typedef struct { +typedef struct ServerOptions { u_int num_ports; u_int ports_from_cmdline; u_short ports[MAX_PORTS]; /* Port number to listen on. */ @@ -137,6 +137,12 @@ int use_pam; /* Enable auth via PAM */ int permit_tun; + + mode_t streamlocal_bind_mask; /* + * Mask to use when binding forwarded + * domain sockets. + */ + int streamlocal_bind_unlink;/* Attempt unlink before binding? */ } ServerOptions; void initialize_server_options(ServerOptions *); Index: clientloop.c ================================================== ================= --- clientloop.c (.../vendor/openssh/4.3p2) (revision 18230) +++ clientloop.c (.../branches/b6698/common/openssh4) (revision 18230) @@ -676,6 +676,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_group s[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) { @@ -687,8 +765,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 @@ -704,16 +780,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); @@ -897,6 +978,8 @@ int local = 0; u_short cancel_port; Forward fwd; + SocketName name, listen_host, connect_host; + ForwardOptions fwd_opts; leave_raw_mode(); handler = signal(SIGINT, SIG_IGN); @@ -964,23 +1047,25 @@ logit("Bad forwarding close port"); goto out; } - channel_request_rforward_cancel(cancel_host, cancel_port); + SOCKETNAME_INIT_TCPIP(&name, cancel_host, cancel_port); + channel_request_rforward_cancel(&name); } else { if (!parse_forward(&fwd, s)) { logit("Bad forwarding specification."); goto out; } + SOCKETNAME_INIT_TCPIP(&listen_host, fwd.listen_host, fwd.listen_port); + SOCKETNAME_INIT_TCPIP(&connect_host, fwd.connect_host, fwd.connect_port); if (local) { - if (channel_setup_local_fwd_listener(fwd.listen_host, - fwd.listen_port, fwd.connect_host, - fwd.connect_port, options.gateway_ports) < 0) { + forward_options_init_from_client(&fwd_opts, &options); + if (channel_setup_local_fwd_listener(&listen_host, + &connect_host, &fwd_opts) < 0) { logit("Port forwarding failed."); goto out; } } else { - channel_request_remote_forwarding(fwd.listen_host, - fwd.listen_port, fwd.connect_host, - fwd.connect_port); + channel_request_remote_forwarding(&listen_host, + &connect_host); } logit("Forwarding port."); @@ -1651,6 +1736,7 @@ Channel *c = NULL; char *listen_address, *originator_address; int listen_port, originator_port; + SocketName name; int sock; /* Get rest of the packet */ @@ -1663,7 +1749,8 @@ debug("client_request_forwarded_tcpip: listen %s port %d, originator %s port %d", listen_address, listen_port, originator_address, originator_port); - sock = channel_connect_by_listen_address(listen_port); + SOCKETNAME_INIT_TCPIP(&name, listen_address, listen_port); + sock = channel_connect_by_listen_address(&name); if (sock < 0) { xfree(originator_address); xfree(listen_address); @@ -1678,7 +1765,64 @@ return c; } +/* + * a connection came in on a listening local (unix domain) socket at the + * remote server. If we so desire, attempt to connect the stream to a local + * end point. + */ static Channel * +client_request_forwarded_streamlocal(const char *request_type, int rchan) +{ + Channel *c = NULL; + char *listen_path, *originator_info; + SocketName name; + int sock; + + /* Get remote path and name/value pairs */ + listen_path = packet_get_string(NULL); + originator_info = NULL; + do { + /* just discard for now */ + if (originator_info != NULL) { + debug("client_request_forwarded_streamlocal " + "origin info: %s", originator_info); + xfree(originator_info), originator_info = NULL; + } + + /* + * If provided, these look like "uid=1234" and/or + * "group=foo". Since unix domain sockets can carry possibly + * useful info (like uid, user names, gid, etc.) leave the + * capability here to utilize this data, even if any + * specific type of data is entirely unportable. + * + * This variable length list of name=value strings is + * terminated by an empty string. + */ + originator_info = packet_get_string(NULL); + } while (originator_info && *originator_info); + packet_check_eom(); + if (originator_info) + xfree(originator_info), originator_info = NULL; + + debug("client_request_forwarded_streamlocal: %s", + listen_path); + + SOCKETNAME_INIT_STREAMLOCAL(&name, listen_path); + sock = channel_connect_by_listen_address(&name); + if (sock < 0) { + xfree(listen_path); + return NULL; + } + c = channel_new("forwarded-streamlocal@openssh.com", + SSH_CHANNEL_CONNECTING, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0, + listen_path, 1); + xfree(listen_path); + return c; +} + +static Channel * client_request_x11(const char *request_type, int rchan) { Channel *c = NULL; @@ -1754,6 +1898,8 @@ if (strcmp(ctype, "forwarded-tcpip") == 0) { c = client_request_forwarded_tcpip(ctype, rchan); + } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { + c = client_request_forwarded_streamlocal(ctype, rchan); } else if (strcmp(ctype, "x11") == 0) { c = client_request_x11(ctype, rchan); } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { Index: serverloop.c ================================================== ================= --- serverloop.c (.../vendor/openssh/4.3p2) (revision 18230) +++ serverloop.c (.../branches/b6698/common/openssh4) (revision 18230) @@ -891,6 +891,7 @@ int sock; char *target, *originator; int target_port, originator_port; + SocketName name; target = packet_get_string(NULL); target_port = packet_get_int(); @@ -902,7 +903,8 @@ originator, originator_port, target, target_port); /* XXX check permission */ - sock = channel_connect_to(target, target_port); + SOCKETNAME_INIT_TCPIP(&name, target, target_port); + sock = channel_connect_to(&name); xfree(target); xfree(originator); if (sock < 0) @@ -914,6 +916,36 @@ } static Channel * +server_request_direct_streamlocal(void) +{ + Channel *c; + int sock; + char *target, *originator; + int originator_port; + SocketName name; + + target = packet_get_string(NULL); + originator = packet_get_string(NULL); + originator_port = packet_get_int(); + packet_check_eom(); + + debug("server_request_direct_streamlocal: originator %s port %d, target %s", + originator, originator_port, target); + + /* XXX check permission */ + SOCKETNAME_INIT_STREAMLOCAL(&name, target); + sock = channel_connect_to(&name); + xfree(target); + xfree(originator); + if (sock < 0) + return NULL; + c = channel_new("direct-streamlocal@openssh.com", SSH_CHANNEL_CONNECTING, + sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, + CHAN_TCP_PACKET_DEFAULT, 0, "direct-streamlocal", 1); + return c; +} + +static Channel * server_request_tun(void) { Channel *c = NULL; @@ -1004,6 +1036,8 @@ c = server_request_session(); } else if (strcmp(ctype, "direct-tcpip") == 0) { c = server_request_direct_tcpip(); + } else if (strcmp(ctype, "direct-streamlocal@openssh.com") == 0) { + c = server_request_direct_streamlocal(); } else if (strcmp(ctype, "tun@openssh.com") == 0) { c = server_request_tun(); } @@ -1040,6 +1074,8 @@ char *rtype; int want_reply; int success = 0; + SocketName name; + ForwardOptions fwd_opts; rtype = packet_get_string(NULL); want_reply = packet_get_char(); @@ -1070,8 +1106,10 @@ packet_send_debug("Server has disabled port forwarding."); } else { /* Start listening on the port */ + SOCKETNAME_INIT_TCPIP(&name, listen_address, listen_port); + forward_options_init_from_server(&fwd_opts, &options); success = channel_setup_remote_fwd_listener( - listen_address, listen_port, options.gateway_ports); + &name, &fwd_opts); } xfree(listen_address); } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) { @@ -1083,8 +1121,38 @@ debug("%s: cancel-tcpip-forward addr %s port %d", __func__, cancel_address, cancel_port); - success = channel_cancel_rport_listener(cancel_address, - cancel_port); + SOCKETNAME_INIT_TCPIP(&name, cancel_address, cancel_port); + success = channel_cancel_rport_listener(&name); + } else if (strcmp(rtype, "streamlocal-forward@openssh.com") == 0) { + char *listen_path; + + listen_path = packet_get_string(NULL); + debug("server_input_global_request: streamlocal-forward listen path %s", + listen_path); + + /* check permissions */ + if (!options.allow_tcp_forwarding || + no_port_forwarding_flag + ) { + success = 0; + packet_send_debug("Server has disabled port forwarding."); + } else { + /* Start listening on the port */ + SOCKETNAME_INIT_STREAMLOCAL(&name, listen_path); + forward_options_init_from_server(&fwd_opts, &options); + success = channel_setup_remote_fwd_listener( + &name, &fwd_opts); + } + xfree(listen_path); + } else if (strcmp(rtype, "cancel-streamlocal-forward@openssh.com") == 0) { + char *cancel_path; + + cancel_path = packet_get_string(NULL); + debug("%s: cancel-streamlocal-forward path %s", __func__, + cancel_path); + + SOCKETNAME_INIT_STREAMLOCAL(&name, cancel_path); + success = channel_cancel_rport_listener(&name); } if (want_reply) { packet_start(success ? Index: readconf.c ================================================== ================= --- readconf.c (.../vendor/openssh/4.3p2) (revision 18230) +++ readconf.c (.../branches/b6698/common/openssh4) (revision 18230) @@ -106,11 +106,14 @@ oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, oHostKeyAlgorithms, oBindAddress, oSmartcardDevice, - oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oClearAllForwardings, oFakeStreamLocalForwards, + oStreamLocalBindMask, oStreamLocalBindUnlink, oNoHostAuthenticationForLocalhost, oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssDelegateCreds, oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, - oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, + oSendEnv, oControlBindMask, oControlPath, oControlMaster, + oControlAllowUsers, oControlDenyUsers, oControlAllowGroups, + oControlDenyGroups, oHashKnownHosts, oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, oDeprecated, oUnsupported } OpCodes; @@ -191,6 +194,9 @@ { "smartcarddevice", oUnsupported }, #endif { "clearallforwardings", oClearAllForwardings }, + { "fakestreamlocalforwards", oFakeStreamLocalForwards }, + { "streamlocalbindmask", oStreamLocalBindMask }, + { "streamlocalbindunlink", oStreamLocalBindUnlink }, { "enablesshkeysign", oEnableSSHKeysign }, { "verifyhostkeydns", oVerifyHostKeyDNS }, { "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost }, @@ -200,8 +206,13 @@ { "serveraliveinterval", oServerAliveInterval }, { "serveralivecountmax", oServerAliveCountMax }, { "sendenv", oSendEnv }, + { "controlbindmask", oControlBindMask }, { "controlpath", oControlPath }, { "controlmaster", oControlMaster }, + { "controlallowusers", oControlAllowUsers }, + { "controldenyusers", oControlDenyUsers }, + { "controlallowgroups", oControlAllowUsers }, + { "controldenygroups", oControlDenyGroups }, { "hashknownhosts", oHashKnownHosts }, { "tunnel", oTunnel }, { "tunneldevice", oTunnelDevice }, @@ -221,7 +232,8 @@ Forward *fwd; #ifndef NO_IPPORT_RESERVED_CONCEPT extern uid_t original_real_uid; - if (newfwd->listen_port < IPPORT_RESERVED && original_real_uid != 0) + if (newfwd->listen_port < IPPORT_RESERVED && original_real_uid != 0 + && newfwd->listen_path == NULL) fatal("Privileged ports can only be forwarded by root."); #endif if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) @@ -231,8 +243,13 @@ fwd->listen_host = (newfwd->listen_host == NULL) ? NULL : xstrdup(newfwd->listen_host); fwd->listen_port = newfwd->listen_port; - fwd->connect_host = xstrdup(newfwd->connect_host); + fwd->listen_path = (newfwd->listen_path == NULL) ? + NULL : xstrdup(newfwd->listen_path); + fwd->connect_host = (newfwd->connect_host == NULL) ? + NULL : xstrdup(newfwd->connect_host); fwd->connect_port = newfwd->connect_port; + fwd->connect_path = (newfwd->connect_path == NULL) ? + NULL : xstrdup(newfwd->connect_path); } /* @@ -252,8 +269,13 @@ fwd->listen_host = (newfwd->listen_host == NULL) ? NULL : xstrdup(newfwd->listen_host); fwd->listen_port = newfwd->listen_port; - fwd->connect_host = xstrdup(newfwd->connect_host); + fwd->listen_path = (newfwd->listen_path == NULL) ? + NULL : xstrdup(newfwd->listen_path); + fwd->connect_host = (newfwd->connect_host == NULL) ? + NULL : xstrdup(newfwd->connect_host); fwd->connect_port = newfwd->connect_port; + fwd->connect_path = (newfwd->connect_path == NULL) ? + NULL : xstrdup(newfwd->connect_path); } static void @@ -719,6 +741,25 @@ intptr = &options->clear_forwardings; goto parse_flag; + case oFakeStreamLocalForwards: + intptr = &options->fake_streamlocal_forwards; + goto parse_flag; + + case oStreamLocalBindMask: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing StreamLocalBindMask argument.", + filename, linenum); + value = strtol(arg, &endofnumber, 0); + if (*endofnumber != '\0' || value < 0 || value > 0777) + fatal("%.200s line %d: Bad mask.", filename, linenum); + options->streamlocal_bind_mask = value; + break; + + case oStreamLocalBindUnlink: + intptr = &options->streamlocal_bind_unlink; + goto parse_flag; + case oHost: *activep = 0; while ((arg = strdelim(&s)) != NULL && *arg != '\0') @@ -801,6 +842,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; @@ -829,6 +881,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; @@ -1005,6 +1097,9 @@ options->num_local_forwards = 0; options->num_remote_forwards = 0; options->clear_forwardings = -1; + options->fake_streamlocal_forwards = -1; + options->streamlocal_bind_mask = 0177; + options->streamlocal_bind_unlink = -1; options->log_level = SYSLOG_LEVEL_NOT_SET; options->preferred_authentications = NULL; options->bind_address = NULL; @@ -1017,8 +1112,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; options->tun_open = -1; options->tun_local = -1; @@ -1131,6 +1231,10 @@ options->log_level = SYSLOG_LEVEL_INFO; if (options->clear_forwardings == 1) clear_forwardings(options); + if (options->fake_streamlocal_forwards == -1) + options->fake_streamlocal_forwards = 0; + if (options->streamlocal_bind_unlink == -1) + options->streamlocal_bind_unlink = 0; if (options->no_host_authentication_for_localhost == - 1) options->no_host_authentication_for_localhost = 0; if (options->identities_only == -1) @@ -1165,7 +1269,113 @@ /* options->preferred_authentications will be set in ssh */ } + /* + * parse_forward_part + * + * parses the next part (semi-colon delimited) of a port forwarding + * specification. + * + * This works similar to shell word parsing, except that square brackets are + * used for quoting instead of single or double quotes; semi-colons delimit + * the parts instead of whitespace; and adjacent delimiters are not merged. + * The canonical backslash escaping mechanism works identically, though. + * + * NOTE: All whitespace is preserved as-is. + * + * Returns the logical string length of the parsed part. On return `dstbuf' + * will contain up to `dstbufsz' - 1 characters of the part, and will always + * be zero terminated (if `dstbufsz' > 0). `end' will contain the next + * position in `src' to be parsed. If *end is NUL then there is no more + * input. + */ +size_t +parse_forward_part(char *dstbuf, size_t dstbufsz, const char *src, char **end, int *special) +{ + struct { char *pos, *end; } dst; + int quoted; + int escaped; + int ibuf; + char *cbuf; + + dst.pos = dstbuf; + dst.end = dstbuf + dstbufsz; + + if (!end) + end = &cbuf; + + if (!special) + special = &ibuf; + + quoted = '\0'; + escaped = 0; + + for (; src && *src != '\0'; src++) { + switch (*src) { + case ':': + if (escaped || quoted) + goto copyout; + + src++; /* Eat it before breaking the loop. */ + + goto nomore; + + /* NOT REACHED */ + case '[': + if (escaped) { + goto copyout; + } else if (quoted) { + goto copyout; + } else { + quoted = 1; + *special = 1; + } + + break; + case ']': + if (escaped) { + goto copyout; + } else if (quoted) { + quoted = 0; + } else { + goto copyout; + } + + break; + case '\\': + if (escaped) { + goto copyout; + } else { + escaped = 1; + *special = 1; + } + + break; +copyout: + default: + escaped = 0; + + if (dst.pos < dst.end) + *dst.pos = *src; + + dst.pos++; + } /* switch (*src) */ + } /* for (*src; src++) */ + +nomore: + + *end = (char *)src; + + if (dst.pos < dst.end) + *dst.pos = '\0'; + else if (dst.end > dstbuf) + dst.end[-1] = '\0'; + + return dst.pos - dstbuf; +} /* parse_forward_part */ + + +/* * parse_forward * parses a string containing a port forwarding specification of the form: * [listenhost:]listenport:connecthost:connectport @@ -1174,8 +1384,11 @@ int parse_forward(Forward *fwd, const char *fwdspec) { - int i; - char *p, *cp, *fwdarg[4]; + unsigned int i; + char *p, *cp, *fwdarg[4] = { 0 }; + int fwdesc[4] = { 0 }; /* Tag whether part used escaping or quoting. */ + char part[MAX(NI_MAXHOST + 1, sizeof ((struct sockaddr_un *)0)->sun_path)]; + size_t partlen; memset(fwd, '\0', sizeof(*fwd)); @@ -1185,47 +1398,105 @@ while (*cp && isspace(*cp)) cp++; - for (i = 0; i < 4; ++i) - if ((fwdarg[i] = hpdelim(&cp)) == NULL) - break; + for (i = 0; i < 4 && *cp != '\0'; ++i) { + partlen = parse_forward_part(part, sizeof part, cp, &cp, &fwdesc[i]); - /* Check for trailing garbage in 4-arg case*/ - if (cp != NULL) + /* Was the part too long to be sane? */ + if (partlen >= sizeof part) + goto fail_free; + + fwdarg[i] = xstrdup(part); + } + + /* Check for trailing garbage in 4-arg case */ + if (*cp != '\0') i = 0; /* failure */ switch (i) { + case 2: + /* Force escaping of all paths, just to be consistent. */ + if (!fwdesc[0] || !fwdesc[1]) { + i = 0; + } else { + fwd->listen_host = NULL; + fwd->listen_port = 0; + fwd->listen_path = xstrdup(fwdarg[0]); + + fwd->connect_host = NULL; + fwd->connect_port = 0; + fwd->connect_path = xstrdup(fwdarg[1]); + } + break; case 3: - fwd->listen_host = NULL; - fwd->listen_port = a2port(fwdarg[0]); - fwd->connect_host = xstrdup(cleanhostname(fwdarg[1])); - fwd->connect_port = a2port(fwdarg[2]); + if (fwdesc[0]) { + fwd->listen_host = NULL; + fwd->listen_port = 0; + fwd->listen_path = xstrdup(fwdarg[0]); + fwd->connect_host = xstrdup(fwdarg[1]); + fwd->connect_port = a2port(fwdarg[2]); + fwd->connect_path = NULL; + } else if (fwdesc[2]) { + fwd->listen_host = xstrdup(fwdarg[0]); + fwd->listen_port = a2port(fwdarg[1]); + fwd->listen_path = NULL; + fwd->connect_host = NULL; + fwd->connect_port = 0; + fwd->connect_path = xstrdup(fwdarg[2]); + } else { + fwd->listen_host = NULL; + fwd->listen_port = a2port(fwdarg[0]); + fwd->listen_path = NULL; + fwd->connect_host = xstrdup(fwdarg[1]); + fwd->connect_port = a2port(fwdarg[2]); + fwd->connect_path = NULL; + } break; - case 4: fwd->listen_host = xstrdup(cleanhostname(fwdarg[0])); fwd->listen_port = a2port(fwdarg[1]); + fwd->listen_path = NULL; fwd->connect_host = xstrdup(cleanhostname(fwdarg[2])); fwd->connect_port = a2port(fwdarg[3]); + fwd->connect_path = NULL; break; default: i = 0; /* failure */ } - xfree(p); + xfree(p), p = 0; - if (fwd->listen_port == 0 && fwd->connect_port == 0) + if (fwd->listen_port == 0 && fwd->listen_path == NULL + && fwd->connect_port == 0 && fwd->connect_path == NULL) goto fail_free; if (fwd->connect_host != NULL && strlen(fwd->connect_host) >= NI_MAXHOST) goto fail_free; + for (i = 0; i < sizeof fwdarg / sizeof *fwdarg; i++) { + if (fwdarg[i] != NULL) + xfree(fwdarg[i]), fwdarg[i] = NULL; + } + return (i); fail_free: if (fwd->connect_host != NULL) - xfree(fwd->connect_host); + xfree(fwd->connect_host), fwd->connect_host = NULL; + if (fwd->connect_path != NULL) + xfree(fwd->connect_path), fwd->connect_path = NULL; if (fwd->listen_host != NULL) - xfree(fwd->listen_host); + xfree(fwd->listen_host), fwd->listen_host = NULL; + if (fwd->listen_path != NULL) + xfree(fwd->listen_path), fwd->listen_path = NULL; + + if (p) + xfree(p), p = NULL; + + for (i = 0; i < sizeof fwdarg / sizeof *fwdarg; i++) { + if (fwdarg[i] != NULL) + xfree(fwdarg[i]), fwdarg[i] = NULL; + } + return (0); } Index: readconf.h ================================================== ================= --- readconf.h (.../vendor/openssh/4.3p2) (revision 18230) +++ readconf.h (.../branches/b6698/common/openssh4) (revision 18230) @@ -23,14 +23,21 @@ typedef struct { char *listen_host; /* Host (address) to listen on. */ u_short listen_port; /* Port to forward. */ + char *listen_path; /* Path to bind domain socket. */ char *connect_host; /* Host to connect. */ u_short connect_port; /* Port to connect on connect_host. */ + char *connect_path; /* Path to connect domain socket. */ } Forward; /* Data structure for representing option data. */ -#define MAX_SEND_ENV 256 +#define MAX_SEND_ENV 256 -typedef struct { +#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 Options { int forward_agent; /* Forward authentication agent. */ int forward_x11; /* Forward X11 display. */ int forward_x11_trusted; /* Trust Forward X11 display. */ @@ -99,6 +106,10 @@ int num_remote_forwards; Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; int clear_forwardings; + int fake_streamlocal_forwards; + mode_t streamlocal_bind_mask; + int streamlocal_bind_unlink; + int enable_ssh_keysign; int rekey_limit; @@ -110,8 +121,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; Index: channels.c ================================================== ================= --- channels.c (.../vendor/openssh/4.3p2) (revision 18230) +++ channels.c (.../branches/b6698/common/openssh4) (revision 18230) @@ -55,7 +55,11 @@ #include "authfd.h" #include "pathnames.h" #include "bufaux.h" +#include "readconf.h" +#include "servconf.h" +#define FORWARD_PERMISSIONS_FIXED 1 + /* -- channel core */ /* @@ -76,7 +80,22 @@ */ static int channel_max_fd = 0; +/* -- socket naming */ +const SocketName socketname_initializer = { + NULL, /* .host */ + 0, /* .port */ + NULL, /* .path */ +}; + +/* -- forward options */ + +static const ForwardOptions forward_options_initializer = { + 0, /* .tcpip_gateway_ports */ + 0177, /* .streamlocal_bind_mask */ + 0, /* .streamlocal_bind_unlink */ +}; + /* -- tcp forwarding */ /* @@ -86,9 +105,14 @@ * network (which might be behind a firewall). */ typedef struct { +#if FORWARD_PERMISSIONS_FIXED + SocketName connect_addr; + SocketName listen_addr; +#else char *host_to_connect; /* Connect to 'host'. */ u_short port_to_connect; /* Connect to 'port'. */ u_short listen_port; /* Remote side should listen port number. */ +#endif } ForwardPermission; /* List of all permitted host/port pairs to connect. */ @@ -137,6 +161,76 @@ /* helper */ static void port_open_helper(Channel *c, char *rtype); +/* -- socket naming */ + +const char * +socketname_tostring(const SocketName *n) +{ + static char bufs[3][32 + SSH_CHANNEL_PATH_LEN]; + static const int nbufs = sizeof(bufs) / sizeof(bufs[0]); + static struct { char *next, *this; } buf; + static const size_t bufsz = sizeof bufs[0]; + + /* + * Implement a simple ring buffer. Definitely not async-safe, but + * useful when converting multiple socketnames for a printf without + * having to bother with dynamic allocation. + */ + if (buf.next == NULL || buf.next > &bufs[nbufs - 1][0]) + buf.next = &bufs[0][0]; + buf.this = buf.next; + buf.next += bufsz; + + if (SOCKETNAME_IS_STREAMLOCAL(n)) { + snprintf(buf.this, bufsz, "unix:%s", n->path); + } else { + snprintf(buf.this, bufsz, "tcpip:[%s]:%d", + (n->host)? n->host : "LOCALHOST", n->port); + } + + return buf.this; +} + +int +socketname_eq(const SocketName *n1, const SocketName *n2) +{ + if (n1->port != n2->port) { + return 0; + } else if (n1->path != NULL || n2->path != NULL) { + return (n1->path != NULL && n2->path != NULL && + 0 == strcmp(n1->path, n2->path)); + } else if (n1->host != NULL && n2->host != NULL) { + /* + * XXX: Do we need to get more sophisticated, checking for + * wildcards and such? + */ + return (0 == strcmp(n1->host, n2->host)); + } else + return 1; /* If only one empty then they match. */ +} + +/* -- forward options */ + +void +forward_options_init_from_client(ForwardOptions *fwd_opts, struct Options *opts) +{ + *fwd_opts = forward_options_initializer; + + fwd_opts->tcpip_gateway_ports = opts->gateway_ports; + fwd_opts->streamlocal_bind_mask = opts->streamlocal_bind_mask; + fwd_opts->streamlocal_bind_unlink = opts->streamlocal_bind_unlink; +} + +void +forward_options_init_from_server(ForwardOptions *fwd_opts, struct ServerOptions *opts) +{ + *fwd_opts = forward_options_initializer; + + fwd_opts->tcpip_gateway_ports = opts->gateway_ports; + fwd_opts->streamlocal_bind_mask = opts->streamlocal_bind_mask; + fwd_opts->streamlocal_bind_unlink = opts->streamlocal_bind_unlink; +} + /* -- channel core */ Channel * @@ -424,6 +518,8 @@ c = channels[i]; if (c != NULL) { switch (c->type) { + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: @@ -485,6 +581,8 @@ case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_DYNAMIC: @@ -530,6 +628,8 @@ case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: case SSH_CHANNEL_OPENING: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: @@ -578,6 +678,8 @@ case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_ZOMBIE: @@ -1211,13 +1313,10 @@ static void port_open_helper(Channel *c, char *rtype) { - int direct; char buf[1024]; char *remote_ipaddr = get_peer_ipaddr(c->sock); int remote_port = get_peer_port(c->sock); - direct = (strcmp(rtype, "direct-tcpip") == 0); - snprintf(buf, sizeof buf, "%s: listening port %d for %.100s port %d, " "connect from %.200s port %d", @@ -1233,19 +1332,46 @@ packet_put_int(c->self); packet_put_int(c->local_window_max); packet_put_int(c->local_maxpacket); - if (direct) { + if (strcmp(rtype, "direct-tcpip") == 0) { /* target host, port */ packet_put_cstring(c->path); packet_put_int(c->host_port); + } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { + /* target path */ + packet_put_cstring(c->path); + } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { + /* listen path */ + packet_put_cstring(c->path); } else { /* listen address, port */ packet_put_cstring(c->path); packet_put_int(c->listening_port); } - /* originator host and port */ - packet_put_cstring(remote_ipaddr); - packet_put_int((u_int)remote_port); - packet_send(); + + /* + * XXX: Maybe we want to simplify this, since it is a little + * inconsistent with direct forwardings (unless + * FakeStreamLocalForwards is used to switch between + * enhanced direct forwarding modes) originating from the + * unix domain, but this seemed liked a good opportunity to + * keep open the door to remote domain socket forwarding + * being capable of other interesting things. + */ + if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { + /* + * here we could send interestings tidbits, like + * uid=1234 and group=wheel, so something like + * getpeername() could be emulated on the other + * side. + */ + packet_put_cstring(""); /* terminate the list */ + packet_send(); + } else { + /* originator host and port */ + packet_put_cstring(remote_ipaddr); + packet_put_int((u_int)remote_port); + packet_send(); + } } else { packet_start(SSH_MSG_PORT_OPEN); packet_put_int(c->self); @@ -1292,6 +1418,14 @@ if (c->type == SSH_CHANNEL_RPORT_LISTENER) { nextstate = SSH_CHANNEL_OPENING; rtype = "forwarded-tcpip"; + } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { + nextstate = SSH_CHANNEL_OPENING; + rtype = "forwarded-streamlocal@openssh.com"; +#if 0 /* if (!options.FakeStreamLocalForwards) */ + } else if (c->type == SSH_CHANNEL_UNIX_LISTENER) { + nextstate = SSH_CHANNEL_OPENING; + rtype = "direct-streamlocal@openssh.com"; +#endif } else { if (c->host_port == 0) { nextstate = SSH_CHANNEL_DYNAMIC; @@ -1665,6 +1799,8 @@ channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; @@ -1673,6 +1809,8 @@ channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; @@ -2243,6 +2381,7 @@ u_short host_port; char *host, *originator_string; int remote_id, sock = -1; + SocketName name; remote_id = packet_get_int(); host = packet_get_string(NULL); @@ -2254,7 +2393,8 @@ originator_string = xstrdup("unknown (remote did not supply name)"); } packet_check_eom(); - sock = channel_connect_to(host, host_port); + SOCKETNAME_INIT_TCPIP(&name, host, host_port); + sock = channel_connect_to(&name); if (sock != -1) { c = channel_new("connected socket", SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0, @@ -2280,8 +2420,8 @@ } static int -channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port, - const char *host_to_connect, u_short port_to_connect, int gateway_ports) +channel_setup_tcpip_fwd_listener(int type, const char *listen_addr, u_short listen_port, + const char *host_to_connect, u_short port_to_connect, ForwardOptions *opts) { Channel *c; int sock, r, success = 0, wildcard = 0, is_client; @@ -2320,19 +2460,19 @@ addr = NULL; if (listen_addr == NULL) { /* No address specified: default to gateway_ports setting */ - if (gateway_ports) + if (opts->tcpip_gateway_ports) wildcard = 1; - } else if (gateway_ports || is_client) { + } else if (opts->tcpip_gateway_ports || is_client) { if (((datafellows & SSH_OLD_FORWARD_ADDR) && strcmp(listen_addr, "0.0.0.0") == 0) || *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || - (!is_client && gateway_ports == 1)) + (!is_client && opts->tcpip_gateway_ports == 1)) wildcard = 1; else if (strcmp(listen_addr, "localhost") != 0) addr = listen_addr; } - debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s", + debug3("channel_setup_tcpip_fwd_listener: type %d wildcard %d addr %s", type, wildcard, (addr == NULL) ? "NULL" : addr); /* @@ -2350,7 +2490,7 @@ packet_disconnect("getaddrinfo: fatal error: %s", gai_strerror(r)); } else { - error("channel_setup_fwd_listener: " + error("channel_setup_tcpip_fwd_listener: " "getaddrinfo(%.64s): %s", addr, gai_strerror(r)); } return 0; @@ -2361,7 +2501,7 @@ continue; if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { - error("channel_setup_fwd_listener: getnameinfo failed"); + error("channel_setup_tcpip_fwd_listener: getnameinfo failed"); continue; } /* Create a port to listen for the host. */ @@ -2403,24 +2543,131 @@ success = 1; } if (success == 0) - error("channel_setup_fwd_listener: cannot listen to port: %d", + error("channel_setup_tcpip_fwd_listener: cannot listen to port: %d", listen_port); freeaddrinfo(aitop); return success; } +static int +channel_setup_streamlocal_fwd_listener(int type, const char *listen_path, + const char *host_to_connect, u_short port_to_connect, ForwardOptions *opts) +{ + Channel *c; + struct sockaddr_un sun; + int sock; + const char *host_or_path; + mode_t omask; + + host_or_path = (type == SSH_CHANNEL_RUNIX_LISTENER) ? + listen_path : host_to_connect; + + if (host_or_path == NULL) { + error("No forward host or path name."); + return 0; + } + if (strlen(host_or_path) > SSH_CHANNEL_PATH_LEN - 1) { + error("Forward host or path name too long."); + return 0; + } + + debug3("channel_setup_streamlocal_fwd_listener: type %d path %s", + type, listen_path); + + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_LOCAL; + /* strncpy was made for this usage */ + strncpy(sun.sun_path, listen_path, sizeof(sun.sun_path)); + sock = socket(sun.sun_family, SOCK_STREAM, 0); + if (sock < 0) { + verbose("socket: %.100s", strerror(errno)); + return 0; + } + + channel_set_reuseaddr(sock); + + debug("Local forwarding listening on path %s.", listen_path); + + if (opts->streamlocal_bind_unlink == 1 && + 0 != unlink(listen_path) && errno != ENOENT) + error("unlink(%s): %.100s", listen_path, strerror(errno)); + + omask = umask(opts->streamlocal_bind_mask); + + if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) { + error("bind: %.100s", strerror(errno)); + umask(omask); + close(sock); + error("channel_setup_streamlocal_fwd_listener: cannot listen to path: %s", + listen_path); + return 0; + } + if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { + error("listen: %.100s", strerror(errno)); + close(sock); + error("channel_setup_streamlocal_fwd_listener: cannot listen to path: %s", + listen_path); + return 0; + } + c = channel_new("unix listener", type, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, + 0, "port listener", 1); + strlcpy(c->path, host_or_path, sizeof(c->path)); + c->host_port = port_to_connect; + c->listening_port = 0; + return 1; +} + +static int +channel_setup_fwd_listener(int type, const SocketName *listen_addr, + const SocketName *host_to_connect, ForwardOptions *opts) +{ + char *host_or_path = NULL; + u_short port_to_connect = 0; + + if (host_to_connect != NULL) { + host_or_path = (host_to_connect->path)? + host_to_connect->path : host_to_connect->host; + + port_to_connect = host_to_connect->port; + } + + if (type == SSH_CHANNEL_UNIX_LISTENER || type == SSH_CHANNEL_RUNIX_LISTENER) { + return channel_setup_streamlocal_fwd_listener(type, + listen_addr->path, host_or_path, port_to_connect, opts); + } else { + return channel_setup_tcpip_fwd_listener(type, + listen_addr->host, listen_addr->port, host_or_path, + port_to_connect, opts); + } +} + int -channel_cancel_rport_listener(const char *host, u_short port) +channel_cancel_rport_listener(const SocketName *name) { u_int i; int found = 0; + const char *host_or_path; + u_short port; + host_or_path = (name->path)? name->path : name->host; + port = name->port; + for (i = 0; i < channels_alloc; i++) { Channel *c = channels[i]; - if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER && - strncmp(c->path, host, sizeof(c->path)) == 0 && - c->listening_port == port) { + /* + * XXX: Looks like string truncation bug here. Need + * something like a strlcmp(), which can take a limit to + * prevent walking past the end of a non-NUL terminated + * buffer but also handle NUL termination detection and + * comparison appropriately. + */ + if (c != NULL + && (c->type == SSH_CHANNEL_RPORT_LISTENER + || c->type == SSH_CHANNEL_RUNIX_LISTENER) + && strncmp(c->path, host_or_path, sizeof(c->path)) == 0 + && c->listening_port == port) { debug2("%s: close channel %d", __func__, i); channel_free(c); found = 1; @@ -2432,21 +2679,30 @@ /* protocol local port fwd, used by ssh (and sshd in v1) */ int -channel_setup_local_fwd_listener(const char *listen_host, u_short listen_port, - const char *host_to_connect, u_short port_to_connect, int gateway_ports) +channel_setup_local_fwd_listener(const SocketName *listen_addr, + const SocketName *connect_addr, ForwardOptions *opts) { - return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTEN ER, - listen_host, listen_port, host_to_connect, port_to_connect, - gateway_ports); + if (SOCKETNAME_IS_STREAMLOCAL(listen_addr)) { + return channel_setup_fwd_listener(SSH_CHANNEL_UNIX_LISTEN ER, + listen_addr, connect_addr, opts); + } else { + return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTEN ER, + listen_addr, connect_addr, opts); + } } /* protocol v2 remote port fwd, used by sshd */ int -channel_setup_remote_fwd_listener(const char *listen_address, - u_short listen_port, int gateway_ports) +channel_setup_remote_fwd_listener(const SocketName *listen_addr, + ForwardOptions *opts) { - return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTE NER, - listen_address, listen_port, NULL, 0, gateway_ports); + if (SOCKETNAME_IS_STREAMLOCAL(listen_addr)) { + return channel_setup_fwd_listener(SSH_CHANNEL_RUNIX_LISTE NER, + listen_addr, NULL, opts); + } else { + return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTE NER, + listen_addr, NULL, opts); + } } /* @@ -2455,8 +2711,8 @@ */ void -channel_request_remote_forwarding(const char *listen_host, u_short listen_port, - const char *host_to_connect, u_short port_to_connect) +channel_request_remote_forwarding(const SocketName *listen_name, + const SocketName *connect_name) { int type, success = 0; @@ -2466,28 +2722,42 @@ /* Send the forward request to the remote side. */ if (compat20) { - const char *address_to_bind; - if (listen_host == NULL) - address_to_bind = "localhost"; - else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) - address_to_bind = ""; - else - address_to_bind = listen_host; + if (SOCKETNAME_IS_TCPIP(listen_name)) { + const char *address_to_bind; + if (listen_name->host == NULL) + address_to_bind = "localhost"; + else if (*listen_name->host == '\0' || strcmp(listen_name->host, "*") == 0) + address_to_bind = ""; + else + address_to_bind = listen_name->host; - packet_start(SSH2_MSG_GLOBAL_REQUEST); - packet_put_cstring("tcpip-forward"); - packet_put_char(1); /* boolean: want reply */ - packet_put_cstring(address_to_bind); - packet_put_int(listen_port); - packet_send(); - packet_write_wait(); - /* Assume that server accepts the request */ - success = 1; - } else { + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("tcpip-forward"); + packet_put_char(1); /* boolean: want reply */ + packet_put_cstring(address_to_bind); + packet_put_int(listen_name->port); + packet_send(); + packet_write_wait(); + /* Assume that server accepts the request */ + success = 1; + } else { + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("streamlocal-forward@openssh.com"); + packet_put_char(1); /* boolean: want reply */ + packet_put_cstring(listen_name->path); + /* XXX what else do we want to send? */ + packet_send(); + packet_write_wait(); + /* Assume that server accepts the request */ + success = 1; + } + } else if (SOCKETNAME_IS_TCPIP(listen_name)) { packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); - packet_put_int(listen_port); - packet_put_cstring(host_to_connect); - packet_put_int(port_to_connect); + packet_put_int(listen_name->port); + /* XXX okay to send a filesystem path? */ + packet_put_cstring((connect_name->host) ? + connect_name->host : connect_name->path); + packet_put_int(connect_name->port); packet_send(); packet_write_wait(); @@ -2505,12 +2775,24 @@ packet_disconnect("Protocol error for port forward request:" "received packet type %d.", type); } + } else { + logit("Warning: Server does not support remote stream local forwarding."); } if (success) { +#if FORWARD_PERMISSIONS_FIXED + SOCKETNAME_COPY( + &permitted_opens[num_permitted_opens].connect_addr, + connect_name); + SOCKETNAME_COPY( + &permitted_opens[num_permitted_opens].listen_addr, + listen_name); + num_permitted_opens++; +#elif 0 permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; permitted_opens[num_permitted_opens].listen_port = listen_port; num_permitted_opens++; +#endif } } @@ -2519,14 +2801,31 @@ * local side. */ void -channel_request_rforward_cancel(const char *host, u_short port) +channel_request_rforward_cancel(const SocketName *host) { + const char *host_or_path; +#if FORWARD_PERMISSIONS_FIXED int i; +#endif + host_or_path = (host->path)? host->path : host->host; + if (!compat20) return; +#if FORWARD_PERMISSIONS_FIXED for (i = 0; i < num_permitted_opens; i++) { + if ((permitted_opens[i].connect_addr.host != NULL + || permitted_opens[i].connect_addr.path != NULL) + && SOCKETNAME_EQ(&permitted_opens[i].listen_addr, host)) + break; + } + if (i >= num_permitted_opens) { + debug("%s: requested forward not found", __func__); + return; + } +#elif 0 + for (i = 0; i < num_permitted_opens; i++) { if (permitted_opens[i].host_to_connect != NULL && permitted_opens[i].listen_port == port) break; @@ -2535,17 +2834,32 @@ debug("%s: requested forward not found", __func__); return; } - packet_start(SSH2_MSG_GLOBAL_REQUEST); - packet_put_cstring("cancel-tcpip-forward"); - packet_put_char(0); - packet_put_cstring(host == NULL ? "" : host); - packet_put_int(port); - packet_send(); +#endif + if (SOCKETNAME_IS_STREAMLOCAL(host)) { + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("cancel-streamlocal-forward@openssh.com"); + packet_put_char(0); + packet_put_cstring(host_or_path); + packet_send(); + } else { + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("cancel-tcpip-forward"); + packet_put_char(0); + packet_put_cstring(host_or_path == NULL ? "" : host_or_path); + packet_put_int(host->port); + packet_send(); + } + +#if FORWARD_PERMISSIONS_FIXED + SOCKETNAME_FREE_MEMBERS(&permitted_opens[i].connect_addr); + SOCKETNAME_FREE_MEMBERS(&permitted_opens[i].listen_addr); +#elif 0 permitted_opens[i].listen_port = 0; permitted_opens[i].port_to_connect = 0; xfree(permitted_opens[i].host_to_connect); permitted_opens[i].host_to_connect = NULL; +#endif } /* @@ -2555,10 +2869,11 @@ */ void -channel_input_port_forward_request(int is_root, int gateway_ports) +channel_input_port_forward_request(int is_root, ForwardOptions *opts) { u_short port, host_port; char *hostname; + SocketName listen_addr, connect_addr; /* Get arguments from the packet. */ port = packet_get_int(); @@ -2579,8 +2894,10 @@ #endif /* Initiate forwarding */ - channel_setup_local_fwd_listener(NULL, port, hostname, - host_port, gateway_ports); + SOCKETNAME_INIT_TCPIP(&listen_addr, NULL, port); + SOCKETNAME_INIT_TCPIP(&connect_addr, hostname, host_port); + channel_setup_local_fwd_listener(&listen_addr, &connect_addr, + opts); /* Free the argument string. */ xfree(hostname); @@ -2605,9 +2922,15 @@ fatal("channel_request_remote_forwarding: too many forwards"); debug("allow port forwarding to host %s port %d", host, port); +#if FORWARD_PERMISSIONS_FIXED + SOCKETNAME_SET_TCPIP(&permitted_opens[num_permitted_opens].connect_addr, host, port); + SOCKETNAME_INIT(&permitted_opens[num_permitted_opens].listen_addr); + num_permitted_opens++; +#elif 0 permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); permitted_opens[num_permitted_opens].port_to_connect = port; num_permitted_opens++; +#endif all_opens_permitted = 0; } @@ -2617,76 +2940,116 @@ { int i; +#if FORWARD_PERMISSIONS_FIXED + for (i = 0; i < num_permitted_opens; i++) { + SOCKETNAME_FREE_MEMBERS(&permitted_opens[i].connect_addr); + SOCKETNAME_FREE_MEMBERS(&permitted_opens[i].listen_addr); + } + num_permitted_opens = 0; +#elif 0 for (i = 0; i < num_permitted_opens; i++) if (permitted_opens[i].host_to_connect != NULL) xfree(permitted_opens[i].host_to_connect); num_permitted_opens = 0; - +#endif } /* return socket to remote host, port */ static int -connect_to(const char *host, u_short port) +connect_to(const SocketName *name) { struct addrinfo hints, *ai, *aitop; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; int gaierr; + struct sockaddr_un sun; int sock = -1; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = IPv4or6; - hints.ai_socktype = SOCK_STREAM; - snprintf(strport, sizeof strport, "%d", port); - if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { - error("connect_to %.100s: unknown host (%s)", host, - gai_strerror(gaierr)); - return -1; - } - for (ai = aitop; ai; ai = ai->ai_next) { - if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) - continue; - if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), - strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { - error("connect_to: getnameinfo failed"); - continue; - } - sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (SOCKETNAME_IS_STREAMLOCAL(name)) { + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_LOCAL; + /* strncpy was made for this usage */ + strncpy(sun.sun_path, name->path, sizeof(sun.sun_path)); + sock = socket(sun.sun_family, SOCK_STREAM, 0); if (sock < 0) { - if (ai->ai_next == NULL) - error("socket: %.100s", strerror(errno)); - else - verbose("socket: %.100s", strerror(errno)); - continue; + error("socket: %.100s", strerror(errno)); + return -1; } if (set_nonblock(sock) == -1) fatal("%s: set_nonblock(%d)", __func__, sock); - if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && + /* TODO: attempt to unlink socket path if requested */ + if (connect(sock, (struct sockaddr *)&sun, sizeof sun) < 0 && errno != EINPROGRESS) { - error("connect_to %.100s port %s: %.100s", ntop, strport, + error("connect_to path %s: %.100s", name->path, strerror(errno)); close(sock); - continue; /* fail -- try next */ + return -1; } - break; /* success */ - + } else { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", name->port); + if ((gaierr = getaddrinfo(name->host, strport, &hints, &aitop)) != 0) { + error("connect_to %.100s: unknown host (%s)", name->host, + gai_strerror(gaierr)); + return -1; + } + for (ai = aitop; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), + strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + error("connect_to: getnameinfo failed"); + continue; + } + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock < 0) { + if (ai->ai_next == NULL) + error("socket: %.100s", strerror(errno)); + else + verbose("socket: %.100s", strerror(errno)); + continue; + } + if (set_nonblock(sock) == -1) + fatal("%s: set_nonblock(%d)", __func__, sock); + if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && + errno != EINPROGRESS) { + error("connect_to %.100s port %s: %.100s", ntop, strport, + strerror(errno)); + close(sock); + continue; /* fail -- try next */ + } + break; /* success */ + + } + freeaddrinfo(aitop); + if (!ai) { + error("connect_to %.100s port %d: failed.", name->host, name->port); + return -1; + } } - freeaddrinfo(aitop); - if (!ai) { - error("connect_to %.100s port %d: failed.", host, port); - return -1; - } /* success */ set_nodelay(sock); return sock; } int -channel_connect_by_listen_address(u_short listen_port) +channel_connect_by_listen_address(const SocketName *name) { +#if FORWARD_PERMISSIONS_FIXED int i; for (i = 0; i < num_permitted_opens; i++) + if (SOCKETNAME_ISSET(&permitted_opens[i].connect_addr) + && SOCKETNAME_EQ(&permitted_opens[i].listen_addr, name)) + return connect_to(&permitted_opens[i].connect_addr); + error("WARNING: Server requests forwarding for unknown address %s", + SOCKETNAME_TOSTRING(name)); +#elif 0 + int i; + + for (i = 0; i < num_permitted_opens; i++) if (permitted_opens[i].host_to_connect != NULL && permitted_opens[i].listen_port == listen_port) return connect_to( @@ -2694,30 +3057,45 @@ permitted_opens[i].port_to_connect); error("WARNING: Server requests forwarding for unknown listen_port %d", listen_port); +#endif return -1; } /* Check if connecting to that port is permitted and connect. */ int -channel_connect_to(const char *host, u_short port) +channel_connect_to(const SocketName *name) { int i, permit; permit = all_opens_permitted; +#if FORWARD_PERMISSIONS_FIXED if (!permit) { for (i = 0; i < num_permitted_opens; i++) + if (SOCKETNAME_EQ(&permitted_opens[i].connect_addr, name)) + permit = 1; + + } +#elif 0 + if (!permit) { + for (i = 0; i < num_permitted_opens; i++) if (permitted_opens[i].host_to_connect != NULL && permitted_opens[i].port_to_connect == port && strcmp(permitted_opens[i].host_to_connect, host) == 0) permit = 1; } +#endif if (!permit) { - logit("Received request to connect to host %.100s port %d, " - "but the request was denied.", host, port); + if (name->path != NULL) + logit("Received request to connect to socket %.100s, " + "but the request was denied.", name->path); + else + logit("Received request to connect to host %.100s port %d, " + "but the request was denied.", name->host, name->port); + return -1; } - return connect_to(host, port); + return connect_to(name); } void Index: ssh_config.0 ================================================== ================= --- ssh_config.0 (.../vendor/openssh/4.3p2) (revision 18230) +++ ssh_config.0 (.../branches/b6698/common/openssh4) (revision 18230) @@ -161,6 +161,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 port on the local machine be forwarded over the secure channel, and the application protocol is then used to Index: channels.h ================================================== ================= --- channels.h (.../vendor/openssh/4.3p2) (revision 18230) +++ channels.h (.../branches/b6698/common/openssh4) (revision 18230) @@ -40,6 +40,93 @@ #include "buffer.h" +/* Definitions and data structures to describe a socket address. */ +#define SOCKETNAME_INITIALIZER (socketname_initializer) + +#define SOCKETNAME_INIT(n) do { \ + *(n) = SOCKETNAME_INITIALIZER; \ +} while(0) + +#define SOCKETNAME_INIT_TCPIP(n, h, p) do { \ + SOCKETNAME_INIT((n)); \ + (n)->host = (h); \ + (n)->port = (p); \ +} while(0) + +#define SOCKETNAME_INIT_STREAMLOCAL(n, p) do { \ + SOCKETNAME_INIT((n)); \ + (n)->path = (p); \ +} while(0) + +#define SOCKETNAME_COPY(dst, src) do { \ + *(dst) = SOCKETNAME_INITIALIZER; \ + if ((src)->host) \ + (dst)->host = xstrdup((src)->host); \ + (dst)->port = (src)->port; \ + if ((src)->path) \ + (dst)->path = xstrdup((src)->path); \ +} while(0) + +#define SOCKETNAME_FREE_MEMBERS(n) do { \ + if ((n)->host) \ + xfree((n)->host); \ + if ((n)->path) \ + xfree((n)->path); \ + SOCKETNAME_INIT(n); \ +} while(0) + +#define SOCKETNAME_SET_TCPIP(n, h, p) do { \ + SOCKETNAME_INIT(n); \ + if ((h) != NULL) \ + (n)->host = xstrdup((h)); \ + (n)->port = (p); \ +} while(0) + +#define SOCKETNAME_SET_STREAMLOCAL(n, p) do { \ + SOCKETNAME_INIT(n); \ + (n)->path = xstrdup((p)); \ +} while(0) + +#define SOCKETNAME_TOSTRING(n) socketname_tostring((n)) + +#define SOCKETNAME_ISSET(n) ((n)->host != NULL || \ + (n)->path != NULL || \ + (n)->port > 0) +#define SOCKETNAME_IS_TCPIP(n) ((n)->path == NULL) +#define SOCKETNAME_IS_STREAMLOCAL(n) ((n)->path != NULL) + +#define SOCKETNAME_EQ(n1, n2) socketname_eq((n1), (n2)) + +struct SocketName; +typedef struct SocketName SocketName; + +struct SocketName { + char *host; /* Inet address. */ + u_short port; /* Inet port. */ + char *path; /* Local (Unix domain) path. */ +}; + +const char *socketname_tostring(const SocketName *); + +int socketname_eq(const SocketName *, const SocketName *); + +extern const SocketName socketname_initializer; + +/* Port forwarding options. */ +struct Options; +struct ServerOptions; + +typedef struct { + int tcpip_gateway_ports; + mode_t streamlocal_bind_mask; + int streamlocal_bind_unlink; +} ForwardOptions; + +extern const ForwardOptions forward_options_initializer; + +void forward_options_init_from_client(ForwardOptions *, struct Options *); +void forward_options_init_from_server(ForwardOptions *, struct ServerOptions *); + /* Definitions for channel types. */ #define SSH_CHANNEL_X11_LISTENER 1 /* Listening for inet X11 conn. */ #define SSH_CHANNEL_PORT_LISTENER 2 /* Listening on a port. */ @@ -55,7 +142,9 @@ #define SSH_CHANNEL_CONNECTING 12 #define SSH_CHANNEL_DYNAMIC 13 #define SSH_CHANNEL_ZOMBIE 14 /* Almost dead. */ -#define SSH_CHANNEL_MAX_TYPE 15 +#define SSH_CHANNEL_UNIX_LISTENER 15 /* Listening on a domain socket. */ +#define SSH_CHANNEL_RUNIX_LISTENER 16 /* Listening to a R-style domain socket */ +#define SSH_CHANNEL_MAX_TYPE 17 #define SSH_CHANNEL_PATH_LEN 256 @@ -208,16 +297,16 @@ void channel_permit_all_opens(void); void channel_add_permitted_opens(char *, int); void channel_clear_permitted_opens(void); -void channel_input_port_forward_request(int, int); -int channel_connect_to(const char *, u_short); -int channel_connect_by_listen_address(u_short); -void channel_request_remote_forwarding(const char *, u_short, - const char *, u_short); -int channel_setup_local_fwd_listener(const char *, u_short, - const char *, u_short, int); -void channel_request_rforward_cancel(const char *host, u_short port); -int channel_setup_remote_fwd_listener(const char *, u_short, int); -int channel_cancel_rport_listener(const char *, u_short); +void channel_input_port_forward_request(int, ForwardOptions *); +int channel_connect_to(const SocketName *); +int channel_connect_by_listen_address(const SocketName *); +void channel_request_remote_forwarding(const SocketName *, + const SocketName *); +int channel_setup_local_fwd_listener(const SocketName *, + const SocketName *, ForwardOptions *); +void channel_request_rforward_cancel(const SocketName *); +int channel_setup_remote_fwd_listener(const SocketName *, ForwardOptions *); +int channel_cancel_rport_listener(const SocketName *); /* x11 forwarding */ Index: ssh_config.5 ================================================== ================= --- ssh_config.5 (.../vendor/openssh/4.3p2) (revision 18230) +++ ssh_config.5 (.../branches/b6698/common/openssh4) (revision 18230) @@ -317,6 +317,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 port on the local machine be forwarded over the secure channel, and the application |