From ea4f00deaa457f8d8262528cf628691ced497c2e Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Sun, 8 Mar 2009 17:24:44 -0400 Subject: Implement SetGroupGID in the InfoPipe --- server/db/sysdb.h | 5 + server/db/sysdb_ops.c | 67 ++++++++ server/infopipe/infopipe.c | 43 ++++++ server/infopipe/infopipe_groups.c | 169 ++++++++++++++++++++- server/infopipe/infopipe_private.h | 6 +- .../org.freeipa.sssd.infopipe.Introspect.xml | 2 +- 6 files changed, 289 insertions(+), 3 deletions(-) (limited to 'server') diff --git a/server/db/sysdb.h b/server/db/sysdb.h index f59b56c1d..e323ad0d6 100644 --- a/server/db/sysdb.h +++ b/server/db/sysdb.h @@ -285,6 +285,11 @@ int sysdb_add_group(struct sysdb_req *sysreq, const char *name, gid_t gid, sysdb_callback_t fn, void *pvt); +int sysdb_set_group_gid(struct sysdb_req *sysreq, + struct sss_domain_info *domain, + const char *name, gid_t gid, + sysdb_callback_t fn, void *pvt); + /* legacy functions for proxy providers */ int sysdb_legacy_store_user(struct sysdb_req *sysreq, diff --git a/server/db/sysdb_ops.c b/server/db/sysdb_ops.c index 6762575f2..9ea2a0aa5 100644 --- a/server/db/sysdb_ops.c +++ b/server/db/sysdb_ops.c @@ -1022,7 +1022,74 @@ static int group_add_call(struct group_add_ctx *group_ctx) return EOK; } +/* This function is not safe, but is included for completeness + * It is much better to allow SSSD to internally manage the + * group GID values. sysdb_set_group_gid() will perform no + * validation that the new GID is unused. The only check it + * will perform is whether the requested GID is in the range + * of IDs allocated for the domain. + */ +int sysdb_set_group_gid(struct sysdb_req *sysreq, + struct sss_domain_info *domain, + const char *name, gid_t gid, + sysdb_callback_t fn, void *pvt) +{ + struct group_add_ctx *group_ctx; + struct sysdb_ctx *sysdb; + struct ldb_message *msg; + struct ldb_request *req; + int flags = LDB_FLAG_MOD_REPLACE; + int ret; + + if (!sysdb_req_check_running(sysreq)) { + DEBUG(2, ("Invalid request! Not running at this time.\n")); + return EINVAL; + } + + /* Validate that the target GID is within the domain range */ + if((gid < domain->id_min) || + (domain->id_max && (gid > domain->id_max))) { + DEBUG(2, ("Invalid request. Domain ID out of range")); + return EDOM; + } + + group_ctx = talloc(sysreq, struct group_add_ctx); + if (!group_ctx) return ENOMEM; + + group_ctx->cbctx = talloc_zero(group_ctx, struct sysdb_cb_ctx); + if (!group_ctx->cbctx) return ENOMEM; + + group_ctx->sysreq = sysreq; + group_ctx->domain = domain; + group_ctx->cbctx->fn = fn; + group_ctx->cbctx->pvt = pvt; + group_ctx->name = name; + group_ctx->gid = gid; + + sysdb = sysdb_req_get_ctx(group_ctx->sysreq); + + msg = ldb_msg_new(group_ctx); + if (!msg) return ENOMEM; + + msg->dn = sysdb_group_dn(sysdb, msg, + group_ctx->domain->name, + group_ctx->name); + if (!msg->dn) return ENOMEM; + ret = add_ulong(msg, flags, SYSDB_GIDNUM, + (unsigned long)(group_ctx->gid)); + + ret = ldb_build_mod_req(&req, sysdb->ldb, group_ctx, msg, NULL, + group_ctx->cbctx, sysdb_op_callback, NULL); + if (ret == LDB_SUCCESS) { + ret = ldb_request(sysdb->ldb, req); + } + if (ret != LDB_SUCCESS) { + return sysdb_error_to_errno(ret); + } + + return EOK; +} /* "sysdb_legacy_" functions * the set of functions named sysdb_legacy_* are used by modules diff --git a/server/infopipe/infopipe.c b/server/infopipe/infopipe.c index 09ffcbdf6..4ec971caa 100644 --- a/server/infopipe/infopipe.c +++ b/server/infopipe/infopipe.c @@ -141,6 +141,49 @@ static int infp_monitor_init(struct infp_ctx *infp_ctx) return EOK; } +/* Helper function to return an immediate error message in the event + * of internal error in the InfoPipe to avoid forcing the clients to + * time out waiting for a reply. + */ +void infp_return_failure(struct infp_req_ctx *infp_req, const char *message) +{ + DBusMessage *reply; + + reply = dbus_message_new_error(infp_req->req_message, + DBUS_ERROR_FAILED, + message); + /* If the reply was NULL, we ran out of memory, so we won't + * bother trying to queue the message to send. In this case, + * our safest move is to allow the client to time out waiting + * for a reply. + */ + if(reply) { + sbus_conn_send_reply(infp_req->sconn, reply); + dbus_message_unref(reply); + } +} + +/* Helper function to return an ack to the caller to indicate + * that the internal process completed succesfully. An ack in + * InfoPipe is simply an empty D-BUS method return (as opposed + * to a D-BUS error or signal) + */ +void infp_return_success(struct infp_req_ctx *infp_req) +{ + DBusMessage *reply; + + reply = dbus_message_new_method_return(infp_req->req_message); + /* If the reply was NULL, we ran out of memory, so we won't + * bother trying to queue the message to send. In this case, + * our safest move is to allow the client to time out waiting + * for a reply. + */ + if(reply) { + sbus_conn_send_reply(infp_req->sconn, reply); + dbus_message_unref(reply); + } +} + struct sbus_method infp_methods[] = { INFP_PERMISSION_METHODS INFP_USER_METHODS diff --git a/server/infopipe/infopipe_groups.c b/server/infopipe/infopipe_groups.c index bb3741c9a..9aaecf2f2 100644 --- a/server/infopipe/infopipe_groups.c +++ b/server/infopipe/infopipe_groups.c @@ -657,15 +657,182 @@ int infp_groups_remove_members(DBusMessage *message, INFP_ACTION_TYPE_REMOVEMEMBER); } +struct infp_setgid_ctx { + struct infp_req_ctx *infp_req; + char *group_name; + gid_t gid; + struct sysdb_req *sysdb_req; +}; + +static void infp_do_gid_callback(void *ptr, + int status, + struct ldb_result *res) +{ + char *error_msg = NULL; + struct infp_setgid_ctx *grmod_req = + talloc_get_type(ptr, struct infp_setgid_ctx); + + /* Commit or cancel the transaction, based on the + * return status + */ + sysdb_transaction_done(grmod_req->sysdb_req, status); + + if(status != EOK) { + if (status == ENOENT) { + error_msg = talloc_strdup(grmod_req, "No such group"); + } + infp_return_failure(grmod_req->infp_req, error_msg); + talloc_free(grmod_req); + return; + } + + infp_return_success(grmod_req->infp_req); + talloc_free(grmod_req); +} + +static void infp_do_gid(struct sysdb_req *req, void *pvt) +{ + int ret; + DBusMessage *reply; + char *error_msg; + gid_t max; + struct infp_setgid_ctx *grmod_req = + talloc_get_type(pvt, struct infp_setgid_ctx); + grmod_req->sysdb_req = req; + + ret = sysdb_set_group_gid(grmod_req->sysdb_req, + grmod_req->infp_req->domain, + grmod_req->group_name, + grmod_req->gid, + infp_do_gid_callback, + grmod_req); + if (ret != EOK) { + if(ret == EDOM) { + /* GID was out of range */ + max = grmod_req->infp_req->domain->id_max? + grmod_req->infp_req->domain->id_max: + (gid_t)-1; + error_msg = talloc_asprintf(grmod_req, + "GID %u outside the range [%u..%u]", + grmod_req->gid, + grmod_req->infp_req->domain->id_min, + max); + reply = dbus_message_new_error(grmod_req->infp_req->req_message, + DBUS_ERROR_LIMITS_EXCEEDED, + error_msg); + if (reply) sbus_conn_send_reply(grmod_req->infp_req->sconn, reply); + } + talloc_free(grmod_req); + return; + } +} + int infp_groups_set_gid(DBusMessage *message, struct sbus_conn_ctx *sconn) { DBusMessage *reply; + DBusError error; + char *einval_msg; + struct infp_setgid_ctx *grmod_req; + int ret; - reply = dbus_message_new_error(message, DBUS_ERROR_NOT_SUPPORTED, "Not yet implemented"); + /* Arguments */ + const char *arg_group; + const char *arg_domain; + const gid_t arg_gid; + grmod_req = talloc_zero(NULL, struct infp_setgid_ctx); + if (grmod_req == NULL) { + ret = ENOMEM; + goto error; + } + + /* Create an infp_req_ctx */ + grmod_req->infp_req = infp_req_init(grmod_req, message, sconn); + if(grmod_req->infp_req == NULL) { + ret = EIO; + goto error; + } + + dbus_error_init(&error); + if (!dbus_message_get_args(message, &error, + DBUS_TYPE_STRING, &arg_group, + DBUS_TYPE_STRING, &arg_domain, + DBUS_TYPE_UINT32, &arg_gid, + DBUS_TYPE_INVALID)) { + DEBUG(0, ("Parsing arguments to %s failed: %s:%s\n", + INFP_GROUPS_SET_GID, error.name, error.message)); + einval_msg = talloc_strdup(grmod_req, error.message); + dbus_error_free(&error); + goto einval; + } + + /* FIXME: Allow modifying groups on domains other than LOCAL */ + if (strcasecmp(arg_domain, "LOCAL") != 0) { + goto denied; + } + + grmod_req->infp_req->domain = + btreemap_get_value(grmod_req->infp_req->infp->domain_map, + (const void *)arg_domain); + + /* Check for a valid domain */ + if(grmod_req->infp_req->domain == NULL) { + einval_msg = talloc_strdup(grmod_req, "Invalid domain."); + goto einval; + } + + /* Check permissions */ + if (!infp_get_permissions(grmod_req->infp_req->caller, + grmod_req->infp_req->domain, + INFP_OBJ_TYPE_GROUP, + arg_group, + INFP_ACTION_TYPE_MODIFY, + INFP_ATTR_TYPE_GROUPID)) goto denied; + + grmod_req->gid = arg_gid; + grmod_req->group_name = talloc_strdup(grmod_req, arg_group); + if (grmod_req->group_name == NULL) { + ret = ENOMEM; + goto error; + } + + ret = sysdb_transaction(grmod_req, + grmod_req->infp_req->infp->sysdb, + infp_do_gid, + grmod_req); + if (ret != EOK) goto error; + + return EOK; + +denied: + reply = dbus_message_new_error(message, DBUS_ERROR_ACCESS_DENIED, NULL); + if(reply == NULL) { + ret = ENOMEM; + goto error; + } /* send reply */ sbus_conn_send_reply(sconn, reply); + dbus_message_unref(reply); + talloc_free(grmod_req); + return EOK; + +einval: + reply = dbus_message_new_error(message, + DBUS_ERROR_INVALID_ARGS, + einval_msg); + if (reply == NULL) { + ret = ENOMEM; + goto error; + } + sbus_conn_send_reply(sconn, reply); dbus_message_unref(reply); + talloc_free(grmod_req); return EOK; + +error: + talloc_free(grmod_req); + return ret; + + } diff --git a/server/infopipe/infopipe_private.h b/server/infopipe/infopipe_private.h index 0f523c00f..066f11e90 100644 --- a/server/infopipe/infopipe_private.h +++ b/server/infopipe/infopipe_private.h @@ -72,7 +72,8 @@ enum infp_attribute_types { INFP_ATTR_TYPE_SESSION, INFP_ATTR_TYPE_LAST_LOGIN, INFP_ATTR_TYPE_USERPIC, - INFP_ATTR_TYPE_USERID + INFP_ATTR_TYPE_USERID, + INFP_ATTR_TYPE_GROUPID }; int infp_get_attribute_type(const char *attribute); @@ -91,4 +92,7 @@ int infp_get_ldb_val_from_dbus(TALLOC_CTX *mem_ctx, DBusMessageIter *iter, struc struct infp_req_ctx *infp_req_init(TALLOC_CTX *mem_ctx, DBusMessage *message, struct sbus_conn_ctx *sconn); +void infp_return_success(struct infp_req_ctx *infp_req); +void infp_return_failure(struct infp_req_ctx *infp_req, const char *message); + #endif /* INFOPIPE_PRIVATE_H_ */ diff --git a/server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml b/server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml index c1a8b3df2..5656d42ce 100644 --- a/server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml +++ b/server/infopipe/org.freeipa.sssd.infopipe.Introspect.xml @@ -269,7 +269,7 @@ /> - + -- cgit