Line data Source code
1 : /*
2 : SSSD
3 :
4 : PAM e credentials
5 :
6 : Copyright (C) Sumit Bose <sbose@redhat.com> 2009
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include <time.h>
23 : #include <security/pam_modules.h>
24 :
25 : #include "util/util.h"
26 : #include "db/sysdb.h"
27 : #include "util/crypto/sss_crypto.h"
28 : #include "providers/data_provider.h"
29 : #include "responder/pam/pamsrv.h"
30 :
31 :
32 : #define NULL_CHECK_OR_JUMP(var, msg, ret, err, label) do { \
33 : if (var == NULL) { \
34 : DEBUG(SSSDBG_CRIT_FAILURE, msg); \
35 : ret = (err); \
36 : goto label; \
37 : } \
38 : } while(0)
39 :
40 : #define NEQ_CHECK_OR_JUMP(var, val, msg, ret, err, label) do { \
41 : if (var != (val)) { \
42 : DEBUG(SSSDBG_CRIT_FAILURE, msg); \
43 : ret = (err); \
44 : goto label; \
45 : } \
46 : } while(0)
47 :
48 :
49 : struct LOCAL_request {
50 : struct tevent_context *ev;
51 : struct sysdb_ctx *dbctx;
52 : struct sss_domain_info *domain;
53 : struct sysdb_attrs *mod_attrs;
54 :
55 : struct ldb_result *res;
56 : int error;
57 :
58 : struct pam_auth_req *preq;
59 : };
60 :
61 0 : static void prepare_reply(struct LOCAL_request *lreq)
62 : {
63 : struct pam_data *pd;
64 :
65 0 : pd = lreq->preq->pd;
66 :
67 0 : if (lreq->error != EOK && pd->pam_status == PAM_SUCCESS)
68 0 : pd->pam_status = PAM_SYSTEM_ERR;
69 :
70 0 : lreq->preq->callback(lreq->preq);
71 0 : }
72 :
73 0 : static void do_successful_login(struct LOCAL_request *lreq)
74 : {
75 : int ret;
76 :
77 0 : lreq->mod_attrs = sysdb_new_attrs(lreq);
78 0 : NULL_CHECK_OR_JUMP(lreq->mod_attrs, "sysdb_new_attrs failed.\n",
79 : lreq->error, ENOMEM, done);
80 :
81 0 : ret = sysdb_attrs_add_long(lreq->mod_attrs,
82 : SYSDB_LAST_LOGIN, (long)time(NULL));
83 0 : NEQ_CHECK_OR_JUMP(ret, EOK, "sysdb_attrs_add_long failed.\n",
84 : lreq->error, ret, done);
85 :
86 0 : ret = sysdb_attrs_add_long(lreq->mod_attrs, SYSDB_FAILED_LOGIN_ATTEMPTS, 0L);
87 0 : NEQ_CHECK_OR_JUMP(ret, EOK, "sysdb_attrs_add_long failed.\n",
88 : lreq->error, ret, done);
89 :
90 0 : ret = sysdb_set_user_attr(lreq->domain,
91 0 : lreq->preq->pd->user,
92 : lreq->mod_attrs, SYSDB_MOD_REP);
93 0 : NEQ_CHECK_OR_JUMP(ret, EOK, "sysdb_set_user_attr failed.\n",
94 : lreq->error, ret, done);
95 :
96 : done:
97 0 : return;
98 : }
99 :
100 0 : static void do_failed_login(struct LOCAL_request *lreq)
101 : {
102 : int ret;
103 : int failedLoginAttempts;
104 : struct pam_data *pd;
105 :
106 0 : pd = lreq->preq->pd;
107 0 : pd->pam_status = PAM_AUTH_ERR;
108 : /* TODO: maybe add more inteligent delay calculation */
109 0 : pd->response_delay = 3;
110 :
111 0 : lreq->mod_attrs = sysdb_new_attrs(lreq);
112 0 : NULL_CHECK_OR_JUMP(lreq->mod_attrs, "sysdb_new_attrs failed.\n",
113 : lreq->error, ENOMEM, done);
114 :
115 0 : ret = sysdb_attrs_add_long(lreq->mod_attrs,
116 : SYSDB_LAST_FAILED_LOGIN, (long)time(NULL));
117 0 : NEQ_CHECK_OR_JUMP(ret, EOK, "sysdb_attrs_add_long failed.\n",
118 : lreq->error, ret, done);
119 :
120 0 : failedLoginAttempts = ldb_msg_find_attr_as_int(lreq->res->msgs[0],
121 : SYSDB_FAILED_LOGIN_ATTEMPTS,
122 : 0);
123 0 : failedLoginAttempts++;
124 :
125 0 : ret = sysdb_attrs_add_long(lreq->mod_attrs,
126 : SYSDB_FAILED_LOGIN_ATTEMPTS,
127 : (long)failedLoginAttempts);
128 0 : NEQ_CHECK_OR_JUMP(ret, EOK, "sysdb_attrs_add_long failed.\n",
129 : lreq->error, ret, done);
130 :
131 0 : ret = sysdb_set_user_attr(lreq->domain,
132 0 : lreq->preq->pd->user,
133 : lreq->mod_attrs, SYSDB_MOD_REP);
134 0 : NEQ_CHECK_OR_JUMP(ret, EOK, "sysdb_set_user_attr failed.\n",
135 : lreq->error, ret, done);
136 :
137 : done:
138 0 : return;
139 : }
140 :
141 0 : static void do_pam_acct_mgmt(struct LOCAL_request *lreq)
142 : {
143 : const char *disabled;
144 : struct pam_data *pd;
145 :
146 0 : pd = lreq->preq->pd;
147 :
148 0 : disabled = ldb_msg_find_attr_as_string(lreq->res->msgs[0],
149 : SYSDB_DISABLED, NULL);
150 0 : if ((disabled != NULL) &&
151 0 : (strncasecmp(disabled, "false",5) != 0) &&
152 0 : (strncasecmp(disabled, "no",2) != 0) ) {
153 0 : pd->pam_status = PAM_PERM_DENIED;
154 : }
155 0 : }
156 :
157 0 : static void do_pam_chauthtok(struct LOCAL_request *lreq)
158 : {
159 : int ret;
160 : const char *password;
161 : char *salt;
162 : char *new_hash;
163 : struct pam_data *pd;
164 :
165 0 : pd = lreq->preq->pd;
166 :
167 0 : ret = sss_authtok_get_password(pd->newauthtok, &password, NULL);
168 0 : if (ret) {
169 : /* TODO: should we allow null passwords via a config option ? */
170 0 : if (ret == ENOENT) {
171 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Empty passwords are not allowed!\n");
172 : }
173 0 : lreq->error = EINVAL;
174 0 : goto done;
175 : }
176 :
177 0 : ret = s3crypt_gen_salt(lreq, &salt);
178 0 : NEQ_CHECK_OR_JUMP(ret, EOK, "Salt generation failed.\n",
179 : lreq->error, ret, done);
180 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Using salt [%s]\n", salt);
181 :
182 0 : ret = s3crypt_sha512(lreq, password, salt, &new_hash);
183 0 : NEQ_CHECK_OR_JUMP(ret, EOK, "Hash generation failed.\n",
184 : lreq->error, ret, done);
185 0 : DEBUG(SSSDBG_CONF_SETTINGS, "New hash [%s]\n", new_hash);
186 :
187 0 : lreq->mod_attrs = sysdb_new_attrs(lreq);
188 0 : NULL_CHECK_OR_JUMP(lreq->mod_attrs, "sysdb_new_attrs failed.\n",
189 : lreq->error, ENOMEM, done);
190 :
191 0 : ret = sysdb_attrs_add_string(lreq->mod_attrs, SYSDB_PWD, new_hash);
192 0 : NEQ_CHECK_OR_JUMP(ret, EOK, "sysdb_attrs_add_string failed.\n",
193 : lreq->error, ret, done);
194 :
195 0 : ret = sysdb_attrs_add_long(lreq->mod_attrs,
196 : "lastPasswordChange", (long)time(NULL));
197 0 : NEQ_CHECK_OR_JUMP(ret, EOK, "sysdb_attrs_add_long failed.\n",
198 : lreq->error, ret, done);
199 :
200 0 : ret = sysdb_set_user_attr(lreq->domain,
201 0 : lreq->preq->pd->user,
202 : lreq->mod_attrs, SYSDB_MOD_REP);
203 0 : NEQ_CHECK_OR_JUMP(ret, EOK, "sysdb_set_user_attr failed.\n",
204 : lreq->error, ret, done);
205 :
206 : done:
207 0 : sss_authtok_set_empty(pd->newauthtok);
208 0 : }
209 :
210 0 : int LOCAL_pam_handler(struct pam_auth_req *preq)
211 : {
212 : struct LOCAL_request *lreq;
213 : static const char *attrs[] = {SYSDB_NAME,
214 : SYSDB_PWD,
215 : SYSDB_DISABLED,
216 : SYSDB_LAST_LOGIN,
217 : "lastPasswordChange",
218 : "accountExpires",
219 : SYSDB_FAILED_LOGIN_ATTEMPTS,
220 : "passwordHint",
221 : "passwordHistory",
222 : SYSDB_LAST_FAILED_LOGIN,
223 : NULL};
224 : struct ldb_result *res;
225 0 : const char *username = NULL;
226 0 : const char *pwdhash = NULL;
227 0 : char *new_hash = NULL;
228 : const char *password;
229 0 : struct pam_data *pd = preq->pd;
230 : int ret;
231 :
232 0 : DEBUG(SSSDBG_CONF_SETTINGS, "LOCAL pam handler.\n");
233 :
234 0 : lreq = talloc_zero(preq, struct LOCAL_request);
235 0 : if (!lreq) {
236 0 : return ENOMEM;
237 : }
238 :
239 0 : lreq->dbctx = preq->domain->sysdb;
240 0 : if (lreq->dbctx == NULL) {
241 0 : DEBUG(SSSDBG_FATAL_FAILURE,
242 : "Fatal: Sysdb CTX not found for this domain!\n");
243 0 : talloc_free(lreq);
244 0 : return ENOENT;
245 : }
246 0 : lreq->domain = preq->domain;
247 0 : lreq->ev = preq->cctx->ev;
248 0 : lreq->preq = preq;
249 :
250 0 : pd->pam_status = PAM_SUCCESS;
251 :
252 0 : ret = sysdb_get_user_attr(lreq, preq->domain, preq->pd->user, attrs,
253 : &res);
254 0 : if (ret != EOK) {
255 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_get_user_attr failed.\n");
256 0 : talloc_free(lreq);
257 0 : return ret;
258 : }
259 :
260 0 : if (res->count < 1) {
261 0 : DEBUG(SSSDBG_CONF_SETTINGS,
262 : "No user found with filter ["SYSDB_PWNAM_FILTER"]\n",
263 : pd->user, pd->user, pd->user);
264 0 : pd->pam_status = PAM_USER_UNKNOWN;
265 0 : goto done;
266 0 : } else if (res->count > 1) {
267 0 : DEBUG(SSSDBG_CONF_SETTINGS,
268 : "More than one object found with filter ["SYSDB_PWNAM_FILTER"]\n",
269 : pd->user, pd->user, pd->user);
270 0 : lreq->error = EFAULT;
271 0 : goto done;
272 : }
273 :
274 0 : username = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL);
275 0 : if (strcmp(username, pd->user) != 0) {
276 0 : DEBUG(SSSDBG_CRIT_FAILURE,
277 : "Expected username [%s] get [%s].\n", pd->user, username);
278 0 : lreq->error = EINVAL;
279 0 : goto done;
280 : }
281 :
282 0 : lreq->res = res;
283 :
284 0 : switch (pd->cmd) {
285 : case SSS_PAM_AUTHENTICATE:
286 : case SSS_PAM_CHAUTHTOK:
287 : case SSS_PAM_CHAUTHTOK_PRELIM:
288 0 : if ((pd->cmd == SSS_PAM_CHAUTHTOK ||
289 0 : pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) &&
290 0 : lreq->preq->cctx->priv == 1) {
291 : /* TODO: maybe this is a candiate for an explicit audit message. */
292 0 : DEBUG(SSSDBG_CONF_SETTINGS,
293 : "allowing root to reset a password.\n");
294 0 : break;
295 : }
296 0 : ret = sss_authtok_get_password(pd->authtok, &password, NULL);
297 0 : NEQ_CHECK_OR_JUMP(ret, EOK, "Failed to get password.\n",
298 : lreq->error, ret, done);
299 :
300 0 : pwdhash = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_PWD, NULL);
301 0 : NULL_CHECK_OR_JUMP(pwdhash, "No password stored.\n",
302 : lreq->error, LDB_ERR_NO_SUCH_ATTRIBUTE, done);
303 0 : DEBUG(SSSDBG_CONF_SETTINGS,
304 : "user: [%s], password hash: [%s]\n", username, pwdhash);
305 :
306 0 : ret = s3crypt_sha512(lreq, password, pwdhash, &new_hash);
307 0 : NEQ_CHECK_OR_JUMP(ret, EOK, "nss_sha512_crypt failed.\n",
308 : lreq->error, ret, done);
309 :
310 0 : DEBUG(SSSDBG_CONF_SETTINGS,
311 : "user: [%s], new hash: [%s]\n", username, new_hash);
312 :
313 0 : if (strcmp(new_hash, pwdhash) != 0) {
314 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Passwords do not match.\n");
315 0 : do_failed_login(lreq);
316 0 : goto done;
317 : }
318 :
319 0 : break;
320 : }
321 :
322 0 : switch (pd->cmd) {
323 : case SSS_PAM_AUTHENTICATE:
324 0 : do_successful_login(lreq);
325 0 : break;
326 : case SSS_PAM_CHAUTHTOK:
327 0 : do_pam_chauthtok(lreq);
328 0 : break;
329 : case SSS_PAM_ACCT_MGMT:
330 0 : do_pam_acct_mgmt(lreq);
331 0 : break;
332 : case SSS_PAM_SETCRED:
333 0 : break;
334 : case SSS_PAM_OPEN_SESSION:
335 0 : break;
336 : case SSS_PAM_CLOSE_SESSION:
337 0 : break;
338 : case SSS_PAM_CHAUTHTOK_PRELIM:
339 0 : break;
340 : default:
341 0 : lreq->error = EINVAL;
342 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unknown PAM task [%d].\n", pd->cmd);
343 : }
344 :
345 : done:
346 0 : sss_authtok_set_empty(pd->newauthtok);
347 0 : sss_authtok_set_empty(pd->authtok);
348 0 : prepare_reply(lreq);
349 0 : return EOK;
350 : }
351 :
|