diff options
author | Colin Watson <cjwatson@debian.org> | 2008-07-22 19:45:18 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2008-07-22 19:45:18 +0000 |
commit | 137d76ba65883aa8143af1fcad83b57e7badef0c (patch) | |
tree | f426e804bb5248ceafedfab7bb78ae6e6752942c /session.c | |
parent | dac7d049dad31f5f84d421d4eb628a7e13f977d7 (diff) | |
parent | ef94e5613d37bcbf880f21ee6094e4b1c7683a4c (diff) |
* New upstream release (closes: #474301). Important changes not previously
backported to 4.7p1:
- 4.9/4.9p1 (http://www.openssh.com/txt/release-4.9):
+ Added chroot(2) support for sshd(8), controlled by a new option
"ChrootDirectory" (closes: #139047, LP: #24777).
+ Linked sftp-server(8) into sshd(8). The internal sftp server is used
when the command "internal-sftp" is specified in a Subsystem or
ForceCommand declaration. When used with ChrootDirectory, the
internal sftp server requires no special configuration of files
inside the chroot environment.
+ Added a protocol extension method "posix-rename@openssh.com" for
sftp-server(8) to perform POSIX atomic rename() operations; sftp(1)
prefers this if available (closes: #308561).
+ Removed the fixed limit of 100 file handles in sftp-server(8).
+ ssh(8) will now skip generation of SSH protocol 1 ephemeral server
keys when in inetd mode and protocol 2 connections are negotiated.
This speeds up protocol 2 connections to inetd-mode servers that
also allow Protocol 1.
+ Accept the PermitRootLogin directive in a sshd_config(5) Match
block. Allows for, e.g. permitting root only from the local network.
+ Reworked sftp(1) argument splitting and escaping to be more
internally consistent (i.e. between sftp commands) and more
consistent with sh(1). Please note that this will change the
interpretation of some quoted strings, especially those with
embedded backslash escape sequences.
+ Support "Banner=none" in sshd_config(5) to disable sending of a
pre-login banner (e.g. in a Match block).
+ ssh(1) ProxyCommands are now executed with $SHELL rather than
/bin/sh.
+ ssh(1)'s ConnectTimeout option is now applied to both the TCP
connection and the SSH banner exchange (previously it just covered
the TCP connection). This allows callers of ssh(1) to better detect
and deal with stuck servers that accept a TCP connection but don't
progress the protocol, and also makes ConnectTimeout useful for
connections via a ProxyCommand.
+ scp(1) incorrectly reported "stalled" on slow copies (closes:
#140828).
+ scp(1) date underflow for timestamps before epoch.
+ ssh(1) used the obsolete SIG DNS RRtype for host keys in DNS,
instead of the current standard RRSIG.
+ Correctly drain ACKs when a sftp(1) upload write fails midway,
avoids a fatal() exit from what should be a recoverable condition.
+ Fixed ssh-keygen(1) selective host key hashing (i.e. "ssh-keygen -HF
hostname") to not include any IP address in the data to be hashed.
+ Make ssh(1) skip listening on the IPv6 wildcard address when a
binding address of 0.0.0.0 is used against an old SSH server that
does not support the RFC4254 syntax for wildcard bind addresses.
+ Enable IPV6_V6ONLY socket option on sshd(8) listen socket, as is
already done for X11/TCP forwarding sockets (closes: #439661).
+ Fix FD leak that could hang a ssh(1) connection multiplexing master.
+ Make ssh(1) -q option documentation consistent with reality.
+ Fixed sshd(8) PAM support not calling pam_session_close(), or
failing to call it with root privileges (closes: #372680).
+ Fix activation of OpenSSL engine support when requested in configure
(LP: #119295).
- 5.1/5.1p1 (http://www.openssh.com/txt/release-5.1):
+ Introduce experimental SSH Fingerprint ASCII Visualisation to ssh(1)
and ssh-keygen(1). Visual fingerprint display is controlled by a new
ssh_config(5) option "VisualHostKey". The intent is to render SSH
host keys in a visual form that is amenable to easy recall and
rejection of changed host keys.
+ sshd_config(5) now supports CIDR address/masklen matching in "Match
address" blocks, with a fallback to classic wildcard matching.
+ sshd(8) now supports CIDR matching in ~/.ssh/authorized_keys
from="..." restrictions, also with a fallback to classic wildcard
matching.
+ Added an extended test mode (-T) to sshd(8) to request that it write
its effective configuration to stdout and exit. Extended test mode
also supports the specification of connection parameters (username,
source address and hostname) to test the application of
sshd_config(5) Match rules.
+ ssh(1) now prints the number of bytes transferred and the overall
connection throughput for SSH protocol 2 sessions when in verbose
mode (previously these statistics were displayed for protocol 1
connections only).
+ sftp-server(8) now supports extension methods statvfs@openssh.com
and fstatvfs@openssh.com that implement statvfs(2)-like operations.
+ sftp(1) now has a "df" command to the sftp client that uses the
statvfs@openssh.com to produce a df(1)-like display of filesystem
space and inode utilisation (requires statvfs@openssh.com support on
the server).
+ Added a MaxSessions option to sshd_config(5) to allow control of the
number of multiplexed sessions supported over a single TCP
connection. This allows increasing the number of allowed sessions
above the previous default of 10, disabling connection multiplexing
(MaxSessions=1) or disallowing login/shell/subsystem sessions
entirely (MaxSessions=0).
+ Added a no-more-sessions@openssh.com global request extension that
is sent from ssh(1) to sshd(8) when the client knows that it will
never request another session (i.e. when session multiplexing is
disabled). This allows a server to disallow further session requests
and terminate the session in cases where the client has been
hijacked.
+ ssh-keygen(1) now supports the use of the -l option in combination
with -F to search for a host in ~/.ssh/known_hosts and display its
fingerprint.
+ ssh-keyscan(1) now defaults to "rsa" (protocol 2) keys, instead of
"rsa1".
+ Added an AllowAgentForwarding option to sshd_config(8) to control
whether authentication agent forwarding is permitted. Note that this
is a loose control, as a client may install their own unofficial
forwarder.
+ ssh(1) and sshd(8): avoid unnecessary malloc/copy/free when
receiving network data, resulting in a ~10% speedup.
+ ssh(1) and sshd(8) will now try additional addresses when connecting
to a port forward destination whose DNS name resolves to more than
one address. The previous behaviour was to try the only first
address and give up if that failed.
+ ssh(1) and sshd(8) now support signalling that channels are
half-closed for writing, through a channel protocol extension
notification "eow@openssh.com". This allows propagation of closed
file descriptors, so that commands such as "ssh -2 localhost od
/bin/ls | true" do not send unnecessary data over the wire.
+ sshd(8): increased the default size of ssh protocol 1 ephemeral keys
from 768 to 1024 bits.
+ When ssh(1) has been requested to fork after authentication ("ssh
-f") with ExitOnForwardFailure enabled, delay the fork until after
replies for any -R forwards have been seen. Allows for robust
detection of -R forward failure when using -f.
+ "Match group" blocks in sshd_config(5) now support negation of
groups. E.g. "Match group staff,!guests".
+ sftp(1) and sftp-server(8) now allow chmod-like operations to set
set[ug]id/sticky bits.
+ The MaxAuthTries option is now permitted in sshd_config(5) match
blocks.
+ Multiplexed ssh(1) sessions now support a subset of the ~ escapes
that are available to a primary connection.
+ ssh(1) connection multiplexing will now fall back to creating a new
connection in most error cases (closes: #352830).
+ Make ssh(1) deal more gracefully with channel requests that fail.
Previously it would optimistically assume that requests would always
succeed, which could cause hangs if they did not (e.g. when the
server runs out of file descriptors).
+ ssh(1) now reports multiplexing errors via the multiplex slave's
stderr where possible (subject to LogLevel in the mux master).
+ Prevent sshd(8) from erroneously applying public key restrictions
leaned from ~/.ssh/authorized_keys to other authentication methods
when public key authentication subsequently fails (LP: #161047).
+ Fixed an UMAC alignment problem that manifested on Itanium
platforms.
Diffstat (limited to 'session.c')
-rw-r--r-- | session.c | 578 |
1 files changed, 407 insertions, 171 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: session.c,v 1.221 2007/01/21 01:41:54 stevesk Exp $ */ | 1 | /* $OpenBSD: session.c,v 1.241 2008/06/16 13:22:53 dtucker Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | * All rights reserved | 4 | * All rights reserved |
@@ -59,6 +59,7 @@ | |||
59 | #include <string.h> | 59 | #include <string.h> |
60 | #include <unistd.h> | 60 | #include <unistd.h> |
61 | 61 | ||
62 | #include "openbsd-compat/sys-queue.h" | ||
62 | #include "xmalloc.h" | 63 | #include "xmalloc.h" |
63 | #include "ssh.h" | 64 | #include "ssh.h" |
64 | #include "ssh1.h" | 65 | #include "ssh1.h" |
@@ -84,9 +85,11 @@ | |||
84 | #include "sshlogin.h" | 85 | #include "sshlogin.h" |
85 | #include "serverloop.h" | 86 | #include "serverloop.h" |
86 | #include "canohost.h" | 87 | #include "canohost.h" |
88 | #include "misc.h" | ||
87 | #include "session.h" | 89 | #include "session.h" |
88 | #include "kex.h" | 90 | #include "kex.h" |
89 | #include "monitor_wrap.h" | 91 | #include "monitor_wrap.h" |
92 | #include "sftp.h" | ||
90 | 93 | ||
91 | #if defined(KRB5) && defined(USE_AFS) | 94 | #if defined(KRB5) && defined(USE_AFS) |
92 | #include <kafs.h> | 95 | #include <kafs.h> |
@@ -95,13 +98,13 @@ | |||
95 | /* func */ | 98 | /* func */ |
96 | 99 | ||
97 | Session *session_new(void); | 100 | Session *session_new(void); |
98 | void session_set_fds(Session *, int, int, int); | 101 | void session_set_fds(Session *, int, int, int, int); |
99 | void session_pty_cleanup(Session *); | 102 | void session_pty_cleanup(Session *); |
100 | void session_proctitle(Session *); | 103 | void session_proctitle(Session *); |
101 | int session_setup_x11fwd(Session *); | 104 | int session_setup_x11fwd(Session *); |
102 | void do_exec_pty(Session *, const char *); | 105 | int do_exec_pty(Session *, const char *); |
103 | void do_exec_no_pty(Session *, const char *); | 106 | int do_exec_no_pty(Session *, const char *); |
104 | void do_exec(Session *, const char *); | 107 | int do_exec(Session *, const char *); |
105 | void do_login(Session *, const char *); | 108 | void do_login(Session *, const char *); |
106 | #ifdef LOGIN_NEEDS_UTMPX | 109 | #ifdef LOGIN_NEEDS_UTMPX |
107 | static void do_pre_login(Session *s); | 110 | static void do_pre_login(Session *s); |
@@ -129,8 +132,13 @@ extern Buffer loginmsg; | |||
129 | const char *original_command = NULL; | 132 | const char *original_command = NULL; |
130 | 133 | ||
131 | /* data */ | 134 | /* data */ |
132 | #define MAX_SESSIONS 64 | 135 | static int sessions_first_unused = -1; |
133 | Session sessions[MAX_SESSIONS]; | 136 | static int sessions_nalloc = 0; |
137 | static Session *sessions = NULL; | ||
138 | |||
139 | #define SUBSYSTEM_NONE 0 | ||
140 | #define SUBSYSTEM_EXT 1 | ||
141 | #define SUBSYSTEM_INT_SFTP 2 | ||
134 | 142 | ||
135 | #ifdef HAVE_LOGIN_CAP | 143 | #ifdef HAVE_LOGIN_CAP |
136 | login_cap_t *lc; | 144 | login_cap_t *lc; |
@@ -160,7 +168,7 @@ static int | |||
160 | auth_input_request_forwarding(struct passwd * pw) | 168 | auth_input_request_forwarding(struct passwd * pw) |
161 | { | 169 | { |
162 | Channel *nc; | 170 | Channel *nc; |
163 | int sock; | 171 | int sock = -1; |
164 | struct sockaddr_un sunaddr; | 172 | struct sockaddr_un sunaddr; |
165 | 173 | ||
166 | if (auth_sock_name != NULL) { | 174 | if (auth_sock_name != NULL) { |
@@ -172,43 +180,48 @@ auth_input_request_forwarding(struct passwd * pw) | |||
172 | temporarily_use_uid(pw); | 180 | temporarily_use_uid(pw); |
173 | 181 | ||
174 | /* Allocate a buffer for the socket name, and format the name. */ | 182 | /* Allocate a buffer for the socket name, and format the name. */ |
175 | auth_sock_name = xmalloc(MAXPATHLEN); | 183 | auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX"); |
176 | auth_sock_dir = xmalloc(MAXPATHLEN); | ||
177 | strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN); | ||
178 | 184 | ||
179 | /* Create private directory for socket */ | 185 | /* Create private directory for socket */ |
180 | if (mkdtemp(auth_sock_dir) == NULL) { | 186 | if (mkdtemp(auth_sock_dir) == NULL) { |
181 | packet_send_debug("Agent forwarding disabled: " | 187 | packet_send_debug("Agent forwarding disabled: " |
182 | "mkdtemp() failed: %.100s", strerror(errno)); | 188 | "mkdtemp() failed: %.100s", strerror(errno)); |
183 | restore_uid(); | 189 | restore_uid(); |
184 | xfree(auth_sock_name); | ||
185 | xfree(auth_sock_dir); | 190 | xfree(auth_sock_dir); |
186 | auth_sock_name = NULL; | ||
187 | auth_sock_dir = NULL; | 191 | auth_sock_dir = NULL; |
188 | return 0; | 192 | goto authsock_err; |
189 | } | 193 | } |
190 | snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld", | 194 | |
191 | auth_sock_dir, (long) getpid()); | 195 | xasprintf(&auth_sock_name, "%s/agent.%ld", |
196 | auth_sock_dir, (long) getpid()); | ||
192 | 197 | ||
193 | /* Create the socket. */ | 198 | /* Create the socket. */ |
194 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | 199 | sock = socket(AF_UNIX, SOCK_STREAM, 0); |
195 | if (sock < 0) | 200 | if (sock < 0) { |
196 | packet_disconnect("socket: %.100s", strerror(errno)); | 201 | error("socket: %.100s", strerror(errno)); |
202 | restore_uid(); | ||
203 | goto authsock_err; | ||
204 | } | ||
197 | 205 | ||
198 | /* Bind it to the name. */ | 206 | /* Bind it to the name. */ |
199 | memset(&sunaddr, 0, sizeof(sunaddr)); | 207 | memset(&sunaddr, 0, sizeof(sunaddr)); |
200 | sunaddr.sun_family = AF_UNIX; | 208 | sunaddr.sun_family = AF_UNIX; |
201 | strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); | 209 | strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); |
202 | 210 | ||
203 | if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) | 211 | if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { |
204 | packet_disconnect("bind: %.100s", strerror(errno)); | 212 | error("bind: %.100s", strerror(errno)); |
213 | restore_uid(); | ||
214 | goto authsock_err; | ||
215 | } | ||
205 | 216 | ||
206 | /* Restore the privileged uid. */ | 217 | /* Restore the privileged uid. */ |
207 | restore_uid(); | 218 | restore_uid(); |
208 | 219 | ||
209 | /* Start listening on the socket. */ | 220 | /* Start listening on the socket. */ |
210 | if (listen(sock, SSH_LISTEN_BACKLOG) < 0) | 221 | if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { |
211 | packet_disconnect("listen: %.100s", strerror(errno)); | 222 | error("listen: %.100s", strerror(errno)); |
223 | goto authsock_err; | ||
224 | } | ||
212 | 225 | ||
213 | /* Allocate a channel for the authentication agent socket. */ | 226 | /* Allocate a channel for the authentication agent socket. */ |
214 | nc = channel_new("auth socket", | 227 | nc = channel_new("auth socket", |
@@ -217,6 +230,19 @@ auth_input_request_forwarding(struct passwd * pw) | |||
217 | 0, "auth socket", 1); | 230 | 0, "auth socket", 1); |
218 | strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); | 231 | strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); |
219 | return 1; | 232 | return 1; |
233 | |||
234 | authsock_err: | ||
235 | if (auth_sock_name != NULL) | ||
236 | xfree(auth_sock_name); | ||
237 | if (auth_sock_dir != NULL) { | ||
238 | rmdir(auth_sock_dir); | ||
239 | xfree(auth_sock_dir); | ||
240 | } | ||
241 | if (sock != -1) | ||
242 | close(sock); | ||
243 | auth_sock_name = NULL; | ||
244 | auth_sock_dir = NULL; | ||
245 | return 0; | ||
220 | } | 246 | } |
221 | 247 | ||
222 | static void | 248 | static void |
@@ -329,7 +355,8 @@ do_authenticated1(Authctxt *authctxt) | |||
329 | break; | 355 | break; |
330 | 356 | ||
331 | case SSH_CMSG_AGENT_REQUEST_FORWARDING: | 357 | case SSH_CMSG_AGENT_REQUEST_FORWARDING: |
332 | if (no_agent_forwarding_flag || compat13) { | 358 | if (!options.allow_agent_forwarding || |
359 | no_agent_forwarding_flag || compat13) { | ||
333 | debug("Authentication agent forwarding not permitted for this authentication."); | 360 | debug("Authentication agent forwarding not permitted for this authentication."); |
334 | break; | 361 | break; |
335 | } | 362 | } |
@@ -365,10 +392,14 @@ do_authenticated1(Authctxt *authctxt) | |||
365 | if (type == SSH_CMSG_EXEC_CMD) { | 392 | if (type == SSH_CMSG_EXEC_CMD) { |
366 | command = packet_get_string(&dlen); | 393 | command = packet_get_string(&dlen); |
367 | debug("Exec command '%.500s'", command); | 394 | debug("Exec command '%.500s'", command); |
368 | do_exec(s, command); | 395 | if (do_exec(s, command) != 0) |
396 | packet_disconnect( | ||
397 | "command execution failed"); | ||
369 | xfree(command); | 398 | xfree(command); |
370 | } else { | 399 | } else { |
371 | do_exec(s, NULL); | 400 | if (do_exec(s, NULL) != 0) |
401 | packet_disconnect( | ||
402 | "shell execution failed"); | ||
372 | } | 403 | } |
373 | packet_check_eom(); | 404 | packet_check_eom(); |
374 | session_close(s); | 405 | session_close(s); |
@@ -393,46 +424,84 @@ do_authenticated1(Authctxt *authctxt) | |||
393 | } | 424 | } |
394 | } | 425 | } |
395 | 426 | ||
427 | #define USE_PIPES | ||
396 | /* | 428 | /* |
397 | * This is called to fork and execute a command when we have no tty. This | 429 | * This is called to fork and execute a command when we have no tty. This |
398 | * will call do_child from the child, and server_loop from the parent after | 430 | * will call do_child from the child, and server_loop from the parent after |
399 | * setting up file descriptors and such. | 431 | * setting up file descriptors and such. |
400 | */ | 432 | */ |
401 | void | 433 | int |
402 | do_exec_no_pty(Session *s, const char *command) | 434 | do_exec_no_pty(Session *s, const char *command) |
403 | { | 435 | { |
404 | pid_t pid; | 436 | pid_t pid; |
405 | 437 | ||
406 | #ifdef USE_PIPES | 438 | #ifdef USE_PIPES |
407 | int pin[2], pout[2], perr[2]; | 439 | int pin[2], pout[2], perr[2]; |
440 | |||
408 | /* Allocate pipes for communicating with the program. */ | 441 | /* Allocate pipes for communicating with the program. */ |
409 | if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) | 442 | if (pipe(pin) < 0) { |
410 | packet_disconnect("Could not create pipes: %.100s", | 443 | error("%s: pipe in: %.100s", __func__, strerror(errno)); |
411 | strerror(errno)); | 444 | return -1; |
412 | #else /* USE_PIPES */ | 445 | } |
446 | if (pipe(pout) < 0) { | ||
447 | error("%s: pipe out: %.100s", __func__, strerror(errno)); | ||
448 | close(pin[0]); | ||
449 | close(pin[1]); | ||
450 | return -1; | ||
451 | } | ||
452 | if (pipe(perr) < 0) { | ||
453 | error("%s: pipe err: %.100s", __func__, strerror(errno)); | ||
454 | close(pin[0]); | ||
455 | close(pin[1]); | ||
456 | close(pout[0]); | ||
457 | close(pout[1]); | ||
458 | return -1; | ||
459 | } | ||
460 | #else | ||
413 | int inout[2], err[2]; | 461 | int inout[2], err[2]; |
462 | |||
414 | /* Uses socket pairs to communicate with the program. */ | 463 | /* Uses socket pairs to communicate with the program. */ |
415 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || | 464 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) { |
416 | socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) | 465 | error("%s: socketpair #1: %.100s", __func__, strerror(errno)); |
417 | packet_disconnect("Could not create socket pairs: %.100s", | 466 | return -1; |
418 | strerror(errno)); | 467 | } |
419 | #endif /* USE_PIPES */ | 468 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) { |
469 | error("%s: socketpair #2: %.100s", __func__, strerror(errno)); | ||
470 | close(inout[0]); | ||
471 | close(inout[1]); | ||
472 | return -1; | ||
473 | } | ||
474 | #endif | ||
475 | |||
420 | if (s == NULL) | 476 | if (s == NULL) |
421 | fatal("do_exec_no_pty: no session"); | 477 | fatal("do_exec_no_pty: no session"); |
422 | 478 | ||
423 | session_proctitle(s); | 479 | session_proctitle(s); |
424 | 480 | ||
425 | #if defined(USE_PAM) | ||
426 | if (options.use_pam && !use_privsep) | ||
427 | do_pam_setcred(1); | ||
428 | #endif /* USE_PAM */ | ||
429 | |||
430 | /* Fork the child. */ | 481 | /* Fork the child. */ |
431 | if ((pid = fork()) == 0) { | 482 | switch ((pid = fork())) { |
483 | case -1: | ||
484 | error("%s: fork: %.100s", __func__, strerror(errno)); | ||
485 | #ifdef USE_PIPES | ||
486 | close(pin[0]); | ||
487 | close(pin[1]); | ||
488 | close(pout[0]); | ||
489 | close(pout[1]); | ||
490 | close(perr[0]); | ||
491 | close(perr[1]); | ||
492 | #else | ||
493 | close(inout[0]); | ||
494 | close(inout[1]); | ||
495 | close(err[0]); | ||
496 | close(err[1]); | ||
497 | #endif | ||
498 | return -1; | ||
499 | case 0: | ||
432 | is_child = 1; | 500 | is_child = 1; |
433 | 501 | ||
434 | /* Child. Reinitialize the log since the pid has changed. */ | 502 | /* Child. Reinitialize the log since the pid has changed. */ |
435 | log_init(__progname, options.log_level, options.log_facility, log_stderr); | 503 | log_init(__progname, options.log_level, |
504 | options.log_facility, log_stderr); | ||
436 | 505 | ||
437 | /* | 506 | /* |
438 | * Create a new session and process group since the 4.4BSD | 507 | * Create a new session and process group since the 4.4BSD |
@@ -462,7 +531,7 @@ do_exec_no_pty(Session *s, const char *command) | |||
462 | if (dup2(perr[1], 2) < 0) | 531 | if (dup2(perr[1], 2) < 0) |
463 | perror("dup2 stderr"); | 532 | perror("dup2 stderr"); |
464 | close(perr[1]); | 533 | close(perr[1]); |
465 | #else /* USE_PIPES */ | 534 | #else |
466 | /* | 535 | /* |
467 | * Redirect stdin, stdout, and stderr. Stdin and stdout will | 536 | * Redirect stdin, stdout, and stderr. Stdin and stdout will |
468 | * use the same socket, as some programs (particularly rdist) | 537 | * use the same socket, as some programs (particularly rdist) |
@@ -472,11 +541,14 @@ do_exec_no_pty(Session *s, const char *command) | |||
472 | close(err[1]); | 541 | close(err[1]); |
473 | if (dup2(inout[0], 0) < 0) /* stdin */ | 542 | if (dup2(inout[0], 0) < 0) /* stdin */ |
474 | perror("dup2 stdin"); | 543 | perror("dup2 stdin"); |
475 | if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ | 544 | if (dup2(inout[0], 1) < 0) /* stdout (same as stdin) */ |
476 | perror("dup2 stdout"); | 545 | perror("dup2 stdout"); |
546 | close(inout[0]); | ||
477 | if (dup2(err[0], 2) < 0) /* stderr */ | 547 | if (dup2(err[0], 2) < 0) /* stderr */ |
478 | perror("dup2 stderr"); | 548 | perror("dup2 stderr"); |
479 | #endif /* USE_PIPES */ | 549 | close(err[0]); |
550 | #endif | ||
551 | |||
480 | 552 | ||
481 | #ifdef _UNICOS | 553 | #ifdef _UNICOS |
482 | cray_init_job(s->pw); /* set up cray jid and tmpdir */ | 554 | cray_init_job(s->pw); /* set up cray jid and tmpdir */ |
@@ -485,7 +557,10 @@ do_exec_no_pty(Session *s, const char *command) | |||
485 | /* Do processing for the child (exec command etc). */ | 557 | /* Do processing for the child (exec command etc). */ |
486 | do_child(s, command); | 558 | do_child(s, command); |
487 | /* NOTREACHED */ | 559 | /* NOTREACHED */ |
560 | default: | ||
561 | break; | ||
488 | } | 562 | } |
563 | |||
489 | #ifdef _UNICOS | 564 | #ifdef _UNICOS |
490 | signal(WJSIGNAL, cray_job_termination_handler); | 565 | signal(WJSIGNAL, cray_job_termination_handler); |
491 | #endif /* _UNICOS */ | 566 | #endif /* _UNICOS */ |
@@ -493,11 +568,18 @@ do_exec_no_pty(Session *s, const char *command) | |||
493 | if (is_winnt) | 568 | if (is_winnt) |
494 | cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); | 569 | cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); |
495 | #endif | 570 | #endif |
496 | if (pid < 0) | 571 | |
497 | packet_disconnect("fork failed: %.100s", strerror(errno)); | ||
498 | s->pid = pid; | 572 | s->pid = pid; |
499 | /* Set interactive/non-interactive mode. */ | 573 | /* Set interactive/non-interactive mode. */ |
500 | packet_set_interactive(s->display != NULL); | 574 | packet_set_interactive(s->display != NULL); |
575 | |||
576 | /* | ||
577 | * Clear loginmsg, since it's the child's responsibility to display | ||
578 | * it to the user, otherwise multiple sessions may accumulate | ||
579 | * multiple copies of the login messages. | ||
580 | */ | ||
581 | buffer_clear(&loginmsg); | ||
582 | |||
501 | #ifdef USE_PIPES | 583 | #ifdef USE_PIPES |
502 | /* We are the parent. Close the child sides of the pipes. */ | 584 | /* We are the parent. Close the child sides of the pipes. */ |
503 | close(pin[0]); | 585 | close(pin[0]); |
@@ -509,35 +591,32 @@ do_exec_no_pty(Session *s, const char *command) | |||
509 | close(perr[0]); | 591 | close(perr[0]); |
510 | perr[0] = -1; | 592 | perr[0] = -1; |
511 | } | 593 | } |
512 | session_set_fds(s, pin[1], pout[0], perr[0]); | 594 | session_set_fds(s, pin[1], pout[0], perr[0], 0); |
513 | } else { | 595 | } else { |
514 | /* Enter the interactive session. */ | 596 | /* Enter the interactive session. */ |
515 | server_loop(pid, pin[1], pout[0], perr[0]); | 597 | server_loop(pid, pin[1], pout[0], perr[0]); |
516 | /* server_loop has closed pin[1], pout[0], and perr[0]. */ | 598 | /* server_loop has closed pin[1], pout[0], and perr[0]. */ |
517 | } | 599 | } |
518 | #else /* USE_PIPES */ | 600 | #else |
519 | /* We are the parent. Close the child sides of the socket pairs. */ | 601 | /* We are the parent. Close the child sides of the socket pairs. */ |
520 | close(inout[0]); | 602 | close(inout[0]); |
521 | close(err[0]); | 603 | close(err[0]); |
522 | 604 | ||
523 | /* | 605 | /* |
524 | * Clear loginmsg, since it's the child's responsibility to display | ||
525 | * it to the user, otherwise multiple sessions may accumulate | ||
526 | * multiple copies of the login messages. | ||
527 | */ | ||
528 | buffer_clear(&loginmsg); | ||
529 | |||
530 | /* | ||
531 | * Enter the interactive session. Note: server_loop must be able to | 606 | * Enter the interactive session. Note: server_loop must be able to |
532 | * handle the case that fdin and fdout are the same. | 607 | * handle the case that fdin and fdout are the same. |
533 | */ | 608 | */ |
534 | if (compat20) { | 609 | if (compat20) { |
535 | session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1]); | 610 | session_set_fds(s, inout[1], inout[1], |
611 | s->is_subsystem ? -1 : err[1], 0); | ||
612 | if (s->is_subsystem) | ||
613 | close(err[1]); | ||
536 | } else { | 614 | } else { |
537 | server_loop(pid, inout[1], inout[1], err[1]); | 615 | server_loop(pid, inout[1], inout[1], err[1]); |
538 | /* server_loop has closed inout[1] and err[1]. */ | 616 | /* server_loop has closed inout[1] and err[1]. */ |
539 | } | 617 | } |
540 | #endif /* USE_PIPES */ | 618 | #endif |
619 | return 0; | ||
541 | } | 620 | } |
542 | 621 | ||
543 | /* | 622 | /* |
@@ -546,7 +625,7 @@ do_exec_no_pty(Session *s, const char *command) | |||
546 | * setting up file descriptors, controlling tty, updating wtmp, utmp, | 625 | * setting up file descriptors, controlling tty, updating wtmp, utmp, |
547 | * lastlog, and other such operations. | 626 | * lastlog, and other such operations. |
548 | */ | 627 | */ |
549 | void | 628 | int |
550 | do_exec_pty(Session *s, const char *command) | 629 | do_exec_pty(Session *s, const char *command) |
551 | { | 630 | { |
552 | int fdout, ptyfd, ttyfd, ptymaster; | 631 | int fdout, ptyfd, ttyfd, ptymaster; |
@@ -557,20 +636,46 @@ do_exec_pty(Session *s, const char *command) | |||
557 | ptyfd = s->ptyfd; | 636 | ptyfd = s->ptyfd; |
558 | ttyfd = s->ttyfd; | 637 | ttyfd = s->ttyfd; |
559 | 638 | ||
560 | #if defined(USE_PAM) | 639 | /* |
561 | if (options.use_pam) { | 640 | * Create another descriptor of the pty master side for use as the |
562 | do_pam_set_tty(s->tty); | 641 | * standard input. We could use the original descriptor, but this |
563 | if (!use_privsep) | 642 | * simplifies code in server_loop. The descriptor is bidirectional. |
564 | do_pam_setcred(1); | 643 | * Do this before forking (and cleanup in the child) so as to |
644 | * detect and gracefully fail out-of-fd conditions. | ||
645 | */ | ||
646 | if ((fdout = dup(ptyfd)) < 0) { | ||
647 | error("%s: dup #1: %s", __func__, strerror(errno)); | ||
648 | close(ttyfd); | ||
649 | close(ptyfd); | ||
650 | return -1; | ||
651 | } | ||
652 | /* we keep a reference to the pty master */ | ||
653 | if ((ptymaster = dup(ptyfd)) < 0) { | ||
654 | error("%s: dup #2: %s", __func__, strerror(errno)); | ||
655 | close(ttyfd); | ||
656 | close(ptyfd); | ||
657 | close(fdout); | ||
658 | return -1; | ||
565 | } | 659 | } |
566 | #endif | ||
567 | 660 | ||
568 | /* Fork the child. */ | 661 | /* Fork the child. */ |
569 | if ((pid = fork()) == 0) { | 662 | switch ((pid = fork())) { |
663 | case -1: | ||
664 | error("%s: fork: %.100s", __func__, strerror(errno)); | ||
665 | close(fdout); | ||
666 | close(ptymaster); | ||
667 | close(ttyfd); | ||
668 | close(ptyfd); | ||
669 | return -1; | ||
670 | case 0: | ||
570 | is_child = 1; | 671 | is_child = 1; |
571 | 672 | ||
673 | close(fdout); | ||
674 | close(ptymaster); | ||
675 | |||
572 | /* Child. Reinitialize the log because the pid has changed. */ | 676 | /* Child. Reinitialize the log because the pid has changed. */ |
573 | log_init(__progname, options.log_level, options.log_facility, log_stderr); | 677 | log_init(__progname, options.log_level, |
678 | options.log_facility, log_stderr); | ||
574 | /* Close the master side of the pseudo tty. */ | 679 | /* Close the master side of the pseudo tty. */ |
575 | close(ptyfd); | 680 | close(ptyfd); |
576 | 681 | ||
@@ -601,11 +706,16 @@ do_exec_pty(Session *s, const char *command) | |||
601 | do_pre_login(s); | 706 | do_pre_login(s); |
602 | # endif | 707 | # endif |
603 | #endif | 708 | #endif |
604 | 709 | /* | |
605 | /* Do common processing for the child, such as execing the command. */ | 710 | * Do common processing for the child, such as execing |
606 | do_child(s, command); | 711 | * the command. |
607 | /* NOTREACHED */ | 712 | */ |
713 | do_child(s, command); | ||
714 | /* NOTREACHED */ | ||
715 | default: | ||
716 | break; | ||
608 | } | 717 | } |
718 | |||
609 | #ifdef _UNICOS | 719 | #ifdef _UNICOS |
610 | signal(WJSIGNAL, cray_job_termination_handler); | 720 | signal(WJSIGNAL, cray_job_termination_handler); |
611 | #endif /* _UNICOS */ | 721 | #endif /* _UNICOS */ |
@@ -613,36 +723,22 @@ do_exec_pty(Session *s, const char *command) | |||
613 | if (is_winnt) | 723 | if (is_winnt) |
614 | cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); | 724 | cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); |
615 | #endif | 725 | #endif |
616 | if (pid < 0) | 726 | |
617 | packet_disconnect("fork failed: %.100s", strerror(errno)); | ||
618 | s->pid = pid; | 727 | s->pid = pid; |
619 | 728 | ||
620 | /* Parent. Close the slave side of the pseudo tty. */ | 729 | /* Parent. Close the slave side of the pseudo tty. */ |
621 | close(ttyfd); | 730 | close(ttyfd); |
622 | 731 | ||
623 | /* | ||
624 | * Create another descriptor of the pty master side for use as the | ||
625 | * standard input. We could use the original descriptor, but this | ||
626 | * simplifies code in server_loop. The descriptor is bidirectional. | ||
627 | */ | ||
628 | fdout = dup(ptyfd); | ||
629 | if (fdout < 0) | ||
630 | packet_disconnect("dup #1 failed: %.100s", strerror(errno)); | ||
631 | |||
632 | /* we keep a reference to the pty master */ | ||
633 | ptymaster = dup(ptyfd); | ||
634 | if (ptymaster < 0) | ||
635 | packet_disconnect("dup #2 failed: %.100s", strerror(errno)); | ||
636 | s->ptymaster = ptymaster; | ||
637 | |||
638 | /* Enter interactive session. */ | 732 | /* Enter interactive session. */ |
733 | s->ptymaster = ptymaster; | ||
639 | packet_set_interactive(1); | 734 | packet_set_interactive(1); |
640 | if (compat20) { | 735 | if (compat20) { |
641 | session_set_fds(s, ptyfd, fdout, -1); | 736 | session_set_fds(s, ptyfd, fdout, -1, 1); |
642 | } else { | 737 | } else { |
643 | server_loop(pid, ptyfd, fdout, -1); | 738 | server_loop(pid, ptyfd, fdout, -1); |
644 | /* server_loop _has_ closed ptyfd and fdout. */ | 739 | /* server_loop _has_ closed ptyfd and fdout. */ |
645 | } | 740 | } |
741 | return 0; | ||
646 | } | 742 | } |
647 | 743 | ||
648 | #ifdef LOGIN_NEEDS_UTMPX | 744 | #ifdef LOGIN_NEEDS_UTMPX |
@@ -677,16 +773,26 @@ do_pre_login(Session *s) | |||
677 | * This is called to fork and execute a command. If another command is | 773 | * This is called to fork and execute a command. If another command is |
678 | * to be forced, execute that instead. | 774 | * to be forced, execute that instead. |
679 | */ | 775 | */ |
680 | void | 776 | int |
681 | do_exec(Session *s, const char *command) | 777 | do_exec(Session *s, const char *command) |
682 | { | 778 | { |
779 | int ret; | ||
780 | |||
683 | if (options.adm_forced_command) { | 781 | if (options.adm_forced_command) { |
684 | original_command = command; | 782 | original_command = command; |
685 | command = options.adm_forced_command; | 783 | command = options.adm_forced_command; |
784 | if (strcmp(INTERNAL_SFTP_NAME, command) == 0) | ||
785 | s->is_subsystem = SUBSYSTEM_INT_SFTP; | ||
786 | else if (s->is_subsystem) | ||
787 | s->is_subsystem = SUBSYSTEM_EXT; | ||
686 | debug("Forced command (config) '%.900s'", command); | 788 | debug("Forced command (config) '%.900s'", command); |
687 | } else if (forced_command) { | 789 | } else if (forced_command) { |
688 | original_command = command; | 790 | original_command = command; |
689 | command = forced_command; | 791 | command = forced_command; |
792 | if (strcmp(INTERNAL_SFTP_NAME, command) == 0) | ||
793 | s->is_subsystem = SUBSYSTEM_INT_SFTP; | ||
794 | else if (s->is_subsystem) | ||
795 | s->is_subsystem = SUBSYSTEM_EXT; | ||
690 | debug("Forced command (key option) '%.900s'", command); | 796 | debug("Forced command (key option) '%.900s'", command); |
691 | } | 797 | } |
692 | 798 | ||
@@ -701,11 +807,10 @@ do_exec(Session *s, const char *command) | |||
701 | PRIVSEP(audit_run_command(shell)); | 807 | PRIVSEP(audit_run_command(shell)); |
702 | } | 808 | } |
703 | #endif | 809 | #endif |
704 | |||
705 | if (s->ttyfd != -1) | 810 | if (s->ttyfd != -1) |
706 | do_exec_pty(s, command); | 811 | ret = do_exec_pty(s, command); |
707 | else | 812 | else |
708 | do_exec_no_pty(s, command); | 813 | ret = do_exec_no_pty(s, command); |
709 | 814 | ||
710 | original_command = NULL; | 815 | original_command = NULL; |
711 | 816 | ||
@@ -715,6 +820,8 @@ do_exec(Session *s, const char *command) | |||
715 | * multiple copies of the login messages. | 820 | * multiple copies of the login messages. |
716 | */ | 821 | */ |
717 | buffer_clear(&loginmsg); | 822 | buffer_clear(&loginmsg); |
823 | |||
824 | return ret; | ||
718 | } | 825 | } |
719 | 826 | ||
720 | /* administrative, login(1)-like work */ | 827 | /* administrative, login(1)-like work */ |
@@ -897,8 +1004,9 @@ read_environment_file(char ***env, u_int *envsize, | |||
897 | ; | 1004 | ; |
898 | if (!*cp || *cp == '#' || *cp == '\n') | 1005 | if (!*cp || *cp == '#' || *cp == '\n') |
899 | continue; | 1006 | continue; |
900 | if (strchr(cp, '\n')) | 1007 | |
901 | *strchr(cp, '\n') = '\0'; | 1008 | cp[strcspn(cp, "\n")] = '\0'; |
1009 | |||
902 | value = strchr(cp, '='); | 1010 | value = strchr(cp, '='); |
903 | if (value == NULL) { | 1011 | if (value == NULL) { |
904 | fprintf(stderr, "Bad line %u in %.100s\n", lineno, | 1012 | fprintf(stderr, "Bad line %u in %.100s\n", lineno, |
@@ -1203,7 +1311,7 @@ do_rc_files(Session *s, const char *shell) | |||
1203 | 1311 | ||
1204 | /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */ | 1312 | /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */ |
1205 | if (!s->is_subsystem && options.adm_forced_command == NULL && | 1313 | if (!s->is_subsystem && options.adm_forced_command == NULL && |
1206 | !no_user_rc && (stat(_PATH_SSH_USER_RC, &st) >= 0)) { | 1314 | !no_user_rc && stat(_PATH_SSH_USER_RC, &st) >= 0) { |
1207 | snprintf(cmd, sizeof cmd, "%s -c '%s %s'", | 1315 | snprintf(cmd, sizeof cmd, "%s -c '%s %s'", |
1208 | shell, _PATH_BSHELL, _PATH_SSH_USER_RC); | 1316 | shell, _PATH_BSHELL, _PATH_SSH_USER_RC); |
1209 | if (debug_flag) | 1317 | if (debug_flag) |
@@ -1284,10 +1392,72 @@ do_nologin(struct passwd *pw) | |||
1284 | } | 1392 | } |
1285 | } | 1393 | } |
1286 | 1394 | ||
1395 | /* | ||
1396 | * Chroot into a directory after checking it for safety: all path components | ||
1397 | * must be root-owned directories with strict permissions. | ||
1398 | */ | ||
1399 | static void | ||
1400 | safely_chroot(const char *path, uid_t uid) | ||
1401 | { | ||
1402 | const char *cp; | ||
1403 | char component[MAXPATHLEN]; | ||
1404 | struct stat st; | ||
1405 | |||
1406 | if (*path != '/') | ||
1407 | fatal("chroot path does not begin at root"); | ||
1408 | if (strlen(path) >= sizeof(component)) | ||
1409 | fatal("chroot path too long"); | ||
1410 | |||
1411 | /* | ||
1412 | * Descend the path, checking that each component is a | ||
1413 | * root-owned directory with strict permissions. | ||
1414 | */ | ||
1415 | for (cp = path; cp != NULL;) { | ||
1416 | if ((cp = strchr(cp, '/')) == NULL) | ||
1417 | strlcpy(component, path, sizeof(component)); | ||
1418 | else { | ||
1419 | cp++; | ||
1420 | memcpy(component, path, cp - path); | ||
1421 | component[cp - path] = '\0'; | ||
1422 | } | ||
1423 | |||
1424 | debug3("%s: checking '%s'", __func__, component); | ||
1425 | |||
1426 | if (stat(component, &st) != 0) | ||
1427 | fatal("%s: stat(\"%s\"): %s", __func__, | ||
1428 | component, strerror(errno)); | ||
1429 | if (st.st_uid != 0 || (st.st_mode & 022) != 0) | ||
1430 | fatal("bad ownership or modes for chroot " | ||
1431 | "directory %s\"%s\"", | ||
1432 | cp == NULL ? "" : "component ", component); | ||
1433 | if (!S_ISDIR(st.st_mode)) | ||
1434 | fatal("chroot path %s\"%s\" is not a directory", | ||
1435 | cp == NULL ? "" : "component ", component); | ||
1436 | |||
1437 | } | ||
1438 | |||
1439 | if (chdir(path) == -1) | ||
1440 | fatal("Unable to chdir to chroot path \"%s\": " | ||
1441 | "%s", path, strerror(errno)); | ||
1442 | if (chroot(path) == -1) | ||
1443 | fatal("chroot(\"%s\"): %s", path, strerror(errno)); | ||
1444 | if (chdir("/") == -1) | ||
1445 | fatal("%s: chdir(/) after chroot: %s", | ||
1446 | __func__, strerror(errno)); | ||
1447 | verbose("Changed root directory to \"%s\"", path); | ||
1448 | } | ||
1449 | |||
1287 | /* Set login name, uid, gid, and groups. */ | 1450 | /* Set login name, uid, gid, and groups. */ |
1288 | void | 1451 | void |
1289 | do_setusercontext(struct passwd *pw) | 1452 | do_setusercontext(struct passwd *pw) |
1290 | { | 1453 | { |
1454 | char *chroot_path, *tmp; | ||
1455 | |||
1456 | #ifdef WITH_SELINUX | ||
1457 | /* Cache selinux status for later use */ | ||
1458 | (void)ssh_selinux_enabled(); | ||
1459 | #endif | ||
1460 | |||
1291 | #ifndef HAVE_CYGWIN | 1461 | #ifndef HAVE_CYGWIN |
1292 | if (getuid() == 0 || geteuid() == 0) | 1462 | if (getuid() == 0 || geteuid() == 0) |
1293 | #endif /* HAVE_CYGWIN */ | 1463 | #endif /* HAVE_CYGWIN */ |
@@ -1301,21 +1471,13 @@ do_setusercontext(struct passwd *pw) | |||
1301 | # ifdef __bsdi__ | 1471 | # ifdef __bsdi__ |
1302 | setpgid(0, 0); | 1472 | setpgid(0, 0); |
1303 | # endif | 1473 | # endif |
1304 | #ifdef GSSAPI | ||
1305 | if (options.gss_authentication) { | ||
1306 | temporarily_use_uid(pw); | ||
1307 | ssh_gssapi_storecreds(); | ||
1308 | restore_uid(); | ||
1309 | } | ||
1310 | #endif | ||
1311 | # ifdef USE_PAM | 1474 | # ifdef USE_PAM |
1312 | if (options.use_pam) { | 1475 | if (options.use_pam) { |
1313 | do_pam_session(); | ||
1314 | do_pam_setcred(use_privsep); | 1476 | do_pam_setcred(use_privsep); |
1315 | } | 1477 | } |
1316 | # endif /* USE_PAM */ | 1478 | # endif /* USE_PAM */ |
1317 | if (setusercontext(lc, pw, pw->pw_uid, | 1479 | if (setusercontext(lc, pw, pw->pw_uid, |
1318 | (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { | 1480 | (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) { |
1319 | perror("unable to set user context"); | 1481 | perror("unable to set user context"); |
1320 | exit(1); | 1482 | exit(1); |
1321 | } | 1483 | } |
@@ -1338,13 +1500,6 @@ do_setusercontext(struct passwd *pw) | |||
1338 | exit(1); | 1500 | exit(1); |
1339 | } | 1501 | } |
1340 | endgrent(); | 1502 | endgrent(); |
1341 | #ifdef GSSAPI | ||
1342 | if (options.gss_authentication) { | ||
1343 | temporarily_use_uid(pw); | ||
1344 | ssh_gssapi_storecreds(); | ||
1345 | restore_uid(); | ||
1346 | } | ||
1347 | #endif | ||
1348 | # ifdef USE_PAM | 1503 | # ifdef USE_PAM |
1349 | /* | 1504 | /* |
1350 | * PAM credentials may take the form of supplementary groups. | 1505 | * PAM credentials may take the form of supplementary groups. |
@@ -1352,21 +1507,39 @@ do_setusercontext(struct passwd *pw) | |||
1352 | * Reestablish them here. | 1507 | * Reestablish them here. |
1353 | */ | 1508 | */ |
1354 | if (options.use_pam) { | 1509 | if (options.use_pam) { |
1355 | do_pam_session(); | ||
1356 | do_pam_setcred(use_privsep); | 1510 | do_pam_setcred(use_privsep); |
1357 | } | 1511 | } |
1358 | # endif /* USE_PAM */ | 1512 | # endif /* USE_PAM */ |
1359 | # if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) | 1513 | # if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) |
1360 | irix_setusercontext(pw); | 1514 | irix_setusercontext(pw); |
1361 | # endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */ | 1515 | # endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */ |
1362 | # ifdef _AIX | 1516 | # ifdef _AIX |
1363 | aix_usrinfo(pw); | 1517 | aix_usrinfo(pw); |
1364 | # endif /* _AIX */ | 1518 | # endif /* _AIX */ |
1365 | #ifdef USE_LIBIAF | 1519 | # ifdef USE_LIBIAF |
1366 | if (set_id(pw->pw_name) != 0) { | 1520 | if (set_id(pw->pw_name) != 0) { |
1367 | exit(1); | 1521 | exit(1); |
1368 | } | 1522 | } |
1369 | #endif /* USE_LIBIAF */ | 1523 | # endif /* USE_LIBIAF */ |
1524 | #endif | ||
1525 | |||
1526 | if (options.chroot_directory != NULL && | ||
1527 | strcasecmp(options.chroot_directory, "none") != 0) { | ||
1528 | tmp = tilde_expand_filename(options.chroot_directory, | ||
1529 | pw->pw_uid); | ||
1530 | chroot_path = percent_expand(tmp, "h", pw->pw_dir, | ||
1531 | "u", pw->pw_name, (char *)NULL); | ||
1532 | safely_chroot(chroot_path, pw->pw_uid); | ||
1533 | free(tmp); | ||
1534 | free(chroot_path); | ||
1535 | } | ||
1536 | |||
1537 | #ifdef HAVE_LOGIN_CAP | ||
1538 | if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) { | ||
1539 | perror("unable to set user context (setuser)"); | ||
1540 | exit(1); | ||
1541 | } | ||
1542 | #else | ||
1370 | /* Permanently switch to the desired uid. */ | 1543 | /* Permanently switch to the desired uid. */ |
1371 | permanently_set_uid(pw); | 1544 | permanently_set_uid(pw); |
1372 | #endif | 1545 | #endif |
@@ -1465,14 +1638,16 @@ child_close_fds(void) | |||
1465 | * environment, closing extra file descriptors, setting the user and group | 1638 | * environment, closing extra file descriptors, setting the user and group |
1466 | * ids, and executing the command or shell. | 1639 | * ids, and executing the command or shell. |
1467 | */ | 1640 | */ |
1641 | #define ARGV_MAX 10 | ||
1468 | void | 1642 | void |
1469 | do_child(Session *s, const char *command) | 1643 | do_child(Session *s, const char *command) |
1470 | { | 1644 | { |
1471 | extern char **environ; | 1645 | extern char **environ; |
1472 | char **env; | 1646 | char **env; |
1473 | char *argv[10]; | 1647 | char *argv[ARGV_MAX]; |
1474 | const char *shell, *shell0, *hostname = NULL; | 1648 | const char *shell, *shell0, *hostname = NULL; |
1475 | struct passwd *pw = s->pw; | 1649 | struct passwd *pw = s->pw; |
1650 | int r = 0; | ||
1476 | 1651 | ||
1477 | /* remove hostkey from the child's memory */ | 1652 | /* remove hostkey from the child's memory */ |
1478 | destroy_sensitive_data(); | 1653 | destroy_sensitive_data(); |
@@ -1588,20 +1763,42 @@ do_child(Session *s, const char *command) | |||
1588 | 1763 | ||
1589 | /* Change current directory to the user's home directory. */ | 1764 | /* Change current directory to the user's home directory. */ |
1590 | if (chdir(pw->pw_dir) < 0) { | 1765 | if (chdir(pw->pw_dir) < 0) { |
1591 | fprintf(stderr, "Could not chdir to home directory %s: %s\n", | 1766 | /* Suppress missing homedir warning for chroot case */ |
1592 | pw->pw_dir, strerror(errno)); | ||
1593 | #ifdef HAVE_LOGIN_CAP | 1767 | #ifdef HAVE_LOGIN_CAP |
1594 | if (login_getcapbool(lc, "requirehome", 0)) | 1768 | r = login_getcapbool(lc, "requirehome", 0); |
1595 | exit(1); | ||
1596 | #endif | 1769 | #endif |
1770 | if (r || options.chroot_directory == NULL) | ||
1771 | fprintf(stderr, "Could not chdir to home " | ||
1772 | "directory %s: %s\n", pw->pw_dir, | ||
1773 | strerror(errno)); | ||
1774 | if (r) | ||
1775 | exit(1); | ||
1597 | } | 1776 | } |
1598 | 1777 | ||
1778 | closefrom(STDERR_FILENO + 1); | ||
1779 | |||
1599 | if (!options.use_login) | 1780 | if (!options.use_login) |
1600 | do_rc_files(s, shell); | 1781 | do_rc_files(s, shell); |
1601 | 1782 | ||
1602 | /* restore SIGPIPE for child */ | 1783 | /* restore SIGPIPE for child */ |
1603 | signal(SIGPIPE, SIG_DFL); | 1784 | signal(SIGPIPE, SIG_DFL); |
1604 | 1785 | ||
1786 | if (s->is_subsystem == SUBSYSTEM_INT_SFTP) { | ||
1787 | extern int optind, optreset; | ||
1788 | int i; | ||
1789 | char *p, *args; | ||
1790 | |||
1791 | setproctitle("%s@internal-sftp-server", s->pw->pw_name); | ||
1792 | args = strdup(command ? command : "sftp-server"); | ||
1793 | for (i = 0, (p = strtok(args, " ")); p; (p = strtok(NULL, " "))) | ||
1794 | if (i < ARGV_MAX - 1) | ||
1795 | argv[i++] = p; | ||
1796 | argv[i] = NULL; | ||
1797 | optind = optreset = 1; | ||
1798 | __progname = argv[0]; | ||
1799 | exit(sftp_server_main(i, argv, s->pw)); | ||
1800 | } | ||
1801 | |||
1605 | if (options.use_login) { | 1802 | if (options.use_login) { |
1606 | launch_login(pw, hostname); | 1803 | launch_login(pw, hostname); |
1607 | /* NEVERREACHED */ | 1804 | /* NEVERREACHED */ |
@@ -1653,43 +1850,79 @@ do_child(Session *s, const char *command) | |||
1653 | exit(1); | 1850 | exit(1); |
1654 | } | 1851 | } |
1655 | 1852 | ||
1853 | void | ||
1854 | session_unused(int id) | ||
1855 | { | ||
1856 | debug3("%s: session id %d unused", __func__, id); | ||
1857 | if (id >= options.max_sessions || | ||
1858 | id >= sessions_nalloc) { | ||
1859 | fatal("%s: insane session id %d (max %d nalloc %d)", | ||
1860 | __func__, id, options.max_sessions, sessions_nalloc); | ||
1861 | } | ||
1862 | bzero(&sessions[id], sizeof(*sessions)); | ||
1863 | sessions[id].self = id; | ||
1864 | sessions[id].used = 0; | ||
1865 | sessions[id].chanid = -1; | ||
1866 | sessions[id].ptyfd = -1; | ||
1867 | sessions[id].ttyfd = -1; | ||
1868 | sessions[id].ptymaster = -1; | ||
1869 | sessions[id].x11_chanids = NULL; | ||
1870 | sessions[id].next_unused = sessions_first_unused; | ||
1871 | sessions_first_unused = id; | ||
1872 | } | ||
1873 | |||
1656 | Session * | 1874 | Session * |
1657 | session_new(void) | 1875 | session_new(void) |
1658 | { | 1876 | { |
1659 | int i; | 1877 | Session *s, *tmp; |
1660 | static int did_init = 0; | 1878 | |
1661 | if (!did_init) { | 1879 | if (sessions_first_unused == -1) { |
1662 | debug("session_new: init"); | 1880 | if (sessions_nalloc >= options.max_sessions) |
1663 | for (i = 0; i < MAX_SESSIONS; i++) { | 1881 | return NULL; |
1664 | sessions[i].used = 0; | 1882 | debug2("%s: allocate (allocated %d max %d)", |
1883 | __func__, sessions_nalloc, options.max_sessions); | ||
1884 | tmp = xrealloc(sessions, sessions_nalloc + 1, | ||
1885 | sizeof(*sessions)); | ||
1886 | if (tmp == NULL) { | ||
1887 | error("%s: cannot allocate %d sessions", | ||
1888 | __func__, sessions_nalloc + 1); | ||
1889 | return NULL; | ||
1665 | } | 1890 | } |
1666 | did_init = 1; | 1891 | sessions = tmp; |
1892 | session_unused(sessions_nalloc++); | ||
1667 | } | 1893 | } |
1668 | for (i = 0; i < MAX_SESSIONS; i++) { | 1894 | |
1669 | Session *s = &sessions[i]; | 1895 | if (sessions_first_unused >= sessions_nalloc || |
1670 | if (! s->used) { | 1896 | sessions_first_unused < 0) { |
1671 | memset(s, 0, sizeof(*s)); | 1897 | fatal("%s: insane first_unused %d max %d nalloc %d", |
1672 | s->chanid = -1; | 1898 | __func__, sessions_first_unused, options.max_sessions, |
1673 | s->ptyfd = -1; | 1899 | sessions_nalloc); |
1674 | s->ttyfd = -1; | ||
1675 | s->used = 1; | ||
1676 | s->self = i; | ||
1677 | s->x11_chanids = NULL; | ||
1678 | debug("session_new: session %d", i); | ||
1679 | return s; | ||
1680 | } | ||
1681 | } | 1900 | } |
1682 | return NULL; | 1901 | |
1902 | s = &sessions[sessions_first_unused]; | ||
1903 | if (s->used) { | ||
1904 | fatal("%s: session %d already used", | ||
1905 | __func__, sessions_first_unused); | ||
1906 | } | ||
1907 | sessions_first_unused = s->next_unused; | ||
1908 | s->used = 1; | ||
1909 | s->next_unused = -1; | ||
1910 | debug("session_new: session %d", s->self); | ||
1911 | |||
1912 | return s; | ||
1683 | } | 1913 | } |
1684 | 1914 | ||
1685 | static void | 1915 | static void |
1686 | session_dump(void) | 1916 | session_dump(void) |
1687 | { | 1917 | { |
1688 | int i; | 1918 | int i; |
1689 | for (i = 0; i < MAX_SESSIONS; i++) { | 1919 | for (i = 0; i < sessions_nalloc; i++) { |
1690 | Session *s = &sessions[i]; | 1920 | Session *s = &sessions[i]; |
1691 | debug("dump: used %d session %d %p channel %d pid %ld", | 1921 | |
1922 | debug("dump: used %d next_unused %d session %d %p " | ||
1923 | "channel %d pid %ld", | ||
1692 | s->used, | 1924 | s->used, |
1925 | s->next_unused, | ||
1693 | s->self, | 1926 | s->self, |
1694 | s, | 1927 | s, |
1695 | s->chanid, | 1928 | s->chanid, |
@@ -1719,7 +1952,7 @@ Session * | |||
1719 | session_by_tty(char *tty) | 1952 | session_by_tty(char *tty) |
1720 | { | 1953 | { |
1721 | int i; | 1954 | int i; |
1722 | for (i = 0; i < MAX_SESSIONS; i++) { | 1955 | for (i = 0; i < sessions_nalloc; i++) { |
1723 | Session *s = &sessions[i]; | 1956 | Session *s = &sessions[i]; |
1724 | if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { | 1957 | if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { |
1725 | debug("session_by_tty: session %d tty %s", i, tty); | 1958 | debug("session_by_tty: session %d tty %s", i, tty); |
@@ -1735,10 +1968,11 @@ static Session * | |||
1735 | session_by_channel(int id) | 1968 | session_by_channel(int id) |
1736 | { | 1969 | { |
1737 | int i; | 1970 | int i; |
1738 | for (i = 0; i < MAX_SESSIONS; i++) { | 1971 | for (i = 0; i < sessions_nalloc; i++) { |
1739 | Session *s = &sessions[i]; | 1972 | Session *s = &sessions[i]; |
1740 | if (s->used && s->chanid == id) { | 1973 | if (s->used && s->chanid == id) { |
1741 | debug("session_by_channel: session %d channel %d", i, id); | 1974 | debug("session_by_channel: session %d channel %d", |
1975 | i, id); | ||
1742 | return s; | 1976 | return s; |
1743 | } | 1977 | } |
1744 | } | 1978 | } |
@@ -1752,7 +1986,7 @@ session_by_x11_channel(int id) | |||
1752 | { | 1986 | { |
1753 | int i, j; | 1987 | int i, j; |
1754 | 1988 | ||
1755 | for (i = 0; i < MAX_SESSIONS; i++) { | 1989 | for (i = 0; i < sessions_nalloc; i++) { |
1756 | Session *s = &sessions[i]; | 1990 | Session *s = &sessions[i]; |
1757 | 1991 | ||
1758 | if (s->x11_chanids == NULL || !s->used) | 1992 | if (s->x11_chanids == NULL || !s->used) |
@@ -1775,7 +2009,7 @@ session_by_pid(pid_t pid) | |||
1775 | { | 2009 | { |
1776 | int i; | 2010 | int i; |
1777 | debug("session_by_pid: pid %ld", (long)pid); | 2011 | debug("session_by_pid: pid %ld", (long)pid); |
1778 | for (i = 0; i < MAX_SESSIONS; i++) { | 2012 | for (i = 0; i < sessions_nalloc; i++) { |
1779 | Session *s = &sessions[i]; | 2013 | Session *s = &sessions[i]; |
1780 | if (s->used && s->pid == pid) | 2014 | if (s->used && s->pid == pid) |
1781 | return s; | 2015 | return s; |
@@ -1831,7 +2065,8 @@ session_pty_req(Session *s) | |||
1831 | 2065 | ||
1832 | /* Allocate a pty and open it. */ | 2066 | /* Allocate a pty and open it. */ |
1833 | debug("Allocating pty."); | 2067 | debug("Allocating pty."); |
1834 | if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) { | 2068 | if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, |
2069 | sizeof(s->tty)))) { | ||
1835 | if (s->term) | 2070 | if (s->term) |
1836 | xfree(s->term); | 2071 | xfree(s->term); |
1837 | s->term = NULL; | 2072 | s->term = NULL; |
@@ -1874,15 +2109,17 @@ session_subsystem_req(Session *s) | |||
1874 | if (strcmp(subsys, options.subsystem_name[i]) == 0) { | 2109 | if (strcmp(subsys, options.subsystem_name[i]) == 0) { |
1875 | prog = options.subsystem_command[i]; | 2110 | prog = options.subsystem_command[i]; |
1876 | cmd = options.subsystem_args[i]; | 2111 | cmd = options.subsystem_args[i]; |
1877 | if (stat(prog, &st) < 0) { | 2112 | if (!strcmp(INTERNAL_SFTP_NAME, prog)) { |
2113 | s->is_subsystem = SUBSYSTEM_INT_SFTP; | ||
2114 | } else if (stat(prog, &st) < 0) { | ||
1878 | error("subsystem: cannot stat %s: %s", prog, | 2115 | error("subsystem: cannot stat %s: %s", prog, |
1879 | strerror(errno)); | 2116 | strerror(errno)); |
1880 | break; | 2117 | break; |
2118 | } else { | ||
2119 | s->is_subsystem = SUBSYSTEM_EXT; | ||
1881 | } | 2120 | } |
1882 | debug("subsystem: exec() %s", cmd); | 2121 | debug("subsystem: exec() %s", cmd); |
1883 | s->is_subsystem = 1; | 2122 | success = do_exec(s, cmd) == 0; |
1884 | do_exec(s, cmd); | ||
1885 | success = 1; | ||
1886 | break; | 2123 | break; |
1887 | } | 2124 | } |
1888 | } | 2125 | } |
@@ -1925,19 +2162,19 @@ static int | |||
1925 | session_shell_req(Session *s) | 2162 | session_shell_req(Session *s) |
1926 | { | 2163 | { |
1927 | packet_check_eom(); | 2164 | packet_check_eom(); |
1928 | do_exec(s, NULL); | 2165 | return do_exec(s, NULL) == 0; |
1929 | return 1; | ||
1930 | } | 2166 | } |
1931 | 2167 | ||
1932 | static int | 2168 | static int |
1933 | session_exec_req(Session *s) | 2169 | session_exec_req(Session *s) |
1934 | { | 2170 | { |
1935 | u_int len; | 2171 | u_int len, success; |
2172 | |||
1936 | char *command = packet_get_string(&len); | 2173 | char *command = packet_get_string(&len); |
1937 | packet_check_eom(); | 2174 | packet_check_eom(); |
1938 | do_exec(s, command); | 2175 | success = do_exec(s, command) == 0; |
1939 | xfree(command); | 2176 | xfree(command); |
1940 | return 1; | 2177 | return success; |
1941 | } | 2178 | } |
1942 | 2179 | ||
1943 | static int | 2180 | static int |
@@ -1947,8 +2184,7 @@ session_break_req(Session *s) | |||
1947 | packet_get_int(); /* ignored */ | 2184 | packet_get_int(); /* ignored */ |
1948 | packet_check_eom(); | 2185 | packet_check_eom(); |
1949 | 2186 | ||
1950 | if (s->ttyfd == -1 || | 2187 | if (s->ttyfd == -1 || tcsendbreak(s->ttyfd, 0) < 0) |
1951 | tcsendbreak(s->ttyfd, 0) < 0) | ||
1952 | return 0; | 2188 | return 0; |
1953 | return 1; | 2189 | return 1; |
1954 | } | 2190 | } |
@@ -1993,7 +2229,7 @@ session_auth_agent_req(Session *s) | |||
1993 | { | 2229 | { |
1994 | static int called = 0; | 2230 | static int called = 0; |
1995 | packet_check_eom(); | 2231 | packet_check_eom(); |
1996 | if (no_agent_forwarding_flag) { | 2232 | if (no_agent_forwarding_flag || !options.allow_agent_forwarding) { |
1997 | debug("session_auth_agent_req: no_agent_forwarding_flag"); | 2233 | debug("session_auth_agent_req: no_agent_forwarding_flag"); |
1998 | return 0; | 2234 | return 0; |
1999 | } | 2235 | } |
@@ -2049,7 +2285,7 @@ session_input_channel_req(Channel *c, const char *rtype) | |||
2049 | } | 2285 | } |
2050 | 2286 | ||
2051 | void | 2287 | void |
2052 | session_set_fds(Session *s, int fdin, int fdout, int fderr) | 2288 | session_set_fds(Session *s, int fdin, int fdout, int fderr, int is_tty) |
2053 | { | 2289 | { |
2054 | if (!compat20) | 2290 | if (!compat20) |
2055 | fatal("session_set_fds: called for proto != 2.0"); | 2291 | fatal("session_set_fds: called for proto != 2.0"); |
@@ -2062,8 +2298,7 @@ session_set_fds(Session *s, int fdin, int fdout, int fderr) | |||
2062 | channel_set_fds(s->chanid, | 2298 | channel_set_fds(s->chanid, |
2063 | fdout, fdin, fderr, | 2299 | fdout, fdin, fderr, |
2064 | fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, | 2300 | fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, |
2065 | 1, | 2301 | 1, is_tty, CHAN_SES_WINDOW_DEFAULT); |
2066 | CHAN_SES_WINDOW_DEFAULT); | ||
2067 | } | 2302 | } |
2068 | 2303 | ||
2069 | /* | 2304 | /* |
@@ -2095,8 +2330,9 @@ session_pty_cleanup2(Session *s) | |||
2095 | * the pty cleanup, so that another process doesn't get this pty | 2330 | * the pty cleanup, so that another process doesn't get this pty |
2096 | * while we're still cleaning up. | 2331 | * while we're still cleaning up. |
2097 | */ | 2332 | */ |
2098 | if (close(s->ptymaster) < 0) | 2333 | if (s->ptymaster != -1 && close(s->ptymaster) < 0) |
2099 | error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno)); | 2334 | error("close(s->ptymaster/%d): %s", |
2335 | s->ptymaster, strerror(errno)); | ||
2100 | 2336 | ||
2101 | /* unlink pty from session */ | 2337 | /* unlink pty from session */ |
2102 | s->ttyfd = -1; | 2338 | s->ttyfd = -1; |
@@ -2204,7 +2440,7 @@ session_exit_message(Session *s, int status) | |||
2204 | channel_request_start(s->chanid, "exit-signal", 0); | 2440 | channel_request_start(s->chanid, "exit-signal", 0); |
2205 | packet_put_cstring(sig2name(WTERMSIG(status))); | 2441 | packet_put_cstring(sig2name(WTERMSIG(status))); |
2206 | #ifdef WCOREDUMP | 2442 | #ifdef WCOREDUMP |
2207 | packet_put_char(WCOREDUMP(status)); | 2443 | packet_put_char(WCOREDUMP(status)? 1 : 0); |
2208 | #else /* WCOREDUMP */ | 2444 | #else /* WCOREDUMP */ |
2209 | packet_put_char(0); | 2445 | packet_put_char(0); |
2210 | #endif /* WCOREDUMP */ | 2446 | #endif /* WCOREDUMP */ |
@@ -2256,7 +2492,6 @@ session_close(Session *s) | |||
2256 | xfree(s->auth_data); | 2492 | xfree(s->auth_data); |
2257 | if (s->auth_proto) | 2493 | if (s->auth_proto) |
2258 | xfree(s->auth_proto); | 2494 | xfree(s->auth_proto); |
2259 | s->used = 0; | ||
2260 | if (s->env != NULL) { | 2495 | if (s->env != NULL) { |
2261 | for (i = 0; i < s->num_env; i++) { | 2496 | for (i = 0; i < s->num_env; i++) { |
2262 | xfree(s->env[i].name); | 2497 | xfree(s->env[i].name); |
@@ -2265,6 +2500,7 @@ session_close(Session *s) | |||
2265 | xfree(s->env); | 2500 | xfree(s->env); |
2266 | } | 2501 | } |
2267 | session_proctitle(s); | 2502 | session_proctitle(s); |
2503 | session_unused(s->self); | ||
2268 | } | 2504 | } |
2269 | 2505 | ||
2270 | void | 2506 | void |
@@ -2328,7 +2564,7 @@ void | |||
2328 | session_destroy_all(void (*closefunc)(Session *)) | 2564 | session_destroy_all(void (*closefunc)(Session *)) |
2329 | { | 2565 | { |
2330 | int i; | 2566 | int i; |
2331 | for (i = 0; i < MAX_SESSIONS; i++) { | 2567 | for (i = 0; i < sessions_nalloc; i++) { |
2332 | Session *s = &sessions[i]; | 2568 | Session *s = &sessions[i]; |
2333 | if (s->used) { | 2569 | if (s->used) { |
2334 | if (closefunc != NULL) | 2570 | if (closefunc != NULL) |
@@ -2347,7 +2583,7 @@ session_tty_list(void) | |||
2347 | char *cp; | 2583 | char *cp; |
2348 | 2584 | ||
2349 | buf[0] = '\0'; | 2585 | buf[0] = '\0'; |
2350 | for (i = 0; i < MAX_SESSIONS; i++) { | 2586 | for (i = 0; i < sessions_nalloc; i++) { |
2351 | Session *s = &sessions[i]; | 2587 | Session *s = &sessions[i]; |
2352 | if (s->used && s->ttyfd != -1) { | 2588 | if (s->used && s->ttyfd != -1) { |
2353 | 2589 | ||