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