/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* rexd - a remote execution daemon based on SUN Remote Procedure Calls
*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <errno.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <rpc/key_prot.h>
#include <wait.h>
#include <sys/systeminfo.h>
#include "rex.h"
#include <security/pam_appl.h>
#include <stropts.h>
#include <sys/ttcompat.h>
/* #define stderr stdout */ /* XXX */
/* name template for temp mount points */
int Debug = 0;
extern int Master; /* half of the pty */
extern char **environ;
extern void rex_cleanup(void);
char *, char **);
char *, char **);
extern void audit_rexd_setup();
extern int audit_settid(int);
/* process rex requests */
void ListnerTimer(int); /* destroy listener */
void CatchChild(int); /* handle child signals */
void oob(int); /* out of band signals */
void sigwinch(int); /* window change signals -- dummy */
int
{
/*
* the server is a typical RPC daemon, except that we only
* accept TCP connections.
*/
int pollretval;
int npollfds = 0;
audit_rexd_setup(); /* BSM */
/*
* Remember the start and extent of argv for setproctitle().
* Open the console for error printouts, but don't let it be
* our controlling terminal.
*/
if (argc > 1) {
DesOnly = 1;
Debug = 1;
}
if (argc > 2) {
DesOnly = 1;
Debug = 1;
}
/*
* argv start and extent for setproctitle()
*/
if (argc > 0)
else
/*
* console open for errors w/o being the controlling terminal
*/
close(1);
close(2);
}
setsid(); /* get rid of controlling terminal */
/*
* setup signals
*/
/*
* Enable non-blocking mode and maximum record size checks for
* connection oriented transports.
*/
}
/*
* determine how we started to see if we are already in the background
* and get appropriately registered with rpcbind (portmapper)
*/
if (isfrominetd(0)) {
/*
* Started from inetd: use fd 0 as socket
*/
if (Debug)
printf("Started from inetd\n");
exit(1);
}
exit(1);
}
} else {
if (Debug)
printf("started from shell\n");
if (!Debug) {
/*
* Started from shell, background
* thyself and run forever.
*/
if (pid < 0) { /* fork error */
perror("rpc.rexd: can't fork");
exit(1);
}
if (pid) { /* parent terminates */
exit(0);
}
}
/*
* child process continues to establish connections
*/
if (Debug)
printf("before svctcp_create() call\n");
== NULL) {
exit(1);
}
dorex, IPPROTO_TCP)) {
exit(1);
}
}
/*
* Create a private temporary directory to hold rexd's mounts
*/
perror("rexd: mkdir");
"rexd: can't create temp directory %s\n",
TempDir);
exit(1);
}
if (Debug)
printf("created temporary directory\n");
/*
* normally we would call svc_run() at this point, but we need to be
* informed of when the RPC connection is broken, in case the other
* side crashes.
*/
while (TRUE) {
if (Debug)
printf("Entered While loop\n");
if (MySocket) {
int i;
char *waste;
/* try to find MySocket in the pollfd set */
for (i = 0; i < svc_max_pollfd; i++)
break;
/*
* If we didn't find it, the connection died for
* some random reason, e.g. client crashed.
*/
if (i == svc_max_pollfd) {
if (Debug)
printf("Connection died\n");
rex_cleanup();
exit(1);
}
}
/*
* Get existing array of pollfd's, should really compress
* this but it shouldn't get very large (or sparse).
*/
if (npollfds != svc_max_pollfd) {
sizeof (pollfd_t) * svc_max_pollfd);
}
if (npollfds == 0)
break; /* None waiting, hence return */
sizeof (pollfd_t) * svc_max_pollfd);
if (Debug)
printf("Before select readfds\n");
case -1:
if (Debug)
printf("Poll failed\n");
continue;
perror("rexd: poll failed");
exit(1);
case 0:
if (Debug)
printf("Poll returned zero\n");
continue;
default:
if (Debug)
printf("Before HelperRead\n");
if (HasHelper)
if (Debug)
printf("After HelperRead\n");
if (Debug)
printf("before svc_getreq_poll\n");
}
if (Debug)
printf("After switch\n");
}
return (0);
}
/*
* This function gets called after the listner has timed out waiting
* for any new connections coming in.
*/
void
{
/*
* svc_destroy not done here due to problems with M_ERROR
* on stream head and inetd
*/
exit(0);
}
struct authunix_parms
struct authdes_cred *des_cred;
{
return (NULL);
return (NULL);
else
return (unix_cred);
}
/*
* dorex - handle one of the rex procedure calls, dispatching to the
* correct function.
*/
void
{
if (ListnerTransp) {
/*
* First call - fork a server for this connection
*/
if (count > 4)
{
perror("rexd: cannot fork");
break;
}
sleep(5);
}
if (pid != 0) {
/*
* Parent - return to service loop to accept further
* connections.
*/
return;
}
/*
* child - close listner transport to avoid confusion
* Also need to close all other service transports
* besides the one we are interested in.
* Save ours so that we know when it goes away.
*/
if (Debug)
printf("child server process\n");
alarm(0);
if (transp != ListnerTransp) {
}
/* temp workaround to restore sanity in TLI state */
t_close(0); /* opened in parent possibly by inetd */
/*
* XXX: svc_pollfd[] is a read-only structure. This
* appears to be dead code, which should be removed.
* However, until it can be clearly understood, leaving
* in.
*/
}
}
}
/*
* execute the requested prodcedure
*/
case NULLPROC:
if (Debug) /* XXX */
printf("dorex: call to NULLPROC\n");
exit(1);
}
return;
case REXPROC_START:
if (Debug) /* XXX */
printf("dorex: call to REXPROC_START\n");
exit(1);
}
if (Debug)
printf("svc_getargs: suceeded\n");
if (DesOnly) {
"Unix too weak auth(DesOnly)!\n");
} else
} else {
sleep(5);
exit(1);
}
sleep(5);
exit(1);
}
(char **)&result.rlt_message,
if (Debug)
printf("rex_startup: completed\n");
== FALSE) {
rex_cleanup();
exit(1);
}
if (Debug)
printf("svc_sendreply: suceeded\n");
rex_cleanup();
exit(0);
}
return;
case REXPROC_MODES:
{
if (Debug) /* XXX */
printf("dorex: call to REXPROC_MODES\n");
exit(1);
}
if (Debug)
printf("svc_getargs succ REXPROC_MODES call\n");
exit(1);
}
}
return;
case REXPROC_WINCH: /* XXX Fix? */
{
if (Debug) /* XXX */
printf("dorex: call to REXPROC_WINCH\n");
== FALSE) {
exit(1);
}
SetPtySize(&size);
"rexd: window change reply failed");
exit(1);
}
}
return;
case REXPROC_SIGNAL:
{
int sigNumber;
if (Debug) /* XXX */
printf("dorex: call to REXPROC_SIGNAL\n");
exit(1);
}
exit(1);
}
}
return;
case REXPROC_WAIT:
if (Debug) /* XXX */
printf("dorex: call to REXPROC_WAIT\n");
== FALSE) {
exit(1);
}
rex_cleanup();
exit(0);
/* NOTREACHED */
default:
if (Debug)
printf("dorex: call to bad process!\n");
exit(1);
}
}
/*
* signal handler for SIGCHLD - called when user process dies or is stopped
*/
void
{
int status;
if (Debug)
printf("Enter Catchild\n");
if (Debug)
printf("pid==child\n");
if (WIFSTOPPED(status)) {
if (Debug)
printf("WIFSTOPPED\n");
/* tell remote client to stop */
/* port of BSD sigpause(0); */
/* restart child */
/* killpg() of SunOS 4.1.1 */
return;
}
/*
* XXX this probably does not cover all interesting
* exit cases hence reread the man page to determine
* if we need more data or more test cases
*/
ChildDied = 1;
if (Debug)
printf("Within If HasHelper\n");
HasHelper = 0;
}
}
}
}
/*
* oob -- called when we should restart the stopped child.
*/
void
{
int atmark;
for (;;) {
perror("ioctl");
break;
}
if (atmark)
break;
}
}
/*
* rex_wait - wait for command to finish, unmount the file system,
* and return the exit status.
* message gets an optional string error message.
*/
int
char **message;
{
int count;
if (child == 0) {
rex_cleanup();
return (1);
}
sleep(1);
if (ChildStatus & 0xFF)
return (ChildStatus);
return (ChildStatus >> 8);
}
/*
* cleanup - unmount and remove our temporary directory
*/
void
{
if (tmpdir) {
"rexd: child killed to unmount %s\r\n",
nfsdir);
}
chdir("/");
tmpdir);
perror("rmdir");
}
if (Debug)
if (HasHelper)
HasHelper = 0;
}
/*
* This function does the server work to get a command executed
* Returns 0 if OK, nonzero if error
*/
int
struct authunix_parms *ucred;
char **message;
struct sockaddr_in *calleraddr;
{
int len;
extern pam_handle_t *pamh;
if (Debug)
printf("Beginning of Rex_Startup\n");
if (child) { /* already started */
if (Debug)
printf("Killing \"child\" process\n");
return (1);
}
/* sigset(SIGCHLD, CatchChild); */
return (1);
} else {
fsname = defaultDir;
subdir = "";
}
if (Debug)
if (Debug) {
printf("rex_startup on host %s:\nrequests fsname=%s",
}
/*
* The requested directory is local to our machine,
* so just change to it.
*/
} else {
if (Debug)
/*
* The requested directory is already mounted. If the
* mount is not by another rexd, just change to it.
* Otherwise, mount it again. If just changing to
* the mounted directy, be careful. It might be mounted
* in a different place.
* (dirbuf is modified in place!)
*/
/*
* XXX errno is set to ENOENT on success
* of mktemp because of accesss checks for file
*/
errno = 0;
perror("Already Mounted");
if (pamh) {
}
return (1);
}
if (Debug)
printf("created %s (%d)\n",
if (Debug)
printf("mount_nfs:error return\n");
if (pamh) {
}
return (1);
}
if (Debug)
printf("mount_nfs: success return\n");
} else
} else {
if (Debug)
/*
* The requested directory is not mounted anywhere,
* so try to mount our own copy of it. We set nfsdir
* so that it gets unmounted later, and tmpdir so that
* it also gets removed when we are done.
*/
/*
* XXX errno is set to ENOENT on success of mktemp
* becuase of accesss checks for file
*/
errno = 0;
perror("Not Already Mounted");
if (pamh) {
}
return (1);
}
if (Debug)
if (Debug)
printf("mount_nfs:error return\n");
if (pamh) {
}
return (1);
}
if (Debug)
printf("mount_nfs: success return\n");
}
}
/*
* "dirbuf" now contains the local mount point, so just tack on
* the subdirectory to get the pathname to which we "chdir"
*/
if (Debug)
printf("Before doconnect\n");
OutputSocket = fd0;
/*
* Arrange for fd0 to send the SIGURG signal when out-of-band data
* arrives, which indicates that we should send the stopped child a
* SIGCONT signal so that we can resume work.
*/
/* ioctl(fd0, SIOCSPGRP, ?X?); */
if (Debug)
printf("Before \"use same port\"\n");
/*
* use the same connection for both stdin and stdout
*/
}
/*
* allocate a pseudo-terminal if necessary
*/
if (Debug)
printf("Before AllocatePty call\n");
/* AllocatePty has grantpt() call which has bug */
/* Hence clear SIGCHLD handler setting */
if (pamh) {
}
return (1);
}
HasHelper = 1;
}
/*
* this sigset()call moved to after AllocatePty() call
* because a bug in waitpid() inside grantpt()
* causes CatchChild() to be invoked.
*/
/* block the sigpause until signal in */
/* child releases the signal */
}
if (Debug)
printf("Before a \"child\" fork\n");
if (child < 0) {
if (pamh) {
}
return (1);
}
if (child) {
/*
* parent rexd: close network connections if needed,
* then return to the main loop.
*/
}
if (Debug)
printf("Parent ret to main loop, child does startup\n");
if (pamh) {
}
return (0);
}
/* child rexd */
if (Debug)
printf("Child rexd\n");
/* setpgrp(0, 0) */
setsid(); /* make session leader */
if (Debug)
printf("After setsid\n");
if (Debug)
printf("Before OpenPtySlave\n");
/* reopen slave so that child has controlling tty */
OpenPtySlave();
if (Debug)
printf("After OpenPtySlave\n");
}
if (Debug)
}
if (Debug)
/*
* Use the same connection for both stdout and stderr
*/
} else {
if (Debug)
}
/*
* use ptys instead of sockets in interactive mode
*/
LoginUser();
}
/* setup terminal ID (use read file descriptor) */
if (audit_settid(fd0) != 0) {
errprintf("cannot set audit characteristics\n");
return (1);
}
closefrom(3);
if (Debug)
audit_rexd_fail("user id is not valid",
user,
exit(1);
}
/* set the real (and effective) GID */
exit(1);
}
/* Set the supplementary group access list. */
exit(1);
}
audit_rexd_fail("user id is not valid",
user,
exit(1);
}
user,
/* set the real (and effective) UID */
exit(1);
}
if (pamh) {
}
if (Debug) /* XXX */
exit(1);
}
/* pause to sync with first SIGWINCH sent as part of */
/* protocol and handled by parent doing other rex primitves */
}
/*
* Null command means execute the default shell for this user
*/
args[0] = defaultShell;
exit(1);
}
if (Debug)
/* XXX */
if (Debug)
/* XXX get rid of errno in parens */
exit(1);
}
/*
* Search the mount table to see if the given file system is already
* mounted. If so, return the place that it is mounted on.
*/
int
char *fsname;
char *mountedon;
{
return (0);
return (1);
}
}
return (0);
}
/*
* resulting file descriptor.
*/
int
struct sockaddr_in *sin;
short port;
int fd;
{
perror("rexd: connect");
exit(1);
}
return (fd);
}
void
{
}
/*
* SETPROCTITLE -- set the title of this process for "ps"
*
* Does nothing if there were not enough arguments on the command
* line for the information.
*
* Side Effects:
* Clobbers argv[] of our main procedure.
*/
void
{
register char *tohere;
return;
while (*tohere++) /* Skip to end of printf output */
;
*tohere++ = ' ';
}
/*
* Determine if started from inetd or not
*/
int
int fd;
{
/*
* If fd looks like a TLI endpoint, we assume
* that we were started by a port monitor. If
* t_getstate fails with TBADF, this is not a
* TLI endpoint.
*/
return (1);
return (0);
}