#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)
#define DIR_SIZE (256+1)
#define SHL_SIZE (256+1)
#define ENTRY_LENGTH 512
#define UID_MIN 100
#define M_MASK 01
#define L_MASK 02
#define C_MASK 04
#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
#define LOGNAME_EXIST 01
#define BOTH_FILES 02
#define WRITE_P_ENTRY 04
#define WRITE_S_ENTRY 010
#define NEED_DEF_UID 020
#define FOUND 040
#define LOCKED 0100
#define UATTR_FILE 0200
#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;
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/";
char pwdflr[] = "x";
char lkstring[] = "*LK*";
char nullstr[] = "";
char *msg;
#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;
uid_t high;
};
extern userattr_t *fgetuserattr(FILE *);
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;
#ifndef att
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;
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;
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;
long NIS_pos;
long cur_pos;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
tzset();
prognamp = argv[0];
if (geteuid() != 0)
bad_perm();
if (lckpwdf() != 0)
no_lock();
info_mask |= LOCKED;
passwd_st.pw_passwd = pwdflr;
passwd_st.pw_name = nullstr;
passwd_st.pw_uid = -1;
passwd_st.pw_gid = 1;
passwd_st.pw_age = nullstr;
passwd_st.pw_comment = nullstr;
passwd_st.pw_gecos = nullstr;
passwd_st.pw_dir = nullstr;
passwd_st.pw_shell = nullstr;
shadow_st.sp_namp = nullstr;
shadow_st.sp_pwdp = lkstring;
shadow_st.sp_lstchg = -1;
shadow_st.sp_min = -1;
shadow_st.sp_max = -1;
shadow_st.sp_warn = -1;
shadow_st.sp_inact = -1;
shadow_st.sp_expire = -1;
shadow_st.sp_flag = 0;
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;
while ((c = getopt(argc, argv,
"ml:c:h:u:g:s:f:e:k:A:P:R:T:oadK:")) != -1) {
switch (c) {
case 'm':
if ((A_MASK|D_MASK|M_MASK) & optn_mask)
bad_usage("Invalid combination of options");
optn_mask |= M_MASK;
break;
case 'l' :
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' :
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' :
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' :
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' :
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' :
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' :
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' :
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' :
if ((D_MASK|O_MASK) & optn_mask)
bad_usage("Invalid combination of options");
optn_mask |= O_MASK;
break;
case 'a' :
if ((A_MASK|M_MASK|D_MASK|L_MASK) & optn_mask)
bad_usage("Invalid combination of options");
optn_mask |= A_MASK;
break;
case 'd' :
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 :
{
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;
}
}
}
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");
if ((strlen(argv[optind]) == 0) || strpbrk(argv[optind], ":\n"))
bad_arg("Invalid name");
lognamp = argv [optind];
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];
}
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);
}
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;
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();
}
}
if (!(optn_mask & U_MASK) && (optn_mask & A_MASK)) {
info_mask |= NEED_DEF_UID;
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);
}
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;
info_mask |= WRITE_P_ENTRY;
while (!end_of_file) {
pw_ptr1p = fgetpwent(pwf);
if (pw_ptr1p == NULL) {
if (!feof(pwf)) {
rid_tmpf();
bad_pasf();
}
else
break;
}
if (!NIS_entry_seen)
info_mask |= WRITE_P_ENTRY;
else
info_mask &= ~WRITE_P_ENTRY;
if (info_mask & NEED_DEF_UID)
add_uid(pw_ptr1p->pw_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();
bad_uid();
}
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) {
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");
}
info_mask |= FOUND;
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);
}
}
if (optn_mask & A_MASK) {
if (!NIS_entry_seen) {
char *p;
p = strchr("+-", pw_ptr1p->pw_name[0]);
if (p != NULL) {
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();
}
}
}
if (error >= 1) {
msg = "%s: Bad entry found in /etc/passwd. Run pwconv.\n";
fprintf(stderr, gettext(msg), prognamp);
}
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");
}
if (info_mask & NEED_DEF_UID)
passwd_st.pw_uid = uid_sp->high + 1;
if (optn_mask & A_MASK) {
ck_p_sz(&passwd_st);
if (putpwent(&passwd_st, fp_ptemp)) {
rid_tmpf();
file_error();
}
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);
if (fflush(fp_ptemp) != 0 || fsync(fd_ptemp) != 0)
file_error();
(void) fclose(fp_ptemp);
if (info_mask & BOTH_FILES) {
info_mask &= ~FOUND;
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;
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) {
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) {
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();
}
}
}
if (error >= 1) {
msg = BAD_ENT_MESSAGE;
fprintf(stderr, gettext(msg), prognamp);
}
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();
}
if (NIS_entry_seen) {
file_copy(spf, NIS_pos);
}
}
if (fflush(fp_stemp) != 0 || fsync(fd_stemp) != 0)
file_error();
(void) fclose(fp_stemp);
(void) fclose(spf);
}
if (info_mask & UATTR_FILE) {
info_mask &= ~FOUND;
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 (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
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;
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) {
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);
}
if (error >= 1) {
msg = BAD_ENT_MESSAGE;
fprintf(stderr, gettext(msg), prognamp);
}
if (!(info_mask & FOUND) && !(L_MASK & optn_mask) &&
!(D_MASK & optn_mask)) {
putuserattrent(&userattr_st, fp_uatemp);
}
if (fflush(fp_uatemp) != 0 || fsync(fd_uatemp) != 0)
file_error();
(void) fclose(fp_uatemp);
(void) fclose(uaf);
}
for (i = 1; i < NSIG; i++)
(void) sigset(i, SIG_IGN);
errno = 0;
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);
}
int
rec_pwd(void)
{
if (unlink(PASSWD) || link(OPASSWD, PASSWD))
return (-1);
return (0);
}
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);
}
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;
}
void
add_uid(uid_t uid)
{
struct uid_blk *uid_p;
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;
}
}
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;
}
}
}
}
}
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);
}
void
ck_p_sz(struct passwd *pwp)
{
char ctp[128];
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");
}
}
void
ck_s_sz(struct spwd *ssp)
{
char ctp[128];
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");
}
}
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();
}
}
}