📄 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.138 2002/06/20 23:05:55 markus Exp $");#include "ssh.h"#include "ssh1.h"#include "ssh2.h"#include "xmalloc.h"#include "sshpty.h"#include "packet.h"#include "buffer.h"#include "mpaux.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"#ifdef HAVE_CYGWIN#include <windows.h>#include <sys/cygwin.h>#define is_winnt (GetVersion() < 0x80000000)#endif/* func */Session *session_new(void);void session_set_fds(Session *, int, int, int);void session_pty_cleanup(void *);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);/* original command from peer. */const char *original_command = NULL;/* data */#define MAX_SESSIONS 10Session sessions[MAX_SESSIONS];#ifdef WITH_AIXAUTHENTICATEchar *aixloginmsg;#endif /* WITH_AIXAUTHENTICATE */#ifdef HAVE_LOGIN_CAPlogin_cap_t *lc;#endif/* 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(void *_pw){ struct passwd *pw = _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-XXXXXXXX", MAXPATHLEN); /* Create private directory for socket */ if (mkdtemp(auth_sock_dir) == NULL) { packet_send_debug("Agent forwarding disabled: " "mkdtemp() failed: %.100s", strerror(errno)); restore_uid(); xfree(auth_sock_name); xfree(auth_sock_dir); auth_sock_name = NULL; auth_sock_dir = NULL; return 0; } snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld", auth_sock_dir, (long) getpid()); /* delete agent socket on fatal() */ fatal_add_cleanup(auth_sock_cleanup_proc, pw); /* Create the socket. */ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) packet_disconnect("socket: %.100s", strerror(errno)); /* Bind it to the name. */ memset(&sunaddr, 0, sizeof(sunaddr)); sunaddr.sun_family = AF_UNIX; strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) packet_disconnect("bind: %.100s", strerror(errno)); /* Restore the privileged uid. */ restore_uid(); /* Start listening on the socket. */ if (listen(sock, 5) < 0) packet_disconnect("listen: %.100s", strerror(errno)); /* Allocate a channel for the authentication agent socket. */ nc = channel_new("auth socket", SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, xstrdup("auth socket"), 1); strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); return 1;}voiddo_authenticated(Authctxt *authctxt){ /* * Cancel the alarm we set to limit the time taken for * authentication. */ alarm(0); if (startup_pipe != -1) { close(startup_pipe); startup_pipe = -1; }#ifdef WITH_AIXAUTHENTICATE /* We don't have a pty yet, so just label the line as "ssh" */ if (loginsuccess(authctxt->user, get_canonical_hostname(options.verify_reverse_mapping), "ssh", &aixloginmsg) < 0) aixloginmsg = NULL;#endif /* WITH_AIXAUTHENTICATE */ /* 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); /* remove agent socket */ if (auth_sock_name != NULL) auth_sock_cleanup_proc(authctxt->pw);#ifdef KRB4 if (options.kerberos_ticket_cleanup) krb4_cleanup_proc(authctxt);#endif#ifdef KRB5 if (options.kerberos_ticket_cleanup) krb5_cleanup_proc(authctxt);#endif}/* * 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 compression_level = 0, enable_compression_after_reply = 0; u_int proto_len, data_len, dlen; s = session_new(); 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 illegal 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;#if defined(AFS) || defined(KRB5) case SSH_CMSG_HAVE_KERBEROS_TGT: if (!options.kerberos_tgt_passing) { verbose("Kerberos TGT passing disabled."); } else { char *kdata = packet_get_string(&dlen); packet_check_eom(); /* XXX - 0x41, see creds_to_radix version */ if (kdata[0] != 0x41) {#ifdef KRB5 krb5_data tgt; tgt.data = kdata; tgt.length = dlen; if (auth_krb5_tgt(s->authctxt, &tgt)) success = 1; else verbose("Kerberos v5 TGT refused for %.100s", s->authctxt->user);#endif /* KRB5 */ } else {#ifdef AFS if (auth_krb4_tgt(s->authctxt, kdata)) success = 1; else verbose("Kerberos v4 TGT refused for %.100s", s->authctxt->user);#endif /* AFS */ } xfree(kdata); } break;#endif /* AFS || KRB5 */#ifdef AFS case SSH_CMSG_HAVE_AFS_TOKEN: if (!options.afs_token_passing || !k_hasafs()) { verbose("AFS token passing disabled."); } else { /* Accept AFS token. */ char *token = packet_get_string(&dlen); packet_check_eom(); if (auth_afs_token(s->authctxt, token)) success = 1; else verbose("AFS token refused for %.100s", s->authctxt->user); xfree(token); } break;#endif /* AFS */ 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. */ log("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) do_pam_session(s->pw->pw_name, NULL); do_pam_setcred(1); if (is_pam_password_change_required()) packet_disconnect("Password change required but no " "TTY available");#endif /* USE_PAM */ /* Fork the child. */ if ((pid = fork()) == 0) { /* 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -