pam_smbpass/pam_smb_passwd.c

説明を見る。
00001 /*
00002    Unix SMB/CIFS implementation.
00003    Use PAM to update user passwords in the local SAM
00004    Copyright (C) Steve Langasek         1998-2003
00005    
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015    
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software
00018    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019 
00020 */
00021 
00022 /* indicate the following groups are defined */
00023 #define PAM_SM_PASSWORD
00024 
00025 #include "includes.h"
00026 
00027 /* This is only used in the Sun implementation.  FIXME: we really
00028    want a define here that distinguishes between the Solaris PAM
00029    and others (including FreeBSD). */
00030 
00031 #ifndef LINUX
00032 #include <security/pam_appl.h>
00033 #endif
00034 
00035 #include <security/pam_modules.h>
00036 
00037 #include "general.h" 
00038 
00039 #include "support.h"
00040 
00041 int smb_update_db( pam_handle_t *pamh, int ctrl, const char *user,  const char *pass_new )
00042 {
00043         int retval;
00044         pstring err_str;
00045         pstring msg_str;
00046 
00047         err_str[0] = '\0';
00048         msg_str[0] = '\0';
00049 
00050         retval = NT_STATUS_IS_OK(local_password_change( user, LOCAL_SET_PASSWORD, pass_new,
00051                                         err_str, sizeof(err_str),
00052                                         msg_str, sizeof(msg_str) ));
00053 
00054         if (!retval) {
00055                 if (*err_str) {
00056                         err_str[PSTRING_LEN-1] = '\0';
00057                         make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
00058                 }
00059 
00060                 /* FIXME: what value is appropriate here? */
00061                 retval = PAM_AUTHTOK_ERR;
00062         } else {
00063                 if (*msg_str) {
00064                         msg_str[PSTRING_LEN-1] = '\0';
00065                         make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
00066                 }
00067                 retval = PAM_SUCCESS;
00068         }
00069 
00070         return retval;      
00071 }
00072 
00073 
00074 /* data tokens */
00075 
00076 #define _SMB_OLD_AUTHTOK  "-SMB-OLD-PASS"
00077 #define _SMB_NEW_AUTHTOK  "-SMB-NEW-PASS"
00078 
00079 /*
00080  * FUNCTION: pam_sm_chauthtok()
00081  *
00082  * This function is called twice by the PAM library, once with
00083  * PAM_PRELIM_CHECK set, and then again with PAM_UPDATE_AUTHTOK set.  With
00084  * Linux-PAM, these two passes generally involve first checking the old
00085  * token and then changing the token.  This is what we do here.
00086  *
00087  * Having obtained a new password. The function updates the
00088  * SMB_PASSWD_FILE file (normally, $(LIBDIR)/smbpasswd).
00089  */
00090 
00091 int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
00092                      int argc, const char **argv)
00093 {
00094     unsigned int ctrl;
00095     int retval;
00096 
00097     extern BOOL in_client;
00098 
00099     struct samu *sampass = NULL;
00100     void (*oldsig_handler)(int);
00101     const char *user;
00102     char *pass_old;
00103     char *pass_new;
00104 
00105     /* Samba initialization. */
00106     load_case_tables();
00107     setup_logging( "pam_smbpass", False );
00108     in_client = True;
00109 
00110     ctrl = set_ctrl(flags, argc, argv);
00111 
00112     /*
00113      * First get the name of a user.  No need to do anything if we can't
00114      * determine this.
00115      */
00116 
00117     retval = pam_get_user( pamh, &user, "Username: " );
00118     if (retval != PAM_SUCCESS) {
00119         if (on( SMB_DEBUG, ctrl )) {
00120             _log_err( LOG_DEBUG, "password: could not identify user" );
00121         }
00122         return retval;
00123     }
00124     if (on( SMB_DEBUG, ctrl )) {
00125         _log_err( LOG_DEBUG, "username [%s] obtained", user );
00126     }
00127 
00128     if (geteuid() != 0) {
00129         _log_err( LOG_DEBUG, "Cannot access samba password database, not running as root.");
00130         return PAM_AUTHINFO_UNAVAIL;
00131     }
00132 
00133     /* Getting into places that might use LDAP -- protect the app
00134        from a SIGPIPE it's not expecting */
00135     oldsig_handler = CatchSignal(SIGPIPE, SIGNAL_CAST SIG_IGN);
00136 
00137     if (!initialize_password_db(False)) {
00138         _log_err( LOG_ALERT, "Cannot access samba password database" );
00139         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
00140         return PAM_AUTHINFO_UNAVAIL;
00141     }
00142 
00143     /* obtain user record */
00144     if ( !(sampass = samu_new( NULL )) ) {
00145         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
00146         return nt_status_to_pam(NT_STATUS_NO_MEMORY);
00147     }
00148 
00149     if (!pdb_getsampwnam(sampass,user)) {
00150         _log_err( LOG_ALERT, "Failed to find entry for user %s.", user );
00151         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
00152         return PAM_USER_UNKNOWN;
00153     }
00154     if (on( SMB_DEBUG, ctrl )) {
00155         _log_err( LOG_DEBUG, "Located account for %s", user );
00156     }
00157 
00158     if (flags & PAM_PRELIM_CHECK) {
00159         /*
00160          * obtain and verify the current password (OLDAUTHTOK) for
00161          * the user.
00162          */
00163 
00164         char *Announce;
00165 
00166         if (_smb_blankpasswd( ctrl, sampass )) {
00167 
00168             TALLOC_FREE(sampass);
00169             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
00170             return PAM_SUCCESS;
00171         }
00172 
00173         /* Password change by root, or for an expired token, doesn't
00174            require authentication.  Is this a good choice? */
00175         if (getuid() != 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
00176 
00177             /* tell user what is happening */
00178 #define greeting "Changing password for "
00179             Announce = SMB_MALLOC_ARRAY(char, sizeof(greeting)+strlen(user));
00180             if (Announce == NULL) {
00181                 _log_err(LOG_CRIT, "password: out of memory");
00182                 TALLOC_FREE(sampass);
00183                 CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
00184                 return PAM_BUF_ERR;
00185             }
00186             strncpy( Announce, greeting, sizeof(greeting) );
00187             strncpy( Announce+sizeof(greeting)-1, user, strlen(user)+1 );
00188 #undef greeting
00189 
00190             set( SMB__OLD_PASSWD, ctrl );
00191             retval = _smb_read_password( pamh, ctrl, Announce, "Current SMB password: ",
00192                                          NULL, _SMB_OLD_AUTHTOK, &pass_old );
00193             SAFE_FREE( Announce );
00194 
00195             if (retval != PAM_SUCCESS) {
00196                 _log_err( LOG_NOTICE
00197                           , "password - (old) token not obtained" );
00198                 TALLOC_FREE(sampass);
00199                 CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
00200                 return retval;
00201             }
00202 
00203             /* verify that this is the password for this user */
00204 
00205             retval = _smb_verify_password( pamh, sampass, pass_old, ctrl );
00206 
00207         } else {
00208             pass_old = NULL;
00209             retval = PAM_SUCCESS;           /* root doesn't have to */
00210         }
00211 
00212         pass_old = NULL;
00213         TALLOC_FREE(sampass);
00214         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
00215         return retval;
00216 
00217     } else if (flags & PAM_UPDATE_AUTHTOK) {
00218 
00219         /*
00220          * obtain the proposed password
00221          */
00222 
00223         /*
00224          * get the old token back. NULL was ok only if root [at this
00225          * point we assume that this has already been enforced on a
00226          * previous call to this function].
00227          */
00228 
00229         if (off( SMB_NOT_SET_PASS, ctrl )) {
00230             retval = pam_get_item( pamh, PAM_OLDAUTHTOK,
00231                                    (const void **)&pass_old );
00232         } else {
00233             retval = pam_get_data( pamh, _SMB_OLD_AUTHTOK,
00234                                    (const void **)&pass_old );
00235             if (retval == PAM_NO_MODULE_DATA) {
00236                 pass_old = NULL;
00237                 retval = PAM_SUCCESS;
00238             }
00239         }
00240 
00241         if (retval != PAM_SUCCESS) {
00242             _log_err( LOG_NOTICE, "password: user not authenticated" );
00243             TALLOC_FREE(sampass);
00244             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
00245             return retval;
00246         }
00247 
00248         /*
00249          * use_authtok is to force the use of a previously entered
00250          * password -- needed for pluggable password strength checking
00251          * or other module stacking
00252          */
00253 
00254         if (on( SMB_USE_AUTHTOK, ctrl )) {
00255             set( SMB_USE_FIRST_PASS, ctrl );
00256         }
00257 
00258         retval = _smb_read_password( pamh, ctrl
00259                                       , NULL
00260                                       , "Enter new SMB password: "
00261                                       , "Retype new SMB password: "
00262                                       , _SMB_NEW_AUTHTOK
00263                                       , &pass_new );
00264 
00265         if (retval != PAM_SUCCESS) {
00266             if (on( SMB_DEBUG, ctrl )) {
00267                 _log_err( LOG_ALERT
00268                           , "password: new password not obtained" );
00269             }
00270             pass_old = NULL;                               /* tidy up */
00271             TALLOC_FREE(sampass);
00272             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
00273             return retval;
00274         }
00275 
00276         /*
00277          * At this point we know who the user is and what they
00278          * propose as their new password. Verify that the new
00279          * password is acceptable.
00280          */ 
00281 
00282         if (pass_new[0] == '\0') {     /* "\0" password = NULL */
00283             pass_new = NULL;
00284         }
00285 
00286         retval = _pam_smb_approve_pass(pamh, ctrl, pass_old, pass_new);
00287 
00288         if (retval != PAM_SUCCESS) {
00289             _log_err(LOG_NOTICE, "new password not acceptable");
00290             pass_new = pass_old = NULL;               /* tidy up */
00291             TALLOC_FREE(sampass);
00292             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
00293             return retval;
00294         }
00295 
00296         /*
00297          * By reaching here we have approved the passwords and must now
00298          * rebuild the smb password file.
00299          */
00300 
00301         /* update the password database */
00302 
00303         retval = smb_update_db(pamh, ctrl, user, pass_new);
00304         if (retval == PAM_SUCCESS) {
00305             uid_t uid;
00306             
00307             /* password updated */
00308                 if (!sid_to_uid(pdb_get_user_sid(sampass), &uid)) {
00309                         _log_err( LOG_NOTICE, "Unable to get uid for user %s",
00310                                 pdb_get_username(sampass));
00311                         _log_err( LOG_NOTICE, "password for (%s) changed by (%s/%d)",
00312                                 user, uidtoname(getuid()), getuid());
00313                 } else {
00314                         _log_err( LOG_NOTICE, "password for (%s/%d) changed by (%s/%d)",
00315                                 user, uid, uidtoname(getuid()), getuid());
00316                 }
00317         } else {
00318                 _log_err( LOG_ERR, "password change failed for user %s", user);
00319         }
00320 
00321         pass_old = pass_new = NULL;
00322         if (sampass) {
00323                 TALLOC_FREE(sampass);
00324                 sampass = NULL;
00325         }
00326 
00327     } else {            /* something has broken with the library */
00328 
00329         _log_err( LOG_ALERT, "password received unknown request" );
00330         retval = PAM_ABORT;
00331 
00332     }
00333     
00334     if (sampass) {
00335         TALLOC_FREE(sampass);
00336         sampass = NULL;
00337     }
00338 
00339     TALLOC_FREE(sampass);
00340     CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
00341     return retval;
00342 }
00343 
00344 /* static module data */
00345 #ifdef PAM_STATIC
00346 struct pam_module _pam_smbpass_passwd_modstruct = {
00347      "pam_smbpass",
00348      NULL,
00349      NULL,
00350      NULL,
00351      NULL,
00352      NULL,
00353      pam_sm_chauthtok
00354 };
00355 #endif
00356 

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