1    	/*
2    	   SSSD
3    	
4    	   PAM Responder
5    	
6    	   Copyright (C) Simo Sorce <ssorce@redhat.com>	2009
7    	   Copyright (C) Sumit Bose <sbose@redhat.com>	2009
8    	
9    	   This program is free software; you can redistribute it and/or modify
10   	   it under the terms of the GNU General Public License as published by
11   	   the Free Software Foundation; either version 3 of the License, or
12   	   (at your option) any later version.
13   	
14   	   This program is distributed in the hope that it will be useful,
15   	   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   	   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   	   GNU General Public License for more details.
18   	
19   	   You should have received a copy of the GNU General Public License
20   	   along with this program.  If not, see <http://www.gnu.org/licenses/>.
21   	*/
22   	
23   	#include <time.h>
24   	#include "util/util.h"
25   	#include "db/sysdb.h"
26   	#include "confdb/confdb.h"
27   	#include "responder/common/responder_packet.h"
28   	#include "responder/common/responder.h"
29   	#include "providers/data_provider.h"
30   	#include "responder/pam/pamsrv.h"
31   	#include "db/sysdb.h"
32   	
33   	static void pam_reply(struct pam_auth_req *preq);
34   	
35   	static int extract_authtok(uint32_t *type, uint32_t *size, uint8_t **tok, uint8_t *body, size_t blen, size_t *c) {
36   	    uint32_t data_size;
37   	
38   	    if (blen-(*c) < 2*sizeof(uint32_t)) return EINVAL;
39   	
40   	    memcpy(&data_size, &body[*c], sizeof(uint32_t));
41   	    *c += sizeof(uint32_t);
42   	    if (data_size < sizeof(uint32_t) || (*c)+(data_size) > blen) return EINVAL;
43   	    *size = data_size - sizeof(uint32_t);
44   	
45   	    memcpy(type, &body[*c], sizeof(uint32_t));
46   	    *c += sizeof(uint32_t);
47   	
48   	    *tok = body+(*c);
49   	
50   	    *c += (*size);
51   	
52   	    return EOK;
53   	}
54   	
55   	static int extract_string(char **var, uint8_t *body, size_t blen, size_t *c) {
56   	    uint32_t size;
57   	    uint8_t *str;
58   	
59   	    if (blen-(*c) < sizeof(uint32_t)+1) return EINVAL;
60   	
61   	    memcpy(&size, &body[*c], sizeof(uint32_t));
62   	    *c += sizeof(uint32_t);
63   	    if (*c+size > blen) return EINVAL;
64   	
65   	    str = body+(*c);
66   	
67   	    if (str[size-1]!='\0') return EINVAL;
68   	
69   	    *c += size;
70   	
71   	    *var = (char *) str;
72   	
73   	    return EOK;
74   	}
75   	
76   	static int extract_uint32_t(uint32_t *var, uint8_t *body, size_t blen, size_t *c) {
77   	    uint32_t size;
78   	
79   	    if (blen-(*c) < 2*sizeof(uint32_t)) return EINVAL;
80   	
81   	    memcpy(&size, &body[*c], sizeof(uint32_t));
82   	    *c += sizeof(uint32_t);
83   	
84   	    memcpy(var, &body[*c], sizeof(uint32_t));
85   	    *c += sizeof(uint32_t);
86   	
87   	    return EOK;
88   	}
89   	
90   	static int pam_parse_in_data_v2(struct sss_names_ctx *snctx,
91   	                             struct pam_data *pd,
92   	                             uint8_t *body, size_t blen)
93   	{
94   	    size_t c;
95   	    uint32_t type;
96   	    uint32_t size;
97   	    char *pam_user;
98   	    int ret;
99   	    uint32_t terminator = SSS_END_OF_PAM_REQUEST;
100  	
101  	    if (blen < 4*sizeof(uint32_t)+2 ||
102  	        ((uint32_t *)body)[0] != SSS_START_OF_PAM_REQUEST ||
103  	        memcmp(&body[blen - sizeof(uint32_t)], &terminator, sizeof(uint32_t)) != 0) {
104  	        DEBUG(1, ("Received data is invalid.\n"));
105  	        return EINVAL;
106  	    }
107  	
108  	    c = sizeof(uint32_t);
109  	    do {
110  	        memcpy(&type, &body[c], sizeof(uint32_t));
111  	        c += sizeof(uint32_t);
112  	        if (c > blen) return EINVAL;
113  	
114  	        switch(type) {
115  	            case SSS_PAM_ITEM_USER:
116  	                ret = extract_string(&pam_user, body, blen, &c);
117  	                if (ret != EOK) return ret;
118  	
119  	                ret = sss_parse_name(pd, snctx, pam_user,
120  	                                     &pd->domain, &pd->user);
121  	                if (ret != EOK) return ret;
122  	                break;
123  	            case SSS_PAM_ITEM_SERVICE:
124  	                ret = extract_string(&pd->service, body, blen, &c);
125  	                if (ret != EOK) return ret;
126  	                break;
127  	            case SSS_PAM_ITEM_TTY:
128  	                ret = extract_string(&pd->tty, body, blen, &c);
129  	                if (ret != EOK) return ret;
130  	                break;
131  	            case SSS_PAM_ITEM_RUSER:
132  	                ret = extract_string(&pd->ruser, body, blen, &c);
133  	                if (ret != EOK) return ret;
134  	                break;
135  	            case SSS_PAM_ITEM_RHOST:
136  	                ret = extract_string(&pd->rhost, body, blen, &c);
137  	                if (ret != EOK) return ret;
138  	                break;
139  	            case SSS_PAM_ITEM_CLI_PID:
140  	                ret = extract_uint32_t(&pd->cli_pid,
141  	                                       body, blen, &c);
142  	                if (ret != EOK) return ret;
143  	                break;
144  	            case SSS_PAM_ITEM_AUTHTOK:
145  	                ret = extract_authtok(&pd->authtok_type, &pd->authtok_size,
146  	                                      &pd->authtok, body, blen, &c);
147  	                if (ret != EOK) return ret;
148  	                break;
149  	            case SSS_PAM_ITEM_NEWAUTHTOK:
150  	                ret = extract_authtok(&pd->newauthtok_type,
151  	                                      &pd->newauthtok_size,
152  	                                      &pd->newauthtok, body, blen, &c);
153  	                if (ret != EOK) return ret;
154  	                break;
155  	            case SSS_END_OF_PAM_REQUEST:
156  	                if (c != blen) return EINVAL;
157  	                break;
158  	            default:
159  	                DEBUG(1,("Ignoring unknown data type [%d].\n", type));
160  	                size = ((uint32_t *)&body[c])[0];
161  	                c += size+sizeof(uint32_t);
162  	        }
163  	    } while(c < blen);
164  	
165  	    if (pd->user == NULL || *pd->user == '\0') return EINVAL;
166  	
167  	    DEBUG_PAM_DATA(4, pd);
168  	
169  	    return EOK;
170  	
171  	}
172  	
173  	static int pam_parse_in_data_v3(struct sss_names_ctx *snctx,
174  	                             struct pam_data *pd,
175  	                             uint8_t *body, size_t blen)
176  	{
177  	    int ret;
178  	
179  	    ret = pam_parse_in_data_v2(snctx, pd, body, blen);
180  	    if (ret != EOK) {
181  	        DEBUG(1, ("pam_parse_in_data_v2 failed.\n"));
182  	        return ret;
183  	    }
184  	
185  	    if (pd->cli_pid == 0) {
186  	        DEBUG(1, ("Missing client PID.\n"));
187  	        return EINVAL;
188  	    }
189  	
190  	    return EOK;
191  	}
192  	
193  	static int pam_parse_in_data(struct sss_names_ctx *snctx,
194  	                             struct pam_data *pd,
195  	                             uint8_t *body, size_t blen)
196  	{
197  	    int start;
198  	    int end;
199  	    int last;
200  	    int ret;
201  	
202  	    last = blen - 1;
203  	    end = 0;
204  	
205  	    /* user name */
206  	    for (start = end; end < last; end++) if (body[end] == '\0') break;
207  	    if (body[end++] != '\0') return EINVAL;
208  	
209  	    ret = sss_parse_name(pd, snctx, (char *)&body[start], &pd->domain, &pd->user);
210  	    if (ret != EOK) return ret;
211  	
212  	    for (start = end; end < last; end++) if (body[end] == '\0') break;
213  	    if (body[end++] != '\0') return EINVAL;
214  	    pd->service = (char *) &body[start];
215  	
216  	    for (start = end; end < last; end++) if (body[end] == '\0') break;
217  	    if (body[end++] != '\0') return EINVAL;
218  	    pd->tty = (char *) &body[start];
219  	
220  	    for (start = end; end < last; end++) if (body[end] == '\0') break;
221  	    if (body[end++] != '\0') return EINVAL;
222  	    pd->ruser = (char *) &body[start];
223  	
224  	    for (start = end; end < last; end++) if (body[end] == '\0') break;
225  	    if (body[end++] != '\0') return EINVAL;
226  	    pd->rhost = (char *) &body[start];
227  	
228  	    start = end;
229  	    pd->authtok_type = (int) body[start];
230  	
231  	    start += sizeof(uint32_t);
232  	    pd->authtok_size = (int) body[start];
233  	
234  	    start += sizeof(uint32_t);
235  	    end = start + pd->authtok_size;
236  	    if (pd->authtok_size == 0) {
237  	        pd->authtok = NULL;
238  	    } else {
239  	        if (end <= blen) {
240  	            pd->authtok = (uint8_t *) &body[start];
241  	        } else {
242  	            DEBUG(1, ("Invalid authtok size: %d\n", pd->authtok_size));
243  	            return EINVAL;
244  	        }
245  	    }
246  	
247  	    start = end;
248  	    pd->newauthtok_type = (int) body[start];
249  	
250  	    start += sizeof(uint32_t);
251  	    pd->newauthtok_size = (int) body[start];
252  	
253  	    start += sizeof(uint32_t);
254  	    end = start + pd->newauthtok_size;
255  	
256  	    if (pd->newauthtok_size == 0) {
257  	        pd->newauthtok = NULL;
258  	    } else {
259  	        if (end <= blen) {
260  	            pd->newauthtok = (uint8_t *) &body[start];
261  	        } else {
262  	            DEBUG(1, ("Invalid newauthtok size: %d\n", pd->newauthtok_size));
263  	            return EINVAL;
264  	        }
265  	    }
266  	
267  	    DEBUG_PAM_DATA(4, pd);
268  	
269  	    return EOK;
270  	}
271  	
272  	/*=Save-Last-Login-State===================================================*/
273  	
274  	struct set_last_login_state {
275  	    struct tevent_context *ev;
276  	    struct sysdb_ctx *dbctx;
277  	
278  	    struct sss_domain_info *dom;
279  	    const char *username;
280  	    struct sysdb_attrs *attrs;
281  	
282  	    struct sysdb_handle *handle;
283  	
284  	    struct ldb_result *res;
285  	};
286  	
287  	static void set_last_login_trans_done(struct tevent_req *subreq);
288  	static void set_last_login_attrs_done(struct tevent_req *subreq);
289  	static void set_last_login_done(struct tevent_req *subreq);
290  	
291  	static struct tevent_req *set_last_login_send(TALLOC_CTX *memctx,
292  	                                              struct tevent_context *ev,
293  	                                              struct sysdb_ctx *dbctx,
294  	                                              struct sss_domain_info *dom,
295  	                                              const char *username,
296  	                                              struct sysdb_attrs *attrs)
297  	{
298  	    struct tevent_req *req, *subreq;
299  	    struct set_last_login_state *state;
300  	
301  	    req = tevent_req_create(memctx, &state, struct set_last_login_state);
302  	    if (!req) {
303  	        return NULL;
304  	    }
305  	
306  	    state->ev = ev;
307  	    state->dbctx = dbctx;
308  	    state->dom = dom;
309  	    state->username = username;
310  	    state->attrs = attrs;
311  	    state->handle = NULL;
312  	
313  	    subreq = sysdb_transaction_send(state, state->ev, state->dbctx);
314  	    if (!subreq) {
315  	        talloc_free(req);
316  	        return NULL;
317  	    }
318  	    tevent_req_set_callback(subreq, set_last_login_trans_done, req);
319  	
320  	    return req;
321  	}
322  	
323  	static void set_last_login_trans_done(struct tevent_req *subreq)
324  	{
325  	    struct tevent_req *req = tevent_req_callback_data(subreq,
326  	                                                      struct tevent_req);
327  	    struct set_last_login_state *state = tevent_req_data(req,
328  	                                                struct set_last_login_state);
329  	    int ret;
330  	
331  	    ret = sysdb_transaction_recv(subreq, state, &state->handle);
332  	    talloc_zfree(subreq);
333  	    if (ret != EOK) {
334  	        DEBUG(1, ("Unable to acquire sysdb transaction lock\n"));
335  	        tevent_req_error(req, ret);
336  	        return;
337  	    }
338  	
339  	    subreq = sysdb_set_user_attr_send(state, state->ev, state->handle,
340  	                                      state->dom, state->username,
341  	                                      state->attrs, SYSDB_MOD_REP);
342  	    if (!subreq) {
343  	        tevent_req_error(req, ENOMEM);
344  	        return;
345  	    }
346  	    tevent_req_set_callback(subreq, set_last_login_attrs_done, req);
347  	}
348  	
349  	static void set_last_login_attrs_done(struct tevent_req *subreq)
350  	{
351  	    struct tevent_req *req = tevent_req_callback_data(subreq,
352  	                                                      struct tevent_req);
353  	    struct set_last_login_state *state = tevent_req_data(req,
354  	                                                struct set_last_login_state);
355  	    int ret;
356  	
357  	    ret = sysdb_set_user_attr_recv(subreq);
358  	    talloc_zfree(subreq);
359  	    if (ret != EOK) {
360  	        DEBUG(4, ("set_user_attr_callback, status [%d][%s]\n",
361  	                  ret, strerror(ret)));
362  	        tevent_req_error(req, ret);
363  	        return;
364  	    }
365  	
366  	    subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
367  	    if (!subreq) {
368  	        tevent_req_error(req, ENOMEM);
369  	        return;
370  	    }
371  	    tevent_req_set_callback(subreq, set_last_login_done, req);
372  	}
373  	
374  	static void set_last_login_done(struct tevent_req *subreq)
375  	{
376  	    struct tevent_req *req = tevent_req_callback_data(subreq,
377  	                                                      struct tevent_req);
378  	    int ret;
379  	
380  	    ret = sysdb_transaction_commit_recv(subreq);
381  	    if (ret != EOK) {
382  	        DEBUG(2, ("set_last_login failed.\n"));
383  	        tevent_req_error(req, ret);
384  	        return;
385  	    }
386  	
387  	    tevent_req_done(req);
388  	}
389  	
390  	static int set_last_login_recv(struct tevent_req *req)
391  	{
392  	    TEVENT_REQ_RETURN_ON_ERROR(req);
393  	
394  	    return EOK;
395  	}
396  	
397  	/*=========================================================================*/
398  	
399  	
400  	static void set_last_login_reply(struct tevent_req *req);
401  	
402  	static errno_t set_last_login(struct pam_auth_req *preq)
403  	{
404  	    struct tevent_req *req;
405  	    struct sysdb_ctx *dbctx;
406  	    struct sysdb_attrs *attrs;
407  	    errno_t ret;
408  	
409  	    attrs = sysdb_new_attrs(preq);
410  	    if (!attrs) {
411  	        ret = ENOMEM;
412  	        goto fail;
413  	    }
414  	
415  	    ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_ONLINE_AUTH, time(NULL));
416  	    if (ret != EOK) {
417  	        goto fail;
418  	    }
419  	
420  	    ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_LOGIN, time(NULL));
421  	    if (ret != EOK) {
422  	        goto fail;
423  	    }
424  	
425  	    ret = sysdb_get_ctx_from_list(preq->cctx->rctx->db_list, preq->domain,
426  	                                  &dbctx);
427  	    if (ret != EOK) {
428  	        DEBUG(0, ("Fatal: Sysdb context not found for this domain!\n"));
429  	        goto fail;
430  	    }
431  	
432  	    req = set_last_login_send(preq, preq->cctx->ev, dbctx,
433  	                              preq->domain, preq->pd->user, attrs);
434  	    if (!req) {
435  	        ret = ENOMEM;
436  	        goto fail;
437  	    }
438  	    tevent_req_set_callback(req, set_last_login_reply, preq);
439  	
440  	    return EOK;
441  	
442  	fail:
443  	    return ret;
444  	}
445  	
446  	static void set_last_login_reply(struct tevent_req *req)
447  	{
448  	    struct pam_auth_req *preq = tevent_req_callback_data(req,
449  	                                                         struct pam_auth_req);
450  	    int ret;
451  	
452  	    ret = set_last_login_recv(req);
453  	    if (ret != EOK) {
454  	        preq->pd->pam_status = PAM_SYSTEM_ERR;
455  	    } else {
456  	        preq->pd->last_auth_saved = true;
457  	    }
458  	
459  	    preq->callback(preq);
460  	}
461  	
462  	static void pam_reply_delay(struct tevent_context *ev, struct tevent_timer *te,
463  	                            struct timeval tv, void *pvt)
464  	{
465  	    struct pam_auth_req *preq;
466  	
467  	    DEBUG(4, ("pam_reply_delay get called.\n"));
468  	
469  	    preq = talloc_get_type(pvt, struct pam_auth_req);
470  	
471  	    pam_reply(preq);
472  	}
473  	
474  	static void pam_cache_auth_done(struct tevent_req *req);
475  	
476  	static void pam_reply(struct pam_auth_req *preq)
477  	{
478  	    struct cli_ctx *cctx;
479  	    uint8_t *body;
480  	    size_t blen;
481  	    int ret;
482  	    int32_t resp_c;
483  	    int32_t resp_size;
484  	    struct response_data *resp;
485  	    int p;
486  	    struct timeval tv;
487  	    struct tevent_timer *te;
488  	    struct pam_data *pd;
489  	    struct tevent_req *req;
490  	    struct sysdb_ctx *sysdb;
491  	    struct pam_ctx *pctx;
492  	    uint32_t user_info_type;
493  	
494  	    pd = preq->pd;
495  	    cctx = preq->cctx;
496  	
497  	    DEBUG(4, ("pam_reply get called.\n"));
498  	
499  	    if (pd->pam_status == PAM_AUTHINFO_UNAVAIL) {
500  	        switch(pd->cmd) {
501  	            case SSS_PAM_AUTHENTICATE:
502  	                if ((preq->domain != NULL) &&
503  	                    (preq->domain->cache_credentials == true) &&
504  	                    (pd->offline_auth == false)) {
505  	
506  	                    /* do auth with offline credentials */
507  	                    pd->offline_auth = true;
508  	
509  	                    ret = sysdb_get_ctx_from_list(preq->cctx->rctx->db_list,
510  	                                                  preq->domain, &sysdb);
511  	                    if (ret != EOK) {
512  	                        DEBUG(0, ("Fatal: Sysdb CTX not found for "
513  	                                  "domain [%s]!\n", preq->domain->name));
514  	                        goto done;
515  	                    }
516  	
517  	                    pctx = talloc_get_type(preq->cctx->rctx->pvt_ctx,
518  	                                           struct pam_ctx);
519  	
520  	                    req = sysdb_cache_auth_send(preq, preq->cctx->ev, sysdb,
521  	                                                preq->domain, pd->user,
522  	                                                pd->authtok, pd->authtok_size,
523  	                                                pctx->rctx->cdb, false);
524  	                    if (req == NULL) {
525  	                        DEBUG(1, ("Failed to setup offline auth.\n"));
526  	                        /* this error is not fatal, continue */
527  	                    } else {
528  	                        tevent_req_set_callback(req, pam_cache_auth_done, preq);
529  	                        return;
530  	                    }
531  	                }
532  	                break;
533  	            case SSS_PAM_CHAUTHTOK_PRELIM:
534  	            case SSS_PAM_CHAUTHTOK:
535  	                DEBUG(5, ("Password change not possible while offline.\n"));
536  	                pd->pam_status = PAM_AUTHTOK_ERR;
537  	                user_info_type = SSS_PAM_USER_INFO_OFFLINE_CHPASS;
538  	                pam_add_response(pd, SSS_PAM_USER_INFO, sizeof(uint32_t),
539  	                                 (const uint8_t *) &user_info_type);
540  	                break;
541  	/* TODO: we need the pam session cookie here to make sure that cached
542  	 * authentication was successful */
543  	            case SSS_PAM_SETCRED:
544  	            case SSS_PAM_ACCT_MGMT:
545  	            case SSS_PAM_OPEN_SESSION:
546  	            case SSS_PAM_CLOSE_SESSION:
547  	                DEBUG(2, ("Assuming offline authentication setting status for "
548  	                          "pam call %d to PAM_SUCCESS.\n", pd->cmd));
549  	                pd->pam_status = PAM_SUCCESS;
550  	                break;
551  	            default:
552  	                DEBUG(1, ("Unknown PAM call [%d].\n", pd->cmd));
553  	                pd->pam_status = PAM_MODULE_UNKNOWN;
554  	        }
555  	    }
556  	
557  	    if (pd->response_delay > 0) {
558  	        ret = gettimeofday(&tv, NULL);
559  	        if (ret != EOK) {
560  	            DEBUG(1, ("gettimeofday failed [%d][%s].\n",
561  	                      errno, strerror(errno)));
562  	            goto done;
563  	        }
564  	        tv.tv_sec += pd->response_delay;
565  	        tv.tv_usec = 0;
566  	        pd->response_delay = 0;
567  	
568  	        te = tevent_add_timer(cctx->ev, cctx, tv, pam_reply_delay, preq);
569  	        if (te == NULL) {
570  	            DEBUG(1, ("Failed to add event pam_reply_delay.\n"));
571  	            goto done;
572  	        }
573  	
574  	        return;
575  	    }
576  	
577  	    /* If this was a successful login, save the lastLogin time */
578  	    if (pd->cmd == SSS_PAM_AUTHENTICATE &&
579  	        pd->pam_status == PAM_SUCCESS &&
580  	        preq->domain->cache_credentials &&
581  	        !pd->offline_auth &&
582  	        !pd->last_auth_saved &&
583  	        NEED_CHECK_PROVIDER(preq->domain->provider)) {
584  	        ret = set_last_login(preq);
585  	        if (ret != EOK) {
586  	            goto done;
587  	        }
588  	
589  	        return;
590  	    }
591  	
592  	    ret = sss_packet_new(cctx->creq, 0, sss_packet_get_cmd(cctx->creq->in),
593  	                         &cctx->creq->out);
594  	    if (ret != EOK) {
595  	        goto done;
596  	    }
597  	
598  	    if (pd->domain != NULL) {
599  	        pam_add_response(pd, SSS_PAM_DOMAIN_NAME, strlen(pd->domain)+1,
600  	                         (uint8_t *) pd->domain);
601  	    }
602  	
603  	    resp_c = 0;
604  	    resp_size = 0;
605  	    resp = pd->resp_list;
606  	    while(resp != NULL) {
607  	        resp_c++;
608  	        resp_size += resp->len;
609  	        resp = resp->next;
610  	    }
611  	
612  	    ret = sss_packet_grow(cctx->creq->out, sizeof(int32_t) +
613  	                                           sizeof(int32_t) +
614  	                                           resp_c * 2* sizeof(int32_t) +
615  	                                           resp_size);
616  	    if (ret != EOK) {
617  	        goto done;
618  	    }
619  	
620  	    sss_packet_get_body(cctx->creq->out, &body, &blen);
621  	    DEBUG(4, ("blen: %d\n", blen));
622  	    p = 0;
623  	
624  	    memcpy(&body[p], &pd->pam_status, sizeof(int32_t));
625  	    p += sizeof(int32_t);
626  	
627  	    memcpy(&body[p], &resp_c, sizeof(int32_t));
628  	    p += sizeof(int32_t);
629  	
630  	    resp = pd->resp_list;
631  	    while(resp != NULL) {
632  	        memcpy(&body[p], &resp->type, sizeof(int32_t));
633  	        p += sizeof(int32_t);
634  	        memcpy(&body[p], &resp->len, sizeof(int32_t));
635  	        p += sizeof(int32_t);
636  	        memcpy(&body[p], resp->data, resp->len);
637  	        p += resp->len;
638  	
639  	        resp = resp->next;
640  	    }
641  	
642  	done:
643  	    sss_cmd_done(cctx, preq);
644  	}
645  	
646  	static void pam_cache_auth_done(struct tevent_req *req)
647  	{
648  	    int ret;
649  	    struct pam_auth_req *preq = tevent_req_callback_data(req,
650  	                                                         struct pam_auth_req);
651  	    uint32_t resp_type;
652  	    size_t resp_len;
653  	    uint8_t *resp;
654  	    time_t expire_date = 0;
655  	    time_t delayed_until = -1;
656  	    long long dummy;
657  	
658  	    ret = sysdb_cache_auth_recv(req, &expire_date, &delayed_until);
659  	    talloc_zfree(req);
660  	
661  	    switch (ret) {
662  	        case EOK:
663  	            preq->pd->pam_status = PAM_SUCCESS;
664  	
665  	            resp_type = SSS_PAM_USER_INFO_OFFLINE_AUTH;
666  	            resp_len = sizeof(uint32_t) + sizeof(long long);
667  	            resp = talloc_size(preq->pd, resp_len);
668  	            if (resp == NULL) {
669  	                DEBUG(1, ("talloc_size failed, cannot prepare user info.\n"));
670  	            } else {
671  	                memcpy(resp, &resp_type, sizeof(uint32_t));
672  	                dummy = (long long) expire_date;
673  	                memcpy(resp+sizeof(uint32_t), &dummy, sizeof(long long));
674  	                ret = pam_add_response(preq->pd, SSS_PAM_USER_INFO, resp_len,
675  	                                       (const uint8_t *) resp);
676  	                if (ret != EOK) {
677  	                    DEBUG(1, ("pam_add_response failed.\n"));
678  	                }
679  	            }
680  	            break;
681  	        case ENOENT:
682  	            preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
683  	            break;
684  	        case EINVAL:
685  	            preq->pd->pam_status = PAM_AUTH_ERR;
686  	            break;
687  	        case EACCES:
688  	            preq->pd->pam_status = PAM_PERM_DENIED;
689  	            if (delayed_until >= 0) {
690  	                resp_type = SSS_PAM_USER_INFO_OFFLINE_AUTH_DELAYED;
691  	                resp_len = sizeof(uint32_t) + sizeof(long long);
692  	                resp = talloc_size(preq->pd, resp_len);
693  	                if (resp == NULL) {
694  	                    DEBUG(1, ("talloc_size failed, cannot prepare user info.\n"));
695  	                } else {
696  	                    memcpy(resp, &resp_type, sizeof(uint32_t));
697  	                    dummy = (long long) delayed_until;
698  	                    memcpy(resp+sizeof(uint32_t), &dummy, sizeof(long long));
699  	                    ret = pam_add_response(preq->pd, SSS_PAM_USER_INFO, resp_len,
700  	                                           (const uint8_t *) resp);
701  	                    if (ret != EOK) {
702  	                        DEBUG(1, ("pam_add_response failed.\n"));
703  	                    }
704  	                }
705  	            }
706  	            break;
707  	        default:
708  	            preq->pd->pam_status = PAM_SYSTEM_ERR;
709  	    }
710  	
711  	    pam_reply(preq);
712  	    return;
713  	}
714  	
715  	static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
716  	                                       const char *err_msg, void *ptr);
717  	static void pam_check_user_callback(void *ptr, int status,
718  	                                    struct ldb_result *res);
719  	static void pam_dom_forwarder(struct pam_auth_req *preq);
720  	
721  	/* TODO: we should probably return some sort of cookie that is set in the
722  	 * PAM_ENVIRONMENT, so that we can save performing some calls and cache
723  	 * data. */
724  	
725  	static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
726  	{
727  	    struct sss_domain_info *dom;
728  	    struct sysdb_ctx *sysdb;
729  	    struct pam_auth_req *preq;
730  	    struct pam_data *pd;
731  	    uint8_t *body;
732  	    size_t blen;
733  	    int timeout;
734  	    int ret;
735  	    uint32_t terminator = SSS_END_OF_PAM_REQUEST;
736  	    preq = talloc_zero(cctx, struct pam_auth_req);
737  	    if (!preq) {
738  	        return ENOMEM;
739  	    }
740  	    preq->cctx = cctx;
741  	
742  	    preq->pd = talloc_zero(preq, struct pam_data);
743  	    if (!preq->pd) {
744  	        talloc_free(preq);
745  	        return ENOMEM;
746  	    }
747  	    pd = preq->pd;
748  	
749  	    sss_packet_get_body(cctx->creq->in, &body, &blen);
750  	    if (blen >= sizeof(uint32_t) &&
751  	        memcmp(&body[blen - sizeof(uint32_t)], &terminator, sizeof(uint32_t)) != 0) {
752  	        DEBUG(1, ("Received data not terminated.\n"));
753  	        ret = EINVAL;
754  	        goto done;
755  	    }
756  	
757  	    pd->cmd = pam_cmd;
758  	    pd->priv = cctx->priv;
759  	
760  	    switch (cctx->cli_protocol_version->version) {
761  	        case 1:
762  	            ret = pam_parse_in_data(cctx->rctx->names, pd, body, blen);
763  	            break;
764  	        case 2:
765  	            ret = pam_parse_in_data_v2(cctx->rctx->names, pd, body, blen);
766  	            break;
767  	        case 3:
768  	            ret = pam_parse_in_data_v3(cctx->rctx->names, pd, body, blen);
769  	            break;
770  	        default:
771  	            DEBUG(1, ("Illegal protocol version [%d].\n",
772  	                      cctx->cli_protocol_version->version));
773  	            ret = EINVAL;
774  	    }
775  	    if (ret != EOK) {
776  	        ret = EINVAL;
777  	        goto done;
778  	    }
779  	
780  	    /* now check user is valid */
781  	    if (pd->domain) {
782  	        for (dom = cctx->rctx->domains; dom; dom = dom->next) {
783  	            if (strcasecmp(dom->name, pd->domain) == 0) break;
784  	        }
785  	        if (!dom) {
786  	            ret = ENOENT;
787  	            goto done;
788  	        }
789  	        preq->domain = dom;
790  	    }
791  	    else {
792  	        for (dom = preq->cctx->rctx->domains; dom; dom = dom->next) {
793  	            if (dom->fqnames) continue;
794  	
795  	/* FIXME: need to support negative cache */
796  	#if HAVE_NEG_CACHE
797  	            ncret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
798  	                                          dom->name, cmdctx->name);
799  	            if (ncret == ENOENT) break;
800  	#endif
801  	            break;
802  	        }
803  	        if (!dom) {
804  	            ret = ENOENT;
805  	            goto done;
806  	        }
807  	        preq->domain = dom;
808  	    }
809  	
810  	    if (preq->domain->provider == NULL) {
811  	        DEBUG(1, ("Domain [%s] has no auth provider.\n", preq->domain->name));
812  	        ret = EINVAL;
813  	        goto done;
814  	    }
815  	
816  	    /* When auth is requested always search the provider first,
817  	     * do not rely on cached data unless the provider is completely
818  	     * offline */
819  	    if (NEED_CHECK_PROVIDER(preq->domain->provider) &&
820  	        (pam_cmd == SSS_PAM_AUTHENTICATE || pam_cmd == SSS_PAM_SETCRED)) {
821  	
822  	        /* no need to re-check later on */
823  	        preq->check_provider = false;
824  	        timeout = SSS_CLI_SOCKET_TIMEOUT/2;
825  	
826  	        ret = sss_dp_send_acct_req(preq->cctx->rctx, preq,
827  	                                   pam_check_user_dp_callback, preq,
828  	                                   timeout, preq->domain->name,
829  	                                   false, SSS_DP_INITGROUPS,
830  	                                   preq->pd->user, 0);
831  	    }
832  	    else {
833  	        preq->check_provider = NEED_CHECK_PROVIDER(preq->domain->provider);
834  	
835  	        ret = sysdb_get_ctx_from_list(cctx->rctx->db_list,
836  	                                      preq->domain, &sysdb);
837  	        if (ret != EOK) {
838  	            DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
839  	            goto done;
840  	        }
841  	        ret = sysdb_getpwnam(preq, sysdb,
842  	                             preq->domain, preq->pd->user,
843  	                             pam_check_user_callback, preq);
844  	    }
845  	
846  	done:
847  	    if (ret != EOK) {
848  	        switch (ret) {
Event unterminated_case: This case (value 2) is not terminated by a 'break' statement.
Also see events: [fallthrough]
849  	        case ENOENT:
850  	            pd->pam_status = PAM_USER_UNKNOWN;
Event fallthrough: The above case falls through to this one.
Also see events: [unterminated_case]
851  	        default:
852  	            pd->pam_status = PAM_SYSTEM_ERR;
853  	        }
854  	        pam_reply(preq);
855  	    }
856  	    return EOK;
857  	}
858  	
859  	static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
860  	                                       const char *err_msg, void *ptr)
861  	{
862  	    struct pam_auth_req *preq = talloc_get_type(ptr, struct pam_auth_req);
863  	    struct sysdb_ctx *sysdb;
864  	    int ret;
865  	
866  	    if (err_maj) {
867  	        DEBUG(2, ("Unable to get information from Data Provider\n"
868  	                  "Error: %u, %u, %s\n",
869  	                  (unsigned int)err_maj, (unsigned int)err_min, err_msg));
870  	    }
871  	
872  	    /* always try to see if we have the user in cache even if the provider
873  	     * returned an error */
874  	    ret = sysdb_get_ctx_from_list(preq->cctx->rctx->db_list,
875  	                                  preq->domain, &sysdb);
876  	    if (ret != EOK) {
877  	        DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
878  	        goto done;
879  	    }
880  	    ret = sysdb_getpwnam(preq, sysdb,
881  	                         preq->domain, preq->pd->user,
882  	                         pam_check_user_callback, preq);
883  	
884  	done:
885  	    if (ret != EOK) {
886  	        preq->pd->pam_status = PAM_SYSTEM_ERR;
887  	        pam_reply(preq);
888  	    }
889  	}
890  	
891  	static void pam_check_user_callback(void *ptr, int status,
892  	                                    struct ldb_result *res)
893  	{
894  	    struct pam_auth_req *preq = talloc_get_type(ptr, struct pam_auth_req);
895  	    struct sss_domain_info *dom;
896  	    struct sysdb_ctx *sysdb;
897  	    uint64_t cacheExpire;
898  	    bool call_provider = false;
899  	    time_t timeout;
900  	    int ret;
901  	
902  	    if (status != LDB_SUCCESS) {
903  	        preq->pd->pam_status = PAM_SYSTEM_ERR;
904  	        pam_reply(preq);
905  	        return;
906  	    }
907  	
908  	    timeout = SSS_CLI_SOCKET_TIMEOUT/2;
909  	
910  	    if (preq->check_provider) {
911  	        switch (res->count) {
912  	        case 0:
913  	            call_provider = true;
914  	            break;
915  	
916  	        case 1:
917  	            cacheExpire = ldb_msg_find_attr_as_uint64(res->msgs[0],
918  	                                                      SYSDB_CACHE_EXPIRE, 0);
919  	            if (cacheExpire < time(NULL)) {
920  	                call_provider = true;
921  	            }
922  	            break;
923  	
924  	        default:
925  	            DEBUG(1, ("check user call returned more than one result !?!\n"));
926  	            preq->pd->pam_status = PAM_SYSTEM_ERR;
927  	            pam_reply(preq);
928  	            return;
929  	        }
930  	    }
931  	
932  	    if (call_provider) {
933  	
934  	        /* dont loop forever :-) */
935  	        preq->check_provider = false;
936  	
937  	        /* keep around current data in case backend is offline */
938  	        if (res->count) {
939  	            preq->data = talloc_steal(preq, res);
940  	        }
941  	
942  	        ret = sss_dp_send_acct_req(preq->cctx->rctx, preq,
943  	                                   pam_check_user_dp_callback, preq,
944  	                                   timeout, preq->domain->name,
945  	                                   false, SSS_DP_USER,
946  	                                   preq->pd->user, 0);
947  	        if (ret != EOK) {
948  	            DEBUG(3, ("Failed to dispatch request: %d(%s)\n",
949  	                      ret, strerror(ret)));
950  	            preq->pd->pam_status = PAM_SYSTEM_ERR;
951  	            pam_reply(preq);
952  	        }
953  	        return;
954  	    }
955  	
956  	    switch (res->count) {
957  	    case 0:
958  	        if (!preq->pd->domain) {
959  	            /* search next as the domain was unknown */
960  	
961  	            ret = EOK;
962  	
963  	            /* skip domains that require FQnames or have negative caches */
964  	            for (dom = preq->domain->next; dom; dom = dom->next) {
965  	
966  	                if (dom->fqnames) continue;
967  	
968  	#if HAVE_NEG_CACHE
969  	                ncret = nss_ncache_check_user(nctx->ncache,
970  	                                              nctx->neg_timeout,
971  	                                              dom->name, cmdctx->name);
972  	                if (ncret == ENOENT) break;
973  	
974  	                neghit = true;
975  	#endif
976  	                break;
977  	            }
978  	#if HAVE_NEG_CACHE
979  	            /* reset neghit if we still have a domain to check */
980  	            if (dom) neghit = false;
981  	
982  	           if (neghit) {
983  	                DEBUG(2, ("User [%s] does not exist! (negative cache)\n",
984  	                          cmdctx->name));
985  	                ret = ENOENT;
986  	            }
987  	#endif
988  	            if (dom == NULL) {
989  	                DEBUG(2, ("No matching domain found for [%s], fail!\n",
990  	                          preq->pd->user));
991  	                ret = ENOENT;
992  	            }
993  	
994  	            if (ret == EOK) {
995  	                preq->domain = dom;
996  	                preq->data = NULL;
997  	
998  	                DEBUG(4, ("Requesting info for [%s@%s]\n",
999  	                          preq->pd->user, preq->domain->name));
1000 	
1001 	                /* When auth is requested always search the provider first,
1002 	                 * do not rely on cached data unless the provider is
1003 	                 * completely offline */
1004 	                if (NEED_CHECK_PROVIDER(preq->domain->provider) &&
1005 	                    (preq->pd->cmd == SSS_PAM_AUTHENTICATE ||
1006 	                     preq->pd->cmd == SSS_PAM_SETCRED)) {
1007 	
1008 	                    /* no need to re-check later on */
1009 	                    preq->check_provider = false;
1010 	
1011 	                    ret = sss_dp_send_acct_req(preq->cctx->rctx, preq,
1012 	                                               pam_check_user_dp_callback,
1013 	                                               preq, timeout,
1014 	                                               preq->domain->name,
1015 	                                               false, SSS_DP_USER,
1016 	                                               preq->pd->user, 0);
1017 	                }
1018 	                else {
1019 	                    preq->check_provider = NEED_CHECK_PROVIDER(preq->domain->provider);
1020 	
1021 	                    ret = sysdb_get_ctx_from_list(preq->cctx->rctx->db_list,
1022 	                                                  preq->domain, &sysdb);
1023 	                    if (ret != EOK) {
1024 	                        DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
1025 	                        preq->pd->pam_status = PAM_SYSTEM_ERR;
1026 	                        pam_reply(preq);
1027 	                        return;
1028 	                    }
1029 	                    ret = sysdb_getpwnam(preq, sysdb,
1030 	                                         preq->domain, preq->pd->user,
1031 	                                         pam_check_user_callback, preq);
1032 	                }
1033 	                if (ret != EOK) {
1034 	                    DEBUG(1, ("Failed to make request to our cache!\n"));
1035 	                }
1036 	            }
1037 	
1038 	            /* we made another call, end here */
1039 	            if (ret == EOK) return;
1040 	        }
1041 	        else {
1042 	            ret = ENOENT;
1043 	        }
1044 	
1045 	        DEBUG(2, ("No results for check user call\n"));
1046 	
1047 	#if HAVE_NEG_CACHE
1048 	        /* set negative cache only if not result of cache check */
1049 	        if (!neghit) {
1050 	            ret = nss_ncache_set_user(nctx->ncache, false,
1051 	                                      dctx->domain->name, cmdctx->name);
1052 	            if (ret != EOK) {
1053 	                NSS_CMD_FATAL_ERROR(cctx);
1054 	            }
1055 	        }
1056 	#endif
1057 	
1058 	        if (ret != EOK) {
1059 	            if (ret == ENOENT) {
1060 	                preq->pd->pam_status = PAM_USER_UNKNOWN;
1061 	            } else {
1062 	                preq->pd->pam_status = PAM_SYSTEM_ERR;
1063 	            }
1064 	            pam_reply(preq);
1065 	            return;
1066 	        }
1067 	        break;
1068 	
1069 	    case 1:
1070 	
1071 	        /* BINGO */
1072 	        pam_dom_forwarder(preq);
1073 	        return;
1074 	
1075 	    default:
1076 	        DEBUG(1, ("check user call returned more than one result !?!\n"));
1077 	        preq->pd->pam_status = PAM_SYSTEM_ERR;
1078 	        pam_reply(preq);
1079 	    }
1080 	}
1081 	
1082 	static void pam_dom_forwarder(struct pam_auth_req *preq)
1083 	{
1084 	    int ret;
1085 	
1086 	    if (!preq->pd->domain) {
1087 	        preq->pd->domain = preq->domain->name;
1088 	    }
1089 	
1090 	    if (!NEED_CHECK_PROVIDER(preq->domain->provider)) {
1091 	        preq->callback = pam_reply;
1092 	        ret = LOCAL_pam_handler(preq);
1093 	    }
1094 	    else {
1095 	        preq->callback = pam_reply;
1096 	        ret = pam_dp_send_req(preq, SSS_CLI_SOCKET_TIMEOUT/2);
1097 	        DEBUG(4, ("pam_dp_send_req returned %d\n", ret));
1098 	    }
1099 	
1100 	    if (ret != EOK) {
1101 	        preq->pd->pam_status = PAM_SYSTEM_ERR;
1102 	        pam_reply(preq);
1103 	    }
1104 	}
1105 	
1106 	static int pam_cmd_authenticate(struct cli_ctx *cctx) {
1107 	    DEBUG(4, ("entering pam_cmd_authenticate\n"));
1108 	    return pam_forwarder(cctx, SSS_PAM_AUTHENTICATE);
1109 	}
1110 	
1111 	static int pam_cmd_setcred(struct cli_ctx *cctx) {
1112 	    DEBUG(4, ("entering pam_cmd_setcred\n"));
1113 	    return pam_forwarder(cctx, SSS_PAM_SETCRED);
1114 	}
1115 	
1116 	static int pam_cmd_acct_mgmt(struct cli_ctx *cctx) {
1117 	    DEBUG(4, ("entering pam_cmd_acct_mgmt\n"));
1118 	    return pam_forwarder(cctx, SSS_PAM_ACCT_MGMT);
1119 	}
1120 	
1121 	static int pam_cmd_open_session(struct cli_ctx *cctx) {
1122 	    DEBUG(4, ("entering pam_cmd_open_session\n"));
1123 	    return pam_forwarder(cctx, SSS_PAM_OPEN_SESSION);
1124 	}
1125 	
1126 	static int pam_cmd_close_session(struct cli_ctx *cctx) {
1127 	    DEBUG(4, ("entering pam_cmd_close_session\n"));
1128 	    return pam_forwarder(cctx, SSS_PAM_CLOSE_SESSION);
1129 	}
1130 	
1131 	static int pam_cmd_chauthtok(struct cli_ctx *cctx) {
1132 	    DEBUG(4, ("entering pam_cmd_chauthtok\n"));
1133 	    return pam_forwarder(cctx, SSS_PAM_CHAUTHTOK);
1134 	}
1135 	
1136 	static int pam_cmd_chauthtok_prelim(struct cli_ctx *cctx) {
1137 	    DEBUG(4, ("entering pam_cmd_chauthtok_prelim\n"));
1138 	    return pam_forwarder(cctx, SSS_PAM_CHAUTHTOK_PRELIM);
1139 	}
1140 	
1141 	struct cli_protocol_version *register_cli_protocol_version(void)
1142 	{
1143 	    static struct cli_protocol_version pam_cli_protocol_version[] = {
1144 	        {3, "2009-09-14", "make cli_pid mandatory"},
1145 	        {2, "2009-05-12", "new format <type><size><data>"},
1146 	        {1, "2008-09-05", "initial version, \\0 terminated strings"},
1147 	        {0, NULL, NULL}
1148 	    };
1149 	
1150 	    return pam_cli_protocol_version;
1151 	}
1152 	
1153 	struct sss_cmd_table *get_pam_cmds(void)
1154 	{
1155 	    static struct sss_cmd_table sss_cmds[] = {
1156 	        {SSS_GET_VERSION, sss_cmd_get_version},
1157 	        {SSS_PAM_AUTHENTICATE, pam_cmd_authenticate},
1158 	        {SSS_PAM_SETCRED, pam_cmd_setcred},
1159 	        {SSS_PAM_ACCT_MGMT, pam_cmd_acct_mgmt},
1160 	        {SSS_PAM_OPEN_SESSION, pam_cmd_open_session},
1161 	        {SSS_PAM_CLOSE_SESSION, pam_cmd_close_session},
1162 	        {SSS_PAM_CHAUTHTOK, pam_cmd_chauthtok},
1163 	        {SSS_PAM_CHAUTHTOK_PRELIM, pam_cmd_chauthtok_prelim},
1164 	        {SSS_CLI_NULL, NULL}
1165 	    };
1166 	
1167 	    return sss_cmds;
1168 	}