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