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.\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_AUDIT_FLAGS_KW
},
96 #define UA_KEYS (sizeof (ua_opts)/sizeof (kvopts_t))
99 char defdir
[] = "/home/"; /* default home directory for new user */
100 char pwdflr
[] = "x"; /* password string for /etc/passwd */
101 char lkstring
[] = "*LK*"; /* lock string for shadow password */
102 char nullstr
[] = ""; /* null string */
103 char *msg
; /* pointer to error message */
105 #define DATMSK "DATEMSK=/etc/datemsk"
107 #define OUSERATTR_FILENAME "/etc/ouser_attr"
108 #define USERATTR_TEMP "/etc/uatmp"
111 struct uid_blk
*link
;
112 uid_t low
; /* low bound for this uid block */
113 uid_t high
; /* high bound for this uid block */
116 extern userattr_t
*fgetuserattr(FILE *);
117 extern struct passwd
*fgetpwent(FILE *);
120 * Declare all functions that do not return integers. This is here
121 * to get rid of some lint messages
124 void uid_bcom(struct uid_blk
*), add_ublk(uid_t
, struct uid_blk
*),
126 bad_usage(char *), bad_arg(char *), bad_uid(void), bad_pasf(void),
127 file_error(void), bad_news(void), no_lock(void), add_uid(uid_t
),
128 rid_tmpf(void), ck_p_sz(struct passwd
*), ck_s_sz(struct spwd
*),
129 bad_name(char *), bad_uattr(void);
131 static FILE *fp_ptemp
, *fp_stemp
, *fp_uatemp
;
132 static int fd_ptemp
, fd_stemp
, fd_uatemp
;
135 * The uid_blk structure is used in the search for the default
136 * uid. Each uid_blk represent a range of uid(s) that are currently
137 * used on the system.
143 * getspnan routine that ONLY looks at the local shadow file
146 local_getspnam(char *name
)
151 if ((shadf
= fopen("/etc/shadow", "r")) == NULL
)
154 while ((sp
= fgetspent(shadf
)) != NULL
) {
155 if (strcmp(sp
->sp_namp
, name
) == 0)
166 putuserattrent(userattr_t
*user
, FILE *f
)
174 * Avoid trivial entries. Those with no attributes or with
175 * only "type=normal". This retains backward compatibility.
177 if (user
->attr
== NULL
)
180 kv_pair
= user
->attr
->data
;
182 for (i
= j
= 0; i
< user
->attr
->length
; i
++) {
183 key
= kv_pair
[i
].key
;
184 val
= kv_pair
[i
].value
;
185 if ((key
== NULL
) || (val
== NULL
))
187 if (strlen(val
) == 0 ||
188 (strcmp(key
, USERATTR_TYPE_KW
) == 0 &&
189 strcmp(val
, USERATTR_TYPE_NORMAL_KW
) == 0))
196 (void) fprintf(f
, "%s:%s:%s:%s:", user
->name
, user
->qualifier
,
197 user
->res1
, user
->res2
);
199 for (i
= j
= 0; i
< user
->attr
->length
; i
++) {
200 key
= kv_pair
[i
].key
;
201 val
= _escape(kv_pair
[i
].value
, KV_SPECIAL
);
202 if ((key
== NULL
) || (val
== NULL
))
204 if (strlen(val
) == 0)
207 (void) fprintf(f
, KV_DELIMITER
);
208 (void) fprintf(f
, "%s=%s", key
, val
);
211 (void) fprintf(f
, "\n");
215 assign_attr(userattr_t
*user
, const char *newkey
, char *val
)
223 if (user
->attr
!= NULL
) {
224 kv_pair
= user
->attr
->data
;
225 for (i
= 0; i
< user
->attr
->length
; i
++) {
226 key
= kv_pair
[i
].key
;
230 } else if (strcmp(key
, newkey
) == 0) {
231 kv_pair
[i
].value
= strdup(val
);
237 avail
= user
->attr
->length
++;
238 kv_pair
[avail
].key
= strdup(newkey
);
239 kv_pair
[avail
].value
= strdup(val
);
244 unassign_role(userattr_t
*user
, char *rolelist
, char *role
)
253 templist
= strdup(roleptr
);
254 temprole
= strtok(templist
, ",");
256 if (strcmp(temprole
, role
) == 0) {
258 length
= strlen(role
);
259 roleptr
+= temprole
- templist
;
261 if (*(roleptr
+ length
) == ',')
263 strcpy(roleptr
, roleptr
+ length
);
264 length
= strlen(roleptr
) - 1;
265 if (*(roleptr
+ length
) == ',')
266 *(roleptr
+ length
) = '\0';
267 assign_attr(user
, USERATTR_ROLES_KW
, rolelist
);
270 temprole
= strtok(NULL
, ",");
275 struct uid_blk
*uid_sp
;
276 char *prognamp
; /* program name */
278 int optn_mask
= 0, info_mask
= 0;
281 main(int argc
, char **argv
)
284 char *lognamp
, *char_p
;
288 FILE *pwf
, *spf
, *uaf
;
290 struct passwd
*pw_ptr1p
, passwd_st
;
291 struct spwd
*sp_ptr1p
, shadow_st
;
292 userattr_t
*ua_ptr1p
, userattr_st
;
293 static kv_t ua_kv
[KV_ADD_KEYS
];
298 (void) setlocale(LC_ALL
, "");
300 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
301 #define TEXT_DOMAIN "SYS_TEST"
303 (void) textdomain(TEXT_DOMAIN
);
306 /* Get program name */
313 /* Lock the password file(s) */
317 info_mask
|= LOCKED
; /* remember we locked */
319 /* initialize the two structures */
321 passwd_st
.pw_passwd
= pwdflr
; /* bogus password */
322 passwd_st
.pw_name
= nullstr
; /* login name */
323 passwd_st
.pw_uid
= -1; /* no uid */
324 passwd_st
.pw_gid
= 1; /* default gid */
325 passwd_st
.pw_age
= nullstr
; /* no aging info. */
326 passwd_st
.pw_comment
= nullstr
; /* no comments */
327 passwd_st
.pw_gecos
= nullstr
; /* no comments */
328 passwd_st
.pw_dir
= nullstr
; /* no default directory */
329 passwd_st
.pw_shell
= nullstr
; /* no default shell */
331 shadow_st
.sp_namp
= nullstr
; /* no name */
332 shadow_st
.sp_pwdp
= lkstring
; /* locked password */
333 shadow_st
.sp_lstchg
= -1; /* no lastchanged date */
334 shadow_st
.sp_min
= -1; /* no min */
335 shadow_st
.sp_max
= -1; /* no max */
336 shadow_st
.sp_warn
= -1; /* no warn */
337 shadow_st
.sp_inact
= -1; /* no inactive */
338 shadow_st
.sp_expire
= -1; /* no expire */
339 shadow_st
.sp_flag
= 0; /* no flag */
341 userattr_st
.name
= nullstr
;
342 userattr_st
.qualifier
= nullstr
;
343 userattr_st
.res1
= nullstr
;
344 userattr_st
.res2
= nullstr
;
347 ua_kv
[0].key
= USERATTR_TYPE_KW
;
348 ua_kv
[0].value
= USERATTR_TYPE_NORMAL_KW
;
350 userattr_st
.attr
= &ua_kva
;
352 /* parse the command line */
354 while ((c
= getopt(argc
, argv
,
355 "ml:c:h:u:g:s:f:e:k:A:P:R:T:oadK:")) != -1) {
361 if ((A_MASK
|D_MASK
|M_MASK
) & optn_mask
)
362 bad_usage("Invalid combination of options");
370 if ((A_MASK
|D_MASK
|L_MASK
) & optn_mask
)
371 bad_usage("Invalid combination of options");
373 if (strpbrk(optarg
, ":\n") ||
375 bad_arg("Invalid argument to option -l");
378 passwd_st
.pw_name
= optarg
;
379 shadow_st
.sp_namp
= optarg
;
380 userattr_st
.name
= optarg
;
386 if ((D_MASK
|F_MASK
) & optn_mask
)
387 bad_usage("Invalid combination of options");
388 if (((shadow_st
.sp_inact
=
389 strtol(optarg
, &char_p
, 10)) < (long)0) ||
392 bad_arg("Invalid argument to option -f");
393 if (shadow_st
.sp_inact
== 0)
394 shadow_st
.sp_inact
= -1;
399 /* set expire date */
401 if ((D_MASK
|E_MASK
) & optn_mask
)
402 bad_usage("Invalid combination of options");
404 if ((strlen(optarg
)) < (size_t)2)
405 shadow_st
.sp_expire
= -1;
408 if ((tm_ptr
= getdate(optarg
)) == NULL
) {
409 msg
= "Invalid argument to option -e";
412 if ((date
= mktime(tm_ptr
)) < 0) {
413 msg
= "Invalid argument to option -e";
416 shadow_st
.sp_expire
= (date
/ DAY
);
417 if (shadow_st
.sp_expire
<= DAY_NOW
) {
418 msg
= "Invalid argument to option -e";
429 if ((D_MASK
|C_MASK
) & optn_mask
)
430 bad_usage("Invalid combination of options");
432 if (strlen(optarg
) > (size_t)CMT_SIZE
||
433 strpbrk(optarg
, ":\n"))
434 bad_arg("Invalid argument to option -c");
437 passwd_st
.pw_comment
= optarg
;
438 passwd_st
.pw_gecos
= optarg
;
442 /* The home directory */
444 if ((D_MASK
|H_MASK
) & optn_mask
)
445 bad_usage("Invalid combination of options");
447 if (strlen(optarg
) > (size_t)DIR_SIZE
||
448 strpbrk(optarg
, ":\n"))
449 bad_arg("Invalid argument to option -h");
452 passwd_st
.pw_dir
= optarg
;
458 if ((D_MASK
|U_MASK
) & optn_mask
)
459 bad_usage("Invalid combination of options");
462 passwd_st
.pw_uid
= (uid_t
)strtol(optarg
, &char_p
, 10);
463 if ((*char_p
!= '\0') ||
464 (passwd_st
.pw_uid
< 0) ||
465 (strlen(optarg
) == 0))
466 bad_arg("Invalid argument to option -u");
473 if ((D_MASK
|G_MASK
) & optn_mask
)
474 bad_usage("Invalid combination of options");
477 passwd_st
.pw_gid
= (gid_t
)strtol(optarg
, &char_p
, 10);
479 if ((*char_p
!= '\0') || (passwd_st
.pw_gid
< 0) ||
480 (strlen(optarg
) == 0))
481 bad_arg("Invalid argument to option -g");
487 if ((D_MASK
|S_MASK
) & optn_mask
)
488 bad_usage("Invalid combination of options");
490 if (strlen(optarg
) > (size_t)SHL_SIZE
||
491 strpbrk(optarg
, ":\n"))
492 bad_arg("Invalid argument to option -s");
495 passwd_st
.pw_shell
= optarg
;
499 /* Override unique uid */
501 if ((D_MASK
|O_MASK
) & optn_mask
)
502 bad_usage("Invalid combination of options");
510 if ((A_MASK
|M_MASK
|D_MASK
|L_MASK
) & optn_mask
)
511 bad_usage("Invalid combination of options");
519 if ((D_MASK
|M_MASK
|L_MASK
|C_MASK
|
520 H_MASK
|U_MASK
|G_MASK
|S_MASK
|
521 O_MASK
|A_MASK
) & optn_mask
)
522 bad_usage("Invalid combination of options");
528 if (D_MASK
& optn_mask
)
529 bad_usage("Invalid combination of options");
531 char_p
= strchr(optarg
, '=');
533 bad_usage("Missing value in -K option");
537 for (i
= 0; i
< UA_KEYS
; i
++) {
538 if (strcmp(optarg
, ua_opts
[i
].key
) == 0) {
539 ua_opts
[i
].newvalue
=
540 _escape(char_p
, KV_SPECIAL
);
541 assign_attr(&userattr_st
, optarg
,
547 bad_usage("bad key");
548 optn_mask
|= UATTR_MASK
;
557 /* Extended User Attributes */
561 for (j
= 0; j
< UA_KEYS
; j
++) {
562 if (ua_opts
[j
].option
== (char)c
) {
563 if ((D_MASK
) & optn_mask
)
567 optn_mask
|= UATTR_MASK
;
568 assign_attr(&userattr_st
,
572 ua_opts
[j
].newvalue
=
573 _escape(optarg
, KV_SPECIAL
);
582 /* check command syntax for the following errors */
583 /* too few or too many arguments */
584 /* no -a -m or -d option */
586 /* -m with no other option */
588 if (optind
== argc
|| argc
> (optind
+1) ||
589 !((A_MASK
|M_MASK
|D_MASK
) & optn_mask
) ||
590 ((optn_mask
& O_MASK
) && !(optn_mask
& U_MASK
)) ||
591 ((optn_mask
& M_MASK
) &&
593 (L_MASK
|C_MASK
|H_MASK
|U_MASK
|G_MASK
|S_MASK
|F_MASK
|
594 E_MASK
|UATTR_MASK
))))
595 bad_usage("Invalid command syntax");
597 /* null string argument or bad characters ? */
598 if ((strlen(argv
[optind
]) == 0) || strpbrk(argv
[optind
], ":\n"))
599 bad_arg("Invalid name");
601 lognamp
= argv
[optind
];
604 * if we are adding a new user or modifying an existing user
605 * (not the logname), then copy logname into the two data
609 if ((A_MASK
& optn_mask
) ||
610 ((M_MASK
& optn_mask
) && !(optn_mask
& L_MASK
))) {
611 passwd_st
.pw_name
= argv
[optind
];
612 shadow_st
.sp_namp
= argv
[optind
];
613 userattr_st
.name
= argv
[optind
];
616 /* Put in directory if we are adding and we need a default */
618 if (!(optn_mask
& H_MASK
) && (optn_mask
& A_MASK
)) {
619 if ((passwd_st
.pw_dir
= malloc((size_t)DIR_SIZE
)) == NULL
)
622 *passwd_st
.pw_dir
= '\0';
623 (void) strcat(passwd_st
.pw_dir
, defdir
);
624 (void) strcat(passwd_st
.pw_dir
, lognamp
);
627 /* Check the number of password files we are touching */
629 if ((!((M_MASK
& optn_mask
) && !(L_MASK
& optn_mask
))) ||
630 ((M_MASK
& optn_mask
) && ((E_MASK
& optn_mask
) ||
631 (F_MASK
& optn_mask
))))
632 info_mask
|= BOTH_FILES
;
634 if ((D_MASK
|L_MASK
|UATTR_MASK
) & optn_mask
)
635 info_mask
|= UATTR_FILE
;
637 /* Open the temporary file(s) with appropriate permission mask */
638 /* and the appropriate owner */
640 if (stat(PASSWD
, &statbuf
) < 0)
643 fd_ptemp
= open(PASSTEMP
, O_CREAT
|O_EXCL
|O_WRONLY
, statbuf
.st_mode
);
644 if (fd_ptemp
== -1) {
645 if (errno
== EEXIST
) {
646 if (unlink(PASSTEMP
)) {
647 msg
= "%s: warning: cannot unlink %s\n";
648 (void) fprintf(stderr
, gettext(msg
), prognamp
,
651 fd_ptemp
= open(PASSTEMP
, O_CREAT
|O_EXCL
|O_WRONLY
,
653 if (fd_ptemp
== -1) {
660 fp_ptemp
= fdopen(fd_ptemp
, "w");
661 if (fp_ptemp
== NULL
)
663 error
= fchown(fd_ptemp
, statbuf
.st_uid
, statbuf
.st_gid
);
665 error
= fchmod(fd_ptemp
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
);
667 (void) fclose(fp_ptemp
);
668 if (unlink(PASSTEMP
)) {
669 msg
= "%s: warning: cannot unlink %s\n";
670 (void) fprintf(stderr
, gettext(msg
), prognamp
,
676 if (info_mask
& BOTH_FILES
) {
677 if (stat(SHADOW
, &statbuf
) < 0) {
681 fd_stemp
= open(SHADTEMP
, O_CREAT
|O_EXCL
|O_WRONLY
,
683 if (fd_stemp
== -1) {
684 if (errno
== EEXIST
) {
685 if (unlink(SHADTEMP
)) {
686 msg
= "%s: warning: cannot unlink %s\n";
687 (void) fprintf(stderr
, gettext(msg
),
690 fd_stemp
= open(SHADTEMP
,
691 O_CREAT
|O_EXCL
|O_WRONLY
, statbuf
.st_mode
);
692 if (fd_stemp
== -1) {
702 fp_stemp
= fdopen(fd_stemp
, "w");
703 if (fp_stemp
== NULL
) {
707 error
= fchown(fd_stemp
, statbuf
.st_uid
, statbuf
.st_gid
);
709 error
= fchmod(fd_stemp
, S_IRUSR
);
716 if (info_mask
& UATTR_FILE
) {
717 if (stat(USERATTR_FILENAME
, &statbuf
) < 0) {
721 fd_uatemp
= open(USERATTR_TEMP
, O_CREAT
|O_EXCL
|O_WRONLY
,
723 if (fd_uatemp
== -1) {
724 if (errno
== EEXIST
) {
725 if (unlink(USERATTR_TEMP
)) {
726 msg
= "%s: warning: cannot unlink %s\n";
727 (void) fprintf(stderr
, gettext(msg
),
728 prognamp
, USERATTR_TEMP
);
730 fd_uatemp
= open(USERATTR_TEMP
,
731 O_CREAT
|O_EXCL
|O_WRONLY
, statbuf
.st_mode
);
732 if (fd_uatemp
== -1) {
742 fp_uatemp
= fdopen(fd_uatemp
, "w");
743 if (fp_uatemp
== NULL
) {
747 error
= fchown(fd_uatemp
, statbuf
.st_uid
, statbuf
.st_gid
);
749 error
= fchmod(fd_uatemp
,
750 S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
);
756 /* Default uid needed ? */
758 if (!(optn_mask
& U_MASK
) && (optn_mask
& A_MASK
)) {
759 /* mark it in the information mask */
760 info_mask
|= NEED_DEF_UID
;
762 /* create the head of the uid number list */
763 uid_sp
= malloc(sizeof (struct uid_blk
));
764 if (uid_sp
== NULL
) {
770 uid_sp
->low
= (UID_MIN
-1);
771 uid_sp
->high
= (UID_MIN
-1);
776 if ((pwf
= fopen("/etc/passwd", "r")) == NULL
) {
784 /* The while loop for reading PASSWD entries */
785 info_mask
|= WRITE_P_ENTRY
;
787 while (!end_of_file
) {
788 pw_ptr1p
= fgetpwent(pwf
);
789 if (pw_ptr1p
== NULL
) {
791 /* A real error - report it and exit */
799 info_mask
|= WRITE_P_ENTRY
;
802 * Set up the uid usage blocks to find the first
803 * available uid above UID_MIN, if needed
806 if (info_mask
& NEED_DEF_UID
)
807 add_uid(pw_ptr1p
->pw_uid
);
809 /* Check for unique UID */
811 if (strcmp(lognamp
, pw_ptr1p
->pw_name
) &&
812 (pw_ptr1p
->pw_uid
== passwd_st
.pw_uid
) &&
813 ((optn_mask
& U_MASK
) && !(optn_mask
& O_MASK
))) {
814 rid_tmpf(); /* get rid of temp files */
818 /* Check for unique new logname */
820 if (strcmp(lognamp
, pw_ptr1p
->pw_name
) == 0 &&
821 optn_mask
& L_MASK
&&
822 strcmp(pw_ptr1p
->pw_name
, passwd_st
.pw_name
) == 0) {
825 if (!getspnam(pw_ptr1p
->pw_name
))
827 if (!local_getspnam(pw_ptr1p
->pw_name
))
831 bad_name("logname already exists");
834 if (strcmp(lognamp
, pw_ptr1p
->pw_name
) == 0) {
836 /* no good if we want to add an existing logname */
837 if (optn_mask
& A_MASK
) {
840 if (!getspnam(lognamp
))
842 if (!local_getspnam(lognamp
))
846 bad_name("name already exists");
849 /* remember we found it */
852 /* Do not write it out on the fly */
853 if (optn_mask
& D_MASK
)
854 info_mask
&= ~WRITE_P_ENTRY
;
856 if (optn_mask
& M_MASK
) {
859 if (!getspnam(lognamp
))
861 if (!local_getspnam(lognamp
))
867 if (optn_mask
& L_MASK
)
868 pw_ptr1p
->pw_name
= passwd_st
.pw_name
;
870 if (optn_mask
& U_MASK
)
871 pw_ptr1p
->pw_uid
= passwd_st
.pw_uid
;
873 if (optn_mask
& G_MASK
)
874 pw_ptr1p
->pw_gid
= passwd_st
.pw_gid
;
876 if (optn_mask
& C_MASK
) {
877 pw_ptr1p
->pw_comment
=
878 passwd_st
.pw_comment
;
881 passwd_st
.pw_comment
;
884 if (optn_mask
& H_MASK
)
885 pw_ptr1p
->pw_dir
= passwd_st
.pw_dir
;
887 if (optn_mask
& S_MASK
)
888 pw_ptr1p
->pw_shell
= passwd_st
.pw_shell
;
889 ck_p_sz(pw_ptr1p
); /* check entry size */
893 if (info_mask
& WRITE_P_ENTRY
) {
894 if (putpwent(pw_ptr1p
, fp_ptemp
)) {
899 } /* end-of-while-loop */
902 msg
= BAD_ENT_MESSAGE
;
903 fprintf(stderr
, gettext(msg
), prognamp
);
906 /* Cannot find the target entry and we are deleting or modifying */
908 if (!(info_mask
& FOUND
) && (optn_mask
& (D_MASK
|M_MASK
))) {
911 if (getspnam(lognamp
) != NULL
)
913 if (local_getspnam(lognamp
) != NULL
)
917 bad_name("name does not exist");
920 /* First available uid above UID_MIN is ... */
922 if (info_mask
& NEED_DEF_UID
)
923 passwd_st
.pw_uid
= uid_sp
->high
+ 1;
925 /* Write out the added entry now */
927 if (optn_mask
& A_MASK
) {
928 ck_p_sz(&passwd_st
); /* Check entry size */
929 if (putpwent(&passwd_st
, fp_ptemp
)) {
937 /* flush and sync the file before closing it */
938 if (fflush(fp_ptemp
) != 0 || fsync(fd_ptemp
) != 0)
941 /* Now we are done with PASSWD */
942 (void) fclose(fp_ptemp
);
944 /* Do this if we are touching both password files */
947 if (info_mask
& BOTH_FILES
) {
948 info_mask
&= ~FOUND
; /* Reset FOUND flag */
950 /* The while loop for reading SHADOW entries */
951 info_mask
|= WRITE_S_ENTRY
;
957 if ((spf
= fopen("/etc/shadow", "r")) == NULL
) {
962 while (!end_of_file
) {
963 sp_ptr1p
= fgetspent(spf
);
964 if (sp_ptr1p
== NULL
) {
973 info_mask
|= WRITE_S_ENTRY
;
976 * See if the new logname already exist in the
979 if ((optn_mask
& M_MASK
) &&
980 strcmp(lognamp
, shadow_st
.sp_namp
) != 0 &&
981 strcmp(sp_ptr1p
->sp_namp
, shadow_st
.sp_namp
) == 0) {
986 if (strcmp(lognamp
, sp_ptr1p
->sp_namp
) == 0) {
988 if (optn_mask
& A_MASK
) {
989 /* password file inconsistent */
994 if (optn_mask
& M_MASK
) {
995 sp_ptr1p
->sp_namp
= shadow_st
.sp_namp
;
996 if (F_MASK
& optn_mask
)
999 if (E_MASK
& optn_mask
)
1000 sp_ptr1p
->sp_expire
=
1001 shadow_st
.sp_expire
;
1006 if (optn_mask
& D_MASK
)
1007 info_mask
&= ~WRITE_S_ENTRY
;
1010 if (info_mask
& WRITE_S_ENTRY
) {
1011 if (putspent(sp_ptr1p
, fp_stemp
)) {
1017 } /* end-of-while-loop */
1021 msg
= BAD_ENT_MESSAGE
;
1022 fprintf(stderr
, gettext(msg
), prognamp
);
1026 * If we cannot find the entry and we are deleting or
1030 if (!(info_mask
& FOUND
) && (optn_mask
& (D_MASK
|M_MASK
))) {
1035 if (optn_mask
& A_MASK
) {
1036 ck_s_sz(&shadow_st
);
1037 if (putspent(&shadow_st
, fp_stemp
)) {
1043 /* flush and sync the file before closing it */
1044 if (fflush(fp_stemp
) != 0 || fsync(fd_stemp
) != 0)
1046 (void) fclose(fp_stemp
);
1048 /* Done with SHADOW */
1051 } /* End of if info_mask */
1053 if (info_mask
& UATTR_FILE
) {
1054 info_mask
&= ~FOUND
; /* Reset FOUND flag */
1056 /* The while loop for reading USER_ATTR entries */
1057 info_mask
|= WRITE_S_ENTRY
;
1063 if ((uaf
= fopen(USERATTR_FILENAME
, "r")) == NULL
) {
1068 while (!end_of_file
) {
1069 ua_ptr1p
= fgetuserattr(uaf
);
1070 if (ua_ptr1p
== NULL
) {
1079 if (ua_ptr1p
->name
[0] == '#') {
1081 * If this is a comment, write it back as it
1084 if (ua_ptr1p
->qualifier
[0] == '\0' &&
1085 ua_ptr1p
->res1
[0] == '\0' &&
1086 ua_ptr1p
->res2
[0] == '\0' &&
1087 (ua_ptr1p
->attr
== NULL
||
1088 ua_ptr1p
->attr
->length
== 0))
1089 (void) fprintf(fp_uatemp
, "%s\n",
1093 * This is a commented user_attr entry;
1094 * reformat it, and write it back.
1096 putuserattrent(ua_ptr1p
, fp_uatemp
);
1097 free_userattr(ua_ptr1p
);
1101 info_mask
|= WRITE_S_ENTRY
;
1104 * See if the new logname already exist in the
1107 if ((optn_mask
& M_MASK
) &&
1108 strcmp(lognamp
, userattr_st
.name
) != 0 &&
1109 strcmp(ua_ptr1p
->name
, userattr_st
.name
) == 0) {
1114 if (strcmp(lognamp
, ua_ptr1p
->name
) == 0) {
1116 if (optn_mask
& A_MASK
) {
1117 /* password file inconsistent */
1122 if (optn_mask
& M_MASK
) {
1126 for (j
= 0; j
< UA_KEYS
; j
++) {
1127 if (ua_opts
[j
].newvalue
!= NULL
)
1130 kva_match(ua_ptr1p
->attr
,
1131 (char *)ua_opts
[j
].key
);
1134 assign_attr(&userattr_st
,
1138 free_userattr(ua_ptr1p
);
1139 ua_ptr1p
= &userattr_st
;
1142 if (optn_mask
& D_MASK
)
1143 info_mask
&= ~WRITE_S_ENTRY
;
1144 } else if (optn_mask
& D_MASK
) {
1147 rolelist
= kva_match(ua_ptr1p
->attr
,
1150 unassign_role(ua_ptr1p
,
1155 if (info_mask
& WRITE_S_ENTRY
) {
1156 putuserattrent(ua_ptr1p
, fp_uatemp
);
1159 if (!(optn_mask
& M_MASK
))
1160 free_userattr(ua_ptr1p
);
1161 } /* end-of-while-loop */
1165 msg
= BAD_ENT_MESSAGE
;
1166 fprintf(stderr
, gettext(msg
), prognamp
);
1170 * Add entry in user_attr if masks is UATTR_MASK
1171 * We don't need to do anything for L_MASK if there's
1172 * no user_attr entry for the user being modified.
1174 if (!(info_mask
& FOUND
) && !(L_MASK
& optn_mask
) &&
1175 !(D_MASK
& optn_mask
)) {
1176 putuserattrent(&userattr_st
, fp_uatemp
);
1179 /* flush and sync the file before closing it */
1180 if (fflush(fp_uatemp
) != 0 || fsync(fd_uatemp
) != 0)
1182 (void) fclose(fp_uatemp
);
1184 /* Done with USERATTR */
1187 } /* End of if info_mask */
1188 /* ignore all signals */
1190 for (i
= 1; i
< NSIG
; i
++)
1191 (void) sigset(i
, SIG_IGN
);
1193 errno
= 0; /* For correcting sigset to SIGKILL */
1195 if (unlink(OPASSWD
) && access(OPASSWD
, 0) == 0)
1198 if (link(PASSWD
, OPASSWD
) == -1)
1202 if (rename(PASSTEMP
, PASSWD
) == -1) {
1203 if (link(OPASSWD
, PASSWD
))
1209 if (info_mask
& BOTH_FILES
) {
1211 if (unlink(OSHADOW
) && access(OSHADOW
, 0) == 0) {
1218 if (link(SHADOW
, OSHADOW
) == -1) {
1226 if (rename(SHADTEMP
, SHADOW
) == -1) {
1227 if (rename(OSHADOW
, SHADOW
) == -1)
1237 if (info_mask
& UATTR_FILE
) {
1238 if (unlink(OUSERATTR_FILENAME
) &&
1239 access(OUSERATTR_FILENAME
, 0) == 0) {
1246 if (link(USERATTR_FILENAME
, OUSERATTR_FILENAME
) == -1) {
1254 if (rename(USERATTR_TEMP
, USERATTR_FILENAME
) == -1) {
1255 if (rename(OUSERATTR_FILENAME
, USERATTR_FILENAME
) == -1)
1269 * Return 0 status, indicating success
1275 /* Try to recover the old password file */
1280 if (unlink(PASSWD
) || link(OPASSWD
, PASSWD
))
1286 /* combine two uid_blk's */
1289 uid_bcom(struct uid_blk
*uid_p
)
1291 struct uid_blk
*uid_tp
;
1293 uid_tp
= uid_p
->link
;
1294 uid_p
->high
= uid_tp
->high
;
1295 uid_p
->link
= uid_tp
->link
;
1300 /* add a new uid_blk */
1303 add_ublk(uid_t num
, struct uid_blk
*uid_p
)
1305 struct uid_blk
*uid_tp
;
1307 uid_tp
= malloc(sizeof (struct uid_blk
));
1308 if (uid_tp
== NULL
) {
1313 uid_tp
->high
= uid_tp
->low
= num
;
1314 uid_tp
->link
= uid_p
->link
;
1315 uid_p
->link
= uid_tp
;
1319 * Here we are using a linked list of uid_blk to keep track of all
1320 * the used uids. Each uid_blk represents a range of used uid,
1321 * with low represents the low inclusive end and high represents
1322 * the high inclusive end. In the beginning, we initialize a linked
1323 * list of one uid_blk with low = high = (UID_MIN-1). This was
1325 * Each time we read in another used uid, we add it onto the linked
1326 * list by either making a new uid_blk, decrementing the low of
1327 * an existing uid_blk, incrementing the high of an existing
1328 * uid_blk, or combining two existing uid_blks. After we finished
1329 * building this linked list, the first available uid above or
1330 * equal to UID_MIN is the high of the first uid_blk in the linked
1333 /* add_uid() adds uid to the link list of used uids */
1337 struct uid_blk
*uid_p
;
1338 /* Only keep track of the ones above UID_MIN */
1340 if (uid
>= UID_MIN
) {
1343 while (uid_p
!= NULL
) {
1345 if (uid_p
->link
!= NULL
) {
1347 if (uid
>= uid_p
->link
->low
)
1348 uid_p
= uid_p
->link
;
1350 else if (uid
>= uid_p
->low
&&
1351 uid
<= uid_p
->high
) {
1355 else if (uid
== (uid_p
->high
+1)) {
1357 if (++uid_p
->high
==
1358 (uid_p
->link
->low
- 1)) {
1364 else if (uid
== (uid_p
->link
->low
- 1)) {
1365 uid_p
->link
->low
--;
1369 else if (uid
< uid_p
->link
->low
) {
1370 add_ublk(uid
, uid_p
);
1373 } /* if uid_p->link */
1377 if (uid
== (uid_p
->high
+ 1)) {
1380 } else if (uid
>= uid_p
->low
&&
1381 uid
<= uid_p
->high
) {
1384 add_ublk(uid
, uid_p
);
1396 (void) fprintf(stderr
, gettext("%s: Permission denied\n"), prognamp
);
1403 if (strlen(sp
) != 0)
1404 (void) fprintf(stderr
, "%s: %s\n", prognamp
, gettext(sp
));
1405 (void) fprintf(stderr
, gettext("Usage:\n\
1406 %s -a [-c comment] [-h homedir] [-u uid [-o]] [-g gid] \n\
1407 [-s shell] [-f inactive] [-e expire] name\n\
1408 %s -m -c comment | -h homedir | -u uid [-o] | -g gid |\n\
1409 -s shell | -f inactive | -e expire | -l logname name\n\
1410 %s -d name\n"), prognamp
, prognamp
, prognamp
);
1411 if (info_mask
& LOCKED
)
1419 (void) fprintf(stderr
, "%s: %s\n", prognamp
, gettext(s
));
1421 if (info_mask
& LOCKED
)
1429 (void) fprintf(stderr
, "%s: %s\n", prognamp
, gettext(s
));
1437 (void) fprintf(stderr
, gettext("%s: UID in use\n"), prognamp
);
1446 msg
= "%s: Inconsistent password files\n";
1447 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1456 msg
= "%s: Bad user_attr database\n";
1457 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1466 msg
= "%s: Unexpected failure. Password files unchanged\n";
1467 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1476 msg
= "%s: Unexpected failure. Password file(s) missing\n";
1477 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1486 msg
= "%s: Password file(s) busy. Try again later\n";
1487 (void) fprintf(stderr
, gettext(msg
), prognamp
);
1492 /* Check for the size of the whole passwd entry */
1494 ck_p_sz(struct passwd
*pwp
)
1498 /* Ensure that the combined length of the individual */
1499 /* fields will fit in a passwd entry. The 1 accounts for the */
1500 /* newline and the 6 accounts for the colons (:'s) */
1501 if (((int)strlen(pwp
->pw_name
) + 1 +
1502 sprintf(ctp
, "%d", pwp
->pw_uid
) +
1503 sprintf(ctp
, "%d", pwp
->pw_gid
) +
1504 (int)strlen(pwp
->pw_comment
) +
1505 (int)strlen(pwp
->pw_dir
) +
1506 (int)strlen(pwp
->pw_shell
) + 6) > (ENTRY_LENGTH
-1)) {
1508 bad_arg("New password entry too long");
1512 /* Check for the size of the whole passwd entry */
1514 ck_s_sz(struct spwd
*ssp
)
1518 /* Ensure that the combined length of the individual */
1519 /* fields will fit in a shadow entry. The 1 accounts for the */
1520 /* newline and the 7 accounts for the colons (:'s) */
1521 if (((int)strlen(ssp
->sp_namp
) + 1 +
1522 (int)strlen(ssp
->sp_pwdp
) +
1523 sprintf(ctp
, "%d", ssp
->sp_lstchg
) +
1524 sprintf(ctp
, "%d", ssp
->sp_min
) +
1525 sprintf(ctp
, "%d", ssp
->sp_max
) +
1526 sprintf(ctp
, "%d", ssp
->sp_warn
) +
1527 sprintf(ctp
, "%d", ssp
->sp_inact
) +
1528 sprintf(ctp
, "%d", ssp
->sp_expire
) + 7) > (ENTRY_LENGTH
- 1)) {
1530 bad_arg("New password entry too long");
1534 /* Get rid of the temp files */
1538 (void) fclose(fp_ptemp
);
1540 if (unlink(PASSTEMP
)) {
1541 msg
= "%s: warning: cannot unlink %s\n";
1542 (void) fprintf(stderr
, gettext(msg
), prognamp
, PASSTEMP
);
1545 if (info_mask
& BOTH_FILES
) {
1546 (void) fclose(fp_stemp
);
1548 if (unlink(SHADTEMP
)) {
1549 msg
= "%s: warning: cannot unlink %s\n";
1550 (void) fprintf(stderr
, gettext(msg
), prognamp
,
1555 if (info_mask
& UATTR_FILE
) {
1556 (void) fclose(fp_uatemp
);
1558 if (unlink(USERATTR_TEMP
)) {
1559 msg
= "%s: warning: cannot unlink %s\n";
1560 (void) fprintf(stderr
, gettext(msg
), prognamp
,