X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/399d1ea6b10ba2986888ce173947bf92214b1390..6a2c4cd8b3908e0d7e28789e83b4dc2b69aaf6d4:/channels.c diff --git a/channels.c b/channels.c index 50d6f16a..a72d9b93 100644 --- a/channels.c +++ b/channels.c @@ -39,14 +39,13 @@ */ #include "includes.h" -RCSID("$OpenBSD: channels.c,v 1.168 2002/02/14 23:27:59 markus Exp $"); +RCSID("$OpenBSD: channels.c,v 1.207 2004/06/21 17:36:31 avsm Exp $"); #include "ssh.h" #include "ssh1.h" #include "ssh2.h" #include "packet.h" #include "xmalloc.h" -#include "uidswap.h" #include "log.h" #include "misc.h" #include "channels.h" @@ -55,7 +54,7 @@ RCSID("$OpenBSD: channels.c,v 1.168 2002/02/14 23:27:59 markus Exp $"); #include "key.h" #include "authfd.h" #include "pathnames.h" - +#include "bufaux.h" /* -- channel core */ @@ -129,10 +128,6 @@ static u_int x11_fake_data_len; #define NUM_SOCKS 10 -/* Name and directory of socket for authentication agent forwarding. */ -static char *auth_sock_name = NULL; -static char *auth_sock_dir = NULL; - /* AF_UNSPEC or AF_INET or AF_INET6 */ static int IPv4or6 = AF_UNSPEC; @@ -146,13 +141,13 @@ channel_lookup(int id) { Channel *c; - if (id < 0 || id > channels_alloc) { - log("channel_lookup: %d: bad id", id); + if (id < 0 || id >= channels_alloc) { + logit("channel_lookup: %d: bad id", id); return NULL; } c = channels[id]; if (c == NULL) { - log("channel_lookup: %d: bad id: channel free", id); + logit("channel_lookup: %d: bad id: channel free", id); return NULL; } return c; @@ -177,12 +172,13 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd, c->rfd = rfd; c->wfd = wfd; c->sock = (rfd == wfd) ? rfd : -1; + c->ctl_fd = -1; /* XXX: set elsewhere */ c->efd = efd; c->extended_usage = extusage; /* XXX ugly hack: nonblock is only set by the server */ if (nonblock && isatty(c->rfd)) { - debug("channel %d: rfd %d isatty", c->self, c->rfd); + debug2("channel %d: rfd %d isatty", c->self, c->rfd); c->isatty = 1; if (!isatty(c->wfd)) { error("channel %d: wfd %d is not a tty?", @@ -191,6 +187,7 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd, } else { c->isatty = 0; } + c->wfd_isatty = isatty(c->wfd); /* enable nonblocking mode */ if (nonblock) { @@ -210,7 +207,7 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd, Channel * channel_new(char *ctype, int type, int rfd, int wfd, int efd, - int window, int maxpack, int extusage, char *remote_name, int nonblock) + u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) { int i, found; Channel *c; @@ -221,7 +218,6 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, channels = xmalloc(channels_alloc * sizeof(Channel *)); for (i = 0; i < channels_alloc; i++) channels[i] = NULL; - fatal_add_cleanup((void (*) (void *)) channel_free_all, NULL); } /* Try to find a free slot where to put the new channel. */ for (found = -1, i = 0; i < channels_alloc; i++) @@ -233,9 +229,13 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, if (found == -1) { /* There are no free slots. Take last+1 slot and expand the array. */ found = channels_alloc; + if (channels_alloc > 10000) + fatal("channel_new: internal error: channels_alloc %d " + "too big.", channels_alloc); + channels = xrealloc(channels, + (channels_alloc + 10) * sizeof(Channel *)); channels_alloc += 10; debug2("channel: expanding %d", channels_alloc); - channels = xrealloc(channels, channels_alloc * sizeof(Channel *)); for (i = found; i < channels_alloc; i++) channels[i] = NULL; } @@ -257,13 +257,14 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, c->local_consumed = 0; c->local_maxpacket = maxpack; c->remote_id = -1; - c->remote_name = remote_name; + c->remote_name = xstrdup(remote_name); c->remote_window = 0; c->remote_maxpacket = 0; c->force_drain = 0; c->single_connection = 0; c->detach_user = NULL; c->confirm = NULL; + c->confirm_ctx = NULL; c->input_filter = NULL; debug("channel %d: new [%s]", found, remote_name); return c; @@ -305,10 +306,11 @@ channel_close_fd(int *fdp) static void channel_close_fds(Channel *c) { - debug3("channel_close_fds: channel %d: r %d w %d e %d", - c->self, c->rfd, c->wfd, c->efd); + debug3("channel %d: close_fds r %d w %d e %d c %d", + c->self, c->rfd, c->wfd, c->efd, c->ctl_fd); channel_close_fd(&c->sock); + channel_close_fd(&c->ctl_fd); channel_close_fd(&c->rfd); channel_close_fd(&c->wfd); channel_close_fd(&c->efd); @@ -325,15 +327,17 @@ channel_free(Channel *c) for (n = 0, i = 0; i < channels_alloc; i++) if (channels[i]) n++; - debug("channel_free: channel %d: %s, nchannels %d", c->self, + debug("channel %d: free: %s, nchannels %d", c->self, c->remote_name ? c->remote_name : "???", n); s = channel_open_message(); - debug3("channel_free: status: %s", s); + debug3("channel %d: status: %s", c->self, s); xfree(s); if (c->sock != -1) shutdown(c->sock, SHUT_RDWR); + if (c->ctl_fd != -1) + shutdown(c->ctl_fd, SHUT_RDWR); channel_close_fds(c); buffer_free(&c->input); buffer_free(&c->output); @@ -414,13 +418,13 @@ channel_not_very_much_buffered_data(void) #if 0 if (!compat20 && buffer_len(&c->input) > packet_get_maxsize()) { - debug("channel %d: big input buffer %d", + debug2("channel %d: big input buffer %d", c->self, buffer_len(&c->input)); return 0; } #endif if (buffer_len(&c->output) > packet_get_maxsize()) { - debug("channel %d: big output buffer %d > %d", + debug2("channel %d: big output buffer %u > %u", c->self, buffer_len(&c->output), packet_get_maxsize()); return 0; @@ -483,7 +487,7 @@ channel_find_open(void) for (i = 0; i < channels_alloc; i++) { c = channels[i]; - if (c == NULL) + if (c == NULL || c->remote_id < 0) continue; switch (c->type) { case SSH_CHANNEL_CLOSED: @@ -551,12 +555,13 @@ channel_open_message(void) case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: - snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n", + snprintf(buf, sizeof buf, + " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cfd %d)\r\n", c->self, c->remote_name, c->type, c->remote_id, c->istate, buffer_len(&c->input), c->ostate, buffer_len(&c->output), - c->rfd, c->wfd); + c->rfd, c->wfd, c->ctl_fd); buffer_append(&buffer, buf, strlen(buf)); continue; default: @@ -574,11 +579,12 @@ void channel_send_open(int id) { Channel *c = channel_lookup(id); + if (c == NULL) { - log("channel_send_open: %d: bad id", id); + logit("channel_send_open: %d: bad id", id); return; } - debug("send channel open %d", id); + debug2("channel %d: send open", id); packet_start(SSH2_MSG_CHANNEL_OPEN); packet_put_cstring(c->ctype); packet_put_int(c->self); @@ -588,35 +594,39 @@ channel_send_open(int id) } void -channel_request_start(int local_id, char *service, int wantconfirm) +channel_request_start(int id, char *service, int wantconfirm) { - Channel *c = channel_lookup(local_id); + Channel *c = channel_lookup(id); + if (c == NULL) { - log("channel_request_start: %d: unknown channel id", local_id); + logit("channel_request_start: %d: unknown channel id", id); return; } - debug("channel request %d: %s", local_id, service) ; + debug2("channel %d: request %s confirm %d", id, service, wantconfirm); packet_start(SSH2_MSG_CHANNEL_REQUEST); packet_put_int(c->remote_id); packet_put_cstring(service); packet_put_char(wantconfirm); } void -channel_register_confirm(int id, channel_callback_fn *fn) +channel_register_confirm(int id, channel_callback_fn *fn, void *ctx) { Channel *c = channel_lookup(id); + if (c == NULL) { - log("channel_register_comfirm: %d: bad id", id); + logit("channel_register_comfirm: %d: bad id", id); return; } c->confirm = fn; + c->confirm_ctx = ctx; } void channel_register_cleanup(int id, channel_callback_fn *fn) { Channel *c = channel_lookup(id); + if (c == NULL) { - log("channel_register_cleanup: %d: bad id", id); + logit("channel_register_cleanup: %d: bad id", id); return; } c->detach_user = fn; @@ -625,8 +635,9 @@ void channel_cancel_cleanup(int id) { Channel *c = channel_lookup(id); + if (c == NULL) { - log("channel_cancel_cleanup: %d: bad id", id); + logit("channel_cancel_cleanup: %d: bad id", id); return; } c->detach_user = NULL; @@ -635,8 +646,9 @@ void channel_register_filter(int id, channel_filter_fn *fn) { Channel *c = channel_lookup(id); + if (c == NULL) { - log("channel_register_filter: %d: bad id", id); + logit("channel_register_filter: %d: bad id", id); return; } c->input_filter = fn; @@ -647,6 +659,7 @@ channel_set_fds(int id, int rfd, int wfd, int efd, int extusage, int nonblock, u_int window_max) { Channel *c = channel_lookup(id); + if (c == NULL || c->type != SSH_CHANNEL_LARVAL) fatal("channel_activate for non-larval channel %d.", id); channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); @@ -706,7 +719,11 @@ channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset) if (buffer_len(&c->output) > 0) { FD_SET(c->wfd, writeset); } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { - chan_obuf_empty(c); + if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) + debug2("channel %d: obuf_empty delayed efd %d/(%d)", + c->self, c->efd, buffer_len(&c->extended)); + else + chan_obuf_empty(c); } } /** XXX check close conditions, too */ @@ -714,10 +731,15 @@ channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset) if (c->extended_usage == CHAN_EXTENDED_WRITE && buffer_len(&c->extended) > 0) FD_SET(c->efd, writeset); - else if (c->extended_usage == CHAN_EXTENDED_READ && + else if (!(c->flags & CHAN_EOF_SENT) && + c->extended_usage == CHAN_EXTENDED_READ && buffer_len(&c->extended) < c->remote_window) FD_SET(c->efd, readset); } + /* XXX: What about efd? races? */ + if (compat20 && c->ctl_fd != -1 && + c->istate == CHAN_INPUT_OPEN && c->ostate == CHAN_OUTPUT_OPEN) + FD_SET(c->ctl_fd, readset); } static void @@ -728,7 +750,7 @@ channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) packet_put_int(c->remote_id); packet_send(); c->type = SSH_CHANNEL_CLOSED; - debug("channel %d: closing after input drain.", c->self); + debug2("channel %d: closing after input drain.", c->self); } } @@ -769,7 +791,7 @@ x11_open_helper(Buffer *b) proto_len = ucp[6] + 256 * ucp[7]; data_len = ucp[8] + 256 * ucp[9]; } else { - debug("Initial X11 packet contains bad byte order byte: 0x%x", + debug2("Initial X11 packet contains bad byte order byte: 0x%x", ucp[0]); return -1; } @@ -782,14 +804,14 @@ x11_open_helper(Buffer *b) /* Check if authentication protocol matches. */ if (proto_len != strlen(x11_saved_proto) || memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { - debug("X11 connection uses different authentication protocol."); + debug2("X11 connection uses different authentication protocol."); return -1; } /* Check if authentication data matches our fake data. */ if (data_len != x11_fake_data_len || memcmp(ucp + 12 + ((proto_len + 3) & ~3), x11_fake_data, x11_fake_data_len) != 0) { - debug("X11 auth data does not match fake data."); + debug2("X11 auth data does not match fake data."); return -1; } /* Check fake data length */ @@ -812,6 +834,7 @@ static void channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) { int ret = x11_open_helper(&c->output); + if (ret == 1) { /* Start normal processing for the channel. */ c->type = SSH_CHANNEL_OPEN; @@ -821,7 +844,7 @@ channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) * We have received an X11 connection that has bad * authentication information. */ - log("X11 connection rejected because of wrong authentication."); + logit("X11 connection rejected because of wrong authentication."); buffer_clear(&c->input); buffer_clear(&c->output); channel_close_fd(&c->sock); @@ -844,8 +867,8 @@ channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) c->type = SSH_CHANNEL_OPEN; channel_pre_open(c, readset, writeset); } else if (ret == -1) { - log("X11 connection rejected because of wrong authentication."); - debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); + logit("X11 connection rejected because of wrong authentication."); + debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); chan_read_failed(c); buffer_clear(&c->input); chan_ibuf_empty(c); @@ -855,7 +878,7 @@ channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) chan_write_failed(c); else c->type = SSH_CHANNEL_OPEN; - debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); + debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); } } @@ -863,7 +886,7 @@ channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) static int channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) { - u_char *p, *host; + char *p, *host; int len, have, i, found; char username[256]; struct { @@ -913,7 +936,7 @@ channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) strlcpy(c->path, host, sizeof(c->path)); c->host_port = ntohs(s4_req.dest_port); - debug("channel %d: dynamic request: socks4 host %s port %u command %u", + debug2("channel %d: dynamic request: socks4 host %s port %u command %u", c->self, host, c->host_port, s4_req.command); if (s4_req.command != 1) { @@ -929,6 +952,117 @@ channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) return 1; } +/* try to decode a socks5 header */ +#define SSH_SOCKS5_AUTHDONE 0x1000 +#define SSH_SOCKS5_NOAUTH 0x00 +#define SSH_SOCKS5_IPV4 0x01 +#define SSH_SOCKS5_DOMAIN 0x03 +#define SSH_SOCKS5_IPV6 0x04 +#define SSH_SOCKS5_CONNECT 0x01 +#define SSH_SOCKS5_SUCCESS 0x00 + +static int +channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) +{ + struct { + u_int8_t version; + u_int8_t command; + u_int8_t reserved; + u_int8_t atyp; + } s5_req, s5_rsp; + u_int16_t dest_port; + u_char *p, dest_addr[255+1]; + int i, have, found, nmethods, addrlen, af; + + debug2("channel %d: decode socks5", c->self); + p = buffer_ptr(&c->input); + if (p[0] != 0x05) + return -1; + have = buffer_len(&c->input); + if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { + /* format: ver | nmethods | methods */ + if (have < 2) + return 0; + nmethods = p[1]; + if (have < nmethods + 2) + return 0; + /* look for method: "NO AUTHENTICATION REQUIRED" */ + for (found = 0, i = 2 ; i < nmethods + 2; i++) { + if (p[i] == SSH_SOCKS5_NOAUTH ) { + found = 1; + break; + } + } + if (!found) { + debug("channel %d: method SSH_SOCKS5_NOAUTH not found", + c->self); + return -1; + } + buffer_consume(&c->input, nmethods + 2); + buffer_put_char(&c->output, 0x05); /* version */ + buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ + FD_SET(c->sock, writeset); + c->flags |= SSH_SOCKS5_AUTHDONE; + debug2("channel %d: socks5 auth done", c->self); + return 0; /* need more */ + } + debug2("channel %d: socks5 post auth", c->self); + if (have < sizeof(s5_req)+1) + return 0; /* need more */ + memcpy((char *)&s5_req, p, sizeof(s5_req)); + if (s5_req.version != 0x05 || + s5_req.command != SSH_SOCKS5_CONNECT || + s5_req.reserved != 0x00) { + debug2("channel %d: only socks5 connect supported", c->self); + return -1; + } + switch(s5_req.atyp){ + case SSH_SOCKS5_IPV4: + addrlen = 4; + af = AF_INET; + break; + case SSH_SOCKS5_DOMAIN: + addrlen = p[sizeof(s5_req)]; + af = -1; + break; + case SSH_SOCKS5_IPV6: + addrlen = 16; + af = AF_INET6; + break; + default: + debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); + return -1; + } + if (have < 4 + addrlen + 2) + return 0; + buffer_consume(&c->input, sizeof(s5_req)); + if (s5_req.atyp == SSH_SOCKS5_DOMAIN) + buffer_consume(&c->input, 1); /* host string length */ + buffer_get(&c->input, (char *)&dest_addr, addrlen); + buffer_get(&c->input, (char *)&dest_port, 2); + dest_addr[addrlen] = '\0'; + if (s5_req.atyp == SSH_SOCKS5_DOMAIN) + strlcpy(c->path, (char *)dest_addr, sizeof(c->path)); + else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL) + return -1; + c->host_port = ntohs(dest_port); + + debug2("channel %d: dynamic request: socks5 host %s port %u command %u", + c->self, c->path, c->host_port, s5_req.command); + + s5_rsp.version = 0x05; + s5_rsp.command = SSH_SOCKS5_SUCCESS; + s5_rsp.reserved = 0; /* ignored */ + s5_rsp.atyp = SSH_SOCKS5_IPV4; + ((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY; + dest_port = 0; /* ignored */ + + buffer_append(&c->output, (char *)&s5_rsp, sizeof(s5_rsp)); + buffer_append(&c->output, (char *)&dest_addr, sizeof(struct in_addr)); + buffer_append(&c->output, (char *)&dest_port, sizeof(dest_port)); + return 1; +} + /* dynamic port forwarding */ static void channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) @@ -941,7 +1075,7 @@ channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) debug2("channel %d: pre_dynamic: have %d", c->self, have); /* buffer_dump(&c->input); */ /* check if the fixed size part of the packet is in buffer. */ - if (have < 4) { + if (have < 3) { /* need more */ FD_SET(c->sock, readset); return; @@ -952,6 +1086,9 @@ channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) case 0x04: ret = channel_decode_socks4(c, readset, writeset); break; + case 0x05: + ret = channel_decode_socks5(c, readset, writeset); + break; default: ret = -1; break; @@ -985,7 +1122,7 @@ channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) addrlen = sizeof(addr); newsock = accept(c->sock, &addr, &addrlen); if (c->single_connection) { - debug("single_connection: closing X11 listener."); + debug2("single_connection: closing X11 listener."); channel_close_fd(&c->sock); chan_mark_dead(c); } @@ -1001,8 +1138,7 @@ channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) nc = channel_new("accepted x11 socket", SSH_CHANNEL_OPENING, newsock, newsock, -1, - c->local_window_max, c->local_maxpacket, - 0, xstrdup(buf), 1); + c->local_window_max, c->local_maxpacket, 0, buf, 1); if (compat20) { packet_start(SSH2_MSG_CHANNEL_OPEN); packet_put_cstring("x11"); @@ -1012,7 +1148,7 @@ channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) /* originator ipaddr and port */ packet_put_cstring(remote_ipaddr); if (datafellows & SSH_BUG_X11FWD) { - debug("ssh2 x11 bug compat mode"); + debug2("ssh2 x11 bug compat mode"); } else { packet_put_int(remote_port); } @@ -1116,10 +1252,9 @@ channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) error("accept: %.100s", strerror(errno)); return; } - nc = channel_new(rtype, - nextstate, newsock, newsock, -1, - c->local_window_max, c->local_maxpacket, - 0, xstrdup(rtype), 1); + set_nodelay(newsock); + nc = channel_new(rtype, nextstate, newsock, newsock, -1, + c->local_window_max, c->local_maxpacket, 0, rtype, 1); nc->listening_port = c->listening_port; nc->host_port = c->host_port; strlcpy(nc->path, c->path, sizeof(nc->path)); @@ -1145,7 +1280,6 @@ static void channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) { Channel *nc; - char *name; int newsock; struct sockaddr addr; socklen_t addrlen; @@ -1157,11 +1291,10 @@ channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) error("accept from auth socket: %.100s", strerror(errno)); return; } - name = xstrdup("accepted auth socket"); nc = channel_new("accepted auth socket", SSH_CHANNEL_OPENING, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, - 0, name, 1); + 0, "accepted auth socket", 1); if (compat20) { packet_start(SSH2_MSG_CHANNEL_OPEN); packet_put_cstring("auth-agent@openssh.com"); @@ -1183,8 +1316,7 @@ channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset) socklen_t sz = sizeof(err); if (FD_ISSET(c->sock, writeset)) { - if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, (char *)&err, - &sz) < 0) { + if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { err = errno; error("getsockopt SO_ERROR failed"); } @@ -1235,16 +1367,16 @@ channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) if (len < 0 && (errno == EINTR || errno == EAGAIN)) return 1; if (len <= 0) { - debug("channel %d: read<=0 rfd %d len %d", + debug2("channel %d: read<=0 rfd %d len %d", c->self, c->rfd, len); if (c->type != SSH_CHANNEL_OPEN) { - debug("channel %d: not open", c->self); + debug2("channel %d: not open", c->self); chan_mark_dead(c); return -1; } else if (compat13) { buffer_clear(&c->output); c->type = SSH_CHANNEL_INPUT_DRAINING; - debug("channel %d: input draining.", c->self); + debug2("channel %d: input draining.", c->self); } else { chan_read_failed(c); } @@ -1252,7 +1384,7 @@ channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) } if (c->input_filter != NULL) { if (c->input_filter(c, buf, len) == -1) { - debug("channel %d: filter stops", c->self); + debug2("channel %d: filter stops", c->self); chan_read_failed(c); } } else { @@ -1275,17 +1407,22 @@ channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) buffer_len(&c->output) > 0) { data = buffer_ptr(&c->output); dlen = buffer_len(&c->output); +#ifdef _AIX + /* XXX: Later AIX versions can't push as much data to tty */ + if (compat20 && c->wfd_isatty) + dlen = MIN(dlen, 8*1024); +#endif len = write(c->wfd, data, dlen); if (len < 0 && (errno == EINTR || errno == EAGAIN)) return 1; if (len <= 0) { if (c->type != SSH_CHANNEL_OPEN) { - debug("channel %d: not open", c->self); + debug2("channel %d: not open", c->self); chan_mark_dead(c); return -1; } else if (compat13) { buffer_clear(&c->output); - debug("channel %d: input draining.", c->self); + debug2("channel %d: input draining.", c->self); c->type = SSH_CHANNEL_INPUT_DRAINING; } else { chan_write_failed(c); @@ -1356,6 +1493,33 @@ channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) return 1; } static int +channel_handle_ctl(Channel *c, fd_set * readset, fd_set * writeset) +{ + char buf[16]; + int len; + + /* Monitor control fd to detect if the slave client exits */ + if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) { + len = read(c->ctl_fd, buf, sizeof(buf)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) + return 1; + if (len <= 0) { + debug2("channel %d: ctl read<=0", c->self); + if (c->type != SSH_CHANNEL_OPEN) { + debug2("channel %d: not open", c->self); + chan_mark_dead(c); + return -1; + } else { + chan_read_failed(c); + chan_write_failed(c); + } + return -1; + } else + fatal("%s: unexpected data on ctl fd", __func__); + } + return 1; +} +static int channel_check_window(Channel *c) { if (c->type == SSH_CHANNEL_OPEN && @@ -1385,6 +1549,7 @@ channel_post_open(Channel *c, fd_set * readset, fd_set * writeset) if (!compat20) return; channel_handle_efd(c, readset, writeset); + channel_handle_ctl(c, readset, writeset); channel_check_window(c); } @@ -1392,6 +1557,7 @@ static void channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) { int len; + /* Send buffered output data to the socket. */ if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { len = write(c->sock, buffer_ptr(&c->output), @@ -1469,6 +1635,7 @@ static void channel_handler_init(void) { int i; + for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { channel_pre[i] = NULL; channel_post[i] = NULL; @@ -1490,16 +1657,16 @@ channel_garbage_collect(Channel *c) if (c->detach_user != NULL) { if (!chan_is_dead(c, 0)) return; - debug("channel %d: gc: notify user", c->self); + debug2("channel %d: gc: notify user", c->self); c->detach_user(c->self, NULL); /* if we still have a callback */ if (c->detach_user != NULL) return; - debug("channel %d: gc: user detached", c->self); + debug2("channel %d: gc: user detached", c->self); } if (!chan_is_dead(c, 1)) return; - debug("channel %d: garbage collecting", c->self); + debug2("channel %d: garbage collecting", c->self); channel_free(c); } @@ -1568,8 +1735,9 @@ channel_after_select(fd_set * readset, fd_set * writeset) void channel_output_poll(void) { - int len, i; Channel *c; + int i; + u_int len; for (i = 0; i < channels_alloc; i++) { c = channels[i]; @@ -1632,16 +1800,22 @@ channel_output_poll(void) fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); /* * input-buffer is empty and read-socket shutdown: - * tell peer, that we will not send more data: send IEOF + * tell peer, that we will not send more data: send IEOF. + * hack for extended data: delay EOF if EFD still in use. */ - chan_ibuf_empty(c); + if (CHANNEL_EFD_INPUT_ACTIVE(c)) + debug2("channel %d: ibuf_empty delayed efd %d/(%d)", + c->self, c->efd, buffer_len(&c->extended)); + else + chan_ibuf_empty(c); } /* Send extended data, i.e. stderr */ if (compat20 && + !(c->flags & CHAN_EOF_SENT) && c->remote_window > 0 && (len = buffer_len(&c->extended)) > 0 && c->extended_usage == CHAN_EXTENDED_READ) { - debug2("channel %d: rwin %d elen %d euse %d", + debug2("channel %d: rwin %u elen %u euse %d", c->self, c->remote_window, buffer_len(&c->extended), c->extended_usage); if (len > c->remote_window) @@ -1682,20 +1856,32 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) c->type != SSH_CHANNEL_X11_OPEN) return; - /* same for protocol 1.5 if output end is no longer open */ - if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) - return; - /* Get the data. */ data = packet_get_string(&data_len); + /* + * Ignore data for protocol > 1.3 if output end is no longer open. + * For protocol 2 the sending side is reducing its window as it sends + * data, so we must 'fake' consumption of the data in order to ensure + * that window updates are sent back. Otherwise the connection might + * deadlock. + */ + if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { + if (compat20) { + c->local_window -= data_len; + c->local_consumed += data_len; + } + xfree(data); + return; + } + if (compat20) { if (data_len > c->local_maxpacket) { - log("channel %d: rcvd big packet %d, maxpack %d", + logit("channel %d: rcvd big packet %d, maxpack %d", c->self, data_len, c->local_maxpacket); } if (data_len > c->local_window) { - log("channel %d: rcvd too much data %d, win %d", + logit("channel %d: rcvd too much data %d, win %d", c->self, data_len, c->local_window); xfree(data); return; @@ -1711,9 +1897,8 @@ void channel_input_extended_data(int type, u_int32_t seq, void *ctxt) { int id; - int tcode; char *data; - u_int data_len; + u_int data_len, tcode; Channel *c; /* Get the channel number and verify it. */ @@ -1723,20 +1908,27 @@ channel_input_extended_data(int type, u_int32_t seq, void *ctxt) if (c == NULL) packet_disconnect("Received extended_data for bad channel %d.", id); if (c->type != SSH_CHANNEL_OPEN) { - log("channel %d: ext data for non open", id); + logit("channel %d: ext data for non open", id); return; } + if (c->flags & CHAN_EOF_RCVD) { + if (datafellows & SSH_BUG_EXTEOF) + debug("channel %d: accepting ext data after eof", id); + else + packet_disconnect("Received extended_data after EOF " + "on channel %d.", id); + } tcode = packet_get_int(); if (c->efd == -1 || c->extended_usage != CHAN_EXTENDED_WRITE || tcode != SSH2_EXTENDED_DATA_STDERR) { - log("channel %d: bad ext data", c->self); + logit("channel %d: bad ext data", c->self); return; } data = packet_get_string(&data_len); packet_check_eom(); if (data_len > c->local_window) { - log("channel %d: rcvd too much extended_data %d, win %d", + logit("channel %d: rcvd too much extended_data %d, win %d", c->self, data_len, c->local_window); xfree(data); return; @@ -1858,10 +2050,10 @@ channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) c->remote_maxpacket = packet_get_int(); if (c->confirm) { debug2("callback start"); - c->confirm(c->self, NULL); + c->confirm(c->self, c->confirm_ctx); debug2("callback done"); } - debug("channel %d: open confirm rwindow %d rmax %d", c->self, + debug2("channel %d: open confirm rwindow %u rmax %u", c->self, c->remote_window, c->remote_maxpacket); } packet_check_eom(); @@ -1902,7 +2094,7 @@ channel_input_open_failure(int type, u_int32_t seq, void *ctxt) msg = packet_get_string(NULL); lang = packet_get_string(NULL); } - log("channel %d: open failed: %s%s%s", id, + logit("channel %d: open failed: %s%s%s", id, reason2txt(reason), msg ? ": ": "", msg ? msg : ""); if (msg != NULL) xfree(msg); @@ -1918,7 +2110,8 @@ void channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) { Channel *c; - int id, adjust; + int id; + u_int adjust; if (!compat20) return; @@ -1928,13 +2121,13 @@ channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) c = channel_lookup(id); if (c == NULL || c->type != SSH_CHANNEL_OPEN) { - log("Received window adjust for " + logit("Received window adjust for " "non-open channel %d.", id); return; } adjust = packet_get_int(); packet_check_eom(); - debug2("channel %d: rcvd adjust %d", id, adjust); + debug2("channel %d: rcvd adjust %u", id, adjust); c->remote_window += adjust; } @@ -1963,6 +2156,7 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt) originator_string, 1); c->remote_id = remote_id; } + xfree(originator_string); if (c == NULL) { packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remote_id); @@ -1989,7 +2183,6 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por struct addrinfo hints, *ai, *aitop; const char *host; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; - struct linger linger; success = 0; host = (type == SSH_CHANNEL_RPORT_LISTENER) ? @@ -2025,20 +2218,20 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por continue; } /* Create a port to listen for the host. */ - sock = socket(ai->ai_family, SOCK_STREAM, 0); + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) { /* this is no error since kernel may not support ipv6 */ verbose("socket: %.100s", strerror(errno)); continue; } /* - * Set socket options. We would like the socket to disappear - * as soon as it has been closed for whatever reason. + * Set socket options. + * Allow local port reuse in TIME_WAIT. */ - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); - linger.l_onoff = 1; - linger.l_linger = 5; - setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, + sizeof(on)) == -1) + error("setsockopt SO_REUSEADDR: %s", strerror(errno)); + debug("Local forwarding listening on %s port %s.", ntop, strport); /* Bind the socket to the address. */ @@ -2053,7 +2246,7 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por continue; } /* Start listening for connections on the socket. */ - if (listen(sock, 5) < 0) { + if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { error("listen: %.100s", strerror(errno)); close(sock); continue; @@ -2061,7 +2254,7 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por /* Allocate a channel number for the socket. */ c = channel_new("port listener", type, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, - 0, xstrdup("port listener"), 1); + 0, "port listener", 1); strlcpy(c->path, host, sizeof(c->path)); c->host_port = port_to_connect; c->listening_port = listen_port; @@ -2074,6 +2267,26 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por return success; } +int +channel_cancel_rport_listener(const char *host, u_short port) +{ + int i, found = 0; + + 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) { + debug2("%s: close clannel %d", __func__, i); + channel_free(c); + found = 1; + } + } + + return (found); +} + /* protocol local port fwd, used by ssh (and sshd in v1) */ int channel_setup_local_fwd_listener(u_short listen_port, @@ -2112,7 +2325,7 @@ channel_request_remote_forwarding(u_short listen_port, const char *address_to_bind = "0.0.0.0"; packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("tcpip-forward"); - packet_put_char(0); /* boolean: want reply */ + packet_put_char(1); /* boolean: want reply */ packet_put_cstring(address_to_bind); packet_put_int(listen_port); packet_send(); @@ -2134,7 +2347,7 @@ channel_request_remote_forwarding(u_short listen_port, success = 1; break; case SSH_SMSG_FAILURE: - log("Warning: Server denied remote port forwarding."); + logit("Warning: Server denied remote port forwarding."); break; default: /* Unknown packet */ @@ -2150,6 +2363,42 @@ channel_request_remote_forwarding(u_short listen_port, } } +/* + * Request cancellation of remote forwarding of connection host:port from + * local side. + */ + +void +channel_request_rforward_cancel(u_short port) +{ + int i; + const char *address_to_bind = "0.0.0.0"; + + if (!compat20) + return; + + for (i = 0; i < num_permitted_opens; i++) { + if (permitted_opens[i].host_to_connect != NULL && + permitted_opens[i].listen_port == port) + break; + } + if (i >= num_permitted_opens) { + 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(address_to_bind); + packet_put_int(port); + packet_send(); + + permitted_opens[i].listen_port = 0; + permitted_opens[i].port_to_connect = 0; + free(permitted_opens[i].host_to_connect); + permitted_opens[i].host_to_connect = NULL; +} + /* * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates * listening for the port, and sends back a success reply (or disconnect @@ -2173,9 +2422,13 @@ channel_input_port_forward_request(int is_root, int gateway_ports) * privileged port. */ if (port < IPPORT_RESERVED && !is_root) - packet_disconnect("Requested forwarding of port %d but user is not root.", - port); + packet_disconnect( + "Requested forwarding of port %d but user is not root.", + port); + if (host_port == 0) + packet_disconnect("Dynamic forwarding denied."); #endif + /* Initiate forwarding */ channel_setup_local_fwd_listener(port, hostname, host_port, gateway_ports); @@ -2215,7 +2468,8 @@ channel_clear_permitted_opens(void) int i; for (i = 0; i < num_permitted_opens; i++) - xfree(permitted_opens[i].host_to_connect); + if (permitted_opens[i].host_to_connect != NULL) + xfree(permitted_opens[i].host_to_connect); num_permitted_opens = 0; } @@ -2247,13 +2501,16 @@ connect_to(const char *host, u_short port) error("connect_to: getnameinfo failed"); continue; } - sock = socket(ai->ai_family, SOCK_STREAM, 0); + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) { - error("socket: %.100s", strerror(errno)); + if (ai->ai_next == NULL) + error("socket: %.100s", strerror(errno)); + else + verbose("socket: %.100s", strerror(errno)); continue; } - if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) - fatal("connect_to: F_SETFL: %s", strerror(errno)); + 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, @@ -2270,6 +2527,7 @@ connect_to(const char *host, u_short port) return -1; } /* success */ + set_nodelay(sock); return sock; } @@ -2279,7 +2537,8 @@ channel_connect_by_listen_address(u_short listen_port) int i; for (i = 0; i < num_permitted_opens; i++) - if (permitted_opens[i].listen_port == listen_port) + if (permitted_opens[i].host_to_connect != NULL && + permitted_opens[i].listen_port == listen_port) return connect_to( permitted_opens[i].host_to_connect, permitted_opens[i].port_to_connect); @@ -2297,29 +2556,51 @@ channel_connect_to(const char *host, u_short port) permit = all_opens_permitted; if (!permit) { for (i = 0; i < num_permitted_opens; i++) - if (permitted_opens[i].port_to_connect == port && + 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 (!permit) { - log("Received request to connect to host %.100s port %d, " + logit("Received request to connect to host %.100s port %d, " "but the request was denied.", host, port); return -1; } return connect_to(host, port); } +void +channel_send_window_changes(void) +{ + int i; + struct winsize ws; + + for (i = 0; i < channels_alloc; i++) { + if (channels[i] == NULL || + channels[i]->type != SSH_CHANNEL_OPEN) + continue; + if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) + continue; + channel_request_start(i, "window-change", 0); + packet_put_int(ws.ws_col); + packet_put_int(ws.ws_row); + packet_put_int(ws.ws_xpixel); + packet_put_int(ws.ws_ypixel); + packet_send(); + } +} + /* -- X11 forwarding */ /* * Creates an internet domain socket for listening for X11 connections. - * Returns a suitable display number for the DISPLAY variable, or -1 if - * an error occurs. + * Returns 0 and a suitable display number for the DISPLAY variable + * stored in display_numberp , or -1 if an error occurs. */ int x11_create_display_inet(int x11_display_offset, int x11_use_localhost, - int single_connection) + int single_connection, u_int *display_numberp) { Channel *nc = NULL; int display_number, sock; @@ -2344,10 +2625,12 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; - sock = socket(ai->ai_family, SOCK_STREAM, 0); + sock = socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol); if (sock < 0) { if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { error("socket: %.100s", strerror(errno)); + freeaddrinfo(aitop); return -1; } else { debug("x11_create_display_inet: Socket family %d not supported", @@ -2355,8 +2638,15 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, continue; } } +#ifdef IPV6_V6ONLY + if (ai->ai_family == AF_INET6) { + int on = 1; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) + error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); + } +#endif if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { - debug("bind port %d: %.100s", port, strerror(errno)); + debug2("bind port %d: %.100s", port, strerror(errno)); close(sock); if (ai->ai_next) @@ -2373,7 +2663,12 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, if (num_socks == NUM_SOCKS) break; #else - break; + if (x11_use_localhost) { + if (num_socks == NUM_SOCKS) + break; + } else { + break; + } #endif } freeaddrinfo(aitop); @@ -2387,7 +2682,7 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, /* Start listening for connections on the socket. */ for (n = 0; n < num_socks; n++) { sock = socks[n]; - if (listen(sock, 5) < 0) { + if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { error("listen: %.100s", strerror(errno)); close(sock); return -1; @@ -2400,12 +2695,13 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, nc = channel_new("x11 listener", SSH_CHANNEL_X11_LISTENER, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, - 0, xstrdup("X11 inet listener"), 1); + 0, "X11 inet listener", 1); nc->single_connection = single_connection; } /* Return the display number for the DISPLAY environment variable. */ - return display_number; + *display_numberp = display_number; + return (0); } static int @@ -2497,14 +2793,14 @@ x11_connect_display(void) } for (ai = aitop; ai; ai = ai->ai_next) { /* Create a socket. */ - sock = socket(ai->ai_family, SOCK_STREAM, 0); + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) { - debug("socket: %.100s", strerror(errno)); + debug2("socket: %.100s", strerror(errno)); continue; } /* Connect it to the display. */ if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { - debug("connect %.100s port %d: %.100s", buf, + debug2("connect %.100s port %d: %.100s", buf, 6000 + display_number, strerror(errno)); close(sock); continue; @@ -2556,6 +2852,7 @@ x11_input_open(int type, u_int32_t seq, void *ctxt) c->remote_id = remote_id; c->force_drain = 1; } + xfree(remote_host); if (c == NULL) { /* Send refusal to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); @@ -2574,6 +2871,7 @@ void deny_input_open(int type, u_int32_t seq, void *ctxt) { int rchan = packet_get_int(); + switch (type) { case SSH_SMSG_AGENT_OPEN: error("Warning: ssh server tried agent forwarding."); @@ -2605,7 +2903,7 @@ x11_request_forwarding_with_spoofing(int client_session_id, char *new_data; int screen_number; const char *cp; - u_int32_t rand = 0; + u_int32_t rnd = 0; cp = getenv("DISPLAY"); if (cp) @@ -2630,10 +2928,10 @@ x11_request_forwarding_with_spoofing(int client_session_id, if (sscanf(data + 2 * i, "%2x", &value) != 1) fatal("x11_request_forwarding: bad authentication data: %.100s", data); if (i % 4 == 0) - rand = arc4random(); + rnd = arc4random(); x11_saved_data[i] = value; - x11_fake_data[i] = rand & 0xff; - rand >>= 8; + x11_fake_data[i] = rnd & 0xff; + rnd >>= 8; } x11_saved_data_len = data_len; x11_fake_data_len = data_len; @@ -2672,147 +2970,3 @@ auth_request_forwarding(void) packet_send(); packet_write_wait(); } - -/* - * Returns the name of the forwarded authentication socket. Returns NULL if - * there is no forwarded authentication socket. The returned value points to - * a static buffer. - */ - -char * -auth_get_socket_name(void) -{ - return auth_sock_name; -} - -/* removes the agent forwarding socket */ - -void -auth_sock_cleanup_proc(void *_pw) -{ - struct passwd *pw = _pw; - - if (auth_sock_name) { - temporarily_use_uid(pw); - unlink(auth_sock_name); - rmdir(auth_sock_dir); - auth_sock_name = NULL; - restore_uid(); - } -} - -/* - * This is called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. - * This starts forwarding authentication requests. - */ - -int -auth_input_request_forwarding(struct passwd * pw) -{ - Channel *nc; - int sock; - struct sockaddr_un sunaddr; - - if (auth_get_socket_name() != NULL) { - error("authentication forwarding requested twice."); - return 0; - } - - /* Temporarily drop privileged uid for mkdir/bind. */ - temporarily_use_uid(pw); - - /* Allocate a buffer for the socket name, and format the name. */ - auth_sock_name = xmalloc(MAXPATHLEN); - auth_sock_dir = xmalloc(MAXPATHLEN); - strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); - - /* Create private directory for socket */ - if (mkdtemp(auth_sock_dir) == NULL) { - packet_send_debug("Agent forwarding disabled: " - "mkdtemp() failed: %.100s", strerror(errno)); - restore_uid(); - xfree(auth_sock_name); - xfree(auth_sock_dir); - auth_sock_name = NULL; - auth_sock_dir = NULL; - return 0; - } - snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%d", - auth_sock_dir, (int) getpid()); - - /* delete agent socket on fatal() */ - fatal_add_cleanup(auth_sock_cleanup_proc, pw); - - /* Create the socket. */ - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) - packet_disconnect("socket: %.100s", strerror(errno)); - - /* Bind it to the name. */ - memset(&sunaddr, 0, sizeof(sunaddr)); - sunaddr.sun_family = AF_UNIX; - strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); - - if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) - packet_disconnect("bind: %.100s", strerror(errno)); - - /* Restore the privileged uid. */ - restore_uid(); - - /* Start listening on the socket. */ - if (listen(sock, 5) < 0) - packet_disconnect("listen: %.100s", strerror(errno)); - - /* Allocate a channel for the authentication agent socket. */ - nc = channel_new("auth socket", - SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, - CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, - 0, xstrdup("auth socket"), 1); - strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); - return 1; -} - -/* This is called to process an SSH_SMSG_AGENT_OPEN message. */ - -void -auth_input_open_request(int type, u_int32_t seq, void *ctxt) -{ - Channel *c = NULL; - int remote_id, sock; - char *name; - - /* Read the remote channel number from the message. */ - remote_id = packet_get_int(); - packet_check_eom(); - - /* - * Get a connection to the local authentication agent (this may again - * get forwarded). - */ - sock = ssh_get_authentication_socket(); - - /* - * If we could not connect the agent, send an error message back to - * the server. This should never happen unless the agent dies, - * because authentication forwarding is only enabled if we have an - * agent. - */ - if (sock >= 0) { - name = xstrdup("authentication agent connection"); - c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, - -1, 0, 0, 0, name, 1); - c->remote_id = remote_id; - c->force_drain = 1; - } - if (c == NULL) { - packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(remote_id); - } else { - /* Send a confirmation to the remote host. */ - debug("Forwarding authentication connection."); - packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); - packet_put_int(remote_id); - packet_put_int(c->self); - } - packet_send(); -}