diff options
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | README.openssh2 | 36 | ||||
-rw-r--r-- | auth.c | 188 | ||||
-rw-r--r-- | auth.h | 4 | ||||
-rw-r--r-- | channels.c | 7 | ||||
-rw-r--r-- | compat.c | 6 | ||||
-rw-r--r-- | servconf.c | 12 | ||||
-rw-r--r-- | servconf.h | 3 | ||||
-rw-r--r-- | serverloop.c | 192 | ||||
-rw-r--r-- | session.c | 328 | ||||
-rw-r--r-- | session.h | 7 | ||||
-rw-r--r-- | ssh.h | 6 | ||||
-rw-r--r-- | sshd.c | 240 |
13 files changed, 1002 insertions, 37 deletions
@@ -2,7 +2,15 @@ - Avoid some compiler warnings in fake-get*.c - Add IPTOS macros for systems which lack them - Only set define entropy collection macros if they are found - + - More large OpenBSD CVS updates: + - [auth.c auth.h servconf.c servconf.h serverloop.c session.c] + [session.h ssh.h sshd.c README.openssh2] + ssh2 server side, see README.openssh2; enable with 'sshd -2' + - [channels.c] + no adjust after close + - [sshd.c compat.c ] + interop w/ latest ssh.com windows client. + 20000406 - OpenBSD CVS update: - [channels.c] diff --git a/README.openssh2 b/README.openssh2 new file mode 100644 index 00000000..59f8cf9f --- /dev/null +++ b/README.openssh2 @@ -0,0 +1,36 @@ +$Id: README.openssh2,v 1.2 2000/04/06 21:28:22 markus Exp $ + +works: + secsh-transport: works w/o rekey + proposal exchange, i.e. different enc/mac/comp per direction + encryption: blowfish-cbc, 3des-cbc, arcfour, cast128-cbc + mac: hmac-md5, hmac-sha1, (hmac-ripemd160) + compression: zlib, none + secsh-userauth: passwd only + secsh-connection: pty+shell or command, flow control works (window adjust) + tcp-forwarding: -L works + dss: verification works, + key database in ~/.ssh/known_hosts with bits == 0 hack + dss: signature works, keygen w/ openssl: + $ umask 077 + $ openssl dsaparam 1024 -out dsa1024.pem + $ openssl gendsa -out /etc/ssh_dsa_key dsa1024.pem -rand /dev/arandom + start sshd with '-2' flag + client interops w/ sshd2, lshd + server interops w/ ssh2, lsh, ssh.com's Windows client, SecureCRT + server supports multiple concurrent sessions (e.g. with SSH.com Windows client) +todo: + re-keying + secsh-connection features: + tcp-forwarding, agent-fwd, x11-fwd + auth other than passwd: + pubkey, keyboard-interactive + config + server-auth w/ old host-keys + cleanup + advanced key storage? + keynote + sftp + +-markus +$Date: 2000/04/06 21:28:22 $ @@ -1,10 +1,11 @@ /* * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved + * Copyright (c) 2000 Markus Friedl. All rights reserved. */ #include "includes.h" -RCSID("$OpenBSD: auth.c,v 1.1 2000/03/28 21:15:45 markus Exp $"); +RCSID("$OpenBSD: auth.c,v 1.2 2000/04/06 08:55:22 markus Exp $"); #include "xmalloc.h" #include "rsa.h" @@ -15,12 +16,17 @@ RCSID("$OpenBSD: auth.c,v 1.1 2000/03/28 21:15:45 markus Exp $"); #include "cipher.h" #include "mpaux.h" #include "servconf.h" +#include "compat.h" #include "channels.h" #include "match.h" +#include "bufaux.h" +#include "ssh2.h" +#include "auth.h" #include "session.h" #include "dispatch.h" + /* import */ extern ServerOptions options; extern char *forced_command; @@ -604,3 +610,183 @@ do_authentication() /* Perform session preparation. */ do_authenticated(pw); } + + +void input_service_request(int type, int plen); +void input_userauth_request(int type, int plen); +void ssh2_pty_cleanup(void); + +typedef struct Authctxt Authctxt; +struct Authctxt { + char *user; + char *service; + struct passwd pw; + int valid; +}; +static Authctxt *authctxt = NULL; +static int userauth_success = 0; + +struct passwd* +auth_get_user(void) +{ + return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL; +} +struct passwd* +auth_set_user(char *u, char *s) +{ + struct passwd *pw, *copy; + + if (authctxt == NULL) { + authctxt = xmalloc(sizeof(*authctxt)); + authctxt->valid = 0; + authctxt->user = xstrdup(u); + authctxt->service = xstrdup(s); + setproctitle("%s", u); + pw = getpwnam(u); + if (!pw || !allowed_user(pw)) { + log("auth_set_user: bad user %s", u); + return NULL; + } +#ifdef USE_PAM + start_pam(pw); +#endif + copy = &authctxt->pw; + memset(copy, 0, sizeof(*copy)); + copy->pw_name = xstrdup(pw->pw_name); + copy->pw_passwd = xstrdup(pw->pw_passwd); + copy->pw_uid = pw->pw_uid; + copy->pw_gid = pw->pw_gid; + copy->pw_dir = xstrdup(pw->pw_dir); + copy->pw_shell = xstrdup(pw->pw_shell); + authctxt->valid = 1; + } else { + if (strcmp(u, authctxt->user) != 0 || + strcmp(s, authctxt->service) != 0) { + log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)", + u, s, authctxt->user, authctxt->service); + return NULL; + } + } + return auth_get_user(); +} + +static void +protocol_error(int type, int plen) +{ + log("auth: protocol error: type %d plen %d", type, plen); + packet_start(SSH2_MSG_UNIMPLEMENTED); + packet_put_int(0); + packet_send(); + packet_write_wait(); +} +void +input_service_request(int type, int plen) +{ + unsigned int len; + int accept = 0; + char *service = packet_get_string(&len); + + if (strcmp(service, "ssh-userauth") == 0) { + if (!userauth_success) { + accept = 1; + /* now we can handle user-auth requests */ + dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); + } + } + /* XXX all other service requests are denied */ + + if (accept) { + packet_start(SSH2_MSG_SERVICE_ACCEPT); + packet_put_cstring(service); + packet_send(); + packet_write_wait(); + } else { + debug("bad service request %s", service); + packet_disconnect("bad service request %s", service); + } + xfree(service); +} +void +input_userauth_request(int type, int plen) +{ + static int try = 0; + unsigned int len; + int c, authenticated = 0; + char *user, *service, *method; + struct passwd *pw; + + if (++try == AUTH_FAIL_MAX) + packet_disconnect("too many failed userauth_requests"); + + user = packet_get_string(&len); + service = packet_get_string(&len); + method = packet_get_string(&len); + debug("userauth-request for user %s service %s method %s", user, service, method); + + /* XXX we only allow the ssh-connection service */ + pw = auth_set_user(user, service); + if (pw && strcmp(service, "ssh-connection")==0) { + if (strcmp(method, "none") == 0 && try == 1) { +#ifdef USE_PAM + /* Do PAM auth with password */ + authenticated = auth_pam_password(pw, ""); +#else /* USE_PAM */ + /* Try authentication with the password. */ + authenticated = auth_password(pw, ""); +#endif /* USE_PAM */ + } else if (strcmp(method, "password") == 0) { + char *password; + c = packet_get_char(); + if (c) + debug("password change not supported"); + password = packet_get_string(&len); +#ifdef USE_PAM + /* Do PAM auth with password */ + authenticated = auth_pam_password(pw, password); +#else /* USE_PAM */ + /* Try authentication with the password. */ + authenticated = auth_password(pw, password); +#endif /* USE_PAM */ + memset(password, 0, len); + xfree(password); + } else if (strcmp(method, "publickey") == 0) { + /* XXX TODO */ + char *pkalg; + char *pkblob; + c = packet_get_char(); + pkalg = packet_get_string(&len); + pkblob = packet_get_string(&len); + xfree(pkalg); + xfree(pkblob); + } + } + /* XXX check if other auth methods are needed */ + if (authenticated) { + /* turn off userauth */ + dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error); + /* success! */ + packet_start(SSH2_MSG_USERAUTH_SUCCESS); + packet_send(); + packet_write_wait(); + log("userauth success for %s", user); + /* now we can break out */ + userauth_success = 1; + } else { + packet_start(SSH2_MSG_USERAUTH_FAILURE); + packet_put_cstring("password"); + packet_put_char(0); /* partial success */ + packet_send(); + packet_write_wait(); + } + xfree(service); + xfree(user); + xfree(method); +} +void +do_authentication2() +{ + dispatch_init(&protocol_error); + dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); + dispatch_run(DISPATCH_BLOCK, &userauth_success); + do_authenticated2(); +} @@ -2,5 +2,9 @@ #define AUTH_H void do_authentication(void); +void do_authentication2(void); + +struct passwd * +auth_get_user(void); #endif @@ -17,7 +17,7 @@ */ #include "includes.h" -RCSID("$Id: channels.c,v 1.22 2000/04/06 02:32:38 damien Exp $"); +RCSID("$Id: channels.c,v 1.23 2000/04/12 08:45:06 damien Exp $"); #include "ssh.h" #include "packet.h" @@ -674,7 +674,7 @@ channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) int channel_check_window(Channel *c, fd_set * readset, fd_set * writeset) { - if (!(c->flags & CHAN_CLOSE_SENT) && + if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && c->local_window < c->local_window_max/2 && c->local_consumed > 0) { packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); @@ -837,7 +837,8 @@ channel_output_poll() c->istate != CHAN_INPUT_WAIT_DRAIN) continue; } - if (compat20 && (c->flags & CHAN_CLOSE_SENT)) { + if (compat20 && + (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { debug("channel: %d: no data after CLOSE", c->self); continue; } @@ -28,7 +28,7 @@ */ #include "includes.h" -RCSID("$Id: compat.c,v 1.5 2000/04/06 02:32:39 damien Exp $"); +RCSID("$Id: compat.c,v 1.6 2000/04/12 08:45:06 damien Exp $"); #include "ssh.h" #include "packet.h" @@ -58,9 +58,7 @@ compat_datafellows(const char *version) size_t len; static const char *check[] = { "2.0.1", - "2.1.0.beta.9", - "2.1.0.pre.3", - "2.1.0.public.beta.1", + "2.1.0", NULL }; for (i = 0; check[i]; i++) { @@ -12,7 +12,7 @@ */ #include "includes.h" -RCSID("$Id: servconf.c,v 1.9 2000/03/09 10:27:51 damien Exp $"); +RCSID("$Id: servconf.c,v 1.10 2000/04/12 08:45:06 damien Exp $"); #include "ssh.h" #include "servconf.h" @@ -31,6 +31,7 @@ initialize_server_options(ServerOptions *options) options->ports_from_cmdline = 0; options->listen_addrs = NULL; options->host_key_file = NULL; + options->dsa_key_file = NULL; options->server_key_bits = -1; options->login_grace_time = -1; options->key_regeneration_time = -1; @@ -78,6 +79,8 @@ fill_default_server_options(ServerOptions *options) add_listen_addr(options, NULL); if (options->host_key_file == NULL) options->host_key_file = HOST_KEY_FILE; + if (options->dsa_key_file == NULL) + options->dsa_key_file = DSA_KEY_FILE; if (options->server_key_bits == -1) options->server_key_bits = 768; if (options->login_grace_time == -1) @@ -159,7 +162,7 @@ typedef enum { sPrintMotd, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset, sStrictModes, sEmptyPasswd, sRandomSeedFile, sKeepAlives, sCheckMail, sUseLogin, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, - sIgnoreUserKnownHosts + sIgnoreUserKnownHosts, sDSAKeyFile } ServerOpCodes; /* Textual representation of the tokens. */ @@ -169,6 +172,7 @@ static struct { } keywords[] = { { "port", sPort }, { "hostkey", sHostKeyFile }, + { "dsakey", sDSAKeyFile }, { "serverkeybits", sServerKeyBits }, { "logingracetime", sLoginGraceTime }, { "keyregenerationinterval", sKeyRegenerationTime }, @@ -338,7 +342,9 @@ parse_int: break; case sHostKeyFile: - charptr = &options->host_key_file; + case sDSAKeyFile: + charptr = (opcode == sHostKeyFile ) ? + &options->host_key_file : &options->dsa_key_file; cp = strtok(NULL, WHITESPACE); if (!cp) { fprintf(stderr, "%s line %d: missing file name.\n", @@ -13,7 +13,7 @@ * */ -/* RCSID("$Id: servconf.h,v 1.6 2000/01/14 04:45:51 damien Exp $"); */ +/* RCSID("$Id: servconf.h,v 1.7 2000/04/12 08:45:07 damien Exp $"); */ #ifndef SERVCONF_H #define SERVCONF_H @@ -32,6 +32,7 @@ typedef struct { char *listen_addr; /* Address on which the server listens. */ struct addrinfo *listen_addrs; /* Addresses on which the server listens. */ char *host_key_file; /* File containing host key. */ + char *dsa_key_file; /* File containing dsa host key. */ int server_key_bits;/* Size of the server key. */ int login_grace_time; /* Disconnect if no auth in this time * (sec). */ diff --git a/serverloop.c b/serverloop.c index 8bf448ce..0ea57faa 100644 --- a/serverloop.c +++ b/serverloop.c @@ -5,6 +5,10 @@ * Created: Sun Sep 10 00:30:37 1995 ylo * Server main loop for handling the interactive session. */ +/* + * SSH2 support by Markus Friedl. + * Copyright (c) 2000 Markus Friedl. All rights reserved. + */ #include "includes.h" #include "xmalloc.h" @@ -16,6 +20,8 @@ #include "channels.h" #include "compat.h" +#include "ssh2.h" +#include "session.h" #include "dispatch.h" static Buffer stdin_buffer; /* Buffer for stdin data. */ @@ -72,6 +78,15 @@ sigchld_handler(int sig) signal(SIGCHLD, sigchld_handler); errno = save_errno; } +void +sigchld_handler2(int sig) +{ + int save_errno = errno; + debug("Received SIGCHLD."); + child_terminated = 1; + signal(SIGCHLD, sigchld_handler2); + errno = save_errno; +} /* * Make packets from buffered stderr data, and buffer it for sending @@ -154,15 +169,21 @@ retry_select: * Read packets from the client unless we have too much buffered * stdin or channel data. */ - if (buffer_len(&stdin_buffer) < 4096 && - channel_not_very_much_buffered_data()) - FD_SET(connection_in, readset); + if (compat20) { + // wrong: bad conditionXXX + if (channel_not_very_much_buffered_data()) + FD_SET(connection_in, readset); + } else { + if (buffer_len(&stdin_buffer) < 4096 && + channel_not_very_much_buffered_data()) + FD_SET(connection_in, readset); + } /* * If there is not too much data already buffered going to the * client, try to get some more data from the program. */ - if (packet_not_very_much_data_to_write()) { + if (!compat20 && packet_not_very_much_data_to_write()) { if (!fdout_eof) FD_SET(fdout, readset); if (!fderr_eof) @@ -182,7 +203,7 @@ retry_select: /* If we have buffered data, try to write some of that data to the program. */ - if (fdin != -1 && buffer_len(&stdin_buffer) > 0) + if (!compat20 && fdin != -1 && buffer_len(&stdin_buffer) > 0) FD_SET(fdin, writeset); /* Update the maximum descriptor number if appropriate. */ @@ -204,6 +225,8 @@ retry_select: tv.tv_usec = 1000 * (max_time_milliseconds % 1000); tvp = &tv; } + if (tvp!=NULL) + debug("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds); /* Wait for something to happen, or the timeout to expire. */ ret = select(max_fd + 1, readset, writeset, NULL, tvp); @@ -250,6 +273,9 @@ process_input(fd_set * readset) /* Buffer any received data. */ packet_process_incoming(buf, len); } + if (compat20) + return; + /* Read and buffer any available stdout data from the program. */ if (!fdout_eof && FD_ISSET(fdout, readset)) { len = read(fdout, buf, sizeof(buf)); @@ -279,7 +305,7 @@ process_output(fd_set * writeset) int len; /* Write buffered data to program stdin. */ - if (fdin != -1 && FD_ISSET(fdin, writeset)) { + if (!compat20 && fdin != -1 && FD_ISSET(fdin, writeset)) { len = write(fdin, buffer_ptr(&stdin_buffer), buffer_len(&stdin_buffer)); if (len <= 0) { @@ -578,6 +604,51 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) /* NOTREACHED */ } +void +server_loop2(void) +{ + fd_set readset, writeset; + int had_channel = 0; + int status; + pid_t pid; + + debug("Entering interactive session for SSH2."); + + signal(SIGCHLD, sigchld_handler2); + child_terminated = 0; + connection_in = packet_get_connection_in(); + connection_out = packet_get_connection_out(); + max_fd = connection_in; + if (connection_out > max_fd) + max_fd = connection_out; + server_init_dispatch(); + + for (;;) { + process_buffered_input_packets(); + if (!had_channel && channel_still_open()) + had_channel = 1; + if (had_channel && !channel_still_open()) { + debug("!channel_still_open."); + break; + } + if (packet_not_very_much_data_to_write()) + channel_output_poll(); + wait_until_can_do_something(&readset, &writeset, 0); + if (child_terminated) { + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + session_close_by_pid(pid, status); + child_terminated = 0; + } + channel_after_select(&readset, &writeset); + process_input(&readset); + process_output(&writeset); + } + signal(SIGCHLD, SIG_DFL); + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + session_close_by_pid(pid, status); + channel_stop_listening(); +} + void server_input_stdin_data(int type, int plen) { @@ -622,6 +693,111 @@ server_input_window_size(int type, int plen) pty_change_window_size(fdin, row, col, xpixel, ypixel); } +int +input_direct_tcpip(void) +{ + int sock; + char *host, *originator; + int host_port, originator_port; + + host = packet_get_string(NULL); + host_port = packet_get_int(); + originator = packet_get_string(NULL); + originator_port = packet_get_int(); + /* XXX check permission */ + sock = channel_connect_to(host, host_port); + xfree(host); + xfree(originator); + if (sock < 0) + return -1; + return channel_new("direct-tcpip", SSH_CHANNEL_OPEN, + sock, sock, -1, 4*1024, 32*1024, 0, xstrdup("direct-tcpip")); +} + +void +server_input_channel_open(int type, int plen) +{ + Channel *c = NULL; + char *ctype; + int id; + unsigned int len; + int rchan; + int rmaxpack; + int rwindow; + + ctype = packet_get_string(&len); + rchan = packet_get_int(); + rwindow = packet_get_int(); + rmaxpack = packet_get_int(); + + log("channel_input_open: ctype %s rchan %d win %d max %d", + ctype, rchan, rwindow, rmaxpack); + + if (strcmp(ctype, "session") == 0) { + debug("open session"); + /* + * A server session has no fd to read or write + * until a CHANNEL_REQUEST for a shell is made, + * so we set the type to SSH_CHANNEL_LARVAL. + * Additionally, a callback for handling all + * CHANNEL_REQUEST messages is registered. + */ + id = channel_new(ctype, SSH_CHANNEL_LARVAL, + -1, -1, -1, 0, 32*1024, 0, xstrdup("server-session")); + if (session_open(id) == 1) { + channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST, + session_input_channel_req, (void *)0); + channel_register_cleanup(id, session_close_by_channel); + c = channel_lookup(id); + } else { + debug("session open failed, free channel %d", id); + channel_free(id); + } + } else if (strcmp(ctype, "direct-tcpip") == 0) { + debug("open direct-tcpip"); + id = input_direct_tcpip(); + if (id >= 0) + c = channel_lookup(id); + } + if (c != NULL) { + debug("confirm %s", ctype); + c->remote_id = rchan; + c->remote_window = rwindow; + c->remote_maxpacket = rmaxpack; + + packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(c->remote_id); + packet_put_int(c->self); + packet_put_int(c->local_window); + packet_put_int(c->local_maxpacket); + packet_send(); + } else { + debug("failure %s", ctype); + packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(rchan); + packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); + packet_put_cstring("bla bla"); + packet_put_cstring(""); + packet_send(); + } + xfree(ctype); +} + +void +server_init_dispatch_20() +{ + debug("server_init_dispatch_20"); + dispatch_init(&dispatch_protocol_error); + dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); + dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); + dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); + dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request); + dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); +} void server_init_dispatch_13() { @@ -648,7 +824,9 @@ server_init_dispatch_15() void server_init_dispatch() { - if (compat13) + if (compat20) + server_init_dispatch_20(); + else if (compat13) server_init_dispatch_13(); else server_init_dispatch_15(); @@ -2,9 +2,13 @@ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved */ +/* + * SSH2 support by Markus Friedl. + * Copyright (c) 2000 Markus Friedl. All rights reserved. + */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.1 2000/03/28 21:15:45 markus Exp $"); +RCSID("$OpenBSD: session.c,v 1.2 2000/04/06 08:55:22 markus Exp $"); #include "xmalloc.h" #include "ssh.h" @@ -19,6 +23,10 @@ RCSID("$OpenBSD: session.c,v 1.1 2000/03/28 21:15:45 markus Exp $"); #include "channels.h" #include "nchan.h" +#include "bufaux.h" +#include "ssh2.h" +#include "auth.h" + /* types */ #define TTYSZ 64 @@ -448,9 +456,13 @@ do_exec_no_pty(Session *s, const char *command, struct passwd * pw) close(pout[1]); close(perr[1]); - /* Enter the interactive session. */ - server_loop(pid, pin[1], pout[0], perr[0]); - /* server_loop has closed pin[1], pout[1], and perr[1]. */ + if (compat20) { + session_set_fds(s, pin[1], pout[0], perr[0]); + } else { + /* Enter the interactive session. */ + server_loop(pid, pin[1], pout[0], perr[0]); + /* server_loop has closed pin[1], pout[1], and perr[1]. */ + } #else /* USE_PIPES */ /* We are the parent. Close the child sides of the socket pairs. */ close(inout[0]); @@ -460,8 +472,12 @@ do_exec_no_pty(Session *s, const char *command, struct passwd * pw) * Enter the interactive session. Note: server_loop must be able to * handle the case that fdin and fdout are the same. */ - server_loop(pid, inout[1], inout[1], err[1]); - /* server_loop has closed inout[1] and err[1]. */ + if (compat20) { + session_set_fds(s, inout[1], inout[1], err[1]); + } else { + server_loop(pid, inout[1], inout[1], err[1]); + /* server_loop has closed inout[1] and err[1]. */ + } #endif /* USE_PIPES */ } @@ -631,9 +647,13 @@ do_exec_pty(Session *s, const char *command, struct passwd * pw) s->ptymaster = ptymaster; /* Enter interactive session. */ - server_loop(pid, ptyfd, fdout, -1); - /* server_loop _has_ closed ptyfd and fdout. */ - session_pty_cleanup(s); + if (compat20) { + session_set_fds(s, ptyfd, fdout, -1); + } else { + server_loop(pid, ptyfd, fdout, -1); + /* server_loop _has_ closed ptyfd and fdout. */ + session_pty_cleanup(s); + } } /* @@ -1126,6 +1146,181 @@ session_dump(void) } } +int +session_open(int chanid) +{ + Session *s = session_new(); + debug("session_open: channel %d", chanid); + if (s == NULL) { + error("no more sessions"); + return 0; + } + debug("session_open: session %d: link with channel %d", s->self, chanid); + s->chanid = chanid; + s->pw = auth_get_user(); + if (s->pw == NULL) + fatal("no user for session %i channel %d", + s->self, s->chanid); + return 1; +} + +Session * +session_by_channel(int id) +{ + int i; + for(i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + if (s->used && s->chanid == id) { + debug("session_by_channel: session %d channel %d", i, id); + return s; + } + } + debug("session_by_channel: unknown channel %d", id); + session_dump(); + return NULL; +} + +Session * +session_by_pid(pid_t pid) +{ + int i; + debug("session_by_pid: pid %d", pid); + for(i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + if (s->used && s->pid == pid) + return s; + } + error("session_by_pid: unknown pid %d", pid); + session_dump(); + return NULL; +} + +int +session_window_change_req(Session *s) +{ + s->col = packet_get_int(); + s->row = packet_get_int(); + s->xpixel = packet_get_int(); + s->ypixel = packet_get_int(); + pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); + return 1; +} + +int +session_pty_req(Session *s) +{ + unsigned int len; + + if (s->ttyfd != -1) + return -1; + s->term = packet_get_string(&len); + s->col = packet_get_int(); + s->row = packet_get_int(); + s->xpixel = packet_get_int(); + s->ypixel = packet_get_int(); + + if (strcmp(s->term, "") == 0) { + xfree(s->term); + s->term = NULL; + } + /* Allocate a pty and open it. */ + if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) { + xfree(s->term); + s->term = NULL; + s->ptyfd = -1; + s->ttyfd = -1; + error("session_pty_req: session %d alloc failed", s->self); + return -1; + } + debug("session_pty_req: session %d alloc %s", s->self, s->tty); + /* + * Add a cleanup function to clear the utmp entry and record logout + * time in case we call fatal() (e.g., the connection gets closed). + */ + fatal_add_cleanup(pty_cleanup_proc, (void *)s); + pty_setowner(s->pw, s->tty); + /* Get window size from the packet. */ + pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); + + return 1; +} + +void +session_input_channel_req(int id, void *arg) +{ + unsigned int len; + int reply; + int success = 0; + char *rtype; + Session *s; + Channel *c; + + rtype = packet_get_string(&len); + reply = packet_get_char(); + + s = session_by_channel(id); + if (s == NULL) + fatal("session_input_channel_req: channel %d: no session", id); + c = channel_lookup(id); + if (c == NULL) + fatal("session_input_channel_req: channel %d: bad channel", id); + + debug("session_input_channel_req: session %d channel %d request %s reply %d", + s->self, id, rtype, reply); + + /* + * a session is in LARVAL state until a shell + * or programm is executed + */ + if (c->type == SSH_CHANNEL_LARVAL) { + if (strcmp(rtype, "shell") == 0) { + if (s->ttyfd == -1) + do_exec_no_pty(s, NULL, s->pw); + else + do_exec_pty(s, NULL, s->pw); + success = 1; + } else if (strcmp(rtype, "exec") == 0) { + char *command = packet_get_string(&len); + if (s->ttyfd == -1) + do_exec_no_pty(s, command, s->pw); + else + do_exec_pty(s, command, s->pw); + xfree(command); + success = 1; + } else if (strcmp(rtype, "pty-req") == 0) { + if (session_pty_req(s) > 0) + success = 1; + } + } + if (strcmp(rtype, "window-change") == 0) { + success = session_window_change_req(s); + } + + if (reply) { + packet_start(success ? + SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); + packet_put_int(c->remote_id); + packet_send(); + } + xfree(rtype); +} + +void +session_set_fds(Session *s, int fdin, int fdout, int fderr) +{ + if (!compat20) + fatal("session_set_fds: called for proto != 2.0"); + /* + * now that have a child and a pipe to the child, + * we can activate our channel and register the fd's + */ + if (s->chanid == -1) + fatal("no channel for session %d", s->self); + channel_set_fds(s->chanid, + fdout, fdin, fderr, + fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ); +} + void session_pty_cleanup(Session *s) { @@ -1151,3 +1346,118 @@ session_pty_cleanup(Session *s) if (close(s->ptymaster) < 0) error("close(s->ptymaster): %s", strerror(errno)); } + +void +session_exit_message(Session *s, int status) +{ + Channel *c; + if (s == NULL) + fatal("session_close: no session"); + c = channel_lookup(s->chanid); + if (c == NULL) + fatal("session_close: session %d: no channel %d", + s->self, s->chanid); + debug("session_exit_message: session %d channel %d pid %d", + s->self, s->chanid, s->pid); + + if (WIFEXITED(status)) { + channel_request_start(s->chanid, + "exit-status", 0); + packet_put_int(WEXITSTATUS(status)); + packet_send(); + } else if (WIFSIGNALED(status)) { + channel_request_start(s->chanid, + "exit-signal", 0); + packet_put_int(WTERMSIG(status)); + packet_put_char(WCOREDUMP(status)); + packet_put_cstring(""); + packet_put_cstring(""); + packet_send(); + } else { + /* Some weird exit cause. Just exit. */ + packet_disconnect("wait returned status %04x.", status); + } + + /* disconnect channel */ + debug("session_exit_message: release channel %d", s->chanid); + channel_cancel_cleanup(s->chanid); + if (c->istate == CHAN_INPUT_OPEN) + chan_read_failed(c); + chan_write_failed(c); + s->chanid = -1; +} + +void +session_free(Session *s) +{ + debug("session_free: session %d pid %d", s->self, s->pid); + if (s->term) + xfree(s->term); + if (s->display) + xfree(s->display); + if (s->auth_data) + xfree(s->auth_data); + if (s->auth_proto) + xfree(s->auth_proto); + s->used = 0; +} + +void +session_close(Session *s) +{ + session_pty_cleanu |