]> granicus.if.org Git - linux-pam/blob - modules/pam_pwdb/support.-c
Relevant BUGIDs: 728887
[linux-pam] / modules / pam_pwdb / support.-c
1 /* 
2  * $Id$
3  *
4  * Copyright information at end of file.
5  */
6
7 /*
8  * here is the string to inform the user that the new passwords they
9  * typed were not the same.
10  */
11
12 #define MISTYPED_PASS "Sorry, passwords do not match"
13
14 /* type definition for the control options */
15
16 typedef struct {
17      const char *token;
18      unsigned int mask;            /* shall assume 32 bits of flags */
19      unsigned int flag;
20 } UNIX_Ctrls;
21
22 /*
23  * macro to determine if a given flag is on
24  */
25
26 #define on(x,ctrl)  (unix_args[x].flag & ctrl)
27
28 /*
29  * macro to determine that a given flag is NOT on
30  */
31
32 #define off(x,ctrl) (!on(x,ctrl))
33
34 /*
35  * macro to turn on/off a ctrl flag manually
36  */
37
38 #define set(x,ctrl)   (ctrl = ((ctrl)&unix_args[x].mask)|unix_args[x].flag)
39 #define unset(x,ctrl) (ctrl &= ~(unix_args[x].flag))
40
41 /* the generic mask */
42
43 #define _ALL_ON_  (~0U)
44
45 /* end of macro definitions definitions for the control flags */
46
47 /* ****************************************************************** *
48  * ctrl flags proper..
49  */
50
51 /*
52  * here are the various options recognized by the unix module. They
53  * are enumerated here and then defined below. Internal arguments are
54  * given NULL tokens.
55  */
56
57 #define UNIX__OLD_PASSWD          0     /* internal */
58 #define UNIX__VERIFY_PASSWD       1     /* internal */
59 #define UNIX__IAMROOT             2     /* internal */
60
61 #define UNIX_AUDIT                3     /* print more things than debug..
62                                            some information may be sensitive */
63 #define UNIX_USE_FIRST_PASS       4
64 #define UNIX_TRY_FIRST_PASS       5
65 #define UNIX_NOT_SET_PASS         6     /* don't set the AUTHTOK items */
66
67 #define UNIX__PRELIM              7     /* internal */
68 #define UNIX__UPDATE              8     /* internal */
69 #define UNIX__NONULL              9     /* internal */
70 #define UNIX__QUIET              10     /* internal */
71 #define UNIX_USE_AUTHTOK         11     /* insist on reading PAM_AUTHTOK */
72 #define UNIX_SHADOW              12     /* signal shadow on */
73 #define UNIX_MD5_PASS            13     /* force the use of MD5 passwords */
74 #define UNIX__NULLOK             14     /* Null token ok */
75 #define UNIX_RADIUS              15     /* wish to use RADIUS for password */
76 #define UNIX__SET_DB             16     /* internal - signals redirect to db */
77 #define UNIX_DEBUG               17     /* send more info to syslog(3) */
78 #define UNIX_NODELAY             18     /* admin does not want a fail-delay */
79 #define UNIX_UNIX                19     /* wish to use /etc/passwd for pwd */
80 #define UNIX_BIGCRYPT            20     /* use DEC-C2 crypt()^x function */
81 #define UNIX_LIKE_AUTH           21     /* need to auth for setcred to work */
82 #define UNIX_NOREAP              22     /* don't reap child process */
83 /* -------------- */
84 #define UNIX_CTRLS_              23     /* number of ctrl arguments defined */
85
86
87 static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = {
88 /* symbol                 token name          ctrl mask      ctrl       *
89  * ------------------     ------------------  -------------- ---------- */
90
91 /* UNIX__OLD_PASSWD */    {  NULL,            _ALL_ON_,                 01 },
92 /* UNIX__VERIFY_PASSWD */ {  NULL,            _ALL_ON_,                 02 },
93 /* UNIX__IAMROOT */       {  NULL,            _ALL_ON_,                 04 },
94 /* UNIX_AUDIT */          { "audit",          _ALL_ON_,                010 },
95 /* UNIX_USE_FIRST_PASS */ { "use_first_pass", _ALL_ON_^(060),          020 },
96 /* UNIX_TRY_FIRST_PASS */ { "try_first_pass", _ALL_ON_^(060),          040 },
97 /* UNIX_NOT_SET_PASS */   { "not_set_pass",   _ALL_ON_,               0100 },
98 /* UNIX__PRELIM */        {  NULL,            _ALL_ON_^(0600),        0200 },
99 /* UNIX__UPDATE */        {  NULL,            _ALL_ON_^(0600),        0400 },
100 /* UNIX__NONULL */        {  NULL,            _ALL_ON_,              01000 },
101 /* UNIX__QUIET */         {  NULL,            _ALL_ON_,              02000 },
102 /* UNIX_USE_AUTHTOK */    { "use_authtok",    _ALL_ON_,              04000 },
103 /* UNIX_SHADOW */         { "shadow",         _ALL_ON_^(0140000),   010000 },
104 /* UNIX_MD5_PASS */       { "md5",            _ALL_ON_^(02000000),  020000 },
105 /* UNIX__NULLOK */        { "nullok",         _ALL_ON_^(01000),          0 },
106 /* UNIX_RADIUS */         { "radius",         _ALL_ON_^(0110000),   040000 },
107 /* UNIX__SET_DB */        {  NULL,            _ALL_ON_,            0100000 },
108 /* UNIX_DEBUG */          { "debug",          _ALL_ON_,            0200000 },
109 /* UNIX_NODELAY */        { "nodelay",        _ALL_ON_,            0400000 },
110 /* UNIX_UNIX */           { "unix",           _ALL_ON_^(050000),  01000000 },
111 /* UNIX_BIGCRYPT */       { "bigcrypt",       _ALL_ON_^(020000),  02000000 },
112 /* UNIX_LIKE_AUTH */      { "likeauth",       _ALL_ON_,           04000000 },
113 /* UNIX_NOREAP */         {"noreap",          _ALL_ON_,          010000000 },
114 };
115
116 #define UNIX_DEFAULTS  (unix_args[UNIX__NONULL].flag)
117
118 /* syslogging function for errors and other information */
119
120 static void _log_err(int err, const char *format, ...)
121 {
122     va_list args;
123
124     va_start(args, format);
125     openlog("PAM_pwdb", LOG_CONS|LOG_PID, LOG_AUTH);
126     vsyslog(err, format, args);
127     va_end(args);
128     closelog();
129 }
130
131 /* this is a front-end for module-application conversations */
132
133 static int converse(pam_handle_t *pamh, int ctrl, int nargs
134                     , struct pam_message **message
135                     , struct pam_response **response)
136 {
137     int retval;
138     struct pam_conv *conv;
139
140     D(("begin to converse"));
141
142     retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ; 
143     if ( retval == PAM_SUCCESS ) {
144
145         retval = conv->conv(nargs, ( const struct pam_message ** ) message
146                             , response, conv->appdata_ptr);
147
148         D(("returned from application's conversation function"));
149
150         if (retval != PAM_SUCCESS && on(UNIX_DEBUG,ctrl) ) {
151             _log_err(LOG_DEBUG, "conversation failure [%s]"
152                      , pam_strerror(pamh, retval));
153         }
154
155     } else if (retval != PAM_CONV_AGAIN) {
156         _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
157                  , pam_strerror(pamh, retval));
158     }
159
160     D(("ready to return from module conversation"));
161
162     return retval;                  /* propagate error status */
163 }
164
165 static int make_remark(pam_handle_t *pamh, unsigned int ctrl
166                        , int type, const char *text)
167 {
168     int retval=PAM_SUCCESS;
169
170     if ( off(UNIX__QUIET, ctrl) ) {
171         struct pam_message *pmsg[1], msg[1];
172         struct pam_response *resp;
173
174         pmsg[0] = &msg[0];
175         msg[0].msg = text;
176         msg[0].msg_style = type;
177
178         resp = NULL;
179         retval = converse(pamh, ctrl, 1, pmsg, &resp);
180
181         if (resp) {
182             _pam_drop_reply(resp, 1);
183         }
184     }
185     return retval;
186 }
187
188 /*
189  * set the control flags for the UNIX module.
190  */
191
192 static int set_ctrl(int flags, int argc, const char **argv)
193 {
194     unsigned int ctrl;
195
196     D(("called."));
197
198     ctrl = UNIX_DEFAULTS;         /* the default selection of options */
199
200     /* set some flags manually */
201
202     if ( getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK) ) {
203         set(UNIX__IAMROOT, ctrl);
204     }
205     if ( flags & PAM_UPDATE_AUTHTOK ) {
206         set(UNIX__UPDATE, ctrl);
207     }
208     if ( flags & PAM_PRELIM_CHECK ) {
209         set(UNIX__PRELIM, ctrl);
210     }
211     if ( flags & PAM_DISALLOW_NULL_AUTHTOK ) {
212         set(UNIX__NONULL, ctrl);
213     }
214     if ( flags & PAM_SILENT ) {
215         set(UNIX__QUIET, ctrl);
216     }
217
218     /* now parse the arguments to this module */
219
220     while (argc-- > 0) {
221         int j;
222
223         D(("pam_pwdb arg: %s",*argv));
224
225         for (j=0; j<UNIX_CTRLS_; ++j) {
226             if (unix_args[j].token
227                 && ! strcmp(*argv, unix_args[j].token) ) {
228                 break;
229             }
230         }
231
232         if ( j >= UNIX_CTRLS_ ) {
233             _log_err(LOG_ERR, "unrecognized option [%s]",*argv);
234         } else {
235             ctrl &= unix_args[j].mask;    /* for turning things off */
236             ctrl |= unix_args[j].flag;    /* for turning things on  */
237         }
238
239         ++argv;                            /* step to next argument */
240     }
241
242     /* these are used for updating passwords in specific places */
243
244     if (on(UNIX_SHADOW,ctrl) || on(UNIX_RADIUS,ctrl) || on(UNIX_UNIX,ctrl)) {
245         set(UNIX__SET_DB, ctrl);
246     }
247
248     /* auditing is a more sensitive version of debug */
249
250     if ( on(UNIX_AUDIT,ctrl) ) {
251         set(UNIX_DEBUG, ctrl);
252     }
253
254     /* return the set of flags */
255
256     D(("done."));
257     return ctrl;
258 }
259
260 /* use this to free strings. ESPECIALLY password strings */
261
262 static char *_pam_delete(register char *xx)
263 {
264     _pam_overwrite(xx);
265     _pam_drop(xx);
266     return NULL;
267 }
268
269 static void _cleanup(pam_handle_t *pamh, void *x, int error_status)
270 {
271     x = _pam_delete( (char *) x );
272 }
273
274 /* ************************************************************** *
275  * Useful non-trivial functions                                   *
276  * ************************************************************** */
277
278 #include "pam_unix_md.-c"
279
280 /*
281  * the following is used to keep track of the number of times a user fails
282  * to authenticate themself.
283  */
284
285 #define FAIL_PREFIX                   "-UN*X-FAIL-"
286 #define UNIX_MAX_RETRIES              3
287
288 struct _pam_failed_auth {
289     char *user;                  /* user that's failed to be authenticated */
290     char *name;                  /* attempt from user with name */
291     int id;                      /* uid of name'd user */
292     int count;                   /* number of failures so far */
293 };
294
295 #ifndef PAM_DATA_REPLACE
296 #error "Need to get an updated libpam 0.52 or better"
297 #endif
298
299 static void _cleanup_failures(pam_handle_t *pamh, void *fl, int err)
300 {
301     int quiet;
302     const char *service=NULL;
303     struct _pam_failed_auth *failure;
304
305     D(("called"));
306
307     quiet = err & PAM_DATA_SILENT;     /* should we log something? */
308     err  &= PAM_DATA_REPLACE;          /* are we just replacing data? */
309     failure = (struct _pam_failed_auth *) fl;
310
311     if ( failure != NULL ) {
312
313         if ( !quiet && !err ) {   /* under advisement from Sun,may go away */
314
315             /* log the number of authentication failures */
316             if ( failure->count > 1 ) {
317                 (void) pam_get_item(pamh, PAM_SERVICE
318                                     , (const void **)&service);
319                 _log_err(LOG_NOTICE
320                          , "%d more authentication failure%s; %s(uid=%d) -> "
321                          "%s for %s service"
322                          , failure->count-1, failure->count==2 ? "":"s"
323                          , failure->name
324                          , failure->id
325                          , failure->user
326                          , service == NULL ? "**unknown**":service
327                     );
328                 if ( failure->count > UNIX_MAX_RETRIES ) {
329                     _log_err(LOG_ALERT
330                              , "service(%s) ignoring max retries; %d > %d"
331                              , service == NULL ? "**unknown**":service
332                              , failure->count
333                              , UNIX_MAX_RETRIES );
334                 }
335             }
336         }
337         failure->user = _pam_delete(failure->user);            /* tidy up */
338         failure->name = _pam_delete(failure->name);            /* tidy up */
339         free(failure);
340     }
341 }
342
343 /*
344  * verify the password of a user
345  */
346
347 #include <signal.h>
348 #include <sys/types.h>
349 #include <sys/wait.h>
350
351 static int pwdb_run_helper_binary(pam_handle_t *pamh, const char *passwd,
352                                   unsigned int ctrl, const char *user)
353 {
354     int retval, child, fds[2];
355     void (*sighandler)(int) = NULL;
356
357     D(("called."));
358     /* create a pipe for the password */
359     if (pipe(fds) != 0) {
360         D(("could not make pipe"));
361         return PAM_AUTH_ERR;
362     }
363
364     if (off(UNIX_NOREAP, ctrl)) {
365         /*
366          * This code arranges that the demise of the child does not cause
367          * the application to receive a signal it is not expecting - which
368          * may kill the application or worse.
369          *
370          * The "noreap" module argument is provided so that the admin can
371          * override this behavior.
372          */
373         sighandler = signal(SIGCHLD, SIG_IGN);
374     }
375
376     /* fork */
377     child = fork();
378     if (child == 0) {
379         static char *args[] = { NULL, NULL, NULL };
380         static char *envp[] = { NULL };
381
382         /* XXX - should really tidy up PAM here too */
383         while (pwdb_end() == PWDB_SUCCESS);
384
385         /* reopen stdin as pipe */
386         close(fds[1]);
387         dup2(fds[0], STDIN_FILENO);
388
389         /* exec binary helper */
390         args[0] = x_strdup(CHKPWD_HELPER);
391         args[1] = x_strdup(user);
392
393         execve(CHKPWD_HELPER, args, envp);
394
395         /* should not get here: exit with error */
396         D(("helper binary is not available"));
397         exit(PWDB_SUCCESS+1);
398     } else if (child > 0) {
399         /* wait for child */
400         if (passwd != NULL) {            /* send the password to the child */
401             write(fds[1], passwd, strlen(passwd)+1);
402             passwd = NULL;
403         } else {
404             write(fds[1], "", 1);                        /* blank password */
405         }
406         close(fds[0]);   /* we close this after the write because we want
407                             to avoid a possible SIGPIPE. */
408         close(fds[1]);
409         (void) waitpid(child, &retval, 0);  /* wait for helper to complete */
410         retval = (retval == PWDB_SUCCESS) ? PAM_SUCCESS:PAM_AUTH_ERR;
411     } else {
412         D(("fork failed"));
413         retval = PAM_AUTH_ERR;
414     }
415
416     if (sighandler != NULL) {
417         (void) signal(SIGCHLD, sighandler);   /* restore old signal handler */
418     }
419
420     D(("returning %d", retval));
421     return retval;
422 }
423
424 static int _unix_verify_password(pam_handle_t *pamh, const char *name,
425                                  const char *p, unsigned int ctrl)
426 {
427     const struct pwdb *pw=NULL;
428     const struct pwdb_entry *pwe=NULL;
429
430     const char *salt;
431     char *pp;
432     char *data_name;
433     int retval;
434     int verify_result;
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, 1000000);  /* 1 sec delay for on failure */
442     }
443 #endif
444
445     /* locate the entry for this user */
446
447     D(("locating user's record"));
448     retval = pwdb_locate("user", PWDB_DEFAULT, name, PWDB_ID_UNKNOWN, &pw);
449     if (retval == PWDB_PASS_PHRASE_REQD) {
450         /*
451          * give the password to the pwdb library. It may be needed to
452          * access the database
453          */
454
455         retval = pwdb_set_entry( pw, "pass_phrase", p, 1+strlen(p)
456                                  , NULL, NULL, 0);
457         if (retval != PWDB_SUCCESS) {
458             _log_err(LOG_ALERT, "find pass; %s", pwdb_strerror(retval));
459             (void) pwdb_delete(&pw);
460             p = NULL;
461             return PAM_CRED_INSUFFICIENT;
462         }
463
464         retval = pwdb_locate("user", pw->source, name, PWDB_ID_UNKNOWN, &pw);
465     }
466
467     if (retval != PWDB_SUCCESS) {
468         D(("user's record unavailable"));
469         if ( on(UNIX_AUDIT, ctrl) ) {
470             /* this might be a typo and the user has given a password
471                instead of a username. Careful with this. */
472             _log_err(LOG_ALERT, "check pass; user (%s) unknown", name);
473         } else {
474             _log_err(LOG_ALERT, "check pass; user unknown");
475         }
476         (void) pwdb_delete(&pw);
477         p = NULL;
478         return PAM_USER_UNKNOWN;
479     }
480
481     /*
482      * courtesy of PWDB the password for the user is stored in
483      * encrypted form in the "passwd" entry of pw.
484      */
485
486     retval = pwdb_get_entry(pw, "passwd", &pwe);
487     if (retval != PWDB_SUCCESS) {
488         if (geteuid()) {
489             /* we are not root perhaps this is the reason? Run helper */
490             D(("running helper binary"));
491             retval = pwdb_run_helper_binary(pamh, p, ctrl, name);
492         } else {
493             retval = PAM_AUTHINFO_UNAVAIL;
494             _log_err(LOG_ALERT, "get passwd; %s", pwdb_strerror(retval));
495         }
496         (void) pwdb_delete(&pw);
497         p = NULL;
498         return retval;
499     }
500     salt = (const char *) pwe->value;
501
502     /*
503      * XXX: Cristian, the above is not the case for RADIUS(?) Some
504      * lines should be added for RADIUS to verify the password in
505      * clear text...
506      */
507
508     data_name = (char *) malloc(sizeof(FAIL_PREFIX)+strlen(name));
509     if ( data_name == NULL ) {
510         _log_err(LOG_CRIT, "no memory for data-name");
511     }
512     strcpy(data_name, FAIL_PREFIX);
513     strcpy(data_name + sizeof(FAIL_PREFIX)-1, name);
514
515     if ( !( (salt && *salt) || (p && *p) ) ) {
516
517         D(("two null passwords to compare"));
518
519         /* the stored password is NULL */
520         pp = NULL;
521         if ( off(UNIX__NONULL, ctrl ) ) {    /* this means we've succeeded */
522             verify_result = PAM_SUCCESS;
523         } else {
524             verify_result = PAM_AUTH_ERR;
525         }
526
527     } else if ( !( salt && p ) ) {
528
529         D(("one of the two to compare are NULL"));
530
531         pp = NULL;
532         verify_result = PAM_AUTH_ERR;
533
534     } else {
535
536         /* there is no way that p can be NULL (one can be "") */
537         pp = _pam_md(p, salt);
538
539         /* the moment of truth -- do we agree with the password? */
540         D(("comparing state of pp[%s] and salt[%s]", pp, salt));
541
542         if ( strcmp( pp, salt ) == 0 ) {
543             verify_result = PAM_SUCCESS;
544         } else {
545             _pam_delete(pp);
546             pp = _pam_md_compat(p, salt);
547             if ( strcmp( pp, salt ) == 0 ) {
548                 verify_result = PAM_SUCCESS;
549             } else {
550                 verify_result = PAM_AUTH_ERR;
551             }
552         }
553
554         p = NULL;                                /* no longer needed here */
555
556     }
557
558     if ( verify_result == PAM_SUCCESS ) {
559
560         retval = PAM_SUCCESS;
561         if (data_name) {                     /* reset failures */
562             pam_set_data(pamh, data_name, NULL, _cleanup_failures);
563         }
564
565     } else {
566
567         retval = PAM_AUTH_ERR;
568         if (data_name != NULL) {
569             struct _pam_failed_auth *new=NULL;
570             const struct _pam_failed_auth *old=NULL;
571
572             /* get a failure recorder */
573
574             new = (struct _pam_failed_auth *)
575                 malloc(sizeof(struct _pam_failed_auth));
576
577             if (new != NULL) {
578
579                 new->user = x_strdup(name);
580                 new->id = getuid();
581                 new->name = x_strdup(getlogin() ? getlogin():"" );
582
583                 /* any previous failures for this user ? */
584                 pam_get_data(pamh, data_name, (const void **)&old );
585
586                 if (old != NULL) {
587                     new->count = old->count +1;
588                     if (new->count >= UNIX_MAX_RETRIES) {
589                         retval = PAM_MAXTRIES;
590                     }
591                 } else {
592                     const char *service=NULL;
593                     (void) pam_get_item(pamh, PAM_SERVICE
594                                         , (const void **)&service);
595                     _log_err(LOG_NOTICE
596                              , "authentication failure; %s(uid=%d) -> "
597                              "%s for %s service"
598                              , new->name
599                              , new->id
600                              , new->user
601                              , service == NULL ? "**unknown**":service
602                         );
603                     new->count = 1;
604                 }
605
606                 pam_set_data(pamh, data_name, new, _cleanup_failures);
607
608             } else {
609                 _log_err(LOG_CRIT, "no memory for failure recorder");
610             }
611         }
612
613     }
614
615     (void) pwdb_entry_delete(&pwe);
616     (void) pwdb_delete(&pw);
617     salt = NULL;
618     _pam_delete(data_name);
619     _pam_delete(pp);
620
621     D(("done [%d].", retval));
622
623     return retval;
624 }
625
626 /*
627  * this function obtains the name of the current user and ensures
628  * that the PAM_USER item is set to this value
629  */
630
631 static int _unix_get_user(pam_handle_t *pamh, unsigned int ctrl
632                           , const char *prompt, const char **user)
633 {
634     int retval;
635
636     D(("called"));
637
638     retval = pam_get_user(pamh, user, prompt);
639     if (retval != PAM_SUCCESS) {
640         D(("trouble reading username"));
641         return retval;
642     }
643
644     /*
645      * Various libraries at various times have had bugs related to
646      * '+' or '-' as the first character of a user name. Don't take
647      * any chances here. Require that the username starts with an
648      * alphanumeric character.
649      */
650
651     if (*user == NULL || !isalnum(**user)) {
652         D(("bad username"));
653         if (on(UNIX_DEBUG,ctrl)) {
654             _log_err(LOG_ERR, "bad username [%s]", *user);
655         }
656         return PAM_USER_UNKNOWN;
657     }
658
659     if (retval == PAM_SUCCESS && on(UNIX_DEBUG,ctrl)) {
660         _log_err(LOG_DEBUG, "username [%s] obtained", *user);
661     }
662
663     return retval;
664 }
665
666 /*
667  * _unix_blankpasswd() is a quick check for a blank password
668  *
669  * returns TRUE if user does not have a password
670  * - to avoid prompting for one in such cases (CG)
671  */
672
673 static int _unix_blankpasswd(unsigned int ctrl, const char *name)
674 {
675     const struct pwdb *pw=NULL;
676     const struct pwdb_entry *pwe=NULL;
677     int retval;
678
679     D(("called"));
680
681     /*
682      * This function does not have to be too smart if something goes
683      * wrong, return FALSE and let this case to be treated somewhere
684      * else (CG)
685      */
686
687     if ( on(UNIX__NONULL, ctrl) )
688         return 0;                     /* will fail but don't let on yet */
689
690     /* find the user's database entry */
691
692     retval = pwdb_locate("user", PWDB_DEFAULT, name, PWDB_ID_UNKNOWN, &pw);
693     if (retval != PWDB_SUCCESS || pw == NULL ) {
694
695         retval = 0;
696
697     } else {
698
699         /* Does this user have a password? */
700
701         retval = pwdb_get_entry(pw, "passwd", &pwe);
702         if ( retval != PWDB_SUCCESS || pwe == NULL )
703             retval = 0;
704         else if ( pwe->value == NULL || ((char *)pwe->value)[0] == '\0' )
705             retval = 1;
706         else
707             retval = 0;
708
709     }
710
711     /* tidy up */
712
713     if ( pw ) {
714         (void) pwdb_delete(&pw);
715         if ( pwe )
716             (void) pwdb_entry_delete(&pwe);
717     }
718
719     return retval;
720 }
721
722 /*
723  * obtain a password from the user
724  */
725
726 static int _unix_read_password( pam_handle_t *pamh
727                                 , unsigned int ctrl
728                                 , const char *comment
729                                 , const char *prompt1
730                                 , const char *prompt2
731                                 , const char *data_name
732                                 , const char **pass )
733 {
734     int authtok_flag;
735     int retval;
736     const char *item;
737     char *token;
738
739     D(("called"));
740
741     /*
742      * make sure nothing inappropriate gets returned
743      */
744
745     *pass = token = NULL;
746
747     /*
748      * which authentication token are we getting?
749      */
750
751     authtok_flag = on(UNIX__OLD_PASSWD,ctrl) ? PAM_OLDAUTHTOK:PAM_AUTHTOK ;
752
753     /*
754      * should we obtain the password from a PAM item ?
755      */
756
757     if ( on(UNIX_TRY_FIRST_PASS,ctrl) || on(UNIX_USE_FIRST_PASS,ctrl) ) {
758         retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
759         if (retval != PAM_SUCCESS ) {
760             /* very strange. */
761             _log_err(LOG_ALERT
762                      , "pam_get_item returned error to unix-read-password"
763                 );
764             return retval;
765         } else if (item != NULL) {    /* we have a password! */
766             *pass = item;
767             item = NULL;
768             return PAM_SUCCESS;
769         } else if (on(UNIX_USE_FIRST_PASS,ctrl)) {
770             return PAM_AUTHTOK_RECOVER_ERR;       /* didn't work */
771         } else if (on(UNIX_USE_AUTHTOK, ctrl)
772                    && off(UNIX__OLD_PASSWD, ctrl)) {
773             return PAM_AUTHTOK_RECOVER_ERR;
774         }
775     }
776
777     /*
778      * getting here implies we will have to get the password from the
779      * user directly.
780      */
781
782     {
783         struct pam_message msg[3],*pmsg[3];
784         struct pam_response *resp;
785         int i, replies;
786
787         /* prepare to converse */
788
789         if ( comment != NULL && off(UNIX__QUIET, ctrl) ) {
790             pmsg[0] = &msg[0];
791             msg[0].msg_style = PAM_TEXT_INFO;
792             msg[0].msg = comment;
793             i = 1;
794         } else {
795             i = 0;
796         }
797
798         pmsg[i] = &msg[i];
799         msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
800         msg[i++].msg = prompt1;
801         replies = 1;
802
803         if ( prompt2 != NULL ) {
804             pmsg[i] = &msg[i];
805             msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
806             msg[i++].msg = prompt2;
807             ++replies;
808         }
809
810         /* so call the conversation expecting i responses */
811         resp = NULL;
812         retval = converse(pamh, ctrl, i, pmsg, &resp);
813
814         if (resp != NULL) {
815
816             /* interpret the response */
817
818             if (retval == PAM_SUCCESS) {     /* a good conversation */
819
820                 token = x_strdup(resp[i-replies].resp);
821                 if (token != NULL) {
822                     if (replies == 2) {
823
824                         /* verify that password entered correctly */
825                         if (!resp[i-1].resp
826                             || strcmp(token,resp[i-1].resp)) {
827                             token = _pam_delete(token); /* mistyped */
828                             retval = PAM_AUTHTOK_RECOVER_ERR;
829                             make_remark(pamh, ctrl
830                                         , PAM_ERROR_MSG, MISTYPED_PASS);
831                         }
832                     }
833
834                 } else {
835                     _log_err(LOG_NOTICE
836                              , "could not recover authentication token");
837                 }
838
839             }
840
841             /*
842              * tidy up the conversation (resp_retcode) is ignored
843              * -- what is it for anyway? AGM
844              */
845
846             _pam_drop_reply(resp, i);
847
848         } else {
849             retval = (retval == PAM_SUCCESS)
850                 ? PAM_AUTHTOK_RECOVER_ERR:retval ;
851         }
852     }
853
854     if (retval != PAM_SUCCESS) {
855         if ( on(UNIX_DEBUG,ctrl) )
856             _log_err(LOG_DEBUG,"unable to obtain a password");
857         return retval;
858     }
859
860     /* 'token' is the entered password */
861
862     if ( off(UNIX_NOT_SET_PASS, ctrl) ) {
863
864         /* we store this password as an item */
865
866         retval = pam_set_item(pamh, authtok_flag, token);
867         token = _pam_delete(token);   /* clean it up */
868         if ( retval != PAM_SUCCESS
869              || (retval = pam_get_item(pamh, authtok_flag
870                                        , (const void **)&item))
871              != PAM_SUCCESS ) {
872
873             _log_err(LOG_CRIT, "error manipulating password");
874             return retval;
875
876         }
877
878     } else {
879         /*
880          * then store it as data specific to this module. pam_end()
881          * will arrange to clean it up.
882          */
883
884         retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
885         if (retval != PAM_SUCCESS) {
886             _log_err(LOG_CRIT, "error manipulating password data [%s]"
887                      , pam_strerror(pamh, retval) );
888             token = _pam_delete(token);
889             return retval;
890         }
891         item = token;
892         token = NULL;           /* break link to password */
893     }
894
895     *pass = item;
896     item = NULL;                 /* break link to password */
897
898     return PAM_SUCCESS;
899 }
900
901 static int _pam_unix_approve_pass(pam_handle_t *pamh
902                                   , unsigned int ctrl
903                                   , const char *pass_old
904                                   , const char *pass_new)
905 {
906      D(("&new=%p, &old=%p",pass_old,pass_new));
907      D(("new=[%s]",pass_new));
908      D(("old=[%s]",pass_old));
909
910      if (pass_new == NULL || (pass_old && !strcmp(pass_old,pass_new))) {
911           if ( on(UNIX_DEBUG, ctrl) ) {
912                _log_err(LOG_DEBUG, "bad authentication token");
913           }
914           make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
915                        "No password supplied":"Password unchanged" );
916           return PAM_AUTHTOK_ERR;
917      }
918
919      /*
920       * if one wanted to hardwire authentication token strength
921       * checking this would be the place - AGM
922       */
923
924      return PAM_SUCCESS;
925 }
926
927 /* ****************************************************************** *
928  * Copyright (c) Andrew G. Morgan 1996-8.
929  * Copyright (c) Alex O. Yuriev, 1996.
930  * Copyright (c) Cristian Gafton 1996.
931  *
932  * Redistribution and use in source and binary forms, with or without
933  * modification, are permitted provided that the following conditions
934  * are met:
935  * 1. Redistributions of source code must retain the above copyright
936  *    notice, and the entire permission notice in its entirety,
937  *    including the disclaimer of warranties.
938  * 2. Redistributions in binary form must reproduce the above copyright
939  *    notice, this list of conditions and the following disclaimer in the
940  *    documentation and/or other materials provided with the distribution.
941  * 3. The name of the author may not be used to endorse or promote
942  *    products derived from this software without specific prior
943  *    written permission.
944  * 
945  * ALTERNATIVELY, this product may be distributed under the terms of
946  * the GNU Public License, in which case the provisions of the GPL are
947  * required INSTEAD OF the above restrictions.  (This clause is
948  * necessary due to a potential bad interaction between the GPL and
949  * the restrictions contained in a BSD-style copyright.)
950  * 
951  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
952  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
953  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
954  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
955  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
956  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
957  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
958  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
959  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
960  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
961  * OF THE POSSIBILITY OF SUCH DAMAGE.
962  */
963