1d4567212SWarner Losh /* $OpenBSD: rcmdsh.c,v 1.5 1998/04/25 16:23:58 millert Exp $ */ 2d4567212SWarner Losh 3d4567212SWarner Losh /* 4d4567212SWarner Losh * This is an rcmd() replacement originally by 5d4567212SWarner Losh * Chris Siebenmann <cks@utcc.utoronto.ca>. 6d4567212SWarner Losh */ 7d4567212SWarner Losh 8e117e7a5SRuslan Ermilov #include <sys/cdefs.h> 9e117e7a5SRuslan Ermilov __FBSDID("$FreeBSD$"); 10d4567212SWarner Losh 11d4567212SWarner Losh #include <sys/types.h> 12d4567212SWarner Losh #include <sys/socket.h> 13d4567212SWarner Losh #include <sys/wait.h> 14e117e7a5SRuslan Ermilov 15d4567212SWarner Losh #include <errno.h> 16d4567212SWarner Losh #include <netdb.h> 17e117e7a5SRuslan Ermilov #include <paths.h> 18e117e7a5SRuslan Ermilov #include <pwd.h> 19d4567212SWarner Losh #include <stdio.h> 20d4567212SWarner Losh #include <string.h> 21d4567212SWarner Losh #include <unistd.h> 22d4567212SWarner Losh 23d4567212SWarner Losh #ifndef _PATH_RSH 24d4567212SWarner Losh #define _PATH_RSH "/usr/bin/rsh" 25d4567212SWarner Losh #endif 26d4567212SWarner Losh 27d4567212SWarner Losh /* 28d4567212SWarner Losh * This is a replacement rcmd() function that uses the rsh(1) 29d4567212SWarner Losh * program in place of a direct rcmd(3) function call so as to 30d4567212SWarner Losh * avoid having to be root. Note that rport is ignored. 31d4567212SWarner Losh */ 32d4567212SWarner Losh /* ARGSUSED */ 33d4567212SWarner Losh int 34d4567212SWarner Losh rcmdsh(ahost, rport, locuser, remuser, cmd, rshprog) 35d4567212SWarner Losh char **ahost; 36e117e7a5SRuslan Ermilov int rport __unused; 37e117e7a5SRuslan Ermilov const char *locuser, *remuser, *cmd, *rshprog; 38d4567212SWarner Losh { 39d4567212SWarner Losh struct hostent *hp; 40d4567212SWarner Losh int cpid, sp[2]; 41d4567212SWarner Losh char *p; 42d4567212SWarner Losh struct passwd *pw; 43d4567212SWarner Losh 44d4567212SWarner Losh /* What rsh/shell to use. */ 45d4567212SWarner Losh if (rshprog == NULL) 46d4567212SWarner Losh rshprog = _PATH_RSH; 47d4567212SWarner Losh 48d4567212SWarner Losh /* locuser must exist on this host. */ 49d4567212SWarner Losh if ((pw = getpwnam(locuser)) == NULL) { 50d4567212SWarner Losh (void)fprintf(stderr, "rcmdsh: unknown user: %s\n", locuser); 51d4567212SWarner Losh return (-1); 52d4567212SWarner Losh } 53d4567212SWarner Losh 54d4567212SWarner Losh /* Validate remote hostname. */ 55d4567212SWarner Losh if (strcmp(*ahost, "localhost") != 0) { 56d4567212SWarner Losh if ((hp = gethostbyname(*ahost)) == NULL) { 57d4567212SWarner Losh herror(*ahost); 58d4567212SWarner Losh return (-1); 59d4567212SWarner Losh } 60d4567212SWarner Losh *ahost = hp->h_name; 61d4567212SWarner Losh } 62d4567212SWarner Losh 63d4567212SWarner Losh /* Get a socketpair we'll use for stdin and stdout. */ 64e117e7a5SRuslan Ermilov if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) { 65d4567212SWarner Losh perror("rcmdsh: socketpair"); 66d4567212SWarner Losh return (-1); 67d4567212SWarner Losh } 68d4567212SWarner Losh 69d4567212SWarner Losh cpid = fork(); 70e117e7a5SRuslan Ermilov if (cpid == -1) { 71d4567212SWarner Losh perror("rcmdsh: fork failed"); 72d4567212SWarner Losh return (-1); 73d4567212SWarner Losh } else if (cpid == 0) { 74d4567212SWarner Losh /* 75d4567212SWarner Losh * Child. We use sp[1] to be stdin/stdout, and close sp[0]. 76d4567212SWarner Losh */ 77d4567212SWarner Losh (void)close(sp[0]); 78e117e7a5SRuslan Ermilov if (dup2(sp[1], 0) == -1 || dup2(0, 1) == -1) { 79d4567212SWarner Losh perror("rcmdsh: dup2 failed"); 80d4567212SWarner Losh _exit(255); 81d4567212SWarner Losh } 82d4567212SWarner Losh /* Fork again to lose parent. */ 83d4567212SWarner Losh cpid = fork(); 84e117e7a5SRuslan Ermilov if (cpid == -1) { 85d4567212SWarner Losh perror("rcmdsh: fork to lose parent failed"); 86d4567212SWarner Losh _exit(255); 87d4567212SWarner Losh } 88d4567212SWarner Losh if (cpid > 0) 89d4567212SWarner Losh _exit(0); 90d4567212SWarner Losh 91d4567212SWarner Losh /* In grandchild here. Become local user for rshprog. */ 92e117e7a5SRuslan Ermilov if (setuid(pw->pw_uid) == -1) { 93d4567212SWarner Losh (void)fprintf(stderr, "rcmdsh: setuid(%u): %s\n", 94d4567212SWarner Losh pw->pw_uid, strerror(errno)); 95d4567212SWarner Losh _exit(255); 96d4567212SWarner Losh } 97d4567212SWarner Losh 98d4567212SWarner Losh /* 99e117e7a5SRuslan Ermilov * If remote host is "localhost" and local and remote users 100d4567212SWarner Losh * are the same, avoid running remote shell for efficiency. 101d4567212SWarner Losh */ 102e117e7a5SRuslan Ermilov if (strcmp(*ahost, "localhost") == 0 && 103e117e7a5SRuslan Ermilov strcmp(locuser, remuser) == 0) { 104d4567212SWarner Losh if (pw->pw_shell[0] == '\0') 105d4567212SWarner Losh rshprog = _PATH_BSHELL; 106d4567212SWarner Losh else 107d4567212SWarner Losh rshprog = pw->pw_shell; 108d4567212SWarner Losh p = strrchr(rshprog, '/'); 109d4567212SWarner Losh execlp(rshprog, p ? p + 1 : rshprog, "-c", cmd, 110d4567212SWarner Losh (char *)NULL); 111d4567212SWarner Losh } else { 112d4567212SWarner Losh p = strrchr(rshprog, '/'); 113d4567212SWarner Losh execlp(rshprog, p ? p + 1 : rshprog, *ahost, "-l", 114d4567212SWarner Losh remuser, cmd, (char *)NULL); 115d4567212SWarner Losh } 116d4567212SWarner Losh (void)fprintf(stderr, "rcmdsh: execlp %s failed: %s\n", 117d4567212SWarner Losh rshprog, strerror(errno)); 118d4567212SWarner Losh _exit(255); 119d4567212SWarner Losh } else { 120d4567212SWarner Losh /* Parent. close sp[1], return sp[0]. */ 121d4567212SWarner Losh (void)close(sp[1]); 122d4567212SWarner Losh /* Reap child. */ 123d4567212SWarner Losh (void)wait(NULL); 124d4567212SWarner Losh return (sp[0]); 125d4567212SWarner Losh } 126d4567212SWarner Losh /* NOTREACHED */ 127d4567212SWarner Losh } 128