1286910Sdelphij/* $OpenBSD: rcmdsh.c,v 1.7 2002/03/12 00:05:44 millert Exp $ */ 285342Simp 3331722Seadler/* 494757Snectar * Copyright (c) 2001, MagniComp 594757Snectar * All rights reserved. 694757Snectar * 794757Snectar * Redistribution and use in source and binary forms, with or without 894757Snectar * modification, are permitted provided that the following conditions 994757Snectar * are met: 1094757Snectar * 1. Redistributions of source code must retain the above copyright 1194757Snectar * notice, this list of conditions and the following disclaimer. 1294757Snectar * 2. Redistributions in binary form must reproduce the above copyright 1394757Snectar * notice, this list of conditions and the following disclaimer in 1494757Snectar * the documentation and/or other materials provided with the distribution. 1594757Snectar * 3. Neither the name of the MagniComp nor the names of its contributors may 1694757Snectar * be used to endorse or promote products derived from this software 1794757Snectar * without specific prior written permission. 1894757Snectar * 1994757Snectar * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2094757Snectar * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2194757Snectar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2294757Snectar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 2394757Snectar * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2494757Snectar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 2594757Snectar * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 2694757Snectar * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2794757Snectar * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 2894757Snectar * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2994757Snectar */ 3094757Snectar 3194757Snectar/* 3286214Sru * This is an rcmd() replacement originally by 3385342Simp * Chris Siebenmann <cks@utcc.utoronto.ca>. 3485342Simp */ 3585342Simp 3686214Sru#include <sys/cdefs.h> 3786214Sru__FBSDID("$FreeBSD$"); 3885342Simp 39292521Sjilles#include "namespace.h" 4086214Sru#include <sys/types.h> 4186214Sru#include <sys/socket.h> 4286214Sru#include <sys/wait.h> 43111618Snectar#include <arpa/inet.h> 4485342Simp 4586214Sru#include <errno.h> 4686214Sru#include <netdb.h> 4786214Sru#include <paths.h> 4886214Sru#include <pwd.h> 4986214Sru#include <stdio.h> 5086214Sru#include <string.h> 5186214Sru#include <unistd.h> 52292521Sjilles#include "un-namespace.h" 5386214Sru 5485342Simp/* 5585342Simp * This is a replacement rcmd() function that uses the rsh(1) 5685342Simp * program in place of a direct rcmd(3) function call so as to 5785342Simp * avoid having to be root. Note that rport is ignored. 5885342Simp */ 5985342Simpint 60286910Sdelphijrcmdsh(char **ahost, int rport, const char *locuser, const char *remuser, 61286910Sdelphij const char *cmd, const char *rshprog) 6285342Simp{ 6394789Sume struct addrinfo hints, *res; 64286910Sdelphij int sp[2], error; 65286910Sdelphij pid_t cpid; 6685342Simp char *p; 6785342Simp struct passwd *pw; 6894789Sume char num[8]; 6994789Sume static char hbuf[NI_MAXHOST]; 7085342Simp 7185342Simp /* What rsh/shell to use. */ 7285342Simp if (rshprog == NULL) 7385342Simp rshprog = _PATH_RSH; 7485342Simp 7585342Simp /* locuser must exist on this host. */ 7685342Simp if ((pw = getpwnam(locuser)) == NULL) { 7786214Sru (void)fprintf(stderr, "rcmdsh: unknown user: %s\n", locuser); 7886214Sru return (-1); 7985342Simp } 8085342Simp 8185342Simp /* Validate remote hostname. */ 8285342Simp if (strcmp(*ahost, "localhost") != 0) { 8394789Sume memset(&hints, 0, sizeof(hints)); 8494789Sume hints.ai_flags = AI_CANONNAME; 8594789Sume hints.ai_family = PF_UNSPEC; 8694789Sume hints.ai_socktype = SOCK_STREAM; 87111618Snectar (void)snprintf(num, sizeof(num), "%u", 88111618Snectar (unsigned int)ntohs(rport)); 8994789Sume error = getaddrinfo(*ahost, num, &hints, &res); 9094789Sume if (error) { 9194789Sume fprintf(stderr, "rcmdsh: getaddrinfo: %s\n", 9294789Sume gai_strerror(error)); 9386214Sru return (-1); 9485342Simp } 9594789Sume if (res->ai_canonname) { 9694789Sume strncpy(hbuf, res->ai_canonname, sizeof(hbuf) - 1); 9794789Sume hbuf[sizeof(hbuf) - 1] = '\0'; 9894789Sume *ahost = hbuf; 9994789Sume } 10094789Sume freeaddrinfo(res); 10185342Simp } 10285342Simp 10385342Simp /* Get a socketpair we'll use for stdin and stdout. */ 104292521Sjilles if (_socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) { 10585342Simp perror("rcmdsh: socketpair"); 10686214Sru return (-1); 10785342Simp } 10885342Simp 10985342Simp cpid = fork(); 11086214Sru if (cpid == -1) { 11185342Simp perror("rcmdsh: fork failed"); 11286214Sru return (-1); 11385342Simp } else if (cpid == 0) { 11485342Simp /* 11585342Simp * Child. We use sp[1] to be stdin/stdout, and close sp[0]. 11685342Simp */ 117292521Sjilles (void)_close(sp[0]); 118292521Sjilles if (_dup2(sp[1], 0) == -1 || _dup2(0, 1) == -1) { 11985342Simp perror("rcmdsh: dup2 failed"); 12085342Simp _exit(255); 12185342Simp } 12285342Simp /* Fork again to lose parent. */ 12385342Simp cpid = fork(); 12486214Sru if (cpid == -1) { 12585342Simp perror("rcmdsh: fork to lose parent failed"); 12685342Simp _exit(255); 12785342Simp } 12885342Simp if (cpid > 0) 12985342Simp _exit(0); 13085342Simp 13185342Simp /* In grandchild here. Become local user for rshprog. */ 13286214Sru if (setuid(pw->pw_uid) == -1) { 13386214Sru (void)fprintf(stderr, "rcmdsh: setuid(%u): %s\n", 13486214Sru pw->pw_uid, strerror(errno)); 13585342Simp _exit(255); 13685342Simp } 13785342Simp 13885342Simp /* 13986214Sru * If remote host is "localhost" and local and remote users 14085342Simp * are the same, avoid running remote shell for efficiency. 14185342Simp */ 14286214Sru if (strcmp(*ahost, "localhost") == 0 && 14386214Sru strcmp(locuser, remuser) == 0) { 14485342Simp if (pw->pw_shell[0] == '\0') 14585342Simp rshprog = _PATH_BSHELL; 14685342Simp else 14785342Simp rshprog = pw->pw_shell; 14885342Simp p = strrchr(rshprog, '/'); 14986214Sru execlp(rshprog, p ? p + 1 : rshprog, "-c", cmd, 15086214Sru (char *)NULL); 15185342Simp } else { 15285342Simp p = strrchr(rshprog, '/'); 15386214Sru execlp(rshprog, p ? p + 1 : rshprog, *ahost, "-l", 15486214Sru remuser, cmd, (char *)NULL); 15585342Simp } 15686214Sru (void)fprintf(stderr, "rcmdsh: execlp %s failed: %s\n", 15786214Sru rshprog, strerror(errno)); 15885342Simp _exit(255); 15985342Simp } else { 16085342Simp /* Parent. close sp[1], return sp[0]. */ 161292521Sjilles (void)_close(sp[1]); 16285342Simp /* Reap child. */ 163292521Sjilles (void)_waitpid(cpid, NULL, 0); 16486214Sru return (sp[0]); 16585342Simp } 16685342Simp /* NOTREACHED */ 16785342Simp} 168