This is newchannels.c in view mode; [Download] [Up]
/* channels.c Author: Tatu Ylonen <ylo@cs.hut.fi> Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland All rights reserved Created: Fri Mar 24 16:35:24 1995 ylo This file contains functions for generic socket connection forwarding. There is also code for initiating connection forwarding for X11 connections, arbitrary tcp/ip connections, and the authentication agent connection. */ /* * $Id: newchannels.c,v 1.46 1998/07/08 00:46:01 kivinen Exp $ * $Log: newchannels.c,v $ * Revision 1.46 1998/07/08 00:46:01 kivinen * Splitted ip address display name from * HPSUX_NONSTANDARD_X11_KLUDGE to separate * NONSTANDARD_IP_ADDRESS_X11_KLUDGE ifdef. * Changed deny/allow stuff to use match_host instead of * match_pattern. * * Revision 1.45 1998/06/11 00:25:48 kivinen * Fixed typo in strncpy call. * * Revision 1.44 1998/06/11 00:08:20 kivinen * Added date to X11 connection rejected message. Added checking * of /var/X directory first and if that doesn't exists then try * X11_DIR and if that doesn't exists try /tmp/.X11-unix. * * Revision 1.43 1998/05/23 20:22:30 kivinen * Changed () -> (void). Fixed typo in debug text. Added check * that chown succeeds. * * Revision 1.42 1998/03/27 16:58:26 kivinen * Added gatewayports support. * * Revision 1.41 1998/01/03 06:41:38 kivinen * Fixed bug in allow/deny forward to host name handling. * * Revision 1.40 1997/08/21 22:16:26 ylo * Fixed security bug with port number > 65535 in remote forwarding. * * Revision 1.39 1997/04/27 22:20:11 kivinen * Fixed bug in port number parsing in * channel_add_{allow,deny}_forwd_to. * * Revision 1.38 1997/04/27 21:52:42 kivinen * Added F-SECURE stuff. Added {Allow,Deny}Forwarding{To,Port} * feature. * * Revision 1.37 1997/04/17 04:01:22 kivinen * Removed extra port variable. Added return -1 to * channel_allocate to get rid of warning. * * Revision 1.36 1997/04/05 21:48:54 kivinen * Fixed some debug messages (removed extra newline at the end). * Added clearing of input buffer after output have closed. * * Revision 1.35 1997/03/26 07:14:47 kivinen * Added checks about failed read and write for open channel. * * Revision 1.34 1997/03/25 05:39:36 kivinen * Added check that hp == NULL in hpux kludge display setting. * * Revision 1.33 1997/03/19 19:25:01 kivinen * Added input buffer clearing for error conditions, so packet.c * can check that buffer must be empty before new packet is read * in. * * Revision 1.32 1997/03/19 17:58:10 kivinen * Added checks that x11 and authentication agent forwarding is * really requested when open requests is received. * Added checks that strlen(hostname) <= 255. * * Revision 1.31 1997/01/23 14:41:35 ttsalo * Fixed a typo (%.200d -> %.200s) * * Revision 1.30 1997/01/22 21:18:58 ttsalo * Fixed a memory deallocation bug * * Revision 1.29 1996/12/04 19:04:40 ttsalo * Changed a debug message * * Revision 1.28 1996/12/04 18:16:52 ttsalo * Added printing of channel type in allocation * * Revision 1.27 1996/11/27 15:38:24 ttsalo * Added X11DisplayOffset-option * * Revision 1.26 1996/11/24 08:22:39 kivinen * Added tcp wrapper code to x11 and port forwarding. * Removed extra channel_send_ieof from rejected * X11 channel open. * Fixed channel_request_remote_forwarding so it wont call fatal * if the other ends doens't permit forwarding. * Changed auth_input_request_forwarding to return true if it * succeeds and false otherwise. Changed it to send errors with * packet_send_debug to client instead of disconnect. Made the * error messages more verbose. * * Revision 1.25 1996/11/09 17:00:32 ttsalo * Chdir out from socket directory before deleting it * * Revision 1.24 1996/11/04 06:34:45 ylo * Fixed a number of erroneous messages. * * Revision 1.23 1996/10/30 14:24:53 ttsalo * Changed two stats to lstats * * Revision 1.22 1996/10/29 22:42:43 kivinen * log -> log_msg. Fixed auth_input_request_forwarding to check * that the parent directory of SSH_AGENT_SOCKET_DIR and the .. * are same (check that the last component of agent directory * isn't symlink). * Disconnect if agent directory mkdir fails. * Renamed remotech to remote_channel in * auth_input_open_request. * * Revision 1.21 1996/10/29 14:18:51 ttsalo * Fixed a bug * * Revision 1.20 1996/10/29 14:07:24 ttsalo * Clarified some error messages * * Revision 1.19 1996/10/29 13:38:45 ttsalo * Improved the security of auth_input_request_forwarding() * * Revision 1.18 1996/10/24 14:05:10 ttsalo * Cleaning up old fd-auth trash * * Revision 1.17 1996/10/20 16:27:34 ttsalo * Many changes, agent stuff should now work as defined in the specs * * Revision 1.16 1996/09/28 16:26:09 ylo * Added a workaround for channel deadlocks... This may cause * sshd to grow occasionally, and indefinitely in some situations. * * Revision 1.15 1996/09/27 17:21:39 ylo * Merged ultrix, Next and Linux patches from Corey Satten. This * effectively puts all file descriptors in non-blocking mode, * because these systems appear to sometimes wake up from select * and then block in write even when they are not supposed to. * * Revision 1.14 1996/09/27 14:00:11 ttsalo * Replaced a chmod with umask setting, chmod was dangerous. * * Revision 1.13 1996/09/14 08:44:12 ylo * Print X11 auth protocols if different. * * Revision 1.12 1996/09/14 08:42:14 ylo * Fixed a (minor) bug in interactive output packet sizing. * Reduced maximum non-interactive packet size to 8192. * * Revision 1.11 1996/09/12 18:31:11 ttsalo * st->uid to st->st.uid * * Revision 1.10 1996/09/11 17:56:22 kivinen * Fixed serious security bug in auth_input_request_forwarding, * now we chown the directory only if we created it. Changed * auth_input_request_forwarding to check the permissions of * directory itself and not to call * userfile_check_owner_permissions (it doesn't check for read * and execute permissions). * * Revision 1.9 1996/09/08 17:21:06 ttsalo * A lot of changes in agent-socket handling * * Revision 1.8 1996/09/04 12:41:49 ttsalo * Minor fixes * * Revision 1.7 1996/08/29 14:51:25 ttsalo * Agent-socket directory handling implemented * * Revision 1.6 1996/08/21 20:43:54 ttsalo * Made ssh-agent use a different, more secure way of storing * it's initial socket. * * Revision 1.5 1996/07/31 07:10:35 huima * Fixed the connection closing bug. * * Revision 1.4 1996/07/15 00:28:57 ylo * When an X11 connection is rejected, log where the rejected * connection came from. * * Revision 1.3 1996/07/12 07:22:47 ttsalo * Patch from <Rein.Tollevik@si.sintef.no> to handle HP-UX * nonstandard X11 socket kludging. * * Revision 1.2 1996/05/06 09:53:24 huima * Fixed a major in the channels allocation. * * Revision 1.1 1996/04/22 23:43:52 huima * New channels code added to the repository. * * Revision 1.1.1.1 1996/02/18 21:38:12 ylo * Imported ssh-1.2.13 * * Revision 1.13 1995/10/02 01:20:08 ylo * Added a cast to avoid compiler warning. * * Revision 1.12 1995/09/24 23:58:49 ylo * Added support for screen number in X11 forwarding. * Reduced max packet size in interactive mode from 1024 bytes to * 512 bytes for forwarded connections. * * Revision 1.11 1995/09/21 17:08:40 ylo * Support AF_UNIX_SIZE. * * Revision 1.10 1995/09/10 23:25:35 ylo * Fixed HPSUX DISPLAY kludge. * * Revision 1.9 1995/09/10 22:45:23 ylo * Changed Unix domain socket and umask stuff. * * Revision 1.8 1995/09/09 21:26:39 ylo * /m/shadows/u2/users/ylo/ssh/README * * Revision 1.7 1995/09/06 15:58:14 ylo * Added BROKEN_INET_ADDR. * * Revision 1.6 1995/08/29 22:20:40 ylo * New file descriptor code for agent forwarding. * * Revision 1.5 1995/08/21 23:22:49 ylo * Clear sockaddr_in structures before use. * * Reject X11 connections that don't match fake data. * * Revision 1.4 1995/08/18 22:47:11 ylo * Fixed a typo (missing parentheses in packet_is_interactive * call). * * Removed extra shutdown in channel_close_all(). This caused * the "accept: software caused connection abort" messages and * busy looping that made the previous version of ssh unusable on * most systems. * * Revision 1.3 1995/07/27 02:16:43 ylo * Fixed output draining on forwarded TCP/IP connections. * Use smaller packets for interactive sessions. * * Revision 1.2 1995/07/13 01:19:29 ylo * Removed "Last modified" header. * Added cvs log. * * $Endlog$ */ #include "includes.h" #if !defined(HAVE_GETHOSTNAME) || defined(NONSTANDARD_IP_ADDRESS_X11_KLUDGE) #include <sys/utsname.h> #endif #include "ssh.h" #include "packet.h" #include "xmalloc.h" #include "buffer.h" #include "authfd.h" #include "emulate.h" #include "servconf.h" #ifdef LIBWRAP #include <tcpd.h> #include <syslog.h> #ifdef NEED_SYS_SYSLOG_H #include <sys/syslog.h> #endif /* NEED_SYS_SYSLOG_H */ #endif /* LIBWRAP */ /* Directory in which the fake unix-domain X11 displays reside. */ #ifndef X11_DIR #define X11_DIR "/tmp/.X11-unix" #endif /* Maximum number of fake X11 displays to try. */ #define MAX_DISPLAYS 1000 /* Definitions for channel types. */ #define SSH_CHANNEL_FREE 0 /* This channel is free (unused). */ #define SSH_CHANNEL_X11_LISTENER 1 /* Listening for inet X11 conn. */ #define SSH_CHANNEL_PORT_LISTENER 2 /* Listening on a port. */ #define SSH_CHANNEL_OPENING 3 /* waiting for confirmation */ #define SSH_CHANNEL_OPEN 4 /* normal open two-way channel */ /* obsolete SSH_CHANNEL_CLOSED 5 waiting for close confirmation */ /* obsolete SSH_CHANNEL_AUTH_FD 6 authentication fd */ /* obsolete SSH_CHANNEL_AUTH_SOCKET 7 authentication socket */ /* obsolete SSH_CHANNEL_AUTH_SOCKET_FD 8 connection to auth socket */ #define SSH_CHANNEL_X11_OPEN 9 /* reading first X11 packet */ #define SSH_CHANNEL_AUTH_LISTENER 10 /* Agent proxy listening for connections */ /* Status flags */ #define STATUS_INPUT_SOCKET_CLOSED 0x0001 #define STATUS_OUTPUT_SOCKET_CLOSED 0x0002 #define STATUS_EOF_SENT 0x0004 #define STATUS_EOF_RECEIVED 0x0008 #define STATUS_CLOSE_SENT 0x0010 #define STATUS_CLOSE_RECEIVED 0x0020 #define STATUS_KLUDGE_A 0x0040 #define STATUS_KLUDGE_B 0x0080 #define STATUS_TERMINATE 0x003f /* Data structure for channel data. This is iniailized in channel_allocate and cleared in channel_free. */ typedef struct { int type; int sock; int remote_id, local_id; int status_flags; /* for keeping book on the internal state */ Buffer input; Buffer output; char path[200]; /* path for unix domain sockets, or host name for forwards */ int host_port; /* port to connect for forwards */ int listening_port; /* port being listened for forwards */ char *remote_name; int is_x_connection; } Channel; /* Pointer to an array containing all allocated channels. The array is dynamically extended as needed. */ static Channel *channels = NULL; /* Size of the channel array. All slots of the array must always be initialized (at least the type field); unused slots are marked with type SSH_CHANNEL_FREE. */ static int channels_alloc = 0; /* Number of currently used channels. Used to determine if we should expand the array. */ static int channels_used = 0; /* Maximum file descriptor value used in any of the channels. This is updated in channel_allocate. */ static int channel_max_fd_value = 0; /* These two variables are for authentication agent forwarding. */ static int channel_forwarded_auth_fd = -1; static char *channel_forwarded_auth_socket_name = NULL; /* Agent forwarding socket directory name */ static char *channel_forwarded_auth_socket_dir_name = NULL; /* Saved X11 authentication protocol name. */ char *x11_saved_proto = NULL; /* Saved X11 authentication data. This is the real data. */ char *x11_saved_data = NULL; unsigned int x11_saved_data_len = 0; /* Fake X11 authentication data. This is what the server will be sending us; we should replace any occurrences of this by the real data. */ char *x11_fake_data = NULL; unsigned int x11_fake_data_len; #ifdef F_SECURE_COMMERCIAL #endif /* F_SECURE_COMMERCIAL */ /* Data structure for storing which hosts are permitted for forward requests. The local sides of any remote forwards are stored in this array to prevent a corrupt remote server from accessing arbitrary TCP/IP ports on our local network (which might be behind a firewall). */ typedef struct { char *host; /* Host name. */ int port; /* Port number. */ } ForwardPermission; /* List of all permitted host/port pairs to connect. */ static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; /* Number of permitted host/port pairs in the array. */ static int num_permitted_opens = 0; /* If this is true, all opens are permitted. This is the case on the server on which we have to trust the client anyway, and the user could do anything after logging in anyway. */ static int all_opens_permitted = 0; /* X11 forwarding permitted */ static int x11_forwarding_permitted = 0; /* Agent forwarding permitted */ static int auth_forwarding_permitted = 0; /* This is set to true if both sides support SSH_PROTOFLAG_HOST_IN_FWD_OPEN. */ static int have_hostname_in_open = 0; /* Sets specific protocol options. */ void channel_set_options(int hostname_in_open) { have_hostname_in_open = hostname_in_open; } /* Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually called by the server, because the user could connect to any port anyway, and the server has no way to know but to trust the client anyway. */ void channel_permit_all_opens(void) { all_opens_permitted = 1; } /* Allocate a new channel object and set its type and socket. This will cause remote_name to be freed. */ int channel_allocate(int type, int sock, char *remote_name) { int i; /* Update the maximum file descriptor value. */ if (sock > channel_max_fd_value) channel_max_fd_value = sock; /* Do initial allocation if this is the first call. */ if (channels_alloc == 0) { channels_alloc = 10; channels = xmalloc(channels_alloc * sizeof(Channel)); for (i = 0; i < channels_alloc; i++) channels[i].type = SSH_CHANNEL_FREE; /* Kludge: arrange a call to channel_stop_listening if we terminate with fatal(). */ fatal_add_cleanup((void (*)(void *))channel_stop_listening, NULL); } i = 0; if (channels_used == channels_alloc) { /* There are no free slots. Must expand the array. */ channels_alloc += 10; debug("Expanding the array..."); channels = xrealloc(channels, channels_alloc * sizeof(Channel)); for (i = channels_used; i < channels_alloc; i++) channels[i].type = SSH_CHANNEL_FREE; i = channels_used; debug("Array now %d channels [first free at %d].", channels_alloc, i); } /* Try to find a free slot where to put the new channel. */ while (i < channels_alloc) { if (channels[i].type == SSH_CHANNEL_FREE) { /* Found a free slot. Initialize the fields and return its number. */ buffer_init(&channels[i].input); buffer_init(&channels[i].output); channels[i].type = type; channels[i].sock = sock; channels[i].remote_id = -1; channels[i].remote_name = remote_name; channels[i].status_flags = 0; channels[i].local_id = i; channels[i].is_x_connection = 0; channels_used++; debug("Allocated channel %d of type %d.", i, type); return i; } i++; } fatal ("Internal bug in channel_allocate."); return -1; } /* Free the channel and close its socket. */ void channel_free(int channel) { assert(channel >= 0 && channel < channels_alloc && channels[channel].type != SSH_CHANNEL_FREE); close(channels[channel].sock); buffer_free(&channels[channel].input); buffer_free(&channels[channel].output); channels[channel].type = SSH_CHANNEL_FREE; channels_used--; if (channels[channel].remote_name) { xfree(channels[channel].remote_name); channels[channel].remote_name = NULL; } } /* Support for the new protocol */ /* A channel can be freed when it has sent and received input eof and output closed */ void channel_check_termination(Channel *ch) { if ((ch->status_flags & STATUS_TERMINATE) == STATUS_TERMINATE) { #ifdef SUPPORT_OLD_CHANNELS if ((emulation_information & EMULATE_OLD_CHANNEL_CODE) && !(ch->status_flags & STATUS_KLUDGE_A)) { ch->status_flags &= ~(STATUS_CLOSE_RECEIVED | STATUS_EOF_RECEIVED); debug("Discarding termination of channel %d.", ch->local_id); return; } #endif debug("Channel %d terminates.", ch->local_id); #ifdef SUPPORT_OLD_CHANNELS if (emulation_information & EMULATE_OLD_CHANNEL_CODE) if (ch->status_flags & STATUS_KLUDGE_B) { packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); packet_put_int(ch->remote_id); packet_send(); } #endif channel_free(ch->local_id); } } /* If ieof has not yet been sent, send it */ void channel_send_ieof(Channel *ch) { if (!(ch->status_flags & STATUS_EOF_SENT)) { debug("Channel %d sends ieof.", ch->local_id); ch->status_flags |= STATUS_EOF_SENT; #ifdef SUPPORT_OLD_CHANNELS /* This is SSH_MSG_CHANNEL_CLOSE in the old protocol */ #endif packet_start(SSH_MSG_CHANNEL_INPUT_EOF); packet_put_int(ch->remote_id); packet_send(); #ifdef SUPPORT_OLD_CHANNELS if (emulation_information & EMULATE_OLD_CHANNEL_CODE) ch->status_flags |= STATUS_CLOSE_SENT; #endif channel_check_termination(ch); } } /* If oclosed has not yet been sent, send it */ #ifdef SUPPORT_OLD_CHANNELS /* This will be never called if we're emulating the old code */ #endif void channel_send_oclosed(Channel *ch) { if (!(ch->status_flags & STATUS_CLOSE_SENT)) { debug("Channel %d sends oclosed.", ch->local_id); ch->status_flags |= STATUS_CLOSE_SENT; packet_start(SSH_MSG_CHANNEL_OUTPUT_CLOSED); packet_put_int(ch->remote_id); packet_send(); channel_check_termination(ch); } } /* Close input if hasn't been done yet. Input buffer is not discarded, because we want to drain it over the channel. */ #ifdef SUPPORT_OLD_CHANNELS void channel_close_output(Channel *ch); #endif void channel_close_input(Channel *ch) { if (!(ch->status_flags & STATUS_INPUT_SOCKET_CLOSED)) { debug("Channel %d closes incoming data stream.", ch->local_id); ch->status_flags |= STATUS_INPUT_SOCKET_CLOSED; shutdown(ch->sock, 0); if (ch->type != SSH_CHANNEL_OPEN) { channel_send_ieof(ch); } #ifdef SUPPORT_OLD_CHANNELS if (emulation_information & EMULATE_OLD_CHANNEL_CODE) { channel_close_output(ch); } #endif } } /* Close output if it hasn't been done yet. Output buffer is discarded, because we couldn't put it anywhere else (as the socket is closed). Send info about the close immediately. */ void channel_close_output(Channel *ch) { if (!(ch->status_flags & STATUS_OUTPUT_SOCKET_CLOSED)) { debug("Channel %d closes outgoing data stream.", ch->local_id); ch->status_flags |= STATUS_OUTPUT_SOCKET_CLOSED; shutdown(ch->sock, 1); if (buffer_len(&ch->output)) buffer_consume(&ch->output, buffer_len(&ch->output)); #ifdef SUPPORT_OLD_CHANNELS if (emulation_information & EMULATE_OLD_CHANNEL_CODE) { debug("This is emulation."); channel_close_input(ch); } else #endif channel_send_oclosed(ch); } } /* Receive input eof. Nothing is done yet, because the output buffer might want to drain away. */ void channel_receive_ieof(Channel *ch) { if (ch->status_flags & STATUS_EOF_RECEIVED) packet_disconnect("Received double input eof."); debug("Channel %d receives input eof.", ch->local_id); ch->status_flags |= STATUS_EOF_RECEIVED; if (ch->is_x_connection) { debug("X problem fix: close the other direction."); channel_close_input(ch); } if (ch->type != SSH_CHANNEL_OPEN) channel_close_output(ch); channel_check_termination(ch); } /* Receive output closed. Input will be immediately closed, because the other party is not interested in our packets. */ void channel_receive_oclosed(Channel *ch) { if (ch->status_flags & STATUS_CLOSE_RECEIVED) packet_disconnect("Received double close."); debug("Channel %d receives output closed.", ch->local_id); ch->status_flags |= STATUS_CLOSE_RECEIVED; buffer_clear(&ch->input); channel_close_input(ch); channel_check_termination(ch); } /* This is called after receiving CHANNEL_INPUT_EOF */ void channel_ieof(void) { int channel; /* Get the channel number and verify it. */ channel = packet_get_int(); if (channel < 0 || channel >= channels_alloc || channels[channel].type == SSH_CHANNEL_FREE) packet_disconnect("Received ieof for nonexistent channel %d.", channel); channel_receive_ieof(&channels[channel]); } /* This is called after receiving CHANNEL_OUTPUT_CLOSED */ void channel_oclosed(void) { int channel; /* Get the channel number and verify it. */ channel = packet_get_int(); if (channel < 0 || channel >= channels_alloc || channels[channel].type == SSH_CHANNEL_FREE) packet_disconnect("Received oclosed for nonexistent channel %d.", channel); channel_receive_oclosed(&channels[channel]); } #ifdef SUPPORT_OLD_CHANNELS void channel_emulated_close(int set_a) { int channel; debug("Emulated close."); channel = packet_get_int(); if (channel < 0 || channel >= channels_alloc || channels[channel].type == SSH_CHANNEL_FREE) packet_disconnect("Received emulated_close for nonexistent channel %d.", channel); if (set_a) channels[channel].status_flags |= STATUS_KLUDGE_A; else channels[channel].status_flags |= STATUS_KLUDGE_B; channel_receive_ieof(&channels[channel]); channel_receive_oclosed(&channels[channel]); } #endif /* This is called just before select() to add any bits relevant to channels in the select bitmasks. */ void channel_prepare_select(fd_set *readset, fd_set *writeset) { int i; Channel *ch; unsigned char *ucp; unsigned int proto_len, data_len; for (i = 0; i < channels_alloc; i++) { ch = &channels[i]; redo: switch (ch->type) { case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_AUTH_LISTENER: FD_SET(ch->sock, readset); break; case SSH_CHANNEL_OPEN: if ((buffer_len(&ch->input) < packet_max_size() / 2) && (!(ch->status_flags & STATUS_INPUT_SOCKET_CLOSED))) FD_SET(ch->sock, readset); if (!(ch->status_flags & STATUS_OUTPUT_SOCKET_CLOSED)) { if (buffer_len(&ch->output) > 0) { FD_SET(ch->sock, writeset); } else { if (ch->status_flags & STATUS_EOF_RECEIVED) { channel_close_output(ch); } } } break; case SSH_CHANNEL_X11_OPEN: /* This is a special state for X11 authentication spoofing. An opened X11 connection (when authentication spoofing is being done) remains in this state until the first packet has been completely read. The authentication data in that packet is then substituted by the real data if it matches the fake data, and the channel is put into normal mode. */ /* Check if the fixed size part of the packet is in buffer. */ if (buffer_len(&ch->output) < 12) break; /* Parse the lengths of variable-length fields. */ ucp = (unsigned char *)buffer_ptr(&ch->output); if (ucp[0] == 0x42) { /* Byte order MSB first. */ proto_len = 256 * ucp[6] + ucp[7]; data_len = 256 * ucp[8] + ucp[9]; } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ 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", ucp[0]); ch->type = SSH_CHANNEL_OPEN; goto reject; } /* Check if the whole packet is in buffer. */ if (buffer_len(&ch->output) < 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) break; /* Check if authentication protocol matches. */ if (proto_len != strlen(x11_saved_proto) || memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { if (proto_len > 100) proto_len = 100; /* Limit length of output. */ debug("X11 connection uses different authentication protocol: '%.100s' vs. '%.*s'.", x11_saved_proto, proto_len, (const char *)(ucp + 12)); ch->type = SSH_CHANNEL_OPEN; goto reject; } /* 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."); ch->type = SSH_CHANNEL_OPEN; goto reject; } /* Received authentication protocol and data match our fake data. Substitute the fake data with real data. */ assert(x11_fake_data_len == x11_saved_data_len); memcpy(ucp + 12 + ((proto_len + 3) & ~3), x11_saved_data, x11_saved_data_len); /* Start normal processing for the channel. */ ch->type = SSH_CHANNEL_OPEN; goto redo; reject: /* We have received an X11 connection that has bad authentication information. */ { time_t t; char buffer[255], *p; t = time(NULL); strncpy(buffer, ctime(&t), sizeof(buffer)); buffer[sizeof(buffer) - 1] = '\0'; p = strchr(buffer, '\n'); if (p) *p = '\0'; p = strchr(buffer, '\r'); if (p) *p = '\0'; log_msg("X11 connection rejected because of wrong authentication at %s.\r\na", buffer); if (ch->remote_name) log_msg("Rejected connection at %s: %.200s\r\n", buffer, ch->remote_name); } buffer_clear(&ch->input); buffer_clear(&ch->output); channel_close_input(ch); channel_close_output(ch); /* Output closed has been sent in close_output except if we're emulating the old code, but then we wouldn't send it anyway. */ /* We will then wait until both closing packets have come, then the channel gets destroyed - and the socket closed. */ break; case SSH_CHANNEL_FREE: default: continue; } } } /* After select, perform any appropriate operations for channels which have events pending. */ void channel_after_select(fd_set *readset, fd_set *writeset) { struct sockaddr addr; int addrlen, newsock, i, newch, len, temp; Channel *ch; char buf[16384], *remote_hostname; /* Loop over all channels... */ for (i = 0; i < channels_alloc; i++) { ch = &channels[i]; switch (ch->type) { case SSH_CHANNEL_X11_LISTENER: /* This is our fake X11 server socket. */ if (FD_ISSET(ch->sock, readset)) { debug("X11 connection requested."); addrlen = sizeof(addr); newsock = accept(ch->sock, &addr, &addrlen); if (newsock < 0) { error("accept: %.100s", strerror(errno)); break; } remote_hostname = get_remote_hostname(newsock); sprintf(buf, "X11 connection from %.200s port %d", remote_hostname, get_peer_port(newsock)); xfree(remote_hostname); #ifdef LIBWRAP { struct request_info req; struct servent *serv; /* fill req struct with port name and fd number */ request_init(&req, RQ_DAEMON, "sshdfwd-X11", RQ_FILE, newsock, NULL); fromhost(&req); if (!hosts_access(&req)) { packet_send_debug("Fwd X11 connection from %.500s refused by tcp_wrappers.", eval_client(&req)); error("Fwd X11 connection from %.500s refused by tcp_wrappers.", eval_client(&req)); shutdown(newsock, 2); close(newsock); break; } log_msg("fwd X11 connect from %.500s", eval_client(&req)); } #endif /* LIBWRAP */ newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, xstrdup(buf)); channels[newch].is_x_connection = 1; packet_start(SSH_SMSG_X11_OPEN); packet_put_int(newch); if (have_hostname_in_open) packet_put_string(buf, strlen(buf)); packet_send(); } break; case SSH_CHANNEL_PORT_LISTENER: /* This socket is listening for connections to a forwarded TCP/IP port. */ if (FD_ISSET(ch->sock, readset)) { debug("Connection to port %d forwarding to %.100s:%d requested.", ch->listening_port, ch->path, ch->host_port); addrlen = sizeof(addr); newsock = accept(ch->sock, &addr, &addrlen); if (newsock < 0) { error("accept: %.100s", strerror(errno)); break; } remote_hostname = get_remote_hostname(newsock); sprintf(buf, "port %d, connection from %.200s port %d", ch->listening_port, remote_hostname, get_peer_port(newsock)); xfree(remote_hostname); #ifdef LIBWRAP { struct request_info req; struct servent *serv; char fwdportname[32]; /* try to find port's name in /etc/services */ serv = getservbyport(htons(ch->listening_port), "tcp"); if (serv == NULL) { /* not found (or faulty getservbyport) - use the number as a name */ sprintf(fwdportname,"sshdfwd-%d", ch->listening_port); } else { sprintf(fwdportname, "sshdfwd-%.20s", serv->s_name); } /* fill req struct with port name and fd number */ request_init(&req, RQ_DAEMON, fwdportname, RQ_FILE, newsock, NULL); fromhost(&req); if (!hosts_access(&req)) { packet_send_debug("Fwd connection from %.500s to local port %s refused by tcp_wrappers.", eval_client(&req), fwdportname); error("Fwd connection from %.500s to local port %s refused by tcp_wrappers.", eval_client(&req), fwdportname); shutdown(newsock, 2); close(newsock); break; } log_msg("fwd connect from %.500s to local port %s", eval_client(&req), fwdportname); } #endif /* LIBWRAP */ newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, xstrdup(buf)); packet_start(SSH_MSG_PORT_OPEN); packet_put_int(newch); packet_put_string(ch->path, strlen(ch->path)); packet_put_int(ch->host_port); if (have_hostname_in_open) packet_put_string(buf, strlen(buf)); packet_send(); } break; case SSH_CHANNEL_AUTH_LISTENER: /* This socket is listening for connections to a forwarded agent port. */ if (FD_ISSET(ch->sock, readset)) { debug("Connection to agent proxy requested from unix domain socket."); addrlen = sizeof(addr); newsock = accept(ch->sock, &addr, &addrlen); if (newsock < 0) { error("accept: %.100s", strerror(errno)); break; } sprintf(buf, "Forwarded agent connection"); newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, xstrdup(buf)); packet_start(SSH_SMSG_AGENT_OPEN); packet_put_int(newch); packet_send(); } break; case SSH_CHANNEL_OPEN: /* This is an open two-way communication channel. It is not of interest to us at this point what kind of data is being transmitted. */ /* Read available incoming data and append it to buffer. */ if (FD_ISSET(ch->sock, readset)) { len = sizeof(buf); if (len > packet_max_size() / 4) len = packet_max_size() / 4; len = read(ch->sock, buf, len); if (len < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) goto no_rdata; if (len <= 0) { channel_close_input(ch); break; } buffer_append(&ch->input, buf, len); } no_rdata: /* Send buffered output data to the socket. */ if (FD_ISSET(ch->sock, writeset) && (temp = buffer_len(&ch->output)) > 0) { len = write(ch->sock, buffer_ptr(&ch->output), temp); if (len < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) goto no_wdata; if (len <= 0) { channel_close_output(ch); break; } buffer_consume(&ch->output, len); } no_wdata: break; case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_FREE: default: continue; } } } /* If there is data to send to the connection, send some of it now. */ void channel_output_poll(void) { int len, i; Channel *ch; for (i = 0; i < channels_alloc; i++) { /* If we have very much data going to the output socket, don't send more now. */ if (!packet_not_very_much_data_to_write()) break; /* Don't send any more data now. */ ch = &channels[i]; /* We are only interested in channels that can have buffered incoming data. */ if (ch->type != SSH_CHANNEL_OPEN) continue; /* Get the amount of buffered data for this channel. */ len = buffer_len(&ch->input); if (len > 0) { /* Send some data for the other side over the secure connection. */ if (packet_is_interactive()) { if (len > 512) len = 512; } else { if (len > 8192) len = 8192; /* Keep the packets at reasonable size. */ if (len > packet_max_size() / 2) len = packet_max_size() / 2; } packet_start(SSH_MSG_CHANNEL_DATA); packet_put_int(ch->remote_id); packet_put_string(buffer_ptr(&ch->input), len); packet_send(); buffer_consume(&ch->input, len); } /* check if we should send epsilon out */ else { /* input buffer is empty, input socket closed, input eof not sent, send it now */ if ((ch->status_flags & (STATUS_INPUT_SOCKET_CLOSED | STATUS_EOF_SENT)) == STATUS_INPUT_SOCKET_CLOSED) { channel_send_ieof(ch); } } } } /* This is called when a packet of type CHANNEL_DATA has just been received. The message type has already been consumed, but channel number and data is still there. */ void channel_input_data(void) { int channel; char *data; unsigned int data_len; /* Get the channel number and verify it. */ channel = packet_get_int(); if (channel < 0 || channel >= channels_alloc || channels[channel].type == SSH_CHANNEL_FREE) packet_disconnect("Received data for nonexistent channel %d.", channel); /* Ignore any data for non-open channels (might happen on close) */ if (channels[channel].type != SSH_CHANNEL_OPEN && channels[channel].type != SSH_CHANNEL_X11_OPEN) { packet_get_all(); return; } /* Get the data. */ if (channels[channel].status_flags & STATUS_EOF_RECEIVED) packet_disconnect("Other party sent data after eof for channel %d.", channel); if (!(channels[channel].status_flags & STATUS_OUTPUT_SOCKET_CLOSED)) { data = packet_get_string(&data_len); buffer_append(&channels[channel].output, data, data_len); xfree(data); } else { packet_get_all(); } } /* Returns true if no channel has too much buffered data, and false if one or more channel is overfull. */ int channel_not_very_much_buffered_data(void) { #if 0 unsigned int i; Channel *ch; for (i = 0; i < channels_alloc; i++) { ch = &channels[i]; switch (channels[i].type) { case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_AUTH_LISTENER: continue; case SSH_CHANNEL_OPEN: if (buffer_len(&ch->input) > 32000) return 0; if (buffer_len(&ch->output) > 32000) return 0; continue; case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_FREE: default: continue; } } #endif /* 0 */ return 1; } #ifdef SUPPORT_OLD_CHANNELS void channel_input_close_confirmation(void) { if (emulation_information & EMULATE_OLD_CHANNEL_CODE) channel_emulated_close(1); else channel_oclosed(); } void channel_input_close(void) { if (emulation_information & EMULATE_OLD_CHANNEL_CODE) channel_emulated_close(0); else channel_ieof(); } #endif /* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */ void channel_input_open_confirmation(void) { int channel, remote_channel; /* Get the channel number and verify it. */ channel = packet_get_int(); if (channel < 0 || channel >= channels_alloc || channels[channel].type != SSH_CHANNEL_OPENING) packet_disconnect("Received open confirmation for non-opening channel %d.", channel); /* Get remote side's id for this channel. */ remote_channel = packet_get_int(); /* Record the remote channel number and mark that the channel is now open. */ debug("Channel now open, status bits %x", channels[channel].status_flags); channels[channel].remote_id = remote_channel; channels[channel].type = SSH_CHANNEL_OPEN; } /* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */ void channel_input_open_failure(void) { int channel; /* Get the channel number and verify it. */ channel = packet_get_int(); if (channel < 0 || channel >= channels_alloc || channels[channel].type != SSH_CHANNEL_OPENING) packet_disconnect("Received open failure for non-opening channel %d.", channel); /* Free the channel. This will also close the socket. */ channel_free(channel); } /* Stops listening for channels, and removes any unix domain sockets that we might have. */ void channel_stop_listening(void) { int i; for (i = 0; i < channels_alloc; i++) { switch (channels[i].type) { case SSH_CHANNEL_AUTH_LISTENER: auth_delete_socket(NULL); break; case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_X11_LISTENER: close(channels[i].sock); channel_free(i); break; default: break; } } } /* Closes the sockets of all channels. This is used to close extra file descriptors after a fork. */ void channel_close_all(void) { int i; for (i = 0; i < channels_alloc; i++) { if (channels[i].type != SSH_CHANNEL_FREE) close(channels[i].sock); } } /* Returns the maximum file descriptor number used by the channels. */ int channel_max_fd(void) { return channel_max_fd_value; } /* Returns true if any channel is still open. */ int channel_still_open(void) { unsigned int i; for (i = 0; i < channels_alloc; i++) switch (channels[i].type) { case SSH_CHANNEL_FREE: case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_AUTH_LISTENER: continue; case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: return 1; default: fatal("channel_still_open: bad channel type %d", channels[i].type); /*NOTREACHED*/ } return 0; } /* Returns a message describing the currently open forwarded connections, suitable for sending to the client. The message contains crlf pairs for newlines. */ char *channel_open_message(void) { Buffer buffer; int i; char buf[512], *cp; buffer_init(&buffer); sprintf(buf, "The following connections are open:\r\n"); buffer_append(&buffer, buf, strlen(buf)); for (i = 0; i < channels_alloc; i++) switch (channels[i].type) { case SSH_CHANNEL_FREE: case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_AUTH_LISTENER: continue; case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: sprintf(buf, " %.300s\r\n", channels[i].remote_name); buffer_append(&buffer, buf, strlen(buf)); continue; default: fatal("channel_still_open: bad channel type %d", channels[i].type); /*NOTREACHED*/ } buffer_append(&buffer, "\0", 1); cp = xstrdup(buffer_ptr(&buffer)); buffer_free(&buffer); return cp; } /* Initiate forwarding of connections to local port "port" through the secure channel to host:port from remote side. */ void channel_request_local_forwarding(int port, const char *host, int host_port, int gatewayports) { int ch, sock; struct sockaddr_in sin; if (strlen(host) > sizeof(channels[0].path) - 1) packet_disconnect("Forward host name too long."); /* Create a port to listen for the host. */ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) packet_disconnect("socket: %.100s", strerror(errno)); #if defined(O_NONBLOCK) && !defined(O_NONBLOCK_BROKEN) (void)fcntl(sock, F_SETFL, O_NONBLOCK); #else /* O_NONBLOCK && !O_NONBLOCK_BROKEN */ (void)fcntl(sock, F_SETFL, O_NDELAY); #endif /* O_NONBLOCK && !O_NONBLOCK_BROKEN */ /* Initialize socket address. */ memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; if (gatewayports) sin.sin_addr.s_addr = INADDR_ANY; else #ifdef BROKEN_INET_ADDR sin.sin_addr.s_addr = inet_network("127.0.0.1"); #else /* BROKEN_INET_ADDR */ sin.sin_addr.s_addr = inet_addr("127.0.0.1"); #endif /* BROKEN_INET_ADDR */ sin.sin_port = htons(port); /* Bind the socket to the address. */ if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) packet_disconnect("bind: %.100s", strerror(errno)); /* Start listening for connections on the socket. */ if (listen(sock, 5) < 0) packet_disconnect("listen: %.100s", strerror(errno)); /* Allocate a channel number for the socket. */ ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock, xstrdup("port listener")); strcpy(channels[ch].path, host); /* note: host name stored here */ channels[ch].host_port = host_port; /* port on host to connect to */ channels[ch].listening_port = port; /* port being listened */ } /* Initiate forwarding of connections to port "port" on remote host through the secure channel to host:port from local side. */ void channel_request_remote_forwarding(int port, const char *host, int remote_port) { int type; /* Send the forward request to the remote side. */ packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); packet_put_int(port); packet_put_string(host, strlen(host)); packet_put_int(remote_port); packet_send(); packet_write_wait(); /* Wait for response from the remote side. It will send a disconnect message on failure, and we will never see it here. */ type = packet_read(); if (type == SSH_SMSG_FAILURE) { debug("Remote end denied port forwarding to %d:%.50s:%d", port, host, remote_port); return; } if (type != SSH_SMSG_SUCCESS) packet_disconnect("Protocol error: expected packet type %d, got %d", SSH_SMSG_SUCCESS, type); /* Record locally that connection to this host/port is permitted. */ if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) fatal("channel_request_remote_forwarding: too many forwards"); permitted_opens[num_permitted_opens].host = xstrdup(host); permitted_opens[num_permitted_opens].port = remote_port; num_permitted_opens++; } #ifdef F_SECURE_COMMERCIAL #endif /* F_SECURE_COMMERCIAL */ /* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates listening for the port, and sends back a success reply (or disconnect message if there was an error). This never returns if there was an error. */ void channel_input_port_forward_request(int is_root) { int port, host_port; char *hostname; /* Get arguments from the packet. */ port = packet_get_int(); hostname = packet_get_string(NULL); host_port = packet_get_int(); if (strlen(hostname) > 255) packet_disconnect("Requested forwarding hostname too long: %.200s.", hostname); /* Check that an unprivileged user is not trying to forward a privileged port. */ if ((port < 1024 || port > 65535) && !is_root) packet_disconnect("Requested forwarding of port %d but user is not root.", port); #ifdef F_SECURE_COMMERCIAL #endif /* F_SECURE_COMMERCIAL */ /* Initiate forwarding. */ channel_request_local_forwarding(port, hostname, host_port, 1); /* Free the argument string. */ xfree(hostname); return; fail: xfree(hostname); return; } /* This is called after receiving PORT_OPEN message. This attempts to connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION or CHANNEL_OPEN_FAILURE. */ void channel_input_port_open(void) { int remote_channel, sock, newch, host_port, i; struct sockaddr_in sin; char *host, *originator_string; struct hostent *hp; /* Get remote channel number. */ remote_channel = packet_get_int(); /* Get host name to connect to. */ host = packet_get_string(NULL); if (strlen(host) > 255) packet_disconnect("Requested forwarding hostname too long: %.200s.", host); /* Get port to connect to. */ host_port = packet_get_int(); /* Get remote originator name. */ if (have_hostname_in_open) originator_string = packet_get_string(NULL); else originator_string = xstrdup("unknown (remote did not supply name)"); /* Check if opening that port is permitted. */ if (!all_opens_permitted) { /* Go trough all permitted ports. */ for (i = 0; i < num_permitted_opens; i++) if (permitted_opens[i].port == host_port && strcmp(permitted_opens[i].host, host) == 0) break; /* Check if we found the requested port among those permitted. */ if (i >= num_permitted_opens) { /* The port is not permitted. */ log_msg("Received request to connect to %.100s:%d, but the request was denied.", host, host_port); goto fail; } } memset(&sin, 0, sizeof(sin)); #ifdef BROKEN_INET_ADDR sin.sin_addr.s_addr = inet_network(host); #else /* BROKEN_INET_ADDR */ sin.sin_addr.s_addr = inet_addr(host); #endif /* BROKEN_INET_ADDR */ if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) { /* It was a valid numeric host address. */ sin.sin_family = AF_INET; } else { /* Look up the host address from the name servers. */ hp = gethostbyname(host); if (!hp) { error("%.100s: unknown host.", host); goto fail; } if (!hp->h_addr_list[0]) { error("%.100s: host has no IP address.", host); goto fail; } sin.sin_family = hp->h_addrtype; memcpy(&sin.sin_addr, hp->h_addr_list[0], sizeof(sin.sin_addr)); } sin.sin_port = htons(host_port); #ifdef F_SECURE_COMMERCIAL #endif /* F_SECURE_COMMERCIAL */ /* Create the socket. */ sock = socket(sin.sin_family, SOCK_STREAM, 0); if (sock < 0) { error("socket: %.100s", strerror(errno)); goto fail; } /* Connect to the host/port. */ if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { error("connect %.100s:%d: %.100s", host, host_port, strerror(errno)); close(sock); goto fail; } /* Successful connection. */ #if defined(O_NONBLOCK) && !defined(O_NONBLOCK_BROKEN) (void)fcntl(sock, F_SETFL, O_NONBLOCK); #else /* O_NONBLOCK && !O_NONBLOCK_BROKEN */ (void)fcntl(sock, F_SETFL, O_NDELAY); #endif /* O_NONBLOCK && !O_NONBLOCK_BROKEN */ /* Allocate a channel for this connection. */ newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); channels[newch].remote_id = remote_channel; /* Send a confirmation to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(remote_channel); packet_put_int(newch); packet_send(); /* Free the argument string. */ xfree(host); return; fail: /* Free the argument string. */ xfree(host); xfree(originator_string); /* Send refusal to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remote_channel); packet_send(); } /* Creates an internet domain socket for listening for X11 connections. Returns a suitable value for the DISPLAY variable, or NULL if an error occurs. */ char *x11_create_display_inet(int screen_number) { extern ServerOptions options; int display_number, port, sock; struct sockaddr_in sin; char buf[512]; #ifdef HAVE_GETHOSTNAME char hostname[257]; #else struct utsname uts; #endif /* open first vacant display, starting at an offset (default 1) so * as not to clobber the low numbers. (Modification by Jari Kokko) */ for (display_number = options.x11_display_offset; display_number < MAX_DISPLAYS; display_number++) { port = 6000 + display_number; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(port); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { error("socket: %.100s", strerror(errno)); return NULL; } #if defined(O_NONBLOCK) && !defined(O_NONBLOCK_BROKEN) (void)fcntl(sock, F_SETFL, O_NONBLOCK); #else /* O_NONBLOCK && !O_NONBLOCK_BROKEN */ (void)fcntl(sock, F_SETFL, O_NDELAY); #endif /* O_NONBLOCK && !O_NONBLOCK_BROKEN */ if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { debug("bind port %d: %.100s", port, strerror(errno)); shutdown(sock, 2); close(sock); continue; } break; } if (display_number >= MAX_DISPLAYS) { error("Failed to allocate internet-domain X11 display socket."); return NULL; } /* Start listening for connections on the socket. */ if (listen(sock, 5) < 0) { error("listen: %.100s", strerror(errno)); shutdown(sock, 2); close(sock); return NULL; } /* Set up a suitable value for the DISPLAY variable. */ #ifdef NONSTANDARD_IP_ADDRESS_X11_KLUDGE /* HPSUX has some special shared memory stuff in their X server, which appears to be enabled if the host name matches that of the local machine. However, it can be circumvented by using the IP address of the local machine instead. */ if (gethostname(hostname, sizeof(hostname)) < 0) fatal("gethostname: %.100s", strerror(errno)); { struct hostent *hp; struct in_addr addr; hp = gethostbyname(hostname); if (hp == NULL || !hp->h_addr_list[0]) { error("Could not get server IP address for %.200s.", hostname); packet_send_debug("Could not get server IP address for %.200s.", hostname); shutdown(sock, 2); close(sock); return NULL; } memcpy(&addr, hp->h_addr_list[0], sizeof(addr)); sprintf(buf, "%.100s:%d.%d", inet_ntoa(addr), display_number, screen_number); } #else /* NONSTANDARD_IP_ADDRESS_X11_KLUDGE */ #ifdef HAVE_GETHOSTNAME if (gethostname(hostname, sizeof(hostname)) < 0) fatal("gethostname: %.100s", strerror(errno)); sprintf(buf, "%.400s:%d.%d", hostname, display_number, screen_number); #else /* HAVE_GETHOSTNAME */ if (uname(&uts) < 0) fatal("uname: %s", strerror(errno)); sprintf(buf, "%.400s:%d.%d", uts.nodename, display_number, screen_number); #endif /* HAVE_GETHOSTNAME */ #endif /* NONSTANDARD_IP_ADDRESS_X11_KLUDGE */ /* Allocate a channel for the socket. */ (void)channel_allocate(SSH_CHANNEL_X11_LISTENER, sock, xstrdup("X11 inet listener")); /* Return a suitable value for the DISPLAY environment variable. */ return xstrdup(buf); } /* This is called when SSH_SMSG_X11_OPEN is received. The packet contains the remote channel number. We should do whatever we want, and respond with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */ void x11_input_open(void) { int remote_channel, display_number, sock, newch; const char *display; struct sockaddr_un ssun; struct sockaddr_in sin; char buf[255], *cp, *remote_host; struct hostent *hp; /* Get remote channel number. */ remote_channel = packet_get_int(); /* Get remote originator name. */ if (have_hostname_in_open) remote_host = packet_get_string(NULL); else remote_host = xstrdup("unknown (remote did not supply name)"); debug("Received X11 open request."); if (!x11_forwarding_permitted) { error("Warning: Server attempted X11 forwarding without client request"); error("Warning: This is a probable break-in attempt (compromised server?)"); goto fail; } /* Try to open a socket for the local X server. */ display = getenv("DISPLAY"); if (!display) { error("DISPLAY not set."); goto fail; } /* Now we decode the value of the DISPLAY variable and make a connection to the real X server. */ /* Check if it is a unix domain socket. Unix domain displays are in one of the following formats: unix:d[.s], :d[.s], ::d[.s] */ if (strncmp(display, "unix:", 5) == 0 || display[0] == ':') { /* Connect to the unix domain socket. */ if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { error("Could not parse display number from DISPLAY: %.100s", display); goto fail; } /* Create a socket. */ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { error("socket: %.100s", strerror(errno)); goto fail; } /* Connect it to the display socket. */ ssun.sun_family = AF_UNIX; #ifdef HPSUX_NONSTANDARD_X11_KLUDGE { /* HPSUX release 10.X uses /var/spool/sockets/X11/0 for the unix-domain sockets, while earlier releases stores the socket in /usr/spool/sockets/X11/0 with soft-link from /tmp/.X11-unix/`uname -n`0 */ struct stat st; if (stat("/var/spool/sockets/X11", &st) == 0) { sprintf(ssun.sun_path, "%s/%d", "/var/spool/sockets/X11", display_number); } else { if (stat("/usr/spool/sockets/X11", &st) == 0) { sprintf(ssun.sun_path, "%s/%d", "/usr/spool/sockets/X11", display_number); } else { struct utsname utsbuf; /* HPSUX stores unix-domain sockets in /tmp/.X11-unix/`hostname`0 instead of the normal /tmp/.X11-unix/X0. */ if (uname(&utsbuf) < 0) fatal("uname: %.100s", strerror(errno)); sprintf(ssun.sun_path, "%.20s/%.64s%d", X11_DIR, utsbuf.nodename, display_number); } } } #else /* HPSUX_NONSTANDARD_X11_KLUDGE */ { struct stat st; if (stat("/var/X", &st) == 0) { sprintf(ssun.sun_path, "%.80s/X%d", "/var/X/.X11-unix", display_number); } else if (stat(X11_DIR, &st) == 0) { sprintf(ssun.sun_path, "%.80s/X%d", X11_DIR, display_number); } else { sprintf(ssun.sun_path, "%.80s/X%d", "/tmp/.X11-unix", display_number); } } #endif /* HPSUX_NONSTANDARD_X11_KLUDGE */ if (connect(sock, (struct sockaddr *)&ssun, AF_UNIX_SIZE(ssun)) < 0) { error("connect %.100s: %.100s", ssun.sun_path, strerror(errno)); close(sock); goto fail; } /* OK, we now have a connection to the display. */ goto success; } /* Connect to an inet socket. The DISPLAY value is supposedly hostname:d[.s], where hostname may also be numeric IP address. */ strncpy(buf, display, sizeof(buf)); buf[sizeof(buf) - 1] = 0; cp = strchr(buf, ':'); if (!cp) { error("Could not find ':' in DISPLAY: %.100s", display); goto fail; } *cp = 0; /* buf now contains the host name. But first we parse the display number. */ if (sscanf(cp + 1, "%d", &display_number) != 1) { error("Could not parse display number from DISPLAY: %.100s", display); goto fail; } /* Try to parse the host name as a numeric IP address. */ memset(&sin, 0, sizeof(sin)); #ifdef BROKEN_INET_ADDR sin.sin_addr.s_addr = inet_network(buf); #else /* BROKEN_INET_ADDR */ sin.sin_addr.s_addr = inet_addr(buf); #endif /* BROKEN_INET_ADDR */ if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) { /* It was a valid numeric host address. */ sin.sin_family = AF_INET; } else { /* Not a numeric IP address. */ /* Look up the host address from the name servers. */ hp = gethostbyname(buf); if (!hp) { error("%.100s: unknown host.", buf); goto fail; } if (!hp->h_addr_list[0]) { error("%.100s: host has no IP address.", buf); goto fail; } sin.sin_family = hp->h_addrtype; memcpy(&sin.sin_addr, hp->h_addr_list[0], sizeof(sin.sin_addr)); } /* Set port number. */ sin.sin_port = htons(6000 + display_number); /* Create a socket. */ sock = socket(sin.sin_family, SOCK_STREAM, 0); if (sock < 0) { error("socket: %.100s", strerror(errno)); goto fail; } /* Connect it to the display. */ if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { error("connect %.100s:%d: %.100s", buf, 6000 + display_number, strerror(errno)); close(sock); goto fail; } success: /* We have successfully obtained a connection to the real X display. */ #if defined(O_NONBLOCK) && !defined(O_NONBLOCK_BROKEN) (void)fcntl(sock, F_SETFL, O_NONBLOCK); #else /* O_NONBLOCK && !O_NONBLOCK_BROKEN */ (void)fcntl(sock, F_SETFL, O_NDELAY); #endif /* O_NONBLOCK && !O_NONBLOCK_BROKEN */ /* Allocate a channel for this connection. */ if (x11_saved_proto == NULL) newch = channel_allocate(SSH_CHANNEL_OPEN, sock, remote_host); else newch = channel_allocate(SSH_CHANNEL_X11_OPEN, sock, remote_host); channels[newch].is_x_connection = 1; channels[newch].remote_id = remote_channel; debug("Sending open confirmation to the remote host."); /* Send a confirmation to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(remote_channel); packet_put_int(newch); packet_send(); return; fail: debug ("Failed..."); /* Send refusal to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remote_channel); packet_send(); } /* Requests forwarding of X11 connections, generates fake authentication data, and enables authentication spoofing. */ void x11_request_forwarding_with_spoofing(RandomState *state, const char *proto, const char *data) { unsigned int data_len = (unsigned int)strlen(data) / 2; unsigned int i, value; char *new_data; int screen_number; const char *cp; cp = getenv("DISPLAY"); if (cp) cp = strchr(cp, ':'); if (cp) cp = strchr(cp, '.'); if (cp) screen_number = atoi(cp + 1); else screen_number = 0; /* Save protocol name. */ x11_saved_proto = xstrdup(proto); /* Extract real authentication data and generate fake data of the same length. */ x11_saved_data = xmalloc(data_len); x11_fake_data = xmalloc(data_len); for (i = 0; i < data_len; i++) { if (sscanf(data + 2 * i, "%2x", &value) != 1) fatal("x11_request_forwarding: bad authentication data: %.100s", data); x11_saved_data[i] = value; x11_fake_data[i] = random_get_byte(state); } x11_saved_data_len = data_len; x11_fake_data_len = data_len; /* Convert the fake data into hex. */ new_data = xmalloc(2 * data_len + 1); for (i = 0; i < data_len; i++) sprintf(new_data + 2 * i, "%02x", (unsigned char)x11_fake_data[i]); /* Send the request packet. */ packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); packet_put_string(proto, strlen(proto)); packet_put_string(new_data, strlen(new_data)); packet_put_int(screen_number); packet_send(); packet_write_wait(); xfree(new_data); x11_forwarding_permitted = 1; } /* Sends a message to the server to request authentication fd forwarding. */ void auth_request_forwarding(void) { packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); packet_send(); packet_write_wait(); auth_forwarding_permitted = 1; } /* Returns the number of the file descriptor to pass to child programs as the authentication fd. Returns -1 if there is no forwarded authentication fd. */ int auth_get_fd(void) { return channel_forwarded_auth_fd; } /* 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 channel_forwarded_auth_socket_name; } /* Called on exit, tries to remove authentication socket and per-user socket directory */ void auth_delete_socket(void *context) { if (channel_forwarded_auth_socket_name) { remove(channel_forwarded_auth_socket_name); xfree(channel_forwarded_auth_socket_name); channel_forwarded_auth_socket_name = NULL; } if (channel_forwarded_auth_socket_dir_name) { chdir("/"); rmdir(channel_forwarded_auth_socket_dir_name); xfree(channel_forwarded_auth_socket_dir_name); channel_forwarded_auth_socket_dir_name = NULL; } } /* This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. This starts forwarding authentication requests. Socket directory will be owned by the user, and will have 700- permissions. Actual socket will have 222-permissions and can be owned by anyone (sshd's socket will be owned by root and ssh-agent's by user). This returns true if everything succeeds, otherwise it will return false (agent forwarding disabled). */ int auth_input_request_forwarding(struct passwd *pw) { int ret; int sock, newch, directory_created; struct sockaddr_un sunaddr; struct stat st, st2, parent_st; mode_t old_umask; char *last_dir; if (auth_get_socket_name() != NULL) fatal("Protocol error: authentication forwarding requested twice."); /* Allocate a buffer for the socket name, and format the name. And directory name. */ channel_forwarded_auth_socket_name = xmalloc(strlen(SSH_AGENT_SOCKET_DIR) + strlen(SSH_AGENT_SOCKET) + strlen(pw->pw_name) + 10); channel_forwarded_auth_socket_dir_name = xmalloc(strlen(SSH_AGENT_SOCKET_DIR) + strlen(pw->pw_name) + 10); sprintf(channel_forwarded_auth_socket_dir_name, SSH_AGENT_SOCKET_DIR, pw->pw_name); /* Use the plain socket name for now, change to absolute path later */ sprintf(channel_forwarded_auth_socket_name, SSH_AGENT_SOCKET, (int)getpid()); /* Register the cleanup function before making the directory */ fatal_add_cleanup(&auth_delete_socket, NULL); /* Stat parent dir */ last_dir = strrchr(channel_forwarded_auth_socket_dir_name, '/'); if (last_dir == NULL || last_dir == channel_forwarded_auth_socket_dir_name) { packet_send_debug("* Remote error: Invalid SSH_AGENT_SOCKET_DIR \'%.100s\', it should contain at least one /.", channel_forwarded_auth_socket_dir_name); packet_send_debug("* Remote error: Authentication fowarding disabled."); return 0; } *last_dir = '\0'; ret = stat(channel_forwarded_auth_socket_dir_name, &parent_st); if (ret < 0) { packet_send_debug("* Remote error: Agent parent directory \'%.100s\' stat failed: %.100s", channel_forwarded_auth_socket_dir_name, strerror(errno)); packet_send_debug("* Remote error: Authentication fowarding disabled."); return 0; } *last_dir = '/'; /* Check the per-user socket directory. Stat it, if it doesn't exist, mkdir it and stat it. Then chdir to it and stat "." and compare it with the earlier stat (dev and inode) so that we can be sure we ended where we wanted. Then stat ".." and check that it is sticky. Only after this we can think about chowning the ".". */ ret = lstat(channel_forwarded_auth_socket_dir_name, &st); directory_created = 0; if (ret < 0 && errno != ENOENT) { packet_send_debug("* Remote error: stat of agent directory \'%.100s\' failed: %.100s", channel_forwarded_auth_socket_dir_name, strerror(errno)); packet_send_debug("* Remote error: Authentication fowarding disabled."); return 0; } if (ret < 0 && errno == ENOENT) { if (mkdir(channel_forwarded_auth_socket_dir_name, S_IRWXU) == 0) { directory_created = 1; ret = lstat(channel_forwarded_auth_socket_dir_name, &st); } else { packet_send_debug("* Remote error: Agent directory \'%.100s\' mkdir failed: %.100s", channel_forwarded_auth_socket_dir_name, strerror(errno)); packet_send_debug("* Remote error: Authentication fowarding disabled."); return 0; } } else { /* Simple owner & mode check. If directory has just been created, don't care about the owner yet. */ if ((st.st_uid != pw->pw_uid) || (st.st_mode & 077) != 0) { packet_send_debug("* Remote error: Agent socket creation:" "Bad modes/owner for directory \'%s\' (modes are %o, should be 040700)", channel_forwarded_auth_socket_dir_name, st.st_mode); packet_send_debug("* Remote error: Authentication fowarding disabled."); return 0; } } chdir(channel_forwarded_auth_socket_dir_name); /* Check that we really are where we wanted to go */ if (stat(".", &st2) != 0) { packet_send_debug("* Remote error: stat \'.\' failed after chdir to \'%.100s\': %.100s", channel_forwarded_auth_socket_dir_name, strerror(errno)); packet_send_debug("* Remote error: Authentication fowarding disabled."); return 0; } if (st.st_dev != st2.st_dev || st.st_ino != st2.st_ino) { packet_send_debug("* Remote error: Agent socket creation: wrong directory after chdir"); packet_send_debug("* Remote error: Authentication fowarding disabled."); return 0; } /* Check that parent is sticky, and it really is what it is supposed to be */ if (stat("..", &st) != 0) { packet_send_debug("* Remote error: Agent socket directory stat \'..\' failed: %.100s", strerror(errno)); packet_send_debug("* Remote error: Authentication fowarding disabled."); return 0; } if ((st.st_mode & 01000) == 0) { packet_send_debug("* Remote error: Agent socket creation: Directory \'%s/..\' is not sticky, mode is %o, should be 041777", channel_forwarded_auth_socket_dir_name, st.st_mode); packet_send_debug("* Remote error: Authentication forwarding disabled."); return 0; } if (st.st_dev != parent_st.st_dev || st.st_ino != parent_st.st_ino) { packet_send_debug("* Remote error: Agent socket creation: wrong parent directory after chdir (last component of socket name is symlink?)"); packet_send_debug("* Remote error: Authentication fowarding disabled."); return 0; } /* Create the socket. */ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) packet_disconnect("Agent socket creation failed: %.100s", strerror(errno)); /* Bind it to the name. */ memset(&sunaddr, 0, AF_UNIX_SIZE(sunaddr)); sunaddr.sun_family = AF_UNIX; strncpy(sunaddr.sun_path, channel_forwarded_auth_socket_name, sizeof(sunaddr.sun_path)); /* Use umask to get desired permissions, chmod is too dangerous NOTE: If your system doesn't handle umask correctly when creating unix-domain sockets, you might not be able to use ssh-agent connections on your system */ old_umask = umask(S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); if (bind(sock, (struct sockaddr *)&sunaddr, AF_UNIX_SIZE(sunaddr)) < 0) packet_disconnect("Agent socket bind failed: %.100s", strerror(errno)); umask(old_umask); if (directory_created) if (chown(".", pw->pw_uid, pw->pw_gid) < 0) packet_disconnect("Agent socket directory chown failed: %.100s", strerror(errno)); /* Start listening on the socket. */ if (listen(sock, 5) < 0) packet_disconnect("Agent socket listen failed: %.100s", strerror(errno)); /* Change the relative socket name to absolute */ sprintf(channel_forwarded_auth_socket_name, SSH_AGENT_SOCKET_DIR"/"SSH_AGENT_SOCKET, pw->pw_name, (int)getpid()); /* Allocate a channel for the authentication agent socket. */ newch = channel_allocate(SSH_CHANNEL_AUTH_LISTENER, sock, xstrdup("auth socket")); strcpy(channels[newch].path, channel_forwarded_auth_socket_name); return 1; } /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ void auth_input_open_request(void) { int remote_channel, sock, newch; char *dummyname; /* Read the port number from the message. */ remote_channel = packet_get_int(); if (!auth_forwarding_permitted) { error("Warning: Server attempted agent forwarding without client request"); error("Warning: This is a probable break-in attempt (compromised server?)"); packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remote_channel); packet_send(); return; } /* Get a connection to the local authentication agent (this may again get forwarded). */ sock = ssh_get_authentication_connection_fd(); /* If we could not connect the agent, inform the server side that opening failed. This should never happen unless the agent dies, because authentication forwarding is only enabled if we have an agent. */ if (sock < 0) { packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remote_channel); packet_send(); return; } debug("Forwarding authentication connection."); dummyname = xstrdup("authentication agent connection"); /* Allocate a channel for this connection. */ newch = channel_allocate(SSH_CHANNEL_OPEN, sock, dummyname); channels[newch].remote_id = remote_channel; /* Send a confirmation to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(remote_channel); packet_put_int(newch); packet_send(); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.