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