Release 0.1.54.3
[platform/core/security/key-manager.git] / src / pam_plugin / pam-key-manager-plugin.cpp
1 /*
2  *  Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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
15  */
16 /*
17  * @file        pam-key-manager-plugin.cpp
18  * @author      Maciej Karpiuk (m.karpiuk2@samsung.com)
19  * @version     1.0
20  * @brief       PAM module to handle session and password events.
21  */
22
23 #include <sys/param.h>
24
25 #include <string>
26 #include <pwd.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <symbol-visibility.h>
32 #include <security/pam_modules.h>
33 #include <security/pam_ext.h>
34 #include <security/pam_appl.h>
35 #include <syslog.h>
36 #include <shadow.h>
37 #include <ckm/ckm-control.h>
38
39 namespace {
40 #define PASSWORD_SHADOWED   "x"
41 std::string old_password;
42
43 bool identify_user_pwd(pam_handle_t *pamh, uid_t &uid, std::string &passwd)
44 {
45         int pam_err;
46         const char *user;
47
48         if ((pam_err = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
49                 return true;
50
51         struct passwd pwd;
52         struct passwd *result = nullptr;
53         int bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
54
55         if (bufsize <= 0)
56                 bufsize = 16384; /* should be more than enough */
57
58         memset(&pwd, 0x00, sizeof(pwd));
59         std::vector<char> buf(bufsize, 0);
60
61         int ret = getpwnam_r(user, &pwd, buf.data(), bufsize, &result);
62         if (ret != 0 || result == nullptr)
63                 return true;
64
65         if (strcmp(pwd.pw_passwd, PASSWORD_SHADOWED) == 0) {
66                 struct spwd pwd_sh;
67                 struct spwd *result_sh = nullptr;
68
69                 memset(&pwd_sh, 0x00, sizeof(pwd_sh));
70                 std::vector<char> buf_sh(bufsize, 0);
71
72                 ret = getspnam_r(user, &pwd_sh, buf_sh.data(), bufsize, &result_sh);
73                 if (ret != 0 || result_sh == nullptr)
74                         return true;
75
76                 passwd = std::string(pwd_sh.sp_pwdp);
77         } else {
78                 passwd = std::string(pwd.pw_passwd);
79         }
80
81         uid = pwd.pw_uid;
82         return false;
83 }
84 }
85
86 COMMON_API PAM_EXTERN int
87 pam_sm_open_session(pam_handle_t *pamh, int /*flags*/, int /*argc*/,
88                                         const char **/*argv*/)
89 {
90         // identify user
91         uid_t uid = -1;
92         std::string passwd;
93
94         if (identify_user_pwd(pamh, uid, passwd))
95                 return PAM_SESSION_ERR;
96
97         auto control = CKM::Control::create();
98         int ec = control->unlockUserKey(uid, passwd.c_str());
99
100         if (ec == CKM_API_SUCCESS)
101                 return PAM_SUCCESS;
102
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);
106
107                 // key-manager<->system password desync
108                 // remove the user content
109                 ec = control->removeUserData(uid);
110
111                 if (ec == CKM_API_SUCCESS) {
112                         ec = CKM::Control::create()->unlockUserKey(uid, passwd.c_str());
113
114                         if (ec == CKM_API_SUCCESS)
115                                 return PAM_SUCCESS;
116
117                         pam_syslog(pamh, LOG_ERR, "key-manager and system password desynchronized,"
118                                            "attempt to create new database failed: %d\n", ec);
119                 } else {
120                         pam_syslog(pamh, LOG_ERR, "key-manager and system password desynchronized and"
121                                            "recovery attempt to remove broken database failed: %d\n", ec);
122                 }
123         }
124
125         return PAM_SESSION_ERR;
126 }
127
128 COMMON_API PAM_EXTERN int
129 pam_sm_close_session(pam_handle_t *pamh, int /*flags*/, int /*argc*/,
130                                          const char **/*argv*/)
131 {
132         // identify user
133         uid_t uid = -1;
134         std::string passwd;
135
136         if (identify_user_pwd(pamh, uid, passwd))
137                 return PAM_SESSION_ERR;
138
139         if (CKM::Control::create()->lockUserKey(uid) == CKM_API_SUCCESS)
140                 return PAM_SUCCESS;
141
142         return PAM_SESSION_ERR;
143 }
144
145 COMMON_API PAM_EXTERN int
146 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
147 {
148         if (argc == 0) {
149                 pam_syslog(pamh, LOG_ERR,
150                                    "key-manager plugin called with inappropriate arguments\n");
151                 return PAM_SERVICE_ERR;
152         }
153
154         // identify user
155         uid_t uid = -1;
156         std::string passwd;
157
158         if (identify_user_pwd(pamh, uid, passwd))
159                 return PAM_USER_UNKNOWN;
160
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;
167
168                         return PAM_SUCCESS;
169                 } else if (strstr(argv[0], "after")) {
170                         if (flags & PAM_PRELIM_CHECK)
171                                 return PAM_SUCCESS;
172
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;
177                         }
178
179                         std::string local_old_pwd = old_password;
180                         old_password.clear();
181
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());
186
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;
193                         }
194
195                         ec = ctrl->changeUserPassword(uid, local_old_pwd.c_str(), passwd.c_str());
196
197                         if (CKM_API_SUCCESS != ec) {
198                                 pam_syslog(pamh, LOG_ERR, "attempt to change key-manager password ec: %d\n",
199                                                    ec);
200                                 return PAM_SERVICE_ERR;
201                         }
202
203                         return PAM_SUCCESS;
204                 }
205         }
206
207         pam_syslog(pamh, LOG_ERR,
208                            "key-manager plugin called with no valid \"change_step\" option setting\n");
209         return PAM_SERVICE_ERR;
210 }