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