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