root/usr/src/cmd/oamuser/user/userdefs.c
/*
 * 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
 * or http://www.opensolaris.org/os/licensing.
 * 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
 */

/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/*
 * Copyright (c) 2013 RackTop Systems.
 * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 */

#include        <sys/types.h>
#include        <stdio.h>
#include        <string.h>
#include        <userdefs.h>
#include        <user_attr.h>
#include        <limits.h>
#include        <stdlib.h>
#include        <stddef.h>
#include        <time.h>
#include        <unistd.h>
#include        "userdisp.h"
#include        "funcs.h"
#include        "messages.h"

/* Print out a NL when the line gets too long */
#define PRINTNL()       \
        if (outcount > 40) { \
                outcount = 0; \
                (void) fprintf(fptr, "\n"); \
        }

#define SKIPWS(ptr)     while (*(ptr) == ' ' || *(ptr) == '\t') (ptr)++

static char *dup_to_nl(char *);

static struct userdefs defaults = {
        DEFRID, DEFGROUP, DEFGNAME, DEFPARENT, DEFSKL,
        DEFSHL, DEFINACT, DEFEXPIRE, DEFAUTH, DEFPROF,
        DEFROLE, DEFPROJ, DEFPROJNAME, DEFLIMPRIV,
        DEFDFLTPRIV, DEFLOCK_AFTER_RETRIES, DEFROLEAUTH
};

#define INT     0
#define STR     1
#define PROJID  2

#define DEFOFF(field)           offsetof(struct userdefs, field)
#define FIELD(up, pe, type)     (*(type *)((char *)(up) + (pe)->off))

typedef struct parsent {
        const char *name;       /* deffoo= */
        const size_t nmsz;      /* length of def= string (excluding \0) */
        const int type;         /* type of entry */
        const ptrdiff_t off;    /* offset in userdefs structure */
        const char *uakey;      /* user_attr key, if defined */
} parsent_t;

static const parsent_t tab[] = {
        { GIDSTR,       sizeof (GIDSTR) - 1,    INT,    DEFOFF(defgroup) },
        { GNAMSTR,      sizeof (GNAMSTR) - 1,   STR,    DEFOFF(defgname) },
        { PARSTR,       sizeof (PARSTR) - 1,    STR,    DEFOFF(defparent) },
        { SKLSTR,       sizeof (SKLSTR) - 1,    STR,    DEFOFF(defskel) },
        { SHELLSTR,     sizeof (SHELLSTR) - 1,  STR,    DEFOFF(defshell) },
        { INACTSTR,     sizeof (INACTSTR) - 1,  INT,    DEFOFF(definact) },
        { EXPIRESTR,    sizeof (EXPIRESTR) - 1, STR,    DEFOFF(defexpire) },
        { AUTHSTR,      sizeof (AUTHSTR) - 1,   STR,    DEFOFF(defauth),
                USERATTR_AUTHS_KW },
        { ROLESTR,      sizeof (ROLESTR) - 1,   STR,    DEFOFF(defrole),
                USERATTR_ROLES_KW },
        { PROFSTR,      sizeof (PROFSTR) - 1,   STR,    DEFOFF(defprof),
                USERATTR_PROFILES_KW },
        { PROJSTR,      sizeof (PROJSTR) - 1,   PROJID, DEFOFF(defproj) },
        { PROJNMSTR,    sizeof (PROJNMSTR) - 1, STR,    DEFOFF(defprojname) },
        { LIMPRSTR,     sizeof (LIMPRSTR) - 1,  STR,    DEFOFF(deflimpriv),
                USERATTR_LIMPRIV_KW },
        { DFLTPRSTR,    sizeof (DFLTPRSTR) - 1, STR,    DEFOFF(defdfltpriv),
                USERATTR_DFLTPRIV_KW },
        { LOCK_AFTER_RETRIESSTR,        sizeof (LOCK_AFTER_RETRIESSTR) - 1,
                STR,    DEFOFF(deflock_after_retries),
                USERATTR_LOCK_AFTER_RETRIES_KW },
        { ROLEAUTHSTR,  sizeof (ROLEAUTHSTR) - 1, STR,  DEFOFF(defroleauth),
                USERATTR_ROLEAUTH_KW },
};

#define NDEF    (sizeof (tab) / sizeof (parsent_t))

FILE *defptr;           /* default file - fptr */

