2 * Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
17 * @file pam-key-manager-plugin.cpp
18 * @author Maciej Karpiuk (m.karpiuk2@samsung.com)
20 * @brief PAM module to handle session and password events.
23 #include <sys/param.h>
31 #include <symbol-visibility.h>
32 #include <security/pam_modules.h>
33 #include <security/pam_ext.h>
34 #include <security/pam_appl.h>
37 #include <ckm/ckm-control.h>
40 #define PASSWORD_SHADOWED "x"
41 std::string old_password;
43 bool identify_user_pwd(pam_handle_t *pamh, uid_t &uid, std::string &passwd)
48 if ((pam_err = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
52 struct passwd *result = nullptr;
53 int bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
56 bufsize = 16384; /* should be more than enough */
58 memset(&pwd, 0x00, sizeof(pwd));
59 std::vector<char> buf(bufsize, 0);
61 int ret = getpwnam_r(user, &pwd, buf.data(), bufsize, &result);
62 if (ret != 0 || result == nullptr)
65 if (strcmp(pwd.pw_passwd, PASSWORD_SHADOWED) == 0) {
67 struct spwd *result_sh = nullptr;
69 memset(&pwd_sh, 0x00, sizeof(pwd_sh));
70 std::vector<char> buf_sh(bufsize, 0);
72 ret = getspnam_r(user, &pwd_sh, buf_sh.data(), bufsize, &result_sh);
73 if (ret != 0 || result_sh == nullptr)
76 passwd = std::string(pwd_sh.sp_pwdp);
78 passwd = std::string(pwd.pw_passwd);
86 COMMON_API PAM_EXTERN int
87 pam_sm_open_session(pam_handle_t *pamh, int /*flags*/, int /*argc*/,
88 const char **/*argv*/)
94 if (identify_user_pwd(pamh, uid, passwd))
95 return PAM_SESSION_ERR;
97 auto control = CKM::Control::create();
98 int ec = control->unlockUserKey(uid, passwd.c_str());
100 if (ec == CKM_API_SUCCESS)
103 if (ec == CKM_API_ERROR_AUTHENTICATION_FAILED) {
104 pam_syslog(pamh, LOG_ERR, "key-manager and system password desynchronized,"
105 "removing key-manager database for user: %d\n", uid);
107 // key-manager<->system password desync
108 // remove the user content
109 ec = control->removeUserData(uid);
111 if (ec == CKM_API_SUCCESS) {
112 ec = CKM::Control::create()->unlockUserKey(uid, passwd.c_str());
114 if (ec == CKM_API_SUCCESS)
117 pam_syslog(pamh, LOG_ERR, "key-manager and system password desynchronized,"
118 "attempt to create new database failed: %d\n", ec);
120 pam_syslog(pamh, LOG_ERR, "key-manager and system password desynchronized and"
121 "recovery attempt to remove broken database failed: %d\n", ec);
125 return PAM_SESSION_ERR;
128 COMMON_API PAM_EXTERN int
129 pam_sm_close_session(pam_handle_t *pamh, int /*flags*/, int /*argc*/,
130 const char **/*argv*/)
136 if (identify_user_pwd(pamh, uid, passwd))
137 return PAM_SESSION_ERR;
139 if (CKM::Control::create()->lockUserKey(uid) == CKM_API_SUCCESS)
142 return PAM_SESSION_ERR;
145 COMMON_API PAM_EXTERN int
146 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
149 pam_syslog(pamh, LOG_ERR,
150 "key-manager plugin called with inappropriate arguments\n");
151 return PAM_SERVICE_ERR;
158 if (identify_user_pwd(pamh, uid, passwd))
159 return PAM_USER_UNKNOWN;
161 // attention: argv[0] is the argument, not the binary/so name
162 // args are in arg_name=value format
163 if (strstr(argv[0], "change_step")) {
164 if (strstr(argv[0], "before")) {
165 if (!(flags & PAM_PRELIM_CHECK))
166 old_password = passwd;
169 } else if (strstr(argv[0], "after")) {
170 if (flags & PAM_PRELIM_CHECK)
173 if (old_password.size() == 0) {
174 pam_syslog(pamh, LOG_ERR,
175 "attempt to change key-manager password w/o old password\n");
176 return PAM_SERVICE_ERR;
179 std::string local_old_pwd = old_password;
180 old_password.clear();
182 // CKM does not allow to change user password if database does
183 // not exists. We must create database before change password.
184 auto ctrl = CKM::Control::create();
185 int ec = ctrl->unlockUserKey(uid, local_old_pwd.c_str());
187 if (CKM_API_SUCCESS != ec) {
188 // no DB reset here: somebody else might have changed password in mean time
189 // if desync happened, next login attempt will remove the DB
190 pam_syslog(pamh, LOG_ERR, "attempt to change key-manager password failed:"
191 "can not open/create the database, ec: %d\n", ec);
192 return PAM_SERVICE_ERR;
195 ec = ctrl->changeUserPassword(uid, local_old_pwd.c_str(), passwd.c_str());
197 if (CKM_API_SUCCESS != ec) {
198 pam_syslog(pamh, LOG_ERR, "attempt to change key-manager password ec: %d\n",
200 return PAM_SERVICE_ERR;
207 pam_syslog(pamh, LOG_ERR,
208 "key-manager plugin called with no valid \"change_step\" option setting\n");
209 return PAM_SERVICE_ERR;