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