1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
27
28 #include <stdio.h>
29 #include <sys/types.h>
30 #include <shadow.h>
31 #include <pwd.h>
32 #include <string.h>
33 #include <signal.h>
34 #include <sys/stat.h>
35 #include <errno.h>
36 #include <time.h>
37 #include <unistd.h>
38 #include <stdlib.h>
39 #include <locale.h>
40 #include <fcntl.h>
41 #include <secdb.h>
42 #include <user_attr.h>
43 #include <nss.h>
44
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 */
50
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 */
54 #define H_MASK 010
55 #define U_MASK 020
56 #define G_MASK 040
57 #define S_MASK 0100
58 #define O_MASK 0200
59 #define A_MASK 0400
60 #define D_MASK 01000
61 #define F_MASK 02000
62 #define E_MASK 04000
63
64 #define UATTR_MASK 010000
65
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"
76
77 typedef struct kvopts {
78 const char option;
79 const char *key;
80 char *newvalue;
81 } kvopts_t;
82
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 },
99 };
100
101 #define UA_KEYS (sizeof (ua_opts)/sizeof (kvopts_t))
102
103
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 */
109
110 #define DATMSK "DATEMSK=/etc/datemsk"
111
112 #define OUSERATTR_FILENAME "/etc/ouser_attr"
113 #define USERATTR_TEMP "/etc/uatmp"
114
115 struct uid_blk {
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 */
119 };
120
121 extern userattr_t *fgetuserattr(FILE *);
122
123
124 /*
125 * Declare all functions that do not return integers. This is here
126 * to get rid of some lint messages
127 */
128
129 void uid_bcom(struct uid_blk *), add_ublk(uid_t, struct uid_blk *),
130 bad_perm(void),
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);
135
136 void file_copy(FILE *spf, long NIS_pos);
137
138 static FILE *fp_ptemp, *fp_stemp, *fp_uatemp;
139 static int fd_ptemp, fd_stemp, fd_uatemp;
140
141 /*
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.
145 */
146
147
148 #ifndef att
149 /*
150 * getspnan routine that ONLY looks at the local shadow file
151 */
152 struct spwd *
local_getspnam(char * name)153 local_getspnam(char *name)
154 {
155 FILE *shadf;
156 struct spwd *sp;
157
158 if ((shadf = fopen("/etc/shadow", "r")) == NULL)
159 return (NULL);
160
161 while ((sp = fgetspent(shadf)) != NULL) {
162 if (strcmp(sp->sp_namp, name) == 0)
163 break;
164 }
165
166 fclose(shadf);
167
168 return (sp);
169 }
170 #endif
171
172 static void
putuserattrent(userattr_t * user,FILE * f)173 putuserattrent(userattr_t *user, FILE *f)
174 {
175 int i, j;
176 char *key;
177 char *val;
178 kv_t *kv_pair;
179
180 /*
181 * Avoid trivial entries. Those with no attributes or with
182 * only "type=normal". This retains backward compatibility.
183 */
184 if (user->attr == NULL)
185 return;
186
187 kv_pair = user->attr->data;
188
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))
193 break;
194 if (strlen(val) == 0 ||
195 (strcmp(key, USERATTR_TYPE_KW) == 0 &&
196 strcmp(val, USERATTR_TYPE_NORMAL_KW) == 0))
197 continue;
198 j++;
199 }
200 if (j == 0)
201 return;
202
203 (void) fprintf(f, "%s:%s:%s:%s:", user->name, user->qualifier,
204 user->res1, user->res2);
205
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))
210 break;
211 if (strlen(val) == 0)
212 continue;
213 if (j > 0)
214 (void) fprintf(f, KV_DELIMITER);
215 (void) fprintf(f, "%s=%s", key, val);
216 j++;
217 }
218 (void) fprintf(f, "\n");
219 }
220
221 static void
assign_attr(userattr_t * user,const char * newkey,char * val)222 assign_attr(userattr_t *user, const char *newkey, char *val) {
223
224 int i;
225 char *key;
226 kv_t *kv_pair;
227 int avail = -1;
228
229 if (user->attr != NULL) {
230 kv_pair = user->attr->data;
231 for (i = 0; i < user->attr->length; i++) {
232 key = kv_pair[i].key;
233 if (key == NULL) {
234 avail = i;
235 continue;
236 } else if (strcmp(key, newkey) == 0) {
237 kv_pair[i].value = strdup(val);
238 return;
239 }
240 }
241
242 if (avail == -1)
243 avail = user->attr->length++;
244 kv_pair[avail].key = strdup(newkey);
245 kv_pair[avail].value = strdup(val);
246 }
247 }
248
249 static void
unassign_role(userattr_t * user,char * rolelist,char * role)250 unassign_role(userattr_t *user, char *rolelist, char *role) {
251
252 char *roleptr;
253 char *templist;
254 char *temprole;
255 int length;
256
257 roleptr = rolelist;
258 templist = strdup(roleptr);
259 temprole = strtok(templist, ",");
260 while (temprole) {
261 if (strcmp(temprole, role) == 0) {
262
263 length = strlen(role);
264 roleptr += temprole - templist;
265
266 if (*(roleptr + length) == ',')
267 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);
273 break;
274 } else {
275 temprole = strtok(NULL, ",");
276 }
277 }
278 }
279
280 struct uid_blk *uid_sp;
281 char *prognamp; /* program name */
282 extern int errno;
283 int optn_mask = 0, info_mask = 0;
284 extern int getdate_err;
285
286 int
main(int argc,char ** argv)287 main(int argc, char **argv)
288 {
289 int c, i;
290 char *lognamp, *char_p;
291 int end_of_file = 0;
292 int error;
293 long date = 0;
294 FILE *pwf, *spf, *uaf;
295
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];
300 kva_t ua_kva;
301 struct stat statbuf;
302 struct tm *tm_ptr;
303 int NIS_entry_seen; /* NIS scanning flag */
304 /*
305 * NIS start pos, really pointer to first entry AFTER first
306 * NIS-referant entry
307 */
308 long NIS_pos;
309 long cur_pos; /* Current pos, used with nis-pos above */
310
311 (void) setlocale(LC_ALL, "");
312
313 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
314 #define TEXT_DOMAIN "SYS_TEST"
315 #endif
316 (void) textdomain(TEXT_DOMAIN);
317
318 tzset();
319 /* Get program name */
320 prognamp = argv[0];
321
322 /* Check identity */
323 if (geteuid() != 0)
324 bad_perm();
325
326 /* Lock the password file(s) */
327
328 if (lckpwdf() != 0)
329 no_lock();
330 info_mask |= LOCKED; /* remember we locked */
331
332 /* initialize the two structures */
333
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 */
343
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 */
353
354 userattr_st.name = nullstr;
355 userattr_st.qualifier = nullstr;
356 userattr_st.res1 = nullstr;
357 userattr_st.res2 = nullstr;
358
359 ua_kva.length = 1;
360 ua_kv[0].key = USERATTR_TYPE_KW;
361 ua_kv[0].value = USERATTR_TYPE_NORMAL_KW;
362 ua_kva.data = ua_kv;
363 userattr_st.attr = &ua_kva;
364
365 /* parse the command line */
366
367 while ((c = getopt(argc, argv,
368 "ml:c:h:u:g:s:f:e:k:A:P:R:T:oadK:")) != -1) {
369
370 switch (c) {
371 case 'm':
372 /* Modify */
373
374 if ((A_MASK|D_MASK|M_MASK) & optn_mask)
375 bad_usage("Invalid combination of options");
376
377 optn_mask |= M_MASK;
378 break;
379
380 case 'l' :
381 /* Change logname */
382
383 if ((A_MASK|D_MASK|L_MASK) & optn_mask)
384 bad_usage("Invalid combination of options");
385
386 if (strpbrk(optarg, ":\n") ||
387 strlen(optarg) == 0)
388 bad_arg("Invalid argument to option -l");
389
390 optn_mask |= L_MASK;
391 passwd_st.pw_name = optarg;
392 shadow_st.sp_namp = optarg;
393 userattr_st.name = optarg;
394 break;
395
396 case 'f' :
397 /* set inactive */
398
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) ||
403 (*char_p != '\0') ||
404 strlen(optarg) == 0)
405 bad_arg("Invalid argument to option -f");
406 if (shadow_st.sp_inact == 0)
407 shadow_st.sp_inact = -1;
408 optn_mask |= F_MASK;
409 break;
410
411 case 'e' :
412 /* set expire date */
413
414 if ((D_MASK|E_MASK) & optn_mask)
415 bad_usage("Invalid combination of options");
416
417 if ((strlen(optarg)) < (size_t)2)
418 shadow_st.sp_expire = -1;
419 else {
420 putenv(DATMSK);
421 if ((tm_ptr = getdate(optarg)) == NULL) {
422 msg = "Invalid argument to option -e";
423 bad_arg(msg);
424 }
425 if ((date = mktime(tm_ptr)) < 0) {
426 msg = "Invalid argument to option -e";
427 bad_arg(msg);
428 }
429 shadow_st.sp_expire = (date / DAY);
430 if (shadow_st.sp_expire <= DAY_NOW) {
431 msg = "Invalid argument to option -e";
432 bad_arg(msg);
433 }
434 }
435
436 optn_mask |= E_MASK;
437 break;
438
439 case 'c' :
440 /* The comment */
441
442 if ((D_MASK|C_MASK) & optn_mask)
443 bad_usage("Invalid combination of options");
444
445 if (strlen(optarg) > (size_t)CMT_SIZE ||
446 strpbrk(optarg, ":\n"))
447 bad_arg("Invalid argument to option -c");
448
449 optn_mask |= C_MASK;
450 passwd_st.pw_comment = optarg;
451 passwd_st.pw_gecos = optarg;
452 break;
453
454 case 'h' :
455 /* The home directory */
456
457 if ((D_MASK|H_MASK) & optn_mask)
458 bad_usage("Invalid combination of options");
459
460 if (strlen(optarg) > (size_t)DIR_SIZE ||
461 strpbrk(optarg, ":\n"))
462 bad_arg("Invalid argument to option -h");
463
464 optn_mask |= H_MASK;
465 passwd_st.pw_dir = optarg;
466 break;
467
468 case 'u' :
469 /* The uid */
470
471 if ((D_MASK|U_MASK) & optn_mask)
472 bad_usage("Invalid combination of options");
473
474 optn_mask |= U_MASK;
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");
480
481 break;
482
483 case 'g' :
484 /* The gid */
485
486 if ((D_MASK|G_MASK) & optn_mask)
487 bad_usage("Invalid combination of options");
488
489 optn_mask |= G_MASK;
490 passwd_st.pw_gid = (gid_t)strtol(optarg, &char_p, 10);
491
492 if ((*char_p != '\0') || (passwd_st.pw_gid < 0) ||
493 (strlen(optarg) == 0))
494 bad_arg("Invalid argument to option -g");
495 break;
496
497 case 's' :
498 /* The shell */
499
500 if ((D_MASK|S_MASK) & optn_mask)
501 bad_usage("Invalid combination of options");
502
503 if (strlen(optarg) > (size_t)SHL_SIZE ||
504 strpbrk(optarg, ":\n"))
505 bad_arg("Invalid argument to option -s");
506
507 optn_mask |= S_MASK;
508 passwd_st.pw_shell = optarg;
509 break;
510
511 case 'o' :
512 /* Override unique uid */
513
514 if ((D_MASK|O_MASK) & optn_mask)
515 bad_usage("Invalid combination of options");
516
517 optn_mask |= O_MASK;
518 break;
519
520 case 'a' :
521 /* Add */
522
523 if ((A_MASK|M_MASK|D_MASK|L_MASK) & optn_mask)
524 bad_usage("Invalid combination of options");
525
526 optn_mask |= A_MASK;
527 break;
528
529 case 'd' :
530 /* Delete */
531
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");
536
537 optn_mask |= D_MASK;
538 break;
539
540 case 'K':
541 if (D_MASK & optn_mask)
542 bad_usage("Invalid combination of options");
543
544 char_p = strchr(optarg, '=');
545 if (char_p == NULL)
546 bad_usage("Missing value in -K option");
547
548 *char_p++ = '\0';
549
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,
555 char_p);
556 break;
557 }
558 }
559 if (i == UA_KEYS)
560 bad_usage("bad key");
561 optn_mask |= UATTR_MASK;
562 break;
563
564 case '?' :
565
566 bad_usage("");
567 break;
568
569 default :
570 /* Extended User Attributes */
571 {
572 int j;
573
574 for (j = 0; j < UA_KEYS; j++) {
575 if (ua_opts[j].option == (char)c) {
576 if ((D_MASK) & optn_mask)
577 bad_usage("Invalid "
578 "combination of "
579 " options");
580 optn_mask |= UATTR_MASK;
581 assign_attr(&userattr_st,
582 ua_opts[j].key,
583 _escape(optarg,
584 KV_SPECIAL));
585 ua_opts[j].newvalue =
586 _escape(optarg, KV_SPECIAL);
587 break;
588 }
589 }
590 break;
591 }
592 }
593 }
594
595 /* check command syntax for the following errors */
596 /* too few or too many arguments */
597 /* no -a -m or -d option */
598 /* -o without -u */
599 /* -m with no other option */
600
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) &&
605 !(optn_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");
609
610 /* null string argument or bad characters ? */
611 if ((strlen(argv[optind]) == 0) || strpbrk(argv[optind], ":\n"))
612 bad_arg("Invalid name");
613
614 lognamp = argv [optind];
615
616 /*
617 * if we are adding a new user or modifying an existing user
618 * (not the logname), then copy logname into the two data
619 * structures
620 */
621
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];
627 }
628
629 /* Put in directory if we are adding and we need a default */
630
631 if (!(optn_mask & H_MASK) && (optn_mask & A_MASK)) {
632 if ((passwd_st.pw_dir = malloc((size_t)DIR_SIZE)) == NULL)
633 file_error();
634
635 *passwd_st.pw_dir = '\0';
636 (void) strcat(passwd_st.pw_dir, defdir);
637 (void) strcat(passwd_st.pw_dir, lognamp);
638 }
639
640 /* Check the number of password files we are touching */
641
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;
646
647 if ((D_MASK|L_MASK|UATTR_MASK) & optn_mask)
648 info_mask |= UATTR_FILE;
649
650 /* Open the temporary file(s) with appropriate permission mask */
651 /* and the appropriate owner */
652
653 if (stat(PASSWD, &statbuf) < 0)
654 file_error();
655
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,
662 PASSTEMP);
663 }
664 fd_ptemp = open(PASSTEMP, O_CREAT|O_EXCL|O_WRONLY,
665 statbuf.st_mode);
666 if (fd_ptemp == -1) {
667 file_error();
668 }
669
670 } else
671 file_error();
672 }
673 fp_ptemp = fdopen(fd_ptemp, "w");
674 if (fp_ptemp == NULL)
675 file_error();
676 error = fchown(fd_ptemp, statbuf.st_uid, statbuf.st_gid);
677 if (error == 0)
678 error = fchmod(fd_ptemp, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
679 if (error != 0) {
680 (void) fclose(fp_ptemp);
681 if (unlink(PASSTEMP)) {
682 msg = "%s: warning: cannot unlink %s\n";
683 (void) fprintf(stderr, gettext(msg), prognamp,
684 PASSTEMP);
685 }
686 file_error();
687 }
688
689 if (info_mask & BOTH_FILES) {
690 if (stat(SHADOW, &statbuf) < 0) {
691 rid_tmpf();
692 file_error();
693 }
694 fd_stemp = open(SHADTEMP, O_CREAT|O_EXCL|O_WRONLY,
695 statbuf.st_mode);
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),
701 prognamp, SHADTEMP);
702 }
703 fd_stemp = open(SHADTEMP,
704 O_CREAT|O_EXCL|O_WRONLY, statbuf.st_mode);
705 if (fd_stemp == -1) {
706 rid_tmpf();
707 file_error();
708 }
709
710 } else {
711 rid_tmpf();
712 file_error();
713 }
714 }
715 fp_stemp = fdopen(fd_stemp, "w");
716 if (fp_stemp == NULL) {
717 rid_tmpf();
718 file_error();
719 }
720 error = fchown(fd_stemp, statbuf.st_uid, statbuf.st_gid);
721 if (error == 0)
722 error = fchmod(fd_stemp, S_IRUSR);
723 if (error != 0) {
724 rid_tmpf();
725 file_error();
726 }
727 }
728
729 if (info_mask & UATTR_FILE) {
730 if (stat(USERATTR_FILENAME, &statbuf) < 0) {
731 rid_tmpf();
732 file_error();
733 }
734 fd_uatemp = open(USERATTR_TEMP, O_CREAT|O_EXCL|O_WRONLY,
735 statbuf.st_mode);
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);
742 }
743 fd_uatemp = open(USERATTR_TEMP,
744 O_CREAT|O_EXCL|O_WRONLY, statbuf.st_mode);
745 if (fd_uatemp == -1) {
746 rid_tmpf();
747 file_error();
748 }
749
750 } else {
751 rid_tmpf();
752 file_error();
753 }
754 }
755 fp_uatemp = fdopen(fd_uatemp, "w");
756 if (fp_uatemp == NULL) {
757 rid_tmpf();
758 file_error();
759 }
760 error = fchown(fd_uatemp, statbuf.st_uid, statbuf.st_gid);
761 if (error == 0)
762 error = fchmod(fd_uatemp,
763 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
764 if (error != 0) {
765 rid_tmpf();
766 file_error();
767 }
768 }
769 /* Default uid needed ? */
770
771 if (!(optn_mask & U_MASK) && (optn_mask & A_MASK)) {
772 /* mark it in the information mask */
773 info_mask |= NEED_DEF_UID;
774
775 /* create the head of the uid number list */
776 uid_sp = malloc(sizeof (struct uid_blk));
777 if (uid_sp == NULL) {
778 rid_tmpf();
779 file_error();
780 }
781
782 uid_sp->link = NULL;
783 uid_sp->low = (UID_MIN -1);
784 uid_sp->high = (UID_MIN -1);
785 }
786
787 /*
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.
795 *
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
801 * remember
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.
805 */
806
807
808 error = 0;
809
810 if ((pwf = fopen("/etc/passwd", "r")) == NULL) {
811 rid_tmpf();
812 if (errno == ENOENT)
813 bad_news();
814 else
815 file_error();
816 }
817
818 NIS_entry_seen = 0;
819 cur_pos = 0;
820 /* The while loop for reading PASSWD entries */
821 info_mask |= WRITE_P_ENTRY;
822
823 while (!end_of_file) {
824 pw_ptr1p = fgetpwent(pwf);
825 if (pw_ptr1p == NULL) {
826 if (!feof(pwf)) {
827 /* A real error - report it and exit */
828 rid_tmpf();
829 bad_pasf();
830 }
831 else
832 break;
833 }
834
835 if (!NIS_entry_seen)
836 info_mask |= WRITE_P_ENTRY;
837 else
838 info_mask &= ~WRITE_P_ENTRY;
839
840 /*
841 * Set up the uid usage blocks to find the first
842 * available uid above UID_MIN, if needed
843 */
844
845 if (info_mask & NEED_DEF_UID)
846 add_uid(pw_ptr1p->pw_uid);
847
848 /* Check for unique UID */
849
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 */
854 bad_uid();
855 }
856
857 /* Check for unique new logname */
858
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) {
862 rid_tmpf();
863 #ifdef att
864 if (!getspnam(pw_ptr1p->pw_name))
865 #else
866 if (!local_getspnam(pw_ptr1p->pw_name))
867 #endif
868 bad_pasf();
869 else
870 bad_name("logname already exists");
871 }
872
873 if (strcmp(lognamp, pw_ptr1p->pw_name) == 0) {
874
875 /* no good if we want to add an existing logname */
876 if (optn_mask & A_MASK) {
877 rid_tmpf();
878 #ifdef att
879 if (!getspnam(lognamp))
880 #else
881 if (!local_getspnam(lognamp))
882 #endif
883 bad_pasf();
884 else
885 bad_name("name already exists");
886 }
887
888 /* remember we found it */
889 info_mask |= FOUND;
890
891 /* Do not write it out on the fly */
892 if (optn_mask & D_MASK)
893 info_mask &= ~WRITE_P_ENTRY;
894
895 if (optn_mask & M_MASK) {
896
897 #ifdef att
898 if (!getspnam(lognamp))
899 #else
900 if (!local_getspnam(lognamp))
901 #endif
902 {
903 rid_tmpf();
904 bad_pasf();
905 }
906 if (optn_mask & L_MASK)
907 pw_ptr1p->pw_name = passwd_st.pw_name;
908
909 if (optn_mask & U_MASK)
910 pw_ptr1p->pw_uid = passwd_st.pw_uid;
911
912 if (optn_mask & G_MASK)
913 pw_ptr1p->pw_gid = passwd_st.pw_gid;
914
915 if (optn_mask & C_MASK) {
916 pw_ptr1p->pw_comment =
917 passwd_st.pw_comment;
918
919 pw_ptr1p->pw_gecos =
920 passwd_st.pw_comment;
921 }
922
923 if (optn_mask & H_MASK)
924 pw_ptr1p->pw_dir = passwd_st.pw_dir;
925
926 if (optn_mask & S_MASK)
927 pw_ptr1p->pw_shell = passwd_st.pw_shell;
928 ck_p_sz(pw_ptr1p); /* check entry size */
929 }
930 }
931
932 if (optn_mask & A_MASK) {
933 if (!NIS_entry_seen) {
934 char *p;
935 p = strchr("+-", pw_ptr1p->pw_name[0]);
936 if (p != NULL) {
937 /*
938 * Found first NIS entry.
939 * so remember it.
940 */
941 NIS_pos = cur_pos;
942 NIS_entry_seen = 1;
943 info_mask &= ~WRITE_P_ENTRY;
944 }
945 else
946 cur_pos = ftell(pwf);
947 }
948 }
949
950 if (info_mask & WRITE_P_ENTRY) {
951 if (putpwent(pw_ptr1p, fp_ptemp)) {
952 rid_tmpf();
953 file_error();
954 }
955 }
956 } /* end-of-while-loop */
957
958 if (error >= 1) {
959 msg = "%s: Bad entry found in /etc/passwd. Run pwconv.\n";
960 fprintf(stderr, gettext(msg), prognamp);
961 }
962
963 /* Cannot find the target entry and we are deleting or modifying */
964
965 if (!(info_mask & FOUND) && (optn_mask & (D_MASK|M_MASK))) {
966 rid_tmpf();
967 #ifdef att
968 if (getspnam(lognamp) != NULL)
969 #else
970 if (local_getspnam(lognamp) != NULL)
971 #endif
972 bad_pasf();
973 else
974 bad_name("name does not exist");
975 }
976
977 /* First available uid above UID_MIN is ... */
978
979 if (info_mask & NEED_DEF_UID)
980 passwd_st.pw_uid = uid_sp->high + 1;
981
982 /* Write out the added entry now */
983
984 if (optn_mask & A_MASK) {
985 ck_p_sz(&passwd_st); /* Check entry size */
986 if (putpwent(&passwd_st, fp_ptemp)) {
987 rid_tmpf();
988 file_error();
989 }
990 /*
991 * Now put out the rest of the password file, if needed.
992 */
993 if (NIS_entry_seen) {
994 int n;
995 char buf[1024];
996
997 if (fseek(pwf, NIS_pos, SEEK_SET) < 0) {
998 rid_tmpf();
999 file_error();
1000 }
1001 while ((n = fread(buf, sizeof (char), 1024, pwf)) > 0) {
1002 if (fwrite(buf, sizeof (char), n, fp_ptemp)
1003 != n) {
1004 rid_tmpf();
1005 file_error();
1006 }
1007 }
1008 }
1009 }
1010
1011 (void) fclose(pwf);
1012
1013 /* flush and sync the file before closing it */
1014 if (fflush(fp_ptemp) != 0 || fsync(fd_ptemp) != 0)
1015 file_error();
1016
1017 /* Now we are done with PASSWD */
1018 (void) fclose(fp_ptemp);
1019
1020 /* Do this if we are touching both password files */
1021
1022
1023 if (info_mask & BOTH_FILES) {
1024 info_mask &= ~FOUND; /* Reset FOUND flag */
1025
1026 /* The while loop for reading SHADOW entries */
1027 info_mask |= WRITE_S_ENTRY;
1028
1029 end_of_file = 0;
1030 errno = 0;
1031 error = 0;
1032
1033 NIS_entry_seen = 0;
1034 cur_pos = 0;
1035
1036 if ((spf = fopen("/etc/shadow", "r")) == NULL) {
1037 rid_tmpf();
1038 file_error();
1039 }
1040
1041 while (!end_of_file) {
1042 sp_ptr1p = fgetspent(spf);
1043 if (sp_ptr1p == NULL) {
1044 if (!feof(spf)) {
1045 rid_tmpf();
1046 bad_pasf();
1047 }
1048 else
1049 break;
1050 }
1051
1052 if (!NIS_entry_seen)
1053 info_mask |= WRITE_S_ENTRY;
1054 else
1055 info_mask &= ~WRITE_S_ENTRY;
1056
1057 /*
1058 * See if the new logname already exist in the
1059 * shadow passwd file
1060 */
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) {
1064 rid_tmpf();
1065 bad_pasf();
1066 }
1067
1068 if (strcmp(lognamp, sp_ptr1p->sp_namp) == 0) {
1069 info_mask |= FOUND;
1070 if (optn_mask & A_MASK) {
1071 /* password file inconsistent */
1072 rid_tmpf();
1073 bad_pasf();
1074 }
1075
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 =
1080 shadow_st.sp_inact;
1081 if (E_MASK & optn_mask)
1082 sp_ptr1p->sp_expire =
1083 shadow_st.sp_expire;
1084
1085 ck_s_sz(sp_ptr1p);
1086 }
1087
1088 if (optn_mask & D_MASK)
1089 info_mask &= ~WRITE_S_ENTRY;
1090 }
1091
1092 if (optn_mask & A_MASK) {
1093 if (!NIS_entry_seen) {
1094 char *p;
1095 p = strchr("+-", sp_ptr1p->sp_namp[0]);
1096 if (p != NULL) {
1097 /*
1098 * Found first NIS entry.
1099 * so remember it.
1100 */
1101 NIS_pos = cur_pos;
1102 NIS_entry_seen = 1;
1103 info_mask &= ~WRITE_S_ENTRY;
1104 }
1105 else
1106 cur_pos = ftell(spf);
1107 }
1108 }
1109
1110 if (info_mask & WRITE_S_ENTRY) {
1111 if (putspent(sp_ptr1p, fp_stemp)) {
1112 rid_tmpf();
1113 file_error();
1114 }
1115 }
1116
1117 } /* end-of-while-loop */
1118
1119 if (error >= 1) {
1120
1121 msg = BAD_ENT_MESSAGE;
1122 fprintf(stderr, gettext(msg), prognamp);
1123 }
1124
1125 /*
1126 * If we cannot find the entry and we are deleting or
1127 * modifying
1128 */
1129
1130 if (!(info_mask & FOUND) && (optn_mask & (D_MASK|M_MASK))) {
1131 rid_tmpf();
1132 bad_pasf();
1133 }
1134
1135 if (optn_mask & A_MASK) {
1136 ck_s_sz(&shadow_st);
1137 if (putspent(&shadow_st, fp_stemp)) {
1138 rid_tmpf();
1139 file_error();
1140 }
1141
1142 /*
1143 * Now put out the rest of the shadow file, if needed.
1144 */
1145 if (NIS_entry_seen) {
1146 file_copy(spf, NIS_pos);
1147 }
1148 }
1149
1150 /* flush and sync the file before closing it */
1151 if (fflush(fp_stemp) != 0 || fsync(fd_stemp) != 0)
1152 file_error();
1153 (void) fclose(fp_stemp);
1154
1155 /* Done with SHADOW */
1156 (void) fclose(spf);
1157
1158 } /* End of if info_mask */
1159
1160 if (info_mask & UATTR_FILE) {
1161 info_mask &= ~FOUND; /* Reset FOUND flag */
1162
1163 /* The while loop for reading USER_ATTR entries */
1164 info_mask |= WRITE_S_ENTRY;
1165
1166 end_of_file = 0;
1167 errno = 0;
1168 error = 0;
1169
1170 NIS_entry_seen = 0;
1171 cur_pos = 0;
1172
1173 if ((uaf = fopen(USERATTR_FILENAME, "r")) == NULL) {
1174 rid_tmpf();
1175 file_error();
1176 }
1177
1178 while (!end_of_file) {
1179 ua_ptr1p = fgetuserattr(uaf);
1180 if (ua_ptr1p == NULL) {
1181 if (!feof(uaf)) {
1182 rid_tmpf();
1183 bad_uattr();
1184 }
1185 else
1186 break;
1187 }
1188
1189 if (ua_ptr1p->name[0] == '#') {
1190 /*
1191 * If this is a comment, write it back as it
1192 * is.
1193 */
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",
1200 ua_ptr1p->name);
1201 else
1202 /*
1203 * This is a commented user_attr entry;
1204 * reformat it, and write it back.
1205 */
1206 putuserattrent(ua_ptr1p, fp_uatemp);
1207 free_userattr(ua_ptr1p);
1208 continue;
1209 }
1210
1211 if (!NIS_entry_seen)
1212 info_mask |= WRITE_S_ENTRY;
1213 else
1214 info_mask &= ~WRITE_S_ENTRY;
1215
1216 /*
1217 * See if the new logname already exist in the
1218 * user_attr file
1219 */
1220 if ((optn_mask & M_MASK) &&
1221 strcmp(lognamp, userattr_st.name) != 0 &&
1222 strcmp(ua_ptr1p->name, userattr_st.name) == 0) {
1223 rid_tmpf();
1224 bad_pasf();
1225 }
1226
1227 if (strcmp(lognamp, ua_ptr1p->name) == 0) {
1228 info_mask |= FOUND;
1229 if (optn_mask & A_MASK) {
1230 /* password file inconsistent */
1231 rid_tmpf();
1232 bad_pasf();
1233 }
1234
1235 if (optn_mask & M_MASK) {
1236 int j;
1237 char *value;
1238
1239 for (j = 0; j < UA_KEYS; j++) {
1240 if (ua_opts[j].newvalue != NULL)
1241 continue;
1242 value =
1243 kva_match(ua_ptr1p->attr,
1244 (char *)ua_opts[j].key);
1245 if (value == NULL)
1246 continue;
1247 assign_attr(&userattr_st,
1248 ua_opts[j].key,
1249 value);
1250 }
1251 free_userattr(ua_ptr1p);
1252 ua_ptr1p = &userattr_st;
1253 }
1254
1255 if (optn_mask & D_MASK)
1256 info_mask &= ~WRITE_S_ENTRY;
1257 } else if (optn_mask & D_MASK) {
1258 char *rolelist;
1259
1260 rolelist = kva_match(ua_ptr1p->attr,
1261 USERATTR_ROLES_KW);
1262 if (rolelist) {
1263 unassign_role(ua_ptr1p,
1264 rolelist, lognamp);
1265 }
1266 }
1267
1268 if (info_mask & WRITE_S_ENTRY) {
1269 putuserattrent(ua_ptr1p, fp_uatemp);
1270 }
1271
1272 if (!(optn_mask & M_MASK))
1273 free_userattr(ua_ptr1p);
1274 } /* end-of-while-loop */
1275
1276 if (error >= 1) {
1277
1278 msg = BAD_ENT_MESSAGE;
1279 fprintf(stderr, gettext(msg), prognamp);
1280 }
1281
1282 /*
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.
1286 */
1287 if (!(info_mask & FOUND) && !(L_MASK & optn_mask) &&
1288 !(D_MASK & optn_mask)) {
1289 putuserattrent(&userattr_st, fp_uatemp);
1290 }
1291
1292 /* flush and sync the file before closing it */
1293 if (fflush(fp_uatemp) != 0 || fsync(fd_uatemp) != 0)
1294 file_error();
1295 (void) fclose(fp_uatemp);
1296
1297 /* Done with USERATTR */
1298 (void) fclose(uaf);
1299
1300 } /* End of if info_mask */
1301 /* ignore all signals */
1302
1303 for (i = 1; i < NSIG; i++)
1304 (void) sigset(i, SIG_IGN);
1305
1306 errno = 0; /* For correcting sigset to SIGKILL */
1307
1308 if (unlink(OPASSWD) && access(OPASSWD, 0) == 0)
1309 file_error();
1310
1311 if (link(PASSWD, OPASSWD) == -1)
1312 file_error();
1313
1314
1315 if (rename(PASSTEMP, PASSWD) == -1) {
1316 if (link(OPASSWD, PASSWD))
1317 bad_news();
1318 file_error();
1319 }
1320
1321
1322 if (info_mask & BOTH_FILES) {
1323
1324 if (unlink(OSHADOW) && access(OSHADOW, 0) == 0) {
1325 if (rec_pwd())
1326 bad_news();
1327 else
1328 file_error();
1329 }
1330
1331 if (link(SHADOW, OSHADOW) == -1) {
1332 if (rec_pwd())
1333 bad_news();
1334 else
1335 file_error();
1336 }
1337
1338
1339 if (rename(SHADTEMP, SHADOW) == -1) {
1340 if (rename(OSHADOW, SHADOW) == -1)
1341 bad_news();
1342
1343 if (rec_pwd())
1344 bad_news();
1345 else
1346 file_error();
1347 }
1348
1349 }
1350 if (info_mask & UATTR_FILE) {
1351 if (unlink(OUSERATTR_FILENAME) &&
1352 access(OUSERATTR_FILENAME, 0) == 0) {
1353 if (rec_pwd())
1354 bad_news();
1355 else
1356 file_error();
1357 }
1358
1359 if (link(USERATTR_FILENAME, OUSERATTR_FILENAME) == -1) {
1360 if (rec_pwd())
1361 bad_news();
1362 else
1363 file_error();
1364 }
1365
1366
1367 if (rename(USERATTR_TEMP, USERATTR_FILENAME) == -1) {
1368 if (rename(OUSERATTR_FILENAME, USERATTR_FILENAME) == -1)
1369 bad_news();
1370
1371 if (rec_pwd())
1372 bad_news();
1373 else
1374 file_error();
1375 }
1376
1377 }
1378
1379 ulckpwdf();
1380
1381 /*
1382 * Return 0 status, indicating success
1383 */
1384 return (0);
1385
1386 } /* end of main */
1387
1388 /* Try to recover the old password file */
1389
1390 int
rec_pwd(void)1391 rec_pwd(void)
1392 {
1393 if (unlink(PASSWD) || link(OPASSWD, PASSWD))
1394 return (-1);
1395
1396 return (0);
1397 }
1398
1399 /* combine two uid_blk's */
1400
1401 void
uid_bcom(struct uid_blk * uid_p)1402 uid_bcom(struct uid_blk *uid_p)
1403 {
1404 struct uid_blk *uid_tp;
1405
1406 uid_tp = uid_p->link;
1407 uid_p->high = uid_tp->high;
1408 uid_p->link = uid_tp->link;
1409
1410 free(uid_tp);
1411 }
1412
1413 /* add a new uid_blk */
1414
1415 void
add_ublk(uid_t num,struct uid_blk * uid_p)1416 add_ublk(uid_t num, struct uid_blk *uid_p)
1417 {
1418 struct uid_blk *uid_tp;
1419
1420 uid_tp = malloc(sizeof (struct uid_blk));
1421 if (uid_tp == NULL) {
1422 rid_tmpf();
1423 file_error();
1424 }
1425
1426 uid_tp->high = uid_tp->low = num;
1427 uid_tp->link = uid_p->link;
1428 uid_p->link = uid_tp;
1429 }
1430
1431 /*
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
1437 * done in main().
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
1444 * list + 1.
1445 */
1446 /* add_uid() adds uid to the link list of used uids */
1447 void
add_uid(uid_t uid)1448 add_uid(uid_t uid)
1449 {
1450 struct uid_blk *uid_p;
1451 /* Only keep track of the ones above UID_MIN */
1452
1453 if (uid >= UID_MIN) {
1454 uid_p = uid_sp;
1455
1456 while (uid_p != NULL) {
1457
1458 if (uid_p->link != NULL) {
1459
1460 if (uid >= uid_p->link->low)
1461 uid_p = uid_p->link;
1462
1463 else if (uid >= uid_p->low &&
1464 uid <= uid_p->high) {
1465 uid_p = NULL;
1466 }
1467
1468 else if (uid == (uid_p->high+1)) {
1469
1470 if (++uid_p->high ==
1471 (uid_p->link->low - 1)) {
1472 uid_bcom(uid_p);
1473 }
1474 uid_p = NULL;
1475 }
1476
1477 else if (uid == (uid_p->link->low - 1)) {
1478 uid_p->link->low --;
1479 uid_p = NULL;
1480 }
1481
1482 else if (uid < uid_p->link->low) {
1483 add_ublk(uid, uid_p);
1484 uid_p = NULL;
1485 }
1486 } /* if uid_p->link */
1487
1488 else {
1489
1490 if (uid == (uid_p->high + 1)) {
1491 uid_p->high++;
1492 uid_p = NULL;
1493 } else if (uid >= uid_p->low &&
1494 uid <= uid_p->high) {
1495 uid_p = NULL;
1496 } else {
1497 add_ublk(uid, uid_p);
1498 uid_p = NULL;
1499 }
1500 } /* else */
1501 } /* while uid_p */
1502
1503 } /* if uid */
1504 }
1505
1506 void
bad_perm(void)1507 bad_perm(void)
1508 {
1509 (void) fprintf(stderr, gettext("%s: Permission denied\n"), prognamp);
1510 exit(1);
1511 }
1512
1513 void
bad_usage(char * sp)1514 bad_usage(char *sp)
1515 {
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)
1525 ulckpwdf();
1526 exit(2);
1527 }
1528
1529 void
bad_arg(char * s)1530 bad_arg(char *s)
1531 {
1532 (void) fprintf(stderr, "%s: %s\n", prognamp, gettext(s));
1533
1534 if (info_mask & LOCKED)
1535 ulckpwdf();
1536 exit(3);
1537 }
1538
1539 void
bad_name(char * s)1540 bad_name(char *s)
1541 {
1542 (void) fprintf(stderr, "%s: %s\n", prognamp, gettext(s));
1543 ulckpwdf();
1544 exit(9);
1545 }
1546
1547 void
bad_uid(void)1548 bad_uid(void)
1549 {
1550 (void) fprintf(stderr, gettext("%s: UID in use\n"), prognamp);
1551
1552 ulckpwdf();
1553 exit(4);
1554 }
1555
1556 void
bad_pasf(void)1557 bad_pasf(void)
1558 {
1559 msg = "%s: Inconsistent password files\n";
1560 (void) fprintf(stderr, gettext(msg), prognamp);
1561
1562 ulckpwdf();
1563 exit(5);
1564 }
1565
1566 void
bad_uattr(void)1567 bad_uattr(void)
1568 {
1569 msg = "%s: Bad user_attr database\n";
1570 (void) fprintf(stderr, gettext(msg), prognamp);
1571
1572 ulckpwdf();
1573 exit(5);
1574 }
1575
1576 void
file_error(void)1577 file_error(void)
1578 {
1579 msg = "%s: Unexpected failure. Password files unchanged\n";
1580 (void) fprintf(stderr, gettext(msg), prognamp);
1581
1582 ulckpwdf();
1583 exit(6);
1584 }
1585
1586 void
bad_news(void)1587 bad_news(void)
1588 {
1589 msg = "%s: Unexpected failure. Password file(s) missing\n";
1590 (void) fprintf(stderr, gettext(msg), prognamp);
1591
1592 ulckpwdf();
1593 exit(7);
1594 }
1595
1596 void
no_lock(void)1597 no_lock(void)
1598 {
1599 msg = "%s: Password file(s) busy. Try again later\n";
1600 (void) fprintf(stderr, gettext(msg), prognamp);
1601
1602 exit(8);
1603 }
1604
1605 /* Check for the size of the whole passwd entry */
1606 void
ck_p_sz(struct passwd * pwp)1607 ck_p_sz(struct passwd *pwp)
1608 {
1609 char ctp[128];
1610
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)) {
1620 rid_tmpf();
1621 bad_arg("New password entry too long");
1622 }
1623 }
1624
1625 /* Check for the size of the whole passwd entry */
1626 void
ck_s_sz(struct spwd * ssp)1627 ck_s_sz(struct spwd *ssp)
1628 {
1629 char ctp[128];
1630
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)) {
1642 rid_tmpf();
1643 bad_arg("New password entry too long");
1644 }
1645 }
1646
1647 /* Get rid of the temp files */
1648 void
rid_tmpf(void)1649 rid_tmpf(void)
1650 {
1651 (void) fclose(fp_ptemp);
1652
1653 if (unlink(PASSTEMP)) {
1654 msg = "%s: warning: cannot unlink %s\n";
1655 (void) fprintf(stderr, gettext(msg), prognamp, PASSTEMP);
1656 }
1657
1658 if (info_mask & BOTH_FILES) {
1659 (void) fclose(fp_stemp);
1660
1661 if (unlink(SHADTEMP)) {
1662 msg = "%s: warning: cannot unlink %s\n";
1663 (void) fprintf(stderr, gettext(msg), prognamp,
1664 SHADTEMP);
1665 }
1666 }
1667
1668 if (info_mask & UATTR_FILE) {
1669 (void) fclose(fp_uatemp);
1670
1671 if (unlink(USERATTR_TEMP)) {
1672 msg = "%s: warning: cannot unlink %s\n";
1673 (void) fprintf(stderr, gettext(msg), prognamp,
1674 USERATTR_TEMP);
1675 }
1676 }
1677 }
1678
1679 void
file_copy(FILE * spf,long NIS_pos)1680 file_copy(FILE *spf, long NIS_pos)
1681 {
1682 int n;
1683 char buf[1024];
1684
1685 if (fseek(spf, NIS_pos, SEEK_SET) < 0) {
1686 rid_tmpf();
1687 file_error();
1688 }
1689 while ((n = fread(buf, sizeof (char), 1024, spf)) > 0) {
1690 if (fwrite(buf, sizeof (char), n, fp_stemp) != n) {
1691 rid_tmpf();
1692 file_error();
1693 }
1694 }
1695 }
1696