]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/support.c
Relevant BUGIDs: 125704, 124930
[linux-pam] / modules / pam_unix / support.c
1 /* 
2  * $Id$
3  *
4  * Copyright information at end of file.
5  */
6
7 #define _BSD_SOURCE
8
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #include <malloc.h>
14 #include <pwd.h>
15 #include <shadow.h>
16 #include <limits.h>
17 #include <utmp.h>
18
19 #include <security/_pam_macros.h>
20 #include <security/pam_modules.h>
21
22 #include "md5.h"
23 #include "support.h"
24
25 extern char *crypt(const char *key, const char *salt);
26 extern char *bigcrypt(const char *key, const char *salt);
27
28 /* syslogging function for errors and other information */
29
30 void _log_err(int err, const char *format,...)
31 {
32         va_list args;
33
34         va_start(args, format);
35         openlog("PAM_unix", LOG_CONS | LOG_PID, LOG_AUTH);
36         vsyslog(err, format, args);
37         va_end(args);
38         closelog();
39 }
40
41 /* this is a front-end for module-application conversations */
42
43 static int converse(pam_handle_t * pamh, int ctrl, int nargs
44                     ,struct pam_message **message
45                     ,struct pam_response **response)
46 {
47         int retval;
48         struct pam_conv *conv;
49
50         D(("begin to converse"));
51
52         retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
53         if (retval == PAM_SUCCESS) {
54
55                 retval = conv->conv(nargs, (const struct pam_message **) message
56                                     ,response, conv->appdata_ptr);
57
58                 D(("returned from application's conversation function"));
59
60                 if (retval != PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) {
61                         _log_err(LOG_DEBUG, "conversation failure [%s]"
62                                  ,pam_strerror(pamh, retval));
63                 }
64         } else if (retval != PAM_CONV_AGAIN) {
65                 _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
66                          ,pam_strerror(pamh, retval));
67         }
68         D(("ready to return from module conversation"));
69
70         return retval;          /* propagate error status */
71 }
72
73 int _make_remark(pam_handle_t * pamh, unsigned int ctrl
74                        ,int type, const char *text)
75 {
76         int retval = PAM_SUCCESS;
77
78         if (off(UNIX__QUIET, ctrl)) {
79                 struct pam_message *pmsg[1], msg[1];
80                 struct pam_response *resp;
81
82                 pmsg[0] = &msg[0];
83                 msg[0].msg = text;
84                 msg[0].msg_style = type;
85
86                 resp = NULL;
87                 retval = converse(pamh, ctrl, 1, pmsg, &resp);
88
89                 if (resp) {
90                         _pam_drop_reply(resp, 1);
91                 }
92         }
93         return retval;
94 }
95
96   /*
97    * Beacause getlogin() is braindead and sometimes it just
98    * doesn't work, we reimplement it here.
99    */
100 char *PAM_getlogin(void)
101 {
102         struct utmp *ut, line;
103         char *curr_tty, *retval;
104         static char curr_user[sizeof(ut->ut_user) + 4];
105
106         retval = NULL;
107
108         curr_tty = ttyname(0);
109         if (curr_tty != NULL) {
110                 D(("PAM_getlogin ttyname: %s", curr_tty));
111                 curr_tty += 5;
112                 setutent();
113                 strncpy(line.ut_line, curr_tty, sizeof line.ut_line);
114                 if ((ut = getutline(&line)) != NULL) {
115                         strncpy(curr_user, ut->ut_user, sizeof(ut->ut_user));
116                         retval = curr_user;
117                 }
118                 endutent();
119         }
120         D(("PAM_getlogin retval: %s", retval));
121
122         return retval;
123 }
124
125 /*
126  * set the control flags for the UNIX module.
127  */
128
129 int _set_ctrl(int flags, int *remember, int argc, const char **argv)
130 {
131         unsigned int ctrl;
132
133         D(("called."));
134
135         ctrl = UNIX_DEFAULTS;   /* the default selection of options */
136
137         /* set some flags manually */
138
139         if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
140                 D(("IAMROOT"));
141                 set(UNIX__IAMROOT, ctrl);
142         }
143         if (flags & PAM_UPDATE_AUTHTOK) {
144                 D(("UPDATE_AUTHTOK"));
145                 set(UNIX__UPDATE, ctrl);
146         }
147         if (flags & PAM_PRELIM_CHECK) {
148                 D(("PRELIM_CHECK"));
149                 set(UNIX__PRELIM, ctrl);
150         }
151         if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
152                 D(("DISALLOW_NULL_AUTHTOK"));
153                 set(UNIX__NONULL, ctrl);
154         }
155         if (flags & PAM_SILENT) {
156                 D(("SILENT"));
157                 set(UNIX__QUIET, ctrl);
158         }
159         /* now parse the arguments to this module */
160
161         while (argc-- > 0) {
162                 int j;
163
164                 D(("pam_unix arg: %s", *argv));
165
166                 for (j = 0; j < UNIX_CTRLS_; ++j) {
167                         if (unix_args[j].token
168                             && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) {
169                                 break;
170                         }
171                 }
172
173                 if (j >= UNIX_CTRLS_) {
174                         _log_err(LOG_ERR, "unrecognized option [%s]", *argv);
175                 } else {
176                         ctrl &= unix_args[j].mask;      /* for turning things off */
177                         ctrl |= unix_args[j].flag;      /* for turning things on  */
178
179                         if (remember != NULL) {
180                                 if (j == UNIX_REMEMBER_PASSWD) {
181                                         *remember = strtol(*argv + 9, NULL, 10);
182                                         if ((*remember == LONG_MIN) || (*remember == LONG_MAX))
183                                                 *remember = -1;
184                                         if (*remember > 400)
185                                                 *remember = 400;
186                                 }
187                         }
188                 }
189
190                 ++argv;         /* step to next argument */
191         }
192
193         /* auditing is a more sensitive version of debug */
194
195         if (on(UNIX_AUDIT, ctrl)) {
196                 set(UNIX_DEBUG, ctrl);
197         }
198         /* return the set of flags */
199
200         D(("done."));
201         return ctrl;
202 }
203
204 static void _cleanup(pam_handle_t * pamh, void *x, int error_status)
205 {
206         _pam_delete(x);
207 }
208
209 /* ************************************************************** *
210  * Useful non-trivial functions                                   *
211  * ************************************************************** */
212
213   /*
214    * the following is used to keep track of the number of times a user fails
215    * to authenticate themself.
216    */
217
218 #define FAIL_PREFIX                   "-UN*X-FAIL-"
219 #define UNIX_MAX_RETRIES              3
220
221 struct _pam_failed_auth {
222         char *user;             /* user that's failed to be authenticated */
223         char *name;             /* attempt from user with name */
224         int id;                 /* uid of name'd user */
225         int count;              /* number of failures so far */
226 };
227
228 #ifndef PAM_DATA_REPLACE
229 #error "Need to get an updated libpam 0.52 or better"
230 #endif
231
232 static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
233 {
234         int quiet;
235         const char *service = NULL;
236         struct _pam_failed_auth *failure;
237
238         D(("called"));
239
240         quiet = err & PAM_DATA_SILENT;  /* should we log something? */
241         err &= PAM_DATA_REPLACE;        /* are we just replacing data? */
242         failure = (struct _pam_failed_auth *) fl;
243
244         if (failure != NULL) {
245
246                 if (!quiet && !err) {   /* under advisement from Sun,may go away */
247
248                         /* log the number of authentication failures */
249                         if (failure->count > 1) {
250                                 (void) pam_get_item(pamh, PAM_SERVICE
251                                               ,(const void **) &service);
252                                 _log_err(LOG_NOTICE
253                                          ,"%d more authentication failure%s; %s(uid=%d) -> "
254                                          "%s for %s service"
255                                          ,failure->count - 1, failure->count == 2 ? "" : "s"
256                                          ,failure->name
257                                          ,failure->id
258                                          ,failure->user
259                                 ,service == NULL ? "**unknown**" : service
260                                     );
261                                 if (failure->count > UNIX_MAX_RETRIES) {
262                                         _log_err(LOG_ALERT
263                                                  ,"service(%s) ignoring max retries; %d > %d"
264                                                  ,service == NULL ? "**unknown**" : service
265                                                  ,failure->count
266                                                  ,UNIX_MAX_RETRIES);
267                                 }
268                         }
269                 }
270                 _pam_delete(failure->user);     /* tidy up */
271                 _pam_delete(failure->name);     /* tidy up */
272                 free(failure);
273         }
274 }
275
276 /*
277  * _unix_blankpasswd() is a quick check for a blank password
278  *
279  * returns TRUE if user does not have a password
280  * - to avoid prompting for one in such cases (CG)
281  */
282
283 int _unix_blankpasswd(unsigned int ctrl, const char *name)
284 {
285         struct passwd *pwd = NULL;
286         struct spwd *spwdent = NULL;
287         char *salt = NULL;
288         int retval;
289
290         D(("called"));
291
292         /*
293          * This function does not have to be too smart if something goes
294          * wrong, return FALSE and let this case to be treated somewhere
295          * else (CG)
296          */
297
298         if (on(UNIX__NONULL, ctrl))
299                 return 0;       /* will fail but don't let on yet */
300
301         /* UNIX passwords area */
302         pwd = getpwnam(name);   /* Get password file entry... */
303
304         if (pwd != NULL) {
305                 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
306                 { /* NIS+ */                 
307                         uid_t save_euid, save_uid;
308         
309                         save_euid = geteuid();
310                         save_uid = getuid();
311                         if (save_uid == pwd->pw_uid)
312                                 setreuid( save_euid, save_uid );
313                         else  {
314                                 setreuid( 0, -1 );
315                                 if (setreuid( -1, pwd->pw_uid ) == -1) {
316                                         setreuid( -1, 0 );
317                                         setreuid( 0, -1 );
318                                         if(setreuid( -1, pwd->pw_uid ) == -1)
319                                                 /* Will fail elsewhere. */
320                                                 return 0;
321                                 }
322                         }
323         
324                         spwdent = getspnam( name );
325                         if (save_uid == pwd->pw_uid)
326                                 setreuid( save_uid, save_euid );
327                         else {
328                                 if (setreuid( -1, 0 ) == -1)
329                                 setreuid( save_uid, -1 );
330                                 setreuid( -1, save_euid );
331                         }
332                 } else if (strcmp(pwd->pw_passwd, "x") == 0) {
333                         /*
334                          * ...and shadow password file entry for this user,
335                          * if shadowing is enabled
336                          */
337                         spwdent = getspnam(name);
338                 }
339                 if (spwdent)
340                         salt = x_strdup(spwdent->sp_pwdp);
341                 else
342                         salt = x_strdup(pwd->pw_passwd);
343         }
344         /* Does this user have a password? */
345         if (salt == NULL) {
346                 retval = 0;
347         } else {
348                 if (strlen(salt) == 0)
349                         retval = 1;
350                 else
351                         retval = 0;
352         }
353
354         /* tidy up */
355
356         if (salt)
357                 _pam_delete(salt);
358
359         return retval;
360 }
361
362 /*
363  * verify the password of a user
364  */
365
366 #include <sys/types.h>
367 #include <sys/wait.h>
368
369 static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd, unsigned int ctrl)
370 {
371     int retval, child, fds[2];
372
373     D(("called."));
374     /* create a pipe for the password */
375     if (pipe(fds) != 0) {
376         D(("could not make pipe"));
377         return PAM_AUTH_ERR;
378     }
379
380     /* fork */
381     child = fork();
382     if (child == 0) {
383         static char *args[] = { NULL, NULL };
384         static char *envp[] = { NULL };
385
386         /* XXX - should really tidy up PAM here too */
387
388         /* reopen stdin as pipe */
389         close(fds[1]);
390         dup2(fds[0], STDIN_FILENO);
391
392         /* exec binary helper */
393         args[0] = x_strdup(CHKPWD_HELPER);
394         execve(CHKPWD_HELPER, args, envp);
395
396         /* should not get here: exit with error */
397         D(("helper binary is not available"));
398         exit(PAM_AUTHINFO_UNAVAIL);
399     } else if (child > 0) {
400         /* wait for child */
401         /* if the stored password is NULL */
402         if (off(UNIX__NONULL, ctrl)) {  /* this means we've succeeded */
403             write(fds[1], "nullok\0\0", 8);
404         } else {
405             write(fds[1], "nonull\0\0", 8);
406         }
407         if (passwd != NULL) {            /* send the password to the child */
408             write(fds[1], passwd, strlen(passwd)+1);
409             passwd = NULL;
410         } else {
411             write(fds[1], "", 1);                        /* blank password */
412         }
413         close(fds[0]);       /* close here to avoid possible SIGPIPE above */
414         close(fds[1]);
415         (void) waitpid(child, &retval, 0);  /* wait for helper to complete */
416         retval = (retval == 0) ? PAM_SUCCESS:PAM_AUTH_ERR;
417     } else {
418         D(("fork failed"));
419         retval = PAM_AUTH_ERR;
420     }
421
422     D(("returning %d", retval));
423     return retval;
424 }
425
426 int _unix_verify_password(pam_handle_t * pamh, const char *name
427                           ,const char *p, unsigned int ctrl)
428 {
429         struct passwd *pwd = NULL;
430         struct spwd *spwdent = NULL;
431         char *salt = NULL;
432         char *pp = NULL;
433         char *data_name;
434         int retval;
435
436         D(("called"));
437
438 #ifdef HAVE_PAM_FAIL_DELAY
439         if (off(UNIX_NODELAY, ctrl)) {
440                 D(("setting delay"));
441                 (void) pam_fail_delay(pamh, 2000000);   /* 2 sec delay for on failure */
442         }
443 #endif
444
445         /* locate the entry for this user */
446
447         D(("locating user's record"));
448
449         /* UNIX passwords area */
450         pwd = getpwnam(name);   /* Get password file entry... */
451
452         if (pwd != NULL) {
453                 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
454                 { /* NIS+ */                 
455                         uid_t save_euid, save_uid;
456         
457                         save_euid = geteuid();
458                         save_uid = getuid();
459                         if (save_uid == pwd->pw_uid)
460                                 setreuid( save_euid, save_uid );
461                         else  {
462                                 setreuid( 0, -1 );
463                                 if (setreuid( -1, pwd->pw_uid ) == -1) {
464                                         setreuid( -1, 0 );
465                                         setreuid( 0, -1 );
466                                         if(setreuid( -1, pwd->pw_uid ) == -1)
467                                                 return PAM_CRED_INSUFFICIENT;
468                                 }
469                         }
470         
471                         spwdent = getspnam( name );
472                         if (save_uid == pwd->pw_uid)
473                                 setreuid( save_uid, save_euid );
474                         else {
475                                 if (setreuid( -1, 0 ) == -1)
476                                 setreuid( save_uid, -1 );
477                                 setreuid( -1, save_euid );
478                         }
479                 } else if (strcmp(pwd->pw_passwd, "x") == 0) {
480                         /*
481                          * ...and shadow password file entry for this user,
482                          * if shadowing is enabled
483                          */
484                         spwdent = getspnam(name);
485                 }
486                 if (spwdent)
487                         salt = x_strdup(spwdent->sp_pwdp);
488                 else
489                         salt = x_strdup(pwd->pw_passwd);
490         }
491
492         data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
493         if (data_name == NULL) {
494                 _log_err(LOG_CRIT, "no memory for data-name");
495         } else {
496                 strcpy(data_name, FAIL_PREFIX);
497                 strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
498         }
499
500         retval = PAM_SUCCESS;
501         if (pwd == NULL || salt == NULL || !strcmp(salt, "x")) {
502                 if (geteuid()) {
503                         /* we are not root perhaps this is the reason? Run helper */
504                         D(("running helper binary"));
505                         retval = _unix_run_helper_binary(pamh, p, ctrl);
506                 } else {
507                         D(("user's record unavailable"));
508                         if (on(UNIX_AUDIT, ctrl)) {
509                                 /* this might be a typo and the user has given a password
510                                    instead of a username. Careful with this. */
511                                 _log_err(LOG_ALERT, "check pass; user (%s) unknown", name);
512                         } else {
513                                 _log_err(LOG_ALERT, "check pass; user unknown");
514                         }
515                         p = NULL;
516                         retval = PAM_AUTHINFO_UNAVAIL;
517                 }
518         } else {
519                 if (!strlen(salt)) {
520                         /* the stored password is NULL */
521                         if (off(UNIX__NONULL, ctrl)) {  /* this means we've succeeded */
522                                 D(("user has empty password - access granted"));
523                                 retval = PAM_SUCCESS;
524                         } else {
525                                 D(("user has empty password - access denied"));
526                                 retval = PAM_AUTH_ERR;
527                         }
528                 } else if (!p) {
529                                 retval = PAM_AUTH_ERR;
530                 } else {
531                         if (!strncmp(salt, "$1$", 3)) {
532                                 pp = Goodcrypt_md5(p, salt);
533                                 if (strcmp(pp, salt) != 0) {
534                                         pp = Brokencrypt_md5(p, salt);
535                                 }
536                         } else {
537                                 pp = bigcrypt(p, salt);
538                         }
539                         p = NULL;               /* no longer needed here */
540
541                         /* the moment of truth -- do we agree with the password? */
542                         D(("comparing state of pp[%s] and salt[%s]", pp, salt));
543
544                         if (strcmp(pp, salt) == 0) {
545                                 retval = PAM_SUCCESS;
546                         } else {
547                                 retval = PAM_AUTH_ERR;
548                         }
549                 }
550         }
551
552         if (retval == PAM_SUCCESS) {
553                 if (data_name)  /* reset failures */
554                         pam_set_data(pamh, data_name, NULL, _cleanup_failures);
555         } else {
556                 if (data_name != NULL) {
557                         struct _pam_failed_auth *new = NULL;
558                         const struct _pam_failed_auth *old = NULL;
559
560                         /* get a failure recorder */
561
562                         new = (struct _pam_failed_auth *)
563                             malloc(sizeof(struct _pam_failed_auth));
564
565                         if (new != NULL) {
566
567                                 new->user = x_strdup(name);
568                                 new->id = getuid();
569                                 new->name = x_strdup(PAM_getlogin()? PAM_getlogin() : "");
570
571                                 /* any previous failures for this user ? */
572                                 pam_get_data(pamh, data_name, (const void **) &old);
573
574                                 if (old != NULL) {
575                                         new->count = old->count + 1;
576                                         if (new->count >= UNIX_MAX_RETRIES) {
577                                                 retval = PAM_MAXTRIES;
578                                         }
579                                 } else {
580                                         const char *service=NULL;
581                                         (void) pam_get_item(pamh, PAM_SERVICE,
582                                                             (const void **)&service);
583                                         _log_err(LOG_NOTICE
584                                                         ,"authentication failure; %s(uid=%d) -> "
585                                                          "%s for %s service"
586                                                         ,new->name
587                                                         ,new->id
588                                                         ,new->user
589                                                         ,service == NULL ? "**unknown**":service
590                                                         );
591                                         new->count = 1;
592                                 }
593
594                                 pam_set_data(pamh, data_name, new, _cleanup_failures);
595
596                         } else {
597                                 _log_err(LOG_CRIT, "no memory for failure recorder");
598                         }
599                 }
600         }
601
602         if (data_name)
603                 _pam_delete(data_name);
604         if (salt)
605                 _pam_delete(salt);
606         if (pp)
607                 _pam_overwrite(pp);
608
609         D(("done [%d].", retval));
610
611         return retval;
612 }
613
614 /*
615  * obtain a password from the user
616  */
617
618 int _unix_read_password(pam_handle_t * pamh
619                         ,unsigned int ctrl
620                         ,const char *comment
621                         ,const char *prompt1
622                         ,const char *prompt2
623                         ,const char *data_name
624                         ,const char **pass)
625 {
626         int authtok_flag;
627         int retval;
628         const char *item;
629         char *token;
630
631         D(("called"));
632
633         /*
634          * make sure nothing inappropriate gets returned
635          */
636
637         *pass = token = NULL;
638
639         /*
640          * which authentication token are we getting?
641          */
642
643         authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
644
645         /*
646          * should we obtain the password from a PAM item ?
647          */
648
649         if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
650                 retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
651                 if (retval != PAM_SUCCESS) {
652                         /* very strange. */
653                         _log_err(LOG_ALERT
654                                  ,"pam_get_item returned error to unix-read-password"
655                             );
656                         return retval;
657                 } else if (item != NULL) {      /* we have a password! */
658                         *pass = item;
659                         item = NULL;
660                         return PAM_SUCCESS;
661                 } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
662                         return PAM_AUTHTOK_RECOVER_ERR;         /* didn't work */
663                 } else if (on(UNIX_USE_AUTHTOK, ctrl)
664                            && off(UNIX__OLD_PASSWD, ctrl)) {
665                         return PAM_AUTHTOK_RECOVER_ERR;
666                 }
667         }
668         /*
669          * getting here implies we will have to get the password from the
670          * user directly.
671          */
672
673         {
674                 struct pam_message msg[3], *pmsg[3];
675                 struct pam_response *resp;
676                 int i, replies;
677
678                 /* prepare to converse */
679
680                 if (comment != NULL && off(UNIX__QUIET, ctrl)) {
681                         pmsg[0] = &msg[0];
682                         msg[0].msg_style = PAM_TEXT_INFO;
683                         msg[0].msg = comment;
684                         i = 1;
685                 } else {
686                         i = 0;
687                 }
688
689                 pmsg[i] = &msg[i];
690                 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
691                 msg[i++].msg = prompt1;
692                 replies = 1;
693
694                 if (prompt2 != NULL) {
695                         pmsg[i] = &msg[i];
696                         msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
697                         msg[i++].msg = prompt2;
698                         ++replies;
699                 }
700                 /* so call the conversation expecting i responses */
701                 resp = NULL;
702                 retval = converse(pamh, ctrl, i, pmsg, &resp);
703
704                 if (resp != NULL) {
705
706                         /* interpret the response */
707
708                         if (retval == PAM_SUCCESS) {    /* a good conversation */
709
710                                 token = x_strdup(resp[i - replies].resp);
711                                 if (token != NULL) {
712                                         if (replies == 2) {
713
714                                                 /* verify that password entered correctly */
715                                                 if (!resp[i - 1].resp
716                                                     || strcmp(token, resp[i - 1].resp)) {
717                                                         _pam_delete(token);     /* mistyped */
718                                                         retval = PAM_AUTHTOK_RECOVER_ERR;
719                                                         _make_remark(pamh, ctrl
720                                                                     ,PAM_ERROR_MSG, MISTYPED_PASS);
721                                                 }
722                                         }
723                                 } else {
724                                         _log_err(LOG_NOTICE
725                                                  ,"could not recover authentication token");
726                                 }
727
728                         }
729                         /*
730                          * tidy up the conversation (resp_retcode) is ignored
731                          * -- what is it for anyway? AGM
732                          */
733
734                         _pam_drop_reply(resp, i);
735
736                 } else {
737                         retval = (retval == PAM_SUCCESS)
738                             ? PAM_AUTHTOK_RECOVER_ERR : retval;
739                 }
740         }
741
742         if (retval != PAM_SUCCESS) {
743                 if (on(UNIX_DEBUG, ctrl))
744                         _log_err(LOG_DEBUG, "unable to obtain a password");
745                 return retval;
746         }
747         /* 'token' is the entered password */
748
749         if (off(UNIX_NOT_SET_PASS, ctrl)) {
750
751                 /* we store this password as an item */
752
753                 retval = pam_set_item(pamh, authtok_flag, token);
754                 _pam_delete(token);     /* clean it up */
755                 if (retval != PAM_SUCCESS
756                     || (retval = pam_get_item(pamh, authtok_flag
757                                               ,(const void **) &item))
758                     != PAM_SUCCESS) {
759
760                         _log_err(LOG_CRIT, "error manipulating password");
761                         return retval;
762
763                 }
764         } else {
765                 /*
766                  * then store it as data specific to this module. pam_end()
767                  * will arrange to clean it up.
768                  */
769
770                 retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
771                 if (retval != PAM_SUCCESS) {
772                         _log_err(LOG_CRIT, "error manipulating password data [%s]"
773                                  ,pam_strerror(pamh, retval));
774                         _pam_delete(token);
775                         return retval;
776                 }
777                 item = token;
778                 token = NULL;   /* break link to password */
779         }
780
781         *pass = item;
782         item = NULL;            /* break link to password */
783
784         return PAM_SUCCESS;
785 }
786
787 /* ****************************************************************** *
788  * Copyright (c) Jan Rêkorajski 1999.
789  * Copyright (c) Andrew G. Morgan 1996-8.
790  * Copyright (c) Alex O. Yuriev, 1996.
791  * Copyright (c) Cristian Gafton 1996.
792  *
793  * Redistribution and use in source and binary forms, with or without
794  * modification, are permitted provided that the following conditions
795  * are met:
796  * 1. Redistributions of source code must retain the above copyright
797  *    notice, and the entire permission notice in its entirety,
798  *    including the disclaimer of warranties.
799  * 2. Redistributions in binary form must reproduce the above copyright
800  *    notice, this list of conditions and the following disclaimer in the
801  *    documentation and/or other materials provided with the distribution.
802  * 3. The name of the author may not be used to endorse or promote
803  *    products derived from this software without specific prior
804  *    written permission.
805  * 
806  * ALTERNATIVELY, this product may be distributed under the terms of
807  * the GNU Public License, in which case the provisions of the GPL are
808  * required INSTEAD OF the above restrictions.  (This clause is
809  * necessary due to a potential bad interaction between the GPL and
810  * the restrictions contained in a BSD-style copyright.)
811  * 
812  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
813  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
814  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
815  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
816  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
817  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
818  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
819  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
820  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
821  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
822  * OF THE POSSIBILITY OF SUCH DAMAGE.
823  */