root/usr.sbin/rpc.pcnfsd/pcnfsd_misc.c
/*      $NetBSD: pcnfsd_misc.c,v 1.17 2020/04/22 23:46:02 joerg Exp $   */

/* RE_SID: @(%)/usr/dosnfs/shades_SCCS/unix/pcnfsd/v2/src/SCCS/s.pcnfsd_misc.c 1.5 92/01/24 19:59:13 SMI */
/*
**=====================================================================
** Copyright (c) 1986,1987,1988,1989,1990,1991 by Sun Microsystems, Inc.
**      @(#)pcnfsd_misc.c       1.5     1/24/92
**=====================================================================
*/
/*
**=====================================================================
**             I N C L U D E   F I L E   S E C T I O N                *
**                                                                    *
** If your port requires different include files, add a suitable      *
** #define in the customization section, and make the inclusion or    *
** exclusion of the files conditional on this.                        *
**=====================================================================
*/

#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <util.h>
#ifdef SUPPORT_UTMPX
#include <utmpx.h>
#endif

#ifdef ISC_2_0
#include <sys/fcntl.h>
#endif

#ifdef SHADOW_SUPPORT
#include <shadow.h>
#endif

#ifdef WTMP
int     wtmp_enabled = 1;
#endif

#include "common.h"
#include "pcnfsd.h"
#include "extern.h"

/*
**---------------------------------------------------------------------
** Other #define's
**---------------------------------------------------------------------
*/

#define zchar           0x5b

char   *mapfont(char, char, char);
void    myhandler(int);
void    start_watchdog(int);
void    stop_watchdog(void);

/*
**=====================================================================
**                      C O D E   S E C T I O N                       *
**=====================================================================
*/
/*
**---------------------------------------------------------------------
**                          Support procedures
**---------------------------------------------------------------------
*/


void
scramble(char *s1, char *s2)
{
        while (*s1) {
                *s2++ = (*s1 ^ zchar) & 0x7f;
                s1++;
        }
        *s2 = 0;
}



struct passwd *
get_password(char *usrnam)
{
        struct passwd *p;
        static struct passwd localp;
        __aconst char *pswd, *ushell;


#ifdef SHADOW_SUPPORT
        struct spwd *sp;
        int     shadowfile;
#endif

#ifdef SHADOW_SUPPORT
/*
**--------------------------------------------------------------
** Check the existence of SHADOW.  If it is there, then we are
** running a two-password-file system.
**--------------------------------------------------------------
*/
        if (access(SHADOW, 0))
                shadowfile = 0; /* SHADOW is not there */
        else
                shadowfile = 1;

        setpwent();
        if (shadowfile)
                (void) setspent();      /* Setting the shadow password file */
        if ((p = getpwnam(usrnam)) == NULL ||
            (shadowfile && (sp = getspnam(usrnam)) == NULL))
                return (NULL);

        if (shadowfile) {
                pswd = sp->sp_pwdp;
                (void) endspent();
        } else
                pswd = p->pw_passwd;

#else
        p = getpwnam(usrnam);
        if (p == NULL)
                return (NULL);
        pswd = p->pw_passwd;
#endif

#ifdef ISC_2_0
/* *----------------------------------------------------------- * We
 * may have an 'x' in which case look in /etc/shadow ..
 * *----------------------------------------------------------- */
        if (((strlen(pswd)) == 1) && pswd[0] == 'x') {
                struct spwd *shadow = getspnam(usrnam);

                if (!shadow)
                        return (NULL);
                pswd = shadow->sp_pwdp;
        }
#endif
        localp = *p;
        localp.pw_passwd = pswd;
#ifdef USE_GETUSERSHELL

        setusershell();
        while (ushell = getusershell()) {
                if (!strcmp(ushell, localp.pw_shell)) {
                        ok = 1;
                        break;
                }
        }
        endusershell();
        if (!ok)
                return (NULL);
#else
/*
* the best we can do is to ensure that the shell ends in "sh"
*/
        ushell = localp.pw_shell;
        if (strlen(ushell) < 2)
                return (NULL);
        ushell += strlen(ushell) - 2;
        if (strcmp(ushell, "sh"))
                return (NULL);

#endif
        return (&localp);
}



/*
**---------------------------------------------------------------------
**                      Print support procedures
**---------------------------------------------------------------------
*/


