1*ba1276acSMatthew Dillon /* $OpenBSD: session.c,v 1.338 2024/05/17 00:30:24 djm Exp $ */
218de8d7fSPeter Avalos /*
318de8d7fSPeter Avalos * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
418de8d7fSPeter Avalos * All rights reserved
518de8d7fSPeter Avalos *
618de8d7fSPeter Avalos * As far as I am concerned, the code I have written for this software
718de8d7fSPeter Avalos * can be used freely for any purpose. Any derived versions of this
818de8d7fSPeter Avalos * software must be clearly marked as such, and if the derived work is
918de8d7fSPeter Avalos * incompatible with the protocol description in the RFC file, it must be
1018de8d7fSPeter Avalos * called by a name other than "ssh" or "Secure Shell".
1118de8d7fSPeter Avalos *
1218de8d7fSPeter Avalos * SSH2 support by Markus Friedl.
1318de8d7fSPeter Avalos * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
1418de8d7fSPeter Avalos *
1518de8d7fSPeter Avalos * Redistribution and use in source and binary forms, with or without
1618de8d7fSPeter Avalos * modification, are permitted provided that the following conditions
1718de8d7fSPeter Avalos * are met:
1818de8d7fSPeter Avalos * 1. Redistributions of source code must retain the above copyright
1918de8d7fSPeter Avalos * notice, this list of conditions and the following disclaimer.
2018de8d7fSPeter Avalos * 2. Redistributions in binary form must reproduce the above copyright
2118de8d7fSPeter Avalos * notice, this list of conditions and the following disclaimer in the
2218de8d7fSPeter Avalos * documentation and/or other materials provided with the distribution.
2318de8d7fSPeter Avalos *
2418de8d7fSPeter Avalos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2518de8d7fSPeter Avalos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2618de8d7fSPeter Avalos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2718de8d7fSPeter Avalos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2818de8d7fSPeter Avalos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2918de8d7fSPeter Avalos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3018de8d7fSPeter Avalos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3118de8d7fSPeter Avalos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3218de8d7fSPeter Avalos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3318de8d7fSPeter Avalos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3418de8d7fSPeter Avalos */
3518de8d7fSPeter Avalos
3618de8d7fSPeter Avalos #include "includes.h"
3718de8d7fSPeter Avalos
3818de8d7fSPeter Avalos #include <sys/types.h>
3918de8d7fSPeter Avalos #ifdef HAVE_SYS_STAT_H
4018de8d7fSPeter Avalos # include <sys/stat.h>
4118de8d7fSPeter Avalos #endif
4218de8d7fSPeter Avalos #include <sys/socket.h>
4318de8d7fSPeter Avalos #include <sys/un.h>
4418de8d7fSPeter Avalos #include <sys/wait.h>
4518de8d7fSPeter Avalos
4618de8d7fSPeter Avalos #include <arpa/inet.h>
4718de8d7fSPeter Avalos
48e9778795SPeter Avalos #include <ctype.h>
4918de8d7fSPeter Avalos #include <errno.h>
50856ea928SPeter Avalos #include <fcntl.h>
5118de8d7fSPeter Avalos #include <grp.h>
5236e94dc5SPeter Avalos #include <netdb.h>
5318de8d7fSPeter Avalos #ifdef HAVE_PATHS_H
5418de8d7fSPeter Avalos #include <paths.h>
5518de8d7fSPeter Avalos #endif
5618de8d7fSPeter Avalos #include <pwd.h>
5718de8d7fSPeter Avalos #include <signal.h>
5818de8d7fSPeter Avalos #include <stdio.h>
5918de8d7fSPeter Avalos #include <stdlib.h>
6018de8d7fSPeter Avalos #include <string.h>
610cbfa66cSDaniel Fojt #include <stdarg.h>
6218de8d7fSPeter Avalos #include <unistd.h>
63e9778795SPeter Avalos #include <limits.h>
6418de8d7fSPeter Avalos
6518de8d7fSPeter Avalos #include "openbsd-compat/sys-queue.h"
6618de8d7fSPeter Avalos #include "xmalloc.h"
6718de8d7fSPeter Avalos #include "ssh.h"
6818de8d7fSPeter Avalos #include "ssh2.h"
6918de8d7fSPeter Avalos #include "sshpty.h"
7018de8d7fSPeter Avalos #include "packet.h"
71664f4763Szrj #include "sshbuf.h"
72664f4763Szrj #include "ssherr.h"
7318de8d7fSPeter Avalos #include "match.h"
7418de8d7fSPeter Avalos #include "uidswap.h"
7518de8d7fSPeter Avalos #include "channels.h"
76664f4763Szrj #include "sshkey.h"
7718de8d7fSPeter Avalos #include "cipher.h"
7818de8d7fSPeter Avalos #ifdef GSSAPI
7918de8d7fSPeter Avalos #include "ssh-gss.h"
8018de8d7fSPeter Avalos #endif
8118de8d7fSPeter Avalos #include "hostfile.h"
8218de8d7fSPeter Avalos #include "auth.h"
8318de8d7fSPeter Avalos #include "auth-options.h"
8436e94dc5SPeter Avalos #include "authfd.h"
8518de8d7fSPeter Avalos #include "pathnames.h"
8618de8d7fSPeter Avalos #include "log.h"
8736e94dc5SPeter Avalos #include "misc.h"
8818de8d7fSPeter Avalos #include "servconf.h"
8918de8d7fSPeter Avalos #include "sshlogin.h"
9018de8d7fSPeter Avalos #include "serverloop.h"
9118de8d7fSPeter Avalos #include "canohost.h"
9218de8d7fSPeter Avalos #include "session.h"
9318de8d7fSPeter Avalos #include "kex.h"
9418de8d7fSPeter Avalos #include "monitor_wrap.h"
9518de8d7fSPeter Avalos #include "sftp.h"
962c9c1408SMatthew Dillon #include "atomicio.h"
9718de8d7fSPeter Avalos
9818de8d7fSPeter Avalos #if defined(KRB5) && defined(USE_AFS)
9918de8d7fSPeter Avalos #include <kafs.h>
10018de8d7fSPeter Avalos #endif
10118de8d7fSPeter Avalos
1021c188a7fSPeter Avalos #ifdef WITH_SELINUX
1031c188a7fSPeter Avalos #include <selinux/selinux.h>
1041c188a7fSPeter Avalos #endif
1051c188a7fSPeter Avalos
106*ba1276acSMatthew Dillon /*
107*ba1276acSMatthew Dillon * Hack for systems that do not support FD passing: allocate PTYs directly
108*ba1276acSMatthew Dillon * without calling into the monitor. This requires either the post-auth
109*ba1276acSMatthew Dillon * privsep process retain root privileges (see the comment in
110*ba1276acSMatthew Dillon * sshd-session.c:privsep_postauth) or that PTY allocation doesn't require
111*ba1276acSMatthew Dillon * privileges to begin with (e.g. Cygwin).
112*ba1276acSMatthew Dillon */
113*ba1276acSMatthew Dillon #ifdef DISABLE_FD_PASSING
114*ba1276acSMatthew Dillon #define mm_pty_allocate pty_allocate
115*ba1276acSMatthew Dillon #endif
116*ba1276acSMatthew Dillon
117cb5eb4f1SPeter Avalos #define IS_INTERNAL_SFTP(c) \
118cb5eb4f1SPeter Avalos (!strncmp(c, INTERNAL_SFTP_NAME, sizeof(INTERNAL_SFTP_NAME) - 1) && \
119cb5eb4f1SPeter Avalos (c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\0' || \
120cb5eb4f1SPeter Avalos c[sizeof(INTERNAL_SFTP_NAME) - 1] == ' ' || \
121cb5eb4f1SPeter Avalos c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\t'))
122cb5eb4f1SPeter Avalos
12318de8d7fSPeter Avalos /* func */
12418de8d7fSPeter Avalos
12518de8d7fSPeter Avalos Session *session_new(void);
1262c9c1408SMatthew Dillon void session_set_fds(struct ssh *, Session *, int, int, int, int, int);
12718de8d7fSPeter Avalos void session_pty_cleanup(Session *);
12818de8d7fSPeter Avalos void session_proctitle(Session *);
1292c9c1408SMatthew Dillon int session_setup_x11fwd(struct ssh *, Session *);
1302c9c1408SMatthew Dillon int do_exec_pty(struct ssh *, Session *, const char *);
1312c9c1408SMatthew Dillon int do_exec_no_pty(struct ssh *, Session *, const char *);
1322c9c1408SMatthew Dillon int do_exec(struct ssh *, Session *, const char *);
1332c9c1408SMatthew Dillon void do_login(struct ssh *, Session *, const char *);
1342c9c1408SMatthew Dillon void do_child(struct ssh *, Session *, const char *);
13518de8d7fSPeter Avalos void do_motd(void);
13618de8d7fSPeter Avalos int check_quietlogin(Session *, const char *);
13718de8d7fSPeter Avalos
1382c9c1408SMatthew Dillon static void do_authenticated2(struct ssh *, Authctxt *);
13918de8d7fSPeter Avalos
1402c9c1408SMatthew Dillon static int session_pty_req(struct ssh *, Session *);
14118de8d7fSPeter Avalos
14218de8d7fSPeter Avalos /* import */
14318de8d7fSPeter Avalos extern ServerOptions options;
14418de8d7fSPeter Avalos extern char *__progname;
14518de8d7fSPeter Avalos extern int debug_flag;
14618de8d7fSPeter Avalos extern u_int utmp_len;
14718de8d7fSPeter Avalos extern int startup_pipe;
14818de8d7fSPeter Avalos extern void destroy_sensitive_data(void);
149664f4763Szrj extern struct sshbuf *loginmsg;
150664f4763Szrj extern struct sshauthopt *auth_opts;
151664f4763Szrj extern char *tun_fwd_ifnames; /* serverloop.c */
15218de8d7fSPeter Avalos
15318de8d7fSPeter Avalos /* original command from peer. */
15418de8d7fSPeter Avalos const char *original_command = NULL;
15518de8d7fSPeter Avalos
15618de8d7fSPeter Avalos /* data */
15718de8d7fSPeter Avalos static int sessions_first_unused = -1;
15818de8d7fSPeter Avalos static int sessions_nalloc = 0;
15918de8d7fSPeter Avalos static Session *sessions = NULL;
16018de8d7fSPeter Avalos
16118de8d7fSPeter Avalos #define SUBSYSTEM_NONE 0
16218de8d7fSPeter Avalos #define SUBSYSTEM_EXT 1
16318de8d7fSPeter Avalos #define SUBSYSTEM_INT_SFTP 2
164856ea928SPeter Avalos #define SUBSYSTEM_INT_SFTP_ERROR 3
16518de8d7fSPeter Avalos
16618de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
16718de8d7fSPeter Avalos login_cap_t *lc;
16818de8d7fSPeter Avalos #endif
16918de8d7fSPeter Avalos
17018de8d7fSPeter Avalos static int is_child = 0;
171e9778795SPeter Avalos static int in_chroot = 0;
17218de8d7fSPeter Avalos
1732c9c1408SMatthew Dillon /* File containing userauth info, if ExposeAuthInfo set */
1742c9c1408SMatthew Dillon static char *auth_info_file = NULL;
1752c9c1408SMatthew Dillon
17618de8d7fSPeter Avalos /* Name and directory of socket for authentication agent forwarding. */
17718de8d7fSPeter Avalos static char *auth_sock_name = NULL;
17818de8d7fSPeter Avalos static char *auth_sock_dir = NULL;
17918de8d7fSPeter Avalos
18018de8d7fSPeter Avalos /* removes the agent forwarding socket */
18118de8d7fSPeter Avalos
18218de8d7fSPeter Avalos static void
auth_sock_cleanup_proc(struct passwd * pw)18318de8d7fSPeter Avalos auth_sock_cleanup_proc(struct passwd *pw)
18418de8d7fSPeter Avalos {
18518de8d7fSPeter Avalos if (auth_sock_name != NULL) {
18618de8d7fSPeter Avalos temporarily_use_uid(pw);
18718de8d7fSPeter Avalos unlink(auth_sock_name);
18818de8d7fSPeter Avalos rmdir(auth_sock_dir);
18918de8d7fSPeter Avalos auth_sock_name = NULL;
19018de8d7fSPeter Avalos restore_uid();
19118de8d7fSPeter Avalos }
19218de8d7fSPeter Avalos }
19318de8d7fSPeter Avalos
19418de8d7fSPeter Avalos static int
auth_input_request_forwarding(struct ssh * ssh,struct passwd * pw)1952c9c1408SMatthew Dillon auth_input_request_forwarding(struct ssh *ssh, struct passwd * pw)
19618de8d7fSPeter Avalos {
19718de8d7fSPeter Avalos Channel *nc;
19818de8d7fSPeter Avalos int sock = -1;
19918de8d7fSPeter Avalos
20018de8d7fSPeter Avalos if (auth_sock_name != NULL) {
20118de8d7fSPeter Avalos error("authentication forwarding requested twice.");
20218de8d7fSPeter Avalos return 0;
20318de8d7fSPeter Avalos }
20418de8d7fSPeter Avalos
20518de8d7fSPeter Avalos /* Temporarily drop privileged uid for mkdir/bind. */
20618de8d7fSPeter Avalos temporarily_use_uid(pw);
20718de8d7fSPeter Avalos
20818de8d7fSPeter Avalos /* Allocate a buffer for the socket name, and format the name. */
20918de8d7fSPeter Avalos auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX");
21018de8d7fSPeter Avalos
21118de8d7fSPeter Avalos /* Create private directory for socket */
21218de8d7fSPeter Avalos if (mkdtemp(auth_sock_dir) == NULL) {
213664f4763Szrj ssh_packet_send_debug(ssh, "Agent forwarding disabled: "
21418de8d7fSPeter Avalos "mkdtemp() failed: %.100s", strerror(errno));
21518de8d7fSPeter Avalos restore_uid();
21636e94dc5SPeter Avalos free(auth_sock_dir);
21718de8d7fSPeter Avalos auth_sock_dir = NULL;
21818de8d7fSPeter Avalos goto authsock_err;
21918de8d7fSPeter Avalos }
22018de8d7fSPeter Avalos
22118de8d7fSPeter Avalos xasprintf(&auth_sock_name, "%s/agent.%ld",
22218de8d7fSPeter Avalos auth_sock_dir, (long) getpid());
22318de8d7fSPeter Avalos
22436e94dc5SPeter Avalos /* Start a Unix listener on auth_sock_name. */
22536e94dc5SPeter Avalos sock = unix_listener(auth_sock_name, SSH_LISTEN_BACKLOG, 0);
22618de8d7fSPeter Avalos
22718de8d7fSPeter Avalos /* Restore the privileged uid. */
22818de8d7fSPeter Avalos restore_uid();
22918de8d7fSPeter Avalos
23036e94dc5SPeter Avalos /* Check for socket/bind/listen failure. */
23136e94dc5SPeter Avalos if (sock < 0)
23218de8d7fSPeter Avalos goto authsock_err;
23318de8d7fSPeter Avalos
23418de8d7fSPeter Avalos /* Allocate a channel for the authentication agent socket. */
235*ba1276acSMatthew Dillon nc = channel_new(ssh, "auth-listener",
23618de8d7fSPeter Avalos SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1,
23718de8d7fSPeter Avalos CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
23818de8d7fSPeter Avalos 0, "auth socket", 1);
239cb5eb4f1SPeter Avalos nc->path = xstrdup(auth_sock_name);
24018de8d7fSPeter Avalos return 1;
24118de8d7fSPeter Avalos
24218de8d7fSPeter Avalos authsock_err:
24336e94dc5SPeter Avalos free(auth_sock_name);
24418de8d7fSPeter Avalos if (auth_sock_dir != NULL) {
245664f4763Szrj temporarily_use_uid(pw);
24618de8d7fSPeter Avalos rmdir(auth_sock_dir);
247664f4763Szrj restore_uid();
24836e94dc5SPeter Avalos free(auth_sock_dir);
24918de8d7fSPeter Avalos }
25018de8d7fSPeter Avalos if (sock != -1)
25118de8d7fSPeter Avalos close(sock);
25218de8d7fSPeter Avalos auth_sock_name = NULL;
25318de8d7fSPeter Avalos auth_sock_dir = NULL;
25418de8d7fSPeter Avalos return 0;
25518de8d7fSPeter Avalos }
25618de8d7fSPeter Avalos
25718de8d7fSPeter Avalos static void
display_loginmsg(void)25818de8d7fSPeter Avalos display_loginmsg(void)
25918de8d7fSPeter Avalos {
260664f4763Szrj int r;
261664f4763Szrj
262664f4763Szrj if (sshbuf_len(loginmsg) == 0)
263664f4763Szrj return;
264664f4763Szrj if ((r = sshbuf_put_u8(loginmsg, 0)) != 0)
26550a69bb5SSascha Wildner fatal_fr(r, "sshbuf_put_u8");
266664f4763Szrj printf("%s", (char *)sshbuf_ptr(loginmsg));
267664f4763Szrj sshbuf_reset(loginmsg);
26818de8d7fSPeter Avalos }
26918de8d7fSPeter Avalos
2702c9c1408SMatthew Dillon static void
prepare_auth_info_file(struct passwd * pw,struct sshbuf * info)2712c9c1408SMatthew Dillon prepare_auth_info_file(struct passwd *pw, struct sshbuf *info)
2722c9c1408SMatthew Dillon {
2732c9c1408SMatthew Dillon int fd = -1, success = 0;
2742c9c1408SMatthew Dillon
2752c9c1408SMatthew Dillon if (!options.expose_userauth_info || info == NULL)
2762c9c1408SMatthew Dillon return;
2772c9c1408SMatthew Dillon
2782c9c1408SMatthew Dillon temporarily_use_uid(pw);
2792c9c1408SMatthew Dillon auth_info_file = xstrdup("/tmp/sshauth.XXXXXXXXXXXXXXX");
2802c9c1408SMatthew Dillon if ((fd = mkstemp(auth_info_file)) == -1) {
28150a69bb5SSascha Wildner error_f("mkstemp: %s", strerror(errno));
2822c9c1408SMatthew Dillon goto out;
2832c9c1408SMatthew Dillon }
2842c9c1408SMatthew Dillon if (atomicio(vwrite, fd, sshbuf_mutable_ptr(info),
2852c9c1408SMatthew Dillon sshbuf_len(info)) != sshbuf_len(info)) {
28650a69bb5SSascha Wildner error_f("write: %s", strerror(errno));
2872c9c1408SMatthew Dillon goto out;
2882c9c1408SMatthew Dillon }
2892c9c1408SMatthew Dillon if (close(fd) != 0) {
29050a69bb5SSascha Wildner error_f("close: %s", strerror(errno));
2912c9c1408SMatthew Dillon goto out;
2922c9c1408SMatthew Dillon }
2932c9c1408SMatthew Dillon success = 1;
2942c9c1408SMatthew Dillon out:
2952c9c1408SMatthew Dillon if (!success) {
2962c9c1408SMatthew Dillon if (fd != -1)
2972c9c1408SMatthew Dillon close(fd);
2982c9c1408SMatthew Dillon free(auth_info_file);
2992c9c1408SMatthew Dillon auth_info_file = NULL;
3002c9c1408SMatthew Dillon }
3012c9c1408SMatthew Dillon restore_uid();
3022c9c1408SMatthew Dillon }
3032c9c1408SMatthew Dillon
304664f4763Szrj static void
set_fwdpermit_from_authopts(struct ssh * ssh,const struct sshauthopt * opts)305664f4763Szrj set_fwdpermit_from_authopts(struct ssh *ssh, const struct sshauthopt *opts)
306664f4763Szrj {
307664f4763Szrj char *tmp, *cp, *host;
308664f4763Szrj int port;
309664f4763Szrj size_t i;
310664f4763Szrj
311664f4763Szrj if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) {
312664f4763Szrj channel_clear_permission(ssh, FORWARD_USER, FORWARD_LOCAL);
313664f4763Szrj for (i = 0; i < auth_opts->npermitopen; i++) {
314664f4763Szrj tmp = cp = xstrdup(auth_opts->permitopen[i]);
315664f4763Szrj /* This shouldn't fail as it has already been checked */
316ee116499SAntonio Huete Jimenez if ((host = hpdelim2(&cp, NULL)) == NULL)
31750a69bb5SSascha Wildner fatal_f("internal error: hpdelim");
318664f4763Szrj host = cleanhostname(host);
319664f4763Szrj if (cp == NULL || (port = permitopen_port(cp)) < 0)
32050a69bb5SSascha Wildner fatal_f("internal error: permitopen port");
321664f4763Szrj channel_add_permission(ssh,
322664f4763Szrj FORWARD_USER, FORWARD_LOCAL, host, port);
323664f4763Szrj free(tmp);
324664f4763Szrj }
325664f4763Szrj }
326664f4763Szrj if ((options.allow_tcp_forwarding & FORWARD_REMOTE) != 0) {
327664f4763Szrj channel_clear_permission(ssh, FORWARD_USER, FORWARD_REMOTE);
328664f4763Szrj for (i = 0; i < auth_opts->npermitlisten; i++) {
329664f4763Szrj tmp = cp = xstrdup(auth_opts->permitlisten[i]);
330664f4763Szrj /* This shouldn't fail as it has already been checked */
331664f4763Szrj if ((host = hpdelim(&cp)) == NULL)
33250a69bb5SSascha Wildner fatal_f("internal error: hpdelim");
333664f4763Szrj host = cleanhostname(host);
334664f4763Szrj if (cp == NULL || (port = permitopen_port(cp)) < 0)
33550a69bb5SSascha Wildner fatal_f("internal error: permitlisten port");
336664f4763Szrj channel_add_permission(ssh,
337664f4763Szrj FORWARD_USER, FORWARD_REMOTE, host, port);
338664f4763Szrj free(tmp);
339664f4763Szrj }
340664f4763Szrj }
341664f4763Szrj }
342664f4763Szrj
34318de8d7fSPeter Avalos void
do_authenticated(struct ssh * ssh,Authctxt * authctxt)3442c9c1408SMatthew Dillon do_authenticated(struct ssh *ssh, Authctxt *authctxt)
34518de8d7fSPeter Avalos {
34618de8d7fSPeter Avalos setproctitle("%s", authctxt->pw->pw_name);
34718de8d7fSPeter Avalos
348664f4763Szrj auth_log_authopts("active", auth_opts, 0);
349664f4763Szrj
35018de8d7fSPeter Avalos /* setup the channel layer */
35136e94dc5SPeter Avalos /* XXX - streamlocal? */
352664f4763Szrj set_fwdpermit_from_authopts(ssh, auth_opts);
35318de8d7fSPeter Avalos
354664f4763Szrj if (!auth_opts->permit_port_forwarding_flag ||
355664f4763Szrj options.disable_forwarding) {
356664f4763Szrj channel_disable_admin(ssh, FORWARD_LOCAL);
357664f4763Szrj channel_disable_admin(ssh, FORWARD_REMOTE);
358664f4763Szrj } else {
359664f4763Szrj if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0)
360664f4763Szrj channel_disable_admin(ssh, FORWARD_LOCAL);
361664f4763Szrj else
362664f4763Szrj channel_permit_all(ssh, FORWARD_LOCAL);
363664f4763Szrj if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0)
364664f4763Szrj channel_disable_admin(ssh, FORWARD_REMOTE);
365664f4763Szrj else
366664f4763Szrj channel_permit_all(ssh, FORWARD_REMOTE);
367664f4763Szrj }
368664f4763Szrj auth_debug_send(ssh);
369856ea928SPeter Avalos
3702c9c1408SMatthew Dillon prepare_auth_info_file(authctxt->pw, authctxt->session_info);
37118de8d7fSPeter Avalos
3722c9c1408SMatthew Dillon do_authenticated2(ssh, authctxt);
3732c9c1408SMatthew Dillon
3742c9c1408SMatthew Dillon do_cleanup(ssh, authctxt);
37518de8d7fSPeter Avalos }
37618de8d7fSPeter Avalos
377e9778795SPeter Avalos /* Check untrusted xauth strings for metacharacters */
378e9778795SPeter Avalos static int
xauth_valid_string(const char * s)379e9778795SPeter Avalos xauth_valid_string(const char *s)
380e9778795SPeter Avalos {
381e9778795SPeter Avalos size_t i;
382e9778795SPeter Avalos
383e9778795SPeter Avalos for (i = 0; s[i] != '\0'; i++) {
384e9778795SPeter Avalos if (!isalnum((u_char)s[i]) &&
385e9778795SPeter Avalos s[i] != '.' && s[i] != ':' && s[i] != '/' &&
386e9778795SPeter Avalos s[i] != '-' && s[i] != '_')
387e9778795SPeter Avalos return 0;
388e9778795SPeter Avalos }
389e9778795SPeter Avalos return 1;
390e9778795SPeter Avalos }
391e9778795SPeter Avalos
39236e94dc5SPeter Avalos #define USE_PIPES 1
39318de8d7fSPeter Avalos /*
39418de8d7fSPeter Avalos * This is called to fork and execute a command when we have no tty. This
39518de8d7fSPeter Avalos * will call do_child from the child, and server_loop from the parent after
39618de8d7fSPeter Avalos * setting up file descriptors and such.
39718de8d7fSPeter Avalos */
39818de8d7fSPeter Avalos int
do_exec_no_pty(struct ssh * ssh,Session * s,const char * command)3992c9c1408SMatthew Dillon do_exec_no_pty(struct ssh *ssh, Session *s, const char *command)
40018de8d7fSPeter Avalos {
40118de8d7fSPeter Avalos pid_t pid;
40218de8d7fSPeter Avalos #ifdef USE_PIPES
40318de8d7fSPeter Avalos int pin[2], pout[2], perr[2];
40418de8d7fSPeter Avalos
405856ea928SPeter Avalos if (s == NULL)
406856ea928SPeter Avalos fatal("do_exec_no_pty: no session");
407856ea928SPeter Avalos
40818de8d7fSPeter Avalos /* Allocate pipes for communicating with the program. */
4090cbfa66cSDaniel Fojt if (pipe(pin) == -1) {
41050a69bb5SSascha Wildner error_f("pipe in: %.100s", strerror(errno));
41118de8d7fSPeter Avalos return -1;
41218de8d7fSPeter Avalos }
4130cbfa66cSDaniel Fojt if (pipe(pout) == -1) {
41450a69bb5SSascha Wildner error_f("pipe out: %.100s", strerror(errno));
41518de8d7fSPeter Avalos close(pin[0]);
41618de8d7fSPeter Avalos close(pin[1]);
41718de8d7fSPeter Avalos return -1;
41818de8d7fSPeter Avalos }
4190cbfa66cSDaniel Fojt if (pipe(perr) == -1) {
42050a69bb5SSascha Wildner error_f("pipe err: %.100s", strerror(errno));
42118de8d7fSPeter Avalos close(pin[0]);
42218de8d7fSPeter Avalos close(pin[1]);
42318de8d7fSPeter Avalos close(pout[0]);
42418de8d7fSPeter Avalos close(pout[1]);
42518de8d7fSPeter Avalos return -1;
42618de8d7fSPeter Avalos }
42718de8d7fSPeter Avalos #else
42818de8d7fSPeter Avalos int inout[2], err[2];
42918de8d7fSPeter Avalos
430856ea928SPeter Avalos if (s == NULL)
431856ea928SPeter Avalos fatal("do_exec_no_pty: no session");
432856ea928SPeter Avalos
43318de8d7fSPeter Avalos /* Uses socket pairs to communicate with the program. */
4340cbfa66cSDaniel Fojt if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) {
43550a69bb5SSascha Wildner error_f("socketpair #1: %.100s", strerror(errno));
43618de8d7fSPeter Avalos return -1;
43718de8d7fSPeter Avalos }
4380cbfa66cSDaniel Fojt if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) == -1) {
43950a69bb5SSascha Wildner error_f("socketpair #2: %.100s", strerror(errno));
44018de8d7fSPeter Avalos close(inout[0]);
44118de8d7fSPeter Avalos close(inout[1]);
44218de8d7fSPeter Avalos return -1;
44318de8d7fSPeter Avalos }
44418de8d7fSPeter Avalos #endif
44518de8d7fSPeter Avalos
44618de8d7fSPeter Avalos session_proctitle(s);
44718de8d7fSPeter Avalos
44818de8d7fSPeter Avalos /* Fork the child. */
44918de8d7fSPeter Avalos switch ((pid = fork())) {
45018de8d7fSPeter Avalos case -1:
45150a69bb5SSascha Wildner error_f("fork: %.100s", strerror(errno));
45218de8d7fSPeter Avalos #ifdef USE_PIPES
45318de8d7fSPeter Avalos close(pin[0]);
45418de8d7fSPeter Avalos close(pin[1]);
45518de8d7fSPeter Avalos close(pout[0]);
45618de8d7fSPeter Avalos close(pout[1]);
45718de8d7fSPeter Avalos close(perr[0]);
45818de8d7fSPeter Avalos close(perr[1]);
45918de8d7fSPeter Avalos #else
46018de8d7fSPeter Avalos close(inout[0]);
46118de8d7fSPeter Avalos close(inout[1]);
46218de8d7fSPeter Avalos close(err[0]);
46318de8d7fSPeter Avalos close(err[1]);
46418de8d7fSPeter Avalos #endif
46518de8d7fSPeter Avalos return -1;
46618de8d7fSPeter Avalos case 0:
46718de8d7fSPeter Avalos is_child = 1;
46818de8d7fSPeter Avalos
46918de8d7fSPeter Avalos /*
47018de8d7fSPeter Avalos * Create a new session and process group since the 4.4BSD
47118de8d7fSPeter Avalos * setlogin() affects the entire process group.
47218de8d7fSPeter Avalos */
4730cbfa66cSDaniel Fojt if (setsid() == -1)
47418de8d7fSPeter Avalos error("setsid failed: %.100s", strerror(errno));
47518de8d7fSPeter Avalos
47618de8d7fSPeter Avalos #ifdef USE_PIPES
47718de8d7fSPeter Avalos /*
47818de8d7fSPeter Avalos * Redirect stdin. We close the parent side of the socket
47918de8d7fSPeter Avalos * pair, and make the child side the standard input.
48018de8d7fSPeter Avalos */
48118de8d7fSPeter Avalos close(pin[1]);
4820cbfa66cSDaniel Fojt if (dup2(pin[0], 0) == -1)
48318de8d7fSPeter Avalos perror("dup2 stdin");
48418de8d7fSPeter Avalos close(pin[0]);
48518de8d7fSPeter Avalos
48618de8d7fSPeter Avalos /* Redirect stdout. */
48718de8d7fSPeter Avalos close(pout[0]);
4880cbfa66cSDaniel Fojt if (dup2(pout[1], 1) == -1)
48918de8d7fSPeter Avalos perror("dup2 stdout");
49018de8d7fSPeter Avalos close(pout[1]);
49118de8d7fSPeter Avalos
49218de8d7fSPeter Avalos /* Redirect stderr. */
49318de8d7fSPeter Avalos close(perr[0]);
4940cbfa66cSDaniel Fojt if (dup2(perr[1], 2) == -1)
49518de8d7fSPeter Avalos perror("dup2 stderr");
49618de8d7fSPeter Avalos close(perr[1]);
49718de8d7fSPeter Avalos #else
49818de8d7fSPeter Avalos /*
49918de8d7fSPeter Avalos * Redirect stdin, stdout, and stderr. Stdin and stdout will
50018de8d7fSPeter Avalos * use the same socket, as some programs (particularly rdist)
50118de8d7fSPeter Avalos * seem to depend on it.
50218de8d7fSPeter Avalos */
50318de8d7fSPeter Avalos close(inout[1]);
50418de8d7fSPeter Avalos close(err[1]);
5050cbfa66cSDaniel Fojt if (dup2(inout[0], 0) == -1) /* stdin */
50618de8d7fSPeter Avalos perror("dup2 stdin");
5070cbfa66cSDaniel Fojt if (dup2(inout[0], 1) == -1) /* stdout (same as stdin) */
50818de8d7fSPeter Avalos perror("dup2 stdout");
50918de8d7fSPeter Avalos close(inout[0]);
5100cbfa66cSDaniel Fojt if (dup2(err[0], 2) == -1) /* stderr */
51118de8d7fSPeter Avalos perror("dup2 stderr");
51218de8d7fSPeter Avalos close(err[0]);
51318de8d7fSPeter Avalos #endif
51418de8d7fSPeter Avalos
51518de8d7fSPeter Avalos /* Do processing for the child (exec command etc). */
5162c9c1408SMatthew Dillon do_child(ssh, s, command);
51718de8d7fSPeter Avalos /* NOTREACHED */
51818de8d7fSPeter Avalos default:
51918de8d7fSPeter Avalos break;
52018de8d7fSPeter Avalos }
52118de8d7fSPeter Avalos
52218de8d7fSPeter Avalos #ifdef HAVE_CYGWIN
52318de8d7fSPeter Avalos cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
52418de8d7fSPeter Avalos #endif
52518de8d7fSPeter Avalos
52618de8d7fSPeter Avalos s->pid = pid;
52718de8d7fSPeter Avalos /* Set interactive/non-interactive mode. */
528664f4763Szrj ssh_packet_set_interactive(ssh, s->display != NULL,
5299f304aafSPeter Avalos options.ip_qos_interactive, options.ip_qos_bulk);
53018de8d7fSPeter Avalos
53118de8d7fSPeter Avalos /*
53218de8d7fSPeter Avalos * Clear loginmsg, since it's the child's responsibility to display
53318de8d7fSPeter Avalos * it to the user, otherwise multiple sessions may accumulate
53418de8d7fSPeter Avalos * multiple copies of the login messages.
53518de8d7fSPeter Avalos */
536664f4763Szrj sshbuf_reset(loginmsg);
53718de8d7fSPeter Avalos
53818de8d7fSPeter Avalos #ifdef USE_PIPES
53918de8d7fSPeter Avalos /* We are the parent. Close the child sides of the pipes. */
54018de8d7fSPeter Avalos close(pin[0]);
54118de8d7fSPeter Avalos close(pout[1]);
54218de8d7fSPeter Avalos close(perr[1]);
54318de8d7fSPeter Avalos
5442c9c1408SMatthew Dillon session_set_fds(ssh, s, pin[1], pout[0], perr[0],
545856ea928SPeter Avalos s->is_subsystem, 0);
54618de8d7fSPeter Avalos #else
54718de8d7fSPeter Avalos /* We are the parent. Close the child sides of the socket pairs. */
54818de8d7fSPeter Avalos close(inout[0]);
54918de8d7fSPeter Avalos close(err[0]);
55018de8d7fSPeter Avalos
55118de8d7fSPeter Avalos /*
55218de8d7fSPeter Avalos * Enter the interactive session. Note: server_loop must be able to
55318de8d7fSPeter Avalos * handle the case that fdin and fdout are the same.
55418de8d7fSPeter Avalos */
555664f4763Szrj session_set_fds(ssh, s, inout[1], inout[1], err[1],
556856ea928SPeter Avalos s->is_subsystem, 0);
55718de8d7fSPeter Avalos #endif
55818de8d7fSPeter Avalos return 0;
55918de8d7fSPeter Avalos }
56018de8d7fSPeter Avalos
56118de8d7fSPeter Avalos /*
56218de8d7fSPeter Avalos * This is called to fork and execute a command when we have a tty. This
56318de8d7fSPeter Avalos * will call do_child from the child, and server_loop from the parent after
56418de8d7fSPeter Avalos * setting up file descriptors, controlling tty, updating wtmp, utmp,
56518de8d7fSPeter Avalos * lastlog, and other such operations.
56618de8d7fSPeter Avalos */
56718de8d7fSPeter Avalos int
do_exec_pty(struct ssh * ssh,Session * s,const char * command)5682c9c1408SMatthew Dillon do_exec_pty(struct ssh *ssh, Session *s, const char *command)
56918de8d7fSPeter Avalos {
57018de8d7fSPeter Avalos int fdout, ptyfd, ttyfd, ptymaster;
57118de8d7fSPeter Avalos pid_t pid;
57218de8d7fSPeter Avalos
57318de8d7fSPeter Avalos if (s == NULL)
57418de8d7fSPeter Avalos fatal("do_exec_pty: no session");
57518de8d7fSPeter Avalos ptyfd = s->ptyfd;
57618de8d7fSPeter Avalos ttyfd = s->ttyfd;
57718de8d7fSPeter Avalos
57818de8d7fSPeter Avalos /*
57918de8d7fSPeter Avalos * Create another descriptor of the pty master side for use as the
58018de8d7fSPeter Avalos * standard input. We could use the original descriptor, but this
58118de8d7fSPeter Avalos * simplifies code in server_loop. The descriptor is bidirectional.
58218de8d7fSPeter Avalos * Do this before forking (and cleanup in the child) so as to
58318de8d7fSPeter Avalos * detect and gracefully fail out-of-fd conditions.
58418de8d7fSPeter Avalos */
5850cbfa66cSDaniel Fojt if ((fdout = dup(ptyfd)) == -1) {
58650a69bb5SSascha Wildner error_f("dup #1: %s", strerror(errno));
58718de8d7fSPeter Avalos close(ttyfd);
58818de8d7fSPeter Avalos close(ptyfd);
58918de8d7fSPeter Avalos return -1;
59018de8d7fSPeter Avalos }
59118de8d7fSPeter Avalos /* we keep a reference to the pty master */
5920cbfa66cSDaniel Fojt if ((ptymaster = dup(ptyfd)) == -1) {
59350a69bb5SSascha Wildner error_f("dup #2: %s", strerror(errno));
59418de8d7fSPeter Avalos close(ttyfd);
59518de8d7fSPeter Avalos close(ptyfd);
59618de8d7fSPeter Avalos close(fdout);
59718de8d7fSPeter Avalos return -1;
59818de8d7fSPeter Avalos }
59918de8d7fSPeter Avalos
60018de8d7fSPeter Avalos /* Fork the child. */
60118de8d7fSPeter Avalos switch ((pid = fork())) {
60218de8d7fSPeter Avalos case -1:
60350a69bb5SSascha Wildner error_f("fork: %.100s", strerror(errno));
60418de8d7fSPeter Avalos close(fdout);
60518de8d7fSPeter Avalos close(ptymaster);
60618de8d7fSPeter Avalos close(ttyfd);
60718de8d7fSPeter Avalos close(ptyfd);
60818de8d7fSPeter Avalos return -1;
60918de8d7fSPeter Avalos case 0:
61018de8d7fSPeter Avalos is_child = 1;
61118de8d7fSPeter Avalos
61218de8d7fSPeter Avalos close(fdout);
61318de8d7fSPeter Avalos close(ptymaster);
61418de8d7fSPeter Avalos
61518de8d7fSPeter Avalos /* Close the master side of the pseudo tty. */
61618de8d7fSPeter Avalos close(ptyfd);
61718de8d7fSPeter Avalos
61818de8d7fSPeter Avalos /* Make the pseudo tty our controlling tty. */
61918de8d7fSPeter Avalos pty_make_controlling_tty(&ttyfd, s->tty);
62018de8d7fSPeter Avalos
62118de8d7fSPeter Avalos /* Redirect stdin/stdout/stderr from the pseudo tty. */
6220cbfa66cSDaniel Fojt if (dup2(ttyfd, 0) == -1)
62318de8d7fSPeter Avalos error("dup2 stdin: %s", strerror(errno));
6240cbfa66cSDaniel Fojt if (dup2(ttyfd, 1) == -1)
62518de8d7fSPeter Avalos error("dup2 stdout: %s", strerror(errno));
6260cbfa66cSDaniel Fojt if (dup2(ttyfd, 2) == -1)
62718de8d7fSPeter Avalos error("dup2 stderr: %s", strerror(errno));
62818de8d7fSPeter Avalos
62918de8d7fSPeter Avalos /* Close the extra descriptor for the pseudo tty. */
63018de8d7fSPeter Avalos close(ttyfd);
63118de8d7fSPeter Avalos
63218de8d7fSPeter Avalos /* record login, etc. similar to login(1) */
6332c9c1408SMatthew Dillon #ifndef HAVE_OSF_SIA
6342c9c1408SMatthew Dillon do_login(ssh, s, command);
63518de8d7fSPeter Avalos #endif
63618de8d7fSPeter Avalos /*
63718de8d7fSPeter Avalos * Do common processing for the child, such as execing
63818de8d7fSPeter Avalos * the command.
63918de8d7fSPeter Avalos */
6402c9c1408SMatthew Dillon do_child(ssh, s, command);
64118de8d7fSPeter Avalos /* NOTREACHED */
64218de8d7fSPeter Avalos default:
64318de8d7fSPeter Avalos break;
64418de8d7fSPeter Avalos }
64518de8d7fSPeter Avalos
64618de8d7fSPeter Avalos #ifdef HAVE_CYGWIN
64718de8d7fSPeter Avalos cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
64818de8d7fSPeter Avalos #endif
64918de8d7fSPeter Avalos
65018de8d7fSPeter Avalos s->pid = pid;
65118de8d7fSPeter Avalos
65218de8d7fSPeter Avalos /* Parent. Close the slave side of the pseudo tty. */
65318de8d7fSPeter Avalos close(ttyfd);
65418de8d7fSPeter Avalos
65518de8d7fSPeter Avalos /* Enter interactive session. */
65618de8d7fSPeter Avalos s->ptymaster = ptymaster;
657664f4763Szrj ssh_packet_set_interactive(ssh, 1,
6589f304aafSPeter Avalos options.ip_qos_interactive, options.ip_qos_bulk);
6592c9c1408SMatthew Dillon session_set_fds(ssh, s, ptyfd, fdout, -1, 1, 1);
66018de8d7fSPeter Avalos return 0;
66118de8d7fSPeter Avalos }
66218de8d7fSPeter Avalos
66318de8d7fSPeter Avalos /*
66418de8d7fSPeter Avalos * This is called to fork and execute a command. If another command is
66518de8d7fSPeter Avalos * to be forced, execute that instead.
66618de8d7fSPeter Avalos */
66718de8d7fSPeter Avalos int
do_exec(struct ssh * ssh,Session * s,const char * command)6682c9c1408SMatthew Dillon do_exec(struct ssh *ssh, Session *s, const char *command)
66918de8d7fSPeter Avalos {
67018de8d7fSPeter Avalos int ret;
671e9778795SPeter Avalos const char *forced = NULL, *tty = NULL;
672e9778795SPeter Avalos char session_type[1024];
67318de8d7fSPeter Avalos
67418de8d7fSPeter Avalos if (options.adm_forced_command) {
67518de8d7fSPeter Avalos original_command = command;
67618de8d7fSPeter Avalos command = options.adm_forced_command;
67736e94dc5SPeter Avalos forced = "(config)";
678664f4763Szrj } else if (auth_opts->force_command != NULL) {
67918de8d7fSPeter Avalos original_command = command;
680664f4763Szrj command = auth_opts->force_command;
68136e94dc5SPeter Avalos forced = "(key-option)";
68236e94dc5SPeter Avalos }
683664f4763Szrj s->forced = 0;
68436e94dc5SPeter Avalos if (forced != NULL) {
685664f4763Szrj s->forced = 1;
686856ea928SPeter Avalos if (IS_INTERNAL_SFTP(command)) {
687856ea928SPeter Avalos s->is_subsystem = s->is_subsystem ?
688856ea928SPeter Avalos SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR;
689856ea928SPeter Avalos } else if (s->is_subsystem)
69018de8d7fSPeter Avalos s->is_subsystem = SUBSYSTEM_EXT;
69136e94dc5SPeter Avalos snprintf(session_type, sizeof(session_type),
69236e94dc5SPeter Avalos "forced-command %s '%.900s'", forced, command);
69336e94dc5SPeter Avalos } else if (s->is_subsystem) {
69436e94dc5SPeter Avalos snprintf(session_type, sizeof(session_type),
69536e94dc5SPeter Avalos "subsystem '%.900s'", s->subsys);
69636e94dc5SPeter Avalos } else if (command == NULL) {
69736e94dc5SPeter Avalos snprintf(session_type, sizeof(session_type), "shell");
69836e94dc5SPeter Avalos } else {
69936e94dc5SPeter Avalos /* NB. we don't log unforced commands to preserve privacy */
70036e94dc5SPeter Avalos snprintf(session_type, sizeof(session_type), "command");
70118de8d7fSPeter Avalos }
70218de8d7fSPeter Avalos
70336e94dc5SPeter Avalos if (s->ttyfd != -1) {
70436e94dc5SPeter Avalos tty = s->tty;
70536e94dc5SPeter Avalos if (strncmp(tty, "/dev/", 5) == 0)
70636e94dc5SPeter Avalos tty += 5;
70736e94dc5SPeter Avalos }
70836e94dc5SPeter Avalos
709e9778795SPeter Avalos verbose("Starting session: %s%s%s for %s from %.200s port %d id %d",
71036e94dc5SPeter Avalos session_type,
71136e94dc5SPeter Avalos tty == NULL ? "" : " on ",
71236e94dc5SPeter Avalos tty == NULL ? "" : tty,
71336e94dc5SPeter Avalos s->pw->pw_name,
714e9778795SPeter Avalos ssh_remote_ipaddr(ssh),
715e9778795SPeter Avalos ssh_remote_port(ssh),
716e9778795SPeter Avalos s->self);
71736e94dc5SPeter Avalos
71818de8d7fSPeter Avalos #ifdef SSH_AUDIT_EVENTS
71918de8d7fSPeter Avalos if (command != NULL)
720*ba1276acSMatthew Dillon mm_audit_run_command(command);
72118de8d7fSPeter Avalos else if (s->ttyfd == -1) {
72218de8d7fSPeter Avalos char *shell = s->pw->pw_shell;
72318de8d7fSPeter Avalos
72418de8d7fSPeter Avalos if (shell[0] == '\0') /* empty shell means /bin/sh */
72518de8d7fSPeter Avalos shell =_PATH_BSHELL;
726*ba1276acSMatthew Dillon mm_audit_run_command(shell);
72718de8d7fSPeter Avalos }
72818de8d7fSPeter Avalos #endif
72918de8d7fSPeter Avalos if (s->ttyfd != -1)
7302c9c1408SMatthew Dillon ret = do_exec_pty(ssh, s, command);
73118de8d7fSPeter Avalos else
7322c9c1408SMatthew Dillon ret = do_exec_no_pty(ssh, s, command);
73318de8d7fSPeter Avalos
73418de8d7fSPeter Avalos original_command = NULL;
73518de8d7fSPeter Avalos
73618de8d7fSPeter Avalos /*
73718de8d7fSPeter Avalos * Clear loginmsg: it's the child's responsibility to display
73818de8d7fSPeter Avalos * it to the user, otherwise multiple sessions may accumulate
73918de8d7fSPeter Avalos * multiple copies of the login messages.
74018de8d7fSPeter Avalos */
741664f4763Szrj sshbuf_reset(loginmsg);
74218de8d7fSPeter Avalos
74318de8d7fSPeter Avalos return ret;
74418de8d7fSPeter Avalos }
74518de8d7fSPeter Avalos
74618de8d7fSPeter Avalos /* administrative, login(1)-like work */
74718de8d7fSPeter Avalos void
do_login(struct ssh * ssh,Session * s,const char * command)7482c9c1408SMatthew Dillon do_login(struct ssh *ssh, Session *s, const char *command)
74918de8d7fSPeter Avalos {
75018de8d7fSPeter Avalos socklen_t fromlen;
75118de8d7fSPeter Avalos struct sockaddr_storage from;
75218de8d7fSPeter Avalos
75318de8d7fSPeter Avalos /*
75418de8d7fSPeter Avalos * Get IP address of client. If the connection is not a socket, let
75518de8d7fSPeter Avalos * the address be 0.0.0.0.
75618de8d7fSPeter Avalos */
75718de8d7fSPeter Avalos memset(&from, 0, sizeof(from));
75818de8d7fSPeter Avalos fromlen = sizeof(from);
759664f4763Szrj if (ssh_packet_connection_is_on_socket(ssh)) {
760664f4763Szrj if (getpeername(ssh_packet_get_connection_in(ssh),
7610cbfa66cSDaniel Fojt (struct sockaddr *)&from, &fromlen) == -1) {
76218de8d7fSPeter Avalos debug("getpeername: %.100s", strerror(errno));
76318de8d7fSPeter Avalos cleanup_exit(255);
76418de8d7fSPeter Avalos }
76518de8d7fSPeter Avalos }
76618de8d7fSPeter Avalos
76718de8d7fSPeter Avalos if (check_quietlogin(s, command))
76818de8d7fSPeter Avalos return;
76918de8d7fSPeter Avalos
77018de8d7fSPeter Avalos display_loginmsg();
77118de8d7fSPeter Avalos
77218de8d7fSPeter Avalos do_motd();
77318de8d7fSPeter Avalos }
77418de8d7fSPeter Avalos
77518de8d7fSPeter Avalos /*
77618de8d7fSPeter Avalos * Display the message of the day.
77718de8d7fSPeter Avalos */
77818de8d7fSPeter Avalos void
do_motd(void)77918de8d7fSPeter Avalos do_motd(void)
78018de8d7fSPeter Avalos {
78118de8d7fSPeter Avalos FILE *f;
78218de8d7fSPeter Avalos char buf[256];
78318de8d7fSPeter Avalos
78418de8d7fSPeter Avalos if (options.print_motd) {
78518de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
78618de8d7fSPeter Avalos f = fopen(login_getcapstr(lc, "welcome", "/etc/motd",
78718de8d7fSPeter Avalos "/etc/motd"), "r");
78818de8d7fSPeter Avalos #else
78918de8d7fSPeter Avalos f = fopen("/etc/motd", "r");
79018de8d7fSPeter Avalos #endif
79118de8d7fSPeter Avalos if (f) {
79218de8d7fSPeter Avalos while (fgets(buf, sizeof(buf), f))
79318de8d7fSPeter Avalos fputs(buf, stdout);
79418de8d7fSPeter Avalos fclose(f);
79518de8d7fSPeter Avalos }
79618de8d7fSPeter Avalos }
79718de8d7fSPeter Avalos }
79818de8d7fSPeter Avalos
79918de8d7fSPeter Avalos
80018de8d7fSPeter Avalos /*
80118de8d7fSPeter Avalos * Check for quiet login, either .hushlogin or command given.
80218de8d7fSPeter Avalos */
80318de8d7fSPeter Avalos int
check_quietlogin(Session * s,const char * command)80418de8d7fSPeter Avalos check_quietlogin(Session *s, const char *command)
80518de8d7fSPeter Avalos {
80618de8d7fSPeter Avalos char buf[256];
80718de8d7fSPeter Avalos struct passwd *pw = s->pw;
80818de8d7fSPeter Avalos struct stat st;
80918de8d7fSPeter Avalos
81018de8d7fSPeter Avalos /* Return 1 if .hushlogin exists or a command given. */
81118de8d7fSPeter Avalos if (command != NULL)
81218de8d7fSPeter Avalos return 1;
81318de8d7fSPeter Avalos snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir);
81418de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
81518de8d7fSPeter Avalos if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0)
81618de8d7fSPeter Avalos return 1;
81718de8d7fSPeter Avalos #else
81818de8d7fSPeter Avalos if (stat(buf, &st) >= 0)
81918de8d7fSPeter Avalos return 1;
82018de8d7fSPeter Avalos #endif
82118de8d7fSPeter Avalos return 0;
82218de8d7fSPeter Avalos }
82318de8d7fSPeter Avalos
82418de8d7fSPeter Avalos /*
82518de8d7fSPeter Avalos * Reads environment variables from the given file and adds/overrides them
82618de8d7fSPeter Avalos * into the environment. If the file does not exist, this does nothing.
82718de8d7fSPeter Avalos * Otherwise, it must consist of empty lines, comments (line starts with '#')
82818de8d7fSPeter Avalos * and assignments of the form name=value. No other forms are allowed.
82950a69bb5SSascha Wildner * If allowlist is not NULL, then it is interpreted as a pattern list and
830664f4763Szrj * only variable names that match it will be accepted.
83118de8d7fSPeter Avalos */
83218de8d7fSPeter Avalos static void
read_environment_file(char *** env,u_int * envsize,const char * filename,const char * allowlist)83318de8d7fSPeter Avalos read_environment_file(char ***env, u_int *envsize,
83450a69bb5SSascha Wildner const char *filename, const char *allowlist)
83518de8d7fSPeter Avalos {
83618de8d7fSPeter Avalos FILE *f;
837664f4763Szrj char *line = NULL, *cp, *value;
838664f4763Szrj size_t linesize = 0;
83918de8d7fSPeter Avalos u_int lineno = 0;
84018de8d7fSPeter Avalos
84118de8d7fSPeter Avalos f = fopen(filename, "r");
84218de8d7fSPeter Avalos if (!f)
84318de8d7fSPeter Avalos return;
84418de8d7fSPeter Avalos
845664f4763Szrj while (getline(&line, &linesize, f) != -1) {
84618de8d7fSPeter Avalos if (++lineno > 1000)
84718de8d7fSPeter Avalos fatal("Too many lines in environment file %s", filename);
848664f4763Szrj for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
84918de8d7fSPeter Avalos ;
85018de8d7fSPeter Avalos if (!*cp || *cp == '#' || *cp == '\n')
85118de8d7fSPeter Avalos continue;
85218de8d7fSPeter Avalos
85318de8d7fSPeter Avalos cp[strcspn(cp, "\n")] = '\0';
85418de8d7fSPeter Avalos
85518de8d7fSPeter Avalos value = strchr(cp, '=');
85618de8d7fSPeter Avalos if (value == NULL) {
85718de8d7fSPeter Avalos fprintf(stderr, "Bad line %u in %.100s\n", lineno,
85818de8d7fSPeter Avalos filename);
85918de8d7fSPeter Avalos continue;
86018de8d7fSPeter Avalos }
86118de8d7fSPeter Avalos /*
86218de8d7fSPeter Avalos * Replace the equals sign by nul, and advance value to
86318de8d7fSPeter Avalos * the value string.
86418de8d7fSPeter Avalos */
86518de8d7fSPeter Avalos *value = '\0';
86618de8d7fSPeter Avalos value++;
86750a69bb5SSascha Wildner if (allowlist != NULL &&
86850a69bb5SSascha Wildner match_pattern_list(cp, allowlist, 0) != 1)
869664f4763Szrj continue;
87018de8d7fSPeter Avalos child_set_env(env, envsize, cp, value);
87118de8d7fSPeter Avalos }
872664f4763Szrj free(line);
87318de8d7fSPeter Avalos fclose(f);
87418de8d7fSPeter Avalos }
87518de8d7fSPeter Avalos
87618de8d7fSPeter Avalos #ifdef HAVE_ETC_DEFAULT_LOGIN
87718de8d7fSPeter Avalos /*
87818de8d7fSPeter Avalos * Return named variable from specified environment, or NULL if not present.
87918de8d7fSPeter Avalos */
88018de8d7fSPeter Avalos static char *
child_get_env(char ** env,const char * name)88118de8d7fSPeter Avalos child_get_env(char **env, const char *name)
88218de8d7fSPeter Avalos {
88318de8d7fSPeter Avalos int i;
88418de8d7fSPeter Avalos size_t len;
88518de8d7fSPeter Avalos
88618de8d7fSPeter Avalos len = strlen(name);
88718de8d7fSPeter Avalos for (i=0; env[i] != NULL; i++)
88818de8d7fSPeter Avalos if (strncmp(name, env[i], len) == 0 && env[i][len] == '=')
88918de8d7fSPeter Avalos return(env[i] + len + 1);
89018de8d7fSPeter Avalos return NULL;
89118de8d7fSPeter Avalos }
89218de8d7fSPeter Avalos
89318de8d7fSPeter Avalos /*
89418de8d7fSPeter Avalos * Read /etc/default/login.
89518de8d7fSPeter Avalos * We pick up the PATH (or SUPATH for root) and UMASK.
89618de8d7fSPeter Avalos */
89718de8d7fSPeter Avalos static void
read_etc_default_login(char *** env,u_int * envsize,uid_t uid)89818de8d7fSPeter Avalos read_etc_default_login(char ***env, u_int *envsize, uid_t uid)
89918de8d7fSPeter Avalos {
90018de8d7fSPeter Avalos char **tmpenv = NULL, *var;
90118de8d7fSPeter Avalos u_int i, tmpenvsize = 0;
90218de8d7fSPeter Avalos u_long mask;
90318de8d7fSPeter Avalos
90418de8d7fSPeter Avalos /*
90518de8d7fSPeter Avalos * We don't want to copy the whole file to the child's environment,
90618de8d7fSPeter Avalos * so we use a temporary environment and copy the variables we're
90718de8d7fSPeter Avalos * interested in.
90818de8d7fSPeter Avalos */
909664f4763Szrj read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login",
91050a69bb5SSascha Wildner options.permit_user_env_allowlist);
91118de8d7fSPeter Avalos
91218de8d7fSPeter Avalos if (tmpenv == NULL)
91318de8d7fSPeter Avalos return;
91418de8d7fSPeter Avalos
91518de8d7fSPeter Avalos if (uid == 0)
91618de8d7fSPeter Avalos var = child_get_env(tmpenv, "SUPATH");
91718de8d7fSPeter Avalos else
91818de8d7fSPeter Avalos var = child_get_env(tmpenv, "PATH");
91918de8d7fSPeter Avalos if (var != NULL)
92018de8d7fSPeter Avalos child_set_env(env, envsize, "PATH", var);
92118de8d7fSPeter Avalos
92218de8d7fSPeter Avalos if ((var = child_get_env(tmpenv, "UMASK")) != NULL)
92318de8d7fSPeter Avalos if (sscanf(var, "%5lo", &mask) == 1)
92418de8d7fSPeter Avalos umask((mode_t)mask);
92518de8d7fSPeter Avalos
92618de8d7fSPeter Avalos for (i = 0; tmpenv[i] != NULL; i++)
92736e94dc5SPeter Avalos free(tmpenv[i]);
92836e94dc5SPeter Avalos free(tmpenv);
92918de8d7fSPeter Avalos }
93018de8d7fSPeter Avalos #endif /* HAVE_ETC_DEFAULT_LOGIN */
93118de8d7fSPeter Avalos
9320cbfa66cSDaniel Fojt #if defined(USE_PAM) || defined(HAVE_CYGWIN)
93372be967aSzrj static void
copy_environment_denylist(char ** source,char *** env,u_int * envsize,const char * denylist)93450a69bb5SSascha Wildner copy_environment_denylist(char **source, char ***env, u_int *envsize,
93550a69bb5SSascha Wildner const char *denylist)
93618de8d7fSPeter Avalos {
93718de8d7fSPeter Avalos char *var_name, *var_val;
93818de8d7fSPeter Avalos int i;
93918de8d7fSPeter Avalos
94018de8d7fSPeter Avalos if (source == NULL)
94118de8d7fSPeter Avalos return;
94218de8d7fSPeter Avalos
94318de8d7fSPeter Avalos for(i = 0; source[i] != NULL; i++) {
94418de8d7fSPeter Avalos var_name = xstrdup(source[i]);
94518de8d7fSPeter Avalos if ((var_val = strstr(var_name, "=")) == NULL) {
94636e94dc5SPeter Avalos free(var_name);
94718de8d7fSPeter Avalos continue;
94818de8d7fSPeter Avalos }
94918de8d7fSPeter Avalos *var_val++ = '\0';
95018de8d7fSPeter Avalos
95150a69bb5SSascha Wildner if (denylist == NULL ||
95250a69bb5SSascha Wildner match_pattern_list(var_name, denylist, 0) != 1) {
95318de8d7fSPeter Avalos debug3("Copy environment: %s=%s", var_name, var_val);
95418de8d7fSPeter Avalos child_set_env(env, envsize, var_name, var_val);
9552c9c1408SMatthew Dillon }
95618de8d7fSPeter Avalos
95736e94dc5SPeter Avalos free(var_name);
95818de8d7fSPeter Avalos }
95918de8d7fSPeter Avalos }
9600cbfa66cSDaniel Fojt #endif /* defined(USE_PAM) || defined(HAVE_CYGWIN) */
96118de8d7fSPeter Avalos
962ac187a81SSascha Wildner #ifdef HAVE_CYGWIN
963ac187a81SSascha Wildner static void
copy_environment(char ** source,char *** env,u_int * envsize)9642c9c1408SMatthew Dillon copy_environment(char **source, char ***env, u_int *envsize)
96518de8d7fSPeter Avalos {
96650a69bb5SSascha Wildner copy_environment_denylist(source, env, envsize, NULL);
9672c9c1408SMatthew Dillon }
968ac187a81SSascha Wildner #endif
969ac187a81SSascha Wildner
9702c9c1408SMatthew Dillon static char **
do_setup_env(struct ssh * ssh,Session * s,const char * shell)9712c9c1408SMatthew Dillon do_setup_env(struct ssh *ssh, Session *s, const char *shell)
9722c9c1408SMatthew Dillon {
97318de8d7fSPeter Avalos char buf[256];
974664f4763Szrj size_t n;
97518de8d7fSPeter Avalos u_int i, envsize;
976664f4763Szrj char *ocp, *cp, *value, **env, *laddr;
97718de8d7fSPeter Avalos struct passwd *pw = s->pw;
97840c002afSPeter Avalos #if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN)
97918de8d7fSPeter Avalos char *path = NULL;
98018de8d7fSPeter Avalos #endif
98118de8d7fSPeter Avalos
98218de8d7fSPeter Avalos /* Initialize the environment. */
98318de8d7fSPeter Avalos envsize = 100;
98418de8d7fSPeter Avalos env = xcalloc(envsize, sizeof(char *));
98518de8d7fSPeter Avalos env[0] = NULL;
98618de8d7fSPeter Avalos
98718de8d7fSPeter Avalos #ifdef HAVE_CYGWIN
98818de8d7fSPeter Avalos /*
98918de8d7fSPeter Avalos * The Windows environment contains some setting which are
99018de8d7fSPeter Avalos * important for a running system. They must not be dropped.
99118de8d7fSPeter Avalos */
99218de8d7fSPeter Avalos {
99318de8d7fSPeter Avalos char **p;
99418de8d7fSPeter Avalos
99518de8d7fSPeter Avalos p = fetch_windows_environment();
99618de8d7fSPeter Avalos copy_environment(p, &env, &envsize);
99718de8d7fSPeter Avalos free_windows_environment(p);
99818de8d7fSPeter Avalos }
99918de8d7fSPeter Avalos #endif
100018de8d7fSPeter Avalos
100118de8d7fSPeter Avalos #ifdef GSSAPI
100218de8d7fSPeter Avalos /* Allow any GSSAPI methods that we've used to alter
10030cbfa66cSDaniel Fojt * the child's environment as they see fit
100418de8d7fSPeter Avalos */
100518de8d7fSPeter Avalos ssh_gssapi_do_child(&env, &envsize);
100618de8d7fSPeter Avalos #endif
100718de8d7fSPeter Avalos
100818de8d7fSPeter Avalos /* Set basic environment. */
100918de8d7fSPeter Avalos for (i = 0; i < s->num_env; i++)
10102c9c1408SMatthew Dillon child_set_env(&env, &envsize, s->env[i].name, s->env[i].val);
101118de8d7fSPeter Avalos
101218de8d7fSPeter Avalos child_set_env(&env, &envsize, "USER", pw->pw_name);
101318de8d7fSPeter Avalos child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
101418de8d7fSPeter Avalos #ifdef _AIX
101518de8d7fSPeter Avalos child_set_env(&env, &envsize, "LOGIN", pw->pw_name);
101618de8d7fSPeter Avalos #endif
101718de8d7fSPeter Avalos child_set_env(&env, &envsize, "HOME", pw->pw_dir);
101818de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
101918de8d7fSPeter Avalos if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH) < 0)
102018de8d7fSPeter Avalos child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
102118de8d7fSPeter Avalos else
102218de8d7fSPeter Avalos child_set_env(&env, &envsize, "PATH", getenv("PATH"));
102318de8d7fSPeter Avalos #else /* HAVE_LOGIN_CAP */
102418de8d7fSPeter Avalos # ifndef HAVE_CYGWIN
102518de8d7fSPeter Avalos /*
102618de8d7fSPeter Avalos * There's no standard path on Windows. The path contains
102718de8d7fSPeter Avalos * important components pointing to the system directories,
102818de8d7fSPeter Avalos * needed for loading shared libraries. So the path better
102918de8d7fSPeter Avalos * remains intact here.
103018de8d7fSPeter Avalos */
103118de8d7fSPeter Avalos # ifdef HAVE_ETC_DEFAULT_LOGIN
103218de8d7fSPeter Avalos read_etc_default_login(&env, &envsize, pw->pw_uid);
103318de8d7fSPeter Avalos path = child_get_env(env, "PATH");
103418de8d7fSPeter Avalos # endif /* HAVE_ETC_DEFAULT_LOGIN */
103518de8d7fSPeter Avalos if (path == NULL || *path == '\0') {
103618de8d7fSPeter Avalos child_set_env(&env, &envsize, "PATH",
10372c9c1408SMatthew Dillon s->pw->pw_uid == 0 ? SUPERUSER_PATH : _PATH_STDPATH);
103818de8d7fSPeter Avalos }
103918de8d7fSPeter Avalos # endif /* HAVE_CYGWIN */
104018de8d7fSPeter Avalos #endif /* HAVE_LOGIN_CAP */
104118de8d7fSPeter Avalos
1042664f4763Szrj if (!options.use_pam) {
1043664f4763Szrj snprintf(buf, sizeof buf, "%.200s/%.50s",
1044664f4763Szrj _PATH_MAILDIR, pw->pw_name);
104518de8d7fSPeter Avalos child_set_env(&env, &envsize, "MAIL", buf);
1046664f4763Szrj }
104718de8d7fSPeter Avalos
104818de8d7fSPeter Avalos /* Normal systems set SHELL by default. */
104918de8d7fSPeter Avalos child_set_env(&env, &envsize, "SHELL", shell);
10502c9c1408SMatthew Dillon
105118de8d7fSPeter Avalos if (getenv("TZ"))
105218de8d7fSPeter Avalos child_set_env(&env, &envsize, "TZ", getenv("TZ"));
105318de8d7fSPeter Avalos if (s->term)
105418de8d7fSPeter Avalos child_set_env(&env, &envsize, "TERM", s->term);
105518de8d7fSPeter Avalos if (s->display)
105618de8d7fSPeter Avalos child_set_env(&env, &envsize, "DISPLAY", s->display);
105718de8d7fSPeter Avalos
105818de8d7fSPeter Avalos /*
105918de8d7fSPeter Avalos * Since we clear KRB5CCNAME at startup, if it's set now then it
106018de8d7fSPeter Avalos * must have been set by a native authentication method (eg AIX or
106118de8d7fSPeter Avalos * SIA), so copy it to the child.
106218de8d7fSPeter Avalos */
106318de8d7fSPeter Avalos {
106418de8d7fSPeter Avalos char *cp;
106518de8d7fSPeter Avalos
106618de8d7fSPeter Avalos if ((cp = getenv("KRB5CCNAME")) != NULL)
106718de8d7fSPeter Avalos child_set_env(&env, &envsize, "KRB5CCNAME", cp);
106818de8d7fSPeter Avalos }
106918de8d7fSPeter Avalos
107018de8d7fSPeter Avalos #ifdef _AIX
107118de8d7fSPeter Avalos {
107218de8d7fSPeter Avalos char *cp;
107318de8d7fSPeter Avalos
107418de8d7fSPeter Avalos if ((cp = getenv("AUTHSTATE")) != NULL)
107518de8d7fSPeter Avalos child_set_env(&env, &envsize, "AUTHSTATE", cp);
1076664f4763Szrj read_environment_file(&env, &envsize, "/etc/environment",
107750a69bb5SSascha Wildner options.permit_user_env_allowlist);
107818de8d7fSPeter Avalos }
107918de8d7fSPeter Avalos #endif
108018de8d7fSPeter Avalos #ifdef KRB5
108118de8d7fSPeter Avalos if (s->authctxt->krb5_ccname)
108218de8d7fSPeter Avalos child_set_env(&env, &envsize, "KRB5CCNAME",
108318de8d7fSPeter Avalos s->authctxt->krb5_ccname);
108418de8d7fSPeter Avalos #endif
1085664f4763Szrj if (auth_sock_name != NULL)
1086664f4763Szrj child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
1087664f4763Szrj auth_sock_name);
1088664f4763Szrj
1089664f4763Szrj
1090664f4763Szrj /* Set custom environment options from pubkey authentication. */
1091664f4763Szrj if (options.permit_user_env) {
1092664f4763Szrj for (n = 0 ; n < auth_opts->nenv; n++) {
1093664f4763Szrj ocp = xstrdup(auth_opts->env[n]);
1094664f4763Szrj cp = strchr(ocp, '=');
109550a69bb5SSascha Wildner if (cp != NULL) {
1096664f4763Szrj *cp = '\0';
109750a69bb5SSascha Wildner /* Apply PermitUserEnvironment allowlist */
109850a69bb5SSascha Wildner if (options.permit_user_env_allowlist == NULL ||
1099664f4763Szrj match_pattern_list(ocp,
110050a69bb5SSascha Wildner options.permit_user_env_allowlist, 0) == 1)
1101664f4763Szrj child_set_env(&env, &envsize,
1102664f4763Szrj ocp, cp + 1);
1103664f4763Szrj }
1104664f4763Szrj free(ocp);
1105664f4763Szrj }
1106664f4763Szrj }
1107664f4763Szrj
1108664f4763Szrj /* read $HOME/.ssh/environment. */
1109664f4763Szrj if (options.permit_user_env) {
111050a69bb5SSascha Wildner snprintf(buf, sizeof buf, "%.200s/%s/environment",
111150a69bb5SSascha Wildner pw->pw_dir, _PATH_SSH_USER_DIR);
1112664f4763Szrj read_environment_file(&env, &envsize, buf,
111350a69bb5SSascha Wildner options.permit_user_env_allowlist);
1114664f4763Szrj }
1115664f4763Szrj
111618de8d7fSPeter Avalos #ifdef USE_PAM
111718de8d7fSPeter Avalos /*
111818de8d7fSPeter Avalos * Pull in any environment variables that may have
111918de8d7fSPeter Avalos * been set by PAM.
112018de8d7fSPeter Avalos */
11212c9c1408SMatthew Dillon if (options.use_pam) {
112218de8d7fSPeter Avalos char **p;
112318de8d7fSPeter Avalos
11242c9c1408SMatthew Dillon /*
1125664f4763Szrj * Don't allow PAM-internal env vars to leak
1126664f4763Szrj * back into the session environment.
11272c9c1408SMatthew Dillon */
112850a69bb5SSascha Wildner #define PAM_ENV_DENYLIST "SSH_AUTH_INFO*,SSH_CONNECTION*"
112918de8d7fSPeter Avalos p = fetch_pam_child_environment();
113050a69bb5SSascha Wildner copy_environment_denylist(p, &env, &envsize,
113150a69bb5SSascha Wildner PAM_ENV_DENYLIST);
113218de8d7fSPeter Avalos free_pam_environment(p);
113318de8d7fSPeter Avalos
113418de8d7fSPeter Avalos p = fetch_pam_environment();
113550a69bb5SSascha Wildner copy_environment_denylist(p, &env, &envsize,
113650a69bb5SSascha Wildner PAM_ENV_DENYLIST);
113718de8d7fSPeter Avalos free_pam_environment(p);
113818de8d7fSPeter Avalos }
113918de8d7fSPeter Avalos #endif /* USE_PAM */
114018de8d7fSPeter Avalos
1141664f4763Szrj /* Environment specified by admin */
1142664f4763Szrj for (i = 0; i < options.num_setenv; i++) {
1143664f4763Szrj cp = xstrdup(options.setenv[i]);
1144664f4763Szrj if ((value = strchr(cp, '=')) == NULL) {
1145664f4763Szrj /* shouldn't happen; vars are checked in servconf.c */
1146664f4763Szrj fatal("Invalid config SetEnv: %s", options.setenv[i]);
114718de8d7fSPeter Avalos }
1148664f4763Szrj *value++ = '\0';
1149664f4763Szrj child_set_env(&env, &envsize, cp, value);
1150*ba1276acSMatthew Dillon free(cp);
1151664f4763Szrj }
1152664f4763Szrj
1153664f4763Szrj /* SSH_CLIENT deprecated */
1154664f4763Szrj snprintf(buf, sizeof buf, "%.50s %d %d",
1155664f4763Szrj ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1156664f4763Szrj ssh_local_port(ssh));
1157664f4763Szrj child_set_env(&env, &envsize, "SSH_CLIENT", buf);
1158664f4763Szrj
1159664f4763Szrj laddr = get_local_ipaddr(ssh_packet_get_connection_in(ssh));
1160664f4763Szrj snprintf(buf, sizeof buf, "%.50s %d %.50s %d",
1161664f4763Szrj ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1162664f4763Szrj laddr, ssh_local_port(ssh));
1163664f4763Szrj free(laddr);
1164664f4763Szrj child_set_env(&env, &envsize, "SSH_CONNECTION", buf);
1165664f4763Szrj
1166664f4763Szrj if (tun_fwd_ifnames != NULL)
1167664f4763Szrj child_set_env(&env, &envsize, "SSH_TUNNEL", tun_fwd_ifnames);
1168664f4763Szrj if (auth_info_file != NULL)
1169664f4763Szrj child_set_env(&env, &envsize, "SSH_USER_AUTH", auth_info_file);
1170664f4763Szrj if (s->ttyfd != -1)
1171664f4763Szrj child_set_env(&env, &envsize, "SSH_TTY", s->tty);
1172664f4763Szrj if (original_command)
1173664f4763Szrj child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND",
1174664f4763Szrj original_command);
1175664f4763Szrj
117618de8d7fSPeter Avalos if (debug_flag) {
117718de8d7fSPeter Avalos /* dump the environment */
117818de8d7fSPeter Avalos fprintf(stderr, "Environment:\n");
117918de8d7fSPeter Avalos for (i = 0; env[i]; i++)
118018de8d7fSPeter Avalos fprintf(stderr, " %.200s\n", env[i]);
118118de8d7fSPeter Avalos }
118218de8d7fSPeter Avalos return env;
118318de8d7fSPeter Avalos }
118418de8d7fSPeter Avalos
118518de8d7fSPeter Avalos /*
118618de8d7fSPeter Avalos * Run $HOME/.ssh/rc, /etc/ssh/sshrc, or xauth (whichever is found
118718de8d7fSPeter Avalos * first in this order).
118818de8d7fSPeter Avalos */
118918de8d7fSPeter Avalos static void
do_rc_files(struct ssh * ssh,Session * s,const char * shell)1190664f4763Szrj do_rc_files(struct ssh *ssh, Session *s, const char *shell)
119118de8d7fSPeter Avalos {
119218de8d7fSPeter Avalos FILE *f = NULL;
119350a69bb5SSascha Wildner char *cmd = NULL, *user_rc = NULL;
119418de8d7fSPeter Avalos int do_xauth;
119518de8d7fSPeter Avalos struct stat st;
119618de8d7fSPeter Avalos
119718de8d7fSPeter Avalos do_xauth =
119818de8d7fSPeter Avalos s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL;
119950a69bb5SSascha Wildner xasprintf(&user_rc, "%s/%s", s->pw->pw_dir, _PATH_SSH_USER_RC);
120018de8d7fSPeter Avalos
120118de8d7fSPeter Avalos /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */
120218de8d7fSPeter Avalos if (!s->is_subsystem && options.adm_forced_command == NULL &&
1203664f4763Szrj auth_opts->permit_user_rc && options.permit_user_rc &&
120450a69bb5SSascha Wildner stat(user_rc, &st) >= 0) {
120550a69bb5SSascha Wildner if (xasprintf(&cmd, "%s -c '%s %s'", shell, _PATH_BSHELL,
120650a69bb5SSascha Wildner user_rc) == -1)
120750a69bb5SSascha Wildner fatal_f("xasprintf: %s", strerror(errno));
120818de8d7fSPeter Avalos if (debug_flag)
120918de8d7fSPeter Avalos fprintf(stderr, "Running %s\n", cmd);
121018de8d7fSPeter Avalos f = popen(cmd, "w");
121118de8d7fSPeter Avalos if (f) {
121218de8d7fSPeter Avalos if (do_xauth)
121318de8d7fSPeter Avalos fprintf(f, "%s %s\n", s->auth_proto,
121418de8d7fSPeter Avalos s->auth_data);
121518de8d7fSPeter Avalos pclose(f);
121618de8d7fSPeter Avalos } else
121718de8d7fSPeter Avalos fprintf(stderr, "Could not run %s\n",
121850a69bb5SSascha Wildner user_rc);
121918de8d7fSPeter Avalos } else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) {
122018de8d7fSPeter Avalos if (debug_flag)
122118de8d7fSPeter Avalos fprintf(stderr, "Running %s %s\n", _PATH_BSHELL,
122218de8d7fSPeter Avalos _PATH_SSH_SYSTEM_RC);
122318de8d7fSPeter Avalos f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w");
122418de8d7fSPeter Avalos if (f) {
122518de8d7fSPeter Avalos if (do_xauth)
122618de8d7fSPeter Avalos fprintf(f, "%s %s\n", s->auth_proto,
122718de8d7fSPeter Avalos s->auth_data);
122818de8d7fSPeter Avalos pclose(f);
122918de8d7fSPeter Avalos } else
123018de8d7fSPeter Avalos fprintf(stderr, "Could not run %s\n",
123118de8d7fSPeter Avalos _PATH_SSH_SYSTEM_RC);
123218de8d7fSPeter Avalos } else if (do_xauth && options.xauth_location != NULL) {
123318de8d7fSPeter Avalos /* Add authority data to .Xauthority if appropriate. */
123418de8d7fSPeter Avalos if (debug_flag) {
123518de8d7fSPeter Avalos fprintf(stderr,
123618de8d7fSPeter Avalos "Running %.500s remove %.100s\n",
123718de8d7fSPeter Avalos options.xauth_location, s->auth_display);
123818de8d7fSPeter Avalos fprintf(stderr,
123918de8d7fSPeter Avalos "%.500s add %.100s %.100s %.100s\n",
124018de8d7fSPeter Avalos options.xauth_location, s->auth_display,
124118de8d7fSPeter Avalos s->auth_proto, s->auth_data);
124218de8d7fSPeter Avalos }
124350a69bb5SSascha Wildner if (xasprintf(&cmd, "%s -q -", options.xauth_location) == -1)
124450a69bb5SSascha Wildner fatal_f("xasprintf: %s", strerror(errno));
124518de8d7fSPeter Avalos f = popen(cmd, "w");
124618de8d7fSPeter Avalos if (f) {
124718de8d7fSPeter Avalos fprintf(f, "remove %s\n",
124818de8d7fSPeter Avalos s->auth_display);
124918de8d7fSPeter Avalos fprintf(f, "add %s %s %s\n",
125018de8d7fSPeter Avalos s->auth_display, s->auth_proto,
125118de8d7fSPeter Avalos s->auth_data);
125218de8d7fSPeter Avalos pclose(f);
125318de8d7fSPeter Avalos } else {
125418de8d7fSPeter Avalos fprintf(stderr, "Could not run %s\n",
125518de8d7fSPeter Avalos cmd);
125618de8d7fSPeter Avalos }
125718de8d7fSPeter Avalos }
125850a69bb5SSascha Wildner free(cmd);
125950a69bb5SSascha Wildner free(user_rc);
126018de8d7fSPeter Avalos }
126118de8d7fSPeter Avalos
126218de8d7fSPeter Avalos static void
do_nologin(struct passwd * pw)126318de8d7fSPeter Avalos do_nologin(struct passwd *pw)
126418de8d7fSPeter Avalos {
126518de8d7fSPeter Avalos FILE *f = NULL;
1266*ba1276acSMatthew Dillon char buf[1024], *def_nl = _PATH_NOLOGIN;
1267856ea928SPeter Avalos struct stat sb;
1268*ba1276acSMatthew Dillon const char *nl;
126918de8d7fSPeter Avalos
127018de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
127199e85e0dSPeter Avalos if (login_getcapbool(lc, "ignorenologin", 0) || pw->pw_uid == 0)
1272856ea928SPeter Avalos return;
1273*ba1276acSMatthew Dillon nl = login_getcapstr(lc, "nologin", def_nl, def_nl);
127418de8d7fSPeter Avalos #else
1275856ea928SPeter Avalos if (pw->pw_uid == 0)
1276856ea928SPeter Avalos return;
1277856ea928SPeter Avalos nl = def_nl;
127818de8d7fSPeter Avalos #endif
127950a69bb5SSascha Wildner if (stat(nl, &sb) == -1)
1280856ea928SPeter Avalos return;
1281856ea928SPeter Avalos
1282856ea928SPeter Avalos /* /etc/nologin exists. Print its contents if we can and exit. */
1283856ea928SPeter Avalos logit("User %.100s not allowed because %s exists", pw->pw_name, nl);
1284856ea928SPeter Avalos if ((f = fopen(nl, "r")) != NULL) {
128518de8d7fSPeter Avalos while (fgets(buf, sizeof(buf), f))
128618de8d7fSPeter Avalos fputs(buf, stderr);
128718de8d7fSPeter Avalos fclose(f);
128818de8d7fSPeter Avalos }
1289856ea928SPeter Avalos exit(254);
129018de8d7fSPeter Avalos }
129118de8d7fSPeter Avalos
129218de8d7fSPeter Avalos /*
129318de8d7fSPeter Avalos * Chroot into a directory after checking it for safety: all path components
129418de8d7fSPeter Avalos * must be root-owned directories with strict permissions.
129518de8d7fSPeter Avalos */
129618de8d7fSPeter Avalos static void
safely_chroot(const char * path,uid_t uid)129718de8d7fSPeter Avalos safely_chroot(const char *path, uid_t uid)
129818de8d7fSPeter Avalos {
129918de8d7fSPeter Avalos const char *cp;
1300e9778795SPeter Avalos char component[PATH_MAX];
130118de8d7fSPeter Avalos struct stat st;
130218de8d7fSPeter Avalos
1303664f4763Szrj if (!path_absolute(path))
130418de8d7fSPeter Avalos fatal("chroot path does not begin at root");
130518de8d7fSPeter Avalos if (strlen(path) >= sizeof(component))
130618de8d7fSPeter Avalos fatal("chroot path too long");
130718de8d7fSPeter Avalos
130818de8d7fSPeter Avalos /*
130918de8d7fSPeter Avalos * Descend the path, checking that each component is a
131018de8d7fSPeter Avalos * root-owned directory with strict permissions.
131118de8d7fSPeter Avalos */
131218de8d7fSPeter Avalos for (cp = path; cp != NULL;) {
131318de8d7fSPeter Avalos if ((cp = strchr(cp, '/')) == NULL)
131418de8d7fSPeter Avalos strlcpy(component, path, sizeof(component));
131518de8d7fSPeter Avalos else {
131618de8d7fSPeter Avalos cp++;
131718de8d7fSPeter Avalos memcpy(component, path, cp - path);
131818de8d7fSPeter Avalos component[cp - path] = '\0';
131918de8d7fSPeter Avalos }
132018de8d7fSPeter Avalos
132150a69bb5SSascha Wildner debug3_f("checking '%s'", component);
132218de8d7fSPeter Avalos
132318de8d7fSPeter Avalos if (stat(component, &st) != 0)
132450a69bb5SSascha Wildner fatal_f("stat(\"%s\"): %s",
132518de8d7fSPeter Avalos component, strerror(errno));
132618de8d7fSPeter Avalos if (st.st_uid != 0 || (st.st_mode & 022) != 0)
132718de8d7fSPeter Avalos fatal("bad ownership or modes for chroot "
132818de8d7fSPeter Avalos "directory %s\"%s\"",
132918de8d7fSPeter Avalos cp == NULL ? "" : "component ", component);
133018de8d7fSPeter Avalos if (!S_ISDIR(st.st_mode))
133118de8d7fSPeter Avalos fatal("chroot path %s\"%s\" is not a directory",
133218de8d7fSPeter Avalos cp == NULL ? "" : "component ", component);
133318de8d7fSPeter Avalos
133418de8d7fSPeter Avalos }
133518de8d7fSPeter Avalos
133618de8d7fSPeter Avalos if (chdir(path) == -1)
133718de8d7fSPeter Avalos fatal("Unable to chdir to chroot path \"%s\": "
133818de8d7fSPeter Avalos "%s", path, strerror(errno));
133918de8d7fSPeter Avalos if (chroot(path) == -1)
134018de8d7fSPeter Avalos fatal("chroot(\"%s\"): %s", path, strerror(errno));
134118de8d7fSPeter Avalos if (chdir("/") == -1)
134250a69bb5SSascha Wildner fatal_f("chdir(/) after chroot: %s", strerror(errno));
134318de8d7fSPeter Avalos verbose("Changed root directory to \"%s\"", path);
134418de8d7fSPeter Avalos }
134518de8d7fSPeter Avalos
134618de8d7fSPeter Avalos /* Set login name, uid, gid, and groups. */
134718de8d7fSPeter Avalos void
do_setusercontext(struct passwd * pw)134818de8d7fSPeter Avalos do_setusercontext(struct passwd *pw)
134918de8d7fSPeter Avalos {
1350664f4763Szrj char uidstr[32], *chroot_path, *tmp;
135118de8d7fSPeter Avalos
13529f304aafSPeter Avalos platform_setusercontext(pw);
135318de8d7fSPeter Avalos
13549f304aafSPeter Avalos if (platform_privileged_uidswap()) {
135518de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
135618de8d7fSPeter Avalos if (setusercontext(lc, pw, pw->pw_uid,
135718de8d7fSPeter Avalos (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) {
135818de8d7fSPeter Avalos perror("unable to set user context");
135918de8d7fSPeter Avalos exit(1);
136018de8d7fSPeter Avalos }
136118de8d7fSPeter Avalos #else
136218de8d7fSPeter Avalos if (setlogin(pw->pw_name) < 0)
136318de8d7fSPeter Avalos error("setlogin failed: %s", strerror(errno));
136418de8d7fSPeter Avalos if (setgid(pw->pw_gid) < 0) {
136518de8d7fSPeter Avalos perror("setgid");
136618de8d7fSPeter Avalos exit(1);
136718de8d7fSPeter Avalos }
136818de8d7fSPeter Avalos /* Initialize the group list. */
136918de8d7fSPeter Avalos if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
137018de8d7fSPeter Avalos perror("initgroups");
137118de8d7fSPeter Avalos exit(1);
137218de8d7fSPeter Avalos }
137318de8d7fSPeter Avalos endgrent();
137418de8d7fSPeter Avalos #endif
1375856ea928SPeter Avalos
13769f304aafSPeter Avalos platform_setusercontext_post_groups(pw);
137718de8d7fSPeter Avalos
1378e9778795SPeter Avalos if (!in_chroot && options.chroot_directory != NULL &&
137918de8d7fSPeter Avalos strcasecmp(options.chroot_directory, "none") != 0) {
138018de8d7fSPeter Avalos tmp = tilde_expand_filename(options.chroot_directory,
138118de8d7fSPeter Avalos pw->pw_uid);
1382664f4763Szrj snprintf(uidstr, sizeof(uidstr), "%llu",
1383664f4763Szrj (unsigned long long)pw->pw_uid);
138418de8d7fSPeter Avalos chroot_path = percent_expand(tmp, "h", pw->pw_dir,
1385664f4763Szrj "u", pw->pw_name, "U", uidstr, (char *)NULL);
138618de8d7fSPeter Avalos safely_chroot(chroot_path, pw->pw_uid);
138718de8d7fSPeter Avalos free(tmp);
138818de8d7fSPeter Avalos free(chroot_path);
138936e94dc5SPeter Avalos /* Make sure we don't attempt to chroot again */
139036e94dc5SPeter Avalos free(options.chroot_directory);
139136e94dc5SPeter Avalos options.chroot_directory = NULL;
1392e9778795SPeter Avalos in_chroot = 1;
139318de8d7fSPeter Avalos }
139418de8d7fSPeter Avalos
139518de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
139618de8d7fSPeter Avalos if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) {
139718de8d7fSPeter Avalos perror("unable to set user context (setuser)");
139818de8d7fSPeter Avalos exit(1);
139918de8d7fSPeter Avalos }
140036e94dc5SPeter Avalos /*
140136e94dc5SPeter Avalos * FreeBSD's setusercontext() will not apply the user's
140236e94dc5SPeter Avalos * own umask setting unless running with the user's UID.
140336e94dc5SPeter Avalos */
140436e94dc5SPeter Avalos (void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUMASK);
140518de8d7fSPeter Avalos #else
140636e94dc5SPeter Avalos # ifdef USE_LIBIAF
1407e9778795SPeter Avalos /*
1408e9778795SPeter Avalos * In a chroot environment, the set_id() will always fail;
1409e9778795SPeter Avalos * typically because of the lack of necessary authentication
1410e9778795SPeter Avalos * services and runtime such as ./usr/lib/libiaf.so,
1411e9778795SPeter Avalos * ./usr/lib/libpam.so.1, and ./etc/passwd We skip it in the
1412e9778795SPeter Avalos * internal sftp chroot case. We'll lose auditing and ACLs but
1413e9778795SPeter Avalos * permanently_set_uid will take care of the rest.
141436e94dc5SPeter Avalos */
1415e9778795SPeter Avalos if (!in_chroot && set_id(pw->pw_name) != 0)
141636e94dc5SPeter Avalos fatal("set_id(%s) Failed", pw->pw_name);
141736e94dc5SPeter Avalos # endif /* USE_LIBIAF */
141818de8d7fSPeter Avalos /* Permanently switch to the desired uid. */
141918de8d7fSPeter Avalos permanently_set_uid(pw);
142018de8d7fSPeter Avalos #endif
142136e94dc5SPeter Avalos } else if (options.chroot_directory != NULL &&
142236e94dc5SPeter Avalos strcasecmp(options.chroot_directory, "none") != 0) {
142336e94dc5SPeter Avalos fatal("server lacks privileges to chroot to ChrootDirectory");
142418de8d7fSPeter Avalos }
142518de8d7fSPeter Avalos
142618de8d7fSPeter Avalos if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
142718de8d7fSPeter Avalos fatal("Failed to set uids to %u.", (u_int) pw->pw_uid);
142818de8d7fSPeter Avalos }
142918de8d7fSPeter Avalos
143018de8d7fSPeter Avalos static void
do_pwchange(Session * s)143118de8d7fSPeter Avalos do_pwchange(Session *s)
143218de8d7fSPeter Avalos {
143318de8d7fSPeter Avalos fflush(NULL);
143418de8d7fSPeter Avalos fprintf(stderr, "WARNING: Your password has expired.\n");
143518de8d7fSPeter Avalos if (s->ttyfd != -1) {
143618de8d7fSPeter Avalos fprintf(stderr,
143718de8d7fSPeter Avalos "You must change your password now and login again!\n");
14381c188a7fSPeter Avalos #ifdef WITH_SELINUX
14391c188a7fSPeter Avalos setexeccon(NULL);
14401c188a7fSPeter Avalos #endif
144118de8d7fSPeter Avalos #ifdef PASSWD_NEEDS_USERNAME
144218de8d7fSPeter Avalos execl(_PATH_PASSWD_PROG, "passwd", s->pw->pw_name,
144318de8d7fSPeter Avalos (char *)NULL);
144418de8d7fSPeter Avalos #else
144518de8d7fSPeter Avalos execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL);
144618de8d7fSPeter Avalos #endif
144718de8d7fSPeter Avalos perror("passwd");
144818de8d7fSPeter Avalos } else {
144918de8d7fSPeter Avalos fprintf(stderr,
145018de8d7fSPeter Avalos "Password change required but no TTY available.\n");
145118de8d7fSPeter Avalos }
145218de8d7fSPeter Avalos exit(1);
145318de8d7fSPeter Avalos }
145418de8d7fSPeter Avalos
145518de8d7fSPeter Avalos static void
child_close_fds(struct ssh * ssh)14562c9c1408SMatthew Dillon child_close_fds(struct ssh *ssh)
145718de8d7fSPeter Avalos {
1458e9778795SPeter Avalos extern int auth_sock;
145936e94dc5SPeter Avalos
1460e9778795SPeter Avalos if (auth_sock != -1) {
1461e9778795SPeter Avalos close(auth_sock);
1462e9778795SPeter Avalos auth_sock = -1;
146336e94dc5SPeter Avalos }
146436e94dc5SPeter Avalos
1465664f4763Szrj if (ssh_packet_get_connection_in(ssh) ==
1466664f4763Szrj ssh_packet_get_connection_out(ssh))
1467664f4763Szrj close(ssh_packet_get_connection_in(ssh));
146818de8d7fSPeter Avalos else {
1469664f4763Szrj close(ssh_packet_get_connection_in(ssh));
1470664f4763Szrj close(ssh_packet_get_connection_out(ssh));
147118de8d7fSPeter Avalos }
147218de8d7fSPeter Avalos /*
147318de8d7fSPeter Avalos * Close all descriptors related to channels. They will still remain
147418de8d7fSPeter Avalos * open in the parent.
147518de8d7fSPeter Avalos */
147618de8d7fSPeter Avalos /* XXX better use close-on-exec? -markus */
14772c9c1408SMatthew Dillon channel_close_all(ssh);
147818de8d7fSPeter Avalos
147918de8d7fSPeter Avalos /*
148018de8d7fSPeter Avalos * Close any extra file descriptors. Note that there may still be
148118de8d7fSPeter Avalos * descriptors left by system functions. They will be closed later.
148218de8d7fSPeter Avalos */
148318de8d7fSPeter Avalos endpwent();
148418de8d7fSPeter Avalos
148550a69bb5SSascha Wildner /* Stop directing logs to a high-numbered fd before we close it */
148650a69bb5SSascha Wildner log_redirect_stderr_to(NULL);
148750a69bb5SSascha Wildner
148818de8d7fSPeter Avalos /*
148918de8d7fSPeter Avalos * Close any extra open file descriptors so that we don't have them
149018de8d7fSPeter Avalos * hanging around in clients. Note that we want to do this after
149118de8d7fSPeter Avalos * initgroups, because at least on Solaris 2.3 it leaves file
149218de8d7fSPeter Avalos * descriptors open.
149318de8d7fSPeter Avalos */
14949f304aafSPeter Avalos closefrom(STDERR_FILENO + 1);
149518de8d7fSPeter Avalos }
149618de8d7fSPeter Avalos
149718de8d7fSPeter Avalos /*
149818de8d7fSPeter Avalos * Performs common processing for the child, such as setting up the
149918de8d7fSPeter Avalos * environment, closing extra file descriptors, setting the user and group
150018de8d7fSPeter Avalos * ids, and executing the command or shell.
150118de8d7fSPeter Avalos */
150218de8d7fSPeter Avalos #define ARGV_MAX 10
150318de8d7fSPeter Avalos void
do_child(struct ssh * ssh,Session * s,const char * command)15042c9c1408SMatthew Dillon do_child(struct ssh *ssh, Session *s, const char *command)
150518de8d7fSPeter Avalos {
150618de8d7fSPeter Avalos extern char **environ;
1507664f4763Szrj char **env, *argv[ARGV_MAX], remote_id[512];
15082c9c1408SMatthew Dillon const char *shell, *shell0;
150918de8d7fSPeter Avalos struct passwd *pw = s->pw;
151018de8d7fSPeter Avalos int r = 0;
151118de8d7fSPeter Avalos
1512664f4763Szrj sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id));
1513664f4763Szrj
151418de8d7fSPeter Avalos /* remove hostkey from the child's memory */
151518de8d7fSPeter Avalos destroy_sensitive_data();
1516664f4763Szrj ssh_packet_clear_keys(ssh);
151718de8d7fSPeter Avalos
151818de8d7fSPeter Avalos /* Force a password change */
151918de8d7fSPeter Avalos if (s->authctxt->force_pwchange) {
152018de8d7fSPeter Avalos do_setusercontext(pw);
15212c9c1408SMatthew Dillon child_close_fds(ssh);
152218de8d7fSPeter Avalos do_pwchange(s);
152318de8d7fSPeter Avalos exit(1);
152418de8d7fSPeter Avalos }
152518de8d7fSPeter Avalos
152618de8d7fSPeter Avalos /*
152718de8d7fSPeter Avalos * Login(1) does this as well, and it needs uid 0 for the "-h"
152818de8d7fSPeter Avalos * switch, so we let login(1) to this for us.
152918de8d7fSPeter Avalos */
153018de8d7fSPeter Avalos #ifdef HAVE_OSF_SIA
153118de8d7fSPeter Avalos session_setup_sia(pw, s->ttyfd == -1 ? NULL : s->tty);
153218de8d7fSPeter Avalos if (!check_quietlogin(s, command))
153318de8d7fSPeter Avalos do_motd();
153418de8d7fSPeter Avalos #else /* HAVE_OSF_SIA */
153518de8d7fSPeter Avalos /* When PAM is enabled we rely on it to do the nologin check */
153618de8d7fSPeter Avalos if (!options.use_pam)
153718de8d7fSPeter Avalos do_nologin(pw);
153818de8d7fSPeter Avalos do_setusercontext(pw);
153918de8d7fSPeter Avalos /*
154018de8d7fSPeter Avalos * PAM session modules in do_setusercontext may have
154118de8d7fSPeter Avalos * generated messages, so if this in an interactive
154218de8d7fSPeter Avalos * login then display them too.
154318de8d7fSPeter Avalos */
154418de8d7fSPeter Avalos if (!check_quietlogin(s, command))
154518de8d7fSPeter Avalos display_loginmsg();
154618de8d7fSPeter Avalos #endif /* HAVE_OSF_SIA */
154718de8d7fSPeter Avalos
154818de8d7fSPeter Avalos #ifdef USE_PAM
15492c9c1408SMatthew Dillon if (options.use_pam && !is_pam_session_open()) {
155018de8d7fSPeter Avalos debug3("PAM session not opened, exiting");
155118de8d7fSPeter Avalos display_loginmsg();
155218de8d7fSPeter Avalos exit(254);
155318de8d7fSPeter Avalos }
155418de8d7fSPeter Avalos #endif
155518de8d7fSPeter Avalos
155618de8d7fSPeter Avalos /*
155718de8d7fSPeter Avalos * Get the shell from the password data. An empty shell field is
155818de8d7fSPeter Avalos * legal, and means /bin/sh.
155918de8d7fSPeter Avalos */
156018de8d7fSPeter Avalos shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
156118de8d7fSPeter Avalos
156218de8d7fSPeter Avalos /*
156318de8d7fSPeter Avalos * Make sure $SHELL points to the shell from the password file,
156418de8d7fSPeter Avalos * even if shell is overridden from login.conf
156518de8d7fSPeter Avalos */
15662c9c1408SMatthew Dillon env = do_setup_env(ssh, s, shell);
156718de8d7fSPeter Avalos
156818de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
156918de8d7fSPeter Avalos shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell);
157018de8d7fSPeter Avalos #endif
157118de8d7fSPeter Avalos
157218de8d7fSPeter Avalos /*
157318de8d7fSPeter Avalos * Close the connection descriptors; note that this is the child, and
157418de8d7fSPeter Avalos * the server will still have the socket open, and it is important
157518de8d7fSPeter Avalos * that we do not shutdown it. Note that the descriptors cannot be
157618de8d7fSPeter Avalos * closed before building the environment, as we call
1577e9778795SPeter Avalos * ssh_remote_ipaddr there.
157818de8d7fSPeter Avalos */
15792c9c1408SMatthew Dillon child_close_fds(ssh);
158018de8d7fSPeter Avalos
158118de8d7fSPeter Avalos /*
158218de8d7fSPeter Avalos * Must take new environment into use so that .ssh/rc,
158318de8d7fSPeter Avalos * /etc/ssh/sshrc and xauth are run in the proper environment.
158418de8d7fSPeter Avalos */
158518de8d7fSPeter Avalos environ = env;
158618de8d7fSPeter Avalos
158718de8d7fSPeter Avalos #if defined(KRB5) && defined(USE_AFS)
158818de8d7fSPeter Avalos /*
158918de8d7fSPeter Avalos * At this point, we check to see if AFS is active and if we have
159018de8d7fSPeter Avalos * a valid Kerberos 5 TGT. If so, it seems like a good idea to see
159118de8d7fSPeter Avalos * if we can (and need to) extend the ticket into an AFS token. If
159218de8d7fSPeter Avalos * we don't do this, we run into potential problems if the user's
159318de8d7fSPeter Avalos * home directory is in AFS and it's not world-readable.
159418de8d7fSPeter Avalos */
159518de8d7fSPeter Avalos
159618de8d7fSPeter Avalos if (options.kerberos_get_afs_token && k_hasafs() &&
159718de8d7fSPeter Avalos (s->authctxt->krb5_ctx != NULL)) {
159818de8d7fSPeter Avalos char cell[64];
159918de8d7fSPeter Avalos
160018de8d7fSPeter Avalos debug("Getting AFS token");
160118de8d7fSPeter Avalos
160218de8d7fSPeter Avalos k_setpag();
160318de8d7fSPeter Avalos
160418de8d7fSPeter Avalos if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
160518de8d7fSPeter Avalos krb5_afslog(s->authctxt->krb5_ctx,
160618de8d7fSPeter Avalos s->authctxt->krb5_fwd_ccache, cell, NULL);
160718de8d7fSPeter Avalos
160818de8d7fSPeter Avalos krb5_afslog_home(s->authctxt->krb5_ctx,
160918de8d7fSPeter Avalos s->authctxt->krb5_fwd_ccache, NULL, NULL, pw->pw_dir);
161018de8d7fSPeter Avalos }
161118de8d7fSPeter Avalos #endif
161218de8d7fSPeter Avalos
161318de8d7fSPeter Avalos /* Change current directory to the user's home directory. */
16140cbfa66cSDaniel Fojt if (chdir(pw->pw_dir) == -1) {
161518de8d7fSPeter Avalos /* Suppress missing homedir warning for chroot case */
161618de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
161718de8d7fSPeter Avalos r = login_getcapbool(lc, "requirehome", 0);
161818de8d7fSPeter Avalos #endif
1619e9778795SPeter Avalos if (r || !in_chroot) {
162018de8d7fSPeter Avalos fprintf(stderr, "Could not chdir to home "
162118de8d7fSPeter Avalos "directory %s: %s\n", pw->pw_dir,
162218de8d7fSPeter Avalos strerror(errno));
1623e9778795SPeter Avalos }
162418de8d7fSPeter Avalos if (r)
162518de8d7fSPeter Avalos exit(1);
162618de8d7fSPeter Avalos }
162718de8d7fSPeter Avalos
162818de8d7fSPeter Avalos closefrom(STDERR_FILENO + 1);
162918de8d7fSPeter Avalos
1630664f4763Szrj do_rc_files(ssh, s, shell);
163118de8d7fSPeter Avalos
163218de8d7fSPeter Avalos /* restore SIGPIPE for child */
16330cbfa66cSDaniel Fojt ssh_signal(SIGPIPE, SIG_DFL);
163418de8d7fSPeter Avalos
1635856ea928SPeter Avalos if (s->is_subsystem == SUBSYSTEM_INT_SFTP_ERROR) {
1636664f4763Szrj error("Connection from %s: refusing non-sftp session",
1637664f4763Szrj remote_id);
1638856ea928SPeter Avalos printf("This service allows sftp connections only.\n");
1639856ea928SPeter Avalos fflush(NULL);
1640856ea928SPeter Avalos exit(1);
1641856ea928SPeter Avalos } else if (s->is_subsystem == SUBSYSTEM_INT_SFTP) {
164218de8d7fSPeter Avalos extern int optind, optreset;
164318de8d7fSPeter Avalos int i;
164418de8d7fSPeter Avalos char *p, *args;
164518de8d7fSPeter Avalos
164640c002afSPeter Avalos setproctitle("%s@%s", s->pw->pw_name, INTERNAL_SFTP_NAME);
1647cb5eb4f1SPeter Avalos args = xstrdup(command ? command : "sftp-server");
164818de8d7fSPeter Avalos for (i = 0, (p = strtok(args, " ")); p; (p = strtok(NULL, " ")))
164918de8d7fSPeter Avalos if (i < ARGV_MAX - 1)
165018de8d7fSPeter Avalos argv[i++] = p;
165118de8d7fSPeter Avalos argv[i] = NULL;
165218de8d7fSPeter Avalos optind = optreset = 1;
165318de8d7fSPeter Avalos __progname = argv[0];
1654856ea928SPeter Avalos #ifdef WITH_SELINUX
1655856ea928SPeter Avalos ssh_selinux_change_context("sftpd_t");
1656856ea928SPeter Avalos #endif
165718de8d7fSPeter Avalos exit(sftp_server_main(i, argv, s->pw));
165818de8d7fSPeter Avalos }
165918de8d7fSPeter Avalos
1660856ea928SPeter Avalos fflush(NULL);
1661856ea928SPeter Avalos
166218de8d7fSPeter Avalos /* Get the last component of the shell name. */
166318de8d7fSPeter Avalos if ((shell0 = strrchr(shell, '/')) != NULL)
166418de8d7fSPeter Avalos shell0++;
166518de8d7fSPeter Avalos else
166618de8d7fSPeter Avalos shell0 = shell;
166718de8d7fSPeter Avalos
166818de8d7fSPeter Avalos /*
166918de8d7fSPeter Avalos * If we have no command, execute the shell. In this case, the shell
167018de8d7fSPeter Avalos * name to be passed in argv[0] is preceded by '-' to indicate that
167118de8d7fSPeter Avalos * this is a login shell.
167218de8d7fSPeter Avalos */
167318de8d7fSPeter Avalos if (!command) {
167418de8d7fSPeter Avalos char argv0[256];
167518de8d7fSPeter Avalos
167618de8d7fSPeter Avalos /* Start the shell. Set initial character to '-'. */
167718de8d7fSPeter Avalos argv0[0] = '-';
167818de8d7fSPeter Avalos
167918de8d7fSPeter Avalos if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1)
168018de8d7fSPeter Avalos >= sizeof(argv0) - 1) {
168118de8d7fSPeter Avalos errno = EINVAL;
168218de8d7fSPeter Avalos perror(shell);
168318de8d7fSPeter Avalos exit(1);
168418de8d7fSPeter Avalos }
168518de8d7fSPeter Avalos
168618de8d7fSPeter Avalos /* Execute the shell. */
168718de8d7fSPeter Avalos argv[0] = argv0;
168818de8d7fSPeter Avalos argv[1] = NULL;
168918de8d7fSPeter Avalos execve(shell, argv, env);
169018de8d7fSPeter Avalos
169118de8d7fSPeter Avalos /* Executing the shell failed. */
169218de8d7fSPeter Avalos perror(shell);
169318de8d7fSPeter Avalos exit(1);
169418de8d7fSPeter Avalos }
169518de8d7fSPeter Avalos /*
169618de8d7fSPeter Avalos * Execute the command using the user's shell. This uses the -c
169718de8d7fSPeter Avalos * option to execute the command.
169818de8d7fSPeter Avalos */
169918de8d7fSPeter Avalos argv[0] = (char *) shell0;
170018de8d7fSPeter Avalos argv[1] = "-c";
170118de8d7fSPeter Avalos argv[2] = (char *) command;
170218de8d7fSPeter Avalos argv[3] = NULL;
170318de8d7fSPeter Avalos execve(shell, argv, env);
170418de8d7fSPeter Avalos perror(shell);
170518de8d7fSPeter Avalos exit(1);
170618de8d7fSPeter Avalos }
170718de8d7fSPeter Avalos
170818de8d7fSPeter Avalos void
session_unused(int id)170918de8d7fSPeter Avalos session_unused(int id)
171018de8d7fSPeter Avalos {
171150a69bb5SSascha Wildner debug3_f("session id %d unused", id);
171218de8d7fSPeter Avalos if (id >= options.max_sessions ||
171318de8d7fSPeter Avalos id >= sessions_nalloc) {
171450a69bb5SSascha Wildner fatal_f("insane session id %d (max %d nalloc %d)",
171550a69bb5SSascha Wildner id, options.max_sessions, sessions_nalloc);
171618de8d7fSPeter Avalos }
171736e94dc5SPeter Avalos memset(&sessions[id], 0, sizeof(*sessions));
171818de8d7fSPeter Avalos sessions[id].self = id;
171918de8d7fSPeter Avalos sessions[id].used = 0;
172018de8d7fSPeter Avalos sessions[id].chanid = -1;
172118de8d7fSPeter Avalos sessions[id].ptyfd = -1;
172218de8d7fSPeter Avalos sessions[id].ttyfd = -1;
172318de8d7fSPeter Avalos sessions[id].ptymaster = -1;
172418de8d7fSPeter Avalos sessions[id].x11_chanids = NULL;
172518de8d7fSPeter Avalos sessions[id].next_unused = sessions_first_unused;
172618de8d7fSPeter Avalos sessions_first_unused = id;
172718de8d7fSPeter Avalos }
172818de8d7fSPeter Avalos
172918de8d7fSPeter Avalos Session *
session_new(void)173018de8d7fSPeter Avalos session_new(void)
173118de8d7fSPeter Avalos {
173218de8d7fSPeter Avalos Session *s, *tmp;
173318de8d7fSPeter Avalos
173418de8d7fSPeter Avalos if (sessions_first_unused == -1) {
173518de8d7fSPeter Avalos if (sessions_nalloc >= options.max_sessions)
173618de8d7fSPeter Avalos return NULL;
173750a69bb5SSascha Wildner debug2_f("allocate (allocated %d max %d)",
173850a69bb5SSascha Wildner sessions_nalloc, options.max_sessions);
17392c9c1408SMatthew Dillon tmp = xrecallocarray(sessions, sessions_nalloc,
17402c9c1408SMatthew Dillon sessions_nalloc + 1, sizeof(*sessions));
174118de8d7fSPeter Avalos if (tmp == NULL) {
174250a69bb5SSascha Wildner error_f("cannot allocate %d sessions",
174350a69bb5SSascha Wildner sessions_nalloc + 1);
174418de8d7fSPeter Avalos return NULL;
174518de8d7fSPeter Avalos }
174618de8d7fSPeter Avalos sessions = tmp;
174718de8d7fSPeter Avalos session_unused(sessions_nalloc++);
174818de8d7fSPeter Avalos }
174918de8d7fSPeter Avalos
175018de8d7fSPeter Avalos if (sessions_first_unused >= sessions_nalloc ||
175118de8d7fSPeter Avalos sessions_first_unused < 0) {
175250a69bb5SSascha Wildner fatal_f("insane first_unused %d max %d nalloc %d",
175350a69bb5SSascha Wildner sessions_first_unused, options.max_sessions,
175418de8d7fSPeter Avalos sessions_nalloc);
175518de8d7fSPeter Avalos }
175618de8d7fSPeter Avalos
175718de8d7fSPeter Avalos s = &sessions[sessions_first_unused];
175850a69bb5SSascha Wildner if (s->used)
175950a69bb5SSascha Wildner fatal_f("session %d already used", sessions_first_unused);
176018de8d7fSPeter Avalos sessions_first_unused = s->next_unused;
176118de8d7fSPeter Avalos s->used = 1;
176218de8d7fSPeter Avalos s->next_unused = -1;
176318de8d7fSPeter Avalos debug("session_new: session %d", s->self);
176418de8d7fSPeter Avalos
176518de8d7fSPeter Avalos return s;
176618de8d7fSPeter Avalos }
176718de8d7fSPeter Avalos
176818de8d7fSPeter Avalos static void
session_dump(void)176918de8d7fSPeter Avalos session_dump(void)
177018de8d7fSPeter Avalos {
177118de8d7fSPeter Avalos int i;
177218de8d7fSPeter Avalos for (i = 0; i < sessions_nalloc; i++) {
177318de8d7fSPeter Avalos Session *s = &sessions[i];
177418de8d7fSPeter Avalos
177550a69bb5SSascha Wildner debug("dump: used %d next_unused %d session %d "
177618de8d7fSPeter Avalos "channel %d pid %ld",
177718de8d7fSPeter Avalos s->used,
177818de8d7fSPeter Avalos s->next_unused,
177918de8d7fSPeter Avalos s->self,
178018de8d7fSPeter Avalos s->chanid,
178118de8d7fSPeter Avalos (long)s->pid);
178218de8d7fSPeter Avalos }
178318de8d7fSPeter Avalos }
178418de8d7fSPeter Avalos
178518de8d7fSPeter Avalos int
session_open(Authctxt * authctxt,int chanid)178618de8d7fSPeter Avalos session_open(Authctxt *authctxt, int chanid)
178718de8d7fSPeter Avalos {
178818de8d7fSPeter Avalos Session *s = session_new();
178918de8d7fSPeter Avalos debug("session_open: channel %d", chanid);
179018de8d7fSPeter Avalos if (s == NULL) {
179118de8d7fSPeter Avalos error("no more sessions");
179218de8d7fSPeter Avalos return 0;
179318de8d7fSPeter Avalos }
179418de8d7fSPeter Avalos s->authctxt = authctxt;
179518de8d7fSPeter Avalos s->pw = authctxt->pw;
179618de8d7fSPeter Avalos if (s->pw == NULL || !authctxt->valid)
179718de8d7fSPeter Avalos fatal("no user for session %d", s->self);
179818de8d7fSPeter Avalos debug("session_open: session %d: link with channel %d", s->self, chanid);
179918de8d7fSPeter Avalos s->chanid = chanid;
180018de8d7fSPeter Avalos return 1;
180118de8d7fSPeter Avalos }
180218de8d7fSPeter Avalos
180318de8d7fSPeter Avalos Session *
session_by_tty(char * tty)180418de8d7fSPeter Avalos session_by_tty(char *tty)
180518de8d7fSPeter Avalos {
180618de8d7fSPeter Avalos int i;
180718de8d7fSPeter Avalos for (i = 0; i < sessions_nalloc; i++) {
180818de8d7fSPeter Avalos Session *s = &sessions[i];
180918de8d7fSPeter Avalos if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) {
181018de8d7fSPeter Avalos debug("session_by_tty: session %d tty %s", i, tty);
181118de8d7fSPeter Avalos return s;
181218de8d7fSPeter Avalos }
181318de8d7fSPeter Avalos }
181418de8d7fSPeter Avalos debug("session_by_tty: unknown tty %.100s", tty);
181518de8d7fSPeter Avalos session_dump();
181618de8d7fSPeter Avalos return NULL;
181718de8d7fSPeter Avalos }
181818de8d7fSPeter Avalos
181918de8d7fSPeter Avalos static Session *
session_by_channel(int id)182018de8d7fSPeter Avalos session_by_channel(int id)
182118de8d7fSPeter Avalos {
182218de8d7fSPeter Avalos int i;
182318de8d7fSPeter Avalos for (i = 0; i < sessions_nalloc; i++) {
182418de8d7fSPeter Avalos Session *s = &sessions[i];
182518de8d7fSPeter Avalos if (s->used && s->chanid == id) {
182618de8d7fSPeter Avalos debug("session_by_channel: session %d channel %d",
182718de8d7fSPeter Avalos i, id);
182818de8d7fSPeter Avalos return s;
182918de8d7fSPeter Avalos }
183018de8d7fSPeter Avalos }
183118de8d7fSPeter Avalos debug("session_by_channel: unknown channel %d", id);
183218de8d7fSPeter Avalos session_dump();
183318de8d7fSPeter Avalos return NULL;
183418de8d7fSPeter Avalos }
183518de8d7fSPeter Avalos
183618de8d7fSPeter Avalos static Session *
session_by_x11_channel(int id)183718de8d7fSPeter Avalos session_by_x11_channel(int id)
183818de8d7fSPeter Avalos {
183918de8d7fSPeter Avalos int i, j;
184018de8d7fSPeter Avalos
184118de8d7fSPeter Avalos for (i = 0; i < sessions_nalloc; i++) {
184218de8d7fSPeter Avalos Session *s = &sessions[i];
184318de8d7fSPeter Avalos
184418de8d7fSPeter Avalos if (s->x11_chanids == NULL || !s->used)
184518de8d7fSPeter Avalos continue;
184618de8d7fSPeter Avalos for (j = 0; s->x11_chanids[j] != -1; j++) {
184718de8d7fSPeter Avalos if (s->x11_chanids[j] == id) {
184818de8d7fSPeter Avalos debug("session_by_x11_channel: session %d "
184918de8d7fSPeter Avalos "channel %d", s->self, id);
185018de8d7fSPeter Avalos return s;
185118de8d7fSPeter Avalos }
185218de8d7fSPeter Avalos }
185318de8d7fSPeter Avalos }
185418de8d7fSPeter Avalos debug("session_by_x11_channel: unknown channel %d", id);
185518de8d7fSPeter Avalos session_dump();
185618de8d7fSPeter Avalos return NULL;
185718de8d7fSPeter Avalos }
185818de8d7fSPeter Avalos
185918de8d7fSPeter Avalos static Session *
session_by_pid(pid_t pid)186018de8d7fSPeter Avalos session_by_pid(pid_t pid)
186118de8d7fSPeter Avalos {
186218de8d7fSPeter Avalos int i;
186318de8d7fSPeter Avalos debug("session_by_pid: pid %ld", (long)pid);
186418de8d7fSPeter Avalos for (i = 0; i < sessions_nalloc; i++) {
186518de8d7fSPeter Avalos Session *s = &sessions[i];
186618de8d7fSPeter Avalos if (s->used && s->pid == pid)
186718de8d7fSPeter Avalos return s;
186818de8d7fSPeter Avalos }
186918de8d7fSPeter Avalos error("session_by_pid: unknown pid %ld", (long)pid);
187018de8d7fSPeter Avalos session_dump();
187118de8d7fSPeter Avalos return NULL;
187218de8d7fSPeter Avalos }
187318de8d7fSPeter Avalos
187418de8d7fSPeter Avalos static int
session_window_change_req(struct ssh * ssh,Session * s)18752c9c1408SMatthew Dillon session_window_change_req(struct ssh *ssh, Session *s)
187618de8d7fSPeter Avalos {
1877664f4763Szrj int r;
1878664f4763Szrj
1879664f4763Szrj if ((r = sshpkt_get_u32(ssh, &s->col)) != 0 ||
1880664f4763Szrj (r = sshpkt_get_u32(ssh, &s->row)) != 0 ||
1881664f4763Szrj (r = sshpkt_get_u32(ssh, &s->xpixel)) != 0 ||
1882664f4763Szrj (r = sshpkt_get_u32(ssh, &s->ypixel)) != 0 ||
1883664f4763Szrj (r = sshpkt_get_end(ssh)) != 0)
1884664f4763Szrj sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
188518de8d7fSPeter Avalos pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
188618de8d7fSPeter Avalos return 1;
188718de8d7fSPeter Avalos }
188818de8d7fSPeter Avalos
188918de8d7fSPeter Avalos static int
session_pty_req(struct ssh * ssh,Session * s)18902c9c1408SMatthew Dillon session_pty_req(struct ssh *ssh, Session *s)
189118de8d7fSPeter Avalos {
1892664f4763Szrj int r;
189318de8d7fSPeter Avalos
1894664f4763Szrj if (!auth_opts->permit_pty_flag || !options.permit_tty) {
1895664f4763Szrj debug("Allocating a pty not permitted for this connection.");
189618de8d7fSPeter Avalos return 0;
189718de8d7fSPeter Avalos }
189818de8d7fSPeter Avalos if (s->ttyfd != -1) {
1899664f4763Szrj ssh_packet_disconnect(ssh, "Protocol error: you already have a pty.");
190018de8d7fSPeter Avalos return 0;
190118de8d7fSPeter Avalos }
190218de8d7fSPeter Avalos
1903664f4763Szrj if ((r = sshpkt_get_cstring(ssh, &s->term, NULL)) != 0 ||
1904664f4763Szrj (r = sshpkt_get_u32(ssh, &s->col)) != 0 ||
1905664f4763Szrj (r = sshpkt_get_u32(ssh, &s->row)) != 0 ||
1906664f4763Szrj (r = sshpkt_get_u32(ssh, &s->xpixel)) != 0 ||
1907664f4763Szrj (r = sshpkt_get_u32(ssh, &s->ypixel)) != 0)
1908664f4763Szrj sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
190918de8d7fSPeter Avalos
191018de8d7fSPeter Avalos if (strcmp(s->term, "") == 0) {
191136e94dc5SPeter Avalos free(s->term);
191218de8d7fSPeter Avalos s->term = NULL;
191318de8d7fSPeter Avalos }
191418de8d7fSPeter Avalos
191518de8d7fSPeter Avalos /* Allocate a pty and open it. */
191618de8d7fSPeter Avalos debug("Allocating pty.");
1917*ba1276acSMatthew Dillon if (!mm_pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) {
191836e94dc5SPeter Avalos free(s->term);
191918de8d7fSPeter Avalos s->term = NULL;
192018de8d7fSPeter Avalos s->ptyfd = -1;
192118de8d7fSPeter Avalos s->ttyfd = -1;
192218de8d7fSPeter Avalos error("session_pty_req: session %d alloc failed", s->self);
192318de8d7fSPeter Avalos return 0;
192418de8d7fSPeter Avalos }
192518de8d7fSPeter Avalos debug("session_pty_req: session %d alloc %s", s->self, s->tty);
192618de8d7fSPeter Avalos
1927664f4763Szrj ssh_tty_parse_modes(ssh, s->ttyfd);
1928664f4763Szrj
1929664f4763Szrj if ((r = sshpkt_get_end(ssh)) != 0)
1930664f4763Szrj sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
193118de8d7fSPeter Avalos
193218de8d7fSPeter Avalos /* Set window size from the packet. */
193318de8d7fSPeter Avalos pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
193418de8d7fSPeter Avalos
193518de8d7fSPeter Avalos session_proctitle(s);
193618de8d7fSPeter Avalos return 1;
193718de8d7fSPeter Avalos }
193818de8d7fSPeter Avalos
193918de8d7fSPeter Avalos static int
session_subsystem_req(struct ssh * ssh,Session * s)19402c9c1408SMatthew Dillon session_subsystem_req(struct ssh *ssh, Session *s)
194118de8d7fSPeter Avalos {
194218de8d7fSPeter Avalos struct stat st;
1943664f4763Szrj int r, success = 0;
1944*ba1276acSMatthew Dillon char *prog, *cmd, *type;
194518de8d7fSPeter Avalos u_int i;
194618de8d7fSPeter Avalos
1947664f4763Szrj if ((r = sshpkt_get_cstring(ssh, &s->subsys, NULL)) != 0 ||
1948664f4763Szrj (r = sshpkt_get_end(ssh)) != 0)
1949664f4763Szrj sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
195036e94dc5SPeter Avalos debug2("subsystem request for %.100s by user %s", s->subsys,
1951856ea928SPeter Avalos s->pw->pw_name);
195218de8d7fSPeter Avalos
195318de8d7fSPeter Avalos for (i = 0; i < options.num_subsystems; i++) {
195436e94dc5SPeter Avalos if (strcmp(s->subsys, options.subsystem_name[i]) == 0) {
195518de8d7fSPeter Avalos prog = options.subsystem_command[i];
195618de8d7fSPeter Avalos cmd = options.subsystem_args[i];
1957856ea928SPeter Avalos if (strcmp(INTERNAL_SFTP_NAME, prog) == 0) {
195818de8d7fSPeter Avalos s->is_subsystem = SUBSYSTEM_INT_SFTP;
1959856ea928SPeter Avalos debug("subsystem: %s", prog);
196018de8d7fSPeter Avalos } else {
19610cbfa66cSDaniel Fojt if (stat(prog, &st) == -1)
1962856ea928SPeter Avalos debug("subsystem: cannot stat %s: %s",
1963856ea928SPeter Avalos prog, strerror(errno));
196418de8d7fSPeter Avalos s->is_subsystem = SUBSYSTEM_EXT;
196518de8d7fSPeter Avalos debug("subsystem: exec() %s", cmd);
1966856ea928SPeter Avalos }
1967*ba1276acSMatthew Dillon xasprintf(&type, "session:subsystem:%s",
1968*ba1276acSMatthew Dillon options.subsystem_name[i]);
1969*ba1276acSMatthew Dillon channel_set_xtype(ssh, s->chanid, type);
1970*ba1276acSMatthew Dillon free(type);
19712c9c1408SMatthew Dillon success = do_exec(ssh, s, cmd) == 0;
197218de8d7fSPeter Avalos break;
197318de8d7fSPeter Avalos }
197418de8d7fSPeter Avalos }
197518de8d7fSPeter Avalos
197618de8d7fSPeter Avalos if (!success)
197736e94dc5SPeter Avalos logit("subsystem request for %.100s by user %s failed, "
197836e94dc5SPeter Avalos "subsystem not found", s->subsys, s->pw->pw_name);
197918de8d7fSPeter Avalos
198018de8d7fSPeter Avalos return success;
198118de8d7fSPeter Avalos }
198218de8d7fSPeter Avalos
198318de8d7fSPeter Avalos static int
session_x11_req(struct ssh * ssh,Session * s)19842c9c1408SMatthew Dillon session_x11_req(struct ssh *ssh, Session *s)
198518de8d7fSPeter Avalos {
1986664f4763Szrj int r, success;
1987664f4763Szrj u_char single_connection = 0;
198818de8d7fSPeter Avalos
198918de8d7fSPeter Avalos if (s->auth_proto != NULL || s->auth_data != NULL) {
199018de8d7fSPeter Avalos error("session_x11_req: session %d: "
199118de8d7fSPeter Avalos "x11 forwarding already active", s->self);
199218de8d7fSPeter Avalos return 0;
199318de8d7fSPeter Avalos }
1994664f4763Szrj if ((r = sshpkt_get_u8(ssh, &single_connection)) != 0 ||
1995664f4763Szrj (r = sshpkt_get_cstring(ssh, &s->auth_proto, NULL)) != 0 ||
1996664f4763Szrj (r = sshpkt_get_cstring(ssh, &s->auth_data, NULL)) != 0 ||
1997664f4763Szrj (r = sshpkt_get_u32(ssh, &s->screen)) != 0 ||
1998664f4763Szrj (r = sshpkt_get_end(ssh)) != 0)
1999664f4763Szrj sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
2000664f4763Szrj
2001664f4763Szrj s->single_connection = single_connection;
200218de8d7fSPeter Avalos
2003e9778795SPeter Avalos if (xauth_valid_string(s->auth_proto) &&
2004e9778795SPeter Avalos xauth_valid_string(s->auth_data))
20052c9c1408SMatthew Dillon success = session_setup_x11fwd(ssh, s);
2006e9778795SPeter Avalos else {
2007e9778795SPeter Avalos success = 0;
2008e9778795SPeter Avalos error("Invalid X11 forwarding data");
2009e9778795SPeter Avalos }
201018de8d7fSPeter Avalos if (!success) {
201136e94dc5SPeter Avalos free(s->auth_proto);
201236e94dc5SPeter Avalos free(s->auth_data);
201318de8d7fSPeter Avalos s->auth_proto = NULL;
201418de8d7fSPeter Avalos s->auth_data = NULL;
201518de8d7fSPeter Avalos }
201618de8d7fSPeter Avalos return success;
201718de8d7fSPeter Avalos }
201818de8d7fSPeter Avalos
201918de8d7fSPeter Avalos static int
session_shell_req(struct ssh * ssh,Session * s)20202c9c1408SMatthew Dillon session_shell_req(struct ssh *ssh, Session *s)
202118de8d7fSPeter Avalos {
2022664f4763Szrj int r;
2023664f4763Szrj
2024664f4763Szrj if ((r = sshpkt_get_end(ssh)) != 0)
2025664f4763Szrj sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
2026*ba1276acSMatthew Dillon
2027*ba1276acSMatthew Dillon channel_set_xtype(ssh, s->chanid, "session:shell");
2028*ba1276acSMatthew Dillon
20292c9c1408SMatthew Dillon return do_exec(ssh, s, NULL) == 0;
203018de8d7fSPeter Avalos }
203118de8d7fSPeter Avalos
203218de8d7fSPeter Avalos static int
session_exec_req(struct ssh * ssh,Session * s)20332c9c1408SMatthew Dillon session_exec_req(struct ssh *ssh, Session *s)
203418de8d7fSPeter Avalos {
2035664f4763Szrj u_int success;
2036664f4763Szrj int r;
2037664f4763Szrj char *command = NULL;
203818de8d7fSPeter Avalos
2039664f4763Szrj if ((r = sshpkt_get_cstring(ssh, &command, NULL)) != 0 ||
2040664f4763Szrj (r = sshpkt_get_end(ssh)) != 0)
2041664f4763Szrj sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
2042664f4763Szrj
2043*ba1276acSMatthew Dillon channel_set_xtype(ssh, s->chanid, "session:command");
2044*ba1276acSMatthew Dillon
20452c9c1408SMatthew Dillon success = do_exec(ssh, s, command) == 0;
204636e94dc5SPeter Avalos free(command);
204718de8d7fSPeter Avalos return success;
204818de8d7fSPeter Avalos }
204918de8d7fSPeter Avalos
205018de8d7fSPeter Avalos static int
session_break_req(struct ssh * ssh,Session * s)20512c9c1408SMatthew Dillon session_break_req(struct ssh *ssh, Session *s)
205218de8d7fSPeter Avalos {
2053664f4763Szrj int r;
205418de8d7fSPeter Avalos
2055664f4763Szrj if ((r = sshpkt_get_u32(ssh, NULL)) != 0 || /* ignore */
2056664f4763Szrj (r = sshpkt_get_end(ssh)) != 0)
2057664f4763Szrj sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
205818de8d7fSPeter Avalos
20590cbfa66cSDaniel Fojt if (s->ptymaster == -1 || tcsendbreak(s->ptymaster, 0) == -1)
206018de8d7fSPeter Avalos return 0;
206118de8d7fSPeter Avalos return 1;
206218de8d7fSPeter Avalos }
206318de8d7fSPeter Avalos
206418de8d7fSPeter Avalos static int
session_env_req(struct ssh * ssh,Session * s)20652c9c1408SMatthew Dillon session_env_req(struct ssh *ssh, Session *s)
206618de8d7fSPeter Avalos {
206718de8d7fSPeter Avalos char *name, *val;
2068664f4763Szrj u_int i;
2069664f4763Szrj int r;
207018de8d7fSPeter Avalos
2071664f4763Szrj if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0 ||
2072664f4763Szrj (r = sshpkt_get_cstring(ssh, &val, NULL)) != 0 ||
2073664f4763Szrj (r = sshpkt_get_end(ssh)) != 0)
2074664f4763Szrj sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
207518de8d7fSPeter Avalos
207618de8d7fSPeter Avalos /* Don't set too many environment variables */
207718de8d7fSPeter Avalos if (s->num_env > 128) {
207818de8d7fSPeter Avalos debug2("Ignoring env request %s: too many env vars", name);
207918de8d7fSPeter Avalos goto fail;
208018de8d7fSPeter Avalos }
208118de8d7fSPeter Avalos
208218de8d7fSPeter Avalos for (i = 0; i < options.num_accept_env; i++) {
208318de8d7fSPeter Avalos if (match_pattern(name, options.accept_env[i])) {
208418de8d7fSPeter Avalos debug2("Setting env %d: %s=%s", s->num_env, name, val);
20852c9c1408SMatthew Dillon s->env = xrecallocarray(s->env, s->num_env,
20862c9c1408SMatthew Dillon s->num_env + 1, sizeof(*s->env));
208718de8d7fSPeter Avalos s->env[s->num_env].name = name;
208818de8d7fSPeter Avalos s->env[s->num_env].val = val;
208918de8d7fSPeter Avalos s->num_env++;
209018de8d7fSPeter Avalos return (1);
209118de8d7fSPeter Avalos }
209218de8d7fSPeter Avalos }
209318de8d7fSPeter Avalos debug2("Ignoring env request %s: disallowed name", name);
209418de8d7fSPeter Avalos
209518de8d7fSPeter Avalos fail:
209636e94dc5SPeter Avalos free(name);
209736e94dc5SPeter Avalos free(val);
209818de8d7fSPeter Avalos return (0);
209918de8d7fSPeter Avalos }
210018de8d7fSPeter Avalos
2101664f4763Szrj /*
2102664f4763Szrj * Conversion of signals from ssh channel request names.
2103664f4763Szrj * Subset of signals from RFC 4254 section 6.10C, with SIGINFO as
2104664f4763Szrj * local extension.
2105664f4763Szrj */
2106664f4763Szrj static int
name2sig(char * name)2107664f4763Szrj name2sig(char *name)
2108664f4763Szrj {
2109664f4763Szrj #define SSH_SIG(x) if (strcmp(name, #x) == 0) return SIG ## x
2110664f4763Szrj SSH_SIG(HUP);
2111664f4763Szrj SSH_SIG(INT);
2112664f4763Szrj SSH_SIG(KILL);
2113664f4763Szrj SSH_SIG(QUIT);
2114664f4763Szrj SSH_SIG(TERM);
2115664f4763Szrj SSH_SIG(USR1);
2116664f4763Szrj SSH_SIG(USR2);
2117664f4763Szrj #undef SSH_SIG
2118664f4763Szrj #ifdef SIGINFO
2119664f4763Szrj if (strcmp(name, "INFO@openssh.com") == 0)
2120664f4763Szrj return SIGINFO;
2121664f4763Szrj #endif
2122664f4763Szrj return -1;
2123664f4763Szrj }
2124664f4763Szrj
2125664f4763Szrj static int
session_signal_req(struct ssh * ssh,Session * s)2126664f4763Szrj session_signal_req(struct ssh *ssh, Session *s)
2127664f4763Szrj {
2128664f4763Szrj char *signame = NULL;
2129664f4763Szrj int r, sig, success = 0;
2130664f4763Szrj
2131664f4763Szrj if ((r = sshpkt_get_cstring(ssh, &signame, NULL)) != 0 ||
2132664f4763Szrj (r = sshpkt_get_end(ssh)) != 0) {
213350a69bb5SSascha Wildner error_fr(r, "parse");
2134664f4763Szrj goto out;
2135664f4763Szrj }
2136664f4763Szrj if ((sig = name2sig(signame)) == -1) {
213750a69bb5SSascha Wildner error_f("unsupported signal \"%s\"", signame);
2138664f4763Szrj goto out;
2139664f4763Szrj }
2140664f4763Szrj if (s->pid <= 0) {
214150a69bb5SSascha Wildner error_f("no pid for session %d", s->self);
2142664f4763Szrj goto out;
2143664f4763Szrj }
2144664f4763Szrj if (s->forced || s->is_subsystem) {
214550a69bb5SSascha Wildner error_f("refusing to send signal %s to %s session",
2146664f4763Szrj signame, s->forced ? "forced-command" : "subsystem");
2147664f4763Szrj goto out;
2148664f4763Szrj }
2149*ba1276acSMatthew Dillon if (mm_is_monitor()) {
215050a69bb5SSascha Wildner error_f("session signalling requires privilege separation");
2151664f4763Szrj goto out;
2152664f4763Szrj }
2153664f4763Szrj
215450a69bb5SSascha Wildner debug_f("signal %s, killpg(%ld, %d)", signame, (long)s->pid, sig);
2155664f4763Szrj temporarily_use_uid(s->pw);
2156664f4763Szrj r = killpg(s->pid, sig);
2157664f4763Szrj restore_uid();
2158664f4763Szrj if (r != 0) {
215950a69bb5SSascha Wildner error_f("killpg(%ld, %d): %s", (long)s->pid,
2160664f4763Szrj sig, strerror(errno));
2161664f4763Szrj goto out;
2162664f4763Szrj }
2163664f4763Szrj
2164664f4763Szrj /* success */
2165664f4763Szrj success = 1;
2166664f4763Szrj out:
2167664f4763Szrj free(signame);
2168664f4763Szrj return success;
2169664f4763Szrj }
2170664f4763Szrj
217118de8d7fSPeter Avalos static int
session_auth_agent_req(struct ssh * ssh,Session * s)21722c9c1408SMatthew Dillon session_auth_agent_req(struct ssh *ssh, Session *s)
217318de8d7fSPeter Avalos {
217418de8d7fSPeter Avalos static int called = 0;
2175664f4763Szrj int r;
2176664f4763Szrj
2177664f4763Szrj if ((r = sshpkt_get_end(ssh)) != 0)
2178664f4763Szrj sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
2179664f4763Szrj if (!auth_opts->permit_agent_forwarding_flag ||
2180664f4763Szrj !options.allow_agent_forwarding) {
218150a69bb5SSascha Wildner debug_f("agent forwarding disabled");
218218de8d7fSPeter Avalos return 0;
218318de8d7fSPeter Avalos }
218418de8d7fSPeter Avalos if (called) {
218518de8d7fSPeter Avalos return 0;
218618de8d7fSPeter Avalos } else {
218718de8d7fSPeter Avalos called = 1;
21882c9c1408SMatthew Dillon return auth_input_request_forwarding(ssh, s->pw);
218918de8d7fSPeter Avalos }
219018de8d7fSPeter Avalos }
219118de8d7fSPeter Avalos
219218de8d7fSPeter Avalos int
session_input_channel_req(struct ssh * ssh,Channel * c,const char * rtype)21932c9c1408SMatthew Dillon session_input_channel_req(struct ssh *ssh, Channel *c, const char *rtype)
219418de8d7fSPeter Avalos {
219518de8d7fSPeter Avalos int success = 0;
219618de8d7fSPeter Avalos Session *s;
219718de8d7fSPeter Avalos
219818de8d7fSPeter Avalos if ((s = session_by_channel(c->self)) == NULL) {
219950a69bb5SSascha Wildner logit_f("no session %d req %.100s", c->self, rtype);
220018de8d7fSPeter Avalos return 0;
220118de8d7fSPeter Avalos }
220250a69bb5SSascha Wildner debug_f("session %d req %s", s->self, rtype);
220318de8d7fSPeter Avalos
220418de8d7fSPeter Avalos /*
220518de8d7fSPeter Avalos * a session is in LARVAL state until a shell, a command
220618de8d7fSPeter Avalos * or a subsystem is executed
220718de8d7fSPeter Avalos */
220818de8d7fSPeter Avalos if (c->type == SSH_CHANNEL_LARVAL) {
220918de8d7fSPeter Avalos if (strcmp(rtype, "shell") == 0) {
22102c9c1408SMatthew Dillon success = session_shell_req(ssh, s);
221118de8d7fSPeter Avalos } else if (strcmp(rtype, "exec") == 0) {
22122c9c1408SMatthew Dillon success = session_exec_req(ssh, s);
221318de8d7fSPeter Avalos } else if (strcmp(rtype, "pty-req") == 0) {
22142c9c1408SMatthew Dillon success = session_pty_req(ssh, s);
221518de8d7fSPeter Avalos } else if (strcmp(rtype, "x11-req") == 0) {
22162c9c1408SMatthew Dillon success = session_x11_req(ssh, s);
221718de8d7fSPeter Avalos } else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) {
22182c9c1408SMatthew Dillon success = session_auth_agent_req(ssh, s);
221918de8d7fSPeter Avalos } else if (strcmp(rtype, "subsystem") == 0) {
22202c9c1408SMatthew Dillon success = session_subsystem_req(ssh, s);
222118de8d7fSPeter Avalos } else if (strcmp(rtype, "env") == 0) {
22222c9c1408SMatthew Dillon success = session_env_req(ssh, s);
222318de8d7fSPeter Avalos }
222418de8d7fSPeter Avalos }
222518de8d7fSPeter Avalos if (strcmp(rtype, "window-change") == 0) {
22262c9c1408SMatthew Dillon success = session_window_change_req(ssh, s);
222718de8d7fSPeter Avalos } else if (strcmp(rtype, "break") == 0) {
22282c9c1408SMatthew Dillon success = session_break_req(ssh, s);
2229664f4763Szrj } else if (strcmp(rtype, "signal") == 0) {
2230664f4763Szrj success = session_signal_req(ssh, s);
223118de8d7fSPeter Avalos }
223218de8d7fSPeter Avalos
223318de8d7fSPeter Avalos return success;
223418de8d7fSPeter Avalos }
223518de8d7fSPeter Avalos
223618de8d7fSPeter Avalos void
session_set_fds(struct ssh * ssh,Session * s,int fdin,int fdout,int fderr,int ignore_fderr,int is_tty)22372c9c1408SMatthew Dillon session_set_fds(struct ssh *ssh, Session *s,
22382c9c1408SMatthew Dillon int fdin, int fdout, int fderr, int ignore_fderr, int is_tty)
223918de8d7fSPeter Avalos {
224018de8d7fSPeter Avalos /*
224118de8d7fSPeter Avalos * now that have a child and a pipe to the child,
224218de8d7fSPeter Avalos * we can activate our channel and register the fd's
224318de8d7fSPeter Avalos */
224418de8d7fSPeter Avalos if (s->chanid == -1)
224518de8d7fSPeter Avalos fatal("no channel for session %d", s->self);
22462c9c1408SMatthew Dillon channel_set_fds(ssh, s->chanid,
224718de8d7fSPeter Avalos fdout, fdin, fderr,
2248856ea928SPeter Avalos ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
224918de8d7fSPeter Avalos 1, is_tty, CHAN_SES_WINDOW_DEFAULT);
225018de8d7fSPeter Avalos }
225118de8d7fSPeter Avalos
225218de8d7fSPeter Avalos /*
225318de8d7fSPeter Avalos * Function to perform pty cleanup. Also called if we get aborted abnormally
225418de8d7fSPeter Avalos * (e.g., due to a dropped connection).
225518de8d7fSPeter Avalos */
225618de8d7fSPeter Avalos void
session_pty_cleanup2(Session * s)225718de8d7fSPeter Avalos session_pty_cleanup2(Session *s)
225818de8d7fSPeter Avalos {
225918de8d7fSPeter Avalos if (s == NULL) {
226050a69bb5SSascha Wildner error_f("no session");
226118de8d7fSPeter Avalos return;
226218de8d7fSPeter Avalos }
226318de8d7fSPeter Avalos if (s->ttyfd == -1)
226418de8d7fSPeter Avalos return;
226518de8d7fSPeter Avalos
226650a69bb5SSascha Wildner debug_f("session %d release %s", s->self, s->tty);
226718de8d7fSPeter Avalos
226818de8d7fSPeter Avalos /* Record that the user has logged out. */
226918de8d7fSPeter Avalos if (s->pid != 0)
227018de8d7fSPeter Avalos record_logout(s->pid, s->tty, s->pw->pw_name);
227118de8d7fSPeter Avalos
227218de8d7fSPeter Avalos /* Release the pseudo-tty. */
227318de8d7fSPeter Avalos if (getuid() == 0)
227418de8d7fSPeter Avalos pty_release(s->tty);
227518de8d7fSPeter Avalos
227618de8d7fSPeter Avalos /*
227718de8d7fSPeter Avalos * Close the server side of the socket pairs. We must do this after
227818de8d7fSPeter Avalos * the pty cleanup, so that another process doesn't get this pty
227918de8d7fSPeter Avalos * while we're still cleaning up.
228018de8d7fSPeter Avalos */
22810cbfa66cSDaniel Fojt if (s->ptymaster != -1 && close(s->ptymaster) == -1)
228218de8d7fSPeter Avalos error("close(s->ptymaster/%d): %s",
228318de8d7fSPeter Avalos s->ptymaster, strerror(errno));
228418de8d7fSPeter Avalos
228518de8d7fSPeter Avalos /* unlink pty from session */
228618de8d7fSPeter Avalos s->ttyfd = -1;
228718de8d7fSPeter Avalos }
228818de8d7fSPeter Avalos
228918de8d7fSPeter Avalos void
session_pty_cleanup(Session * s)229018de8d7fSPeter Avalos session_pty_cleanup(Session *s)
229118de8d7fSPeter Avalos {
2292*ba1276acSMatthew Dillon mm_session_pty_cleanup2(s);
229318de8d7fSPeter Avalos }
229418de8d7fSPeter Avalos
229518de8d7fSPeter Avalos static char *
sig2name(int sig)229618de8d7fSPeter Avalos sig2name(int sig)
229718de8d7fSPeter Avalos {
229818de8d7fSPeter Avalos #define SSH_SIG(x) if (sig == SIG ## x) return #x
229918de8d7fSPeter Avalos SSH_SIG(ABRT);
230018de8d7fSPeter Avalos SSH_SIG(ALRM);
230118de8d7fSPeter Avalos SSH_SIG(FPE);
230218de8d7fSPeter Avalos SSH_SIG(HUP);
230318de8d7fSPeter Avalos SSH_SIG(ILL);
230418de8d7fSPeter Avalos SSH_SIG(INT);
230518de8d7fSPeter Avalos SSH_SIG(KILL);
230618de8d7fSPeter Avalos SSH_SIG(PIPE);
230718de8d7fSPeter Avalos SSH_SIG(QUIT);
230818de8d7fSPeter Avalos SSH_SIG(SEGV);
230918de8d7fSPeter Avalos SSH_SIG(TERM);
231018de8d7fSPeter Avalos SSH_SIG(USR1);
231118de8d7fSPeter Avalos SSH_SIG(USR2);
231218de8d7fSPeter Avalos #undef SSH_SIG
231318de8d7fSPeter Avalos return "SIG@openssh.com";
231418de8d7fSPeter Avalos }
231518de8d7fSPeter Avalos
231618de8d7fSPeter Avalos static void
session_close_x11(struct ssh * ssh,int id)23172c9c1408SMatthew Dillon session_close_x11(struct ssh *ssh, int id)
231818de8d7fSPeter Avalos {
231918de8d7fSPeter Avalos Channel *c;
232018de8d7fSPeter Avalos
23212c9c1408SMatthew Dillon if ((c = channel_by_id(ssh, id)) == NULL) {
232250a69bb5SSascha Wildner debug_f("x11 channel %d missing", id);
232318de8d7fSPeter Avalos } else {
232418de8d7fSPeter Avalos /* Detach X11 listener */
232550a69bb5SSascha Wildner debug_f("detach x11 channel %d", id);
23262c9c1408SMatthew Dillon channel_cancel_cleanup(ssh, id);
232718de8d7fSPeter Avalos if (c->ostate != CHAN_OUTPUT_CLOSED)
23282c9c1408SMatthew Dillon chan_mark_dead(ssh, c);
232918de8d7fSPeter Avalos }
233018de8d7fSPeter Avalos }
233118de8d7fSPeter Avalos
233218de8d7fSPeter Avalos static void
session_close_single_x11(struct ssh * ssh,int id,int force,void * arg)2333*ba1276acSMatthew Dillon session_close_single_x11(struct ssh *ssh, int id, int force, void *arg)
233418de8d7fSPeter Avalos {
233518de8d7fSPeter Avalos Session *s;
233618de8d7fSPeter Avalos u_int i;
233718de8d7fSPeter Avalos
233850a69bb5SSascha Wildner debug3_f("channel %d", id);
23392c9c1408SMatthew Dillon channel_cancel_cleanup(ssh, id);
234018de8d7fSPeter Avalos if ((s = session_by_x11_channel(id)) == NULL)
234150a69bb5SSascha Wildner fatal_f("no x11 channel %d", id);
234218de8d7fSPeter Avalos for (i = 0; s->x11_chanids[i] != -1; i++) {
234350a69bb5SSascha Wildner debug_f("session %d: closing channel %d",
234450a69bb5SSascha Wildner s->self, s->x11_chanids[i]);
234518de8d7fSPeter Avalos /*
234618de8d7fSPeter Avalos * The channel "id" is already closing, but make sure we
234718de8d7fSPeter Avalos * close all of its siblings.
234818de8d7fSPeter Avalos */
234918de8d7fSPeter Avalos if (s->x11_chanids[i] != id)
23502c9c1408SMatthew Dillon session_close_x11(ssh, s->x11_chanids[i]);
235118de8d7fSPeter Avalos }
235236e94dc5SPeter Avalos free(s->x11_chanids);
235318de8d7fSPeter Avalos s->x11_chanids = NULL;
235436e94dc5SPeter Avalos free(s->display);
235518de8d7fSPeter Avalos s->display = NULL;
235636e94dc5SPeter Avalos free(s->auth_proto);
235718de8d7fSPeter Avalos s->auth_proto = NULL;
235836e94dc5SPeter Avalos free(s->auth_data);
235918de8d7fSPeter Avalos s->auth_data = NULL;
236036e94dc5SPeter Avalos free(s->auth_display);
236118de8d7fSPeter Avalos s->auth_display = NULL;
236218de8d7fSPeter Avalos }
236318de8d7fSPeter Avalos
236418de8d7fSPeter Avalos static void
session_exit_message(struct ssh * ssh,Session * s,int status)23652c9c1408SMatthew Dillon session_exit_message(struct ssh *ssh, Session *s, int status)
236618de8d7fSPeter Avalos {
236718de8d7fSPeter Avalos Channel *c;
2368664f4763Szrj int r;
2369*ba1276acSMatthew Dillon char *note = NULL;
237018de8d7fSPeter Avalos
23712c9c1408SMatthew Dillon if ((c = channel_lookup(ssh, s->chanid)) == NULL)
237250a69bb5SSascha Wildner fatal_f("session %d: no channel %d", s->self, s->chanid);
237318de8d7fSPeter Avalos
237418de8d7fSPeter Avalos if (WIFEXITED(status)) {
23752c9c1408SMatthew Dillon channel_request_start(ssh, s->chanid, "exit-status", 0);
2376664f4763Szrj if ((r = sshpkt_put_u32(ssh, WEXITSTATUS(status))) != 0 ||
2377664f4763Szrj (r = sshpkt_send(ssh)) != 0)
2378664f4763Szrj sshpkt_fatal(ssh, r, "%s: exit reply", __func__);
2379*ba1276acSMatthew Dillon xasprintf(¬e, "exit %d", WEXITSTATUS(status));
238018de8d7fSPeter Avalos } else if (WIFSIGNALED(status)) {
23812c9c1408SMatthew Dillon channel_request_start(ssh, s->chanid, "exit-signal", 0);
2382664f4763Szrj #ifndef WCOREDUMP
2383664f4763Szrj # define WCOREDUMP(x) (0)
2384664f4763Szrj #endif
2385664f4763Szrj if ((r = sshpkt_put_cstring(ssh, sig2name(WTERMSIG(status)))) != 0 ||
2386664f4763Szrj (r = sshpkt_put_u8(ssh, WCOREDUMP(status)? 1 : 0)) != 0 ||
2387664f4763Szrj (r = sshpkt_put_cstring(ssh, "")) != 0 ||
2388664f4763Szrj (r = sshpkt_put_cstring(ssh, "")) != 0 ||
2389664f4763Szrj (r = sshpkt_send(ssh)) != 0)
2390664f4763Szrj sshpkt_fatal(ssh, r, "%s: exit reply", __func__);
2391*ba1276acSMatthew Dillon xasprintf(¬e, "signal %d%s", WTERMSIG(status),
2392*ba1276acSMatthew Dillon WCOREDUMP(status) ? " core dumped" : "");
239318de8d7fSPeter Avalos } else {
239418de8d7fSPeter Avalos /* Some weird exit cause. Just exit. */
2395*ba1276acSMatthew Dillon ssh_packet_disconnect(ssh, "wait returned status %04x.",
2396*ba1276acSMatthew Dillon status);
239718de8d7fSPeter Avalos }
239818de8d7fSPeter Avalos
2399*ba1276acSMatthew Dillon debug_f("session %d channel %d pid %ld %s", s->self, s->chanid,
2400*ba1276acSMatthew Dillon (long)s->pid, note == NULL ? "UNKNOWN" : note);
2401*ba1276acSMatthew Dillon free(note);
2402*ba1276acSMatthew Dillon
240318de8d7fSPeter Avalos /* disconnect channel */
240450a69bb5SSascha Wildner debug_f("release channel %d", s->chanid);
240518de8d7fSPeter Avalos
240618de8d7fSPeter Avalos /*
240718de8d7fSPeter Avalos * Adjust cleanup callback attachment to send close messages when
240818de8d7fSPeter Avalos * the channel gets EOF. The session will be then be closed
24090cbfa66cSDaniel Fojt * by session_close_by_channel when the child sessions close their fds.
241018de8d7fSPeter Avalos */
24112c9c1408SMatthew Dillon channel_register_cleanup(ssh, c->self, session_close_by_channel, 1);
241218de8d7fSPeter Avalos
241318de8d7fSPeter Avalos /*
241418de8d7fSPeter Avalos * emulate a write failure with 'chan_write_failed', nobody will be
241518de8d7fSPeter Avalos * interested in data we write.
241618de8d7fSPeter Avalos * Note that we must not call 'chan_read_failed', since there could
241718de8d7fSPeter Avalos * be some more data waiting in the pipe.
241818de8d7fSPeter Avalos */
241918de8d7fSPeter Avalos if (c->ostate != CHAN_OUTPUT_CLOSED)
24202c9c1408SMatthew Dillon chan_write_failed(ssh, c);
242118de8d7fSPeter Avalos }
242218de8d7fSPeter Avalos
242318de8d7fSPeter Avalos void
session_close(struct ssh * ssh,Session * s)24242c9c1408SMatthew Dillon session_close(struct ssh *ssh, Session *s)
242518de8d7fSPeter Avalos {
242618de8d7fSPeter Avalos u_int i;
242718de8d7fSPeter Avalos
2428e9778795SPeter Avalos verbose("Close session: user %s from %.200s port %d id %d",
2429e9778795SPeter Avalos s->pw->pw_name,
2430e9778795SPeter Avalos ssh_remote_ipaddr(ssh),
2431e9778795SPeter Avalos ssh_remote_port(ssh),
2432e9778795SPeter Avalos s->self);
2433e9778795SPeter Avalos
243418de8d7fSPeter Avalos if (s->ttyfd != -1)
243518de8d7fSPeter Avalos session_pty_cleanup(s);
243636e94dc5SPeter Avalos free(s->term);
243736e94dc5SPeter Avalos free(s->display);
243836e94dc5SPeter Avalos free(s->x11_chanids);
243936e94dc5SPeter Avalos free(s->auth_display);
244036e94dc5SPeter Avalos free(s->auth_data);
244136e94dc5SPeter Avalos free(s->auth_proto);
244236e94dc5SPeter Avalos free(s->subsys);
244318de8d7fSPeter Avalos if (s->env != NULL) {
244418de8d7fSPeter Avalos for (i = 0; i < s->num_env; i++) {
244536e94dc5SPeter Avalos free(s->env[i].name);
244636e94dc5SPeter Avalos free(s->env[i].val);
244718de8d7fSPeter Avalos }
244836e94dc5SPeter Avalos free(s->env);
244918de8d7fSPeter Avalos }
245018de8d7fSPeter Avalos session_proctitle(s);
245118de8d7fSPeter Avalos session_unused(s->self);
245218de8d7fSPeter Avalos }
245318de8d7fSPeter Avalos
245418de8d7fSPeter Avalos void
session_close_by_pid(struct ssh * ssh,pid_t pid,int status)24552c9c1408SMatthew Dillon session_close_by_pid(struct ssh *ssh, pid_t pid, int status)
245618de8d7fSPeter Avalos {
245718de8d7fSPeter Avalos Session *s = session_by_pid(pid);
245818de8d7fSPeter Avalos if (s == NULL) {
245950a69bb5SSascha Wildner debug_f("no session for pid %ld", (long)pid);
246018de8d7fSPeter Avalos return;
246118de8d7fSPeter Avalos }
246218de8d7fSPeter Avalos if (s->chanid != -1)
24632c9c1408SMatthew Dillon session_exit_message(ssh, s, status);
246418de8d7fSPeter Avalos if (s->ttyfd != -1)
246518de8d7fSPeter Avalos session_pty_cleanup(s);
246618de8d7fSPeter Avalos s->pid = 0;
246718de8d7fSPeter Avalos }
246818de8d7fSPeter Avalos
246918de8d7fSPeter Avalos /*
247018de8d7fSPeter Avalos * this is called when a channel dies before
247118de8d7fSPeter Avalos * the session 'child' itself dies
247218de8d7fSPeter Avalos */
247318de8d7fSPeter Avalos void
session_close_by_channel(struct ssh * ssh,int id,int force,void * arg)2474*ba1276acSMatthew Dillon session_close_by_channel(struct ssh *ssh, int id, int force, void *arg)
247518de8d7fSPeter Avalos {
247618de8d7fSPeter Avalos Session *s = session_by_channel(id);
247718de8d7fSPeter Avalos u_int i;
247818de8d7fSPeter Avalos
247918de8d7fSPeter Avalos if (s == NULL) {
248050a69bb5SSascha Wildner debug_f("no session for id %d", id);
248118de8d7fSPeter Avalos return;
248218de8d7fSPeter Avalos }
248350a69bb5SSascha Wildner debug_f("channel %d child %ld", id, (long)s->pid);
248418de8d7fSPeter Avalos if (s->pid != 0) {
248550a69bb5SSascha Wildner debug_f("channel %d: has child, ttyfd %d", id, s->ttyfd);
248618de8d7fSPeter Avalos /*
2487*ba1276acSMatthew Dillon * delay detach of session (unless this is a forced close),
2488*ba1276acSMatthew Dillon * but release pty, since the fd's to the child are already
2489*ba1276acSMatthew Dillon * closed
249018de8d7fSPeter Avalos */
249118de8d7fSPeter Avalos if (s->ttyfd != -1)
249218de8d7fSPeter Avalos session_pty_cleanup(s);
2493*ba1276acSMatthew Dillon if (!force)
249418de8d7fSPeter Avalos return;
249518de8d7fSPeter Avalos }
249618de8d7fSPeter Avalos /* detach by removing callback */
24972c9c1408SMatthew Dillon channel_cancel_cleanup(ssh, s->chanid);
249818de8d7fSPeter Avalos
249918de8d7fSPeter Avalos /* Close any X11 listeners associated with this session */
250018de8d7fSPeter Avalos if (s->x11_chanids != NULL) {
250118de8d7fSPeter Avalos for (i = 0; s->x11_chanids[i] != -1; i++) {
25022c9c1408SMatthew Dillon session_close_x11(ssh, s->x11_chanids[i]);
250318de8d7fSPeter Avalos s->x11_chanids[i] = -1;
250418de8d7fSPeter Avalos }
250518de8d7fSPeter Avalos }
250618de8d7fSPeter Avalos
250718de8d7fSPeter Avalos s->chanid = -1;
25082c9c1408SMatthew Dillon session_close(ssh, s);
250918de8d7fSPeter Avalos }
251018de8d7fSPeter Avalos
251118de8d7fSPeter Avalos void
session_destroy_all(struct ssh * ssh,void (* closefunc)(Session *))25122c9c1408SMatthew Dillon session_destroy_all(struct ssh *ssh, void (*closefunc)(Session *))
251318de8d7fSPeter Avalos {
251418de8d7fSPeter Avalos int i;
251518de8d7fSPeter Avalos for (i = 0; i < sessions_nalloc; i++) {
251618de8d7fSPeter Avalos Session *s = &sessions[i];
251718de8d7fSPeter Avalos if (s->used) {
251818de8d7fSPeter Avalos if (closefunc != NULL)
251918de8d7fSPeter Avalos closefunc(s);
252018de8d7fSPeter Avalos else
25212c9c1408SMatthew Dillon session_close(ssh, s);
252218de8d7fSPeter Avalos }
252318de8d7fSPeter Avalos }
252418de8d7fSPeter Avalos }
252518de8d7fSPeter Avalos
252618de8d7fSPeter Avalos static char *
session_tty_list(void)252718de8d7fSPeter Avalos session_tty_list(void)
252818de8d7fSPeter Avalos {
252918de8d7fSPeter Avalos static char buf[1024];
253018de8d7fSPeter Avalos int i;
253118de8d7fSPeter Avalos char *cp;
253218de8d7fSPeter Avalos
253318de8d7fSPeter Avalos buf[0] = '\0';
253418de8d7fSPeter Avalos for (i = 0; i < sessions_nalloc; i++) {
253518de8d7fSPeter Avalos Session *s = &sessions[i];
253618de8d7fSPeter Avalos if (s->used && s->ttyfd != -1) {
253718de8d7fSPeter Avalos
253818de8d7fSPeter Avalos if (strncmp(s->tty, "/dev/", 5) != 0) {
253918de8d7fSPeter Avalos cp = strrchr(s->tty, '/');
254018de8d7fSPeter Avalos cp = (cp == NULL) ? s->tty : cp + 1;
254118de8d7fSPeter Avalos } else
254218de8d7fSPeter Avalos cp = s->tty + 5;
254318de8d7fSPeter Avalos
254418de8d7fSPeter Avalos if (buf[0] != '\0')
254518de8d7fSPeter Avalos strlcat(buf, ",", sizeof buf);
254618de8d7fSPeter Avalos strlcat(buf, cp, sizeof buf);
254718de8d7fSPeter Avalos }
254818de8d7fSPeter Avalos }
254918de8d7fSPeter Avalos if (buf[0] == '\0')
255018de8d7fSPeter Avalos strlcpy(buf, "notty", sizeof buf);
255118de8d7fSPeter Avalos return buf;
255218de8d7fSPeter Avalos }
255318de8d7fSPeter Avalos
255418de8d7fSPeter Avalos void
session_proctitle(Session * s)255518de8d7fSPeter Avalos session_proctitle(Session *s)
255618de8d7fSPeter Avalos {
255718de8d7fSPeter Avalos if (s->pw == NULL)
255818de8d7fSPeter Avalos error("no user for session %d", s->self);
255918de8d7fSPeter Avalos else
256018de8d7fSPeter Avalos setproctitle("%s@%s", s->pw->pw_name, session_tty_list());
256118de8d7fSPeter Avalos }
256218de8d7fSPeter Avalos
256318de8d7fSPeter Avalos int
session_setup_x11fwd(struct ssh * ssh,Session * s)25642c9c1408SMatthew Dillon session_setup_x11fwd(struct ssh *ssh, Session *s)
256518de8d7fSPeter Avalos {
256618de8d7fSPeter Avalos struct stat st;
256718de8d7fSPeter Avalos char display[512], auth_display[512];
256836e94dc5SPeter Avalos char hostname[NI_MAXHOST];
256918de8d7fSPeter Avalos u_int i;
257018de8d7fSPeter Avalos
2571664f4763Szrj if (!auth_opts->permit_x11_forwarding_flag) {
2572664f4763Szrj ssh_packet_send_debug(ssh, "X11 forwarding disabled by key options.");
257318de8d7fSPeter Avalos return 0;
257418de8d7fSPeter Avalos }
257518de8d7fSPeter Avalos if (!options.x11_forwarding) {
257618de8d7fSPeter Avalos debug("X11 forwarding disabled in server configuration file.");
257718de8d7fSPeter Avalos return 0;
257818de8d7fSPeter Avalos }
2579e9778795SPeter Avalos if (options.xauth_location == NULL ||
258018de8d7fSPeter Avalos (stat(options.xauth_location, &st) == -1)) {
2581664f4763Szrj ssh_packet_send_debug(ssh, "No xauth program; cannot forward X11.");
258218de8d7fSPeter Avalos return 0;
258318de8d7fSPeter Avalos }
258418de8d7fSPeter Avalos if (s->display != NULL) {
258518de8d7fSPeter Avalos debug("X11 display already set.");
258618de8d7fSPeter Avalos return 0;
258718de8d7fSPeter Avalos }
25882c9c1408SMatthew Dillon if (x11_create_display_inet(ssh, options.x11_display_offset,
258918de8d7fSPeter Avalos options.x11_use_localhost, s->single_connection,
259018de8d7fSPeter Avalos &s->display_number, &s->x11_chanids) == -1) {
259118de8d7fSPeter Avalos debug("x11_create_display_inet failed.");
259218de8d7fSPeter Avalos return 0;
259318de8d7fSPeter Avalos }
259418de8d7fSPeter Avalos for (i = 0; s->x11_chanids[i] != -1; i++) {
25952c9c1408SMatthew Dillon channel_register_cleanup(ssh, s->x11_chanids[i],
259618de8d7fSPeter Avalos session_close_single_x11, 0);
259718de8d7fSPeter Avalos }
259818de8d7fSPeter Avalos
259918de8d7fSPeter Avalos /* Set up a suitable value for the DISPLAY variable. */
26000cbfa66cSDaniel Fojt if (gethostname(hostname, sizeof(hostname)) == -1)
260118de8d7fSPeter Avalos fatal("gethostname: %.100s", strerror(errno));
260218de8d7fSPeter Avalos /*
260318de8d7fSPeter Avalos * auth_display must be used as the displayname when the
260418de8d7fSPeter Avalos * authorization entry is added with xauth(1). This will be
260518de8d7fSPeter Avalos * different than the DISPLAY string for localhost displays.
260618de8d7fSPeter Avalos */
260718de8d7fSPeter Avalos if (options.x11_use_localhost) {
260818de8d7fSPeter Avalos snprintf(display, sizeof display, "localhost:%u.%u",
260918de8d7fSPeter Avalos s->display_number, s->screen);
261018de8d7fSPeter Avalos snprintf(auth_display, sizeof auth_display, "unix:%u.%u",
261118de8d7fSPeter Avalos s->display_number, s->screen);
261218de8d7fSPeter Avalos s->display = xstrdup(display);
261318de8d7fSPeter Avalos s->auth_display = xstrdup(auth_display);
261418de8d7fSPeter Avalos } else {
261518de8d7fSPeter Avalos #ifdef IPADDR_IN_DISPLAY
261618de8d7fSPeter Avalos struct hostent *he;
261718de8d7fSPeter Avalos struct in_addr my_addr;
261818de8d7fSPeter Avalos
261918de8d7fSPeter Avalos he = gethostbyname(hostname);
262018de8d7fSPeter Avalos if (he == NULL) {
262118de8d7fSPeter Avalos error("Can't get IP address for X11 DISPLAY.");
2622664f4763Szrj ssh_packet_send_debug(ssh, "Can't get IP address for X11 DISPLAY.");
262318de8d7fSPeter Avalos return 0;
262418de8d7fSPeter Avalos }
262518de8d7fSPeter Avalos memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr));
262618de8d7fSPeter Avalos snprintf(display, sizeof display, "%.50s:%u.%u", inet_ntoa(my_addr),
262718de8d7fSPeter Avalos s->display_number, s->screen);
262818de8d7fSPeter Avalos #else
262918de8d7fSPeter Avalos snprintf(display, sizeof display, "%.400s:%u.%u", hostname,
263018de8d7fSPeter Avalos s->display_number, s->screen);
263118de8d7fSPeter Avalos #endif
263218de8d7fSPeter Avalos s->display = xstrdup(display);
263318de8d7fSPeter Avalos s->auth_display = xstrdup(display);
263418de8d7fSPeter Avalos }
263518de8d7fSPeter Avalos
263618de8d7fSPeter Avalos return 1;
263718de8d7fSPeter Avalos }
263818de8d7fSPeter Avalos
263918de8d7fSPeter Avalos static void
do_authenticated2(struct ssh * ssh,Authctxt * authctxt)26402c9c1408SMatthew Dillon do_authenticated2(struct ssh *ssh, Authctxt *authctxt)
264118de8d7fSPeter Avalos {
26422c9c1408SMatthew Dillon server_loop2(ssh, authctxt);
264318de8d7fSPeter Avalos }
264418de8d7fSPeter Avalos
264518de8d7fSPeter Avalos void
do_cleanup(struct ssh * ssh,Authctxt * authctxt)26462c9c1408SMatthew Dillon do_cleanup(struct ssh *ssh, Authctxt *authctxt)
264718de8d7fSPeter Avalos {
264818de8d7fSPeter Avalos static int called = 0;
264918de8d7fSPeter Avalos
265018de8d7fSPeter Avalos debug("do_cleanup");
265118de8d7fSPeter Avalos
265218de8d7fSPeter Avalos /* no cleanup if we're in the child for login shell */
265318de8d7fSPeter Avalos if (is_child)
265418de8d7fSPeter Avalos return;
265518de8d7fSPeter Avalos
265618de8d7fSPeter Avalos /* avoid double cleanup */
265718de8d7fSPeter Avalos if (called)
265818de8d7fSPeter Avalos return;
265918de8d7fSPeter Avalos called = 1;
266018de8d7fSPeter Avalos
266118de8d7fSPeter Avalos if (authctxt == NULL)
266218de8d7fSPeter Avalos return;
266318de8d7fSPeter Avalos
266418de8d7fSPeter Avalos #ifdef USE_PAM
266518de8d7fSPeter Avalos if (options.use_pam) {
266618de8d7fSPeter Avalos sshpam_cleanup();
266718de8d7fSPeter Avalos sshpam_thread_cleanup();
266818de8d7fSPeter Avalos }
266918de8d7fSPeter Avalos #endif
267018de8d7fSPeter Avalos
267118de8d7fSPeter Avalos if (!authctxt->authenticated)
267218de8d7fSPeter Avalos return;
267318de8d7fSPeter Avalos
267418de8d7fSPeter Avalos #ifdef KRB5
267518de8d7fSPeter Avalos if (options.kerberos_ticket_cleanup &&
267618de8d7fSPeter Avalos authctxt->krb5_ctx)
267718de8d7fSPeter Avalos krb5_cleanup_proc(authctxt);
267818de8d7fSPeter Avalos #endif
267918de8d7fSPeter Avalos
268018de8d7fSPeter Avalos #ifdef GSSAPI
26812c9c1408SMatthew Dillon if (options.gss_cleanup_creds)
268218de8d7fSPeter Avalos ssh_gssapi_cleanup_creds();
268318de8d7fSPeter Avalos #endif
268418de8d7fSPeter Avalos
268518de8d7fSPeter Avalos /* remove agent socket */
268618de8d7fSPeter Avalos auth_sock_cleanup_proc(authctxt->pw);
268718de8d7fSPeter Avalos
26882c9c1408SMatthew Dillon /* remove userauth info */
26892c9c1408SMatthew Dillon if (auth_info_file != NULL) {
26902c9c1408SMatthew Dillon temporarily_use_uid(authctxt->pw);
26912c9c1408SMatthew Dillon unlink(auth_info_file);
26922c9c1408SMatthew Dillon restore_uid();
26932c9c1408SMatthew Dillon free(auth_info_file);
26942c9c1408SMatthew Dillon auth_info_file = NULL;
26952c9c1408SMatthew Dillon }
26962c9c1408SMatthew Dillon
269718de8d7fSPeter Avalos /*
269818de8d7fSPeter Avalos * Cleanup ptys/utmp only if privsep is disabled,
269918de8d7fSPeter Avalos * or if running in monitor.
270018de8d7fSPeter Avalos */
2701*ba1276acSMatthew Dillon if (mm_is_monitor())
27022c9c1408SMatthew Dillon session_destroy_all(ssh, session_pty_cleanup2);
270318de8d7fSPeter Avalos }
2704e9778795SPeter Avalos
2705e9778795SPeter Avalos /* Return a name for the remote host that fits inside utmp_size */
2706e9778795SPeter Avalos
2707e9778795SPeter Avalos const char *
session_get_remote_name_or_ip(struct ssh * ssh,u_int utmp_size,int use_dns)2708e9778795SPeter Avalos session_get_remote_name_or_ip(struct ssh *ssh, u_int utmp_size, int use_dns)
2709e9778795SPeter Avalos {
2710e9778795SPeter Avalos const char *remote = "";
2711e9778795SPeter Avalos
2712e9778795SPeter Avalos if (utmp_size > 0)
2713e9778795SPeter Avalos remote = auth_get_canonical_hostname(ssh, use_dns);
2714e9778795SPeter Avalos if (utmp_size == 0 || strlen(remote) > utmp_size)
2715e9778795SPeter Avalos remote = ssh_remote_ipaddr(ssh);
2716e9778795SPeter Avalos return remote;
2717e9778795SPeter Avalos }
2718e9778795SPeter Avalos
2719