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