root/usr.sbin/rpc.pcnfsd/pcnfsd_v2.c
/*      $NetBSD: pcnfsd_v2.c,v 1.14 2018/01/23 21:06:25 sevan Exp $     */

/* RE_SID: @(%)/usr/dosnfs/shades_SCCS/unix/pcnfsd/v2/src/SCCS/s.pcnfsd_v2.c 1.2 91/12/18 13:26:13 SMI */
/*
**=====================================================================
** Copyright (c) 1986,1987,1988,1989,1990,1991 by Sun Microsystems, Inc.
**      @(#)pcnfsd_v2.c 1.2     12/18/91
**=====================================================================
*/
/*
**=====================================================================
**             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/stat.h>

#include <grp.h>
#include <netdb.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifdef USE_YP
#include <rpcsvc/ypclnt.h>
#endif

#ifndef SYSV
#include <sys/wait.h>
#endif

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

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

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

/*
**=====================================================================
**                      C O D E   S E C T I O N                       *
**=====================================================================
*/


static char no_comment[] = "No comment";
static char not_supported[] = "Not supported";
static char pcnfsd_version[] = "@(#)pcnfsd_v2.c 1.2 - rpc.pcnfsd V2.0 (c) 1991 Sun Technology Enterprises, Inc.";

/*ARGSUSED*/
void   *
pcnfsd2_null_2_svc(void *arg, struct svc_req *req)
{
        static char dummy;
        return ((void *) &dummy);
}

v2_auth_results *
pcnfsd2_auth_2_svc(v2_auth_args *arg, struct svc_req *req)
{
        static v2_auth_results r;

        char    uname[32];
        char    pw[64];
        int     c1, c2;
        struct passwd *p;
        static u_int extra_gids[EXTRAGIDLEN];
        static char home[256];
#ifdef USE_YP
        char   *yphome;
        char   *cp;
#endif                          /* USE_YP */


        r.stat = AUTH_RES_FAIL; /* assume failure */
        r.uid = (int) -2;
        r.gid = (int) -2;
        r.cm = &no_comment[0];
        r.gids.gids_len = 0;
        r.gids.gids_val = &extra_gids[0];
        home[0] = '\0';
        r.home = &home[0];
        r.def_umask = umask(0);
        (void) umask(r.def_umask);      /* or use 022 */

        scramble(arg->id, uname);
        scramble(arg->pw, pw);

#ifdef USER_CACHE
        if (check_cache(uname, pw, &r.uid, &r.gid)) {
                r.stat = AUTH_RES_OK;
#ifdef WTMP
                wlogin(uname, req);
#endif
                fillin_extra_groups
                    (uname, r.gid, &r.gids.gids_len, extra_gids);
#ifdef USE_YP
                yphome = find_entry(uname, "auto.home");
                if (yphome) {
                        strlcpy(home, yphome, sizeof(home));
                        free(yphome);
                        cp = strchr(home, ':');
                        cp++;
                        cp = strchr(cp, ':');
                        if (cp)
                                *cp = '/';
                }
#endif
                return (&r);
        }
#endif

        p = get_password(uname);
        if (p == NULL)
                return (&r);

        c1 = strlen(pw);
        c2 = strlen(p->pw_passwd);
        if ((c1 && !c2) || (c2 && !c1) ||
            (strcmp(p->pw_passwd, crypt(pw, p->pw_passwd)))) {
                return (&r);
        }
        r.stat = AUTH_RES_OK;
        r.uid = p->pw_uid;
        r.gid = p->pw_gid;
#ifdef WTMP
        wlogin(uname, req);
#endif
        fillin_extra_groups(uname, r.gid, &r.gids.gids_len, extra_gids);

#ifdef USE_YP
        yphome = find_entry(uname, "auto.home");
        if (yphome) {
                strlcpy(home, yphome, sizeof(home));
                free(yphome);
                cp = strchr(home, ':');
                cp++;
                cp = strchr(cp, ':');
                if (cp)
                        *cp = '/';
        }
#endif

#ifdef USER_CACHE
        add_cache_entry(p);
#endif

        return (&r);

}

v2_pr_init_results *
pcnfsd2_pr_init_2_svc(v2_pr_init_args *arg, struct svc_req *req)
{
        static v2_pr_init_results res;

        res.stat =
            (pirstat) pr_init(arg->system, arg->pn, &res.dir);
        res.cm = &no_comment[0];


        return (&res);
}

