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