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/sha512crypt.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(1, (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(1, (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 sysdb_attrs *mod_attrs;
53 struct sysdb_handle *handle;
54
55 struct ldb_result *res;
56 int error;
57
58 struct pam_auth_req *preq;
59 };
60
61 static void prepare_reply(struct LOCAL_request *lreq)
62 {
63 struct pam_data *pd;
64
65 pd = lreq->preq->pd;
66
67 if (lreq->error != EOK && pd->pam_status == PAM_SUCCESS)
68 pd->pam_status = PAM_SYSTEM_ERR;
69
70 lreq->preq->callback(lreq->preq);
71 }
72
73 static void set_user_attr_done(struct tevent_req *req)
74 {
75 struct LOCAL_request *lreq;
76 int ret;
77
78 lreq = tevent_req_callback_data(req, struct LOCAL_request);
79
80 ret = sysdb_transaction_commit_recv(req);
81 if (ret) {
82 DEBUG(2, ("set_user_attr failed.\n"));
83 lreq->error =ret;
84 }
85
86 prepare_reply(lreq);
87 }
88
89 static void set_user_attr_req_done(struct tevent_req *subreq);
90 static void set_user_attr_req(struct tevent_req *req)
91 {
92 struct LOCAL_request *lreq = tevent_req_callback_data(req,
93 struct LOCAL_request);
94 struct tevent_req *subreq;
95 int ret;
96
97 DEBUG(4, ("entering set_user_attr_req\n"));
98
99 ret = sysdb_transaction_recv(req, lreq, &lreq->handle);
100 if (ret) {
101 lreq->error = ret;
102 return prepare_reply(lreq);
103 }
104
105 subreq = sysdb_set_user_attr_send(lreq, lreq->ev, lreq->handle,
106 lreq->preq->domain,
107 lreq->preq->pd->user,
108 lreq->mod_attrs, SYSDB_MOD_REP);
109 if (!subreq) {
110 /* cancel transaction */
111 talloc_zfree(lreq->handle);
112 lreq->error = ret;
113 return prepare_reply(lreq);
114 }
115 tevent_req_set_callback(subreq, set_user_attr_req_done, lreq);
116 }
117
118 static void set_user_attr_req_done(struct tevent_req *subreq)
119 {
120 struct LOCAL_request *lreq = tevent_req_callback_data(subreq,
121 struct LOCAL_request);
122 struct tevent_req *req;
123 int ret;
124
125 ret = sysdb_set_user_attr_recv(subreq);
126 talloc_zfree(subreq);
127
128 DEBUG(4, ("set_user_attr_callback, status [%d][%s]\n", ret, strerror(ret)));
129
130 if (ret) {
131 lreq->error = ret;
132 goto fail;
133 }
134
135 req = sysdb_transaction_commit_send(lreq, lreq->ev, lreq->handle);
136 if (!req) {
137 lreq->error = ENOMEM;
138 goto fail;
139 }
140 tevent_req_set_callback(req, set_user_attr_done, lreq);
141
142 return;
143
144 fail:
145 DEBUG(2, ("set_user_attr failed.\n"));
146
147 /* cancel transaction */
148 talloc_zfree(lreq->handle);
149
150 prepare_reply(lreq);
151 }
152
153 static void do_successful_login(struct LOCAL_request *lreq)
154 {
155 struct tevent_req *req;
156 int ret;
157
158 lreq->mod_attrs = sysdb_new_attrs(lreq);
159 NULL_CHECK_OR_JUMP(lreq->mod_attrs, ("sysdb_new_attrs failed.\n"),
160 lreq->error, ENOMEM, done);
161
162 ret = sysdb_attrs_add_long(lreq->mod_attrs,
163 SYSDB_LAST_LOGIN, (long)time(NULL));
164 NEQ_CHECK_OR_JUMP(ret, EOK, ("sysdb_attrs_add_long failed.\n"),
165 lreq->error, ret, done);
166
167 ret = sysdb_attrs_add_long(lreq->mod_attrs, SYSDB_FAILED_LOGIN_ATTEMPTS, 0L);
168 NEQ_CHECK_OR_JUMP(ret, EOK, ("sysdb_attrs_add_long failed.\n"),
169 lreq->error, ret, done);
170
171 req = sysdb_transaction_send(lreq, lreq->ev, lreq->dbctx);
172 if (!req) {
173 lreq->error = ENOMEM;
174 goto done;
175 }
176 tevent_req_set_callback(req, set_user_attr_req, lreq);
177
178 return;
179
180 done:
181
182 prepare_reply(lreq);
183 }
184
185 static void do_failed_login(struct LOCAL_request *lreq)
186 {
187 struct tevent_req *req;
188 int ret;
189 int failedLoginAttempts;
190 struct pam_data *pd;
191
192 pd = lreq->preq->pd;
193 pd->pam_status = PAM_AUTH_ERR;
194 /* TODO: maybe add more inteligent delay calculation */
195 pd->response_delay = 3;
196
197 lreq->mod_attrs = sysdb_new_attrs(lreq);
198 NULL_CHECK_OR_JUMP(lreq->mod_attrs, ("sysdb_new_attrs failed.\n"),
199 lreq->error, ENOMEM, done);
200
201 ret = sysdb_attrs_add_long(lreq->mod_attrs,
202 SYSDB_LAST_FAILED_LOGIN, (long)time(NULL));
203 NEQ_CHECK_OR_JUMP(ret, EOK, ("sysdb_attrs_add_long failed.\n"),
204 lreq->error, ret, done);
205
206 failedLoginAttempts = ldb_msg_find_attr_as_int(lreq->res->msgs[0],
207 SYSDB_FAILED_LOGIN_ATTEMPTS,
208 0);
209 failedLoginAttempts++;
210
211 ret = sysdb_attrs_add_long(lreq->mod_attrs,
212 SYSDB_FAILED_LOGIN_ATTEMPTS,
213 (long)failedLoginAttempts);
214 NEQ_CHECK_OR_JUMP(ret, EOK, ("sysdb_attrs_add_long failed.\n"),
215 lreq->error, ret, done);
216
217 req = sysdb_transaction_send(lreq, lreq->ev, lreq->dbctx);
218 if (!req) {
219 lreq->error = ENOMEM;
220 goto done;
221 }
222 tevent_req_set_callback(req, set_user_attr_req, lreq);
223
224 return;
225
226 done:
227
228 prepare_reply(lreq);
229 }
230
231 static void do_pam_acct_mgmt(struct LOCAL_request *lreq)
232 {
233 const char *disabled;
234 struct pam_data *pd;
235
236 pd = lreq->preq->pd;
237
238 disabled = ldb_msg_find_attr_as_string(lreq->res->msgs[0],
239 SYSDB_DISABLED, NULL);
240 if ((disabled != NULL) &&
241 (strncasecmp(disabled, "false",5) != 0) &&
242 (strncasecmp(disabled, "no",2) != 0) ) {
243 pd->pam_status = PAM_PERM_DENIED;
244 }
245
246 prepare_reply(lreq);
247 }
248
249 static void do_pam_chauthtok(struct LOCAL_request *lreq)
250 {
251 struct tevent_req *req;
252 int ret;
253 char *newauthtok;
254 char *salt;
255 char *new_hash;
256 struct pam_data *pd;
257
258 pd = lreq->preq->pd;
259
260 newauthtok = talloc_strndup(lreq, (char *) pd->newauthtok,
261 pd->newauthtok_size);
262 NULL_CHECK_OR_JUMP(newauthtok, ("talloc_strndup failed.\n"), lreq->error,
263 ENOMEM, done);
264 memset(pd->newauthtok, 0, pd->newauthtok_size);
265
266 if (strlen(newauthtok) == 0) {
267 /* TODO: should we allow null passwords via a config option ? */
268 DEBUG(1, ("Empty passwords are not allowed!\n"));
269 ret = EINVAL;
270 goto done;
271 }
272
273 ret = s3crypt_gen_salt(lreq, &salt);
274 NEQ_CHECK_OR_JUMP(ret, EOK, ("Salt generation failed.\n"),
275 lreq->error, ret, done);
276 DEBUG(4, ("Using salt [%s]\n", salt));
277
278 ret = s3crypt_sha512(lreq, newauthtok, salt, &new_hash);
279 NEQ_CHECK_OR_JUMP(ret, EOK, ("Hash generation failed.\n"),
280 lreq->error, ret, done);
281 DEBUG(4, ("New hash [%s]\n", new_hash));
282 memset(newauthtok, 0, pd->newauthtok_size);
283
284 lreq->mod_attrs = sysdb_new_attrs(lreq);
285 NULL_CHECK_OR_JUMP(lreq->mod_attrs, ("sysdb_new_attrs failed.\n"),
286 lreq->error, ENOMEM, done);
287
288 ret = sysdb_attrs_add_string(lreq->mod_attrs, SYSDB_PWD, new_hash);
289 NEQ_CHECK_OR_JUMP(ret, EOK, ("sysdb_attrs_add_string failed.\n"),
290 lreq->error, ret, done);
291
292 ret = sysdb_attrs_add_long(lreq->mod_attrs,
293 "lastPasswordChange", (long)time(NULL));
294 NEQ_CHECK_OR_JUMP(ret, EOK, ("sysdb_attrs_add_long failed.\n"),
295 lreq->error, ret, done);
296
297 req = sysdb_transaction_send(lreq, lreq->ev, lreq->dbctx);
298 if (!req) {
299 lreq->error = ENOMEM;
300 goto done;
301 }
302 tevent_req_set_callback(req, set_user_attr_req, lreq);
303
304 return;
305 done:
306
307 prepare_reply(lreq);
308 }
309
310 static void local_handler_callback(void *pvt, int ldb_status,
311 struct ldb_result *res)
312 {
313 struct LOCAL_request *lreq;
314 const char *username = NULL;
315 const char *password = NULL;
Event const: After this line, the value of "newauthtok" is equal to 0. Event assignment: Assigning: "newauthtok" = "NULL".
Also see events: [dead_error_condition][dead_error_line] | |
316 char *newauthtok = NULL;
317 char *new_hash = NULL;
318 char *authtok = NULL;
319 struct pam_data *pd;
320 int ret;
321
322 lreq = talloc_get_type(pvt, struct LOCAL_request);
323 pd = lreq->preq->pd;
324
325 DEBUG(4, ("pam_handler_callback called with ldb_status [%d].\n",
326 ldb_status));
327
328 NEQ_CHECK_OR_JUMP(ldb_status, LDB_SUCCESS, ("ldb search failed.\n"),
329 lreq->error, sysdb_error_to_errno(ldb_status), done);
330
331
332 if (res->count < 1) {
333 DEBUG(4, ("No user found with filter ["SYSDB_PWNAM_FILTER"]\n",
334 pd->user));
335 pd->pam_status = PAM_USER_UNKNOWN;
336 goto done;
337 } else if (res->count > 1) {
338 DEBUG(4, ("More than one object found with filter ["SYSDB_PWNAM_FILTER"]\n"));
339 lreq->error = EFAULT;
340 goto done;
341 }
342
343 username = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL);
344 if (strcmp(username, pd->user) != 0) {
345 DEBUG(1, ("Expected username [%s] get [%s].\n", pd->user, username));
346 lreq->error = EINVAL;
347 goto done;
348 }
349
350 lreq->res = res;
351
352 switch (pd->cmd) {
353 case SSS_PAM_AUTHENTICATE:
354 case SSS_PAM_CHAUTHTOK:
355 case SSS_PAM_CHAUTHTOK_PRELIM:
356 if ((pd->cmd == SSS_PAM_CHAUTHTOK ||
357 pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) &&
358 lreq->preq->cctx->priv == 1) {
359 /* TODO: maybe this is a candiate for an explicit audit message. */
360 DEBUG(4, ("allowing root to reset a password.\n"));
361 break;
362 }
363 authtok = talloc_strndup(lreq, (char *) pd->authtok,
364 pd->authtok_size);
365 NULL_CHECK_OR_JUMP(authtok, ("talloc_strndup failed.\n"),
366 lreq->error, ENOMEM, done);
367 memset(pd->authtok, 0, pd->authtok_size);
368
369 password = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_PWD, NULL);
370 NULL_CHECK_OR_JUMP(password, ("No password stored.\n"),
371 lreq->error, LDB_ERR_NO_SUCH_ATTRIBUTE, done);
372 DEBUG(4, ("user: [%s], password hash: [%s]\n", username, password));
373
374 ret = s3crypt_sha512(lreq, authtok, password, &new_hash);
375 memset(authtok, 0, pd->authtok_size);
376 NEQ_CHECK_OR_JUMP(ret, EOK, ("nss_sha512_crypt failed.\n"),
377 lreq->error, ret, done);
378
379 DEBUG(4, ("user: [%s], new hash: [%s]\n", username, new_hash));
380
381 if (strcmp(new_hash, password) != 0) {
382 DEBUG(1, ("Passwords do not match.\n"));
383 do_failed_login(lreq);
384 return;
385 }
386
387 break;
388 }
389
390 switch (pd->cmd) {
391 case SSS_PAM_AUTHENTICATE:
392 do_successful_login(lreq);
393 return;
394 break;
395 case SSS_PAM_CHAUTHTOK:
396 do_pam_chauthtok(lreq);
397 return;
398 break;
399 case SSS_PAM_ACCT_MGMT:
400 do_pam_acct_mgmt(lreq);
401 return;
402 break;
403 case SSS_PAM_SETCRED:
404 break;
405 case SSS_PAM_OPEN_SESSION:
406 break;
407 case SSS_PAM_CLOSE_SESSION:
408 break;
409 case SSS_PAM_CHAUTHTOK_PRELIM:
410 break;
411 default:
412 lreq->error = EINVAL;
413 DEBUG(1, ("Unknown PAM task [%d].\n"));
414 }
415
416 done:
417 if (pd->authtok != NULL)
418 memset(pd->authtok, 0, pd->authtok_size);
419 if (authtok != NULL)
420 memset(authtok, 0, pd->authtok_size);
421 if (pd->newauthtok != NULL)
422 memset(pd->newauthtok, 0, pd->newauthtok_size);
423 if (newauthtok != NULL)
424 memset(newauthtok, 0, pd->newauthtok_size);
425
426 prepare_reply(lreq);
427 }
428
429 int LOCAL_pam_handler(struct pam_auth_req *preq)
430 {
431 int ret;
432 struct LOCAL_request *lreq;
433
434 static const char *attrs[] = {SYSDB_NAME,
435 SYSDB_PWD,
436 SYSDB_DISABLED,
437 SYSDB_LAST_LOGIN,
438 "lastPasswordChange",
439 "accountExpires",
440 SYSDB_FAILED_LOGIN_ATTEMPTS,
441 "passwordHint",
442 "passwordHistory",
443 SYSDB_LAST_FAILED_LOGIN,
444 NULL};
445
446 DEBUG(4, ("LOCAL pam handler.\n"));
447
448 lreq = talloc_zero(preq, struct LOCAL_request);
449 if (!lreq) {
450 return ENOMEM;
451 }
452
453 ret = sysdb_get_ctx_from_list(preq->cctx->rctx->db_list,
454 preq->domain, &lreq->dbctx);
455 if (ret != EOK) {
456 DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
457 talloc_free(lreq);
458 return ret;
459 }
460 lreq->ev = preq->cctx->ev;
461 lreq->preq = preq;
462
463 preq->pd->pam_status = PAM_SUCCESS;
464
465 ret = sysdb_get_user_attr(lreq, lreq->dbctx,
466 preq->domain, preq->pd->user, attrs,
467 local_handler_callback, lreq);
468
469 if (ret != EOK) {
470 DEBUG(1, ("sysdb_get_user_attr failed.\n"));
471 talloc_free(lreq);
472 return ret;
473 }
474
475 return EOK;
476 }