root/usr/src/cmd/passmgmt/passmgmt.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 */

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

#include <stdio.h>
#include <sys/types.h>
#include <shadow.h>
#include <pwd.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <locale.h>
#include <fcntl.h>
#include <secdb.h>
#include <user_attr.h>
#include <nss.h>

#define CMT_SIZE        (128+1) /* Argument sizes + 1 (for '\0') */
#define DIR_SIZE        (256+1)
#define SHL_SIZE        (256+1)
#define ENTRY_LENGTH    512     /* Max length of an /etc/passwd entry */
#define UID_MIN         100     /* Lower bound of default UID */

#define M_MASK          01      /* Masks for the optn_mask variable */
#define L_MASK          02      /* It keeps track of which options   */
#define C_MASK          04      /* have been entered */
#define H_MASK          010
#define U_MASK          020
#define G_MASK          040
#define S_MASK          0100
#define O_MASK          0200
#define A_MASK          0400
#define D_MASK          01000
#define F_MASK          02000
#define E_MASK          04000

#define UATTR_MASK      010000

                                        /* flags for info_mask */
#define LOGNAME_EXIST   01              /* logname exists */
#define BOTH_FILES      02              /* touch both password files */
#define WRITE_P_ENTRY   04              /* write out password entry */
#define WRITE_S_ENTRY   010             /* write out shadow entry */
#define NEED_DEF_UID    020             /* need default uid */
#define FOUND           040             /* found the entry in password file */
#define LOCKED          0100            /* did we lock the password file */
#define UATTR_FILE      0200            /* touch user_attr file */
#define BAD_ENT_MESSAGE "%s: Bad entry found in /etc/passwd.  Run pwconv.\n"

typedef struct kvopts {
        const char      option;
        const char      *key;
        char            *newvalue;
} kvopts_t;

/* mapping of extensible keywords and options */
kvopts_t ua_opts[] =  {
{ 'A',  USERATTR_AUTHS_KW },
{ 'P',  USERATTR_PROFILES_KW },
{ 'R',  USERATTR_ROLES_KW },
{ 'T',  USERATTR_TYPE_KW },
{ '\0', USERATTR_DEFAULTPROJ_KW },
{ '\0', USERATTR_LIMPRIV_KW },
{ '\0', USERATTR_DFLTPRIV_KW },
{ '\0', USERATTR_LOCK_AFTER_RETRIES_KW },
{ '\0', USERATTR_ROLEAUTH_KW },
{ '\0', USERATTR_LABELVIEW },
{ '\0', USERATTR_CLEARANCE },
{ '\0', USERATTR_MINLABEL },
{ '\0', USERATTR_AUDIT_FLAGS_KW },
};

#define UA_KEYS         (sizeof (ua_opts)/sizeof (kvopts_t))


char defdir[] = "/home/";       /* default home directory for new user */
char pwdflr[] = "x";            /* password string for /etc/passwd */
char lkstring[] = "*LK*";       /* lock string for shadow password */
char nullstr[] = "";            /* null string */
char    *msg;                   /* pointer to error message     */

#define DATMSK "DATEMSK=/etc/datemsk"

#define OUSERATTR_FILENAME      "/etc/ouser_attr"
#define USERATTR_TEMP           "/etc/uatmp"

struct uid_blk {
        struct uid_blk *link;
        uid_t low;              /* low bound for this uid block */
        uid_t high;             /* high bound for this uid block */
};

extern userattr_t *fgetuserattr(FILE *);


/*
 * Declare all functions that do not return integers.  This is here
 * to get rid of some lint messages
 */

void    uid_bcom(struct uid_blk *), add_ublk(uid_t, struct uid_blk *),
        bad_perm(void),
        bad_usage(char *), bad_arg(char *), bad_uid(void), bad_pasf(void),
        file_error(void), bad_news(void), no_lock(void), add_uid(uid_t),
        rid_tmpf(void), ck_p_sz(struct passwd *), ck_s_sz(struct spwd *),
        bad_name(char *), bad_uattr(void);

void file_copy(FILE *spf, long NIS_pos);

static FILE *fp_ptemp, *fp_stemp, *fp_uatemp;
static int fd_ptemp, fd_stemp, fd_uatemp;

/*
 * The uid_blk structure is used in the search for the default
 * uid.  Each uid_blk represent a range of uid(s) that are currently
 * used on the system.
 */


#ifndef att
/*
 * getspnan routine that ONLY looks at the local shadow file
 */
struct spwd *
local_getspnam(char *name)
{
        FILE *shadf;
        struct spwd *sp;

        if ((shadf = fopen("/etc/shadow", "r")) == NULL)
                return (NULL);

        while ((sp = fgetspent(shadf)) != NULL) {
                if (strcmp(sp->sp_namp, name) == 0)
                        break;
        }

        fclose(shadf);

        return (sp);
}
#endif

static void
putuserattrent(userattr_t *user, FILE *f)
{
        int             i, j;
        char            *key;
        char            *val;
        kv_t            *kv_pair;

        /*
         * Avoid trivial entries.  Those with no attributes or with
         * only "type=normal".  This retains backward compatibility.
         */
        if (user->attr == NULL)
                return;

        kv_pair = user->attr->data;

        for (i = j = 0; i < user->attr->length; i++) {
                key = kv_pair[i].key;
                val = kv_pair[i].value;
                if ((key == NULL) || (val == NULL))
                        break;
                if (strlen(val) == 0 ||
                    (strcmp(key, USERATTR_TYPE_KW) == 0 &&
                    strcmp(val, USERATTR_TYPE_NORMAL_KW) == 0))
                        continue;
                j++;
        }
        if (j == 0)
                return;

        (void) fprintf(f, "%s:%s:%s:%s:", user->name, user->qualifier,
            user->res1, user->res2);

        for (i = j = 0; i < user->attr->length; i++) {
                key = kv_pair[i].key;
                val = _escape(kv_pair[i].value, KV_SPECIAL);
                if ((key == NULL) || (val == NULL))
                        break;
                if (strlen(val) == 0)
                        continue;
                if (j > 0)
                        (void) fprintf(f, KV_DELIMITER);
                (void) fprintf(f, "%s=%s", key, val);
                j++;
        }
        (void) fprintf(f, "\n");
}

