mirror of
https://github.com/clearml/dropbear
synced 2025-06-26 18:17:32 +00:00
- Generalise spawn_command function
--HG-- extra : convert_revision : 9927a5fe084c8053c747a40515f0213141ef8139
This commit is contained in:
parent
d7c1e58217
commit
7ac24b10b9
77
dbutil.c
77
dbutil.c
@ -389,6 +389,83 @@ int connect_remote(const char* remotehost, const char* remoteport,
|
|||||||
return sock;
|
return sock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Sets up a pipe for a, returning three non-blocking file descriptors
|
||||||
|
* and the pid. exec_fn is the function that will actually execute the child process,
|
||||||
|
* it will be run after the child has fork()ed, and is passed exec_data. */
|
||||||
|
int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
|
||||||
|
int *ret_writefd, int *ret_readfd, int *ret_errfd, pid_t *ret_pid)
|
||||||
|
{
|
||||||
|
int infds[2];
|
||||||
|
int outfds[2];
|
||||||
|
int errfds[2];
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
const int FDIN = 0;
|
||||||
|
const int FDOUT = 1;
|
||||||
|
|
||||||
|
/* redirect stdin/stdout/stderr */
|
||||||
|
if (pipe(infds) != 0)
|
||||||
|
return DROPBEAR_FAILURE;
|
||||||
|
if (pipe(outfds) != 0)
|
||||||
|
return DROPBEAR_FAILURE;
|
||||||
|
if (pipe(errfds) != 0)
|
||||||
|
return DROPBEAR_FAILURE;
|
||||||
|
|
||||||
|
#ifdef __uClinux__
|
||||||
|
pid = vfork();
|
||||||
|
#else
|
||||||
|
pid = fork();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (pid < 0)
|
||||||
|
return DROPBEAR_FAILURE;
|
||||||
|
|
||||||
|
if (!pid) {
|
||||||
|
/* child */
|
||||||
|
|
||||||
|
TRACE(("back to normal sigchld"))
|
||||||
|
/* Revert to normal sigchld handling */
|
||||||
|
if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
|
||||||
|
dropbear_exit("signal() error");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* redirect stdin/stdout */
|
||||||
|
|
||||||
|
if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
|
||||||
|
(dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
|
||||||
|
(dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
|
||||||
|
TRACE(("leave noptycommand: error redirecting FDs"))
|
||||||
|
dropbear_exit("child dup2() failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
close(infds[FDOUT]);
|
||||||
|
close(infds[FDIN]);
|
||||||
|
close(outfds[FDIN]);
|
||||||
|
close(outfds[FDOUT]);
|
||||||
|
close(errfds[FDIN]);
|
||||||
|
close(errfds[FDOUT]);
|
||||||
|
|
||||||
|
exec_fn(exec_data);
|
||||||
|
/* not reached */
|
||||||
|
return DROPBEAR_FAILURE;
|
||||||
|
} else {
|
||||||
|
/* parent */
|
||||||
|
close(infds[FDIN]);
|
||||||
|
close(outfds[FDOUT]);
|
||||||
|
close(errfds[FDOUT]);
|
||||||
|
|
||||||
|
setnonblocking(errfds[FDIN]);
|
||||||
|
setnonblocking(outfds[FDIN]);
|
||||||
|
setnonblocking(infds[FDOUT]);
|
||||||
|
|
||||||
|
*ret_pid = pid;
|
||||||
|
*ret_writefd = infds[FDOUT];
|
||||||
|
*ret_readfd = outfds[FDIN];
|
||||||
|
*ret_errfd = errfds[FDIN];
|
||||||
|
return DROPBEAR_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Return a string representation of the socket address passed. The return
|
/* Return a string representation of the socket address passed. The return
|
||||||
* value is allocated with malloc() */
|
* value is allocated with malloc() */
|
||||||
unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) {
|
unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) {
|
||||||
|
2
dbutil.h
2
dbutil.h
@ -49,6 +49,8 @@ char * stripcontrol(const char * text);
|
|||||||
unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport);
|
unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport);
|
||||||
int dropbear_listen(const char* address, const char* port,
|
int dropbear_listen(const char* address, const char* port,
|
||||||
int *socks, unsigned int sockcount, char **errstring, int *maxfd);
|
int *socks, unsigned int sockcount, char **errstring, int *maxfd);
|
||||||
|
int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
|
||||||
|
int *writefd, int *readfd, int *errfd, pid_t *pid);
|
||||||
int connect_remote(const char* remotehost, const char* remoteport,
|
int connect_remote(const char* remotehost, const char* remoteport,
|
||||||
int nonblocking, char ** errstring);
|
int nonblocking, char ** errstring);
|
||||||
char* getaddrhostname(struct sockaddr_storage * addr);
|
char* getaddrhostname(struct sockaddr_storage * addr);
|
||||||
|
@ -47,7 +47,7 @@ static int sessionsignal(struct ChanSess *chansess);
|
|||||||
static int noptycommand(struct Channel *channel, struct ChanSess *chansess);
|
static int noptycommand(struct Channel *channel, struct ChanSess *chansess);
|
||||||
static int ptycommand(struct Channel *channel, struct ChanSess *chansess);
|
static int ptycommand(struct Channel *channel, struct ChanSess *chansess);
|
||||||
static int sessionwinchange(struct ChanSess *chansess);
|
static int sessionwinchange(struct ChanSess *chansess);
|
||||||
static void execchild(struct ChanSess *chansess);
|
static void execchild(void *user_data_chansess);
|
||||||
static void addchildpid(struct ChanSess *chansess, pid_t pid);
|
static void addchildpid(struct ChanSess *chansess, pid_t pid);
|
||||||
static void sesssigchild_handler(int val);
|
static void sesssigchild_handler(int val);
|
||||||
static void closechansess(struct Channel *channel);
|
static void closechansess(struct Channel *channel);
|
||||||
@ -633,102 +633,37 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
|
|||||||
* pty.
|
* pty.
|
||||||
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
||||||
static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
|
static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
|
||||||
|
int ret;
|
||||||
int infds[2];
|
|
||||||
int outfds[2];
|
|
||||||
int errfds[2];
|
|
||||||
pid_t pid;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
const int FDIN = 0;
|
|
||||||
const int FDOUT = 1;
|
|
||||||
|
|
||||||
TRACE(("enter noptycommand"))
|
TRACE(("enter noptycommand"))
|
||||||
|
ret = spawn_command(execchild, chansess,
|
||||||
|
&channel->writefd, &channel->readfd, &channel->errfd,
|
||||||
|
&chansess->pid);
|
||||||
|
|
||||||
/* redirect stdin/stdout/stderr */
|
if (ret == DROPBEAR_FAILURE) {
|
||||||
if (pipe(infds) != 0)
|
return ret;
|
||||||
return DROPBEAR_FAILURE;
|
}
|
||||||
if (pipe(outfds) != 0)
|
|
||||||
return DROPBEAR_FAILURE;
|
|
||||||
if (pipe(errfds) != 0)
|
|
||||||
return DROPBEAR_FAILURE;
|
|
||||||
|
|
||||||
#ifdef __uClinux__
|
ses.maxfd = MAX(ses.maxfd, channel->writefd);
|
||||||
pid = vfork();
|
ses.maxfd = MAX(ses.maxfd, channel->readfd);
|
||||||
#else
|
ses.maxfd = MAX(ses.maxfd, channel->errfd);
|
||||||
pid = fork();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (pid < 0)
|
addchildpid(chansess, chansess->pid);
|
||||||
return DROPBEAR_FAILURE;
|
|
||||||
|
|
||||||
if (!pid) {
|
if (svr_ses.lastexit.exitpid != -1) {
|
||||||
/* child */
|
TRACE(("parent side: lastexitpid is %d", svr_ses.lastexit.exitpid))
|
||||||
|
/* The child probably exited and the signal handler triggered
|
||||||
TRACE(("back to normal sigchld"))
|
* possibly before we got around to adding the childpid. So we fill
|
||||||
/* Revert to normal sigchld handling */
|
* out its data manually */
|
||||||
if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
|
int i;
|
||||||
dropbear_exit("signal() error");
|
for (i = 0; i < svr_ses.childpidsize; i++) {
|
||||||
}
|
if (svr_ses.childpids[i].pid == svr_ses.lastexit.exitpid) {
|
||||||
|
TRACE(("found match for lastexitpid"))
|
||||||
/* redirect stdin/stdout */
|
svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
|
||||||
|
svr_ses.lastexit.exitpid = -1;
|
||||||
if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
|
|
||||||
(dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
|
|
||||||
(dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
|
|
||||||
TRACE(("leave noptycommand: error redirecting FDs"))
|
|
||||||
return DROPBEAR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(infds[FDOUT]);
|
|
||||||
close(infds[FDIN]);
|
|
||||||
close(outfds[FDIN]);
|
|
||||||
close(outfds[FDOUT]);
|
|
||||||
close(errfds[FDIN]);
|
|
||||||
close(errfds[FDOUT]);
|
|
||||||
|
|
||||||
execchild(chansess);
|
|
||||||
/* not reached */
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* parent */
|
|
||||||
TRACE(("continue noptycommand: parent"))
|
|
||||||
chansess->pid = pid;
|
|
||||||
TRACE(("child pid is %d", pid))
|
|
||||||
|
|
||||||
addchildpid(chansess, pid);
|
|
||||||
|
|
||||||
if (svr_ses.lastexit.exitpid != -1) {
|
|
||||||
TRACE(("parent side: lastexitpid is %d", svr_ses.lastexit.exitpid))
|
|
||||||
/* The child probably exited and the signal handler triggered
|
|
||||||
* possibly before we got around to adding the childpid. So we fill
|
|
||||||
* out its data manually */
|
|
||||||
for (i = 0; i < svr_ses.childpidsize; i++) {
|
|
||||||
if (svr_ses.childpids[i].pid == svr_ses.lastexit.exitpid) {
|
|
||||||
TRACE(("found match for lastexitpid"))
|
|
||||||
svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
|
|
||||||
svr_ses.lastexit.exitpid = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close(infds[FDIN]);
|
|
||||||
close(outfds[FDOUT]);
|
|
||||||
close(errfds[FDOUT]);
|
|
||||||
channel->writefd = infds[FDOUT];
|
|
||||||
channel->readfd = outfds[FDIN];
|
|
||||||
channel->errfd = errfds[FDIN];
|
|
||||||
ses.maxfd = MAX(ses.maxfd, channel->writefd);
|
|
||||||
ses.maxfd = MAX(ses.maxfd, channel->readfd);
|
|
||||||
ses.maxfd = MAX(ses.maxfd, channel->errfd);
|
|
||||||
|
|
||||||
setnonblocking(channel->readfd);
|
|
||||||
setnonblocking(channel->writefd);
|
|
||||||
setnonblocking(channel->errfd);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
#undef FDIN
|
|
||||||
#undef FDOUT
|
|
||||||
|
|
||||||
TRACE(("leave noptycommand"))
|
TRACE(("leave noptycommand"))
|
||||||
return DROPBEAR_SUCCESS;
|
return DROPBEAR_SUCCESS;
|
||||||
@ -873,12 +808,13 @@ static void addchildpid(struct ChanSess *chansess, pid_t pid) {
|
|||||||
|
|
||||||
/* Clean up, drop to user privileges, set up the environment and execute
|
/* Clean up, drop to user privileges, set up the environment and execute
|
||||||
* the command/shell. This function does not return. */
|
* the command/shell. This function does not return. */
|
||||||
static void execchild(struct ChanSess *chansess) {
|
static void execchild(void *user_data) {
|
||||||
|
|
||||||
char *argv[4];
|
char *argv[4];
|
||||||
char * usershell = NULL;
|
char * usershell = NULL;
|
||||||
char * baseshell = NULL;
|
char * baseshell = NULL;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
struct ChanSess *chansess = user_data;
|
||||||
|
|
||||||
/* with uClinux we'll have vfork()ed, so don't want to overwrite the
|
/* with uClinux we'll have vfork()ed, so don't want to overwrite the
|
||||||
* hostkey. can't think of a workaround to clear it */
|
* hostkey. can't think of a workaround to clear it */
|
||||||
|
Loading…
Reference in New Issue
Block a user