home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
TELECOM
/
OS9_Unix.lzh
/
RSHSRC
/
doit.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-09-30
|
13KB
|
450 lines
/*
* Copyright (c) 1983, 1988 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1983, 1988 The Regents of the University of California.\n\
All rights reserved.\n\
Additional material Copyright (c) 1992 Ivan Powis\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)rshd.c 5.17.1.2 (Berkeley) 2/7/89";
#endif /* not lint */
/*
* Remote shell server. We're invoked by the rcmd(3) function.
* ... rejigged for OS9 ip Sep 1992
*/
#include <sys/types.h>
#include <signal.h>
#include <inet/socket.h>
#include <inet/in.h>
#include <inet/netdb.h>
#include <errno.h>
#include <time.h>
#include <sys/param.h>
#include <stdio.h>
#include <pwd.h>
#include "rshd.h"
#ifndef NULL
#define NULL (void *) 0
#endif
#define perror(A) prerr(0,_errmsg(errno,(A)))
#define perror2(A,B) prerr(0,_errmsg(errno,(A),(B)))
/*
* Global variables
*/
extern int logflag; /* set level for logging */
extern int newsockfd, errpth; /* paths for initial socket and logfile */
extern time_t timmy; /* time for stamping log file */
char *index();
char *rindex();
char *strncat();
static char servuname[16], cliuname[16], cmdbuf[NCARGS+1];
char env_user[20] = "USER="; /* the environment strings we set */
char env_home[64] = "HOME=";
char env_shell[64] = "SHELL=";
char *env_ptrs[] =
{env_home, env_shell, R_SHELL_PATH, env_user, 0};
char *arg_list[] =
{"rshdc","y","shell",cmdbuf,NULL};
extern int os9forkc();
int
doit(cli_addrp)
struct sockaddr_in *cli_addrp; /* client's Internet address */
{
static char remotehost[2 * MAXHOSTNAMELEN + 1];
static char buf[BUFSIZ], c;
int status, sockfd2, cc, oursecport, almid, childpid, ok;
short clisecport;
char *cp, *hostname;
struct passwd *pwd;
struct hostent *hp;
#ifdef IP_OPTIONS
{
u_char optbuf[BUFSIZ/3], *optptr;
char lbuf[BUFSIZ], *lptr;
int optsize, ipproto;
struct protoent *ip;
if ( (ip = getprotobyname("ip")) != NULL)
ipproto = ip->p_proto;
else
ipproto = IPPROTO_IP;
optsize = sizeof(optbuf);
if (getsockopt(newsockfd, ipproto, IP_OPTIONS, (char *) optbuf, &optsize) == 0
&& optsize != 0) {
/*
* The client has set IP options. This isn't allowed.
* Use syslog() to record the fact.
*/
lptr = lbuf;
optptr = optbuf;
for ( ; optsize > 0; optptr++, optsize--, lptr += 3)
sprintf(lptr, " %2.2x", *optptr);
/* print each option byte as 3 ASCII chars */
if(LOG_NOTICE) _errmsg(0,
"Connection received using IP options (ignored): %s", lbuf);
/*
* Turn off the options. If this doesn't work, we quit.
*/
if (setsockopt(newsockfd, ipproto, IP_OPTIONS,
(char *) NULL, &optsize) != 0) {
if(LOG_ERR) perror("setsockopt IP_OPTIONS NULL");
return(-1);
}
}
}
#endif
/*
* Verify that the client's address was bound to a reserved port.
*/
cli_addrp->sin_port = ntohs((u_short) cli_addrp->sin_port);
/* need host byte ordered port# to compare */
if (cli_addrp->sin_port >= IPPORT_RESERVED ||
cli_addrp->sin_port < IPPORT_RESERVED/2) {
if(LOG_NOTICE)_errmsg(0,"Connection from %s on illegal port",
inet_ntoa(cli_addrp->sin_addr));
return(-1);
}
/*
* Read the ASCII string specifying the secondary port# from
* the socket. We set a timer of 60 seconds to do this read,
* else we assume something is wrong. If the client doesn't want
* the secondary port, they just send the terminating null byte.
*/
almid=alm_set(SIGWAKE,60*CLK_TCK);
clisecport = 0;
for ( ; ; ) {
if ( (cc = read(newsockfd, &c, 1)) != 1) {
if (cc < 0)
if(LOG_NOTICE) perror("read");
shutdown(newsockfd, 2);
return(-1);
}
if (c == 0) /* null byte terminates the string */
break;
clisecport = (clisecport * 10) + (c - '0');
}
alm_delete(almid);
if (clisecport != 0) {
/*
* If the secondary port# is nonzero, then we have to
* connect to that port (which the client has already
* created and is listening on). The secondary port#
* that the client tells us to connect to has to also be
* a reserved port#. Also, our end of this secondary
* connection has to also have a reserved TCP port bound
* to it, plus.
*/
if (clisecport >= IPPORT_RESERVED) {
if(LOG_ERR) _errmsg(0, "2nd port not reserved");
return(-1);
}
oursecport = IPPORT_RESERVED - 1; /* starting port# to try */
if ( (sockfd2 = rresvport(&oursecport)) < 0) {
if(LOG_ERR) perror("can't get stderr port");
return(-1);
}
/*
* Use the cli_addr structure that we already have.
* The 32-bit Internet address is obviously that of the
* client's, just change the port# to the one specified
* by the client as the secondary port.
*/
TRACE("connecting secondary port");
cli_addrp->sin_port = htons((u_short) clisecport);
if (connect(sockfd2, (struct sockaddr *) cli_addrp,
sizeof(*cli_addrp)) < 0) {
if(LOG_INFO) perror("connect second port");
return(-1);
}
}
/*------------------------------------------------------------------*/
/* At this point we have the primary channel on newsockfd, the */
/* secondary channel (if required) on sockfd2 and the error log */
/* on standard error path (2) and on errpth. Paths 0,1 are undefined*/
/*------------------------------------------------------------------*/
/*
* Get the "name" of the client from its Internet address.
* This is used for the authentication below.
*/
TRACE("get clients name");
hp = gethostbyaddr((char *) &cli_addrp->sin_addr,
sizeof(struct in_addr), cli_addrp->sin_family);
if (hp) {
/*
* If the name returned by gethostbyaddr() is in our domain,
* attempt to verify that we haven't been fooled by someone
* in a remote net. Look up the name and check that this
* address corresponds to the name.
*/
if (local_domain(hp->h_name)) {
strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
remotehost[sizeof(remotehost) - 1] = 0;
if ( (hp = gethostbyname(remotehost)) == NULL) {
if(LOG_INFO)_errmsg(0,"Couldn't look up address for %s",
remotehost);
my_error("Couldn't look up addr for your host");
return(-1);
}
if (bcmp(hp->h_addr,(caddr_t) &cli_addrp->sin_addr,
sizeof(cli_addrp->sin_addr)) != 0){
if(LOG_NOTICE){
_errmsg(0,"Host addr %s not listed for host %s",
inet_ntoa(cli_addrp->sin_addr), hp->h_name);
}
my_error("Host address mismatch");
return(-1);
}
}
hostname = hp->h_name;
} else
hostname = inet_ntoa(cli_addrp->sin_addr);
/*
* Read three strings from the client.
*/
if(getstr(cliuname, sizeof(cliuname), "cliuname")==-1) return(-1);
if(getstr(servuname, sizeof(servuname), "servuname")==-1) return (-1);
if(getstr(cmdbuf, sizeof(cmdbuf), "command")==-1) return (-1);
/*
* Look up servuname in the password file. The servuname has
* to be a valid account on this system.
*/
setpwent();
if ( (pwd = getpwnam(servuname)) == (struct passwd *) NULL) {
if(LOG_NOTICE)
_errmsg(0,"invalid %s from %s %s",servuname,hostname,ctime(&timmy));
my_error("Login incorrect.\l");
return(-1);
}
endpwent();
/*
* We'll execute the client's command in the home directory
* of servuname.
*/
if (chdir(pwd->pw_dir) < 0) {
my_error("No remote directory.\l");
return(-1);
}
if (chxdir(pwd->pw_xdir) < 0) {
my_error("No remote execution directory.\l");
return(-1);
}
TRACE("ruseroking");
if (pwd->pw_passwd != NULL && *pwd->pw_passwd != '\0' &&
(ok=ruserok(hostname, pwd->pw_uid == 0, cliuname, servuname)) != 0) {
if(LOG_NOTICE)_errmsg(0,"Ruserok code%d\n",ok);
my_error("Permission denied.\l");
return(-1);
}
/*
* Now write the null byte back to the client telling it
* that everything is OK.
* Note that this means that any error messages that we generate
* from now on (such as the perror() if the execl() fails), won't
* be seen by the rcmd() function, but will be seen by the
* application that called rcmd() when it reads from the socket.
*/
if (write(newsockfd, "", 1) != 1){
if(LOG_DEBUG) perror("writing null\n");
return(-1);
}
/*
* ==================================================================
* We can now fork the processes required to handle communications on
* the socket(s), and then return this process to the 'inetd' portion
* listening for further connections
* ==================================================================
*/
/*
* Set up an initial environment for the shell that we exec().
*/
env_home[5]=0;
env_shell[6]=0;
env_user[5]=0; /*reset strings to 'xxxx=' */
strncat(env_home, pwd->pw_dir, sizeof(env_home)-6);
strncat(env_user, pwd->pw_name, sizeof(env_user)-6);
if (*pwd->pw_shell == '\0') pwd->pw_shell = DEF_SHELL;
if ( (cp = index(pwd->pw_shell, ' ')) != NULL)
*cp=0; /* hack off login shell options */
strncat(env_shell, pwd->pw_shell, sizeof(env_shell)-7);
if ( (cp = rindex(pwd->pw_shell, '/')) != NULL)
cp++; /* step past first slash */
else
cp = pwd->pw_shell; /* no slash in shell string */
arg_list[2]=cp; /*change to login shell*/
/*
* Set the gid & uid to become the user specified by "servuname".
*/
setuid( (pwd->pw_gid<<16) | (pwd->pw_uid));
/*
* Place the sockets on the standard paths 0,1,2
*/
TRACE2(servuname,cmdbuf);
dup2(newsockfd,0);
dup2(newsockfd,1);
if(clisecport){
dup2(sockfd2,2);
arg_list[1]="y";
} else {
dup2(newsockfd,2);
arg_list[1]="n";
}
/*
* Do the forking (the pronunciation changes during the development cycle!)
* arg_list => rshdc y/n shell cmdbuf
*/
childpid=os9exec(os9forkc,arg_list[0],arg_list,env_ptrs,0,0,3);
if(childpid<=0) perror("Forking child "); /* to client */
else wait(&status);
/*
* Restore uid, close paths with sockets on them and block 0,1 so that
* the socket descriptors are not initially assigne to these paths on the
* next iteration.
*/
setuid(0);
dup2(errpth,0);
dup2(errpth,1);
dup2(errpth,2); /* restore local error path */
if(clisecport) close(sockfd2); /*close auxiliary socket*/
return(0);
}
/*
* Read a string from the socket. Make sure it fits, else fatal error.
*/
int
getstr(buf, cnt, errmesg)
char *buf;
int cnt; /* sizeof() the char array */
char *errmesg; /* in case error message required */
{
char c;
do {
if (read(newsockfd, &c, 1) != 1){
if(LOG_DEBUG)perror("getstr");
return(-1); /* error or EOF */
}
*buf++ = c;
if (--cnt == 0) {
my_error("%s too long.\l", errmesg);
return(-1);
}
} while (c != 0); /* null byte terminates the string */
return (0);
}
/*
* Send an error message back to the rcmd() client.
* The first byte we send must be binary 1, followed by the ASCII
* error message, followed by a newline.
*/
my_error(fmt,a,b,c,d,e,f)
char *fmt;
{
char buff[BUFSIZ];
buff[0] = 1;
sprintf(buff + 1, fmt, a,b,c,d,e,f);
write(newsockfd, buff, strlen(buff)); /* newsockfd = socket */
}
/*
* Check whether the specified host is in our local domain, as determined
* by the part of the name following the first period, in its name and in ours.
* If either name is unqualified (contains no period), assume that the host
* is local, as it will be interpreted as such.
*/
int /* return 1 if local domain, else return 0 */
local_domain(host)
char *host;
{
register char *ptr1, *ptr2;
char localhost[MAXHOSTNAMELEN];
if ( (ptr1 = index(host, '.')) == NULL)
return(1); /* no period in remote host name */
gethostname(localhost, sizeof(localhost));
if ( (ptr2 = index(localhost, '.')) == NULL)
return(1); /* no period in local host name */
/*
* Both host names contain a period. Now compare both names,
* starting with the first period in each name (i.e., the names
* of their respective domains). If equal, then the remote domain
* equals the local domain, return 1.
*/
if (strcmp(ptr1, ptr2) == 0) /* **** _was_ case insensitive compare */
return(1);
return(0);
}