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