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