From d45672120f0aeab46254d514727c0cec784a1826 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Tue, 23 Oct 2001 06:22:15 +0000 Subject: [PATCH] Allow users to specify a command to use as remote command instead of using rcmd directly. This has been in my tree for a long time, but we may need to sync with OpenBSD before MFC. Obtained from: openbsd PR: 15830 MFC after: 2 months --- lib/libc/net/Makefile.inc | 4 +- lib/libc/net/rcmd.c | 24 +++++-- lib/libc/net/rcmdsh.3 | 105 ++++++++++++++++++++++++++++++ lib/libc/net/rcmdsh.c | 130 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 257 insertions(+), 6 deletions(-) create mode 100644 lib/libc/net/rcmdsh.3 create mode 100644 lib/libc/net/rcmdsh.c diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc index 343755d2c3a0..7db1b069bfa9 100644 --- a/lib/libc/net/Makefile.inc +++ b/lib/libc/net/Makefile.inc @@ -17,7 +17,7 @@ SRCS+= addr2ascii.c ascii2addr.c base64.c ether_addr.c getaddrinfo.c \ ns_name.c ns_netint.c \ ns_ntoa.c ns_parse.c ns_print.c ns_ttl.c \ nsdispatch.c nslexer.c nsparser.c \ - nsap_addr.c rcmd.c recv.c res_comp.c res_data.c res_debug.c \ + nsap_addr.c rcmd.c rcmdsh.c recv.c res_comp.c res_data.c res_debug.c \ res_init.c res_mkquery.c res_mkupdate.c res_query.c res_send.c \ res_update.c rthdr.c send.c vars.c # not supported: iso_addr.c @@ -48,7 +48,7 @@ MAN+= addr2ascii.3 byteorder.3 ethers.3 getaddrinfo.3 gethostbyname.3 \ if_indextoname.3 \ inet.3 inet_net.3 \ inet6_option_space.3 inet6_rthdr_space.3 linkaddr.3 \ - nsdispatch.3 rcmd.3 resolver.3 + nsdispatch.3 rcmd.3 rcmdsh.3 resolver.3 # not installed: iso_addr.3 ns.3 MLINKS+=addr2ascii.3 ascii2addr.3 diff --git a/lib/libc/net/rcmd.c b/lib/libc/net/rcmd.c index 3fad8d6b9b90..68ccb7176ce8 100644 --- a/lib/libc/net/rcmd.c +++ b/lib/libc/net/rcmd.c @@ -48,6 +48,7 @@ static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; #include #include #include +#include #include #include #include @@ -105,11 +106,29 @@ rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af) sigset_t oldmask, newmask; pid_t pid; int s, aport, lport, timo, error; - char c; + char c, *p; int refused, nres; char num[8]; static char canonnamebuf[MAXDNAME]; /* is it proper here? */ + /* call rcmdsh() with specified remote shell if appropriate. */ + if (!issetugid() && (p = getenv("RSH"))) { + struct servent *sp = getservbyname("shell", "tcp"); + + if (sp && sp->s_port == rport) + return (rcmdsh(ahost, rport, locuser, remuser, + cmd, p)); + } + + /* use rsh(1) if non-root and remote port is shell. */ + if (geteuid()) { + struct servent *sp = getservbyname("shell", "tcp"); + + if (sp && sp->s_port == rport) + return (rcmdsh(ahost, rport, locuser, remuser, + cmd, NULL)); + } + pid = getpid(); memset(&hints, 0, sizeof(hints)); @@ -579,9 +598,6 @@ __ivaliduser_af(hostf, raddr, luser, ruser, af, len) return __ivaliduser_sa(hostf, sa, sa->sa_len, luser, ruser); } -/* - * Returns 0 if ok, -1 if not ok. - */ int __ivaliduser_sa(hostf, raddr, salen, luser, ruser) FILE *hostf; diff --git a/lib/libc/net/rcmdsh.3 b/lib/libc/net/rcmdsh.3 new file mode 100644 index 000000000000..120157e4f6b5 --- /dev/null +++ b/lib/libc/net/rcmdsh.3 @@ -0,0 +1,105 @@ +.\" $OpenBSD: rcmdsh.3,v 1.6 1999/07/05 04:41:00 aaron Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd September 1, 1996 +.Dt RCMDSH 3 +.Os +.Sh NAME +.Nm rcmdsh +.Nd return a stream to a remote command without superuser +.Sh SYNOPSIS +.Fd #include +.Ft int +.Fn rcmdsh "char **ahost" "int inport" "const char *locuser" "const char *remuser" "const char *cmd" "char *rshprog" +.Sh DESCRIPTION +The +.Fn rcmdsh +function +is used by normal users to execute a command on +a remote machine using an authentication scheme based +on reserved port numbers using +.Xr rshd 8 +or the value of +.Fa rshprog +(if non-null). +.Pp +The +.Fn rcmdsh +function +looks up the host +.Fa *ahost +using +.Xr gethostbyname 3 , +returning \-1 if the host does not exist. +Otherwise +.Fa *ahost +is set to the standard name of the host +and a connection is established to a server +residing at the well-known Internet port +.Li shell/tcp +(or whatever port is used by +.Fa rshprog +). The parameter +.Fa inport +is ignored; it is only included to provide an interface similar to +.Xr rcmd 3 . +.Pp +If the connection succeeds, +a socket in the +.Tn UNIX +domain of type +.Dv SOCK_STREAM +is returned to the caller, and given to the remote +command as stdin and stdout, and stderr. +.Sh DIAGNOSTICS +The +.Fn rcmdsh +function +returns a valid socket descriptor on success. +It returns \-1 on error and prints a diagnostic message on the standard error. +.Sh SEE ALSO +.Xr rsh 1 , +.Xr socketpair 2 , +.Xr rcmd 3 , +.Xr rshd 8 +.Sh BUGS +If +.Xr rsh 1 +gets an error a file descriptor is still returned instead of \-1. +.Sh HISTORY +The +.Fn rcmdsh +function first appeared in +.Ox 2.0 . diff --git a/lib/libc/net/rcmdsh.c b/lib/libc/net/rcmdsh.c new file mode 100644 index 000000000000..6ead7d19950b --- /dev/null +++ b/lib/libc/net/rcmdsh.c @@ -0,0 +1,130 @@ +/* $OpenBSD: rcmdsh.c,v 1.5 1998/04/25 16:23:58 millert Exp $ */ + +/* + * This is an rcmd() replacement originally by + * Chris Siebenmann . + * + * $FreeBSD$ + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$FreeBSD$" +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _PATH_RSH +#define _PATH_RSH "/usr/bin/rsh" +#endif + +/* + * This is a replacement rcmd() function that uses the rsh(1) + * program in place of a direct rcmd(3) function call so as to + * avoid having to be root. Note that rport is ignored. + */ +/* ARGSUSED */ +int +rcmdsh(ahost, rport, locuser, remuser, cmd, rshprog) + char **ahost; + int rport; + const char *locuser, *remuser, *cmd; + char *rshprog; +{ + struct hostent *hp; + int cpid, sp[2]; + char *p; + struct passwd *pw; + + /* What rsh/shell to use. */ + if (rshprog == NULL) + rshprog = _PATH_RSH; + + /* locuser must exist on this host. */ + if ((pw = getpwnam(locuser)) == NULL) { + (void) fprintf(stderr, "rcmdsh: unknown user: %s\n", locuser); + return(-1); + } + + /* Validate remote hostname. */ + if (strcmp(*ahost, "localhost") != 0) { + if ((hp = gethostbyname(*ahost)) == NULL) { + herror(*ahost); + return(-1); + } + *ahost = hp->h_name; + } + + /* Get a socketpair we'll use for stdin and stdout. */ + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) < 0) { + perror("rcmdsh: socketpair"); + return(-1); + } + + cpid = fork(); + if (cpid < 0) { + perror("rcmdsh: fork failed"); + return(-1); + } else if (cpid == 0) { + /* + * Child. We use sp[1] to be stdin/stdout, and close sp[0]. + */ + (void) close(sp[0]); + if (dup2(sp[1], 0) < 0 || dup2(0, 1) < 0) { + perror("rcmdsh: dup2 failed"); + _exit(255); + } + /* Fork again to lose parent. */ + cpid = fork(); + if (cpid < 0) { + perror("rcmdsh: fork to lose parent failed"); + _exit(255); + } + if (cpid > 0) + _exit(0); + + /* In grandchild here. Become local user for rshprog. */ + if (setuid(pw->pw_uid)) { + (void) fprintf(stderr, "rcmdsh: setuid(%u): %s\n", + pw->pw_uid, strerror(errno)); + _exit(255); + } + + /* + * If remote host is "localhost" and local and remote user + * are the same, avoid running remote shell for efficiency. + */ + if (!strcmp(*ahost, "localhost") && !strcmp(locuser, remuser)) { + if (pw->pw_shell[0] == '\0') + rshprog = _PATH_BSHELL; + else + rshprog = pw->pw_shell; + p = strrchr(rshprog, '/'); + execlp(rshprog, p ? p+1 : rshprog, "-c", cmd, + (char *) NULL); + } else { + p = strrchr(rshprog, '/'); + execlp(rshprog, p ? p+1 : rshprog, *ahost, "-l", + remuser, cmd, (char *) NULL); + } + (void) fprintf(stderr, "rcmdsh: execlp %s failed: %s\n", + rshprog, strerror(errno)); + _exit(255); + } else { + /* Parent. close sp[1], return sp[0]. */ + (void) close(sp[1]); + /* Reap child. */ + (void) wait(NULL); + return(sp[0]); + } + /* NOTREACHED */ +}