blob: 8d73e61e337433d26e4b1b3075ee9b643cdcaf14 [file] [log] [blame]
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001/*
Damien Miller95def091999-11-25 00:26:21 +11002 * Author: Tatu Ylonen <ylo@cs.hut.fi>
3 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4 * All rights reserved
5 * Created: Sat Mar 18 16:36:11 1995 ylo
6 * Ssh client program. This program can be used to log into a remote machine.
7 * The software supports strong authentication, encryption, and forwarding
8 * of X11, TCP/IP, and authentication connections.
9 *
10 * Modified to work with SSL by Niels Provos <provos@citi.umich.edu> in Canada.
11 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100012
13#include "includes.h"
Damien Miller95def091999-11-25 00:26:21 +110014RCSID("$Id: ssh.c,v 1.11 1999/11/24 13:26:23 damien Exp $");
Damien Millerd4a8b7e1999-10-27 13:42:43 +100015
16#include "xmalloc.h"
17#include "ssh.h"
18#include "packet.h"
19#include "buffer.h"
20#include "authfd.h"
21#include "readconf.h"
22#include "uidswap.h"
23
Damien Miller3f905871999-11-15 17:10:57 +110024#ifdef HAVE___PROGNAME
25extern char *__progname;
26#else /* HAVE___PROGNAME */
27const char *__progname = "ssh";
28#endif /* HAVE___PROGNAME */
29
Damien Miller95def091999-11-25 00:26:21 +110030/* Flag indicating whether debug mode is on. This can be set on the command line. */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100031int debug_flag = 0;
32
Damien Miller95def091999-11-25 00:26:21 +110033/* Flag indicating whether to allocate a pseudo tty. This can be set on the command
34 line, and is automatically set if no command is given on the command line. */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100035int tty_flag = 0;
36
37/* Flag indicating that nothing should be read from stdin. This can be set
38 on the command line. */
39int stdin_null_flag = 0;
40
41/* Flag indicating that ssh should fork after authentication. This is useful
42 so that the pasphrase can be entered manually, and then ssh goes to the
43 background. */
44int fork_after_authentication_flag = 0;
45
46/* General data structure for command line options and options configurable
47 in configuration files. See readconf.h. */
48Options options;
49
50/* Name of the host we are connecting to. This is the name given on the
51 command line, or the HostName specified for the user-supplied name
52 in a configuration file. */
53char *host;
54
55/* socket address the host resolves to */
56struct sockaddr_in hostaddr;
57
58/* Flag to indicate that we have received a window change signal which has
59 not yet been processed. This will cause a message indicating the new
60 window size to be sent to the server a little later. This is volatile
61 because this is updated in a signal handler. */
62volatile int received_window_change_signal = 0;
63
64/* Value of argv[0] (set in the main program). */
65char *av0;
66
67/* Flag indicating whether we have a valid host private key loaded. */
68int host_private_key_loaded = 0;
69
70/* Host private key. */
71RSA *host_private_key = NULL;
72
73/* Original real UID. */
74uid_t original_real_uid;
75
76/* Prints a help message to the user. This function never returns. */
77
78void
79usage()
80{
Damien Miller95def091999-11-25 00:26:21 +110081 fprintf(stderr, "Usage: %s [options] host [command]\n", av0);
82 fprintf(stderr, "Options:\n");
83 fprintf(stderr, " -l user Log in using this user name.\n");
84 fprintf(stderr, " -n Redirect input from /dev/null.\n");
85 fprintf(stderr, " -a Disable authentication agent forwarding.\n");
Damien Millerd4a8b7e1999-10-27 13:42:43 +100086#ifdef AFS
Damien Miller95def091999-11-25 00:26:21 +110087 fprintf(stderr, " -k Disable Kerberos ticket and AFS token forwarding.\n");
88#endif /* AFS */
89 fprintf(stderr, " -x Disable X11 connection forwarding.\n");
90 fprintf(stderr, " -i file Identity for RSA authentication (default: ~/.ssh/identity).\n");
91 fprintf(stderr, " -t Tty; allocate a tty even if command is given.\n");
92 fprintf(stderr, " -v Verbose; display verbose debugging messages.\n");
93 fprintf(stderr, " -V Display version number only.\n");
94 fprintf(stderr, " -P Don't allocate a privileged port.\n");
95 fprintf(stderr, " -q Quiet; don't display any warning messages.\n");
96 fprintf(stderr, " -f Fork into background after authentication.\n");
97 fprintf(stderr, " -e char Set escape character; ``none'' = disable (default: ~).\n");
Damien Millerd4a8b7e1999-10-27 13:42:43 +100098
Damien Miller95def091999-11-25 00:26:21 +110099 fprintf(stderr, " -c cipher Select encryption algorithm: "
100 "``3des'', "
101 "``blowfish''\n");
102 fprintf(stderr, " -p port Connect to this port. Server must be on the same port.\n");
103 fprintf(stderr, " -L listen-port:host:port Forward local port to remote address\n");
104 fprintf(stderr, " -R listen-port:host:port Forward remote port to local address\n");
105 fprintf(stderr, " These cause %s to listen for connections on a port, and\n", av0);
106 fprintf(stderr, " forward them to the other side by connecting to host:port.\n");
107 fprintf(stderr, " -C Enable compression.\n");
108 fprintf(stderr, " -g Allow remote hosts to connect to forwarded ports.\n");
109 fprintf(stderr, " -o 'option' Process the option as if it was read from a configuration file.\n");
110 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000111}
112
Damien Miller95def091999-11-25 00:26:21 +1100113/*
114 * Connects to the given host using rsh (or prints an error message and exits
115 * if rsh is not available). This function never returns.
116 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000117void
Damien Miller95def091999-11-25 00:26:21 +1100118rsh_connect(char *host, char *user, Buffer * command)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000119{
Damien Miller95def091999-11-25 00:26:21 +1100120 char *args[10];
121 int i;
122
123 log("Using rsh. WARNING: Connection will not be encrypted.");
124 /* Build argument list for rsh. */
125 i = 0;
126 args[i++] = _PATH_RSH;
127 /* host may have to come after user on some systems */
128 args[i++] = host;
129 if (user) {
130 args[i++] = "-l";
131 args[i++] = user;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000132 }
Damien Miller95def091999-11-25 00:26:21 +1100133 if (buffer_len(command) > 0) {
134 buffer_append(command, "\0", 1);
135 args[i++] = buffer_ptr(command);
136 }
137 args[i++] = NULL;
138 if (debug_flag) {
139 for (i = 0; args[i]; i++) {
140 if (i != 0)
141 fprintf(stderr, " ");
142 fprintf(stderr, "%s", args[i]);
143 }
144 fprintf(stderr, "\n");
145 }
146 execv(_PATH_RSH, args);
147 perror(_PATH_RSH);
148 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000149}
150
Damien Miller95def091999-11-25 00:26:21 +1100151/*
152 * Main program for the ssh client.
153 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000154int
155main(int ac, char **av)
156{
Damien Miller95def091999-11-25 00:26:21 +1100157 int i, opt, optind, type, exit_status, ok, fwd_port, fwd_host_port,
158 authfd;
159 char *optarg, *cp, buf[256];
160 Buffer command;
161 struct winsize ws;
162 struct stat st;
163 struct passwd *pw, pwcopy;
164 int interactive = 0, dummy;
165 uid_t original_effective_uid;
166 int plen;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000167
Damien Miller95def091999-11-25 00:26:21 +1100168 /* Save the original real uid. It will be needed later
169 (uid-swapping may clobber the real uid). */
170 original_real_uid = getuid();
171 original_effective_uid = geteuid();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000172
Damien Miller95def091999-11-25 00:26:21 +1100173 /* If we are installed setuid root be careful to not drop core. */
174 if (original_real_uid != original_effective_uid) {
175 struct rlimit rlim;
176 rlim.rlim_cur = rlim.rlim_max = 0;
177 if (setrlimit(RLIMIT_CORE, &rlim) < 0)
178 fatal("setrlimit failed: %.100s", strerror(errno));
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000179 }
Damien Miller95def091999-11-25 00:26:21 +1100180 /* Use uid-swapping to give up root privileges for the duration of
181 option processing. We will re-instantiate the rights when we
182 are ready to create the privileged port, and will permanently
183 drop them when the port has been created (actually, when the
184 connection has been made, as we may need to create the port
185 several times). */
186 temporarily_use_uid(original_real_uid);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000187
Damien Miller95def091999-11-25 00:26:21 +1100188 /* Set our umask to something reasonable, as some files are
189 created with the default umask. This will make them
190 world-readable but writable only by the owner, which is ok for
191 all files for which we don't set the modes explicitly. */
192 umask(022);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000193
Damien Miller95def091999-11-25 00:26:21 +1100194 /* Save our own name. */
195 av0 = av[0];
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000196
Damien Miller95def091999-11-25 00:26:21 +1100197 /* Initialize option structure to indicate that no values have been set. */
198 initialize_options(&options);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000199
Damien Miller95def091999-11-25 00:26:21 +1100200 /* Parse command-line arguments. */
201 host = NULL;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000202
Damien Miller95def091999-11-25 00:26:21 +1100203 /* If program name is not one of the standard names, use it as host name. */
204 if (strchr(av0, '/'))
205 cp = strrchr(av0, '/') + 1;
206 else
207 cp = av0;
208 if (strcmp(cp, "rsh") != 0 && strcmp(cp, "ssh") != 0 &&
209 strcmp(cp, "rlogin") != 0 && strcmp(cp, "slogin") != 0)
210 host = cp;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000211
Damien Miller95def091999-11-25 00:26:21 +1100212 for (optind = 1; optind < ac; optind++) {
213 if (av[optind][0] != '-') {
214 if (host)
215 break;
216 if ((cp = strchr(av[optind], '@'))) {
217 options.user = av[optind];
218 *cp = '\0';
219 host = ++cp;
220 } else
221 host = av[optind];
222 continue;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000223 }
Damien Miller95def091999-11-25 00:26:21 +1100224 opt = av[optind][1];
225 if (!opt)
226 usage();
227 if (strchr("eilcpLRo", opt)) { /* options with arguments */
228 optarg = av[optind] + 2;
229 if (strcmp(optarg, "") == 0) {
230 if (optind >= ac - 1)
231 usage();
232 optarg = av[++optind];
233 }
234 } else {
235 if (av[optind][2])
236 usage();
237 optarg = NULL;
238 }
239 switch (opt) {
240 case 'n':
241 stdin_null_flag = 1;
242 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000243
Damien Miller95def091999-11-25 00:26:21 +1100244 case 'f':
245 fork_after_authentication_flag = 1;
246 stdin_null_flag = 1;
247 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000248
Damien Miller95def091999-11-25 00:26:21 +1100249 case 'x':
250 options.forward_x11 = 0;
251 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000252
Damien Miller95def091999-11-25 00:26:21 +1100253 case 'X':
254 options.forward_x11 = 1;
255 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000256
Damien Miller95def091999-11-25 00:26:21 +1100257 case 'g':
258 options.gateway_ports = 1;
259 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000260
Damien Miller95def091999-11-25 00:26:21 +1100261 case 'P':
262 options.use_privileged_port = 0;
263 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000264
Damien Miller95def091999-11-25 00:26:21 +1100265 case 'a':
266 options.forward_agent = 0;
267 break;
268#ifdef AFS
269 case 'k':
270 options.kerberos_tgt_passing = 0;
271 options.afs_token_passing = 0;
272 break;
273#endif
274 case 'i':
275 if (stat(optarg, &st) < 0) {
276 fprintf(stderr, "Warning: Identity file %s does not exist.\n",
277 optarg);
278 break;
279 }
280 if (options.num_identity_files >= SSH_MAX_IDENTITY_FILES)
281 fatal("Too many identity files specified (max %d)",
282 SSH_MAX_IDENTITY_FILES);
283 options.identity_files[options.num_identity_files++] =
284 xstrdup(optarg);
285 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000286
Damien Miller95def091999-11-25 00:26:21 +1100287 case 't':
288 tty_flag = 1;
289 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000290
Damien Miller95def091999-11-25 00:26:21 +1100291 case 'v':
292 case 'V':
293 fprintf(stderr, "SSH Version %s, protocol version %d.%d.\n",
294 SSH_VERSION, PROTOCOL_MAJOR, PROTOCOL_MINOR);
295 fprintf(stderr, "Compiled with SSL.\n");
296 if (opt == 'V')
297 exit(0);
298 debug_flag = 1;
299 options.log_level = SYSLOG_LEVEL_DEBUG;
300 break;
301
302 case 'q':
303 options.log_level = SYSLOG_LEVEL_QUIET;
304 break;
305
306 case 'e':
307 if (optarg[0] == '^' && optarg[2] == 0 &&
308 (unsigned char) optarg[1] >= 64 && (unsigned char) optarg[1] < 128)
309 options.escape_char = (unsigned char) optarg[1] & 31;
310 else if (strlen(optarg) == 1)
311 options.escape_char = (unsigned char) optarg[0];
312 else if (strcmp(optarg, "none") == 0)
313 options.escape_char = -2;
314 else {
315 fprintf(stderr, "Bad escape character '%s'.\n", optarg);
316 exit(1);
317 }
318 break;
319
320 case 'c':
321 options.cipher = cipher_number(optarg);
322 if (options.cipher == -1) {
323 fprintf(stderr, "Unknown cipher type '%s'\n", optarg);
324 exit(1);
325 }
326 break;
327
328 case 'p':
329 options.port = atoi(optarg);
330 if (options.port < 1 || options.port > 65535) {
331 fprintf(stderr, "Bad port %s.\n", optarg);
332 exit(1);
333 }
334 break;
335
336 case 'l':
337 options.user = optarg;
338 break;
339
340 case 'R':
341 if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf,
342 &fwd_host_port) != 3) {
343 fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg);
344 usage();
345 /* NOTREACHED */
346 }
347 add_remote_forward(&options, fwd_port, buf, fwd_host_port);
348 break;
349
350 case 'L':
351 if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf,
352 &fwd_host_port) != 3) {
353 fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg);
354 usage();
355 /* NOTREACHED */
356 }
357 add_local_forward(&options, fwd_port, buf, fwd_host_port);
358 break;
359
360 case 'C':
361 options.compression = 1;
362 break;
363
364 case 'o':
365 dummy = 1;
366 if (process_config_line(&options, host ? host : "", optarg,
367 "command-line", 0, &dummy) != 0)
368 exit(1);
369 break;
370
371 default:
372 usage();
373 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000374 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000375
Damien Miller95def091999-11-25 00:26:21 +1100376 /* Check that we got a host name. */
377 if (!host)
378 usage();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000379
Damien Miller95def091999-11-25 00:26:21 +1100380 /* check if RSA support exists */
381 if (rsa_alive() == 0) {
382 fprintf(stderr,
383 "%s: no RSA support in libssl and libcrypto. See ssl(8).\n",
384 __progname);
385 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000386 }
Damien Miller95def091999-11-25 00:26:21 +1100387 /* Initialize the command to execute on remote host. */
388 buffer_init(&command);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000389
Damien Miller95def091999-11-25 00:26:21 +1100390 /* Save the command to execute on the remote host in a buffer.
391 There is no limit on the length of the command, except by the
392 maximum packet size. Also sets the tty flag if there is no
393 command. */
394 if (optind == ac) {
395 /* No command specified - execute shell on a tty. */
396 tty_flag = 1;
397 } else {
398 /* A command has been specified. Store it into the
399 buffer. */
400 for (i = optind; i < ac; i++) {
401 if (i > optind)
402 buffer_append(&command, " ", 1);
403 buffer_append(&command, av[i], strlen(av[i]));
404 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000405 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000406
Damien Miller95def091999-11-25 00:26:21 +1100407 /* Cannot fork to background if no command. */
408 if (fork_after_authentication_flag && buffer_len(&command) == 0)
409 fatal("Cannot fork into background without a command to execute.");
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000410
Damien Miller95def091999-11-25 00:26:21 +1100411 /* Allocate a tty by default if no command specified. */
412 if (buffer_len(&command) == 0)
413 tty_flag = 1;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000414
Damien Miller95def091999-11-25 00:26:21 +1100415 /* Do not allocate a tty if stdin is not a tty. */
416 if (!isatty(fileno(stdin))) {
417 if (tty_flag)
418 fprintf(stderr, "Pseudo-terminal will not be allocated because stdin is not a terminal.\n");
419 tty_flag = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000420 }
Damien Miller95def091999-11-25 00:26:21 +1100421 /* Get user data. */
422 pw = getpwuid(original_real_uid);
423 if (!pw) {
424 fprintf(stderr, "You don't exist, go away!\n");
425 exit(1);
426 }
427 /* Take a copy of the returned structure. */
428 memset(&pwcopy, 0, sizeof(pwcopy));
429 pwcopy.pw_name = xstrdup(pw->pw_name);
430 pwcopy.pw_passwd = xstrdup(pw->pw_passwd);
431 pwcopy.pw_uid = pw->pw_uid;
432 pwcopy.pw_gid = pw->pw_gid;
433 pwcopy.pw_dir = xstrdup(pw->pw_dir);
434 pwcopy.pw_shell = xstrdup(pw->pw_shell);
435 pw = &pwcopy;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000436
Damien Miller95def091999-11-25 00:26:21 +1100437 /* Initialize "log" output. Since we are the client all output
438 actually goes to the terminal. */
439 log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000440
Damien Miller95def091999-11-25 00:26:21 +1100441 /* Read per-user configuration file. */
442 snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_CONFFILE);
443 read_config_file(buf, host, &options);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000444
Damien Miller95def091999-11-25 00:26:21 +1100445 /* Read systemwide configuration file. */
446 read_config_file(HOST_CONFIG_FILE, host, &options);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000447
Damien Miller95def091999-11-25 00:26:21 +1100448 /* Fill configuration defaults. */
449 fill_default_options(&options);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000450
Damien Miller95def091999-11-25 00:26:21 +1100451 /* reinit */
452 log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000453
Damien Miller95def091999-11-25 00:26:21 +1100454 if (options.user == NULL)
455 options.user = xstrdup(pw->pw_name);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000456
Damien Miller95def091999-11-25 00:26:21 +1100457 if (options.hostname != NULL)
458 host = options.hostname;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000459
Damien Miller95def091999-11-25 00:26:21 +1100460 /* Find canonic host name. */
461 if (strchr(host, '.') == 0) {
462 struct hostent *hp = gethostbyname(host);
463 if (hp != 0) {
464 if (strchr(hp->h_name, '.') != 0)
465 host = xstrdup(hp->h_name);
466 else if (hp->h_aliases != 0
467 && hp->h_aliases[0] != 0
468 && strchr(hp->h_aliases[0], '.') != 0)
469 host = xstrdup(hp->h_aliases[0]);
470 }
471 }
472 /* Disable rhosts authentication if not running as root. */
473 if (original_effective_uid != 0 || !options.use_privileged_port) {
474 options.rhosts_authentication = 0;
475 options.rhosts_rsa_authentication = 0;
476 }
477 /* If using rsh has been selected, exec it now (without trying
478 anything else). Note that we must release privileges first. */
479 if (options.use_rsh) {
480 /* Restore our superuser privileges. This must be done
481 before permanently setting the uid. */
482 restore_uid();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000483
Damien Miller95def091999-11-25 00:26:21 +1100484 /* Switch to the original uid permanently. */
485 permanently_set_uid(original_real_uid);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000486
Damien Miller95def091999-11-25 00:26:21 +1100487 /* Execute rsh. */
488 rsh_connect(host, options.user, &command);
489 fatal("rsh_connect returned");
490 }
491 /* Restore our superuser privileges. */
492 restore_uid();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000493
Damien Miller95def091999-11-25 00:26:21 +1100494 /* Open a connection to the remote host. This needs root
495 privileges if rhosts_{rsa_}authentication is enabled. */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000496
Damien Miller95def091999-11-25 00:26:21 +1100497 ok = ssh_connect(host, &hostaddr, options.port,
498 options.connection_attempts,
499 !options.rhosts_authentication &&
500 !options.rhosts_rsa_authentication,
501 original_real_uid,
502 options.proxy_command);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000503
Damien Miller95def091999-11-25 00:26:21 +1100504 /* If we successfully made the connection, load the host private
505 key in case we will need it later for combined rsa-rhosts
506 authentication. This must be done before releasing extra
507 privileges, because the file is only readable by root. */
508 if (ok) {
509 host_private_key = RSA_new();
510 if (load_private_key(HOST_KEY_FILE, "", host_private_key, NULL))
511 host_private_key_loaded = 1;
512 }
513 /* Get rid of any extra privileges that we may have. We will no
514 longer need them. Also, extra privileges could make it very
515 hard to read identity files and other non-world-readable files
516 from the user's home directory if it happens to be on a NFS
517 volume where root is mapped to nobody. */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000518
Damien Miller95def091999-11-25 00:26:21 +1100519 /* Note that some legacy systems need to postpone the following
520 call to permanently_set_uid() until the private hostkey is
521 destroyed with RSA_free(). Otherwise the calling user could
522 ptrace() the process, read the private hostkey and impersonate
523 the host. OpenBSD does not allow ptracing of setuid processes. */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000524
Damien Miller95def091999-11-25 00:26:21 +1100525 permanently_set_uid(original_real_uid);
526
527 /* Now that we are back to our own permissions, create ~/.ssh
528 directory if it doesn\'t already exist. */
529 snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_DIR);
530 if (stat(buf, &st) < 0)
531 if (mkdir(buf, 0755) < 0)
532 error("Could not create directory '%.200s'.", buf);
533
534 /* Check if the connection failed, and try "rsh" if appropriate. */
535 if (!ok) {
536 if (options.port != 0)
537 log("Secure connection to %.100s on port %d refused%.100s.",
538 host, options.port,
539 options.fallback_to_rsh ? "; reverting to insecure method" : "");
540 else
541 log("Secure connection to %.100s refused%.100s.", host,
542 options.fallback_to_rsh ? "; reverting to insecure method" : "");
543
544 if (options.fallback_to_rsh) {
545 rsh_connect(host, options.user, &command);
546 fatal("rsh_connect returned");
547 }
548 exit(1);
549 }
550 /* Expand ~ in options.identity_files. */
551 for (i = 0; i < options.num_identity_files; i++)
552 options.identity_files[i] =
553 tilde_expand_filename(options.identity_files[i], original_real_uid);
554
555 /* Expand ~ in known host file names. */
556 options.system_hostfile = tilde_expand_filename(options.system_hostfile,
557 original_real_uid);
558 options.user_hostfile = tilde_expand_filename(options.user_hostfile,
559 original_real_uid);
560
561 /* Log into the remote system. This never returns if the login fails. */
562 ssh_login(host_private_key_loaded, host_private_key,
563 host, &hostaddr, original_real_uid);
564
565 /* We no longer need the host private key. Clear it now. */
566 if (host_private_key_loaded)
567 RSA_free(host_private_key); /* Destroys contents safely */
568
569 /* Close connection cleanly after attack. */
570 cipher_attack_detected = packet_disconnect;
571
572 /* If requested, fork and let ssh continue in the background. */
573 if (fork_after_authentication_flag) {
574 int ret = fork();
575 if (ret == -1)
576 fatal("fork failed: %.100s", strerror(errno));
577 if (ret != 0)
578 exit(0);
579 setsid();
580 }
581 /* Enable compression if requested. */
582 if (options.compression) {
583 debug("Requesting compression at level %d.", options.compression_level);
584
585 if (options.compression_level < 1 || options.compression_level > 9)
586 fatal("Compression level must be from 1 (fast) to 9 (slow, best).");
587
588 /* Send the request. */
589 packet_start(SSH_CMSG_REQUEST_COMPRESSION);
590 packet_put_int(options.compression_level);
591 packet_send();
592 packet_write_wait();
593 type = packet_read(&plen);
594 if (type == SSH_SMSG_SUCCESS)
595 packet_start_compression(options.compression_level);
596 else if (type == SSH_SMSG_FAILURE)
597 log("Warning: Remote host refused compression.");
598 else
599 packet_disconnect("Protocol error waiting for compression response.");
600 }
601 /* Allocate a pseudo tty if appropriate. */
602 if (tty_flag) {
603 debug("Requesting pty.");
604
605 /* Start the packet. */
606 packet_start(SSH_CMSG_REQUEST_PTY);
607
608 /* Store TERM in the packet. There is no limit on the
609 length of the string. */
610 cp = getenv("TERM");
611 if (!cp)
612 cp = "";
613 packet_put_string(cp, strlen(cp));
614
615 /* Store window size in the packet. */
616 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
617 memset(&ws, 0, sizeof(ws));
618 packet_put_int(ws.ws_row);
619 packet_put_int(ws.ws_col);
620 packet_put_int(ws.ws_xpixel);
621 packet_put_int(ws.ws_ypixel);
622
623 /* Store tty modes in the packet. */
624 tty_make_modes(fileno(stdin));
625
626 /* Send the packet, and wait for it to leave. */
627 packet_send();
628 packet_write_wait();
629
630 /* Read response from the server. */
631 type = packet_read(&plen);
632 if (type == SSH_SMSG_SUCCESS)
633 interactive = 1;
634 else if (type == SSH_SMSG_FAILURE)
635 log("Warning: Remote host failed or refused to allocate a pseudo tty.");
636 else
637 packet_disconnect("Protocol error waiting for pty request response.");
638 }
639 /* Request X11 forwarding if enabled and DISPLAY is set. */
640 if (options.forward_x11 && getenv("DISPLAY") != NULL) {
641 char line[512], proto[512], data[512];
642 FILE *f;
643 int forwarded = 0, got_data = 0, i;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000644
645#ifdef XAUTH_PATH
Damien Miller95def091999-11-25 00:26:21 +1100646 /* Try to get Xauthority information for the display. */
647 snprintf(line, sizeof line, "%.100s list %.200s 2>/dev/null",
648 XAUTH_PATH, getenv("DISPLAY"));
649 f = popen(line, "r");
650 if (f && fgets(line, sizeof(line), f) &&
651 sscanf(line, "%*s %s %s", proto, data) == 2)
652 got_data = 1;
653 if (f)
654 pclose(f);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000655#endif /* XAUTH_PATH */
Damien Miller95def091999-11-25 00:26:21 +1100656 /* If we didn't get authentication data, just make up some
657 data. The forwarding code will check the validity of
658 the response anyway, and substitute this data. The X11
659 server, however, will ignore this fake data and use
660 whatever authentication mechanisms it was using
661 otherwise for the local connection. */
662 if (!got_data) {
663 u_int32_t rand = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000664
Damien Miller95def091999-11-25 00:26:21 +1100665 strlcpy(proto, "MIT-MAGIC-COOKIE-1", sizeof proto);
666 for (i = 0; i < 16; i++) {
667 if (i % 4 == 0)
668 rand = arc4random();
669 snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", rand & 0xff);
670 rand >>= 8;
671 }
672 }
673 /* Got local authentication reasonable information.
674 Request forwarding with authentication spoofing. */
675 debug("Requesting X11 forwarding with authentication spoofing.");
676 x11_request_forwarding_with_spoofing(proto, data);
677
678 /* Read response from the server. */
679 type = packet_read(&plen);
680 if (type == SSH_SMSG_SUCCESS) {
681 forwarded = 1;
682 interactive = 1;
683 } else if (type == SSH_SMSG_FAILURE)
684 log("Warning: Remote host denied X11 forwarding.");
685 else
686 packet_disconnect("Protocol error waiting for X11 forwarding");
687 }
688 /* Tell the packet module whether this is an interactive session. */
689 packet_set_interactive(interactive, options.keepalives);
690
691 /* Clear agent forwarding if we don\'t have an agent. */
692 authfd = ssh_get_authentication_socket();
693 if (authfd < 0)
694 options.forward_agent = 0;
695 else
696 ssh_close_authentication_socket(authfd);
697
698 /* Request authentication agent forwarding if appropriate. */
699 if (options.forward_agent) {
700 debug("Requesting authentication agent forwarding.");
701 auth_request_forwarding();
702
703 /* Read response from the server. */
704 type = packet_read(&plen);
705 packet_integrity_check(plen, 0, type);
706 if (type != SSH_SMSG_SUCCESS)
707 log("Warning: Remote host denied authentication agent forwarding.");
708 }
709 /* Initiate local TCP/IP port forwardings. */
710 for (i = 0; i < options.num_local_forwards; i++) {
711 debug("Connections to local port %d forwarded to remote address %.200s:%d",
712 options.local_forwards[i].port,
713 options.local_forwards[i].host,
714 options.local_forwards[i].host_port);
715 channel_request_local_forwarding(options.local_forwards[i].port,
716 options.local_forwards[i].host,
717 options.local_forwards[i].host_port);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000718 }
719
Damien Miller95def091999-11-25 00:26:21 +1100720 /* Initiate remote TCP/IP port forwardings. */
721 for (i = 0; i < options.num_remote_forwards; i++) {
722 debug("Connections to remote port %d forwarded to local address %.200s:%d",
723 options.remote_forwards[i].port,
724 options.remote_forwards[i].host,
725 options.remote_forwards[i].host_port);
726 channel_request_remote_forwarding(options.remote_forwards[i].port,
727 options.remote_forwards[i].host,
728 options.remote_forwards[i].host_port);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000729 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000730
Damien Miller95def091999-11-25 00:26:21 +1100731 /* If a command was specified on the command line, execute the
732 command now. Otherwise request the server to start a shell. */
733 if (buffer_len(&command) > 0) {
734 int len = buffer_len(&command);
735 if (len > 900)
736 len = 900;
737 debug("Sending command: %.*s", len, buffer_ptr(&command));
738 packet_start(SSH_CMSG_EXEC_CMD);
739 packet_put_string(buffer_ptr(&command), buffer_len(&command));
740 packet_send();
741 packet_write_wait();
742 } else {
743 debug("Requesting shell.");
744 packet_start(SSH_CMSG_EXEC_SHELL);
745 packet_send();
746 packet_write_wait();
747 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000748
Damien Miller95def091999-11-25 00:26:21 +1100749 /* Enter the interactive session. */
750 exit_status = client_loop(tty_flag, tty_flag ? options.escape_char : -1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000751
Damien Miller95def091999-11-25 00:26:21 +1100752 /* Close the connection to the remote host. */
753 packet_close();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000754
Damien Miller95def091999-11-25 00:26:21 +1100755 /* Exit with the status returned by the program on the remote side. */
756 exit(exit_status);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000757}