v2_pr_start_results *
pcnfsd2_pr_start_2_svc(v2_pr_start_args *arg, struct svc_req *req)
{
        static v2_pr_start_results res;

        res.stat =
            (psrstat) pr_start2(arg->system, arg->pn, arg->user,
            arg->file, arg->opts, &res.id);
        res.cm = &no_comment[0];

        return (&res);
}
/*ARGSUSED*/
v2_pr_list_results *
pcnfsd2_pr_list_2_svc(void *arg, struct svc_req *req)
{
        static v2_pr_list_results res;

        if (printers == NULL)
                (void) build_pr_list();
        res.cm = &no_comment[0];
        res.printers = printers;

        return (&res);
}

v2_pr_queue_results *
pcnfsd2_pr_queue_2_svc(v2_pr_queue_args *arg, struct svc_req *req)
{
        static v2_pr_queue_results res;

        res.stat = build_pr_queue(arg->pn, arg->user,
            arg->just_mine, &res.qlen, &res.qshown);
        res.cm = &no_comment[0];
        res.just_yours = arg->just_mine;
        res.jobs = queue;


        return (&res);
}

v2_pr_status_results *
pcnfsd2_pr_status_2_svc(v2_pr_status_args *arg, struct svc_req *req)
{
        static v2_pr_status_results res;
        static char status[128];

        res.stat = get_pr_status(arg->pn, &res.avail, &res.printing,
            &res.qlen, &res.needs_operator, &status[0], sizeof(status));
        res.status = &status[0];
        res.cm = &no_comment[0];

        return (&res);
}

v2_pr_cancel_results *
pcnfsd2_pr_cancel_2_svc(v2_pr_cancel_args *arg, struct svc_req *req)
{
        static v2_pr_cancel_results res;

        res.stat = pr_cancel(arg->pn, arg->user, arg->id);
        res.cm = &no_comment[0];

        return (&res);
}
/*ARGSUSED*/
v2_pr_requeue_results *
pcnfsd2_pr_requeue_2_svc(v2_pr_requeue_args *arg, struct svc_req *req)
{
        static v2_pr_requeue_results res;
        res.stat = PC_RES_FAIL;
        res.cm = &not_supported[0];

        return (&res);
}
/*ARGSUSED*/
v2_pr_hold_results *
pcnfsd2_pr_hold_2_svc(v2_pr_hold_args *arg, struct svc_req *req)
{
        static v2_pr_hold_results res;

        res.stat = PC_RES_FAIL;
        res.cm = &not_supported[0];

        return (&res);
}
/*ARGSUSED*/
v2_pr_release_results *
pcnfsd2_pr_release_2_svc(v2_pr_release_args *arg, struct svc_req *req)
{
        static v2_pr_release_results res;

        res.stat = PC_RES_FAIL;
        res.cm = &not_supported[0];

        return (&res);
}
/*ARGSUSED*/
v2_pr_admin_results *
pcnfsd2_pr_admin_2_svc(v2_pr_admin_args *arg, struct svc_req *req)
{
        static v2_pr_admin_results res;
/*
** The default action for admin is to fail.
** If someone wishes to implement an administration
** mechanism, and isn't worried about the security
** holes, go right ahead.
*/

        res.cm = &not_supported[0];
        res.stat = PI_RES_FAIL;

        return (&res);
}

void
free_mapreq_results(mapreq_res p)
{
        if (p->mapreq_next)
                free_mapreq_results(p->mapreq_next);    /* recurse */
        if (p->name)
                (void) free(p->name);
        (void) free(p);
        return;
}

static char *my_strdup(const char *);

static char *
my_strdup(const char *s)
{
        size_t len;
        char   *r;
        len = strlen(s);
        r = (char *) grab(len + 1);
        memcpy(r, s, len + 1);
        return (r);
}

v2_mapid_results *
pcnfsd2_mapid_2_svc(v2_mapid_args *arg, struct svc_req *req)
{
        static v2_mapid_results res;
        struct passwd *p_passwd;
        struct group *p_group;

        mapreq_arg a;
        mapreq_res next_r;
        mapreq_res last_r = NULL;


        if (res.res_list) {
                free_mapreq_results(res.res_list);
                res.res_list = NULL;
        }
        a = arg->req_list;
        while (a) {
                next_r = (struct mapreq_res_item *)
                    grab(sizeof(struct mapreq_res_item));
                next_r->stat = MAP_RES_UNKNOWN;
                next_r->req = a->req;
                next_r->id = a->id;
                next_r->name = NULL;
                next_r->mapreq_next = NULL;

                if (last_r == NULL)
                        res.res_list = next_r;
                else
                        last_r->mapreq_next = next_r;
                last_r = next_r;
                switch (a->req) {
                case MAP_REQ_UID:
                        p_passwd = getpwuid((uid_t) a->id);
                        if (p_passwd) {
                                next_r->name = my_strdup(p_passwd->pw_name);
                                next_r->stat = MAP_RES_OK;
                        }
                        break;
                case MAP_REQ_GID:
                        p_group = getgrgid((gid_t) a->id);
                        if (p_group) {
                                next_r->name = my_strdup(p_group->gr_name);
                                next_r->stat = MAP_RES_OK;
                        }
                        break;
                case MAP_REQ_UNAME:
                        next_r->name = my_strdup(a->name);
                        p_passwd = getpwnam(a->name);
                        if (p_passwd) {
                                next_r->id = p_passwd->pw_uid;
                                next_r->stat = MAP_RES_OK;
                        }
                        break;
                case MAP_REQ_GNAME:
                        next_r->name = my_strdup(a->name);
                        p_group = getgrnam(a->name);
                        if (p_group) {
                                next_r->id = p_group->gr_gid;
                                next_r->stat = MAP_RES_OK;
                        }
                        break;
                }
                if (next_r->name == NULL)
                        next_r->name = my_strdup("");
                a = a->mapreq_next;
        }

        res.cm = &no_comment[0];

        return (&res);
}


