session.c revision cd7d5faf5bbb52336a6f85578a90b31a648ac3fa
/*
* 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
*
* 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.
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "includes.h"
#ifdef HAVE_DEFOPEN
#include <deflt.h>
#include <ulimit.h>
#endif /* HAVE_DEFOPEN */
#ifdef HAVE_LIBGEN_H
#include <libgen.h>
#endif
#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"
#ifdef USE_PAM
#include <security/pam_appl.h>
#endif /* USE_PAM */
#ifdef GSSAPI
#include "ssh-gss.h"
#endif
#ifdef ALTPRIVSEP
#include "altprivsep.h"
#endif /* ALTPRIVSEP */
#ifdef HAVE_CYGWIN
#include <windows.h>
#endif
/* func */
Session *session_new(void);
void session_set_fds(Session *, int, int, int);
void session_pty_cleanup(void *);
void session_xauthfile_cleanup(void *s);
void session_proctitle(Session *);
int session_setup_x11fwd(Session *);
void do_exec_pty(Session *, const char *);
void do_exec_no_pty(Session *, const char *);
#ifdef LOGIN_NEEDS_UTMPX
static void do_pre_login(Session *s);
#endif
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 *);
static int session_env_req(Session *s);
static void session_free_env(char ***envp);
#ifdef USE_PAM
static void session_do_pam(Session *, int);
#endif /* USE_PAM */
/* import */
extern ServerOptions options;
extern char *__progname;
extern int log_stderr;
extern int debug_flag;
extern void destroy_sensitive_data(void);
#ifdef GSSAPI
extern Gssctxt *xxx_gssctxt;
#endif /* GSSAPI */
/* original command from peer. */
const char *original_command = NULL;
/* data */
#define MAX_SESSIONS 10
#ifdef WITH_AIXAUTHENTICATE
char *aixloginmsg;
#endif /* WITH_AIXAUTHENTICATE */
#ifdef HAVE_LOGIN_CAP
#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 void
auth_sock_cleanup_proc(void *_pw)
{
if (auth_sock_name != NULL) {
restore_uid();
}
}
static int
{
int sock;
struct sockaddr_un sunaddr;
if (auth_sock_name != NULL) {
error("authentication forwarding requested twice.");
return 0;
}
/* Allocate a buffer for the socket name, and format the name. */
/* Create private directory for socket */
packet_send_debug("Agent forwarding disabled: "
restore_uid();
return 0;
}
auth_sock_dir, (long) getpid());
/* delete agent socket on fatal() */
/* Create the socket. */
if (sock < 0)
/* Bind it to the name. */
/* Restore the privileged uid. */
restore_uid();
/* Start listening on the socket. */
/* Allocate a channel for the authentication agent socket. */
return 1;
}
void
{
/* setup the channel layer */
if (compat20)
else
/* remove agent socket */
if (auth_sock_name != NULL)
#ifdef KRB4
#endif
#ifdef KRB5
#endif
}
/*
* Prepares for an interactive session. This is called after the user has
* been successfully authenticated. During this message exchange, pseudo
* are requested, etc.
*/
static void
{
Session *s;
char *command;
int enable_compression_after_reply = 0;
s = session_new();
/*
* 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) {
packet_send_debug("Received illegal compression level %d.",
break;
}
if (!options.compression) {
debug2("compression disabled");
break;
}
/* Enable compression after we have responded with SUCCESS. */
success = 1;
break;
case SSH_CMSG_REQUEST_PTY:
success = session_pty_req(s);
break;
if (packet_remaining() == 4) {
if (!screen_flag)
debug2("Buggy client: "
"X11 screen flag missing");
s->screen = packet_get_int();
} else {
s->screen = 0;
}
success = session_setup_x11fwd(s);
if (!success) {
xfree(s->auth_proto);
s->auth_proto = NULL;
}
break;
if (no_agent_forwarding_flag || compat13) {
debug("Authentication agent forwarding not permitted for this authentication.");
break;
}
debug("Received authentication agent forwarding request.");
break;
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;
}
success = 1;
break;
case SSH_CMSG_MAX_PACKET_SIZE:
if (packet_set_maxsize(packet_get_int()) > 0)
success = 1;
break;
if (!options.kerberos_tgt_passing) {
verbose("Kerberos TGT passing disabled.");
} else {
/* XXX - 0x41, see creds_to_radix version */
if (kdata[0] != 0x41) {
#ifdef KRB5
success = 1;
else
#endif /* KRB5 */
} else {
#ifdef AFS
success = 1;
else
#endif /* AFS */
}
}
break;
#endif /* AFS || KRB5 */
#ifdef AFS
case SSH_CMSG_HAVE_AFS_TOKEN:
verbose("AFS token passing disabled.");
} else {
/* Accept AFS token. */
success = 1;
else
verbose("AFS token refused for %.100s",
}
break;
#endif /* AFS */
case SSH_CMSG_EXEC_SHELL:
case SSH_CMSG_EXEC_CMD:
if (type == SSH_CMSG_EXEC_CMD) {
} else {
}
session_close(s);
return;
default:
/*
* Any unknown messages in this phase are ignored,
* and a failure message is returned.
*/
}
packet_send();
/* Enable compression now that we have replied if appropriate. */
}
}
}
/*
* 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.
*/
void
{
#ifdef USE_PIPES
/* Allocate pipes for communicating with the program. */
packet_disconnect("Could not create pipes: %.100s",
#else /* USE_PIPES */
/* Uses socket pairs to communicate with the program. */
packet_disconnect("Could not create socket pairs: %.100s",
#endif /* USE_PIPES */
if (s == NULL)
fatal("do_exec_no_pty: no session");
/* Fork the child. */
/* Child. Reinitialize the log since the pid has changed. */
/*
* Create a new session and process group since the 4.4BSD
* setlogin() affects the entire process group.
*/
if (setsid() < 0)
#ifdef USE_PIPES
/*
* Redirect stdin. We close the parent side of the socket
* pair, and make the child side the standard input.
*/
perror("dup2 stdin");
/* Redirect stdout. */
perror("dup2 stdout");
/* Redirect stderr. */
perror("dup2 stderr");
#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.
*/
perror("dup2 stdin");
perror("dup2 stdout");
perror("dup2 stderr");
#endif /* USE_PIPES */
#ifdef _UNICOS
#endif
/* Do processing for the child (exec command etc). */
/* NOTREACHED */
}
#ifdef _UNICOS
#endif /* _UNICOS */
#ifdef HAVE_CYGWIN
if (is_winnt)
#endif
if (pid < 0)
/* Set interactive/non-interactive mode. */
#ifdef USE_PIPES
/* We are the parent. Close the child sides of the pipes. */
if (compat20) {
/* Don't close channel before sending exit-status! */
} else {
/* Enter the interactive session. */
/* 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. */
/*
* Enter the interactive session. Note: server_loop must be able to
* handle the case that fdin and fdout are the same.
*/
if (compat20) {
/* Don't close channel before sending exit-status! */
} else {
/* 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.
*/
void
{
if (s == NULL)
fatal("do_exec_pty: no session");
#ifdef USE_PAM
#endif /* USE_PAM */
/*
* This pipe lets sshd wait for child to exec or exit. This is
* particularly important for ALTPRIVSEP because the child is
* the one to call the monitor to request a record_login() and
* we don't want the child and the parent to compete for the
* monitor's attention. But this is generic code and doesn't
* hurt to have here even if ALTPRIVSEP is not used.
*/
/* Fork the child. */
/* Child. Reinitialize the log because the pid has changed. */
/* Close the master side of the pseudo tty. */
/* Make the pseudo tty our controlling tty. */
/* Close the extra descriptor for the pseudo tty. */
/* record login, etc. similar to login(1) */
#if !defined(HAVE_OSF_SIA)
#ifdef _UNICOS
#endif /* _UNICOS */
}
# ifdef LOGIN_NEEDS_UTMPX
else
do_pre_login(s);
# endif
#endif /* !HAVE_OSF_SIA */
/*
* do_pre_login() will have completed the record_login(), so
* close the pipe to the parent so it can re-enter its event
* loop and service the ptm; if enough debug messages get
* written to the pty before this happens there will be a
* deadlock.
*/
/* Do common processing for the child, such as execing the command. */
/* NOTREACHED */
}
/* Wait for child to exec() or exit() */
#ifdef _UNICOS
#endif /* _UNICOS */
#ifdef HAVE_CYGWIN
if (is_winnt)
#endif
if (pid < 0)
/* Parent. Close the slave side of the pseudo tty. */
/*
* Create another descriptor of the pty master side for use as the
* standard input. We could use the original descriptor, but this
* simplifies code in server_loop. The descriptor is bidirectional.
*/
if (fdout < 0)
/* we keep a reference to the pty master */
if (ptymaster < 0)
/* Enter interactive session. */
if (compat20) {
/* Don't close channel before sending exit-status! */
} else {
/* server_loop _has_ closed ptyfd and fdout. */
}
}
#ifdef LOGIN_NEEDS_UTMPX
static void
do_pre_login(Session *s)
{
struct sockaddr_storage from;
/*
* Get IP address of client. If the connection is not a socket, let
* the address be 0.0.0.0.
*/
if (packet_connection_is_on_socket()) {
}
}
}
#endif
/*
* This is called to fork and execute a command. If another command is
* to be forced, execute that instead.
*/
void
{
if (command)
if (forced_command) {
}
if (s->ttyfd != -1)
do_exec_pty(s, command);
else
do_exec_no_pty(s, command);
}
/* administrative, login(1)-like work */
void
{
char *time_string;
#ifndef ALTPRIVSEP
#endif /* ALTPRIVSEP*/
/* Record that there was a login on that tty from the remote host. */
#ifdef ALTPRIVSEP
#endif /* ALTPRIVSEP*/
if (check_quietlogin(s, command))
return;
#ifdef USE_PAM
#endif /* USE_PAM */
#ifdef WITH_AIXAUTHENTICATE
if (aixloginmsg && *aixloginmsg)
#endif /* WITH_AIXAUTHENTICATE */
#ifndef NO_SSH_LASTLOG
else
s->hostname);
}
#endif /* NO_SSH_LASTLOG */
do_motd();
}
/*
* Display the message of the day.
*/
void
do_motd(void)
{
FILE *f;
char buf[256];
if (options.print_motd) {
#ifdef HAVE_LOGIN_CAP
#else
#endif
if (f) {
fclose(f);
}
}
}
/*
* Check for quiet login, either .hushlogin or command given.
*/
int
{
char buf[256];
/* Return 1 if .hushlogin exists or a command given. */
return 1;
#ifdef HAVE_LOGIN_CAP
return 1;
#else
return 1;
#endif
return 0;
}
/*
* Sets the value of the given variable in the environment. If the variable
* already exists, its value is overriden.
*/
void
const char *value)
{
char **env;
/*
* Find the slot where the value should be stored. If the variable
* already exists, we reuse the slot; otherwise we append a new slot
* at the end of the array, expanding if necessary.
*/
for (i = 0; env[i]; i++)
break;
if (env[i]) {
/* Reuse the slot. */
} else {
/* New variable. Expand if necessary. */
if (i >= (*envsizep) - 1) {
if (*envsizep >= 1000)
fatal("child_set_env: too many env vars,"
" skipping: %.100s", name);
(*envsizep) += 50;
}
/* Need to set the NULL pointer at end of array beyond the new slot. */
}
/* Allocate space and format the variable in the appropriate slot. */
}
/*
* into the environment. If the file does not exist, this does nothing.
* Otherwise, it must consist of empty lines, comments (line starts with '#')
* and assignments of the form name=value. No other forms are allowed.
*/
static void
const char *filename)
{
FILE *f;
char buf[4096];
if (!f)
return;
if (++lineno > 1000)
;
continue;
continue;
}
/*
* Replace the equals sign by nul, and advance value to
* the value string.
*/
*value = '\0';
value++;
}
fclose(f);
}
{
int i;
return;
continue;
}
*var_val++ = '\0';
}
}
#ifdef HAVE_DEFOPEN
static
void
{
int flags;
char *ptr;
if (defopen(_PATH_DEFAULT_LOGIN))
return;
/* Ignore case */
/* TZ & HZ */
/* PATH */
/* SHELL */
else
}
/* UMASK */
else
(void) umask(022);
/* ULIMIT */
}
#endif /* HAVE_DEFOPEN */
static char **
{
char buf[256];
char path_maildir[] = _PATH_MAILDIR;
char **env;
/* Initialize the environment. */
envsize = 100;
#ifdef HAVE_CYGWIN
/*
* The Windows environment contains some setting which are
* important for a running system. They must not be dropped.
*/
#endif
#ifdef GSSAPI
/* Allow any GSSAPI methods that we've used to alter
* the childs environment as they see fit
*/
#endif
/* Set basic environment. */
#ifdef HAVE_LOGIN_CAP
else
#else /* HAVE_LOGIN_CAP */
# ifndef HAVE_CYGWIN
/*
* There's no standard path on Windows. The path contains
* important components pointing to the system directories,
* needed for loading shared libraries. So the path better
* remains intact here.
*/
# ifdef SUPERUSER_PATH
# else
# endif /* SUPERUSER_PATH */
# endif /* HAVE_CYGWIN */
#endif /* HAVE_LOGIN_CAP */
/* Normal systems set SHELL by default. */
#ifdef HAVE_DEFOPEN
#endif /* HAVE_DEFOPEN */
}
#define PASS_ENV(x) \
if (getenv(x)) \
if (getenv("TZ"))
PASS_ENV("LANG")
PASS_ENV("LC_ALL")
PASS_ENV("LC_CTYPE")
PASS_ENV("LC_COLLATE")
PASS_ENV("LC_TIME")
PASS_ENV("LC_NUMERIC")
PASS_ENV("LC_MONETARY")
PASS_ENV("LC_MESSAGES")
/* Set custom environment options from RSA authentication. */
while (custom_environment) {
;
if (str[i] == '=') {
str[i] = 0;
}
}
}
/* SSH_CLIENT deprecated */
if (s->ttyfd != -1)
if (s->term)
if (s->display)
if (original_command)
#ifdef _UNICOS
if (cray_tmpdir[0] != '\0')
#endif /* _UNICOS */
#ifdef _AIX
{
char *cp;
}
#endif
#ifdef KRB4
if (s->authctxt->krb4_ticket_file)
s->authctxt->krb4_ticket_file);
#endif
#ifdef KRB5
if (s->authctxt->krb5_ticket_file)
s->authctxt->krb5_ticket_file);
#endif
#ifdef USE_PAM
/*
* Pull in any environment variables that may have
* been set by PAM.
*/
{
char **p;
p = fetch_pam_environment(s->authctxt);
}
#endif /* USE_PAM */
if (auth_sock_name != NULL)
/* read $HOME/.ssh/environment. */
}
if (debug_flag) {
/* dump the environment */
for (i = 0; env[i]; i++)
}
return env;
}
/*
* first in this order).
*/
static void
{
char cmd[1024];
int do_xauth;
do_xauth =
/* ignore _PATH_SSH_USER_RC for subsystems */
if (debug_flag)
if (f) {
if (do_xauth)
s->auth_data);
pclose(f);
} else
if (debug_flag)
if (f) {
if (do_xauth)
s->auth_data);
pclose(f);
} else
/* Add authority data to .Xauthority if appropriate. */
if (debug_flag) {
"Running %.500s add "
"%.100s %.100s %.100s\n",
s->auth_proto, s->auth_data);
}
if (f) {
fprintf(f, "add %s %s %s\n",
s->auth_display, s->auth_proto,
s->auth_data);
pclose(f);
} else {
cmd);
}
}
}
static void
{
char buf[1024];
#ifdef HAVE_LOGIN_CAP
_PATH_NOLOGIN), "r");
#else
#endif
if (f) {
log("User %.100s not allowed because %s exists",
fclose(f);
exit(254);
}
}
/* Set login name, uid, gid, and groups. */
void
{
#ifdef HAVE_CYGWIN
if (is_winnt) {
#else /* HAVE_CYGWIN */
#endif /* HAVE_CYGWIN */
#ifdef HAVE_SETPCRED
#endif /* HAVE_SETPCRED */
#ifdef HAVE_LOGIN_CAP
# ifdef __bsdi__
setpgid(0, 0);
# endif
(LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) {
perror("unable to set user context");
exit(1);
}
#else
# if defined(HAVE_GETLUID) && defined(HAVE_SETLUID)
/* Sets login uid for accounting */
# endif /* defined(HAVE_GETLUID) && defined(HAVE_SETLUID) */
perror("setgid");
exit(1);
}
/* Initialize the group list. */
perror("initgroups");
exit(1);
}
endgrent();
# if 0
# ifdef USE_PAM
/*
* PAM credentials may take the form of supplementary groups.
* These will have been wiped by the above initgroups() call.
* Reestablish them here.
*/
do_pam_setcred(0);
# endif /* USE_PAM */
# endif /* 0 */
# endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */
# ifdef _AIX
# endif /* _AIX */
/* Permanently switch to the desired uid. */
#endif
}
}
static void
{
/* Launch login(1). */
#ifdef xxxLOGIN_NEEDS_TERM
#endif /* LOGIN_NEEDS_TERM */
#ifdef LOGIN_NO_ENDOPT
#else
#endif
/* Login couldn't be executed, die. */
perror("login");
exit(1);
}
/*
* Performs common processing for the child, such as setting up the
* environment, closing extra file descriptors, setting the user and group
* ids, and executing the command or shell.
*/
void
{
extern char **environ;
char **env;
char *argv[10];
/* remove hostkey from the child's memory */
/* login(1) is only called if we execute the login shell */
#ifdef _UNICOS
#endif /* _UNICOS */
/*
* Login(1) does this as well, and it needs uid 0 for the "-h"
* switch, so we let login(1) to this for us.
*/
#ifdef HAVE_OSF_SIA
if (!check_quietlogin(s, command))
do_motd();
#else /* HAVE_OSF_SIA */
do_nologin(pw);
#endif /* HAVE_OSF_SIA */
}
/*
* Get the shell from the password data. An empty shell field is
*/
#ifdef HAVE_LOGIN_CAP
#endif
/* we have to stash the hostname before we close our socket. */
/*
* Close the connection descriptors; note that this is the child, and
* the server will still have the socket open, and it is important
* that we do not shutdown it. Note that the descriptors cannot be
* closed before building the environment, as we call
* get_remote_ipaddr there.
*/
if (packet_get_connection_in() == packet_get_connection_out())
else {
}
/*
* Close all descriptors related to channels. They will still remain
* open in the parent.
*/
/* XXX better use close-on-exec? -markus */
/*
* Close any extra file descriptors. Note that there may still be
* descriptors left by system functions. They will be closed later.
*/
endpwent();
/*
* Close any extra open file descriptors so that we don\'t have them
* hanging around in clients. Note that we want to do this after
* initgroups, because at least on Solaris 2.3 it leaves file
* descriptors open.
*/
/*
*/
#ifdef AFS
/* Try to get AFS tokens for the local cell. */
if (k_hasafs()) {
char cell[64];
krb_afslog(cell, 0);
krb_afslog(0, 0);
}
#endif /* AFS */
/* Change current directory to the user\'s home directory. */
gettext("Could not chdir to home directory %s: %s\n"),
#ifdef HAVE_LOGIN_CAP
exit(1);
#endif
}
do_rc_files(s, shell);
/* restore SIGPIPE for child */
/* NEVERREACHED */
}
/* Get the last component of the shell name. */
shell0++;
else
/*
* If we have no command, execute the shell. In this case, the shell
* name to be passed in argv[0] is preceded by '-' to indicate that
* this is a login shell.
*/
if (!command) {
char argv0[256];
/* Start the shell. Set initial character to '-'. */
argv0[0] = '-';
>= sizeof(argv0) - 1) {
exit(1);
}
/* Execute the shell. */
/* Executing the shell failed. */
exit(1);
}
/*
* Execute the command using the user's shell. This uses the -c
* option to execute the command.
*/
exit(1);
}
Session *
session_new(void)
{
int i;
static int did_init = 0;
if (!did_init) {
debug("session_new: init");
for (i = 0; i < MAX_SESSIONS; i++) {
}
did_init = 1;
}
for (i = 0; i < MAX_SESSIONS; i++) {
if (! s->used) {
memset(s, 0, sizeof(*s));
s->chanid = -1;
s->ptyfd = -1;
s->ttyfd = -1;
s->used = 1;
s->self = i;
debug("session_new: session %d", i);
return s;
}
}
return NULL;
}
static void
session_dump(void)
{
int i;
for (i = 0; i < MAX_SESSIONS; i++) {
debug("dump: used %d session %d %p channel %d pid %ld",
s->used,
s->self,
s,
s->chanid,
(long)s->pid);
}
}
int
{
Session *s = session_new();
if (s == NULL) {
error("no more sessions");
return 0;
}
return 1;
}
#ifndef lint
Session *
session_by_tty(char *tty)
{
int i;
for (i = 0; i < MAX_SESSIONS; i++) {
return s;
}
}
session_dump();
return NULL;
}
#endif /* lint */
static Session *
session_by_channel(int id)
{
int i;
for (i = 0; i < MAX_SESSIONS; i++) {
return s;
}
}
session_dump();
return NULL;
}
static Session *
{
int i;
for (i = 0; i < MAX_SESSIONS; i++) {
return s;
}
session_dump();
return NULL;
}
static int
{
s->col = packet_get_int();
s->row = packet_get_int();
s->xpixel = packet_get_int();
s->ypixel = packet_get_int();
return 1;
}
static int
{
int n_bytes;
if (no_pty_flag) {
debug("Allocating a pty not permitted for this authentication.");
return 0;
}
if (s->ttyfd != -1) {
packet_disconnect("Protocol error: you already have a pty.");
return 0;
}
/* Get the time and hostname when the user last logged in. */
if (options.print_lastlog) {
s->hostname[0] = '\0';
/*
* PAM may update the last login date.
*
* Ideally PAM would also show the last login date as a
* PAM_TEXT_INFO conversation message, and then we could just
* always force the use of keyboard-interactive just so we can
* pass any such PAM prompts and messages from the account and
* session stacks, but skip pam_authenticate() if other userauth
* has succeeded and the user's password isn't expired.
*
* Unfortunately this depends on support for keyboard-
* interactive in the client, and support for lastlog messages
* in some PAM module.
*
* As it is Solaris updates the lastlog in PAM, but does
* not show the lastlog date in PAM. If and when this state of
* affairs changes this hack can be reconsidered, and, maybe,
* removed.
*
* So we're stuck with a crude hack: get the lastlog
* time before calling pam_open_session() and store it
* in the Authctxt and then use it here once. After
* that, if the client opens any more pty sessions we'll
* show the last lastlog entry since userauth.
*/
s->authctxt->last_login_host,
sizeof(s->hostname));
s->authctxt->last_login_time = 0;
}
}
if (compat20) {
s->col = packet_get_int();
s->row = packet_get_int();
} else {
s->row = packet_get_int();
s->col = packet_get_int();
}
s->xpixel = packet_get_int();
s->ypixel = packet_get_int();
}
/* Allocate a pty and open it. */
debug("Allocating pty.");
if (s->term)
s->ptyfd = -1;
s->ttyfd = -1;
return 0;
}
/* for SSH1 the tty modes length is not given */
if (!compat20)
n_bytes = packet_remaining();
/*
* 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(session_pty_cleanup, (void *)s);
/* Set window size from the packet. */
return 1;
}
static int
{
int success = 0;
int i;
for (i = 0; i < options.num_subsystems; i++) {
break;
}
s->is_subsystem = 1;
success = 1;
break;
}
}
if (!success)
log("subsystem request for %.100s failed, subsystem not found",
subsys);
return success;
}
/*
* Serve "x11-req" channel request for X11 forwarding for the current session
* channel.
*/
static int
{
char xauthdir[] = "/tmp/ssh-xauth-XXXXXX";
s->single_connection = packet_get_char();
s->screen = packet_get_int();
success = session_setup_x11fwd(s);
if (!success) {
xfree(s->auth_proto);
s->auth_proto = NULL;
return (success);
}
/*
* Create per session X authority file so that different sessions
* don't contend for one common file. The reason for this is that
* xauth(1) locking doesn't work too well over network filesystems.
*
* If mkdtemp() or open() fails then s->auth_file remains NULL which
* means that we won't set XAUTHORITY variable in child's environment
* and xauth(1) will use the default location for the authority file.
*/
xauthdir);
/*
* we don't want that "creating new authority file" message to
* be printed by xauth(1) so we must create that file
* beforehand.
*/
error("failed to create the temporary X authority "
"file %s: %.100s; will use the default one",
error("cannot remove xauth directory %s: %.100s",
}
} else {
debug("temporary X authority file %s created",
s->auth_file);
/*
* add a cleanup function to remove the temporary
* xauth file in case we call fatal() (e.g., the
* connection gets closed).
*/
fatal_add_cleanup(session_xauthfile_cleanup, (void *)s);
}
}
else {
error("failed to create a directory for the temporary X "
"authority file: %.100s; will use the default xauth file",
}
return (success);
}
static int
{
return 1;
}
static int
{
return 1;
}
static int
{
static int called = 0;
if (no_agent_forwarding_flag) {
debug("session_auth_agent_req: no_agent_forwarding_flag");
return 0;
}
if (called) {
return 0;
} else {
called = 1;
return auth_input_request_forwarding(s->pw);
}
}
static int
{
char *current;
cat = LC_COLLATE;
cat = LC_NUMERIC;
cat = LC_MONETARY;
cat = LC_MESSAGES;
return (ret);
}
static int
{
Channel *c;
char **p;
int ret = 0;
/*
* We'll need the channel ID for the packet_send_debug messages,
* so get it now.
*/
goto done; /* shouldn't happen! */
/* For now allow only LANG and LC_* */
goto done;
}
goto done;
}
/*
* Always append new environment variables without regard to old
* ones being overriden. The way these are actually added to
* the environment of the session process later settings
* override earlier ones; see copy_environment().
*/
char **env;
p = env;
} else {
}
(*p++) = e;
*p = NULL;
ret = 1;
done:
return (ret);
}
static void
session_free_env(char ***envp)
{
char **env, **p;
return;
xfree(*p);
}
int
{
int success = 0;
Session *s;
log("session_input_channel_req: no session %d req %.100s",
return 0;
}
/*
* a session is in LARVAL state until a shell, a command
* or a subsystem is executed
*/
if (c->type == SSH_CHANNEL_LARVAL) {
success = session_shell_req(s);
success = session_exec_req(s);
success = session_pty_req(s);
success = session_x11_req(s);
success = session_subsystem_req(s);
success = session_env_req(s);
}
}
}
return success;
}
void
{
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)
1,
}
/*
* Function to perform pty cleanup. Also called if we get aborted abnormally
* (e.g., due to a dropped connection).
*/
void
session_pty_cleanup2(void *session)
{
if (s == NULL) {
error("session_pty_cleanup: no session");
return;
}
if (s->ttyfd == -1)
return;
#ifdef USE_PAM
session_do_pam(s, 0);
#endif /* USE_PAM */
/* Record that the user has logged out. */
if (s->pid != 0) {
#ifdef ALTPRIVSEP
#endif /* ALTPRIVSEP */
}
/* Release the pseudo-tty. */
if (getuid() == 0)
pty_release(s->tty);
/*
* Close the server side of the socket pairs. We must do this after
* the pty cleanup, so that another process doesn't get this pty
* while we're still cleaning up.
*/
/* unlink pty from session */
s->ttyfd = -1;
}
void
session_pty_cleanup(void *session)
{
}
/*
* We use a different temporary X authority file per every session so we
* should remove those files when fatal() is called.
*/
void
session_xauthfile_cleanup(void *session)
{
if (s == NULL) {
error("session_xauthfile_cleanup: no session");
return;
}
s->auth_file);
error("session_xauthfile_cleanup: cannot remove xauth file: "
return;
}
/* dirname() will modify s->auth_file but that's ok */
error("session_xauthfile_cleanup: "
return;
}
}
static char *
{
return "SIG@openssh.com";
}
static void
{
Channel *c;
fatal("session_exit_message: session %d: no channel %d",
debug("session_exit_message: session %d channel %d pid %ld",
packet_send();
} else if (WIFSIGNALED(status)) {
#ifdef WCOREDUMP
#else /* WCOREDUMP */
packet_put_char(0);
#endif /* WCOREDUMP */
packet_put_cstring("");
packet_put_cstring("");
packet_send();
} else {
/* Some weird exit cause. Just exit. */
}
/* Ok to close channel now */
channel_set_wait_for_exit(s->chanid, 0);
/* disconnect channel */
/*
* emulate a write failure with 'chan_write_failed', nobody will be
* interested in data we write.
* Note that we must not call 'chan_read_failed', since there could
* be some more data waiting in the pipe.
*/
if (c->ostate != CHAN_OUTPUT_CLOSED)
s->chanid = -1;
}
void
session_close(Session *s)
{
if (s->ttyfd != -1) {
fatal_remove_cleanup(session_pty_cleanup, (void *)s);
}
fatal_remove_cleanup(session_xauthfile_cleanup, (void *)s);
}
if (s->term)
if (s->display)
if (s->auth_display)
xfree(s->auth_display);
if (s->auth_data)
if (s->auth_proto)
xfree(s->auth_proto);
if (s->command)
session_free_env(&s->env);
s->used = 0;
}
void
{
if (s == NULL) {
debug("session_close_by_pid: no session for pid %ld",
(long)pid);
return;
}
if (s->chanid != -1)
session_close(s);
}
/*
* This is called when a channel dies before the session 'child' itself dies.
* It can happen for example if we exit from an interactive shell before we
* exit from forwarded X11 applications.
*/
void
{
if (s == NULL) {
return;
}
debug("session_close_by_channel: channel %d child %ld",
if (s->pid != 0) {
/*
* delay detach of session, but release pty, since
* the fd's to the child are already closed
*/
if (s->ttyfd != -1) {
fatal_remove_cleanup(session_pty_cleanup, (void *)s);
}
return;
}
/* detach by removing callback */
s->chanid = -1;
session_close(s);
}
void
{
int i;
for (i = 0; i < MAX_SESSIONS; i++) {
if (s->used) {
closefunc(s);
else
session_close(s);
}
}
}
static char *
session_tty_list(void)
{
static char buf[1024];
int i;
buf[0] = '\0';
for (i = 0; i < MAX_SESSIONS; i++) {
if (buf[0] != '\0')
}
}
if (buf[0] == '\0')
return buf;
}
void
{
else
}
int
{
char hostname[MAXHOSTNAMELEN];
if (no_x11_forwarding_flag) {
packet_send_debug("X11 forwarding disabled in user configuration file.");
return 0;
}
if (!options.x11_forwarding) {
debug("X11 forwarding disabled in server configuration file.");
return 0;
}
if (!options.xauth_location ||
packet_send_debug("No xauth program; cannot forward with spoofing.");
return 0;
}
packet_send_debug("X11 forwarding disabled; "
"not compatible with UseLogin=yes.");
return 0;
}
debug("X11 display already set.");
return 0;
}
&s->display_number) == -1) {
debug("x11_create_display_inet failed.");
return 0;
}
/* Set up a suitable value for the DISPLAY variable. */
/*
* auth_display must be used as the displayname when the
* authorization entry is added with xauth(1). This will be
* different than the DISPLAY string for localhost displays.
*/
if (options.x11_use_localhost) {
s->display_number, s->screen);
s->display_number, s->screen);
} else {
#ifdef IPADDR_IN_DISPLAY
error("Can't get IP address for X11 DISPLAY.");
packet_send_debug("Can't get IP address for X11 DISPLAY.");
return 0;
}
s->display_number, s->screen);
#else
s->display_number, s->screen);
#endif
}
return 1;
}
#ifdef USE_PAM
int session_do_pam_conv(int, struct pam_message **,
struct pam_response **, void *);
static struct pam_conv session_pam_conv = {
};
static void
{
int pam_retval;
return;
/* Save current PAM item values */
where = "getting PAM_CONV";
(void **) &old_conv_ptr);
if (pam_retval != PAM_SUCCESS)
goto done;
old_conv = *old_conv_ptr;
where = "getting PAM_TTY";
(void **) &old_tty);
if (pam_retval != PAM_SUCCESS)
goto done;
/* Change PAM_TTY and PAM_CONV items */
where = "setting PAM_TTY";
if (pam_retval != PAM_SUCCESS)
goto done;
where = "setting PAM_CONV";
if (pam_retval != PAM_SUCCESS)
goto done;
/* Call pam_open/close_session() */
if (do_open) {
where = "calling pam_open_session()";
}
else {
where = "calling pam_close_session()";
}
/* Reset PAM_TTY and PAM_CONV items to previous values */
where = "setting PAM_TTY";
if (pam_retval != PAM_SUCCESS)
goto done;
where = "setting PAM_CONV";
if (pam_retval != PAM_SUCCESS)
goto done;
done:
if (old_tty_copy)
if (pam_retval == PAM_SUCCESS)
return;
/* fatal()? probably not... */
}
int
struct pam_message **prompts,
struct pam_response **resp,
void *app_data)
{
struct pam_response *reply;
int count;
char *prompt;
return PAM_CONV_ERR;
/* PAM will free this later */
case PAM_TEXT_INFO:
/* Write to stdout of channel */
debug2("session_do_pam_conv: text info "
"prompt: %s", prompt);
}
break;
case PAM_ERROR_MSG:
/* Write to stderr of channel */
debug2("session_do_pam_conv: error "
"prompt: %s", prompt);
}
break;
case PAM_PROMPT_ECHO_ON:
case PAM_PROMPT_ECHO_OFF:
/*
* here on sessions with ttys.
*/
default:
return PAM_CONV_ERR;
}
}
return PAM_SUCCESS;
}
#endif /* USE_PAM */
static void
{
}