diff options
author | Damien Miller <djm@mindrot.org> | 2004-06-15 10:34:08 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2004-06-15 10:34:08 +1000 |
commit | 0e220dbfbcc9fe252e8f1f4890dbfa415aad35db (patch) | |
tree | b78bab0c628cd5bdb0ec95340f533a1be2fae75f /ssh.c | |
parent | 05202ffe214115afa24bf6e7a6d8c8457e6759bb (diff) |
- djm@cvs.openbsd.org 2004/06/13 15:03:02
[channels.c channels.h clientloop.c clientloop.h includes.h readconf.c]
[readconf.h scp.1 sftp.1 ssh.1 ssh.c ssh_config.5]
implement session multiplexing in the client (the server has supported
this since 2.0); ok markus@
Diffstat (limited to 'ssh.c')
-rw-r--r-- | ssh.c | 276 |
1 files changed, 184 insertions, 92 deletions
@@ -40,7 +40,7 @@ | |||
40 | */ | 40 | */ |
41 | 41 | ||
42 | #include "includes.h" | 42 | #include "includes.h" |
43 | RCSID("$OpenBSD: ssh.c,v 1.213 2004/05/08 00:01:37 deraadt Exp $"); | 43 | RCSID("$OpenBSD: ssh.c,v 1.214 2004/06/13 15:03:02 djm Exp $"); |
44 | 44 | ||
45 | #include <openssl/evp.h> | 45 | #include <openssl/evp.h> |
46 | #include <openssl/err.h> | 46 | #include <openssl/err.h> |
@@ -53,21 +53,24 @@ RCSID("$OpenBSD: ssh.c,v 1.213 2004/05/08 00:01:37 deraadt Exp $"); | |||
53 | #include "xmalloc.h" | 53 | #include "xmalloc.h" |
54 | #include "packet.h" | 54 | #include "packet.h" |
55 | #include "buffer.h" | 55 | #include "buffer.h" |
56 | #include "bufaux.h" | ||
56 | #include "channels.h" | 57 | #include "channels.h" |
57 | #include "key.h" | 58 | #include "key.h" |
58 | #include "authfd.h" | 59 | #include "authfd.h" |
59 | #include "authfile.h" | 60 | #include "authfile.h" |
60 | #include "pathnames.h" | 61 | #include "pathnames.h" |
62 | #include "dispatch.h" | ||
61 | #include "clientloop.h" | 63 | #include "clientloop.h" |
62 | #include "log.h" | 64 | #include "log.h" |
63 | #include "readconf.h" | 65 | #include "readconf.h" |
64 | #include "sshconnect.h" | 66 | #include "sshconnect.h" |
65 | #include "dispatch.h" | ||
66 | #include "misc.h" | 67 | #include "misc.h" |
67 | #include "kex.h" | 68 | #include "kex.h" |
68 | #include "mac.h" | 69 | #include "mac.h" |
69 | #include "sshpty.h" | 70 | #include "sshpty.h" |
70 | #include "match.h" | 71 | #include "match.h" |
72 | #include "msg.h" | ||
73 | #include "monitor_fdpass.h" | ||
71 | 74 | ||
72 | #ifdef SMARTCARD | 75 | #ifdef SMARTCARD |
73 | #include "scard.h" | 76 | #include "scard.h" |
@@ -141,6 +144,13 @@ static int client_global_request_id = 0; | |||
141 | /* pid of proxycommand child process */ | 144 | /* pid of proxycommand child process */ |
142 | pid_t proxy_command_pid = 0; | 145 | pid_t proxy_command_pid = 0; |
143 | 146 | ||
147 | /* fd to control socket */ | ||
148 | int control_fd = -1; | ||
149 | |||
150 | /* Only used in control client mode */ | ||
151 | volatile sig_atomic_t control_client_terminate = 0; | ||
152 | u_int control_server_pid = 0; | ||
153 | |||
144 | /* Prints a help message to the user. This function never returns. */ | 154 | /* Prints a help message to the user. This function never returns. */ |
145 | 155 | ||
146 | static void | 156 | static void |
@@ -158,6 +168,7 @@ usage(void) | |||
158 | static int ssh_session(void); | 168 | static int ssh_session(void); |
159 | static int ssh_session2(void); | 169 | static int ssh_session2(void); |
160 | static void load_public_identity_files(void); | 170 | static void load_public_identity_files(void); |
171 | static void control_client(const char *path); | ||
161 | 172 | ||
162 | /* | 173 | /* |
163 | * Main program for the ssh client. | 174 | * Main program for the ssh client. |
@@ -228,7 +239,7 @@ main(int ac, char **av) | |||
228 | 239 | ||
229 | again: | 240 | again: |
230 | while ((opt = getopt(ac, av, | 241 | while ((opt = getopt(ac, av, |
231 | "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:NPR:TVXY")) != -1) { | 242 | "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:MNPR:S:TVXY")) != -1) { |
232 | switch (opt) { | 243 | switch (opt) { |
233 | case '1': | 244 | case '1': |
234 | options.protocol = SSH_PROTO_1; | 245 | options.protocol = SSH_PROTO_1; |
@@ -364,6 +375,9 @@ again: | |||
364 | exit(1); | 375 | exit(1); |
365 | } | 376 | } |
366 | break; | 377 | break; |
378 | case 'M': | ||
379 | options.control_master = 1; | ||
380 | break; | ||
367 | case 'p': | 381 | case 'p': |
368 | options.port = a2port(optarg); | 382 | options.port = a2port(optarg); |
369 | if (options.port == 0) { | 383 | if (options.port == 0) { |
@@ -432,6 +446,13 @@ again: | |||
432 | case 's': | 446 | case 's': |
433 | subsystem_flag = 1; | 447 | subsystem_flag = 1; |
434 | break; | 448 | break; |
449 | case 'S': | ||
450 | if (options.control_path != NULL) | ||
451 | free(options.control_path); | ||
452 | options.control_path = xstrdup(optarg); | ||
453 | if (options.control_master == -1) | ||
454 | options.control_master = 0; | ||
455 | break; | ||
435 | case 'b': | 456 | case 'b': |
436 | options.bind_address = optarg; | 457 | options.bind_address = optarg; |
437 | break; | 458 | break; |
@@ -566,6 +587,13 @@ again: | |||
566 | strcmp(options.proxy_command, "none") == 0) | 587 | strcmp(options.proxy_command, "none") == 0) |
567 | options.proxy_command = NULL; | 588 | options.proxy_command = NULL; |
568 | 589 | ||
590 | if (options.control_path != NULL) { | ||
591 | options.control_path = tilde_expand_filename( | ||
592 | options.control_path, original_real_uid); | ||
593 | } | ||
594 | if (options.control_path != NULL && options.control_master == 0) | ||
595 | control_client(options.control_path); /* This doesn't return */ | ||
596 | |||
569 | /* Open a connection to the remote host. */ | 597 | /* Open a connection to the remote host. */ |
570 | if (ssh_connect(host, &hostaddr, options.port, | 598 | if (ssh_connect(host, &hostaddr, options.port, |
571 | options.address_family, options.connection_attempts, | 599 | options.address_family, options.connection_attempts, |
@@ -678,6 +706,9 @@ again: | |||
678 | exit_status = compat20 ? ssh_session2() : ssh_session(); | 706 | exit_status = compat20 ? ssh_session2() : ssh_session(); |
679 | packet_close(); | 707 | packet_close(); |
680 | 708 | ||
709 | if (options.control_path != NULL && control_fd != -1) | ||
710 | unlink(options.control_path); | ||
711 | |||
681 | /* | 712 | /* |
682 | * Send SIGHUP to proxy command if used. We don't wait() in | 713 | * Send SIGHUP to proxy command if used. We don't wait() in |
683 | * case it hangs and instead rely on init to reap the child | 714 | * case it hangs and instead rely on init to reap the child |
@@ -974,7 +1005,7 @@ ssh_session(void) | |||
974 | } | 1005 | } |
975 | 1006 | ||
976 | static void | 1007 | static void |
977 | client_subsystem_reply(int type, u_int32_t seq, void *ctxt) | 1008 | ssh_subsystem_reply(int type, u_int32_t seq, void *ctxt) |
978 | { | 1009 | { |
979 | int id, len; | 1010 | int id, len; |
980 | 1011 | ||
@@ -1006,40 +1037,50 @@ client_global_request_reply_fwd(int type, u_int32_t seq, void *ctxt) | |||
1006 | options.remote_forwards[i].port); | 1037 | options.remote_forwards[i].port); |
1007 | } | 1038 | } |
1008 | 1039 | ||
1009 | /* request pty/x11/agent/tcpfwd/shell for channel */ | ||
1010 | static void | 1040 | static void |
1011 | ssh_session2_setup(int id, void *arg) | 1041 | ssh_control_listener(void) |
1012 | { | 1042 | { |
1013 | int len; | 1043 | struct sockaddr_un addr; |
1014 | int interactive = 0; | 1044 | mode_t old_umask; |
1015 | struct termios tio; | 1045 | |
1046 | if (options.control_path == NULL || options.control_master != 1) | ||
1047 | return; | ||
1016 | 1048 | ||
1017 | debug2("ssh_session2_setup: id %d", id); | 1049 | memset(&addr, '\0', sizeof(addr)); |
1050 | addr.sun_family = AF_UNIX; | ||
1051 | addr.sun_len = offsetof(struct sockaddr_un, sun_path) + | ||
1052 | strlen(options.control_path) + 1; | ||
1018 | 1053 | ||
1019 | if (tty_flag) { | 1054 | if (strlcpy(addr.sun_path, options.control_path, |
1020 | struct winsize ws; | 1055 | sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) |
1021 | char *cp; | 1056 | fatal("ControlPath too long"); |
1022 | cp = getenv("TERM"); | ||
1023 | if (!cp) | ||
1024 | cp = ""; | ||
1025 | /* Store window size in the packet. */ | ||
1026 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) | ||
1027 | memset(&ws, 0, sizeof(ws)); | ||
1028 | 1057 | ||
1029 | channel_request_start(id, "pty-req", 0); | 1058 | if ((control_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) |
1030 | packet_put_cstring(cp); | 1059 | fatal("%s socket(): %s\n", __func__, strerror(errno)); |
1031 | packet_put_int(ws.ws_col); | 1060 | |
1032 | packet_put_int(ws.ws_row); | 1061 | old_umask = umask(0177); |
1033 | packet_put_int(ws.ws_xpixel); | 1062 | if (bind(control_fd, (struct sockaddr*)&addr, addr.sun_len) == -1) { |
1034 | packet_put_int(ws.ws_ypixel); | 1063 | control_fd = -1; |
1035 | tio = get_saved_tio(); | 1064 | if (errno == EINVAL) |
1036 | tty_make_modes(/*ignored*/ 0, &tio); | 1065 | fatal("ControlSocket %s already exists", |
1037 | packet_send(); | 1066 | options.control_path); |
1038 | interactive = 1; | 1067 | else |
1039 | /* XXX wait for reply */ | 1068 | fatal("%s bind(): %s\n", __func__, strerror(errno)); |
1040 | } | 1069 | } |
1041 | if (options.forward_x11 && | 1070 | umask(old_umask); |
1042 | getenv("DISPLAY") != NULL) { | 1071 | |
1072 | if (listen(control_fd, 64) == -1) | ||
1073 | fatal("%s listen(): %s\n", __func__, strerror(errno)); | ||
1074 | |||
1075 | set_nonblock(control_fd); | ||
1076 | } | ||
1077 | |||
1078 | /* request pty/x11/agent/tcpfwd/shell for channel */ | ||
1079 | static void | ||
1080 | ssh_session2_setup(int id, void *arg) | ||
1081 | { | ||
1082 | int interactive = tty_flag; | ||
1083 | if (options.forward_x11 && getenv("DISPLAY") != NULL) { | ||
1043 | char *proto, *data; | 1084 | char *proto, *data; |
1044 | /* Get reasonable local authentication information. */ | 1085 | /* Get reasonable local authentication information. */ |
1045 | x11_get_proto(&proto, &data); | 1086 | x11_get_proto(&proto, &data); |
@@ -1057,65 +1098,8 @@ ssh_session2_setup(int id, void *arg) | |||
1057 | packet_send(); | 1098 | packet_send(); |
1058 | } | 1099 | } |
1059 | 1100 | ||
1060 | /* Transfer any environment variables from client to server */ | 1101 | client_session2_setup(id, tty_flag, subsystem_flag, getenv("TERM"), |
1061 | if (options.num_send_env != 0) { | 1102 | NULL, fileno(stdin), &command, &ssh_subsystem_reply); |
1062 | int i, j, matched; | ||
1063 | extern char **environ; | ||
1064 | char *name, *val; | ||
1065 | |||
1066 | debug("Sending environment."); | ||
1067 | for (i = 0; environ && environ[i] != NULL; i++) { | ||
1068 | /* Split */ | ||
1069 | name = xstrdup(environ[i]); | ||
1070 | if ((val = strchr(name, '=')) == NULL) { | ||
1071 | free(name); | ||
1072 | continue; | ||
1073 | } | ||
1074 | *val++ = '\0'; | ||
1075 | |||
1076 | matched = 0; | ||
1077 | for (j = 0; j < options.num_send_env; j++) { | ||
1078 | if (match_pattern(name, options.send_env[j])) { | ||
1079 | matched = 1; | ||
1080 | break; | ||
1081 | } | ||
1082 | } | ||
1083 | if (!matched) { | ||
1084 | debug3("Ignored env %s", name); | ||
1085 | free(name); | ||
1086 | continue; | ||
1087 | } | ||
1088 | |||
1089 | debug("Sending env %s = %s", name, val); | ||
1090 | channel_request_start(id, "env", 0); | ||
1091 | packet_put_cstring(name); | ||
1092 | packet_put_cstring(val); | ||
1093 | packet_send(); | ||
1094 | free(name); | ||
1095 | } | ||
1096 | } | ||
1097 | |||
1098 | len = buffer_len(&command); | ||
1099 | if (len > 0) { | ||
1100 | if (len > 900) | ||
1101 | len = 900; | ||
1102 | if (subsystem_flag) { | ||
1103 | debug("Sending subsystem: %.*s", len, (u_char *)buffer_ptr(&command)); | ||
1104 | channel_request_start(id, "subsystem", /*want reply*/ 1); | ||
1105 | /* register callback for reply */ | ||
1106 | /* XXX we assume that client_loop has already been called */ | ||
1107 | dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &client_subsystem_reply); | ||
1108 | dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &client_subsystem_reply); | ||
1109 | } else { | ||
1110 | debug("Sending command: %.*s", len, (u_char *)buffer_ptr(&command)); | ||
1111 | channel_request_start(id, "exec", 0); | ||
1112 | } | ||
1113 | packet_put_string(buffer_ptr(&command), buffer_len(&command)); | ||
1114 | packet_send(); | ||
1115 | } else { | ||
1116 | channel_request_start(id, "shell", 0); | ||
1117 | packet_send(); | ||
1118 | } | ||
1119 | 1103 | ||
1120 | packet_set_interactive(interactive); | 1104 | packet_set_interactive(interactive); |
1121 | } | 1105 | } |
@@ -1161,7 +1145,7 @@ ssh_session2_open(void) | |||
1161 | 1145 | ||
1162 | channel_send_open(c->self); | 1146 | channel_send_open(c->self); |
1163 | if (!no_shell_flag) | 1147 | if (!no_shell_flag) |
1164 | channel_register_confirm(c->self, ssh_session2_setup); | 1148 | channel_register_confirm(c->self, ssh_session2_setup, NULL); |
1165 | 1149 | ||
1166 | return c->self; | 1150 | return c->self; |
1167 | } | 1151 | } |
@@ -1173,6 +1157,7 @@ ssh_session2(void) | |||
1173 | 1157 | ||
1174 | /* XXX should be pre-session */ | 1158 | /* XXX should be pre-session */ |
1175 | ssh_init_forwarding(); | 1159 | ssh_init_forwarding(); |
1160 | ssh_control_listener(); | ||
1176 | 1161 | ||
1177 | if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN)) | 1162 | if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN)) |
1178 | id = ssh_session2_open(); | 1163 | id = ssh_session2_open(); |
@@ -1226,3 +1211,110 @@ load_public_identity_files(void) | |||
1226 | options.identity_keys[i] = public; | 1211 | options.identity_keys[i] = public; |
1227 | } | 1212 | } |
1228 | } | 1213 | } |
1214 | |||
1215 | static void | ||
1216 | control_client_sighandler(int signo) | ||
1217 | { | ||
1218 | control_client_terminate = signo; | ||
1219 | } | ||
1220 | |||
1221 | static void | ||
1222 | control_client_sigrelay(int signo) | ||
1223 | { | ||
1224 | if (control_server_pid > 1) | ||
1225 | kill(control_server_pid, signo); | ||
1226 | } | ||
1227 | |||
1228 | static void | ||
1229 | control_client(const char *path) | ||
1230 | { | ||
1231 | struct sockaddr_un addr; | ||
1232 | int r, sock, exitval; | ||
1233 | Buffer m; | ||
1234 | char *cp; | ||
1235 | |||
1236 | memset(&addr, '\0', sizeof(addr)); | ||
1237 | addr.sun_family = AF_UNIX; | ||
1238 | addr.sun_len = offsetof(struct sockaddr_un, sun_path) + | ||
1239 | strlen(path) + 1; | ||
1240 | |||
1241 | if (strlcpy(addr.sun_path, path, | ||
1242 | sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) | ||
1243 | fatal("ControlPath too long"); | ||
1244 | |||
1245 | if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) | ||
1246 | fatal("%s socket(): %s", __func__, strerror(errno)); | ||
1247 | |||
1248 | if (connect(sock, (struct sockaddr*)&addr, addr.sun_len) == -1) | ||
1249 | fatal("Couldn't connect to %s: %s", path, strerror(errno)); | ||
1250 | |||
1251 | if ((cp = getenv("TERM")) == NULL) | ||
1252 | cp = ""; | ||
1253 | |||
1254 | signal(SIGINT, control_client_sighandler); | ||
1255 | signal(SIGTERM, control_client_sighandler); | ||
1256 | signal(SIGWINCH, control_client_sigrelay); | ||
1257 | |||
1258 | buffer_init(&m); | ||
1259 | |||
1260 | /* Get PID of controlee */ | ||
1261 | if (ssh_msg_recv(sock, &m) == -1) | ||
1262 | fatal("%s: msg_recv", __func__); | ||
1263 | if (buffer_get_char(&m) != 0) | ||
1264 | fatal("%s: wrong version", __func__); | ||
1265 | control_server_pid = buffer_get_int(&m); | ||
1266 | |||
1267 | /* XXX: env passing */ | ||
1268 | |||
1269 | buffer_clear(&m); | ||
1270 | buffer_put_int(&m, tty_flag); | ||
1271 | buffer_put_int(&m, subsystem_flag); | ||
1272 | buffer_put_cstring(&m, cp); | ||
1273 | |||
1274 | buffer_append(&command, "\0", 1); | ||
1275 | buffer_put_cstring(&m, buffer_ptr(&command)); | ||
1276 | |||
1277 | if (ssh_msg_send(sock, /* version */0, &m) == -1) | ||
1278 | fatal("%s: msg_send", __func__); | ||
1279 | |||
1280 | mm_send_fd(sock, STDIN_FILENO); | ||
1281 | mm_send_fd(sock, STDOUT_FILENO); | ||
1282 | mm_send_fd(sock, STDERR_FILENO); | ||
1283 | |||
1284 | /* Wait for reply, so master has a chance to gather ttymodes */ | ||
1285 | buffer_clear(&m); | ||
1286 | if (ssh_msg_recv(sock, &m) == -1) | ||
1287 | fatal("%s: msg_recv", __func__); | ||
1288 | if (buffer_get_char(&m) != 0) | ||
1289 | fatal("%s: master returned error", __func__); | ||
1290 | buffer_free(&m); | ||
1291 | |||
1292 | if (tty_flag) | ||
1293 | enter_raw_mode(); | ||
1294 | |||
1295 | /* Stick around until the controlee closes the client_fd */ | ||
1296 | exitval = 0; | ||
1297 | for (;!control_client_terminate;) { | ||
1298 | r = read(sock, &exitval, sizeof(exitval)); | ||
1299 | if (r == 0) { | ||
1300 | debug2("Received EOF from master"); | ||
1301 | break; | ||
1302 | } | ||
1303 | if (r > 0) | ||
1304 | debug2("Received exit status from master %d", exitval); | ||
1305 | if (r == -1 && errno != EINTR) | ||
1306 | fatal("%s: read %s", __func__, strerror(errno)); | ||
1307 | } | ||
1308 | |||
1309 | if (control_client_terminate) | ||
1310 | debug2("Exiting on signal %d", control_client_terminate); | ||
1311 | |||
1312 | close(sock); | ||
1313 | |||
1314 | leave_raw_mode(); | ||
1315 | |||
1316 | if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET) | ||
1317 | fprintf(stderr, "Connection to master closed.\r\n"); | ||
1318 | |||
1319 | exit(exitval); | ||
1320 | } | ||