Annotation of src/usr.bin/ssh/channel.c, Revision 1.1
1.1 ! markus 1: /*
! 2: * Author: Tatu Ylonen <ylo@cs.hut.fi>
! 3: * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
! 4: * All rights reserved
! 5: * This file contains functions for generic socket connection forwarding.
! 6: * There is also code for initiating connection forwarding for X11 connections,
! 7: * arbitrary tcp/ip connections, and the authentication agent connection.
! 8: *
! 9: * As far as I am concerned, the code I have written for this software
! 10: * can be used freely for any purpose. Any derived versions of this
! 11: * software must be clearly marked as such, and if the derived work is
! 12: * incompatible with the protocol description in the RFC file, it must be
! 13: * called by a name other than "ssh" or "Secure Shell".
! 14: *
! 15: *
! 16: * SSH2 support added by Markus Friedl.
! 17: * Copyright (c) 1999,2000 Markus Friedl. All rights reserved.
! 18: * Copyright (c) 1999 Dug Song. All rights reserved.
! 19: * Copyright (c) 1999 Theo de Raadt. All rights reserved.
! 20: *
! 21: * Redistribution and use in source and binary forms, with or without
! 22: * modification, are permitted provided that the following conditions
! 23: * are met:
! 24: * 1. Redistributions of source code must retain the above copyright
! 25: * notice, this list of conditions and the following disclaimer.
! 26: * 2. Redistributions in binary form must reproduce the above copyright
! 27: * notice, this list of conditions and the following disclaimer in the
! 28: * documentation and/or other materials provided with the distribution.
! 29: *
! 30: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 31: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 32: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 33: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 34: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 35: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 36: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 37: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 38: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 39: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 40: */
! 41:
! 42: #include "includes.h"
! 43: RCSID("$OpenBSD: channels.c,v 1.119 2001/05/28 23:25:24 markus Exp $");
! 44:
! 45: #include "ssh2.h"
! 46: #include "packet.h"
! 47: #include "xmalloc.h"
! 48: #include "log.h"
! 49: #include "misc.h"
! 50: #include "channel.h"
! 51: #include "compat.h"
! 52:
! 53: /*
! 54: * Pointer to an array containing all allocated channels. The array is
! 55: * dynamically extended as needed.
! 56: */
! 57: Channel **channels = NULL;
! 58:
! 59: /*
! 60: * Size of the channel array. All slots of the array must always be
! 61: * initialized (at least the type field); unused slots set to NULL
! 62: */
! 63: int channels_alloc = 0;
! 64:
! 65: /*
! 66: * Maximum file descriptor value used in any of the channels. This is
! 67: * updated in channel_new.
! 68: */
! 69: int channel_max_fd = 0;
! 70:
! 71:
! 72: Channel *
! 73: channel_lookup(int id)
! 74: {
! 75: Channel *c;
! 76:
! 77: if (id < 0 || id > channels_alloc) {
! 78: log("channel_lookup: %d: bad id", id);
! 79: return NULL;
! 80: }
! 81: c = channels[id];
! 82: if (c == NULL) {
! 83: log("channel_lookup: %d: bad id: channel free", id);
! 84: return NULL;
! 85: }
! 86: return c;
! 87: }
! 88:
! 89: /*
! 90: * Register filedescriptors for a channel, used when allocating a channel or
! 91: * when the channel consumer/producer is ready, e.g. shell exec'd
! 92: */
! 93:
! 94: void
! 95: channel_register_fds(Channel *c, int rfd, int wfd, int efd,
! 96: int extusage, int nonblock)
! 97: {
! 98: /* Update the maximum file descriptor value. */
! 99: channel_max_fd = MAX(channel_max_fd, rfd);
! 100: channel_max_fd = MAX(channel_max_fd, wfd);
! 101: channel_max_fd = MAX(channel_max_fd, efd);
! 102:
! 103: /* XXX set close-on-exec -markus */
! 104:
! 105: c->rfd = rfd;
! 106: c->wfd = wfd;
! 107: c->sock = (rfd == wfd) ? rfd : -1;
! 108: c->efd = efd;
! 109: c->extended_usage = extusage;
! 110:
! 111: /* XXX ugly hack: nonblock is only set by the server */
! 112: if (nonblock && isatty(c->rfd)) {
! 113: debug("channel %d: rfd %d isatty", c->self, c->rfd);
! 114: c->isatty = 1;
! 115: if (!isatty(c->wfd)) {
! 116: error("channel %d: wfd %d is not a tty?",
! 117: c->self, c->wfd);
! 118: }
! 119: } else {
! 120: c->isatty = 0;
! 121: }
! 122:
! 123: /* enable nonblocking mode */
! 124: if (nonblock) {
! 125: if (rfd != -1)
! 126: set_nonblock(rfd);
! 127: if (wfd != -1)
! 128: set_nonblock(wfd);
! 129: if (efd != -1)
! 130: set_nonblock(efd);
! 131: }
! 132: }
! 133:
! 134: /*
! 135: * Allocate a new channel object and set its type and socket. This will cause
! 136: * remote_name to be freed.
! 137: */
! 138:
! 139: Channel *
! 140: channel_new(char *ctype, int type, int rfd, int wfd, int efd,
! 141: int window, int maxpack, int extusage, char *remote_name, int nonblock)
! 142: {
! 143: int i, found;
! 144: Channel *c;
! 145:
! 146: /* Do initial allocation if this is the first call. */
! 147: if (channels_alloc == 0) {
! 148: chan_init();
! 149: channels_alloc = 10;
! 150: channels = xmalloc(channels_alloc * sizeof(Channel *));
! 151: for (i = 0; i < channels_alloc; i++)
! 152: channels[i] = NULL;
! 153: /*
! 154: * Kludge: arrange a call to channel_stop_listening if we
! 155: * terminate with fatal().
! 156: */
! 157: fatal_add_cleanup((void (*) (void *)) channel_stop_listening, NULL);
! 158: }
! 159: /* Try to find a free slot where to put the new channel. */
! 160: for (found = -1, i = 0; i < channels_alloc; i++)
! 161: if (channels[i] == NULL) {
! 162: /* Found a free slot. */
! 163: found = i;
! 164: break;
! 165: }
! 166: if (found == -1) {
! 167: /* There are no free slots. Take last+1 slot and expand the array. */
! 168: found = channels_alloc;
! 169: channels_alloc += 10;
! 170: debug2("channel: expanding %d", channels_alloc);
! 171: channels = xrealloc(channels, channels_alloc * sizeof(Channel *));
! 172: for (i = found; i < channels_alloc; i++)
! 173: channels[i] = NULL;
! 174: }
! 175: /* Initialize and return new channel. */
! 176: c = channels[found] = xmalloc(sizeof(Channel));
! 177: buffer_init(&c->input);
! 178: buffer_init(&c->output);
! 179: buffer_init(&c->extended);
! 180: chan_init_iostates(c);
! 181: channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
! 182: c->self = found;
! 183: c->type = type;
! 184: c->ctype = ctype;
! 185: c->local_window = window;
! 186: c->local_window_max = window;
! 187: c->local_consumed = 0;
! 188: c->local_maxpacket = maxpack;
! 189: c->remote_id = -1;
! 190: c->remote_name = remote_name;
! 191: c->remote_window = 0;
! 192: c->remote_maxpacket = 0;
! 193: c->cb_fn = NULL;
! 194: c->cb_arg = NULL;
! 195: c->cb_event = 0;
! 196: c->dettach_user = NULL;
! 197: c->input_filter = NULL;
! 198: debug("channel %d: new [%s]", found, remote_name);
! 199: return c;
! 200: }
! 201:
! 202: /* Close all channel fd/socket. */
! 203:
! 204: void
! 205: channel_close_fds(Channel *c)
! 206: {
! 207: debug3("channel_close_fds: channel %d: r %d w %d e %d",
! 208: c->self, c->rfd, c->wfd, c->efd);
! 209:
! 210: if (c->sock != -1) {
! 211: close(c->sock);
! 212: c->sock = -1;
! 213: }
! 214: if (c->rfd != -1) {
! 215: close(c->rfd);
! 216: c->rfd = -1;
! 217: }
! 218: if (c->wfd != -1) {
! 219: close(c->wfd);
! 220: c->wfd = -1;
! 221: }
! 222: if (c->efd != -1) {
! 223: close(c->efd);
! 224: c->efd = -1;
! 225: }
! 226: }
! 227:
! 228: /* Free the channel and close its fd/socket. */
! 229:
! 230: void
! 231: channel_free(Channel *c)
! 232: {
! 233: char *s;
! 234: int i, n;
! 235:
! 236: for (n = 0, i = 0; i < channels_alloc; i++)
! 237: if (channels[i])
! 238: n++;
! 239: debug("channel_free: channel %d: %s, nchannels %d", c->self,
! 240: c->remote_name ? c->remote_name : "???", n);
! 241:
! 242: s = channel_open_message();
! 243: debug3("channel_free: status: %s", s);
! 244: xfree(s);
! 245:
! 246: if (c->dettach_user != NULL) {
! 247: debug("channel_free: channel %d: dettaching channel user", c->self);
! 248: c->dettach_user(c->self, NULL);
! 249: }
! 250: if (c->sock != -1)
! 251: shutdown(c->sock, SHUT_RDWR);
! 252: channel_close_fds(c);
! 253: buffer_free(&c->input);
! 254: buffer_free(&c->output);
! 255: buffer_free(&c->extended);
! 256: if (c->remote_name) {
! 257: xfree(c->remote_name);
! 258: c->remote_name = NULL;
! 259: }
! 260: channels[c->self] = NULL;
! 261: xfree(c);
! 262: }
! 263:
! 264:
! 265: /*
! 266: * Stops listening for channels, and removes any unix domain sockets that we
! 267: * might have.
! 268: */
! 269:
! 270: void
! 271: channel_stop_listening()
! 272: {
! 273: int i;
! 274: Channel *c;
! 275:
! 276: for (i = 0; i < channels_alloc; i++) {
! 277: c = channels[i];
! 278: if (c != NULL) {
! 279: switch (c->type) {
! 280: case SSH_CHANNEL_AUTH_SOCKET:
! 281: close(c->sock);
! 282: unlink(c->path);
! 283: channel_free(c);
! 284: break;
! 285: case SSH_CHANNEL_PORT_LISTENER:
! 286: case SSH_CHANNEL_RPORT_LISTENER:
! 287: case SSH_CHANNEL_X11_LISTENER:
! 288: close(c->sock);
! 289: channel_free(c);
! 290: break;
! 291: default:
! 292: break;
! 293: }
! 294: }
! 295: }
! 296: }
! 297:
! 298: /*
! 299: * Closes the sockets/fds of all channels. This is used to close extra file
! 300: * descriptors after a fork.
! 301: */
! 302:
! 303: void
! 304: channel_close_all()
! 305: {
! 306: int i;
! 307:
! 308: for (i = 0; i < channels_alloc; i++)
! 309: if (channels[i] != NULL)
! 310: channel_close_fds(channels[i]);
! 311: }
! 312:
! 313: /*
! 314: * Returns true if no channel has too much buffered data, and false if one or
! 315: * more channel is overfull.
! 316: */
! 317:
! 318: int
! 319: channel_not_very_much_buffered_data()
! 320: {
! 321: u_int i;
! 322: Channel *c;
! 323:
! 324: for (i = 0; i < channels_alloc; i++) {
! 325: c = channels[i];
! 326: if (c != NULL && c->type == SSH_CHANNEL_OPEN) {
! 327: if (!compat20 && buffer_len(&c->input) > packet_get_maxsize()) {
! 328: debug("channel %d: big input buffer %d",
! 329: c->self, buffer_len(&c->input));
! 330: return 0;
! 331: }
! 332: if (buffer_len(&c->output) > packet_get_maxsize()) {
! 333: debug("channel %d: big output buffer %d",
! 334: c->self, buffer_len(&c->output));
! 335: return 0;
! 336: }
! 337: }
! 338: }
! 339: return 1;
! 340: }
! 341:
! 342: /* Returns true if any channel is still open. */
! 343:
! 344: int
! 345: channel_still_open()
! 346: {
! 347: int i;
! 348: Channel *c;
! 349:
! 350: for (i = 0; i < channels_alloc; i++) {
! 351: c = channels[i];
! 352: if (c == NULL)
! 353: continue;
! 354: switch (c->type) {
! 355: case SSH_CHANNEL_X11_LISTENER:
! 356: case SSH_CHANNEL_PORT_LISTENER:
! 357: case SSH_CHANNEL_RPORT_LISTENER:
! 358: case SSH_CHANNEL_CLOSED:
! 359: case SSH_CHANNEL_AUTH_SOCKET:
! 360: case SSH_CHANNEL_DYNAMIC:
! 361: case SSH_CHANNEL_CONNECTING:
! 362: case SSH_CHANNEL_ZOMBIE:
! 363: continue;
! 364: case SSH_CHANNEL_LARVAL:
! 365: if (!compat20)
! 366: fatal("cannot happen: SSH_CHANNEL_LARVAL");
! 367: continue;
! 368: case SSH_CHANNEL_OPENING:
! 369: case SSH_CHANNEL_OPEN:
! 370: case SSH_CHANNEL_X11_OPEN:
! 371: return 1;
! 372: case SSH_CHANNEL_INPUT_DRAINING:
! 373: case SSH_CHANNEL_OUTPUT_DRAINING:
! 374: if (!compat13)
! 375: fatal("cannot happen: OUT_DRAIN");
! 376: return 1;
! 377: default:
! 378: fatal("channel_still_open: bad channel type %d", c->type);
! 379: /* NOTREACHED */
! 380: }
! 381: }
! 382: return 0;
! 383: }
! 384:
! 385: /* Returns the id of an open channel suitable for keepaliving */
! 386:
! 387: int
! 388: channel_find_open()
! 389: {
! 390: int i;
! 391: Channel *c;
! 392:
! 393: for (i = 0; i < channels_alloc; i++) {
! 394: c = channels[i];
! 395: if (c == NULL)
! 396: continue;
! 397: switch (c->type) {
! 398: case SSH_CHANNEL_CLOSED:
! 399: case SSH_CHANNEL_DYNAMIC:
! 400: case SSH_CHANNEL_X11_LISTENER:
! 401: case SSH_CHANNEL_PORT_LISTENER:
! 402: case SSH_CHANNEL_RPORT_LISTENER:
! 403: case SSH_CHANNEL_OPENING:
! 404: case SSH_CHANNEL_CONNECTING:
! 405: case SSH_CHANNEL_ZOMBIE:
! 406: continue;
! 407: case SSH_CHANNEL_LARVAL:
! 408: case SSH_CHANNEL_AUTH_SOCKET:
! 409: case SSH_CHANNEL_OPEN:
! 410: case SSH_CHANNEL_X11_OPEN:
! 411: return i;
! 412: case SSH_CHANNEL_INPUT_DRAINING:
! 413: case SSH_CHANNEL_OUTPUT_DRAINING:
! 414: if (!compat13)
! 415: fatal("cannot happen: OUT_DRAIN");
! 416: return i;
! 417: default:
! 418: fatal("channel_find_open: bad channel type %d", c->type);
! 419: /* NOTREACHED */
! 420: }
! 421: }
! 422: return -1;
! 423: }
! 424:
! 425:
! 426: /*
! 427: * Returns a message describing the currently open forwarded connections,
! 428: * suitable for sending to the client. The message contains crlf pairs for
! 429: * newlines.
! 430: */
! 431:
! 432: char *
! 433: channel_open_message()
! 434: {
! 435: Buffer buffer;
! 436: Channel *c;
! 437: char buf[1024], *cp;
! 438: int i;
! 439:
! 440: buffer_init(&buffer);
! 441: snprintf(buf, sizeof buf, "The following connections are open:\r\n");
! 442: buffer_append(&buffer, buf, strlen(buf));
! 443: for (i = 0; i < channels_alloc; i++) {
! 444: c = channels[i];
! 445: if (c == NULL)
! 446: continue;
! 447: switch (c->type) {
! 448: case SSH_CHANNEL_X11_LISTENER:
! 449: case SSH_CHANNEL_PORT_LISTENER:
! 450: case SSH_CHANNEL_RPORT_LISTENER:
! 451: case SSH_CHANNEL_CLOSED:
! 452: case SSH_CHANNEL_AUTH_SOCKET:
! 453: case SSH_CHANNEL_ZOMBIE:
! 454: continue;
! 455: case SSH_CHANNEL_LARVAL:
! 456: case SSH_CHANNEL_OPENING:
! 457: case SSH_CHANNEL_CONNECTING:
! 458: case SSH_CHANNEL_DYNAMIC:
! 459: case SSH_CHANNEL_OPEN:
! 460: case SSH_CHANNEL_X11_OPEN:
! 461: case SSH_CHANNEL_INPUT_DRAINING:
! 462: case SSH_CHANNEL_OUTPUT_DRAINING:
! 463: snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n",
! 464: c->self, c->remote_name,
! 465: c->type, c->remote_id,
! 466: c->istate, buffer_len(&c->input),
! 467: c->ostate, buffer_len(&c->output),
! 468: c->rfd, c->wfd);
! 469: buffer_append(&buffer, buf, strlen(buf));
! 470: continue;
! 471: default:
! 472: fatal("channel_open_message: bad channel type %d", c->type);
! 473: /* NOTREACHED */
! 474: }
! 475: }
! 476: buffer_append(&buffer, "\0", 1);
! 477: cp = xstrdup(buffer_ptr(&buffer));
! 478: buffer_free(&buffer);
! 479: return cp;
! 480: }
! 481:
! 482: void
! 483: channel_start_open(int id)
! 484: {
! 485: Channel *c = channel_lookup(id);
! 486: if (c == NULL) {
! 487: log("channel_open: %d: bad id", id);
! 488: return;
! 489: }
! 490: debug("send channel open %d", id);
! 491: packet_start(SSH2_MSG_CHANNEL_OPEN);
! 492: packet_put_cstring(c->ctype);
! 493: packet_put_int(c->self);
! 494: packet_put_int(c->local_window);
! 495: packet_put_int(c->local_maxpacket);
! 496: }
! 497: void
! 498: channel_open(int id)
! 499: {
! 500: /* XXX REMOVE ME */
! 501: channel_start_open(id);
! 502: packet_send();
! 503: }
! 504: void
! 505: channel_request(int id, char *service, int wantconfirm)
! 506: {
! 507: channel_request_start(id, service, wantconfirm);
! 508: packet_send();
! 509: debug("channel request %d: %s", id, service) ;
! 510: }
! 511: void
! 512: channel_request_start(int id, char *service, int wantconfirm)
! 513: {
! 514: Channel *c = channel_lookup(id);
! 515: if (c == NULL) {
! 516: log("channel_request: %d: bad id", id);
! 517: return;
! 518: }
! 519: packet_start(SSH2_MSG_CHANNEL_REQUEST);
! 520: packet_put_int(c->remote_id);
! 521: packet_put_cstring(service);
! 522: packet_put_char(wantconfirm);
! 523: }
! 524: void
! 525: channel_register_callback(int id, int mtype, channel_callback_fn *fn, void *arg)
! 526: {
! 527: Channel *c = channel_lookup(id);
! 528: if (c == NULL) {
! 529: log("channel_register_callback: %d: bad id", id);
! 530: return;
! 531: }
! 532: c->cb_event = mtype;
! 533: c->cb_fn = fn;
! 534: c->cb_arg = arg;
! 535: }
! 536: void
! 537: channel_register_cleanup(int id, channel_callback_fn *fn)
! 538: {
! 539: Channel *c = channel_lookup(id);
! 540: if (c == NULL) {
! 541: log("channel_register_cleanup: %d: bad id", id);
! 542: return;
! 543: }
! 544: c->dettach_user = fn;
! 545: }
! 546: void
! 547: channel_cancel_cleanup(int id)
! 548: {
! 549: Channel *c = channel_lookup(id);
! 550: if (c == NULL) {
! 551: log("channel_cancel_cleanup: %d: bad id", id);
! 552: return;
! 553: }
! 554: c->dettach_user = NULL;
! 555: }
! 556: void
! 557: channel_register_filter(int id, channel_filter_fn *fn)
! 558: {
! 559: Channel *c = channel_lookup(id);
! 560: if (c == NULL) {
! 561: log("channel_register_filter: %d: bad id", id);
! 562: return;
! 563: }
! 564: c->input_filter = fn;
! 565: }
! 566:
! 567: void
! 568: channel_set_fds(int id, int rfd, int wfd, int efd,
! 569: int extusage, int nonblock)
! 570: {
! 571: Channel *c = channel_lookup(id);
! 572: if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
! 573: fatal("channel_activate for non-larval channel %d.", id);
! 574: channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
! 575: c->type = SSH_CHANNEL_OPEN;
! 576: /* XXX window size? */
! 577: c->local_window = c->local_window_max = c->local_maxpacket * 2;
! 578: packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
! 579: packet_put_int(c->remote_id);
! 580: packet_put_int(c->local_window);
! 581: packet_send();
! 582: }