Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / pam / pam_modules / unix / unix_update_authtok_file.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: unix_update_authtok_file.c /main/5 1996/05/09 04:35:55 drk $ */
24 /*
25  * Copyright (c) 1992-1995, by Sun Microsystems, Inc.
26  * All rights reserved.
27  */
28
29 #ident  "@(#)unix_update_authtok_file.c 1.26     95/09/11 SMI"
30
31 #include "unix_headers.h"
32
33 static int      update_spent(pam_handle_t *, char *, char **,
34                         struct passwd *, struct spwd *, int *, int, int);
35 static int      process_passwd(pam_handle_t *, char *, char *,
36                         struct passwd *, int);
37
38 /*
39  * update_authtok_file():
40  *      To update the authentication token file.
41  *
42  *      This function is called by either __set_authtoken_attr() to
43  *      update the token attributes or pam_chauthtok() to update the
44  *      authentication token.  The parameter "field" has to be specified
45  *      as "attr" if the caller wants to update token attributes, and
46  *      the attribute-value pairs to be set needs to be passed in by parameter
47  *      "data".  If the function is called to update authentication
48  *      token itself, then "field" needs to be specified as "passwd"
49  *      and the new authentication token has to be passed in by "data".
50  */
51
52 int
53 update_authtok_file(pamh, field, data, unix_pwd, privileged, nowarn)
54         pam_handle_t *pamh;
55         char *field;
56         char *data[];
57         struct passwd *unix_pwd;
58         int privileged;
59         int nowarn;
60 {
61         char *prognamep;
62         char *usrname;
63         struct stat     buf;
64         register int    found = 0;
65         FILE            *tsfp, *spfp;
66         struct spwd     unix_sp;
67         char            spbuf[1024];
68         char            messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
69         int             passwd_flag = 0; /* attrs in shadow or passwd file */
70         int             retcode;
71
72         if ((retcode = pam_get_item(pamh, PAM_SERVICE, (void **)&prognamep))
73                                                         != PAM_SUCCESS ||
74             (retcode = pam_get_item(pamh, PAM_USER, (void **)&usrname))
75                                                         != PAM_SUCCESS)
76                 return (retcode);
77
78         /*
79          * XXX:
80          * Assume effective UID already set to 0
81          * so we can update the passwd file.
82          *
83          * This will be the last update (after nis/nis+)
84          */
85
86         errno = 0;
87
88         /* lock the password file */
89         if (lckpwdf() != 0) {
90                 if (!nowarn) {
91                         sprintf(messages[0], PAM_MSG(pamh, 90,
92                         "%s%s: Password database busy. Try again later."),
93                                 prognamep, UNIX_MSG);
94                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
95                                 1, messages, NULL);
96                 }
97                 return (PAM_AUTHTOK_LOCK_BUSY);
98         }
99
100         /* Mode of the shadow file should be 400 or 000 */
101         if (stat(SHADOW, &buf) < 0) {
102                 syslog(LOG_ERR, "%s: stat of shadow file failed",
103                         prognamep);
104                 (void) ulckpwdf();
105                 return (PAM_AUTHTOK_ERR);
106         }
107
108         (void) umask(S_IAMB & ~(buf.st_mode & S_IRUSR));
109         if ((tsfp = fopen(SHADTEMP, "w")) == NULL) {
110                 if (!nowarn) {
111                         sprintf(messages[0], PAM_MSG(pamh, 91,
112                 "%s%s: Unexpected failure. Password database unchanged."),
113                                 prognamep, UNIX_MSG);
114                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
115                                 1, messages, NULL);
116                 }
117                 (void) ulckpwdf();
118                 return (PAM_AUTHTOK_ERR);
119         }
120
121         /*
122          *      copy passwd files to temps, replacing matching lines
123          *      with new password attributes.
124          */
125         if ((spfp = fopen(SHADOW, "r")) == NULL) {
126                 fclose(tsfp);
127                 goto err;
128         }
129         while (fgetspent_r(spfp, &unix_sp, spbuf, sizeof (spbuf)) != NULL) {
130                 if (strcmp(unix_sp.sp_namp, usrname) == 0) {
131                         found = 1;
132                         retcode = update_spent(pamh, field, data,
133                                 unix_pwd, &unix_sp, &passwd_flag,
134                                 privileged, nowarn);
135                         if (retcode != PAM_SUCCESS) {
136                                 fclose(tsfp);
137                                 fclose(spfp);
138                                 goto err;
139                         }
140                         if (passwd_flag) {
141                                 /* The attributes are in passwd file */
142                                 if ((retcode = process_passwd(pamh,
143                                         prognamep, usrname, unix_pwd,
144                                         nowarn)) != PAM_SUCCESS) {
145                                         fclose(tsfp);
146                                         fclose(spfp);
147                                         goto err;
148                                 }
149                         }
150                 }
151                 if (putspent(&unix_sp, tsfp) != 0) {
152                         if (!nowarn) {
153                                 sprintf(messages[0], PAM_MSG(pamh, 91,
154                 "%s%s: Unexpected failure. Password database unchanged."),
155                                         prognamep, UNIX_MSG);
156                                 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
157                                         1, messages, NULL);
158                         }
159                         fclose(tsfp);
160                         fclose(spfp);
161                         goto err;
162                 }
163                 memset(spbuf, 0, sizeof (spbuf));
164         } /* end of while */
165
166         if ((fclose(tsfp)) || (fclose(spfp))) {
167                 if (!nowarn) {
168                         sprintf(messages[0], PAM_MSG(pamh, 91,
169                 "%s%s: Unexpected failure. Password database unchanged."),
170                                 prognamep, UNIX_MSG);
171                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
172                                 1, messages, NULL);
173                 }
174                 goto err;
175         }
176
177         /* Check if user name exists */
178         if (found == 0) {
179                 if (!nowarn) {
180                         sprintf(messages[0], PAM_MSG(pamh, 91,
181                 "%s%s: Unexpected failure. Password database unchanged."),
182                                 prognamep, UNIX_MSG);
183                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
184                                 1, messages, NULL);
185                 }
186                 goto err;
187         }
188
189         /*
190          *      Rename temp file back to  appropriate passwd file.
191          */
192
193         /* remove old shadow file */
194         if (unlink(OSHADOW) && access(OSHADOW, 0) == 0) {
195                 if (!nowarn) {
196                         sprintf(messages[0], PAM_MSG(pamh, 91,
197                 "%s%s: Unexpected failure. Password database unchanged."),
198                                 prognamep, UNIX_MSG);
199                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
200                                 1, messages, NULL);
201                 }
202                 goto err;
203         }
204
205         /* rename shadow file to old shadow file */
206         if (rename(SHADOW, OSHADOW) == -1) {
207                 if (!nowarn) {
208                         sprintf(messages[0], PAM_MSG(pamh, 91,
209                 "%s%s: Unexpected failure. Password database unchanged."),
210                                 prognamep, UNIX_MSG);
211                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
212                                 1, messages, NULL);
213                 }
214                 goto err;
215         }
216
217         /* rename temparory shadow file to shadow file */
218         if (rename(SHADTEMP, SHADOW) == -1) {
219                 (void) unlink(SHADOW);
220                 if (link(OSHADOW, SHADOW)) {
221                         if (!nowarn) {
222                                 sprintf(messages[0], PAM_MSG(pamh, 92,
223                         "%s%s: Unexpected failure. Password database missing."),
224                                         prognamep, UNIX_MSG);
225                                 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
226                                         1, messages, NULL);
227                         }
228                         goto err;
229                 }
230                 if (!nowarn) {
231                         sprintf(messages[0], PAM_MSG(pamh, 91,
232                 "%s%s: Unexpected failure. Password database unchanged."),
233                                 prognamep, UNIX_MSG);
234                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
235                                 1, messages, NULL);
236                 }
237                 goto err;
238         }
239
240         (void) ulckpwdf();
241         memset(spbuf, 0, sizeof (spbuf));
242         if (strcmp(field, "passwd") == 0) {
243                 sprintf(messages[0], PAM_MSG(pamh, 93,
244                         "%s%s: passwd successfully changed for %s"),
245                         prognamep, UNIX_MSG, usrname);
246                 (void) __pam_display_msg(pamh, PAM_TEXT_INFO,
247                                 1, messages, NULL);
248         }
249         return (PAM_SUCCESS);
250 err:
251         unlink(SHADTEMP);
252         (void) ulckpwdf();
253         memset(spbuf, 0, sizeof (spbuf));
254         return (PAM_AUTHTOK_ERR);
255 }
256
257
258 /*
259  * update_spent():
260  *      To update a shadow password file entry in the Unix
261  *      authentication token file.
262  *      This function is called by update_authtok_file() to
263  *      update the token to token attributes.
264  *      The parameter "field" indicates whenther token attributes or
265  *      token itself will be changes, and the parameter "data" has
266  *      the new values for the attributes or token.
267  *
268  */
269
270 static int
271 update_spent(pamh, field, data, unix_pwd, unix_sp,
272                 passwd_flag, privileged, nowarn)
273         pam_handle_t *pamh;
274         char *field;
275         char **data;
276         struct passwd *unix_pwd;
277         struct spwd *unix_sp;
278         int *passwd_flag;
279         int privileged;
280         int nowarn;
281 {
282         char            *value;
283         char            *char_p;
284         char            *tmp_pwd_entry;
285         char            **data_p = data;
286         int             mindate;
287         int             maxdate;
288         int             warndate;
289         char            messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
290         static char     *lkstring = "*LK*";     /* lock string to lock */
291                                                 /* user's password */
292
293         if (strcmp(field, "attr") == 0) {
294                 while (*data != NULL) {
295                         /* check attribute: AUTHTOK_DEL */
296                         if ((value =
297                                 attr_match("AUTHTOK_DEL", *data))
298                                                                 != NULL) {
299                                 if (strcmp(value, "1") == 0) {
300
301                                         /* delete password */
302                                         if (unix_sp->sp_pwdp)
303                                                 memset(unix_sp->sp_pwdp, 0,
304                                                 strlen(unix_sp->sp_pwdp));
305
306                                         /*
307                                          * set "AUTHTOK_EXT" will clear
308                                          * the sp_lstchg field. We do not
309                                          * want sp_lstchg field to be set
310                                          * if one execute passwd -d -f
311                                          * name or passwd -l -f name.
312                                          */
313                                         if (attr_find("AUTHTOK_EXP",
314                                             data_p) == 0)
315                                                 unix_sp->sp_lstchg = DAY_NOW;
316                                 }
317                                 data++;
318                                 continue;
319                         }
320
321                         /* check attribute: AUTHTOK_LK */
322                         if ((value = attr_match("AUTHTOK_LK", *data))
323                                                                 != NULL) {
324                                 if (strcmp(value, "1") == 0) {
325                                         memset(unix_sp->sp_pwdp, 0,
326                                                 strlen(unix_sp->sp_pwdp));
327                                         /* lock password */
328                                         unix_sp->sp_pwdp = lkstring;
329                                         if (attr_find("AUTHTOK_EXP",
330                                             data_p) == 0)
331                                                 unix_sp->sp_lstchg = DAY_NOW;
332                                 }
333                                 data++;
334                                 continue;
335                         }
336
337                         /* check attribute: AUTHTOK_EXP */
338                         if ((value = attr_match("AUTHTOK_EXP", *data))
339                                                                 != NULL) {
340                                 if (strcmp(value, "1") == 0) {
341                                         /* expire password */
342                                         unix_sp->sp_lstchg = (long) 0;
343                                 }
344                                 data++;
345                                 continue;
346                         }
347
348                         /* check attribute: AUTHTOK_MAXAGE */
349                         if ((value = attr_match("AUTHTOK_MAXAGE", *data))
350                                                                 != NULL) {
351                                 /* set max field */
352                                 maxdate = (int)strtol(value, &char_p, 10);
353                                 if ((attr_find("AUTHTOK_MINAGE", data_p) ==
354                                     0) && unix_sp->sp_min == -1)
355                                         unix_sp->sp_min = 0;
356                                 if (maxdate == -1) {    /* turn off aging */
357                                         unix_sp->sp_min = -1;
358                                         unix_sp->sp_warn = -1;
359                                 } else if (unix_sp->sp_max == -1)
360                                         /*
361                                          * It was set to 0 before. That
362                                          * will force passwd change at the
363                                          * next login. There are several
364                                          * ways to force passwd change. I don't
365                                          * think turning on aging should imply
366                                          * that.
367                                          */
368                                         unix_sp->sp_lstchg = DAY_NOW;
369
370                                 unix_sp->sp_max = maxdate;
371                                 data++;
372                                 continue;
373                         }
374
375                         /* check attribute: AUTHTOK_MINAGE */
376                         if ((value = attr_match("AUTHTOK_MINAGE", *data))
377                                                                 != NULL) {
378                                 /* set min field */
379                                 mindate = (int)strtol(value, &char_p, 10);
380                                 if ((attr_find("AUTHTOK_MAXAGE", data_p) ==
381                                     0) &&
382                                     unix_sp->sp_max == -1 && mindate != -1) {
383                                         return (PAM_AUTHTOK_DISABLE_AGING);
384                                 }
385                                 unix_sp->sp_min = mindate;
386                                 data++;
387                                 continue;
388                         }
389
390                         /* check attribute: AUTHTOK_WARNDATE */
391                         if ((value =
392                                 attr_match
393                                 ("AUTHTOK_WARNDATE", *data)) != NULL) {
394                                 /* set warn field */
395                                 warndate = (int)strtol(value, &char_p, 10);
396                                 if (unix_sp->sp_max == -1 && warndate != -1) {
397                                         return (PAM_AUTHTOK_DISABLE_AGING);
398                                 }
399                                 unix_sp->sp_warn = warndate;
400                                 data++;
401                                 continue;
402                         }
403
404                         /* new shell */
405                         if ((value = attr_match("AUTHTOK_SHELL", *data))
406                             != NULL) {
407                                 if (unix_pwd == NULL) {
408                                         if (!nowarn) {
409                                             sprintf(messages[0],
410                                             PAM_MSG(pamh, 94,
411                                             "%s: No local passwd record"),
412                                                 UNIX_MSG);
413                                             (void) __pam_display_msg(
414                                                 pamh, PAM_ERROR_MSG,
415                                                 1, messages, NULL);
416                                         }
417                                         return (PAM_AUTHTOK_RECOVERY_ERR);
418                                 }
419                                 tmp_pwd_entry = unix_pwd->pw_shell;
420                                 unix_pwd->pw_shell =
421                                         getloginshell(pamh, unix_pwd->pw_shell,
422                                                 privileged, nowarn);
423                                 if (tmp_pwd_entry)
424                                         free(tmp_pwd_entry);
425                                 /* if NULL, shell unchanged */
426                                 if (unix_pwd->pw_shell == NULL)
427                                         return (PAM_SUCCESS);
428                                 *passwd_flag = 1;
429                                 data++;
430                                 continue;
431                         }
432
433                         /* new homedir */
434                         if ((value = attr_match("AUTHTOK_HOMEDIR", *data))
435                             != NULL) {
436                                 if (unix_pwd == NULL) {
437                                         if (!nowarn) {
438                                             sprintf(messages[0],
439                                             PAM_MSG(pamh, 94,
440                                             "%s: No local passwd record"),
441                                                 UNIX_MSG);
442                                             (void) __pam_display_msg(
443                                                 pamh, PAM_ERROR_MSG, 1,
444                                                 messages, NULL);
445                                         }
446                                         return (PAM_AUTHTOK_RECOVERY_ERR);
447                                 }
448                                 tmp_pwd_entry = unix_pwd->pw_dir;
449                                 unix_pwd->pw_dir =
450                                         gethomedir(pamh, unix_pwd->pw_dir,
451                                                 nowarn);
452                                 if (tmp_pwd_entry)
453                                         free(tmp_pwd_entry);
454                                 /* if NULL, homedir unchanged */
455                                 if (unix_pwd->pw_dir == NULL)
456                                         return (PAM_SUCCESS);
457                                 *passwd_flag = 1;
458                                 data++;
459                                 continue;
460                         }
461
462                         /* new gecos */
463                         if ((value = attr_match("AUTHTOK_GECOS", *data))
464                             != NULL) {
465                                 if (unix_pwd == NULL) {
466                                         if (!nowarn) {
467                                             sprintf(messages[0],
468                                             PAM_MSG(pamh, 94,
469                                             "%s: No local passwd record"),
470                                                 UNIX_MSG);
471                                             (void) __pam_display_msg(
472                                                 pamh, PAM_ERROR_MSG, 1,
473                                                 messages, NULL);
474                                         }
475                                         return (PAM_AUTHTOK_RECOVERY_ERR);
476                                 }
477                                 tmp_pwd_entry = unix_pwd->pw_gecos;
478                                 unix_pwd->pw_gecos =
479                                         getfingerinfo(pamh, unix_pwd->pw_gecos,
480                                                 nowarn);
481                                 if (tmp_pwd_entry)
482                                         free(tmp_pwd_entry);
483                                 /* if NULL, gecos unchanged */
484                                 if (unix_pwd->pw_gecos == NULL)
485                                         return (PAM_SUCCESS);
486                                 *passwd_flag = 1;
487                                 data++;
488                                 continue;
489                         }
490                 }
491         } else {
492                 if (strcmp(field, "passwd") == 0) { /* change password */
493                         unix_sp->sp_pwdp = *data_p;
494
495                         /* update the last change field */
496                         unix_sp->sp_lstchg = DAY_NOW;
497                         if (unix_sp->sp_max == 0) {   /* turn off aging */
498                                 unix_sp->sp_max = -1;
499                                 unix_sp->sp_min = -1;
500                         }
501                 }
502         }
503         return (PAM_SUCCESS);
504 }
505
506 /*
507  * shell, homedir and gecos are in passwd file. The update is modeled
508  * after shadow file.
509  */
510 static int
511 process_passwd(pamh, prognamep, usrname, unix_pwd, nowarn)
512         pam_handle_t    *pamh;
513         char            *prognamep;
514         char            *usrname;
515         struct passwd   *unix_pwd;
516         int             nowarn;
517 {
518         FILE            *tpfp;  /* tmp passwd file pointer */
519         FILE            *pwfp;  /* passwd file pointer */
520         char            messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
521         struct passwd   *unix_p;
522         struct passwd   unix_tmp;
523         int             found;
524         char            buf[4 * BUFSIZ];
525         struct stat     stat_buf;
526
527         if (stat(PASSWD, &stat_buf) < 0) {
528                 if (!nowarn) {
529                         sprintf(messages[0], PAM_MSG(pamh, 91,
530                 "%s%s: Unexpected failure. Password database unchanged."),
531                                 prognamep, UNIX_MSG);
532                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
533                                 1, messages, NULL);
534                 }
535                 return (PAM_AUTHTOK_ERR);
536         }
537
538         (void) umask(S_IAMB & ~(stat_buf.st_mode & S_IRUSR));
539         if ((tpfp = fopen(PASSTEMP, "w")) == NULL) {
540                 if (!nowarn) {
541                         sprintf(messages[0], PAM_MSG(pamh, 91,
542                 "%s%s: Unexpected failure. Password database unchanged."),
543                                 prognamep, UNIX_MSG);
544                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
545                                 1, messages, NULL);
546                 }
547                 return (PAM_AUTHTOK_ERR);
548         }
549
550         if ((pwfp = fopen(PASSWD, "r")) == NULL) {
551                 fclose(tpfp);
552                 goto err;
553         }
554
555
556         while ((unix_p = fgetpwent_r(pwfp, &unix_tmp, buf, 4*BUFSIZ)) != NULL) {
557                 if (strcmp(unix_p->pw_name, usrname) == 0) {
558                         found = 1;
559                         unix_p->pw_gecos = unix_pwd->pw_gecos;
560                         unix_p->pw_dir = unix_pwd->pw_dir;
561                         unix_p->pw_shell = unix_pwd->pw_shell;
562                 }
563                 if (putpwent(unix_p, tpfp) != 0) {
564                         if (!nowarn) {
565                                 sprintf(messages[0], PAM_MSG(pamh, 91,
566                 "%s%s: Unexpected failure. Password database unchanged."),
567                                         prognamep, UNIX_MSG);
568                                 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
569                                         1, messages, NULL);
570                         }
571                         fclose(tpfp);
572                         fclose(pwfp);
573                         goto err;
574                 }
575                 memset(buf, 0, 4 * BUFSIZ);
576
577         } /* end of while */
578
579         if ((fclose(tpfp)) || (fclose(pwfp))) {
580                 if (!nowarn) {
581                         sprintf(messages[0], PAM_MSG(pamh, 91,
582                 "%s%s: Unexpected failure. Password database unchanged."),
583                                 prognamep, UNIX_MSG);
584                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
585                                 1, messages, NULL);
586                 }
587                 goto err;
588         }
589
590         /* Check if user name exists */
591         if (found == 0) {
592                 if (!nowarn) {
593                         sprintf(messages[0], PAM_MSG(pamh, 91,
594                 "%s%s: Unexpected failure. Password database unchanged."),
595                         prognamep, UNIX_MSG);
596                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
597                                 1, messages, NULL);
598                 }
599                 goto err;
600         }
601
602         /*
603          *      Rename temp file back to  appropriate passwd file.
604          */
605
606         /* remove old passwd file */
607         if (unlink(OPASSWD) && access(OPASSWD, 0) == 0) {
608                 if (!nowarn) {
609                         sprintf(messages[0], PAM_MSG(pamh, 91,
610                                 "%s%s: %s"),
611                                 prognamep, UNIX_MSG);
612                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
613                                 1, messages, NULL);
614                 }
615                 goto err;
616         }
617
618         /* rename password file to old password file */
619         if (rename(PASSWD, OPASSWD) == -1) {
620                 if (!nowarn) {
621                         sprintf(messages[0], PAM_MSG(pamh, 91,
622                 "%s%s: Unexpected failure. Password database unchanged."),
623                                 prognamep, UNIX_MSG);
624                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
625                                 1, messages, NULL);
626                 }
627                 goto err;
628         }
629
630         /* rename temporary password file to password file */
631         if (rename(PASSTEMP, PASSWD) == -1) {
632                 (void) unlink(PASSWD);
633                 if (link(OPASSWD, PASSWD)) {
634                         if (!nowarn) {
635                                 sprintf(messages[0], PAM_MSG(pamh, 91,
636                 "%s%s: Unexpected failure. Password database unchanged."),
637                                         prognamep, UNIX_MSG);
638                                 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
639                                         1, messages, NULL);
640                         }
641                         goto err;
642                 }
643                 if (!nowarn) {
644                         sprintf(messages[0], PAM_MSG(pamh, 91,
645                 "%s%s: Unexpected failure. Password database unchanged."),
646                                 prognamep, UNIX_MSG);
647                         (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
648                                 1, messages, NULL);
649                 }
650                 goto err;
651         }
652
653         (void) chmod(PASSWD, 0644);
654         return (PAM_SUCCESS);
655
656 err:
657         (void) unlink(PASSTEMP);
658         return (PAM_AUTHTOK_ERR);
659 }