#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
#include <limits.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <paths.h>
#include <unistd.h>
int
rcmdsh(char **ahost, int rport, const char *locuser, const char *remuser,
const char *cmd, char *rshprog)
{
static char hbuf[HOST_NAME_MAX+1];
struct addrinfo hint, *res;
int sp[2];
pid_t cpid;
char *p, pwbuf[_PW_BUF_LEN];
struct passwd pwstore, *pw = NULL;
if (rshprog == NULL)
rshprog = _PATH_RSH;
getpwnam_r(locuser, &pwstore, pwbuf, sizeof(pwbuf), &pw);
if (pw == NULL) {
(void) fprintf(stderr, "rcmdsh: unknown user: %s\n", locuser);
return(-1);
}
if (strcmp(*ahost, "localhost") != 0) {
memset(&hint, 0, sizeof(hint));
hint.ai_family = PF_UNSPEC;
hint.ai_flags = AI_CANONNAME;
if (getaddrinfo(*ahost, NULL, &hint, &res) == 0) {
if (res->ai_canonname) {
strlcpy(hbuf, res->ai_canonname, sizeof(hbuf));
*ahost = hbuf;
}
freeaddrinfo(res);
}
}
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) {
perror("rcmdsh: socketpair");
return(-1);
}
cpid = fork();
if (cpid == -1) {
perror("rcmdsh: fork failed");
return(-1);
} else if (cpid == 0) {
(void) close(sp[0]);
if (dup2(sp[1], 0) == -1 || dup2(0, 1) == -1) {
perror("rcmdsh: dup2 failed");
_exit(255);
}
cpid = fork();
if (cpid == -1) {
perror("rcmdsh: fork to lose parent failed");
_exit(255);
}
if (cpid > 0)
_exit(0);
if (setuid(pw->pw_uid)) {
(void) fprintf(stderr, "rcmdsh: setuid(%u): %s\n",
pw->pw_uid, strerror(errno));
_exit(255);
}
if (!strcmp(*ahost, "localhost") && !strcmp(locuser, remuser)) {
char *argv[4];
if (pw->pw_shell[0] == '\0')
rshprog = _PATH_BSHELL;
else
rshprog = pw->pw_shell;
p = strrchr(rshprog, '/');
argv[0] = p ? p + 1 : rshprog;
argv[1] = "-c";
argv[2] = (char *)cmd;
argv[3] = NULL;
execvp(rshprog, argv);
} else if ((p = strchr(rshprog, ' ')) == NULL) {
char *argv[6];
p = strrchr(rshprog, '/');
argv[0] = p ? p + 1 : rshprog;
argv[1] = "-l";
argv[2] = (char *)remuser;
argv[3] = *ahost;
argv[4] = (char *)cmd;
argv[5] = NULL;
execvp(rshprog, argv);
} else {
char **argv, **ap;
int n;
for (n = 7; (p = strchr(++p, ' ')) != NULL; n++)
continue;
rshprog = strdup(rshprog);
ap = argv = calloc(sizeof(char *), n);
if (rshprog == NULL || argv == NULL) {
perror("rcmdsh");
_exit(255);
}
while ((p = strsep(&rshprog, " ")) != NULL) {
if (*p == '\0')
continue;
*ap++ = p;
}
if (ap != argv)
rshprog = argv[0];
if ((p = strrchr(argv[0], '/')) != NULL)
argv[0] = p + 1;
*ap++ = "-l";
*ap++ = (char *)remuser;
*ap++ = *ahost;
*ap++ = (char *)cmd;
*ap++ = NULL;
execvp(rshprog, argv);
}
(void) fprintf(stderr, "rcmdsh: execvp %s failed: %s\n",
rshprog, strerror(errno));
_exit(255);
} else {
(void) close(sp[1]);
while (waitpid(cpid, NULL, 0) == -1 && errno == EINTR)
;
return(sp[0]);
}
}
DEF_WEAK(rcmdsh);