/*ARGSUSED*/
v2_alert_results *
pcnfsd2_alert_2_svc(v2_alert_args *arg, struct svc_req *req)
{
        static v2_alert_results res;

        res.stat = ALERT_RES_FAIL;
        res.cm = &not_supported[0];

        return (&res);
}
/*ARGSUSED*/
v2_info_results *
pcnfsd2_info_2_svc(v2_info_args *arg, struct svc_req *req)
{
        static v2_info_results res;
        static int facilities[FACILITIESMAX];
        static int onetime = 1;

#define UNSUPPORTED -1
#define QUICK 100
#define SLOW 2000

        if (onetime) {
                onetime = 0;
                facilities[PCNFSD2_NULL] = QUICK;
                facilities[PCNFSD2_INFO] = QUICK;
                facilities[PCNFSD2_PR_INIT] = QUICK;
                facilities[PCNFSD2_PR_START] = SLOW;
                facilities[PCNFSD2_PR_LIST] = QUICK;    /* except first time */
                facilities[PCNFSD2_PR_QUEUE] = SLOW;
                facilities[PCNFSD2_PR_STATUS] = SLOW;
                facilities[PCNFSD2_PR_CANCEL] = SLOW;
                facilities[PCNFSD2_PR_ADMIN] = UNSUPPORTED;
                facilities[PCNFSD2_PR_REQUEUE] = UNSUPPORTED;
                facilities[PCNFSD2_PR_HOLD] = UNSUPPORTED;
                facilities[PCNFSD2_PR_RELEASE] = UNSUPPORTED;
                facilities[PCNFSD2_MAPID] = QUICK;
                facilities[PCNFSD2_AUTH] = QUICK;
                facilities[PCNFSD2_ALERT] = QUICK;
        }
        res.facilities.facilities_len = PCNFSD2_ALERT + 1;
        res.facilities.facilities_val = facilities;

        res.vers = &pcnfsd_version[0];
        res.cm = &no_comment[0];

        return (&res);
}



void
fillin_extra_groups(char *uname, gid_t main_gid, int *len, gid_t extra_gids[EXTRAGIDLEN])
{
        struct group *grp;
        __aconst char *__aconst *members;
        int     n = 0;

        setgrent();

        while (n < EXTRAGIDLEN) {
                grp = getgrent();
                if (grp == NULL)
                        break;
                if (grp->gr_gid == main_gid)
                        continue;
                for (members = grp->gr_mem; members && *members; members++) {
                        if (!strcmp(*members, uname)) {
                                extra_gids[n++] = grp->gr_gid;
                                break;
                        }
                }
        }
        endgrent();
        *len = n;
}

#ifdef USE_YP
/* the following is from rpcsvc/yp_prot.h */
#define YPMAXDOMAIN 64

/*
 * find_entry returns NULL on any error (printing a message) and
 * otherwise returns a pointer to the malloc'd result. The caller
 * is responsible for free()ing the result string.
 */
char   *
find_entry(const char *key, const char *map)
{
        int     err;
        char   *val = NULL;
        char   *cp;
        int     len = 0;
        static char domain[YPMAXDOMAIN + 1];

        if (getdomainname(domain, YPMAXDOMAIN)) {
                msg_out("rpc.pcnfsd: getdomainname failed");
                return (NULL);
        }
        if ((err = yp_bind(domain)) != 0) {
#ifdef  DEBUG
                msg_out("rpc.pcnfsd: yp_bind failed");
#endif
                return (NULL);
        }
        err = yp_match(domain, map, key, strlen(key), &val, &len);

        if (err) {
                msg_out("rpc.pcnfsd: yp_match failed");
                if (val)
                        free(val);
                return (NULL);
        }
        if ((cp = strchr(val, '\n')) != NULL)
                *cp = '\0';     /* in case we get an extra NL at the end */
        return (val);
}
#endif