char   *
mapfont(char f, char i, char b)
{
        static char fontname[64];

        fontname[0] = 0;        /* clear it out */

        switch (f) {
        case 'c':
                (void) strlcpy(fontname, "Courier", sizeof(fontname));
                break;
        case 'h':
                (void) strlcpy(fontname, "Helvetica", sizeof(fontname));
                break;
        case 't':
                (void) strlcpy(fontname, "Times", sizeof(fontname));
                break;
        default:
                (void) strlcpy(fontname, "Times-Roman", sizeof(fontname));
                goto finis;
        }
        if (i != 'o' && b != 'b') {     /* no bold or oblique */
                if (f == 't')   /* special case Times */
                        (void) strlcat(fontname, "-Roman", sizeof(fontname));
                goto finis;
        }
        (void) strlcat(fontname, "-", sizeof(fontname));
        if (b == 'b')
                (void) strlcat(fontname, "Bold", sizeof(fontname));
        if (i == 'o')           /* o-blique */
                (void) strlcat(fontname, f == 't' ? "Italic" : "Oblique",
                    sizeof(fontname));

finis:  return (&fontname[0]);
}
/*
* run_ps630 performs the Diablo 630 emulation filtering process. ps630
* was broken in certain Sun releases: it would not accept point size or
* font changes. If your version is fixed, undefine the symbol
* PS630_IS_BROKEN and rebuild pc-nfsd.
*/
/* #define PS630_IS_BROKEN 1 */

void
run_ps630(char *f, char *opts)
{
        char    temp_file[256];
        char    commbuf[256];
        int     i;

        (void) strlcpy(temp_file, f, sizeof(temp_file));
        (void) strlcat(temp_file, "X", sizeof(temp_file)); /* intermediate file name */

#ifndef PS630_IS_BROKEN
        (void) snprintf(commbuf, sizeof(commbuf), "ps630 -s %c%c -p %s -f ",
            opts[2], opts[3], temp_file);
        (void) strlcat(commbuf, mapfont(opts[4], opts[5], opts[6]),
            sizeof(commbuf));
        (void) strlcat(commbuf, " -F ", sizeof(commbuf));
        (void) strlcat(commbuf, mapfont(opts[7], opts[8], opts[9]),
            sizeof(commbuf));
        (void) strlcat(commbuf, "  ", sizeof(commbuf));
        (void) strlcat(commbuf, f, sizeof(commbuf));
#else                           /* PS630_IS_BROKEN */
/*
 * The pitch and font features of ps630 appear to be broken at
 * this time.
 */
        (void) snprintf(commbuf, sizeof(commbuf), "ps630 -p %s %s",
            temp_file, f);
#endif                          /* PS630_IS_BROKEN */


        if ((i = system(commbuf)) != 0) {
                /*
                 * Under (un)certain conditions, ps630 may return -1 even
                 * if it worked. Hence the commenting out of this error
                 * report.
                 */
                 /* (void)fprintf(stderr, "\n\nrun_ps630 rc = %d\n", i) */ ;
                /* exit(1); */
        }
        if (rename(temp_file, f)) {
                perror("run_ps630: rename");
                exit(1);
        }
        return;
}





/*
**---------------------------------------------------------------------
**                      WTMP update support
**---------------------------------------------------------------------
*/


#ifdef WTMP
void
wlogin(char *name, struct svc_req *req)
{
        struct sockaddr_in *who;
        struct hostent *hp;
        char *host;

        if (!wtmp_enabled)
                return;

/* Get network address of client. */
        who = &req->rq_xprt->xp_raddr;

/* Get name of connected client */
        hp = gethostbyaddr((char *) &who->sin_addr,
            sizeof(struct in_addr),
            who->sin_family);

        if (hp) {
                host = hp->h_name;
        } else {
                host = inet_ntoa(who->sin_addr);
        }

#ifdef SUPPORT_UTMP
        logwtmp("PC-NFS", name, host);
#endif
#ifdef SUPPORT_UTMPX
        logwtmpx("PC-NFS", name, host, 0, USER_PROCESS);
#endif
}
#endif                          /* WTMP */


/*
**---------------------------------------------------------------------
**                      Run-process-as-user procedures
**---------------------------------------------------------------------
*/


#define READER_FD       0
#define WRITER_FD       1

static int child_pid;

static char cached_user[64] = "";
static uid_t cached_uid;
static gid_t cached_gid;

static struct sigaction old_action;
static struct sigaction new_action;
static struct itimerval timer;

int     interrupted = 0;
static FILE *pipe_handle;

void
myhandler(int dummy)
{
        interrupted = 1;
        fclose(pipe_handle);
        kill(child_pid, SIGKILL);
        msg_out("rpc.pcnfsd: su_popen timeout - killed child process");
}

void
start_watchdog(int n)
{
/*
 * Setup SIGALRM handler, force interrupt of ongoing syscall
 */

        new_action.sa_handler = myhandler;
        sigemptyset(&(new_action.sa_mask));
        new_action.sa_flags = 0;
#ifdef SA_INTERRUPT
        new_action.sa_flags |= SA_INTERRUPT;
#endif
        sigaction(SIGALRM, &new_action, &old_action);

/*
 * Set interval timer for n seconds
 */
        timer.it_interval.tv_sec = 0;
        timer.it_interval.tv_usec = 0;
        timer.it_value.tv_sec = n;
        timer.it_value.tv_usec = 0;
        setitimer(ITIMER_REAL, &timer, NULL);
        interrupted = 0;

}

