summaryrefslogtreecommitdiff
path: root/daemon/cynara.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/cynara.c')
-rw-r--r--daemon/cynara.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/daemon/cynara.c b/daemon/cynara.c
new file mode 100644
index 0000000..79129c7
--- /dev/null
+++ b/daemon/cynara.c
@@ -0,0 +1,348 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib-unix.h>
+#include <cynara-client-async.h>
+
+#include "log.h"
+
+#include "cynara.h"
+
+#define BUXTON_CYNARA_PERMISSIVE_MODE "BUXTON_CYNARA_PERMISSIVE_MODE"
+
+struct bxt_cyn_cb {
+ cynara_check_id id;
+
+ struct bxt_client *cli;
+ buxton_cynara_callback callback;
+ void *user_data;
+};
+
+static cynara_async *cynara;
+static int cynara_fd = -1;
+static guint cynara_fd_id;
+static gboolean cynara_skip;
+static GHashTable *cynara_tbl;
+
+static void cyn_err(const char *prefix, int err)
+{
+ char errmsg[128];
+
+ errmsg[0] = '\0';
+ cynara_strerror(err, errmsg, sizeof(errmsg));
+ bxt_err("Cynara: %s%s%d : %s", prefix ? prefix : "", prefix ? ": " : "",
+ err, errmsg);
+}
+
+static void free_cb(gpointer data)
+{
+ struct bxt_cyn_cb *cyn_cb = data;
+
+ if (!cyn_cb)
+ return;
+
+ if (cyn_cb->callback) {
+ if (cynara) {
+ int r;
+
+ r = cynara_async_cancel_request(cynara, cyn_cb->id);
+ if (r != CYNARA_API_SUCCESS)
+ cyn_err("cancel", r);
+ }
+
+ cyn_cb->callback(cyn_cb->cli, BUXTON_CYNARA_CANCELED,
+ cyn_cb->user_data);
+ }
+
+ free(cyn_cb);
+ bxt_dbg("Cynara: free %p", cyn_cb);
+}
+
+static gboolean proc_cb(gint fd, GIOCondition cond, gpointer data)
+{
+ int r;
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ cynara_fd_id = 0;
+ return G_SOURCE_REMOVE;
+ }
+
+ r = cynara_async_process(cynara);
+ if (r != CYNARA_API_SUCCESS)
+ cyn_err("process", r);
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void status_cb(int old_fd, int new_fd, cynara_async_status status,
+ void *data)
+{
+ if (old_fd != -1) {
+ if (cynara_fd_id) {
+ g_source_remove(cynara_fd_id);
+ cynara_fd_id = 0;
+ }
+ cynara_fd = -1;
+ }
+
+ if (new_fd != -1) {
+ GIOCondition cond;
+
+ cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+ if (status == CYNARA_STATUS_FOR_RW)
+ cond |= G_IO_OUT;
+
+ cynara_fd_id = g_unix_fd_add(new_fd, cond, proc_cb, data);
+ cynara_fd = new_fd;
+ }
+}
+
+static enum buxton_cynara_res check_cache(const char *clabel, const char *sess,
+ const char *uid, const char *priv)
+{
+ int r;
+
+ assert(cynara);
+ assert(clabel);
+ assert(sess);
+ assert(uid);
+ assert(priv);
+
+ r = cynara_async_check_cache(cynara, clabel, sess, uid, priv);
+ switch (r) {
+ case CYNARA_API_ACCESS_ALLOWED:
+ r = BUXTON_CYNARA_ALLOWED;
+ break;
+ case CYNARA_API_ACCESS_DENIED:
+ r = BUXTON_CYNARA_DENIED;
+ break;
+ case CYNARA_API_CACHE_MISS:
+ r = BUXTON_CYNARA_UNKNOWN;
+ break;
+ default:
+ cyn_err("cache", r);
+ r = BUXTON_CYNARA_UNKNOWN;
+ break;
+ }
+
+ return r;
+}
+
+static void resp_cb(cynara_check_id id, cynara_async_call_cause cause,
+ int resp, void *data)
+{
+ struct bxt_cyn_cb *cyn_cb;
+ enum buxton_cynara_res res;
+
+ bxt_dbg("check id %u, cause %d, resp %d", id, cause, resp);
+
+ if (!cynara_tbl)
+ return;
+
+ cyn_cb = g_hash_table_lookup(cynara_tbl, GUINT_TO_POINTER(id));
+ if (!cyn_cb || cyn_cb != data) {
+ bxt_err("Cynara: resp: %u not exist in table", id);
+ return;
+ }
+
+ switch (cause) {
+ case CYNARA_CALL_CAUSE_ANSWER:
+ if (resp == CYNARA_API_ACCESS_ALLOWED)
+ res = BUXTON_CYNARA_ALLOWED;
+ else
+ res = BUXTON_CYNARA_DENIED;
+ break;
+ case CYNARA_CALL_CAUSE_CANCEL:
+ case CYNARA_CALL_CAUSE_FINISH:
+ case CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE:
+ default:
+ bxt_err("Cynara: resp: not answer");
+ res = BUXTON_CYNARA_ERROR;
+ break;
+ }
+
+ if (res == BUXTON_CYNARA_DENIED) {
+ bxt_info("id %u denied%s", id,
+ cynara_skip ? "(ignored)" : "");
+ if (cynara_skip)
+ res = BUXTON_CYNARA_ALLOWED;
+ }
+
+ if (cyn_cb->callback) {
+ cyn_cb->callback(cyn_cb->cli, res, cyn_cb->user_data);
+ cyn_cb->callback = NULL;
+ }
+ g_hash_table_remove(cynara_tbl, GUINT_TO_POINTER(id));
+}
+
+static enum buxton_cynara_res check_server(struct bxt_client *client,
+ const char *clabel, const char *sess,
+ const char *uid, const char *priv,
+ buxton_cynara_callback callback, void *user_data)
+{
+ int r;
+ struct bxt_cyn_cb *cyn_cb;
+
+ assert(cynara);
+ assert(cynara_tbl);
+
+ assert(client);
+ assert(clabel);
+ assert(sess);
+ assert(uid);
+ assert(priv);
+ assert(callback);
+
+ cyn_cb = calloc(1, sizeof(*cyn_cb));
+ if (!cyn_cb)
+ return BUXTON_CYNARA_ERROR;
+
+ r = cynara_async_create_request(cynara, clabel, sess, uid, priv,
+ &cyn_cb->id, resp_cb, cyn_cb);
+ if (r != CYNARA_API_SUCCESS) {
+ cyn_err("request", r);
+ free(cyn_cb);
+ return BUXTON_CYNARA_ERROR;
+ }
+
+ bxt_info("'%s;%s;%s;%s' id %u", clabel, sess, uid, priv, cyn_cb->id);
+
+ cyn_cb->cli = client;
+ cyn_cb->callback = callback;
+ cyn_cb->user_data = user_data;
+
+ g_hash_table_insert(cynara_tbl, GUINT_TO_POINTER(cyn_cb->id), cyn_cb);
+ bxt_dbg("Cynara: %p added", cyn_cb);
+
+ return BUXTON_CYNARA_UNKNOWN;
+}
+
+enum buxton_cynara_res buxton_cynara_check(struct bxt_client *client,
+ const char *client_label, const char *session,
+ uid_t uid, const char *priv,
+ buxton_cynara_callback callback, void *user_data)
+{
+ int r;
+ char uid_str[16];
+
+ if (!client || !client_label || !session || !priv || !callback) {
+ errno = EINVAL;
+ bxt_err("cynara check: invalid argument:%s%s%s%s%s",
+ client ? "" : " client",
+ client_label ? "" : " client_label",
+ session ? "" : " session",
+ priv ? "" : " privilege",
+ callback ? "" : " callback");
+ return BUXTON_CYNARA_ERROR;
+ }
+
+ if (!*priv)
+ return BUXTON_CYNARA_ALLOWED;
+
+ if (!cynara) {
+ bxt_err("Cynara is not initialized");
+ errno = ENOTCONN;
+ return BUXTON_CYNARA_ERROR;
+ }
+
+ snprintf(uid_str, sizeof(uid_str), "%d", uid);
+
+ r = check_cache(client_label, session, uid_str, priv);
+ if (r != BUXTON_CYNARA_UNKNOWN) {
+ /* r should be ALLOWED or DENIED */
+ if (r == BUXTON_CYNARA_DENIED) {
+ bxt_info("'%s;%s;%s;%s' denied%s",
+ client_label, session, uid_str, priv,
+ cynara_skip ? "(ignored)" : "");
+ if (cynara_skip)
+ r = BUXTON_CYNARA_ALLOWED;
+ }
+ return r;
+ }
+
+ return check_server(client, client_label, session, uid_str, priv,
+ callback, user_data);
+}
+
+void buxton_cynara_cancel(struct bxt_client *client)
+{
+ GHashTableIter iter;
+ struct bxt_cyn_cb *cyn_cb;
+
+ if (!cynara || !cynara_tbl || !client)
+ return;
+
+ g_hash_table_iter_init(&iter, cynara_tbl);
+
+ while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&cyn_cb)) {
+ if (cyn_cb->cli == client)
+ g_hash_table_iter_remove(&iter);
+ }
+}
+
+int buxton_cynara_init(void)
+{
+ int r;
+ char *skip;
+
+ if (cynara)
+ return 0;
+
+ skip = getenv(BUXTON_CYNARA_PERMISSIVE_MODE);
+ if (skip && skip[0] == '1') {
+ bxt_info("Permissive mode enabled");
+ cynara_skip = TRUE;
+ }
+
+ cynara_tbl = g_hash_table_new_full(NULL, NULL, NULL, free_cb);
+ if (!cynara_tbl)
+ return -1;
+
+ r = cynara_async_initialize(&cynara, NULL, status_cb, NULL);
+ if (r != CYNARA_API_SUCCESS) {
+ cyn_err("init", r);
+ return -1;
+ }
+
+ return 0;
+}
+
+void buxton_cynara_exit(void)
+{
+ if (!cynara)
+ return;
+
+ if (cynara_fd_id) {
+ g_source_remove(cynara_fd_id);
+ cynara_fd_id = 0;
+ }
+
+ g_hash_table_destroy(cynara_tbl);
+ cynara_tbl = NULL;
+
+ cynara_async_finish(cynara);
+ cynara = NULL;
+ cynara_fd = -1;
+}
+