root/source3/pam_smbpass/pam_smb_passwd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. smb_update_db
  2. pam_sm_chauthtok

   1 /*
   2    Unix SMB/CIFS implementation.
   3    Use PAM to update user passwords in the local SAM
   4    Copyright (C) Steve Langasek         1998-2003
   5    
   6    This program is free software; you can redistribute it and/or modify
   7    it under the terms of the GNU General Public License as published by
   8    the Free Software Foundation; either version 3 of the License, or
   9    (at your option) any later version.
  10    
  11    This program is distributed in the hope that it will be useful,
  12    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14    GNU General Public License for more details.
  15    
  16    You should have received a copy of the GNU General Public License
  17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18 
  19 */
  20 
  21 /* indicate the following groups are defined */
  22 #define PAM_SM_PASSWORD
  23 
  24 #include "includes.h"
  25 
  26 /* This is only used in the Sun implementation.  FIXME: we really
  27    want a define here that distinguishes between the Solaris PAM
  28    and others (including FreeBSD). */
  29 
  30 #ifndef LINUX
  31 #if defined(HAVE_SECURITY_PAM_APPL_H)
  32 #include <security/pam_appl.h>
  33 #elif defined(HAVE_PAM_PAM_APPL_H)
  34 #include <pam/pam_appl.h>
  35 #endif
  36 #endif
  37 
  38 #if defined(HAVE_SECURITY_PAM_MODULES_H)
  39 #include <security/pam_modules.h>
  40 #elif defined(HAVE_PAM_PAM_MODULES_H)
  41 #include <pam/pam_modules.h>
  42 #endif
  43 
  44 #include "general.h" 
  45 
  46 #include "support.h"
  47 
  48 int smb_update_db( pam_handle_t *pamh, int ctrl, const char *user,  const char *pass_new )
     /* [<][>][^][v][top][bottom][index][help] */
  49 {
  50         int retval;
  51         char *err_str = NULL;
  52         char *msg_str = NULL;
  53 
  54         retval = NT_STATUS_IS_OK(local_password_change(user, LOCAL_SET_PASSWORD, pass_new,
  55                                         &err_str,
  56                                         &msg_str));
  57 
  58         if (!retval) {
  59                 if (err_str) {
  60                         make_remark(pamh, ctrl, PAM_ERROR_MSG, err_str );
  61                 }
  62 
  63                 /* FIXME: what value is appropriate here? */
  64                 retval = PAM_AUTHTOK_ERR;
  65         } else {
  66                 if (msg_str) {
  67                         make_remark(pamh, ctrl, PAM_TEXT_INFO, msg_str );
  68                 }
  69                 retval = PAM_SUCCESS;
  70         }
  71 
  72         SAFE_FREE(err_str);
  73         SAFE_FREE(msg_str);
  74         return retval;      
  75 }
  76 
  77 
  78 /* data tokens */
  79 
  80 #define _SMB_OLD_AUTHTOK  "-SMB-OLD-PASS"
  81 #define _SMB_NEW_AUTHTOK  "-SMB-NEW-PASS"
  82 
  83 /*
  84  * FUNCTION: pam_sm_chauthtok()
  85  *
  86  * This function is called twice by the PAM library, once with
  87  * PAM_PRELIM_CHECK set, and then again with PAM_UPDATE_AUTHTOK set.  With
  88  * Linux-PAM, these two passes generally involve first checking the old
  89  * token and then changing the token.  This is what we do here.
  90  *
  91  * Having obtained a new password. The function updates the
  92  * SMB_PASSWD_FILE file (normally, $(LIBDIR)/smbpasswd).
  93  */
  94 
  95 int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
     /* [<][>][^][v][top][bottom][index][help] */
  96                      int argc, const char **argv)
  97 {
  98     unsigned int ctrl;
  99     int retval;
 100 
 101     struct samu *sampass = NULL;
 102     void (*oldsig_handler)(int);
 103     const char *user;
 104     char *pass_old;
 105     char *pass_new;
 106 
 107     /* Samba initialization. */
 108     load_case_tables();
 109     lp_set_in_client(True);
 110 
 111     ctrl = set_ctrl(pamh, flags, argc, argv);
 112 
 113     /*
 114      * First get the name of a user.  No need to do anything if we can't
 115      * determine this.
 116      */
 117 
 118     retval = pam_get_user( pamh, &user, "Username: " );
 119     if (retval != PAM_SUCCESS) {
 120         if (on( SMB_DEBUG, ctrl )) {
 121             _log_err(pamh, LOG_DEBUG, "password: could not identify user");
 122         }
 123         return retval;
 124     }
 125     if (on( SMB_DEBUG, ctrl )) {
 126         _log_err(pamh, LOG_DEBUG, "username [%s] obtained", user);
 127     }
 128 
 129     if (geteuid() != 0) {
 130         _log_err(pamh, LOG_DEBUG, "Cannot access samba password database, not running as root.");
 131         return PAM_AUTHINFO_UNAVAIL;
 132     }
 133 
 134     /* Getting into places that might use LDAP -- protect the app
 135        from a SIGPIPE it's not expecting */
 136     oldsig_handler = CatchSignal(SIGPIPE, SIGNAL_CAST SIG_IGN);
 137 
 138     if (!initialize_password_db(False, NULL)) {
 139       _log_err(pamh, LOG_ALERT, "Cannot access samba password database" );
 140         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
 141         return PAM_AUTHINFO_UNAVAIL;
 142     }
 143 
 144     /* obtain user record */
 145     if ( !(sampass = samu_new( NULL )) ) {
 146         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
 147         return nt_status_to_pam(NT_STATUS_NO_MEMORY);
 148     }
 149 
 150     if (!pdb_getsampwnam(sampass,user)) {
 151         _log_err(pamh, LOG_ALERT, "Failed to find entry for user %s.", user);
 152         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
 153         return PAM_USER_UNKNOWN;
 154     }
 155     if (on( SMB_DEBUG, ctrl )) {
 156         _log_err(pamh, LOG_DEBUG, "Located account for %s", user);
 157     }
 158 
 159     if (flags & PAM_PRELIM_CHECK) {
 160         /*
 161          * obtain and verify the current password (OLDAUTHTOK) for
 162          * the user.
 163          */
 164 
 165         char *Announce;
 166 
 167         if (_smb_blankpasswd( ctrl, sampass )) {
 168 
 169             TALLOC_FREE(sampass);
 170             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
 171             return PAM_SUCCESS;
 172         }
 173 
 174         /* Password change by root, or for an expired token, doesn't
 175            require authentication.  Is this a good choice? */
 176         if (getuid() != 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
 177 
 178             /* tell user what is happening */
 179 #define greeting "Changing password for "
 180             Announce = SMB_MALLOC_ARRAY(char, sizeof(greeting)+strlen(user));
 181             if (Announce == NULL) {
 182                 _log_err(pamh, LOG_CRIT, "password: out of memory");
 183                 TALLOC_FREE(sampass);
 184                 CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
 185                 return PAM_BUF_ERR;
 186             }
 187             strncpy( Announce, greeting, sizeof(greeting) );
 188             strncpy( Announce+sizeof(greeting)-1, user, strlen(user)+1 );
 189 #undef greeting
 190 
 191             set( SMB__OLD_PASSWD, ctrl );
 192             retval = _smb_read_password( pamh, ctrl, Announce, "Current SMB password: ",
 193                                          NULL, _SMB_OLD_AUTHTOK, &pass_old );
 194             SAFE_FREE( Announce );
 195 
 196             if (retval != PAM_SUCCESS) {
 197                 _log_err(pamh, LOG_NOTICE,
 198                          "password - (old) token not obtained");
 199                 TALLOC_FREE(sampass);
 200                 CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
 201                 return retval;
 202             }
 203 
 204             /* verify that this is the password for this user */
 205 
 206             retval = _smb_verify_password( pamh, sampass, pass_old, ctrl );
 207 
 208         } else {
 209             pass_old = NULL;
 210             retval = PAM_SUCCESS;           /* root doesn't have to */
 211         }
 212 
 213         pass_old = NULL;
 214         TALLOC_FREE(sampass);
 215         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
 216         return retval;
 217 
 218     } else if (flags & PAM_UPDATE_AUTHTOK) {
 219 
 220         /*
 221          * obtain the proposed password
 222          */
 223 
 224         /*
 225          * get the old token back. NULL was ok only if root [at this
 226          * point we assume that this has already been enforced on a
 227          * previous call to this function].
 228          */
 229 
 230         if (off( SMB_NOT_SET_PASS, ctrl )) {
 231             retval = _pam_get_item( pamh, PAM_OLDAUTHTOK,
 232                                    &pass_old );
 233         } else {
 234             retval = _pam_get_data( pamh, _SMB_OLD_AUTHTOK,
 235                                    &pass_old );
 236             if (retval == PAM_NO_MODULE_DATA) {
 237                 pass_old = NULL;
 238                 retval = PAM_SUCCESS;
 239             }
 240         }
 241 
 242         if (retval != PAM_SUCCESS) {
 243             _log_err(pamh, LOG_NOTICE, "password: user not authenticated");
 244             TALLOC_FREE(sampass);
 245             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
 246             return retval;
 247         }
 248 
 249         /*
 250          * use_authtok is to force the use of a previously entered
 251          * password -- needed for pluggable password strength checking
 252          * or other module stacking
 253          */
 254 
 255         if (on( SMB_USE_AUTHTOK, ctrl )) {
 256             set( SMB_USE_FIRST_PASS, ctrl );
 257         }
 258 
 259         retval = _smb_read_password( pamh, ctrl
 260                                       , NULL
 261                                       , "Enter new SMB password: "
 262                                       , "Retype new SMB password: "
 263                                       , _SMB_NEW_AUTHTOK
 264                                       , &pass_new );
 265 
 266         if (retval != PAM_SUCCESS) {
 267             if (on( SMB_DEBUG, ctrl )) {
 268                 _log_err(pamh, LOG_ALERT,
 269                          "password: new password not obtained");
 270             }
 271             pass_old = NULL;                               /* tidy up */
 272             TALLOC_FREE(sampass);
 273             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
 274             return retval;
 275         }
 276 
 277         /*
 278          * At this point we know who the user is and what they
 279          * propose as their new password. Verify that the new
 280          * password is acceptable.
 281          */ 
 282 
 283         if (pass_new[0] == '\0') {     /* "\0" password = NULL */
 284             pass_new = NULL;
 285         }
 286 
 287         retval = _pam_smb_approve_pass(pamh, ctrl, pass_old, pass_new);
 288 
 289         if (retval != PAM_SUCCESS) {
 290             _log_err(pamh, LOG_NOTICE, "new password not acceptable");
 291             pass_new = pass_old = NULL;               /* tidy up */
 292             TALLOC_FREE(sampass);
 293             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
 294             return retval;
 295         }
 296 
 297         /*
 298          * By reaching here we have approved the passwords and must now
 299          * rebuild the smb password file.
 300          */
 301 
 302         /* update the password database */
 303 
 304         retval = smb_update_db(pamh, ctrl, user, pass_new);
 305         if (retval == PAM_SUCCESS) {
 306             uid_t uid;
 307             
 308             /* password updated */
 309                 if (!sid_to_uid(pdb_get_user_sid(sampass), &uid)) {
 310                         _log_err(pamh, LOG_NOTICE,
 311                                  "Unable to get uid for user %s",
 312                                 pdb_get_username(sampass));
 313                         _log_err(pamh, LOG_NOTICE, "password for (%s) changed by (%s/%d)",
 314                                 user, uidtoname(getuid()), getuid());
 315                 } else {
 316                         _log_err(pamh, LOG_NOTICE, "password for (%s/%d) changed by (%s/%d)",
 317                                 user, uid, uidtoname(getuid()), getuid());
 318                 }
 319         } else {
 320                 _log_err(pamh, LOG_ERR, "password change failed for user %s", user);
 321         }
 322 
 323         pass_old = pass_new = NULL;
 324         if (sampass) {
 325                 TALLOC_FREE(sampass);
 326                 sampass = NULL;
 327         }
 328 
 329     } else {            /* something has broken with the library */
 330 
 331         _log_err(pamh, LOG_ALERT, "password received unknown request");
 332         retval = PAM_ABORT;
 333 
 334     }
 335     
 336     if (sampass) {
 337         TALLOC_FREE(sampass);
 338         sampass = NULL;
 339     }
 340 
 341     TALLOC_FREE(sampass);
 342     CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
 343     return retval;
 344 }
 345 
 346 /* static module data */
 347 #ifdef PAM_STATIC
 348 struct pam_module _pam_smbpass_passwd_modstruct = {
 349      "pam_smbpass",
 350      NULL,
 351      NULL,
 352      NULL,
 353      NULL,
 354      NULL,
 355      pam_sm_chauthtok
 356 };
 357 #endif
 358 

/* [<][>][^][v][top][bottom][index][help] */