]>
Commit | Line | Data |
---|---|---|
1d7f9b6e | 1 | /* $OpenBSD: mux.c,v 1.101 2023/11/23 03:37:05 dtucker Exp $ */ |
b1cbfa25 DM |
2 | /* |
3 | * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org> | |
4 | * | |
5 | * Permission to use, copy, modify, and distribute this software for any | |
6 | * purpose with or without fee is hereby granted, provided that the above | |
7 | * copyright notice and this permission notice appear in all copies. | |
8 | * | |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | */ | |
17 | ||
18 | /* ssh session multiplexing support */ | |
19 | ||
e1537f95 DM |
20 | #include "includes.h" |
21 | ||
b1cbfa25 | 22 | #include <sys/types.h> |
b1cbfa25 DM |
23 | #include <sys/stat.h> |
24 | #include <sys/socket.h> | |
25 | #include <sys/un.h> | |
26 | ||
27 | #include <errno.h> | |
28 | #include <fcntl.h> | |
8d33f2aa | 29 | #include <limits.h> |
b1cbfa25 DM |
30 | #include <signal.h> |
31 | #include <stdarg.h> | |
32 | #include <stddef.h> | |
33 | #include <stdlib.h> | |
34 | #include <stdio.h> | |
35 | #include <string.h> | |
36 | #include <unistd.h> | |
ce38d823 | 37 | #ifdef HAVE_PATHS_H |
b1cbfa25 | 38 | #include <paths.h> |
ce38d823 | 39 | #endif |
b1cbfa25 | 40 | |
e1537f95 DM |
41 | #ifdef HAVE_POLL_H |
42 | #include <poll.h> | |
43 | #else | |
44 | # ifdef HAVE_SYS_POLL_H | |
45 | # include <sys/poll.h> | |
46 | # endif | |
47 | #endif | |
48 | ||
a7058ec7 DM |
49 | #ifdef HAVE_UTIL_H |
50 | # include <util.h> | |
51 | #endif | |
52 | ||
b1cbfa25 DM |
53 | #include "openbsd-compat/sys-queue.h" |
54 | #include "xmalloc.h" | |
55 | #include "log.h" | |
56 | #include "ssh.h" | |
388f6fc4 | 57 | #include "ssh2.h" |
b1cbfa25 DM |
58 | #include "pathnames.h" |
59 | #include "misc.h" | |
60 | #include "match.h" | |
f4608a70 | 61 | #include "sshbuf.h" |
b1cbfa25 DM |
62 | #include "channels.h" |
63 | #include "msg.h" | |
64 | #include "packet.h" | |
65 | #include "monitor_fdpass.h" | |
66 | #include "sshpty.h" | |
5467fbcb | 67 | #include "sshkey.h" |
b1cbfa25 DM |
68 | #include "readconf.h" |
69 | #include "clientloop.h" | |
8d057847 | 70 | #include "ssherr.h" |
e535fbe2 | 71 | #include "misc.h" |
b1cbfa25 DM |
72 | |
73 | /* from ssh.c */ | |
74 | extern int tty_flag; | |
75 | extern Options options; | |
b1cbfa25 | 76 | extern char *host; |
cecee2d6 | 77 | extern struct sshbuf *command; |
e1537f95 | 78 | extern volatile sig_atomic_t quit_pending; |
b1cbfa25 | 79 | |
2fb66cac DT |
80 | /* Context for session open confirmation callback */ |
81 | struct mux_session_confirm_ctx { | |
e1537f95 DM |
82 | u_int want_tty; |
83 | u_int want_subsys; | |
84 | u_int want_x_fwd; | |
85 | u_int want_agent_fwd; | |
f4608a70 | 86 | struct sshbuf *cmd; |
2fb66cac DT |
87 | char *term; |
88 | struct termios tio; | |
89 | char **env; | |
d530f5f4 | 90 | u_int rid; |
2fb66cac DT |
91 | }; |
92 | ||
357610d1 DM |
93 | /* Context for stdio fwd open confirmation callback */ |
94 | struct mux_stdio_confirm_ctx { | |
95 | u_int rid; | |
96 | }; | |
97 | ||
388f6fc4 DM |
98 | /* Context for global channel callback */ |
99 | struct mux_channel_confirm_ctx { | |
100 | u_int cid; /* channel id */ | |
101 | u_int rid; /* request id */ | |
102 | int fid; /* forward id */ | |
103 | }; | |
104 | ||
b1cbfa25 DM |
105 | /* fd to control socket */ |
106 | int muxserver_sock = -1; | |
107 | ||
e1537f95 DM |
108 | /* client request id */ |
109 | u_int muxclient_request_id = 0; | |
110 | ||
b1cbfa25 DM |
111 | /* Multiplexing control command */ |
112 | u_int muxclient_command = 0; | |
113 | ||
114 | /* Set when signalled. */ | |
115 | static volatile sig_atomic_t muxclient_terminate = 0; | |
116 | ||
117 | /* PID of multiplex server */ | |
118 | static u_int muxserver_pid = 0; | |
119 | ||
e1537f95 | 120 | static Channel *mux_listener_channel = NULL; |
b1cbfa25 | 121 | |
e1537f95 DM |
122 | struct mux_master_state { |
123 | int hello_rcvd; | |
124 | }; | |
b1cbfa25 | 125 | |
e1537f95 DM |
126 | /* mux protocol messages */ |
127 | #define MUX_MSG_HELLO 0x00000001 | |
128 | #define MUX_C_NEW_SESSION 0x10000002 | |
129 | #define MUX_C_ALIVE_CHECK 0x10000004 | |
130 | #define MUX_C_TERMINATE 0x10000005 | |
131 | #define MUX_C_OPEN_FWD 0x10000006 | |
132 | #define MUX_C_CLOSE_FWD 0x10000007 | |
133 | #define MUX_C_NEW_STDIO_FWD 0x10000008 | |
6c3eec7a | 134 | #define MUX_C_STOP_LISTENING 0x10000009 |
8d057847 | 135 | #define MUX_C_PROXY 0x1000000f |
e1537f95 DM |
136 | #define MUX_S_OK 0x80000001 |
137 | #define MUX_S_PERMISSION_DENIED 0x80000002 | |
138 | #define MUX_S_FAILURE 0x80000003 | |
139 | #define MUX_S_EXIT_MESSAGE 0x80000004 | |
140 | #define MUX_S_ALIVE 0x80000005 | |
141 | #define MUX_S_SESSION_OPENED 0x80000006 | |
388f6fc4 | 142 | #define MUX_S_REMOTE_PORT 0x80000007 |
555f3b85 | 143 | #define MUX_S_TTY_ALLOC_FAIL 0x80000008 |
8d057847 | 144 | #define MUX_S_PROXY 0x8000000f |
e1537f95 DM |
145 | |
146 | /* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ | |
147 | #define MUX_FWD_LOCAL 1 | |
148 | #define MUX_FWD_REMOTE 2 | |
149 | #define MUX_FWD_DYNAMIC 3 | |
150 | ||
dbee4119 | 151 | static void mux_session_confirm(struct ssh *, int, int, void *); |
152 | static void mux_stdio_confirm(struct ssh *, int, int, void *); | |
153 | ||
9d883a1c | 154 | static int mux_master_process_hello(struct ssh *, u_int, |
dbee4119 | 155 | Channel *, struct sshbuf *, struct sshbuf *); |
9d883a1c | 156 | static int mux_master_process_new_session(struct ssh *, u_int, |
dbee4119 | 157 | Channel *, struct sshbuf *, struct sshbuf *); |
9d883a1c | 158 | static int mux_master_process_alive_check(struct ssh *, u_int, |
dbee4119 | 159 | Channel *, struct sshbuf *, struct sshbuf *); |
9d883a1c | 160 | static int mux_master_process_terminate(struct ssh *, u_int, |
dbee4119 | 161 | Channel *, struct sshbuf *, struct sshbuf *); |
9d883a1c | 162 | static int mux_master_process_open_fwd(struct ssh *, u_int, |
dbee4119 | 163 | Channel *, struct sshbuf *, struct sshbuf *); |
9d883a1c | 164 | static int mux_master_process_close_fwd(struct ssh *, u_int, |
dbee4119 | 165 | Channel *, struct sshbuf *, struct sshbuf *); |
9d883a1c | 166 | static int mux_master_process_stdio_fwd(struct ssh *, u_int, |
dbee4119 | 167 | Channel *, struct sshbuf *, struct sshbuf *); |
9d883a1c | 168 | static int mux_master_process_stop_listening(struct ssh *, u_int, |
dbee4119 | 169 | Channel *, struct sshbuf *, struct sshbuf *); |
9d883a1c | 170 | static int mux_master_process_proxy(struct ssh *, u_int, |
dbee4119 | 171 | Channel *, struct sshbuf *, struct sshbuf *); |
e1537f95 DM |
172 | |
173 | static const struct { | |
174 | u_int type; | |
dbee4119 | 175 | int (*handler)(struct ssh *, u_int, Channel *, |
176 | struct sshbuf *, struct sshbuf *); | |
e1537f95 | 177 | } mux_master_handlers[] = { |
9d883a1c | 178 | { MUX_MSG_HELLO, mux_master_process_hello }, |
179 | { MUX_C_NEW_SESSION, mux_master_process_new_session }, | |
180 | { MUX_C_ALIVE_CHECK, mux_master_process_alive_check }, | |
181 | { MUX_C_TERMINATE, mux_master_process_terminate }, | |
182 | { MUX_C_OPEN_FWD, mux_master_process_open_fwd }, | |
183 | { MUX_C_CLOSE_FWD, mux_master_process_close_fwd }, | |
184 | { MUX_C_NEW_STDIO_FWD, mux_master_process_stdio_fwd }, | |
185 | { MUX_C_STOP_LISTENING, mux_master_process_stop_listening }, | |
186 | { MUX_C_PROXY, mux_master_process_proxy }, | |
e1537f95 DM |
187 | { 0, NULL } |
188 | }; | |
b1cbfa25 | 189 | |
6d755706 | 190 | /* Cleanup callback fired on closure of mux client _session_ channel */ |
ea8342c2 | 191 | static void |
c6043815 | 192 | mux_master_session_cleanup_cb(struct ssh *ssh, int cid, int force, void *unused) |
e1537f95 | 193 | { |
dbee4119 | 194 | Channel *cc, *c = channel_by_id(ssh, cid); |
e1537f95 | 195 | |
816036f1 | 196 | debug3_f("entering for channel %d", cid); |
e1537f95 | 197 | if (c == NULL) |
816036f1 | 198 | fatal_f("channel_by_id(%i) == NULL", cid); |
e1537f95 | 199 | if (c->ctl_chan != -1) { |
dbee4119 | 200 | if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) |
816036f1 | 201 | fatal_f("channel %d missing control channel %d", |
202 | c->self, c->ctl_chan); | |
e1537f95 | 203 | c->ctl_chan = -1; |
9f53229c | 204 | cc->remote_id = 0; |
205 | cc->have_remote_id = 0; | |
dbee4119 | 206 | chan_rcvd_oclose(ssh, cc); |
b1cbfa25 | 207 | } |
dbee4119 | 208 | channel_cancel_cleanup(ssh, c->self); |
b1cbfa25 DM |
209 | } |
210 | ||
6d755706 | 211 | /* Cleanup callback fired on closure of mux client _control_ channel */ |
b1cbfa25 | 212 | static void |
c6043815 | 213 | mux_master_control_cleanup_cb(struct ssh *ssh, int cid, int force, void *unused) |
b1cbfa25 | 214 | { |
dbee4119 | 215 | Channel *sc, *c = channel_by_id(ssh, cid); |
e1537f95 | 216 | |
816036f1 | 217 | debug3_f("entering for channel %d", cid); |
e1537f95 | 218 | if (c == NULL) |
816036f1 | 219 | fatal_f("channel_by_id(%i) == NULL", cid); |
9f53229c | 220 | if (c->have_remote_id) { |
dbee4119 | 221 | if ((sc = channel_by_id(ssh, c->remote_id)) == NULL) |
816036f1 | 222 | fatal_f("channel %d missing session channel %u", |
223 | c->self, c->remote_id); | |
9f53229c | 224 | c->remote_id = 0; |
225 | c->have_remote_id = 0; | |
e1537f95 | 226 | sc->ctl_chan = -1; |
172859cf DM |
227 | if (sc->type != SSH_CHANNEL_OPEN && |
228 | sc->type != SSH_CHANNEL_OPENING) { | |
816036f1 | 229 | debug2_f("channel %d: not open", sc->self); |
dbee4119 | 230 | chan_mark_dead(ssh, sc); |
a21cdfac | 231 | } else { |
0dac03fe | 232 | if (sc->istate == CHAN_INPUT_OPEN) |
dbee4119 | 233 | chan_read_failed(ssh, sc); |
0dac03fe | 234 | if (sc->ostate == CHAN_OUTPUT_OPEN) |
dbee4119 | 235 | chan_write_failed(ssh, sc); |
a21cdfac | 236 | } |
b1cbfa25 | 237 | } |
dbee4119 | 238 | channel_cancel_cleanup(ssh, c->self); |
b1cbfa25 DM |
239 | } |
240 | ||
e1537f95 DM |
241 | /* Check mux client environment variables before passing them to mux master. */ |
242 | static int | |
37b62fd5 | 243 | env_permitted(const char *env) |
b1cbfa25 | 244 | { |
22e1a3a7 | 245 | u_int i; |
246 | int ret; | |
e1537f95 | 247 | char name[1024], *cp; |
b1cbfa25 | 248 | |
e1537f95 | 249 | if ((cp = strchr(env, '=')) == NULL || cp == env) |
b1cbfa25 | 250 | return 0; |
e1537f95 DM |
251 | ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); |
252 | if (ret <= 0 || (size_t)ret >= sizeof(name)) { | |
816036f1 | 253 | error_f("name '%.100s...' too long", env); |
b1cbfa25 DM |
254 | return 0; |
255 | } | |
256 | ||
e1537f95 DM |
257 | for (i = 0; i < options.num_send_env; i++) |
258 | if (match_pattern(name, options.send_env[i])) | |
259 | return 1; | |
260 | ||
261 | return 0; | |
262 | } | |
263 | ||
264 | /* Mux master protocol message handlers */ | |
b1cbfa25 | 265 | |
e1537f95 | 266 | static int |
9d883a1c | 267 | mux_master_process_hello(struct ssh *ssh, u_int rid, |
f4608a70 | 268 | Channel *c, struct sshbuf *m, struct sshbuf *reply) |
e1537f95 DM |
269 | { |
270 | u_int ver; | |
271 | struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; | |
f4608a70 | 272 | int r; |
e1537f95 DM |
273 | |
274 | if (state == NULL) | |
816036f1 | 275 | fatal_f("channel %d: c->mux_ctx == NULL", c->self); |
e1537f95 | 276 | if (state->hello_rcvd) { |
816036f1 | 277 | error_f("HELLO received twice"); |
e1537f95 | 278 | return -1; |
b1cbfa25 | 279 | } |
f4608a70 | 280 | if ((r = sshbuf_get_u32(m, &ver)) != 0) { |
816036f1 | 281 | error_fr(r, "parse"); |
e1537f95 | 282 | return -1; |
b1cbfa25 | 283 | } |
e1537f95 | 284 | if (ver != SSHMUX_VER) { |
816036f1 | 285 | error_f("unsupported multiplexing protocol version %u " |
286 | "(expected %u)", ver, SSHMUX_VER); | |
e1537f95 DM |
287 | return -1; |
288 | } | |
816036f1 | 289 | debug2_f("channel %d client version %u", c->self, ver); |
b1cbfa25 | 290 | |
e1537f95 | 291 | /* No extensions are presently defined */ |
f4608a70 | 292 | while (sshbuf_len(m) > 0) { |
293 | char *name = NULL; | |
9d883a1c | 294 | size_t value_len = 0; |
b1cbfa25 | 295 | |
f4608a70 | 296 | if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0 || |
9d883a1c | 297 | (r = sshbuf_get_string_direct(m, NULL, &value_len)) != 0) { |
816036f1 | 298 | error_fr(r, "parse extension"); |
f4608a70 | 299 | return -1; |
b1cbfa25 | 300 | } |
816036f1 | 301 | debug2_f("Unrecognised extension \"%s\" length %zu", |
302 | name, value_len); | |
a627d42e | 303 | free(name); |
b1cbfa25 | 304 | } |
e1537f95 DM |
305 | state->hello_rcvd = 1; |
306 | return 0; | |
307 | } | |
308 | ||
f4608a70 | 309 | /* Enqueue a "ok" response to the reply buffer */ |
310 | static void | |
311 | reply_ok(struct sshbuf *reply, u_int rid) | |
312 | { | |
313 | int r; | |
314 | ||
315 | if ((r = sshbuf_put_u32(reply, MUX_S_OK)) != 0 || | |
316 | (r = sshbuf_put_u32(reply, rid)) != 0) | |
816036f1 | 317 | fatal_fr(r, "reply"); |
f4608a70 | 318 | } |
319 | ||
320 | /* Enqueue an error response to the reply buffer */ | |
321 | static void | |
322 | reply_error(struct sshbuf *reply, u_int type, u_int rid, const char *msg) | |
323 | { | |
324 | int r; | |
325 | ||
326 | if ((r = sshbuf_put_u32(reply, type)) != 0 || | |
327 | (r = sshbuf_put_u32(reply, rid)) != 0 || | |
328 | (r = sshbuf_put_cstring(reply, msg)) != 0) | |
816036f1 | 329 | fatal_fr(r, "reply"); |
f4608a70 | 330 | } |
331 | ||
e1537f95 | 332 | static int |
9d883a1c | 333 | mux_master_process_new_session(struct ssh *ssh, u_int rid, |
f4608a70 | 334 | Channel *c, struct sshbuf *m, struct sshbuf *reply) |
e1537f95 DM |
335 | { |
336 | Channel *nc; | |
337 | struct mux_session_confirm_ctx *cctx; | |
f4608a70 | 338 | char *cmd, *cp; |
339 | u_int i, j, env_len, escape_char, window, packetmax; | |
340 | int r, new_fd[3]; | |
b1cbfa25 DM |
341 | |
342 | /* Reply for SSHMUX_COMMAND_OPEN */ | |
e1537f95 DM |
343 | cctx = xcalloc(1, sizeof(*cctx)); |
344 | cctx->term = NULL; | |
d530f5f4 | 345 | cctx->rid = rid; |
f4608a70 | 346 | cmd = NULL; |
ab523b02 DM |
347 | cctx->env = NULL; |
348 | env_len = 0; | |
f4608a70 | 349 | if ((r = sshbuf_skip_string(m)) != 0 || /* reserved */ |
350 | (r = sshbuf_get_u32(m, &cctx->want_tty)) != 0 || | |
351 | (r = sshbuf_get_u32(m, &cctx->want_x_fwd)) != 0 || | |
352 | (r = sshbuf_get_u32(m, &cctx->want_agent_fwd)) != 0 || | |
353 | (r = sshbuf_get_u32(m, &cctx->want_subsys)) != 0 || | |
354 | (r = sshbuf_get_u32(m, &escape_char)) != 0 || | |
355 | (r = sshbuf_get_cstring(m, &cctx->term, NULL)) != 0 || | |
356 | (r = sshbuf_get_cstring(m, &cmd, NULL)) != 0) { | |
e1537f95 | 357 | malf: |
a627d42e | 358 | free(cmd); |
ab523b02 | 359 | for (j = 0; j < env_len; j++) |
a627d42e DT |
360 | free(cctx->env[j]); |
361 | free(cctx->env); | |
362 | free(cctx->term); | |
363 | free(cctx); | |
816036f1 | 364 | error_f("malformed message"); |
e1537f95 | 365 | return -1; |
b1cbfa25 | 366 | } |
e1537f95 | 367 | |
e1537f95 | 368 | #define MUX_MAX_ENV_VARS 4096 |
f4608a70 | 369 | while (sshbuf_len(m) > 0) { |
370 | if ((r = sshbuf_get_cstring(m, &cp, NULL)) != 0) | |
e1537f95 | 371 | goto malf; |
e1537f95 | 372 | if (!env_permitted(cp)) { |
a627d42e | 373 | free(cp); |
e1537f95 DM |
374 | continue; |
375 | } | |
657a5fbc | 376 | cctx->env = xreallocarray(cctx->env, env_len + 2, |
e1537f95 DM |
377 | sizeof(*cctx->env)); |
378 | cctx->env[env_len++] = cp; | |
379 | cctx->env[env_len] = NULL; | |
380 | if (env_len > MUX_MAX_ENV_VARS) { | |
816036f1 | 381 | error_f(">%d environment variables received, " |
382 | "ignoring additional", MUX_MAX_ENV_VARS); | |
e1537f95 DM |
383 | break; |
384 | } | |
b1cbfa25 DM |
385 | } |
386 | ||
816036f1 | 387 | debug2_f("channel %d: request tty %d, X %d, agent %d, subsys %d, " |
388 | "term \"%s\", cmd \"%s\", env %u", c->self, | |
e1537f95 DM |
389 | cctx->want_tty, cctx->want_x_fwd, cctx->want_agent_fwd, |
390 | cctx->want_subsys, cctx->term, cmd, env_len); | |
b1cbfa25 | 391 | |
f4608a70 | 392 | if ((cctx->cmd = sshbuf_new()) == NULL) |
816036f1 | 393 | fatal_f("sshbuf_new"); |
f4608a70 | 394 | if ((r = sshbuf_put(cctx->cmd, cmd, strlen(cmd))) != 0) |
816036f1 | 395 | fatal_fr(r, "sshbuf_put"); |
a627d42e | 396 | free(cmd); |
e1537f95 | 397 | cmd = NULL; |
b1cbfa25 DM |
398 | |
399 | /* Gather fds from client */ | |
400 | for(i = 0; i < 3; i++) { | |
e1537f95 | 401 | if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { |
816036f1 | 402 | error_f("failed to receive fd %d from client", i); |
b1cbfa25 DM |
403 | for (j = 0; j < i; j++) |
404 | close(new_fd[j]); | |
405 | for (j = 0; j < env_len; j++) | |
a627d42e DT |
406 | free(cctx->env[j]); |
407 | free(cctx->env); | |
408 | free(cctx->term); | |
f4608a70 | 409 | sshbuf_free(cctx->cmd); |
a627d42e | 410 | free(cctx); |
f4608a70 | 411 | reply_error(reply, MUX_S_FAILURE, rid, |
e1537f95 DM |
412 | "did not receive file descriptors"); |
413 | return -1; | |
b1cbfa25 DM |
414 | } |
415 | } | |
416 | ||
816036f1 | 417 | debug3_f("got fds stdin %d, stdout %d, stderr %d", |
b1cbfa25 DM |
418 | new_fd[0], new_fd[1], new_fd[2]); |
419 | ||
e1537f95 | 420 | /* XXX support multiple child sessions in future */ |
9f53229c | 421 | if (c->have_remote_id) { |
816036f1 | 422 | debug2_f("session already open"); |
f4608a70 | 423 | reply_error(reply, MUX_S_FAILURE, rid, |
424 | "Multiple sessions not supported"); | |
e1537f95 | 425 | cleanup: |
b1cbfa25 DM |
426 | close(new_fd[0]); |
427 | close(new_fd[1]); | |
428 | close(new_fd[2]); | |
a627d42e | 429 | free(cctx->term); |
b1cbfa25 DM |
430 | if (env_len != 0) { |
431 | for (i = 0; i < env_len; i++) | |
a627d42e DT |
432 | free(cctx->env[i]); |
433 | free(cctx->env); | |
b1cbfa25 | 434 | } |
f4608a70 | 435 | sshbuf_free(cctx->cmd); |
a627d42e | 436 | free(cctx); |
b1cbfa25 DM |
437 | return 0; |
438 | } | |
e1537f95 DM |
439 | |
440 | if (options.control_master == SSHCTL_MASTER_ASK || | |
441 | options.control_master == SSHCTL_MASTER_AUTO_ASK) { | |
442 | if (!ask_permission("Allow shared connection to %s? ", host)) { | |
816036f1 | 443 | debug2_f("session refused by user"); |
f4608a70 | 444 | reply_error(reply, MUX_S_PERMISSION_DENIED, rid, |
445 | "Permission denied"); | |
e1537f95 DM |
446 | goto cleanup; |
447 | } | |
448 | } | |
449 | ||
450 | /* Try to pick up ttymodes from client before it goes raw */ | |
451 | if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) | |
816036f1 | 452 | error_f("tcgetattr: %s", strerror(errno)); |
b1cbfa25 | 453 | |
b1cbfa25 DM |
454 | window = CHAN_SES_WINDOW_DEFAULT; |
455 | packetmax = CHAN_SES_PACKET_DEFAULT; | |
456 | if (cctx->want_tty) { | |
457 | window >>= 1; | |
458 | packetmax >>= 1; | |
459 | } | |
e1537f95 | 460 | |
dbee4119 | 461 | nc = channel_new(ssh, "session", SSH_CHANNEL_OPENING, |
b1cbfa25 | 462 | new_fd[0], new_fd[1], new_fd[2], window, packetmax, |
7be4ac81 | 463 | CHAN_EXTENDED_WRITE, "client-session", CHANNEL_NONBLOCK_STDIO); |
b1cbfa25 | 464 | |
e1537f95 | 465 | nc->ctl_chan = c->self; /* link session -> control channel */ |
1a14c131 | 466 | c->remote_id = nc->self; /* link control -> session channel */ |
9f53229c | 467 | c->have_remote_id = 1; |
e1537f95 | 468 | |
2fb66cac | 469 | if (cctx->want_tty && escape_char != 0xffffffff) { |
dbee4119 | 470 | channel_register_filter(ssh, nc->self, |
2fb66cac | 471 | client_simple_escape_filter, NULL, |
84c56f53 | 472 | client_filter_cleanup, |
2fb66cac DT |
473 | client_new_escape_filter_ctx((int)escape_char)); |
474 | } | |
b1cbfa25 | 475 | |
816036f1 | 476 | debug2_f("channel_new: %d linked to control channel %d", |
477 | nc->self, nc->ctl_chan); | |
b1cbfa25 | 478 | |
dbee4119 | 479 | channel_send_open(ssh, nc->self); |
480 | channel_register_open_confirm(ssh, nc->self, mux_session_confirm, cctx); | |
d530f5f4 | 481 | c->mux_pause = 1; /* stop handling messages until open_confirm done */ |
dbee4119 | 482 | channel_register_cleanup(ssh, nc->self, |
483 | mux_master_session_cleanup_cb, 1); | |
b1cbfa25 | 484 | |
d530f5f4 | 485 | /* reply is deferred, sent by mux_session_confirm */ |
e1537f95 | 486 | return 0; |
b1cbfa25 DM |
487 | } |
488 | ||
e1537f95 | 489 | static int |
9d883a1c | 490 | mux_master_process_alive_check(struct ssh *ssh, u_int rid, |
f4608a70 | 491 | Channel *c, struct sshbuf *m, struct sshbuf *reply) |
b1cbfa25 | 492 | { |
f4608a70 | 493 | int r; |
494 | ||
816036f1 | 495 | debug2_f("channel %d: alive check", c->self); |
b1cbfa25 | 496 | |
e1537f95 | 497 | /* prepare reply */ |
f4608a70 | 498 | if ((r = sshbuf_put_u32(reply, MUX_S_ALIVE)) != 0 || |
499 | (r = sshbuf_put_u32(reply, rid)) != 0 || | |
500 | (r = sshbuf_put_u32(reply, (u_int)getpid())) != 0) | |
816036f1 | 501 | fatal_fr(r, "reply"); |
b1cbfa25 | 502 | |
e1537f95 | 503 | return 0; |
b1cbfa25 DM |
504 | } |
505 | ||
b1cbfa25 | 506 | static int |
9d883a1c | 507 | mux_master_process_terminate(struct ssh *ssh, u_int rid, |
f4608a70 | 508 | Channel *c, struct sshbuf *m, struct sshbuf *reply) |
b1cbfa25 | 509 | { |
816036f1 | 510 | debug2_f("channel %d: terminate request", c->self); |
e1537f95 DM |
511 | |
512 | if (options.control_master == SSHCTL_MASTER_ASK || | |
513 | options.control_master == SSHCTL_MASTER_AUTO_ASK) { | |
514 | if (!ask_permission("Terminate shared connection to %s? ", | |
515 | host)) { | |
816036f1 | 516 | debug2_f("termination refused by user"); |
f4608a70 | 517 | reply_error(reply, MUX_S_PERMISSION_DENIED, rid, |
518 | "Permission denied"); | |
e1537f95 DM |
519 | return 0; |
520 | } | |
521 | } | |
b1cbfa25 | 522 | |
e1537f95 | 523 | quit_pending = 1; |
f4608a70 | 524 | reply_ok(reply, rid); |
e1537f95 DM |
525 | /* XXX exit happens too soon - message never makes it to client */ |
526 | return 0; | |
b1cbfa25 DM |
527 | } |
528 | ||
e1537f95 | 529 | static char * |
7acefbbc | 530 | format_forward(u_int ftype, struct Forward *fwd) |
b1cbfa25 | 531 | { |
e1537f95 DM |
532 | char *ret; |
533 | ||
534 | switch (ftype) { | |
535 | case MUX_FWD_LOCAL: | |
536 | xasprintf(&ret, "local forward %.200s:%d -> %.200s:%d", | |
7acefbbc | 537 | (fwd->listen_path != NULL) ? fwd->listen_path : |
e1537f95 | 538 | (fwd->listen_host == NULL) ? |
7acefbbc | 539 | (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : |
e1537f95 | 540 | fwd->listen_host, fwd->listen_port, |
7acefbbc | 541 | (fwd->connect_path != NULL) ? fwd->connect_path : |
e1537f95 DM |
542 | fwd->connect_host, fwd->connect_port); |
543 | break; | |
544 | case MUX_FWD_DYNAMIC: | |
545 | xasprintf(&ret, "dynamic forward %.200s:%d -> *", | |
546 | (fwd->listen_host == NULL) ? | |
7acefbbc | 547 | (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : |
31d8d231 | 548 | fwd->listen_host, fwd->listen_port); |
e1537f95 DM |
549 | break; |
550 | case MUX_FWD_REMOTE: | |
551 | xasprintf(&ret, "remote forward %.200s:%d -> %.200s:%d", | |
7acefbbc | 552 | (fwd->listen_path != NULL) ? fwd->listen_path : |
e1537f95 DM |
553 | (fwd->listen_host == NULL) ? |
554 | "LOCALHOST" : fwd->listen_host, | |
555 | fwd->listen_port, | |
7acefbbc | 556 | (fwd->connect_path != NULL) ? fwd->connect_path : |
e1537f95 | 557 | fwd->connect_host, fwd->connect_port); |
b1cbfa25 DM |
558 | break; |
559 | default: | |
816036f1 | 560 | fatal_f("unknown forward type %u", ftype); |
b1cbfa25 | 561 | } |
e1537f95 DM |
562 | return ret; |
563 | } | |
b1cbfa25 | 564 | |
e1537f95 DM |
565 | static int |
566 | compare_host(const char *a, const char *b) | |
567 | { | |
568 | if (a == NULL && b == NULL) | |
569 | return 1; | |
570 | if (a == NULL || b == NULL) | |
571 | return 0; | |
572 | return strcmp(a, b) == 0; | |
573 | } | |
b1cbfa25 | 574 | |
e1537f95 | 575 | static int |
7acefbbc | 576 | compare_forward(struct Forward *a, struct Forward *b) |
e1537f95 DM |
577 | { |
578 | if (!compare_host(a->listen_host, b->listen_host)) | |
579 | return 0; | |
7acefbbc DM |
580 | if (!compare_host(a->listen_path, b->listen_path)) |
581 | return 0; | |
e1537f95 DM |
582 | if (a->listen_port != b->listen_port) |
583 | return 0; | |
584 | if (!compare_host(a->connect_host, b->connect_host)) | |
585 | return 0; | |
7acefbbc DM |
586 | if (!compare_host(a->connect_path, b->connect_path)) |
587 | return 0; | |
e1537f95 DM |
588 | if (a->connect_port != b->connect_port) |
589 | return 0; | |
b1cbfa25 | 590 | |
e1537f95 DM |
591 | return 1; |
592 | } | |
b1cbfa25 | 593 | |
388f6fc4 | 594 | static void |
dbee4119 | 595 | mux_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) |
388f6fc4 DM |
596 | { |
597 | struct mux_channel_confirm_ctx *fctx = ctxt; | |
598 | char *failmsg = NULL; | |
7acefbbc | 599 | struct Forward *rfwd; |
388f6fc4 | 600 | Channel *c; |
f4608a70 | 601 | struct sshbuf *out; |
e3128b38 | 602 | u_int port; |
f4608a70 | 603 | int r; |
388f6fc4 | 604 | |
dbee4119 | 605 | if ((c = channel_by_id(ssh, fctx->cid)) == NULL) { |
388f6fc4 | 606 | /* no channel for reply */ |
816036f1 | 607 | error_f("unknown channel"); |
388f6fc4 DM |
608 | return; |
609 | } | |
f4608a70 | 610 | if ((out = sshbuf_new()) == NULL) |
816036f1 | 611 | fatal_f("sshbuf_new"); |
ca430d4d | 612 | if (fctx->fid >= options.num_remote_forwards || |
613 | (options.remote_forwards[fctx->fid].connect_path == NULL && | |
614 | options.remote_forwards[fctx->fid].connect_host == NULL)) { | |
388f6fc4 DM |
615 | xasprintf(&failmsg, "unknown forwarding id %d", fctx->fid); |
616 | goto fail; | |
617 | } | |
618 | rfwd = &options.remote_forwards[fctx->fid]; | |
816036f1 | 619 | debug_f("%s for: listen %d, connect %s:%d", |
388f6fc4 | 620 | type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", |
7acefbbc DM |
621 | rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : |
622 | rfwd->connect_host, rfwd->connect_port); | |
388f6fc4 DM |
623 | if (type == SSH2_MSG_REQUEST_SUCCESS) { |
624 | if (rfwd->listen_port == 0) { | |
e3128b38 | 625 | if ((r = sshpkt_get_u32(ssh, &port)) != 0) |
816036f1 | 626 | fatal_fr(r, "parse port"); |
e3128b38 | 627 | if (port > 65535) { |
628 | fatal("Invalid allocated port %u for " | |
629 | "mux remote forward to %s:%d", port, | |
630 | rfwd->connect_host, rfwd->connect_port); | |
631 | } | |
632 | rfwd->allocated_port = (int)port; | |
8312cfb8 | 633 | debug("Allocated port %u for mux remote forward" |
388f6fc4 DM |
634 | " to %s:%d", rfwd->allocated_port, |
635 | rfwd->connect_host, rfwd->connect_port); | |
f4608a70 | 636 | if ((r = sshbuf_put_u32(out, |
637 | MUX_S_REMOTE_PORT)) != 0 || | |
638 | (r = sshbuf_put_u32(out, fctx->rid)) != 0 || | |
639 | (r = sshbuf_put_u32(out, | |
640 | rfwd->allocated_port)) != 0) | |
816036f1 | 641 | fatal_fr(r, "reply"); |
115063a6 | 642 | channel_update_permission(ssh, rfwd->handle, |
31d8d231 | 643 | rfwd->allocated_port); |
388f6fc4 | 644 | } else { |
f4608a70 | 645 | reply_ok(out, fctx->rid); |
388f6fc4 DM |
646 | } |
647 | goto out; | |
648 | } else { | |
68afb8c5 | 649 | if (rfwd->listen_port == 0) |
115063a6 | 650 | channel_update_permission(ssh, rfwd->handle, -1); |
7acefbbc DM |
651 | if (rfwd->listen_path != NULL) |
652 | xasprintf(&failmsg, "remote port forwarding failed for " | |
653 | "listen path %s", rfwd->listen_path); | |
654 | else | |
655 | xasprintf(&failmsg, "remote port forwarding failed for " | |
656 | "listen port %d", rfwd->listen_port); | |
ca430d4d | 657 | |
31d8d231 | 658 | debug2_f("clearing registered forwarding for listen %d, " |
816036f1 | 659 | "connect %s:%d", rfwd->listen_port, |
ca430d4d | 660 | rfwd->connect_path ? rfwd->connect_path : |
661 | rfwd->connect_host, rfwd->connect_port); | |
662 | ||
663 | free(rfwd->listen_host); | |
664 | free(rfwd->listen_path); | |
665 | free(rfwd->connect_host); | |
666 | free(rfwd->connect_path); | |
667 | memset(rfwd, 0, sizeof(*rfwd)); | |
388f6fc4 DM |
668 | } |
669 | fail: | |
816036f1 | 670 | error_f("%s", failmsg); |
f4608a70 | 671 | reply_error(out, MUX_S_FAILURE, fctx->rid, failmsg); |
a627d42e | 672 | free(failmsg); |
388f6fc4 | 673 | out: |
f4608a70 | 674 | if ((r = sshbuf_put_stringb(c->output, out)) != 0) |
816036f1 | 675 | fatal_fr(r, "enqueue"); |
f4608a70 | 676 | sshbuf_free(out); |
388f6fc4 | 677 | if (c->mux_pause <= 0) |
816036f1 | 678 | fatal_f("mux_pause %d", c->mux_pause); |
388f6fc4 DM |
679 | c->mux_pause = 0; /* start processing messages again */ |
680 | } | |
681 | ||
e1537f95 | 682 | static int |
9d883a1c | 683 | mux_master_process_open_fwd(struct ssh *ssh, u_int rid, |
f4608a70 | 684 | Channel *c, struct sshbuf *m, struct sshbuf *reply) |
e1537f95 | 685 | { |
7acefbbc | 686 | struct Forward fwd; |
e1537f95 | 687 | char *fwd_desc = NULL; |
7acefbbc | 688 | char *listen_addr, *connect_addr; |
e1537f95 | 689 | u_int ftype; |
ce986546 | 690 | u_int lport, cport; |
f4608a70 | 691 | int r, i, ret = 0, freefwd = 1; |
e1537f95 | 692 | |
45b0eb75 | 693 | memset(&fwd, 0, sizeof(fwd)); |
694 | ||
7acefbbc | 695 | /* XXX - lport/cport check redundant */ |
f4608a70 | 696 | if ((r = sshbuf_get_u32(m, &ftype)) != 0 || |
697 | (r = sshbuf_get_cstring(m, &listen_addr, NULL)) != 0 || | |
698 | (r = sshbuf_get_u32(m, &lport)) != 0 || | |
699 | (r = sshbuf_get_cstring(m, &connect_addr, NULL)) != 0 || | |
700 | (r = sshbuf_get_u32(m, &cport)) != 0 || | |
7acefbbc DM |
701 | (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || |
702 | (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { | |
816036f1 | 703 | error_f("malformed message"); |
e1537f95 DM |
704 | ret = -1; |
705 | goto out; | |
706 | } | |
7acefbbc DM |
707 | if (*listen_addr == '\0') { |
708 | free(listen_addr); | |
709 | listen_addr = NULL; | |
e1537f95 | 710 | } |
7acefbbc DM |
711 | if (*connect_addr == '\0') { |
712 | free(connect_addr); | |
713 | connect_addr = NULL; | |
e1537f95 DM |
714 | } |
715 | ||
7acefbbc DM |
716 | memset(&fwd, 0, sizeof(fwd)); |
717 | fwd.listen_port = lport; | |
718 | if (fwd.listen_port == PORT_STREAMLOCAL) | |
719 | fwd.listen_path = listen_addr; | |
720 | else | |
721 | fwd.listen_host = listen_addr; | |
722 | fwd.connect_port = cport; | |
723 | if (fwd.connect_port == PORT_STREAMLOCAL) | |
724 | fwd.connect_path = connect_addr; | |
725 | else | |
726 | fwd.connect_host = connect_addr; | |
727 | ||
816036f1 | 728 | debug2_f("channel %d: request %s", c->self, |
e1537f95 DM |
729 | (fwd_desc = format_forward(ftype, &fwd))); |
730 | ||
731 | if (ftype != MUX_FWD_LOCAL && ftype != MUX_FWD_REMOTE && | |
732 | ftype != MUX_FWD_DYNAMIC) { | |
816036f1 | 733 | logit_f("invalid forwarding type %u", ftype); |
e1537f95 | 734 | invalid: |
7acefbbc DM |
735 | free(listen_addr); |
736 | free(connect_addr); | |
f4608a70 | 737 | reply_error(reply, MUX_S_FAILURE, rid, |
738 | "Invalid forwarding request"); | |
e1537f95 DM |
739 | return 0; |
740 | } | |
7acefbbc | 741 | if (ftype == MUX_FWD_DYNAMIC && fwd.listen_path) { |
816036f1 | 742 | logit_f("streamlocal and dynamic forwards " |
743 | "are mutually exclusive"); | |
7acefbbc DM |
744 | goto invalid; |
745 | } | |
746 | if (fwd.listen_port != PORT_STREAMLOCAL && fwd.listen_port >= 65536) { | |
816036f1 | 747 | logit_f("invalid listen port %u", fwd.listen_port); |
e1537f95 DM |
748 | goto invalid; |
749 | } | |
dbee4119 | 750 | if ((fwd.connect_port != PORT_STREAMLOCAL && |
751 | fwd.connect_port >= 65536) || | |
752 | (ftype != MUX_FWD_DYNAMIC && ftype != MUX_FWD_REMOTE && | |
753 | fwd.connect_port == 0)) { | |
816036f1 | 754 | logit_f("invalid connect port %u", |
e1537f95 DM |
755 | fwd.connect_port); |
756 | goto invalid; | |
757 | } | |
dbee4119 | 758 | if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL && |
759 | fwd.connect_path == NULL) { | |
816036f1 | 760 | logit_f("missing connect host"); |
e1537f95 DM |
761 | goto invalid; |
762 | } | |
763 | ||
764 | /* Skip forwards that have already been requested */ | |
765 | switch (ftype) { | |
766 | case MUX_FWD_LOCAL: | |
767 | case MUX_FWD_DYNAMIC: | |
768 | for (i = 0; i < options.num_local_forwards; i++) { | |
769 | if (compare_forward(&fwd, | |
770 | options.local_forwards + i)) { | |
771 | exists: | |
816036f1 | 772 | debug2_f("found existing forwarding"); |
f4608a70 | 773 | reply_ok(reply, rid); |
e1537f95 DM |
774 | goto out; |
775 | } | |
b1cbfa25 | 776 | } |
e1537f95 DM |
777 | break; |
778 | case MUX_FWD_REMOTE: | |
779 | for (i = 0; i < options.num_remote_forwards; i++) { | |
f4608a70 | 780 | if (!compare_forward(&fwd, options.remote_forwards + i)) |
781 | continue; | |
782 | if (fwd.listen_port != 0) | |
783 | goto exists; | |
816036f1 | 784 | debug2_f("found allocated port"); |
f4608a70 | 785 | if ((r = sshbuf_put_u32(reply, |
786 | MUX_S_REMOTE_PORT)) != 0 || | |
787 | (r = sshbuf_put_u32(reply, rid)) != 0 || | |
788 | (r = sshbuf_put_u32(reply, | |
789 | options.remote_forwards[i].allocated_port)) != 0) | |
816036f1 | 790 | fatal_fr(r, "reply FWD_REMOTE"); |
f4608a70 | 791 | goto out; |
b1cbfa25 | 792 | } |
e1537f95 | 793 | break; |
b1cbfa25 DM |
794 | } |
795 | ||
e1537f95 DM |
796 | if (options.control_master == SSHCTL_MASTER_ASK || |
797 | options.control_master == SSHCTL_MASTER_AUTO_ASK) { | |
798 | if (!ask_permission("Open %s on %s?", fwd_desc, host)) { | |
816036f1 | 799 | debug2_f("forwarding refused by user"); |
f4608a70 | 800 | reply_error(reply, MUX_S_PERMISSION_DENIED, rid, |
801 | "Permission denied"); | |
e1537f95 DM |
802 | goto out; |
803 | } | |
b1cbfa25 DM |
804 | } |
805 | ||
e1537f95 | 806 | if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) { |
dbee4119 | 807 | if (!channel_setup_local_fwd_listener(ssh, &fwd, |
7acefbbc | 808 | &options.fwd_opts)) { |
e1537f95 | 809 | fail: |
816036f1 | 810 | logit_f("requested %s failed", fwd_desc); |
f4608a70 | 811 | reply_error(reply, MUX_S_FAILURE, rid, |
812 | "Port forwarding failed"); | |
e1537f95 DM |
813 | goto out; |
814 | } | |
815 | add_local_forward(&options, &fwd); | |
816 | freefwd = 0; | |
817 | } else { | |
388f6fc4 DM |
818 | struct mux_channel_confirm_ctx *fctx; |
819 | ||
dbee4119 | 820 | fwd.handle = channel_request_remote_forwarding(ssh, &fwd); |
68afb8c5 | 821 | if (fwd.handle < 0) |
e1537f95 DM |
822 | goto fail; |
823 | add_remote_forward(&options, &fwd); | |
388f6fc4 DM |
824 | fctx = xcalloc(1, sizeof(*fctx)); |
825 | fctx->cid = c->self; | |
826 | fctx->rid = rid; | |
232cfb1b | 827 | fctx->fid = options.num_remote_forwards - 1; |
388f6fc4 DM |
828 | client_register_global_confirm(mux_confirm_remote_forward, |
829 | fctx); | |
e1537f95 | 830 | freefwd = 0; |
388f6fc4 DM |
831 | c->mux_pause = 1; /* wait for mux_confirm_remote_forward */ |
832 | /* delayed reply in mux_confirm_remote_forward */ | |
833 | goto out; | |
e1537f95 | 834 | } |
f4608a70 | 835 | reply_ok(reply, rid); |
e1537f95 | 836 | out: |
a627d42e | 837 | free(fwd_desc); |
e1537f95 | 838 | if (freefwd) { |
a627d42e | 839 | free(fwd.listen_host); |
7acefbbc | 840 | free(fwd.listen_path); |
a627d42e | 841 | free(fwd.connect_host); |
7acefbbc | 842 | free(fwd.connect_path); |
e1537f95 DM |
843 | } |
844 | return ret; | |
845 | } | |
b1cbfa25 | 846 | |
e1537f95 | 847 | static int |
9d883a1c | 848 | mux_master_process_close_fwd(struct ssh *ssh, u_int rid, |
f4608a70 | 849 | Channel *c, struct sshbuf *m, struct sshbuf *reply) |
e1537f95 | 850 | { |
7acefbbc | 851 | struct Forward fwd, *found_fwd; |
e1537f95 | 852 | char *fwd_desc = NULL; |
f6dff7cd | 853 | const char *error_reason = NULL; |
7acefbbc | 854 | char *listen_addr = NULL, *connect_addr = NULL; |
e1537f95 | 855 | u_int ftype; |
f4608a70 | 856 | int r, i, ret = 0; |
ce986546 | 857 | u_int lport, cport; |
e1537f95 | 858 | |
45b0eb75 | 859 | memset(&fwd, 0, sizeof(fwd)); |
860 | ||
f4608a70 | 861 | if ((r = sshbuf_get_u32(m, &ftype)) != 0 || |
862 | (r = sshbuf_get_cstring(m, &listen_addr, NULL)) != 0 || | |
863 | (r = sshbuf_get_u32(m, &lport)) != 0 || | |
864 | (r = sshbuf_get_cstring(m, &connect_addr, NULL)) != 0 || | |
865 | (r = sshbuf_get_u32(m, &cport)) != 0 || | |
7acefbbc DM |
866 | (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || |
867 | (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { | |
816036f1 | 868 | error_f("malformed message"); |
e1537f95 DM |
869 | ret = -1; |
870 | goto out; | |
871 | } | |
b1cbfa25 | 872 | |
7acefbbc DM |
873 | if (*listen_addr == '\0') { |
874 | free(listen_addr); | |
875 | listen_addr = NULL; | |
e1537f95 | 876 | } |
7acefbbc DM |
877 | if (*connect_addr == '\0') { |
878 | free(connect_addr); | |
879 | connect_addr = NULL; | |
e1537f95 DM |
880 | } |
881 | ||
7acefbbc DM |
882 | memset(&fwd, 0, sizeof(fwd)); |
883 | fwd.listen_port = lport; | |
884 | if (fwd.listen_port == PORT_STREAMLOCAL) | |
885 | fwd.listen_path = listen_addr; | |
886 | else | |
887 | fwd.listen_host = listen_addr; | |
888 | fwd.connect_port = cport; | |
889 | if (fwd.connect_port == PORT_STREAMLOCAL) | |
890 | fwd.connect_path = connect_addr; | |
891 | else | |
892 | fwd.connect_host = connect_addr; | |
893 | ||
816036f1 | 894 | debug2_f("channel %d: request cancel %s", c->self, |
e1537f95 DM |
895 | (fwd_desc = format_forward(ftype, &fwd))); |
896 | ||
f6dff7cd DM |
897 | /* make sure this has been requested */ |
898 | found_fwd = NULL; | |
899 | switch (ftype) { | |
900 | case MUX_FWD_LOCAL: | |
901 | case MUX_FWD_DYNAMIC: | |
902 | for (i = 0; i < options.num_local_forwards; i++) { | |
903 | if (compare_forward(&fwd, | |
904 | options.local_forwards + i)) { | |
905 | found_fwd = options.local_forwards + i; | |
906 | break; | |
907 | } | |
908 | } | |
909 | break; | |
910 | case MUX_FWD_REMOTE: | |
911 | for (i = 0; i < options.num_remote_forwards; i++) { | |
912 | if (compare_forward(&fwd, | |
913 | options.remote_forwards + i)) { | |
914 | found_fwd = options.remote_forwards + i; | |
915 | break; | |
916 | } | |
917 | } | |
918 | break; | |
919 | } | |
e1537f95 | 920 | |
f6dff7cd DM |
921 | if (found_fwd == NULL) |
922 | error_reason = "port not forwarded"; | |
923 | else if (ftype == MUX_FWD_REMOTE) { | |
924 | /* | |
925 | * This shouldn't fail unless we confused the host/port | |
926 | * between options.remote_forwards and permitted_opens. | |
68afb8c5 | 927 | * However, for dynamic allocated listen ports we need |
7acefbbc | 928 | * to use the actual listen port. |
f6dff7cd | 929 | */ |
dbee4119 | 930 | if (channel_request_rforward_cancel(ssh, found_fwd) == -1) |
f6dff7cd DM |
931 | error_reason = "port not in permitted opens"; |
932 | } else { /* local and dynamic forwards */ | |
933 | /* Ditto */ | |
dbee4119 | 934 | if (channel_cancel_lport_listener(ssh, &fwd, fwd.connect_port, |
7acefbbc | 935 | &options.fwd_opts) == -1) |
f6dff7cd DM |
936 | error_reason = "port not found"; |
937 | } | |
938 | ||
f4608a70 | 939 | if (error_reason != NULL) |
940 | reply_error(reply, MUX_S_FAILURE, rid, error_reason); | |
941 | else { | |
942 | reply_ok(reply, rid); | |
a627d42e | 943 | free(found_fwd->listen_host); |
7acefbbc | 944 | free(found_fwd->listen_path); |
a627d42e | 945 | free(found_fwd->connect_host); |
7acefbbc | 946 | free(found_fwd->connect_path); |
f6dff7cd | 947 | found_fwd->listen_host = found_fwd->connect_host = NULL; |
7acefbbc | 948 | found_fwd->listen_path = found_fwd->connect_path = NULL; |
f6dff7cd | 949 | found_fwd->listen_port = found_fwd->connect_port = 0; |
f6dff7cd | 950 | } |
e1537f95 | 951 | out: |
a627d42e | 952 | free(fwd_desc); |
7acefbbc DM |
953 | free(listen_addr); |
954 | free(connect_addr); | |
e1537f95 DM |
955 | |
956 | return ret; | |
957 | } | |
958 | ||
959 | static int | |
9d883a1c | 960 | mux_master_process_stdio_fwd(struct ssh *ssh, u_int rid, |
f4608a70 | 961 | Channel *c, struct sshbuf *m, struct sshbuf *reply) |
e1537f95 DM |
962 | { |
963 | Channel *nc; | |
f4608a70 | 964 | char *chost = NULL; |
8d33f2aa | 965 | u_int _cport, i, j; |
966 | int ok = 0, cport, r, new_fd[2]; | |
357610d1 | 967 | struct mux_stdio_confirm_ctx *cctx; |
e1537f95 | 968 | |
f4608a70 | 969 | if ((r = sshbuf_skip_string(m)) != 0 || /* reserved */ |
970 | (r = sshbuf_get_cstring(m, &chost, NULL)) != 0 || | |
8d33f2aa | 971 | (r = sshbuf_get_u32(m, &_cport)) != 0) { |
a627d42e | 972 | free(chost); |
816036f1 | 973 | error_f("malformed message"); |
e1537f95 DM |
974 | return -1; |
975 | } | |
8d33f2aa | 976 | if (_cport == (u_int)PORT_STREAMLOCAL) |
977 | cport = PORT_STREAMLOCAL; | |
978 | else if (_cport <= INT_MAX) | |
979 | cport = (int)_cport; | |
980 | else { | |
981 | free(chost); | |
982 | error_f("invalid port 0x%x", _cport); | |
983 | return -1; | |
984 | } | |
e1537f95 | 985 | |
8d33f2aa | 986 | debug2_f("channel %d: stdio fwd to %s:%d", c->self, chost, cport); |
e1537f95 DM |
987 | |
988 | /* Gather fds from client */ | |
989 | for(i = 0; i < 2; i++) { | |
990 | if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { | |
816036f1 | 991 | error_f("failed to receive fd %d from client", i); |
e1537f95 DM |
992 | for (j = 0; j < i; j++) |
993 | close(new_fd[j]); | |
a627d42e | 994 | free(chost); |
e1537f95 DM |
995 | |
996 | /* prepare reply */ | |
f4608a70 | 997 | reply_error(reply, MUX_S_FAILURE, rid, |
e1537f95 DM |
998 | "did not receive file descriptors"); |
999 | return -1; | |
1000 | } | |
1001 | } | |
b1cbfa25 | 1002 | |
816036f1 | 1003 | debug3_f("got fds stdin %d, stdout %d", new_fd[0], new_fd[1]); |
e1537f95 DM |
1004 | |
1005 | /* XXX support multiple child sessions in future */ | |
9f53229c | 1006 | if (c->have_remote_id) { |
816036f1 | 1007 | debug2_f("session already open"); |
f4608a70 | 1008 | reply_error(reply, MUX_S_FAILURE, rid, |
1009 | "Multiple sessions not supported"); | |
e1537f95 DM |
1010 | cleanup: |
1011 | close(new_fd[0]); | |
1012 | close(new_fd[1]); | |
a627d42e | 1013 | free(chost); |
e1537f95 DM |
1014 | return 0; |
1015 | } | |
1016 | ||
1017 | if (options.control_master == SSHCTL_MASTER_ASK || | |
1018 | options.control_master == SSHCTL_MASTER_AUTO_ASK) { | |
8d33f2aa | 1019 | if (cport == PORT_STREAMLOCAL) { |
1020 | ok = ask_permission("Allow forward to path %s", chost); | |
1021 | } else { | |
1022 | ok = ask_permission("Allow forward to [%s]:%d? ", | |
1023 | chost, cport); | |
1024 | } | |
1025 | if (!ok) { | |
816036f1 | 1026 | debug2_f("stdio fwd refused by user"); |
f4608a70 | 1027 | reply_error(reply, MUX_S_PERMISSION_DENIED, rid, |
1028 | "Permission denied"); | |
e1537f95 DM |
1029 | goto cleanup; |
1030 | } | |
1031 | } | |
1032 | ||
7be4ac81 | 1033 | nc = channel_connect_stdio_fwd(ssh, chost, cport, new_fd[0], new_fd[1], |
1034 | CHANNEL_NONBLOCK_STDIO); | |
1a66079c | 1035 | free(chost); |
e1537f95 DM |
1036 | |
1037 | nc->ctl_chan = c->self; /* link session -> control channel */ | |
1a14c131 | 1038 | c->remote_id = nc->self; /* link control -> session channel */ |
9f53229c | 1039 | c->have_remote_id = 1; |
e1537f95 | 1040 | |
816036f1 | 1041 | debug2_f("channel_new: %d control %d", nc->self, nc->ctl_chan); |
e1537f95 | 1042 | |
dbee4119 | 1043 | channel_register_cleanup(ssh, nc->self, |
1044 | mux_master_session_cleanup_cb, 1); | |
e1537f95 | 1045 | |
357610d1 DM |
1046 | cctx = xcalloc(1, sizeof(*cctx)); |
1047 | cctx->rid = rid; | |
dbee4119 | 1048 | channel_register_open_confirm(ssh, nc->self, mux_stdio_confirm, cctx); |
357610d1 | 1049 | c->mux_pause = 1; /* stop handling messages until open_confirm done */ |
e1537f95 | 1050 | |
357610d1 | 1051 | /* reply is deferred, sent by mux_session_confirm */ |
e1537f95 DM |
1052 | return 0; |
1053 | } | |
1054 | ||
357610d1 DM |
1055 | /* Callback on open confirmation in mux master for a mux stdio fwd session. */ |
1056 | static void | |
dbee4119 | 1057 | mux_stdio_confirm(struct ssh *ssh, int id, int success, void *arg) |
357610d1 DM |
1058 | { |
1059 | struct mux_stdio_confirm_ctx *cctx = arg; | |
1060 | Channel *c, *cc; | |
f4608a70 | 1061 | struct sshbuf *reply; |
1062 | int r; | |
357610d1 DM |
1063 | |
1064 | if (cctx == NULL) | |
816036f1 | 1065 | fatal_f("cctx == NULL"); |
dbee4119 | 1066 | if ((c = channel_by_id(ssh, id)) == NULL) |
816036f1 | 1067 | fatal_f("no channel for id %d", id); |
dbee4119 | 1068 | if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) |
816036f1 | 1069 | fatal_f("channel %d lacks control channel %d", |
357610d1 | 1070 | id, c->ctl_chan); |
f4608a70 | 1071 | if ((reply = sshbuf_new()) == NULL) |
816036f1 | 1072 | fatal_f("sshbuf_new"); |
357610d1 DM |
1073 | |
1074 | if (!success) { | |
816036f1 | 1075 | debug3_f("sending failure reply"); |
f4608a70 | 1076 | reply_error(reply, MUX_S_FAILURE, cctx->rid, |
1077 | "Session open refused by peer"); | |
357610d1 | 1078 | /* prepare reply */ |
357610d1 DM |
1079 | goto done; |
1080 | } | |
1081 | ||
816036f1 | 1082 | debug3_f("sending success reply"); |
357610d1 | 1083 | /* prepare reply */ |
f4608a70 | 1084 | if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 || |
1085 | (r = sshbuf_put_u32(reply, cctx->rid)) != 0 || | |
1086 | (r = sshbuf_put_u32(reply, c->self)) != 0) | |
816036f1 | 1087 | fatal_fr(r, "reply"); |
357610d1 DM |
1088 | |
1089 | done: | |
1090 | /* Send reply */ | |
f4608a70 | 1091 | if ((r = sshbuf_put_stringb(cc->output, reply)) != 0) |
816036f1 | 1092 | fatal_fr(r, "enqueue"); |
f4608a70 | 1093 | sshbuf_free(reply); |
357610d1 DM |
1094 | |
1095 | if (cc->mux_pause <= 0) | |
816036f1 | 1096 | fatal_f("mux_pause %d", cc->mux_pause); |
357610d1 DM |
1097 | cc->mux_pause = 0; /* start processing messages again */ |
1098 | c->open_confirm_ctx = NULL; | |
1099 | free(cctx); | |
1100 | } | |
1101 | ||
6c3eec7a | 1102 | static int |
9d883a1c | 1103 | mux_master_process_stop_listening(struct ssh *ssh, u_int rid, |
f4608a70 | 1104 | Channel *c, struct sshbuf *m, struct sshbuf *reply) |
6c3eec7a | 1105 | { |
816036f1 | 1106 | debug_f("channel %d: stop listening", c->self); |
6c3eec7a DM |
1107 | |
1108 | if (options.control_master == SSHCTL_MASTER_ASK || | |
1109 | options.control_master == SSHCTL_MASTER_AUTO_ASK) { | |
1110 | if (!ask_permission("Disable further multiplexing on shared " | |
1111 | "connection to %s? ", host)) { | |
816036f1 | 1112 | debug2_f("stop listen refused by user"); |
f4608a70 | 1113 | reply_error(reply, MUX_S_PERMISSION_DENIED, rid, |
1114 | "Permission denied"); | |
6c3eec7a DM |
1115 | return 0; |
1116 | } | |
1117 | } | |
1118 | ||
1119 | if (mux_listener_channel != NULL) { | |
dbee4119 | 1120 | channel_free(ssh, mux_listener_channel); |
6c3eec7a | 1121 | client_stop_mux(); |
a627d42e | 1122 | free(options.control_path); |
6c3eec7a DM |
1123 | options.control_path = NULL; |
1124 | mux_listener_channel = NULL; | |
1125 | muxserver_sock = -1; | |
1126 | } | |
1127 | ||
f4608a70 | 1128 | reply_ok(reply, rid); |
6c3eec7a DM |
1129 | return 0; |
1130 | } | |
1131 | ||
8d057847 | 1132 | static int |
9d883a1c | 1133 | mux_master_process_proxy(struct ssh *ssh, u_int rid, |
f4608a70 | 1134 | Channel *c, struct sshbuf *m, struct sshbuf *reply) |
8d057847 | 1135 | { |
f4608a70 | 1136 | int r; |
1137 | ||
816036f1 | 1138 | debug_f("channel %d: proxy request", c->self); |
8d057847 | 1139 | |
1140 | c->mux_rcb = channel_proxy_downstream; | |
f4608a70 | 1141 | if ((r = sshbuf_put_u32(reply, MUX_S_PROXY)) != 0 || |
1142 | (r = sshbuf_put_u32(reply, rid)) != 0) | |
816036f1 | 1143 | fatal_fr(r, "reply"); |
8d057847 | 1144 | |
1145 | return 0; | |
1146 | } | |
1147 | ||
6d755706 | 1148 | /* Channel callbacks fired on read/write from mux client fd */ |
e1537f95 | 1149 | static int |
dbee4119 | 1150 | mux_master_read_cb(struct ssh *ssh, Channel *c) |
e1537f95 DM |
1151 | { |
1152 | struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; | |
f4608a70 | 1153 | struct sshbuf *in = NULL, *out = NULL; |
1154 | u_int type, rid, i; | |
1155 | int r, ret = -1; | |
1156 | ||
1157 | if ((out = sshbuf_new()) == NULL) | |
816036f1 | 1158 | fatal_f("sshbuf_new"); |
e1537f95 DM |
1159 | |
1160 | /* Setup ctx and */ | |
1161 | if (c->mux_ctx == NULL) { | |
c094d1e4 | 1162 | state = xcalloc(1, sizeof(*state)); |
e1537f95 | 1163 | c->mux_ctx = state; |
dbee4119 | 1164 | channel_register_cleanup(ssh, c->self, |
e1537f95 DM |
1165 | mux_master_control_cleanup_cb, 0); |
1166 | ||
1167 | /* Send hello */ | |
f4608a70 | 1168 | if ((r = sshbuf_put_u32(out, MUX_MSG_HELLO)) != 0 || |
1169 | (r = sshbuf_put_u32(out, SSHMUX_VER)) != 0) | |
816036f1 | 1170 | fatal_fr(r, "reply"); |
e1537f95 | 1171 | /* no extensions */ |
f4608a70 | 1172 | if ((r = sshbuf_put_stringb(c->output, out)) != 0) |
816036f1 | 1173 | fatal_fr(r, "enqueue"); |
1174 | debug3_f("channel %d: hello sent", c->self); | |
f4608a70 | 1175 | ret = 0; |
1176 | goto out; | |
e1537f95 DM |
1177 | } |
1178 | ||
e1537f95 | 1179 | /* Channel code ensures that we receive whole packets */ |
f4608a70 | 1180 | if ((r = sshbuf_froms(c->input, &in)) != 0) { |
e1537f95 | 1181 | malf: |
816036f1 | 1182 | error_f("malformed message"); |
e1537f95 DM |
1183 | goto out; |
1184 | } | |
e1537f95 | 1185 | |
f4608a70 | 1186 | if ((r = sshbuf_get_u32(in, &type)) != 0) |
e1537f95 | 1187 | goto malf; |
816036f1 | 1188 | debug3_f("channel %d packet type 0x%08x len %zu", c->self, |
1189 | type, sshbuf_len(in)); | |
e1537f95 DM |
1190 | |
1191 | if (type == MUX_MSG_HELLO) | |
1192 | rid = 0; | |
1193 | else { | |
1194 | if (!state->hello_rcvd) { | |
816036f1 | 1195 | error_f("expected MUX_MSG_HELLO(0x%08x), " |
1196 | "received 0x%08x", MUX_MSG_HELLO, type); | |
e1537f95 DM |
1197 | goto out; |
1198 | } | |
f4608a70 | 1199 | if ((r = sshbuf_get_u32(in, &rid)) != 0) |
e1537f95 DM |
1200 | goto malf; |
1201 | } | |
1202 | ||
1203 | for (i = 0; mux_master_handlers[i].handler != NULL; i++) { | |
1204 | if (type == mux_master_handlers[i].type) { | |
dbee4119 | 1205 | ret = mux_master_handlers[i].handler(ssh, rid, |
f4608a70 | 1206 | c, in, out); |
e1537f95 DM |
1207 | break; |
1208 | } | |
1209 | } | |
1210 | if (mux_master_handlers[i].handler == NULL) { | |
816036f1 | 1211 | error_f("unsupported mux message 0x%08x", type); |
f4608a70 | 1212 | reply_error(out, MUX_S_FAILURE, rid, "unsupported request"); |
e1537f95 DM |
1213 | ret = 0; |
1214 | } | |
1215 | /* Enqueue reply packet */ | |
816036f1 | 1216 | if (sshbuf_len(out) != 0 && |
1217 | (r = sshbuf_put_stringb(c->output, out)) != 0) | |
1218 | fatal_fr(r, "enqueue"); | |
e1537f95 | 1219 | out: |
f4608a70 | 1220 | sshbuf_free(in); |
1221 | sshbuf_free(out); | |
e1537f95 DM |
1222 | return ret; |
1223 | } | |
1224 | ||
1225 | void | |
dbee4119 | 1226 | mux_exit_message(struct ssh *ssh, Channel *c, int exitval) |
e1537f95 | 1227 | { |
f4608a70 | 1228 | struct sshbuf *m; |
e1537f95 | 1229 | Channel *mux_chan; |
f4608a70 | 1230 | int r; |
e1537f95 | 1231 | |
816036f1 | 1232 | debug3_f("channel %d: exit message, exitval %d", c->self, exitval); |
e1537f95 | 1233 | |
dbee4119 | 1234 | if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) |
816036f1 | 1235 | fatal_f("channel %d missing mux %d", c->self, c->ctl_chan); |
e1537f95 DM |
1236 | |
1237 | /* Append exit message packet to control socket output queue */ | |
f4608a70 | 1238 | if ((m = sshbuf_new()) == NULL) |
816036f1 | 1239 | fatal_f("sshbuf_new"); |
f4608a70 | 1240 | if ((r = sshbuf_put_u32(m, MUX_S_EXIT_MESSAGE)) != 0 || |
1241 | (r = sshbuf_put_u32(m, c->self)) != 0 || | |
1242 | (r = sshbuf_put_u32(m, exitval)) != 0 || | |
1243 | (r = sshbuf_put_stringb(mux_chan->output, m)) != 0) | |
816036f1 | 1244 | fatal_fr(r, "reply"); |
f4608a70 | 1245 | sshbuf_free(m); |
e1537f95 DM |
1246 | } |
1247 | ||
555f3b85 | 1248 | void |
dbee4119 | 1249 | mux_tty_alloc_failed(struct ssh *ssh, Channel *c) |
555f3b85 | 1250 | { |
f4608a70 | 1251 | struct sshbuf *m; |
555f3b85 | 1252 | Channel *mux_chan; |
f4608a70 | 1253 | int r; |
555f3b85 | 1254 | |
816036f1 | 1255 | debug3_f("channel %d: TTY alloc failed", c->self); |
555f3b85 | 1256 | |
dbee4119 | 1257 | if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) |
816036f1 | 1258 | fatal_f("channel %d missing mux %d", c->self, c->ctl_chan); |
555f3b85 DM |
1259 | |
1260 | /* Append exit message packet to control socket output queue */ | |
f4608a70 | 1261 | if ((m = sshbuf_new()) == NULL) |
816036f1 | 1262 | fatal_f("sshbuf_new"); |
f4608a70 | 1263 | if ((r = sshbuf_put_u32(m, MUX_S_TTY_ALLOC_FAIL)) != 0 || |
1264 | (r = sshbuf_put_u32(m, c->self)) != 0 || | |
1265 | (r = sshbuf_put_stringb(mux_chan->output, m)) != 0) | |
816036f1 | 1266 | fatal_fr(r, "reply"); |
f4608a70 | 1267 | sshbuf_free(m); |
555f3b85 DM |
1268 | } |
1269 | ||
e1537f95 DM |
1270 | /* Prepare a mux master to listen on a Unix domain socket. */ |
1271 | void | |
dbee4119 | 1272 | muxserver_listen(struct ssh *ssh) |
e1537f95 | 1273 | { |
e1537f95 | 1274 | mode_t old_umask; |
603134e0 DM |
1275 | char *orig_control_path = options.control_path; |
1276 | char rbuf[16+1]; | |
1277 | u_int i, r; | |
f42f7684 | 1278 | int oerrno; |
e1537f95 DM |
1279 | |
1280 | if (options.control_path == NULL || | |
1281 | options.control_master == SSHCTL_MASTER_NO) | |
ca19bfe2 | 1282 | return; |
e1537f95 DM |
1283 | |
1284 | debug("setting up multiplex master socket"); | |
1285 | ||
603134e0 DM |
1286 | /* |
1287 | * Use a temporary path before listen so we can pseudo-atomically | |
1288 | * establish the listening socket in its final location to avoid | |
1289 | * other processes racing in between bind() and listen() and hitting | |
1290 | * an unready socket. | |
1291 | */ | |
1292 | for (i = 0; i < sizeof(rbuf) - 1; i++) { | |
1293 | r = arc4random_uniform(26+26+10); | |
1294 | rbuf[i] = (r < 26) ? 'a' + r : | |
1295 | (r < 26*2) ? 'A' + r - 26 : | |
1296 | '0' + r - 26 - 26; | |
1297 | } | |
1298 | rbuf[sizeof(rbuf) - 1] = '\0'; | |
1299 | options.control_path = NULL; | |
1300 | xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf); | |
816036f1 | 1301 | debug3_f("temporary control path %s", options.control_path); |
603134e0 | 1302 | |
e1537f95 | 1303 | old_umask = umask(0177); |
7acefbbc | 1304 | muxserver_sock = unix_listener(options.control_path, 64, 0); |
f42f7684 | 1305 | oerrno = errno; |
7acefbbc DM |
1306 | umask(old_umask); |
1307 | if (muxserver_sock < 0) { | |
f42f7684 | 1308 | if (oerrno == EINVAL || oerrno == EADDRINUSE) { |
e1537f95 DM |
1309 | error("ControlSocket %s already exists, " |
1310 | "disabling multiplexing", options.control_path); | |
603134e0 | 1311 | disable_mux_master: |
60432d8c DM |
1312 | if (muxserver_sock != -1) { |
1313 | close(muxserver_sock); | |
1314 | muxserver_sock = -1; | |
1315 | } | |
a627d42e DT |
1316 | free(orig_control_path); |
1317 | free(options.control_path); | |
e1537f95 DM |
1318 | options.control_path = NULL; |
1319 | options.control_master = SSHCTL_MASTER_NO; | |
1320 | return; | |
7acefbbc DM |
1321 | } else { |
1322 | /* unix_listener() logs the error */ | |
1323 | cleanup_exit(255); | |
1324 | } | |
e1537f95 | 1325 | } |
e1537f95 | 1326 | |
603134e0 DM |
1327 | /* Now atomically "move" the mux socket into position */ |
1328 | if (link(options.control_path, orig_control_path) != 0) { | |
1329 | if (errno != EEXIST) { | |
816036f1 | 1330 | fatal_f("link mux listener %s => %s: %s", |
603134e0 DM |
1331 | options.control_path, orig_control_path, |
1332 | strerror(errno)); | |
1333 | } | |
1334 | error("ControlSocket %s already exists, disabling multiplexing", | |
1335 | orig_control_path); | |
603134e0 DM |
1336 | unlink(options.control_path); |
1337 | goto disable_mux_master; | |
1338 | } | |
1339 | unlink(options.control_path); | |
a627d42e | 1340 | free(options.control_path); |
603134e0 DM |
1341 | options.control_path = orig_control_path; |
1342 | ||
e1537f95 DM |
1343 | set_nonblock(muxserver_sock); |
1344 | ||
dbee4119 | 1345 | mux_listener_channel = channel_new(ssh, "mux listener", |
e1537f95 DM |
1346 | SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1, |
1347 | CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, | |
603134e0 | 1348 | 0, options.control_path, 1); |
e1537f95 | 1349 | mux_listener_channel->mux_rcb = mux_master_read_cb; |
816036f1 | 1350 | debug3_f("mux listener channel %d fd %d", |
e1537f95 DM |
1351 | mux_listener_channel->self, mux_listener_channel->sock); |
1352 | } | |
1353 | ||
1354 | /* Callback on open confirmation in mux master for a mux client session. */ | |
1355 | static void | |
dbee4119 | 1356 | mux_session_confirm(struct ssh *ssh, int id, int success, void *arg) |
e1537f95 DM |
1357 | { |
1358 | struct mux_session_confirm_ctx *cctx = arg; | |
1359 | const char *display; | |
d530f5f4 | 1360 | Channel *c, *cc; |
f4608a70 | 1361 | int i, r; |
1362 | struct sshbuf *reply; | |
e1537f95 DM |
1363 | |
1364 | if (cctx == NULL) | |
816036f1 | 1365 | fatal_f("cctx == NULL"); |
dbee4119 | 1366 | if ((c = channel_by_id(ssh, id)) == NULL) |
816036f1 | 1367 | fatal_f("no channel for id %d", id); |
dbee4119 | 1368 | if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) |
816036f1 | 1369 | fatal_f("channel %d lacks control channel %d", |
d530f5f4 | 1370 | id, c->ctl_chan); |
f4608a70 | 1371 | if ((reply = sshbuf_new()) == NULL) |
816036f1 | 1372 | fatal_f("sshbuf_new"); |
d530f5f4 DM |
1373 | |
1374 | if (!success) { | |
816036f1 | 1375 | debug3_f("sending failure reply"); |
f4608a70 | 1376 | reply_error(reply, MUX_S_FAILURE, cctx->rid, |
1377 | "Session open refused by peer"); | |
d530f5f4 DM |
1378 | goto done; |
1379 | } | |
e1537f95 DM |
1380 | |
1381 | display = getenv("DISPLAY"); | |
1382 | if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { | |
1383 | char *proto, *data; | |
1ab6a51f | 1384 | |
e1537f95 | 1385 | /* Get reasonable local authentication information. */ |
dbee4119 | 1386 | if (client_x11_get_proto(ssh, display, options.xauth_location, |
1ab6a51f | 1387 | options.forward_x11_trusted, options.forward_x11_timeout, |
ed4ce82d | 1388 | &proto, &data) == 0) { |
1389 | /* Request forwarding with authentication spoofing. */ | |
1390 | debug("Requesting X11 forwarding with authentication " | |
1391 | "spoofing."); | |
dbee4119 | 1392 | x11_request_forwarding_with_spoofing(ssh, id, |
1393 | display, proto, data, 1); | |
ed4ce82d | 1394 | /* XXX exit_on_forward_failure */ |
dbee4119 | 1395 | client_expect_confirm(ssh, id, "X11 forwarding", |
ed4ce82d | 1396 | CONFIRM_WARN); |
1397 | } | |
e1537f95 DM |
1398 | } |
1399 | ||
1400 | if (cctx->want_agent_fwd && options.forward_agent) { | |
1401 | debug("Requesting authentication agent forwarding."); | |
dbee4119 | 1402 | channel_request_start(ssh, id, "auth-agent-req@openssh.com", 0); |
e3128b38 | 1403 | if ((r = sshpkt_send(ssh)) != 0) |
816036f1 | 1404 | fatal_fr(r, "send"); |
e1537f95 DM |
1405 | } |
1406 | ||
dbee4119 | 1407 | client_session2_setup(ssh, id, cctx->want_tty, cctx->want_subsys, |
f4608a70 | 1408 | cctx->term, &cctx->tio, c->rfd, cctx->cmd, cctx->env); |
e1537f95 | 1409 | |
816036f1 | 1410 | debug3_f("sending success reply"); |
d530f5f4 | 1411 | /* prepare reply */ |
f4608a70 | 1412 | if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 || |
1413 | (r = sshbuf_put_u32(reply, cctx->rid)) != 0 || | |
1414 | (r = sshbuf_put_u32(reply, c->self)) != 0) | |
816036f1 | 1415 | fatal_fr(r, "reply"); |
d530f5f4 DM |
1416 | |
1417 | done: | |
1418 | /* Send reply */ | |
f4608a70 | 1419 | if ((r = sshbuf_put_stringb(cc->output, reply)) != 0) |
816036f1 | 1420 | fatal_fr(r, "enqueue"); |
f4608a70 | 1421 | sshbuf_free(reply); |
d530f5f4 DM |
1422 | |
1423 | if (cc->mux_pause <= 0) | |
816036f1 | 1424 | fatal_f("mux_pause %d", cc->mux_pause); |
d530f5f4 | 1425 | cc->mux_pause = 0; /* start processing messages again */ |
e1537f95 | 1426 | c->open_confirm_ctx = NULL; |
f4608a70 | 1427 | sshbuf_free(cctx->cmd); |
a627d42e | 1428 | free(cctx->term); |
e1537f95 DM |
1429 | if (cctx->env != NULL) { |
1430 | for (i = 0; cctx->env[i] != NULL; i++) | |
a627d42e DT |
1431 | free(cctx->env[i]); |
1432 | free(cctx->env); | |
e1537f95 | 1433 | } |
a627d42e | 1434 | free(cctx); |
e1537f95 DM |
1435 | } |
1436 | ||
1437 | /* ** Multiplexing client support */ | |
1438 | ||
1439 | /* Exit signal handler */ | |
1440 | static void | |
1441 | control_client_sighandler(int signo) | |
1442 | { | |
1443 | muxclient_terminate = signo; | |
1444 | } | |
1445 | ||
1446 | /* | |
1447 | * Relay signal handler - used to pass some signals from mux client to | |
1448 | * mux master. | |
1449 | */ | |
1450 | static void | |
1451 | control_client_sigrelay(int signo) | |
1452 | { | |
1453 | int save_errno = errno; | |
1454 | ||
1455 | if (muxserver_pid > 1) | |
1456 | kill(muxserver_pid, signo); | |
1457 | ||
1458 | errno = save_errno; | |
1459 | } | |
1460 | ||
1461 | static int | |
e535fbe2 | 1462 | mux_client_read(int fd, struct sshbuf *b, size_t need, int timeout_ms) |
e1537f95 | 1463 | { |
f4608a70 | 1464 | size_t have; |
e1537f95 DM |
1465 | ssize_t len; |
1466 | u_char *p; | |
f4608a70 | 1467 | int r; |
e1537f95 | 1468 | |
f4608a70 | 1469 | if ((r = sshbuf_reserve(b, need, &p)) != 0) |
816036f1 | 1470 | fatal_fr(r, "reserve"); |
e1537f95 DM |
1471 | for (have = 0; have < need; ) { |
1472 | if (muxclient_terminate) { | |
1473 | errno = EINTR; | |
1474 | return -1; | |
1475 | } | |
1476 | len = read(fd, p + have, need - have); | |
4d28fa78 | 1477 | if (len == -1) { |
e1537f95 DM |
1478 | switch (errno) { |
1479 | #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) | |
1480 | case EWOULDBLOCK: | |
1481 | #endif | |
1482 | case EAGAIN: | |
803e22ea | 1483 | if (waitrfd(fd, &timeout_ms, |
1484 | &muxclient_terminate) == -1 && | |
1485 | errno != EINTR) | |
e535fbe2 | 1486 | return -1; /* timeout */ |
e1537f95 DM |
1487 | /* FALLTHROUGH */ |
1488 | case EINTR: | |
1489 | continue; | |
1490 | default: | |
1491 | return -1; | |
1492 | } | |
1493 | } | |
1494 | if (len == 0) { | |
1495 | errno = EPIPE; | |
1496 | return -1; | |
1497 | } | |
f4608a70 | 1498 | have += (size_t)len; |
e1537f95 DM |
1499 | } |
1500 | return 0; | |
1501 | } | |
1502 | ||
1503 | static int | |
f4608a70 | 1504 | mux_client_write_packet(int fd, struct sshbuf *m) |
e1537f95 | 1505 | { |
f4608a70 | 1506 | struct sshbuf *queue; |
e1537f95 | 1507 | u_int have, need; |
f4608a70 | 1508 | int r, oerrno, len; |
1509 | const u_char *ptr; | |
e1537f95 DM |
1510 | struct pollfd pfd; |
1511 | ||
1512 | pfd.fd = fd; | |
1513 | pfd.events = POLLOUT; | |
f4608a70 | 1514 | if ((queue = sshbuf_new()) == NULL) |
816036f1 | 1515 | fatal_f("sshbuf_new"); |
f4608a70 | 1516 | if ((r = sshbuf_put_stringb(queue, m)) != 0) |
816036f1 | 1517 | fatal_fr(r, "enqueue"); |
e1537f95 | 1518 | |
f4608a70 | 1519 | need = sshbuf_len(queue); |
1520 | ptr = sshbuf_ptr(queue); | |
e1537f95 DM |
1521 | |
1522 | for (have = 0; have < need; ) { | |
1523 | if (muxclient_terminate) { | |
f4608a70 | 1524 | sshbuf_free(queue); |
e1537f95 DM |
1525 | errno = EINTR; |
1526 | return -1; | |
1527 | } | |
1528 | len = write(fd, ptr + have, need - have); | |
4d28fa78 | 1529 | if (len == -1) { |
e1537f95 DM |
1530 | switch (errno) { |
1531 | #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) | |
1532 | case EWOULDBLOCK: | |
1533 | #endif | |
1534 | case EAGAIN: | |
1535 | (void)poll(&pfd, 1, -1); | |
1536 | /* FALLTHROUGH */ | |
1537 | case EINTR: | |
1538 | continue; | |
1539 | default: | |
1540 | oerrno = errno; | |
f4608a70 | 1541 | sshbuf_free(queue); |
e1537f95 DM |
1542 | errno = oerrno; |
1543 | return -1; | |
1544 | } | |
1545 | } | |
1546 | if (len == 0) { | |
f4608a70 | 1547 | sshbuf_free(queue); |
e1537f95 DM |
1548 | errno = EPIPE; |
1549 | return -1; | |
1550 | } | |
1551 | have += (u_int)len; | |
1552 | } | |
f4608a70 | 1553 | sshbuf_free(queue); |
e1537f95 DM |
1554 | return 0; |
1555 | } | |
1556 | ||
1557 | static int | |
e535fbe2 | 1558 | mux_client_read_packet_timeout(int fd, struct sshbuf *m, int timeout_ms) |
e1537f95 | 1559 | { |
f4608a70 | 1560 | struct sshbuf *queue; |
1561 | size_t need, have; | |
633de33b | 1562 | const u_char *ptr; |
f4608a70 | 1563 | int r, oerrno; |
e1537f95 | 1564 | |
f4608a70 | 1565 | if ((queue = sshbuf_new()) == NULL) |
816036f1 | 1566 | fatal_f("sshbuf_new"); |
e535fbe2 | 1567 | if (mux_client_read(fd, queue, 4, timeout_ms) != 0) { |
e1537f95 | 1568 | if ((oerrno = errno) == EPIPE) |
816036f1 | 1569 | debug3_f("read header failed: %s", |
746e9067 | 1570 | strerror(errno)); |
f4608a70 | 1571 | sshbuf_free(queue); |
e1537f95 DM |
1572 | errno = oerrno; |
1573 | return -1; | |
ca19bfe2 | 1574 | } |
f4608a70 | 1575 | need = PEEK_U32(sshbuf_ptr(queue)); |
e535fbe2 | 1576 | if (mux_client_read(fd, queue, need, timeout_ms) != 0) { |
e1537f95 | 1577 | oerrno = errno; |
816036f1 | 1578 | debug3_f("read body failed: %s", strerror(errno)); |
f4608a70 | 1579 | sshbuf_free(queue); |
e1537f95 DM |
1580 | errno = oerrno; |
1581 | return -1; | |
1582 | } | |
f4608a70 | 1583 | if ((r = sshbuf_get_string_direct(queue, &ptr, &have)) != 0 || |
1584 | (r = sshbuf_put(m, ptr, have)) != 0) | |
816036f1 | 1585 | fatal_fr(r, "dequeue"); |
f4608a70 | 1586 | sshbuf_free(queue); |
e1537f95 DM |
1587 | return 0; |
1588 | } | |
1589 | ||
1590 | static int | |
e535fbe2 | 1591 | mux_client_read_packet(int fd, struct sshbuf *m) |
1592 | { | |
1593 | return mux_client_read_packet_timeout(fd, m, -1); | |
1594 | } | |
1595 | ||
1596 | static int | |
1597 | mux_client_hello_exchange(int fd, int timeout_ms) | |
e1537f95 | 1598 | { |
f4608a70 | 1599 | struct sshbuf *m; |
e1537f95 | 1600 | u_int type, ver; |
f4608a70 | 1601 | int r, ret = -1; |
e1537f95 | 1602 | |
f4608a70 | 1603 | if ((m = sshbuf_new()) == NULL) |
816036f1 | 1604 | fatal_f("sshbuf_new"); |
f4608a70 | 1605 | if ((r = sshbuf_put_u32(m, MUX_MSG_HELLO)) != 0 || |
1606 | (r = sshbuf_put_u32(m, SSHMUX_VER)) != 0) | |
816036f1 | 1607 | fatal_fr(r, "assemble hello"); |
e1537f95 DM |
1608 | /* no extensions */ |
1609 | ||
f4608a70 | 1610 | if (mux_client_write_packet(fd, m) != 0) { |
816036f1 | 1611 | debug_f("write packet: %s", strerror(errno)); |
5b2f34a7 | 1612 | goto out; |
1613 | } | |
e1537f95 | 1614 | |
f4608a70 | 1615 | sshbuf_reset(m); |
b1cbfa25 | 1616 | |
e1537f95 | 1617 | /* Read their HELLO */ |
e535fbe2 | 1618 | if (mux_client_read_packet_timeout(fd, m, timeout_ms) != 0) { |
816036f1 | 1619 | debug_f("read packet failed"); |
5b2f34a7 | 1620 | goto out; |
ca19bfe2 | 1621 | } |
e1537f95 | 1622 | |
f4608a70 | 1623 | if ((r = sshbuf_get_u32(m, &type)) != 0) |
816036f1 | 1624 | fatal_fr(r, "parse type"); |
5b2f34a7 | 1625 | if (type != MUX_MSG_HELLO) { |
816036f1 | 1626 | error_f("expected HELLO (%u) got %u", MUX_MSG_HELLO, type); |
5b2f34a7 | 1627 | goto out; |
1628 | } | |
f4608a70 | 1629 | if ((r = sshbuf_get_u32(m, &ver)) != 0) |
816036f1 | 1630 | fatal_fr(r, "parse version"); |
5b2f34a7 | 1631 | if (ver != SSHMUX_VER) { |
1632 | error("Unsupported multiplexing protocol version %d " | |
e1537f95 | 1633 | "(expected %d)", ver, SSHMUX_VER); |
5b2f34a7 | 1634 | goto out; |
1635 | } | |
816036f1 | 1636 | debug2_f("master version %u", ver); |
e1537f95 | 1637 | /* No extensions are presently defined */ |
f4608a70 | 1638 | while (sshbuf_len(m) > 0) { |
1639 | char *name = NULL; | |
e1537f95 | 1640 | |
f4608a70 | 1641 | if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0 || |
1642 | (r = sshbuf_skip_string(m)) != 0) { /* value */ | |
816036f1 | 1643 | error_fr(r, "parse extension"); |
f4608a70 | 1644 | goto out; |
1645 | } | |
e1537f95 | 1646 | debug2("Unrecognised master extension \"%s\"", name); |
a627d42e | 1647 | free(name); |
ca19bfe2 | 1648 | } |
5b2f34a7 | 1649 | /* success */ |
1650 | ret = 0; | |
1651 | out: | |
f4608a70 | 1652 | sshbuf_free(m); |
5b2f34a7 | 1653 | return ret; |
e1537f95 DM |
1654 | } |
1655 | ||
1656 | static u_int | |
1657 | mux_client_request_alive(int fd) | |
1658 | { | |
f4608a70 | 1659 | struct sshbuf *m; |
e1537f95 DM |
1660 | char *e; |
1661 | u_int pid, type, rid; | |
f4608a70 | 1662 | int r; |
e1537f95 | 1663 | |
816036f1 | 1664 | debug3_f("entering"); |
e1537f95 | 1665 | |
f4608a70 | 1666 | if ((m = sshbuf_new()) == NULL) |
816036f1 | 1667 | fatal_f("sshbuf_new"); |
f4608a70 | 1668 | if ((r = sshbuf_put_u32(m, MUX_C_ALIVE_CHECK)) != 0 || |
1669 | (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) | |
816036f1 | 1670 | fatal_fr(r, "assemble"); |
e1537f95 | 1671 | |
f4608a70 | 1672 | if (mux_client_write_packet(fd, m) != 0) |
816036f1 | 1673 | fatal_f("write packet: %s", strerror(errno)); |
e1537f95 | 1674 | |
f4608a70 | 1675 | sshbuf_reset(m); |
e1537f95 DM |
1676 | |
1677 | /* Read their reply */ | |
f4608a70 | 1678 | if (mux_client_read_packet(fd, m) != 0) { |
1679 | sshbuf_free(m); | |
e1537f95 | 1680 | return 0; |
ca19bfe2 | 1681 | } |
e1537f95 | 1682 | |
f4608a70 | 1683 | if ((r = sshbuf_get_u32(m, &type)) != 0) |
816036f1 | 1684 | fatal_fr(r, "parse type"); |
e1537f95 | 1685 | if (type != MUX_S_ALIVE) { |
f4608a70 | 1686 | if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) |
816036f1 | 1687 | fatal_fr(r, "parse error message"); |
1688 | fatal_f("master returned error: %s", e); | |
ca19bfe2 | 1689 | } |
e1537f95 | 1690 | |
f4608a70 | 1691 | if ((r = sshbuf_get_u32(m, &rid)) != 0) |
816036f1 | 1692 | fatal_fr(r, "parse remote ID"); |
f4608a70 | 1693 | if (rid != muxclient_request_id) |
816036f1 | 1694 | fatal_f("out of sequence reply: my id %u theirs %u", |
1695 | muxclient_request_id, rid); | |
f4608a70 | 1696 | if ((r = sshbuf_get_u32(m, &pid)) != 0) |
816036f1 | 1697 | fatal_fr(r, "parse PID"); |
f4608a70 | 1698 | sshbuf_free(m); |
e1537f95 | 1699 | |
816036f1 | 1700 | debug3_f("done pid = %u", pid); |
e1537f95 DM |
1701 | |
1702 | muxclient_request_id++; | |
1703 | ||
1704 | return pid; | |
1705 | } | |
1706 | ||
1707 | static void | |
1708 | mux_client_request_terminate(int fd) | |
1709 | { | |
f4608a70 | 1710 | struct sshbuf *m; |
e1537f95 DM |
1711 | char *e; |
1712 | u_int type, rid; | |
f4608a70 | 1713 | int r; |
e1537f95 | 1714 | |
816036f1 | 1715 | debug3_f("entering"); |
e1537f95 | 1716 | |
f4608a70 | 1717 | if ((m = sshbuf_new()) == NULL) |
816036f1 | 1718 | fatal_f("sshbuf_new"); |
f4608a70 | 1719 | if ((r = sshbuf_put_u32(m, MUX_C_TERMINATE)) != 0 || |
1720 | (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) | |
816036f1 | 1721 | fatal_fr(r, "request"); |
e1537f95 | 1722 | |
f4608a70 | 1723 | if (mux_client_write_packet(fd, m) != 0) |
816036f1 | 1724 | fatal_f("write packet: %s", strerror(errno)); |
b1cbfa25 | 1725 | |
f4608a70 | 1726 | sshbuf_reset(m); |
b1cbfa25 | 1727 | |
e1537f95 | 1728 | /* Read their reply */ |
f4608a70 | 1729 | if (mux_client_read_packet(fd, m) != 0) { |
e1537f95 DM |
1730 | /* Remote end exited already */ |
1731 | if (errno == EPIPE) { | |
f4608a70 | 1732 | sshbuf_free(m); |
e1537f95 | 1733 | return; |
2fb66cac | 1734 | } |
816036f1 | 1735 | fatal_f("read from master failed: %s", strerror(errno)); |
e1537f95 DM |
1736 | } |
1737 | ||
f4608a70 | 1738 | if ((r = sshbuf_get_u32(m, &type)) != 0 || |
1739 | (r = sshbuf_get_u32(m, &rid)) != 0) | |
816036f1 | 1740 | fatal_fr(r, "parse"); |
f4608a70 | 1741 | if (rid != muxclient_request_id) |
816036f1 | 1742 | fatal_f("out of sequence reply: my id %u theirs %u", |
1743 | muxclient_request_id, rid); | |
e1537f95 DM |
1744 | switch (type) { |
1745 | case MUX_S_OK: | |
b1cbfa25 | 1746 | break; |
e1537f95 | 1747 | case MUX_S_PERMISSION_DENIED: |
f4608a70 | 1748 | if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) |
816036f1 | 1749 | fatal_fr(r, "parse error message"); |
e1537f95 DM |
1750 | fatal("Master refused termination request: %s", e); |
1751 | case MUX_S_FAILURE: | |
f4608a70 | 1752 | if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) |
816036f1 | 1753 | fatal_fr(r, "parse error message"); |
1754 | fatal_f("termination request failed: %s", e); | |
b1cbfa25 | 1755 | default: |
816036f1 | 1756 | fatal_f("unexpected response from master 0x%08x", type); |
b1cbfa25 | 1757 | } |
f4608a70 | 1758 | sshbuf_free(m); |
e1537f95 DM |
1759 | muxclient_request_id++; |
1760 | } | |
1761 | ||
1762 | static int | |
7acefbbc | 1763 | mux_client_forward(int fd, int cancel_flag, u_int ftype, struct Forward *fwd) |
e1537f95 | 1764 | { |
f4608a70 | 1765 | struct sshbuf *m; |
e1537f95 | 1766 | char *e, *fwd_desc; |
f4608a70 | 1767 | const char *lhost, *chost; |
e1537f95 | 1768 | u_int type, rid; |
f4608a70 | 1769 | int r; |
e1537f95 DM |
1770 | |
1771 | fwd_desc = format_forward(ftype, fwd); | |
f6dff7cd DM |
1772 | debug("Requesting %s %s", |
1773 | cancel_flag ? "cancellation of" : "forwarding of", fwd_desc); | |
a627d42e | 1774 | free(fwd_desc); |
e1537f95 | 1775 | |
f4608a70 | 1776 | type = cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD; |
1777 | if (fwd->listen_path != NULL) | |
1778 | lhost = fwd->listen_path; | |
1779 | else if (fwd->listen_host == NULL) | |
1780 | lhost = ""; | |
1781 | else if (*fwd->listen_host == '\0') | |
1782 | lhost = "*"; | |
1783 | else | |
1784 | lhost = fwd->listen_host; | |
e1537f95 | 1785 | |
f4608a70 | 1786 | if (fwd->connect_path != NULL) |
1787 | chost = fwd->connect_path; | |
1788 | else if (fwd->connect_host == NULL) | |
1789 | chost = ""; | |
1790 | else | |
1791 | chost = fwd->connect_host; | |
1792 | ||
1793 | if ((m = sshbuf_new()) == NULL) | |
816036f1 | 1794 | fatal_f("sshbuf_new"); |
f4608a70 | 1795 | if ((r = sshbuf_put_u32(m, type)) != 0 || |
1796 | (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || | |
1797 | (r = sshbuf_put_u32(m, ftype)) != 0 || | |
1798 | (r = sshbuf_put_cstring(m, lhost)) != 0 || | |
1799 | (r = sshbuf_put_u32(m, fwd->listen_port)) != 0 || | |
1800 | (r = sshbuf_put_cstring(m, chost)) != 0 || | |
1801 | (r = sshbuf_put_u32(m, fwd->connect_port)) != 0) | |
816036f1 | 1802 | fatal_fr(r, "request"); |
f4608a70 | 1803 | |
1804 | if (mux_client_write_packet(fd, m) != 0) | |
816036f1 | 1805 | fatal_f("write packet: %s", strerror(errno)); |
e1537f95 | 1806 | |
f4608a70 | 1807 | sshbuf_reset(m); |
b1cbfa25 | 1808 | |
e1537f95 | 1809 | /* Read their reply */ |
f4608a70 | 1810 | if (mux_client_read_packet(fd, m) != 0) { |
1811 | sshbuf_free(m); | |
e1537f95 | 1812 | return -1; |
ca19bfe2 | 1813 | } |
b1cbfa25 | 1814 | |
f4608a70 | 1815 | if ((r = sshbuf_get_u32(m, &type)) != 0 || |
1816 | (r = sshbuf_get_u32(m, &rid)) != 0) | |
816036f1 | 1817 | fatal_fr(r, "parse"); |
f4608a70 | 1818 | if (rid != muxclient_request_id) |
816036f1 | 1819 | fatal_f("out of sequence reply: my id %u theirs %u", |
1820 | muxclient_request_id, rid); | |
f4608a70 | 1821 | |
e1537f95 DM |
1822 | switch (type) { |
1823 | case MUX_S_OK: | |
1824 | break; | |
388f6fc4 | 1825 | case MUX_S_REMOTE_PORT: |
f6dff7cd | 1826 | if (cancel_flag) |
816036f1 | 1827 | fatal_f("got MUX_S_REMOTE_PORT for cancel"); |
f4608a70 | 1828 | if ((r = sshbuf_get_u32(m, &fwd->allocated_port)) != 0) |
816036f1 | 1829 | fatal_fr(r, "parse port"); |
8312cfb8 | 1830 | verbose("Allocated port %u for remote forward to %s:%d", |
388f6fc4 DM |
1831 | fwd->allocated_port, |
1832 | fwd->connect_host ? fwd->connect_host : "", | |
1833 | fwd->connect_port); | |
1834 | if (muxclient_command == SSHMUX_COMMAND_FORWARD) | |
b1d38a3c | 1835 | fprintf(stdout, "%i\n", fwd->allocated_port); |
388f6fc4 | 1836 | break; |
e1537f95 | 1837 | case MUX_S_PERMISSION_DENIED: |
f4608a70 | 1838 | if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) |
816036f1 | 1839 | fatal_fr(r, "parse error message"); |
f4608a70 | 1840 | sshbuf_free(m); |
e1537f95 DM |
1841 | error("Master refused forwarding request: %s", e); |
1842 | return -1; | |
1843 | case MUX_S_FAILURE: | |
f4608a70 | 1844 | if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) |
816036f1 | 1845 | fatal_fr(r, "parse error message"); |
f4608a70 | 1846 | sshbuf_free(m); |
816036f1 | 1847 | error_f("forwarding request failed: %s", e); |
e1537f95 DM |
1848 | return -1; |
1849 | default: | |
816036f1 | 1850 | fatal_f("unexpected response from master 0x%08x", type); |
ca19bfe2 | 1851 | } |
f4608a70 | 1852 | sshbuf_free(m); |
ca19bfe2 | 1853 | |
e1537f95 DM |
1854 | muxclient_request_id++; |
1855 | return 0; | |
1856 | } | |
b1cbfa25 | 1857 | |
e1537f95 | 1858 | static int |
f6dff7cd | 1859 | mux_client_forwards(int fd, int cancel_flag) |
e1537f95 | 1860 | { |
f6dff7cd | 1861 | int i, ret = 0; |
e1537f95 | 1862 | |
816036f1 | 1863 | debug3_f("%s forwardings: %d local, %d remote", |
f6dff7cd | 1864 | cancel_flag ? "cancel" : "request", |
e1537f95 DM |
1865 | options.num_local_forwards, options.num_remote_forwards); |
1866 | ||
1867 | /* XXX ExitOnForwardingFailure */ | |
1868 | for (i = 0; i < options.num_local_forwards; i++) { | |
f6dff7cd | 1869 | if (mux_client_forward(fd, cancel_flag, |
e1537f95 DM |
1870 | options.local_forwards[i].connect_port == 0 ? |
1871 | MUX_FWD_DYNAMIC : MUX_FWD_LOCAL, | |
1872 | options.local_forwards + i) != 0) | |
f6dff7cd | 1873 | ret = -1; |
e1537f95 DM |
1874 | } |
1875 | for (i = 0; i < options.num_remote_forwards; i++) { | |
f6dff7cd | 1876 | if (mux_client_forward(fd, cancel_flag, MUX_FWD_REMOTE, |
e1537f95 | 1877 | options.remote_forwards + i) != 0) |
f6dff7cd | 1878 | ret = -1; |
e1537f95 | 1879 | } |
f6dff7cd | 1880 | return ret; |
e1537f95 DM |
1881 | } |
1882 | ||
1883 | static int | |
1884 | mux_client_request_session(int fd) | |
1885 | { | |
f4608a70 | 1886 | struct sshbuf *m; |
1887 | char *e; | |
f64f8c00 | 1888 | const char *term = NULL; |
22e1a3a7 | 1889 | u_int i, echar, rid, sid, esid, exitval, type, exitval_seen; |
e1537f95 | 1890 | extern char **environ; |
2d34205d | 1891 | int r, rawmode = 0; |
e1537f95 | 1892 | |
816036f1 | 1893 | debug3_f("entering"); |
e1537f95 DM |
1894 | |
1895 | if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { | |
816036f1 | 1896 | error_f("master alive request failed"); |
e1537f95 DM |
1897 | return -1; |
1898 | } | |
1899 | ||
3bf2a6ac | 1900 | ssh_signal(SIGPIPE, SIG_IGN); |
e1537f95 | 1901 | |
e0c5088f | 1902 | if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1) |
816036f1 | 1903 | fatal_f("stdfd_devnull failed"); |
e1537f95 | 1904 | |
f64f8c00 | 1905 | if ((term = lookup_env_in_list("TERM", options.setenv, |
1906 | options.num_setenv)) == NULL || *term == '\0') | |
1907 | term = getenv("TERM"); | |
1908 | ||
f4608a70 | 1909 | echar = 0xffffffff; |
1910 | if (options.escape_char != SSH_ESCAPECHAR_NONE) | |
1911 | echar = (u_int)options.escape_char; | |
1912 | ||
1913 | if ((m = sshbuf_new()) == NULL) | |
816036f1 | 1914 | fatal_f("sshbuf_new"); |
f4608a70 | 1915 | if ((r = sshbuf_put_u32(m, MUX_C_NEW_SESSION)) != 0 || |
1916 | (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || | |
1917 | (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */ | |
1918 | (r = sshbuf_put_u32(m, tty_flag)) != 0 || | |
1919 | (r = sshbuf_put_u32(m, options.forward_x11)) != 0 || | |
1920 | (r = sshbuf_put_u32(m, options.forward_agent)) != 0 || | |
eda8909d | 1921 | (r = sshbuf_put_u32(m, options.session_type == SESSION_TYPE_SUBSYSTEM)) != 0 || |
f4608a70 | 1922 | (r = sshbuf_put_u32(m, echar)) != 0 || |
f64f8c00 | 1923 | (r = sshbuf_put_cstring(m, term == NULL ? "" : term)) != 0 || |
f4608a70 | 1924 | (r = sshbuf_put_stringb(m, command)) != 0) |
816036f1 | 1925 | fatal_fr(r, "request"); |
e1537f95 | 1926 | |
7082bb58 | 1927 | /* Pass environment */ |
e1537f95 | 1928 | if (options.num_send_env > 0 && environ != NULL) { |
e1537f95 | 1929 | for (i = 0; environ[i] != NULL; i++) { |
f4608a70 | 1930 | if (!env_permitted(environ[i])) |
1931 | continue; | |
1932 | if ((r = sshbuf_put_cstring(m, environ[i])) != 0) | |
816036f1 | 1933 | fatal_fr(r, "request sendenv"); |
e1537f95 DM |
1934 | } |
1935 | } | |
f4608a70 | 1936 | for (i = 0; i < options.num_setenv; i++) { |
1937 | if ((r = sshbuf_put_cstring(m, options.setenv[i])) != 0) | |
816036f1 | 1938 | fatal_fr(r, "request setenv"); |
f4608a70 | 1939 | } |
e1537f95 | 1940 | |
f4608a70 | 1941 | if (mux_client_write_packet(fd, m) != 0) |
816036f1 | 1942 | fatal_f("write packet: %s", strerror(errno)); |
e1537f95 DM |
1943 | |
1944 | /* Send the stdio file descriptors */ | |
1945 | if (mm_send_fd(fd, STDIN_FILENO) == -1 || | |
1946 | mm_send_fd(fd, STDOUT_FILENO) == -1 || | |
1947 | mm_send_fd(fd, STDERR_FILENO) == -1) | |
816036f1 | 1948 | fatal_f("send fds failed"); |
e1537f95 | 1949 | |
816036f1 | 1950 | debug3_f("session request sent"); |
e1537f95 DM |
1951 | |
1952 | /* Read their reply */ | |
f4608a70 | 1953 | sshbuf_reset(m); |
1954 | if (mux_client_read_packet(fd, m) != 0) { | |
816036f1 | 1955 | error_f("read from master failed: %s", strerror(errno)); |
f4608a70 | 1956 | sshbuf_free(m); |
e1537f95 DM |
1957 | return -1; |
1958 | } | |
1959 | ||
f4608a70 | 1960 | if ((r = sshbuf_get_u32(m, &type)) != 0 || |
1961 | (r = sshbuf_get_u32(m, &rid)) != 0) | |
816036f1 | 1962 | fatal_fr(r, "parse"); |
f4608a70 | 1963 | if (rid != muxclient_request_id) |
816036f1 | 1964 | fatal_f("out of sequence reply: my id %u theirs %u", |
1965 | muxclient_request_id, rid); | |
f4608a70 | 1966 | |
e1537f95 DM |
1967 | switch (type) { |
1968 | case MUX_S_SESSION_OPENED: | |
f4608a70 | 1969 | if ((r = sshbuf_get_u32(m, &sid)) != 0) |
816036f1 | 1970 | fatal_fr(r, "parse session ID"); |
1971 | debug_f("master session id: %u", sid); | |
e1537f95 DM |
1972 | break; |
1973 | case MUX_S_PERMISSION_DENIED: | |
f4608a70 | 1974 | if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) |
816036f1 | 1975 | fatal_fr(r, "parse error message"); |
445c9a50 | 1976 | error("Master refused session request: %s", e); |
f4608a70 | 1977 | sshbuf_free(m); |
e1537f95 DM |
1978 | return -1; |
1979 | case MUX_S_FAILURE: | |
f4608a70 | 1980 | if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) |
816036f1 | 1981 | fatal_fr(r, "parse error message"); |
1982 | error_f("session request failed: %s", e); | |
f4608a70 | 1983 | sshbuf_free(m); |
e1537f95 DM |
1984 | return -1; |
1985 | default: | |
f4608a70 | 1986 | sshbuf_free(m); |
816036f1 | 1987 | error_f("unexpected response from master 0x%08x", type); |
e1537f95 DM |
1988 | return -1; |
1989 | } | |
1990 | muxclient_request_id++; | |
b1cbfa25 | 1991 | |
d7d2bc95 | 1992 | if (pledge("stdio proc tty", NULL) == -1) |
816036f1 | 1993 | fatal_f("pledge(): %s", strerror(errno)); |
4626cbaf | 1994 | platform_pledge_mux(); |
d7d2bc95 | 1995 | |
3bf2a6ac | 1996 | ssh_signal(SIGHUP, control_client_sighandler); |
1997 | ssh_signal(SIGINT, control_client_sighandler); | |
1998 | ssh_signal(SIGTERM, control_client_sighandler); | |
1999 | ssh_signal(SIGWINCH, control_client_sigrelay); | |
b1cbfa25 | 2000 | |
2d34205d | 2001 | if (options.fork_after_authentication) |
2002 | daemon(1, 1); | |
2003 | else { | |
2004 | rawmode = tty_flag; | |
2005 | if (tty_flag) { | |
2006 | enter_raw_mode( | |
2007 | options.request_tty == REQUEST_TTY_FORCE); | |
2008 | } | |
2009 | } | |
b1cbfa25 DM |
2010 | |
2011 | /* | |
2012 | * Stick around until the controlee closes the client_fd. | |
e1537f95 DM |
2013 | * Before it does, it is expected to write an exit message. |
2014 | * This process must read the value and wait for the closure of | |
2015 | * the client_fd; if this one closes early, the multiplex master will | |
2016 | * terminate early too (possibly losing data). | |
b1cbfa25 | 2017 | */ |
e1537f95 | 2018 | for (exitval = 255, exitval_seen = 0;;) { |
f4608a70 | 2019 | sshbuf_reset(m); |
2020 | if (mux_client_read_packet(fd, m) != 0) | |
b1cbfa25 | 2021 | break; |
f4608a70 | 2022 | if ((r = sshbuf_get_u32(m, &type)) != 0) |
816036f1 | 2023 | fatal_fr(r, "parse type"); |
555f3b85 DM |
2024 | switch (type) { |
2025 | case MUX_S_TTY_ALLOC_FAIL: | |
f4608a70 | 2026 | if ((r = sshbuf_get_u32(m, &esid)) != 0) |
816036f1 | 2027 | fatal_fr(r, "parse session ID"); |
f4608a70 | 2028 | if (esid != sid) |
816036f1 | 2029 | fatal_f("tty alloc fail on unknown session: " |
2030 | "my id %u theirs %u", sid, esid); | |
555f3b85 DM |
2031 | leave_raw_mode(options.request_tty == |
2032 | REQUEST_TTY_FORCE); | |
2033 | rawmode = 0; | |
2034 | continue; | |
2035 | case MUX_S_EXIT_MESSAGE: | |
f4608a70 | 2036 | if ((r = sshbuf_get_u32(m, &esid)) != 0) |
816036f1 | 2037 | fatal_fr(r, "parse session ID"); |
f4608a70 | 2038 | if (esid != sid) |
816036f1 | 2039 | fatal_f("exit on unknown session: " |
2040 | "my id %u theirs %u", sid, esid); | |
555f3b85 | 2041 | if (exitval_seen) |
816036f1 | 2042 | fatal_f("exitval sent twice"); |
f4608a70 | 2043 | if ((r = sshbuf_get_u32(m, &exitval)) != 0) |
816036f1 | 2044 | fatal_fr(r, "parse exitval"); |
555f3b85 DM |
2045 | exitval_seen = 1; |
2046 | continue; | |
2047 | default: | |
f4608a70 | 2048 | if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) |
816036f1 | 2049 | fatal_fr(r, "parse error message"); |
2050 | fatal_f("master returned error: %s", e); | |
b1cbfa25 | 2051 | } |
b1cbfa25 DM |
2052 | } |
2053 | ||
e1537f95 | 2054 | close(fd); |
555f3b85 DM |
2055 | if (rawmode) |
2056 | leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); | |
e1537f95 | 2057 | |
b1cbfa25 | 2058 | if (muxclient_terminate) { |
36945fa1 | 2059 | debug2("Exiting on signal: %s", strsignal(muxclient_terminate)); |
e1537f95 DM |
2060 | exitval = 255; |
2061 | } else if (!exitval_seen) { | |
b1cbfa25 | 2062 | debug2("Control master terminated unexpectedly"); |
e1537f95 | 2063 | exitval = 255; |
b1cbfa25 | 2064 | } else |
e1537f95 | 2065 | debug2("Received exit status from master %d", exitval); |
b1cbfa25 | 2066 | |
a882a097 | 2067 | if (tty_flag && options.log_level >= SYSLOG_LEVEL_INFO) |
b1cbfa25 DM |
2068 | fprintf(stderr, "Shared connection to %s closed.\r\n", host); |
2069 | ||
e1537f95 DM |
2070 | exit(exitval); |
2071 | } | |
2072 | ||
8d057847 | 2073 | static int |
2074 | mux_client_proxy(int fd) | |
2075 | { | |
f4608a70 | 2076 | struct sshbuf *m; |
8d057847 | 2077 | char *e; |
2078 | u_int type, rid; | |
f4608a70 | 2079 | int r; |
2080 | ||
2081 | if ((m = sshbuf_new()) == NULL) | |
816036f1 | 2082 | fatal_f("sshbuf_new"); |
f4608a70 | 2083 | if ((r = sshbuf_put_u32(m, MUX_C_PROXY)) != 0 || |
2084 | (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) | |
816036f1 | 2085 | fatal_fr(r, "request"); |
f4608a70 | 2086 | if (mux_client_write_packet(fd, m) != 0) |
816036f1 | 2087 | fatal_f("write packet: %s", strerror(errno)); |
8d057847 | 2088 | |
f4608a70 | 2089 | sshbuf_reset(m); |
8d057847 | 2090 | |
2091 | /* Read their reply */ | |
f4608a70 | 2092 | if (mux_client_read_packet(fd, m) != 0) { |
2093 | sshbuf_free(m); | |
8d057847 | 2094 | return 0; |
2095 | } | |
f4608a70 | 2096 | if ((r = sshbuf_get_u32(m, &type)) != 0 || |
2097 | (r = sshbuf_get_u32(m, &rid)) != 0) | |
816036f1 | 2098 | fatal_fr(r, "parse"); |
f4608a70 | 2099 | if (rid != muxclient_request_id) |
816036f1 | 2100 | fatal_f("out of sequence reply: my id %u theirs %u", |
2101 | muxclient_request_id, rid); | |
8d057847 | 2102 | if (type != MUX_S_PROXY) { |
f4608a70 | 2103 | if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) |
816036f1 | 2104 | fatal_fr(r, "parse error message"); |
2105 | fatal_f("master returned error: %s", e); | |
8d057847 | 2106 | } |
f4608a70 | 2107 | sshbuf_free(m); |
8d057847 | 2108 | |
816036f1 | 2109 | debug3_f("done"); |
8d057847 | 2110 | muxclient_request_id++; |
2111 | return 0; | |
2112 | } | |
2113 | ||
e1537f95 DM |
2114 | static int |
2115 | mux_client_request_stdio_fwd(int fd) | |
2116 | { | |
f4608a70 | 2117 | struct sshbuf *m; |
e1537f95 DM |
2118 | char *e; |
2119 | u_int type, rid, sid; | |
396d32f3 | 2120 | int r; |
e1537f95 | 2121 | |
816036f1 | 2122 | debug3_f("entering"); |
e1537f95 DM |
2123 | |
2124 | if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { | |
816036f1 | 2125 | error_f("master alive request failed"); |
e1537f95 DM |
2126 | return -1; |
2127 | } | |
2128 | ||
3bf2a6ac | 2129 | ssh_signal(SIGPIPE, SIG_IGN); |
e1537f95 | 2130 | |
e0c5088f | 2131 | if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1) |
816036f1 | 2132 | fatal_f("stdfd_devnull failed"); |
e1537f95 | 2133 | |
f4608a70 | 2134 | if ((m = sshbuf_new()) == NULL) |
816036f1 | 2135 | fatal_f("sshbuf_new"); |
f4608a70 | 2136 | if ((r = sshbuf_put_u32(m, MUX_C_NEW_STDIO_FWD)) != 0 || |
2137 | (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || | |
2138 | (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */ | |
2139 | (r = sshbuf_put_cstring(m, options.stdio_forward_host)) != 0 || | |
2140 | (r = sshbuf_put_u32(m, options.stdio_forward_port)) != 0) | |
816036f1 | 2141 | fatal_fr(r, "request"); |
e1537f95 | 2142 | |
f4608a70 | 2143 | if (mux_client_write_packet(fd, m) != 0) |
816036f1 | 2144 | fatal_f("write packet: %s", strerror(errno)); |
e1537f95 DM |
2145 | |
2146 | /* Send the stdio file descriptors */ | |
2147 | if (mm_send_fd(fd, STDIN_FILENO) == -1 || | |
2148 | mm_send_fd(fd, STDOUT_FILENO) == -1) | |
816036f1 | 2149 | fatal_f("send fds failed"); |
e1537f95 | 2150 | |
b91926a9 | 2151 | if (pledge("stdio proc tty", NULL) == -1) |
816036f1 | 2152 | fatal_f("pledge(): %s", strerror(errno)); |
4626cbaf | 2153 | platform_pledge_mux(); |
b91926a9 | 2154 | |
816036f1 | 2155 | debug3_f("stdio forward request sent"); |
e1537f95 DM |
2156 | |
2157 | /* Read their reply */ | |
f4608a70 | 2158 | sshbuf_reset(m); |
e1537f95 | 2159 | |
f4608a70 | 2160 | if (mux_client_read_packet(fd, m) != 0) { |
816036f1 | 2161 | error_f("read from master failed: %s", strerror(errno)); |
f4608a70 | 2162 | sshbuf_free(m); |
e1537f95 DM |
2163 | return -1; |
2164 | } | |
2165 | ||
f4608a70 | 2166 | if ((r = sshbuf_get_u32(m, &type)) != 0 || |
2167 | (r = sshbuf_get_u32(m, &rid)) != 0) | |
816036f1 | 2168 | fatal_fr(r, "parse"); |
f4608a70 | 2169 | if (rid != muxclient_request_id) |
816036f1 | 2170 | fatal_f("out of sequence reply: my id %u theirs %u", |
2171 | muxclient_request_id, rid); | |
e1537f95 DM |
2172 | switch (type) { |
2173 | case MUX_S_SESSION_OPENED: | |
f4608a70 | 2174 | if ((r = sshbuf_get_u32(m, &sid)) != 0) |
816036f1 | 2175 | fatal_fr(r, "parse session ID"); |
2176 | debug_f("master session id: %u", sid); | |
e1537f95 DM |
2177 | break; |
2178 | case MUX_S_PERMISSION_DENIED: | |
f4608a70 | 2179 | if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) |
816036f1 | 2180 | fatal_fr(r, "parse error message"); |
f4608a70 | 2181 | sshbuf_free(m); |
445c9a50 | 2182 | fatal("Master refused stdio forwarding request: %s", e); |
e1537f95 | 2183 | case MUX_S_FAILURE: |
f4608a70 | 2184 | if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) |
816036f1 | 2185 | fatal_fr(r, "parse error message"); |
f4608a70 | 2186 | sshbuf_free(m); |
357610d1 | 2187 | fatal("Stdio forwarding request failed: %s", e); |
e1537f95 | 2188 | default: |
f4608a70 | 2189 | sshbuf_free(m); |
816036f1 | 2190 | error_f("unexpected response from master 0x%08x", type); |
e1537f95 DM |
2191 | return -1; |
2192 | } | |
2193 | muxclient_request_id++; | |
2194 | ||
3bf2a6ac | 2195 | ssh_signal(SIGHUP, control_client_sighandler); |
2196 | ssh_signal(SIGINT, control_client_sighandler); | |
2197 | ssh_signal(SIGTERM, control_client_sighandler); | |
2198 | ssh_signal(SIGWINCH, control_client_sigrelay); | |
e1537f95 DM |
2199 | |
2200 | /* | |
2201 | * Stick around until the controlee closes the client_fd. | |
2202 | */ | |
f4608a70 | 2203 | sshbuf_reset(m); |
2204 | if (mux_client_read_packet(fd, m) != 0) { | |
e1537f95 DM |
2205 | if (errno == EPIPE || |
2206 | (errno == EINTR && muxclient_terminate != 0)) | |
2207 | return 0; | |
816036f1 | 2208 | fatal_f("mux_client_read_packet: %s", strerror(errno)); |
e1537f95 | 2209 | } |
816036f1 | 2210 | fatal_f("master returned unexpected message %u", type); |
e1537f95 DM |
2211 | } |
2212 | ||
6c3eec7a DM |
2213 | static void |
2214 | mux_client_request_stop_listening(int fd) | |
2215 | { | |
f4608a70 | 2216 | struct sshbuf *m; |
6c3eec7a DM |
2217 | char *e; |
2218 | u_int type, rid; | |
f4608a70 | 2219 | int r; |
6c3eec7a | 2220 | |
816036f1 | 2221 | debug3_f("entering"); |
6c3eec7a | 2222 | |
f4608a70 | 2223 | if ((m = sshbuf_new()) == NULL) |
816036f1 | 2224 | fatal_f("sshbuf_new"); |
f4608a70 | 2225 | if ((r = sshbuf_put_u32(m, MUX_C_STOP_LISTENING)) != 0 || |
2226 | (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) | |
816036f1 | 2227 | fatal_fr(r, "request"); |
6c3eec7a | 2228 | |
f4608a70 | 2229 | if (mux_client_write_packet(fd, m) != 0) |
816036f1 | 2230 | fatal_f("write packet: %s", strerror(errno)); |
6c3eec7a | 2231 | |
f4608a70 | 2232 | sshbuf_reset(m); |
6c3eec7a DM |
2233 | |
2234 | /* Read their reply */ | |
f4608a70 | 2235 | if (mux_client_read_packet(fd, m) != 0) |
816036f1 | 2236 | fatal_f("read from master failed: %s", strerror(errno)); |
6c3eec7a | 2237 | |
f4608a70 | 2238 | if ((r = sshbuf_get_u32(m, &type)) != 0 || |
2239 | (r = sshbuf_get_u32(m, &rid)) != 0) | |
816036f1 | 2240 | fatal_fr(r, "parse"); |
f4608a70 | 2241 | if (rid != muxclient_request_id) |
816036f1 | 2242 | fatal_f("out of sequence reply: my id %u theirs %u", |
2243 | muxclient_request_id, rid); | |
f4608a70 | 2244 | |
6c3eec7a DM |
2245 | switch (type) { |
2246 | case MUX_S_OK: | |
2247 | break; | |
2248 | case MUX_S_PERMISSION_DENIED: | |
f4608a70 | 2249 | if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) |
816036f1 | 2250 | fatal_fr(r, "parse error message"); |
6c3eec7a DM |
2251 | fatal("Master refused stop listening request: %s", e); |
2252 | case MUX_S_FAILURE: | |
f4608a70 | 2253 | if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) |
816036f1 | 2254 | fatal_fr(r, "parse error message"); |
2255 | fatal_f("stop listening request failed: %s", e); | |
6c3eec7a | 2256 | default: |
816036f1 | 2257 | fatal_f("unexpected response from master 0x%08x", type); |
6c3eec7a | 2258 | } |
f4608a70 | 2259 | sshbuf_free(m); |
6c3eec7a DM |
2260 | muxclient_request_id++; |
2261 | } | |
2262 | ||
e1537f95 | 2263 | /* Multiplex client main loop. */ |
8d057847 | 2264 | int |
e1537f95 DM |
2265 | muxclient(const char *path) |
2266 | { | |
2267 | struct sockaddr_un addr; | |
e535fbe2 | 2268 | int sock, timeout = options.connection_timeout, timeout_ms = -1; |
e1537f95 DM |
2269 | u_int pid; |
2270 | ||
2271 | if (muxclient_command == 0) { | |
8543ff3f | 2272 | if (options.stdio_forward_host != NULL) |
e1537f95 DM |
2273 | muxclient_command = SSHMUX_COMMAND_STDIO_FWD; |
2274 | else | |
2275 | muxclient_command = SSHMUX_COMMAND_OPEN; | |
2276 | } | |
2277 | ||
2278 | switch (options.control_master) { | |
2279 | case SSHCTL_MASTER_AUTO: | |
2280 | case SSHCTL_MASTER_AUTO_ASK: | |
1d7f9b6e | 2281 | debug("auto-mux: Trying existing master at '%s'", path); |
e1537f95 DM |
2282 | /* FALLTHROUGH */ |
2283 | case SSHCTL_MASTER_NO: | |
2284 | break; | |
2285 | default: | |
8d057847 | 2286 | return -1; |
e1537f95 DM |
2287 | } |
2288 | ||
2289 | memset(&addr, '\0', sizeof(addr)); | |
2290 | addr.sun_family = AF_UNIX; | |
e1537f95 DM |
2291 | |
2292 | if (strlcpy(addr.sun_path, path, | |
2293 | sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) | |
67dca60f | 2294 | fatal("ControlPath too long ('%s' >= %u bytes)", path, |
31d8d231 | 2295 | (unsigned int)sizeof(addr.sun_path)); |
e1537f95 | 2296 | |
4d28fa78 | 2297 | if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) |
816036f1 | 2298 | fatal_f("socket(): %s", strerror(errno)); |
e1537f95 | 2299 | |
4ba15462 | 2300 | if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { |
e1537f95 DM |
2301 | switch (muxclient_command) { |
2302 | case SSHMUX_COMMAND_OPEN: | |
2303 | case SSHMUX_COMMAND_STDIO_FWD: | |
2304 | break; | |
2305 | default: | |
2306 | fatal("Control socket connect(%.100s): %s", path, | |
2307 | strerror(errno)); | |
2308 | } | |
603134e0 DM |
2309 | if (errno == ECONNREFUSED && |
2310 | options.control_master != SSHCTL_MASTER_NO) { | |
2311 | debug("Stale control socket %.100s, unlinking", path); | |
2312 | unlink(path); | |
2313 | } else if (errno == ENOENT) { | |
e1537f95 | 2314 | debug("Control socket \"%.100s\" does not exist", path); |
603134e0 | 2315 | } else { |
e1537f95 DM |
2316 | error("Control socket connect(%.100s): %s", path, |
2317 | strerror(errno)); | |
2318 | } | |
2319 | close(sock); | |
8d057847 | 2320 | return -1; |
e1537f95 DM |
2321 | } |
2322 | set_nonblock(sock); | |
2323 | ||
e535fbe2 | 2324 | /* Timeout on initial connection only. */ |
2325 | if (timeout > 0 && timeout < INT_MAX / 1000) | |
2326 | timeout_ms = timeout * 1000; | |
2327 | ||
2328 | if (mux_client_hello_exchange(sock, timeout_ms) != 0) { | |
816036f1 | 2329 | error_f("master hello exchange failed"); |
e1537f95 | 2330 | close(sock); |
8d057847 | 2331 | return -1; |
e1537f95 DM |
2332 | } |
2333 | ||
2334 | switch (muxclient_command) { | |
2335 | case SSHMUX_COMMAND_ALIVE_CHECK: | |
2336 | if ((pid = mux_client_request_alive(sock)) == 0) | |
816036f1 | 2337 | fatal_f("master alive check failed"); |
b1d38a3c | 2338 | fprintf(stderr, "Master running (pid=%u)\r\n", pid); |
e1537f95 DM |
2339 | exit(0); |
2340 | case SSHMUX_COMMAND_TERMINATE: | |
2341 | mux_client_request_terminate(sock); | |
0b9ee623 | 2342 | if (options.log_level != SYSLOG_LEVEL_QUIET) |
2343 | fprintf(stderr, "Exit request sent.\r\n"); | |
e1537f95 | 2344 | exit(0); |
388f6fc4 | 2345 | case SSHMUX_COMMAND_FORWARD: |
f6dff7cd | 2346 | if (mux_client_forwards(sock, 0) != 0) |
816036f1 | 2347 | fatal_f("master forward request failed"); |
388f6fc4 | 2348 | exit(0); |
e1537f95 | 2349 | case SSHMUX_COMMAND_OPEN: |
f6dff7cd | 2350 | if (mux_client_forwards(sock, 0) != 0) { |
816036f1 | 2351 | error_f("master forward request failed"); |
8d057847 | 2352 | return -1; |
e1537f95 DM |
2353 | } |
2354 | mux_client_request_session(sock); | |
8d057847 | 2355 | return -1; |
e1537f95 DM |
2356 | case SSHMUX_COMMAND_STDIO_FWD: |
2357 | mux_client_request_stdio_fwd(sock); | |
2358 | exit(0); | |
6c3eec7a DM |
2359 | case SSHMUX_COMMAND_STOP: |
2360 | mux_client_request_stop_listening(sock); | |
0b9ee623 | 2361 | if (options.log_level != SYSLOG_LEVEL_QUIET) |
2362 | fprintf(stderr, "Stop listening request sent.\r\n"); | |
6c3eec7a | 2363 | exit(0); |
f6dff7cd DM |
2364 | case SSHMUX_COMMAND_CANCEL_FWD: |
2365 | if (mux_client_forwards(sock, 1) != 0) | |
816036f1 | 2366 | error_f("master cancel forward request failed"); |
f6dff7cd | 2367 | exit(0); |
8d057847 | 2368 | case SSHMUX_COMMAND_PROXY: |
2369 | mux_client_proxy(sock); | |
2370 | return (sock); | |
e1537f95 DM |
2371 | default: |
2372 | fatal("unrecognised muxclient_command %d", muxclient_command); | |
2373 | } | |
b1cbfa25 | 2374 | } |