static void do_pam_chauthtok(struct LOCAL_request *lreq) { int ret; const char *password; char *salt; char *new_hash; struct pam_data *pd; pd = lreq->preq->pd; ret = sss_authtok_get_password(pd->newauthtok, &password, NULL); if (ret) { /* TODO: should we allow null passwords via a config option ? */ if (ret == ENOENT) { DEBUG(1, ("Empty passwords are not allowed!\n")); } lreq->error = EINVAL; goto done; } ret = s3crypt_gen_salt(lreq, &salt); NEQ_CHECK_OR_JUMP(ret, EOK, ("Salt generation failed.\n"), lreq->error, ret, done); DEBUG(4, ("Using salt [%s]\n", salt)); ret = s3crypt_sha512(lreq, password, salt, &new_hash); NEQ_CHECK_OR_JUMP(ret, EOK, ("Hash generation failed.\n"), lreq->error, ret, done); DEBUG(4, ("New hash [%s]\n", new_hash)); lreq->mod_attrs = sysdb_new_attrs(lreq); NULL_CHECK_OR_JUMP(lreq->mod_attrs, ("sysdb_new_attrs failed.\n"), lreq->error, ENOMEM, done); ret = sysdb_attrs_add_string(lreq->mod_attrs, SYSDB_PWD, new_hash); NEQ_CHECK_OR_JUMP(ret, EOK, ("sysdb_attrs_add_string failed.\n"), lreq->error, ret, done); ret = sysdb_attrs_add_long(lreq->mod_attrs, "lastPasswordChange", (long)time(NULL)); NEQ_CHECK_OR_JUMP(ret, EOK, ("sysdb_attrs_add_long failed.\n"), lreq->error, ret, done); ret = sysdb_set_user_attr(lreq->dbctx, lreq->domain, lreq->preq->pd->user, lreq->mod_attrs, SYSDB_MOD_REP); NEQ_CHECK_OR_JUMP(ret, EOK, ("sysdb_set_user_attr failed.\n"), lreq->error, ret, done); done: sss_authtok_set_empty(pd->newauthtok); }
static void krb5_auth_cache_creds(struct krb5_ctx *krb5_ctx, struct sss_domain_info *domain, struct confdb_ctx *cdb, struct pam_data *pd, uid_t uid, int *pam_status, int *dp_err) { const char *password = NULL; errno_t ret; if (sss_authtok_get_type(pd->authtok) != SSS_AUTHTOK_TYPE_PASSWORD) { DEBUG(SSSDBG_MINOR_FAILURE, "Delayed authentication is only available for password " "authentication (single factor).\n"); return; } ret = sss_authtok_get_password(pd->authtok, &password, NULL); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get password [%d] %s\n", ret, strerror(ret)); *pam_status = PAM_SYSTEM_ERR; *dp_err = DP_ERR_OK; return; } ret = sysdb_cache_auth(domain, pd->user, password, cdb, true, NULL, NULL); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Offline authentication failed\n"); *pam_status = cached_login_pam_status(ret); *dp_err = DP_ERR_OK; return; } ret = add_user_to_delayed_online_authentication(krb5_ctx, pd, uid); if (ret != EOK) { /* This error is not fatal */ DEBUG(SSSDBG_CRIT_FAILURE, "add_user_to_delayed_online_authentication failed.\n"); } *pam_status = PAM_AUTHINFO_UNAVAIL; *dp_err = DP_ERR_OFFLINE; }
static void pam_reply(struct pam_auth_req *preq) { struct cli_ctx *cctx; uint8_t *body; size_t blen; int ret; int32_t resp_c; int32_t resp_size; struct response_data *resp; int p; struct timeval tv; struct tevent_timer *te; struct pam_data *pd; struct pam_ctx *pctx; uint32_t user_info_type; time_t exp_date = -1; time_t delay_until = -1; pd = preq->pd; cctx = preq->cctx; pctx = talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx); DEBUG(SSSDBG_FUNC_DATA, "pam_reply called with result [%d].\n", pd->pam_status); if (pd->pam_status == PAM_AUTHINFO_UNAVAIL) { switch(pd->cmd) { case SSS_PAM_AUTHENTICATE: if ((preq->domain != NULL) && (preq->domain->cache_credentials == true) && (pd->offline_auth == false)) { const char *password = NULL; /* do auth with offline credentials */ pd->offline_auth = true; if (preq->domain->sysdb == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "Fatal: Sysdb CTX not found for domain" " [%s]!\n", preq->domain->name); goto done; } ret = sss_authtok_get_password(pd->authtok, &password, NULL); if (ret) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get password.\n"); goto done; } ret = sysdb_cache_auth(preq->domain, pd->user, password, pctx->rctx->cdb, false, &exp_date, &delay_until); pam_handle_cached_login(preq, ret, exp_date, delay_until); return; } break; case SSS_PAM_CHAUTHTOK_PRELIM: case SSS_PAM_CHAUTHTOK: DEBUG(SSSDBG_FUNC_DATA, "Password change not possible while offline.\n"); pd->pam_status = PAM_AUTHTOK_ERR; user_info_type = SSS_PAM_USER_INFO_OFFLINE_CHPASS; ret = pam_add_response(pd, SSS_PAM_USER_INFO, sizeof(uint32_t), (const uint8_t *) &user_info_type); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n"); goto done; } break; /* TODO: we need the pam session cookie here to make sure that cached * authentication was successful */ case SSS_PAM_SETCRED: case SSS_PAM_ACCT_MGMT: case SSS_PAM_OPEN_SESSION: case SSS_PAM_CLOSE_SESSION: DEBUG(SSSDBG_OP_FAILURE, "Assuming offline authentication setting status for " "pam call %d to PAM_SUCCESS.\n", pd->cmd); pd->pam_status = PAM_SUCCESS; break; default: DEBUG(SSSDBG_CRIT_FAILURE, "Unknown PAM call [%d].\n", pd->cmd); pd->pam_status = PAM_MODULE_UNKNOWN; } } if (pd->response_delay > 0) { ret = gettimeofday(&tv, NULL); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "gettimeofday failed [%d][%s].\n", errno, strerror(errno)); goto done; } tv.tv_sec += pd->response_delay; tv.tv_usec = 0; pd->response_delay = 0; te = tevent_add_timer(cctx->ev, cctx, tv, pam_reply_delay, preq); if (te == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Failed to add event pam_reply_delay.\n"); goto done; } return; } /* If this was a successful login, save the lastLogin time */ if (pd->cmd == SSS_PAM_AUTHENTICATE && pd->pam_status == PAM_SUCCESS && preq->domain->cache_credentials && !pd->offline_auth && !pd->last_auth_saved && NEED_CHECK_PROVIDER(preq->domain->provider)) { ret = set_last_login(preq); if (ret != EOK) { goto done; } return; } ret = sss_packet_new(cctx->creq, 0, sss_packet_get_cmd(cctx->creq->in), &cctx->creq->out); if (ret != EOK) { goto done; } ret = filter_responses(pctx->rctx->cdb, pd->resp_list); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "filter_responses failed, not fatal.\n"); } if (pd->domain != NULL) { ret = pam_add_response(pd, SSS_PAM_DOMAIN_NAME, strlen(pd->domain)+1, (uint8_t *) pd->domain); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n"); goto done; } } resp_c = 0; resp_size = 0; resp = pd->resp_list; while(resp != NULL) { if (!resp->do_not_send_to_client) { resp_c++; resp_size += resp->len; } resp = resp->next; } ret = sss_packet_grow(cctx->creq->out, sizeof(int32_t) + sizeof(int32_t) + resp_c * 2* sizeof(int32_t) + resp_size); if (ret != EOK) { goto done; } sss_packet_get_body(cctx->creq->out, &body, &blen); DEBUG(SSSDBG_FUNC_DATA, "blen: %zu\n", blen); p = 0; memcpy(&body[p], &pd->pam_status, sizeof(int32_t)); p += sizeof(int32_t); memcpy(&body[p], &resp_c, sizeof(int32_t)); p += sizeof(int32_t); resp = pd->resp_list; while(resp != NULL) { if (!resp->do_not_send_to_client) { memcpy(&body[p], &resp->type, sizeof(int32_t)); p += sizeof(int32_t); memcpy(&body[p], &resp->len, sizeof(int32_t)); p += sizeof(int32_t); memcpy(&body[p], resp->data, resp->len); p += resp->len; } resp = resp->next; } done: sss_cmd_done(cctx, preq); }
static void krb5_auth_store_creds(struct sss_domain_info *domain, struct pam_data *pd) { const char *password = NULL; const char *fa2; size_t password_len; size_t fa2_len = 0; int ret = EOK; switch(pd->cmd) { case SSS_CMD_RENEW: /* The authtok is set to the credential cache * during renewal. We don't want to save this * as the cached password. */ break; case SSS_PAM_PREAUTH: /* There are no credentials available during pre-authentication, * nothing to do. */ break; case SSS_PAM_AUTHENTICATE: case SSS_PAM_CHAUTHTOK_PRELIM: if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_2FA) { ret = sss_authtok_get_2fa(pd->authtok, &password, &password_len, &fa2, &fa2_len); if (ret == EOK && password_len < domain->cache_credentials_min_ff_length) { DEBUG(SSSDBG_FATAL_FAILURE, "First factor is too short to be cache, " "minimum length is [%u].\n", domain->cache_credentials_min_ff_length); ret = EINVAL; } } else if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_PASSWORD) { ret = sss_authtok_get_password(pd->authtok, &password, NULL); } else { DEBUG(SSSDBG_MINOR_FAILURE, "Cannot cache authtok type [%d].\n", sss_authtok_get_type(pd->authtok)); ret = EINVAL; } break; case SSS_PAM_CHAUTHTOK: ret = sss_authtok_get_password(pd->newauthtok, &password, NULL); break; default: DEBUG(SSSDBG_FATAL_FAILURE, "unsupported PAM command [%d].\n", pd->cmd); } if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get password [%d] %s\n", ret, strerror(ret)); /* password caching failures are not fatal errors */ return; } if (password == NULL) { if (pd->cmd != SSS_CMD_RENEW && pd->cmd != SSS_PAM_PREAUTH) { DEBUG(SSSDBG_FATAL_FAILURE, "password not available, offline auth may not work.\n"); /* password caching failures are not fatal errors */ } return; } ret = sysdb_cache_password_ex(domain, pd->user, password, sss_authtok_get_type(pd->authtok), fa2_len); if (ret) { DEBUG(SSSDBG_OP_FAILURE, "Failed to cache password, offline auth may not work." " (%d)[%s]!?\n", ret, strerror(ret)); /* password caching failures are not fatal errors */ } }
int LOCAL_pam_handler(struct pam_auth_req *preq) { struct LOCAL_request *lreq; static const char *attrs[] = {SYSDB_NAME, SYSDB_PWD, SYSDB_DISABLED, SYSDB_LAST_LOGIN, "lastPasswordChange", "accountExpires", SYSDB_FAILED_LOGIN_ATTEMPTS, "passwordHint", "passwordHistory", SYSDB_LAST_FAILED_LOGIN, NULL}; struct ldb_result *res; const char *username = NULL; const char *pwdhash = NULL; char *new_hash = NULL; const char *password; struct pam_data *pd = preq->pd; int ret; DEBUG(4, ("LOCAL pam handler.\n")); lreq = talloc_zero(preq, struct LOCAL_request); if (!lreq) { return ENOMEM; } lreq->dbctx = preq->domain->sysdb; if (lreq->dbctx == NULL) { DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n")); talloc_free(lreq); return ENOENT; } lreq->domain = preq->domain; lreq->ev = preq->cctx->ev; lreq->preq = preq; pd->pam_status = PAM_SUCCESS; ret = sysdb_get_user_attr(lreq, lreq->dbctx, preq->domain, preq->pd->user, attrs, &res); if (ret != EOK) { DEBUG(1, ("sysdb_get_user_attr failed.\n")); talloc_free(lreq); return ret; } if (res->count < 1) { DEBUG(4, ("No user found with filter ["SYSDB_PWNAM_FILTER"]\n", pd->user, pd->user)); pd->pam_status = PAM_USER_UNKNOWN; goto done; } else if (res->count > 1) { DEBUG(4, ("More than one object found with filter ["SYSDB_PWNAM_FILTER"]\n", pd->user, pd->user)); lreq->error = EFAULT; goto done; } username = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL); if (strcmp(username, pd->user) != 0) { DEBUG(1, ("Expected username [%s] get [%s].\n", pd->user, username)); lreq->error = EINVAL; goto done; } lreq->res = res; switch (pd->cmd) { case SSS_PAM_AUTHENTICATE: case SSS_PAM_CHAUTHTOK: case SSS_PAM_CHAUTHTOK_PRELIM: if ((pd->cmd == SSS_PAM_CHAUTHTOK || pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) && lreq->preq->cctx->priv == 1) { /* TODO: maybe this is a candiate for an explicit audit message. */ DEBUG(4, ("allowing root to reset a password.\n")); break; } ret = sss_authtok_get_password(pd->authtok, &password, NULL); NEQ_CHECK_OR_JUMP(ret, EOK, ("Failed to get password.\n"), lreq->error, ret, done); pwdhash = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_PWD, NULL); NULL_CHECK_OR_JUMP(pwdhash, ("No password stored.\n"), lreq->error, LDB_ERR_NO_SUCH_ATTRIBUTE, done); DEBUG(4, ("user: [%s], password hash: [%s]\n", username, pwdhash)); ret = s3crypt_sha512(lreq, password, pwdhash, &new_hash); NEQ_CHECK_OR_JUMP(ret, EOK, ("nss_sha512_crypt failed.\n"), lreq->error, ret, done); DEBUG(4, ("user: [%s], new hash: [%s]\n", username, new_hash)); if (strcmp(new_hash, pwdhash) != 0) { DEBUG(1, ("Passwords do not match.\n")); do_failed_login(lreq); goto done; } break; } switch (pd->cmd) { case SSS_PAM_AUTHENTICATE: do_successful_login(lreq); break; case SSS_PAM_CHAUTHTOK: do_pam_chauthtok(lreq); break; case SSS_PAM_ACCT_MGMT: do_pam_acct_mgmt(lreq); break; case SSS_PAM_SETCRED: break; case SSS_PAM_OPEN_SESSION: break; case SSS_PAM_CLOSE_SESSION: break; case SSS_PAM_CHAUTHTOK_PRELIM: break; default: lreq->error = EINVAL; DEBUG(1, ("Unknown PAM task [%d].\n")); } done: sss_authtok_set_empty(pd->newauthtok); sss_authtok_set_empty(pd->authtok); prepare_reply(lreq); return EOK; }