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