1    	/*
2    	    SSSD
3    	
4    	    IPA Backend Module -- Authentication
5    	
6    	    Authors:
7    	        Sumit Bose <sbose@redhat.com>
8    	
9    	    Copyright (C) 2009 Red Hat
10   	
11   	    This program is free software; you can redistribute it and/or modify
12   	    it under the terms of the GNU General Public License as published by
13   	    the Free Software Foundation; either version 3 of the License, or
14   	    (at your option) any later version.
15   	
16   	    This program is distributed in the hope that it will be useful,
17   	    but WITHOUT ANY WARRANTY; without even the implied warranty of
18   	    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   	    GNU General Public License for more details.
20   	
21   	    You should have received a copy of the GNU General Public License
22   	    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   	*/
24   	
25   	#include <sys/param.h>
26   	#include <security/pam_modules.h>
27   	
28   	#include "util/util.h"
29   	#include "providers/ldap/ldap_common.h"
30   	#include "providers/ldap/sdap_async.h"
31   	#include "providers/krb5/krb5_auth.h"
32   	#include "providers/ipa/ipa_common.h"
33   	
34   	#define IPA_CONFIG_MIRATION_ENABLED "ipaMigrationEnabled"
35   	#define IPA_CONFIG_SEARCH_BASE_TEMPLATE "cn=etc,%s"
36   	#define IPA_CONFIG_FILTER "(&(cn=ipaConfig)(objectClass=ipaGuiConfig))"
37   	
38   	static void ipa_auth_reply(struct be_req *be_req, int dp_err, int result)
39   	{
40   	    be_req->fn(be_req, dp_err, result, NULL);
41   	}
42   	
43   	struct get_password_migration_flag_state {
44   	    struct tevent_context *ev;
45   	    struct sdap_auth_ctx *sdap_auth_ctx;
46   	    struct sdap_handle *sh;
47   	    enum sdap_result result;
48   	    struct fo_server *srv;
49   	    char *ipa_domain;
50   	    bool password_migration;
51   	};
52   	
53   	static void get_password_migration_flag_auth_done(struct tevent_req *subreq);
54   	static void get_password_migration_flag_done(struct tevent_req *subreq);
55   	
56   	static struct tevent_req *get_password_migration_flag_send(TALLOC_CTX *memctx,
57   	                                            struct tevent_context *ev,
58   	                                            struct sdap_auth_ctx *sdap_auth_ctx,
59   	                                            char *ipa_domain)
60   	{
61   	    int ret;
62   	    struct tevent_req *req, *subreq;
63   	    struct get_password_migration_flag_state *state;
64   	
65   	    if (sdap_auth_ctx == NULL || ipa_domain == NULL) {
66   	        DEBUG(1, ("Missing parameter.\n"));
67   	        return NULL;
68   	    }
69   	
70   	    req = tevent_req_create(memctx, &state,
71   	                            struct get_password_migration_flag_state);
72   	    if (req == NULL) {
73   	        DEBUG(1, ("tevent_req_create failed.\n"));
74   	        return NULL;
75   	    }
76   	
77   	    state->ev = ev;
78   	    state->sdap_auth_ctx = sdap_auth_ctx;
79   	    state->sh = NULL;
80   	    state->result = SDAP_ERROR;
81   	    state->srv = NULL;
82   	    state->password_migration = false;
83   	    state->ipa_domain = ipa_domain;
84   	
85   	    /* We request to use StartTLS here, because if password migration is
86   	     * enabled we will use this connection for authentication, too. */
87   	    ret = dp_opt_set_bool(sdap_auth_ctx->opts->basic, SDAP_ID_TLS, true);
88   	    if (ret != EOK) {
89   	        DEBUG(1, ("Failed to set SDAP_ID_TLS to true.\n"));
90   	        goto fail;
91   	    }
92   	
93   	    subreq = sdap_cli_connect_send(state, ev, sdap_auth_ctx->opts,
94   	                                   sdap_auth_ctx->be, sdap_auth_ctx->service,
95   	                                   NULL);
96   	    if (subreq == NULL) {
97   	        DEBUG(1, ("sdap_cli_connect_send failed.\n"));
98   	        goto fail;
99   	    }
100  	    tevent_req_set_callback(subreq, get_password_migration_flag_auth_done,
101  	                            req);
102  	
103  	    return req;
104  	
105  	fail:
106  	    talloc_zfree(req);
107  	    return NULL;
108  	}
109  	
110  	static void get_password_migration_flag_auth_done(struct tevent_req *subreq)
111  	{
112  	    struct tevent_req *req = tevent_req_callback_data(subreq,
113  	                                                      struct tevent_req);
114  	    struct get_password_migration_flag_state *state = tevent_req_data(req,
115  	                                      struct get_password_migration_flag_state);
116  	    int ret;
117  	    char *ldap_basedn;
118  	    char *search_base;
119  	    const char **attrs;
120  	
121  	    ret = sdap_cli_connect_recv(subreq, state, &state->sh, NULL);
122  	    talloc_zfree(subreq);
123  	    if (ret) {
124  	        DEBUG(1, ("sdap_auth request failed.\n"));
125  	        tevent_req_error(req, ret);
126  	        return;
127  	    }
128  	
129  	    ret = domain_to_basedn(state, state->ipa_domain, &ldap_basedn);
130  	    if (ret != EOK) {
131  	        DEBUG(1, ("domain_to_basedn failed.\n"));
132  	        tevent_req_error(req, ret);
133  	        return;
134  	    }
135  	
136  	    search_base = talloc_asprintf(state, IPA_CONFIG_SEARCH_BASE_TEMPLATE,
137  	                                  ldap_basedn);
138  	    if (search_base == NULL) {
139  	        DEBUG(1, ("talloc_asprintf failed.\n"));
140  	        tevent_req_error(req, ENOMEM);
141  	        return;
142  	    }
143  	
144  	    attrs = talloc_array(state, const char*, 2);
145  	    if (attrs == NULL) {
146  	        DEBUG(1, ("talloc_array failed.\n"));
147  	        tevent_req_error(req, ENOMEM);
148  	        return;
149  	    }
150  	
151  	    attrs[0] = IPA_CONFIG_MIRATION_ENABLED;
152  	    attrs[1] = NULL;
153  	
154  	    subreq = sdap_get_generic_send(state, state->ev, state->sdap_auth_ctx->opts,
155  	                                   state->sh, search_base, LDAP_SCOPE_SUBTREE,
156  	                                   IPA_CONFIG_FILTER, attrs, NULL, 0);
157  	    if (!subreq) {
158  	        tevent_req_error(req, ENOMEM);
159  	        return;
160  	    }
161  	
162  	    tevent_req_set_callback(subreq, get_password_migration_flag_done, req);
163  	}
164  	
165  	static void get_password_migration_flag_done(struct tevent_req *subreq)
166  	{
167  	    struct tevent_req *req = tevent_req_callback_data(subreq,
168  	                                                      struct tevent_req);
169  	    struct get_password_migration_flag_state *state = tevent_req_data(req,
170  	                                      struct get_password_migration_flag_state);
171  	    int ret;
172  	    size_t reply_count;
173  	    struct sysdb_attrs **reply = NULL;
174  	    const char *value = NULL;
175  	
176  	    ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply);
177  	    talloc_zfree(subreq);
178  	    if (ret) {
179  	        tevent_req_error(req, ret);
180  	        return;
181  	    }
182  	
183  	    if (reply_count != 1) {
184  	        DEBUG(1, ("Unexpected number of results, expected 1, got %d.\n",
185  	                  reply_count));
186  	        tevent_req_error(req, EINVAL);
187  	        return;
188  	    }
189  	
190  	    ret = sysdb_attrs_get_string(reply[0], IPA_CONFIG_MIRATION_ENABLED, &value);
191  	    if (strcasecmp(value, "true") == 0) {
192  	        state->password_migration = true;
193  	    }
194  	
195  	    tevent_req_done(req);
196  	}
197  	
198  	static int get_password_migration_flag_recv(struct tevent_req *req,
199  	                                            TALLOC_CTX *mem_ctx,
200  	                                            bool *password_migration,
201  	                                            struct sdap_handle **sh)
202  	{
203  	    struct get_password_migration_flag_state *state = tevent_req_data(req,
204  	                                      struct get_password_migration_flag_state);
205  	
206  	    TEVENT_REQ_RETURN_ON_ERROR(req);
207  	
208  	    *password_migration = state->password_migration;
209  	    if (sh != NULL) {
210  	        *sh = talloc_steal(mem_ctx, state->sh);
211  	    }
212  	
213  	    return EOK;
214  	}
215  	
216  	
217  	struct ipa_auth_state {
218  	    struct be_req *be_req;
219  	    struct tevent_context *ev;
220  	    struct ipa_auth_ctx *ipa_auth_ctx;
221  	    struct pam_data *pd;
222  	    bool password_migration;
223  	    struct sdap_handle *sh;
224  	};
225  	
226  	static void ipa_auth_handler_done(struct tevent_req *req);
227  	static void ipa_get_migration_flag_done(struct tevent_req *req);
228  	static void ipa_auth_get_user_dn_done(struct tevent_req *req);
229  	static void ipa_auth_ldap_done(struct tevent_req *req);
230  	static void ipa_auth_handler_retry_done(struct tevent_req *req);
231  	
232  	void ipa_auth(struct be_req *be_req)
233  	{
234  	    struct tevent_req *req;
235  	    struct ipa_auth_state *state;
236  	
237  	    state = talloc_zero(be_req, struct ipa_auth_state);
Event var_compare_op: Comparing "state" to null implies that "state" might be null.
Also see events: [var_deref_op]
At conditional (1): "state == NULL": Taking true branch.
238  	    if (state == NULL) {
At conditional (2): "1 <= debug_level": Taking true branch.
At conditional (3): "debug_timestamps": Taking true branch.
239  	        DEBUG(1, ("talloc_zero failed.\n"));
240  	        goto fail;
241  	    }
242  	
243  	    state->password_migration = false;
244  	    state->sh = NULL;
245  	
246  	    state->be_req = be_req;
247  	    state->ev = be_req->be_ctx->ev;
248  	
249  	    state->pd = talloc_get_type(be_req->req_data, struct pam_data);
250  	
251  	    switch (state->pd->cmd) {
252  	        case SSS_PAM_AUTHENTICATE:
253  	            state->ipa_auth_ctx = talloc_get_type(
254  	                                be_req->be_ctx->bet_info[BET_AUTH].pvt_bet_data,
255  	                                struct ipa_auth_ctx);
256  	            break;
257  	        case SSS_PAM_CHAUTHTOK:
258  	        case SSS_PAM_CHAUTHTOK_PRELIM:
259  	            state->ipa_auth_ctx = talloc_get_type(
260  	                              be_req->be_ctx->bet_info[BET_CHPASS].pvt_bet_data,
261  	                              struct ipa_auth_ctx);
262  	            break;
263  	        default:
264  	            DEBUG(1, ("Unsupported PAM task.\n"));
265  	            goto fail;
266  	    }
267  	
268  	    req = krb5_auth_send(state, state->ev, be_req->be_ctx, state->pd,
269  	                         state->ipa_auth_ctx->krb5_auth_ctx);
270  	    if (req == NULL) {
271  	        DEBUG(1, ("krb5_auth_send failed.\n"));
272  	        goto fail;
273  	    }
274  	
275  	    tevent_req_set_callback(req, ipa_auth_handler_done, state);
276  	    return;
277  	
278  	fail:
Event var_deref_op: Dereferencing null variable "state".
Also see events: [var_compare_op]
279  	    state->pd->pam_status = PAM_SYSTEM_ERR;
280  	    ipa_auth_reply(be_req, DP_ERR_FATAL, state->pd->pam_status);
281  	}
282  	
283  	static void ipa_auth_handler_done(struct tevent_req *req)
284  	{
285  	    struct ipa_auth_state *state = tevent_req_callback_data(req,
286  	                                                         struct ipa_auth_state);
287  	    int ret;
288  	    int pam_status = PAM_SYSTEM_ERR;
289  	    int dp_err;
290  	
291  	    ret = krb5_auth_recv(req, &pam_status, &dp_err);
292  	    talloc_zfree(req);
293  	    state->pd->pam_status = pam_status;
294  	    if (ret != EOK && pam_status != PAM_CRED_ERR) {
295  	        DEBUG(1, ("krb5_auth_recv request failed.\n"));
296  	        dp_err = DP_ERR_OK;
297  	        goto done;
298  	    }
299  	
300  	    if (dp_err != DP_ERR_OK) {
301  	        goto done;
302  	    }
303  	
304  	    if (state->pd->cmd == SSS_PAM_AUTHENTICATE &&
305  	        state->pd->pam_status == PAM_CRED_ERR) {
306  	
307  	        req = get_password_migration_flag_send(state, state->ev,
308  	                                             state->ipa_auth_ctx->sdap_auth_ctx,
309  	                                             dp_opt_get_string(
310  	                                               state->ipa_auth_ctx->ipa_options,
311  	                                               IPA_DOMAIN));
312  	        if (req == NULL) {
313  	            DEBUG(1, ("get_password_migration_flag failed.\n"));
314  	            goto done;
315  	        }
316  	
317  	        tevent_req_set_callback(req, ipa_get_migration_flag_done, state);
318  	        return;
319  	    }
320  	
321  	done:
322  	    ipa_auth_reply(state->be_req, dp_err, state->pd->pam_status);
323  	}
324  	
325  	static void ipa_get_migration_flag_done(struct tevent_req *req)
326  	{
327  	    struct ipa_auth_state *state = tevent_req_callback_data(req,
328  	                                                         struct ipa_auth_state);
329  	    int ret;
330  	    int dp_err = DP_ERR_FATAL;
331  	    const char **attrs;
332  	
333  	    ret = get_password_migration_flag_recv(req, state,
334  	                                           &state->password_migration,
335  	                                           &state->sh);
336  	    talloc_zfree(req);
337  	    if (ret != EOK) {
338  	        DEBUG(1, ("get_password_migration_flag request failed.\n"));
339  	        state->pd->pam_status = PAM_SYSTEM_ERR;
340  	        dp_err = DP_ERR_OK;
341  	        goto done;
342  	    }
343  	
344  	    if (state->password_migration) {
345  	        state->pd->pam_status = PAM_SYSTEM_ERR;
346  	        DEBUG(1, ("Assuming Kerberos password is missing, "
347  	                  "starting password migration.\n"));
348  	
349  	        attrs = talloc_array(state, const char *, 2);
350  	        if (attrs == NULL) {
351  	            DEBUG(1, ("talloc_array failed.\n"));
352  	            state->pd->pam_status = PAM_SYSTEM_ERR;
353  	            dp_err = DP_ERR_OK;
354  	            goto done;
355  	        }
356  	        attrs[0] = SYSDB_ORIG_DN;
357  	        attrs[1] = NULL;
358  	
359  	        req = sysdb_search_user_by_name_send(state, state->ev,
360  	                                             state->be_req->be_ctx->sysdb, NULL,
361  	                                             state->be_req->be_ctx->domain,
362  	                                             state->pd->user, attrs);
363  	        if (req == NULL) {
364  	            DEBUG(1, ("sysdb_search_user_by_name_send failed.\n"));
365  	            goto done;
366  	        }
367  	
368  	        tevent_req_set_callback(req, ipa_auth_get_user_dn_done, state);
369  	        return;
370  	    } else {
371  	        DEBUG(5, ("Password migration is not enabled.\n"));
372  	    }
373  	
374  	    dp_err = DP_ERR_OK;
375  	
376  	done:
377  	    ipa_auth_reply(state->be_req, dp_err, state->pd->pam_status);
378  	}
379  	
380  	void ipa_auth_get_user_dn_done(struct tevent_req *req)
381  	{
382  	    struct ipa_auth_state *state = tevent_req_callback_data(req,
383  	                                                         struct ipa_auth_state);
384  	    int ret;
385  	    int dp_err = DP_ERR_FATAL;
386  	    struct dp_opt_blob password;
387  	    struct ldb_message *msg;
388  	    const char *dn;
389  	
390  	    ret = sysdb_search_user_recv(req, state, &msg);
391  	    talloc_zfree(req);
392  	    if (ret != EOK) {
393  	        DEBUG(1, ("sysdb_search_user request failed.\n"));
394  	        state->pd->pam_status = PAM_SYSTEM_ERR;
395  	        dp_err = DP_ERR_OK;
396  	        goto done;
397  	    }
398  	
399  	    dn = ldb_msg_find_attr_as_string(msg, SYSDB_ORIG_DN, NULL);
400  	    if (dn == NULL) {
401  	        DEBUG(1, ("Missing original DN for user [%s].\n", state->pd->user));
402  	        state->pd->pam_status = PAM_SYSTEM_ERR;
403  	        dp_err = DP_ERR_OK;
404  	        goto done;
405  	    }
406  	
407  	    password.data = state->pd->authtok;
408  	    password.length = state->pd->authtok_size;
409  	
410  	    req = sdap_auth_send(state, state->ev, state->sh, NULL, NULL, dn,
411  	                         "password", password);
412  	    if (req == NULL) {
413  	        DEBUG(1, ("sdap_auth_send failed.\n"));
414  	        goto done;
415  	    }
416  	
417  	    tevent_req_set_callback(req, ipa_auth_ldap_done, state);
418  	    return;
419  	
420  	done:
421  	    ipa_auth_reply(state->be_req, dp_err, state->pd->pam_status);
422  	}
423  	
424  	
425  	static void ipa_auth_ldap_done(struct tevent_req *req)
426  	{
427  	    struct ipa_auth_state *state = tevent_req_callback_data(req,
428  	                                                         struct ipa_auth_state);
429  	    int ret;
430  	    int dp_err = DP_ERR_FATAL;
431  	    enum sdap_result result;
432  	
433  	    ret = sdap_auth_recv(req, state, &result, NULL);
434  	    talloc_zfree(req);
435  	    if (ret != EOK) {
436  	        DEBUG(1, ("auth_send request failed.\n"));
437  	        state->pd->pam_status = PAM_SYSTEM_ERR;
438  	        dp_err = DP_ERR_OK;
439  	        goto done;
440  	    }
441  	
442  	/* TODO: do we need to handle expired passwords? */
443  	    if (result != SDAP_AUTH_SUCCESS) {
444  	        DEBUG(1, ("LDAP authentication failed, "
445  	                  "Password migration not possible.\n"));
446  	        state->pd->pam_status = PAM_CRED_INSUFFICIENT;
447  	        dp_err = DP_ERR_OK;
448  	        goto done;
449  	    }
450  	
451  	    DEBUG(1, ("LDAP authentication succeded, "
452  	              "trying Kerberos authentication again.\n"));
453  	
454  	    req = krb5_auth_send(state, state->ev,
455  	                         state->be_req->be_ctx, state->pd,
456  	                         state->ipa_auth_ctx->krb5_auth_ctx);
457  	    if (req == NULL) {
458  	        DEBUG(1, ("krb5_auth_send failed.\n"));
459  	        goto done;
460  	    }
461  	
462  	    tevent_req_set_callback(req, ipa_auth_handler_retry_done, state);
463  	    return;
464  	
465  	done:
466  	    ipa_auth_reply(state->be_req, dp_err, state->pd->pam_status);
467  	}
468  	
469  	static void ipa_auth_handler_retry_done(struct tevent_req *req)
470  	{
471  	    struct ipa_auth_state *state = tevent_req_callback_data(req,
472  	                                                         struct ipa_auth_state);
473  	    int ret;
474  	    int pam_status;
475  	    int dp_err;
476  	
477  	    ret = krb5_auth_recv(req, &pam_status, &dp_err);
478  	    talloc_zfree(req);
479  	    if (ret != EOK) {
480  	        DEBUG(1, ("krb5_auth_recv request failed.\n"));
481  	        state->pd->pam_status = PAM_SYSTEM_ERR;
482  	        dp_err = DP_ERR_OK;
483  	        goto done;
484  	    }
485  	
486  	    state->pd->pam_status = pam_status;
487  	
488  	done:
489  	    ipa_auth_reply(state->be_req, dp_err, state->pd->pam_status);
490  	}