LCOV - code coverage report
Current view: top level - sbus - sssd_dbus_request.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 84 218 38.5 %
Date: 2015-10-19 Functions: 4 13 30.8 %

          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             : }

Generated by: LCOV version 1.10