4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
25 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
29 #include <sys/types.h>
42 #include <user_attr.h>
45 #define CMT_SIZE (128+1) /* Argument sizes + 1 (for '\0') */
46 #define DIR_SIZE (256+1)
47 #define SHL_SIZE (256+1)
48 #define ENTRY_LENGTH 512 /* Max length of an /etc/passwd entry */
49 #define UID_MIN 100 /* Lower bound of default UID */
51 #define M_MASK 01 /* Masks for the optn_mask variable */
52 #define L_MASK 02 /* It keeps track of which options */
53 #define C_MASK 04 /* have been entered */
64 #define UATTR_MASK 010000
66 /* flags for info_mask */
67 #define LOGNAME_EXIST 01 /* logname exists */
68 #define BOTH_FILES 02 /* touch both password files */
69 #define WRITE_P_ENTRY 04 /* write out password entry */
70 #define WRITE_S_ENTRY 010 /* write out shadow entry */
71 #define NEED_DEF_UID 020 /* need default uid */
72 #define FOUND 040 /* found the entry in password file */
73 #define LOCKED 0100 /* did we lock the password file */
74 #define UATTR_FILE 0200 /* touch user_attr file */
75 #define BAD_ENT_MESSAGE "%s: Bad entry found in /etc/passwd. Run pwconv.\n"
77 typedef struct kvopts
{
83 /* mapping of extensible keywords and options */
84 kvopts_t ua_opts
[] = {
85 { 'A', USERATTR_AUTHS_KW
},
86 { 'P', USERATTR_PROFILES_KW
},
87 { 'R', USERATTR_ROLES_KW
},
88 { 'T', USERATTR_TYPE_KW
},
89 { '\0', USERATTR_DEFAULTPROJ_KW
},
90 { '\0', USERATTR_LIMPRIV_KW
},
91 { '\0', USERATTR_DFLTPRIV_KW
},
92 { '\0', USERATTR_LOCK_AFTER_RETRIES_KW
},
93 { '\0', USERATTR_LABELVIEW
},
94 { '\0', USERATTR_CLEARANCE
},
95 { '\0', USERATTR_MINLABEL
},
96 { '\0', USERATTR_IDLECMD_KW
},
97 { '\0', USERATTR_IDLETIME_KW
},
98 { '\0', USERATTR_AUDIT_FLAGS_KW
},
101 #define UA_KEYS (sizeof (ua_opts)/sizeof (kvopts_t))
104 char defdir
[] = "/home/"; /* default home directory for new user */
105 char pwdflr
[] = "x"; /* password string for /etc/passwd */
106 char lkstring
[] = "*LK*"; /* lock string for shadow password */
107 char nullstr
[] = ""; /* null string */
108 char *msg
; /* pointer to error message */
110 #define DATMSK "DATEMSK=/etc/datemsk"
112 #define OUSERATTR_FILENAME "/etc/ouser_attr"
113 #define USERATTR_TEMP "/etc/uatmp"
116 struct uid_blk
*link
;
117 uid_t low
; /* low bound for this uid block */
118 uid_t high
; /* high bound for this uid block */
121 extern userattr_t
*fgetuserattr(FILE *);
125 * Declare all functions that do not return integers. This is here
126 * to get rid of some lint messages
129 void uid_bcom(struct uid_blk
*), add_ublk(uid_t
, struct uid_blk
*),
131 bad_usage(char *), bad_arg(char *), bad_uid(void), bad_pasf(void),
132 file_error(void), bad_news(void), no_lock(void), add_uid(uid_t
),
133 rid_tmpf(void), ck_p_sz(struct passwd
*), ck_s_sz(struct spwd
*),
134 bad_name(char *), bad_uattr(void);
136 void file_copy(FILE *spf
, long NIS_pos
);
138 static FILE *fp_ptemp
, *fp_stemp
, *fp_uatemp
;
139 static int fd_ptemp
, fd_stemp
, fd_uatemp
;
142 * The uid_blk structure is used in the search for the default
143 * uid. Each uid_blk represent a range of uid(s) that are currently
144 * used on the system.
150 * getspnan routine that ONLY looks at the local shadow file
153 local_getspnam(char *name
)
158 if ((shadf
= fopen("/etc/shadow", "r")) == NULL
)
161 while ((sp
= fgetspent(shadf
)) != NULL
) {
162 if (strcmp(sp
->sp_namp
, name
) == 0)
173 putuserattrent(userattr_t
*user
, FILE *f
)
181 * Avoid trivial entries. Those with no attributes or with
182 * only "type=normal". This retains backward compatibility.
184 if (user
->attr
== NULL
)
187 kv_pair
= user
->attr
->data
;
189 for (i
= j
= 0; i
< user
->attr
->length
; i
++) {
190 key
= kv_pair
[i
].key
;
191 val
= kv_pair
[i
].value
;
192 if ((key
== NULL
) || (val
== NULL
))
194 if (strlen(val
) == 0 ||
195 (strcmp(key
, USERATTR_TYPE_KW
) == 0 &&
196 strcmp(val
, USERATTR_TYPE_NORMAL_KW
) == 0))
203 (void) fprintf(f
, "%s:%s:%s:%s:", user
->name
, user
->qualifier
,
204 user
->res1
, user
->res2
);
206 for (i
= j
= 0; i
< user
->attr
->length
; i
++) {
207 key
= kv_pair
[i
].key
;
208 val
= _escape(kv_pair
[i
].value
, KV_SPECIAL
);
209 if ((key
== NULL
) || (val
== NULL
))
211 if (strlen(val
) == 0)
214 (void) fprintf(f
, KV_DELIMITER
);
215 (void) fprintf(f
, "%s=%s", key
, val
);
218 (void) fprintf(f
, "\n");
222 assign_attr(userattr_t
*user
, const char *newkey
, char *val
)
230 if (user
->attr
!= NULL
) {
231 kv_pair
= user
->attr
->data
;
232 for (i
= 0; i
< user
->attr
->length
; i
++) {
233 key
= kv_pair
[i
].key
;
237 } else if (strcmp(key
, newkey
) == 0) {
238 kv_pair
[i
].value
= strdup(val
);
244 avail
= user
->attr
->length
++;
245 kv_pair
[avail
].key
= strdup(newkey
);
246 kv_pair
[avail
].value
= strdup(val
);
251 unassign_role(userattr_t
*user
, char *rolelist
, char *role
)
260 templist
= strdup(roleptr
);
261 temprole
= strtok(templist
, ",");
263 if (strcmp(temprole
, role
) == 0) {
265 length
= strlen(role
);
266 roleptr
+= temprole
- templist
;
268 if (*(roleptr
+ length
) == ',')
270 strcpy(roleptr
, roleptr
+ length
);
271 length
= strlen(roleptr
) - 1;
272 if (*(roleptr
+ length
) == ',')
273 *(roleptr
+ length
) = '\0';
274 assign_attr(user
, USERATTR_ROLES_KW
, rolelist
);
277 temprole
= strtok(NULL
, ",");
282 struct uid_blk
*uid_sp
;
283 char *prognamp
; /* program name */
285 int optn_mask
= 0, info_mask
= 0;
286 extern int getdate_err
;
289 main(int argc
, char **argv
)
292 char *lognamp
, *char_p
;
296 FILE *pwf
, *spf
, *uaf
;
298 struct passwd
*pw_ptr1p
, passwd_st
;
299 struct spwd
*sp_ptr1p
, shadow_st
;
300 userattr_t
*ua_ptr1p
, userattr_st
;
301 static kv_t ua_kv
[KV_ADD_KEYS
];
305 int NIS_entry_seen
; /* NIS scanning flag */
307 * NIS start pos, really pointer to first entry AFTER first
311 long cur_pos
; /* Current pos, used with nis-pos above */
313 (void) setlocale(LC_ALL
, "");
315 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
316 #define TEXT_DOMAIN "SYS_TEST"
318 (void) textdomain(TEXT_DOMAIN
);
321 /* Get program name */
328 /* Lock the password file(s) */
332 info_mask
|= LOCKED
; /* remember we locked */
334 /* initialize the two structures */
336 passwd_st
.pw_passwd
= pwdflr
; /* bogus password */
337 passwd_st
.pw_name
= nullstr
; /* login name */
338 passwd_st
.pw_uid
= -1; /* no uid */
339 passwd_st
.pw_gid
= 1; /* default gid */
340 passwd_st
.pw_age
= nullstr
; /* no aging info. */
341 passwd_st
.pw_comment
= nullstr
; /* no comments */
342 passwd_st
.pw_gecos
= nullstr
; /* no comments */
343 passwd_st
.pw_dir
= nullstr
; /* no default directory */
344 passwd_st
.pw_shell
= nullstr
; /* no default shell */
346 shadow_st
.sp_namp
= nullstr
; /* no name */
347 shadow_st
.sp_pwdp
= lkstring
; /* locked password */
348 shadow_st
.sp_lstchg
= -1; /* no lastchanged date */
349 shadow_st
.sp_min
= -1; /* no min */
350 shadow_st
.sp_max
= -1; /* no max */
351 shadow_st
.sp_warn
= -1; /* no warn */
352 shadow_st
.sp_inact
= -1; /* no inactive */
353 shadow_st
.sp_expire
= -1; /* no expire */
354 shadow_st
.sp_flag
= 0; /* no flag */
356 userattr_st
.name
= nullstr
;
357 userattr_st
.qualifier
= nullstr
;
358 userattr_st
.res1
= nullstr
;
359 userattr_st
.res2
= nullstr
;
362 ua_kv
[0].key
= USERATTR_TYPE_KW
;
363 ua_kv
[0].value
= USERATTR_TYPE_NORMAL_KW
;
365 userattr_st
.attr
= &ua_kva
;
367 /* parse the command line */
369 while ((c
= getopt(argc
, argv
,
370 "ml:c:h:u:g:s:f:e:k:A:P:R:T:oadK:")) != -1) {
376 if ((A_MASK
|D_MASK
|M_MASK
) & optn_mask
)
377 bad_usage("Invalid combination of options");
385 if ((A_MASK
|D_MASK
|L_MASK
) & optn_mask
)
386 bad_usage("Invalid combination of options");
388 if (strpbrk(optarg
, ":\n") ||
390 bad_arg("Invalid argument to option -l");
393 passwd_st
.pw_name
= optarg
;
394 shadow_st
.sp_namp
= optarg
;
395 userattr_st
.name
= optarg
;
401 if ((D_MASK
|F_MASK
) & optn_mask
)
402 bad_usage("Invalid combination of options");
403 if (((shadow_st
.sp_inact
=
404 strtol(optarg
, &char_p
, 10)) < (long)0) ||
407 bad_arg("Invalid argument to option -f");
408 if (shadow_st
.sp_inact
== 0)
409 shadow_st
.sp_inact
= -1;
414 /* set expire date */
416 if ((D_MASK
|E_MASK
) & optn_mask
)
417 bad_usage("Invalid combination of options");
419 if ((strlen(optarg
)) < (size_t)2)
420 shadow_st
.sp_expire
= -1;
423 if ((tm_ptr
= getdate(optarg
)) == NULL
) {
424 msg
= "Invalid argument to option -e";
427 if ((date
= mktime(tm_ptr
)) < 0) {
428 msg
= "Invalid argument to option -e";
431 shadow_st
.sp_expire
= (date
/ DAY
);
432 if (shadow_st
.sp_expire
<= DAY_NOW
) {
433 msg
= "Invalid argument to option -e";
444 if ((D_MASK
|C_MASK
) & optn_mask
)
445 bad_usage("Invalid combination of options");
447 if (strlen(optarg
) > (size_t)CMT_SIZE
||
448 strpbrk(optarg
, ":\n"))
449 bad_arg("Invalid argument to option -c");
452 passwd_st
.pw_comment
= optarg
;
453 passwd_st
.pw_gecos
= optarg
;
457 /* The home directory */
459 if ((D_MASK
|H_MASK
) & optn_mask
)
460 bad_usage("Invalid combination of options");
462 if (strlen(optarg
) > (size_t)DIR_SIZE
||
463 strpbrk(optarg
, ":\n"))
464 bad_arg("Invalid argument to option -h");
467 passwd_st
.pw_dir
= optarg
;
473 if ((D_MASK
|U_MASK
) & optn_mask
)
474 bad_usage("Invalid combination of options");
477 passwd_st
.pw_uid
= (uid_t
)strtol(optarg
, &char_p
, 10);
478 if ((*char_p
!= '\0') ||
479 (passwd_st
.pw_uid
< 0) ||
480 (strlen(optarg
) == 0))
481 bad_arg("Invalid argument to option -u");
488 if ((D_MASK
|G_MASK
) & optn_mask
)
489 bad_usage("Invalid combination of options");
492 passwd_st
.pw_gid
= (gid_t
)strtol(optarg
, &char_p
, 10);
494 if ((*char_p
!= '\0') || (passwd_st
.pw_gid
< 0) ||
495 (strlen(optarg
) == 0))
496 bad_arg("Invalid argument to option -g");
502 if ((D_MASK
|S_MASK
) & optn_mask
)
503 bad_usage("Invalid combination of options");
505 if (strlen(optarg
) > (size_t)SHL_SIZE
||
506 strpbrk(optarg
, ":\n"))
507 bad_arg("Invalid argument to option -s");
510 passwd_st
.pw_shell
= optarg
;
514 /* Override unique uid */
516 if ((D_MASK
|O_MASK
) & optn_mask
)
517 bad_usage("Invalid combination of options");
525 if ((A_MASK
|M_MASK
|D_MASK
|L_MASK
) & optn_mask
)
526 bad_usage("Invalid combination of options");
534 if ((D_MASK
|M_MASK
|L_MASK
|C_MASK
|
535 H_MASK
|U_MASK
|G_MASK
|S_MASK
|
536 O_MASK
|A_MASK
) & optn_mask
)
537 bad_usage("Invalid combination of options");
543 if (D_MASK
& optn_mask
)
544 bad_usage("Invalid combination of options");
546 char_p
= strchr(optarg
, '=');
548 bad_usage("Missing value in -K option");
552 for (i
= 0; i
< UA_KEYS
; i
++) {
553 if (strcmp(optarg
, ua_opts
[i
].key
) == 0) {
554 ua_opts
[i
].newvalue
=
555 _escape(char_p
, KV_SPECIAL
);
556 assign_attr(&userattr_st
, optarg
,
562 bad_usage("bad key");
563 optn_mask
|= UATTR_MASK
;
572 /* Extended User Attributes */
576 for (j
= 0; j
< UA_KEYS
; j
++) {
577 if (ua_opts
[j
].option
== (char)c
) {
578 if ((D_MASK
) & optn_mask
)
582 optn_mask
|= UATTR_MASK
;
583 assign_attr(&userattr_st
,
587 ua_opts
[j
].newvalue
=
588 _escape(optarg
, KV_SPECIAL
);
597 /* check command syntax for the following errors */
598 /* too few or too many arguments */
599 /* no -a -m or -d option */
601 /* -m with no other option */
603 if (optind
== argc
|| argc
> (optind
+1) ||
604 !((A_MASK
|M_MASK
|D_MASK
) & optn_mask
) ||
605 ((optn_mask
& O_MASK
) && !(optn_mask
& U_MASK
)) ||
606 ((optn_mask
& M_MASK
) &&
608 (L_MASK
|C_MASK
|H_MASK
|U_MASK
|G_MASK
|S_MASK
|F_MASK
|
609 E_MASK
|UATTR_MASK
))))
610 bad_usage("Invalid command syntax");
612 /* null string argument or bad characters ? */
613 if ((strlen(argv
[optind
]) == 0) || strpbrk(argv
[optind
], ":\n"))
614 bad_arg("Invalid name");
616 lognamp
= argv
[optind
];
619 * if we are adding a new user or modifying an existing user
620 * (not the logname), then copy logname into the two data
624 if ((A_MASK
& optn_mask
) ||
625 ((M_MASK
& optn_mask
) && !(optn_mask
& L_MASK
))) {
626 passwd_st
.pw_name
= argv
[optind
];
627 shadow_st
.sp_namp
= argv
[optind
];
628 userattr_st
.name
= argv
[optind
];
631 /* Put in directory if we are adding and we need a default */
633 if (!(optn_mask
& H_MASK
) && (optn_mask
& A_MASK
)) {
634 if ((passwd_st
.pw_dir
= malloc((size_t)DIR_SIZE
)) == NULL
)
637 *passwd_st
.pw_dir
= '\0';
638 (void) strcat(passwd_st
.pw_dir
, defdir
);
639 (void) strcat(passwd_st
.pw_dir
, lognamp
);
642 /* Check the number of password files we are touching */
644 if ((!((M_MASK
& optn_mask
) && !(L_MASK
& optn_mask
))) ||
645 ((M_MASK
& optn_mask
) && ((E_MASK
& optn_mask
) ||
646 (F_MASK
& optn_mask
))))
647 info_mask
|= BOTH_FILES
;
649 if ((D_MASK
|L_MASK
|UATTR_MASK
) & optn_mask
)
650 info_mask
|= UATTR_FILE
;
652 /* Open the temporary file(s) with appropriate permission mask */
653 /* and the appropriate owner */
655 if (stat(PASSWD
, &statbuf
) < 0)
658 fd_ptemp
= open(PASSTEMP
, O_CREAT
|O_EXCL
|O_WRONLY
, statbuf
.st_mode
);
659 if (fd_ptemp
== -1) {
660 if (errno
== EEXIST
) {
661 if (unlink(PASSTEMP
)) {
662 msg
= "%s: warning: cannot unlink %s\n";
663 (void) fprintf(stderr
, gettext(msg
), prognamp
,
666 fd_ptemp
= open(PASSTEMP
, O_CREAT
|O_EXCL
|O_WRONLY
,
668 if (fd_ptemp
== -1) {
675 fp_ptemp
= fdopen(fd_ptemp
, "w");
676 if (fp_ptemp
== NULL
)
678 error
= fchown(fd_ptemp
, statbuf
.st_uid
, statbuf
.st_gid
);
680 error
= fchmod(fd_ptemp
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
);
682 (void) fclose(fp_ptemp
);
683 if (unlink(PASSTEMP
)) {
684 msg
= "%s: warning: cannot unlink %s\n";
685 (void) fprintf(stderr
, gettext(msg
), prognamp
,
691 if (info_mask
& BOTH_FILES
) {
692 if (stat(SHADOW
, &statbuf
) < 0) {
696 fd_stemp
= open(SHADTEMP
, O_CREAT
|O_EXCL
|O_WRONLY
,
698 if (fd_stemp
== -1) {
699 if (errno
== EEXIST
) {
700 if (unlink(SHADTEMP
)) {
701 msg
= "%s: warning: cannot unlink %s\n";
702 (void) fprintf(stderr
, gettext(msg
),
705 fd_stemp
= open(SHADTEMP
,
706 O_CREAT
|O_EXCL
|O_WRONLY
, statbuf
.st_mode
);
707 if (fd_stemp
== -1) {
717 fp_stemp
= fdopen(fd_stemp
, "w");
718 if (fp_stemp
== NULL
) {
722 error
= fchown(fd_stemp
, statbuf
.st_uid
, statbuf
.st_gid
);
724 error
= fchmod(fd_stemp
, S_IRUSR
);
731 if (info_mask
& UATTR_FILE
) {
732 if (stat(USERATTR_FILENAME
, &statbuf
) < 0) {
736 fd_uatemp
= open(USERATTR_TEMP
, O_CREAT
|O_EXCL
|O_WRONLY
,
738 if (fd_uatemp
== -1) {
739 if (errno
== EEXIST
) {
740 if (unlink(USERATTR_TEMP
)) {
741 msg
= "%s: warning: cannot unlink %s\n";
742 (void) fprintf(stderr
, gettext(msg
),
743 prognamp
, USERATTR_TEMP
);
745 fd_uatemp
= open(USERATTR_TEMP
,
746 O_CREAT
|O_EXCL
|O_WRONLY
, statbuf
.st_mode
);
747 if (fd_uatemp
== -1) {
757 fp_uatemp
= fdopen(fd_uatemp
, "w");
758 if (fp_uatemp
== NULL
) {
762 error
= fchown(fd_uatemp
, statbuf
.st_uid
, statbuf
.st_gid
);
764 error
= fchmod(fd_uatemp
,
765 S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
);
771 /* Default uid needed ? */
773 if (!(optn_mask
& U_MASK
) && (optn_mask
& A_MASK
)) {
774 /* mark it in the information mask */
775 info_mask
|= NEED_DEF_UID
;
777 /* create the head of the uid number list */
778 uid_sp
= malloc(sizeof (struct uid_blk
));
779 if (uid_sp
== NULL
) {
785 uid_sp
->low
= (UID_MIN
-1);
786 uid_sp
->high
= (UID_MIN
-1);
790 * This next section is modified to allow for NIS passwd file
791 * conventions. In the case where a password entry was being
792 * added to the password file, the original AT&T code read
793 * the entire password file in, noted any information needed, and
794 * copied the entries to a temporary file. Then the new entry
795 * was added to the temporary file, and the temporary file was
796 * moved to be the real password file.
798 * The problem is, that with NIS compatability, we want to add new
799 * entries BEFORE the first NIS-referrant entry, so as not to have
800 * any surprises. To accomplish this without extensively modifying
801 * the logic of the code below, as soon as a NIS-referrant entry is
802 * found we stop copying entries to the TEMP file and instead we
804 * the first NIS entry and where we found it, scan the rest of the
805 * password file without copying entries, then write the new entry, copy
806 * the stored password entry, then copy the rest of the password file.
812 if ((pwf
= fopen("/etc/passwd", "r")) == NULL
) {
822 /* The while loop for reading PASSWD entries */
823 info_mask
|= WRITE_P_ENTRY
;
825 while (!end_of_file
) {
826 pw_ptr1p
= fgetpwent(pwf
);
827 if (pw_ptr1p
== NULL
) {
829 /* A real error - report it and exit */
838 info_mask
|= WRITE_P_ENTRY
;
840 info_mask
&= ~WRITE_P_ENTRY
;
843 * Set up the uid usage blocks to find the first
844 * available uid above UID_MIN, if needed
847 if (info_mask
& NEED_DEF_UID
)
848 add_uid(pw_ptr1p
->pw_uid
);
850 /* Check for unique UID */
852 if (strcmp(lognamp
, pw_ptr1p
->pw_name
) &&
853 (pw_ptr1p
->pw_uid
== passwd_st
.pw_uid
) &&
854 ((optn_mask
& U_MASK
) && !(optn_mask
& O_MASK
))) {
855 rid_tmpf(); /* get rid of temp files */
859 /* Check for unique new logname */
861 if (strcmp(lognamp
, pw_ptr1p
->pw_name
) == 0 &&
862 optn_mask
& L_MASK
&&
863 strcmp(pw_ptr1p
->pw_name
, passwd_st
.pw_name
) == 0) {
866 if (!getspnam(pw_ptr1p
->pw_name
))
868 if (!local_getspnam(pw_ptr1p
->pw_name
))
872 bad_name("logname already exists");
875 if (strcmp(lognamp
, pw_ptr1p
->pw_name
) == 0) {
877 /* no good if we want to add an existing logname */
878 if (optn_mask
& A_MASK
) {
881 if (!getspnam(lognamp
))
883 if (!local_getspnam(lognamp
))
887 bad_name("name already exists");
890 /* remember we found it */
893 /* Do not write it out on the fly */
894 if (optn_mask
& D_MASK
)
895 info_mask
&= ~WRITE_P_ENTRY
;
897 if (optn_mask
& M_MASK
) {
900 if (!getspnam(lognamp
))
902 if (!local_getspnam(lognamp
))
908 if (optn_mask
& L_MASK
)
909 pw_ptr1p
->pw_name
= passwd_st
.pw_name
;
911 if (optn_mask
& U_MASK
)
912 pw_ptr1p
->pw_uid
= passwd_st
.pw_uid
;
914 if (optn_mask
& G_MASK
)
915 pw_ptr1p
->pw_gid
= passwd_st
.pw_gid
;
917 if (optn_mask
& C_MASK
) {
918 pw_ptr1p
->pw_comment
=
919 passwd_st
.pw_comment
;
922 passwd_st
.pw_comment
;
925 if (optn_mask
& H_MASK
)
926 pw_ptr1p
->pw_dir
= passwd_st
.pw_dir
;
928 if (optn_mask
& S_MASK
)
929 pw_ptr1p
->pw_shell
= passwd_st
.pw_shell
;
930 ck_p_sz(pw_ptr1p
); /* check entry size */
934 if (optn_mask
& A_MASK
) {
935 if (!NIS_entry_seen
) {
937 p
= strchr("+-", pw_ptr1p
->pw_name
[0]);
940 * Found first NIS entry.
945 info_mask
&= ~WRITE_P_ENTRY
;
948 cur_pos
= ftell(pwf
);
952 if (info_mask
& WRITE_P_ENTRY
) {
953 if (putpwent(pw_ptr1p
, fp_ptemp
)) {
958 } /* end-of-while-loop */
961 msg
= "%s: Bad entry found in /etc/passwd. Run pwconv.\n";
962 fprintf(stderr
, gettext(msg
), prognamp
);
965 /* Cannot find the target entry and we are deleting or modifying */
967 if (!(info_mask
& FOUND
) && (optn_mask
& (D_MASK
|M_MASK
))) {
970 if (getspnam(lognamp
) != NULL
)
972 if (local_getspnam(lognamp
) != NULL
)
976 bad_name("name does not exist");
979 /* First available uid above UID_MIN is ... */
981 if (info_mask
& NEED_DEF_UID
)
982 passwd_st
.pw_uid
= uid_sp
->high
+ 1;
984 /* Write out the added entry now */
986 if (optn_mask
& A_MASK
) {
987 ck_p_sz(&passwd_st
); /* Check entry size */
988 if (putpwent(&passwd_st
, fp_ptemp
)) {
993 * Now put out the rest of the password file, if needed.
995 if (NIS_entry_seen
) {
999 if (fseek(pwf
, NIS_pos
, SEEK_SET
) < 0) {
1003 while ((n
= fread(buf
, sizeof (char), 1024, pwf
)) > 0) {
1004 if (fwrite(buf
, sizeof (char), n
, fp_ptemp
)
1015 /* flush and sync the file before closing it */
1016 if (fflush(fp_ptemp
) != 0 || fsync(fd_ptemp
) != 0)
1019 /* Now we are done with PASSWD */
1020 (void) fclose(fp_ptemp
);
1022 /* Do this if we are touching both password files */
1025 if (info_mask
& BOTH_FILES
) {
1026 info_mask
&= ~FOUND
; /* Reset FOUND flag */
1028 /* The while loop for reading SHADOW entries */
1029 info_mask
|= WRITE_S_ENTRY
;
1038 if ((spf
= fopen("/etc/shadow", "r")) == NULL
) {
1043 while (!end_of_file
) {
1044 sp_ptr1p
= fgetspent(spf
);
1045 if (sp_ptr1p
== NULL
) {
1054 if (!NIS_entry_seen
)
1055 info_mask
|= WRITE_S_ENTRY
;
1057 info_mask
&= ~WRITE_S_ENTRY
;
1060 * See if the new logname already exist in the
1061 * shadow passwd file
1063 if ((optn_mask
& M_MASK
) &&
1064 strcmp(lognamp
, shadow_st
.sp_namp
) != 0 &&
1065 strcmp(sp_ptr1p
->sp_namp
, shadow_st
.sp_namp
) == 0) {
1070 if (strcmp(lognamp
, sp_ptr1p
->sp_namp
) == 0) {
1072 if (optn_mask
& A_MASK
) {
1073 /* password file inconsistent */
1078 if (optn_mask
& M_MASK
) {
1079 sp_ptr1p
->sp_namp
= shadow_st
.sp_namp
;
1080 if (F_MASK
& optn_mask
)
1081 sp_ptr1p
->sp_inact
=
1083 if (E_MASK
& optn_mask
)
1084 sp_ptr1p
->sp_expire
=
1085 shadow_st
.sp_expire
;
1090 if (optn_mask
& D_MASK
)
1091 info_mask
&= ~WRITE_S_ENTRY
;
1094 if (optn_mask
& A_MASK
) {
1095 if (!NIS_entry_seen
) {
1097 p
= strchr("+-", sp_ptr1p
->sp_namp
[0]);
1100 * Found first NIS entry.
1105 info_mask
&= ~WRITE_S_ENTRY
;
1108 cur_pos
= ftell(spf
);
1112 if (info_mask
& WRITE_S_ENTRY
) {
1113 if (putspent(sp_ptr1p
, fp_stemp
)) {
1119 } /* end-of-while-loop */
1123 msg
= BAD_ENT_MESSAGE
;
1124 fprintf(stderr
, gettext(msg
), prognamp
);
1128 * If we cannot find the entry and we are deleting or
1132 if (!(info_mask
& FOUND
) && (optn_mask
& (D_MASK
|M_MASK
))) {
1137 if (optn_mask
& A_MASK
) {
1138 ck_s_sz(&shadow_st
);
1139 if (putspent(&shadow_st
, fp_stemp
)) {
1145 * Now put out the rest of the shadow file, if needed.
1147 if (NIS_entry_seen
) {
1148 file_copy(spf
, NIS_pos
);
1152 /* flush and sync the file before closing it */
1153 if (fflush(fp_stemp
) != 0 || fsync(fd_stemp
) != 0)
1155 (void) fclose(fp_stemp
);
1157 /* Done with SHADOW */
1160 } /* End of if info_mask */
1162 if (info_mask
& UATTR_FILE
) {
1163 info_mask
&= ~FOUND
; /* Reset FOUND flag */
1165 /* The while loop for reading USER_ATTR entries */
1166 info_mask
|= WRITE_S_ENTRY
;
1175 if ((uaf
= fopen(USERATTR_FILENAME
, "r")) == NULL
) {
1180 while (!end_of_file
) {
1181 ua_ptr1p
= fgetuserattr(uaf
);
1182 if (ua_ptr1p
== NULL
) {
1191 if (ua_ptr1p
->name
[0] == '#') {
1193 * If this is a comment, write it back as it
1196 if (ua_ptr1p
->qualifier
[0] == '\0' &&
1197 ua_ptr1p
->res1
[0] == '\0' &&
1198 ua_ptr1p
->res2
[0] == '\0' &&
1199 (ua_ptr1p
->attr
== NULL
||
1200 ua_ptr1p
->attr
->length
== 0))
1201 (void) fprintf(fp_uatemp
, "%s\n",
1205 * This is a commented user_attr entry;
1206 * reformat it, and write it back.
1208 putuserattrent(ua_ptr1p
, fp_uatemp
);
1209 free_userattr(ua_ptr1p
);
1213 if (!NIS_entry_seen
)
1214 info_mask
|= WRITE_S_ENTRY
;
1216 info_mask
&= ~WRITE_S_ENTRY
;
1219 * See if the new logname already exist in the
1222 if ((optn_mask
& M_MASK
) &&
1223 strcmp(lognamp
, userattr_st
.name
) != 0 &&
1224 strcmp(ua_ptr1p
->name
, userattr_st
.name
) == 0) {
1229 if (strcmp(lognamp
, ua_ptr1p
->name
) == 0) {
1231 if (optn_mask
& A_MASK
) {
1232 /* password file inconsistent */
1237 if (optn_mask
& M_MASK
) {
1241 for (j
= 0; j
< UA_KEYS
; j
++) {
1242 if (ua_opts
[j
].newvalue
!= NULL
)
1245 kva_match(ua_ptr1p
->attr
,
1246 (char *)ua_opts
[j
].key
);
1249 assign_attr(&userattr_st
,
1253 free_userattr(ua_ptr1p
);
1254 ua_ptr1p
= &userattr_st
;
1257 if (optn_mask
& D_MASK
)
1258 info_mask
&= ~WRITE_S_ENTRY
;
1259 } else if (optn_mask
& D_MASK
) {
1262 rolelist
= kva_match(ua_ptr1p
->attr
,
1265 unassign_role(ua_ptr1p
,
1270 if (info_mask
& WRITE_S_ENTRY
) {
1271 putuserattrent(ua_ptr1p
, fp_uatemp
);
1274 if (!(optn_mask
& M_MASK
))
1275 free_userattr(ua_ptr1p
);
1276 } /* end-of-while-loop */
1280 msg
= BAD_ENT_MESSAGE
;
1281 fprintf(stderr
, gettext(msg
), prognamp
);
1285 * Add entry in user_attr if masks is UATTR_MASK
1286 * We don't need to do anything for L_MASK if there's
1287 * no user_attr entry for the user being modified.
1289 if (!(info_mask
& FOUND
) && !(L_MASK
& optn_mask
) &&
1290 !(D_MASK
& optn_mask
)) {
1291 putuserattrent(&userattr_st
, fp_uatemp
);
1294 /* flush and sync the file before closing it */
1295 if (fflush(fp_uatemp
) != 0 || fsync(fd_uatemp
) != 0)
1297 (void) fclose(fp_uatemp
);
1299 /* Done with USERATTR */
1302 } /* End of if info_mask */
1303 /* ignore all signals */
1305 for (i
= 1; i
< NSIG
; i
++)
1306 (void) sigset(i
, SIG_IGN
);
1308 errno
= 0; /* For correcting sigset to SIGKILL */
1310 if (unlink(OPASSWD
) && access(OPASSWD
, 0) == 0)
1313 if (link(PASSWD
, OPASSWD
) == -1)
1317 if (rename(PASSTEMP
, PASSWD
) == -1) {
1318 if (link(OPASSWD
, PASSWD
))
1324 if (info_mask
& BOTH_FILES
) {
1326 if (unlink(OSHADOW
) && access(OSHADOW
, 0) == 0) {
1333 if (link(SHADOW
, OSHADOW
) == -1) {
1341 if (rename(SHADTEMP
, SHADOW
) == -1) {
1342 if (rename(OSHADOW
, SHADOW
) == -1)
1352 if (info_mask
& UATTR_FILE
) {
1353 if (unlink(OUSERATTR_FILENAME
) &&
1354 access(OUSERATTR_FILENAME
, 0) == 0) {
1361 if (link(USERATTR_FILENAME
, OUSERATTR_FILENAME
) == -1) {
1369 if (rename(USERATTR_TEMP
, USERATTR_FILENAME
) == -1) {
1370 if (rename(OUSERATTR_FILENAME
, USERATTR_FILENAME
) == -1)
1384 * Return 0 status, indicating success
1390 /* Try to recover the old password file */
1395 if (unlink(PASSWD
) || link(OPASSWD
, PASSWD
))
1401 /* combine two uid_blk's */
1404 uid_bcom(struct uid_blk
*uid_p
)
1406 struct uid_blk
*uid_tp
;
1408 uid_tp
= uid_p
->link
;
1409 uid_p
->high
= uid_tp
->high
;
1410 uid_p
->link
= uid_tp
->link
;
1415 /* add a new uid_blk */
1418 add_ublk(uid_t num
, struct uid_blk
*uid_p
)
1420 struct uid_blk
*uid_tp
;
1422 uid_tp
= malloc(sizeof (struct uid_blk
));
1423 if (uid_tp
== NULL
) {
1428 uid_tp
->high
= uid_tp
->low
= num
;
1429 uid_tp
->link
= uid_p
->link
;
1430 uid_p
->link
= uid_tp
;
1434 * Here we are using a linked list of uid_blk to keep track of all
1435 * the used uids. Each uid_blk represents a range of used uid,
1436 * with low represents the low inclusive end and high represents
1437 * the high inclusive end. In the beginning, we initialize a linked
1438 * list of one uid_blk with low = high = (UID_MIN-1). This was
1440 * Each time we read in another used uid, we add it onto the linked
1441 * list by either making a new uid_blk, decrementing the low of
1442 * an existing uid_blk, incrementing the high of an existing
1443 * uid_blk, or combining two existing uid_blks. After we finished
1444 * building this linked list, the first available uid above or
1445 * equal to UID_MIN is the high of the first uid_blk in the linked
1448 /* add_uid() adds uid to the link list of used uids */
1452 struct uid_blk
*uid_p
;
1453 /* Only keep track of the ones above UID_MIN */
1455 if (uid
>= UID_MIN
) {
1458 while (uid_p
!= NULL
) {
1460 if (uid_p
->link
!= NULL
) {
1462 if (uid
>= uid_p
->link
->low
)
1463 uid_p
= uid_p
->link
;
1465 else if (uid
>= uid_p
->low
&&
1466 uid
<= uid_p
->high
) {
1470 else if (uid
== (uid_p
->high
+1)) {
1472 if (++uid_p
->high
==
1473 (uid_p
->link
->low
- 1)) {
1479 else if (uid
== (uid_p
->link
->low
- 1)) {
1480 uid_p
->link
->low
--;
1484 else if (uid
< uid_p
->link
->low
) {
1485 add_ublk(uid
, uid_p
);
1488 } /* if uid_p->link */
1492 if (uid
== (uid_p
->high
+ 1)) {
1495 } else if (uid
>= uid_p
->low
&&
1496 uid
<= uid_p
->high
) {
1499 add_ublk(uid
, uid_p
);
1511 (void) fprintf(stderr
, gettext("%s: Permission denied\n"), prognamp
);
1518 if (strlen(sp
) != 0)
1519 (void) fprintf(stderr
, "%s: %s\n", prognamp
, gettext(sp
));
1520 (void) fprintf(stderr
, gettext("Usage:\n\
1521 %s -a [-c comment] [-h homedir] [-u uid [-o]] [-g gid] \n\
1522 [-s shell] [-f inactive] [-e expire] name\n\
1523 %s -m -c comment | -h homedir | -u uid [-o] | -g gid |\n\
1524 -s shell | -f inactive | -e expire | -l logname name\n\
1525 %s -d name\n"), prognamp
, prognamp
, prognamp
);
1526 if (info_mask
& LOCKED
)
1534 (void) fprintf(stderr
, "%s: %s\n", prognamp
, gettext(s
));
1536 if (info_mask
& LOCKED
)
1544 (void) fprintf(stderr
, "%s: %s\n", prognamp
, gettext(s
));
1552 (void) fprintf(stderr
, gettext("%s: UID in use\n"), prognamp
);
1561 msg
= "%s: Inconsistent password files\n";
1562 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1571 msg
= "%s: Bad user_attr database\n";
1572 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1581 msg
= "%s: Unexpected failure. Password files unchanged\n";
1582 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1591 msg
= "%s: Unexpected failure. Password file(s) missing\n";
1592 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1601 msg
= "%s: Password file(s) busy. Try again later\n";
1602 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1607 /* Check for the size of the whole passwd entry */
1609 ck_p_sz(struct passwd
*pwp
)
1613 /* Ensure that the combined length of the individual */
1614 /* fields will fit in a passwd entry. The 1 accounts for the */
1615 /* newline and the 6 accounts for the colons (:'s) */
1616 if (((int)strlen(pwp
->pw_name
) + 1 +
1617 sprintf(ctp
, "%d", pwp
->pw_uid
) +
1618 sprintf(ctp
, "%d", pwp
->pw_gid
) +
1619 (int)strlen(pwp
->pw_comment
) +
1620 (int)strlen(pwp
->pw_dir
) +
1621 (int)strlen(pwp
->pw_shell
) + 6) > (ENTRY_LENGTH
-1)) {
1623 bad_arg("New password entry too long");
1627 /* Check for the size of the whole passwd entry */
1629 ck_s_sz(struct spwd
*ssp
)
1633 /* Ensure that the combined length of the individual */
1634 /* fields will fit in a shadow entry. The 1 accounts for the */
1635 /* newline and the 7 accounts for the colons (:'s) */
1636 if (((int)strlen(ssp
->sp_namp
) + 1 +
1637 (int)strlen(ssp
->sp_pwdp
) +
1638 sprintf(ctp
, "%d", ssp
->sp_lstchg
) +
1639 sprintf(ctp
, "%d", ssp
->sp_min
) +
1640 sprintf(ctp
, "%d", ssp
->sp_max
) +
1641 sprintf(ctp
, "%d", ssp
->sp_warn
) +
1642 sprintf(ctp
, "%d", ssp
->sp_inact
) +
1643 sprintf(ctp
, "%d", ssp
->sp_expire
) + 7) > (ENTRY_LENGTH
- 1)) {
1645 bad_arg("New password entry too long");
1649 /* Get rid of the temp files */
1653 (void) fclose(fp_ptemp
);
1655 if (unlink(PASSTEMP
)) {
1656 msg
= "%s: warning: cannot unlink %s\n";
1657 (void) fprintf(stderr
, gettext(msg
), prognamp
, PASSTEMP
);
1660 if (info_mask
& BOTH_FILES
) {
1661 (void) fclose(fp_stemp
);
1663 if (unlink(SHADTEMP
)) {
1664 msg
= "%s: warning: cannot unlink %s\n";
1665 (void) fprintf(stderr
, gettext(msg
), prognamp
,
1670 if (info_mask
& UATTR_FILE
) {
1671 (void) fclose(fp_uatemp
);
1673 if (unlink(USERATTR_TEMP
)) {
1674 msg
= "%s: warning: cannot unlink %s\n";
1675 (void) fprintf(stderr
, gettext(msg
), prognamp
,
1682 file_copy(FILE *spf
, long NIS_pos
)
1687 if (fseek(spf
, NIS_pos
, SEEK_SET
) < 0) {
1691 while ((n
= fread(buf
, sizeof (char), 1024, spf
)) > 0) {
1692 if (fwrite(buf
, sizeof (char), n
, fp_stemp
) != n
) {