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