Line data Source code
1 : /*
2 : Authors:
3 : Stef Walter <stefw@redhat.com>
4 :
5 : Copyright (C) 2014 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 "util/util.h"
22 : #include "sbus/sssd_dbus.h"
23 : #include "sbus/sssd_dbus_private.h"
24 :
25 : #include <sys/time.h>
26 : #include <dbus/dbus.h>
27 :
28 : #define INTERNAL_ERROR "Internal Error"
29 :
30 0 : static int sbus_request_destructor(struct sbus_request *dbus_req)
31 : {
32 0 : dbus_message_unref(dbus_req->message);
33 0 : return 0;
34 : }
35 :
36 : struct sbus_request *
37 0 : sbus_new_request(struct sbus_connection *conn,
38 : struct sbus_interface *intf,
39 : DBusMessage *message)
40 : {
41 : struct sbus_request *dbus_req;
42 :
43 0 : dbus_req = talloc_zero(conn, struct sbus_request);
44 0 : if (!dbus_req) {
45 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory allocating DBus request\n");
46 0 : return NULL;
47 : }
48 :
49 0 : dbus_req->intf = intf;
50 0 : dbus_req->conn = conn;
51 0 : dbus_req->message = dbus_message_ref(message);
52 0 : dbus_req->path = dbus_message_get_path(message);
53 0 : talloc_set_destructor(dbus_req, sbus_request_destructor);
54 :
55 0 : return dbus_req;
56 : }
57 :
58 : void
59 0 : sbus_request_invoke_or_finish(struct sbus_request *dbus_req,
60 : sbus_msg_handler_fn handler_fn,
61 : void *handler_data,
62 : sbus_method_invoker_fn invoker_fn)
63 : {
64 : DBusError error;
65 : int ret;
66 :
67 0 : if (invoker_fn != NULL) {
68 0 : ret = invoker_fn(dbus_req, handler_fn);
69 0 : } else if (handler_fn != NULL) {
70 0 : ret = handler_fn(dbus_req, handler_data);
71 : } else {
72 0 : ret = EINVAL;
73 : }
74 :
75 0 : switch(ret) {
76 : case EOK:
77 0 : return;
78 : case ENOMEM:
79 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory handling DBus message\n");
80 0 : sbus_request_finish(dbus_req, NULL);
81 0 : break;
82 : default:
83 0 : dbus_error_init(&error);
84 0 : dbus_set_error_const(&error, DBUS_ERROR_FAILED, INTERNAL_ERROR);
85 0 : sbus_request_fail_and_finish(dbus_req, &error);
86 0 : break;
87 : }
88 : }
89 :
90 0 : int sbus_request_finish(struct sbus_request *dbus_req,
91 : DBusMessage *reply)
92 : {
93 0 : if (reply) {
94 0 : sbus_conn_send_reply(dbus_req->conn, reply);
95 : }
96 0 : return talloc_free(dbus_req);
97 : }
98 :
99 0 : int sbus_request_return_and_finish(struct sbus_request *dbus_req,
100 : int first_arg_type,
101 : ...)
102 : {
103 : DBusMessage *reply;
104 : dbus_bool_t dbret;
105 : va_list va;
106 : int ret;
107 :
108 0 : reply = dbus_message_new_method_return(dbus_req->message);
109 0 : if (!reply) {
110 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory allocating DBus message\n");
111 0 : sbus_request_finish(dbus_req, NULL);
112 0 : return ENOMEM;
113 : }
114 :
115 0 : va_start(va, first_arg_type);
116 0 : dbret = dbus_message_append_args_valist(reply, first_arg_type, va);
117 0 : va_end(va);
118 :
119 0 : if (dbret) {
120 0 : ret = sbus_request_finish(dbus_req, reply);
121 :
122 : } else {
123 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Couldn't build DBus message\n");
124 0 : sbus_request_finish(dbus_req, NULL);
125 0 : ret = EINVAL;
126 : }
127 :
128 0 : dbus_message_unref(reply);
129 0 : return ret;
130 : }
131 :
132 0 : int sbus_request_fail_and_finish(struct sbus_request *dbus_req,
133 : const DBusError *error)
134 : {
135 : DBusMessage *reply;
136 : int ret;
137 :
138 0 : if (error == NULL) {
139 0 : sbus_request_finish(dbus_req, NULL);
140 0 : return ENOMEM;
141 : }
142 :
143 0 : reply = dbus_message_new_error(dbus_req->message, error->name, error->message);
144 0 : if (!reply) {
145 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory allocating DBus message\n");
146 0 : sbus_request_finish(dbus_req, NULL);
147 0 : return ENOMEM;
148 : }
149 :
150 0 : ret = sbus_request_finish(dbus_req, reply);
151 0 : dbus_message_unref(reply);
152 0 : return ret;
153 : }
154 :
155 3 : DBusError *sbus_error_new(TALLOC_CTX *mem_ctx,
156 : const char *dbus_err_name,
157 : const char *fmt,
158 : ...)
159 : {
160 : DBusError *dberr;
161 3 : const char *err_msg_dup = NULL;
162 : va_list ap;
163 :
164 3 : dberr = talloc(mem_ctx, DBusError);
165 3 : if (dberr == NULL) return NULL;
166 :
167 3 : if (fmt) {
168 2 : va_start(ap, fmt);
169 2 : err_msg_dup = talloc_vasprintf(dberr, fmt, ap);
170 2 : va_end(ap);
171 2 : if (err_msg_dup == NULL) {
172 0 : talloc_free(dberr);
173 0 : return NULL;
174 : }
175 : }
176 :
177 3 : dbus_error_init(dberr);
178 3 : dbus_set_error_const(dberr, dbus_err_name, err_msg_dup);
179 3 : return dberr;
180 : }
181 :
182 : struct array_arg {
183 : char **dbus_array;
184 : };
185 :
186 0 : static int array_arg_destructor(struct array_arg *arg)
187 : {
188 0 : dbus_free_string_array(arg->dbus_array);
189 0 : return 0;
190 : }
191 :
192 : static bool
193 0 : parent_dbus_string_arrays(struct sbus_request *request, int first_arg_type,
194 : va_list va)
195 : {
196 : struct array_arg *array_arg;
197 : int arg_type;
198 : void **arg_ptr;
199 :
200 : /*
201 : * Here we iterate through the entire thing again and look for
202 : * things we need to fix allocation for. Normally certain types
203 : * returned from dbus_message_get_args() and friends require
204 : * later freeing. We tie those to the talloc context here.
205 : *
206 : * The list of argument has already been validated by the previous
207 : * dbus_message_get_args() call, so we can be cheap.
208 : */
209 :
210 0 : arg_type = first_arg_type;
211 0 : while (arg_type != DBUS_TYPE_INVALID) {
212 :
213 0 : if (arg_type == DBUS_TYPE_ARRAY) {
214 0 : arg_type = va_arg(va, int); /* the array element type */
215 0 : arg_ptr = va_arg(va, void **); /* the array elements */
216 0 : va_arg(va, int *); /* the array length */
217 :
218 : /* Arrays of these things need to be freed */
219 0 : if (arg_type == DBUS_TYPE_STRING ||
220 0 : arg_type == DBUS_TYPE_OBJECT_PATH ||
221 : arg_type == DBUS_TYPE_SIGNATURE) {
222 :
223 0 : array_arg = talloc_zero(request, struct array_arg);
224 0 : if (array_arg == NULL) {
225 : /* no kidding ... */
226 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory while trying not to leak memory\n");
227 0 : return false;
228 : }
229 :
230 0 : array_arg->dbus_array = *arg_ptr;
231 0 : talloc_set_destructor(array_arg, array_arg_destructor);
232 : }
233 :
234 : /* A non array argument */
235 : } else {
236 0 : arg_ptr = va_arg(va, void**);
237 : }
238 :
239 : /* The next type */
240 0 : arg_type = va_arg(va, int);
241 : }
242 :
243 0 : return true;
244 : }
245 :
246 : bool
247 0 : sbus_request_parse_or_finish(struct sbus_request *request,
248 : int first_arg_type,
249 : ...)
250 : {
251 0 : DBusError error = DBUS_ERROR_INIT;
252 0 : bool ret = true;
253 : va_list va2;
254 : va_list va;
255 :
256 0 : va_start(va, first_arg_type);
257 0 : va_copy(va2, va);
258 :
259 0 : if (dbus_message_get_args_valist(request->message, &error,
260 : first_arg_type, va)) {
261 0 : ret = parent_dbus_string_arrays(request, first_arg_type, va2);
262 :
263 : } else {
264 : /* Trying to send the error back to the caller in this case is a joke */
265 0 : if (!dbus_error_is_set(&error) &&
266 0 : dbus_error_has_name(&error, DBUS_ERROR_NO_MEMORY)) {
267 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory parsing DBus message\n");
268 0 : sbus_request_finish(request, NULL);
269 :
270 : /* Log other errors and send them back, this include o.f.d.InvalidArgs */
271 : } else {
272 0 : DEBUG(SSSDBG_OP_FAILURE, "Couldn't parse DBus message %s.%s: %s\n",
273 : dbus_message_get_interface(request->message),
274 : dbus_message_get_member(request->message),
275 : error.message);
276 0 : sbus_request_fail_and_finish(request, &error);
277 : }
278 :
279 0 : dbus_error_free(&error);
280 0 : ret = false;
281 : }
282 :
283 0 : va_end(va2);
284 0 : va_end(va);
285 :
286 0 : return ret;
287 : }
288 :
289 : struct sbus_get_sender_id_state {
290 : struct sbus_connection *conn;
291 : DBusConnection *sysbus_conn;
292 : char *sender;
293 : int64_t uid;
294 : };
295 :
296 : static void sbus_get_sender_id_done(DBusPendingCall *pending, void *ptr);
297 :
298 3 : struct tevent_req *sbus_get_sender_id_send(TALLOC_CTX *mem_ctx,
299 : struct tevent_context *ev,
300 : struct sbus_connection *conn,
301 : const char *sender)
302 : {
303 : struct tevent_req *req;
304 : struct sbus_get_sender_id_state *state;
305 : DBusError dbus_error;
306 3 : DBusMessage *msg = NULL;
307 : dbus_bool_t dbret;
308 : errno_t ret;
309 : hash_key_t key;
310 : hash_value_t value;
311 :
312 3 : req = tevent_req_create(mem_ctx, &state, struct sbus_get_sender_id_state);
313 3 : if (req == NULL) {
314 0 : return NULL;
315 : }
316 3 : state->conn = conn;
317 3 : state->uid = -1;
318 :
319 3 : if (conn->connection_type != SBUS_CONN_TYPE_SYSBUS) {
320 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Not a sysbus message, quit\n");
321 0 : ret = EOK;
322 0 : goto immediate;
323 : }
324 :
325 3 : if (sender == NULL) {
326 1 : ret = ERR_SBUS_NO_SENDER;
327 1 : goto immediate;
328 : }
329 :
330 2 : if (strcmp(sender, "org.freedesktop.DBus") == 0) {
331 0 : ret = ERR_SBUS_SENDER_BUS;
332 0 : goto immediate;
333 : }
334 :
335 2 : state->sender = talloc_strdup(state, sender);
336 2 : if (state->sender == NULL) {
337 0 : ret = ENOMEM;
338 0 : goto immediate;
339 : }
340 :
341 2 : DEBUG(SSSDBG_TRACE_INTERNAL,
342 : "Looking for identity of sender [%s]\n", sender);
343 :
344 2 : key.type = HASH_KEY_STRING;
345 2 : key.str = discard_const(sender);
346 2 : ret = hash_lookup(conn->clients, &key, &value);
347 2 : if (ret == HASH_SUCCESS) {
348 1 : DEBUG(SSSDBG_TRACE_INTERNAL,
349 : "%s already present in the clients table\n", sender);
350 1 : state->uid = (int64_t) value.ul;
351 1 : ret = EOK;
352 1 : goto immediate;
353 1 : } else if (ret != HASH_ERROR_KEY_NOT_FOUND) {
354 0 : DEBUG(SSSDBG_CRIT_FAILURE,
355 : "Failed to look up %s in the clients table\n", sender);
356 0 : ret = ERR_SBUS_GET_SENDER_ERROR;
357 0 : goto immediate;
358 : }
359 :
360 : /* We don't know this sender yet, let's ask the system bus */
361 :
362 : /* Connect to the well-known system bus */
363 1 : dbus_error_init(&dbus_error);
364 1 : state->sysbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error);
365 1 : if (state->sysbus_conn == NULL) {
366 0 : DEBUG(SSSDBG_CRIT_FAILURE,
367 : "Failed to connect to D-BUS system bus.\n");
368 0 : ret = ERR_SBUS_GET_SENDER_ERROR;
369 0 : goto immediate;
370 : }
371 1 : dbus_connection_set_exit_on_disconnect(state->sysbus_conn, FALSE);
372 :
373 : /* If we ever need to get the SELinux context or the PID here, we need
374 : * to call GetConnectionCredentials instead
375 : */
376 1 : msg = dbus_message_new_method_call("org.freedesktop.DBus", /* bus name */
377 : "/org/freedesktop/DBus", /* path */
378 : "org.freedesktop.DBus", /* interface */
379 : "GetConnectionUnixUser");
380 1 : if (msg == NULL) {
381 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
382 0 : ret = ENOMEM;
383 0 : goto immediate;
384 : }
385 :
386 1 : dbret = dbus_message_append_args(msg,
387 : DBUS_TYPE_STRING, &sender,
388 : DBUS_TYPE_INVALID);
389 1 : if (!dbret) {
390 0 : ret = ERR_INTERNAL;
391 0 : goto immediate;
392 : }
393 :
394 1 : ret = sss_dbus_conn_send(state->sysbus_conn, msg, 3000,
395 : sbus_get_sender_id_done,
396 : req, NULL);
397 1 : dbus_message_unref(msg);
398 1 : msg = NULL;
399 1 : if (ret != EOK) {
400 0 : goto immediate;
401 : }
402 :
403 1 : return req;
404 :
405 : immediate:
406 2 : if (ret == EOK) {
407 1 : tevent_req_done(req);
408 : } else {
409 1 : if (msg != NULL) {
410 0 : dbus_message_unref(msg);
411 : }
412 1 : if (state->sysbus_conn != NULL) {
413 0 : dbus_connection_unref(state->sysbus_conn);
414 : }
415 1 : tevent_req_error(req, ret);
416 : }
417 2 : tevent_req_post(req, ev);
418 2 : return req;
419 : }
420 :
421 1 : static void sbus_get_sender_id_done(DBusPendingCall *pending, void *ptr)
422 : {
423 : struct tevent_req *req;
424 : struct sbus_get_sender_id_state *state;
425 : DBusMessage *reply;
426 : DBusError dbus_error;
427 : hash_key_t key;
428 : hash_value_t value;
429 : dbus_bool_t dbret;
430 : int ret;
431 : uid_t uid;
432 :
433 1 : dbus_error_init(&dbus_error);
434 :
435 1 : req = talloc_get_type(ptr, struct tevent_req);
436 1 : state = tevent_req_data(req, struct sbus_get_sender_id_state);
437 :
438 1 : reply = dbus_pending_call_steal_reply(pending);
439 1 : if (!reply) {
440 : /* reply should never be null. This function shouldn't be called
441 : * until reply is valid or timeout has occurred. If reply is NULL
442 : * here, something is seriously wrong and we should bail out.
443 : */
444 0 : DEBUG(SSSDBG_CRIT_FAILURE,
445 : "Severe error. A reply callback was called but no reply "
446 : "was received and no timeout occurred\n");
447 :
448 0 : ret = EIO;
449 0 : goto done;
450 : }
451 :
452 1 : dbret = dbus_message_get_args(reply,
453 : &dbus_error,
454 : DBUS_TYPE_UINT32, &uid,
455 : DBUS_TYPE_INVALID);
456 1 : if (!dbret) {
457 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not parse reply!\n");
458 0 : ret = EIO;
459 0 : goto done;
460 : }
461 :
462 1 : state->uid = uid;
463 :
464 1 : key.type = HASH_KEY_STRING;
465 1 : key.str = talloc_steal(state->conn->clients, state->sender);
466 1 : value.type = HASH_VALUE_UINT;
467 1 : value.ul = state->uid;
468 1 : ret = hash_enter(state->conn->clients, &key, &value);
469 1 : if (ret != HASH_SUCCESS) {
470 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not add key to hash table!\n");
471 0 : ret = EIO;
472 0 : goto done;
473 : }
474 :
475 1 : ret = EOK;
476 : done:
477 1 : dbus_pending_call_unref(pending);
478 1 : dbus_message_unref(reply);
479 1 : dbus_connection_unref(state->sysbus_conn);
480 1 : if (ret != EOK) {
481 0 : tevent_req_error(req, ret);
482 : } else {
483 1 : tevent_req_done(req);
484 : }
485 1 : }
486 :
487 2 : int sbus_get_sender_id_recv(struct tevent_req *req, int64_t *_uid)
488 : {
489 2 : struct sbus_get_sender_id_state *state = \
490 2 : tevent_req_data(req, struct sbus_get_sender_id_state);
491 :
492 3 : TEVENT_REQ_RETURN_ON_ERROR(req);
493 :
494 1 : if (_uid) {
495 1 : *_uid = state->uid;
496 : }
497 :
498 1 : return EOK;
499 : }
|