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);
Event dead_error_condition: On this path, the condition "newauthtok != NULL" cannot be true.
Also see events: [const][assignment][dead_error_line]
423  	    if (newauthtok != NULL)
Event dead_error_line: Execution cannot reach this statement "memset(newauthtok, 0, pd->n...".
Also see events: [dead_error_condition][const][assignment]
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  	}