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>
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 */
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);
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
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 */
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
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:
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
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,
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;
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 */
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
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
1758 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 }
|
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 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
34 */
35 /*
36 * passwd is a program whose sole purpose is to manage
37 * the password file, map, or table. It allows system administrator
38 * to add, change and display password attributes.
39 * Non privileged user can change password or display
40 * password attributes which corresponds to their login name.
41 */
42
43 #include <stdio.h>
44 #include <pwd.h>
45 #include <sys/types.h>
46 #include <errno.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <locale.h>
50 #include <stdarg.h>
51 #include <errno.h>
52 #include <string.h>
53 #include <security/pam_appl.h>
54 #include <security/pam_modules.h>
55 #include <security/pam_impl.h>
70 /*
71 * flags indicate password attributes to be modified
72 */
73
74 #define LFLAG 0x001 /* lock user's password */
75 #define DFLAG 0x002 /* delete user's password */
76 #define MFLAG 0x004 /* set max field -- # of days passwd is valid */
77 #define NFLAG 0x008 /* set min field -- # of days between */
78 /* password changes */
79 #define SFLAG 0x010 /* display password attributes */
80 #define FFLAG 0x020 /* expire user's password */
81 #define AFLAG 0x040 /* display password attributes for all users */
82 #define SAFLAG (SFLAG|AFLAG) /* display password attributes for all users */
83 #define WFLAG 0x100 /* warn user to change passwd */
84 #define OFLAG 0x200 /* domain name */
85 #define EFLAG 0x400 /* change shell */
86 #define GFLAG 0x800 /* change gecos information */
87 #define HFLAG 0x1000 /* change home directory */
88 #define XFLAG 0x2000 /* no login */
89 #define UFLAG 0x4000 /* unlock user's password */
90 #define STFLAG 0x6000 /* read the password from stdin */
91
92 #define NONAGEFLAG (EFLAG | GFLAG | HFLAG)
93 #define AGEFLAG (LFLAG | FFLAG | MFLAG | NFLAG | WFLAG | XFLAG | UFLAG)
94 #define MUTEXFLAG (DFLAG | LFLAG | XFLAG | UFLAG | SAFLAG)
95
96
97 /*
98 * exit code
99 */
100
101 #define SUCCESS 0 /* succeeded */
102 #define NOPERM 1 /* No permission */
103 #define BADOPT 2 /* Invalid combination of option */
104 #define FMERR 3 /* File/table manipulation error */
105 #define FATAL 4 /* Old file/table can not be recovered */
106 #define FBUSY 5 /* Lock file/table busy */
107 #define BADSYN 6 /* Incorrect syntax */
108 #define BADAGE 7 /* Aging is disabled */
109 #define NOMEM 8 /* No memory */
110 #define SYSERR 9 /* System error */
165 #define DEF_ATTEMPTS 3
166
167 /* Number of characters in that make up an encrypted password (for now) */
168 #define NUMCP 13
169
170 #ifdef DEBUG
171 #define dprintf1 printf
172 #else
173 #define dprintf1(w, x)
174 #endif
175
176 extern int optind;
177
178 static int retval = SUCCESS;
179 static int pam_retval = PAM_SUCCESS;
180 static uid_t uid;
181 static char *prognamep;
182 static long maxdate; /* password aging information */
183 static int passwd_conv(int, struct pam_message **,
184 struct pam_response **, void *);
185 static int stdin_conv(int, struct pam_message **,
186 struct pam_response **, void *);
187 static struct pam_conv pam_conv = {passwd_conv, NULL};
188 static pam_handle_t *pamh; /* Authentication handle */
189 static char *usrname; /* user whose attribute we update */
190 static adt_session_data_t *ah; /* audit session handle */
191 static adt_event_data_t *event = NULL; /* event to be generated */
192
193 static pam_repository_t auth_rep;
194 static pwu_repository_t repository;
195 static pwu_repository_t __REPFILES = { "files", NULL, 0 };
196
197 /*
198 * Function Declarations
199 */
200
201 extern void setusershell(void);
202 extern char *getusershell(void);
203 extern void endusershell(void);
204
205 static void passwd_exit(int retcode) __NORETURN;
206 static void rusage(void);
223 * The main routine will call ckarg() to parse the command line
224 * arguments and call the appropriate functions to perform the
225 * tasks specified by the arguments. It allows system
226 * administrator to add, change and display password attributes.
227 * Non privileged user can change password or display
228 * password attributes which corresponds to their login name.
229 */
230
231 int
232 main(int argc, char *argv[])
233 {
234
235 int flag;
236 char **namelist;
237 int num_user;
238 int i;
239 attrlist *attributes = NULL;
240 char *input;
241 int tries = 1;
242 int updated_reps;
243 ssize_t s;
244 char st_pass[PASS_MAX];
245
246 if ((prognamep = strrchr(argv[0], '/')) != NULL)
247 ++prognamep;
248 else
249 prognamep = argv[0];
250
251 auth_rep.type = NULL;
252 auth_rep.scope = NULL;
253 repository.type = NULL;
254 repository.scope = NULL;
255 repository.scope_len = 0;
256
257
258 /* initialization for variables, set locale and textdomain */
259 i = 0;
260 flag = 0;
261
262 uid = getuid(); /* get the user id */
263 (void) setlocale(LC_ALL, "");
264
265 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
284 struct passwd *pass = getpwuid(uid);
285 if (pass != NULL)
286 usrname = pass->pw_name;
287 else {
288 rusage();
289 exit(NOPERM);
290 }
291 } else if (flag == 0) {
292 /*
293 * If flag is zero, change passwd.
294 * Otherwise, it will display or
295 * modify password aging attributes
296 */
297 (void) fprintf(stderr, gettext(MSG_INFO), prognamep,
298 usrname);
299 }
300 } else {
301 usrname = argv[optind];
302 }
303
304 if (flag == STFLAG) {
305 if ((s = read(STDIN_FILENO, st_pass, sizeof (st_pass))) <= 0)
306 passwd_exit(FMERR);
307
308 st_pass[s - 1] = '\0';
309 if ((pam_conv.appdata_ptr = strdup(st_pass)) == NULL)
310 passwd_exit(FMERR);
311 pam_conv.conv = stdin_conv;
312 }
313
314 if (pam_start("passwd", usrname, &pam_conv, &pamh) != PAM_SUCCESS) {
315 passwd_exit(NOPERM);
316 }
317
318 auth_rep.type = repository.type;
319 auth_rep.scope = repository.scope;
320 auth_rep.scope_len = repository.scope_len;
321
322 if (auth_rep.type != NULL) {
323 if (pam_set_item(pamh, PAM_REPOSITORY, (void *)&auth_rep)
324 != PAM_SUCCESS) {
325 passwd_exit(NOPERM);
326 }
327 }
328
329 if (flag == SAFLAG) { /* display password attributes for all users */
330 retval = get_namelist(repository, &namelist, &num_user);
331 if (retval != SUCCESS)
332 (void) passwd_exit(retval);
333
362 case PAM_SUCCESS:
363 break;
364 case PAM_USER_UNKNOWN:
365 (void) fprintf(stderr, gettext(MSG_UNKNOWN), prognamep,
366 usrname);
367 passwd_exit(NOPERM);
368 break;
369 case PAM_PERM_DENIED:
370 passwd_exit(NOPERM);
371 break;
372 case PAM_AUTH_ERR:
373 (void) fprintf(stderr, gettext(MSG_SORRY), prognamep);
374 passwd_exit(NOPERM);
375 break;
376 default:
377 /* system error */
378 passwd_exit(FMERR);
379 break;
380 }
381
382 if (flag == STFLAG || flag == 0) { /* changing user password */
383 int chk_authtok = 0; /* check password strength */
384
385 dprintf1("call pam_chauthtok() repository name =%s\n",
386 repository.type);
387
388 /* Set up for Audit */
389 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
390 perror("adt_start_session");
391 passwd_exit(SYSERR);
392 }
393 if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) {
394 perror("adt_alloc_event");
395 passwd_exit(NOMEM);
396 }
397
398 /* Don't check account expiration when invoked by root */
399 if (ckuid() != SUCCESS) {
400 pam_retval = pam_acct_mgmt(pamh, PAM_SILENT);
401 switch (pam_retval) {
402 case PAM_ACCT_EXPIRED:
688 }
689 /*
690 * ckarg():
691 * This function parses and verifies the
692 * arguments. It takes three parameters:
693 * argc => # of arguments
694 * argv => pointer to an argument
695 * attrlist => pointer to list of password attributes
696 */
697
698 static int
699 ckarg(int argc, char **argv, attrlist **attributes)
700 {
701 extern char *optarg;
702 char *char_p;
703 int opt;
704 int flag;
705
706 flag = 0;
707
708 while ((opt = getopt(argc, argv, "r:aldefghsux:n:w:N:S")) != EOF) {
709 switch (opt) {
710
711 case 'r': /* Repository Specified */
712 /* repository: this option should be specified first */
713
714 if (repository.type != NULL) {
715 (void) fprintf(stderr, gettext(
716 "Repository is already defined or specified.\n"));
717 rusage();
718 retval = BADSYN;
719 return (FAIL);
720 }
721 if (strcmp(optarg, "nis") == 0) {
722 repository.type = optarg;
723 } else if (strcmp(optarg, "ldap") == 0) {
724 repository.type = optarg;
725 } else if (strcmp(optarg, "files") == 0) {
726 repository.type = optarg;
727 } else {
728 (void) fprintf(stderr,
740 repository = __REPFILES;
741
742 /*
743 * Delete the password - only privileged processes
744 * can execute this for FILES or LDAP
745 */
746 if (IS_FILES(repository) == FALSE &&
747 IS_LDAP(repository) == FALSE) {
748 (void) fprintf(stderr, gettext(
749 "-d only applies to files "
750 "or ldap repository\n"));
751 rusage(); /* exit */
752 retval = BADSYN;
753 return (FAIL);
754 }
755
756 if (ckuid() != SUCCESS) {
757 retval = NOPERM;
758 return (FAIL);
759 }
760 if (flag & (LFLAG|SAFLAG|DFLAG|XFLAG|UFLAG|STFLAG)) {
761 rusage();
762 retval = BADOPT;
763 return (FAIL);
764 }
765 flag |= DFLAG;
766 attrlist_add(attributes, ATTR_PASSWD, NULL);
767 break;
768
769 case 'N': /* set account to be "no login" */
770
771 /* if no repository the default for -N is files */
772 if (repository.type == NULL)
773 repository = __REPFILES;
774
775 if (IS_FILES(repository) == FALSE &&
776 IS_LDAP(repository) == FALSE) {
777 (void) fprintf(stderr, gettext(
778 "-N only applies to files or ldap "
779 "repository\n"));
780 rusage(); /* exit */
781 retval = BADOPT;
782 return (FAIL);
783 }
784
785 /*
786 * Only privileged processes can execute this
787 * for FILES or LDAP
788 */
789 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
790 ((retval = ckuid()) != SUCCESS))
791 return (FAIL);
792 if (flag & (MUTEXFLAG|NONAGEFLAG|STFLAG)) {
793 rusage(); /* exit */
794 retval = BADOPT;
795 return (FAIL);
796 }
797 flag |= XFLAG;
798 attrlist_add(attributes, ATTR_NOLOGIN_ACCOUNT, NULL);
799 break;
800
801 case 'l': /* lock the password */
802
803 /* if no repository the default for -l is files */
804 if (repository.type == NULL)
805 repository = __REPFILES;
806
807 if (IS_FILES(repository) == FALSE &&
808 IS_LDAP(repository) == FALSE) {
809 (void) fprintf(stderr, gettext(
810 "-l only applies to files or ldap "
811 "repository\n"));
812 rusage(); /* exit */
813 retval = BADOPT;
814 return (FAIL);
815 }
816
817 /*
818 * Only privileged processes can execute this
819 * for FILES or LDAP
820 */
821 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
822 ((retval = ckuid()) != SUCCESS))
823 return (FAIL);
824 if (flag & (MUTEXFLAG|NONAGEFLAG|STFLAG)) {
825 rusage(); /* exit */
826 retval = BADOPT;
827 return (FAIL);
828 }
829 flag |= LFLAG;
830 attrlist_add(attributes, ATTR_LOCK_ACCOUNT, NULL);
831 break;
832
833 case 'u': /* unlock the password */
834
835 /* if no repository the default for -u is files */
836 if (repository.type == NULL)
837 repository = __REPFILES;
838
839 if (IS_FILES(repository) == FALSE &&
840 IS_LDAP(repository) == FALSE) {
841 (void) fprintf(stderr, gettext(
842 "-u only applies to files or ldap "
843 "repository\n"));
844 rusage(); /* exit */
845 retval = BADOPT;
846 return (FAIL);
847 }
848
849 /*
850 * Only privileged processes can execute this
851 * for FILES or LDAP
852 */
853 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
854 ((retval = ckuid()) != SUCCESS))
855 return (FAIL);
856 if (flag & (MUTEXFLAG|NONAGEFLAG|STFLAG)) {
857 rusage(); /* exit */
858 retval = BADOPT;
859 return (FAIL);
860 }
861 flag |= UFLAG;
862 attrlist_add(attributes, ATTR_UNLOCK_ACCOUNT, NULL);
863 attrlist_add(attributes, ATTR_RST_FAILED_LOGINS, NULL);
864 break;
865
866 case 'x': /* set the max date */
867
868 /* if no repository the default for -x is files */
869 if (repository.type == NULL)
870 repository = __REPFILES;
871
872 if (IS_FILES(repository) == FALSE &&
873 IS_LDAP(repository) == FALSE) {
874 (void) fprintf(stderr, gettext(
875 "-x only applies to files or ldap "
876 "repository\n"));
877 rusage(); /* exit */
878 retval = BADSYN;
879 return (FAIL);
880 }
881
882 /*
883 * Only privileged process can execute this
884 * for FILES or LDAP
885 */
886 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
887 (ckuid() != SUCCESS)) {
888 retval = NOPERM;
889 return (FAIL);
890 }
891 if (flag & (SAFLAG|MFLAG|NONAGEFLAG|STFLAG)) {
892 retval = BADOPT;
893 return (FAIL);
894 }
895 flag |= MFLAG;
896 if ((int)strlen(optarg) <= 0 ||
897 (maxdate = strtol(optarg, &char_p, 10)) < -1 ||
898 *char_p != '\0') {
899 (void) fprintf(stderr, "%s: %s -x\n",
900 prognamep, gettext(MSG_NV));
901 retval = BADSYN;
902 return (FAIL);
903 }
904 attrlist_add(attributes, ATTR_MAX, optarg);
905 break;
906
907 case 'n': /* set the min date */
908
909 /* if no repository the default for -n is files */
910 if (repository.type == NULL)
911 repository = __REPFILES;
912
913 if (IS_FILES(repository) == FALSE &&
914 IS_LDAP(repository) == FALSE) {
915 (void) fprintf(stderr, gettext(
916 "-n only applies to files or ldap "
917 "repository\n"));
918 rusage(); /* exit */
919 retval = BADSYN;
920 return (FAIL);
921 }
922
923 /*
924 * Only privileged process can execute this
925 * for FILES or LDAP
926 */
927 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
928 ((retval = ckuid()) != SUCCESS))
929 return (FAIL);
930 if (flag & (SAFLAG|NFLAG|NONAGEFLAG|STFLAG)) {
931 retval = BADOPT;
932 return (FAIL);
933 }
934 flag |= NFLAG;
935 if ((int)strlen(optarg) <= 0 ||
936 (strtol(optarg, &char_p, 10)) < 0 ||
937 *char_p != '\0') {
938 (void) fprintf(stderr, "%s: %s -n\n",
939 prognamep, gettext(MSG_NV));
940 retval = BADSYN;
941 return (FAIL);
942 }
943 attrlist_add(attributes, ATTR_MIN, optarg);
944 break;
945
946 case 'w': /* set the warning field */
947
948 /* if no repository the default for -w is files */
949 if (repository.type == NULL)
950 repository = __REPFILES;
951
952 if (IS_FILES(repository) == FALSE &&
953 IS_LDAP(repository) == FALSE) {
954 (void) fprintf(stderr, gettext(
955 "-w only applies to files or ldap "
956 "repository\n"));
957 rusage(); /* exit */
958 retval = BADSYN;
959 return (FAIL);
960 }
961
962 /*
963 * Only privileged process can execute this
964 * for FILES or LDAP
965 */
966 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
967 (ckuid() != SUCCESS)) {
968 retval = NOPERM;
969 return (FAIL);
970 }
971 if (flag & (SAFLAG|WFLAG|NONAGEFLAG|STFLAG)) {
972 retval = BADOPT;
973 return (FAIL);
974 }
975 flag |= WFLAG;
976 if ((int)strlen(optarg) <= 0 ||
977 (strtol(optarg, &char_p, 10)) < 0 ||
978 *char_p != '\0') {
979 (void) fprintf(stderr, "%s: %s -w\n",
980 prognamep, gettext(MSG_NV));
981 retval = BADSYN;
982 return (FAIL);
983 }
984 attrlist_add(attributes, ATTR_WARN, optarg);
985 break;
986
987 case 's': /* display password attributes */
988
989 /* if no repository the default for -s is files */
990 if (repository.type == NULL)
991 repository = __REPFILES;
1052 if (repository.type == NULL)
1053 repository = __REPFILES;
1054
1055 if (IS_FILES(repository) == FALSE &&
1056 IS_LDAP(repository) == FALSE) {
1057 (void) fprintf(stderr, gettext(
1058 "-f only applies to files or ldap "
1059 "repository\n"));
1060 rusage(); /* exit */
1061 retval = BADSYN;
1062 return (FAIL);
1063 }
1064
1065 /*
1066 * Only privileged process can execute this
1067 * for FILES or LDAP
1068 */
1069 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
1070 ((retval = ckuid()) != SUCCESS))
1071 return (FAIL);
1072 if (flag & (SAFLAG|FFLAG|NONAGEFLAG|STFLAG)) {
1073 retval = BADOPT;
1074 return (FAIL);
1075 }
1076 flag |= FFLAG;
1077 attrlist_add(attributes, ATTR_EXPIRE_PASSWORD, NULL);
1078 break;
1079
1080 case 'e': /* change login shell */
1081
1082 /* if no repository the default for -e is files */
1083 if (repository.type == NULL)
1084 repository = __REPFILES;
1085
1086 if (flag & (EFLAG|SAFLAG|AGEFLAG|STFLAG)) {
1087 retval = BADOPT;
1088 return (FAIL);
1089 }
1090 flag |= EFLAG;
1091 break;
1092
1093 case 'g': /* change gecos information */
1094
1095 /* if no repository the default for -g is files */
1096 if (repository.type == NULL)
1097 repository = __REPFILES;
1098
1099 /*
1100 * Only privileged process can execute this
1101 * for FILES
1102 */
1103 if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
1104 retval = NOPERM;
1105 return (FAIL);
1106 }
1107 if (flag & (GFLAG|SAFLAG|AGEFLAG|STFLAG)) {
1108 retval = BADOPT;
1109 return (FAIL);
1110 }
1111 flag |= GFLAG;
1112 break;
1113
1114 case 'h': /* change home dir */
1115
1116 /* if no repository the default for -h is files */
1117 if (repository.type == NULL)
1118 repository = __REPFILES;
1119 /*
1120 * Only privileged process can execute this
1121 * for FILES
1122 */
1123 if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
1124 retval = NOPERM;
1125 return (FAIL);
1126 }
1127 if (IS_NIS(repository)) {
1128 (void) fprintf(stderr, "%s\n",
1129 gettext(MSG_NIS_HOMEDIR));
1130 retval = BADSYN;
1131 return (FAIL);
1132 }
1133
1134 if (flag & (HFLAG|SAFLAG|AGEFLAG|STFLAG)) {
1135 retval = BADOPT;
1136 return (FAIL);
1137 }
1138 flag |= HFLAG;
1139 break;
1140 case 'S':
1141 if (ckuid() != SUCCESS) {
1142 retval = NOPERM;
1143 return (FAIL);
1144 }
1145 if (flag & (MUTEXFLAG|NONAGEFLAG|AGEFLAG)) {
1146 rusage(); /* exit */
1147 retval = BADOPT;
1148 return (FAIL);
1149 }
1150 flag |= STFLAG;
1151 break;
1152 case '?':
1153 rusage();
1154 retval = BADOPT;
1155 return (FAIL);
1156 }
1157 }
1158
1159 argc -= optind;
1160 if (argc > 1) {
1161 rusage();
1162 retval = BADSYN;
1163 return (FAIL);
1164 }
1165
1166 /* Make sure (EXPIRE comes after (MAX comes after MIN)) */
1167 attrlist_reorder(attributes);
1168
1169 /* If no options are specified or only the show option */
1170 /* is specified, return because no option error checking */
1171 /* is needed */
1684 m++;
1685 r++;
1686 break;
1687 case PAM_TEXT_INFO:
1688 if (m->msg != NULL) {
1689 (void) fputs(m->msg, stdout);
1690 (void) fputs("\n", stdout);
1691 }
1692 m++;
1693 r++;
1694 break;
1695
1696 default:
1697 break;
1698 }
1699 }
1700 return (PAM_SUCCESS);
1701 }
1702
1703 /*
1704 *
1705 * stdin_conv():
1706 * This is the conv function called for reading
1707 * the password from standard input.
1708 *
1709 */
1710 /*ARGSUSED*/
1711 static int
1712 stdin_conv(int num_msg, struct pam_message **msg,
1713 struct pam_response **response, void *appdata_ptr)
1714 {
1715 struct pam_response *reply = NULL;
1716 int replies = 0;
1717
1718 if (num_msg <= 0)
1719 return (PAM_CONV_ERR);
1720
1721 reply = calloc(num_msg, sizeof (struct pam_response));
1722 if (reply == NULL)
1723 return (PAM_BUF_ERR);
1724
1725 for (replies = 0; replies < num_msg; replies ++) {
1726 reply[replies].resp = NULL;
1727 reply[replies].resp_retcode = PAM_SUCCESS;
1728 switch (msg[replies]->msg_style) {
1729 case PAM_PROMPT_ECHO_OFF:
1730 reply[replies].resp = strdup(appdata_ptr);
1731 if (reply[replies].resp == NULL)
1732 goto err;
1733 break;
1734 case PAM_PROMPT_ECHO_ON:
1735 case PAM_ERROR_MSG:
1736 case PAM_TEXT_INFO:
1737 reply[replies].resp = strdup("");
1738 if (reply[replies].resp == NULL)
1739 goto err;
1740 break;
1741 default:
1742 free(reply);
1743 return (PAM_CONV_ERR);
1744 }
1745 }
1746 *response = reply;
1747 return (PAM_SUCCESS);
1748
1749 err:
1750 free(reply);
1751 return (PAM_BUF_ERR);
1752 }
1753
1754 /*
1755 * Utilities Functions
1756 */
1757
1758 /*
1759 * int attrlist_add(attrlist **l, attrtype type, char *val)
1760 * add an item, with type "type" and value "val", at the tail of list l.
1761 * This functions exits the application on OutOfMem error.
1762 */
1763 void
1764 attrlist_add(attrlist **l, attrtype type, char *val)
1765 {
1766 attrlist **w;
1767
1768 /* tail insert */
1769 for (w = l; *w != NULL; w = &(*w)->next)
1770 ;
1771
1772 if ((*w = malloc(sizeof (**w))) == NULL)
1773 passwd_exit(NOMEM);
1774
1837 void
1838 rusage(void)
1839 {
1840
1841 #define MSG(a) (void) fprintf(stderr, gettext((a)));
1842
1843 MSG("usage:\n");
1844 MSG("\tpasswd [-r files | -r nis | -r ldap] [name]\n");
1845 MSG("\tpasswd [-r files] [-egh] [name]\n");
1846 MSG("\tpasswd [-r files] -sa\n");
1847 MSG("\tpasswd [-r files] -s [name]\n");
1848 MSG("\tpasswd [-r files] [-d|-l|-N|-u] [-f] [-n min] [-w warn] "
1849 "[-x max] name\n");
1850 MSG("\tpasswd -r nis [-eg] [name]\n");
1851 MSG("\t\t[-x max] name\n");
1852 MSG("\tpasswd -r ldap [-egh] [name]\n");
1853 MSG("\tpasswd -r ldap -sa\n");
1854 MSG("\tpasswd -r ldap -s [name]\n");
1855 MSG("\tpasswd -r ldap [-l|-N|-u] [-f] [-n min] [-w warn] "
1856 "[-x max] name\n");
1857 MSG("\tpasswd -S [name]\n");
1858 #undef MSG
1859 }
|