LCOV - code coverage report
Current view: top level - sbus - sssd_dbus_request.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 84 242 34.7 %
Date: 2016-06-29 Functions: 4 14 28.6 %

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

Generated by: LCOV version 1.10