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_AUDIT_FLAGS_KW
},
99 #define UA_KEYS (sizeof (ua_opts)/sizeof (kvopts_t))
102 char defdir
[] = "/home/"; /* default home directory for new user */
103 char pwdflr
[] = "x"; /* password string for /etc/passwd */
104 char lkstring
[] = "*LK*"; /* lock string for shadow password */
105 char nullstr
[] = ""; /* null string */
106 char *msg
; /* pointer to error message */
108 #define DATMSK "DATEMSK=/etc/datemsk"
110 #define OUSERATTR_FILENAME "/etc/ouser_attr"
111 #define USERATTR_TEMP "/etc/uatmp"
114 struct uid_blk
*link
;
115 uid_t low
; /* low bound for this uid block */
116 uid_t high
; /* high bound for this uid block */
119 extern userattr_t
*fgetuserattr(FILE *);
123 * Declare all functions that do not return integers. This is here
124 * to get rid of some lint messages
127 void uid_bcom(struct uid_blk
*), add_ublk(uid_t
, struct uid_blk
*),
129 bad_usage(char *), bad_arg(char *), bad_uid(void), bad_pasf(void),
130 file_error(void), bad_news(void), no_lock(void), add_uid(uid_t
),
131 rid_tmpf(void), ck_p_sz(struct passwd
*), ck_s_sz(struct spwd
*),
132 bad_name(char *), bad_uattr(void);
134 void file_copy(FILE *spf
, long NIS_pos
);
136 static FILE *fp_ptemp
, *fp_stemp
, *fp_uatemp
;
137 static int fd_ptemp
, fd_stemp
, fd_uatemp
;
140 * The uid_blk structure is used in the search for the default
141 * uid. Each uid_blk represent a range of uid(s) that are currently
142 * used on the system.
148 * getspnan routine that ONLY looks at the local shadow file
151 local_getspnam(char *name
)
156 if ((shadf
= fopen("/etc/shadow", "r")) == NULL
)
159 while ((sp
= fgetspent(shadf
)) != NULL
) {
160 if (strcmp(sp
->sp_namp
, name
) == 0)
171 putuserattrent(userattr_t
*user
, FILE *f
)
179 * Avoid trivial entries. Those with no attributes or with
180 * only "type=normal". This retains backward compatibility.
182 if (user
->attr
== NULL
)
185 kv_pair
= user
->attr
->data
;
187 for (i
= j
= 0; i
< user
->attr
->length
; i
++) {
188 key
= kv_pair
[i
].key
;
189 val
= kv_pair
[i
].value
;
190 if ((key
== NULL
) || (val
== NULL
))
192 if (strlen(val
) == 0 ||
193 (strcmp(key
, USERATTR_TYPE_KW
) == 0 &&
194 strcmp(val
, USERATTR_TYPE_NORMAL_KW
) == 0))
201 (void) fprintf(f
, "%s:%s:%s:%s:", user
->name
, user
->qualifier
,
202 user
->res1
, user
->res2
);
204 for (i
= j
= 0; i
< user
->attr
->length
; i
++) {
205 key
= kv_pair
[i
].key
;
206 val
= _escape(kv_pair
[i
].value
, KV_SPECIAL
);
207 if ((key
== NULL
) || (val
== NULL
))
209 if (strlen(val
) == 0)
212 (void) fprintf(f
, KV_DELIMITER
);
213 (void) fprintf(f
, "%s=%s", key
, val
);
216 (void) fprintf(f
, "\n");
220 assign_attr(userattr_t
*user
, const char *newkey
, char *val
)
228 if (user
->attr
!= NULL
) {
229 kv_pair
= user
->attr
->data
;
230 for (i
= 0; i
< user
->attr
->length
; i
++) {
231 key
= kv_pair
[i
].key
;
235 } else if (strcmp(key
, newkey
) == 0) {
236 kv_pair
[i
].value
= strdup(val
);
242 avail
= user
->attr
->length
++;
243 kv_pair
[avail
].key
= strdup(newkey
);
244 kv_pair
[avail
].value
= strdup(val
);
249 unassign_role(userattr_t
*user
, char *rolelist
, char *role
)
258 templist
= strdup(roleptr
);
259 temprole
= strtok(templist
, ",");
261 if (strcmp(temprole
, role
) == 0) {
263 length
= strlen(role
);
264 roleptr
+= temprole
- templist
;
266 if (*(roleptr
+ length
) == ',')
268 strcpy(roleptr
, roleptr
+ length
);
269 length
= strlen(roleptr
) - 1;
270 if (*(roleptr
+ length
) == ',')
271 *(roleptr
+ length
) = '\0';
272 assign_attr(user
, USERATTR_ROLES_KW
, rolelist
);
275 temprole
= strtok(NULL
, ",");
280 struct uid_blk
*uid_sp
;
281 char *prognamp
; /* program name */
283 int optn_mask
= 0, info_mask
= 0;
284 extern int getdate_err
;
287 main(int argc
, char **argv
)
290 char *lognamp
, *char_p
;
294 FILE *pwf
, *spf
, *uaf
;
296 struct passwd
*pw_ptr1p
, passwd_st
;
297 struct spwd
*sp_ptr1p
, shadow_st
;
298 userattr_t
*ua_ptr1p
, userattr_st
;
299 static kv_t ua_kv
[KV_ADD_KEYS
];
303 int NIS_entry_seen
; /* NIS scanning flag */
305 * NIS start pos, really pointer to first entry AFTER first
309 long cur_pos
; /* Current pos, used with nis-pos above */
311 (void) setlocale(LC_ALL
, "");
313 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
314 #define TEXT_DOMAIN "SYS_TEST"
316 (void) textdomain(TEXT_DOMAIN
);
319 /* Get program name */
326 /* Lock the password file(s) */
330 info_mask
|= LOCKED
; /* remember we locked */
332 /* initialize the two structures */
334 passwd_st
.pw_passwd
= pwdflr
; /* bogus password */
335 passwd_st
.pw_name
= nullstr
; /* login name */
336 passwd_st
.pw_uid
= -1; /* no uid */
337 passwd_st
.pw_gid
= 1; /* default gid */
338 passwd_st
.pw_age
= nullstr
; /* no aging info. */
339 passwd_st
.pw_comment
= nullstr
; /* no comments */
340 passwd_st
.pw_gecos
= nullstr
; /* no comments */
341 passwd_st
.pw_dir
= nullstr
; /* no default directory */
342 passwd_st
.pw_shell
= nullstr
; /* no default shell */
344 shadow_st
.sp_namp
= nullstr
; /* no name */
345 shadow_st
.sp_pwdp
= lkstring
; /* locked password */
346 shadow_st
.sp_lstchg
= -1; /* no lastchanged date */
347 shadow_st
.sp_min
= -1; /* no min */
348 shadow_st
.sp_max
= -1; /* no max */
349 shadow_st
.sp_warn
= -1; /* no warn */
350 shadow_st
.sp_inact
= -1; /* no inactive */
351 shadow_st
.sp_expire
= -1; /* no expire */
352 shadow_st
.sp_flag
= 0; /* no flag */
354 userattr_st
.name
= nullstr
;
355 userattr_st
.qualifier
= nullstr
;
356 userattr_st
.res1
= nullstr
;
357 userattr_st
.res2
= nullstr
;
360 ua_kv
[0].key
= USERATTR_TYPE_KW
;
361 ua_kv
[0].value
= USERATTR_TYPE_NORMAL_KW
;
363 userattr_st
.attr
= &ua_kva
;
365 /* parse the command line */
367 while ((c
= getopt(argc
, argv
,
368 "ml:c:h:u:g:s:f:e:k:A:P:R:T:oadK:")) != -1) {
374 if ((A_MASK
|D_MASK
|M_MASK
) & optn_mask
)
375 bad_usage("Invalid combination of options");
383 if ((A_MASK
|D_MASK
|L_MASK
) & optn_mask
)
384 bad_usage("Invalid combination of options");
386 if (strpbrk(optarg
, ":\n") ||
388 bad_arg("Invalid argument to option -l");
391 passwd_st
.pw_name
= optarg
;
392 shadow_st
.sp_namp
= optarg
;
393 userattr_st
.name
= optarg
;
399 if ((D_MASK
|F_MASK
) & optn_mask
)
400 bad_usage("Invalid combination of options");
401 if (((shadow_st
.sp_inact
=
402 strtol(optarg
, &char_p
, 10)) < (long)0) ||
405 bad_arg("Invalid argument to option -f");
406 if (shadow_st
.sp_inact
== 0)
407 shadow_st
.sp_inact
= -1;
412 /* set expire date */
414 if ((D_MASK
|E_MASK
) & optn_mask
)
415 bad_usage("Invalid combination of options");
417 if ((strlen(optarg
)) < (size_t)2)
418 shadow_st
.sp_expire
= -1;
421 if ((tm_ptr
= getdate(optarg
)) == NULL
) {
422 msg
= "Invalid argument to option -e";
425 if ((date
= mktime(tm_ptr
)) < 0) {
426 msg
= "Invalid argument to option -e";
429 shadow_st
.sp_expire
= (date
/ DAY
);
430 if (shadow_st
.sp_expire
<= DAY_NOW
) {
431 msg
= "Invalid argument to option -e";
442 if ((D_MASK
|C_MASK
) & optn_mask
)
443 bad_usage("Invalid combination of options");
445 if (strlen(optarg
) > (size_t)CMT_SIZE
||
446 strpbrk(optarg
, ":\n"))
447 bad_arg("Invalid argument to option -c");
450 passwd_st
.pw_comment
= optarg
;
451 passwd_st
.pw_gecos
= optarg
;
455 /* The home directory */
457 if ((D_MASK
|H_MASK
) & optn_mask
)
458 bad_usage("Invalid combination of options");
460 if (strlen(optarg
) > (size_t)DIR_SIZE
||
461 strpbrk(optarg
, ":\n"))
462 bad_arg("Invalid argument to option -h");
465 passwd_st
.pw_dir
= optarg
;
471 if ((D_MASK
|U_MASK
) & optn_mask
)
472 bad_usage("Invalid combination of options");
475 passwd_st
.pw_uid
= (uid_t
)strtol(optarg
, &char_p
, 10);
476 if ((*char_p
!= '\0') ||
477 (passwd_st
.pw_uid
< 0) ||
478 (strlen(optarg
) == 0))
479 bad_arg("Invalid argument to option -u");
486 if ((D_MASK
|G_MASK
) & optn_mask
)
487 bad_usage("Invalid combination of options");
490 passwd_st
.pw_gid
= (gid_t
)strtol(optarg
, &char_p
, 10);
492 if ((*char_p
!= '\0') || (passwd_st
.pw_gid
< 0) ||
493 (strlen(optarg
) == 0))
494 bad_arg("Invalid argument to option -g");
500 if ((D_MASK
|S_MASK
) & optn_mask
)
501 bad_usage("Invalid combination of options");
503 if (strlen(optarg
) > (size_t)SHL_SIZE
||
504 strpbrk(optarg
, ":\n"))
505 bad_arg("Invalid argument to option -s");
508 passwd_st
.pw_shell
= optarg
;
512 /* Override unique uid */
514 if ((D_MASK
|O_MASK
) & optn_mask
)
515 bad_usage("Invalid combination of options");
523 if ((A_MASK
|M_MASK
|D_MASK
|L_MASK
) & optn_mask
)
524 bad_usage("Invalid combination of options");
532 if ((D_MASK
|M_MASK
|L_MASK
|C_MASK
|
533 H_MASK
|U_MASK
|G_MASK
|S_MASK
|
534 O_MASK
|A_MASK
) & optn_mask
)
535 bad_usage("Invalid combination of options");
541 if (D_MASK
& optn_mask
)
542 bad_usage("Invalid combination of options");
544 char_p
= strchr(optarg
, '=');
546 bad_usage("Missing value in -K option");
550 for (i
= 0; i
< UA_KEYS
; i
++) {
551 if (strcmp(optarg
, ua_opts
[i
].key
) == 0) {
552 ua_opts
[i
].newvalue
=
553 _escape(char_p
, KV_SPECIAL
);
554 assign_attr(&userattr_st
, optarg
,
560 bad_usage("bad key");
561 optn_mask
|= UATTR_MASK
;
570 /* Extended User Attributes */
574 for (j
= 0; j
< UA_KEYS
; j
++) {
575 if (ua_opts
[j
].option
== (char)c
) {
576 if ((D_MASK
) & optn_mask
)
580 optn_mask
|= UATTR_MASK
;
581 assign_attr(&userattr_st
,
585 ua_opts
[j
].newvalue
=
586 _escape(optarg
, KV_SPECIAL
);
595 /* check command syntax for the following errors */
596 /* too few or too many arguments */
597 /* no -a -m or -d option */
599 /* -m with no other option */
601 if (optind
== argc
|| argc
> (optind
+1) ||
602 !((A_MASK
|M_MASK
|D_MASK
) & optn_mask
) ||
603 ((optn_mask
& O_MASK
) && !(optn_mask
& U_MASK
)) ||
604 ((optn_mask
& M_MASK
) &&
606 (L_MASK
|C_MASK
|H_MASK
|U_MASK
|G_MASK
|S_MASK
|F_MASK
|
607 E_MASK
|UATTR_MASK
))))
608 bad_usage("Invalid command syntax");
610 /* null string argument or bad characters ? */
611 if ((strlen(argv
[optind
]) == 0) || strpbrk(argv
[optind
], ":\n"))
612 bad_arg("Invalid name");
614 lognamp
= argv
[optind
];
617 * if we are adding a new user or modifying an existing user
618 * (not the logname), then copy logname into the two data
622 if ((A_MASK
& optn_mask
) ||
623 ((M_MASK
& optn_mask
) && !(optn_mask
& L_MASK
))) {
624 passwd_st
.pw_name
= argv
[optind
];
625 shadow_st
.sp_namp
= argv
[optind
];
626 userattr_st
.name
= argv
[optind
];
629 /* Put in directory if we are adding and we need a default */
631 if (!(optn_mask
& H_MASK
) && (optn_mask
& A_MASK
)) {
632 if ((passwd_st
.pw_dir
= malloc((size_t)DIR_SIZE
)) == NULL
)
635 *passwd_st
.pw_dir
= '\0';
636 (void) strcat(passwd_st
.pw_dir
, defdir
);
637 (void) strcat(passwd_st
.pw_dir
, lognamp
);
640 /* Check the number of password files we are touching */
642 if ((!((M_MASK
& optn_mask
) && !(L_MASK
& optn_mask
))) ||
643 ((M_MASK
& optn_mask
) && ((E_MASK
& optn_mask
) ||
644 (F_MASK
& optn_mask
))))
645 info_mask
|= BOTH_FILES
;
647 if ((D_MASK
|L_MASK
|UATTR_MASK
) & optn_mask
)
648 info_mask
|= UATTR_FILE
;
650 /* Open the temporary file(s) with appropriate permission mask */
651 /* and the appropriate owner */
653 if (stat(PASSWD
, &statbuf
) < 0)
656 fd_ptemp
= open(PASSTEMP
, O_CREAT
|O_EXCL
|O_WRONLY
, statbuf
.st_mode
);
657 if (fd_ptemp
== -1) {
658 if (errno
== EEXIST
) {
659 if (unlink(PASSTEMP
)) {
660 msg
= "%s: warning: cannot unlink %s\n";
661 (void) fprintf(stderr
, gettext(msg
), prognamp
,
664 fd_ptemp
= open(PASSTEMP
, O_CREAT
|O_EXCL
|O_WRONLY
,
666 if (fd_ptemp
== -1) {
673 fp_ptemp
= fdopen(fd_ptemp
, "w");
674 if (fp_ptemp
== NULL
)
676 error
= fchown(fd_ptemp
, statbuf
.st_uid
, statbuf
.st_gid
);
678 error
= fchmod(fd_ptemp
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
);
680 (void) fclose(fp_ptemp
);
681 if (unlink(PASSTEMP
)) {
682 msg
= "%s: warning: cannot unlink %s\n";
683 (void) fprintf(stderr
, gettext(msg
), prognamp
,
689 if (info_mask
& BOTH_FILES
) {
690 if (stat(SHADOW
, &statbuf
) < 0) {
694 fd_stemp
= open(SHADTEMP
, O_CREAT
|O_EXCL
|O_WRONLY
,
696 if (fd_stemp
== -1) {
697 if (errno
== EEXIST
) {
698 if (unlink(SHADTEMP
)) {
699 msg
= "%s: warning: cannot unlink %s\n";
700 (void) fprintf(stderr
, gettext(msg
),
703 fd_stemp
= open(SHADTEMP
,
704 O_CREAT
|O_EXCL
|O_WRONLY
, statbuf
.st_mode
);
705 if (fd_stemp
== -1) {
715 fp_stemp
= fdopen(fd_stemp
, "w");
716 if (fp_stemp
== NULL
) {
720 error
= fchown(fd_stemp
, statbuf
.st_uid
, statbuf
.st_gid
);
722 error
= fchmod(fd_stemp
, S_IRUSR
);
729 if (info_mask
& UATTR_FILE
) {
730 if (stat(USERATTR_FILENAME
, &statbuf
) < 0) {
734 fd_uatemp
= open(USERATTR_TEMP
, O_CREAT
|O_EXCL
|O_WRONLY
,
736 if (fd_uatemp
== -1) {
737 if (errno
== EEXIST
) {
738 if (unlink(USERATTR_TEMP
)) {
739 msg
= "%s: warning: cannot unlink %s\n";
740 (void) fprintf(stderr
, gettext(msg
),
741 prognamp
, USERATTR_TEMP
);
743 fd_uatemp
= open(USERATTR_TEMP
,
744 O_CREAT
|O_EXCL
|O_WRONLY
, statbuf
.st_mode
);
745 if (fd_uatemp
== -1) {
755 fp_uatemp
= fdopen(fd_uatemp
, "w");
756 if (fp_uatemp
== NULL
) {
760 error
= fchown(fd_uatemp
, statbuf
.st_uid
, statbuf
.st_gid
);
762 error
= fchmod(fd_uatemp
,
763 S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
);
769 /* Default uid needed ? */
771 if (!(optn_mask
& U_MASK
) && (optn_mask
& A_MASK
)) {
772 /* mark it in the information mask */
773 info_mask
|= NEED_DEF_UID
;
775 /* create the head of the uid number list */
776 uid_sp
= malloc(sizeof (struct uid_blk
));
777 if (uid_sp
== NULL
) {
783 uid_sp
->low
= (UID_MIN
-1);
784 uid_sp
->high
= (UID_MIN
-1);
788 * This next section is modified to allow for NIS passwd file
789 * conventions. In the case where a password entry was being
790 * added to the password file, the original AT&T code read
791 * the entire password file in, noted any information needed, and
792 * copied the entries to a temporary file. Then the new entry
793 * was added to the temporary file, and the temporary file was
794 * moved to be the real password file.
796 * The problem is, that with NIS compatability, we want to add new
797 * entries BEFORE the first NIS-referrant entry, so as not to have
798 * any surprises. To accomplish this without extensively modifying
799 * the logic of the code below, as soon as a NIS-referrant entry is
800 * found we stop copying entries to the TEMP file and instead we
802 * the first NIS entry and where we found it, scan the rest of the
803 * password file without copying entries, then write the new entry, copy
804 * the stored password entry, then copy the rest of the password file.
810 if ((pwf
= fopen("/etc/passwd", "r")) == NULL
) {
820 /* The while loop for reading PASSWD entries */
821 info_mask
|= WRITE_P_ENTRY
;
823 while (!end_of_file
) {
824 pw_ptr1p
= fgetpwent(pwf
);
825 if (pw_ptr1p
== NULL
) {
827 /* A real error - report it and exit */
836 info_mask
|= WRITE_P_ENTRY
;
838 info_mask
&= ~WRITE_P_ENTRY
;
841 * Set up the uid usage blocks to find the first
842 * available uid above UID_MIN, if needed
845 if (info_mask
& NEED_DEF_UID
)
846 add_uid(pw_ptr1p
->pw_uid
);
848 /* Check for unique UID */
850 if (strcmp(lognamp
, pw_ptr1p
->pw_name
) &&
851 (pw_ptr1p
->pw_uid
== passwd_st
.pw_uid
) &&
852 ((optn_mask
& U_MASK
) && !(optn_mask
& O_MASK
))) {
853 rid_tmpf(); /* get rid of temp files */
857 /* Check for unique new logname */
859 if (strcmp(lognamp
, pw_ptr1p
->pw_name
) == 0 &&
860 optn_mask
& L_MASK
&&
861 strcmp(pw_ptr1p
->pw_name
, passwd_st
.pw_name
) == 0) {
864 if (!getspnam(pw_ptr1p
->pw_name
))
866 if (!local_getspnam(pw_ptr1p
->pw_name
))
870 bad_name("logname already exists");
873 if (strcmp(lognamp
, pw_ptr1p
->pw_name
) == 0) {
875 /* no good if we want to add an existing logname */
876 if (optn_mask
& A_MASK
) {
879 if (!getspnam(lognamp
))
881 if (!local_getspnam(lognamp
))
885 bad_name("name already exists");
888 /* remember we found it */
891 /* Do not write it out on the fly */
892 if (optn_mask
& D_MASK
)
893 info_mask
&= ~WRITE_P_ENTRY
;
895 if (optn_mask
& M_MASK
) {
898 if (!getspnam(lognamp
))
900 if (!local_getspnam(lognamp
))
906 if (optn_mask
& L_MASK
)
907 pw_ptr1p
->pw_name
= passwd_st
.pw_name
;
909 if (optn_mask
& U_MASK
)
910 pw_ptr1p
->pw_uid
= passwd_st
.pw_uid
;
912 if (optn_mask
& G_MASK
)
913 pw_ptr1p
->pw_gid
= passwd_st
.pw_gid
;
915 if (optn_mask
& C_MASK
) {
916 pw_ptr1p
->pw_comment
=
917 passwd_st
.pw_comment
;
920 passwd_st
.pw_comment
;
923 if (optn_mask
& H_MASK
)
924 pw_ptr1p
->pw_dir
= passwd_st
.pw_dir
;
926 if (optn_mask
& S_MASK
)
927 pw_ptr1p
->pw_shell
= passwd_st
.pw_shell
;
928 ck_p_sz(pw_ptr1p
); /* check entry size */
932 if (optn_mask
& A_MASK
) {
933 if (!NIS_entry_seen
) {
935 p
= strchr("+-", pw_ptr1p
->pw_name
[0]);
938 * Found first NIS entry.
943 info_mask
&= ~WRITE_P_ENTRY
;
946 cur_pos
= ftell(pwf
);
950 if (info_mask
& WRITE_P_ENTRY
) {
951 if (putpwent(pw_ptr1p
, fp_ptemp
)) {
956 } /* end-of-while-loop */
959 msg
= "%s: Bad entry found in /etc/passwd. Run pwconv.\n";
960 fprintf(stderr
, gettext(msg
), prognamp
);
963 /* Cannot find the target entry and we are deleting or modifying */
965 if (!(info_mask
& FOUND
) && (optn_mask
& (D_MASK
|M_MASK
))) {
968 if (getspnam(lognamp
) != NULL
)
970 if (local_getspnam(lognamp
) != NULL
)
974 bad_name("name does not exist");
977 /* First available uid above UID_MIN is ... */
979 if (info_mask
& NEED_DEF_UID
)
980 passwd_st
.pw_uid
= uid_sp
->high
+ 1;
982 /* Write out the added entry now */
984 if (optn_mask
& A_MASK
) {
985 ck_p_sz(&passwd_st
); /* Check entry size */
986 if (putpwent(&passwd_st
, fp_ptemp
)) {
991 * Now put out the rest of the password file, if needed.
993 if (NIS_entry_seen
) {
997 if (fseek(pwf
, NIS_pos
, SEEK_SET
) < 0) {
1001 while ((n
= fread(buf
, sizeof (char), 1024, pwf
)) > 0) {
1002 if (fwrite(buf
, sizeof (char), n
, fp_ptemp
)
1013 /* flush and sync the file before closing it */
1014 if (fflush(fp_ptemp
) != 0 || fsync(fd_ptemp
) != 0)
1017 /* Now we are done with PASSWD */
1018 (void) fclose(fp_ptemp
);
1020 /* Do this if we are touching both password files */
1023 if (info_mask
& BOTH_FILES
) {
1024 info_mask
&= ~FOUND
; /* Reset FOUND flag */
1026 /* The while loop for reading SHADOW entries */
1027 info_mask
|= WRITE_S_ENTRY
;
1036 if ((spf
= fopen("/etc/shadow", "r")) == NULL
) {
1041 while (!end_of_file
) {
1042 sp_ptr1p
= fgetspent(spf
);
1043 if (sp_ptr1p
== NULL
) {
1052 if (!NIS_entry_seen
)
1053 info_mask
|= WRITE_S_ENTRY
;
1055 info_mask
&= ~WRITE_S_ENTRY
;
1058 * See if the new logname already exist in the
1059 * shadow passwd file
1061 if ((optn_mask
& M_MASK
) &&
1062 strcmp(lognamp
, shadow_st
.sp_namp
) != 0 &&
1063 strcmp(sp_ptr1p
->sp_namp
, shadow_st
.sp_namp
) == 0) {
1068 if (strcmp(lognamp
, sp_ptr1p
->sp_namp
) == 0) {
1070 if (optn_mask
& A_MASK
) {
1071 /* password file inconsistent */
1076 if (optn_mask
& M_MASK
) {
1077 sp_ptr1p
->sp_namp
= shadow_st
.sp_namp
;
1078 if (F_MASK
& optn_mask
)
1079 sp_ptr1p
->sp_inact
=
1081 if (E_MASK
& optn_mask
)
1082 sp_ptr1p
->sp_expire
=
1083 shadow_st
.sp_expire
;
1088 if (optn_mask
& D_MASK
)
1089 info_mask
&= ~WRITE_S_ENTRY
;
1092 if (optn_mask
& A_MASK
) {
1093 if (!NIS_entry_seen
) {
1095 p
= strchr("+-", sp_ptr1p
->sp_namp
[0]);
1098 * Found first NIS entry.
1103 info_mask
&= ~WRITE_S_ENTRY
;
1106 cur_pos
= ftell(spf
);
1110 if (info_mask
& WRITE_S_ENTRY
) {
1111 if (putspent(sp_ptr1p
, fp_stemp
)) {
1117 } /* end-of-while-loop */
1121 msg
= BAD_ENT_MESSAGE
;
1122 fprintf(stderr
, gettext(msg
), prognamp
);
1126 * If we cannot find the entry and we are deleting or
1130 if (!(info_mask
& FOUND
) && (optn_mask
& (D_MASK
|M_MASK
))) {
1135 if (optn_mask
& A_MASK
) {
1136 ck_s_sz(&shadow_st
);
1137 if (putspent(&shadow_st
, fp_stemp
)) {
1143 * Now put out the rest of the shadow file, if needed.
1145 if (NIS_entry_seen
) {
1146 file_copy(spf
, NIS_pos
);
1150 /* flush and sync the file before closing it */
1151 if (fflush(fp_stemp
) != 0 || fsync(fd_stemp
) != 0)
1153 (void) fclose(fp_stemp
);
1155 /* Done with SHADOW */
1158 } /* End of if info_mask */
1160 if (info_mask
& UATTR_FILE
) {
1161 info_mask
&= ~FOUND
; /* Reset FOUND flag */
1163 /* The while loop for reading USER_ATTR entries */
1164 info_mask
|= WRITE_S_ENTRY
;
1173 if ((uaf
= fopen(USERATTR_FILENAME
, "r")) == NULL
) {
1178 while (!end_of_file
) {
1179 ua_ptr1p
= fgetuserattr(uaf
);
1180 if (ua_ptr1p
== NULL
) {
1189 if (ua_ptr1p
->name
[0] == '#') {
1191 * If this is a comment, write it back as it
1194 if (ua_ptr1p
->qualifier
[0] == '\0' &&
1195 ua_ptr1p
->res1
[0] == '\0' &&
1196 ua_ptr1p
->res2
[0] == '\0' &&
1197 (ua_ptr1p
->attr
== NULL
||
1198 ua_ptr1p
->attr
->length
== 0))
1199 (void) fprintf(fp_uatemp
, "%s\n",
1203 * This is a commented user_attr entry;
1204 * reformat it, and write it back.
1206 putuserattrent(ua_ptr1p
, fp_uatemp
);
1207 free_userattr(ua_ptr1p
);
1211 if (!NIS_entry_seen
)
1212 info_mask
|= WRITE_S_ENTRY
;
1214 info_mask
&= ~WRITE_S_ENTRY
;
1217 * See if the new logname already exist in the
1220 if ((optn_mask
& M_MASK
) &&
1221 strcmp(lognamp
, userattr_st
.name
) != 0 &&
1222 strcmp(ua_ptr1p
->name
, userattr_st
.name
) == 0) {
1227 if (strcmp(lognamp
, ua_ptr1p
->name
) == 0) {
1229 if (optn_mask
& A_MASK
) {
1230 /* password file inconsistent */
1235 if (optn_mask
& M_MASK
) {
1239 for (j
= 0; j
< UA_KEYS
; j
++) {
1240 if (ua_opts
[j
].newvalue
!= NULL
)
1243 kva_match(ua_ptr1p
->attr
,
1244 (char *)ua_opts
[j
].key
);
1247 assign_attr(&userattr_st
,
1251 free_userattr(ua_ptr1p
);
1252 ua_ptr1p
= &userattr_st
;
1255 if (optn_mask
& D_MASK
)
1256 info_mask
&= ~WRITE_S_ENTRY
;
1257 } else if (optn_mask
& D_MASK
) {
1260 rolelist
= kva_match(ua_ptr1p
->attr
,
1263 unassign_role(ua_ptr1p
,
1268 if (info_mask
& WRITE_S_ENTRY
) {
1269 putuserattrent(ua_ptr1p
, fp_uatemp
);
1272 if (!(optn_mask
& M_MASK
))
1273 free_userattr(ua_ptr1p
);
1274 } /* end-of-while-loop */
1278 msg
= BAD_ENT_MESSAGE
;
1279 fprintf(stderr
, gettext(msg
), prognamp
);
1283 * Add entry in user_attr if masks is UATTR_MASK
1284 * We don't need to do anything for L_MASK if there's
1285 * no user_attr entry for the user being modified.
1287 if (!(info_mask
& FOUND
) && !(L_MASK
& optn_mask
) &&
1288 !(D_MASK
& optn_mask
)) {
1289 putuserattrent(&userattr_st
, fp_uatemp
);
1292 /* flush and sync the file before closing it */
1293 if (fflush(fp_uatemp
) != 0 || fsync(fd_uatemp
) != 0)
1295 (void) fclose(fp_uatemp
);
1297 /* Done with USERATTR */
1300 } /* End of if info_mask */
1301 /* ignore all signals */
1303 for (i
= 1; i
< NSIG
; i
++)
1304 (void) sigset(i
, SIG_IGN
);
1306 errno
= 0; /* For correcting sigset to SIGKILL */
1308 if (unlink(OPASSWD
) && access(OPASSWD
, 0) == 0)
1311 if (link(PASSWD
, OPASSWD
) == -1)
1315 if (rename(PASSTEMP
, PASSWD
) == -1) {
1316 if (link(OPASSWD
, PASSWD
))
1322 if (info_mask
& BOTH_FILES
) {
1324 if (unlink(OSHADOW
) && access(OSHADOW
, 0) == 0) {
1331 if (link(SHADOW
, OSHADOW
) == -1) {
1339 if (rename(SHADTEMP
, SHADOW
) == -1) {
1340 if (rename(OSHADOW
, SHADOW
) == -1)
1350 if (info_mask
& UATTR_FILE
) {
1351 if (unlink(OUSERATTR_FILENAME
) &&
1352 access(OUSERATTR_FILENAME
, 0) == 0) {
1359 if (link(USERATTR_FILENAME
, OUSERATTR_FILENAME
) == -1) {
1367 if (rename(USERATTR_TEMP
, USERATTR_FILENAME
) == -1) {
1368 if (rename(OUSERATTR_FILENAME
, USERATTR_FILENAME
) == -1)
1382 * Return 0 status, indicating success
1388 /* Try to recover the old password file */
1393 if (unlink(PASSWD
) || link(OPASSWD
, PASSWD
))
1399 /* combine two uid_blk's */
1402 uid_bcom(struct uid_blk
*uid_p
)
1404 struct uid_blk
*uid_tp
;
1406 uid_tp
= uid_p
->link
;
1407 uid_p
->high
= uid_tp
->high
;
1408 uid_p
->link
= uid_tp
->link
;
1413 /* add a new uid_blk */
1416 add_ublk(uid_t num
, struct uid_blk
*uid_p
)
1418 struct uid_blk
*uid_tp
;
1420 uid_tp
= malloc(sizeof (struct uid_blk
));
1421 if (uid_tp
== NULL
) {
1426 uid_tp
->high
= uid_tp
->low
= num
;
1427 uid_tp
->link
= uid_p
->link
;
1428 uid_p
->link
= uid_tp
;
1432 * Here we are using a linked list of uid_blk to keep track of all
1433 * the used uids. Each uid_blk represents a range of used uid,
1434 * with low represents the low inclusive end and high represents
1435 * the high inclusive end. In the beginning, we initialize a linked
1436 * list of one uid_blk with low = high = (UID_MIN-1). This was
1438 * Each time we read in another used uid, we add it onto the linked
1439 * list by either making a new uid_blk, decrementing the low of
1440 * an existing uid_blk, incrementing the high of an existing
1441 * uid_blk, or combining two existing uid_blks. After we finished
1442 * building this linked list, the first available uid above or
1443 * equal to UID_MIN is the high of the first uid_blk in the linked
1446 /* add_uid() adds uid to the link list of used uids */
1450 struct uid_blk
*uid_p
;
1451 /* Only keep track of the ones above UID_MIN */
1453 if (uid
>= UID_MIN
) {
1456 while (uid_p
!= NULL
) {
1458 if (uid_p
->link
!= NULL
) {
1460 if (uid
>= uid_p
->link
->low
)
1461 uid_p
= uid_p
->link
;
1463 else if (uid
>= uid_p
->low
&&
1464 uid
<= uid_p
->high
) {
1468 else if (uid
== (uid_p
->high
+1)) {
1470 if (++uid_p
->high
==
1471 (uid_p
->link
->low
- 1)) {
1477 else if (uid
== (uid_p
->link
->low
- 1)) {
1478 uid_p
->link
->low
--;
1482 else if (uid
< uid_p
->link
->low
) {
1483 add_ublk(uid
, uid_p
);
1486 } /* if uid_p->link */
1490 if (uid
== (uid_p
->high
+ 1)) {
1493 } else if (uid
>= uid_p
->low
&&
1494 uid
<= uid_p
->high
) {
1497 add_ublk(uid
, uid_p
);
1509 (void) fprintf(stderr
, gettext("%s: Permission denied\n"), prognamp
);
1516 if (strlen(sp
) != 0)
1517 (void) fprintf(stderr
, "%s: %s\n", prognamp
, gettext(sp
));
1518 (void) fprintf(stderr
, gettext("Usage:\n\
1519 %s -a [-c comment] [-h homedir] [-u uid [-o]] [-g gid] \n\
1520 [-s shell] [-f inactive] [-e expire] name\n\
1521 %s -m -c comment | -h homedir | -u uid [-o] | -g gid |\n\
1522 -s shell | -f inactive | -e expire | -l logname name\n\
1523 %s -d name\n"), prognamp
, prognamp
, prognamp
);
1524 if (info_mask
& LOCKED
)
1532 (void) fprintf(stderr
, "%s: %s\n", prognamp
, gettext(s
));
1534 if (info_mask
& LOCKED
)
1542 (void) fprintf(stderr
, "%s: %s\n", prognamp
, gettext(s
));
1550 (void) fprintf(stderr
, gettext("%s: UID in use\n"), prognamp
);
1559 msg
= "%s: Inconsistent password files\n";
1560 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1569 msg
= "%s: Bad user_attr database\n";
1570 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1579 msg
= "%s: Unexpected failure. Password files unchanged\n";
1580 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1589 msg
= "%s: Unexpected failure. Password file(s) missing\n";
1590 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1599 msg
= "%s: Password file(s) busy. Try again later\n";
1600 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1605 /* Check for the size of the whole passwd entry */
1607 ck_p_sz(struct passwd
*pwp
)
1611 /* Ensure that the combined length of the individual */
1612 /* fields will fit in a passwd entry. The 1 accounts for the */
1613 /* newline and the 6 accounts for the colons (:'s) */
1614 if (((int)strlen(pwp
->pw_name
) + 1 +
1615 sprintf(ctp
, "%d", pwp
->pw_uid
) +
1616 sprintf(ctp
, "%d", pwp
->pw_gid
) +
1617 (int)strlen(pwp
->pw_comment
) +
1618 (int)strlen(pwp
->pw_dir
) +
1619 (int)strlen(pwp
->pw_shell
) + 6) > (ENTRY_LENGTH
-1)) {
1621 bad_arg("New password entry too long");
1625 /* Check for the size of the whole passwd entry */
1627 ck_s_sz(struct spwd
*ssp
)
1631 /* Ensure that the combined length of the individual */
1632 /* fields will fit in a shadow entry. The 1 accounts for the */
1633 /* newline and the 7 accounts for the colons (:'s) */
1634 if (((int)strlen(ssp
->sp_namp
) + 1 +
1635 (int)strlen(ssp
->sp_pwdp
) +
1636 sprintf(ctp
, "%d", ssp
->sp_lstchg
) +
1637 sprintf(ctp
, "%d", ssp
->sp_min
) +
1638 sprintf(ctp
, "%d", ssp
->sp_max
) +
1639 sprintf(ctp
, "%d", ssp
->sp_warn
) +
1640 sprintf(ctp
, "%d", ssp
->sp_inact
) +
1641 sprintf(ctp
, "%d", ssp
->sp_expire
) + 7) > (ENTRY_LENGTH
- 1)) {
1643 bad_arg("New password entry too long");
1647 /* Get rid of the temp files */
1651 (void) fclose(fp_ptemp
);
1653 if (unlink(PASSTEMP
)) {
1654 msg
= "%s: warning: cannot unlink %s\n";
1655 (void) fprintf(stderr
, gettext(msg
), prognamp
, PASSTEMP
);
1658 if (info_mask
& BOTH_FILES
) {
1659 (void) fclose(fp_stemp
);
1661 if (unlink(SHADTEMP
)) {
1662 msg
= "%s: warning: cannot unlink %s\n";
1663 (void) fprintf(stderr
, gettext(msg
), prognamp
,
1668 if (info_mask
& UATTR_FILE
) {
1669 (void) fclose(fp_uatemp
);
1671 if (unlink(USERATTR_TEMP
)) {
1672 msg
= "%s: warning: cannot unlink %s\n";
1673 (void) fprintf(stderr
, gettext(msg
), prognamp
,
1680 file_copy(FILE *spf
, long NIS_pos
)
1685 if (fseek(spf
, NIS_pos
, SEEK_SET
) < 0) {
1689 while ((n
= fread(buf
, sizeof (char), 1024, spf
)) > 0) {
1690 if (fwrite(buf
, sizeof (char), n
, fp_stemp
) != n
) {