diff options
author | Stephen Gallagher <sgallagh@redhat.com> | 2009-03-02 09:35:06 -0500 |
---|---|---|
committer | Simo Sorce <ssorce@redhat.com> | 2009-03-02 09:47:54 -0500 |
commit | 5e3966c99180abdcd1e21774a882f1c14c47aae8 (patch) | |
tree | 1a882b9d49f2c7ac25d70f0bbb644b5cefed6439 | |
parent | 6f5b1ad8c234d58041e45aef4558d50f44d7f021 (diff) | |
download | sssd-5e3966c99180abdcd1e21774a882f1c14c47aae8.tar.gz sssd-5e3966c99180abdcd1e21774a882f1c14c47aae8.tar.xz 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.
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | server/confdb/confdb.c | 4 | ||||
-rw-r--r-- | server/db/sysdb.h | 31 | ||||
-rw-r--r-- | server/db/sysdb_search.c | 61 | ||||
-rw-r--r-- | server/infopipe/infopipe.c | 100 | ||||
-rw-r--r-- | server/infopipe/infopipe_private.h | 30 | ||||
-rw-r--r-- | server/infopipe/infopipe_users.c | 666 | ||||
-rw-r--r-- | server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml | 5 | ||||
-rw-r--r-- | server/infopipe/sysbus.c | 29 | ||||
-rw-r--r-- | server/infopipe/sysbus.h | 2 | ||||
-rw-r--r-- | server/sbus/sssd_dbus.h | 2 | ||||
-rw-r--r-- | server/sbus/sssd_dbus_common.c | 29 | ||||
-rw-r--r-- | server/tests/infopipe-tests.c | 6 | ||||
-rw-r--r-- | server/util/btreemap.c | 4 |
14 files changed, 912 insertions, 58 deletions
diff --git a/.gitignore b/.gitignore index 2e6dd43f8..3b784ff55 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ config.log config.status config.h config.h.in +config.rpath Makefile cscope.out *.so diff --git a/server/confdb/confdb.c b/server/confdb/confdb.c index e93a4f45a..4c895d5f1 100644 --- a/server/confdb/confdb.c +++ b/server/confdb/confdb.c @@ -612,8 +612,8 @@ static int _domain_comparator(const void *key1, const void *key2) ret = strcasecmp((const char *)key1, (const char *)key2); if (ret) { /* special case LOCAL to be always the first domain */ - if (strcmp(key1, "LOCAL") == 0) return 1; - if (strcmp(key2, "LOCAL") == 0) return -1; + if (strcmp(key1, "LOCAL") == 0) return -1; + if (strcmp(key2, "LOCAL") == 0) return 1; } return ret; } diff --git a/server/db/sysdb.h b/server/db/sysdb.h index 01df3c0c2..498ad09bf 100644 --- a/server/db/sysdb.h +++ b/server/db/sysdb.h @@ -23,6 +23,7 @@ #define __SYS_DB_H__ #include "ldb.h" +#include "confdb/confdb.h" #define SYSDB_CONF_SECTION "config/sysdb" #define SYSDB_FILE "sssd.ldb" @@ -50,6 +51,17 @@ #define SYSDB_GR_MEMBER "member" #define SYSDB_LEGACY_MEMBER "memberUid" +#define SYSDB_USER_ATTR_DEFAULTGROUP "defaultGroup" +#define SYSDB_USER_ATTR_GECOS "gecos" +#define SYSDB_USER_ATTR_HOMEDIR "homeDirectory" +#define SYSDB_USER_ATTR_SHELL "shell" +#define SYSDB_USER_ATTR_FULLNAME "fullName" +#define SYSDB_USER_ATTR_LOCALE "locale" +#define SYSDB_USER_ATTR_KEYBOARD "keyboard" +#define SYSDB_USER_ATTR_SESSION "session" +#define SYSDB_USER_ATTR_LAST_LOGIN "lastLogin" +#define SYSDB_USER_ATTR_USERPIC "userPicture" + #define SYSDB_LAST_UPDATE "lastUpdate" #define SYSDB_PWNAM_FILTER "(&(objectclass="SYSDB_USER_CLASS")("SYSDB_PW_NAME"=%s))" @@ -70,6 +82,18 @@ SYSDB_PW_HOMEDIR, SYSDB_PW_SHELL, \ SYSDB_LAST_UPDATE, \ NULL} +#define SYSDB_USER_ATTRS {SYSDB_USER_ATTR_DEFAULTGROUP, \ + SYSDB_USER_ATTR_GECOS, \ + SYSDB_USER_ATTR_HOMEDIR, \ + SYSDB_USER_ATTR_SHELL, \ + SYSDB_USER_ATTR_FULLNAME, \ + SYSDB_USER_ATTR_LOCALE, \ + SYSDB_USER_ATTR_KEYBOARD, \ + SYSDB_USER_ATTR_SESSION, \ + SYSDB_USER_ATTR_LAST_LOGIN, \ + SYSDB_USER_ATTR_USERPIC, \ + SYSDB_LAST_UPDATE, \ + NULL} #define SYSDB_GRNAM_ATTRS {SYSDB_GR_NAME, SYSDB_GR_GIDNUM, \ SYSDB_LAST_UPDATE, SYSDB_LEGACY_MEMBER, \ NULL} @@ -142,10 +166,17 @@ int sysdb_initgroups(TALLOC_CTX *mem_ctx, const char *name, bool legacy, sysdb_callback_t fn, void *ptr); +int sysdb_get_user_attr(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + const char *name, + const char **attributes, + sysdb_callback_t fn, void *ptr); struct ldb_context *sysdb_ctx_get_ldb(struct sysdb_ctx *ctx); struct sysdb_ctx *sysdb_req_get_ctx(struct sysdb_req *req); + int sysdb_transaction(TALLOC_CTX *mem_ctx, struct sysdb_ctx *ctx, sysdb_req_fn_t fn, void *pvt); diff --git a/server/db/sysdb_search.c b/server/db/sysdb_search.c index bb71079b8..b1e63112b 100644 --- a/server/db/sysdb_search.c +++ b/server/db/sysdb_search.c @@ -44,6 +44,8 @@ struct sysdb_search_ctx { struct get_mem_ctx *gmctx; struct ldb_result *res; + + const char **attrs; }; static struct sysdb_search_ctx *init_src_ctx(TALLOC_CTX *mem_ctx, @@ -204,6 +206,37 @@ static void pwd_search(struct sysdb_req *sysreq, void *ptr) } } +static void user_search(struct sysdb_req *sysreq, void *ptr) +{ + struct sysdb_search_ctx *sctx; + struct ldb_request *req; + struct ldb_dn *base_dn; + int ret; + + sctx = talloc_get_type(ptr, struct sysdb_search_ctx); + sctx->req = sysreq; + + base_dn = ldb_dn_new_fmt(sctx, sctx->ctx->ldb, + SYSDB_TMPL_USER_BASE, sctx->domain); + if (!base_dn) { + return request_error(sctx, ENOMEM); + } + + ret = ldb_build_search_req(&req, sctx->ctx->ldb, sctx, + base_dn, LDB_SCOPE_SUBTREE, + sctx->expression, sctx->attrs, NULL, + sctx, get_gen_callback, + NULL); + if (ret != LDB_SUCCESS) { + return request_ldberror(sctx, ret); + } + + ret = ldb_request(sctx->ctx->ldb, req); + if (ret != LDB_SUCCESS) { + return request_ldberror(sctx, ret); + } +} + int sysdb_getpwnam(TALLOC_CTX *mem_ctx, struct sysdb_ctx *ctx, const char *domain, @@ -729,3 +762,31 @@ int sysdb_initgroups(TALLOC_CTX *mem_ctx, return sysdb_operation(mem_ctx, ctx, initgr_search, sctx); } +int sysdb_get_user_attr(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + const char *name, + const char **attributes, + sysdb_callback_t fn, void *ptr) +{ + struct sysdb_search_ctx *sctx; + + if (!domain) { + return EINVAL; + } + + sctx = init_src_ctx(mem_ctx, domain->name, domain->legacy, ctx, fn, ptr); + if (!sctx) { + return ENOMEM; + } + + sctx->expression = talloc_asprintf(sctx, SYSDB_PWNAM_FILTER, name); + if (!sctx->expression) { + talloc_free(sctx); + return ENOMEM; + } + + sctx->attrs = attributes; + + return sysdb_operation(mem_ctx, ctx, user_search, sctx); +} diff --git a/server/infopipe/infopipe.c b/server/infopipe/infopipe.c index 4b1eb9204..035bd4b4d 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 8ea2586bf..f1d6694a4 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 2c107bc47..82889fa86 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 412206068..02fda9670 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 6ba1bb72d..a90f92d5f 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 2bed23a4e..cb45fa7ef 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_ */ diff --git a/server/sbus/sssd_dbus.h b/server/sbus/sssd_dbus.h index 43519b3e1..8dd0d3e30 100644 --- a/server/sbus/sssd_dbus.h +++ b/server/sbus/sssd_dbus.h @@ -126,4 +126,6 @@ DBusHandlerResult sbus_message_handler(DBusConnection *conn, void sbus_conn_send_reply(struct sbus_conn_ctx *conn_ctx, DBusMessage *reply); +int sbus_is_dbus_fixed_type(int dbus_type); +int sbus_is_dbus_string_type(int dbus_type); #endif /* _SSSD_DBUS_H_*/ diff --git a/server/sbus/sssd_dbus_common.c b/server/sbus/sssd_dbus_common.c index a9e0d816f..e5011aa60 100644 --- a/server/sbus/sssd_dbus_common.c +++ b/server/sbus/sssd_dbus_common.c @@ -2,6 +2,7 @@ #include "tevent.h" #include "dbus/dbus.h" #include "util/util.h" +#include "util/btreemap.h" struct timeval _dbus_timeout_get_interval_tv(int interval) { struct timeval tv; @@ -43,3 +44,31 @@ void sbus_remove_timeout(DBusTimeout *timeout, void *data) { talloc_free(te); dbus_timeout_set_data(timeout, NULL, NULL); } + +int sbus_is_dbus_fixed_type(int dbus_type) +{ + switch (dbus_type) { + case DBUS_TYPE_BYTE: + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + return true; + } + return false; +} + +int sbus_is_dbus_string_type(int dbus_type) +{ + switch(dbus_type) { + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + return true; + } + return false; +} diff --git a/server/tests/infopipe-tests.c b/server/tests/infopipe-tests.c index 2aaa653a6..0424fcb9b 100644 --- a/server/tests/infopipe-tests.c +++ b/server/tests/infopipe-tests.c @@ -151,8 +151,10 @@ START_TEST(test_infp_introspect) DBUS_TYPE_INVALID); /* Verify that the reply matches the reference file */ - if (strcmp(introspect_xml, returned_xml) != 0) { - fail("Verify Introspection XML: FAILED"); + int c; + if ((c = strcmp(introspect_xml, returned_xml)) != 0) { + DEBUG(0, ("Verify Introspection XML: FAILED %d\nstrlen: %d, %d\n", c, strlen(introspect_xml), strlen(returned_xml))); + fail("");//"Verify Introspection XML: FAILED %d\n %s\nstrlen: %d", c, returned_xml, strlen(returned_xml)); } break; case DBUS_MESSAGE_TYPE_ERROR: diff --git a/server/util/btreemap.c b/server/util/btreemap.c index 2d9b1761c..9bfc98550 100644 --- a/server/util/btreemap.c +++ b/server/util/btreemap.c @@ -65,7 +65,7 @@ int btreemap_search_key(struct btreemap *map, const void *key, struct btreemap * tempnode = map; while (found == BTREEMAP_EMPTY) { - result = tempnode->comparator(tempnode->key, key); + result = tempnode->comparator(key, tempnode->key); if (result > 0) { if (tempnode->right) @@ -150,6 +150,8 @@ int btreemap_set_value(TALLOC_CTX *mem_ctx, new_node->key = talloc_steal(new_node, key); new_node->value = talloc_steal(new_node, value); new_node->comparator = comparator; + new_node->left = NULL; + new_node->right = NULL; if (found == BTREEMAP_EMPTY) |