pam_smbpass updates from a.bokovoy@sam-solutions.net
[bbaumbach/samba-autobuild/.git] / source3 / pam_smbpass / support.c
1 /* Unix NT password database implementation, version 0.6.
2  *
3  * This program is free software; you can redistribute it and/or modify it under
4  * the terms of the GNU General Public License as published by the Free
5  * Software Foundation; either version 2 of the License, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; if not, write to the Free Software Foundation, Inc., 675
15  * Mass Ave, Cambridge, MA 02139, USA.
16  */
17
18 #include "includes.h"
19 #include "general.h"
20
21 #include "support.h"
22
23
24 #define _pam_overwrite(x)        \
25 do {                             \
26      register char *__xx__;      \
27      if ((__xx__=(x)))           \
28           while (*__xx__)        \
29                *__xx__++ = '\0'; \
30 } while (0)
31
32 /*
33  * Don't just free it, forget it too.
34  */
35
36 #define _pam_drop(X) \
37 do {                 \
38     if (X) {         \
39         free(X);     \
40         X=NULL;      \
41     }                \
42 } while (0)
43
44 #define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
45 do {                                              \
46     int reply_i;                                  \
47                                                   \
48     for (reply_i=0; reply_i<replies; ++reply_i) { \
49         if (reply[reply_i].resp) {                \
50             _pam_overwrite(reply[reply_i].resp);  \
51             free(reply[reply_i].resp);            \
52         }                                         \
53     }                                             \
54     if (reply)                                    \
55         free(reply);                              \
56 } while (0)
57
58
59 int converse(pam_handle_t *, int, int, struct pam_message **,
60                          struct pam_response **);
61 int make_remark(pam_handle_t *, unsigned int, int, const char *);
62 void _cleanup(pam_handle_t *, void *, int);
63 char *_pam_delete(register char *);
64
65 /* syslogging function for errors and other information */
66
67 void _log_err( int err, const char *format, ... )
68 {
69     va_list args;
70
71     va_start( args, format );
72     openlog( "PAM_smbpass", LOG_CONS | LOG_PID, LOG_AUTH );
73     vsyslog( err, format, args );
74     va_end( args );
75     closelog();
76 }
77
78 /* this is a front-end for module-application conversations */
79
80 int converse( pam_handle_t * pamh, int ctrl, int nargs
81               , struct pam_message **message
82               , struct pam_response **response )
83 {
84         int retval;
85         struct pam_conv *conv;
86
87         retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
88         if (retval == PAM_SUCCESS) {
89
90                 retval = conv->conv(nargs, (const struct pam_message **) message
91                                                         ,response, conv->appdata_ptr);
92
93                 if (retval != PAM_SUCCESS && on(SMB_DEBUG, ctrl)) {
94                         _log_err(LOG_DEBUG, "conversation failure [%s]"
95                                          ,pam_strerror(pamh, retval));
96                 }
97         } else {
98                 _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
99                                  ,pam_strerror(pamh, retval));
100         }
101
102         return retval;                          /* propagate error status */
103 }
104
105 int make_remark( pam_handle_t * pamh, unsigned int ctrl
106                  , int type, const char *text )
107 {
108         if (off(SMB__QUIET, ctrl)) {
109                 struct pam_message *pmsg[1], msg[1];
110                 struct pam_response *resp;
111
112                 pmsg[0] = &msg[0];
113                 msg[0].msg = text;
114                 msg[0].msg_style = type;
115                 resp = NULL;
116
117                 return converse(pamh, ctrl, 1, pmsg, &resp);
118         }
119         return PAM_SUCCESS;
120 }
121
122
123 /* set the control flags for the SMB module. */
124
125 int set_ctrl( int flags, int argc, const char **argv )
126 {
127     int i = 0;
128     const char *service_file = dyn_CONFIGFILE;
129     unsigned int ctrl;
130
131     ctrl = SMB_DEFAULTS;        /* the default selection of options */
132
133     /* set some flags manually */
134
135     /* A good, sane default (matches Samba's behavior). */
136     set( SMB__NONULL, ctrl );
137
138     if (flags & PAM_SILENT) {
139         set( SMB__QUIET, ctrl );
140     }
141
142     /* Run through the arguments once, looking for an alternate smb config
143        file location */
144     while (i < argc) {
145         int j;
146
147         for (j = 0; j < SMB_CTRLS_; ++j) {
148             if (smb_args[j].token
149                 && !strncmp(argv[i], smb_args[j].token, strlen(smb_args[j].token)))
150             {
151                 break;
152             }
153         }
154
155         if (j == SMB_CONF_FILE) {
156             service_file = argv[i] + 8;
157         }
158         i++;
159     }
160
161     /* Read some options from the Samba config. Can be overridden by
162        the PAM config. */
163     if(lp_load(service_file,True,False,False) == False) {
164         _log_err( LOG_ERR, "Error loading service file %s", service_file );
165     }
166
167     if (lp_null_passwords()) {
168         set( SMB__NULLOK, ctrl );
169     }
170
171     /* now parse the rest of the arguments to this module */
172
173     while (argc-- > 0) {
174         int j;
175
176         for (j = 0; j < SMB_CTRLS_; ++j) {
177             if (smb_args[j].token
178                 && !strncmp(*argv, smb_args[j].token, strlen(smb_args[j].token)))
179             {
180                 break;
181             }
182         }
183
184         if (j >= SMB_CTRLS_) {
185             _log_err( LOG_ERR, "unrecognized option [%s]", *argv );
186         } else {
187             ctrl &= smb_args[j].mask;   /* for turning things off */
188             ctrl |= smb_args[j].flag;   /* for turning things on  */
189         }
190
191         ++argv;                         /* step to next argument */
192     }
193
194     /* auditing is a more sensitive version of debug */
195
196     if (on( SMB_AUDIT, ctrl )) {
197         set( SMB_DEBUG, ctrl );
198     }
199     /* return the set of flags */
200
201     return ctrl;
202 }
203
204 /* use this to free strings. ESPECIALLY password strings */
205
206 char * _pam_delete( register char *xx )
207 {
208     _pam_overwrite( xx );
209     _pam_drop( xx );
210     return NULL;
211 }
212
213 void _cleanup( pam_handle_t * pamh, void *x, int error_status )
214 {
215     x = _pam_delete( (char *) x );
216 }
217
218 /* ************************************************************** *
219  * Useful non-trivial functions                                   *
220  * ************************************************************** */
221
222 void _cleanup_failures( pam_handle_t * pamh, void *fl, int err )
223 {
224     int quiet;
225     const char *service = NULL;
226     struct _pam_failed_auth *failure;
227
228 #ifdef PAM_DATA_SILENT
229     quiet = err & PAM_DATA_SILENT;      /* should we log something? */
230 #else
231     quiet = 0;
232 #endif
233 #ifdef PAM_DATA_REPLACE
234     err &= PAM_DATA_REPLACE;    /* are we just replacing data? */
235 #endif
236     failure = (struct _pam_failed_auth *) fl;
237
238     if (failure != NULL) {
239
240 #ifdef PAM_DATA_SILENT
241         if (!quiet && !err) {   /* under advisement from Sun,may go away */
242 #else
243         if (!quiet) {   /* under advisement from Sun,may go away */
244 #endif
245
246             /* log the number of authentication failures */
247             if (failure->count != 0) {
248                 pam_get_item( pamh, PAM_SERVICE, (const void **) &service );
249                 _log_err( LOG_NOTICE
250                           , "%d authentication %s "
251                             "from %s for service %s as %s(%d)"
252                           , failure->count
253                           , failure->count == 1 ? "failure" : "failures"
254                           , failure->agent
255                           , service == NULL ? "**unknown**" : service 
256                           , failure->user, failure->id );
257                 if (failure->count > SMB_MAX_RETRIES) {
258                     _log_err( LOG_ALERT
259                               , "service(%s) ignoring max retries; %d > %d"
260                               , service == NULL ? "**unknown**" : service
261                               , failure->count
262                               , SMB_MAX_RETRIES );
263                 }
264             }
265         }
266         _pam_delete( failure->agent );  /* tidy up */
267         _pam_delete( failure->user );   /* tidy up */
268         free( failure );
269     }
270 }
271
272 int _smb_verify_password( pam_handle_t * pamh
273                           , const struct smb_passwd *smb_pwent
274                           , const char *p, unsigned int ctrl )
275 {
276     uchar hash_pass[16];
277     uchar lm_pw[16];
278     uchar nt_pw[16];
279     int retval;
280     char *data_name;
281     const char *name;
282
283     if (!smb_pwent)
284         return PAM_ABORT;
285
286     name = smb_pwent->smb_name;
287
288 #ifdef HAVE_PAM_FAIL_DELAY
289     if (off( SMB_NODELAY, ctrl )) {
290         (void) pam_fail_delay( pamh, 1000000 ); /* 1 sec delay for on failure */
291     }
292 #endif
293
294     if (!smb_pwent->smb_passwd)
295     {
296         _log_err( LOG_DEBUG, "user %s has null SMB password"
297                   , name );
298
299         if (off( SMB__NONULL, ctrl )
300             && (smb_pwent->acct_ctrl & ACB_PWNOTREQ))
301         { /* this means we've succeeded */
302             return PAM_SUCCESS;
303         } else {
304             const char *service;
305
306             pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
307             _log_err( LOG_NOTICE
308                       , "failed auth request by %s for service %s as %s(%d)"
309                       , uidtoname( getuid() )
310                       , service ? service : "**unknown**", name
311                       , smb_pwent->smb_userid );
312             return PAM_AUTH_ERR;
313         }
314     }
315
316     data_name = (char *) malloc( sizeof(FAIL_PREFIX) 
317                                  + strlen( name ));
318     if (data_name == NULL) {
319         _log_err( LOG_CRIT, "no memory for data-name" );
320     }
321     strncpy( data_name, FAIL_PREFIX, sizeof(FAIL_PREFIX) );
322     strncpy( data_name + sizeof(FAIL_PREFIX) - 1, name, strlen( name ) + 1 );
323
324     /* First we check whether we've been given the password in already
325        encrypted form. */
326     if (strlen( p ) == 16 || (strlen( p ) == 32
327          && pdb_gethexpwd( p, (char *) hash_pass ))) {
328
329         if (!memcmp( hash_pass, smb_pwent->smb_passwd, 16 )
330             || (smb_pwent->smb_nt_passwd
331                 && !memcmp( hash_pass, smb_pwent->smb_nt_passwd, 16 )))
332         {
333             retval = PAM_SUCCESS;
334             if (data_name) {    /* reset failures */
335                 pam_set_data( pamh, data_name, NULL, _cleanup_failures );
336             }
337             _pam_delete( data_name );
338             memset( hash_pass, '\0', 16 );
339             smb_pwent = NULL;
340             return retval;
341         }
342     }
343
344     /*
345      * The password we were given wasn't an encrypted password, or it
346      * didn't match the one we have.  We encrypt the password now and try
347      * again.
348      */
349
350     nt_lm_owf_gen(p, nt_pw, lm_pw);
351
352     /* the moment of truth -- do we agree with the password? */
353
354     if (!memcmp( nt_pw, smb_pwent->smb_nt_passwd, 16 )) {
355
356         retval = PAM_SUCCESS;
357         if (data_name) {                /* reset failures */
358             pam_set_data(pamh, data_name, NULL, _cleanup_failures);
359         }
360     } else {
361
362         const char *service;
363
364         pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
365
366         if (data_name != NULL) {
367             struct _pam_failed_auth *new = NULL;
368             const struct _pam_failed_auth *old = NULL;
369
370             /* get a failure recorder */
371
372             new = (struct _pam_failed_auth *)
373                       malloc( sizeof(struct _pam_failed_auth) );
374
375             if (new != NULL) {
376
377                 /* any previous failures for this user ? */
378                 pam_get_data(pamh, data_name, (const void **) &old);
379
380                 if (old != NULL) {
381                     new->count = old->count + 1;
382                     if (new->count >= SMB_MAX_RETRIES) {
383                         retval = PAM_MAXTRIES;
384                     }
385                 } else {
386                     _log_err( LOG_NOTICE
387                       , "failed auth request by %s for service %s as %s(%d)"
388                       , uidtoname( getuid() )
389                       , service ? service : "**unknown**", name
390                       , smb_pwent->smb_userid );
391                     new->count = 1;
392                 }
393                 new->user = smb_xstrdup( name );
394                 new->id = smb_pwent->smb_userid;
395                 new->agent = smb_xstrdup( uidtoname( getuid() ) );
396                 pam_set_data( pamh, data_name, new, _cleanup_failures );
397
398             } else {
399                 _log_err( LOG_CRIT, "no memory for failure recorder" );
400                 _log_err( LOG_NOTICE
401                       , "failed auth request by %s for service %s as %s(%d)"
402                       , uidtoname( getuid() )
403                       , service ? service : "**unknown**", name
404                       , smb_pwent->smb_userid );
405             }
406         } else {
407             _log_err( LOG_NOTICE
408                       , "failed auth request by %s for service %s as %s(%d)"
409                       , uidtoname( getuid() )
410                       , service ? service : "**unknown**", name
411                       , smb_pwent->smb_userid );
412             retval = PAM_AUTH_ERR;
413         }
414     }
415
416     _pam_delete( data_name );
417     smb_pwent = NULL;
418     return retval;
419 }
420
421
422 /*
423  * _smb_blankpasswd() is a quick check for a blank password
424  *
425  * returns TRUE if user does not have a password
426  * - to avoid prompting for one in such cases (CG)
427  */
428
429 int _smb_blankpasswd( unsigned int ctrl, const struct smb_passwd *smb_pwent )
430 {
431         int retval;
432
433         /*
434          * This function does not have to be too smart if something goes
435          * wrong, return FALSE and let this case to be treated somewhere
436          * else (CG)
437          */
438
439         if (on( SMB__NONULL, ctrl ))
440                 return 0;               /* will fail but don't let on yet */
441
442         if (smb_pwent->smb_passwd == NULL)
443                 retval = 1;
444         else
445                 retval = 0;
446
447         return retval;
448 }
449
450 /*
451  * obtain a password from the user
452  */
453
454 int _smb_read_password( pam_handle_t * pamh, unsigned int ctrl
455                         , const char *comment, const char *prompt1
456                         , const char *prompt2, const char *data_name
457                         , const char **pass )
458 {
459     int authtok_flag;
460     int retval;
461     const char *item = NULL;
462     char *token;
463
464     struct pam_message msg[3], *pmsg[3];
465     struct pam_response *resp;
466     int i, expect;
467
468
469     /* make sure nothing inappropriate gets returned */
470
471     *pass = token = NULL;
472
473     /* which authentication token are we getting? */
474
475     authtok_flag = on(SMB__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
476
477     /* should we obtain the password from a PAM item ? */
478
479     if (on(SMB_TRY_FIRST_PASS, ctrl) || on(SMB_USE_FIRST_PASS, ctrl)) {
480         retval = pam_get_item( pamh, authtok_flag, (const void **) &item );
481         if (retval != PAM_SUCCESS) {
482             /* very strange. */
483             _log_err( LOG_ALERT
484                       , "pam_get_item returned error to smb_read_password" );
485             return retval;
486         } else if (item != NULL) {      /* we have a password! */
487             *pass = item;
488             item = NULL;
489             return PAM_SUCCESS;
490         } else if (on( SMB_USE_FIRST_PASS, ctrl )) {
491             return PAM_AUTHTOK_RECOVER_ERR;             /* didn't work */
492         } else if (on( SMB_USE_AUTHTOK, ctrl )
493                    && off( SMB__OLD_PASSWD, ctrl ))
494         {
495             return PAM_AUTHTOK_RECOVER_ERR;
496         }
497     }
498
499     /*
500      * getting here implies we will have to get the password from the
501      * user directly.
502      */
503
504     /* prepare to converse */
505     if (comment != NULL && off(SMB__QUIET, ctrl)) {
506         pmsg[0] = &msg[0];
507         msg[0].msg_style = PAM_TEXT_INFO;
508         msg[0].msg = comment;
509         i = 1;
510     } else {
511         i = 0;
512     }
513
514     pmsg[i] = &msg[i];
515     msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
516     msg[i++].msg = prompt1;
517
518     if (prompt2 != NULL) {
519         pmsg[i] = &msg[i];
520         msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
521         msg[i++].msg = prompt2;
522         expect = 2;
523     } else
524         expect = 1;
525
526     resp = NULL;
527
528     retval = converse( pamh, ctrl, i, pmsg, &resp );
529
530     if (resp != NULL) {
531         int j = comment ? 1 : 0;
532         /* interpret the response */
533
534         if (retval == PAM_SUCCESS) {    /* a good conversation */
535
536             token = smb_xstrdup(resp[j++].resp);
537             if (token != NULL) {
538                 if (expect == 2) {
539                     /* verify that password entered correctly */
540                     if (!resp[j].resp || strcmp( token, resp[j].resp )) {
541                         _pam_delete( token );
542                         retval = PAM_AUTHTOK_RECOVER_ERR;
543                         make_remark( pamh, ctrl, PAM_ERROR_MSG
544                                      , MISTYPED_PASS );
545                     }
546                 }
547             } else {
548                 _log_err(LOG_NOTICE, "could not recover authentication token");
549             }
550         }
551
552         /* tidy up */
553         _pam_drop_reply( resp, expect );
554
555     } else {
556         retval = (retval == PAM_SUCCESS) ? PAM_AUTHTOK_RECOVER_ERR : retval;
557     }
558
559     if (retval != PAM_SUCCESS) {
560         if (on( SMB_DEBUG, ctrl ))
561             _log_err( LOG_DEBUG, "unable to obtain a password" );
562         return retval;
563     }
564     /* 'token' is the entered password */
565
566     if (off( SMB_NOT_SET_PASS, ctrl )) {
567
568         /* we store this password as an item */
569
570         retval = pam_set_item( pamh, authtok_flag, (const void *)token );
571         _pam_delete( token );           /* clean it up */
572         if (retval != PAM_SUCCESS
573             || (retval = pam_get_item( pamh, authtok_flag
574                             ,(const void **)&item )) != PAM_SUCCESS)
575         {
576             _log_err( LOG_CRIT, "error manipulating password" );
577             return retval;
578         }
579     } else {
580         /*
581          * then store it as data specific to this module. pam_end()
582          * will arrange to clean it up.
583          */
584
585         retval = pam_set_data( pamh, data_name, (void *) token, _cleanup );
586         if (retval != PAM_SUCCESS
587             || (retval = pam_get_data( pamh, data_name, (const void **)&item ))
588                              != PAM_SUCCESS)
589         {
590             _log_err( LOG_CRIT, "error manipulating password data [%s]"
591                       , pam_strerror( pamh, retval ));
592             _pam_delete( token );
593             item = NULL;
594             return retval;
595         }
596         token = NULL;                   /* break link to password */
597     }
598
599     *pass = item;
600     item = NULL;                        /* break link to password */
601
602     return PAM_SUCCESS;
603 }
604
605 int _pam_smb_approve_pass(pam_handle_t * pamh
606                                                   ,unsigned int ctrl
607                                                   ,const char *pass_old
608                                                   ,const char *pass_new)
609 {
610
611     /* Further checks should be handled through module stacking. -SRL */
612     if (pass_new == NULL || (pass_old && !strcmp( pass_old, pass_new )))
613     {
614         if (on(SMB_DEBUG, ctrl)) {
615             _log_err( LOG_DEBUG,
616                       "passwd: bad authentication token (null or unchanged)" );
617         }
618         make_remark( pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
619                                 "No password supplied" : "Password unchanged" );
620         return PAM_AUTHTOK_ERR;
621     }
622
623     return PAM_SUCCESS;
624 }