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