1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/types.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <sys/stat.h>
31 #include <pwd.h>
32 #include <shadow.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <nss_dbdefs.h>
38 #include <macros.h>
39 #include <syslog.h>
40
41 #include <limits.h> /* LOGNAME_MAX -- max Solaris user name */
42
43 #include "passwdutil.h"
44
45 int files_lock(void);
46 int files_unlock(void);
47 int files_checkhistory(char *user, char *passwd, pwu_repository_t *rep);
48 int files_getattr(char *name, attrlist *item, pwu_repository_t *rep);
49 int files_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
50 void **buf);
51 int files_update(attrlist *items, pwu_repository_t *rep, void *buf);
52 int files_putpwnam(char *name, char *oldpw, pwu_repository_t *rep, void *buf);
53 int files_user_to_authenticate(char *name, pwu_repository_t *rep,
54 char **auth_user, int *privileged);
55
56 static int files_update_history(char *name, struct spwd *spwd);
57
58 /*
59 * files function pointer table, used by passwdutil_init to initialize
60 * the global Repository-OPerations table "rops"
61 */
62 struct repops files_repops = {
63 files_checkhistory,
64 files_getattr,
65 files_getpwnam,
66 files_update,
67 files_putpwnam,
68 files_user_to_authenticate,
69 files_lock,
70 files_unlock
71 };
72
73 /*
74 * this structure defines the buffer used to keep state between
75 * get/update/put calls
76 */
77 struct pwbuf {
78 int update_history;
79 struct passwd *pwd;
80 char *pwd_scratch;
81 struct spwd *spwd;
82 char *spwd_scratch;
83 char *new_sp_pwdp;
84 };
85
86 /*
87 * We should use sysconf, but there is no sysconf name for SHADOW
88 * so we use these from nss_dbdefs
89 */
90 #define PWD_SCRATCH_SIZE NSS_LINELEN_PASSWD
91 #define SPW_SCRATCH_SIZE NSS_LINELEN_SHADOW
92
93 /*
94 * lock functions for files repository
95 */
96 int
files_lock(void)97 files_lock(void)
98 {
99 int res;
100
101 if (lckpwdf()) {
102 switch (errno) {
103 case EINTR:
104 res = PWU_BUSY;
105 break;
106 case EACCES:
107 res = PWU_DENIED;
108 break;
109 case 0:
110 res = PWU_SUCCESS;
111 break;
112 }
113 } else
114 res = PWU_SUCCESS;
115
116 return (res);
117 }
118
119 int
files_unlock(void)120 files_unlock(void)
121 {
122 if (ulckpwdf())
123 return (PWU_SYSTEM_ERROR);
124
125 return (PWU_SUCCESS);
126 }
127
128 /*
129 * files_privileged
130 *
131 * Are we a privileged user with regard to the files repository?
132 */
133 int
files_privileged(void)134 files_privileged(void)
135 {
136 return (getuid() == 0);
137 }
138
139 /*
140 *
141 * private_getpwnam_r()
142 *
143 * A private implementation of getpwnam_r which does *not* fall back to
144 * other services possibly defined in nsswitch.conf
145 *
146 * behaves like getpwnam_r().
147 */
148 struct passwd *
private_getpwnam_r(const char * name,struct passwd * result,char * buffer,int buflen)149 private_getpwnam_r(const char *name, struct passwd *result, char *buffer,
150 int buflen)
151 {
152 FILE *fp;
153 int found;
154
155 if ((fp = fopen(PASSWD, "rF")) == NULL)
156 return (NULL);
157
158 found = 0;
159 while (!found && fgetpwent_r(fp, result, buffer, buflen) != NULL) {
160 if (strcmp(name, result->pw_name) == 0)
161 found = 1;
162 }
163
164 (void) fclose(fp);
165
166 if (!found) {
167 (void) memset(buffer, 0, buflen);
168 (void) memset(result, 0, sizeof (*result));
169 return (NULL);
170 }
171
172 return (result);
173 }
174
175 /*
176 * private_getspnam_r()
177 *
178 * A private implementation of getspnam_r which does *not* fall back to
179 * other services possibly defined in nsswitch.conf.
180 *
181 * Behaves like getspnam_r(). Since we use fgetspent_t(), all numeric
182 * fields that are undefined in /etc/shadow will be set to -1.
183 *
184 */
185 struct spwd *
private_getspnam_r(const char * name,struct spwd * result,char * buffer,int buflen)186 private_getspnam_r(const char *name, struct spwd *result, char *buffer,
187 int buflen)
188 {
189 FILE *fp;
190 int found;
191
192 fp = fopen(SHADOW, "rF");
193 if (fp == NULL)
194 return (NULL);
195
196 found = 0;
197 while (!found && fgetspent_r(fp, result, buffer, buflen) != NULL) {
198 if (strcmp(name, result->sp_namp) == 0)
199 found = 1;
200 }
201
202 (void) fclose(fp);
203
204 if (!found) {
205 (void) memset(buffer, 0, buflen);
206 (void) memset(result, 0, sizeof (*result));
207 return (NULL);
208 }
209 return (result);
210 }
211
212 /*
213 * files_getpwnam(name, items, rep, buf)
214 *
215 */
216 /*ARGSUSED*/
217 int
files_getpwnam(char * name,attrlist * items,pwu_repository_t * rep,void ** buf)218 files_getpwnam(char *name, attrlist *items, pwu_repository_t *rep, void **buf)
219 {
220 attrlist *p;
221 struct pwbuf *pwbuf;
222 int err = PWU_SUCCESS;
223
224 *buf = calloc(1, sizeof (struct pwbuf));
225 pwbuf = (struct pwbuf *)*buf;
226 if (pwbuf == NULL)
227 return (PWU_NOMEM);
228
229 /*
230 * determine which password structure (/etc/passwd or /etc/shadow)
231 * we need for the items we need to update
232 */
233 for (p = items; p != NULL; p = p->next) {
234 switch (p->type) {
235 case ATTR_NAME:
236 case ATTR_UID:
237 case ATTR_GID:
238 case ATTR_AGE:
239 case ATTR_COMMENT:
240 case ATTR_GECOS:
241 case ATTR_HOMEDIR:
242 case ATTR_SHELL:
243 if (pwbuf->pwd == NULL) {
244 pwbuf->pwd = malloc(sizeof (struct passwd));
245 if (pwbuf->pwd == NULL) {
246 err = PWU_NOMEM;
247 goto error;
248 }
249 }
250 break;
251 case ATTR_PASSWD:
252 case ATTR_PASSWD_SERVER_POLICY:
253 case ATTR_LSTCHG:
254 case ATTR_MIN:
255 case ATTR_MAX:
256 case ATTR_WARN:
257 case ATTR_INACT:
258 case ATTR_EXPIRE:
259 case ATTR_FLAG:
260 case ATTR_LOCK_ACCOUNT:
261 case ATTR_EXPIRE_PASSWORD:
262 case ATTR_FAILED_LOGINS:
263 case ATTR_INCR_FAILED_LOGINS:
264 case ATTR_RST_FAILED_LOGINS:
265 case ATTR_NOLOGIN_ACCOUNT:
266 case ATTR_UNLOCK_ACCOUNT:
267 if (pwbuf->spwd == NULL) {
268 pwbuf->spwd = malloc(sizeof (struct spwd));
269 if (pwbuf->spwd == NULL) {
270 err = PWU_NOMEM;
271 goto error;
272 }
273 }
274 break;
275 default:
276 /*
277 * Some other repository might have different values
278 * so we ignore those.
279 */
280 break;
281 }
282 }
283
284 if (pwbuf->pwd) {
285 if ((pwbuf->pwd_scratch = malloc(PWD_SCRATCH_SIZE)) == NULL) {
286 err = PWU_NOMEM;
287 goto error;
288 }
289 if (private_getpwnam_r(name, pwbuf->pwd, pwbuf->pwd_scratch,
290 PWD_SCRATCH_SIZE) == NULL) {
291 err = PWU_NOT_FOUND;
292 goto error;
293 }
294 }
295
296 if (pwbuf->spwd) {
297 if ((pwbuf->spwd_scratch = malloc(SPW_SCRATCH_SIZE)) == NULL) {
298 err = PWU_NOMEM;
299 goto error;
300 }
301 if (private_getspnam_r(name, pwbuf->spwd, pwbuf->spwd_scratch,
302 SPW_SCRATCH_SIZE) == NULL) {
303 err = PWU_NOT_FOUND;
304 goto error;
305 }
306 }
307
308 return (PWU_SUCCESS);
309 error:
310 if (pwbuf->pwd) free(pwbuf->pwd);
311 if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch);
312 if (pwbuf->spwd) free(pwbuf->spwd);
313 if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch);
314 free(pwbuf);
315 *buf = NULL;
316
317 return (err);
318 }
319
320 /*
321 * int files_user_to_authenticate(name, rep, auth_user, privileged)
322 * Determine which user needs to be authenticated. For files, the
323 * possible return values are:
324 * PWU_NOT_FOUND
325 * PWU_SUCCESS and (auth_user == NULL || auth_user = user)
326 * PWU_DENIED
327 * PWU_NOMEM
328 */
329 /*ARGSUSED*/
330 int
files_user_to_authenticate(char * user,pwu_repository_t * rep,char ** auth_user,int * privileged)331 files_user_to_authenticate(char *user, pwu_repository_t *rep,
332 char **auth_user, int *privileged)
333 {
334 struct pwbuf *pwbuf;
335 int res;
336 attrlist attr_tmp[1] = { { ATTR_UID, NULL, NULL } };
337
338 /* check to see if target user is present in files */
339 res = files_getpwnam(user, &attr_tmp[0], rep, (void **)&pwbuf);
340 if (res != PWU_SUCCESS)
341 return (res);
342
343 if (files_privileged()) {
344 *auth_user = NULL;
345 *privileged = 1;
346 res = PWU_SUCCESS;
347 } else {
348 *privileged = 0;
349 if (getuid() == pwbuf->pwd->pw_uid) {
350 if ((*auth_user = strdup(user)) == NULL) {
351 res = PWU_NOMEM;
352 } else {
353 res = PWU_SUCCESS;
354 }
355 } else {
356 res = PWU_DENIED;
357 }
358 }
359
360 if (pwbuf->pwd) free(pwbuf->pwd);
361 if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch);
362 if (pwbuf->spwd) free(pwbuf->spwd);
363 if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch);
364 free(pwbuf);
365
366 return (res);
367 }
368
369 /*
370 * Password history file format:
371 * user:crypw1: ... crypwn: such that n <= MAXHISTORY
372 */
373 #define HISTORY "/etc/security/passhistory"
374 #define HISTEMP "/etc/security/pwhistemp"
375 #define OHISTORY "/etc/security/opwhistory"
376 #define HISTMODE S_IRUSR /* mode to create history file */
377 /*
378 * XXX
379 * 3*LOGNAME_MAX just in case there are long user names.
380 * Traditionally Solaris LOGNAME_MAX (_POSIX_LOGIN_NAME_MAX) is 13,
381 * but some sites often user more.
382 * If LOGNAME_MAX ever becomes reasonable (128) and actually enforced,
383 * fix up here.
384 * XXX
385 */
386 #define MAX_LOGNAME (3 * LOGNAME_MAX)
387
388 /*
389 * files_checkhistory - check if a user's new password is in the user's
390 * old password history.
391 *
392 * Entry
393 * user = username.
394 * passwd = new clear text password.
395 *
396 * Exit
397 * PWU_SUCCESS, passwd found in user's old password history.
398 * The caller should only be interested and fail if
399 * PWU_SUCCESS is returned.
400 * PWU_NOT_FOUND, passwd not in user's old password history.
401 * PWU_errors, PWU_ errors from other routines.
402 *
403 */
404 int
files_checkhistory(char * user,char * passwd,pwu_repository_t * rep)405 files_checkhistory(char *user, char *passwd, pwu_repository_t *rep)
406 {
407 attrlist attr;
408 int res;
409
410 attr.type = ATTR_HISTORY;
411 attr.data.val_s = NULL;
412 attr.next = NULL;
413
414 debug("files_checkhistory(user=%s)", user);
415
416 /*
417 * XXX
418 * This depends on the underlying files_getattr implementation
419 * treating user not found in backing store or no history as
420 * an error.
421 * XXX
422 */
423
424 if ((res = files_getattr(user, &attr, rep)) == PWU_SUCCESS) {
425 char *s;
426 char *crypt_passwd;
427 int histsize;
428 char *last = attr.data.val_s;
429
430 if ((histsize = def_getint("HISTORY=", DEFHISTORY)) == 0) {
431 debug("files_checkhistory: no history requested");
432 res = PWU_NOT_FOUND;
433 goto out;
434 }
435
436 debug("files_checkhistory: histsize = %d", histsize);
437 if (histsize > MAXHISTORY)
438 histsize = MAXHISTORY;
439
440 debug("line to test\n\t%s", last);
441
442 /* compare crypt_passwd to attr.data.val_s strings. */
443 res = PWU_NOT_FOUND;
444 while ((histsize-- > 0) &&
445 (((s = strtok_r(NULL, ":", &last)) != NULL) &&
446 (*s != '\n'))) {
447
448 crypt_passwd = crypt(passwd, s);
449 debug("files_checkhistory: user_pw=%s, history_pw=%s",
450 crypt_passwd, s);
451 if (strcmp(crypt_passwd, s) == 0) {
452 res = PWU_SUCCESS;
453 break;
454 }
455 }
456 debug("files_checkhistory(%s, %s) = %d", user, crypt_passwd,
457 res);
458 }
459 out:
460 if (attr.data.val_s != NULL)
461 free(attr.data.val_s);
462
463 return (res);
464 }
465
466 /*
467 * files_getattr(name, items, rep)
468 *
469 * Get attributes specified in list 'items'
470 */
471 int
files_getattr(char * name,attrlist * items,pwu_repository_t * rep)472 files_getattr(char *name, attrlist *items, pwu_repository_t *rep)
473 {
474 struct pwbuf *pwbuf;
475 struct passwd *pw;
476 struct spwd *spw;
477 attrlist *w;
478 int res;
479
480 res = files_getpwnam(name, items, rep, (void **)&pwbuf);
481 if (res != PWU_SUCCESS)
482 return (res);
483
484 pw = pwbuf->pwd;
485 spw = pwbuf->spwd;
486
487 for (w = items; res == PWU_SUCCESS && w != NULL; w = w->next) {
488 switch (w->type) {
489 case ATTR_NAME:
490 if ((w->data.val_s = strdup(pw->pw_name)) == NULL)
491 res = PWU_NOMEM;
492 break;
493 case ATTR_COMMENT:
494 if ((w->data.val_s = strdup(pw->pw_comment)) == NULL)
495 res = PWU_NOMEM;
496 break;
497 case ATTR_GECOS:
498 if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL)
499 res = PWU_NOMEM;
500 break;
501 case ATTR_HOMEDIR:
502 if ((w->data.val_s = strdup(pw->pw_dir)) == NULL)
503 res = PWU_NOMEM;
504 break;
505 case ATTR_SHELL:
506 if ((w->data.val_s = strdup(pw->pw_shell)) == NULL)
507 res = PWU_NOMEM;
508 break;
509 /*
510 * Nothing special needs to be done for
511 * server policy
512 */
513 case ATTR_PASSWD:
514 case ATTR_PASSWD_SERVER_POLICY:
515 if ((w->data.val_s = strdup(spw->sp_pwdp)) == NULL)
516 res = PWU_NOMEM;
517 break;
518 case ATTR_AGE:
519 if ((w->data.val_s = strdup(pw->pw_age)) == NULL)
520 res = PWU_NOMEM;
521 break;
522 case ATTR_REP_NAME:
523 if ((w->data.val_s = strdup("files")) == NULL)
524 res = PWU_NOMEM;
525 break;
526 case ATTR_HISTORY: {
527 FILE *history;
528 char buf[MAX_LOGNAME + MAXHISTORY +
529 (MAXHISTORY * CRYPT_MAXCIPHERTEXTLEN)+1];
530 char *s, *s1;
531
532 debug("files_getattr: Get password history for %s ",
533 name);
534
535 if ((history = fopen(HISTORY, "rF")) == NULL) {
536 debug("files_getattr: %s not found", HISTORY);
537 res = PWU_OPEN_FAILED;
538 goto getattr_exit;
539 }
540 res = PWU_NOT_FOUND;
541 while ((s = fgets(buf, sizeof (buf), history)) !=
542 NULL) {
543 s1 = strchr(s, ':');
544 if (s1 != NULL) {
545 *s1 = '\0';
546 } else {
547 res = PWU_NOT_FOUND;
548 break;
549 }
550 #ifdef DEBUG
551 debug("got history line for %s", s);
552 #endif /* DEBUG */
553 if (strcmp(s, name) == 0) {
554 /* found user */
555 if ((items->data.val_s =
556 strdup(s1+1)) == NULL)
557 res = PWU_NOMEM;
558 else
559 res = PWU_SUCCESS;
560 break;
561 }
562 }
563 (void) fclose(history);
564 break;
565 }
566
567 /* integer values */
568 case ATTR_UID:
569 w->data.val_i = pw->pw_uid;
570 break;
571 case ATTR_GID:
572 w->data.val_i = pw->pw_gid;
573 break;
574 case ATTR_LSTCHG:
575 w->data.val_i = spw->sp_lstchg;
576 break;
577 case ATTR_MIN:
578 w->data.val_i = spw->sp_min;
579 break;
580 case ATTR_MAX:
581 w->data.val_i = spw->sp_max;
582 break;
583 case ATTR_WARN:
584 w->data.val_i = spw->sp_warn;
585 break;
586 case ATTR_INACT:
587 w->data.val_i = spw->sp_inact;
588 break;
589 case ATTR_EXPIRE:
590 w->data.val_i = spw->sp_expire;
591 break;
592 case ATTR_FLAG:
593 w->data.val_i = spw->sp_flag;
594 break;
595 case ATTR_FAILED_LOGINS:
596 w->data.val_i = spw->sp_flag & FAILCOUNT_MASK;
597 break;
598 default:
599 break;
600 }
601 }
602
603 getattr_exit:
604 if (pwbuf->pwd) free(pwbuf->pwd);
605 if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch);
606 if (pwbuf->spwd) free(pwbuf->spwd);
607 if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch);
608 free(pwbuf);
609
610 return (res);
611 }
612
613 /*
614 * max_present(list)
615 *
616 * see if attribute ATTR_MAX, with value != -1, is present in
617 * attribute-list "list".
618 *
619 * returns 1 if present, 0 otherwise.
620 */
621 static int
max_present(attrlist * list)622 max_present(attrlist *list)
623 {
624 while (list != NULL)
625 if (list->type == ATTR_MAX && list->data.val_i != -1)
626 return (1);
627 else
628 list = list->next;
629
630 return (0);
631 }
632
633 /*
634 * files_update(items, rep, buf)
635 *
636 * update the information in buf with the attributes specified in
637 * items.
638 */
639 /*ARGSUSED*/
640 int
files_update(attrlist * items,pwu_repository_t * rep,void * buf)641 files_update(attrlist *items, pwu_repository_t *rep, void *buf)
642 {
643 struct pwbuf *pwbuf = (struct pwbuf *)buf;
644 struct passwd *pw;
645 struct spwd *spw;
646 attrlist *p;
647 int aging_needed = 0;
648 int aging_set = 0;
649 int disable_aging;
650 char *pword;
651 int len;
652
653 pw = pwbuf->pwd;
654 spw = pwbuf->spwd;
655 pwbuf->update_history = 0;
656
657 /*
658 * if sp_max==0 : disable passwd aging after updating the password
659 */
660 disable_aging = (spw != NULL && spw->sp_max == 0);
661
662 for (p = items; p != NULL; p = p->next) {
663 switch (p->type) {
664 case ATTR_NAME:
665 break; /* We are able to handle this, but... */
666 case ATTR_UID:
667 pw->pw_uid = (uid_t)p->data.val_i;
668 break;
669 case ATTR_GID:
670 pw->pw_gid = (gid_t)p->data.val_i;
671 break;
672 case ATTR_AGE:
673 pw->pw_age = p->data.val_s;
674 break;
675 case ATTR_COMMENT:
676 pw->pw_comment = p->data.val_s;
677 break;
678 case ATTR_GECOS:
679 pw->pw_gecos = p->data.val_s;
680 break;
681 case ATTR_HOMEDIR:
682 pw->pw_dir = p->data.val_s;
683 break;
684 case ATTR_SHELL:
685 pw->pw_shell = p->data.val_s;
686 break;
687
688 /*
689 * Nothing special needs to be done for
690 * server policy
691 */
692 case ATTR_PASSWD:
693 case ATTR_PASSWD_SERVER_POLICY:
694 /*
695 * There is a special case only for files: if the
696 * password is to be deleted (-d to passwd),
697 * p->data.val_s will be NULL.
698 */
699 if (p->data.val_s == NULL) {
700 spw->sp_pwdp = "";
701 } else {
702 char *salt = NULL;
703 char *hash = NULL;
704
705 salt = crypt_gensalt(spw->sp_pwdp, pw);
706
707 if (salt == NULL) {
708 if (errno == ENOMEM)
709 return (PWU_NOMEM);
710 /* algorithm problem? */
711 syslog(LOG_AUTH | LOG_ALERT,
712 "passwdutil: crypt_gensalt %m");
713 return (PWU_UPDATE_FAILED);
714 }
715 hash = crypt(p->data.val_s, salt);
716 free(salt);
717 if (hash == NULL) {
718 errno = ENOMEM;
719 return (PWU_NOMEM);
720 }
721 pword = strdup(hash);
722 if (pword == NULL) {
723 errno = ENOMEM;
724 return (PWU_NOMEM);
725 }
726
727 if (pwbuf->new_sp_pwdp)
728 free(pwbuf->new_sp_pwdp);
729 pwbuf->new_sp_pwdp = pword;
730 spw->sp_pwdp = pword;
731 aging_needed = 1;
732 pwbuf->update_history = 1;
733 }
734 spw->sp_flag &= ~FAILCOUNT_MASK; /* reset count */
735 spw->sp_lstchg = DAY_NOW_32;
736 break;
737 case ATTR_LOCK_ACCOUNT:
738 if (spw->sp_pwdp == NULL) {
739 spw->sp_pwdp = LOCKSTRING;
740 } else if ((strncmp(spw->sp_pwdp, LOCKSTRING,
741 sizeof (LOCKSTRING)-1) != 0) &&
742 (strcmp(spw->sp_pwdp, NOLOGINSTRING) != 0)) {
743 len = sizeof (LOCKSTRING)-1 +
744 strlen(spw->sp_pwdp) + 1;
745 pword = malloc(len);
746 if (pword == NULL) {
747 errno = ENOMEM;
748 return (PWU_NOMEM);
749 }
750 (void) strlcpy(pword, LOCKSTRING, len);
751 (void) strlcat(pword, spw->sp_pwdp, len);
752 if (pwbuf->new_sp_pwdp)
753 free(pwbuf->new_sp_pwdp);
754 pwbuf->new_sp_pwdp = pword;
755 spw->sp_pwdp = pword;
756 }
757 spw->sp_lstchg = DAY_NOW_32;
758 break;
759 case ATTR_UNLOCK_ACCOUNT:
760 if (spw->sp_pwdp != NULL &&
761 strncmp(spw->sp_pwdp, LOCKSTRING,
762 sizeof (LOCKSTRING)-1) == 0) {
763 (void) strcpy(spw->sp_pwdp, spw->sp_pwdp +
764 sizeof (LOCKSTRING)-1);
765 }
766 spw->sp_lstchg = DAY_NOW_32;
767 break;
768 case ATTR_NOLOGIN_ACCOUNT:
769 spw->sp_pwdp = NOLOGINSTRING;
770 if (pwbuf->new_sp_pwdp) {
771 free(pwbuf->new_sp_pwdp);
772 pwbuf->new_sp_pwdp = NULL;
773 }
774 spw->sp_lstchg = DAY_NOW_32;
775 break;
776 case ATTR_EXPIRE_PASSWORD:
777 spw->sp_lstchg = 0;
778 break;
779 case ATTR_LSTCHG:
780 spw->sp_lstchg = p->data.val_i;
781 break;
782 case ATTR_MIN:
783 if (spw->sp_max == -1 &&
784 p->data.val_i != -1 && max_present(p->next) == 0)
785 return (PWU_AGING_DISABLED);
786 spw->sp_min = p->data.val_i;
787 aging_set = 1;
788 break;
789 case ATTR_MAX:
790 if (p->data.val_i == -1) {
791 /* Turn aging off -> Reset min and warn too */
792
793 spw->sp_min = -1;
794 spw->sp_warn = -1;
795 } else {
796 /* Turn aging on */
797
798 if (spw->sp_min == -1) {
799 /*
800 * If minage has not been set with
801 * a command-line option, we set it
802 * to zero.
803 */
804 spw->sp_min = 0;
805 }
806
807 /*
808 * If aging was turned off, we update lstchg.
809 *
810 * We take care not to update lstchg if the
811 * user has no password, otherwise the user
812 * might not be required to provide a password
813 * the next time [s]he logs-in.
814 *
815 * Also, if lstchg != -1 (i.e., not set in
816 * /etc/shadow), we keep the old value.
817 */
818 if (spw->sp_max == -1 &&
819 spw->sp_pwdp != NULL && *spw->sp_pwdp &&
820 spw->sp_lstchg == -1) {
821 spw->sp_lstchg = DAY_NOW_32;
822 }
823 }
824
825 spw->sp_max = p->data.val_i;
826
827 aging_set = 1;
828
829 break;
830 case ATTR_WARN:
831 if (spw->sp_max == -1 && p->data.val_i != -1 &&
832 max_present(p->next) == 0)
833 return (PWU_AGING_DISABLED);
834 spw->sp_warn = p->data.val_i;
835 break;
836 case ATTR_INACT:
837 spw->sp_inact = p->data.val_i;
838 break;
839 case ATTR_EXPIRE:
840 spw->sp_expire = p->data.val_i;
841 break;
842 case ATTR_FLAG:
843 spw->sp_flag = p->data.val_i;
844 break;
845 case ATTR_INCR_FAILED_LOGINS:
846 {
847 int count = (spw->sp_flag & FAILCOUNT_MASK) + 1;
848 spw->sp_flag &= ~FAILCOUNT_MASK;
849 spw->sp_flag |= min(FAILCOUNT_MASK, count);
850 p->data.val_i = count;
851 }
852 break;
853 case ATTR_RST_FAILED_LOGINS:
854 p->data.val_i = spw->sp_flag & FAILCOUNT_MASK;
855 spw->sp_flag &= ~FAILCOUNT_MASK;
856 break;
857 default:
858 break;
859 }
860 }
861
862 /*
863 * What should the new aging values look like?
864 *
865 * There are a number of different conditions
866 *
867 * a) aging is already configured: don't touch it
868 *
869 * b) disable_aging is set: disable aging
870 *
871 * c) aging is not configured: turn on default aging;
872 *
873 * b) and c) of course only if aging_needed and !aging_set.
874 * (i.e., password changed, and aging values not changed)
875 */
876
877 if (spw != NULL && spw->sp_max <= 0) {
878 /* a) aging not yet configured */
879 if (aging_needed && !aging_set) {
880 if (disable_aging) {
881 /* b) turn off aging */
882 spw->sp_min = spw->sp_max = spw->sp_warn = -1;
883 } else {
884 /* c) */
885 turn_on_default_aging(spw);
886 }
887 }
888 }
889
890 return (PWU_SUCCESS);
891 }
892
893 /*
894 * files_update_shadow(char *name, struct spwd *spwd)
895 *
896 * update the shadow password file SHADOW to contain the spwd structure
897 * "spwd" for user "name"
898 */
899 int
files_update_shadow(char * name,struct spwd * spwd)900 files_update_shadow(char *name, struct spwd *spwd)
901 {
902 struct stat64 stbuf;
903 FILE *dst;
904 FILE *src;
905 struct spwd cur;
906 char buf[SPW_SCRATCH_SIZE];
907 int tempfd;
908 mode_t filemode;
909 int result = -1;
910 int err = PWU_SUCCESS;
911
912 /* Mode of the shadow file should be 400 or 000 */
913 if (stat64(SHADOW, &stbuf) < 0) {
914 err = PWU_STAT_FAILED;
915 goto shadow_exit;
916 }
917
918 /* copy mode from current shadow file (0400 or 0000) */
919 filemode = stbuf.st_mode & S_IRUSR;
920
921 /*
922 * we can't specify filemodes to fopen(), and we SHOULD NOT
923 * set umask in multi-thread safe libraries, so we use
924 * a combination of open() and fdopen()
925 */
926 tempfd = open(SHADTEMP, O_WRONLY|O_CREAT|O_TRUNC, filemode);
927 if (tempfd < 0) {
928 err = PWU_OPEN_FAILED;
929 goto shadow_exit;
930 }
931 (void) fchown(tempfd, (uid_t)0, stbuf.st_gid);
932
933 if ((dst = fdopen(tempfd, "wF")) == NULL) {
934 err = PWU_OPEN_FAILED;
935 goto shadow_exit;
936 }
937
938 if ((src = fopen(SHADOW, "rF")) == NULL) {
939 err = PWU_OPEN_FAILED;
940 (void) fclose(dst);
941 (void) unlink(SHADTEMP);
942 goto shadow_exit;
943 }
944
945 /*
946 * copy old shadow to temporary file while replacing the entry
947 * that matches "name".
948 */
949 while (fgetspent_r(src, &cur, buf, sizeof (buf)) != NULL) {
950
951 if (strcmp(cur.sp_namp, name) == 0)
952 result = putspent(spwd, dst);
953 else
954 result = putspent(&cur, dst);
955
956 if (result != 0) {
957 err = PWU_WRITE_FAILED;
958 (void) fclose(src);
959 (void) fclose(dst);
960 goto shadow_exit;
961 }
962 }
963
964 (void) fclose(src);
965
966 if (fclose(dst) != 0) {
967 /*
968 * Something went wrong (ENOSPC for example). Don't
969 * use the resulting temporary file!
970 */
971 err = PWU_CLOSE_FAILED;
972 (void) unlink(SHADTEMP);
973 goto shadow_exit;
974 }
975
976 /*
977 * Rename stmp to shadow:
978 * 1. make sure /etc/oshadow is gone
979 * 2. ln /etc/shadow /etc/oshadow
980 * 3. mv /etc/stmp /etc/shadow
981 */
982 if (unlink(OSHADOW) && access(OSHADOW, 0) == 0) {
983 err = PWU_UPDATE_FAILED;
984 (void) unlink(SHADTEMP);
985 goto shadow_exit;
986 }
987
988 if (link(SHADOW, OSHADOW) == -1) {
989 err = PWU_UPDATE_FAILED;
990 (void) unlink(SHADTEMP);
991 goto shadow_exit;
992 }
993
994 if (rename(SHADTEMP, SHADOW) == -1) {
995 err = PWU_UPDATE_FAILED;
996 (void) unlink(SHADTEMP);
997 goto shadow_exit;
998 }
999 (void) unlink(OSHADOW);
1000
1001 shadow_exit:
1002 return (err);
1003 }
1004
1005 int
files_update_passwd(char * name,struct passwd * pwd)1006 files_update_passwd(char *name, struct passwd *pwd)
1007 {
1008 struct stat64 stbuf;
1009 FILE *src, *dst;
1010 int tempfd;
1011 struct passwd cur;
1012 char buf[PWD_SCRATCH_SIZE];
1013 int result;
1014 int err = PWU_SUCCESS;
1015
1016 if (stat64(PASSWD, &stbuf) < 0) {
1017 err = PWU_STAT_FAILED;
1018 goto passwd_exit;
1019 }
1020
1021 /* see files_update_shadow() for open()+fdopen() rationale */
1022
1023 if ((tempfd = open(PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
1024 err = PWU_OPEN_FAILED;
1025 goto passwd_exit;
1026 }
1027 if ((dst = fdopen(tempfd, "wF")) == NULL) {
1028 err = PWU_OPEN_FAILED;
1029 goto passwd_exit;
1030 }
1031 if ((src = fopen(PASSWD, "rF")) == NULL) {
1032 err = PWU_OPEN_FAILED;
1033 (void) fclose(dst);
1034 (void) unlink(PASSTEMP);
1035 goto passwd_exit;
1036 }
1037
1038 /*
1039 * copy old password entries to temporary file while replacing
1040 * the entry that matches "name"
1041 */
1042 while (fgetpwent_r(src, &cur, buf, sizeof (buf)) != NULL) {
1043 if (strcmp(cur.pw_name, name) == 0)
1044 result = putpwent(pwd, dst);
1045 else
1046 result = putpwent(&cur, dst);
1047 if (result != 0) {
1048 err = PWU_WRITE_FAILED;
1049 (void) fclose(src);
1050 (void) fclose(dst);
1051 goto passwd_exit;
1052 }
1053 }
1054
1055 (void) fclose(src);
1056 if (fclose(dst) != 0) {
1057 err = PWU_CLOSE_FAILED;
1058 goto passwd_exit; /* Don't trust the temporary file */
1059 }
1060
1061 /* Rename temp to passwd */
1062 if (unlink(OPASSWD) && access(OPASSWD, 0) == 0) {
1063 err = PWU_UPDATE_FAILED;
1064 (void) unlink(PASSTEMP);
1065 goto passwd_exit;
1066 }
1067
1068 if (link(PASSWD, OPASSWD) == -1) {
1069 err = PWU_UPDATE_FAILED;
1070 (void) unlink(PASSTEMP);
1071 goto passwd_exit;
1072 }
1073
1074 if (rename(PASSTEMP, PASSWD) == -1) {
1075 err = PWU_UPDATE_FAILED;
1076 (void) unlink(PASSTEMP);
1077 goto passwd_exit;
1078 }
1079
1080 (void) chmod(PASSWD, 0644);
1081
1082 passwd_exit:
1083 return (err);
1084
1085 }
1086
1087 /*
1088 * files_putpwnam(name, oldpw, rep, buf)
1089 *
1090 * store the password attributes contained in "buf" in /etc/passwd and
1091 * /etc/shadow.
1092 */
1093 /*ARGSUSED*/
1094 int
files_putpwnam(char * name,char * oldpw,pwu_repository_t * rep,void * buf)1095 files_putpwnam(char *name, char *oldpw, pwu_repository_t *rep, void *buf)
1096 {
1097 struct pwbuf *pwbuf = (struct pwbuf *)buf;
1098 int result = PWU_SUCCESS;
1099
1100 if (pwbuf->pwd) {
1101 result = files_update_passwd(name, pwbuf->pwd);
1102 }
1103
1104 if (result == PWU_SUCCESS && pwbuf->spwd) {
1105 if (pwbuf->update_history != 0) {
1106 debug("update_history = %d", pwbuf->update_history);
1107 result = files_update_history(name, pwbuf->spwd);
1108 } else {
1109 debug("no password change");
1110 }
1111 if (result == PWU_SUCCESS) {
1112 result = files_update_shadow(name, pwbuf->spwd);
1113 }
1114 }
1115
1116 if (pwbuf->pwd) {
1117 (void) memset(pwbuf->pwd, 0, sizeof (struct passwd));
1118 (void) memset(pwbuf->pwd_scratch, 0, PWD_SCRATCH_SIZE);
1119 free(pwbuf->pwd);
1120 free(pwbuf->pwd_scratch);
1121 }
1122 if (pwbuf->spwd) {
1123 (void) memset(pwbuf->spwd, 0, sizeof (struct spwd));
1124 (void) memset(pwbuf->spwd_scratch, 0, SPW_SCRATCH_SIZE);
1125 free(pwbuf->spwd);
1126 free(pwbuf->spwd_scratch);
1127 }
1128 if (pwbuf->new_sp_pwdp) {
1129 free(pwbuf->new_sp_pwdp);
1130 }
1131
1132 return (result);
1133 }
1134
1135 /*
1136 * NOTE: This is all covered under the repository lock held for updating
1137 * passwd(4) and shadow(4).
1138 */
1139 int
files_update_history(char * name,struct spwd * spwd)1140 files_update_history(char *name, struct spwd *spwd)
1141 {
1142 int histsize;
1143 int tmpfd;
1144 FILE *src; /* history database file */
1145 FILE *dst; /* temp history database being updated */
1146 struct stat64 statbuf;
1147 char buf[MAX_LOGNAME + MAXHISTORY +
1148 (MAXHISTORY * CRYPT_MAXCIPHERTEXTLEN)+1];
1149 int found;
1150
1151 if ((histsize = def_getint("HISTORY=", DEFHISTORY)) == 0) {
1152 debug("files_update_history(%s) no history, unlinking", name);
1153 (void) unlink(HISTORY);
1154 return (PWU_SUCCESS); /* no history update defined */
1155 }
1156 debug("files_update_history(%s, %s) histsize = %d", name, spwd->sp_pwdp,
1157 histsize);
1158
1159 if (histsize > MAXHISTORY)
1160 histsize = MAXHISTORY;
1161 if ((tmpfd = open(HISTEMP, O_WRONLY|O_CREAT|O_TRUNC, HISTMODE)) < 0) {
1162 return (PWU_OPEN_FAILED);
1163 }
1164 (void) fchown(tmpfd, (uid_t)0, (gid_t)0);
1165
1166 /* get ready to copy */
1167 if (((src = fopen(HISTORY, "rF")) == NULL) &&
1168 (errno != ENOENT)) {
1169 (void) unlink(HISTEMP);
1170 return (PWU_OPEN_FAILED);
1171 }
1172 if ((dst = fdopen(tmpfd, "wF")) == NULL) {
1173 (void) fclose(src);
1174 (void) unlink(HISTEMP);
1175 return (PWU_OPEN_FAILED);
1176 }
1177
1178 /* Copy and update if found. Add if not found. */
1179
1180 found = 0;
1181
1182 while ((src != NULL) &&
1183 (fgets(buf, sizeof (buf), src) != NULL)) {
1184 char *user;
1185 char *last;
1186
1187 /* get username field */
1188 user = strtok_r(buf, ":", &last);
1189
1190 #ifdef DEBUG
1191 debug("files_update_history: read=\"%s\"", user);
1192 #endif /* DEBUG */
1193
1194 if (strcmp(user, name) == 0) {
1195 char *crypt;
1196 int i;
1197
1198 /* found user, update */
1199 found++;
1200 (void) fprintf(dst, "%s:%s:", name, spwd->sp_pwdp);
1201 debug("files_update_history: update user\n"
1202 "\t%s:%s:", name, spwd->sp_pwdp);
1203
1204 /* get old crypted password history */
1205 for (i = 0; i < MAXHISTORY-1; i++) {
1206 crypt = strtok_r(NULL, ":", &last);
1207 if (crypt == NULL ||
1208 *crypt == '\n') {
1209 break;
1210 }
1211 (void) fprintf(dst, "%s:", crypt);
1212 debug("\t%d = %s:", i+1, crypt);
1213 }
1214 (void) fprintf(dst, "\n");
1215 } else {
1216
1217 /* copy other users to updated file */
1218 (void) fprintf(dst, "%s:%s", user, last);
1219 #ifdef DEBUG
1220 debug("files_update_history: copy line %s",
1221 user);
1222 #endif /* DEBUG */
1223 }
1224 }
1225
1226 if (found == 0) {
1227
1228 /* user not found, add to history file */
1229 (void) fprintf(dst, "%s:%s:\n", name, spwd->sp_pwdp);
1230 debug("files_update_history: add line\n"
1231 "\t%s:%s:", name, spwd->sp_pwdp);
1232 }
1233
1234 (void) fclose(src);
1235
1236 /* If something messed up in file system, loose the update */
1237 if (fclose(dst) != 0) {
1238
1239 debug("files_update_history: update file close failed %d",
1240 errno);
1241 (void) unlink(HISTEMP);
1242 return (PWU_CLOSE_FAILED);
1243 }
1244
1245 /*
1246 * rename history to ohistory,
1247 * rename tmp to history,
1248 * unlink ohistory.
1249 */
1250
1251 (void) unlink(OHISTORY);
1252
1253 if (stat64(OHISTORY, &statbuf) == 0 ||
1254 ((src != NULL) && (link(HISTORY, OHISTORY) != 0)) ||
1255 rename(HISTEMP, HISTORY) != 0) {
1256
1257 /* old history won't go away, loose the update */
1258 debug("files_update_history: update file rename failed %d",
1259 errno);
1260 (void) unlink(HISTEMP);
1261 return (PWU_UPDATE_FAILED);
1262 }
1263
1264 (void) unlink(OHISTORY);
1265 return (PWU_SUCCESS);
1266 }
1267