void
stop_watchdog()
{
/*
 * Cancel timer
 */

        timer.it_interval.tv_sec = 0;
        timer.it_interval.tv_usec = 0;
        timer.it_value.tv_sec = 0;
        timer.it_value.tv_usec = 0;
        setitimer(ITIMER_REAL, &timer, NULL);

/*
 * restore old signal handling
 */
        sigaction(SIGALRM, &old_action, NULL);
}

FILE   *
su_popen(char *user, char *cmd, int maxtime)
{
        int     p[2];
        int     parent_fd, child_fd, pid;
        struct passwd *pw;

        if (strcmp(cached_user, user)) {
                pw = getpwnam(user);
                if (!pw)
                        pw = getpwnam("nobody");
                if (pw) {
                        cached_uid = pw->pw_uid;
                        cached_gid = pw->pw_gid;
                        strlcpy(cached_user, user, sizeof(cached_user));
                } else {
                        cached_uid = (uid_t) (-2);
                        cached_gid = (gid_t) (-2);
                        cached_user[0] = '\0';
                }
        }
        if (pipe(p) < 0) {
                msg_out("rpc.pcnfsd: unable to create pipe in su_popen");
                return (NULL);
        }
        parent_fd = p[READER_FD];
        child_fd = p[WRITER_FD];
        if ((pid = fork()) == 0) {
                int     i;

                for (i = 0; i < 10; i++)
                        if (i != child_fd)
                                (void) close(i);
                if (child_fd != 1) {
                        (void) dup2(child_fd, 1);
                        (void) close(child_fd);
                }
                dup2(1, 2);     /* let's get stderr as well */

                (void) setgid(cached_gid);
                (void) setuid(cached_uid);

                (void) execl("/bin/sh", "sh", "-c", cmd, (char *) NULL);
                _exit(255);
        }
        if (pid == -1) {
                msg_out("rpc.pcnfsd: fork failed");
                close(parent_fd);
                close(child_fd);
                return (NULL);
        }
        child_pid = pid;
        close(child_fd);
        start_watchdog(maxtime);
        pipe_handle = fdopen(parent_fd, "r");
        return (pipe_handle);
}

int
su_pclose(FILE *ptr)
{
        int     pid, status;

        stop_watchdog();

        fclose(ptr);
        if (child_pid == -1)
                return (-1);
        while ((pid = wait(&status)) != child_pid && pid != -1);
        return (pid == -1 ? -1 : status);
}



/*
** The following routine reads a file "/etc/pcnfsd.conf" if present,
** and uses it to replace certain builtin elements, like the
** name of the print spool directory. The configuration file
** Is the usual kind: Comments begin with '#', blank lines are ignored,
** and valid lines are of the form
**
**      <keyword><whitespace><value>
**
** The following keywords are recognized:
**
**      spooldir
**      printer name alias-for command
**      wtmp yes|no
*/
static void
config_from_file(void)
{
        FILE   *fd;
        char    buff[1024];
        char   *cp;
        char   *kw;
        char   *val;
        char   *arg1;
        char   *arg2;

        if ((fd = fopen("/etc/pcnfsd.conf", "r")) == NULL)
                return;
        while (fgets(buff, 1024, fd)) {
                cp = strchr(buff, '\n');
                *cp = '\0';
                cp = strchr(buff, '#');
                if (cp)
                        *cp = '\0';
                kw = strtok(buff, " \t");
                if (kw == NULL)
                        continue;
                val = strtok(NULL, " \t");
                if (val == NULL)
                        continue;
                if (!strcasecmp(kw, "spooldir")) {
                        strlcpy(sp_name, val, sizeof(sp_name));
                        continue;
                }
#ifdef WTMP
                if (!strcasecmp(kw, "wtmp")) {
                        /* assume default is YES, just look for negatives */
                        if (!strcasecmp(val, "no") ||
                            !strcasecmp(val, "off") ||
                            !strcasecmp(val, "disable") ||
                            !strcmp(val, "0"))
                                wtmp_enabled = 0;
                        continue;
                }
#endif
                if (!strcasecmp(kw, "printer")) {
                        arg1 = strtok(NULL, " \t");
                        arg2 = strtok(NULL, "");
                        (void) add_printer_alias(val, arg1, arg2);
                        continue;
                }
/*
** Add new cases here
*/
        }
        fclose(fd);
}

/*
** hack for main() - call config_from_file() then the real main
** in the rpcgen output, which is hacked by CPPFLAGS to be "mymain"
*/
#undef main

int mymain(int argc, char *argv[]);

int
main(int argc, char *argv[])
{
        config_from_file();
        return mymain(argc, argv);
}

/*
** strembedded - returns true if s1 is embedded (in any case) in s2
*/

int
strembedded(const char *s1, const char *s2)
{
        while (*s2) {
                if (!strcasecmp(s1, s2))
                        return 1;
                s2++;
        }
        return 0;
}