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 >
C/C++ Source or Header  |  1992-09-30  |  13KB  |  450 lines

  1. /*
  2.  * Copyright (c) 1983, 1988 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation,
  8.  * advertising materials, and other materials related to such
  9.  * distribution and use acknowledge that the software was developed
  10.  * by the University of California, Berkeley.  The name of the
  11.  * University may not be used to endorse or promote products derived
  12.  * from this software without specific prior written permission.
  13.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  14.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  15.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16.  */
  17.  
  18. #ifndef lint
  19. char copyright[] =
  20. "@(#) Copyright (c) 1983, 1988 The Regents of the University of California.\n\
  21.  All rights reserved.\n\
  22. Additional material Copyright (c) 1992 Ivan Powis\n";
  23. #endif /* not lint */
  24.  
  25. #ifndef lint
  26. static char sccsid[] = "@(#)rshd.c    5.17.1.2 (Berkeley) 2/7/89";
  27. #endif /* not lint */
  28.  
  29. /*
  30.  * Remote shell server.  We're invoked by the rcmd(3) function.
  31.  *    ... rejigged for OS9 ip Sep 1992
  32.  */
  33.  
  34. #include    <sys/types.h>
  35. #include    <signal.h>
  36. #include    <inet/socket.h>
  37. #include    <inet/in.h>
  38. #include    <inet/netdb.h>
  39. #include    <errno.h>
  40. #include    <time.h>
  41.  
  42. #include    <sys/param.h>
  43. #include    <stdio.h>
  44. #include    <pwd.h>
  45.  
  46. #include    "rshd.h"
  47.  
  48. #ifndef NULL
  49. #define NULL (void *) 0
  50. #endif
  51.  
  52. #define perror(A)       prerr(0,_errmsg(errno,(A)))
  53. #define perror2(A,B)    prerr(0,_errmsg(errno,(A),(B)))
  54.  
  55. /*
  56.  * Global variables
  57.  */
  58.  
  59. extern int logflag;            /* set level for logging */
  60. extern int newsockfd, errpth;    /* paths for initial socket and logfile */
  61. extern time_t timmy;            /* time for stamping log file */
  62.  
  63. char    *index();
  64. char    *rindex();
  65. char    *strncat();
  66.  
  67. static char    servuname[16], cliuname[16], cmdbuf[NCARGS+1];
  68.  
  69. char    env_user[20]  = "USER=";    /* the environment strings we set */
  70. char    env_home[64]  = "HOME=";
  71. char    env_shell[64] = "SHELL=";
  72. char    *env_ptrs[] =
  73.         {env_home, env_shell, R_SHELL_PATH, env_user, 0};
  74. char    *arg_list[] =
  75.         {"rshdc","y","shell",cmdbuf,NULL};
  76.         
  77. extern int os9forkc();
  78.  
  79. int
  80. doit(cli_addrp)
  81. struct sockaddr_in    *cli_addrp;    /* client's Internet address */
  82. {
  83.     static char    remotehost[2 * MAXHOSTNAMELEN + 1];
  84.     static char    buf[BUFSIZ], c;
  85.     int    status, sockfd2, cc, oursecport, almid, childpid, ok;
  86.     short clisecport;
  87.     char *cp, *hostname;
  88.     struct passwd *pwd;
  89.     struct hostent *hp;
  90.  
  91.  
  92. #ifdef IP_OPTIONS
  93.     {
  94.     u_char        optbuf[BUFSIZ/3], *optptr;
  95.     char        lbuf[BUFSIZ], *lptr;
  96.     int        optsize, ipproto;
  97.     struct protoent    *ip;
  98.  
  99.     if ( (ip = getprotobyname("ip")) != NULL)
  100.         ipproto = ip->p_proto;
  101.     else
  102.         ipproto = IPPROTO_IP;
  103.  
  104.     optsize = sizeof(optbuf);
  105.     if (getsockopt(newsockfd, ipproto, IP_OPTIONS, (char *) optbuf, &optsize) == 0
  106.         && optsize != 0) {
  107. /*
  108.  * The client has set IP options.  This isn't allowed.
  109.  * Use syslog() to record the fact.
  110.  */
  111.  
  112.         lptr = lbuf;
  113.         optptr = optbuf;
  114.         for ( ; optsize > 0; optptr++, optsize--, lptr += 3)
  115.             sprintf(lptr, " %2.2x", *optptr);
  116.                 /* print each option byte as 3 ASCII chars */
  117.         if(LOG_NOTICE) _errmsg(0,
  118.             "Connection received using IP options (ignored): %s", lbuf);
  119.  
  120. /*
  121.  * Turn off the options.  If this doesn't work, we quit.
  122.  */
  123.  
  124.         if (setsockopt(newsockfd, ipproto, IP_OPTIONS,
  125.                     (char *) NULL, &optsize) != 0) {
  126.             if(LOG_ERR) perror("setsockopt IP_OPTIONS NULL");
  127.             return(-1);
  128.         }
  129.     }
  130.     }
  131. #endif
  132.  
  133. /*
  134.  * Verify that the client's address was bound to a reserved port.
  135.  */
  136.  
  137.     cli_addrp->sin_port = ntohs((u_short) cli_addrp->sin_port);
  138.                 /* need host byte ordered port# to compare */
  139.     if (cli_addrp->sin_port >= IPPORT_RESERVED  ||
  140.         cli_addrp->sin_port <  IPPORT_RESERVED/2) {
  141.         if(LOG_NOTICE)_errmsg(0,"Connection from %s on illegal port",
  142.                     inet_ntoa(cli_addrp->sin_addr));
  143.         return(-1);
  144.     }
  145.  
  146. /*
  147.  * Read the ASCII string specifying the secondary port# from
  148.  * the socket.  We set a timer of 60 seconds to do this read,
  149.  * else we assume something is wrong.  If the client doesn't want
  150.  * the secondary port, they just send the terminating null byte.
  151.  */
  152.  
  153.     almid=alm_set(SIGWAKE,60*CLK_TCK);
  154.     clisecport = 0;
  155.     for ( ; ; ) {
  156.         if ( (cc = read(newsockfd, &c, 1)) != 1) {
  157.             if (cc < 0)
  158.                 if(LOG_NOTICE) perror("read");
  159.             shutdown(newsockfd, 2);
  160.             return(-1);
  161.         }
  162.         if (c == 0)        /* null byte terminates the string */
  163.             break;
  164.         clisecport = (clisecport * 10) + (c - '0');
  165.     }
  166.     alm_delete(almid);
  167.     
  168.     if (clisecport != 0) {
  169. /*
  170.  * If the secondary port# is nonzero, then we have to
  171.  * connect to that port (which the client has already
  172.  * created and is listening on).  The secondary port#
  173.  * that the client tells us to connect to has to also be
  174.  * a reserved port#.  Also, our end of this secondary
  175.  * connection has to also have a reserved TCP port bound
  176.  * to it, plus.
  177.  */
  178.  
  179.         if (clisecport >= IPPORT_RESERVED) {
  180.             if(LOG_ERR) _errmsg(0, "2nd port not reserved");
  181.             return(-1);
  182.         }
  183.  
  184.         oursecport = IPPORT_RESERVED - 1; /* starting port# to try */
  185.         if ( (sockfd2 = rresvport(&oursecport)) < 0) {
  186.             if(LOG_ERR) perror("can't get stderr port");
  187.             return(-1);
  188.         }
  189.  
  190. /*
  191.  * Use the cli_addr structure that we already have.
  192.  * The 32-bit Internet address is obviously that of the
  193.  * client's, just change the port# to the one specified
  194.  * by the client as the secondary port.
  195.  */
  196.  
  197. TRACE("connecting secondary port");
  198.         cli_addrp->sin_port = htons((u_short) clisecport);
  199.         if (connect(sockfd2, (struct sockaddr *) cli_addrp,
  200.                 sizeof(*cli_addrp)) < 0) {
  201.             if(LOG_INFO) perror("connect second port");
  202.             return(-1);
  203.         }
  204.     }
  205.  
  206. /*------------------------------------------------------------------*/
  207. /*    At this point we have the primary channel on newsockfd, the        */
  208. /*    secondary channel (if required) on sockfd2 and the error log    */
  209. /*    on standard error path (2) and on errpth. Paths 0,1 are undefined*/
  210. /*------------------------------------------------------------------*/
  211.  
  212. /*
  213.  * Get the "name" of the client from its Internet address.
  214.  * This is used for the authentication below.
  215.  */
  216.  
  217. TRACE("get clients name");
  218.     hp = gethostbyaddr((char *) &cli_addrp->sin_addr,
  219.                 sizeof(struct in_addr), cli_addrp->sin_family);
  220.     if (hp) {
  221.         /*
  222.          * If the name returned by gethostbyaddr() is in our domain,
  223.          * attempt to verify that we haven't been fooled by someone
  224.          * in a remote net.  Look up the name and check that this
  225.          * address corresponds to the name.
  226.          */
  227.  
  228.         if (local_domain(hp->h_name)) {
  229.             strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
  230.             remotehost[sizeof(remotehost) - 1] = 0;
  231.             if ( (hp = gethostbyname(remotehost)) == NULL) {
  232.                 if(LOG_INFO)_errmsg(0,"Couldn't look up address for %s",
  233.                                     remotehost);
  234.                 my_error("Couldn't look up addr for your host");
  235.                 return(-1);
  236.             }
  237.             if (bcmp(hp->h_addr,(caddr_t) &cli_addrp->sin_addr,
  238.                               sizeof(cli_addrp->sin_addr)) != 0){
  239.                 if(LOG_NOTICE){
  240.                     _errmsg(0,"Host addr %s not listed for host %s",
  241.                        inet_ntoa(cli_addrp->sin_addr),    hp->h_name);
  242.                    }
  243.                 my_error("Host address mismatch");
  244.                 return(-1);
  245.             }
  246.         }
  247.         hostname = hp->h_name;
  248.     } else
  249.         hostname = inet_ntoa(cli_addrp->sin_addr);
  250.  
  251. /*
  252.  * Read three strings from the client.
  253.  */
  254.  
  255.     if(getstr(cliuname, sizeof(cliuname), "cliuname")==-1) return(-1);
  256.     if(getstr(servuname, sizeof(servuname), "servuname")==-1) return (-1);
  257.     if(getstr(cmdbuf, sizeof(cmdbuf), "command")==-1) return (-1);
  258.  
  259. /*
  260.  * Look up servuname in the password file.  The servuname has
  261.  * to be a valid account on this system.
  262.  */
  263.  
  264.     setpwent();
  265.     if ( (pwd = getpwnam(servuname)) == (struct passwd *) NULL) {
  266.         if(LOG_NOTICE)
  267.             _errmsg(0,"invalid %s from %s %s",servuname,hostname,ctime(&timmy));
  268.         my_error("Login incorrect.\l");
  269.         return(-1);
  270.     }
  271.     endpwent();
  272.  
  273. /*
  274.  * We'll execute the client's command in the home directory
  275.  * of servuname.
  276.  */
  277.  
  278.     if (chdir(pwd->pw_dir) < 0) {
  279.         my_error("No remote directory.\l");
  280.         return(-1);
  281.     }
  282.     if (chxdir(pwd->pw_xdir) < 0) {
  283.         my_error("No remote execution directory.\l");
  284.         return(-1);
  285.     }
  286.  
  287. TRACE("ruseroking");
  288.     if (pwd->pw_passwd != NULL  &&  *pwd->pw_passwd != '\0'  &&
  289.         (ok=ruserok(hostname, pwd->pw_uid == 0, cliuname, servuname)) != 0) {
  290.         if(LOG_NOTICE)_errmsg(0,"Ruserok code%d\n",ok);
  291.         my_error("Permission denied.\l");
  292.         return(-1);
  293.     }
  294.  
  295. /*
  296.  * Now write the null byte back to the client telling it
  297.  * that everything is OK.
  298.  * Note that this means that any error messages that we generate
  299.  * from now on (such as the perror() if the execl() fails), won't
  300.  * be seen by the rcmd() function, but will be seen by the
  301.  * application that called rcmd() when it reads from the socket.
  302.  */
  303.  
  304.     if (write(newsockfd, "", 1) != 1){
  305.         if(LOG_DEBUG) perror("writing null\n");
  306.         return(-1);
  307.     }
  308.  
  309. /*
  310.  *    ==================================================================
  311.  *    We can now fork the processes required to handle communications on
  312.  *    the socket(s), and then return this process to the 'inetd' portion
  313.  *    listening for further connections
  314.  *    ==================================================================
  315.  */
  316.  
  317. /*
  318.  * Set up an initial environment for the shell that we exec().
  319.  */
  320.         env_home[5]=0;
  321.         env_shell[6]=0;
  322.         env_user[5]=0;        /*reset strings to 'xxxx=' */
  323.         strncat(env_home,  pwd->pw_dir,   sizeof(env_home)-6);
  324.         strncat(env_user,  pwd->pw_name,  sizeof(env_user)-6);
  325.  
  326.         if (*pwd->pw_shell == '\0') pwd->pw_shell = DEF_SHELL;
  327.         if ( (cp = index(pwd->pw_shell, ' ')) != NULL)
  328.             *cp=0;                        /* hack off login shell options */
  329.         strncat(env_shell, pwd->pw_shell, sizeof(env_shell)-7);
  330.  
  331.         if ( (cp = rindex(pwd->pw_shell, '/')) != NULL)
  332.                 cp++;                   /* step past first slash */
  333.         else
  334.                 cp = pwd->pw_shell;     /* no slash in shell string */
  335.         arg_list[2]=cp;                    /*change to login shell*/
  336.  
  337. /*
  338.  * Set the gid & uid to become the user specified by "servuname".
  339.  */
  340.  
  341.         setuid( (pwd->pw_gid<<16) | (pwd->pw_uid));
  342.  
  343. /*
  344.  * Place the sockets on the standard paths 0,1,2
  345.  */
  346. TRACE2(servuname,cmdbuf);
  347.     dup2(newsockfd,0);
  348.     dup2(newsockfd,1);
  349.     if(clisecport){
  350.         dup2(sockfd2,2);
  351.         arg_list[1]="y";
  352.     } else {
  353.         dup2(newsockfd,2);
  354.         arg_list[1]="n";
  355.     }
  356. /*
  357.  * Do the forking (the pronunciation changes during the development cycle!)
  358.  *        arg_list => rshdc y/n shell cmdbuf
  359.  */
  360.     childpid=os9exec(os9forkc,arg_list[0],arg_list,env_ptrs,0,0,3);
  361.     if(childpid<=0) perror("Forking child ");    /* to client */
  362.     else wait(&status);
  363. /*
  364.  * Restore uid, close paths with sockets on them and block 0,1 so that
  365.  * the socket descriptors are not initially assigne to these paths on the
  366.  * next iteration.
  367.  */
  368.     setuid(0);
  369.     dup2(errpth,0);
  370.     dup2(errpth,1);
  371.     dup2(errpth,2);    /* restore local error path */
  372.     if(clisecport) close(sockfd2);    /*close auxiliary socket*/
  373.     return(0);
  374. }
  375.      
  376. /*
  377.  * Read a string from the socket.  Make sure it fits, else fatal error.
  378.  */
  379. int
  380. getstr(buf, cnt, errmesg)
  381. char    *buf;
  382. int    cnt;        /* sizeof() the char array */
  383. char    *errmesg;    /* in case error message required */
  384. {
  385.     char    c;
  386.  
  387.     do {
  388.         if (read(newsockfd, &c, 1) != 1){
  389.             if(LOG_DEBUG)perror("getstr");
  390.             return(-1);    /* error or EOF */
  391.         }
  392.         *buf++ = c;
  393.         if (--cnt == 0) {
  394.             my_error("%s too long.\l", errmesg);
  395.             return(-1);
  396.         }
  397.     } while (c != 0);    /* null byte terminates the string */
  398.     return (0);
  399. }
  400.  
  401. /*
  402.  * Send an error message back to the rcmd() client.
  403.  * The first byte we send must be binary 1, followed by the ASCII
  404.  * error message, followed by a newline.
  405.  */
  406.  
  407. my_error(fmt,a,b,c,d,e,f)
  408. char *fmt;
  409. {
  410.     char buff[BUFSIZ];
  411.  
  412.     buff[0] = 1;
  413.     sprintf(buff + 1, fmt, a,b,c,d,e,f);
  414.     write(newsockfd, buff, strlen(buff));    /* newsockfd = socket */
  415. }
  416.  
  417. /*
  418.  * Check whether the specified host is in our local domain, as determined
  419.  * by the part of the name following the first period, in its name and in ours.
  420.  * If either name is unqualified (contains no period), assume that the host
  421.  * is local, as it will be interpreted as such.
  422.  */
  423.  
  424. int                /* return 1 if local domain, else return 0 */
  425. local_domain(host)
  426. char    *host;
  427. {
  428.     register char    *ptr1, *ptr2;
  429.     char        localhost[MAXHOSTNAMELEN];
  430.  
  431.     if ( (ptr1 = index(host, '.')) == NULL)
  432.         return(1);        /* no period in remote host name */
  433.  
  434.     gethostname(localhost, sizeof(localhost));
  435.     if ( (ptr2 = index(localhost, '.')) == NULL)
  436.         return(1);        /* no period in local host name */
  437.  
  438.     /*
  439.      * Both host names contain a period.  Now compare both names,
  440.      * starting with the first period in each name (i.e., the names
  441.      * of their respective domains).  If equal, then the remote domain
  442.      * equals the local domain, return 1.
  443.      */
  444.  
  445.     if (strcmp(ptr1, ptr2) == 0)    /* **** _was_ case insensitive compare */
  446.         return(1);
  447.  
  448.     return(0);
  449. }
  450.