1 /* $OpenBSD: rcmdsh.c,v 1.6 2002/01/02 20:18:32 deraadt 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.6 2002/01/02 20:18:32 deraadt 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 sp[2]; 39 pid_t cpid; 40 char *p; 41 struct passwd *pw; 42 43 /* What rsh/shell to use. */ 44 if (rshprog == NULL) 45 rshprog = _PATH_RSH; 46 47 /* locuser must exist on this host. */ 48 if ((pw = getpwnam(locuser)) == NULL) { 49 (void) fprintf(stderr, "rcmdsh: unknown user: %s\n", locuser); 50 return(-1); 51 } 52 53 /* Validate remote hostname. */ 54 if (strcmp(*ahost, "localhost") != 0) { 55 if ((hp = gethostbyname(*ahost)) == NULL) { 56 herror(*ahost); 57 return(-1); 58 } 59 *ahost = hp->h_name; 60 } 61 62 /* Get a socketpair we'll use for stdin and stdout. */ 63 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) < 0) { 64 perror("rcmdsh: socketpair"); 65 return(-1); 66 } 67 68 cpid = fork(); 69 if (cpid < 0) { 70 perror("rcmdsh: fork failed"); 71 return(-1); 72 } else if (cpid == 0) { 73 /* 74 * Child. We use sp[1] to be stdin/stdout, and close sp[0]. 75 */ 76 (void) close(sp[0]); 77 if (dup2(sp[1], 0) < 0 || dup2(0, 1) < 0) { 78 perror("rcmdsh: dup2 failed"); 79 _exit(255); 80 } 81 /* Fork again to lose parent. */ 82 cpid = fork(); 83 if (cpid < 0) { 84 perror("rcmdsh: fork to lose parent failed"); 85 _exit(255); 86 } 87 if (cpid > 0) 88 _exit(0); 89 90 /* In grandchild here. Become local user for rshprog. */ 91 if (setuid(pw->pw_uid)) { 92 (void) fprintf(stderr, "rcmdsh: setuid(%u): %s\n", 93 pw->pw_uid, strerror(errno)); 94 _exit(255); 95 } 96 97 /* 98 * If remote host is "localhost" and local and remote user 99 * are the same, avoid running remote shell for efficiency. 100 */ 101 if (!strcmp(*ahost, "localhost") && !strcmp(locuser, remuser)) { 102 if (pw->pw_shell[0] == '\0') 103 rshprog = _PATH_BSHELL; 104 else 105 rshprog = pw->pw_shell; 106 p = strrchr(rshprog, '/'); 107 execlp(rshprog, p ? p+1 : rshprog, "-c", cmd, 108 (char *) NULL); 109 } else { 110 p = strrchr(rshprog, '/'); 111 execlp(rshprog, p ? p+1 : rshprog, *ahost, "-l", 112 remuser, cmd, (char *) NULL); 113 } 114 (void) fprintf(stderr, "rcmdsh: execlp %s failed: %s\n", 115 rshprog, strerror(errno)); 116 _exit(255); 117 } else { 118 /* Parent. close sp[1], return sp[0]. */ 119 (void) close(sp[1]); 120 /* Reap child. */ 121 (void) wait(NULL); 122 return(sp[0]); 123 } 124 /* NOTREACHED */ 125 } 126