xref: /freebsd/lib/libc/net/rcmdsh.c (revision a03411e84728e9b267056fd31c7d1d9d1dc1b01e)
1  /*	$OpenBSD: rcmdsh.c,v 1.7 2002/03/12 00:05:44 millert Exp $	*/
2  
3  /*-
4   * SPDX-License-Identifier: BSD-3-Clause
5   *
6   * Copyright (c) 2001, MagniComp
7   * All rights reserved.
8   *
9   * Redistribution and use in source and binary forms, with or without
10   * modification, are permitted provided that the following conditions
11   * are met:
12   * 1. Redistributions of source code must retain the above copyright
13   *    notice, this list of conditions and the following disclaimer.
14   * 2. Redistributions in binary form must reproduce the above copyright
15   *    notice, this list of conditions and the following disclaimer in
16   *    the documentation and/or other materials provided with the distribution.
17   * 3. Neither the name of the MagniComp nor the names of its contributors may
18   *    be used to endorse or promote products derived from this software
19   *    without specific prior written permission.
20   *
21   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24   * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
25   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
30   * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31   */
32  
33  /*
34   * This is an rcmd() replacement originally by
35   * Chris Siebenmann <cks@utcc.utoronto.ca>.
36   */
37  
38  #include "namespace.h"
39  #include <sys/types.h>
40  #include <sys/socket.h>
41  #include <sys/wait.h>
42  #include <arpa/inet.h>
43  
44  #include <errno.h>
45  #include <netdb.h>
46  #include <paths.h>
47  #include <pwd.h>
48  #include <stdio.h>
49  #include <string.h>
50  #include <unistd.h>
51  #include "un-namespace.h"
52  
53  /*
54   * This is a replacement rcmd() function that uses the rsh(1)
55   * program in place of a direct rcmd(3) function call so as to
56   * avoid having to be root.  Note that rport is ignored.
57   */
58  int
59  rcmdsh(char **ahost, int rport, const char *locuser, const char *remuser,
60      const char *cmd, const char *rshprog)
61  {
62  	struct addrinfo hints, *res;
63  	int sp[2], error;
64  	pid_t cpid;
65  	char *p;
66  	struct passwd *pw;
67  	char num[8];
68  	static char hbuf[NI_MAXHOST];
69  
70  	/* What rsh/shell to use. */
71  	if (rshprog == NULL)
72  		rshprog = _PATH_RSH;
73  
74  	/* locuser must exist on this host. */
75  	if ((pw = getpwnam(locuser)) == NULL) {
76  		(void)fprintf(stderr, "rcmdsh: unknown user: %s\n", locuser);
77  		return (-1);
78  	}
79  
80  	/* Validate remote hostname. */
81  	if (strcmp(*ahost, "localhost") != 0) {
82  		memset(&hints, 0, sizeof(hints));
83  		hints.ai_flags = AI_CANONNAME;
84  		hints.ai_family = PF_UNSPEC;
85  		hints.ai_socktype = SOCK_STREAM;
86  		(void)snprintf(num, sizeof(num), "%u",
87  		    (unsigned int)ntohs(rport));
88  		error = getaddrinfo(*ahost, num, &hints, &res);
89  		if (error) {
90  			fprintf(stderr, "rcmdsh: getaddrinfo: %s\n",
91  				gai_strerror(error));
92  			return (-1);
93  		}
94  		if (res->ai_canonname) {
95  			strncpy(hbuf, res->ai_canonname, sizeof(hbuf) - 1);
96  			hbuf[sizeof(hbuf) - 1] = '\0';
97  			*ahost = hbuf;
98  		}
99  		freeaddrinfo(res);
100  	}
101  
102  	/* Get a socketpair we'll use for stdin and stdout. */
103  	if (_socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) {
104  		perror("rcmdsh: socketpair");
105  		return (-1);
106  	}
107  
108  	cpid = fork();
109  	if (cpid == -1) {
110  		perror("rcmdsh: fork failed");
111  		return (-1);
112  	} else if (cpid == 0) {
113  		/*
114  		 * Child.  We use sp[1] to be stdin/stdout, and close sp[0].
115  		 */
116  		(void)_close(sp[0]);
117  		if (_dup2(sp[1], 0) == -1 || _dup2(0, 1) == -1) {
118  			perror("rcmdsh: dup2 failed");
119  			_exit(255);
120  		}
121  		/* Fork again to lose parent. */
122  		cpid = fork();
123  		if (cpid == -1) {
124  			perror("rcmdsh: fork to lose parent failed");
125  			_exit(255);
126  		}
127  		if (cpid > 0)
128  			_exit(0);
129  
130  		/* In grandchild here.  Become local user for rshprog. */
131  		if (setuid(pw->pw_uid) == -1) {
132  			(void)fprintf(stderr, "rcmdsh: setuid(%u): %s\n",
133  			    pw->pw_uid, strerror(errno));
134  			_exit(255);
135  		}
136  
137  		/*
138  		 * If remote host is "localhost" and local and remote users
139  		 * are the same, avoid running remote shell for efficiency.
140  		 */
141  		if (strcmp(*ahost, "localhost") == 0 &&
142  		    strcmp(locuser, remuser) == 0) {
143  			if (pw->pw_shell[0] == '\0')
144  				rshprog = _PATH_BSHELL;
145  			else
146  				rshprog = pw->pw_shell;
147  			p = strrchr(rshprog, '/');
148  			execlp(rshprog, p ? p + 1 : rshprog, "-c", cmd,
149  			    (char *)NULL);
150  		} else {
151  			p = strrchr(rshprog, '/');
152  			execlp(rshprog, p ? p + 1 : rshprog, *ahost, "-l",
153  			    remuser, cmd, (char *)NULL);
154  		}
155  		(void)fprintf(stderr, "rcmdsh: execlp %s failed: %s\n",
156  		    rshprog, strerror(errno));
157  		_exit(255);
158  	} else {
159  		/* Parent. close sp[1], return sp[0]. */
160  		(void)_close(sp[1]);
161  		/* Reap child. */
162  		(void)_waitpid(cpid, NULL, 0);
163  		return (sp[0]);
164  	}
165  	/* NOTREACHED */
166  }
167