static void
assign_attr(userattr_t *user, const char *newkey, char *val)
{

        int             i;
        char            *key;
        kv_t            *kv_pair;
        int             avail = -1;

        if (user->attr != NULL) {
                kv_pair = user->attr->data;
                for (i = 0; i < user->attr->length; i++) {
                        key = kv_pair[i].key;
                        if (key == NULL) {
                                avail = i;
                                continue;
                        } else if (strcmp(key, newkey) == 0) {
                                kv_pair[i].value = strdup(val);
                                return;
                        }
                }

                if (avail == -1)
                        avail = user->attr->length++;
                kv_pair[avail].key = strdup(newkey);
                kv_pair[avail].value = strdup(val);
        }
}

static void
unassign_role(userattr_t *user, char *rolelist, char *role)
{

        char *roleptr;
        char *templist;
        char *temprole;
        int  length;

        roleptr = rolelist;
        templist = strdup(roleptr);
        temprole = strtok(templist, ",");
        while (temprole) {
                if (strcmp(temprole, role) == 0) {

                        length = strlen(role);
                        roleptr += temprole - templist;

                        if (*(roleptr + length) == ',')
                                length++;
                        strcpy(roleptr, roleptr + length);
                        length = strlen(roleptr) - 1;
                        if (*(roleptr + length) == ',')
                                *(roleptr + length) = '\0';
                        assign_attr(user, USERATTR_ROLES_KW, rolelist);
                        break;
                } else {
                        temprole = strtok(NULL, ",");
                }
        }
}

struct uid_blk *uid_sp;
char *prognamp;                 /* program name */
extern int errno;
int optn_mask = 0, info_mask = 0;
extern int getdate_err;

