18823d24bSXin LI /* $OpenBSD: rcmdsh.c,v 1.7 2002/03/12 00:05:44 millert Exp $ */ 2d4567212SWarner Losh 3*8a16b7a1SPedro F. Giffuni /*- 4*8a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 5*8a16b7a1SPedro F. Giffuni * 69368e99fSJacques Vidrine * Copyright (c) 2001, MagniComp 79368e99fSJacques Vidrine * All rights reserved. 89368e99fSJacques Vidrine * 99368e99fSJacques Vidrine * Redistribution and use in source and binary forms, with or without 109368e99fSJacques Vidrine * modification, are permitted provided that the following conditions 119368e99fSJacques Vidrine * are met: 129368e99fSJacques Vidrine * 1. Redistributions of source code must retain the above copyright 139368e99fSJacques Vidrine * notice, this list of conditions and the following disclaimer. 149368e99fSJacques Vidrine * 2. Redistributions in binary form must reproduce the above copyright 159368e99fSJacques Vidrine * notice, this list of conditions and the following disclaimer in 169368e99fSJacques Vidrine * the documentation and/or other materials provided with the distribution. 179368e99fSJacques Vidrine * 3. Neither the name of the MagniComp nor the names of its contributors may 189368e99fSJacques Vidrine * be used to endorse or promote products derived from this software 199368e99fSJacques Vidrine * without specific prior written permission. 209368e99fSJacques Vidrine * 219368e99fSJacques Vidrine * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 229368e99fSJacques Vidrine * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 239368e99fSJacques Vidrine * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 249368e99fSJacques Vidrine * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 259368e99fSJacques Vidrine * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 269368e99fSJacques Vidrine * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 279368e99fSJacques Vidrine * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 289368e99fSJacques Vidrine * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 299368e99fSJacques Vidrine * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 309368e99fSJacques Vidrine * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 319368e99fSJacques Vidrine */ 329368e99fSJacques Vidrine 339368e99fSJacques Vidrine /* 34d4567212SWarner Losh * This is an rcmd() replacement originally by 35d4567212SWarner Losh * Chris Siebenmann <cks@utcc.utoronto.ca>. 36d4567212SWarner Losh */ 37d4567212SWarner Losh 38e117e7a5SRuslan Ermilov #include <sys/cdefs.h> 39e117e7a5SRuslan Ermilov __FBSDID("$FreeBSD$"); 40d4567212SWarner Losh 4159797edfSJilles Tjoelker #include "namespace.h" 42d4567212SWarner Losh #include <sys/types.h> 43d4567212SWarner Losh #include <sys/socket.h> 44d4567212SWarner Losh #include <sys/wait.h> 452bbd7cf8SJacques Vidrine #include <arpa/inet.h> 46e117e7a5SRuslan Ermilov 47d4567212SWarner Losh #include <errno.h> 48d4567212SWarner Losh #include <netdb.h> 49e117e7a5SRuslan Ermilov #include <paths.h> 50e117e7a5SRuslan Ermilov #include <pwd.h> 51d4567212SWarner Losh #include <stdio.h> 52d4567212SWarner Losh #include <string.h> 53d4567212SWarner Losh #include <unistd.h> 5459797edfSJilles Tjoelker #include "un-namespace.h" 55d4567212SWarner Losh 56d4567212SWarner Losh /* 57d4567212SWarner Losh * This is a replacement rcmd() function that uses the rsh(1) 58d4567212SWarner Losh * program in place of a direct rcmd(3) function call so as to 59d4567212SWarner Losh * avoid having to be root. Note that rport is ignored. 60d4567212SWarner Losh */ 61d4567212SWarner Losh int 628823d24bSXin LI rcmdsh(char **ahost, int rport, const char *locuser, const char *remuser, 638823d24bSXin LI const char *cmd, const char *rshprog) 64d4567212SWarner Losh { 657b0e82a9SHajimu UMEMOTO struct addrinfo hints, *res; 668823d24bSXin LI int sp[2], error; 678823d24bSXin LI pid_t cpid; 68d4567212SWarner Losh char *p; 69d4567212SWarner Losh struct passwd *pw; 707b0e82a9SHajimu UMEMOTO char num[8]; 717b0e82a9SHajimu UMEMOTO static char hbuf[NI_MAXHOST]; 72d4567212SWarner Losh 73d4567212SWarner Losh /* What rsh/shell to use. */ 74d4567212SWarner Losh if (rshprog == NULL) 75d4567212SWarner Losh rshprog = _PATH_RSH; 76d4567212SWarner Losh 77d4567212SWarner Losh /* locuser must exist on this host. */ 78d4567212SWarner Losh if ((pw = getpwnam(locuser)) == NULL) { 79d4567212SWarner Losh (void)fprintf(stderr, "rcmdsh: unknown user: %s\n", locuser); 80d4567212SWarner Losh return (-1); 81d4567212SWarner Losh } 82d4567212SWarner Losh 83d4567212SWarner Losh /* Validate remote hostname. */ 84d4567212SWarner Losh if (strcmp(*ahost, "localhost") != 0) { 857b0e82a9SHajimu UMEMOTO memset(&hints, 0, sizeof(hints)); 867b0e82a9SHajimu UMEMOTO hints.ai_flags = AI_CANONNAME; 877b0e82a9SHajimu UMEMOTO hints.ai_family = PF_UNSPEC; 887b0e82a9SHajimu UMEMOTO hints.ai_socktype = SOCK_STREAM; 892bbd7cf8SJacques Vidrine (void)snprintf(num, sizeof(num), "%u", 902bbd7cf8SJacques Vidrine (unsigned int)ntohs(rport)); 917b0e82a9SHajimu UMEMOTO error = getaddrinfo(*ahost, num, &hints, &res); 927b0e82a9SHajimu UMEMOTO if (error) { 937b0e82a9SHajimu UMEMOTO fprintf(stderr, "rcmdsh: getaddrinfo: %s\n", 947b0e82a9SHajimu UMEMOTO gai_strerror(error)); 95d4567212SWarner Losh return (-1); 96d4567212SWarner Losh } 977b0e82a9SHajimu UMEMOTO if (res->ai_canonname) { 987b0e82a9SHajimu UMEMOTO strncpy(hbuf, res->ai_canonname, sizeof(hbuf) - 1); 997b0e82a9SHajimu UMEMOTO hbuf[sizeof(hbuf) - 1] = '\0'; 1007b0e82a9SHajimu UMEMOTO *ahost = hbuf; 1017b0e82a9SHajimu UMEMOTO } 1027b0e82a9SHajimu UMEMOTO freeaddrinfo(res); 103d4567212SWarner Losh } 104d4567212SWarner Losh 105d4567212SWarner Losh /* Get a socketpair we'll use for stdin and stdout. */ 10659797edfSJilles Tjoelker if (_socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) { 107d4567212SWarner Losh perror("rcmdsh: socketpair"); 108d4567212SWarner Losh return (-1); 109d4567212SWarner Losh } 110d4567212SWarner Losh 111d4567212SWarner Losh cpid = fork(); 112e117e7a5SRuslan Ermilov if (cpid == -1) { 113d4567212SWarner Losh perror("rcmdsh: fork failed"); 114d4567212SWarner Losh return (-1); 115d4567212SWarner Losh } else if (cpid == 0) { 116d4567212SWarner Losh /* 117d4567212SWarner Losh * Child. We use sp[1] to be stdin/stdout, and close sp[0]. 118d4567212SWarner Losh */ 11959797edfSJilles Tjoelker (void)_close(sp[0]); 12059797edfSJilles Tjoelker if (_dup2(sp[1], 0) == -1 || _dup2(0, 1) == -1) { 121d4567212SWarner Losh perror("rcmdsh: dup2 failed"); 122d4567212SWarner Losh _exit(255); 123d4567212SWarner Losh } 124d4567212SWarner Losh /* Fork again to lose parent. */ 125d4567212SWarner Losh cpid = fork(); 126e117e7a5SRuslan Ermilov if (cpid == -1) { 127d4567212SWarner Losh perror("rcmdsh: fork to lose parent failed"); 128d4567212SWarner Losh _exit(255); 129d4567212SWarner Losh } 130d4567212SWarner Losh if (cpid > 0) 131d4567212SWarner Losh _exit(0); 132d4567212SWarner Losh 133d4567212SWarner Losh /* In grandchild here. Become local user for rshprog. */ 134e117e7a5SRuslan Ermilov if (setuid(pw->pw_uid) == -1) { 135d4567212SWarner Losh (void)fprintf(stderr, "rcmdsh: setuid(%u): %s\n", 136d4567212SWarner Losh pw->pw_uid, strerror(errno)); 137d4567212SWarner Losh _exit(255); 138d4567212SWarner Losh } 139d4567212SWarner Losh 140d4567212SWarner Losh /* 141e117e7a5SRuslan Ermilov * If remote host is "localhost" and local and remote users 142d4567212SWarner Losh * are the same, avoid running remote shell for efficiency. 143d4567212SWarner Losh */ 144e117e7a5SRuslan Ermilov if (strcmp(*ahost, "localhost") == 0 && 145e117e7a5SRuslan Ermilov strcmp(locuser, remuser) == 0) { 146d4567212SWarner Losh if (pw->pw_shell[0] == '\0') 147d4567212SWarner Losh rshprog = _PATH_BSHELL; 148d4567212SWarner Losh else 149d4567212SWarner Losh rshprog = pw->pw_shell; 150d4567212SWarner Losh p = strrchr(rshprog, '/'); 151d4567212SWarner Losh execlp(rshprog, p ? p + 1 : rshprog, "-c", cmd, 152d4567212SWarner Losh (char *)NULL); 153d4567212SWarner Losh } else { 154d4567212SWarner Losh p = strrchr(rshprog, '/'); 155d4567212SWarner Losh execlp(rshprog, p ? p + 1 : rshprog, *ahost, "-l", 156d4567212SWarner Losh remuser, cmd, (char *)NULL); 157d4567212SWarner Losh } 158d4567212SWarner Losh (void)fprintf(stderr, "rcmdsh: execlp %s failed: %s\n", 159d4567212SWarner Losh rshprog, strerror(errno)); 160d4567212SWarner Losh _exit(255); 161d4567212SWarner Losh } else { 162d4567212SWarner Losh /* Parent. close sp[1], return sp[0]. */ 16359797edfSJilles Tjoelker (void)_close(sp[1]); 164d4567212SWarner Losh /* Reap child. */ 16559797edfSJilles Tjoelker (void)_waitpid(cpid, NULL, 0); 166d4567212SWarner Losh return (sp[0]); 167d4567212SWarner Losh } 168d4567212SWarner Losh /* NOTREACHED */ 169d4567212SWarner Losh } 170