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