1 /* pam_winbind module 2 3 Copyright Andrew Tridgell <tridge@samba.org> 2000 4 Copyright Tim Potter <tpot@samba.org> 2000 5 Copyright Andrew Bartlett <abartlet@samba.org> 2002 6 Copyright Guenther Deschner <gd@samba.org> 2005-2008 7 8 largely based on pam_userdb by Cristian Gafton <gafton@redhat.com> also 9 contains large slabs of code from pam_unix by Elliot Lee 10 <sopwith@redhat.com> (see copyright below for full details) 11 */ 12 13 #include "pam_winbind.h" 14 15 enum pam_winbind_request_type 16 { 17 PAM_WINBIND_AUTHENTICATE, 18 PAM_WINBIND_SETCRED, 19 PAM_WINBIND_ACCT_MGMT, 20 PAM_WINBIND_OPEN_SESSION, 21 PAM_WINBIND_CLOSE_SESSION, 22 PAM_WINBIND_CHAUTHTOK, 23 PAM_WINBIND_CLEANUP 24 }; 25 26 static int wbc_error_to_pam_error(wbcErr status) 27 { 28 switch (status) { 29 case WBC_ERR_SUCCESS: 30 return PAM_SUCCESS; 31 case WBC_ERR_NOT_IMPLEMENTED: 32 return PAM_SERVICE_ERR; 33 case WBC_ERR_UNKNOWN_FAILURE: 34 break; 35 case WBC_ERR_NO_MEMORY: 36 return PAM_BUF_ERR; 37 case WBC_ERR_INVALID_SID: 38 case WBC_ERR_INVALID_PARAM: 39 break; 40 case WBC_ERR_WINBIND_NOT_AVAILABLE: 41 return PAM_AUTHINFO_UNAVAIL; 42 case WBC_ERR_DOMAIN_NOT_FOUND: 43 return PAM_AUTHINFO_UNAVAIL; 44 case WBC_ERR_INVALID_RESPONSE: 45 return PAM_BUF_ERR; 46 case WBC_ERR_NSS_ERROR: 47 return PAM_USER_UNKNOWN; 48 case WBC_ERR_AUTH_ERROR: 49 return PAM_AUTH_ERR; 50 case WBC_ERR_UNKNOWN_USER: 51 return PAM_USER_UNKNOWN; 52 case WBC_ERR_UNKNOWN_GROUP: 53 return PAM_USER_UNKNOWN; 54 case WBC_ERR_PWD_CHANGE_FAILED: 55 break; 56 } 57 58 /* be paranoid */ 59 return PAM_AUTH_ERR; 60 } 61 62 static const char *_pam_error_code_str(int err) 63 { 64 switch (err) { 65 case PAM_SUCCESS: 66 return "PAM_SUCCESS"; 67 case PAM_OPEN_ERR: 68 return "PAM_OPEN_ERR"; 69 case PAM_SYMBOL_ERR: 70 return "PAM_SYMBOL_ERR"; 71 case PAM_SERVICE_ERR: 72 return "PAM_SERVICE_ERR"; 73 case PAM_SYSTEM_ERR: 74 return "PAM_SYSTEM_ERR"; 75 case PAM_BUF_ERR: 76 return "PAM_BUF_ERR"; 77 case PAM_PERM_DENIED: 78 return "PAM_PERM_DENIED"; 79 case PAM_AUTH_ERR: 80 return "PAM_AUTH_ERR"; 81 case PAM_CRED_INSUFFICIENT: 82 return "PAM_CRED_INSUFFICIENT"; 83 case PAM_AUTHINFO_UNAVAIL: 84 return "PAM_AUTHINFO_UNAVAIL"; 85 case PAM_USER_UNKNOWN: 86 return "PAM_USER_UNKNOWN"; 87 case PAM_MAXTRIES: 88 return "PAM_MAXTRIES"; 89 case PAM_NEW_AUTHTOK_REQD: 90 return "PAM_NEW_AUTHTOK_REQD"; 91 case PAM_ACCT_EXPIRED: 92 return "PAM_ACCT_EXPIRED"; 93 case PAM_SESSION_ERR: 94 return "PAM_SESSION_ERR"; 95 case PAM_CRED_UNAVAIL: 96 return "PAM_CRED_UNAVAIL"; 97 case PAM_CRED_EXPIRED: 98 return "PAM_CRED_EXPIRED"; 99 case PAM_CRED_ERR: 100 return "PAM_CRED_ERR"; 101 case PAM_NO_MODULE_DATA: 102 return "PAM_NO_MODULE_DATA"; 103 case PAM_CONV_ERR: 104 return "PAM_CONV_ERR"; 105 case PAM_AUTHTOK_ERR: 106 return "PAM_AUTHTOK_ERR"; 107 case PAM_AUTHTOK_RECOVER_ERR: 108 return "PAM_AUTHTOK_RECOVER_ERR"; 109 case PAM_AUTHTOK_LOCK_BUSY: 110 return "PAM_AUTHTOK_LOCK_BUSY"; 111 case PAM_AUTHTOK_DISABLE_AGING: 112 return "PAM_AUTHTOK_DISABLE_AGING"; 113 case PAM_TRY_AGAIN: 114 return "PAM_TRY_AGAIN"; 115 case PAM_IGNORE: 116 return "PAM_IGNORE"; 117 case PAM_ABORT: 118 return "PAM_ABORT"; 119 case PAM_AUTHTOK_EXPIRED: 120 return "PAM_AUTHTOK_EXPIRED"; 121 #ifdef PAM_MODULE_UNKNOWN 122 case PAM_MODULE_UNKNOWN: 123 return "PAM_MODULE_UNKNOWN"; 124 #endif 125 #ifdef PAM_BAD_ITEM 126 case PAM_BAD_ITEM: 127 return "PAM_BAD_ITEM"; 128 #endif 129 #ifdef PAM_CONV_AGAIN 130 case PAM_CONV_AGAIN: 131 return "PAM_CONV_AGAIN"; 132 #endif 133 #ifdef PAM_INCOMPLETE 134 case PAM_INCOMPLETE: 135 return "PAM_INCOMPLETE"; 136 #endif 137 default: 138 return NULL; 139 } 140 } 141 142 #define _PAM_LOG_FUNCTION_ENTER(function, ctx) \ 143 do { \ 144 _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] ENTER: " \ 145 function " (flags: 0x%04x)", ctx->pamh, ctx->flags); \ 146 _pam_log_state(ctx); \ 147 } while (0) 148 149 #define _PAM_LOG_FUNCTION_LEAVE(function, ctx, retval) \ 150 do { \ 151 _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] LEAVE: " \ 152 function " returning %d (%s)", ctx ? ctx->pamh : NULL, retval, \ 153 _pam_error_code_str(retval)); \ 154 _pam_log_state(ctx); \ 155 } while (0) 156 157 /* data tokens */ 158 159 #define MAX_PASSWD_TRIES 3 160 161 #ifdef HAVE_GETTEXT 162 static char initialized = 0; 163 164 static inline void textdomain_init(void); 165 static inline void textdomain_init(void) 166 { 167 if (!initialized) { 168 bindtextdomain(MODULE_NAME, LOCALEDIR); 169 initialized = 1; 170 } 171 return; 172 } 173 #endif 174 175 176 /* some syslogging */ 177 static void _pam_log_int(const pam_handle_t *pamh, 178 int err, 179 const char *format, 180 va_list args) PRINTF_ATTRIBUTE(3, 0); 181 182 #ifdef HAVE_PAM_VSYSLOG 183 static void _pam_log_int(const pam_handle_t *pamh, 184 int err, 185 const char *format, 186 va_list args) 187 { 188 pam_vsyslog(pamh, err, format, args); 189 } 190 #else 191 static void _pam_log_int(const pam_handle_t *pamh, 192 int err, 193 const char *format, 194 va_list args) 195 { 196 char *base = NULL; 197 va_list args2; 198 const char *service; 199 int ret; 200 201 va_copy(args2, args); 202 203 pam_get_item(pamh, PAM_SERVICE, (const void **) &service); 204 205 ret = vasprintf(&base, format, args); 206 if (ret == -1) { 207 /* what else todo ? */ 208 vsyslog(err, format, args2); 209 va_end(args2); 210 return; 211 } 212 213 syslog(err, "%s(%s): %s", MODULE_NAME, service, base); 214 SAFE_FREE(base); 215 va_end(args2); 216 } 217 #endif /* HAVE_PAM_VSYSLOG */ 218 219 static bool _pam_log_is_silent(int ctrl) 220 { 221 return on(ctrl, WINBIND_SILENT); 222 } 223 224 static void _pam_log(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4); 225 static void _pam_log(struct pwb_context *r, int err, const char *format, ...) 226 { 227 va_list args; 228 229 if (_pam_log_is_silent(r->ctrl)) { 230 return; 231 } 232 233 va_start(args, format); 234 _pam_log_int(r->pamh, err, format, args); 235 va_end(args); 236 } 237 static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5); 238 static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) 239 { 240 va_list args; 241 242 if (_pam_log_is_silent(ctrl)) { 243 return; 244 } 245 246 va_start(args, format); 247 _pam_log_int(pamh, err, format, args); 248 va_end(args); 249 } 250 251 static bool _pam_log_is_debug_enabled(int ctrl) 252 { 253 if (ctrl == -1) { 254 return false; 255 } 256 257 if (_pam_log_is_silent(ctrl)) { 258 return false; 259 } 260 261 if (!(ctrl & WINBIND_DEBUG_ARG)) { 262 return false; 263 } 264 265 return true; 266 } 267 268 static bool _pam_log_is_debug_state_enabled(int ctrl) 269 { 270 if (!(ctrl & WINBIND_DEBUG_STATE)) { 271 return false; 272 } 273 274 return _pam_log_is_debug_enabled(ctrl); 275 } 276 277 static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4); 278 static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...) 279 { 280 va_list args; 281 282 if (!r || !_pam_log_is_debug_enabled(r->ctrl)) { 283 return; 284 } 285 286 va_start(args, format); 287 _pam_log_int(r->pamh, err, format, args); 288 va_end(args); 289 } 290 static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5); 291 static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) 292 { 293 va_list args; 294 295 if (!_pam_log_is_debug_enabled(ctrl)) { 296 return; 297 } 298 299 va_start(args, format); 300 _pam_log_int(pamh, err, format, args); 301 va_end(args); 302 } 303 304 static void _pam_log_state_datum(struct pwb_context *ctx, 305 int item_type, 306 const char *key, 307 int is_string) 308 { 309 const void *data = NULL; 310 if (item_type != 0) { 311 pam_get_item(ctx->pamh, item_type, &data); 312 } else { 313 pam_get_data(ctx->pamh, key, &data); 314 } 315 if (data != NULL) { 316 const char *type = (item_type != 0) ? "ITEM" : "DATA"; 317 if (is_string != 0) { 318 _pam_log_debug(ctx, LOG_DEBUG, 319 "[pamh: %p] STATE: %s(%s) = \"%s\" (%p)", 320 ctx->pamh, type, key, (const char *)data, 321 data); 322 } else { 323 _pam_log_debug(ctx, LOG_DEBUG, 324 "[pamh: %p] STATE: %s(%s) = %p", 325 ctx->pamh, type, key, data); 326 } 327 } 328 } 329 330 #define _PAM_LOG_STATE_DATA_POINTER(ctx, module_data_name) \ 331 _pam_log_state_datum(ctx, 0, module_data_name, 0) 332 333 #define _PAM_LOG_STATE_DATA_STRING(ctx, module_data_name) \ 334 _pam_log_state_datum(ctx, 0, module_data_name, 1) 335 336 #define _PAM_LOG_STATE_ITEM_POINTER(ctx, item_type) \ 337 _pam_log_state_datum(ctx, item_type, #item_type, 0) 338 339 #define _PAM_LOG_STATE_ITEM_STRING(ctx, item_type) \ 340 _pam_log_state_datum(ctx, item_type, #item_type, 1) 341 342 #ifdef DEBUG_PASSWORD 343 #define _LOG_PASSWORD_AS_STRING 1 344 #else 345 #define _LOG_PASSWORD_AS_STRING 0 346 #endif 347 348 #define _PAM_LOG_STATE_ITEM_PASSWORD(ctx, item_type) \ 349 _pam_log_state_datum(ctx, item_type, #item_type, \ 350 _LOG_PASSWORD_AS_STRING) 351 /* 352 * wrapper to preserve old behaviour of iniparser which ignored 353 * key values that had no value assigned like 354 * key = 355 * for a key like above newer iniparser will return a zero-length 356 * string, previously iniparser would return NULL 357 * 358 * JRA: For compatibility, tiniparser behaves like iniparser. 359 */ 360 static const char *tiniparser_getstring_nonempty(struct tiniparser_dictionary *d, 361 const char *key, 362 const char *def) 363 { 364 const char *ret = tiniparser_getstring(d, key, def); 365 if (ret && strlen(ret) == 0) { 366 ret = NULL; 367 } 368 return ret; 369 } 370 371 static void _pam_log_state(struct pwb_context *ctx) 372 { 373 if (!ctx || !_pam_log_is_debug_state_enabled(ctx->ctrl)) { 374 return; 375 } 376 377 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_SERVICE); 378 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER); 379 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_TTY); 380 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RHOST); 381 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RUSER); 382 _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_OLDAUTHTOK); 383 _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_AUTHTOK); 384 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER_PROMPT); 385 _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_CONV); 386 #ifdef PAM_FAIL_DELAY 387 _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_FAIL_DELAY); 388 #endif 389 #ifdef PAM_REPOSITORY 390 _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_REPOSITORY); 391 #endif 392 393 _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_HOMEDIR); 394 _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSCRIPT); 395 _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSERVER); 396 _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_PROFILEPATH); 397 _PAM_LOG_STATE_DATA_STRING(ctx, 398 PAM_WINBIND_NEW_AUTHTOK_REQD); 399 /* Use atoi to get PAM result code */ 400 _PAM_LOG_STATE_DATA_STRING(ctx, 401 PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH); 402 _PAM_LOG_STATE_DATA_POINTER(ctx, PAM_WINBIND_PWD_LAST_SET); 403 } 404 405 static int _pam_parse(const pam_handle_t *pamh, 406 int flags, 407 int argc, 408 const char **argv, 409 enum pam_winbind_request_type type, 410 struct tiniparser_dictionary **result_d) 411 { 412 int ctrl = 0; 413 const char *config_file = NULL; 414 int i; 415 const char **v; 416 struct tiniparser_dictionary *d = NULL; 417 418 if (flags & PAM_SILENT) { 419 ctrl |= WINBIND_SILENT; 420 } 421 422 for (i=argc,v=argv; i-- > 0; ++v) { 423 if (!strncasecmp(*v, "config", strlen("config"))) { 424 ctrl |= WINBIND_CONFIG_FILE; 425 config_file = v[i]; 426 break; 427 } 428 } 429 430 if (config_file == NULL) { 431 config_file = PAM_WINBIND_CONFIG_FILE; 432 } 433 434 d = tiniparser_load(config_file); 435 if (d == NULL) { 436 goto config_from_pam; 437 } 438 439 if (tiniparser_getboolean(d, "global:debug", false)) { 440 ctrl |= WINBIND_DEBUG_ARG; 441 } 442 443 if (tiniparser_getboolean(d, "global:debug_state", false)) { 444 ctrl |= WINBIND_DEBUG_STATE; 445 } 446 447 if (tiniparser_getboolean(d, "global:cached_login", false)) { 448 ctrl |= WINBIND_CACHED_LOGIN; 449 } 450 451 if (tiniparser_getboolean(d, "global:krb5_auth", false)) { 452 ctrl |= WINBIND_KRB5_AUTH; 453 } 454 455 if (tiniparser_getboolean(d, "global:silent", false)) { 456 ctrl |= WINBIND_SILENT; 457 } 458 459 if (tiniparser_getstring_nonempty(d, "global:krb5_ccache_type", NULL) != NULL) { 460 ctrl |= WINBIND_KRB5_CCACHE_TYPE; 461 } 462 463 if ((tiniparser_getstring_nonempty(d, "global:require-membership-of", NULL) 464 != NULL) || 465 (tiniparser_getstring_nonempty(d, "global:require_membership_of", NULL) 466 != NULL)) { 467 ctrl |= WINBIND_REQUIRED_MEMBERSHIP; 468 } 469 470 if (tiniparser_getboolean(d, "global:try_first_pass", false)) { 471 ctrl |= WINBIND_TRY_FIRST_PASS_ARG; 472 } 473 474 if (tiniparser_getint(d, "global:warn_pwd_expire", 0)) { 475 ctrl |= WINBIND_WARN_PWD_EXPIRE; 476 } 477 478 if (tiniparser_getboolean(d, "global:mkhomedir", false)) { 479 ctrl |= WINBIND_MKHOMEDIR; 480 } 481 482 config_from_pam: 483 /* step through arguments */ 484 for (i=argc,v=argv; i-- > 0; ++v) { 485 486 /* generic options */ 487 if (!strcmp(*v,"debug")) 488 ctrl |= WINBIND_DEBUG_ARG; 489 else if (!strcasecmp(*v, "debug_state")) 490 ctrl |= WINBIND_DEBUG_STATE; 491 else if (!strcasecmp(*v, "silent")) 492 ctrl |= WINBIND_SILENT; 493 else if (!strcasecmp(*v, "use_authtok")) 494 ctrl |= WINBIND_USE_AUTHTOK_ARG; 495 else if (!strcasecmp(*v, "try_authtok")) 496 ctrl |= WINBIND_TRY_AUTHTOK_ARG; 497 else if (!strcasecmp(*v, "use_first_pass")) 498 ctrl |= WINBIND_USE_FIRST_PASS_ARG; 499 else if (!strcasecmp(*v, "try_first_pass")) 500 ctrl |= WINBIND_TRY_FIRST_PASS_ARG; 501 else if (!strcasecmp(*v, "unknown_ok")) 502 ctrl |= WINBIND_UNKNOWN_OK_ARG; 503 else if ((type == PAM_WINBIND_AUTHENTICATE 504 || type == PAM_WINBIND_SETCRED) 505 && !strncasecmp(*v, "require_membership_of", 506 strlen("require_membership_of"))) 507 ctrl |= WINBIND_REQUIRED_MEMBERSHIP; 508 else if ((type == PAM_WINBIND_AUTHENTICATE 509 || type == PAM_WINBIND_SETCRED) 510 && !strncasecmp(*v, "require-membership-of", 511 strlen("require-membership-of"))) 512 ctrl |= WINBIND_REQUIRED_MEMBERSHIP; 513 else if (!strcasecmp(*v, "krb5_auth")) 514 ctrl |= WINBIND_KRB5_AUTH; 515 else if (!strncasecmp(*v, "krb5_ccache_type", 516 strlen("krb5_ccache_type"))) 517 ctrl |= WINBIND_KRB5_CCACHE_TYPE; 518 else if (!strcasecmp(*v, "cached_login")) 519 ctrl |= WINBIND_CACHED_LOGIN; 520 else if (!strcasecmp(*v, "mkhomedir")) 521 ctrl |= WINBIND_MKHOMEDIR; 522 else if (!strncasecmp(*v, "warn_pwd_expire", 523 strlen("warn_pwd_expire"))) 524 ctrl |= WINBIND_WARN_PWD_EXPIRE; 525 else if (type != PAM_WINBIND_CLEANUP) { 526 __pam_log(pamh, ctrl, LOG_ERR, 527 "pam_parse: unknown option: %s", *v); 528 return -1; 529 } 530 531 } 532 533 if (result_d) { 534 *result_d = d; 535 } else { 536 if (d) { 537 tiniparser_freedict(d); 538 } 539 } 540 541 return ctrl; 542 }; 543 544 static int _pam_winbind_free_context(struct pwb_context *ctx) 545 { 546 if (!ctx) { 547 return 0; 548 } 549 550 if (ctx->dict) { 551 tiniparser_freedict(ctx->dict); 552 } 553 554 wbcCtxFree(ctx->wbc_ctx); 555 556 return 0; 557 } 558 559 static int _pam_winbind_init_context(pam_handle_t *pamh, 560 int flags, 561 int argc, 562 const char **argv, 563 enum pam_winbind_request_type type, 564 struct pwb_context **ctx_p) 565 { 566 struct pwb_context *r = NULL; 567 const char *service = NULL; 568 char service_name[32] = {0}; 569 int ctrl_code; 570 571 #ifdef HAVE_GETTEXT 572 textdomain_init(); 573 #endif 574 575 r = talloc_zero(NULL, struct pwb_context); 576 if (!r) { 577 return PAM_BUF_ERR; 578 } 579 580 talloc_set_destructor(r, _pam_winbind_free_context); 581 582 r->pamh = pamh; 583 r->flags = flags; 584 r->argc = argc; 585 r->argv = argv; 586 ctrl_code = _pam_parse(pamh, flags, argc, argv, type, &r->dict); 587 if (ctrl_code == -1) { 588 TALLOC_FREE(r); 589 return PAM_SYSTEM_ERR; 590 } 591 r->ctrl = ctrl_code; 592 593 r->wbc_ctx = wbcCtxCreate(); 594 if (r->wbc_ctx == NULL) { 595 TALLOC_FREE(r); 596 return PAM_SYSTEM_ERR; 597 } 598 599 pam_get_item(pamh, PAM_SERVICE, (const void **)&service); 600 601 snprintf(service_name, sizeof(service_name), "PAM_WINBIND[%s]", service); 602 603 wbcSetClientProcessName(service_name); 604 605 *ctx_p = r; 606 607 return PAM_SUCCESS; 608 } 609 610 static void _pam_winbind_cleanup_func(pam_handle_t *pamh, 611 void *data, 612 int error_status) 613 { 614 int ctrl = _pam_parse(pamh, 0, 0, NULL, PAM_WINBIND_CLEANUP, NULL); 615 if (_pam_log_is_debug_state_enabled(ctrl)) { 616 __pam_log_debug(pamh, ctrl, LOG_DEBUG, 617 "[pamh: %p] CLEAN: cleaning up PAM data %p " 618 "(error_status = %d)", pamh, data, 619 error_status); 620 } 621 TALLOC_FREE(data); 622 } 623 624 625 static const struct ntstatus_errors { 626 const char *ntstatus_string; 627 const char *error_string; 628 } ntstatus_errors[] = { 629 {"NT_STATUS_OK", 630 N_("Success")}, 631 {"NT_STATUS_BACKUP_CONTROLLER", 632 N_("No primary Domain Controller available")}, 633 {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", 634 N_("No domain controllers found")}, 635 {"NT_STATUS_NO_LOGON_SERVERS", 636 N_("No logon servers")}, 637 {"NT_STATUS_PWD_TOO_SHORT", 638 N_("Password too short")}, 639 {"NT_STATUS_PWD_TOO_RECENT", 640 N_("The password of this user is too recent to change")}, 641 {"NT_STATUS_PWD_HISTORY_CONFLICT", 642 N_("Password is already in password history")}, 643 {"NT_STATUS_PASSWORD_EXPIRED", 644 N_("Your password has expired")}, 645 {"NT_STATUS_PASSWORD_MUST_CHANGE", 646 N_("You need to change your password now")}, 647 {"NT_STATUS_INVALID_WORKSTATION", 648 N_("You are not allowed to logon from this workstation")}, 649 {"NT_STATUS_INVALID_LOGON_HOURS", 650 N_("You are not allowed to logon at this time")}, 651 {"NT_STATUS_ACCOUNT_EXPIRED", 652 N_("Your account has expired. " 653 "Please contact your System administrator")}, /* SCNR */ 654 {"NT_STATUS_ACCOUNT_DISABLED", 655 N_("Your account is disabled. " 656 "Please contact your System administrator")}, /* SCNR */ 657 {"NT_STATUS_ACCOUNT_LOCKED_OUT", 658 N_("Your account has been locked. " 659 "Please contact your System administrator")}, /* SCNR */ 660 {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", 661 N_("Invalid Trust Account")}, 662 {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", 663 N_("Invalid Trust Account")}, 664 {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", 665 N_("Invalid Trust Account")}, 666 {"NT_STATUS_ACCESS_DENIED", 667 N_("Access is denied")}, 668 {NULL, NULL} 669 }; 670 671 static const char *_get_ntstatus_error_string(const char *nt_status_string) 672 { 673 int i; 674 for (i=0; ntstatus_errors[i].ntstatus_string != NULL; i++) { 675 if (!strcasecmp(ntstatus_errors[i].ntstatus_string, 676 nt_status_string)) { 677 return _(ntstatus_errors[i].error_string); 678 } 679 } 680 return NULL; 681 } 682 683 /* --- authentication management functions --- */ 684 685 /* Attempt a conversation */ 686 687 static int converse(const pam_handle_t *pamh, 688 int nargs, 689 const struct pam_message **message, 690 struct pam_response **response) 691 { 692 int retval; 693 const struct pam_conv *conv; 694 695 retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv); 696 if (retval == PAM_SUCCESS) { 697 retval = conv->conv(nargs, 698 discard_const_p(const struct pam_message *, message), 699 response, conv->appdata_ptr); 700 } 701 702 return retval; /* propagate error status */ 703 } 704 705 706 static int _make_remark(struct pwb_context *ctx, 707 int type, 708 const char *text) 709 { 710 int retval = PAM_SUCCESS; 711 712 const struct pam_message *pmsg[1]; 713 struct pam_message msg[1]; 714 struct pam_response *resp; 715 716 if (ctx->flags & WINBIND_SILENT) { 717 return PAM_SUCCESS; 718 } 719 720 pmsg[0] = &msg[0]; 721 msg[0].msg = discard_const_p(char, text); 722 msg[0].msg_style = type; 723 724 resp = NULL; 725 retval = converse(ctx->pamh, 1, pmsg, &resp); 726 727 if (resp) { 728 _pam_drop_reply(resp, 1); 729 } 730 return retval; 731 } 732 733 static int _make_remark_v(struct pwb_context *ctx, 734 int type, 735 const char *format, 736 va_list args) PRINTF_ATTRIBUTE(3, 0); 737 738 static int _make_remark_v(struct pwb_context *ctx, 739 int type, 740 const char *format, 741 va_list args) 742 { 743 char *var; 744 int ret; 745 746 ret = vasprintf(&var, format, args); 747 if (ret < 0) { 748 _pam_log(ctx, LOG_ERR, "memory allocation failure"); 749 return ret; 750 } 751 752 ret = _make_remark(ctx, type, var); 753 SAFE_FREE(var); 754 return ret; 755 } 756 757 static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...) PRINTF_ATTRIBUTE(3,4); 758 static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...) 759 { 760 int ret; 761 va_list args; 762 763 va_start(args, format); 764 ret = _make_remark_v(ctx, type, format, args); 765 va_end(args); 766 return ret; 767 } 768 769 static int pam_winbind_request_log(struct pwb_context *ctx, 770 int retval, 771 const char *user, 772 const char *fn) 773 { 774 switch (retval) { 775 case PAM_AUTH_ERR: 776 /* incorrect password */ 777 _pam_log(ctx, LOG_WARNING, "user '%s' denied access " 778 "(incorrect password or invalid membership)", user); 779 return retval; 780 case PAM_ACCT_EXPIRED: 781 /* account expired */ 782 _pam_log(ctx, LOG_WARNING, "user '%s' account expired", 783 user); 784 return retval; 785 case PAM_AUTHTOK_EXPIRED: 786 /* password expired */ 787 _pam_log(ctx, LOG_WARNING, "user '%s' password expired", 788 user); 789 return retval; 790 case PAM_NEW_AUTHTOK_REQD: 791 /* new password required */ 792 _pam_log(ctx, LOG_WARNING, "user '%s' new password " 793 "required", user); 794 return retval; 795 case PAM_USER_UNKNOWN: 796 /* the user does not exist */ 797 _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found", 798 user); 799 if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) { 800 return PAM_IGNORE; 801 } 802 return retval; 803 case PAM_AUTHTOK_ERR: 804 /* Authentication token manipulation error */ 805 _pam_log(ctx, LOG_WARNING, "user `%s' authentication token change failed " 806 "(pwd complexity/history/min_age not met?)", user); 807 return retval; 808 case PAM_SUCCESS: 809 /* Otherwise, the authentication looked good */ 810 if (strcmp(fn, "wbcLogonUser") == 0) { 811 _pam_log(ctx, LOG_NOTICE, 812 "user '%s' granted access", user); 813 } else { 814 _pam_log(ctx, LOG_NOTICE, 815 "user '%s' OK", user); 816 } 817 return retval; 818 default: 819 /* we don't know anything about this return value */ 820 _pam_log(ctx, LOG_ERR, 821 "internal module error (retval = %s(%d), user = '%s')", 822 _pam_error_code_str(retval), retval, user); 823 return retval; 824 } 825 } 826 827 static int wbc_auth_error_to_pam_error(struct pwb_context *ctx, 828 struct wbcAuthErrorInfo *e, 829 wbcErr status, 830 const char *username, 831 const char *fn) 832 { 833 int ret = PAM_AUTH_ERR; 834 835 if (WBC_ERROR_IS_OK(status)) { 836 _pam_log_debug(ctx, LOG_DEBUG, "request %s succeeded", 837 fn); 838 ret = PAM_SUCCESS; 839 return pam_winbind_request_log(ctx, ret, username, fn); 840 } 841 842 if (e) { 843 if (e->pam_error != PAM_SUCCESS) { 844 _pam_log(ctx, LOG_ERR, 845 "request %s failed: %s, " 846 "PAM error: %s (%d), NTSTATUS: %s, " 847 "Error message was: %s", 848 fn, 849 wbcErrorString(status), 850 _pam_error_code_str(e->pam_error), 851 e->pam_error, 852 e->nt_string, 853 e->display_string); 854 ret = e->pam_error; 855 return pam_winbind_request_log(ctx, ret, username, fn); 856 } 857 858 _pam_log(ctx, LOG_ERR, "request %s failed, but PAM error 0!", fn); 859 860 ret = PAM_SERVICE_ERR; 861 return pam_winbind_request_log(ctx, ret, username, fn); 862 } 863 864 ret = wbc_error_to_pam_error(status); 865 _pam_log(ctx, LOG_ERR, 866 "request %s failed: %s, PAM error: %s (%d)!", 867 fn, wbcErrorString(status), 868 _pam_error_code_str(ret), ret); 869 return pam_winbind_request_log(ctx, ret, username, fn); 870 } 871 872 #if defined(HAVE_PAM_RADIO_TYPE) 873 static bool _pam_winbind_change_pwd(struct pwb_context *ctx) 874 { 875 struct pam_message msg; 876 const struct pam_message *pmsg; 877 struct pam_response *resp = NULL; 878 int ret; 879 bool retval = false; 880 pmsg = &msg; 881 msg.msg_style = PAM_RADIO_TYPE; 882 msg.msg = _("Do you want to change your password now?"); 883 ret = converse(ctx->pamh, 1, &pmsg, &resp); 884 if (resp == NULL) { 885 if (ret == PAM_SUCCESS) { 886 _pam_log(ctx, LOG_CRIT, "pam_winbind: system error!\n"); 887 return false; 888 } 889 } 890 if (ret != PAM_SUCCESS) { 891 return false; 892 } 893 _pam_log(ctx, LOG_CRIT, "Received [%s] reply from application.\n", resp->resp); 894 895 if ((resp->resp != NULL) && (strcasecmp(resp->resp, "yes") == 0)) { 896 retval = true; 897 } 898 899 _pam_drop_reply(resp, 1); 900 return retval; 901 } 902 #else 903 static bool _pam_winbind_change_pwd(struct pwb_context *ctx) 904 { 905 return false; 906 } 907 #endif 908 909 /** 910 * send a password expiry message if required 911 * 912 * @param ctx PAM winbind context. 913 * @param next_change expected (calculated) next expiry date. 914 * @param already_expired pointer to a boolean to indicate if the password is 915 * already expired. 916 * 917 * @return boolean Returns true if message has been sent, false if not. 918 */ 919 920 static bool _pam_send_password_expiry_message(struct pwb_context *ctx, 921 time_t next_change, 922 time_t now, 923 int warn_pwd_expire, 924 bool *already_expired, 925 bool *change_pwd) 926 { 927 int days = 0; 928 struct tm tm_now, tm_next_change; 929 bool retval = false; 930 int ret; 931 932 if (already_expired) { 933 *already_expired = false; 934 } 935 936 if (change_pwd) { 937 *change_pwd = false; 938 } 939 940 if (next_change <= now) { 941 PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PASSWORD_EXPIRED"); 942 if (already_expired) { 943 *already_expired = true; 944 } 945 return true; 946 } 947 948 if ((next_change < 0) || 949 (next_change > now + warn_pwd_expire * SECONDS_PER_DAY)) { 950 return false; 951 } 952 953 if ((localtime_r(&now, &tm_now) == NULL) || 954 (localtime_r(&next_change, &tm_next_change) == NULL)) { 955 return false; 956 } 957 958 days = (tm_next_change.tm_yday+tm_next_change.tm_year*365) - 959 (tm_now.tm_yday+tm_now.tm_year*365); 960 961 if (days == 0) { 962 ret = _make_remark(ctx, PAM_TEXT_INFO, 963 _("Your password expires today.\n")); 964 965 /* 966 * If change_pwd and already_expired is null. 967 * We are just sending a notification message. 968 * We don't expect any response in this case. 969 */ 970 971 if (!change_pwd && !already_expired) { 972 return true; 973 } 974 975 /* 976 * successfully sent the warning message. 977 * Give the user a chance to change pwd. 978 */ 979 if (ret == PAM_SUCCESS) { 980 if (change_pwd) { 981 retval = _pam_winbind_change_pwd(ctx); 982 if (retval) { 983 *change_pwd = true; 984 } 985 } 986 } 987 return true; 988 } 989 990 if (days > 0 && days < warn_pwd_expire) { 991 992 ret = _make_remark_format(ctx, PAM_TEXT_INFO, 993 _("Your password will expire in %d %s.\n"), 994 days, (days > 1) ? _("days"):_("day")); 995 /* 996 * If change_pwd and already_expired is null. 997 * We are just sending a notification message. 998 * We don't expect any response in this case. 999 */ 1000 1001 if (!change_pwd && !already_expired) { 1002 return true; 1003 } 1004 1005 /* 1006 * successfully sent the warning message. 1007 * Give the user a chance to change pwd. 1008 */ 1009 if (ret == PAM_SUCCESS) { 1010 if (change_pwd) { 1011 retval = _pam_winbind_change_pwd(ctx); 1012 if (retval) { 1013 *change_pwd = true; 1014 } 1015 } 1016 } 1017 return true; 1018 } 1019 1020 return false; 1021 } 1022 1023 /** 1024 * Send a warning if the password expires in the near future 1025 * 1026 * @param ctx PAM winbind context. 1027 * @param response The full authentication response structure. 1028 * @param already_expired boolean, is the pwd already expired? 1029 * 1030 * @return void. 1031 */ 1032 1033 static void _pam_warn_password_expiry(struct pwb_context *ctx, 1034 const struct wbcAuthUserInfo *info, 1035 int warn_pwd_expire, 1036 bool *already_expired, 1037 bool *change_pwd) 1038 { 1039 time_t now = time(NULL); 1040 time_t next_change = 0; 1041 1042 if (info == NULL) { 1043 return; 1044 } 1045 1046 if (already_expired) { 1047 *already_expired = false; 1048 } 1049 1050 if (change_pwd) { 1051 *change_pwd = false; 1052 } 1053 1054 /* accounts with WBC_ACB_PWNOEXP set never receive a warning */ 1055 if (info->acct_flags & WBC_ACB_PWNOEXP) { 1056 return; 1057 } 1058 1059 /* no point in sending a warning if this is a grace logon */ 1060 if (PAM_WB_GRACE_LOGON(info->user_flags)) { 1061 return; 1062 } 1063 1064 /* check if the info3 must change timestamp has been set */ 1065 next_change = info->pass_must_change_time; 1066 1067 if (_pam_send_password_expiry_message(ctx, next_change, now, 1068 warn_pwd_expire, 1069 already_expired, 1070 change_pwd)) { 1071 return; 1072 } 1073 1074 /* no warning sent */ 1075 } 1076 1077 #define IS_SID_STRING(name) (strncmp("S-", name, 2) == 0) 1078 1079 /** 1080 * Append a string, making sure not to overflow and to always return a 1081 * NULL-terminated string. 1082 * 1083 * @param dest Destination string buffer (must already be NULL-terminated). 1084 * @param src Source string buffer. 1085 * @param dest_buffer_size Size of dest buffer in bytes. 1086 * 1087 * @return false if dest buffer is not big enough (no bytes copied), true on 1088 * success. 1089 */ 1090 1091 static bool safe_append_string(char *dest, 1092 const char *src, 1093 int dest_buffer_size) 1094 { 1095 size_t len; 1096 len = strlcat(dest, src, dest_buffer_size); 1097 return (len < dest_buffer_size); 1098 } 1099 1100 /** 1101 * Convert a names into a SID string, appending it to a buffer. 1102 * 1103 * @param ctx PAM winbind context. 1104 * @param user User in PAM request. 1105 * @param name Name to convert. 1106 * @param sid_list_buffer Where to append the string sid. 1107 * @param sid_list_buffer Size of sid_list_buffer (in bytes). 1108 * 1109 * @return false on failure, true on success. 1110 */ 1111 static bool winbind_name_to_sid_string(struct pwb_context *ctx, 1112 const char *user, 1113 const char *name, 1114 char *sid_list_buffer, 1115 int sid_list_buffer_size) 1116 { 1117 char sid_string[WBC_SID_STRING_BUFLEN]; 1118 1119 /* lookup name? */ 1120 if (IS_SID_STRING(name)) { 1121 strlcpy(sid_string, name, sizeof(sid_string)); 1122 } else { 1123 wbcErr wbc_status; 1124 struct wbcDomainSid sid; 1125 enum wbcSidType type; 1126 1127 _pam_log_debug(ctx, LOG_DEBUG, 1128 "no sid given, looking up: %s\n", name); 1129 1130 wbc_status = wbcCtxLookupName(ctx->wbc_ctx, 1131 "", 1132 name, 1133 &sid, 1134 &type); 1135 if (!WBC_ERROR_IS_OK(wbc_status)) { 1136 _pam_log(ctx, LOG_INFO, 1137 "could not lookup name: %s\n", name); 1138 return false; 1139 } 1140 1141 wbcSidToStringBuf(&sid, sid_string, sizeof(sid_string)); 1142 } 1143 1144 if (!safe_append_string(sid_list_buffer, sid_string, 1145 sid_list_buffer_size)) { 1146 return false; 1147 } 1148 return true; 1149 } 1150 1151 /** 1152 * Convert a list of names into a list of sids. 1153 * 1154 * @param ctx PAM winbind context. 1155 * @param user User in PAM request. 1156 * @param name_list List of names or string sids, separated by commas. 1157 * @param sid_list_buffer Where to put the list of string sids. 1158 * @param sid_list_buffer Size of sid_list_buffer (in bytes). 1159 * 1160 * @return false on failure, true on success. 1161 */ 1162 static bool winbind_name_list_to_sid_string_list(struct pwb_context *ctx, 1163 const char *user, 1164 const char *name_list, 1165 char *sid_list_buffer, 1166 int sid_list_buffer_size) 1167 { 1168 bool result = false; 1169 char *current_name = NULL; 1170 const char *search_location; 1171 const char *comma; 1172 int len; 1173 1174 if (sid_list_buffer_size > 0) { 1175 sid_list_buffer[0] = 0; 1176 } 1177 1178 search_location = name_list; 1179 while ((comma = strchr(search_location, ',')) != NULL) { 1180 current_name = strndup(search_location, 1181 comma - search_location); 1182 if (NULL == current_name) { 1183 goto out; 1184 } 1185 1186 if (!winbind_name_to_sid_string(ctx, user, 1187 current_name, 1188 sid_list_buffer, 1189 sid_list_buffer_size)) { 1190 /* 1191 * If one group name failed, we must not fail 1192 * the authentication totally, continue with 1193 * the following group names. If user belongs to 1194 * one of the valid groups, we must allow it 1195 * login. -- BoYang 1196 */ 1197 1198 _pam_log(ctx, LOG_INFO, "cannot convert group %s to sid, " 1199 "check if group %s is valid group.", current_name, 1200 current_name); 1201 _make_remark_format(ctx, PAM_TEXT_INFO, _("Cannot convert group %s " 1202 "to sid, please contact your administrator to see " 1203 "if group %s is valid."), current_name, current_name); 1204 SAFE_FREE(current_name); 1205 search_location = comma + 1; 1206 continue; 1207 } 1208 1209 SAFE_FREE(current_name); 1210 1211 if (!safe_append_string(sid_list_buffer, ",", 1212 sid_list_buffer_size)) { 1213 goto out; 1214 } 1215 1216 search_location = comma + 1; 1217 } 1218 1219 if (!winbind_name_to_sid_string(ctx, user, search_location, 1220 sid_list_buffer, 1221 sid_list_buffer_size)) { 1222 _pam_log(ctx, LOG_INFO, "cannot convert group %s to sid, " 1223 "check if group %s is valid group.", search_location, 1224 search_location); 1225 _make_remark_format(ctx, PAM_TEXT_INFO, _("Cannot convert group %s " 1226 "to sid, please contact your administrator to see " 1227 "if group %s is valid."), search_location, search_location); 1228 1229 /* If no valid groups were converted we should fail outright */ 1230 if (name_list != NULL && strlen(sid_list_buffer) == 0) { 1231 result = false; 1232 goto out; 1233 } 1234 /* 1235 * The lookup of the last name failed.. 1236 * It results in require_member_of_sid ends with ',' 1237 * It is malformatted parameter here, overwrite the last ','. 1238 */ 1239 len = strlen(sid_list_buffer); 1240 if ((len != 0) && (sid_list_buffer[len - 1] == ',')) { 1241 sid_list_buffer[len - 1] = '\0'; 1242 } 1243 } 1244 1245 result = true; 1246 1247 out: 1248 SAFE_FREE(current_name); 1249 return result; 1250 } 1251 1252 /** 1253 * put krb5ccname variable into environment 1254 * 1255 * @param ctx PAM winbind context. 1256 * @param krb5ccname env variable retrieved from winbindd. 1257 * 1258 * @return void. 1259 */ 1260 1261 static void _pam_setup_krb5_env(struct pwb_context *ctx, 1262 struct wbcLogonUserInfo *info) 1263 { 1264 char *var = NULL; 1265 int ret; 1266 uint32_t i; 1267 const char *krb5ccname = NULL; 1268 1269 if (off(ctx->ctrl, WINBIND_KRB5_AUTH)) { 1270 return; 1271 } 1272 1273 if (!info) { 1274 return; 1275 } 1276 1277 for (i=0; i < info->num_blobs; i++) { 1278 if (strcasecmp(info->blobs[i].name, "krb5ccname") == 0) { 1279 krb5ccname = (const char *)info->blobs[i].blob.data; 1280 break; 1281 } 1282 } 1283 1284 if (!krb5ccname || (strlen(krb5ccname) == 0)) { 1285 return; 1286 } 1287 1288 _pam_log_debug(ctx, LOG_DEBUG, 1289 "request returned KRB5CCNAME: %s", krb5ccname); 1290 1291 if (asprintf(&var, "KRB5CCNAME=%s", krb5ccname) == -1) { 1292 return; 1293 } 1294 1295 ret = pam_putenv(ctx->pamh, var); 1296 if (ret != PAM_SUCCESS) { 1297 _pam_log(ctx, LOG_ERR, 1298 "failed to set KRB5CCNAME to %s: %s", 1299 var, pam_strerror(ctx->pamh, ret)); 1300 } 1301 free(var); 1302 } 1303 1304 /** 1305 * Copy unix username if available (further processed in PAM). 1306 * 1307 * @param ctx PAM winbind context 1308 * @param user_ret A pointer that holds a pointer to a string 1309 * @param unix_username A username 1310 * 1311 * @return void. 1312 */ 1313 1314 static void _pam_setup_unix_username(struct pwb_context *ctx, 1315 char **user_ret, 1316 struct wbcLogonUserInfo *info) 1317 { 1318 const char *unix_username = NULL; 1319 uint32_t i; 1320 1321 if (!user_ret || !info) { 1322 return; 1323 } 1324 1325 for (i=0; i < info->num_blobs; i++) { 1326 if (strcasecmp(info->blobs[i].name, "unix_username") == 0) { 1327 unix_username = (const char *)info->blobs[i].blob.data; 1328 break; 1329 } 1330 } 1331 1332 if (!unix_username || !unix_username[0]) { 1333 return; 1334 } 1335 1336 *user_ret = strdup(unix_username); 1337 } 1338 1339 /** 1340 * Set string into the PAM stack. 1341 * 1342 * @param ctx PAM winbind context. 1343 * @param data_name Key name for pam_set_data. 1344 * @param value String value. 1345 * 1346 * @return void. 1347 */ 1348 1349 static void _pam_set_data_string(struct pwb_context *ctx, 1350 const char *data_name, 1351 const char *value) 1352 { 1353 int ret; 1354 1355 if (!data_name || !value || (strlen(data_name) == 0) || 1356 (strlen(value) == 0)) { 1357 return; 1358 } 1359 1360 ret = pam_set_data(ctx->pamh, data_name, talloc_strdup(NULL, value), 1361 _pam_winbind_cleanup_func); 1362 if (ret != PAM_SUCCESS) { 1363 _pam_log_debug(ctx, LOG_DEBUG, 1364 "Could not set data %s: %s\n", 1365 data_name, pam_strerror(ctx->pamh, ret)); 1366 } 1367 } 1368 1369 /** 1370 * Set info3 strings into the PAM stack. 1371 * 1372 * @param ctx PAM winbind context. 1373 * @param data_name Key name for pam_set_data. 1374 * @param value String value. 1375 * 1376 * @return void. 1377 */ 1378 1379 static void _pam_set_data_info3(struct pwb_context *ctx, 1380 const struct wbcAuthUserInfo *info) 1381 { 1382 if (info != NULL) { 1383 _pam_set_data_string(ctx, PAM_WINBIND_HOMEDIR, 1384 info->home_directory); 1385 _pam_set_data_string(ctx, PAM_WINBIND_LOGONSCRIPT, 1386 info->logon_script); 1387 _pam_set_data_string(ctx, PAM_WINBIND_LOGONSERVER, 1388 info->logon_server); 1389 _pam_set_data_string(ctx, PAM_WINBIND_PROFILEPATH, 1390 info->profile_path); 1391 } 1392 } 1393 1394 /** 1395 * Free info3 strings in the PAM stack. 1396 * 1397 * @param pamh PAM handle 1398 * 1399 * @return void. 1400 */ 1401 1402 static void _pam_free_data_info3(pam_handle_t *pamh) 1403 { 1404 pam_set_data(pamh, PAM_WINBIND_HOMEDIR, NULL, NULL); 1405 pam_set_data(pamh, PAM_WINBIND_LOGONSCRIPT, NULL, NULL); 1406 pam_set_data(pamh, PAM_WINBIND_LOGONSERVER, NULL, NULL); 1407 pam_set_data(pamh, PAM_WINBIND_PROFILEPATH, NULL, NULL); 1408 } 1409 1410 /** 1411 * Send PAM_ERROR_MSG for cached or grace logons. 1412 * 1413 * @param ctx PAM winbind context. 1414 * @param username User in PAM request. 1415 * @param info3_user_flgs Info3 flags containing logon type bits. 1416 * 1417 * @return void. 1418 */ 1419 1420 static void _pam_warn_logon_type(struct pwb_context *ctx, 1421 const char *username, 1422 uint32_t info3_user_flgs) 1423 { 1424 /* inform about logon type */ 1425 if (PAM_WB_GRACE_LOGON(info3_user_flgs)) { 1426 1427 _make_remark(ctx, PAM_ERROR_MSG, 1428 _("Grace login. " 1429 "Please change your password as soon you're " 1430 "online again")); 1431 _pam_log_debug(ctx, LOG_DEBUG, 1432 "User %s logged on using grace logon\n", 1433 username); 1434 1435 } else if (PAM_WB_CACHED_LOGON(info3_user_flgs)) { 1436 1437 _make_remark(ctx, PAM_ERROR_MSG, 1438 _("Domain Controller unreachable, " 1439 "using cached credentials instead. " 1440 "Network resources may be unavailable")); 1441 _pam_log_debug(ctx, LOG_DEBUG, 1442 "User %s logged on using cached credentials\n", 1443 username); 1444 } 1445 } 1446 1447 /** 1448 * Send PAM_ERROR_MSG for krb5 errors. 1449 * 1450 * @param ctx PAM winbind context. 1451 * @param username User in PAM request. 1452 * @param info3_user_flgs Info3 flags containing logon type bits. 1453 * 1454 * @return void. 1455 */ 1456 1457 static void _pam_warn_krb5_failure(struct pwb_context *ctx, 1458 const char *username, 1459 uint32_t info3_user_flgs) 1460 { 1461 if (PAM_WB_KRB5_CLOCK_SKEW(info3_user_flgs)) { 1462 _make_remark(ctx, PAM_ERROR_MSG, 1463 _("Failed to establish your Kerberos Ticket cache " 1464 "due time differences\n" 1465 "with the domain controller. " 1466 "Please verify the system time.\n")); 1467 _pam_log_debug(ctx, LOG_DEBUG, 1468 "User %s: Clock skew when getting Krb5 TGT\n", 1469 username); 1470 } 1471 } 1472 1473 static bool _pam_check_remark_auth_err(struct pwb_context *ctx, 1474 const struct wbcAuthErrorInfo *e, 1475 const char *nt_status_string, 1476 int *pam_err) 1477 { 1478 const char *ntstatus = NULL; 1479 const char *error_string = NULL; 1480 1481 if (!e || !pam_err) { 1482 return false; 1483 } 1484 1485 ntstatus = e->nt_string; 1486 if (!ntstatus) { 1487 return false; 1488 } 1489 1490 if (strcasecmp(ntstatus, nt_status_string) == 0) { 1491 1492 error_string = _get_ntstatus_error_string(nt_status_string); 1493 if (error_string) { 1494 _make_remark(ctx, PAM_ERROR_MSG, error_string); 1495 *pam_err = e->pam_error; 1496 return true; 1497 } 1498 1499 if (e->display_string) { 1500 _make_remark(ctx, PAM_ERROR_MSG, _(e->display_string)); 1501 *pam_err = e->pam_error; 1502 return true; 1503 } 1504 1505 _make_remark(ctx, PAM_ERROR_MSG, nt_status_string); 1506 *pam_err = e->pam_error; 1507 1508 return true; 1509 } 1510 1511 return false; 1512 }; 1513 1514 /** 1515 * Compose Password Restriction String for a PAM_ERROR_MSG conversation. 1516 * 1517 * @param i The wbcUserPasswordPolicyInfo struct. 1518 * 1519 * @return string (caller needs to talloc_free). 1520 */ 1521 1522 static char *_pam_compose_pwd_restriction_string(struct pwb_context *ctx, 1523 struct wbcUserPasswordPolicyInfo *i) 1524 { 1525 char *str = NULL; 1526 1527 if (!i) { 1528 goto failed; 1529 } 1530 1531 str = talloc_asprintf(ctx, _("Your password ")); 1532 if (!str) { 1533 goto failed; 1534 } 1535 1536 if (i->min_length_password > 0) { 1537 str = talloc_asprintf_append(str, 1538 _("must be at least %d characters; "), 1539 i->min_length_password); 1540 if (!str) { 1541 goto failed; 1542 } 1543 } 1544 1545 if (i->password_history > 0) { 1546 str = talloc_asprintf_append(str, 1547 _("cannot repeat any of your previous %d " 1548 "passwords; "), 1549 i->password_history); 1550 if (!str) { 1551 goto failed; 1552 } 1553 } 1554 1555 if (i->password_properties & WBC_DOMAIN_PASSWORD_COMPLEX) { 1556 str = talloc_asprintf_append(str, 1557 _("must contain capitals, numerals " 1558 "or punctuation; " 1559 "and cannot contain your account " 1560 "or full name; ")); 1561 if (!str) { 1562 goto failed; 1563 } 1564 } 1565 1566 str = talloc_asprintf_append(str, 1567 _("Please type a different password. " 1568 "Type a password which meets these requirements in " 1569 "both text boxes.")); 1570 if (!str) { 1571 goto failed; 1572 } 1573 1574 return str; 1575 1576 failed: 1577 TALLOC_FREE(str); 1578 return NULL; 1579 } 1580 1581 static int _pam_create_homedir(struct pwb_context *ctx, 1582 const char *dirname, 1583 mode_t mode) 1584 { 1585 struct stat sbuf; 1586 1587 if (stat(dirname, &sbuf) == 0) { 1588 return PAM_SUCCESS; 1589 } 1590 1591 if (mkdir(dirname, mode) != 0) { 1592 1593 _make_remark_format(ctx, PAM_TEXT_INFO, 1594 _("Creating directory: %s failed: %s"), 1595 dirname, strerror(errno)); 1596 _pam_log(ctx, LOG_ERR, "could not create dir: %s (%s)", 1597 dirname, strerror(errno)); 1598 return PAM_PERM_DENIED; 1599 } 1600 1601 return PAM_SUCCESS; 1602 } 1603 1604 static int _pam_chown_homedir(struct pwb_context *ctx, 1605 const char *dirname, 1606 uid_t uid, 1607 gid_t gid) 1608 { 1609 if (chown(dirname, uid, gid) != 0) { 1610 _pam_log(ctx, LOG_ERR, "failed to chown user homedir: %s (%s)", 1611 dirname, strerror(errno)); 1612 return PAM_PERM_DENIED; 1613 } 1614 1615 return PAM_SUCCESS; 1616 } 1617 1618 static int _pam_mkhomedir(struct pwb_context *ctx) 1619 { 1620 struct passwd *pwd = NULL; 1621 char *token = NULL; 1622 char *create_dir = NULL; 1623 char *user_dir = NULL; 1624 int ret; 1625 const char *username; 1626 mode_t mode = 0700; 1627 char *safe_ptr = NULL; 1628 char *p = NULL; 1629 1630 /* Get the username */ 1631 ret = pam_get_user(ctx->pamh, &username, NULL); 1632 if ((ret != PAM_SUCCESS) || (!username)) { 1633 _pam_log_debug(ctx, LOG_DEBUG, "can not get the username"); 1634 return PAM_SERVICE_ERR; 1635 } 1636 1637 pwd = getpwnam(username); 1638 if (pwd == NULL) { 1639 _pam_log_debug(ctx, LOG_DEBUG, "can not get the username"); 1640 return PAM_USER_UNKNOWN; 1641 } 1642 _pam_log_debug(ctx, LOG_DEBUG, "homedir is: %s", pwd->pw_dir); 1643 1644 ret = _pam_create_homedir(ctx, pwd->pw_dir, 0700); 1645 if (ret == PAM_SUCCESS) { 1646 ret = _pam_chown_homedir(ctx, pwd->pw_dir, 1647 pwd->pw_uid, 1648 pwd->pw_gid); 1649 } 1650 1651 if (ret == PAM_SUCCESS) { 1652 return ret; 1653 } 1654 1655 /* maybe we need to create parent dirs */ 1656 create_dir = talloc_strdup(ctx, "/"); 1657 if (!create_dir) { 1658 return PAM_BUF_ERR; 1659 } 1660 1661 /* find final directory */ 1662 user_dir = strrchr(pwd->pw_dir, '/'); 1663 if (!user_dir) { 1664 return PAM_BUF_ERR; 1665 } 1666 user_dir++; 1667 1668 _pam_log(ctx, LOG_DEBUG, "final directory: %s", user_dir); 1669 1670 p = pwd->pw_dir; 1671 1672 while ((token = strtok_r(p, "/", &safe_ptr)) != NULL) { 1673 1674 mode = 0755; 1675 1676 p = NULL; 1677 1678 _pam_log_debug(ctx, LOG_DEBUG, "token is %s", token); 1679 1680 create_dir = talloc_asprintf_append(create_dir, "%s/", token); 1681 if (!create_dir) { 1682 return PAM_BUF_ERR; 1683 } 1684 _pam_log_debug(ctx, LOG_DEBUG, "current_dir is %s", create_dir); 1685 1686 if (strcmp(token, user_dir) == 0) { 1687 _pam_log_debug(ctx, LOG_DEBUG, "assuming last directory: %s", token); 1688 mode = 0700; 1689 } 1690 1691 ret = _pam_create_homedir(ctx, create_dir, mode); 1692 if (ret != PAM_SUCCESS) { 1693 return ret; 1694 } 1695 } 1696 1697 return _pam_chown_homedir(ctx, create_dir, 1698 pwd->pw_uid, 1699 pwd->pw_gid); 1700 } 1701 1702 /* talk to winbindd */ 1703 static int winbind_auth_request(struct pwb_context *ctx, 1704 const char *user, 1705 const char *pass, 1706 const char *member, 1707 const char *cctype, 1708 const int warn_pwd_expire, 1709 struct wbcAuthErrorInfo **p_error, 1710 struct wbcLogonUserInfo **p_info, 1711 time_t *pwd_last_set, 1712 char **user_ret) 1713 { 1714 wbcErr wbc_status; 1715 struct wbcLogonUserParams logon; 1716 char membership_of[1024]; 1717 uid_t user_uid = -1; 1718 uint32_t flags = WBFLAG_PAM_INFO3_TEXT; 1719 struct wbcLogonUserInfo *info = NULL; 1720 struct wbcAuthUserInfo *user_info = NULL; 1721 struct wbcAuthErrorInfo *error = NULL; 1722 int ret = PAM_AUTH_ERR; 1723 int i; 1724 const char *codes[] = { 1725 "NT_STATUS_PASSWORD_EXPIRED", 1726 "NT_STATUS_PASSWORD_MUST_CHANGE", 1727 "NT_STATUS_INVALID_WORKSTATION", 1728 "NT_STATUS_INVALID_LOGON_HOURS", 1729 "NT_STATUS_ACCOUNT_EXPIRED", 1730 "NT_STATUS_ACCOUNT_DISABLED", 1731 "NT_STATUS_ACCOUNT_LOCKED_OUT", 1732 "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", 1733 "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", 1734 "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", 1735 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", 1736 "NT_STATUS_NO_LOGON_SERVERS", 1737 "NT_STATUS_WRONG_PASSWORD", 1738 "NT_STATUS_ACCESS_DENIED" 1739 }; 1740 1741 if (pwd_last_set) { 1742 *pwd_last_set = 0; 1743 } 1744 1745 /* Krb5 auth always has to go against the KDC of the user's realm */ 1746 1747 if (ctx->ctrl & WINBIND_KRB5_AUTH) { 1748 flags |= WBFLAG_PAM_CONTACT_TRUSTDOM; 1749 } 1750 1751 if (ctx->ctrl & (WINBIND_KRB5_AUTH|WINBIND_CACHED_LOGIN)) { 1752 struct passwd *pwd = NULL; 1753 1754 pwd = getpwnam(user); 1755 if (pwd == NULL) { 1756 return PAM_USER_UNKNOWN; 1757 } 1758 user_uid = pwd->pw_uid; 1759 } 1760 1761 if (ctx->ctrl & WINBIND_KRB5_AUTH) { 1762 1763 _pam_log_debug(ctx, LOG_DEBUG, 1764 "enabling krb5 login flag\n"); 1765 1766 flags |= WBFLAG_PAM_KRB5 | 1767 WBFLAG_PAM_FALLBACK_AFTER_KRB5; 1768 } 1769 1770 if (ctx->ctrl & WINBIND_CACHED_LOGIN) { 1771 _pam_log_debug(ctx, LOG_DEBUG, 1772 "enabling cached login flag\n"); 1773 flags |= WBFLAG_PAM_CACHED_LOGIN; 1774 } 1775 1776 if (user_ret) { 1777 *user_ret = NULL; 1778 flags |= WBFLAG_PAM_UNIX_NAME; 1779 } 1780 1781 if (cctype != NULL) { 1782 _pam_log_debug(ctx, LOG_DEBUG, 1783 "enabling request for a %s krb5 ccache\n", 1784 cctype); 1785 } 1786 1787 if (member != NULL) { 1788 1789 ZERO_STRUCT(membership_of); 1790 1791 if (!winbind_name_list_to_sid_string_list(ctx, user, member, 1792 membership_of, 1793 sizeof(membership_of))) { 1794 _pam_log_debug(ctx, LOG_ERR, 1795 "failed to serialize membership of sid " 1796 "\"%s\"\n", member); 1797 return PAM_AUTH_ERR; 1798 } 1799 } 1800 1801 ZERO_STRUCT(logon); 1802 1803 logon.username = user; 1804 logon.password = pass; 1805 1806 if (cctype) { 1807 wbc_status = wbcAddNamedBlob(&logon.num_blobs, 1808 &logon.blobs, 1809 "krb5_cc_type", 1810 0, 1811 discard_const_p(uint8_t, cctype), 1812 strlen(cctype)+1); 1813 if (!WBC_ERROR_IS_OK(wbc_status)) { 1814 goto done; 1815 } 1816 } 1817 1818 wbc_status = wbcAddNamedBlob(&logon.num_blobs, 1819 &logon.blobs, 1820 "flags", 1821 0, 1822 (uint8_t *)&flags, 1823 sizeof(flags)); 1824 if (!WBC_ERROR_IS_OK(wbc_status)) { 1825 goto done; 1826 } 1827 1828 wbc_status = wbcAddNamedBlob(&logon.num_blobs, 1829 &logon.blobs, 1830 "user_uid", 1831 0, 1832 (uint8_t *)&user_uid, 1833 sizeof(user_uid)); 1834 if (!WBC_ERROR_IS_OK(wbc_status)) { 1835 goto done; 1836 } 1837 1838 if (member) { 1839 wbc_status = wbcAddNamedBlob(&logon.num_blobs, 1840 &logon.blobs, 1841 "membership_of", 1842 0, 1843 (uint8_t *)membership_of, 1844 sizeof(membership_of)); 1845 if (!WBC_ERROR_IS_OK(wbc_status)) { 1846 goto done; 1847 } 1848 } 1849 1850 wbc_status = wbcCtxLogonUser(ctx->wbc_ctx, 1851 &logon, 1852 &info, 1853 &error, 1854 NULL); 1855 ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status, 1856 user, "wbcLogonUser"); 1857 wbcFreeMemory(logon.blobs); 1858 logon.blobs = NULL; 1859 1860 if (info && info->info) { 1861 user_info = info->info; 1862 } 1863 1864 if (pwd_last_set && user_info) { 1865 *pwd_last_set = user_info->pass_last_set_time; 1866 } 1867 1868 if (p_info && info) { 1869 *p_info = info; 1870 } 1871 1872 if (p_error && error) { 1873 /* We want to process the error in the caller. */ 1874 *p_error = error; 1875 return ret; 1876 } 1877 1878 for (i=0; i<ARRAY_SIZE(codes); i++) { 1879 int _ret = ret; 1880 if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) { 1881 ret = _ret; 1882 goto done; 1883 } 1884 } 1885 1886 if ((ret == PAM_SUCCESS) && user_info && info) { 1887 1888 bool already_expired = false; 1889 bool change_pwd = false; 1890 1891 /* warn a user if the password is about to expire soon */ 1892 _pam_warn_password_expiry(ctx, user_info, 1893 warn_pwd_expire, 1894 &already_expired, 1895 &change_pwd); 1896 1897 if (already_expired == true) { 1898 1899 SMB_TIME_T last_set = user_info->pass_last_set_time; 1900 SMB_TIME_T must_set = user_info->pass_must_change_time; 1901 1902 _pam_log_debug(ctx, LOG_DEBUG, 1903 "Password has expired " 1904 "(Password was last set: %lld, " 1905 "it must be changed here " 1906 "%lld (now it's: %ld))\n", 1907 (long long int)last_set, 1908 (long long int)must_set, 1909 (long)time(NULL)); 1910 1911 return PAM_AUTHTOK_EXPIRED; 1912 } 1913 1914 if (change_pwd) { 1915 ret = PAM_NEW_AUTHTOK_REQD; 1916 goto done; 1917 } 1918 1919 /* inform about logon type */ 1920 _pam_warn_logon_type(ctx, user, user_info->user_flags); 1921 1922 /* inform about krb5 failures */ 1923 _pam_warn_krb5_failure(ctx, user, user_info->user_flags); 1924 1925 /* set some info3 info for other modules in the stack */ 1926 _pam_set_data_info3(ctx, user_info); 1927 1928 /* put krb5ccname into env */ 1929 _pam_setup_krb5_env(ctx, info); 1930 1931 /* If winbindd returned a username, return the pointer to it 1932 * here. */ 1933 _pam_setup_unix_username(ctx, user_ret, info); 1934 } 1935 1936 done: 1937 wbcFreeMemory(logon.blobs); 1938 if (info && info->blobs && !p_info) { 1939 wbcFreeMemory(info->blobs); 1940 /* 1941 * We set blobs to NULL to prevent a use after free in the 1942 * in the wbcLogonUserInfoDestructor 1943 */ 1944 info->blobs = NULL; 1945 } 1946 if (error && !p_error) { 1947 wbcFreeMemory(error); 1948 } 1949 if (info && !p_info) { 1950 wbcFreeMemory(info); 1951 } 1952 1953 return ret; 1954 } 1955 1956 /* talk to winbindd */ 1957 static int winbind_chauthtok_request(struct pwb_context *ctx, 1958 const char *user, 1959 const char *oldpass, 1960 const char *newpass, 1961 time_t pwd_last_set) 1962 { 1963 wbcErr wbc_status; 1964 struct wbcChangePasswordParams params; 1965 struct wbcAuthErrorInfo *error = NULL; 1966 struct wbcUserPasswordPolicyInfo *policy = NULL; 1967 enum wbcPasswordChangeRejectReason reject_reason = -1; 1968 uint32_t flags = 0; 1969 1970 int i; 1971 const char *codes[] = { 1972 "NT_STATUS_BACKUP_CONTROLLER", 1973 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", 1974 "NT_STATUS_NO_LOGON_SERVERS", 1975 "NT_STATUS_ACCESS_DENIED", 1976 "NT_STATUS_PWD_TOO_SHORT", /* TODO: tell the min pwd length ? */ 1977 "NT_STATUS_PWD_TOO_RECENT", /* TODO: tell the minage ? */ 1978 "NT_STATUS_PWD_HISTORY_CONFLICT" /* TODO: tell the history length ? */ 1979 }; 1980 int ret = PAM_AUTH_ERR; 1981 1982 ZERO_STRUCT(params); 1983 1984 if (ctx->ctrl & WINBIND_KRB5_AUTH) { 1985 flags |= WBFLAG_PAM_KRB5 | 1986 WBFLAG_PAM_CONTACT_TRUSTDOM; 1987 } 1988 1989 if (ctx->ctrl & WINBIND_CACHED_LOGIN) { 1990 flags |= WBFLAG_PAM_CACHED_LOGIN; 1991 } 1992 1993 params.account_name = user; 1994 params.level = WBC_CHANGE_PASSWORD_LEVEL_PLAIN; 1995 params.old_password.plaintext = oldpass; 1996 params.new_password.plaintext = newpass; 1997 params.flags = flags; 1998 1999 wbc_status = wbcCtxChangeUserPasswordEx(ctx->wbc_ctx, 2000 ¶ms, 2001 &error, 2002 &reject_reason, 2003 &policy); 2004 ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status, 2005 user, "wbcChangeUserPasswordEx"); 2006 2007 if (WBC_ERROR_IS_OK(wbc_status)) { 2008 _pam_log(ctx, LOG_NOTICE, 2009 "user '%s' password changed", user); 2010 return PAM_SUCCESS; 2011 } 2012 2013 if (!error) { 2014 wbcFreeMemory(policy); 2015 return ret; 2016 } 2017 2018 for (i=0; i<ARRAY_SIZE(codes); i++) { 2019 int _ret = ret; 2020 if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) { 2021 ret = _ret; 2022 goto done; 2023 } 2024 } 2025 2026 if (!strcasecmp(error->nt_string, 2027 "NT_STATUS_PASSWORD_RESTRICTION")) { 2028 2029 char *pwd_restriction_string = NULL; 2030 SMB_TIME_T min_pwd_age = 0; 2031 2032 if (policy) { 2033 min_pwd_age = policy->min_passwordage; 2034 } 2035 2036 /* FIXME: avoid to send multiple PAM messages after another */ 2037 switch ((int)reject_reason) { 2038 case -1: 2039 break; 2040 case WBC_PWD_CHANGE_NO_ERROR: 2041 if ((min_pwd_age > 0) && 2042 (pwd_last_set + min_pwd_age > time(NULL))) { 2043 PAM_WB_REMARK_DIRECT(ctx, 2044 "NT_STATUS_PWD_TOO_RECENT"); 2045 } 2046 break; 2047 case WBC_PWD_CHANGE_PASSWORD_TOO_SHORT: 2048 PAM_WB_REMARK_DIRECT(ctx, 2049 "NT_STATUS_PWD_TOO_SHORT"); 2050 break; 2051 case WBC_PWD_CHANGE_PWD_IN_HISTORY: 2052 PAM_WB_REMARK_DIRECT(ctx, 2053 "NT_STATUS_PWD_HISTORY_CONFLICT"); 2054 break; 2055 case WBC_PWD_CHANGE_NOT_COMPLEX: 2056 _make_remark(ctx, PAM_ERROR_MSG, 2057 _("Password does not meet " 2058 "complexity requirements")); 2059 break; 2060 default: 2061 _pam_log_debug(ctx, LOG_DEBUG, 2062 "unknown password change " 2063 "reject reason: %d", 2064 reject_reason); 2065 break; 2066 } 2067 2068 pwd_restriction_string = 2069 _pam_compose_pwd_restriction_string(ctx, policy); 2070 if (pwd_restriction_string) { 2071 _make_remark(ctx, PAM_ERROR_MSG, 2072 pwd_restriction_string); 2073 TALLOC_FREE(pwd_restriction_string); 2074 } 2075 } 2076 done: 2077 wbcFreeMemory(error); 2078 wbcFreeMemory(policy); 2079 2080 return ret; 2081 } 2082 2083 /* 2084 * Checks if a user has an account 2085 * 2086 * return values: 2087 * 1 = User not found 2088 * 0 = OK 2089 * -1 = System error 2090 */ 2091 static int valid_user(struct pwb_context *ctx, 2092 const char *user) 2093 { 2094 /* check not only if the user is available over NSS calls, also make 2095 * sure it's really a winbind user, this is important when stacking PAM 2096 * modules in the 'account' or 'password' facility. */ 2097 2098 wbcErr wbc_status; 2099 struct passwd *pwd = NULL; 2100 struct passwd *wb_pwd = NULL; 2101 2102 pwd = getpwnam(user); 2103 if (pwd == NULL) { 2104 return 1; 2105 } 2106 2107 wbc_status = wbcCtxGetpwnam(ctx->wbc_ctx, user, &wb_pwd); 2108 wbcFreeMemory(wb_pwd); 2109 if (!WBC_ERROR_IS_OK(wbc_status)) { 2110 _pam_log(ctx, LOG_DEBUG, "valid_user: wbcGetpwnam gave %s\n", 2111 wbcErrorString(wbc_status)); 2112 } 2113 2114 switch (wbc_status) { 2115 case WBC_ERR_UNKNOWN_USER: 2116 /* match other insane libwbclient return codes */ 2117 case WBC_ERR_WINBIND_NOT_AVAILABLE: 2118 case WBC_ERR_DOMAIN_NOT_FOUND: 2119 return 1; 2120 case WBC_ERR_SUCCESS: 2121 return 0; 2122 default: 2123 break; 2124 } 2125 return -1; 2126 } 2127 2128 static char *_pam_delete(register char *xx) 2129 { 2130 _pam_overwrite(xx); 2131 _pam_drop(xx); 2132 return NULL; 2133 } 2134 2135 /* 2136 * obtain a password from the user 2137 */ 2138 2139 static int _winbind_read_password(struct pwb_context *ctx, 2140 unsigned int ctrl, 2141 const char *comment, 2142 const char *prompt1, 2143 const char *prompt2, 2144 const char **pass) 2145 { 2146 int authtok_flag; 2147 int retval; 2148 const char *item; 2149 char *token; 2150 2151 _pam_log(ctx, LOG_DEBUG, "getting password (0x%08x)", ctrl); 2152 2153 /* 2154 * make sure nothing inappropriate gets returned 2155 */ 2156 2157 *pass = token = NULL; 2158 2159 /* 2160 * which authentication token are we getting? 2161 */ 2162 2163 if (on(WINBIND__OLD_PASSWORD, ctrl)) { 2164 authtok_flag = PAM_OLDAUTHTOK; 2165 } else { 2166 authtok_flag = PAM_AUTHTOK; 2167 } 2168 2169 /* 2170 * should we obtain the password from a PAM item ? 2171 */ 2172 2173 if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || 2174 on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) { 2175 retval = pam_get_item(ctx->pamh, 2176 authtok_flag, 2177 (const void **) &item); 2178 if (retval != PAM_SUCCESS) { 2179 /* very strange. */ 2180 _pam_log(ctx, LOG_ALERT, 2181 "pam_get_item returned error " 2182 "to unix-read-password"); 2183 return retval; 2184 } else if (item != NULL) { /* we have a password! */ 2185 *pass = item; 2186 item = NULL; 2187 _pam_log(ctx, LOG_DEBUG, 2188 "pam_get_item returned a password"); 2189 return PAM_SUCCESS; 2190 } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) { 2191 return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */ 2192 } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl) 2193 && off(WINBIND__OLD_PASSWORD, ctrl)) { 2194 return PAM_AUTHTOK_RECOVER_ERR; 2195 } 2196 } 2197 /* 2198 * getting here implies we will have to get the password from the 2199 * user directly. 2200 */ 2201 2202 { 2203 struct pam_message msg[3]; 2204 const struct pam_message *pmsg[3]; 2205 struct pam_response *resp; 2206 int i, replies; 2207 2208 /* prepare to converse */ 2209 2210 if (comment != NULL && off(ctrl, WINBIND_SILENT)) { 2211 pmsg[0] = &msg[0]; 2212 msg[0].msg_style = PAM_TEXT_INFO; 2213 msg[0].msg = discard_const_p(char, comment); 2214 i = 1; 2215 } else { 2216 i = 0; 2217 } 2218 2219 pmsg[i] = &msg[i]; 2220 msg[i].msg_style = PAM_PROMPT_ECHO_OFF; 2221 msg[i++].msg = discard_const_p(char, prompt1); 2222 replies = 1; 2223 2224 if (prompt2 != NULL) { 2225 pmsg[i] = &msg[i]; 2226 msg[i].msg_style = PAM_PROMPT_ECHO_OFF; 2227 msg[i++].msg = discard_const_p(char, prompt2); 2228 ++replies; 2229 } 2230 /* so call the conversation expecting i responses */ 2231 resp = NULL; 2232 retval = converse(ctx->pamh, i, pmsg, &resp); 2233 if (resp == NULL) { 2234 if (retval == PAM_SUCCESS) { 2235 retval = PAM_AUTHTOK_RECOVER_ERR; 2236 } 2237 goto done; 2238 } 2239 if (retval != PAM_SUCCESS) { 2240 _pam_drop_reply(resp, i); 2241 goto done; 2242 } 2243 2244 /* interpret the response */ 2245 2246 token = x_strdup(resp[i - replies].resp); 2247 if (!token) { 2248 _pam_log(ctx, LOG_NOTICE, 2249 "could not recover " 2250 "authentication token"); 2251 retval = PAM_AUTHTOK_RECOVER_ERR; 2252 goto done; 2253 } 2254 2255 if (replies == 2) { 2256 /* verify that password entered correctly */ 2257 if (!resp[i - 1].resp || 2258 strcmp(token, resp[i - 1].resp)) { 2259 _pam_delete(token); /* mistyped */ 2260 retval = PAM_AUTHTOK_RECOVER_ERR; 2261 _make_remark(ctx, PAM_ERROR_MSG, 2262 MISTYPED_PASS); 2263 } 2264 } 2265 2266 /* 2267 * tidy up the conversation (resp_retcode) is ignored 2268 * -- what is it for anyway? AGM 2269 */ 2270 _pam_drop_reply(resp, i); 2271 } 2272 2273 done: 2274 if (retval != PAM_SUCCESS) { 2275 _pam_log_debug(ctx, LOG_DEBUG, 2276 "unable to obtain a password"); 2277 return retval; 2278 } 2279 /* 'token' is the entered password */ 2280 2281 /* we store this password as an item */ 2282 2283 retval = pam_set_item(ctx->pamh, authtok_flag, token); 2284 _pam_delete(token); /* clean it up */ 2285 if (retval != PAM_SUCCESS || 2286 (retval = pam_get_item(ctx->pamh, authtok_flag, (const void **) &item)) != PAM_SUCCESS) { 2287 2288 _pam_log(ctx, LOG_CRIT, "error manipulating password"); 2289 return retval; 2290 2291 } 2292 2293 *pass = item; 2294 item = NULL; /* break link to password */ 2295 2296 return PAM_SUCCESS; 2297 } 2298 2299 static const char *get_conf_item_string(struct pwb_context *ctx, 2300 const char *item, 2301 int config_flag) 2302 { 2303 int i = 0; 2304 const char *parm_opt = NULL; 2305 2306 if (!(ctx->ctrl & config_flag)) { 2307 goto out; 2308 } 2309 2310 /* let the pam opt take precedence over the pam_winbind.conf option */ 2311 for (i=0; i<ctx->argc; i++) { 2312 2313 if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) { 2314 char *p; 2315 2316 if ((p = strchr(ctx->argv[i], '=')) == NULL) { 2317 _pam_log(ctx, LOG_INFO, 2318 "no \"=\" delimiter for \"%s\" found\n", 2319 item); 2320 goto out; 2321 } 2322 _pam_log_debug(ctx, LOG_INFO, 2323 "PAM config: %s '%s'\n", item, p+1); 2324 return p + 1; 2325 } 2326 } 2327 2328 if (ctx->dict) { 2329 char *key = NULL; 2330 2331 key = talloc_asprintf(ctx, "global:%s", item); 2332 if (!key) { 2333 goto out; 2334 } 2335 2336 parm_opt = tiniparser_getstring_nonempty(ctx->dict, key, NULL); 2337 TALLOC_FREE(key); 2338 2339 _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%s'\n", 2340 item, parm_opt); 2341 } 2342 out: 2343 return parm_opt; 2344 } 2345 2346 static int get_config_item_int(struct pwb_context *ctx, 2347 const char *item, 2348 int config_flag) 2349 { 2350 int i, parm_opt = -1; 2351 2352 if (!(ctx->ctrl & config_flag)) { 2353 goto out; 2354 } 2355 2356 /* let the pam opt take precedence over the pam_winbind.conf option */ 2357 for (i = 0; i < ctx->argc; i++) { 2358 2359 if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) { 2360 char *p; 2361 2362 if ((p = strchr(ctx->argv[i], '=')) == NULL) { 2363 _pam_log(ctx, LOG_INFO, 2364 "no \"=\" delimiter for \"%s\" found\n", 2365 item); 2366 goto out; 2367 } 2368 parm_opt = atoi(p + 1); 2369 _pam_log_debug(ctx, LOG_INFO, 2370 "PAM config: %s '%d'\n", 2371 item, parm_opt); 2372 return parm_opt; 2373 } 2374 } 2375 2376 if (ctx->dict) { 2377 char *key = NULL; 2378 2379 key = talloc_asprintf(ctx, "global:%s", item); 2380 if (!key) { 2381 goto out; 2382 } 2383 2384 parm_opt = tiniparser_getint(ctx->dict, key, -1); 2385 TALLOC_FREE(key); 2386 2387 _pam_log_debug(ctx, LOG_INFO, 2388 "CONFIG file: %s '%d'\n", 2389 item, parm_opt); 2390 } 2391 out: 2392 return parm_opt; 2393 } 2394 2395 static const char *get_krb5_cc_type_from_config(struct pwb_context *ctx) 2396 { 2397 return get_conf_item_string(ctx, "krb5_ccache_type", 2398 WINBIND_KRB5_CCACHE_TYPE); 2399 } 2400 2401 static const char *get_member_from_config(struct pwb_context *ctx) 2402 { 2403 const char *ret = NULL; 2404 ret = get_conf_item_string(ctx, "require_membership_of", 2405 WINBIND_REQUIRED_MEMBERSHIP); 2406 if (ret != NULL) { 2407 return ret; 2408 } 2409 return get_conf_item_string(ctx, "require-membership-of", 2410 WINBIND_REQUIRED_MEMBERSHIP); 2411 } 2412 2413 static int get_warn_pwd_expire_from_config(struct pwb_context *ctx) 2414 { 2415 int ret; 2416 ret = get_config_item_int(ctx, "warn_pwd_expire", 2417 WINBIND_WARN_PWD_EXPIRE); 2418 /* no or broken setting */ 2419 if (ret < 0) { 2420 return DEFAULT_DAYS_TO_WARN_BEFORE_PWD_EXPIRES; 2421 } 2422 return ret; 2423 } 2424 2425 /** 2426 * Retrieve the winbind separator. 2427 * 2428 * @param ctx PAM winbind context. 2429 * 2430 * @return string separator character. NULL on failure. 2431 */ 2432 2433 static char winbind_get_separator(struct pwb_context *ctx) 2434 { 2435 wbcErr wbc_status; 2436 static struct wbcInterfaceDetails *details = NULL; 2437 2438 wbc_status = wbcCtxInterfaceDetails(ctx->wbc_ctx, &details); 2439 if (!WBC_ERROR_IS_OK(wbc_status)) { 2440 _pam_log(ctx, LOG_ERR, 2441 "Could not retrieve winbind interface details: %s", 2442 wbcErrorString(wbc_status)); 2443 return '\0'; 2444 } 2445 2446 if (!details) { 2447 return '\0'; 2448 } 2449 2450 return details->winbind_separator; 2451 } 2452 2453 2454 /** 2455 * Convert a upn to a name. 2456 * 2457 * @param ctx PAM winbind context. 2458 * @param upn User UPN to be translated. 2459 * 2460 * @return converted name. NULL pointer on failure. Caller needs to free. 2461 */ 2462 2463 static char* winbind_upn_to_username(struct pwb_context *ctx, 2464 const char *upn) 2465 { 2466 char sep; 2467 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; 2468 struct wbcDomainSid sid; 2469 enum wbcSidType type; 2470 char *domain = NULL; 2471 char *name; 2472 char *p; 2473 char *result; 2474 2475 /* This cannot work when the winbind separator = @ */ 2476 2477 sep = winbind_get_separator(ctx); 2478 if (!sep || sep == '@') { 2479 return NULL; 2480 } 2481 2482 name = talloc_strdup(ctx, upn); 2483 if (!name) { 2484 return NULL; 2485 } 2486 2487 p = strchr(name, '@'); 2488 if (p == NULL) { 2489 TALLOC_FREE(name); 2490 return NULL; 2491 } 2492 *p = '\0'; 2493 domain = p + 1; 2494 2495 /* Convert the UPN to a SID */ 2496 2497 wbc_status = wbcCtxLookupName(ctx->wbc_ctx, domain, name, &sid, &type); 2498 if (!WBC_ERROR_IS_OK(wbc_status)) { 2499 return NULL; 2500 } 2501 2502 /* Convert the the SID back to the sAMAccountName */ 2503 2504 wbc_status = wbcCtxLookupSid(ctx->wbc_ctx, &sid, &domain, &name, &type); 2505 if (!WBC_ERROR_IS_OK(wbc_status)) { 2506 return NULL; 2507 } 2508 2509 result = talloc_asprintf(ctx, "%s%c%s", domain, sep, name); 2510 wbcFreeMemory(domain); 2511 wbcFreeMemory(name); 2512 return result; 2513 } 2514 2515 static int _pam_delete_cred(pam_handle_t *pamh, int flags, 2516 int argc, enum pam_winbind_request_type type, 2517 const char **argv) 2518 { 2519 int retval = PAM_SUCCESS; 2520 struct pwb_context *ctx = NULL; 2521 struct wbcLogoffUserParams logoff; 2522 struct wbcAuthErrorInfo *error = NULL; 2523 const char *user; 2524 wbcErr wbc_status = WBC_ERR_SUCCESS; 2525 2526 ZERO_STRUCT(logoff); 2527 2528 retval = _pam_winbind_init_context(pamh, flags, argc, argv, type, &ctx); 2529 if (retval != PAM_SUCCESS) { 2530 return retval; 2531 } 2532 2533 _PAM_LOG_FUNCTION_ENTER("_pam_delete_cred", ctx); 2534 2535 if (ctx->ctrl & WINBIND_KRB5_AUTH) { 2536 2537 /* destroy the ccache here */ 2538 2539 uint32_t wbc_flags = 0; 2540 const char *ccname = NULL; 2541 struct passwd *pwd = NULL; 2542 2543 retval = pam_get_user(pamh, &user, _("Username: ")); 2544 if (retval != PAM_SUCCESS) { 2545 _pam_log(ctx, LOG_ERR, 2546 "could not identify user"); 2547 goto out; 2548 } 2549 2550 if (user == NULL) { 2551 _pam_log(ctx, LOG_ERR, 2552 "username was NULL!"); 2553 retval = PAM_USER_UNKNOWN; 2554 goto out; 2555 } 2556 2557 _pam_log_debug(ctx, LOG_DEBUG, 2558 "username [%s] obtained", user); 2559 2560 ccname = pam_getenv(pamh, "KRB5CCNAME"); 2561 if (ccname == NULL) { 2562 _pam_log_debug(ctx, LOG_DEBUG, 2563 "user has no KRB5CCNAME environment"); 2564 } 2565 2566 pwd = getpwnam(user); 2567 if (pwd == NULL) { 2568 retval = PAM_USER_UNKNOWN; 2569 goto out; 2570 } 2571 2572 wbc_flags = WBFLAG_PAM_KRB5 | 2573 WBFLAG_PAM_CONTACT_TRUSTDOM; 2574 2575 logoff.username = user; 2576 2577 if (ccname) { 2578 wbc_status = wbcAddNamedBlob(&logoff.num_blobs, 2579 &logoff.blobs, 2580 "ccfilename", 2581 0, 2582 discard_const_p(uint8_t, ccname), 2583 strlen(ccname)+1); 2584 if (!WBC_ERROR_IS_OK(wbc_status)) { 2585 goto out; 2586 } 2587 } 2588 2589 wbc_status = wbcAddNamedBlob(&logoff.num_blobs, 2590 &logoff.blobs, 2591 "flags", 2592 0, 2593 (uint8_t *)&wbc_flags, 2594 sizeof(wbc_flags)); 2595 if (!WBC_ERROR_IS_OK(wbc_status)) { 2596 goto out; 2597 } 2598 2599 wbc_status = wbcAddNamedBlob(&logoff.num_blobs, 2600 &logoff.blobs, 2601 "user_uid", 2602 0, 2603 (uint8_t *)&pwd->pw_uid, 2604 sizeof(pwd->pw_uid)); 2605 if (!WBC_ERROR_IS_OK(wbc_status)) { 2606 goto out; 2607 } 2608 2609 wbc_status = wbcCtxLogoffUserEx(ctx->wbc_ctx, &logoff, &error); 2610 retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status, 2611 user, "wbcLogoffUser"); 2612 wbcFreeMemory(logoff.blobs); 2613 logoff.blobs = NULL; 2614 2615 if (!WBC_ERROR_IS_OK(wbc_status)) { 2616 _pam_log(ctx, LOG_INFO, 2617 "failed to logoff user %s: %s\n", 2618 user, wbcErrorString(wbc_status)); 2619 } 2620 } 2621 2622 out: 2623 if (logoff.blobs) { 2624 wbcFreeMemory(logoff.blobs); 2625 } 2626 2627 if (!WBC_ERROR_IS_OK(wbc_status)) { 2628 retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status, 2629 user, "wbcLogoffUser"); 2630 } 2631 wbcFreeMemory(error); 2632 2633 /* 2634 * Delete the krb5 ccname variable from the PAM environment 2635 * if it was set by winbind. 2636 */ 2637 if ((ctx->ctrl & WINBIND_KRB5_AUTH) && pam_getenv(pamh, "KRB5CCNAME")) { 2638 pam_putenv(pamh, "KRB5CCNAME"); 2639 } 2640 2641 _PAM_LOG_FUNCTION_LEAVE("_pam_delete_cred", ctx, retval); 2642 2643 TALLOC_FREE(ctx); 2644 2645 return retval; 2646 } 2647 2648 PAM_EXTERN 2649 int pam_sm_authenticate(pam_handle_t *pamh, int flags, 2650 int argc, const char **argv) 2651 { 2652 const char *username; 2653 const char *password; 2654 const char *member = NULL; 2655 const char *cctype = NULL; 2656 int warn_pwd_expire; 2657 int retval = PAM_AUTH_ERR; 2658 char *username_ret = NULL; 2659 char *new_authtok_required = NULL; 2660 char *real_username = NULL; 2661 struct pwb_context *ctx = NULL; 2662 2663 retval = _pam_winbind_init_context(pamh, flags, argc, argv, 2664 PAM_WINBIND_AUTHENTICATE, &ctx); 2665 if (retval != PAM_SUCCESS) { 2666 return retval; 2667 } 2668 2669 _PAM_LOG_FUNCTION_ENTER("pam_sm_authenticate", ctx); 2670 2671 /* Get the username */ 2672 retval = pam_get_user(pamh, &username, NULL); 2673 if ((retval != PAM_SUCCESS) || (!username)) { 2674 _pam_log_debug(ctx, LOG_DEBUG, 2675 "can not get the username"); 2676 retval = PAM_SERVICE_ERR; 2677 goto out; 2678 } 2679 2680 2681 #if defined(AIX) 2682 /* Decode the user name since AIX does not support logn user 2683 names by default. The name is encoded as _#uid. */ 2684 2685 if (username[0] == '_') { 2686 uid_t id = atoi(&username[1]); 2687 struct passwd *pw = NULL; 2688 2689 if ((id!=0) && ((pw = getpwuid(id)) != NULL)) { 2690 real_username = strdup(pw->pw_name); 2691 } 2692 } 2693 #endif 2694 2695 if (!real_username) { 2696 /* Just making a copy of the username we got from PAM */ 2697 if ((real_username = strdup(username)) == NULL) { 2698 _pam_log_debug(ctx, LOG_DEBUG, 2699 "memory allocation failure when copying " 2700 "username"); 2701 retval = PAM_SERVICE_ERR; 2702 goto out; 2703 } 2704 } 2705 2706 /* Maybe this was a UPN */ 2707 2708 if (strchr(real_username, '@') != NULL) { 2709 char *samaccountname = NULL; 2710 2711 samaccountname = winbind_upn_to_username(ctx, 2712 real_username); 2713 if (samaccountname) { 2714 free(real_username); 2715 real_username = strdup(samaccountname); 2716 } 2717 } 2718 2719 retval = _winbind_read_password(ctx, ctx->ctrl, NULL, 2720 _("Password: "), NULL, 2721 &password); 2722 2723 if (retval != PAM_SUCCESS) { 2724 _pam_log(ctx, LOG_ERR, 2725 "Could not retrieve user's password"); 2726 retval = PAM_AUTHTOK_ERR; 2727 goto out; 2728 } 2729 2730 /* Let's not give too much away in the log file */ 2731 2732 #ifdef DEBUG_PASSWORD 2733 _pam_log_debug(ctx, LOG_INFO, 2734 "Verify user '%s' with password '%s'", 2735 real_username, password); 2736 #else 2737 _pam_log_debug(ctx, LOG_INFO, 2738 "Verify user '%s'", real_username); 2739 #endif 2740 2741 member = get_member_from_config(ctx); 2742 cctype = get_krb5_cc_type_from_config(ctx); 2743 warn_pwd_expire = get_warn_pwd_expire_from_config(ctx); 2744 2745 /* Now use the username to look up password */ 2746 retval = winbind_auth_request(ctx, real_username, password, 2747 member, cctype, warn_pwd_expire, 2748 NULL, NULL, NULL, &username_ret); 2749 2750 if (retval == PAM_NEW_AUTHTOK_REQD || 2751 retval == PAM_AUTHTOK_EXPIRED) { 2752 2753 char *new_authtok_required_during_auth = NULL; 2754 2755 new_authtok_required = talloc_asprintf(NULL, "%d", retval); 2756 if (!new_authtok_required) { 2757 retval = PAM_BUF_ERR; 2758 goto out; 2759 } 2760 2761 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, 2762 new_authtok_required, 2763 _pam_winbind_cleanup_func); 2764 2765 retval = PAM_SUCCESS; 2766 2767 new_authtok_required_during_auth = talloc_asprintf(NULL, "%d", true); 2768 if (!new_authtok_required_during_auth) { 2769 retval = PAM_BUF_ERR; 2770 goto out; 2771 } 2772 2773 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH, 2774 new_authtok_required_during_auth, 2775 _pam_winbind_cleanup_func); 2776 2777 goto out; 2778 } 2779 2780 out: 2781 if (username_ret) { 2782 pam_set_item (pamh, PAM_USER, username_ret); 2783 _pam_log_debug(ctx, LOG_INFO, 2784 "Returned user was '%s'", username_ret); 2785 free(username_ret); 2786 } 2787 2788 if (real_username) { 2789 free(real_username); 2790 } 2791 2792 if (!new_authtok_required) { 2793 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, NULL, NULL); 2794 } 2795 2796 if (retval != PAM_SUCCESS) { 2797 _pam_free_data_info3(pamh); 2798 } 2799 2800 _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", ctx, retval); 2801 2802 TALLOC_FREE(ctx); 2803 2804 return retval; 2805 } 2806 2807 PAM_EXTERN 2808 int pam_sm_setcred(pam_handle_t *pamh, int flags, 2809 int argc, const char **argv) 2810 { 2811 int ret = PAM_SYSTEM_ERR; 2812 struct pwb_context *ctx = NULL; 2813 2814 ret = _pam_winbind_init_context(pamh, flags, argc, argv, 2815 PAM_WINBIND_SETCRED, &ctx); 2816 if (ret != PAM_SUCCESS) { 2817 return ret; 2818 } 2819 2820 _PAM_LOG_FUNCTION_ENTER("pam_sm_setcred", ctx); 2821 2822 switch (flags & ~PAM_SILENT) { 2823 2824 case PAM_DELETE_CRED: 2825 ret = _pam_delete_cred(pamh, flags, argc, 2826 PAM_WINBIND_SETCRED, argv); 2827 break; 2828 case PAM_REFRESH_CRED: 2829 _pam_log_debug(ctx, LOG_WARNING, 2830 "PAM_REFRESH_CRED not implemented"); 2831 ret = PAM_SUCCESS; 2832 break; 2833 case PAM_REINITIALIZE_CRED: 2834 _pam_log_debug(ctx, LOG_WARNING, 2835 "PAM_REINITIALIZE_CRED not implemented"); 2836 ret = PAM_SUCCESS; 2837 break; 2838 case PAM_ESTABLISH_CRED: 2839 _pam_log_debug(ctx, LOG_WARNING, 2840 "PAM_ESTABLISH_CRED not implemented"); 2841 ret = PAM_SUCCESS; 2842 break; 2843 default: 2844 ret = PAM_SYSTEM_ERR; 2845 break; 2846 } 2847 2848 _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", ctx, ret); 2849 2850 TALLOC_FREE(ctx); 2851 2852 return ret; 2853 } 2854 2855 /* 2856 * Account management. We want to verify that the account exists 2857 * before returning PAM_SUCCESS 2858 */ 2859 PAM_EXTERN 2860 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, 2861 int argc, const char **argv) 2862 { 2863 const char *username; 2864 int ret = PAM_USER_UNKNOWN; 2865 const char *tmp = NULL; 2866 struct pwb_context *ctx = NULL; 2867 2868 ret = _pam_winbind_init_context(pamh, flags, argc, argv, 2869 PAM_WINBIND_ACCT_MGMT, &ctx); 2870 if (ret != PAM_SUCCESS) { 2871 return ret; 2872 } 2873 2874 _PAM_LOG_FUNCTION_ENTER("pam_sm_acct_mgmt", ctx); 2875 2876 2877 /* Get the username */ 2878 ret = pam_get_user(pamh, &username, NULL); 2879 if ((ret != PAM_SUCCESS) || (!username)) { 2880 _pam_log_debug(ctx, LOG_DEBUG, 2881 "can not get the username"); 2882 ret = PAM_SERVICE_ERR; 2883 goto out; 2884 } 2885 2886 /* Verify the username */ 2887 ret = valid_user(ctx, username); 2888 switch (ret) { 2889 case -1: 2890 /* some sort of system error. The log was already printed */ 2891 ret = PAM_SERVICE_ERR; 2892 goto out; 2893 case 1: 2894 /* the user does not exist */ 2895 _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found", 2896 username); 2897 if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) { 2898 ret = PAM_IGNORE; 2899 goto out; 2900 } 2901 ret = PAM_USER_UNKNOWN; 2902 goto out; 2903 case 0: 2904 pam_get_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, 2905 (const void **)&tmp); 2906 if (tmp != NULL) { 2907 ret = atoi(tmp); 2908 switch (ret) { 2909 case PAM_AUTHTOK_EXPIRED: 2910 /* Since new token is required in this case */ 2911 FALL_THROUGH; 2912 case PAM_NEW_AUTHTOK_REQD: 2913 _pam_log(ctx, LOG_WARNING, 2914 "pam_sm_acct_mgmt success but %s is set", 2915 PAM_WINBIND_NEW_AUTHTOK_REQD); 2916 _pam_log(ctx, LOG_NOTICE, 2917 "user '%s' needs new password", 2918 username); 2919 /* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */ 2920 ret = PAM_NEW_AUTHTOK_REQD; 2921 goto out; 2922 default: 2923 _pam_log(ctx, LOG_WARNING, 2924 "pam_sm_acct_mgmt success"); 2925 _pam_log(ctx, LOG_NOTICE, 2926 "user '%s' granted access", username); 2927 ret = PAM_SUCCESS; 2928 goto out; 2929 } 2930 } 2931 2932 /* Otherwise, the authentication looked good */ 2933 _pam_log(ctx, LOG_NOTICE, 2934 "user '%s' granted access", username); 2935 ret = PAM_SUCCESS; 2936 goto out; 2937 default: 2938 /* we don't know anything about this return value */ 2939 _pam_log(ctx, LOG_ERR, 2940 "internal module error (ret = %d, user = '%s')", 2941 ret, username); 2942 ret = PAM_SERVICE_ERR; 2943 goto out; 2944 } 2945 2946 /* should not be reached */ 2947 ret = PAM_IGNORE; 2948 2949 out: 2950 2951 _PAM_LOG_FUNCTION_LEAVE("pam_sm_acct_mgmt", ctx, ret); 2952 2953 TALLOC_FREE(ctx); 2954 2955 return ret; 2956 } 2957 2958 PAM_EXTERN 2959 int pam_sm_open_session(pam_handle_t *pamh, int flags, 2960 int argc, const char **argv) 2961 { 2962 int ret = PAM_SUCCESS; 2963 struct pwb_context *ctx = NULL; 2964 2965 ret = _pam_winbind_init_context(pamh, flags, argc, argv, 2966 PAM_WINBIND_OPEN_SESSION, &ctx); 2967 if (ret != PAM_SUCCESS) { 2968 return ret; 2969 } 2970 2971 _PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", ctx); 2972 2973 if (ctx->ctrl & WINBIND_MKHOMEDIR) { 2974 /* check and create homedir */ 2975 ret = _pam_mkhomedir(ctx); 2976 } 2977 2978 _PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", ctx, ret); 2979 2980 TALLOC_FREE(ctx); 2981 2982 return ret; 2983 } 2984 2985 PAM_EXTERN 2986 int pam_sm_close_session(pam_handle_t *pamh, int flags, 2987 int argc, const char **argv) 2988 { 2989 int ret = PAM_SUCCESS; 2990 struct pwb_context *ctx = NULL; 2991 2992 ret = _pam_winbind_init_context(pamh, flags, argc, argv, 2993 PAM_WINBIND_CLOSE_SESSION, &ctx); 2994 if (ret != PAM_SUCCESS) { 2995 return ret; 2996 } 2997 2998 _PAM_LOG_FUNCTION_ENTER("pam_sm_close_session", ctx); 2999 3000 _PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", ctx, ret); 3001 3002 TALLOC_FREE(ctx); 3003 3004 return ret; 3005 } 3006 3007 /** 3008 * evaluate whether we need to re-authenticate with kerberos after a 3009 * password change 3010 * 3011 * @param ctx PAM winbind context. 3012 * @param user The username 3013 * 3014 * @return boolean Returns true if required, false if not. 3015 */ 3016 3017 static bool _pam_require_krb5_auth_after_chauthtok(struct pwb_context *ctx, 3018 const char *user) 3019 { 3020 3021 /* Make sure that we only do this if a) the chauthtok got initiated 3022 * during a logon attempt (authenticate->acct_mgmt->chauthtok) b) any 3023 * later password change via the "passwd" command if done by the user 3024 * itself 3025 * NB. If we login from gdm or xdm and the password expires, 3026 * we change the password, but there is no memory cache. 3027 * Thus, even for passthrough login, we should do the 3028 * authentication again to update memory cache. 3029 * --- BoYang 3030 * */ 3031 3032 const char *new_authtok_reqd_during_auth = NULL; 3033 struct passwd *pwd = NULL; 3034 3035 pam_get_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH, 3036 (const void **) &new_authtok_reqd_during_auth); 3037 pam_set_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH, 3038 NULL, NULL); 3039 3040 if (new_authtok_reqd_during_auth) { 3041 return true; 3042 } 3043 3044 pwd = getpwnam(user); 3045 if (!pwd) { 3046 return false; 3047 } 3048 3049 if (getuid() == pwd->pw_uid) { 3050 return true; 3051 } 3052 3053 return false; 3054 } 3055 3056 3057 PAM_EXTERN 3058 int pam_sm_chauthtok(pam_handle_t * pamh, int flags, 3059 int argc, const char **argv) 3060 { 3061 unsigned int lctrl; 3062 int ret; 3063 bool cached_login = false; 3064 3065 /* <DO NOT free() THESE> */ 3066 const char *user; 3067 const char *pass_old; 3068 const char *pass_new; 3069 /* </DO NOT free() THESE> */ 3070 3071 char *Announce; 3072 3073 int retry = 0; 3074 char *username_ret = NULL; 3075 struct wbcAuthErrorInfo *error = NULL; 3076 struct pwb_context *ctx = NULL; 3077 3078 ret = _pam_winbind_init_context(pamh, flags, argc, argv, 3079 PAM_WINBIND_CHAUTHTOK, &ctx); 3080 if (ret != PAM_SUCCESS) { 3081 return ret; 3082 } 3083 3084 _PAM_LOG_FUNCTION_ENTER("pam_sm_chauthtok", ctx); 3085 3086 cached_login = (ctx->ctrl & WINBIND_CACHED_LOGIN); 3087 3088 /* clearing offline bit for auth */ 3089 ctx->ctrl &= ~WINBIND_CACHED_LOGIN; 3090 3091 /* 3092 * First get the name of a user 3093 */ 3094 ret = pam_get_user(pamh, &user, _("Username: ")); 3095 if (ret != PAM_SUCCESS) { 3096 _pam_log(ctx, LOG_ERR, 3097 "password - could not identify user"); 3098 goto out; 3099 } 3100 3101 if (user == NULL) { 3102 _pam_log(ctx, LOG_ERR, "username was NULL!"); 3103 ret = PAM_USER_UNKNOWN; 3104 goto out; 3105 } 3106 3107 _pam_log_debug(ctx, LOG_DEBUG, "username [%s] obtained", user); 3108 3109 /* check if this is really a user in winbindd, not only in NSS */ 3110 ret = valid_user(ctx, user); 3111 switch (ret) { 3112 case 1: 3113 ret = PAM_USER_UNKNOWN; 3114 goto out; 3115 case -1: 3116 ret = PAM_SYSTEM_ERR; 3117 goto out; 3118 default: 3119 break; 3120 } 3121 3122 /* 3123 * obtain and verify the current password (OLDAUTHTOK) for 3124 * the user. 3125 */ 3126 3127 if (flags & PAM_PRELIM_CHECK) { 3128 time_t pwdlastset_prelim = 0; 3129 3130 /* instruct user what is happening */ 3131 3132 #define greeting _("Changing password for") 3133 Announce = talloc_asprintf(ctx, "%s %s", greeting, user); 3134 if (!Announce) { 3135 _pam_log(ctx, LOG_CRIT, 3136 "password - out of memory"); 3137 ret = PAM_BUF_ERR; 3138 goto out; 3139 } 3140 #undef greeting 3141 3142 lctrl = ctx->ctrl | WINBIND__OLD_PASSWORD; 3143 ret = _winbind_read_password(ctx, lctrl, 3144 Announce, 3145 _("(current) NT password: "), 3146 NULL, 3147 (const char **) &pass_old); 3148 TALLOC_FREE(Announce); 3149 if (ret != PAM_SUCCESS) { 3150 _pam_log(ctx, LOG_NOTICE, 3151 "password - (old) token not obtained"); 3152 goto out; 3153 } 3154 3155 /* verify that this is the password for this user */ 3156 3157 ret = winbind_auth_request(ctx, user, pass_old, 3158 NULL, NULL, 0, 3159 &error, NULL, 3160 &pwdlastset_prelim, NULL); 3161 3162 if (ret != PAM_ACCT_EXPIRED && 3163 ret != PAM_AUTHTOK_EXPIRED && 3164 ret != PAM_NEW_AUTHTOK_REQD && 3165 ret != PAM_SUCCESS) { 3166 pass_old = NULL; 3167 goto out; 3168 } 3169 3170 pam_set_data(pamh, PAM_WINBIND_PWD_LAST_SET, 3171 (void *)pwdlastset_prelim, NULL); 3172 3173 ret = pam_set_item(pamh, PAM_OLDAUTHTOK, 3174 (const void *) pass_old); 3175 pass_old = NULL; 3176 if (ret != PAM_SUCCESS) { 3177 _pam_log(ctx, LOG_CRIT, 3178 "failed to set PAM_OLDAUTHTOK"); 3179 } 3180 } else if (flags & PAM_UPDATE_AUTHTOK) { 3181 3182 time_t pwdlastset_update = 0; 3183 3184 /* 3185 * obtain the proposed password 3186 */ 3187 3188 /* 3189 * get the old token back. 3190 */ 3191 3192 ret = pam_get_item(pamh, PAM_OLDAUTHTOK, (const void **) &pass_old); 3193 3194 if (ret != PAM_SUCCESS) { 3195 _pam_log(ctx, LOG_NOTICE, 3196 "user not authenticated"); 3197 goto out; 3198 } 3199 3200 lctrl = ctx->ctrl & ~WINBIND_TRY_FIRST_PASS_ARG; 3201 3202 if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) { 3203 lctrl |= WINBIND_USE_FIRST_PASS_ARG; 3204 } 3205 if (on(WINBIND_TRY_AUTHTOK_ARG, lctrl)) { 3206 lctrl |= WINBIND_TRY_FIRST_PASS_ARG; 3207 } 3208 retry = 0; 3209 ret = PAM_AUTHTOK_ERR; 3210 while ((ret != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) { 3211 /* 3212 * use_authtok is to force the use of a previously entered 3213 * password -- needed for pluggable password strength checking 3214 */ 3215 3216 ret = _winbind_read_password(ctx, lctrl, 3217 NULL, 3218 _("Enter new NT password: "), 3219 _("Retype new NT password: "), 3220 (const char **)&pass_new); 3221 3222 if (ret != PAM_SUCCESS) { 3223 _pam_log_debug(ctx, LOG_ALERT, 3224 "password - " 3225 "new password not obtained"); 3226 pass_old = NULL;/* tidy up */ 3227 goto out; 3228 } 3229 3230 /* 3231 * At this point we know who the user is and what they 3232 * propose as their new password. Verify that the new 3233 * password is acceptable. 3234 */ 3235 3236 if (pass_new[0] == '\0') {/* "\0" password = NULL */ 3237 pass_new = NULL; 3238 } 3239 } 3240 3241 /* 3242 * By reaching here we have approved the passwords and must now 3243 * rebuild the password database file. 3244 */ 3245 pam_get_data(pamh, PAM_WINBIND_PWD_LAST_SET, 3246 (const void **) &pwdlastset_update); 3247 3248 /* 3249 * if cached creds were enabled, make sure to set the 3250 * WINBIND_CACHED_LOGIN bit here in order to have winbindd 3251 * update the cached creds storage - gd 3252 */ 3253 if (cached_login) { 3254 ctx->ctrl |= WINBIND_CACHED_LOGIN; 3255 } 3256 3257 ret = winbind_chauthtok_request(ctx, user, pass_old, 3258 pass_new, pwdlastset_update); 3259 if (ret != PAM_SUCCESS) { 3260 pass_old = pass_new = NULL; 3261 goto out; 3262 } 3263 3264 if (_pam_require_krb5_auth_after_chauthtok(ctx, user)) { 3265 3266 const char *member = NULL; 3267 const char *cctype = NULL; 3268 int warn_pwd_expire; 3269 struct wbcLogonUserInfo *info = NULL; 3270 3271 member = get_member_from_config(ctx); 3272 cctype = get_krb5_cc_type_from_config(ctx); 3273 warn_pwd_expire = get_warn_pwd_expire_from_config(ctx); 3274 3275 /* Keep WINBIND_CACHED_LOGIN bit for 3276 * authentication after changing the password. 3277 * This will update the cached credentials in case 3278 * that winbindd_dual_pam_chauthtok() fails 3279 * to update them. 3280 * --- BoYang 3281 * */ 3282 3283 ret = winbind_auth_request(ctx, user, pass_new, 3284 member, cctype, 0, 3285 &error, &info, 3286 NULL, &username_ret); 3287 pass_old = pass_new = NULL; 3288 3289 if (ret == PAM_SUCCESS) { 3290 3291 struct wbcAuthUserInfo *user_info = NULL; 3292 3293 if (info && info->info) { 3294 user_info = info->info; 3295 } 3296 3297 /* warn a user if the password is about to 3298 * expire soon */ 3299 _pam_warn_password_expiry(ctx, user_info, 3300 warn_pwd_expire, 3301 NULL, NULL); 3302 3303 /* set some info3 info for other modules in the 3304 * stack */ 3305 _pam_set_data_info3(ctx, user_info); 3306 3307 /* put krb5ccname into env */ 3308 _pam_setup_krb5_env(ctx, info); 3309 3310 if (username_ret) { 3311 pam_set_item(pamh, PAM_USER, 3312 username_ret); 3313 _pam_log_debug(ctx, LOG_INFO, 3314 "Returned user was '%s'", 3315 username_ret); 3316 free(username_ret); 3317 } 3318 3319 } 3320 3321 if (info && info->blobs) { 3322 wbcFreeMemory(info->blobs); 3323 } 3324 wbcFreeMemory(info); 3325 3326 goto out; 3327 } 3328 } else { 3329 ret = PAM_SERVICE_ERR; 3330 } 3331 3332 out: 3333 { 3334 /* Deal with offline errors. */ 3335 int i; 3336 const char *codes[] = { 3337 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", 3338 "NT_STATUS_NO_LOGON_SERVERS", 3339 "NT_STATUS_ACCESS_DENIED" 3340 }; 3341 3342 for (i=0; i<ARRAY_SIZE(codes); i++) { 3343 int _ret; 3344 if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) { 3345 break; 3346 } 3347 } 3348 } 3349 3350 wbcFreeMemory(error); 3351 3352 _PAM_LOG_FUNCTION_LEAVE("pam_sm_chauthtok", ctx, ret); 3353 3354 TALLOC_FREE(ctx); 3355 3356 return ret; 3357 } 3358 3359 #ifdef PAM_STATIC 3360 3361 /* static module data */ 3362 3363 struct pam_module _pam_winbind_modstruct = { 3364 MODULE_NAME, 3365 pam_sm_authenticate, 3366 pam_sm_setcred, 3367 pam_sm_acct_mgmt, 3368 pam_sm_open_session, 3369 pam_sm_close_session, 3370 pam_sm_chauthtok 3371 }; 3372 3373 #endif 3374 3375 /* 3376 * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000 3377 * Copyright (c) Tim Potter <tpot@samba.org> 2000 3378 * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002 3379 * Copyright (c) Guenther Deschner <gd@samba.org> 2005-2008 3380 * Copyright (c) Jan Rêkorajski 1999. 3381 * Copyright (c) Andrew G. Morgan 1996-8. 3382 * Copyright (c) Alex O. Yuriev, 1996. 3383 * Copyright (c) Cristian Gafton 1996. 3384 * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software. 3385 * 3386 * Redistribution and use in source and binary forms, with or without 3387 * modification, are permitted provided that the following conditions 3388 * are met: 3389 * 1. Redistributions of source code must retain the above copyright 3390 * notice, and the entire permission notice in its entirety, 3391 * including the disclaimer of warranties. 3392 * 2. Redistributions in binary form must reproduce the above copyright 3393 * notice, this list of conditions and the following disclaimer in the 3394 * documentation and/or other materials provided with the distribution. 3395 * 3. The name of the author may not be used to endorse or promote 3396 * products derived from this software without specific prior 3397 * written permission. 3398 * 3399 * ALTERNATIVELY, this product may be distributed under the terms of 3400 * the GNU Public License, in which case the provisions of the GPL are 3401 * required INSTEAD OF the above restrictions. (This clause is 3402 * necessary due to a potential bad interaction between the GPL and 3403 * the restrictions contained in a BSD-style copyright.) 3404 * 3405 * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED 3406 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 3407 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 3408 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 3409 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 3410 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 3411 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3412 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 3413 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3414 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 3415 * OF THE POSSIBILITY OF SUCH DAMAGE. 3416 */ 3417