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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2023 OmniOS Community Edition (OmniOSce) Association.
26 */
27
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
30
31 /* Copyright (c) 1987, 1988 Microsoft Corporation */
32 /* All Rights Reserved */
33
34 /*
35 * passwd is a program whose sole purpose is to manage
36 * the password file, map, or table. It allows system administrator
37 * to add, change and display password attributes.
38 * Non privileged user can change password or display
39 * password attributes which corresponds to their login name.
40 */
41
42 #include <stdio.h>
43 #include <pwd.h>
44 #include <sys/types.h>
45 #include <errno.h>
46 #include <unistd.h>
47 #include <stdlib.h>
48 #include <locale.h>
49 #include <stdarg.h>
50 #include <errno.h>
51 #include <string.h>
52 #include <security/pam_appl.h>
53 #include <security/pam_modules.h>
54 #include <security/pam_impl.h>
55 #include <rpcsvc/nis.h>
56 #undef GROUP
57 #include <syslog.h>
58 #include <userdefs.h>
59 #include <passwdutil.h>
60
61 #include <nss_dbdefs.h>
62
63 #include <deflt.h>
64
65 #undef GROUP
66 #include <bsm/adt.h>
67 #include <bsm/adt_event.h>
68
69 /*
70 * flags indicate password attributes to be modified
71 */
72
73 #define LFLAG 0x001 /* lock user's password */
74 #define DFLAG 0x002 /* delete user's password */
75 #define MFLAG 0x004 /* set max field -- # of days passwd is valid */
76 #define NFLAG 0x008 /* set min field -- # of days between */
77 /* password changes */
78 #define SFLAG 0x010 /* display password attributes */
79 #define FFLAG 0x020 /* expire user's password */
80 #define AFLAG 0x040 /* display password attributes for all users */
81 #define SAFLAG (SFLAG|AFLAG) /* display password attributes for all users */
82 #define WFLAG 0x100 /* warn user to change passwd */
83 #define OFLAG 0x200 /* domain name */
84 #define EFLAG 0x400 /* change shell */
85 #define GFLAG 0x800 /* change gecos information */
86 #define HFLAG 0x1000 /* change home directory */
87 #define XFLAG 0x2000 /* no login */
88 #define UFLAG 0x4000 /* unlock user's password */
89
90 #define NONAGEFLAG (EFLAG | GFLAG | HFLAG)
91 #define AGEFLAG (LFLAG | FFLAG | MFLAG | NFLAG | WFLAG | XFLAG | UFLAG)
92 #define MUTEXFLAG (DFLAG | LFLAG | XFLAG | UFLAG | SAFLAG)
93
94
95 /*
96 * exit code
97 */
98
99 #define SUCCESS 0 /* succeeded */
100 #define NOPERM 1 /* No permission */
101 #define BADOPT 2 /* Invalid combination of option */
102 #define FMERR 3 /* File/table manipulation error */
103 #define FATAL 4 /* Old file/table can not be recovered */
104 #define FBUSY 5 /* Lock file/table busy */
105 #define BADSYN 6 /* Incorrect syntax */
106 #define BADAGE 7 /* Aging is disabled */
107 #define NOMEM 8 /* No memory */
108 #define SYSERR 9 /* System error */
109 #define EXPIRED 10 /* Account expired */
110
111 /*
112 * define error messages
113 */
114 #define MSG_NP "Permission denied"
115 #define MSG_BS "Invalid combination of options"
116 #define MSG_FE "Unexpected failure. Password file/table unchanged."
117 #define MSG_FF "Unexpected failure. Password file/table missing."
118 #define MSG_FB "Password file/table busy. Try again later."
119 #define MSG_NV "Invalid argument to option"
120 #define MSG_AD "Password aging is disabled"
121 #define MSG_RS "Cannot change from restricted shell %s\n"
122 #define MSG_NM "Out of memory."
123 #define MSG_UNACCEPT "%s is unacceptable as a new shell\n"
124 #define MSG_UNAVAIL "warning: %s is unavailable on this machine\n"
125 #define MSG_COLON "':' is not allowed.\n"
126 #define MSG_MAXLEN "Maximum number of characters allowed is %d."
127 #define MSG_CONTROL "Control characters are not allowed.\n"
128 #define MSG_SHELL_UNCHANGED "Login shell unchanged.\n"
129 #define MSG_GECOS_UNCHANGED "Finger information unchanged.\n"
130 #define MSG_DIR_UNCHANGED "Homedir information unchanged.\n"
131 #define MSG_NAME "\nName [%s]: "
132 #define MSG_HOMEDIR "\nHome Directory [%s]: "
133 #define MSG_OLDSHELL "Old shell: %s\n"
134 #define MSG_NEWSHELL "New shell: "
135 #define MSG_AGAIN "\nPlease try again\n"
136 #define MSG_INPUTHDR "Default values are printed inside of '[]'.\n" \
137 "To accept the default, type <return>.\n" \
138 "To have a blank entry, type the word 'none'.\n"
139 #define MSG_UNKNOWN "%s: User unknown: %s\n"
140 #define MSG_ACCOUNT_EXP "User account has expired: %s\n"
141 #define MSG_AUTHTOK_EXP "Your password has been expired for too long.\n" \
142 "Please contact the system administrator.\n"
143 #define MSG_NIS_HOMEDIR "-h does not apply to NIS"
144 #define MSG_CUR_PASS "Enter existing login password: "
145 #define MSG_CUR_PASS_UNAME "Enter %s's existing login password: "
146 #define MSG_SUCCESS "%s: password information changed for %s\n"
147 #define MSG_SORRY "%s: Sorry, wrong passwd\n"
148 #define MSG_INFO "%s: Changing password for %s\n"
149
150
151 /*
152 * return code from ckarg() routine
153 */
154 #define FAIL -1
155
156 /*
157 * defind password file name
158 */
159 #define PASSWD "/etc/passwd"
160
161 #define MAX_INPUT_LEN 512
162
163 #define DEF_ATTEMPTS 3
164
165 /* Number of characters in that make up an encrypted password (for now) */
166 #define NUMCP 13
167
168 #ifdef DEBUG
169 #define dprintf1 printf
170 #else
171 #define dprintf1(w, x)
172 #endif
173
174 extern int optind;
175
176 static int retval = SUCCESS;
177 static int pam_retval = PAM_SUCCESS;
178 static uid_t uid;
179 static char *prognamep;
180 static long maxdate; /* password aging information */
181 static int passwd_conv(int, const struct pam_message **,
182 struct pam_response **, void *);
183 static struct pam_conv pam_conv = {passwd_conv, NULL};
184 static pam_handle_t *pamh; /* Authentication handle */
185 static char *usrname; /* user whose attribute we update */
186 static adt_session_data_t *ah; /* audit session handle */
187 static adt_event_data_t *event = NULL; /* event to be generated */
188
189 static pam_repository_t auth_rep;
190 static pwu_repository_t repository;
191 static pwu_repository_t __REPFILES = { "files", NULL, 0 };
192
193 /*
194 * Function Declarations
195 */
196
197 extern void setusershell(void);
198 extern char *getusershell(void);
199 extern void endusershell(void);
200
201 static void passwd_exit(int retcode) __NORETURN;
202 static void rusage(void);
203 static int ckuid(void);
204 static int ckarg(int argc, char **argv, attrlist **attributes);
205
206 static int get_namelist(pwu_repository_t, char ***, int *);
207 static int get_namelist_files(char ***, int *);
208 static int get_namelist_local(char ***, int *);
209 static int get_attr(char *, pwu_repository_t *, attrlist **);
210 static void display_attr(char *, attrlist *);
211 static void free_attr(attrlist *);
212 static void attrlist_add(attrlist **, attrtype, char *);
213 static void attrlist_reorder(attrlist **);
214 static char *userinput(char *, pwu_repository_t *, attrtype);
215 static char *getresponse(char *);
216
217 /*
218 * main():
219 * The main routine will call ckarg() to parse the command line
220 * arguments and call the appropriate functions to perform the
221 * tasks specified by the arguments. It allows system
222 * administrator to add, change and display password attributes.
223 * Non privileged user can change password or display
224 * password attributes which corresponds to their login name.
225 */
226
227 int
main(int argc,char * argv[])228 main(int argc, char *argv[])
229 {
230
231 int flag;
232 char **namelist;
233 int num_user;
234 int i;
235 attrlist *attributes = NULL;
236 char *input;
237 int tries = 1;
238 int updated_reps;
239
240
241 if ((prognamep = strrchr(argv[0], '/')) != NULL)
242 ++prognamep;
243 else
244 prognamep = argv[0];
245
246 auth_rep.type = NULL;
247 auth_rep.scope = NULL;
248 repository.type = NULL;
249 repository.scope = NULL;
250 repository.scope_len = 0;
251
252
253 /* initialization for variables, set locale and textdomain */
254 i = 0;
255 flag = 0;
256
257 uid = getuid(); /* get the user id */
258 (void) setlocale(LC_ALL, "");
259
260 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
261 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
262 #endif
263 (void) textdomain(TEXT_DOMAIN);
264
265 /*
266 * ckarg() parses the arguments. In case of an error,
267 * it sets the retval and returns FAIL (-1).
268 */
269
270 flag = ckarg(argc, argv, &attributes);
271 dprintf1("flag is %0x\n", flag);
272 if (flag == FAIL)
273 passwd_exit(retval);
274
275 argc -= optind;
276
277 if (argc < 1) {
278 if ((usrname = getlogin()) == NULL) {
279 struct passwd *pass = getpwuid(uid);
280 if (pass != NULL)
281 usrname = pass->pw_name;
282 else {
283 rusage();
284 exit(NOPERM);
285 }
286 } else if (flag == 0) {
287 /*
288 * If flag is zero, change passwd.
289 * Otherwise, it will display or
290 * modify password aging attributes
291 */
292 (void) fprintf(stderr, gettext(MSG_INFO), prognamep,
293 usrname);
294 }
295 } else {
296 usrname = argv[optind];
297 }
298
299 if (pam_start("passwd", usrname, &pam_conv, &pamh) != PAM_SUCCESS) {
300 passwd_exit(NOPERM);
301 }
302
303 auth_rep.type = repository.type;
304 auth_rep.scope = repository.scope;
305 auth_rep.scope_len = repository.scope_len;
306
307 if (auth_rep.type != NULL) {
308 if (pam_set_item(pamh, PAM_REPOSITORY, (void *)&auth_rep)
309 != PAM_SUCCESS) {
310 passwd_exit(NOPERM);
311 }
312 }
313
314 if (flag == SAFLAG) { /* display password attributes for all users */
315 retval = get_namelist(repository, &namelist, &num_user);
316 if (retval != SUCCESS)
317 (void) passwd_exit(retval);
318
319 if (num_user == 0) {
320 (void) fprintf(stderr, "%s: %s\n", prognamep,
321 gettext(MSG_FF));
322 passwd_exit(FATAL);
323 }
324 i = 0;
325 while (namelist[i] != NULL) {
326 (void) get_attr(namelist[i], &repository,
327 &attributes);
328 (void) display_attr(namelist[i], attributes);
329 (void) free(namelist[i]);
330 (void) free_attr(attributes);
331 i++;
332 }
333 (void) free(namelist);
334 passwd_exit(SUCCESS);
335 } else if (flag == SFLAG) { /* display password attributes by user */
336 if (get_attr(usrname, &repository, &attributes) ==
337 PWU_SUCCESS) {
338 (void) display_attr(usrname, attributes);
339 (void) free_attr(attributes);
340 }
341 passwd_exit(SUCCESS);
342 /* NOT REACHED */
343 }
344
345
346 switch (pam_authenticate(pamh, 0)) {
347 case PAM_SUCCESS:
348 break;
349 case PAM_USER_UNKNOWN:
350 (void) fprintf(stderr, gettext(MSG_UNKNOWN), prognamep,
351 usrname);
352 passwd_exit(NOPERM);
353 break;
354 case PAM_PERM_DENIED:
355 passwd_exit(NOPERM);
356 break;
357 case PAM_AUTH_ERR:
358 (void) fprintf(stderr, gettext(MSG_SORRY), prognamep);
359 passwd_exit(NOPERM);
360 break;
361 default:
362 /* system error */
363 passwd_exit(FMERR);
364 break;
365 }
366
367 if (flag == 0) { /* changing user password */
368 int chk_authtok = 0; /* check password strength */
369
370 dprintf1("call pam_chauthtok() repository name =%s\n",
371 repository.type);
372
373 /* Set up for Audit */
374 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
375 perror("adt_start_session");
376 passwd_exit(SYSERR);
377 }
378 if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) {
379 perror("adt_alloc_event");
380 passwd_exit(NOMEM);
381 }
382
383 /* Don't check account expiration when invoked by root */
384 if (ckuid() != SUCCESS) {
385 pam_retval = pam_acct_mgmt(pamh, PAM_SILENT);
386 switch (pam_retval) {
387 case PAM_ACCT_EXPIRED:
388 (void) fprintf(stderr,
389 gettext(MSG_ACCOUNT_EXP), usrname);
390 passwd_exit(EXPIRED);
391 break;
392 case PAM_AUTHTOK_EXPIRED:
393 (void) fprintf(stderr,
394 gettext(MSG_AUTHTOK_EXP));
395 passwd_exit(NOPERM);
396 break;
397 case PAM_NEW_AUTHTOK_REQD:
398 /* valid error when changing passwords */
399 break;
400 case PAM_SUCCESS:
401 /* Ok to change password */
402 break;
403 default:
404 passwd_exit(NOPERM);
405 }
406 }
407
408
409 pam_retval = PAM_AUTHTOK_ERR;
410 tries = 1;
411 if (ckuid() == SUCCESS) {
412 /* bypass password strength checks */
413 chk_authtok = PAM_NO_AUTHTOK_CHECK;
414 }
415
416 while (pam_retval == PAM_AUTHTOK_ERR && tries <= DEF_ATTEMPTS) {
417 if (tries > 1)
418 (void) printf(gettext(MSG_AGAIN));
419 pam_retval = pam_chauthtok(pamh, chk_authtok);
420 if (pam_retval == PAM_TRY_AGAIN) {
421 (void) sleep(1);
422 pam_retval = pam_chauthtok(pamh, chk_authtok);
423 }
424 tries++;
425 }
426
427 switch (pam_retval) {
428 case PAM_SUCCESS:
429 retval = SUCCESS;
430 break;
431 case PAM_AUTHTOK_DISABLE_AGING:
432 retval = BADAGE;
433 break;
434 case PAM_AUTHTOK_LOCK_BUSY:
435 retval = FBUSY;
436 break;
437 case PAM_TRY_AGAIN:
438 retval = FBUSY;
439 break;
440 case PAM_AUTHTOK_ERR:
441 case PAM_AUTHTOK_RECOVERY_ERR:
442 default:
443 retval = NOPERM;
444 break;
445 }
446
447 (void) passwd_exit(retval);
448 /* NOT REACHED */
449 } else { /* changing attributes */
450 switch (flag) {
451 case EFLAG: /* changing user password attributes */
452 input = userinput(usrname, &repository, ATTR_SHELL);
453 if (input)
454 attrlist_add(&attributes, ATTR_SHELL, input);
455 else
456 (void) printf(gettext(MSG_SHELL_UNCHANGED));
457 break;
458 case GFLAG:
459 input = userinput(usrname, &repository, ATTR_GECOS);
460 if (input)
461 attrlist_add(&attributes, ATTR_GECOS, input);
462 else
463 (void) printf(gettext(MSG_GECOS_UNCHANGED));
464 break;
465 case HFLAG:
466 input = userinput(usrname, &repository, ATTR_HOMEDIR);
467 if (input)
468 attrlist_add(&attributes, ATTR_HOMEDIR, input);
469 else
470 (void) printf(gettext(MSG_DIR_UNCHANGED));
471 break;
472 }
473
474 if (attributes != NULL) {
475 retval = __set_authtoken_attr(usrname,
476 pamh->ps_item[PAM_AUTHTOK].pi_addr,
477 &repository, attributes, &updated_reps);
478 switch (retval) {
479 case PWU_SUCCESS:
480 for (i = 1; i <= REP_LAST; i <<= 1) {
481 if ((updated_reps & i) == 0)
482 continue;
483 (void) printf(gettext(MSG_SUCCESS),
484 prognamep, usrname);
485 }
486 retval = SUCCESS;
487 break;
488 case PWU_AGING_DISABLED:
489 retval = BADAGE;
490 break;
491 default:
492 retval = NOPERM;
493 break;
494 }
495 } else {
496 retval = SUCCESS; /* nothing to change won't fail */
497 }
498 (void) passwd_exit(retval);
499 }
500 /* NOTREACHED */
501 return (0);
502 }
503
504 /*
505 * Get a line of input from the user.
506 *
507 * If the line is empty, or the input equals 'oldval', NULL is returned.
508 * therwise, a malloced string containing the input (minus the trailing
509 * newline) is returned.
510 */
511 char *
getresponse(char * oldval)512 getresponse(char *oldval)
513 {
514 char resp[MAX_INPUT_LEN];
515 char *retval = NULL;
516 int resplen;
517
518 (void) fgets(resp, sizeof (resp) - 1, stdin);
519 resplen = strlen(resp) - 1;
520 if (resp[resplen] == '\n')
521 resp[resplen] = '\0';
522 if (*resp != '\0' && strcmp(resp, oldval) != 0)
523 retval = strdup(resp);
524 return (retval);
525 }
526
527 /*
528 * char *userinput(item)
529 *
530 * user conversation function. The old value of attribute "item" is
531 * displayed while the user is asked to provide a new value.
532 *
533 * returns a malloc()-ed string if the user actualy provided input
534 * or NULL if the user simply hit return or the input equals the old
535 * value (not changed).
536 */
537 char *
userinput(char * name,pwu_repository_t * rep,attrtype type)538 userinput(char *name, pwu_repository_t *rep, attrtype type)
539 {
540 attrlist oldattr;
541 char *oldval; /* shorthand for oldattr.data.val_s */
542 char *valid; /* points to valid shells */
543 char *response;
544 char *cp;
545
546 oldattr.type = type;
547 oldattr.next = NULL;
548
549 if (__get_authtoken_attr(name, rep, &oldattr) != PWU_SUCCESS)
550 passwd_exit(FMERR);
551
552 oldval = oldattr.data.val_s;
553
554 if (type == ATTR_SHELL) {
555 /* No current shell: set DEFSHL as default choice */
556 if (*oldval == '\0') {
557 free(oldval);
558 oldval = strdup(DEFSHL);
559 }
560
561 if (ckuid() != SUCCESS) {
562 /* User must currently have a valid shell */
563 setusershell();
564 valid = getusershell();
565 while (valid && strcmp(valid, oldval) != 0)
566 valid = getusershell();
567 endusershell();
568
569 if (valid == NULL) {
570 (void) fprintf(stderr, gettext(MSG_RS), oldval);
571 free(oldval);
572 return (NULL);
573 }
574 }
575 (void) printf(gettext(MSG_OLDSHELL), oldval);
576 (void) printf(gettext(MSG_NEWSHELL));
577 (void) fflush(stdout);
578
579 response = getresponse(oldval);
580 free(oldval); /* We don't need the old value anymore */
581
582 if (response == NULL || *response == '\0')
583 return (NULL);
584
585 /* Make sure new shell is listed */
586 setusershell();
587 valid = getusershell();
588 while (valid) {
589 char *cp;
590
591 /* Allow user to give shell without path */
592 if (*response == '/') {
593 cp = valid;
594 } else {
595 if ((cp = strrchr(valid, '/')) == NULL)
596 cp = valid;
597 else
598 cp++;
599 }
600 if (strcmp(cp, response) == 0) {
601 if (*response != '/') {
602 /* take shell name including path */
603 free(response);
604 response = strdup(valid);
605 }
606 break;
607 }
608 valid = getusershell();
609 }
610 endusershell();
611
612 if (valid == NULL) { /* No valid shell matches */
613 (void) fprintf(stderr, gettext(MSG_UNACCEPT), response);
614 return (NULL);
615 }
616
617 if (access(response, X_OK) < 0)
618 (void) fprintf(stderr, gettext(MSG_UNAVAIL), response);
619 return (response);
620 /* NOT REACHED */
621 }
622 /*
623 * if type == SHELL, we have returned by now. Only GECOS and
624 * HOMEDIR get to this point.
625 */
626 (void) printf(gettext(MSG_INPUTHDR));
627
628 /*
629 * PRE: oldval points to malloced string with Old Value
630 * INV: oldval remains unchanged
631 * POST:response points to valid string or NULL.
632 */
633 for (;;) {
634 if (type == ATTR_GECOS)
635 (void) printf(gettext(MSG_NAME), oldval);
636 else if (type == ATTR_HOMEDIR)
637 (void) printf(gettext(MSG_HOMEDIR), oldval);
638
639 response = getresponse(oldval);
640
641 if (response && strcmp(response, "none") == 0)
642 *response = '\0';
643
644 /* No-change or empty string are OK */
645 if (response == NULL || *response == '\0')
646 break;
647
648 /* Check for illegal characters */
649 if (strchr(response, ':')) {
650 (void) fprintf(stderr, "%s", gettext(MSG_COLON));
651 free(response);
652 } else if (strlen(response) > MAX_INPUT_LEN - 1) {
653 (void) fprintf(stderr, gettext(MSG_MAXLEN),
654 MAX_INPUT_LEN);
655 free(response);
656 } else {
657 /* don't allow control characters */
658 for (cp = response; *cp >= 040; cp++)
659 ;
660 if (*cp != '\0') {
661 (void) fprintf(stderr, gettext(MSG_CONTROL));
662 free(response);
663 } else
664 break; /* response is a valid string */
665 }
666 /*
667 * We only get here if the input was invalid.
668 * In that case, we again ask the user for input.
669 */
670 }
671 free(oldval);
672 return (response);
673 }
674 /*
675 * ckarg():
676 * This function parses and verifies the
677 * arguments. It takes three parameters:
678 * argc => # of arguments
679 * argv => pointer to an argument
680 * attrlist => pointer to list of password attributes
681 */
682
683 static int
ckarg(int argc,char ** argv,attrlist ** attributes)684 ckarg(int argc, char **argv, attrlist **attributes)
685 {
686 extern char *optarg;
687 char *char_p;
688 int opt;
689 int flag;
690
691 flag = 0;
692
693 while ((opt = getopt(argc, argv, "r:aldefghsux:n:w:N")) != EOF) {
694 switch (opt) {
695
696 case 'r': /* Repository Specified */
697 /* repository: this option should be specified first */
698
699 if (repository.type != NULL) {
700 (void) fprintf(stderr, gettext(
701 "Repository is already defined or specified.\n"));
702 rusage();
703 retval = BADSYN;
704 return (FAIL);
705 }
706 if (strcmp(optarg, "nis") == 0) {
707 repository.type = optarg;
708 } else if (strcmp(optarg, "ldap") == 0) {
709 repository.type = optarg;
710 } else if (strcmp(optarg, "files") == 0) {
711 repository.type = optarg;
712 } else {
713 (void) fprintf(stderr,
714 gettext("invalid repository: %s\n"),
715 optarg);
716 rusage();
717 retval = BADSYN;
718 return (FAIL);
719 }
720 break;
721
722 case 'd': /* Delete Auth Token */
723 /* if no repository the default for -d is files */
724 if (repository.type == NULL)
725 repository = __REPFILES;
726
727 /*
728 * Delete the password - only privileged processes
729 * can execute this for FILES or LDAP
730 */
731 if (IS_FILES(repository) == FALSE &&
732 IS_LDAP(repository) == FALSE) {
733 (void) fprintf(stderr, gettext(
734 "-d only applies to files "
735 "or ldap repository\n"));
736 rusage(); /* exit */
737 retval = BADSYN;
738 return (FAIL);
739 }
740
741 if (ckuid() != SUCCESS) {
742 retval = NOPERM;
743 return (FAIL);
744 }
745 if (flag & (LFLAG|SAFLAG|DFLAG|XFLAG|UFLAG)) {
746 rusage();
747 retval = BADOPT;
748 return (FAIL);
749 }
750 flag |= DFLAG;
751 attrlist_add(attributes, ATTR_PASSWD, NULL);
752 break;
753
754 case 'N': /* set account to be "no login" */
755
756 /* if no repository the default for -N is files */
757 if (repository.type == NULL)
758 repository = __REPFILES;
759
760 if (IS_FILES(repository) == FALSE &&
761 IS_LDAP(repository) == FALSE) {
762 (void) fprintf(stderr, gettext(
763 "-N only applies to files or ldap "
764 "repository\n"));
765 rusage(); /* exit */
766 retval = BADOPT;
767 return (FAIL);
768 }
769
770 /*
771 * Only privileged processes can execute this
772 * for FILES or LDAP
773 */
774 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
775 ((retval = ckuid()) != SUCCESS))
776 return (FAIL);
777 if (flag & (MUTEXFLAG|NONAGEFLAG)) {
778 rusage(); /* exit */
779 retval = BADOPT;
780 return (FAIL);
781 }
782 flag |= XFLAG;
783 attrlist_add(attributes, ATTR_NOLOGIN_ACCOUNT, NULL);
784 break;
785
786 case 'l': /* lock the password */
787
788 /* if no repository the default for -l is files */
789 if (repository.type == NULL)
790 repository = __REPFILES;
791
792 if (IS_FILES(repository) == FALSE &&
793 IS_LDAP(repository) == FALSE) {
794 (void) fprintf(stderr, gettext(
795 "-l only applies to files or ldap "
796 "repository\n"));
797 rusage(); /* exit */
798 retval = BADOPT;
799 return (FAIL);
800 }
801
802 /*
803 * Only privileged processes can execute this
804 * for FILES or LDAP
805 */
806 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
807 ((retval = ckuid()) != SUCCESS))
808 return (FAIL);
809 if (flag & (MUTEXFLAG|NONAGEFLAG)) {
810 rusage(); /* exit */
811 retval = BADOPT;
812 return (FAIL);
813 }
814 flag |= LFLAG;
815 attrlist_add(attributes, ATTR_LOCK_ACCOUNT, NULL);
816 break;
817
818 case 'u': /* unlock the password */
819
820 /* if no repository the default for -u is files */
821 if (repository.type == NULL)
822 repository = __REPFILES;
823
824 if (IS_FILES(repository) == FALSE &&
825 IS_LDAP(repository) == FALSE) {
826 (void) fprintf(stderr, gettext(
827 "-u only applies to files or ldap "
828 "repository\n"));
829 rusage(); /* exit */
830 retval = BADOPT;
831 return (FAIL);
832 }
833
834 /*
835 * Only privileged processes can execute this
836 * for FILES or LDAP
837 */
838 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
839 ((retval = ckuid()) != SUCCESS))
840 return (FAIL);
841 if (flag & (MUTEXFLAG|NONAGEFLAG)) {
842 rusage(); /* exit */
843 retval = BADOPT;
844 return (FAIL);
845 }
846 flag |= UFLAG;
847 attrlist_add(attributes, ATTR_UNLOCK_ACCOUNT, NULL);
848 attrlist_add(attributes, ATTR_RST_FAILED_LOGINS, NULL);
849 break;
850
851 case 'x': /* set the max date */
852
853 /* if no repository the default for -x is files */
854 if (repository.type == NULL)
855 repository = __REPFILES;
856
857 if (IS_FILES(repository) == FALSE &&
858 IS_LDAP(repository) == FALSE) {
859 (void) fprintf(stderr, gettext(
860 "-x only applies to files or ldap "
861 "repository\n"));
862 rusage(); /* exit */
863 retval = BADSYN;
864 return (FAIL);
865 }
866
867 /*
868 * Only privileged process can execute this
869 * for FILES or LDAP
870 */
871 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
872 (ckuid() != SUCCESS)) {
873 retval = NOPERM;
874 return (FAIL);
875 }
876 if (flag & (SAFLAG|MFLAG|NONAGEFLAG)) {
877 retval = BADOPT;
878 return (FAIL);
879 }
880 flag |= MFLAG;
881 if ((int)strlen(optarg) <= 0 ||
882 (maxdate = strtol(optarg, &char_p, 10)) < -1 ||
883 *char_p != '\0') {
884 (void) fprintf(stderr, "%s: %s -x\n",
885 prognamep, gettext(MSG_NV));
886 retval = BADSYN;
887 return (FAIL);
888 }
889 attrlist_add(attributes, ATTR_MAX, optarg);
890 break;
891
892 case 'n': /* set the min date */
893
894 /* if no repository the default for -n is files */
895 if (repository.type == NULL)
896 repository = __REPFILES;
897
898 if (IS_FILES(repository) == FALSE &&
899 IS_LDAP(repository) == FALSE) {
900 (void) fprintf(stderr, gettext(
901 "-n only applies to files or ldap "
902 "repository\n"));
903 rusage(); /* exit */
904 retval = BADSYN;
905 return (FAIL);
906 }
907
908 /*
909 * Only privileged process can execute this
910 * for FILES or LDAP
911 */
912 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
913 ((retval = ckuid()) != SUCCESS))
914 return (FAIL);
915 if (flag & (SAFLAG|NFLAG|NONAGEFLAG)) {
916 retval = BADOPT;
917 return (FAIL);
918 }
919 flag |= NFLAG;
920 if ((int)strlen(optarg) <= 0 ||
921 (strtol(optarg, &char_p, 10)) < 0 ||
922 *char_p != '\0') {
923 (void) fprintf(stderr, "%s: %s -n\n",
924 prognamep, gettext(MSG_NV));
925 retval = BADSYN;
926 return (FAIL);
927 }
928 attrlist_add(attributes, ATTR_MIN, optarg);
929 break;
930
931 case 'w': /* set the warning field */
932
933 /* if no repository the default for -w is files */
934 if (repository.type == NULL)
935 repository = __REPFILES;
936
937 if (IS_FILES(repository) == FALSE &&
938 IS_LDAP(repository) == FALSE) {
939 (void) fprintf(stderr, gettext(
940 "-w only applies to files or ldap "
941 "repository\n"));
942 rusage(); /* exit */
943 retval = BADSYN;
944 return (FAIL);
945 }
946
947 /*
948 * Only privileged process can execute this
949 * for FILES or LDAP
950 */
951 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
952 (ckuid() != SUCCESS)) {
953 retval = NOPERM;
954 return (FAIL);
955 }
956 if (flag & (SAFLAG|WFLAG|NONAGEFLAG)) {
957 retval = BADOPT;
958 return (FAIL);
959 }
960 flag |= WFLAG;
961 if ((int)strlen(optarg) <= 0 ||
962 (strtol(optarg, &char_p, 10)) < 0 ||
963 *char_p != '\0') {
964 (void) fprintf(stderr, "%s: %s -w\n",
965 prognamep, gettext(MSG_NV));
966 retval = BADSYN;
967 return (FAIL);
968 }
969 attrlist_add(attributes, ATTR_WARN, optarg);
970 break;
971
972 case 's': /* display password attributes */
973
974 /* if no repository the default for -s is files */
975 if (repository.type == NULL)
976 repository = __REPFILES;
977
978
979 /* display password attributes */
980 if (IS_FILES(repository) == FALSE &&
981 IS_LDAP(repository) == FALSE) {
982 (void) fprintf(stderr, gettext(
983 "-s only applies to files or ldap "
984 "repository\n"));
985 rusage(); /* exit */
986 retval = BADSYN;
987 return (FAIL);
988 }
989
990 /*
991 * Only privileged process can execute this
992 * for FILES or LDAP
993 */
994 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
995 ((retval = ckuid()) != SUCCESS))
996 return (FAIL);
997 if (flag && (flag != AFLAG)) {
998 retval = BADOPT;
999 return (FAIL);
1000 }
1001 flag |= SFLAG;
1002 break;
1003
1004 case 'a': /* display password attributes */
1005
1006 /* if no repository the default for -a is files */
1007 if (repository.type == NULL)
1008 repository = __REPFILES;
1009
1010 if (IS_FILES(repository) == FALSE &&
1011 IS_LDAP(repository) == FALSE) {
1012 (void) fprintf(stderr, gettext(
1013 "-a only applies to files or ldap "
1014 "repository\n"));
1015 rusage(); /* exit */
1016 retval = BADSYN;
1017 return (FAIL);
1018 }
1019
1020 /*
1021 * Only privileged process can execute this
1022 * for FILES or LDAP
1023 */
1024 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
1025 ((retval = ckuid()) != SUCCESS))
1026 return (FAIL);
1027 if (flag && (flag != SFLAG)) {
1028 retval = BADOPT;
1029 return (FAIL);
1030 }
1031 flag |= AFLAG;
1032 break;
1033
1034 case 'f': /* expire password attributes */
1035
1036 /* if no repository the default for -f is files */
1037 if (repository.type == NULL)
1038 repository = __REPFILES;
1039
1040 if (IS_FILES(repository) == FALSE &&
1041 IS_LDAP(repository) == FALSE) {
1042 (void) fprintf(stderr, gettext(
1043 "-f only applies to files or ldap "
1044 "repository\n"));
1045 rusage(); /* exit */
1046 retval = BADSYN;
1047 return (FAIL);
1048 }
1049
1050 /*
1051 * Only privileged process can execute this
1052 * for FILES or LDAP
1053 */
1054 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
1055 ((retval = ckuid()) != SUCCESS))
1056 return (FAIL);
1057 if (flag & (SAFLAG|FFLAG|NONAGEFLAG)) {
1058 retval = BADOPT;
1059 return (FAIL);
1060 }
1061 flag |= FFLAG;
1062 attrlist_add(attributes, ATTR_EXPIRE_PASSWORD, NULL);
1063 break;
1064
1065 case 'e': /* change login shell */
1066
1067 /* if no repository the default for -e is files */
1068 if (repository.type == NULL)
1069 repository = __REPFILES;
1070
1071 if (flag & (EFLAG|SAFLAG|AGEFLAG)) {
1072 retval = BADOPT;
1073 return (FAIL);
1074 }
1075 flag |= EFLAG;
1076 break;
1077
1078 case 'g': /* change gecos information */
1079
1080 /* if no repository the default for -g is files */
1081 if (repository.type == NULL)
1082 repository = __REPFILES;
1083
1084 /*
1085 * Only privileged process can execute this
1086 * for FILES
1087 */
1088 if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
1089 retval = NOPERM;
1090 return (FAIL);
1091 }
1092 if (flag & (GFLAG|SAFLAG|AGEFLAG)) {
1093 retval = BADOPT;
1094 return (FAIL);
1095 }
1096 flag |= GFLAG;
1097 break;
1098
1099 case 'h': /* change home dir */
1100
1101 /* if no repository the default for -h is files */
1102 if (repository.type == NULL)
1103 repository = __REPFILES;
1104 /*
1105 * Only privileged process can execute this
1106 * for FILES
1107 */
1108 if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
1109 retval = NOPERM;
1110 return (FAIL);
1111 }
1112 if (IS_NIS(repository)) {
1113 (void) fprintf(stderr, "%s\n",
1114 gettext(MSG_NIS_HOMEDIR));
1115 retval = BADSYN;
1116 return (FAIL);
1117 }
1118
1119 if (flag & (HFLAG|SAFLAG|AGEFLAG)) {
1120 retval = BADOPT;
1121 return (FAIL);
1122 }
1123 flag |= HFLAG;
1124 break;
1125
1126 case '?':
1127 rusage();
1128 retval = BADOPT;
1129 return (FAIL);
1130 }
1131 }
1132
1133 argc -= optind;
1134 if (argc > 1) {
1135 rusage();
1136 retval = BADSYN;
1137 return (FAIL);
1138 }
1139
1140 /* Make sure (EXPIRE comes after (MAX comes after MIN)) */
1141 attrlist_reorder(attributes);
1142
1143 /* If no options are specified or only the show option */
1144 /* is specified, return because no option error checking */
1145 /* is needed */
1146 if (!flag || (flag == SFLAG))
1147 return (flag);
1148
1149 /* AFLAG must be used with SFLAG */
1150 if (flag == AFLAG) {
1151 rusage();
1152 retval = BADSYN;
1153 return (FAIL);
1154 }
1155
1156 if (flag != SAFLAG && argc < 1) {
1157 /*
1158 * user name is not specified (argc<1), it can't be
1159 * aging info update.
1160 */
1161 if (!(flag & NONAGEFLAG)) {
1162 rusage();
1163 retval = BADSYN;
1164 return (FAIL);
1165 }
1166 }
1167
1168 /* user name(s) may not be specified when SAFLAG is used. */
1169 if (flag == SAFLAG && argc >= 1) {
1170 rusage();
1171 retval = BADSYN;
1172 return (FAIL);
1173 }
1174
1175 /*
1176 * If aging is being turned off (maxdate == -1), mindate may not
1177 * be specified.
1178 */
1179 if ((maxdate == -1) && (flag & NFLAG)) {
1180 (void) fprintf(stderr, "%s: %s -n\n",
1181 prognamep, gettext(MSG_NV));
1182 retval = BADOPT;
1183 return (FAIL);
1184 }
1185
1186 return (flag);
1187 }
1188
1189 /*
1190 *
1191 * ckuid():
1192 * This function returns SUCCESS if the caller is root, else
1193 * it returns NOPERM.
1194 *
1195 */
1196
1197 static int
ckuid(void)1198 ckuid(void)
1199 {
1200 if (uid != 0) {
1201 return (retval = NOPERM);
1202 }
1203 return (SUCCESS);
1204 }
1205
1206
1207 /*
1208 * get_attr()
1209 */
1210 int
get_attr(char * username,pwu_repository_t * repository,attrlist ** attributes)1211 get_attr(char *username, pwu_repository_t *repository, attrlist **attributes)
1212 {
1213 int res;
1214
1215 attrlist_add(attributes, ATTR_PASSWD, NULL);
1216 attrlist_add(attributes, ATTR_LSTCHG, "0");
1217 attrlist_add(attributes, ATTR_MIN, "0");
1218 attrlist_add(attributes, ATTR_MAX, "0");
1219 attrlist_add(attributes, ATTR_WARN, "0");
1220
1221 res = __get_authtoken_attr(username, repository, *attributes);
1222
1223 if (res == PWU_SUCCESS) {
1224 retval = SUCCESS;
1225 return (PWU_SUCCESS);
1226 }
1227
1228 if (res == PWU_NOT_FOUND)
1229 (void) fprintf(stderr, gettext(MSG_UNKNOWN), prognamep,
1230 username);
1231
1232 retval = NOPERM;
1233 passwd_exit(retval);
1234 /*NOTREACHED*/
1235 }
1236
1237 /*
1238 * display_attr():
1239 * This function prints out the password attributes of a usr
1240 * onto standand output.
1241 */
1242 void
display_attr(char * usrname,attrlist * attributes)1243 display_attr(char *usrname, attrlist *attributes)
1244 {
1245 char *status = NULL;
1246 char *passwd;
1247 long lstchg;
1248 int min = 0, max = 0, warn = 0;
1249
1250 while (attributes) {
1251 switch (attributes->type) {
1252 case ATTR_PASSWD:
1253 passwd = attributes->data.val_s;
1254 if (passwd == NULL || *passwd == '\0')
1255 status = "NP ";
1256 else if (strncmp(passwd, LOCKSTRING,
1257 sizeof (LOCKSTRING)-1) == 0)
1258 status = "LK ";
1259 else if (strncmp(passwd, NOLOGINSTRING,
1260 sizeof (NOLOGINSTRING)-1) == 0)
1261 status = "NL ";
1262 else if ((strlen(passwd) == 13 && passwd[0] != '$') ||
1263 passwd[0] == '$')
1264 status = "PS ";
1265 else
1266 status = "UN ";
1267 break;
1268 case ATTR_LSTCHG:
1269 lstchg = attributes->data.val_i * DAY;
1270 break;
1271 case ATTR_MIN:
1272 min = attributes->data.val_i;
1273 break;
1274 case ATTR_MAX:
1275 max = attributes->data.val_i;
1276 break;
1277 case ATTR_WARN:
1278 warn = attributes->data.val_i;
1279 break;
1280 default:
1281 break;
1282 }
1283 attributes = attributes->next;
1284 }
1285 (void) fprintf(stdout, "%-8s ", usrname);
1286
1287 if (status)
1288 (void) fprintf(stdout, "%s ", status);
1289
1290 if (max != -1) {
1291 if (lstchg == 0) {
1292 (void) fprintf(stdout, "00/00/00 ");
1293 } else {
1294 struct tm *tmp;
1295 tmp = gmtime(&lstchg);
1296 (void) fprintf(stdout, "%.2d/%.2d/%.2d ",
1297 tmp->tm_mon + 1,
1298 tmp->tm_mday,
1299 tmp->tm_year % 100);
1300 }
1301 (void) fprintf(stdout, (min >= 0) ? "%4d " : " ", min);
1302 (void) fprintf(stdout, "%4d ", max);
1303 (void) fprintf(stdout, (warn > 0) ? "%4d " : " ", warn);
1304 }
1305 (void) fprintf(stdout, "\n");
1306 }
1307
1308 void
free_attr(attrlist * attributes)1309 free_attr(attrlist *attributes)
1310 {
1311 while (attributes) {
1312 if (attributes->type == ATTR_PASSWD)
1313 free(attributes->data.val_s);
1314 attributes = attributes->next;
1315 }
1316 }
1317
1318 /*
1319 *
1320 * get_namelist_files():
1321 * This function gets a list of user names on the system from
1322 * the /etc/passwd file.
1323 *
1324 */
1325 int
get_namelist_files(char *** namelist_p,int * num_user)1326 get_namelist_files(char ***namelist_p, int *num_user)
1327 {
1328 FILE *pwfp;
1329 struct passwd *pwd;
1330 int max_user;
1331 int nuser;
1332 char **nl;
1333
1334 nuser = 0;
1335 errno = 0;
1336 pwd = NULL;
1337
1338 if ((pwfp = fopen(PASSWD, "r")) == NULL)
1339 return (NOPERM);
1340
1341 /*
1342 * find out the actual number of entries in the PASSWD file
1343 */
1344 max_user = 1; /* need one slot for terminator NULL */
1345 while ((pwd = fgetpwent(pwfp)) != NULL)
1346 max_user++;
1347
1348 /*
1349 * reset the file stream pointer
1350 */
1351 rewind(pwfp);
1352
1353 nl = (char **)calloc(max_user, (sizeof (char *)));
1354 if (nl == NULL) {
1355 (void) fclose(pwfp);
1356 return (FMERR);
1357 }
1358
1359 while ((pwd = fgetpwent(pwfp)) != NULL) {
1360 if ((nl[nuser] = strdup(pwd->pw_name)) == NULL) {
1361 (void) fclose(pwfp);
1362 return (FMERR);
1363 }
1364 nuser++;
1365 }
1366
1367 nl[nuser] = NULL;
1368 *num_user = nuser;
1369 *namelist_p = nl;
1370 (void) fclose(pwfp);
1371 return (SUCCESS);
1372 }
1373
1374 /*
1375 * get_namelist_local
1376 *
1377 */
1378
1379 /*
1380 * Our private version of the switch frontend for getspent. We want
1381 * to search just the ldap sp file, so we want to bypass
1382 * normal nsswitch.conf based processing. This implementation
1383 * compatible with version 2 of the name service switch.
1384 */
1385 #define NSS_LDAP_ONLY "ldap"
1386
1387 extern int str2spwd(const char *, int, void *, char *, int);
1388
1389 static DEFINE_NSS_DB_ROOT(db_root);
1390 static DEFINE_NSS_GETENT(context);
1391
1392 static char *local_config;
1393 static void
_lc_nss_initf_shadow(nss_db_params_t * p)1394 _lc_nss_initf_shadow(nss_db_params_t *p)
1395 {
1396 p->name = NSS_DBNAM_SHADOW;
1397 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */
1398 p->default_config = local_config; /* Use ldap only */
1399 p->flags = NSS_USE_DEFAULT_CONFIG;
1400 }
1401
1402 static void
_lc_setspent(void)1403 _lc_setspent(void)
1404 {
1405 nss_setent(&db_root, _lc_nss_initf_shadow, &context);
1406 }
1407
1408 static void
_lc_endspent(void)1409 _lc_endspent(void)
1410 {
1411 nss_endent(&db_root, _lc_nss_initf_shadow, &context);
1412 nss_delete(&db_root);
1413 }
1414
1415 static struct spwd *
_lc_getspent_r(struct spwd * result,char * buffer,int buflen)1416 _lc_getspent_r(struct spwd *result, char *buffer, int buflen)
1417 {
1418 nss_XbyY_args_t arg;
1419 char *nam;
1420
1421 /* In getXXent_r(), protect the unsuspecting caller from +/- entries */
1422
1423 do {
1424 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2spwd);
1425 /* No key to fill in */
1426 (void) nss_getent(&db_root, _lc_nss_initf_shadow, &context,
1427 &arg);
1428 } while (arg.returnval != 0 &&
1429 (nam = ((struct spwd *)arg.returnval)->sp_namp) != 0 &&
1430 (*nam == '+' || *nam == '-'));
1431
1432 return (struct spwd *)NSS_XbyY_FINI(&arg);
1433 }
1434
1435 static nss_XbyY_buf_t *buffer;
1436
1437 static struct spwd *
_lc_getspent(void)1438 _lc_getspent(void)
1439 {
1440 nss_XbyY_buf_t *b;
1441
1442 b = NSS_XbyY_ALLOC(&buffer, sizeof (struct spwd), NSS_BUFLEN_SHADOW);
1443
1444 return (b == 0 ? 0 : _lc_getspent_r(b->result, b->buffer, b->buflen));
1445 }
1446
1447 int
get_namelist_local(char *** namelist_p,int * num_user)1448 get_namelist_local(char ***namelist_p, int *num_user)
1449 {
1450 int nuser = 0;
1451 int alloced = 100;
1452 char **nl;
1453 struct spwd *p;
1454
1455
1456 if ((nl = calloc(alloced, sizeof (*nl))) == NULL)
1457 return (FMERR);
1458
1459 (void) _lc_setspent();
1460 while ((p = _lc_getspent()) != NULL) {
1461 if ((nl[nuser] = strdup(p->sp_namp)) == NULL) {
1462 _lc_endspent();
1463 return (FMERR);
1464 }
1465 if (++nuser == alloced) {
1466 alloced += 100;
1467 nl = realloc(nl, alloced * (sizeof (*nl)));
1468 if (nl == NULL) {
1469 _lc_endspent();
1470 return (FMERR);
1471 }
1472 }
1473 }
1474 (void) _lc_endspent();
1475 nl[nuser] = NULL;
1476
1477 *namelist_p = nl;
1478 *num_user = nuser; /* including NULL */
1479
1480 return (SUCCESS);
1481 }
1482
1483 int
get_namelist(pwu_repository_t repository,char *** namelist,int * num_user)1484 get_namelist(pwu_repository_t repository, char ***namelist, int *num_user)
1485 {
1486 if (IS_LDAP(repository)) {
1487 local_config = NSS_LDAP_ONLY;
1488 return (get_namelist_local(namelist, num_user));
1489 } else if (IS_FILES(repository))
1490 return (get_namelist_files(namelist, num_user));
1491
1492 rusage();
1493 return (BADSYN);
1494 }
1495
1496 /*
1497 *
1498 * passwd_exit():
1499 * This function will call exit() with appropriate exit code
1500 * according to the input "retcode" value.
1501 * It also calls pam_end() to clean-up buffers before exit.
1502 *
1503 */
1504
1505 void
passwd_exit(int retcode)1506 passwd_exit(int retcode)
1507 {
1508
1509 if (pamh)
1510 (void) pam_end(pamh, pam_retval);
1511
1512 switch (retcode) {
1513 case SUCCESS:
1514 break;
1515 case NOPERM:
1516 (void) fprintf(stderr, "%s\n", gettext(MSG_NP));
1517 break;
1518 case BADOPT:
1519 (void) fprintf(stderr, "%s\n", gettext(MSG_BS));
1520 break;
1521 case FMERR:
1522 (void) fprintf(stderr, "%s\n", gettext(MSG_FE));
1523 break;
1524 case FATAL:
1525 (void) fprintf(stderr, "%s\n", gettext(MSG_FF));
1526 break;
1527 case FBUSY:
1528 (void) fprintf(stderr, "%s\n", gettext(MSG_FB));
1529 break;
1530 case BADSYN:
1531 (void) fprintf(stderr, "%s\n", gettext(MSG_NV));
1532 break;
1533 case BADAGE:
1534 (void) fprintf(stderr, "%s\n", gettext(MSG_AD));
1535 break;
1536 case NOMEM:
1537 (void) fprintf(stderr, "%s\n", gettext(MSG_NM));
1538 break;
1539 default:
1540 (void) fprintf(stderr, "%s\n", gettext(MSG_NP));
1541 retcode = NOPERM;
1542 break;
1543 }
1544 /* write password record */
1545 if (event != NULL) {
1546 struct passwd *pass;
1547
1548 if ((pass = getpwnam(usrname)) == NULL) {
1549 /* unlikely to ever get here, but ... */
1550 event->adt_passwd.username = usrname;
1551 } else if (pass->pw_uid != uid) {
1552 /* save target user */
1553 event->adt_passwd.uid = pass->pw_uid;
1554 event->adt_passwd.username = pass->pw_name;
1555 }
1556
1557 if (adt_put_event(event,
1558 retcode == SUCCESS ? ADT_SUCCESS : ADT_FAILURE,
1559 retcode == SUCCESS ? ADT_SUCCESS : ADT_FAIL_PAM +
1560 pam_retval) != 0) {
1561 adt_free_event(event);
1562 (void) adt_end_session(ah);
1563 perror("adt_put_event");
1564 exit(retcode);
1565 }
1566 adt_free_event(event);
1567 }
1568 (void) adt_end_session(ah);
1569 exit(retcode);
1570 }
1571
1572 /*
1573 *
1574 * passwd_conv():
1575 * This is the conv (conversation) function called from
1576 * a PAM authentication module to print error messages
1577 * or garner information from the user.
1578 *
1579 */
1580
1581 static int
passwd_conv(int num_msg,const struct pam_message ** msg,struct pam_response ** response,void * appdata_ptr)1582 passwd_conv(int num_msg, const struct pam_message **msg,
1583 struct pam_response **response, void *appdata_ptr)
1584 {
1585 const struct pam_message *m;
1586 struct pam_response *r;
1587 char *temp;
1588 int k, i;
1589
1590 if (num_msg <= 0)
1591 return (PAM_CONV_ERR);
1592
1593 *response = (struct pam_response *)calloc(num_msg,
1594 sizeof (struct pam_response));
1595 if (*response == NULL)
1596 return (PAM_BUF_ERR);
1597
1598 k = num_msg;
1599 m = *msg;
1600 r = *response;
1601 while (k--) {
1602
1603 switch (m->msg_style) {
1604
1605 case PAM_PROMPT_ECHO_OFF:
1606 temp = getpassphrase(m->msg);
1607 if (temp != NULL) {
1608 r->resp = strdup(temp);
1609 (void) memset(temp, 0, strlen(temp));
1610 if (r->resp == NULL) {
1611 /* free responses */
1612 r = *response;
1613 for (i = 0; i < num_msg; i++, r++) {
1614 if (r->resp)
1615 free(r->resp);
1616 }
1617 free(*response);
1618 *response = NULL;
1619 return (PAM_BUF_ERR);
1620 }
1621 }
1622 m++;
1623 r++;
1624 break;
1625
1626 case PAM_PROMPT_ECHO_ON:
1627 if (m->msg != NULL) {
1628 (void) fputs(m->msg, stdout);
1629 }
1630 r->resp = (char *)calloc(PAM_MAX_RESP_SIZE,
1631 sizeof (char));
1632 if (r->resp == NULL) {
1633 /* free responses */
1634 r = *response;
1635 for (i = 0; i < num_msg; i++, r++) {
1636 if (r->resp)
1637 free(r->resp);
1638 }
1639 free(*response);
1640 *response = NULL;
1641 return (PAM_BUF_ERR);
1642 }
1643 if (fgets(r->resp, PAM_MAX_RESP_SIZE-1, stdin)) {
1644 int len = strlen(r->resp);
1645 if (r->resp[len-1] == '\n')
1646 r->resp[len-1] = '\0';
1647 }
1648 m++;
1649 r++;
1650 break;
1651
1652 case PAM_ERROR_MSG:
1653 if (m->msg != NULL) {
1654 (void) fputs(m->msg, stderr);
1655 (void) fputs("\n", stderr);
1656 }
1657 m++;
1658 r++;
1659 break;
1660 case PAM_TEXT_INFO:
1661 if (m->msg != NULL) {
1662 (void) fputs(m->msg, stdout);
1663 (void) fputs("\n", stdout);
1664 }
1665 m++;
1666 r++;
1667 break;
1668
1669 default:
1670 break;
1671 }
1672 }
1673 return (PAM_SUCCESS);
1674 }
1675
1676 /*
1677 * Utilities Functions
1678 */
1679
1680 /*
1681 * int attrlist_add(attrlist **l, attrtype type, char *val)
1682 * add an item, with type "type" and value "val", at the tail of list l.
1683 * This functions exits the application on OutOfMem error.
1684 */
1685 void
attrlist_add(attrlist ** l,attrtype type,char * val)1686 attrlist_add(attrlist **l, attrtype type, char *val)
1687 {
1688 attrlist **w;
1689
1690 /* tail insert */
1691 for (w = l; *w != NULL; w = &(*w)->next)
1692 ;
1693
1694 if ((*w = malloc(sizeof (**w))) == NULL)
1695 passwd_exit(NOMEM);
1696
1697 (*w)->type = type;
1698 (*w)->next = NULL;
1699
1700 switch (type) {
1701 case ATTR_MIN:
1702 case ATTR_WARN:
1703 case ATTR_MAX:
1704 (*w)->data.val_i = atoi(val);
1705 break;
1706 default:
1707 (*w)->data.val_s = val;
1708 break;
1709 }
1710 }
1711
1712 /*
1713 * attrlist_reorder(attrlist **l)
1714 * Make sure that
1715 * - if EXPIRE and MAX or MIN is set, EXPIRE comes after MAX/MIN
1716 * - if both MIN and MAX are set, MAX comes before MIN.
1717 */
1718
1719 static void
attrlist_reorder(attrlist ** l)1720 attrlist_reorder(attrlist **l)
1721 {
1722 attrlist **w;
1723 attrlist *exp = NULL; /* ATTR_EXPIRE_PASSWORD, if found */
1724 attrlist *max = NULL; /* ATTR_MAX, if found */
1725
1726 if (*l == NULL || (*l)->next == NULL)
1727 return; /* order of list with <= one item is ok */
1728
1729 /*
1730 * We simply walk the list, take off the EXPIRE and MAX items if
1731 * they appear, and put them (first MAX, them EXPIRE) at the end
1732 * of the list.
1733 */
1734 w = l;
1735 while (*w != NULL) {
1736 if ((*w)->type == ATTR_EXPIRE_PASSWORD) {
1737 exp = *w;
1738 *w = (*w)->next;
1739 } else if ((*w)->type == ATTR_MAX) {
1740 max = *w;
1741 *w = (*w)->next;
1742 } else
1743 w = &(*w)->next;
1744 }
1745
1746 /* 'w' points to the address of the 'next' field of the last element */
1747
1748 if (max) {
1749 *w = max;
1750 w = &max->next;
1751 }
1752 if (exp) {
1753 *w = exp;
1754 w = &exp->next;
1755 }
1756 *w = NULL;
1757 }
1758
1759 void
rusage(void)1760 rusage(void)
1761 {
1762
1763 #define MSG(a) (void) fprintf(stderr, gettext((a)));
1764
1765 MSG("usage:\n");
1766 MSG("\tpasswd [-r files | -r nis | -r ldap] [name]\n");
1767 MSG("\tpasswd [-r files] [-egh] [name]\n");
1768 MSG("\tpasswd [-r files] -sa\n");
1769 MSG("\tpasswd [-r files] -s [name]\n");
1770 MSG("\tpasswd [-r files] [-d|-l|-N|-u] [-f] [-n min] [-w warn] "
1771 "[-x max] name\n");
1772 MSG("\tpasswd -r nis [-eg] [name]\n");
1773 MSG("\t\t[-x max] name\n");
1774 MSG("\tpasswd -r ldap [-egh] [name]\n");
1775 MSG("\tpasswd -r ldap -sa\n");
1776 MSG("\tpasswd -r ldap -s [name]\n");
1777 MSG("\tpasswd -r ldap [-l|-N|-u] [-f] [-n min] [-w warn] "
1778 "[-x max] name\n");
1779 #undef MSG
1780 }
1781