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