📄 session.c
字号:
/* * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 support by Markus Friedl. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include "includes.h"RCSID("$OpenBSD: session.c,v 1.181 2004/12/23 17:35:48 markus Exp $");#include "ssh.h"#include "ssh1.h"#include "ssh2.h"#include "xmalloc.h"#include "sshpty.h"#include "packet.h"#include "buffer.h"#include "match.h"#include "uidswap.h"#include "compat.h"#include "channels.h"#include "bufaux.h"#include "auth.h"#include "auth-options.h"#include "pathnames.h"#include "log.h"#include "servconf.h"#include "sshlogin.h"#include "serverloop.h"#include "canohost.h"#include "session.h"#include "monitor_wrap.h"#if defined(KRB5) && defined(USE_AFS)#include <kafs.h>#endif#ifdef GSSAPI#include "ssh-gss.h"#endif/* func */Session *session_new(void);void session_set_fds(Session *, int, int, int);void session_pty_cleanup(Session *);void session_proctitle(Session *);int session_setup_x11fwd(Session *);void do_exec_pty(Session *, const char *);void do_exec_no_pty(Session *, const char *);void do_exec(Session *, const char *);void do_login(Session *, const char *);#ifdef LOGIN_NEEDS_UTMPXstatic void do_pre_login(Session *s);#endifvoid do_child(Session *, const char *);void do_motd(void);int check_quietlogin(Session *, const char *);static void do_authenticated1(Authctxt *);static void do_authenticated2(Authctxt *);static int session_pty_req(Session *);/* import */extern ServerOptions options;extern char *__progname;extern int log_stderr;extern int debug_flag;extern u_int utmp_len;extern int startup_pipe;extern void destroy_sensitive_data(void);extern Buffer loginmsg;/* original command from peer. */const char *original_command = NULL;/* data */#define MAX_SESSIONS 10Session sessions[MAX_SESSIONS];#ifdef HAVE_LOGIN_CAPlogin_cap_t *lc;#endifstatic int is_child = 0;/* Name and directory of socket for authentication agent forwarding. */static char *auth_sock_name = NULL;static char *auth_sock_dir = NULL;/* removes the agent forwarding socket */static voidauth_sock_cleanup_proc(struct passwd *pw){ if (auth_sock_name != NULL) { temporarily_use_uid(pw); unlink(auth_sock_name); rmdir(auth_sock_dir); auth_sock_name = NULL; restore_uid(); }}static intauth_input_request_forwarding(struct passwd * pw){ Channel *nc; int sock; struct sockaddr_un sunaddr; if (auth_sock_name != NULL) { error("authentication forwarding requested twice."); return 0; } /* Temporarily drop privileged uid for mkdir/bind. */ temporarily_use_uid(pw); /* Allocate a buffer for the socket name, and format the name. */ auth_sock_name = xmalloc(MAXPATHLEN); auth_sock_dir = xmalloc(MAXPATHLEN); strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN); /* Create private directory for socket */ if (mkdtemp(auth_sock_dir) == NULL) { packet_send_debug("Agent forwarding disabled: " "mkdtemp() failed: %.100s", strerror(errno)); restore_uid(); xfree(auth_sock_name); xfree(auth_sock_dir); auth_sock_name = NULL; auth_sock_dir = NULL; return 0; } snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld", auth_sock_dir, (long) getpid()); /* Create the socket. */ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) packet_disconnect("socket: %.100s", strerror(errno)); /* Bind it to the name. */ memset(&sunaddr, 0, sizeof(sunaddr)); sunaddr.sun_family = AF_UNIX; strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) packet_disconnect("bind: %.100s", strerror(errno)); /* Restore the privileged uid. */ restore_uid(); /* Start listening on the socket. */ if (listen(sock, SSH_LISTEN_BACKLOG) < 0) packet_disconnect("listen: %.100s", strerror(errno)); /* Allocate a channel for the authentication agent socket. */ nc = channel_new("auth socket", SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "auth socket", 1); strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); return 1;}static voiddisplay_loginmsg(void){ if (buffer_len(&loginmsg) > 0) { buffer_append(&loginmsg, "\0", 1); printf("%s", (char *)buffer_ptr(&loginmsg)); buffer_clear(&loginmsg); }}voiddo_authenticated(Authctxt *authctxt){ setproctitle("%s", authctxt->pw->pw_name); /* * Cancel the alarm we set to limit the time taken for * authentication. */ alarm(0); if (startup_pipe != -1) { close(startup_pipe); startup_pipe = -1; } /* setup the channel layer */ if (!no_port_forwarding_flag && options.allow_tcp_forwarding) channel_permit_all_opens(); if (compat20) do_authenticated2(authctxt); else do_authenticated1(authctxt); do_cleanup(authctxt);}/* * Prepares for an interactive session. This is called after the user has * been successfully authenticated. During this message exchange, pseudo * terminals are allocated, X11, TCP/IP, and authentication agent forwardings * are requested, etc. */static voiddo_authenticated1(Authctxt *authctxt){ Session *s; char *command; int success, type, screen_flag; int enable_compression_after_reply = 0; u_int proto_len, data_len, dlen, compression_level = 0; s = session_new(); if (s == NULL) { error("no more sessions"); return; } s->authctxt = authctxt; s->pw = authctxt->pw; /* * We stay in this loop until the client requests to execute a shell * or a command. */ for (;;) { success = 0; /* Get a packet from the client. */ type = packet_read(); /* Process the packet. */ switch (type) { case SSH_CMSG_REQUEST_COMPRESSION: compression_level = packet_get_int(); packet_check_eom(); if (compression_level < 1 || compression_level > 9) { packet_send_debug("Received invalid compression level %d.", compression_level); break; } if (!options.compression) { debug2("compression disabled"); break; } /* Enable compression after we have responded with SUCCESS. */ enable_compression_after_reply = 1; success = 1; break; case SSH_CMSG_REQUEST_PTY: success = session_pty_req(s); break; case SSH_CMSG_X11_REQUEST_FORWARDING: s->auth_proto = packet_get_string(&proto_len); s->auth_data = packet_get_string(&data_len); screen_flag = packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER; debug2("SSH_PROTOFLAG_SCREEN_NUMBER: %d", screen_flag); if (packet_remaining() == 4) { if (!screen_flag) debug2("Buggy client: " "X11 screen flag missing"); s->screen = packet_get_int(); } else { s->screen = 0; } packet_check_eom(); success = session_setup_x11fwd(s); if (!success) { xfree(s->auth_proto); xfree(s->auth_data); s->auth_proto = NULL; s->auth_data = NULL; } break; case SSH_CMSG_AGENT_REQUEST_FORWARDING: if (no_agent_forwarding_flag || compat13) { debug("Authentication agent forwarding not permitted for this authentication."); break; } debug("Received authentication agent forwarding request."); success = auth_input_request_forwarding(s->pw); break; case SSH_CMSG_PORT_FORWARD_REQUEST: if (no_port_forwarding_flag) { debug("Port forwarding not permitted for this authentication."); break; } if (!options.allow_tcp_forwarding) { debug("Port forwarding not permitted."); break; } debug("Received TCP/IP port forwarding request."); channel_input_port_forward_request(s->pw->pw_uid == 0, options.gateway_ports); success = 1; break; case SSH_CMSG_MAX_PACKET_SIZE: if (packet_set_maxsize(packet_get_int()) > 0) success = 1; break; case SSH_CMSG_EXEC_SHELL: case SSH_CMSG_EXEC_CMD: if (type == SSH_CMSG_EXEC_CMD) { command = packet_get_string(&dlen); debug("Exec command '%.500s'", command); do_exec(s, command); xfree(command); } else { do_exec(s, NULL); } packet_check_eom(); session_close(s); return; default: /* * Any unknown messages in this phase are ignored, * and a failure message is returned. */ logit("Unknown packet type received after authentication: %d", type); } packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); /* Enable compression now that we have replied if appropriate. */ if (enable_compression_after_reply) { enable_compression_after_reply = 0; packet_start_compression(compression_level); } }}/* * This is called to fork and execute a command when we have no tty. This * will call do_child from the child, and server_loop from the parent after * setting up file descriptors and such. */voiddo_exec_no_pty(Session *s, const char *command){ pid_t pid;#ifdef USE_PIPES int pin[2], pout[2], perr[2]; /* Allocate pipes for communicating with the program. */ if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) packet_disconnect("Could not create pipes: %.100s", strerror(errno));#else /* USE_PIPES */ int inout[2], err[2]; /* Uses socket pairs to communicate with the program. */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) packet_disconnect("Could not create socket pairs: %.100s", strerror(errno));#endif /* USE_PIPES */ if (s == NULL) fatal("do_exec_no_pty: no session"); session_proctitle(s);#if defined(USE_PAM) if (options.use_pam && !use_privsep) do_pam_setcred(1);#endif /* USE_PAM */ /* Fork the child. */ if ((pid = fork()) == 0) { is_child = 1; /* Child. Reinitialize the log since the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* * Create a new session and process group since the 4.4BSD * setlogin() affects the entire process group. */ if (setsid() < 0) error("setsid failed: %.100s", strerror(errno));#ifdef USE_PIPES /* * Redirect stdin. We close the parent side of the socket * pair, and make the child side the standard input. */ close(pin[1]); if (dup2(pin[0], 0) < 0) perror("dup2 stdin"); close(pin[0]); /* Redirect stdout. */ close(pout[0]); if (dup2(pout[1], 1) < 0) perror("dup2 stdout"); close(pout[1]); /* Redirect stderr. */ close(perr[0]); if (dup2(perr[1], 2) < 0) perror("dup2 stderr"); close(perr[1]);#else /* USE_PIPES */ /* * Redirect stdin, stdout, and stderr. Stdin and stdout will * use the same socket, as some programs (particularly rdist) * seem to depend on it. */ close(inout[1]); close(err[1]); if (dup2(inout[0], 0) < 0) /* stdin */ perror("dup2 stdin"); if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ perror("dup2 stdout"); if (dup2(err[0], 2) < 0) /* stderr */ perror("dup2 stderr");#endif /* USE_PIPES */#ifdef _UNICOS cray_init_job(s->pw); /* set up cray jid and tmpdir */#endif /* Do processing for the child (exec command etc). */ do_child(s, command); /* NOTREACHED */ }#ifdef _UNICOS signal(WJSIGNAL, cray_job_termination_handler);#endif /* _UNICOS */#ifdef HAVE_CYGWIN if (is_winnt) cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);#endif if (pid < 0) packet_disconnect("fork failed: %.100s", strerror(errno)); s->pid = pid; /* Set interactive/non-interactive mode. */ packet_set_interactive(s->display != NULL);#ifdef USE_PIPES /* We are the parent. Close the child sides of the pipes. */ close(pin[0]); close(pout[1]); close(perr[1]); if (compat20) { if (s->is_subsystem) { close(perr[0]); perr[0] = -1; } 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[0], and perr[0]. */ }#else /* USE_PIPES */ /* We are the parent. Close the child sides of the socket pairs. */ close(inout[0]); close(err[0]); /* * Clear loginmsg, since it's the child's responsibility to display * it to the user, otherwise multiple sessions may accumulate * multiple copies of the login messages. */ buffer_clear(&loginmsg); /* * Enter the interactive session. Note: server_loop must be able to * handle the case that fdin and fdout are the same. */ if (compat20) { session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -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 */}/* * This is called to fork and execute a command when we have a tty. This * will call do_child from the child, and server_loop from the parent after * setting up file descriptors, controlling tty, updating wtmp, utmp, * lastlog, and other such operations. */voiddo_exec_pty(Session *s, const char *command){ int fdout, ptyfd, ttyfd, ptymaster; pid_t pid; if (s == NULL) fatal("do_exec_pty: no session"); ptyfd = s->ptyfd; ttyfd = s->ttyfd;#if defined(USE_PAM) if (options.use_pam) { do_pam_set_tty(s->tty); if (!use_privsep) do_pam_setcred(1); }#endif /* Fork the child. */ if ((pid = fork()) == 0) { is_child = 1; /* Child. Reinitialize the log because the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Close the master side of the pseudo tty. */ close(ptyfd); /* Make the pseudo tty our controlling tty. */ pty_make_controlling_tty(&ttyfd, s->tty); /* Redirect stdin/stdout/stderr from the pseudo tty. */ if (dup2(ttyfd, 0) < 0) error("dup2 stdin: %s", strerror(errno)); if (dup2(ttyfd, 1) < 0) error("dup2 stdout: %s", strerror(errno)); if (dup2(ttyfd, 2) < 0) error("dup2 stderr: %s", strerror(errno)); /* Close the extra descriptor for the pseudo tty. */ close(ttyfd); /* record login, etc. similar to login(1) */#ifndef HAVE_OSF_SIA if (!(options.use_login && command == NULL)) {#ifdef _UNICOS cray_init_job(s->pw); /* set up cray jid and tmpdir */#endif /* _UNICOS */ do_login(s, command); }# ifdef LOGIN_NEEDS_UTMPX else do_pre_login(s);# endif#endif /* Do common processing for the child, such as execing the command. */ do_child(s, command); /* NOTREACHED */ }#ifdef _UNICOS signal(WJSIGNAL, cray_job_termination_handler);#endif /* _UNICOS */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -