version 1.324, 2020/07/07 02:47:21 |
version 1.325, 2020/10/18 11:32:02 |
|
|
if (sshbuf_len(loginmsg) == 0) |
if (sshbuf_len(loginmsg) == 0) |
return; |
return; |
if ((r = sshbuf_put_u8(loginmsg, 0)) != 0) |
if ((r = sshbuf_put_u8(loginmsg, 0)) != 0) |
fatal("%s: buffer error: %s", __func__, ssh_err(r)); |
fatal_fr(r, "sshbuf_put_u8"); |
printf("%s", (char *)sshbuf_ptr(loginmsg)); |
printf("%s", (char *)sshbuf_ptr(loginmsg)); |
sshbuf_reset(loginmsg); |
sshbuf_reset(loginmsg); |
} |
} |
|
|
temporarily_use_uid(pw); |
temporarily_use_uid(pw); |
auth_info_file = xstrdup("/tmp/sshauth.XXXXXXXXXXXXXXX"); |
auth_info_file = xstrdup("/tmp/sshauth.XXXXXXXXXXXXXXX"); |
if ((fd = mkstemp(auth_info_file)) == -1) { |
if ((fd = mkstemp(auth_info_file)) == -1) { |
error("%s: mkstemp: %s", __func__, strerror(errno)); |
error_f("mkstemp: %s", strerror(errno)); |
goto out; |
goto out; |
} |
} |
if (atomicio(vwrite, fd, sshbuf_mutable_ptr(info), |
if (atomicio(vwrite, fd, sshbuf_mutable_ptr(info), |
sshbuf_len(info)) != sshbuf_len(info)) { |
sshbuf_len(info)) != sshbuf_len(info)) { |
error("%s: write: %s", __func__, strerror(errno)); |
error_f("write: %s", strerror(errno)); |
goto out; |
goto out; |
} |
} |
if (close(fd) != 0) { |
if (close(fd) != 0) { |
error("%s: close: %s", __func__, strerror(errno)); |
error_f("close: %s", strerror(errno)); |
goto out; |
goto out; |
} |
} |
success = 1; |
success = 1; |
|
|
tmp = cp = xstrdup(auth_opts->permitopen[i]); |
tmp = cp = xstrdup(auth_opts->permitopen[i]); |
/* This shouldn't fail as it has already been checked */ |
/* This shouldn't fail as it has already been checked */ |
if ((host = hpdelim(&cp)) == NULL) |
if ((host = hpdelim(&cp)) == NULL) |
fatal("%s: internal error: hpdelim", __func__); |
fatal_f("internal error: hpdelim"); |
host = cleanhostname(host); |
host = cleanhostname(host); |
if (cp == NULL || (port = permitopen_port(cp)) < 0) |
if (cp == NULL || (port = permitopen_port(cp)) < 0) |
fatal("%s: internal error: permitopen port", |
fatal_f("internal error: permitopen port"); |
__func__); |
|
channel_add_permission(ssh, |
channel_add_permission(ssh, |
FORWARD_USER, FORWARD_LOCAL, host, port); |
FORWARD_USER, FORWARD_LOCAL, host, port); |
free(tmp); |
free(tmp); |
|
|
tmp = cp = xstrdup(auth_opts->permitlisten[i]); |
tmp = cp = xstrdup(auth_opts->permitlisten[i]); |
/* This shouldn't fail as it has already been checked */ |
/* This shouldn't fail as it has already been checked */ |
if ((host = hpdelim(&cp)) == NULL) |
if ((host = hpdelim(&cp)) == NULL) |
fatal("%s: internal error: hpdelim", __func__); |
fatal_f("internal error: hpdelim"); |
host = cleanhostname(host); |
host = cleanhostname(host); |
if (cp == NULL || (port = permitopen_port(cp)) < 0) |
if (cp == NULL || (port = permitopen_port(cp)) < 0) |
fatal("%s: internal error: permitlisten port", |
fatal_f("internal error: permitlisten port"); |
__func__); |
|
channel_add_permission(ssh, |
channel_add_permission(ssh, |
FORWARD_USER, FORWARD_REMOTE, host, port); |
FORWARD_USER, FORWARD_REMOTE, host, port); |
free(tmp); |
free(tmp); |
|
|
|
|
/* Allocate pipes for communicating with the program. */ |
/* Allocate pipes for communicating with the program. */ |
if (pipe(pin) == -1) { |
if (pipe(pin) == -1) { |
error("%s: pipe in: %.100s", __func__, strerror(errno)); |
error_f("pipe in: %.100s", strerror(errno)); |
return -1; |
return -1; |
} |
} |
if (pipe(pout) == -1) { |
if (pipe(pout) == -1) { |
error("%s: pipe out: %.100s", __func__, strerror(errno)); |
error_f("pipe out: %.100s", strerror(errno)); |
close(pin[0]); |
close(pin[0]); |
close(pin[1]); |
close(pin[1]); |
return -1; |
return -1; |
} |
} |
if (pipe(perr) == -1) { |
if (pipe(perr) == -1) { |
error("%s: pipe err: %.100s", __func__, |
error_f("pipe err: %.100s", strerror(errno)); |
strerror(errno)); |
|
close(pin[0]); |
close(pin[0]); |
close(pin[1]); |
close(pin[1]); |
close(pout[0]); |
close(pout[0]); |
|
|
|
|
/* Uses socket pairs to communicate with the program. */ |
/* Uses socket pairs to communicate with the program. */ |
if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) { |
if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) { |
error("%s: socketpair #1: %.100s", __func__, strerror(errno)); |
error_f("socketpair #1: %.100s", strerror(errno)); |
return -1; |
return -1; |
} |
} |
if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) == -1) { |
if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) == -1) { |
error("%s: socketpair #2: %.100s", __func__, |
error_f("socketpair #2: %.100s", strerror(errno)); |
strerror(errno)); |
|
close(inout[0]); |
close(inout[0]); |
close(inout[1]); |
close(inout[1]); |
return -1; |
return -1; |
|
|
/* Fork the child. */ |
/* Fork the child. */ |
switch ((pid = fork())) { |
switch ((pid = fork())) { |
case -1: |
case -1: |
error("%s: fork: %.100s", __func__, strerror(errno)); |
error_f("fork: %.100s", strerror(errno)); |
#ifdef USE_PIPES |
#ifdef USE_PIPES |
close(pin[0]); |
close(pin[0]); |
close(pin[1]); |
close(pin[1]); |
|
|
* detect and gracefully fail out-of-fd conditions. |
* detect and gracefully fail out-of-fd conditions. |
*/ |
*/ |
if ((fdout = dup(ptyfd)) == -1) { |
if ((fdout = dup(ptyfd)) == -1) { |
error("%s: dup #1: %s", __func__, strerror(errno)); |
error_f("dup #1: %s", strerror(errno)); |
close(ttyfd); |
close(ttyfd); |
close(ptyfd); |
close(ptyfd); |
return -1; |
return -1; |
} |
} |
/* we keep a reference to the pty master */ |
/* we keep a reference to the pty master */ |
if ((ptymaster = dup(ptyfd)) == -1) { |
if ((ptymaster = dup(ptyfd)) == -1) { |
error("%s: dup #2: %s", __func__, strerror(errno)); |
error_f("dup #2: %s", strerror(errno)); |
close(ttyfd); |
close(ttyfd); |
close(ptyfd); |
close(ptyfd); |
close(fdout); |
close(fdout); |
|
|
/* Fork the child. */ |
/* Fork the child. */ |
switch ((pid = fork())) { |
switch ((pid = fork())) { |
case -1: |
case -1: |
error("%s: fork: %.100s", __func__, strerror(errno)); |
error_f("fork: %.100s", strerror(errno)); |
close(fdout); |
close(fdout); |
close(ptymaster); |
close(ptymaster); |
close(ttyfd); |
close(ttyfd); |
|
|
stat(user_rc, &st) >= 0) { |
stat(user_rc, &st) >= 0) { |
if (xasprintf(&cmd, "%s -c '%s %s'", shell, _PATH_BSHELL, |
if (xasprintf(&cmd, "%s -c '%s %s'", shell, _PATH_BSHELL, |
user_rc) == -1) |
user_rc) == -1) |
fatal("%s: xasprintf: %s", __func__, strerror(errno)); |
fatal_f("xasprintf: %s", strerror(errno)); |
if (debug_flag) |
if (debug_flag) |
fprintf(stderr, "Running %s\n", cmd); |
fprintf(stderr, "Running %s\n", cmd); |
f = popen(cmd, "w"); |
f = popen(cmd, "w"); |
|
|
s->auth_proto, s->auth_data); |
s->auth_proto, s->auth_data); |
} |
} |
if (xasprintf(&cmd, "%s -q -", options.xauth_location) == -1) |
if (xasprintf(&cmd, "%s -q -", options.xauth_location) == -1) |
fatal("%s: xasprintf: %s", __func__, strerror(errno)); |
fatal_f("xasprintf: %s", strerror(errno)); |
f = popen(cmd, "w"); |
f = popen(cmd, "w"); |
if (f) { |
if (f) { |
fprintf(f, "remove %s\n", |
fprintf(f, "remove %s\n", |
|
|
component[cp - path] = '\0'; |
component[cp - path] = '\0'; |
} |
} |
|
|
debug3("%s: checking '%s'", __func__, component); |
debug3_f("checking '%s'", component); |
|
|
if (stat(component, &st) != 0) |
if (stat(component, &st) != 0) |
fatal("%s: stat(\"%s\"): %s", __func__, |
fatal_f("stat(\"%s\"): %s", |
component, strerror(errno)); |
component, strerror(errno)); |
if (st.st_uid != 0 || (st.st_mode & 022) != 0) |
if (st.st_uid != 0 || (st.st_mode & 022) != 0) |
fatal("bad ownership or modes for chroot " |
fatal("bad ownership or modes for chroot " |
|
|
if (chroot(path) == -1) |
if (chroot(path) == -1) |
fatal("chroot(\"%s\"): %s", path, strerror(errno)); |
fatal("chroot(\"%s\"): %s", path, strerror(errno)); |
if (chdir("/") == -1) |
if (chdir("/") == -1) |
fatal("%s: chdir(/) after chroot: %s", |
fatal_f("chdir(/) after chroot: %s", strerror(errno)); |
__func__, strerror(errno)); |
|
verbose("Changed root directory to \"%s\"", path); |
verbose("Changed root directory to \"%s\"", path); |
} |
} |
|
|
|
|
void |
void |
session_unused(int id) |
session_unused(int id) |
{ |
{ |
debug3("%s: session id %d unused", __func__, id); |
debug3_f("session id %d unused", id); |
if (id >= options.max_sessions || |
if (id >= options.max_sessions || |
id >= sessions_nalloc) { |
id >= sessions_nalloc) { |
fatal("%s: insane session id %d (max %d nalloc %d)", |
fatal_f("insane session id %d (max %d nalloc %d)", |
__func__, id, options.max_sessions, sessions_nalloc); |
id, options.max_sessions, sessions_nalloc); |
} |
} |
memset(&sessions[id], 0, sizeof(*sessions)); |
memset(&sessions[id], 0, sizeof(*sessions)); |
sessions[id].self = id; |
sessions[id].self = id; |
|
|
if (sessions_first_unused == -1) { |
if (sessions_first_unused == -1) { |
if (sessions_nalloc >= options.max_sessions) |
if (sessions_nalloc >= options.max_sessions) |
return NULL; |
return NULL; |
debug2("%s: allocate (allocated %d max %d)", |
debug2_f("allocate (allocated %d max %d)", |
__func__, sessions_nalloc, options.max_sessions); |
sessions_nalloc, options.max_sessions); |
tmp = xrecallocarray(sessions, sessions_nalloc, |
tmp = xrecallocarray(sessions, sessions_nalloc, |
sessions_nalloc + 1, sizeof(*sessions)); |
sessions_nalloc + 1, sizeof(*sessions)); |
if (tmp == NULL) { |
if (tmp == NULL) { |
error("%s: cannot allocate %d sessions", |
error_f("cannot allocate %d sessions", |
__func__, sessions_nalloc + 1); |
sessions_nalloc + 1); |
return NULL; |
return NULL; |
} |
} |
sessions = tmp; |
sessions = tmp; |
|
|
|
|
if (sessions_first_unused >= sessions_nalloc || |
if (sessions_first_unused >= sessions_nalloc || |
sessions_first_unused < 0) { |
sessions_first_unused < 0) { |
fatal("%s: insane first_unused %d max %d nalloc %d", |
fatal_f("insane first_unused %d max %d nalloc %d", |
__func__, sessions_first_unused, options.max_sessions, |
sessions_first_unused, options.max_sessions, |
sessions_nalloc); |
sessions_nalloc); |
} |
} |
|
|
s = &sessions[sessions_first_unused]; |
s = &sessions[sessions_first_unused]; |
if (s->used) { |
if (s->used) |
fatal("%s: session %d already used", |
fatal_f("session %d already used", sessions_first_unused); |
__func__, sessions_first_unused); |
|
} |
|
sessions_first_unused = s->next_unused; |
sessions_first_unused = s->next_unused; |
s->used = 1; |
s->used = 1; |
s->next_unused = -1; |
s->next_unused = -1; |
|
|
|
|
if ((r = sshpkt_get_cstring(ssh, &signame, NULL)) != 0 || |
if ((r = sshpkt_get_cstring(ssh, &signame, NULL)) != 0 || |
(r = sshpkt_get_end(ssh)) != 0) { |
(r = sshpkt_get_end(ssh)) != 0) { |
error("%s: parse packet: %s", __func__, ssh_err(r)); |
error_fr(r, "parse"); |
goto out; |
goto out; |
} |
} |
if ((sig = name2sig(signame)) == -1) { |
if ((sig = name2sig(signame)) == -1) { |
error("%s: unsupported signal \"%s\"", __func__, signame); |
error_f("unsupported signal \"%s\"", signame); |
goto out; |
goto out; |
} |
} |
if (s->pid <= 0) { |
if (s->pid <= 0) { |
error("%s: no pid for session %d", __func__, s->self); |
error_f("no pid for session %d", s->self); |
goto out; |
goto out; |
} |
} |
if (s->forced || s->is_subsystem) { |
if (s->forced || s->is_subsystem) { |
error("%s: refusing to send signal %s to %s session", __func__, |
error_f("refusing to send signal %s to %s session", |
signame, s->forced ? "forced-command" : "subsystem"); |
signame, s->forced ? "forced-command" : "subsystem"); |
goto out; |
goto out; |
} |
} |
if (!use_privsep || mm_is_monitor()) { |
if (!use_privsep || mm_is_monitor()) { |
error("%s: session signalling requires privilege separation", |
error_f("session signalling requires privilege separation"); |
__func__); |
|
goto out; |
goto out; |
} |
} |
|
|
debug("%s: signal %s, killpg(%ld, %d)", __func__, signame, |
debug_f("signal %s, killpg(%ld, %d)", signame, (long)s->pid, sig); |
(long)s->pid, sig); |
|
temporarily_use_uid(s->pw); |
temporarily_use_uid(s->pw); |
r = killpg(s->pid, sig); |
r = killpg(s->pid, sig); |
restore_uid(); |
restore_uid(); |
if (r != 0) { |
if (r != 0) { |
error("%s: killpg(%ld, %d): %s", __func__, (long)s->pid, |
error_f("killpg(%ld, %d): %s", (long)s->pid, |
sig, strerror(errno)); |
sig, strerror(errno)); |
goto out; |
goto out; |
} |
} |
|
|
sshpkt_fatal(ssh, r, "%s: parse packet", __func__); |
sshpkt_fatal(ssh, r, "%s: parse packet", __func__); |
if (!auth_opts->permit_agent_forwarding_flag || |
if (!auth_opts->permit_agent_forwarding_flag || |
!options.allow_agent_forwarding) { |
!options.allow_agent_forwarding) { |
debug("%s: agent forwarding disabled", __func__); |
debug_f("agent forwarding disabled"); |
return 0; |
return 0; |
} |
} |
if (called) { |
if (called) { |
|
|
Session *s; |
Session *s; |
|
|
if ((s = session_by_channel(c->self)) == NULL) { |
if ((s = session_by_channel(c->self)) == NULL) { |
logit("%s: no session %d req %.100s", __func__, c->self, rtype); |
logit_f("no session %d req %.100s", c->self, rtype); |
return 0; |
return 0; |
} |
} |
debug("%s: session %d req %s", __func__, s->self, rtype); |
debug_f("session %d req %s", s->self, rtype); |
|
|
/* |
/* |
* a session is in LARVAL state until a shell, a command |
* a session is in LARVAL state until a shell, a command |
|
|
session_pty_cleanup2(Session *s) |
session_pty_cleanup2(Session *s) |
{ |
{ |
if (s == NULL) { |
if (s == NULL) { |
error("%s: no session", __func__); |
error_f("no session"); |
return; |
return; |
} |
} |
if (s->ttyfd == -1) |
if (s->ttyfd == -1) |
return; |
return; |
|
|
debug("%s: session %d release %s", __func__, s->self, s->tty); |
debug_f("session %d release %s", s->self, s->tty); |
|
|
/* Record that the user has logged out. */ |
/* Record that the user has logged out. */ |
if (s->pid != 0) |
if (s->pid != 0) |
|
|
Channel *c; |
Channel *c; |
|
|
if ((c = channel_by_id(ssh, id)) == NULL) { |
if ((c = channel_by_id(ssh, id)) == NULL) { |
debug("%s: x11 channel %d missing", __func__, id); |
debug_f("x11 channel %d missing", id); |
} else { |
} else { |
/* Detach X11 listener */ |
/* Detach X11 listener */ |
debug("%s: detach x11 channel %d", __func__, id); |
debug_f("detach x11 channel %d", id); |
channel_cancel_cleanup(ssh, id); |
channel_cancel_cleanup(ssh, id); |
if (c->ostate != CHAN_OUTPUT_CLOSED) |
if (c->ostate != CHAN_OUTPUT_CLOSED) |
chan_mark_dead(ssh, c); |
chan_mark_dead(ssh, c); |
|
|
Session *s; |
Session *s; |
u_int i; |
u_int i; |
|
|
debug3("%s: channel %d", __func__, id); |
debug3_f("channel %d", id); |
channel_cancel_cleanup(ssh, id); |
channel_cancel_cleanup(ssh, id); |
if ((s = session_by_x11_channel(id)) == NULL) |
if ((s = session_by_x11_channel(id)) == NULL) |
fatal("%s: no x11 channel %d", __func__, id); |
fatal_f("no x11 channel %d", id); |
for (i = 0; s->x11_chanids[i] != -1; i++) { |
for (i = 0; s->x11_chanids[i] != -1; i++) { |
debug("%s: session %d: closing channel %d", |
debug_f("session %d: closing channel %d", |
__func__, s->self, s->x11_chanids[i]); |
s->self, s->x11_chanids[i]); |
/* |
/* |
* The channel "id" is already closing, but make sure we |
* The channel "id" is already closing, but make sure we |
* close all of its siblings. |
* close all of its siblings. |
|
|
int r; |
int r; |
|
|
if ((c = channel_lookup(ssh, s->chanid)) == NULL) |
if ((c = channel_lookup(ssh, s->chanid)) == NULL) |
fatal("%s: session %d: no channel %d", |
fatal_f("session %d: no channel %d", s->self, s->chanid); |
__func__, s->self, s->chanid); |
debug_f("session %d channel %d pid %ld", |
debug("%s: session %d channel %d pid %ld", |
s->self, s->chanid, (long)s->pid); |
__func__, s->self, s->chanid, (long)s->pid); |
|
|
|
if (WIFEXITED(status)) { |
if (WIFEXITED(status)) { |
channel_request_start(ssh, s->chanid, "exit-status", 0); |
channel_request_start(ssh, s->chanid, "exit-status", 0); |
|
|
} |
} |
|
|
/* disconnect channel */ |
/* disconnect channel */ |
debug("%s: release channel %d", __func__, s->chanid); |
debug_f("release channel %d", s->chanid); |
|
|
/* |
/* |
* Adjust cleanup callback attachment to send close messages when |
* Adjust cleanup callback attachment to send close messages when |
|
|
{ |
{ |
Session *s = session_by_pid(pid); |
Session *s = session_by_pid(pid); |
if (s == NULL) { |
if (s == NULL) { |
debug("%s: no session for pid %ld", __func__, (long)pid); |
debug_f("no session for pid %ld", (long)pid); |
return; |
return; |
} |
} |
if (s->chanid != -1) |
if (s->chanid != -1) |
|
|
u_int i; |
u_int i; |
|
|
if (s == NULL) { |
if (s == NULL) { |
debug("%s: no session for id %d", __func__, id); |
debug_f("no session for id %d", id); |
return; |
return; |
} |
} |
debug("%s: channel %d child %ld", __func__, id, (long)s->pid); |
debug_f("channel %d child %ld", id, (long)s->pid); |
if (s->pid != 0) { |
if (s->pid != 0) { |
debug("%s: channel %d: has child, ttyfd %d", |
debug_f("channel %d: has child, ttyfd %d", id, s->ttyfd); |
__func__, id, s->ttyfd); |
|
/* |
/* |
* delay detach of session, but release pty, since |
* delay detach of session, but release pty, since |
* the fd's to the child are already closed |
* the fd's to the child are already closed |