summaryrefslogtreecommitdiff
path: root/server/infopipe
diff options
context:
space:
mode:
authorStephen Gallagher <sgallagh@redhat.com>2009-03-02 09:35:06 -0500
committerSimo Sorce <ssorce@redhat.com>2009-03-02 09:47:54 -0500
commit5e3966c99180abdcd1e21774a882f1c14c47aae8 (patch)
tree1a882b9d49f2c7ac25d70f0bbb644b5cefed6439 /server/infopipe
parent6f5b1ad8c234d58041e45aef4558d50f44d7f021 (diff)
downloadsssd-5e3966c99180abdcd1e21774a882f1c14c47aae8.tar.gz
sssd-5e3966c99180abdcd1e21774a882f1c14c47aae8.tar.bz2
sssd-5e3966c99180abdcd1e21774a882f1c14c47aae8.zip
Implement GetUserAttributes in the InfoPipe
This patch adds support for requesting user data in the sysdb via the InfoPipe. It currently has support for reading defined entries of integral, floating-point or string types. Tasks remaining: 1) Implement call to the provider when cache is out of date 2) Support byte arrays for userpic and similar I modified sysdb_search_ctx in sysdb_search.c to accept an array of attributes to pass into the LDB search. I also made one additional related fix: the btreemap now sorts in the correct order. Previously I had accidentally transposed the two values for sorting, so the map would always have been in exact reverse order.
Diffstat (limited to 'server/infopipe')
-rw-r--r--server/infopipe/infopipe.c100
-rw-r--r--server/infopipe/infopipe_private.h30
-rw-r--r--server/infopipe/infopipe_users.c666
-rw-r--r--server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml5
-rw-r--r--server/infopipe/sysbus.c29
-rw-r--r--server/infopipe/sysbus.h2
6 files changed, 779 insertions, 53 deletions
diff --git a/server/infopipe/infopipe.c b/server/infopipe/infopipe.c
index 4b1eb920..035bd4b4 100644
--- a/server/infopipe/infopipe.c
+++ b/server/infopipe/infopipe.c
@@ -26,6 +26,8 @@
#include "util/btreemap.h"
#include "sbus/sssd_dbus.h"
#include "sbus/sbus_client.h"
+#include "db/sysdb.h"
+#include "confdb/confdb.h"
#include "monitor/monitor_sbus.h"
#include "monitor/monitor_interfaces.h"
#include "infopipe/sysbus.h"
@@ -264,6 +266,7 @@ static int infp_process_init(TALLOC_CTX *mem_ctx,
ret = infp_monitor_init(infp_ctx);
if (ret != EOK) {
DEBUG(0, ("Fatal error setting up monitor bus\n"));
+ talloc_free(infp_ctx);
return EIO;
}
@@ -274,18 +277,48 @@ static int infp_process_init(TALLOC_CTX *mem_ctx,
infp_methods, infp_introspect);
if (ret != EOK) {
DEBUG(0, ("Failed to connect to the system message bus\n"));
+ talloc_free(infp_ctx);
return EIO;
}
+ /* Connect to the sysdb */
+ ret = sysdb_init(infp_ctx, infp_ctx->ev, infp_ctx->cdb,
+ NULL, &infp_ctx->sysdb);
+ if (ret != EOK) {
+ DEBUG(0, ("Failed to connect to the cache database\n"));
+ talloc_free(infp_ctx);
+ return EIO;
+ }
+
+ /* Read in the domain map */
+ ret = confdb_get_domains(cdb, infp_ctx, &infp_ctx->domain_map);
+ if (ret != EOK) {
+ DEBUG(0, ("Failed to populate the domain map\n"));
+ talloc_free(infp_ctx);
+ return EIO;
+ }
+
+ if (infp_ctx->domain_map == NULL) {
+ /* No domains configured!
+ * Note: this should never happen, since LOCAL
+ * should always be configured
+ */
+ DEBUG(0, ("No domains configured on this client!\n"));
+ talloc_free(infp_ctx);
+ return EIO;
+ }
+
+ infp_ctx->cache_timeout = 600; /* FIXME: read from confdb */
+
/* Add the infp_ctx to the sbus_conn_ctx private data
* so we can pass it into message handler functions
*/
sbus_conn_set_private_data(sysbus_get_sbus_conn(infp_ctx->sysbus), infp_ctx);
- return ret;
+ return EOK;
}
-int get_object_type(const char *obj)
+int infp_get_object_type(const char *obj)
{
int object_type = INFP_OBJ_TYPE_INVALID;
@@ -297,11 +330,13 @@ int get_object_type(const char *obj)
return object_type;
}
-int get_action_type(const char *action)
+int infp_get_action_type(const char *action)
{
int action_type = INFP_ACTION_TYPE_INVALID;
- if (strcasecmp(action, "create") == 0)
+ if (strcasecmp(action, "read") == 0)
+ action_type = INFP_ACTION_TYPE_READ;
+ else if (strcasecmp(action, "create") == 0)
action_type = INFP_ACTION_TYPE_CREATE;
else if ((strcasecmp(action, "delete") == 0))
action_type = INFP_ACTION_TYPE_DELETE;
@@ -315,7 +350,7 @@ int get_action_type(const char *action)
return action_type;
}
-int get_attribute_type(const char *attribute)
+int infp_get_attribute_type(const char *attribute)
{
int attribute_type = INFP_ATTR_TYPE_INVALID;
@@ -353,7 +388,7 @@ int get_attribute_type(const char *attribute)
}
bool infp_get_permissions(const char *username,
- const char *domain,
+ struct sss_domain_info *domain,
int object_type,
const char *instance,
int action_type,
@@ -369,6 +404,11 @@ bool infp_get_permissions(const char *username,
return false;
}
+struct sss_domain_info *infp_get_domain_obj(struct infp_ctx *infp, const char *domain_name)
+{
+ return talloc_get_type(btreemap_get_value(infp->domain_map, (const void *) domain_name), struct sss_domain_info);
+}
+
/* CheckPermissions(STRING domain, STRING object, STRING instance
* ARRAY(STRING action_type, STRING attribute) actions)
*/
@@ -376,18 +416,16 @@ int infp_check_permissions(DBusMessage *message, struct sbus_conn_ctx *sconn)
{
DBusMessage *reply;
TALLOC_CTX *tmp_ctx;
+ struct infp_ctx *infp;
int current_type;
- DBusConnection *conn;
- const char *conn_name;
- uid_t uid;
- char *username;
+ char *caller;
DBusMessageIter iter;
DBusMessageIter action_array_iter;
DBusMessageIter action_struct_iter;
- DBusError error;
int object_type;
const char *einval_msg;
- const char *domain;
+ const char *domain_name;
+ struct sss_domain_info *domain;
const char *object;
const char *instance;
const char *action;
@@ -401,26 +439,11 @@ int infp_check_permissions(DBusMessage *message, struct sbus_conn_ctx *sconn)
return ENOMEM;
}
- /* Get the connection UID */
- conn = sbus_get_connection(sconn);
- conn_name = dbus_message_get_sender(message);
- if (conn_name == NULL) {
- DEBUG(0, ("Critical error: D-BUS client has no unique name\n"));
- talloc_free(tmp_ctx);
- return EIO;
- }
- dbus_error_init(&error);
- uid = dbus_bus_get_unix_user(conn, conn_name, &error);
- if (uid == -1) {
- DEBUG(0, ("Could not identify unix user. Error message was '%s:%s'\n", error.name, error.message));
- dbus_error_free(&error);
- talloc_free(tmp_ctx);
- return EIO;
- }
- username = get_username_from_uid(tmp_ctx, uid);
- if (username == NULL) {
- DEBUG(0, ("No username matched the connected UID\n"));
- talloc_free(tmp_ctx);
+ infp = talloc_get_type(sbus_conn_get_private_data(sconn), struct infp_ctx);
+
+ /* Get the caller */
+ caller = sysbus_get_caller(tmp_ctx, message, sconn);
+ if (caller == NULL) {
return EIO;
}
@@ -435,8 +458,9 @@ int infp_check_permissions(DBusMessage *message, struct sbus_conn_ctx *sconn)
einval_msg = talloc_strdup(tmp_ctx, "Expected domain");
goto einval;
}
- dbus_message_iter_get_basic(&iter, &domain);
- DEBUG(9, ("Domain: %s\n", domain));
+ dbus_message_iter_get_basic(&iter, &domain_name);
+ DEBUG(9, ("Domain: %s\n", domain_name));
+ domain = infp_get_domain_obj(infp, domain_name);
/* Object */
dbus_message_iter_next(&iter);
@@ -447,7 +471,7 @@ int infp_check_permissions(DBusMessage *message, struct sbus_conn_ctx *sconn)
}
dbus_message_iter_get_basic(&iter, &object);
DEBUG(9, ("Object: %s\n", object));
- object_type = get_object_type(object);
+ object_type = infp_get_object_type(object);
if (object_type == INFP_OBJ_TYPE_INVALID) {
einval_msg = talloc_strdup(tmp_ctx, "Invalid object type");
goto einval;
@@ -490,7 +514,7 @@ int infp_check_permissions(DBusMessage *message, struct sbus_conn_ctx *sconn)
}
dbus_message_iter_get_basic(&action_struct_iter, &action);
DEBUG(9, ("Action type: %s\n", action));
- action_type = get_action_type(action);
+ action_type = infp_get_action_type(action);
if(action_type == INFP_ACTION_TYPE_INVALID) {
einval_msg = talloc_asprintf(tmp_ctx, "Action type [%s] is not valid", action);
goto einval;
@@ -504,7 +528,7 @@ int infp_check_permissions(DBusMessage *message, struct sbus_conn_ctx *sconn)
}
dbus_message_iter_get_basic(&action_struct_iter, &attribute);
DEBUG(9, ("Action attribute: %s\n", attribute));
- attribute_type = get_attribute_type(attribute);
+ attribute_type = infp_get_attribute_type(attribute);
if(attribute_type == INFP_ATTR_TYPE_INVALID) {
einval_msg = talloc_asprintf(tmp_ctx, "Attribute [%s] is not valid", attribute);
goto einval;
@@ -518,7 +542,7 @@ int infp_check_permissions(DBusMessage *message, struct sbus_conn_ctx *sconn)
/* Process the actions */
count++;
permissions=talloc_realloc(tmp_ctx, permissions, dbus_bool_t, count);
- permissions[count-1] = infp_get_permissions(username, domain,
+ permissions[count-1] = infp_get_permissions(caller, domain,
object_type, instance,
action_type, attribute_type);
diff --git a/server/infopipe/infopipe_private.h b/server/infopipe/infopipe_private.h
index 8ea2586b..f1d6694a 100644
--- a/server/infopipe/infopipe_private.h
+++ b/server/infopipe/infopipe_private.h
@@ -27,27 +27,39 @@ struct infp_ctx {
struct confdb_ctx *cdb;
struct service_sbus_ctx *ss_ctx;
struct sysbus_ctx *sysbus;
+ struct sysdb_ctx *sysdb;
+ struct btreemap *domain_map;
char *introspect_xml;
+
+ int cache_timeout;
+};
+
+struct infp_req_ctx {
+ struct infp_ctx *infp;
+ struct sbus_conn_ctx *sconn;
+ DBusMessage *req_message;
+ bool check_provider;
};
-enum object_types {
+enum infp_object_types {
INFP_OBJ_TYPE_INVALID = 0,
INFP_OBJ_TYPE_USER,
INFP_OBJ_TYPE_GROUP
};
-int get_object_type(const char *obj);
+int infp_get_object_type(const char *obj);
-enum action_types {
+enum infp_action_types {
INFP_ACTION_TYPE_INVALID = 0,
+ INFP_ACTION_TYPE_READ,
INFP_ACTION_TYPE_CREATE,
INFP_ACTION_TYPE_DELETE,
INFP_ACTION_TYPE_MODIFY,
INFP_ACTION_TYPE_ADDMEMBER,
INFP_ACTION_TYPE_REMOVEMEMBER
};
-int get_action_type(const char *action);
+int infp_get_action_type(const char *action);
-enum attribute_types {
+enum infp_attribute_types {
INFP_ATTR_TYPE_INVALID = 0,
INFP_ATTR_TYPE_DEFAULTGROUP,
INFP_ATTR_TYPE_GECOS,
@@ -60,13 +72,17 @@ enum attribute_types {
INFP_ATTR_TYPE_LAST_LOGIN,
INFP_ATTR_TYPE_USERPIC
};
-int get_attribute_type(const char *attribute);
+int infp_get_attribute_type(const char *attribute);
+
+int infp_get_user_attr_dbus_type(int attr_type, int *subtype);
bool infp_get_permissions(const char *username,
- const char *domain,
+ struct sss_domain_info *domain,
int object_type,
const char *instance,
int action_type,
int action_attribute);
+struct sss_domain_info *infp_get_domain_obj(struct infp_ctx *infp, const char *domain_name);
+
#endif /* INFOPIPE_PRIVATE_H_ */
diff --git a/server/infopipe/infopipe_users.c b/server/infopipe/infopipe_users.c
index 2c107bc4..82889fa8 100644
--- a/server/infopipe/infopipe_users.c
+++ b/server/infopipe/infopipe_users.c
@@ -19,8 +19,17 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <dbus/dbus.h>
+#include <ldb.h>
+#include <time.h>
#include "util/util.h"
-#include "infopipe.h"
+#include "util/btreemap.h"
+#include "confdb/confdb.h"
+#include "infopipe/infopipe.h"
+#include "infopipe/infopipe_private.h"
+#include "infopipe/sysbus.h"
+#include "db/sysdb.h"
+
+static int attr_comparator(const void *key1, const void *key2);
int infp_users_get_cached(DBusMessage *message, struct sbus_conn_ctx *sconn)
{
@@ -61,19 +70,664 @@ int infp_users_delete(DBusMessage *message, struct sbus_conn_ctx *sconn)
return EOK;
}
-int infp_users_get_attr(DBusMessage *message, struct sbus_conn_ctx *sconn)
+struct infp_getattr_ctx {
+ char *caller;
+ struct sss_domain_info *domain;
+ struct infp_req_ctx *infp_req;
+ char **usernames;
+ uint32_t username_count;
+ char **attributes;
+ uint32_t attr_count;
+ uint32_t index;
+ bool check_provider;
+
+ /* The results array must have username_count elements */
+ struct btreemap **results;
+};
+
+static int infp_get_attr_lookup(struct infp_getattr_ctx *infp_getattr_req);
+
+struct infp_attr_variant {
+ int dbus_type;
+ int subtype;
+ int count;
+ void *data;
+};
+
+/* We are restricting variants to three basic types:
+ * Fixed (Numeric) types
+ * Strings
+ * Arrays of fixed (numeric) types
+ */
+static int infp_user_getattr_append_dict(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct btreemap *map)
+{
+ int ret, i;
+ char **attrs;
+ struct infp_attr_variant *value;
+ char *vartype;
+ char *subtype;
+ int attr_count;
+ DBusMessageIter array_iter;
+ DBusMessageIter dict_iter;
+ DBusMessageIter variant_iter;
+ dbus_bool_t dbret;
+
+ ret = btreemap_get_keys(mem_ctx, map, (const void ***)&attrs, &attr_count);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* DICTs are an array of dict pairs */
+ dbret = dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &array_iter);
+ if (!dbret) {
+ ret = ENOMEM;
+ goto error;
+ }
+
+ i = 0;
+ while (i < attr_count) {
+ if (strcasecmp(attrs[i], SYSDB_LAST_UPDATE) == 0) {
+ /* Skip lastUpdate. We shouldn't be returning this */
+ i++;
+ continue;
+ }
+
+ /* Create the variant value */
+ value = talloc_get_type(btreemap_get_value(map, attrs[i]), struct infp_attr_variant);
+ if (value == NULL) {
+ /* Skip any entries that returned an empty value */
+ i++;
+ continue;
+ }
+
+ /* Open a dict container for this pair */
+ dbret = dbus_message_iter_open_container(&array_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_iter);
+ if (!dbret) {
+ ret = ENOMEM;
+ goto error;
+ }
+ /* Write the dict key */
+ dbret = dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &attrs[i]);
+ if (!dbret) {
+ ret = ENOMEM;
+ goto error;
+ }
+
+ vartype = NULL;
+ subtype = NULL;
+ if (sbus_is_dbus_string_type(value->dbus_type)) {
+ /* String types are strings, object paths and signatures */
+ vartype = talloc_asprintf(mem_ctx, "%c", value->dbus_type);
+ if (vartype == NULL) {
+ ret = ENOMEM;
+ goto error;
+ }
+ dbret = dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, vartype, &variant_iter);
+ if (!dbret) {
+ ret = ENOMEM;
+ goto error;
+ }
+ dbret = dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value->data);
+ if (!dbret) {
+ ret = ENOMEM;
+ goto error;
+ }
+ talloc_free(vartype);
+ vartype = NULL;
+ }
+
+ else if (sbus_is_dbus_fixed_type(value->dbus_type)) {
+ /* Fixed types are booleans, bytes, the integral types and the floating-point types */
+ vartype = talloc_asprintf(mem_ctx, "%c", value->dbus_type);
+ if (vartype == NULL) {
+ ret = ENOMEM;
+ goto error;
+ }
+ dbret = dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, vartype, &variant_iter);
+ if (!dbret) {
+ ret = ENOMEM;
+ goto error;
+ }
+ dbret = dbus_message_iter_append_basic(&variant_iter, value->dbus_type, value->data);
+ if (!dbret) {
+ ret = ENOMEM;
+ goto error;
+ }
+ talloc_free(vartype);
+ vartype = NULL;
+ }
+ /* FIXME: Need to support byte arrays for userpic and similar */
+
+ else {
+ /* Value type not yet supported */
+ DEBUG(0, ("Attempted to create DICT value for something not a basic type or fixed array [%d]\n", value->dbus_type));
+ ret = EINVAL;
+ goto error;
+ }
+
+ /* Close the variant */
+ dbret = dbus_message_iter_close_container(&dict_iter, &variant_iter);
+ if(!dbret) {
+ ret = ENOMEM;
+ goto error;
+ }
+
+ /* Close the dict */
+ dbret = dbus_message_iter_close_container(&array_iter, &dict_iter);
+ if(!dbret) {
+ ret = ENOMEM;
+ goto error;
+ }
+ i++;
+ }
+
+ /* Close the dict array */
+ dbret = dbus_message_iter_close_container(iter, &array_iter);
+ if(!dbret) {
+ ret = ENOMEM;
+ goto error;
+ }
+
+ return EOK;
+
+error:
+ talloc_free(attrs);
+ talloc_free(vartype);
+ talloc_free(subtype);
+ return ret;
+}
+
+static int create_getattr_result_map(TALLOC_CTX *mem_ctx, struct infp_getattr_ctx *infp_getattr_req,
+ struct ldb_result *res, struct btreemap **results)
+{
+ int i, ret;
+ int attr_type, subtype;
+ struct infp_attr_variant *variant;
+ const char *tmp_string;
+
+ /* Iterate through the requested attributes */
+ for (i=0; i < infp_getattr_req->attr_count; i++) {
+ /* Ignore any attributes we don't care about */
+ attr_type = infp_get_attribute_type(infp_getattr_req->attributes[i]);
+ if (attr_type != INFP_ATTR_TYPE_INVALID) {
+ variant = talloc_zero(mem_ctx, struct infp_attr_variant);
+ if (variant == NULL) {
+ ret = ENOMEM;
+ goto end;
+ }
+ variant->dbus_type = infp_get_user_attr_dbus_type(attr_type, &subtype);
+ switch (variant->dbus_type) {
+ case DBUS_TYPE_STRING:
+ tmp_string = ldb_msg_find_attr_as_string(res->msgs[0], infp_getattr_req->attributes[i], NULL);
+ if (tmp_string == NULL) {
+ /* Attribute was not found in the result list */
+ talloc_free(variant);
+ continue;
+ }
+ variant->data = (void *)talloc_strdup(variant, tmp_string);
+ if (variant->data == NULL) {
+ talloc_free(variant);
+ continue;
+ }
+ break;
+
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_BYTE:
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ /* We'll treat all integral types as UINT64 internally
+ * These will be correctly converted to their true types
+ * when being marshalled on the wire.
+ */
+ variant->data = (void *)talloc(variant, uint64_t);
+ if (variant->data == NULL) {
+ talloc_free(variant);
+ continue;
+ }
+
+ *(uint64_t *)variant->data = ldb_msg_find_attr_as_uint64(res->msgs[0], infp_getattr_req->attributes[i], 0);
+ break;
+
+ default:
+ talloc_free(variant);
+ continue;
+ }
+
+ /* Add the variant to the map */
+ ret = btreemap_set_value(mem_ctx, results, (const void *)infp_getattr_req->attributes[i], variant, attr_comparator);
+ if (ret != EOK) {
+ talloc_free(variant);
+ continue;
+ }
+ }
+ }
+
+ ret = EOK;
+
+end:
+ return ret;
+}
+
+static void infp_get_attr_lookup_callback(void *ptr, int ldb_status, struct ldb_result *res)
{
+ int ret;
+ int i;
+ bool call_provider = false;
+ int timeout;
+ uint64_t lastUpdate;
DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ struct infp_getattr_ctx *infp_getattr_req = talloc_get_type(ptr, struct infp_getattr_ctx);
- reply = dbus_message_new_error(message, DBUS_ERROR_NOT_SUPPORTED, "Not yet implemented");
+ DEBUG(9, ("Processing results for user [%s]\n", infp_getattr_req->usernames[infp_getattr_req->index]));
- /* send reply */
- sbus_conn_send_reply(sconn, reply);
+ /* Process the current results */
+ if (ldb_status != LDB_SUCCESS) {
+ DEBUG(0, ("Critical error reading from sysdb.\n"));
+ goto done;
+ }
+
+ if(infp_getattr_req->check_provider) {
+ switch(res->count) {
+ case 0:
+ call_provider = true;
+ break;
+
+ case 1:
+ timeout = infp_getattr_req->infp_req->infp->cache_timeout;
+ lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0],
+ SYSDB_LAST_UPDATE, 0);
+ if (lastUpdate + timeout < time(NULL)) {
+ call_provider = true;
+ }
+ break;
+
+ default:
+ DEBUG(0, ("GetUser call returned more than one result. This probably means the sysdb is corrupt!\n"));
+ goto done;
+ }
+ }
+
+ if (call_provider) {
+ /* FIXME call the provider */
+ }
+
+ switch (res->count) {
+ case 0:
+ DEBUG(2, ("No results for GetUser"));
+ infp_getattr_req->results[infp_getattr_req->index] = NULL;
+ break;
+
+ case 1:
+ /* Create the result map */
+ ret = create_getattr_result_map(infp_getattr_req, infp_getattr_req, res,
+ &infp_getattr_req->results[infp_getattr_req->index]);
+ if (ret != EOK) {
+ DEBUG(0, ("Unable to create result map!\n"));
+ goto done;
+ }
+ break;
+ default:
+ /* We received more than one result. This is bad */
+ DEBUG(0, ("GetUser call returned more than one result. This probably means the sysdb is corrupt!\n"));
+ goto done;
+ }
+
+ /* If there are more usernames remaining in the list, re-enter the loop */
+ infp_getattr_req->index++;
+ if (infp_getattr_req->index < infp_getattr_req->username_count) {
+ ret = infp_get_attr_lookup(infp_getattr_req);
+ if (ret != EOK) {
+ DEBUG(0, ("Could not read from cache database\n"));
+ goto done;
+ }
+ return;
+ }
+
+ /* No more names remain, return the result DICTs */
+ reply = dbus_message_new_method_return(infp_getattr_req->infp_req->req_message);
+ if (reply == NULL) {
+ goto done;
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "a{sv}", &array_iter);
+ i = 0;
+ while (i < infp_getattr_req->username_count) {
+ ret = infp_user_getattr_append_dict(infp_getattr_req, &array_iter, infp_getattr_req->results[i]);
+ if (ret != EOK) {
+ DEBUG(0, ("Unable to append response DICT\n"));
+ goto done;
+ }
+ i++;
+ }
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ sbus_conn_send_reply(infp_getattr_req->infp_req->sconn, reply);
+
+done:
+ talloc_free(infp_getattr_req);
+}
+
+int infp_get_user_attr_dbus_type(int attr_type, int *subtype)
+{
+ int dbus_type;
+
+ switch(attr_type) {
+ case INFP_ATTR_TYPE_DEFAULTGROUP:
+ case INFP_ATTR_TYPE_GECOS:
+ case INFP_ATTR_TYPE_HOMEDIR:
+ case INFP_ATTR_TYPE_SHELL:
+ case INFP_ATTR_TYPE_FULLNAME:
+ case INFP_ATTR_TYPE_LOCALE:
+ case INFP_ATTR_TYPE_KEYBOARD:
+ case INFP_ATTR_TYPE_SESSION:
+ dbus_type = DBUS_TYPE_STRING;
+ break;
+ case INFP_ATTR_TYPE_LAST_LOGIN:
+ dbus_type = DBUS_TYPE_UINT64;
+ break;
+ case INFP_ATTR_TYPE_USERPIC:
+ dbus_type = DBUS_TYPE_ARRAY;
+ *subtype = DBUS_TYPE_BYTE;
+ break;
+ default:
+ dbus_type = DBUS_TYPE_INVALID;
+ }
+ return dbus_type;
+}
+
+static int attr_comparator(const void *key1, const void *key2)
+{
+ return strcmp((const char *)key1, (const char *)key2);
+}
+
+static int infp_get_attr_lookup(struct infp_getattr_ctx *infp_getattr_req)
+{
+ uint32_t i;
+ int ret;
+ char **attributes;
+ const char *last_update;
+ int attr_count;
+
+ DEBUG(9, ("Processing lookup for user [%s]\n", infp_getattr_req->usernames[infp_getattr_req->index]));
+
+ if (infp_getattr_req->index >= infp_getattr_req->username_count) {
+ /* Avoid index bound issues */
+ return EINVAL;
+ }
+
+ /* Check permissions */
+ i=0;
+ infp_getattr_req->results[infp_getattr_req->index] = NULL;
+ while(i < infp_getattr_req->attr_count) {
+ if(infp_get_permissions(infp_getattr_req->caller,
+ infp_getattr_req->domain,
+ INFP_OBJ_TYPE_USER,
+ infp_getattr_req->usernames[infp_getattr_req->index],
+ INFP_ACTION_TYPE_READ,
+ infp_get_attribute_type(infp_getattr_req->attributes[i]))
+ ) {
+ /* Add this attribute as a key to the result map
+ * This will guarantee that we are requesting only unique attributes
+ * that we have permission to read
+ */
+ ret = btreemap_set_value(infp_getattr_req, &infp_getattr_req->results[infp_getattr_req->index],
+ infp_getattr_req->attributes[i], NULL, attr_comparator);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+ i++;
+ }
+
+ /* Always add SYSDB_LAST_UPDATE to the list, we won't return it */
+ last_update = talloc_strdup(infp_getattr_req, SYSDB_LAST_UPDATE);
+ ret = btreemap_set_value(infp_getattr_req, &infp_getattr_req->results[infp_getattr_req->index],
+ last_update, NULL, attr_comparator);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* Prepare the list of attributes to request from the sysdb */
+ attr_count = 0;
+ ret = btreemap_get_keys(infp_getattr_req,
+ infp_getattr_req->results[infp_getattr_req->index],
+ (const void ***)&attributes, &attr_count);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (attr_count == 1) {
+ /* There were zero authorized attributes in the list
+ * No need to call sysdb, just move to the next username
+ * The single attribute was SYSDB_LAST_UPDATE which we
+ * added manually.
+ */
+ infp_getattr_req->index++;
+ return infp_get_attr_lookup(infp_getattr_req);
+ }
+
+ /* Add a trailing NULL entry (required for sysdb) */
+ attributes = talloc_realloc(infp_getattr_req, attributes, char *, attr_count+1);
+ if (attributes == NULL) {
+ return ENOMEM;
+ }
+ attributes[attr_count] = NULL;
+
+ /* Call into the sysdb for the requested attributes */
+ ret = sysdb_get_user_attr(infp_getattr_req,
+ infp_getattr_req->infp_req->infp->sysdb,
+ infp_getattr_req->domain,
+ infp_getattr_req->usernames[infp_getattr_req->index],
+ (const char **)attributes,
+ infp_get_attr_lookup_callback, infp_getattr_req);
- dbus_message_unref(reply);
return EOK;
}
+static char **infp_get_all_attributes(TALLOC_CTX *mem_ctx, uint32_t *attr_count)
+{
+ char **attributes;
+ int offset = 0;
+
+ *attr_count = 10;
+ attributes = talloc_array(mem_ctx, char *, *attr_count);
+ if (attributes == NULL) {
+ return NULL;
+ }
+
+ attributes[offset++] = talloc_strdup(attributes, SYSDB_USER_ATTR_DEFAULTGROUP);
+ if(!attributes[offset]) goto error;
+
+ attributes[offset++] = talloc_strdup(attributes, SYSDB_USER_ATTR_GECOS);
+ if(!attributes[offset]) goto error;
+
+ attributes[offset++] = talloc_strdup(attributes, SYSDB_USER_ATTR_HOMEDIR);
+ if(!attributes[offset]) goto error;
+
+ attributes[offset++] = talloc_strdup(attributes, SYSDB_USER_ATTR_SHELL);
+ if(!attributes[offset]) goto error;
+
+ attributes[offset++] = talloc_strdup(attributes, SYSDB_USER_ATTR_FULLNAME);
+ if(!attributes[offset]) goto error;
+
+ attributes[offset++] = talloc_strdup(attributes, SYSDB_USER_ATTR_LOCALE);
+ if(!attributes[offset]) goto error;
+
+ attributes[offset++] = talloc_strdup(attributes, SYSDB_USER_ATTR_KEYBOARD);
+ if(!attributes[offset]) goto error;
+
+ attributes[offset++] = talloc_strdup(attributes, SYSDB_USER_ATTR_SESSION);
+ if(!attributes[offset]) goto error;
+
+ attributes[offset++] = talloc_strdup(attributes, SYSDB_USER_ATTR_LAST_LOGIN);
+ if(!attributes[offset]) goto error;
+
+ attributes[offset++] = talloc_strdup(attributes, SYSDB_USER_ATTR_USERPIC);
+ if(!attributes[offset]) goto error;
+
+ return attributes;
+
+error:
+ talloc_free(attributes);
+ *attr_count = 0;
+ return NULL;
+}
+
+/* GetUserAttributes(ARRAY(STRING) usernames,
+ * STRING domain,
+ * ARRAY(STRING) filter)
+ */
+int infp_users_get_attr(DBusMessage *message, struct sbus_conn_ctx *sconn)
+{
+ int ret, i;
+ DBusMessage *reply;
+ DBusError error;
+ dbus_bool_t dbret;
+ char **usernames;
+ uint32_t username_count;
+ char *domain;
+ char **attributes;
+ uint32_t attr_count;
+ struct infp_getattr_ctx *infp_getattr_req;
+
+ usernames = NULL;
+ attributes = NULL;
+ /* Get the arguments to GetAttributes */
+ dbus_error_init(&error);
+ dbret = dbus_message_get_args(message, &error,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &usernames, &username_count,
+ DBUS_TYPE_STRING, &domain,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &attributes, &attr_count,
+ DBUS_TYPE_INVALID);
+ if(!dbret) {
+ DEBUG(0, ("Parsing arguments failed: %s:%s\n", error.name, error.message));
+ dbus_free_string_array(usernames);
+ dbus_free_string_array(attributes);
+
+ reply = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, error.message);
+ sbus_conn_send_reply(sconn, reply);
+
+ dbus_message_unref(reply);
+ dbus_error_free(&error);
+
+ return EOK;
+ }
+
+ if (username_count < 1) {
+ /* No usernames received. Return an error */
+ reply = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "No usernames specified.");
+ sbus_conn_send_reply(sconn, reply);
+
+ dbus_free_string_array(usernames);
+ dbus_free_string_array(attributes);
+ dbus_message_unref(reply);
+ ret = EOK;
+ }
+
+ /* Create a infp_getattr_ctx */
+ infp_getattr_req = talloc_zero(NULL, struct infp_getattr_ctx);
+ if (infp_getattr_req == NULL) {
+ ret = ENOMEM;
+ goto end;
+ }
+
+ /* Create an infp_req_ctx */
+ infp_getattr_req->infp_req = talloc_zero(infp_getattr_req, struct infp_req_ctx);
+ if (infp_getattr_req == NULL) {
+ ret = ENOMEM;
+ goto end;
+ }
+ infp_getattr_req->infp_req->infp = talloc_get_type(sbus_conn_get_private_data(sconn), struct infp_ctx);
+ infp_getattr_req->infp_req->sconn = sconn;
+ infp_getattr_req->infp_req->req_message = message;
+ infp_getattr_req->domain = btreemap_get_value(infp_getattr_req->infp_req->infp->domain_map, (const void *)domain);
+ infp_getattr_req->check_provider = strcasecmp(domain, "LOCAL");
+
+ /* Copy the username list */
+ infp_getattr_req->usernames = talloc_array(infp_getattr_req, char *, username_count);
+ if (infp_getattr_req->usernames == NULL) {
+ ret = ENOMEM;
+ goto end;
+ }
+
+ i = 0;
+ while (i < username_count) {
+ DEBUG(9, ("Request for user [%s]\n", usernames[i]));
+ infp_getattr_req->usernames[i] = talloc_strdup(infp_getattr_req->usernames, usernames[i]);
+ if (infp_getattr_req == NULL) {
+ ret = ENOMEM;
+ goto end;
+ }
+ i++;
+ }
+ infp_getattr_req->username_count = username_count;
+
+ /* Copy the attribute list */
+ if (attr_count > 0) {
+ infp_getattr_req->attributes = talloc_array(infp_getattr_req, char *, attr_count);
+ if (infp_getattr_req->attributes == NULL) {
+ ret = ENOMEM;
+ goto end;
+ }
+ i = 0;
+ while (i < attr_count) {
+ infp_getattr_req->attributes[i] = talloc_strdup(infp_getattr_req, attributes[i]);
+ if (infp_getattr_req == NULL) {
+ ret = ENOMEM;
+ goto end;
+ }
+ i++;
+ }
+ infp_getattr_req->attr_count = attr_count;
+ } else {
+ /* No attributes specified in the call means retrieve all possible */
+ infp_getattr_req->attributes = infp_get_all_attributes(infp_getattr_req, &infp_getattr_req->attr_count);
+ if (infp_getattr_req->attributes == NULL) {
+ ret = ENOMEM;
+ goto end;
+ }
+ }
+
+ infp_getattr_req->index = 0;
+
+ infp_getattr_req->caller = sysbus_get_caller(infp_getattr_req, message, sconn);
+ if (infp_getattr_req->caller == NULL) {
+ ret = EIO;
+ goto end;
+ }
+
+ /* Prepare the result list */
+ infp_getattr_req->results = talloc_array(infp_getattr_req, struct btreemap *, attr_count);
+ if (infp_getattr_req->results == NULL) {
+ ret = ENOMEM;
+ goto end;
+ }
+
+ /* Look up the first username and start the async loop */
+ ret = infp_get_attr_lookup(infp_getattr_req);
+ if (ret != EOK) {
+ DEBUG(0, ("Could not read from cache database\n"));
+ }
+
+end:
+ dbus_free_string_array(usernames);
+ dbus_free_string_array(attributes);
+ if (ret != EOK) {
+ talloc_free(infp_getattr_req);
+ }
+ return ret;
+}
+
int infp_users_set_attr(DBusMessage *message, struct sbus_conn_ctx *sconn)
{
DBusMessage *reply;
diff --git a/server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml b/server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml
index 41220606..02fda967 100644
--- a/server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml
+++ b/server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml
@@ -28,6 +28,7 @@
@param instance A particular instance of an object (a username or group name). An empty string will be interpreted as all instances.
@param actions A list of actions to check the permissions of. Each action is described as a pair of (action_type, attribute). If attribute is left as an empty string, the query is for a global value (such as create or delete user) If the attribute value does not make sense for a particular object/instance/action, it will be ignored. Action types not applicable to an object/instance will return false (such as addmember on users).
Available action types:
+ read
create
delete
modify
@@ -136,9 +137,9 @@
@note The attribute userpic may contain very large binary data. It is advisable to request this data separately from other attributes to avoid D-BUS message size limits."
/>
<arg name="usernames" type="as" direction="in" />
- <arg name="domain" type="as" direction="in" />
+ <arg name="domain" type="s" direction="in" />
<arg name="filter" type="as" direction="in" />
- <arg name="attributes" type="a{sv}" direction="out" />
+ <arg name="attributes" type="aa{sv}" direction="out" />
</method>
<method name="SetUserAttributes1">
diff --git a/server/infopipe/sysbus.c b/server/infopipe/sysbus.c
index 6ba1bb72..a90f92d5 100644
--- a/server/infopipe/sysbus.c
+++ b/server/infopipe/sysbus.c
@@ -165,3 +165,32 @@ struct sbus_conn_ctx *sysbus_get_sbus_conn(struct sysbus_ctx *sysbus)
{
return sysbus->sconn;
}
+
+char *sysbus_get_caller(TALLOC_CTX *mem_ctx, DBusMessage *message, struct sbus_conn_ctx *sconn)
+{
+ char *caller;
+ const char *conn_name;
+ DBusError error;
+ uid_t uid;
+
+ /* Get the connection UID */
+ conn_name = dbus_message_get_sender(message);
+ if (conn_name == NULL) {
+ DEBUG(0, ("Critical error: D-BUS client has no unique name\n"));
+ return NULL;
+ }
+ dbus_error_init(&error);
+ uid = dbus_bus_get_unix_user(sbus_get_connection(sconn), conn_name, &error);
+ if (uid == -1) {
+ DEBUG(0, ("Could not identify unix user. Error message was '%s:%s'\n", error.name, error.message));
+ dbus_error_free(&error);
+ return NULL;
+ }
+ caller = get_username_from_uid(mem_ctx, uid);
+ if (caller == NULL) {
+ DEBUG(0, ("No username matched the connected UID\n"));
+ return NULL;
+ }
+
+ return caller;
+}
diff --git a/server/infopipe/sysbus.h b/server/infopipe/sysbus.h
index 2bed23a4..cb45fa7e 100644
--- a/server/infopipe/sysbus.h
+++ b/server/infopipe/sysbus.h
@@ -32,4 +32,6 @@ int sysbus_init(TALLOC_CTX *mem_ctx, struct sysbus_ctx **sysbus,
struct sbus_conn_ctx *sysbus_get_sbus_conn(struct sysbus_ctx *sysbus);
+char *sysbus_get_caller(TALLOC_CTX *mem_ctx, DBusMessage *message, struct sbus_conn_ctx *sconn);
+
#endif /* SYSBUS_H_ */