static const parsent_t *
scan(char **start_p)
{
        static int ind = NDEF - 1;
        char *cur_p = *start_p;
        int lastind = ind;

        if (!*cur_p || *cur_p == '\n' || *cur_p == '#')
                return (NULL);

        /*
         * The magic in this loop is remembering the last index when
         * reentering the function; the entries above are also used to
         * order the output to the default file.
         */
        do {
                ind++;
                ind %= NDEF;

                if (strncmp(cur_p, tab[ind].name, tab[ind].nmsz) == 0) {
                        *start_p = cur_p + tab[ind].nmsz;
                        return (&tab[ind]);
                }
        } while (ind != lastind);

        return (NULL);
}

/*
 * getusrdef - access the user defaults file.  If it doesn't exist,
 *              then returns default values of (values in userdefs.h):
 *              defrid = 100
 *              defgroup = 1
 *              defgname = other
 *              defparent = /home
 *              defskel = /usr/sadm/skel
 *              defshell = /bin/sh
 *              definact = 0
 *              defexpire = 0
 *              defauth = 0
 *              defprof = 0
 *              defrole = 0
 *              defroleauth = role
 *
 *      If getusrdef() is unable to access the defaults file, it
 *      returns a NULL pointer.
 *
 *      If user defaults file exists, then getusrdef uses values
 *      in it to override the above values.
 */

struct userdefs *
getusrdef(char *usertype)
{
        char instr[512], *ptr;
        const parsent_t *pe;

        if (is_role(usertype)) {
                if ((defptr = fopen(DEFROLEFILE, "r")) == NULL) {
                        defaults.defshell = DEFROLESHL;
                        defaults.defprof = DEFROLEPROF;
                        defaults.defroleauth = DEFROLEROLEAUTH;
                        return (&defaults);
                }
        } else {
                if ((defptr = fopen(DEFFILE, "r")) == NULL)
                        return (&defaults);
        }

        while (fgets(instr, sizeof (instr), defptr) != NULL) {
                ptr = instr;

                SKIPWS(ptr);

                if (*ptr == '#')
                        continue;

                pe = scan(&ptr);

                if (pe != NULL) {
                        switch (pe->type) {
                        case INT:
                                FIELD(&defaults, pe, int) =
                                    (int)strtol(ptr, NULL, 10);
                                break;
                        case PROJID:
                                FIELD(&defaults, pe, projid_t) =
                                    (projid_t)strtol(ptr, NULL, 10);
                                break;
                        case STR:
                                FIELD(&defaults, pe, char *) = dup_to_nl(ptr);
                                break;
                        }
                }
        }

        (void) fclose(defptr);

        return (&defaults);
}

static char *
dup_to_nl(char *from)
{
        char *res = strdup(from);

        char *p = strchr(res, '\n');
        if (p)
                *p = '\0';

        return (res);
}

void
dispusrdef(FILE *fptr, unsigned flags, char *usertype)
{
        struct userdefs *deflts = getusrdef(usertype);
        int outcount = 0;

        /* Print out values */

        if (flags & D_GROUP) {
                outcount += fprintf(fptr, "group=%s,%ld  ",
                    deflts->defgname, deflts->defgroup);
                PRINTNL();
        }

        if (flags & D_PROJ) {
                outcount += fprintf(fptr, "project=%s,%ld  ",
                    deflts->defprojname, deflts->defproj);
                PRINTNL();
        }

        if (flags & D_BASEDIR) {
                outcount += fprintf(fptr, "basedir=%s  ", deflts->defparent);
                PRINTNL();
        }

        if (flags & D_RID) {
                outcount += fprintf(fptr, "rid=%ld  ", deflts->defrid);
                PRINTNL();
        }

        if (flags & D_SKEL) {
                outcount += fprintf(fptr, "skel=%s  ", deflts->defskel);
                PRINTNL();
        }

        if (flags & D_SHELL) {
                outcount += fprintf(fptr, "shell=%s  ", deflts->defshell);
                PRINTNL();
        }

        if (flags & D_INACT) {
                outcount += fprintf(fptr, "inactive=%d  ", deflts->definact);
                PRINTNL();
        }

        if (flags & D_EXPIRE) {
                outcount += fprintf(fptr, "expire=%s  ", deflts->defexpire);
                PRINTNL();
        }

        if (flags & D_AUTH) {
                outcount += fprintf(fptr, "auths=%s  ", deflts->defauth);
                PRINTNL();
        }

        if (flags & D_PROF) {
                outcount += fprintf(fptr, "profiles=%s  ", deflts->defprof);
                PRINTNL();
        }

        if ((flags & D_ROLE) &&
            (!is_role(usertype))) {
                outcount += fprintf(fptr, "roles=%s  ", deflts->defrole);
                PRINTNL();
        }

        if (flags & D_LPRIV) {
                outcount += fprintf(fptr, "limitpriv=%s  ", deflts->deflimpriv);
                PRINTNL();
        }

        if (flags & D_DPRIV) {
                outcount += fprintf(fptr, "defaultpriv=%s  ",
                    deflts->defdfltpriv);
                PRINTNL();
        }

        if (flags & D_LOCK) {
                outcount += fprintf(fptr, "lock_after_retries=%s  ",
                    deflts->deflock_after_retries);
                PRINTNL();
        }

        if ((flags & D_ROLEAUTH) && is_role(usertype)) {
                outcount += fprintf(fptr, "roleauth=%s  ",
                    deflts->defroleauth);
        }

        if (outcount > 0)
                (void) fprintf(fptr, "\n");
}

