diff options
Diffstat (limited to 'server/infopipe')
-rw-r--r-- | server/infopipe/infopipe.c | 61 | ||||
-rw-r--r-- | server/infopipe/infopipe_private.h | 4 | ||||
-rw-r--r-- | server/infopipe/infopipe_users.c | 403 | ||||
-rw-r--r-- | server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml | 2 |
4 files changed, 453 insertions, 17 deletions
diff --git a/server/infopipe/infopipe.c b/server/infopipe/infopipe.c index 035bd4b4d..272bb166e 100644 --- a/server/infopipe/infopipe.c +++ b/server/infopipe/infopipe.c @@ -354,6 +354,9 @@ int infp_get_attribute_type(const char *attribute) { int attribute_type = INFP_ATTR_TYPE_INVALID; + if(attribute == NULL) + return INFP_ATTR_TYPE_INVALID; + if(strcasecmp(attribute, "defaultgroup") == 0) attribute_type = INFP_ATTR_TYPE_DEFAULTGROUP; else if (strcasecmp(attribute, "gecos") == 0) { @@ -576,6 +579,64 @@ einval: return EOK; } +int infp_get_ldb_val_from_dbus(TALLOC_CTX *mem_ctx, DBusMessageIter *iter, struct ldb_val **value, int dbus_type, int subtype) +{ + struct ldb_val *val = NULL; + void *tmp; + size_t element_size; + int num_elements; + int ret; + + val = talloc_zero(mem_ctx, struct ldb_val); + if (val == NULL) { + ret = ENOMEM; + goto done; + } + + /* Fixed-size types */ + if (sbus_is_dbus_fixed_type(dbus_type)) { + dbus_message_iter_get_basic(iter, &tmp); + val->length = sbus_get_dbus_type_size(dbus_type); + } + + else if (sbus_is_dbus_string_type(dbus_type)) { + dbus_message_iter_get_basic(iter, &tmp); + val->length = strlen((const char *)tmp); + } + + else if (dbus_type == DBUS_TYPE_ARRAY) { + if (!sbus_is_dbus_fixed_type(subtype)) { + ret = EINVAL; + goto done; + } + + element_size = sbus_get_dbus_type_size(subtype); + dbus_message_iter_get_fixed_array(iter, &tmp, &num_elements); + val->length = num_elements * element_size; + } + else { + /* Unsupported type */ + ret = EINVAL; + goto done; + } + + val->data = talloc_memdup(val, tmp, val->length); + if (val->data == NULL) { + ret = ENOMEM; + goto done; + } + + *value = val; + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(val); + *value = NULL; + } + return ret; +} + int main(int argc, const char *argv[]) { int opt; diff --git a/server/infopipe/infopipe_private.h b/server/infopipe/infopipe_private.h index f1d6694a4..e6ec7c012 100644 --- a/server/infopipe/infopipe_private.h +++ b/server/infopipe/infopipe_private.h @@ -39,6 +39,8 @@ struct infp_req_ctx { struct sbus_conn_ctx *sconn; DBusMessage *req_message; bool check_provider; + struct sss_domain_info *domain; + char *caller; }; enum infp_object_types { @@ -85,4 +87,6 @@ bool infp_get_permissions(const char *username, struct sss_domain_info *infp_get_domain_obj(struct infp_ctx *infp, const char *domain_name); +int infp_get_ldb_val_from_dbus(TALLOC_CTX *mem_ctx, DBusMessageIter *iter, struct ldb_val **value, int dbus_type, int subtype); + #endif /* INFOPIPE_PRIVATE_H_ */ diff --git a/server/infopipe/infopipe_users.c b/server/infopipe/infopipe_users.c index 498496fd3..a9ffe8bfd 100644 --- a/server/infopipe/infopipe_users.c +++ b/server/infopipe/infopipe_users.c @@ -30,6 +30,7 @@ #include "db/sysdb.h" static int attr_comparator(const void *key1, const void *key2); +static int username_comparator(const void *key1, const void *key2); int infp_users_get_cached(DBusMessage *message, struct sbus_conn_ctx *sconn) { @@ -71,8 +72,6 @@ int infp_users_delete(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; @@ -156,8 +155,6 @@ static int infp_user_getattr_append_dict(TALLOC_CTX *mem_ctx, goto error; } - DEBUG(99, ("DBUS_TYPE: [%c] for attribute [%s]\n", value->dbus_type, attrs[i])); - vartype = NULL; subtype = NULL; if (sbus_is_dbus_string_type(value->dbus_type)) { @@ -203,9 +200,7 @@ static int infp_user_getattr_append_dict(TALLOC_CTX *mem_ctx, } else if (value->dbus_type == DBUS_TYPE_ARRAY) { - DEBUG(99, ("Marshalling array, subtype [%c]\n", value->subtype)); if(sbus_is_dbus_fixed_type(value->subtype)) { - DEBUG(99, ("Marshalling fixed array\n")); /* Only support adding arrays of fixed types or strings for now */ subtype = talloc_asprintf(mem_ctx, "a%c", value->subtype); @@ -474,6 +469,7 @@ done: int infp_get_user_attr_dbus_type(int attr_type, int *subtype) { int dbus_type; + *subtype = DBUS_TYPE_INVALID; switch(attr_type) { case INFP_ATTR_TYPE_DEFAULTGROUP: @@ -523,8 +519,8 @@ static int infp_get_attr_lookup(struct infp_getattr_ctx *infp_getattr_req) 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, + if(infp_get_permissions(infp_getattr_req->infp_req->caller, + infp_getattr_req->infp_req->domain, INFP_OBJ_TYPE_USER, infp_getattr_req->usernames[infp_getattr_req->index], INFP_ACTION_TYPE_READ, @@ -580,10 +576,10 @@ static int infp_get_attr_lookup(struct infp_getattr_ctx *infp_getattr_req) /* 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->name, + infp_getattr_req->infp_req->domain->name, infp_getattr_req->usernames[infp_getattr_req->index], (const char **)attributes, - infp_getattr_req->domain->legacy, + infp_getattr_req->infp_req->domain->legacy, infp_get_attr_lookup_callback, infp_getattr_req); return EOK; @@ -682,7 +678,7 @@ int infp_users_get_attr(DBusMessage *message, struct sbus_conn_ctx *sconn) 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->infp_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 */ @@ -733,8 +729,8 @@ int infp_users_get_attr(DBusMessage *message, struct sbus_conn_ctx *sconn) infp_getattr_req->index = 0; - infp_getattr_req->caller = sysbus_get_caller(infp_getattr_req, message, sconn); - if (infp_getattr_req->caller == NULL) { + infp_getattr_req->infp_req->caller = sysbus_get_caller(infp_getattr_req->infp_req, message, sconn); + if (infp_getattr_req->infp_req->caller == NULL) { ret = EIO; goto end; } @@ -761,16 +757,391 @@ end: return ret; } +static int username_comparator(const void *key1, const void *key2) +{ + return strcmp((const char *)key1, (const char *)key2); +} + +struct infp_setattr_ctx { + struct infp_req_ctx *infp_req; + const char **usernames; + int username_count; + uint32_t index; + struct sysdb_req *sysdb_req; + + /* Array of sysdb_attrs objects + * The number of elements in this array + * is equal to the username count; + */ + struct sysdb_attrs **changes; +}; +static void infp_do_user_set_attr(struct sysdb_req *req, void *pvt); +static void infp_do_user_set_attr_callback(void *ptr, int ldb_status, struct ldb_result *res) +{ + DBusMessage *reply; + struct infp_setattr_ctx *infp_setattr_req; + + infp_setattr_req = talloc_get_type(ptr, struct infp_setattr_ctx); + + /* Check the ldb_result */ + if (ldb_status != LDB_SUCCESS) { + DEBUG(0, ("Failed to store user attributes to the sysdb\n")); + /* Cancel the transaction */ + sysdb_transaction_done(infp_setattr_req->sysdb_req, sysdb_error_to_errno(ldb_status)); + talloc_free(infp_setattr_req); + return; + } + + /* Process any remaining users */ + infp_setattr_req->index++; + if(infp_setattr_req->index < infp_setattr_req->username_count) { + infp_do_user_set_attr(infp_setattr_req->sysdb_req, infp_setattr_req); + return; + } + + /* This was the last user. Commit the transaction */ + sysdb_transaction_done(infp_setattr_req->sysdb_req, EOK); + + /* Send reply ack */ + reply = dbus_message_new_method_return(infp_setattr_req->infp_req->req_message); + sbus_conn_send_reply(infp_setattr_req->infp_req->sconn, reply); +} + +static void infp_do_user_set_attr(struct sysdb_req *req, void *pvt) +{ + int ret; + struct infp_setattr_ctx *infp_setattr_req; + + infp_setattr_req = talloc_get_type(pvt, struct infp_setattr_ctx); + infp_setattr_req->sysdb_req = req; + + DEBUG(9, ("Setting attributes for user [%s]\n", infp_setattr_req->usernames[infp_setattr_req->index])); + ret = sysdb_set_user_attr(infp_setattr_req->sysdb_req, + infp_setattr_req->infp_req->infp->sysdb, + infp_setattr_req->infp_req->domain->name, + infp_setattr_req->usernames[infp_setattr_req->index], + infp_setattr_req->changes[infp_setattr_req->index], + infp_do_user_set_attr_callback, infp_setattr_req); + if(ret != EOK) { + DEBUG(0, ("Failed to set attributes for user [%s]. Cancelling transaction\n", infp_setattr_req->usernames[infp_setattr_req->index])); + sysdb_transaction_done(req, ret); + talloc_free(infp_setattr_req); + } +} + int infp_users_set_attr(DBusMessage *message, struct sbus_conn_ctx *sconn) { + TALLOC_CTX *dict_ctx; DBusMessage *reply; + DBusMessageIter iter, array_iter, dict_array_iter; + DBusMessageIter dict_iter, variant_iter; + dbus_bool_t dbret; + char *domain_name; + char *einval_msg; + const char *recv_username; + const char *dict_key; + char *username; + char *val_key; + char *values; + char **attributes; + int user_count, change_count; + int change_map_count, dict_entry_count; + int added_entries; + int current_type; + int attr_type, variant_type; + int subtype; + struct infp_setattr_ctx *infp_setattr_req; + struct btreemap *username_map; + struct btreemap *value_map; + struct btreemap *change_map; + struct btreemap **change_array; + struct btreemap **tmp_array; + struct ldb_val *val; + int ret, i, j; + + /* Create a infp_setattr_ctx */ + infp_setattr_req = talloc_zero(NULL, struct infp_setattr_ctx); + if(infp_setattr_req == NULL) { + ret = ENOMEM; + goto error; + } - reply = dbus_message_new_error(message, DBUS_ERROR_NOT_SUPPORTED, "Not yet implemented"); + /* Create an infp_req_ctx */ + infp_setattr_req->infp_req = talloc_zero(infp_setattr_req, struct infp_req_ctx); + if (infp_setattr_req == NULL) { + ret = ENOMEM; + goto error; + } + infp_setattr_req->infp_req->infp = talloc_get_type(sbus_conn_get_private_data(sconn), struct infp_ctx); + infp_setattr_req->infp_req->sconn = sconn; + infp_setattr_req->infp_req->req_message = message; - /* send reply */ - sbus_conn_send_reply(sconn, reply); + /* Get the caller's identity */ + infp_setattr_req->infp_req->caller = sysbus_get_caller(infp_setattr_req->infp_req, message, sconn); + if (infp_setattr_req->infp_req->caller == NULL) { + ret = EIO; + goto error; + } + + /* Process the arguments to SetUserAttributes */ + dbret = dbus_message_iter_init(message, &iter); + if (!dbret) { + einval_msg = talloc_strdup(infp_setattr_req, "No arguments received."); + goto einval; + } + /* Get the list of usernames to process */ + current_type = dbus_message_iter_get_arg_type(&iter); + if (current_type != DBUS_TYPE_ARRAY) { + einval_msg = talloc_strdup(infp_setattr_req, "Expected username list."); + goto einval; + } + if(dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) { + einval_msg = talloc_strdup(infp_setattr_req, "Expected username list."); + goto einval; + } + /* Recurse into the array */ + user_count = 0; + dbus_message_iter_recurse(&iter, &array_iter); + username_map = NULL; + while((current_type=dbus_message_iter_get_arg_type(&array_iter)) != DBUS_TYPE_INVALID) { + dbus_message_iter_get_basic(&array_iter, &recv_username); + username = talloc_strdup(infp_setattr_req, recv_username); + if (username == NULL) { + ret = ENOMEM; + goto error; + } + ret = btreemap_set_value(infp_setattr_req, &username_map, + (const void *)username, NULL, username_comparator); + if (ret != EOK) goto error; + + user_count++; + dbus_message_iter_next(&array_iter); + } + + if (user_count == 0) { + /* No users passed in */ + einval_msg = talloc_strdup(infp_setattr_req, "No usernames provided."); + goto einval; + } + + ret = btreemap_get_keys(infp_setattr_req, username_map, + (const void ***)&infp_setattr_req->usernames, &infp_setattr_req->username_count); + if (ret != EOK) goto error; + + /* Verify that the usernames were all unique. + * If the count of usernames we added differs from the count we're + * getting back, then at least one was a duplicate. + */ + if (infp_setattr_req->username_count != user_count) { + einval_msg = talloc_strdup(infp_setattr_req, "Usernames were not unique."); + goto einval; + } + + /* Get the domain name */ + dbus_message_iter_next(&iter); + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { + einval_msg = talloc_strdup(infp_setattr_req, "No domain provided.\n"); + goto einval; + } + dbus_message_iter_get_basic(&iter, &domain_name); + + infp_setattr_req->infp_req->domain = btreemap_get_value(infp_setattr_req->infp_req->infp->domain_map, + (const void *)domain_name); + if(infp_setattr_req->infp_req->domain == NULL) { + einval_msg = talloc_strdup(infp_setattr_req, "Invalid domain."); + goto einval; + } + + /* Get the array of change DICT entries */ + dbus_message_iter_next(&iter); + current_type = dbus_message_iter_get_arg_type(&iter); + if (current_type != DBUS_TYPE_ARRAY) { + einval_msg = talloc_strdup(infp_setattr_req, "Expected change list."); + goto einval; + } + if(dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_ARRAY) { + einval_msg = talloc_strdup(infp_setattr_req, "Expected array of DICT entry arrays."); + goto einval; + } + + change_count = 0; + change_array = NULL; + dbus_message_iter_recurse(&iter, &array_iter); + if(dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_DICT_ENTRY) { + einval_msg = talloc_strdup(infp_setattr_req, "Expected array of DICT entries."); + goto einval; + } + while((current_type=dbus_message_iter_get_arg_type(&array_iter)) != DBUS_TYPE_INVALID) { + /* Descend into the DICT array */ + dbus_message_iter_recurse(&array_iter, &dict_array_iter); + + /* Create a new talloc context to contain the values from this DICT array */ + dict_ctx = talloc_new(infp_setattr_req); + if(dict_ctx == NULL) { + ret = ENOMEM; + goto error; + } + value_map = NULL; + dict_entry_count = 0; + + while((current_type=dbus_message_iter_get_arg_type(&dict_array_iter)) != DBUS_TYPE_INVALID) { + change_count++; + /* Descend into the DICT entry */ + dbus_message_iter_recurse(&dict_array_iter, &dict_iter); + /* Key must be a string */ + if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { + einval_msg = talloc_strdup(infp_setattr_req, "DICT entries must be keyed on strings."); + goto einval; + } + dbus_message_iter_get_basic(&dict_iter, &dict_key); + if((attr_type = infp_get_attribute_type(dict_key)) == INFP_ATTR_TYPE_INVALID) { + /* Continue to the next DICT entry (ignoring unrecognized attributes) */ + change_count--; /* Don't include ignored values in the count */ + dbus_message_iter_next(&dict_array_iter); + continue; + } + val_key = talloc_strdup(dict_ctx, dict_key); + if(val_key == NULL) { + ret = ENOMEM; + goto error; + } + + /* Value is a variant */ + variant_type = infp_get_user_attr_dbus_type(attr_type, &subtype); + if(variant_type == DBUS_TYPE_INVALID) { + /* This shouldn't happen since the attr_type is valid. + * If this failed, it's a coding error. + */ + DEBUG(0, ("Critical error, valid attribute type could not be paired with a D-BUS type.\n")); + ret = EIO; + goto error; + } + + dbus_message_iter_next(&dict_iter); + if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_VARIANT) { + einval_msg = talloc_strdup(infp_setattr_req, "DICT value must be a variant."); + goto einval; + } + dbus_message_iter_recurse(&dict_iter, &variant_iter); + + if (dbus_message_iter_get_arg_type(&variant_iter) != variant_type) { + einval_msg = talloc_asprintf(infp_setattr_req, "DICT value did not match required type of key [%s]. Expected [%c], received [%c]\n", dict_key, variant_type, dbus_message_iter_get_arg_type(&dict_iter)); + goto einval; + } + + if(variant_type == DBUS_TYPE_ARRAY) { + current_type=dbus_message_iter_get_element_type(&variant_iter); + if (!sbus_is_dbus_fixed_type(current_type)) { + /* We only support fixed-type arrays right now */ + einval_msg = talloc_asprintf(infp_setattr_req, "Invalid array type."); + } + } + + ret = infp_get_ldb_val_from_dbus(dict_ctx, &variant_iter, &val, variant_type, subtype); + if (ret != EOK) { + /* Could not create an LDB val from this variant */ + DEBUG(0, ("Error, valid attribute type could not be converted to an ldb_val.\n")); + goto error; + } + + ret = btreemap_set_value(dict_ctx, &value_map, + (const void *)val_key, val, + attr_comparator); + if (ret != EOK) { + DEBUG(0, ("Could not add change value to the value map.\n")); + goto error; + } + + dict_entry_count++; + dbus_message_iter_next(&dict_array_iter); + } + + /* Verify that all of the dict entries were unique */ + ret = btreemap_get_keys(dict_ctx, value_map, + (const void ***)&values, &added_entries); + if (ret != EOK) goto error; + + if (added_entries != dict_entry_count) { + einval_msg = talloc_strdup(infp_setattr_req, "Attributes to change were not unique."); + goto einval; + } + + /* Add the map to an array */ + tmp_array = talloc_realloc(infp_setattr_req, change_array, struct btreemap *, change_count); + if(tmp_array == NULL) { + ret = ENOMEM; + goto error; + } + change_array = tmp_array; + change_array[change_count-1] = value_map; + + dbus_message_iter_next(&array_iter); + } + + if (change_count != infp_setattr_req->username_count && change_count != 1) { + /* Change counts must be one-to-one with the number of users, + * or else exactly one for all users. + */ + einval_msg = talloc_strdup(infp_setattr_req, "Count of change DICTs not equal to one or count of usernames."); + goto einval; + } + + /* Check permissions and create the sysdb_attrs change list */ + infp_setattr_req->changes = talloc_array(infp_setattr_req, struct sysdb_attrs *, infp_setattr_req->username_count); + for (i = 0; i < infp_setattr_req->username_count; i++) { + if (change_count == 1) + change_map = change_array[0]; + else + change_map = change_array[i]; + + ret = btreemap_get_keys(dict_ctx, change_map, + (const void ***)&attributes, &change_map_count); + if (ret != EOK) goto error; + + infp_setattr_req->changes[i] = sysdb_new_attrs(infp_setattr_req); + if (infp_setattr_req->changes[i] == NULL) { + ret = ENOMEM; + goto error; + } + for (j = 0; j < change_map_count; j++) { + /* Add it to the sydb_attrs change list if permission is granted */ + if (infp_get_permissions(infp_setattr_req->infp_req->caller, + infp_setattr_req->infp_req->domain, + INFP_OBJ_TYPE_USER, + infp_setattr_req->usernames[i], + INFP_ACTION_TYPE_MODIFY, + infp_get_attribute_type(attributes[j]))) + { + ret = sysdb_attrs_add_val(infp_setattr_req->changes[i], attributes[j], + btreemap_get_value(change_map, attributes[j])); + if (ret != EOK) { + goto error; + } + } + } + } + + infp_setattr_req->index = 0; + ret = sysdb_transaction(infp_setattr_req, infp_setattr_req->infp_req->infp->sysdb, + infp_do_user_set_attr, infp_setattr_req); + if (ret != EOK) { + DEBUG(0, ("Could not write to the cache database.\n")) + goto error; + } + + return EOK; + +error: + talloc_free(infp_setattr_req); + return ret; + +einval: + reply = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, einval_msg); + sbus_conn_send_reply(sconn, reply); dbus_message_unref(reply); + + talloc_free(infp_setattr_req); return EOK; } diff --git a/server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml b/server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml index 02fda9670..f159fce5b 100644 --- a/server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml +++ b/server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml @@ -166,7 +166,7 @@ /> <arg name="usernames" type="as" direction="in" /> <arg name="domain" type="s" direction="in" /> - <arg name="attributes" type="a{sv}" direction="in" /> + <arg name="attributes" type="aa{sv}" direction="in" /> </method> <method name="Set_YouReallyDoNotWantToUseThisFunction_UserUID1"> |