int
main(int argc, char **argv)
{
        int c, i;
        char *lognamp, *char_p;
        int end_of_file = 0;
        int error;
        long date = 0;
        FILE *pwf, *spf, *uaf;

        struct passwd *pw_ptr1p, passwd_st;
        struct spwd *sp_ptr1p, shadow_st;
        userattr_t *ua_ptr1p, userattr_st;
        static kv_t ua_kv[KV_ADD_KEYS];
        kva_t ua_kva;
        struct stat statbuf;
        struct tm *tm_ptr;
        int NIS_entry_seen;             /* NIS scanning flag */
        /*
         * NIS start pos, really pointer to first entry AFTER first
         * NIS-referant entry
         */
        long NIS_pos;
        long cur_pos;           /* Current pos, used with nis-pos above */

        (void) setlocale(LC_ALL, "");

#if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST"
#endif
        (void) textdomain(TEXT_DOMAIN);

        tzset();
        /* Get program name */
        prognamp = argv[0];

        /* Check identity */
        if (geteuid() != 0)
                bad_perm();

        /* Lock the password file(s) */

        if (lckpwdf() != 0)
                no_lock();
        info_mask |= LOCKED;            /* remember we locked */

        /* initialize the two structures */

        passwd_st.pw_passwd = pwdflr;           /* bogus password */
        passwd_st.pw_name = nullstr;            /* login name */
        passwd_st.pw_uid = -1;                  /* no uid */
        passwd_st.pw_gid = 1;                   /* default gid */
        passwd_st.pw_age = nullstr;             /* no aging info. */
        passwd_st.pw_comment = nullstr; /* no comments */
        passwd_st.pw_gecos = nullstr;           /* no comments */
        passwd_st.pw_dir = nullstr;             /* no default directory */
        passwd_st.pw_shell = nullstr;           /* no default shell */

        shadow_st.sp_namp = nullstr;    /* no name */
        shadow_st.sp_pwdp = lkstring;   /* locked password */
        shadow_st.sp_lstchg = -1;       /* no lastchanged date */
        shadow_st.sp_min = -1;  /* no min */
        shadow_st.sp_max = -1;  /* no max */
        shadow_st.sp_warn = -1;         /* no warn */
        shadow_st.sp_inact = -1;        /* no inactive */
        shadow_st.sp_expire = -1;       /* no expire */
        shadow_st.sp_flag = 0;  /* no flag */

        userattr_st.name = nullstr;
        userattr_st.qualifier = nullstr;
        userattr_st.res1 = nullstr;
        userattr_st.res2 = nullstr;

        ua_kva.length = 1;
        ua_kv[0].key = USERATTR_TYPE_KW;
        ua_kv[0].value = USERATTR_TYPE_NORMAL_KW;
        ua_kva.data = ua_kv;
        userattr_st.attr = &ua_kva;

        /* parse the command line */

        while ((c = getopt(argc, argv,
            "ml:c:h:u:g:s:f:e:k:A:P:R:T:oadK:")) != -1) {

                switch (c) {
                case 'm':
                        /* Modify */

                        if ((A_MASK|D_MASK|M_MASK) & optn_mask)
                                bad_usage("Invalid combination of options");

                        optn_mask |= M_MASK;
                        break;

                case 'l' :
                        /* Change logname */

                        if ((A_MASK|D_MASK|L_MASK) & optn_mask)
                                bad_usage("Invalid combination of options");

                        if (strpbrk(optarg, ":\n") ||
                            strlen(optarg) == 0)
                                bad_arg("Invalid argument to option -l");

                        optn_mask |= L_MASK;
                        passwd_st.pw_name = optarg;
                        shadow_st.sp_namp = optarg;
                        userattr_st.name = optarg;
                        break;

                case 'f' :
                        /* set inactive */

                        if ((D_MASK|F_MASK) & optn_mask)
                                bad_usage("Invalid combination of options");
                        if (((shadow_st.sp_inact =
                            strtol(optarg, &char_p, 10)) < (long)0) ||
                            (*char_p != '\0') ||
                            strlen(optarg) == 0)
                                bad_arg("Invalid argument to option -f");
                        if (shadow_st.sp_inact == 0)
                                shadow_st.sp_inact = -1;
                        optn_mask |= F_MASK;
                        break;

                case 'e' :
                        /* set expire date */

                        if ((D_MASK|E_MASK) & optn_mask)
                                bad_usage("Invalid combination of options");

                        if ((strlen(optarg)) < (size_t)2)
                                shadow_st.sp_expire = -1;
                        else {
                                putenv(DATMSK);
                                if ((tm_ptr =  getdate(optarg)) == NULL) {
                                        msg = "Invalid argument to option -e";
                                        bad_arg(msg);
                                }
                                if ((date =  mktime(tm_ptr)) < 0) {
                                        msg = "Invalid argument to option -e";
                                        bad_arg(msg);
                                }
                                shadow_st.sp_expire = (date / DAY);
                                if (shadow_st.sp_expire <= DAY_NOW) {
                                        msg = "Invalid argument to option -e";
                                        bad_arg(msg);
                                }
                        }

                        optn_mask |= E_MASK;
                        break;

                case 'c' :
                        /* The comment */

                        if ((D_MASK|C_MASK) & optn_mask)
                                bad_usage("Invalid combination of options");

                        if (strlen(optarg) > (size_t)CMT_SIZE ||
                            strpbrk(optarg, ":\n"))
                                bad_arg("Invalid argument to option -c");

                        optn_mask |= C_MASK;
                        passwd_st.pw_comment = optarg;
                        passwd_st.pw_gecos = optarg;
                        break;

                case 'h' :
                        /* The home directory */

                        if ((D_MASK|H_MASK) & optn_mask)
                                bad_usage("Invalid combination of options");

                        if (strlen(optarg) > (size_t)DIR_SIZE ||
                            strpbrk(optarg, ":\n"))
                                bad_arg("Invalid argument to option -h");

                        optn_mask |= H_MASK;
                        passwd_st.pw_dir = optarg;
                        break;

                case 'u' :
                        /* The uid */

                        if ((D_MASK|U_MASK) & optn_mask)
                                bad_usage("Invalid combination of options");

                        optn_mask |= U_MASK;
                        passwd_st.pw_uid = (uid_t)strtol(optarg, &char_p, 10);
                        if ((*char_p != '\0') ||
                            (passwd_st.pw_uid < 0) ||
                            (strlen(optarg) == 0))
                                bad_arg("Invalid argument to option -u");

                        break;

                case 'g' :
                        /* The gid */

                        if ((D_MASK|G_MASK) & optn_mask)
                                bad_usage("Invalid combination of options");

                        optn_mask |= G_MASK;
                        passwd_st.pw_gid = (gid_t)strtol(optarg, &char_p, 10);

                        if ((*char_p != '\0') || (passwd_st.pw_gid < 0) ||
                            (strlen(optarg) == 0))
                                bad_arg("Invalid argument to option -g");
                        break;

                case 's' :
                        /* The shell */

                        if ((D_MASK|S_MASK) & optn_mask)
                                bad_usage("Invalid combination of options");

                        if (strlen(optarg) > (size_t)SHL_SIZE ||
                            strpbrk(optarg, ":\n"))
                                bad_arg("Invalid argument to option -s");

                        optn_mask |= S_MASK;
                        passwd_st.pw_shell = optarg;
                        break;

                case 'o' :
                        /* Override unique uid  */

                        if ((D_MASK|O_MASK) & optn_mask)
                                bad_usage("Invalid combination of options");

                        optn_mask |= O_MASK;
                        break;

                case 'a' :
                        /* Add */

                        if ((A_MASK|M_MASK|D_MASK|L_MASK) & optn_mask)
                                bad_usage("Invalid combination of options");

                        optn_mask |= A_MASK;
                        break;

                case 'd' :
                        /* Delete */

                        if ((D_MASK|M_MASK|L_MASK|C_MASK|
                            H_MASK|U_MASK|G_MASK|S_MASK|
                            O_MASK|A_MASK) & optn_mask)
                                bad_usage("Invalid combination of options");

                        optn_mask |= D_MASK;
                        break;

                case 'K':
                        if (D_MASK & optn_mask)
                                bad_usage("Invalid combination of options");

                        char_p = strchr(optarg, '=');
                        if (char_p == NULL)
                                bad_usage("Missing value in -K option");

                        *char_p++ = '\0';

                        for (i = 0; i < UA_KEYS; i++) {
                                if (strcmp(optarg, ua_opts[i].key) == 0) {
                                        ua_opts[i].newvalue =
                                            _escape(char_p, KV_SPECIAL);
                                        assign_attr(&userattr_st, optarg,
                                            char_p);
                                        break;
                                }
                        }
                        if (i == UA_KEYS)
                                bad_usage("bad key");
                        optn_mask |= UATTR_MASK;
                        break;

                case '?' :

                        bad_usage("");
                        break;

                default :
                        /* Extended User Attributes */
                        {
                                int j;

                                for (j = 0; j < UA_KEYS; j++) {
                                        if (ua_opts[j].option == (char)c) {
                                                if ((D_MASK) & optn_mask)
                                                        bad_usage("Invalid "
                                                        "combination of "
                                                        " options");
                                                optn_mask |= UATTR_MASK;
                                                assign_attr(&userattr_st,
                                                    ua_opts[j].key,
                                                    _escape(optarg,
                                                    KV_SPECIAL));
                                                ua_opts[j].newvalue =
                                                    _escape(optarg, KV_SPECIAL);
                                                break;
                                        }
                                }
                                break;
                        }
                }
        }

        /* check command syntax for the following errors */
        /* too few or too many arguments */
        /* no -a -m or -d option */
        /* -o without -u */
        /* -m with no other option */

        if (optind == argc || argc > (optind+1) ||
            !((A_MASK|M_MASK|D_MASK) & optn_mask) ||
            ((optn_mask & O_MASK) && !(optn_mask & U_MASK)) ||
            ((optn_mask & M_MASK) &&
            !(optn_mask &
            (L_MASK|C_MASK|H_MASK|U_MASK|G_MASK|S_MASK|F_MASK|
            E_MASK|UATTR_MASK))))
                bad_usage("Invalid command syntax");

        /* null string argument or bad characters ? */
        if ((strlen(argv[optind]) == 0) || strpbrk(argv[optind], ":\n"))
                bad_arg("Invalid name");

        lognamp = argv [optind];

        /*
         * if we are adding a new user or modifying an existing user
         * (not the logname), then copy logname into the two data
         *  structures
         */

        if ((A_MASK & optn_mask) ||
            ((M_MASK & optn_mask) && !(optn_mask & L_MASK))) {
                passwd_st.pw_name = argv [optind];
                shadow_st.sp_namp = argv [optind];
                userattr_st.name = argv [optind];
        }

        /* Put in directory if we are adding and we need a default */

        if (!(optn_mask & H_MASK) && (optn_mask & A_MASK)) {
                if ((passwd_st.pw_dir = malloc((size_t)DIR_SIZE)) == NULL)
                        file_error();

                *passwd_st.pw_dir = '\0';
                (void) strcat(passwd_st.pw_dir, defdir);
                (void) strcat(passwd_st.pw_dir, lognamp);
        }

        /* Check the number of password files we are touching */

        if ((!((M_MASK & optn_mask) && !(L_MASK & optn_mask))) ||
            ((M_MASK & optn_mask) && ((E_MASK & optn_mask) ||
            (F_MASK & optn_mask))))
                info_mask |= BOTH_FILES;

        if ((D_MASK|L_MASK|UATTR_MASK) & optn_mask)
                info_mask |= UATTR_FILE;

        /* Open the temporary file(s) with appropriate permission mask */
        /* and the appropriate owner */

        if (stat(PASSWD, &statbuf) < 0)
                file_error();

        fd_ptemp = open(PASSTEMP, O_CREAT|O_EXCL|O_WRONLY, statbuf.st_mode);
        if (fd_ptemp == -1) {
                if (errno == EEXIST) {
                        if (unlink(PASSTEMP)) {
                                msg = "%s: warning: cannot unlink %s\n";
                                (void) fprintf(stderr, gettext(msg), prognamp,
                                    PASSTEMP);
                        }
                        fd_ptemp = open(PASSTEMP, O_CREAT|O_EXCL|O_WRONLY,
                            statbuf.st_mode);
                        if (fd_ptemp == -1) {
                                file_error();
                        }

                } else
                        file_error();
        }
        fp_ptemp = fdopen(fd_ptemp, "w");
        if (fp_ptemp == NULL)
                file_error();
        error = fchown(fd_ptemp, statbuf.st_uid, statbuf.st_gid);
        if (error == 0)
                error = fchmod(fd_ptemp, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
        if (error != 0) {
                (void) fclose(fp_ptemp);
                if (unlink(PASSTEMP)) {
                        msg = "%s: warning: cannot unlink %s\n";
                        (void) fprintf(stderr, gettext(msg), prognamp,
                            PASSTEMP);
                }
                file_error();
        }

        if (info_mask & BOTH_FILES) {
                if (stat(SHADOW, &statbuf) < 0) {
                        rid_tmpf();
                        file_error();
                }
                fd_stemp = open(SHADTEMP, O_CREAT|O_EXCL|O_WRONLY,
                    statbuf.st_mode);
                if (fd_stemp == -1) {
                        if (errno == EEXIST) {
                                if (unlink(SHADTEMP)) {
                                        msg = "%s: warning: cannot unlink %s\n";
                                        (void) fprintf(stderr, gettext(msg),
                                            prognamp, SHADTEMP);
                                }
                                fd_stemp = open(SHADTEMP,
                                    O_CREAT|O_EXCL|O_WRONLY, statbuf.st_mode);
                                if (fd_stemp == -1) {
                                        rid_tmpf();
                                        file_error();
                                }

                        } else {
                                rid_tmpf();
                                file_error();
                        }
                }
                fp_stemp = fdopen(fd_stemp, "w");
                if (fp_stemp == NULL) {
                        rid_tmpf();
                        file_error();
                }
                error = fchown(fd_stemp, statbuf.st_uid, statbuf.st_gid);
                if (error == 0)
                        error = fchmod(fd_stemp, S_IRUSR);
                if (error != 0) {
                        rid_tmpf();
                        file_error();
                }
        }

        if (info_mask & UATTR_FILE) {
                if (stat(USERATTR_FILENAME, &statbuf) < 0) {
                        rid_tmpf();
                        file_error();
                }
                fd_uatemp = open(USERATTR_TEMP, O_CREAT|O_EXCL|O_WRONLY,
                    statbuf.st_mode);
                if (fd_uatemp == -1) {
                        if (errno == EEXIST) {
                                if (unlink(USERATTR_TEMP)) {
                                        msg = "%s: warning: cannot unlink %s\n";
                                        (void) fprintf(stderr, gettext(msg),
                                            prognamp, USERATTR_TEMP);
                                }
                                fd_uatemp = open(USERATTR_TEMP,
                                    O_CREAT|O_EXCL|O_WRONLY, statbuf.st_mode);
                                if (fd_uatemp == -1) {
                                        rid_tmpf();
                                        file_error();
                                }

                        } else {
                                rid_tmpf();
                                file_error();
                        }
                }
                fp_uatemp = fdopen(fd_uatemp, "w");
                if (fp_uatemp == NULL) {
                        rid_tmpf();
                        file_error();
                }
                error = fchown(fd_uatemp, statbuf.st_uid, statbuf.st_gid);
                if (error == 0)
                        error = fchmod(fd_uatemp,
                            S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
                if (error != 0) {
                        rid_tmpf();
                        file_error();
                }
        }
        /* Default uid needed ? */

        if (!(optn_mask & U_MASK) && (optn_mask & A_MASK)) {
                /* mark it in the information mask */
                info_mask |= NEED_DEF_UID;

                /* create the head of the uid number list */
                uid_sp = malloc(sizeof (struct uid_blk));
                if (uid_sp == NULL) {
                        rid_tmpf();
                        file_error();
                }

                uid_sp->link = NULL;
                uid_sp->low = (UID_MIN -1);
                uid_sp->high = (UID_MIN -1);
        }

        /*
         * This next section is modified to allow for NIS passwd file
         * conventions.  In the case where a password entry was being
         * added to the password file, the original AT&T code read
         * the entire password file in, noted any information needed, and
         * copied the entries to a temporary file.  Then the new entry
         * was added to the temporary file, and the temporary file was
         * moved to be the real password file.
         *
         * The problem is, that with NIS compatability, we want to add new
         * entries BEFORE the first NIS-referrant entry, so as not to have
         * any surprises.  To accomplish this without extensively modifying
         * the logic of the code below, as soon as a NIS-referrant entry is
         * found we stop copying entries to the TEMP file and instead we
         * remember
         * the first NIS entry and where we found it, scan the rest of the
         * password file without copying entries, then write the new entry, copy
         * the stored password entry, then copy the rest of the password file.
         */


        error = 0;

        if ((pwf = fopen("/etc/passwd", "r")) == NULL) {
                rid_tmpf();
                if (errno == ENOENT)
                        bad_news();
                else
                        file_error();
        }

        NIS_entry_seen = 0;
        cur_pos = 0;
        /* The while loop for reading PASSWD entries */
        info_mask |= WRITE_P_ENTRY;

        while (!end_of_file) {
                pw_ptr1p = fgetpwent(pwf);
                if (pw_ptr1p == NULL) {
                        if (!feof(pwf)) {
                                /* A real error - report it and exit */
                                rid_tmpf();
                                bad_pasf();
                        }
                        else
                                break;
                }

                if (!NIS_entry_seen)
                        info_mask |= WRITE_P_ENTRY;
                else
                        info_mask &= ~WRITE_P_ENTRY;

                /*
                 * Set up the uid usage blocks to find the first
                 * available uid above UID_MIN, if needed
                 */

                if (info_mask & NEED_DEF_UID)
                        add_uid(pw_ptr1p->pw_uid);

                /* Check for unique UID */

                if (strcmp(lognamp, pw_ptr1p->pw_name) &&
                    (pw_ptr1p->pw_uid == passwd_st.pw_uid) &&
                    ((optn_mask & U_MASK) && !(optn_mask & O_MASK))) {
                        rid_tmpf();             /* get rid of temp files */
                        bad_uid();
                }

                /* Check for unique new logname */

                if (strcmp(lognamp, pw_ptr1p->pw_name) == 0 &&
                    optn_mask & L_MASK &&
                    strcmp(pw_ptr1p->pw_name, passwd_st.pw_name) == 0) {
                        rid_tmpf();
#ifdef att
                        if (!getspnam(pw_ptr1p->pw_name))
#else
                        if (!local_getspnam(pw_ptr1p->pw_name))
#endif
                                bad_pasf();
                        else
                                bad_name("logname already exists");
                }

                if (strcmp(lognamp, pw_ptr1p->pw_name) == 0) {

                        /* no good if we want to add an existing logname */
                        if (optn_mask & A_MASK) {
                                rid_tmpf();
#ifdef att
                                if (!getspnam(lognamp))
#else
                                if (!local_getspnam(lognamp))
#endif
                                        bad_pasf();
                                else
                                        bad_name("name already exists");
                        }

                        /* remember we found it */
                        info_mask |= FOUND;

                        /* Do not write it out on the fly */
                        if (optn_mask & D_MASK)
                                info_mask &= ~WRITE_P_ENTRY;

                        if (optn_mask & M_MASK) {

#ifdef att
                                if (!getspnam(lognamp))
#else
                                if (!local_getspnam(lognamp))
#endif
                                {
                                        rid_tmpf();
                                        bad_pasf();
                                }
                                if (optn_mask & L_MASK)
                                        pw_ptr1p->pw_name = passwd_st.pw_name;

                                if (optn_mask & U_MASK)
                                        pw_ptr1p->pw_uid = passwd_st.pw_uid;

                                if (optn_mask & G_MASK)
                                        pw_ptr1p->pw_gid = passwd_st.pw_gid;

                                if (optn_mask & C_MASK) {
                                        pw_ptr1p->pw_comment =
                                            passwd_st.pw_comment;

                                        pw_ptr1p->pw_gecos =
                                            passwd_st.pw_comment;
                                }

                                if (optn_mask & H_MASK)
                                        pw_ptr1p->pw_dir = passwd_st.pw_dir;

                                if (optn_mask & S_MASK)
                                        pw_ptr1p->pw_shell = passwd_st.pw_shell;
                                ck_p_sz(pw_ptr1p);  /* check entry size */
                        }
                }

                if (optn_mask & A_MASK) {
                        if (!NIS_entry_seen) {
                                char    *p;
                                p  = strchr("+-", pw_ptr1p->pw_name[0]);
                                if (p != NULL) {
                                        /*
                                         * Found first NIS entry.
                                         * so remember it.
                                         */
                                        NIS_pos = cur_pos;
                                        NIS_entry_seen = 1;
                                        info_mask &= ~WRITE_P_ENTRY;
                                }
                                else
                                        cur_pos = ftell(pwf);
                        }
                }

                if (info_mask & WRITE_P_ENTRY) {
                        if (putpwent(pw_ptr1p, fp_ptemp)) {
                                rid_tmpf();
                                file_error();
                        }
                }
        } /* end-of-while-loop */

        if (error >= 1) {
                msg = "%s: Bad entry found in /etc/passwd.  Run pwconv.\n";
                fprintf(stderr, gettext(msg), prognamp);
        }

        /* Cannot find the target entry and we are deleting or modifying */

        if (!(info_mask & FOUND) && (optn_mask & (D_MASK|M_MASK))) {
                rid_tmpf();
#ifdef att
                if (getspnam(lognamp) != NULL)
#else
                if (local_getspnam(lognamp) != NULL)
#endif
                        bad_pasf();
                else
                        bad_name("name does not exist");
        }

        /* First available uid above UID_MIN is ... */

        if (info_mask & NEED_DEF_UID)
                passwd_st.pw_uid = uid_sp->high + 1;

        /* Write out the added entry now */

        if (optn_mask & A_MASK) {
                ck_p_sz(&passwd_st);    /* Check entry size */
                if (putpwent(&passwd_st, fp_ptemp)) {
                        rid_tmpf();
                        file_error();
                }
                /*
                 * Now put out the rest of the password file, if needed.
                 */
                if (NIS_entry_seen) {
                        int n;
                        char buf[1024];

                        if (fseek(pwf, NIS_pos, SEEK_SET) < 0) {
                                rid_tmpf();
                                file_error();
                        }
                        while ((n = fread(buf, sizeof (char), 1024, pwf)) > 0) {
                                if (fwrite(buf, sizeof (char), n, fp_ptemp)
                                    != n) {
                                        rid_tmpf();
                                        file_error();
                                }
                        }
                }
        }

        (void) fclose(pwf);

        /* flush and sync the file before closing it */
        if (fflush(fp_ptemp) != 0 || fsync(fd_ptemp) != 0)
                file_error();

        /* Now we are done with PASSWD */
        (void) fclose(fp_ptemp);

        /* Do this if we are touching both password files */


        if (info_mask & BOTH_FILES) {
                info_mask &= ~FOUND;            /* Reset FOUND flag */

                /* The while loop for reading SHADOW entries */
                info_mask |= WRITE_S_ENTRY;

                end_of_file = 0;
                errno = 0;
                error = 0;

                NIS_entry_seen = 0;
                cur_pos = 0;

                if ((spf = fopen("/etc/shadow", "r")) == NULL) {
                        rid_tmpf();
                        file_error();
                }

                while (!end_of_file) {
                        sp_ptr1p = fgetspent(spf);
                        if (sp_ptr1p == NULL) {
                                if (!feof(spf)) {
                                        rid_tmpf();
                                        bad_pasf();
                                }
                                else
                                        break;
                        }

                        if (!NIS_entry_seen)
                                info_mask |= WRITE_S_ENTRY;
                        else
                                info_mask &= ~WRITE_S_ENTRY;

                        /*
                         * See if the new logname already exist in the
                         * shadow passwd file
                         */
                        if ((optn_mask & M_MASK) &&
                            strcmp(lognamp, shadow_st.sp_namp) != 0 &&
                            strcmp(sp_ptr1p->sp_namp, shadow_st.sp_namp) == 0) {
                                rid_tmpf();
                                bad_pasf();
                        }

                        if (strcmp(lognamp, sp_ptr1p->sp_namp) == 0) {
                                info_mask |= FOUND;
                                if (optn_mask & A_MASK) {
                                        /* password file inconsistent */
                                        rid_tmpf();
                                        bad_pasf();
                                }

                                if (optn_mask & M_MASK) {
                                        sp_ptr1p->sp_namp = shadow_st.sp_namp;
                                        if (F_MASK & optn_mask)
                                                sp_ptr1p->sp_inact =
                                                    shadow_st.sp_inact;
                                        if (E_MASK & optn_mask)
                                                sp_ptr1p->sp_expire =
                                                    shadow_st.sp_expire;

                                        ck_s_sz(sp_ptr1p);
                                }

                                if (optn_mask & D_MASK)
                                        info_mask &= ~WRITE_S_ENTRY;
                        }

                        if (optn_mask & A_MASK) {
                                if (!NIS_entry_seen) {
                                        char    *p;
                                        p = strchr("+-", sp_ptr1p->sp_namp[0]);
                                        if (p != NULL) {
                                                /*
                                                 * Found first NIS entry.
                                                 * so remember it.
                                                 */
                                                NIS_pos = cur_pos;
                                                NIS_entry_seen = 1;
                                                info_mask &= ~WRITE_S_ENTRY;
                                        }
                                        else
                                                cur_pos = ftell(spf);
                                }
                        }

                        if (info_mask & WRITE_S_ENTRY) {
                                if (putspent(sp_ptr1p, fp_stemp)) {
                                        rid_tmpf();
                                        file_error();
                                }
                        }

                } /* end-of-while-loop */

                if (error >= 1) {

                        msg = BAD_ENT_MESSAGE;
                        fprintf(stderr, gettext(msg), prognamp);
                }

                /*
                 * If we cannot find the entry and we are deleting or
                 *  modifying
                 */

                if (!(info_mask & FOUND) && (optn_mask & (D_MASK|M_MASK))) {
                        rid_tmpf();
                        bad_pasf();
                }

                if (optn_mask & A_MASK) {
                        ck_s_sz(&shadow_st);
                        if (putspent(&shadow_st, fp_stemp)) {
                                rid_tmpf();
                                file_error();
                        }

                        /*
                         * Now put out the rest of the shadow file, if needed.
                         */
                        if (NIS_entry_seen) {
                                file_copy(spf, NIS_pos);
                        }
                }

                /* flush and sync the file before closing it */
                if (fflush(fp_stemp) != 0 || fsync(fd_stemp) != 0)
                        file_error();
                (void) fclose(fp_stemp);

                /* Done with SHADOW */
                (void) fclose(spf);

        } /* End of if info_mask */

        if (info_mask & UATTR_FILE) {
                info_mask &= ~FOUND;            /* Reset FOUND flag */

                /* The while loop for reading USER_ATTR entries */
                info_mask |= WRITE_S_ENTRY;

                end_of_file = 0;
                errno = 0;
                error = 0;

                NIS_entry_seen = 0;
                cur_pos = 0;

                if ((uaf = fopen(USERATTR_FILENAME, "r")) == NULL) {
                        rid_tmpf();
                        file_error();
                }

                while (!end_of_file) {
                        ua_ptr1p = fgetuserattr(uaf);
                        if (ua_ptr1p == NULL) {
                                if (!feof(uaf)) {
                                        rid_tmpf();
                                        bad_uattr();
                                }
                                else
                                        break;
                        }

                        if (ua_ptr1p->name[0] == '#') {
                                /*
                                 * If this is a comment, write it back as it
                                 * is.
                                 */
                                if (ua_ptr1p->qualifier[0] == '\0' &&
                                    ua_ptr1p->res1[0] == '\0' &&
                                    ua_ptr1p->res2[0] == '\0' &&
                                    (ua_ptr1p->attr == NULL ||
                                    ua_ptr1p->attr->length == 0))
                                        (void) fprintf(fp_uatemp, "%s\n",
                                            ua_ptr1p->name);
                                else
                                        /*
                                         * This is a commented user_attr entry;
                                         * reformat it, and write it back.
                                         */
                                        putuserattrent(ua_ptr1p, fp_uatemp);
                                free_userattr(ua_ptr1p);
                                continue;
                        }

                        if (!NIS_entry_seen)
                                info_mask |= WRITE_S_ENTRY;
                        else
                                info_mask &= ~WRITE_S_ENTRY;

                        /*
                         * See if the new logname already exist in the
                         * user_attr file
                         */
                        if ((optn_mask & M_MASK) &&
                            strcmp(lognamp, userattr_st.name) != 0 &&
                            strcmp(ua_ptr1p->name, userattr_st.name) == 0) {
                                rid_tmpf();
                                bad_pasf();
                        }

                        if (strcmp(lognamp, ua_ptr1p->name) == 0) {
                                info_mask |= FOUND;
                                if (optn_mask & A_MASK) {
                                        /* password file inconsistent */
                                        rid_tmpf();
                                        bad_pasf();
                                }

                                if (optn_mask & M_MASK) {
                                        int     j;
                                        char    *value;

                                        for (j = 0; j < UA_KEYS; j++) {
                                                if (ua_opts[j].newvalue != NULL)
                                                        continue;
                                                value =
                                                    kva_match(ua_ptr1p->attr,
                                                    (char *)ua_opts[j].key);
                                                if (value == NULL)
                                                        continue;
                                                assign_attr(&userattr_st,
                                                    ua_opts[j].key,
                                                    value);
                                        }
                                        free_userattr(ua_ptr1p);
                                        ua_ptr1p = &userattr_st;
                                }

                                if (optn_mask & D_MASK)
                                        info_mask &= ~WRITE_S_ENTRY;
                        } else if (optn_mask & D_MASK) {
                                char *rolelist;

                                rolelist = kva_match(ua_ptr1p->attr,
                                    USERATTR_ROLES_KW);
                                if (rolelist) {
                                        unassign_role(ua_ptr1p,
                                            rolelist, lognamp);
                                }
                        }

                        if (info_mask & WRITE_S_ENTRY) {
                                putuserattrent(ua_ptr1p, fp_uatemp);
                        }

                        if (!(optn_mask & M_MASK))
                                free_userattr(ua_ptr1p);
                } /* end-of-while-loop */

                if (error >= 1) {

                        msg = BAD_ENT_MESSAGE;
                        fprintf(stderr, gettext(msg), prognamp);
                }

                /*
                 * Add entry in user_attr if masks is UATTR_MASK
                 * We don't need to do anything for L_MASK if there's
                 * no user_attr entry for the user being modified.
                 */
                if (!(info_mask & FOUND) && !(L_MASK & optn_mask) &&
                    !(D_MASK & optn_mask)) {
                        putuserattrent(&userattr_st, fp_uatemp);
                }

                /* flush and sync the file before closing it */
                if (fflush(fp_uatemp) != 0 || fsync(fd_uatemp) != 0)
                        file_error();
                (void) fclose(fp_uatemp);

                /* Done with USERATTR */
                (void) fclose(uaf);

        } /* End of if info_mask */
        /* ignore all signals */

        for (i = 1; i < NSIG; i++)
                (void) sigset(i, SIG_IGN);

        errno = 0;              /* For correcting sigset to SIGKILL */

        if (unlink(OPASSWD) && access(OPASSWD, 0) == 0)
                file_error();

        if (link(PASSWD, OPASSWD) == -1)
                        file_error();


        if (rename(PASSTEMP, PASSWD) == -1) {
                if (link(OPASSWD, PASSWD))
                        bad_news();
                file_error();
        }


        if (info_mask & BOTH_FILES) {

                if (unlink(OSHADOW) && access(OSHADOW, 0) == 0) {
                        if (rec_pwd())
                                bad_news();
                        else
                                file_error();
                }

                if (link(SHADOW, OSHADOW) == -1) {
                        if (rec_pwd())
                                bad_news();
                        else
                                file_error();
                }


                if (rename(SHADTEMP, SHADOW) == -1) {
                        if (rename(OSHADOW, SHADOW) == -1)
                                bad_news();

                        if (rec_pwd())
                                bad_news();
                        else
                                file_error();
                }

        }
        if (info_mask & UATTR_FILE) {
                if (unlink(OUSERATTR_FILENAME) &&
                    access(OUSERATTR_FILENAME, 0) == 0) {
                        if (rec_pwd())
                                bad_news();
                        else
                                file_error();
                }

                if (link(USERATTR_FILENAME, OUSERATTR_FILENAME) == -1) {
                        if (rec_pwd())
                                bad_news();
                        else
                                file_error();
                }


                if (rename(USERATTR_TEMP, USERATTR_FILENAME) == -1) {
                        if (rename(OUSERATTR_FILENAME, USERATTR_FILENAME) == -1)
                                bad_news();

                        if (rec_pwd())
                                bad_news();
                        else
                                file_error();
                }

        }

        ulckpwdf();

        /*
         * Return 0 status, indicating success
         */
        return (0);

}  /* end of main */

/* Try to recover the old password file */

int
rec_pwd(void)
{
        if (unlink(PASSWD) || link(OPASSWD, PASSWD))
                return (-1);

        return (0);
}

/* combine two uid_blk's */

void
uid_bcom(struct uid_blk *uid_p)
{
        struct uid_blk *uid_tp;

        uid_tp = uid_p->link;
        uid_p->high = uid_tp->high;
        uid_p->link = uid_tp->link;

        free(uid_tp);
}

/* add a new uid_blk */

void
add_ublk(uid_t num, struct uid_blk *uid_p)
{
        struct uid_blk *uid_tp;

        uid_tp = malloc(sizeof (struct uid_blk));
        if (uid_tp == NULL) {
                rid_tmpf();
                file_error();
        }

        uid_tp->high = uid_tp->low = num;
        uid_tp->link = uid_p->link;
        uid_p->link = uid_tp;
}

/*
 *      Here we are using a linked list of uid_blk to keep track of all
 *      the used uids.  Each uid_blk represents a range of used uid,
 *      with low represents the low inclusive end and high represents
 *      the high inclusive end.  In the beginning, we initialize a linked
 *      list of one uid_blk with low = high = (UID_MIN-1).  This was
 *      done in main().
 *      Each time we read in another used uid, we add it onto the linked
 *      list by either making a new uid_blk, decrementing the low of
 *      an existing uid_blk, incrementing the high of an existing
 *      uid_blk, or combining two existing uid_blks.  After we finished
 *      building this linked list, the first available uid above or
 *      equal to UID_MIN is the high of the first uid_blk in the linked
 *      list + 1.
 */
/* add_uid() adds uid to the link list of used uids */
void
add_uid(uid_t uid)
{
        struct uid_blk *uid_p;
        /* Only keep track of the ones above UID_MIN */

        if (uid >= UID_MIN) {
                uid_p = uid_sp;

                while (uid_p != NULL) {

                        if (uid_p->link != NULL) {

                                if (uid >= uid_p->link->low)
                                        uid_p = uid_p->link;

                                else if (uid >= uid_p->low &&
                                    uid <= uid_p->high) {
                                        uid_p = NULL;
                                }

                                else if (uid == (uid_p->high+1)) {

                                        if (++uid_p->high ==
                                            (uid_p->link->low - 1)) {
                                                uid_bcom(uid_p);
                                        }
                                        uid_p = NULL;
                                }

                                else if (uid == (uid_p->link->low - 1)) {
                                        uid_p->link->low --;
                                        uid_p = NULL;
                                }

                                else if (uid < uid_p->link->low) {
                                        add_ublk(uid, uid_p);
                                        uid_p = NULL;
                                }
                        } /* if uid_p->link */

                        else {

                                if (uid == (uid_p->high + 1)) {
                                        uid_p->high++;
                                        uid_p = NULL;
                                } else if (uid >= uid_p->low &&
                                    uid <= uid_p->high) {
                                        uid_p = NULL;
                                } else {
                                        add_ublk(uid, uid_p);
                                        uid_p = NULL;
                                }
                        } /* else */
                } /* while uid_p */

        } /* if uid */
}

void
bad_perm(void)
{
        (void) fprintf(stderr, gettext("%s: Permission denied\n"), prognamp);
        exit(1);
}

void
bad_usage(char *sp)
{
        if (strlen(sp) != 0)
                (void) fprintf(stderr, "%s: %s\n", prognamp, gettext(sp));
        (void) fprintf(stderr, gettext("Usage:\n\
%s -a [-c comment] [-h homedir] [-u uid [-o]] [-g gid] \n\
            [-s shell] [-f inactive] [-e expire] name\n\
%s -m  -c comment | -h homedir | -u uid [-o] | -g gid |\n\
            -s shell | -f inactive | -e expire  |  -l logname  name\n\
%s -d name\n"), prognamp, prognamp, prognamp);
        if (info_mask & LOCKED)
                ulckpwdf();
        exit(2);
}

void
bad_arg(char *s)
{
        (void) fprintf(stderr, "%s: %s\n", prognamp, gettext(s));

        if (info_mask & LOCKED)
                ulckpwdf();
        exit(3);
}

void
bad_name(char *s)
{
        (void) fprintf(stderr, "%s: %s\n", prognamp, gettext(s));
        ulckpwdf();
        exit(9);
}

void
bad_uid(void)
{
        (void) fprintf(stderr, gettext("%s: UID in use\n"), prognamp);

        ulckpwdf();
        exit(4);
}

void
bad_pasf(void)
{
        msg = "%s: Inconsistent password files\n";
        (void) fprintf(stderr, gettext(msg), prognamp);

        ulckpwdf();
        exit(5);
}

void
bad_uattr(void)
{
        msg = "%s: Bad user_attr database\n";
        (void) fprintf(stderr, gettext(msg), prognamp);

        ulckpwdf();
        exit(5);
}

void
file_error(void)
{
        msg = "%s: Unexpected failure.  Password files unchanged\n";
        (void) fprintf(stderr, gettext(msg), prognamp);

        ulckpwdf();
        exit(6);
}

void
bad_news(void)
{
        msg = "%s: Unexpected failure.  Password file(s) missing\n";
        (void) fprintf(stderr, gettext(msg), prognamp);

        ulckpwdf();
        exit(7);
}

void
no_lock(void)
{
        msg = "%s: Password file(s) busy.  Try again later\n";
        (void) fprintf(stderr, gettext(msg), prognamp);

        exit(8);
}

/* Check for the size of the whole passwd entry */
void
ck_p_sz(struct passwd *pwp)
{
        char ctp[128];

        /* Ensure that the combined length of the individual */
        /* fields will fit in a passwd entry. The 1 accounts for the */
        /* newline and the 6 accounts for the colons (:'s) */
        if (((int)strlen(pwp->pw_name) + 1 +
            sprintf(ctp, "%d", pwp->pw_uid) +
            sprintf(ctp, "%d", pwp->pw_gid) +
            (int)strlen(pwp->pw_comment) +
            (int)strlen(pwp->pw_dir) +
            (int)strlen(pwp->pw_shell) + 6) > (ENTRY_LENGTH-1)) {
                rid_tmpf();
                bad_arg("New password entry too long");
        }
}

/* Check for the size of the whole passwd entry */
void
ck_s_sz(struct spwd *ssp)
{
        char ctp[128];

        /* Ensure that the combined length of the individual */
        /* fields will fit in a shadow entry. The 1 accounts for the */
        /* newline and the 7 accounts for the colons (:'s) */
        if (((int)strlen(ssp->sp_namp) + 1 +
            (int)strlen(ssp->sp_pwdp) +
            sprintf(ctp, "%d", ssp->sp_lstchg) +
            sprintf(ctp, "%d", ssp->sp_min) +
            sprintf(ctp, "%d", ssp->sp_max) +
            sprintf(ctp, "%d", ssp->sp_warn) +
            sprintf(ctp, "%d", ssp->sp_inact) +
            sprintf(ctp, "%d", ssp->sp_expire) + 7) > (ENTRY_LENGTH - 1)) {
                rid_tmpf();
                bad_arg("New password entry too long");
        }
}

/* Get rid of the temp files */
void
rid_tmpf(void)
{
        (void) fclose(fp_ptemp);

        if (unlink(PASSTEMP)) {
                msg = "%s: warning: cannot unlink %s\n";
                (void) fprintf(stderr, gettext(msg), prognamp, PASSTEMP);
        }

        if (info_mask & BOTH_FILES) {
                (void) fclose(fp_stemp);

                if (unlink(SHADTEMP)) {
                        msg = "%s: warning: cannot unlink %s\n";
                        (void) fprintf(stderr, gettext(msg), prognamp,
                            SHADTEMP);
                }
        }

        if (info_mask & UATTR_FILE) {
                (void) fclose(fp_uatemp);

                if (unlink(USERATTR_TEMP)) {
                        msg = "%s: warning: cannot unlink %s\n";
                        (void) fprintf(stderr, gettext(msg), prognamp,
                            USERATTR_TEMP);
                }
        }
}

void
file_copy(FILE *spf, long NIS_pos)
{
        int n;
        char buf[1024];

        if (fseek(spf, NIS_pos, SEEK_SET) < 0) {
                rid_tmpf();
                file_error();
        }
        while ((n = fread(buf, sizeof (char), 1024, spf)) > 0) {
                if (fwrite(buf, sizeof (char), n, fp_stemp) != n) {
                        rid_tmpf();
                        file_error();
                }
        }
}