pam_smbpass/support.c

説明を見る。
00001         /* Unix NT password database implementation, version 0.6.
00002          *
00003          * This program is free software; you can redistribute it and/or modify it under
00004          * the terms of the GNU General Public License as published by the Free
00005          * Software Foundation; either version 2 of the License, or (at your option)
00006          * any later version.
00007          *
00008          * This program is distributed in the hope that it will be useful, but WITHOUT
00009          * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00010          * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
00011          * more details.
00012          *
00013          * You should have received a copy of the GNU General Public License along with
00014          * this program; if not, write to the Free Software Foundation, Inc., 675
00015          * Mass Ave, Cambridge, MA 02139, USA.
00016          */
00017 
00018         #include "includes.h"
00019         #include "general.h"
00020 
00021         #include "support.h"
00022 
00023 
00024         #define _pam_overwrite(x)        \
00025         do {                             \
00026              register char *__xx__;      \
00027              if ((__xx__=(x)))           \
00028                   while (*__xx__)        \
00029                        *__xx__++ = '\0'; \
00030         } while (0)
00031 
00032         /*
00033          * Don't just free it, forget it too.
00034          */
00035 
00036         #define _pam_drop(X) \
00037         do {                 \
00038             if (X) {         \
00039                 free(X);     \
00040                 X=NULL;      \
00041             }                \
00042         } while (0)
00043 
00044         #define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
00045         do {                                              \
00046             int reply_i;                                  \
00047                                                           \
00048             for (reply_i=0; reply_i<replies; ++reply_i) { \
00049                 if (reply[reply_i].resp) {                \
00050                     _pam_overwrite(reply[reply_i].resp);  \
00051                     free(reply[reply_i].resp);            \
00052                 }                                         \
00053             }                                             \
00054             if (reply)                                    \
00055                 free(reply);                              \
00056         } while (0)
00057 
00058 
00059         int converse(pam_handle_t *, int, int, struct pam_message **,
00060                                  struct pam_response **);
00061         int make_remark(pam_handle_t *, unsigned int, int, const char *);
00062         void _cleanup(pam_handle_t *, void *, int);
00063         char *_pam_delete(register char *);
00064 
00065         /* default configuration file location */
00066 
00067         char *servicesf = dyn_CONFIGFILE;
00068 
00069         /* syslogging function for errors and other information */
00070 
00071         void _log_err( int err, const char *format, ... )
00072         {
00073             va_list args;
00074 
00075             va_start( args, format );
00076             openlog( "PAM_smbpass", LOG_CONS | LOG_PID, LOG_AUTH );
00077             vsyslog( err, format, args );
00078             va_end( args );
00079             closelog();
00080         }
00081 
00082         /* this is a front-end for module-application conversations */
00083 
00084         int converse( pam_handle_t * pamh, int ctrl, int nargs
00085                       , struct pam_message **message
00086                       , struct pam_response **response )
00087         {
00088                 int retval;
00089                 struct pam_conv *conv;
00090 
00091                 retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
00092                 if (retval == PAM_SUCCESS) {
00093 
00094                         retval = conv->conv(nargs, (const struct pam_message **) message
00095                                                                 ,response, conv->appdata_ptr);
00096 
00097                         if (retval != PAM_SUCCESS && on(SMB_DEBUG, ctrl)) {
00098                                 _log_err(LOG_DEBUG, "conversation failure [%s]"
00099                                                  ,pam_strerror(pamh, retval));
00100                         }
00101                 } else {
00102                         _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
00103                                          ,pam_strerror(pamh, retval));
00104                 }
00105 
00106                 return retval;                          /* propagate error status */
00107         }
00108 
00109         int make_remark( pam_handle_t * pamh, unsigned int ctrl
00110                          , int type, const char *text )
00111         {
00112                 if (off(SMB__QUIET, ctrl)) {
00113                         struct pam_message *pmsg[1], msg[1];
00114                         struct pam_response *resp;
00115 
00116                         pmsg[0] = &msg[0];
00117                         msg[0].msg = CONST_DISCARD(char *, text);
00118                         msg[0].msg_style = type;
00119                         resp = NULL;
00120 
00121                         return converse(pamh, ctrl, 1, pmsg, &resp);
00122                 }
00123                 return PAM_SUCCESS;
00124         }
00125 
00126 
00127         /* set the control flags for the SMB module. */
00128 
00129 int set_ctrl( int flags, int argc, const char **argv )
00130 {
00131     int i = 0;
00132     const char *service_file = dyn_CONFIGFILE;
00133     unsigned int ctrl;
00134 
00135     ctrl = SMB_DEFAULTS;        /* the default selection of options */
00136 
00137     /* set some flags manually */
00138 
00139     /* A good, sane default (matches Samba's behavior). */
00140     set( SMB__NONULL, ctrl );
00141 
00142     /* initialize service file location */
00143     service_file=servicesf;
00144 
00145     if (flags & PAM_SILENT) {
00146         set( SMB__QUIET, ctrl );
00147     }
00148 
00149     /* Run through the arguments once, looking for an alternate smb config
00150        file location */
00151     while (i < argc) {
00152         int j;
00153 
00154         for (j = 0; j < SMB_CTRLS_; ++j) {
00155             if (smb_args[j].token
00156                 && !strncmp(argv[i], smb_args[j].token, strlen(smb_args[j].token)))
00157             {
00158                 break;
00159             }
00160         }
00161 
00162         if (j == SMB_CONF_FILE) {
00163             service_file = argv[i] + 8;
00164         }
00165         i++;
00166     }
00167 
00168     /* Read some options from the Samba config. Can be overridden by
00169        the PAM config. */
00170     if(lp_load(service_file,True,False,False,True) == False) {
00171         _log_err( LOG_ERR, "Error loading service file %s", service_file );
00172     }
00173 
00174     secrets_init();
00175 
00176     if (lp_null_passwords()) {
00177         set( SMB__NULLOK, ctrl );
00178     }
00179 
00180     /* now parse the rest of the arguments to this module */
00181 
00182     while (argc-- > 0) {
00183         int j;
00184 
00185         for (j = 0; j < SMB_CTRLS_; ++j) {
00186             if (smb_args[j].token
00187                 && !strncmp(*argv, smb_args[j].token, strlen(smb_args[j].token)))
00188             {
00189                 break;
00190             }
00191         }
00192 
00193         if (j >= SMB_CTRLS_) {
00194             _log_err( LOG_ERR, "unrecognized option [%s]", *argv );
00195         } else {
00196             ctrl &= smb_args[j].mask;   /* for turning things off */
00197             ctrl |= smb_args[j].flag;   /* for turning things on  */
00198         }
00199 
00200         ++argv;                         /* step to next argument */
00201     }
00202 
00203     /* auditing is a more sensitive version of debug */
00204 
00205     if (on( SMB_AUDIT, ctrl )) {
00206         set( SMB_DEBUG, ctrl );
00207     }
00208     /* return the set of flags */
00209 
00210     return ctrl;
00211 }
00212 
00213 /* use this to free strings. ESPECIALLY password strings */
00214 
00215 char * _pam_delete( register char *xx )
00216 {
00217     _pam_overwrite( xx );
00218     _pam_drop( xx );
00219     return NULL;
00220 }
00221 
00222 void _cleanup( pam_handle_t * pamh, void *x, int error_status )
00223 {
00224     x = _pam_delete( (char *) x );
00225 }
00226 
00227 /* JHT
00228  *
00229  * Safe duplication of character strings. "Paranoid"; don't leave
00230  * evidence of old token around for later stack analysis.
00231  *
00232  */
00233 char * smbpXstrDup( const char *x )
00234 {
00235     register char *newstr = NULL;
00236 
00237     if (x != NULL) {
00238         register int i;
00239 
00240         for (i = 0; x[i]; ++i); /* length of string */
00241         if ((newstr = SMB_MALLOC_ARRAY(char, ++i)) == NULL) {
00242             i = 0;
00243             _log_err( LOG_CRIT, "out of memory in smbpXstrDup" );
00244         } else {
00245             while (i-- > 0) {
00246                 newstr[i] = x[i];
00247             }
00248         }
00249         x = NULL;
00250     }
00251     return newstr;                      /* return the duplicate or NULL on error */
00252 }
00253 
00254 /* ************************************************************** *
00255  * Useful non-trivial functions                                   *
00256  * ************************************************************** */
00257 
00258 void _cleanup_failures( pam_handle_t * pamh, void *fl, int err )
00259 {
00260     int quiet;
00261     const char *service = NULL;
00262     struct _pam_failed_auth *failure;
00263 
00264 #ifdef PAM_DATA_SILENT
00265     quiet = err & PAM_DATA_SILENT;      /* should we log something? */
00266 #else
00267     quiet = 0;
00268 #endif
00269 #ifdef PAM_DATA_REPLACE
00270     err &= PAM_DATA_REPLACE;    /* are we just replacing data? */
00271 #endif
00272     failure = (struct _pam_failed_auth *) fl;
00273 
00274     if (failure != NULL) {
00275 
00276 #ifdef PAM_DATA_SILENT
00277         if (!quiet && !err) {   /* under advisement from Sun,may go away */
00278 #else
00279         if (!quiet) {   /* under advisement from Sun,may go away */
00280 #endif
00281 
00282             /* log the number of authentication failures */
00283             if (failure->count != 0) {
00284                 pam_get_item( pamh, PAM_SERVICE, (const void **) &service );
00285                 _log_err( LOG_NOTICE
00286                           , "%d authentication %s "
00287                             "from %s for service %s as %s(%d)"
00288                           , failure->count
00289                           , failure->count == 1 ? "failure" : "failures"
00290                           , failure->agent
00291                           , service == NULL ? "**unknown**" : service 
00292                           , failure->user, failure->id );
00293                 if (failure->count > SMB_MAX_RETRIES) {
00294                     _log_err( LOG_ALERT
00295                               , "service(%s) ignoring max retries; %d > %d"
00296                               , service == NULL ? "**unknown**" : service
00297                               , failure->count
00298                               , SMB_MAX_RETRIES );
00299                 }
00300             }
00301         }
00302         _pam_delete( failure->agent );  /* tidy up */
00303         _pam_delete( failure->user );   /* tidy up */
00304         SAFE_FREE( failure );
00305     }
00306 }
00307 
00308 int _smb_verify_password( pam_handle_t * pamh, struct samu *sampass,
00309                           const char *p, unsigned int ctrl )
00310 {
00311     uchar lm_pw[16];
00312     uchar nt_pw[16];
00313     int retval = PAM_AUTH_ERR;
00314     char *data_name;
00315     const char *name;
00316 
00317     if (!sampass)
00318         return PAM_ABORT;
00319 
00320     name = pdb_get_username(sampass);
00321 
00322 #ifdef HAVE_PAM_FAIL_DELAY
00323     if (off( SMB_NODELAY, ctrl )) {
00324         (void) pam_fail_delay( pamh, 1000000 ); /* 1 sec delay for on failure */
00325     }
00326 #endif
00327 
00328     if (!pdb_get_nt_passwd(sampass))
00329     {
00330         _log_err( LOG_DEBUG, "user %s has null SMB password"
00331                   , name );
00332 
00333         if (off( SMB__NONULL, ctrl )
00334             && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ))
00335         { /* this means we've succeeded */
00336             return PAM_SUCCESS;
00337         } else {
00338             const char *service;
00339 
00340             pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
00341             _log_err( LOG_NOTICE, "failed auth request by %s for service %s as %s",
00342                       uidtoname(getuid()), service ? service : "**unknown**", name);
00343             return PAM_AUTH_ERR;
00344         }
00345     }
00346 
00347     data_name = SMB_MALLOC_ARRAY(char, sizeof(FAIL_PREFIX) + strlen( name ));
00348     if (data_name == NULL) {
00349         _log_err( LOG_CRIT, "no memory for data-name" );
00350         return PAM_AUTH_ERR;
00351     }
00352     strncpy( data_name, FAIL_PREFIX, sizeof(FAIL_PREFIX) );
00353     strncpy( data_name + sizeof(FAIL_PREFIX) - 1, name, strlen( name ) + 1 );
00354 
00355     /*
00356      * The password we were given wasn't an encrypted password, or it
00357      * didn't match the one we have.  We encrypt the password now and try
00358      * again.
00359      */
00360 
00361     nt_lm_owf_gen(p, nt_pw, lm_pw);
00362 
00363     /* the moment of truth -- do we agree with the password? */
00364 
00365     if (!memcmp( nt_pw, pdb_get_nt_passwd(sampass), 16 )) {
00366 
00367         retval = PAM_SUCCESS;
00368         if (data_name) {                /* reset failures */
00369             pam_set_data(pamh, data_name, NULL, _cleanup_failures);
00370         }
00371     } else {
00372 
00373         const char *service;
00374 
00375         pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
00376 
00377         if (data_name != NULL) {
00378             struct _pam_failed_auth *newauth = NULL;
00379             const struct _pam_failed_auth *old = NULL;
00380 
00381             /* get a failure recorder */
00382 
00383             newauth = SMB_MALLOC_P( struct _pam_failed_auth );
00384 
00385             if (newauth != NULL) {
00386 
00387                 /* any previous failures for this user ? */
00388                 pam_get_data(pamh, data_name, (const void **) &old);
00389 
00390                 if (old != NULL) {
00391                     newauth->count = old->count + 1;
00392                     if (newauth->count >= SMB_MAX_RETRIES) {
00393                         retval = PAM_MAXTRIES;
00394                     }
00395                 } else {
00396                     _log_err(LOG_NOTICE,
00397                       "failed auth request by %s for service %s as %s",
00398                       uidtoname(getuid()),
00399                       service ? service : "**unknown**", name);
00400                     newauth->count = 1;
00401                 }
00402                 if (!sid_to_uid(pdb_get_user_sid(sampass), &(newauth->id))) {
00403                     _log_err(LOG_NOTICE,
00404                       "failed auth request by %s for service %s as %s",
00405                       uidtoname(getuid()),
00406                       service ? service : "**unknown**", name);
00407                 }               
00408                 newauth->user = smbpXstrDup( name );
00409                 newauth->agent = smbpXstrDup( uidtoname( getuid() ) );
00410                 pam_set_data( pamh, data_name, newauth, _cleanup_failures );
00411 
00412             } else {
00413                 _log_err( LOG_CRIT, "no memory for failure recorder" );
00414                 _log_err(LOG_NOTICE,
00415                       "failed auth request by %s for service %s as %s(%d)",
00416                       uidtoname(getuid()),
00417                       service ? service : "**unknown**", name);
00418             }
00419         }
00420         _log_err(LOG_NOTICE,
00421                   "failed auth request by %s for service %s as %s(%d)",
00422                   uidtoname(getuid()),
00423                   service ? service : "**unknown**", name);
00424         retval = PAM_AUTH_ERR;
00425     }
00426 
00427     _pam_delete( data_name );
00428     
00429     return retval;
00430 }
00431 
00432 
00433 /*
00434  * _smb_blankpasswd() is a quick check for a blank password
00435  *
00436  * returns TRUE if user does not have a password
00437  * - to avoid prompting for one in such cases (CG)
00438  */
00439 
00440 int _smb_blankpasswd( unsigned int ctrl, struct samu *sampass )
00441 {
00442         int retval;
00443 
00444         /*
00445          * This function does not have to be too smart if something goes
00446          * wrong, return FALSE and let this case to be treated somewhere
00447          * else (CG)
00448          */
00449 
00450         if (on( SMB__NONULL, ctrl ))
00451                 return 0;               /* will fail but don't let on yet */
00452 
00453         if (!(pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ))
00454                 return 0;
00455 
00456         if (pdb_get_nt_passwd(sampass) == NULL)
00457                 retval = 1;
00458         else
00459                 retval = 0;
00460 
00461         return retval;
00462 }
00463 
00464 /*
00465  * obtain a password from the user
00466  */
00467 
00468 int _smb_read_password( pam_handle_t * pamh, unsigned int ctrl,
00469                         const char *comment, const char *prompt1,
00470                         const char *prompt2, const char *data_name, char **pass )
00471 {
00472     int authtok_flag;
00473     int retval;
00474     char *item = NULL;
00475     char *token;
00476 
00477     struct pam_message msg[3], *pmsg[3];
00478     struct pam_response *resp;
00479     int i, expect;
00480 
00481 
00482     /* make sure nothing inappropriate gets returned */
00483 
00484     *pass = token = NULL;
00485 
00486     /* which authentication token are we getting? */
00487 
00488     authtok_flag = on(SMB__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
00489 
00490     /* should we obtain the password from a PAM item ? */
00491 
00492     if (on(SMB_TRY_FIRST_PASS, ctrl) || on(SMB_USE_FIRST_PASS, ctrl)) {
00493         retval = pam_get_item( pamh, authtok_flag, (const void **) &item );
00494         if (retval != PAM_SUCCESS) {
00495             /* very strange. */
00496             _log_err( LOG_ALERT
00497                       , "pam_get_item returned error to smb_read_password" );
00498             return retval;
00499         } else if (item != NULL) {      /* we have a password! */
00500             *pass = item;
00501             item = NULL;
00502             return PAM_SUCCESS;
00503         } else if (on( SMB_USE_FIRST_PASS, ctrl )) {
00504             return PAM_AUTHTOK_RECOVER_ERR;             /* didn't work */
00505         } else if (on( SMB_USE_AUTHTOK, ctrl )
00506                    && off( SMB__OLD_PASSWD, ctrl ))
00507         {
00508             return PAM_AUTHTOK_RECOVER_ERR;
00509         }
00510     }
00511 
00512     /*
00513      * getting here implies we will have to get the password from the
00514      * user directly.
00515      */
00516 
00517     /* prepare to converse */
00518     if (comment != NULL && off(SMB__QUIET, ctrl)) {
00519         pmsg[0] = &msg[0];
00520         msg[0].msg_style = PAM_TEXT_INFO;
00521         msg[0].msg = CONST_DISCARD(char *, comment);
00522         i = 1;
00523     } else {
00524         i = 0;
00525     }
00526 
00527     pmsg[i] = &msg[i];
00528     msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
00529     msg[i++].msg = CONST_DISCARD(char *, prompt1);
00530 
00531     if (prompt2 != NULL) {
00532         pmsg[i] = &msg[i];
00533         msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
00534         msg[i++].msg = CONST_DISCARD(char *, prompt2);
00535         expect = 2;
00536     } else
00537         expect = 1;
00538 
00539     resp = NULL;
00540 
00541     retval = converse( pamh, ctrl, i, pmsg, &resp );
00542 
00543     if (resp != NULL) {
00544         int j = comment ? 1 : 0;
00545         /* interpret the response */
00546 
00547         if (retval == PAM_SUCCESS) {    /* a good conversation */
00548 
00549             token = smbpXstrDup(resp[j++].resp);
00550             if (token != NULL) {
00551                 if (expect == 2) {
00552                     /* verify that password entered correctly */
00553                     if (!resp[j].resp || strcmp( token, resp[j].resp )) {
00554                         _pam_delete( token );
00555                         retval = PAM_AUTHTOK_RECOVER_ERR;
00556                         make_remark( pamh, ctrl, PAM_ERROR_MSG
00557                                      , MISTYPED_PASS );
00558                     }
00559                 }
00560             } else {
00561                 _log_err(LOG_NOTICE, "could not recover authentication token");
00562             }
00563         }
00564 
00565         /* tidy up */
00566         _pam_drop_reply( resp, expect );
00567 
00568     } else {
00569         retval = (retval == PAM_SUCCESS) ? PAM_AUTHTOK_RECOVER_ERR : retval;
00570     }
00571 
00572     if (retval != PAM_SUCCESS) {
00573         if (on( SMB_DEBUG, ctrl ))
00574             _log_err( LOG_DEBUG, "unable to obtain a password" );
00575         return retval;
00576     }
00577     /* 'token' is the entered password */
00578 
00579     if (off( SMB_NOT_SET_PASS, ctrl )) {
00580 
00581         /* we store this password as an item */
00582 
00583         retval = pam_set_item( pamh, authtok_flag, (const void *)token );
00584         _pam_delete( token );           /* clean it up */
00585         if (retval != PAM_SUCCESS
00586             || (retval = pam_get_item( pamh, authtok_flag
00587                             ,(const void **)&item )) != PAM_SUCCESS)
00588         {
00589             _log_err( LOG_CRIT, "error manipulating password" );
00590             return retval;
00591         }
00592     } else {
00593         /*
00594          * then store it as data specific to this module. pam_end()
00595          * will arrange to clean it up.
00596          */
00597 
00598         retval = pam_set_data( pamh, data_name, (void *) token, _cleanup );
00599         if (retval != PAM_SUCCESS
00600             || (retval = pam_get_data( pamh, data_name, (const void **)&item ))
00601                              != PAM_SUCCESS)
00602         {
00603             _log_err( LOG_CRIT, "error manipulating password data [%s]"
00604                       , pam_strerror( pamh, retval ));
00605             _pam_delete( token );
00606             item = NULL;
00607             return retval;
00608         }
00609         token = NULL;                   /* break link to password */
00610     }
00611 
00612     *pass = item;
00613     item = NULL;                        /* break link to password */
00614 
00615     return PAM_SUCCESS;
00616 }
00617 
00618 int _pam_smb_approve_pass(pam_handle_t * pamh,
00619                 unsigned int ctrl,
00620                 const char *pass_old,
00621                 const char *pass_new )
00622 {
00623 
00624     /* Further checks should be handled through module stacking. -SRL */
00625     if (pass_new == NULL || (pass_old && !strcmp( pass_old, pass_new )))
00626     {
00627         if (on(SMB_DEBUG, ctrl)) {
00628             _log_err( LOG_DEBUG,
00629                       "passwd: bad authentication token (null or unchanged)" );
00630         }
00631         make_remark( pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
00632                                 "No password supplied" : "Password unchanged" );
00633         return PAM_AUTHTOK_ERR;
00634     }
00635 
00636     return PAM_SUCCESS;
00637 }

Sambaに対してSat Aug 29 21:23:11 2009に生成されました。  doxygen 1.4.7