/*
 * putusrdef -
 *      changes default values in defadduser file
 */
int
putusrdef(struct userdefs *defs, char *usertype)
{
        time_t timeval;         /* time value from time */
        int i;
        ptrdiff_t skip;
        char *hdr;

        /*
         * file format is:
         * #<tab>Default values for adduser.  Changed mm/dd/yy hh:mm:ss.
         * defgroup=m   (m=default group id)
         * defgname=str1        (str1=default group name)
         * defparent=str2       (str2=default base directory)
         * definactive=x        (x=default inactive)
         * defexpire=y          (y=default expire)
         * defproj=z            (z=numeric project id)
         * defprojname=str3     (str3=default project name)
         * ... etc ...
         */

        if (is_role(usertype)) {
                if ((defptr = fopen(DEFROLEFILE, "w")) == NULL) {
                        errmsg(M_FAILED);
                        return (EX_UPDATE);
                }
        } else {
                if ((defptr = fopen(DEFFILE, "w")) == NULL) {
                        errmsg(M_FAILED);
                        return (EX_UPDATE);
                }
        }

        if (lockf(fileno(defptr), F_LOCK, 0) != 0) {
                /* print error if can't lock whole of DEFFILE */
                errmsg(M_UPDATE, "created");
                return (EX_UPDATE);
        }

        if (is_role(usertype)) {
                /* If it's a role, we must skip the defrole field */
                skip = DEFOFF(defrole);
                hdr = FHEADER_ROLE;
        } else {
                /* If it's a user, we must skip the defroleauth field */
                skip = DEFOFF(defroleauth);
                hdr = FHEADER;
        }

        /* get time */
        timeval = time(NULL);

        /* write it to file */
        if (fprintf(defptr, "%s%s\n", hdr, ctime(&timeval)) <= 0) {
                errmsg(M_UPDATE, "created");
                return (EX_UPDATE);
        }

        for (i = 0; i < NDEF; i++) {
                int res = 0;

                if (tab[i].off == skip)
                        continue;

                switch (tab[i].type) {
                case INT:
                        res = fprintf(defptr, "%s%d\n", tab[i].name,
                            FIELD(defs, &tab[i], int));
                        break;
                case STR:
                        res = fprintf(defptr, "%s%s\n", tab[i].name,
                            FIELD(defs, &tab[i], char *));
                        break;
                case PROJID:
                        res = fprintf(defptr, "%s%d\n", tab[i].name,
                            (int)FIELD(defs, &tab[i], projid_t));
                        break;
                }

                if (res <= 0) {
                        errmsg(M_UPDATE, "created");
                        return (EX_UPDATE);
                }
        }

        (void) lockf(fileno(defptr), F_ULOCK, 0);
        (void) fclose(defptr);

        return (EX_SUCCESS);
}

/* Export command line keys to defaults for useradd -D */
void
update_def(struct userdefs *ud)
{
        int i;

        for (i = 0; i < NDEF; i++) {
                char *val;
                if (tab[i].uakey != NULL &&
                    (val = getsetdefval(tab[i].uakey, NULL)) != NULL)
                        FIELD(ud, &tab[i], char *) = val;
        }
}

/* Import default keys for ordinary useradd */
void
import_def(struct userdefs *ud)
{
        int i;

        for (i = 0; i < NDEF; i++) {
                if (tab[i].uakey != NULL && tab[i].type == STR) {
                        char *val = FIELD(ud, &tab[i], char *);
                        if (val == getsetdefval(tab[i].uakey, val))
                                nkeys ++;
                }
        }
}