Index: scp.0 =================================================================== --- scp.0 (.../vendor/openssh/4.4p1) (revision 26244) +++ scp.0 (.../branches/b9360/common/openssh4) (revision 26244) @@ -70,6 +70,11 @@ ConnectTimeout ControlMaster ControlPath + ControlBindMask + ControlAllowUsers + ControlAllowGroups + ControlDenyUsers + ControlDenyGroups GlobalKnownHostsFile GSSAPIAuthentication GSSAPIDelegateCredentials Index: scp.1 =================================================================== --- scp.1 (.../vendor/openssh/4.4p1) (revision 26244) +++ scp.1 (.../branches/b9360/common/openssh4) (revision 26244) @@ -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.4p1) (revision 26244) +++ ssh.0 (.../branches/b9360/common/openssh4) (revision 26244) @@ -223,6 +223,11 @@ ConnectTimeout ControlMaster ControlPath + ControlBindMask + ControlAllowUsers + ControlAllowGroups + ControlDenyUsers + ControlDenyGroups DynamicForward EscapeChar ExitOnForwardFailure Index: ssh.1 =================================================================== --- ssh.1 (.../vendor/openssh/4.4p1) (revision 26244) +++ ssh.1 (.../branches/b9360/common/openssh4) (revision 26244) @@ -447,6 +447,11 @@ .It ConnectTimeout .It ControlMaster .It ControlPath +.It ControlBindMask +.It ControlAllowUsers +.It ControlAllowGroups +.It ControlDenyUsers +.It ControlDenyGroups .It DynamicForward .It EscapeChar .It ExitOnForwardFailure Index: sftp.0 =================================================================== --- sftp.0 (.../vendor/openssh/4.4p1) (revision 26244) +++ sftp.0 (.../branches/b9360/common/openssh4) (revision 26244) @@ -74,6 +74,11 @@ ConnectTimeout ControlMaster ControlPath + ControlBindMask + ControlAllowUsers + ControlAllowGroups + ControlDenyUsers + ControlDenyGroups GlobalKnownHostsFile GSSAPIAuthentication GSSAPIDelegateCredentials Index: sftp.1 =================================================================== --- sftp.1 (.../vendor/openssh/4.4p1) (revision 26244) +++ sftp.1 (.../branches/b9360/common/openssh4) (revision 26244) @@ -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.4p1) (revision 26244) +++ ssh.c (.../branches/b9360/common/openssh4) (revision 26244) @@ -809,23 +809,27 @@ { 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 != i && options.exit_on_forward_failure) fatal("Could not request local forwarding."); @@ -834,18 +838,20 @@ /* 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)); if (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) < 0) { + &listen_on, &connect_to) < 0) { if (options.exit_on_forward_failure) fatal("Could not request remote forwarding."); else @@ -1070,7 +1076,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: sshd.c =================================================================== --- sshd.c (.../vendor/openssh/4.4p1) (revision 26244) +++ sshd.c (.../branches/b9360/common/openssh4) (revision 26244) @@ -418,7 +418,11 @@ major = PROTOCOL_MAJOR_1; minor = PROTOCOL_MINOR_1; } - snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION); + if (options.protocol_banner != NULL) { + snprintf(buf, sizeof buf, "%s\n", options.protocol_banner); + } else { + snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION); + } server_version_string = xstrdup(buf); /* Send our protocol version identification. */ Index: session.c =================================================================== --- session.c (.../vendor/openssh/4.4p1) (revision 26244) +++ session.c (.../branches/b9360/common/openssh4) (revision 26244) @@ -260,6 +260,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) { @@ -347,8 +348,9 @@ break; } debug("Received TCP/IP port forwarding request."); + forward_options_init_from_server(&fwd_opts, &options); if (channel_input_port_forward_request(s->pw->pw_uid == 0, - options.gateway_ports) < 0) { + &fwd_opts) < 0) { debug("Port forwarding failed."); break; } Index: servconf.c =================================================================== --- servconf.c (.../vendor/openssh/4.4p1) (revision 26244) +++ servconf.c (.../branches/b9360/common/openssh4) (revision 26244) @@ -122,6 +122,9 @@ options->permit_tun = -1; options->num_permitted_opens = -1; options->adm_forced_command = NULL; + options->streamlocal_bind_mask = 0177; + options->streamlocal_bind_unlink = -1; + options->protocol_banner = NULL; } void @@ -249,6 +252,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) @@ -291,6 +296,8 @@ sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, + sStreamLocalBindMask, sStreamLocalBindUnlink, + sProtocolBanner, sMatch, sPermitOpen, sForceCommand, sUsePrivilegeSeparation, sDeprecated, sUnsupported @@ -316,7 +323,7 @@ /* Standard Options */ { "port", sPort, SSHCFG_GLOBAL }, { "hostkey", sHostKeyFile, SSHCFG_GLOBAL }, - { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */ + { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */ { "pidfile", sPidFile, SSHCFG_GLOBAL }, { "serverkeybits", sServerKeyBits, SSHCFG_GLOBAL }, { "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL }, @@ -366,7 +373,7 @@ { "printlastlog", sPrintLastLog, SSHCFG_GLOBAL }, { "ignorerhosts", sIgnoreRhosts, SSHCFG_GLOBAL }, { "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_GLOBAL }, - { "x11forwarding", sX11Forwarding, SSHCFG_ALL }, + { "x11forwarding", sX11Forwarding, SSHCFG_ALL, }, { "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL }, { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, @@ -377,7 +384,7 @@ { "compression", sCompression, SSHCFG_GLOBAL }, { "tcpkeepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, { "keepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, /* obsolete alias */ - { "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL }, + { "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_GLOBAL }, { "allowusers", sAllowUsers, SSHCFG_GLOBAL }, { "denyusers", sDenyUsers, SSHCFG_GLOBAL }, { "allowgroups", sAllowGroups, SSHCFG_GLOBAL }, @@ -403,6 +410,9 @@ { "match", sMatch, SSHCFG_ALL }, { "permitopen", sPermitOpen, SSHCFG_ALL }, { "forcecommand", sForceCommand, SSHCFG_ALL }, + { "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_GLOBAL }, + { "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_GLOBAL }, + { "protocolbanner", sProtocolBanner, SSHCFG_GLOBAL }, { NULL, sBadOption, 0 } }; @@ -620,7 +630,7 @@ const char *filename, int linenum, int *activep, const char *user, const char *host, const char *address) { - char *cp, **charptr, *arg, *p; + char *cp, **charptr, *endofnumber, *arg, *p; int cmdline = 0, *intptr, value, n; ServerOpCodes opcode; u_short port; @@ -1204,6 +1214,25 @@ *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 sProtocolBanner: + charptr = &options->protocol_banner; + goto parse_filename; + case sMatch: if (cmdline) fatal("Match directive not supported as a command-line " Index: regress/forwarding.sh =================================================================== --- regress/forwarding.sh (.../vendor/openssh/4.4p1) (revision 26244) +++ regress/forwarding.sh (.../branches/b9360/common/openssh4) (revision 26244) @@ -93,3 +93,18 @@ fi sleep 10 done + +trace "transfer over chained unix domain socket forwards and check result" +rm -f $OBJ/unix-1.fwd +rm -f $OBJ/unix-2.fwd +rm -f $OBJ/unix-3.fwd +${SSH} -F $OBJ/ssh_config -f -R127.0.0.1:${base}01:[$OBJ/unix-1.fwd] somehost sleep 10 +${SSH} -F $OBJ/ssh_config -f -L[$OBJ/unix-1.fwd]:[$OBJ/unix-2.fwd] somehost sleep 10 +${SSH} -F $OBJ/ssh_config -f -R[$OBJ/unix-2.fwd]:[$OBJ/unix-3.fwd] somehost sleep 10 +${SSH} -F $OBJ/ssh_config -f -L[$OBJ/unix-3.fwd]:127.0.0.1:$PORT somehost sleep 10 +${SSH} -F $OBJ/ssh_config -p${base}01 -o 'ConnectionAttempts=4' \ + somehost cat $DATA > $OBJ/ls.copy +test -f $OBJ/ls.copy || fail "failed copy $DATA" +cmp $DATA $OBJ/ls.copy || fail "corrupted copy of $DATA" + +sleep 10 Index: servconf.h =================================================================== --- servconf.h (.../vendor/openssh/4.4p1) (revision 26244) +++ servconf.h (.../branches/b9360/common/openssh4) (revision 26244) @@ -36,7 +36,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. */ @@ -141,6 +141,14 @@ int permit_tun; int num_permitted_opens; + + mode_t streamlocal_bind_mask; /* + * Mask to use when binding forwarded + * domain sockets. + */ + int streamlocal_bind_unlink;/* Attempt unlink before binding? */ + + char *protocol_banner; } ServerOptions; void initialize_server_options(ServerOptions *); Index: clientloop.c =================================================================== --- clientloop.c (.../vendor/openssh/4.4p1) (revision 26244) +++ clientloop.c (.../branches/b9360/common/openssh4) (revision 26244) @@ -84,6 +84,7 @@ #include #include #include +#include #include #include "xmalloc.h" @@ -700,6 +701,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) { @@ -711,8 +790,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 @@ -728,16 +805,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); @@ -921,6 +1003,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); @@ -992,23 +1076,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 { - if (channel_request_remote_forwarding(fwd.listen_host, - fwd.listen_port, fwd.connect_host, - fwd.connect_port) < 0) { + if (channel_request_remote_forwarding(&listen_host, + &connect_host) < 0) { logit("Port forwarding failed."); goto out; } @@ -1682,6 +1768,7 @@ Channel *c = NULL; char *listen_address, *originator_address; int listen_port, originator_port; + SocketName name; int sock; /* Get rest of the packet */ @@ -1694,7 +1781,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); @@ -1709,7 +1797,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; @@ -1785,6 +1930,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.4p1) (revision 26244) +++ serverloop.c (.../branches/b9360/common/openssh4) (revision 26244) @@ -927,6 +927,7 @@ int sock; char *target, *originator; int target_port, originator_port; + SocketName name; target = packet_get_string(NULL); target_port = packet_get_int(); @@ -938,7 +939,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) @@ -950,6 +952,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; @@ -1040,6 +1072,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(); } @@ -1076,6 +1110,8 @@ char *rtype; int want_reply; int success = 0; + SocketName name; + ForwardOptions fwd_opts; rtype = packet_get_string(NULL); want_reply = packet_get_char(); @@ -1106,8 +1142,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) { @@ -1119,9 +1157,39 @@ 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); xfree(cancel_address); + } 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.4p1) (revision 26244) +++ readconf.c (.../branches/b9360/common/openssh4) (revision 26244) @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -124,11 +125,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; @@ -210,6 +214,9 @@ { "smartcarddevice", oUnsupported }, #endif { "clearallforwardings", oClearAllForwardings }, + { "fakestreamlocalforwards", oFakeStreamLocalForwards }, + { "streamlocalbindmask", oStreamLocalBindMask }, + { "streamlocalbindunlink", oStreamLocalBindUnlink }, { "enablesshkeysign", oEnableSSHKeysign }, { "verifyhostkeydns", oVerifyHostKeyDNS }, { "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost }, @@ -219,8 +226,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 }, @@ -240,7 +252,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) @@ -250,8 +263,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); } /* @@ -271,8 +289,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 @@ -758,6 +781,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') @@ -840,6 +882,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; @@ -868,6 +921,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; @@ -1045,6 +1138,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; @@ -1057,8 +1153,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; @@ -1173,6 +1274,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) @@ -1207,7 +1312,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 @@ -1216,8 +1427,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)); @@ -1227,47 +1441,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.4p1) (revision 26244) +++ readconf.h (.../branches/b9360/common/openssh4) (revision 26244) @@ -21,14 +21,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. */ @@ -98,6 +105,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; @@ -109,8 +120,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.4p1) (revision 26244) +++ channels.c (.../branches/b9360/common/openssh4) (revision 26244) @@ -75,7 +75,12 @@ #include "key.h" #include "authfd.h" #include "pathnames.h" +#include "bufaux.h" +#include "readconf.h" +#include "servconf.h" +#define FORWARD_PERMISSIONS_FIXED 1 + /* -- channel core */ /* @@ -96,7 +101,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 */ /* @@ -106,9 +126,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 by the user. */ @@ -164,6 +189,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 * @@ -443,6 +538,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: @@ -502,6 +599,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: @@ -546,6 +645,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: @@ -593,6 +694,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: @@ -1238,13 +1341,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", @@ -1260,19 +1360,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); @@ -1320,8 +1447,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"; } else { - if (c->host_port == 0) { + if (c->host_port == -1) { + nextstate = SSH_CHANNEL_OPENING; + rtype = "direct-streamlocal@openssh.com"; + } else if (c->host_port == 0) { nextstate = SSH_CHANNEL_DYNAMIC; rtype = "dynamic-tcpip"; } else { @@ -1709,6 +1842,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; @@ -1717,6 +1852,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; @@ -2301,6 +2438,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); @@ -2312,7 +2450,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, @@ -2338,8 +2477,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, int port_to_connect, ForwardOptions *opts) { Channel *c; int sock, r, success = 0, wildcard = 0, is_client; @@ -2378,19 +2517,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); /* @@ -2408,7 +2547,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; @@ -2419,7 +2558,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. */ @@ -2461,24 +2600,143 @@ 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, int 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; + } + + umask(omask); + + 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, "unix 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; + int port_to_connect = 0; + + /* + * Passing port_to_connect as -1 when the destination is a domain + * socket is quite the hack! This special value is then used by + * channel_post_port_listener(), similar to the SOCKS4/SOCKS5 check, + * to determine the type of request message to send. + */ + if (host_to_connect != NULL) { + if (SOCKETNAME_IS_STREAMLOCAL(host_to_connect)) { + host_or_path = host_to_connect->path; + port_to_connect = -1; + } else { + host_or_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; @@ -2490,21 +2748,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_LISTENER, - 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_LISTENER, + listen_addr, connect_addr, opts); + } else { + return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, + 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_LISTENER, - listen_address, listen_port, NULL, 0, gateway_ports); + if (SOCKETNAME_IS_STREAMLOCAL(listen_addr)) { + return channel_setup_fwd_listener(SSH_CHANNEL_RUNIX_LISTENER, + listen_addr, NULL, opts); + } else { + return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, + listen_addr, NULL, opts); + } } /* @@ -2513,8 +2780,8 @@ */ int -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; @@ -2524,28 +2791,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(); @@ -2562,12 +2843,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 } return (success ? 0 : -1); } @@ -2577,14 +2870,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; @@ -2593,17 +2903,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 } /* @@ -2612,11 +2937,12 @@ * message if there was an error). */ int -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; int success = 0; char *hostname; + SocketName listen_addr, connect_addr; /* Get arguments from the packet. */ port = packet_get_int(); @@ -2637,8 +2963,10 @@ #endif /* Initiate forwarding */ - success = 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); + success = channel_setup_local_fwd_listener(&listen_addr, &connect_addr, + opts); /* Free the argument string. */ xfree(hostname); @@ -2665,9 +2993,15 @@ fatal("channel_add_permitted_opens: 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; } @@ -2679,10 +3013,18 @@ fatal("channel_add_adm_permitted_opens: too many forwards"); debug("config allows port forwarding to host %s port %d", host, port); +#if FORWARD_PERMISSIONS_FIXED + SOCKETNAME_SET_TCPIP(&permitted_adm_opens[num_adm_permitted_opens].connect_addr, host, port); + SOCKETNAME_INIT(&permitted_adm_opens[num_adm_permitted_opens].listen_addr); + return ++num_adm_permitted_opens; +#elif 0 permitted_adm_opens[num_adm_permitted_opens].host_to_connect = xstrdup(host); permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; return ++num_adm_permitted_opens; +#else + return 0; +#endif } void @@ -2690,10 +3032,18 @@ { 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 } void @@ -2701,74 +3051,115 @@ { int i; +#if FORWARD_PERMISSIONS_FIXED + for (i = 0; i < num_adm_permitted_opens; i++) { + SOCKETNAME_FREE_MEMBERS(&permitted_adm_opens[i].connect_addr); + SOCKETNAME_FREE_MEMBERS(&permitted_adm_opens[i].listen_addr); + } + num_adm_permitted_opens = 0; +#elif 0 for (i = 0; i < num_adm_permitted_opens; i++) if (permitted_adm_opens[i].host_to_connect != NULL) xfree(permitted_adm_opens[i].host_to_connect); num_adm_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( @@ -2776,22 +3167,38 @@ 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_adm = 1; 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; + } + + if (num_adm_permitted_opens > 0) { + permit_adm = 0; + for (i = 0; i < num_adm_permitted_opens; i++) + if (SOCKETNAME_EQ(&permitted_adm_opens[i].connect_addr, name)) + permit_adm = 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; + } if (num_adm_permitted_opens > 0) { @@ -2803,13 +3210,18 @@ == 0) permit_adm = 1; } - +#endif if (!permit || !permit_adm) { - 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.4p1) (revision 26244) +++ ssh_config.0 (.../branches/b9360/common/openssh4) (revision 26244) @@ -163,6 +163,38 @@ opportunistic connection sharing include at least %h, %p, and %r. This ensures that shared connections 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.4p1) (revision 26244) +++ channels.h (.../branches/b9360/common/openssh4) (revision 26244) @@ -38,6 +38,93 @@ #ifndef CHANNEL_H #define CHANNEL_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. */ @@ -53,7 +140,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 @@ int channel_add_adm_permitted_opens(char *, int); void channel_clear_permitted_opens(void); void channel_clear_adm_permitted_opens(void); -int channel_input_port_forward_request(int, int); -int channel_connect_to(const char *, u_short); -int channel_connect_by_listen_address(u_short); -int 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); +int channel_input_port_forward_request(int, ForwardOptions *); +int channel_connect_to(const SocketName *); +int channel_connect_by_listen_address(const SocketName *); +int 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.4p1) (revision 26244) +++ ssh_config.5 (.../branches/b9360/common/openssh4) (revision 26244) @@ -320,6 +320,36 @@ used for opportunistic connection sharing include at least %h, %p, and %r. 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