| // Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This is the Chaps client PAM module. It loads the users token into the |
| // user-slot when they login. |
| |
| #include <grp.h> |
| #include <pwd.h> |
| #include <security/pam_appl.h> |
| #include <security/pam_modules.h> |
| |
| #include <string> |
| |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <brillo/secure_blob.h> |
| |
| #include "chaps/chaps_utility.h" |
| #include "chaps/isolate.h" |
| #include "chaps/isolate_login_client.h" |
| #include "chaps/pam_helper.h" |
| #include "chaps/platform_globals.h" |
| #include "chaps/token_file_manager.h" |
| #include "chaps/token_manager_client.h" |
| |
| using std::string; |
| using brillo::SecureBlob; |
| |
| #define PAM_EXPORT_SPEC EXPORT_SPEC PAM_EXTERN |
| |
| namespace { |
| |
| const char kLogoutOnCloseSessionEnvName[] = "CHAPS_LOGOUT_ON_CLOSE_SESSION"; |
| |
| // Isolate login client used to provide chaps login functionality. |
| chaps::IsolateLoginClient* g_login_client = NULL; |
| |
| // Pam helper used to get data passed via the pam_handle. |
| chaps::PamHelper* g_pam_helper = NULL; |
| |
| // Set to true when Init() is first called. |
| bool g_is_initialized = false; |
| |
| } // namespace |
| |
| namespace chaps { |
| |
| EXPORT_SPEC void EnableMock(IsolateLoginClient* login_client, |
| PamHelper* pam_helper) { |
| CHECK(!g_is_initialized); |
| g_login_client = login_client; |
| g_pam_helper = pam_helper; |
| g_is_initialized = true; |
| } |
| |
| EXPORT_SPEC void DisableMock() { |
| g_login_client = NULL; |
| g_pam_helper = NULL; |
| g_is_initialized = false; |
| } |
| |
| } // namespace chaps |
| |
| static bool GetChapsdUser(uid_t* uid, gid_t* gid) { |
| CHECK(uid); |
| CHECK(gid); |
| long buf_length = sysconf(_SC_GETPW_R_SIZE_MAX); // NOLINT long |
| if (buf_length < 0) { |
| buf_length = 4096; |
| } |
| passwd passwd_buf; |
| passwd* chaps_pwd = nullptr; |
| std::vector<char> buf(buf_length); |
| if (getpwnam_r(chaps::kChapsdProcessUser, &passwd_buf, buf.data(), buf_length, |
| &chaps_pwd) || chaps_pwd == nullptr) { |
| return false; |
| } |
| *uid = chaps_pwd->pw_uid; |
| *gid = chaps_pwd->pw_gid; |
| return true; |
| } |
| |
| static bool Init() { |
| if (!g_is_initialized) { |
| uid_t chapsd_uid; |
| gid_t chapsd_gid; |
| if (!GetChapsdUser(&chapsd_uid, &chapsd_gid)) { |
| LOG(ERROR) << "Failed to get chapsd user"; |
| return false; |
| } |
| g_login_client = new chaps::IsolateLoginClient( |
| new chaps::IsolateCredentialManager(), |
| new chaps::TokenFileManager(chapsd_uid, chapsd_gid), |
| new chaps::TokenManagerClient()); |
| g_pam_helper = new chaps::PamHelper(); |
| g_is_initialized = true; |
| } |
| return true; |
| } |
| |
| PAM_EXPORT_SPEC int pam_sm_authenticate(pam_handle_t* pam_handle, int flags, |
| int argc, const char** argv) { |
| logging::SetMinLogLevel((flags & PAM_SILENT) ? |
| logging::LOG_FATAL : logging::LOG_INFO); |
| |
| if (!Init()) |
| return PAM_SERVICE_ERR; |
| |
| string user; |
| if (!g_pam_helper->GetPamUser(pam_handle, &user)) |
| return PAM_SERVICE_ERR; |
| |
| SecureBlob password; |
| if (!g_pam_helper->GetPamPassword(pam_handle, false, &password)) |
| return PAM_AUTH_ERR; |
| |
| if (!g_pam_helper->SaveUserAndPassword(pam_handle, user, password)) |
| return PAM_SERVICE_ERR; |
| |
| return PAM_SUCCESS; |
| } |
| |
| PAM_EXPORT_SPEC int pam_sm_open_session(pam_handle_t* pam_handle, int flags, |
| int argc, const char** argv) { |
| logging::SetMinLogLevel((flags & PAM_SILENT) ? |
| logging::LOG_FATAL : logging::LOG_INFO); |
| if (!Init()) |
| return PAM_SERVICE_ERR; |
| |
| string user; |
| if (!g_pam_helper->GetPamUser(pam_handle, &user)) |
| return PAM_SERVICE_ERR; |
| |
| string saved_user; |
| SecureBlob saved_password; |
| if (!g_pam_helper->RetrieveUserAndPassword(pam_handle, &saved_user, |
| &saved_password)) { |
| // This can happen if pam_sm_authenticate wasn't called in this pam session. |
| return PAM_IGNORE; |
| } |
| |
| if (user.compare(saved_user) != 0) { |
| // User who authenticated is opening a session as a different user, this |
| // can happen for example in when user sudo's. |
| return PAM_IGNORE; |
| } |
| |
| if (!g_login_client->LoginUser(saved_user, saved_password)) |
| return PAM_SERVICE_ERR; |
| |
| if (!g_pam_helper->PutEnvironmentVariable( |
| pam_handle, kLogoutOnCloseSessionEnvName, "1")) |
| return PAM_SERVICE_ERR; |
| |
| return PAM_SUCCESS; |
| } |
| |
| PAM_EXPORT_SPEC int pam_sm_close_session(pam_handle_t* pam_handle, int flags, |
| int argc, const char** argv) { |
| if (!Init()) |
| return PAM_SERVICE_ERR; |
| |
| string close_session_env; |
| if (!g_pam_helper->GetEnvironmentVariable( |
| pam_handle, kLogoutOnCloseSessionEnvName, &close_session_env) || |
| close_session_env.compare("1") != 0) { |
| // We never logged user in, so don't log them out here. |
| return PAM_IGNORE; |
| } |
| |
| string user; |
| if (!g_pam_helper->GetPamUser(pam_handle, &user)) |
| return PAM_SERVICE_ERR; |
| |
| if (!g_login_client->LogoutUser(user)) |
| return PAM_SERVICE_ERR; |
| |
| return PAM_SUCCESS; |
| } |
| |
| PAM_EXPORT_SPEC int pam_sm_chauthtok(pam_handle_t* pam_handle, int flags, |
| int argc, const char** argv) { |
| if (!Init()) |
| return PAM_SERVICE_ERR; |
| |
| if (flags & PAM_PRELIM_CHECK || !(flags & PAM_UPDATE_AUTHTOK)) |
| return PAM_IGNORE; |
| |
| string user; |
| if (!g_pam_helper->GetPamUser(pam_handle, &user)) |
| return PAM_SERVICE_ERR; |
| |
| SecureBlob old_password; |
| if (!g_pam_helper->GetPamPassword(pam_handle, true, &old_password)) |
| return PAM_AUTH_ERR; |
| |
| SecureBlob new_password; |
| if (!g_pam_helper->GetPamPassword(pam_handle, false, &new_password)) |
| return PAM_AUTH_ERR; |
| |
| if (!g_login_client->ChangeUserAuth(user, old_password, new_password)) |
| return PAM_SERVICE_ERR; |
| |
| return PAM_SUCCESS; |
| } |
| |
| PAM_EXPORT_SPEC int pam_sm_setcred(pam_handle_t* pam_handle, int flags, |
| int argc, const char** argv) { |
| return PAM_IGNORE; |
| } |
| |
| PAM_EXPORT_SPEC int pam_sm_acct_mgmt(pam_handle_t* pam_handle, int flags, |
| int argc, const char** argv) { |
| return PAM_IGNORE; |
| } |