Line data Source code
1 : /*
2 : Authors:
3 : Pavel Březina <pbrezina@redhat.com>
4 :
5 : Copyright (C) 2016 Red Hat
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <talloc.h>
22 : #include <tevent.h>
23 : #include <security/pam_appl.h>
24 : #include <security/pam_modules.h>
25 :
26 : #include "sbus/sssd_dbus.h"
27 : #include "providers/data_provider/dp_private.h"
28 : #include "providers/data_provider/dp_iface.h"
29 : #include "providers/backend.h"
30 : #include "util/util.h"
31 :
32 0 : static void dp_pam_reply(struct sbus_request *sbus_req,
33 : const char *request_name,
34 : struct pam_data *pd)
35 : {
36 : DBusMessage *reply;
37 : dbus_bool_t dbret;
38 :
39 0 : DP_REQ_DEBUG(SSSDBG_TRACE_LIBS, request_name,
40 : "Sending result [%d][%s]", pd->pam_status, pd->domain);
41 :
42 0 : reply = dbus_message_new_method_return(sbus_req->message);
43 0 : if (reply == NULL) {
44 0 : DP_REQ_DEBUG(SSSDBG_TRACE_LIBS, request_name,
45 : "Unable to acquire reply message");
46 0 : return;
47 : }
48 :
49 0 : dbret = dp_pack_pam_response(reply, pd);
50 0 : if (!dbret) {
51 0 : DP_REQ_DEBUG(SSSDBG_TRACE_LIBS, request_name,
52 : "Unable to generate reply message");
53 0 : dbus_message_unref(reply);
54 0 : return;
55 : }
56 :
57 0 : sbus_request_finish(sbus_req, reply);
58 0 : dbus_message_unref(reply);
59 0 : return;
60 : }
61 :
62 0 : static errno_t pam_data_create(TALLOC_CTX *mem_ctx,
63 : struct sbus_request *sbus_req,
64 : struct be_ctx *be_ctx,
65 : struct pam_data **_pd)
66 : {
67 : DBusError dbus_error;
68 : struct pam_data *pd;
69 : bool bret;
70 :
71 0 : dbus_error_init(&dbus_error);
72 0 : bret = dp_unpack_pam_request(sbus_req->message, mem_ctx, &pd, &dbus_error);
73 0 : if (bret == false) {
74 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse message!\n");
75 0 : return EINVAL;
76 : }
77 :
78 0 : pd->pam_status = PAM_SYSTEM_ERR;
79 0 : if (pd->domain == NULL) {
80 0 : pd->domain = talloc_strdup(pd, be_ctx->domain->name);
81 0 : if (pd->domain == NULL) {
82 0 : talloc_free(pd);
83 0 : return ENOMEM;
84 : }
85 : }
86 :
87 0 : *_pd = pd;
88 :
89 0 : return EOK;
90 : }
91 :
92 0 : static void choose_target(struct data_provider *provider,
93 : struct pam_data *pd,
94 : enum dp_targets *_target,
95 : enum dp_methods *_method,
96 : const char **_req_name)
97 : {
98 : enum dp_targets target;
99 : enum dp_methods method;
100 : const char *name;
101 :
102 0 : switch (pd->cmd) {
103 : case SSS_PAM_AUTHENTICATE:
104 0 : target = DPT_AUTH;
105 0 : method = DPM_AUTH_HANDLER;
106 0 : name = "PAM Authenticate";
107 0 : break;
108 : case SSS_PAM_PREAUTH:
109 0 : target = DPT_AUTH;
110 0 : method = DPM_AUTH_HANDLER;
111 0 : name = "PAM Preauth";
112 0 : break;
113 : case SSS_PAM_ACCT_MGMT:
114 0 : target = DPT_ACCESS;
115 0 : method = DPM_ACCESS_HANDLER;
116 0 : name = "PAM Account";
117 0 : break;
118 : case SSS_PAM_CHAUTHTOK_PRELIM:
119 0 : target = DPT_CHPASS;
120 0 : method = DPM_AUTH_HANDLER;
121 0 : name = "PAM Chpass 1st";
122 0 : break;
123 : case SSS_PAM_CHAUTHTOK:
124 0 : target = DPT_CHPASS;
125 0 : method = DPM_AUTH_HANDLER;
126 0 : name = "PAM Chpass 2nd";
127 0 : break;
128 : case SSS_PAM_OPEN_SESSION:
129 0 : target = DP_TARGET_SENTINEL;
130 0 : method = DP_METHOD_SENTINEL;
131 0 : name = "PAM Open Session";
132 0 : pd->pam_status = PAM_SUCCESS;
133 0 : break;
134 : case SSS_PAM_SETCRED:
135 0 : target = DP_TARGET_SENTINEL;
136 0 : method = DP_METHOD_SENTINEL;
137 0 : name = "PAM Set Credentials";
138 0 : pd->pam_status = PAM_SUCCESS;
139 0 : break;
140 : case SSS_PAM_CLOSE_SESSION:
141 0 : target = DP_TARGET_SENTINEL;
142 0 : method = DP_METHOD_SENTINEL;
143 0 : name = "PAM Close Session";
144 0 : pd->pam_status = PAM_SUCCESS;
145 0 : break;
146 : default:
147 0 : DEBUG(SSSDBG_TRACE_LIBS, "Unsupported PAM command [%d].\n",
148 : pd->cmd);
149 0 : target = DP_TARGET_SENTINEL;
150 0 : method = DP_METHOD_SENTINEL;
151 0 : name = "PAM Unsupported";
152 0 : pd->pam_status = PAM_MODULE_UNKNOWN;
153 0 : break;
154 : }
155 :
156 : /* Check that target is configured. */
157 0 : if (target != DP_TARGET_SENTINEL
158 0 : && !dp_target_enabled(provider, NULL, target)) {
159 0 : target = DP_TARGET_SENTINEL;
160 0 : method = DP_METHOD_SENTINEL;
161 0 : pd->pam_status = PAM_MODULE_UNKNOWN;
162 : }
163 :
164 0 : *_target = target;
165 0 : *_method = method;
166 0 : *_req_name = name;
167 0 : }
168 :
169 : struct dp_pam_handler_state {
170 : struct data_provider *provider;
171 : struct dp_client *dp_cli;
172 : struct sbus_request *sbus_req;
173 : const char *request_name;
174 : };
175 :
176 : void dp_pam_handler_step_done(struct tevent_req *req);
177 : void dp_pam_handler_selinux_done(struct tevent_req *req);
178 :
179 0 : errno_t dp_pam_handler(struct sbus_request *sbus_req, void *sbus_data)
180 : {
181 : struct dp_pam_handler_state *state;
182 : struct data_provider *provider;
183 0 : struct pam_data *pd = NULL;
184 : struct dp_client *dp_cli;
185 : enum dp_targets target;
186 : enum dp_methods method;
187 : const char *req_name;
188 : struct tevent_req *req;
189 : errno_t ret;
190 :
191 0 : dp_cli = talloc_get_type(sbus_data, struct dp_client);
192 0 : provider = dp_client_provider(dp_cli);
193 :
194 0 : state = talloc_zero(sbus_req, struct dp_pam_handler_state);
195 0 : if (state == NULL) {
196 0 : ret = ENOMEM;
197 0 : goto done;
198 : }
199 :
200 0 : ret = pam_data_create(state, sbus_req, provider->be_ctx, &pd);
201 0 : if (ret != EOK) {
202 0 : return ret;
203 : }
204 :
205 0 : state->provider = provider;
206 0 : state->dp_cli = dp_cli;
207 0 : state->sbus_req = sbus_req;
208 :
209 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Got request with the following data\n");
210 0 : DEBUG_PAM_DATA(SSSDBG_CONF_SETTINGS, pd);
211 :
212 0 : choose_target(provider, pd, &target, &method, &req_name);
213 0 : if (target == DP_TARGET_SENTINEL) {
214 : /* Just send the result. Pam data are freed with this call. */
215 0 : dp_pam_reply(sbus_req, req_name, pd);
216 0 : return EOK;
217 : }
218 :
219 0 : req = dp_req_send(state, provider, dp_cli, pd->domain, req_name,
220 : target, method, 0, pd, &state->request_name);
221 0 : if (req == NULL) {
222 0 : ret = ENOMEM;
223 0 : goto done;
224 : }
225 :
226 0 : tevent_req_set_callback(req, dp_pam_handler_step_done, state);
227 :
228 : done:
229 0 : if (ret != EOK) {
230 0 : talloc_free(pd);
231 : }
232 :
233 0 : return ret;
234 : }
235 :
236 0 : static bool should_invoke_selinux(struct data_provider *provider,
237 : struct pam_data *pd)
238 : {
239 0 : if (!dp_method_enabled(provider, DPT_SELINUX, DPM_SELINUX_HANDLER)) {
240 0 : return false;
241 : }
242 :
243 0 : if (pd->cmd == SSS_PAM_ACCT_MGMT && pd->pam_status == PAM_SUCCESS) {
244 0 : return true;
245 : }
246 :
247 0 : return false;
248 : }
249 :
250 0 : void dp_pam_handler_step_done(struct tevent_req *req)
251 : {
252 : struct dp_pam_handler_state *state;
253 : struct pam_data *pd;
254 : errno_t ret;
255 :
256 0 : state = tevent_req_callback_data(req, struct dp_pam_handler_state);
257 :
258 0 : ret = dp_req_recv(state, req, struct pam_data *, &pd);
259 0 : talloc_zfree(req);
260 0 : if (ret != EOK) {
261 0 : dp_req_reply_error(state->sbus_req, state->request_name, ret);
262 0 : return;
263 : }
264 :
265 0 : if (!should_invoke_selinux(state->provider, pd)) {
266 : /* State and request related data are freed with sbus_req. */
267 0 : dp_pam_reply(state->sbus_req, state->request_name, pd);
268 0 : return;
269 : }
270 :
271 0 : req = dp_req_send(state, state->provider, state->dp_cli, pd->domain,
272 : "PAM SELinux", DPT_SELINUX, DPM_SELINUX_HANDLER,
273 : 0, pd, NULL);
274 0 : if (req == NULL) {
275 0 : DP_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->request_name,
276 : "Unable to process SELinux, killing request...");
277 0 : talloc_free(state->sbus_req);
278 0 : return;
279 : }
280 :
281 0 : tevent_req_set_callback(req, dp_pam_handler_selinux_done, state);
282 : }
283 :
284 0 : void dp_pam_handler_selinux_done(struct tevent_req *req)
285 : {
286 : struct dp_pam_handler_state *state;
287 : struct pam_data *pd;
288 : errno_t ret;
289 :
290 0 : state = tevent_req_callback_data(req, struct dp_pam_handler_state);
291 :
292 0 : ret = dp_req_recv(state, req, struct pam_data *, &pd);
293 0 : talloc_zfree(req);
294 0 : if (ret != EOK) {
295 0 : dp_req_reply_error(state->sbus_req, state->request_name, ret);
296 0 : return;
297 : }
298 :
299 : /* State and request related data are freed with sbus_req. */
300 0 : dp_pam_reply(state->sbus_req, state->request_name, pd);
301 0 : return;
302 : }
|