summaryrefslogtreecommitdiff
path: root/daemon
diff options
context:
space:
mode:
Diffstat (limited to 'daemon')
-rw-r--r--daemon/CMakeLists.txt30
-rw-r--r--daemon/cynara.c348
-rw-r--r--daemon/cynara.h46
-rw-r--r--daemon/daemon.c877
-rw-r--r--daemon/daemon.h51
-rw-r--r--daemon/main.c133
-rw-r--r--daemon/socks.c232
-rw-r--r--daemon/socks.h29
8 files changed, 1746 insertions, 0 deletions
diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt
new file mode 100644
index 0000000..c2e81b8
--- /dev/null
+++ b/daemon/CMakeLists.txt
@@ -0,0 +1,30 @@
+# buxton2d build
+
+PKG_CHECK_MODULES(D_PKGS REQUIRED libsystemd cynara-client-async)
+
+FOREACH(flag ${D_PKGS_CFLAGS})
+ SET(DAEMON_CFLAGS "${DAEMON_CFLAGS} ${flag}")
+ENDFOREACH()
+
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/include)
+
+SET(TARGET "buxton2d")
+SET(SRC main.c
+ daemon.c
+ socks.c
+ cynara.c
+ ../common/common.c
+ ../common/config.c
+ ../common/backends.c
+ ../common/serialize.c
+ ../common/direct.c
+ ../common/proto.c
+)
+ADD_EXECUTABLE(${TARGET} ${SRC})
+SET_TARGET_PROPERTIES(${TARGET} PROPERTIES
+ LINK_FLAGS "-fPIE"
+ COMPILE_FLAGS "${DAEMON_CFLAGS}"
+)
+TARGET_LINK_LIBRARIES(${TARGET} ${PKGS_LDFLAGS} ${D_PKGS_LDFLAGS} -ldl)
+INSTALL(TARGETS ${TARGET} DESTINATION sbin)
+
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;
+}
+
diff --git a/daemon/cynara.h b/daemon/cynara.h
new file mode 100644
index 0000000..31f629e
--- /dev/null
+++ b/daemon/cynara.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+int buxton_cynara_init(void);
+void buxton_cynara_exit(void);
+
+enum buxton_cynara_res {
+ BUXTON_CYNARA_ERROR = -1,
+ BUXTON_CYNARA_UNKNOWN,
+ BUXTON_CYNARA_ALLOWED,
+ BUXTON_CYNARA_DENIED,
+ BUXTON_CYNARA_CANCELED,
+ BUXTON_CYNARA_MAX /* sentinel value */
+};
+
+struct bxt_client;
+
+typedef void (*buxton_cynara_callback)(struct bxt_client *client,
+ enum buxton_cynara_res res, void *user_data);
+
+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);
+
+void buxton_cynara_cancel(struct bxt_client *client);
+
diff --git a/daemon/daemon.c b/daemon/daemon.c
new file mode 100644
index 0000000..69c5325
--- /dev/null
+++ b/daemon/daemon.c
@@ -0,0 +1,877 @@
+/*
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib-unix.h>
+
+#include "common.h"
+#include "log.h"
+#include "direct.h"
+#include "proto.h"
+#include "serialize.h"
+#include "config.h"
+
+#include "daemon.h"
+#include "socks.h"
+#include "cynara.h"
+
+struct bxt_noti {
+ char *layer_key;
+ GList *clients; /* struct bxt_client */
+};
+
+static gboolean signal_cb(gint fd, GIOCondition cond, gpointer data)
+{
+ struct bxt_daemon *bxtd = data;
+ int r;
+ struct signalfd_siginfo si;
+
+ assert(bxtd);
+
+ r = read(fd, &si, sizeof(struct signalfd_siginfo));
+ if (r == -1) {
+ bxt_err("Read signalfd: %d", errno);
+ return G_SOURCE_REMOVE;
+ }
+
+ if (r != sizeof(struct signalfd_siginfo)) {
+ bxt_err("Invalid siginfo received");
+ return G_SOURCE_CONTINUE;
+ }
+
+ switch (si.ssi_signo) {
+ case SIGINT:
+ case SIGTERM:
+ assert(bxtd->loop);
+ g_main_loop_quit(bxtd->loop);
+ break;
+ case SIGPIPE:
+ bxt_err("SIGPIPE received");
+ break;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static int create_sigfd(void)
+{
+ int r;
+ int fd;
+ sigset_t mask;
+ sigset_t old;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGPIPE);
+
+ r = sigprocmask(SIG_BLOCK, &mask, &old);
+ if (r == -1) {
+ bxt_err("sigprocmask: %d", errno);
+ return -1;
+ }
+
+ fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
+ if (fd == -1) {
+ bxt_err("signalfd: %d", errno);
+ sigprocmask(SIG_SETMASK, &old, NULL);
+ return -1;
+ }
+
+ return fd;
+}
+
+static void remove_noti_cli(struct bxt_daemon *bxtd, struct bxt_client *cli)
+{
+ GList *l;
+ struct bxt_noti *noti;
+
+ for (l = cli->notilist; l; l = g_list_next(l)) {
+ noti = l->data;
+
+ noti->clients = g_list_remove(noti->clients, cli);
+ bxt_dbg("client %p deleted from noti %p list", cli, noti);
+
+ if (!noti->clients) {
+ g_hash_table_remove(bxtd->notis, noti->layer_key);
+ bxt_dbg("noti %p deleted from table", noti);
+ }
+ }
+}
+
+static void free_client(struct bxt_client *cli)
+{
+ if (!cli)
+ return;
+
+ remove_noti_cli(cli->bxtd, cli);
+ g_list_free(cli->notilist);
+ cli->notilist = NULL;
+
+ if (cli->fd_id)
+ g_source_remove(cli->fd_id);
+
+ if (cli->fd != -1)
+ close(cli->fd);
+
+ free(cli->label);
+ free(cli);
+ bxt_dbg("free client %p", cli);
+}
+
+static void remove_notilist(struct bxt_noti *noti)
+{
+ GList *l;
+ struct bxt_client *cli;
+
+ for (l = noti->clients; l; l = g_list_next(l)) {
+ cli = l->data;
+
+ cli->notilist = g_list_remove(cli->notilist, noti);
+ bxt_dbg("noti %p deleted from client %p", noti, cli);
+ }
+}
+
+static void free_noti(struct bxt_noti *noti)
+{
+ if (!noti)
+ return;
+
+ remove_notilist(noti);
+ g_list_free(noti->clients);
+ noti->clients = NULL;
+ free(noti->layer_key);
+
+ free(noti);
+ bxt_dbg("free noti %p", noti);
+}
+
+static gboolean del_client(gpointer data)
+{
+ struct bxt_client *cli = data;
+
+ assert(cli);
+ assert(cli->bxtd);
+ assert(cli->bxtd->clients);
+
+ buxton_cynara_cancel(cli);
+
+ bxt_dbg("Client %p removed", cli);
+ g_hash_table_remove(cli->bxtd->clients, cli);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void send_res(struct bxt_client *cli, struct response *resp)
+{
+ int r;
+ uint8_t *data;
+ int len;
+
+ r = serialz_response(resp->type, resp->msgid, resp->res, resp->val,
+ resp->nmlen, resp->names, &data, &len);
+ if (r == -1) {
+ bxt_err("send res: fd %d msgid %u: serialize error %d",
+ cli->fd, resp->msgid, errno);
+ return;
+ }
+
+ r = proto_send_block(cli->fd, resp->type, data, len);
+
+ free(data);
+
+ if (r == -1)
+ bxt_err("send res: error %d", errno);
+}
+
+static char *get_search_key_u(const struct buxton_layer *layer, const char *key)
+{
+ char uid[16];
+ char *u;
+ const struct layer *ly;
+
+ ly = conf_get_layer(layer->name);
+ if (!ly)
+ return NULL;
+
+ if (ly->type == LAYER_USER) {
+ snprintf(uid, sizeof(uid), "%d", layer->uid);
+ u = uid;
+ } else {
+ u = NULL;
+ }
+
+ return get_search_key(layer, key, u);
+}
+
+static void send_notis(struct bxt_daemon *bxtd, struct request *rqst)
+{
+ int r;
+ char *lykey;
+ struct bxt_noti *noti;
+ GList *l;
+ struct request req;
+ uint8_t *data;
+ int len;
+
+ assert(bxtd);
+ assert(rqst);
+
+ lykey = get_search_key_u(rqst->layer, rqst->key);
+ if (!lykey)
+ return;
+
+ noti = g_hash_table_lookup(bxtd->notis, lykey);
+
+ free(lykey);
+
+ if (!noti)
+ return;
+
+ memset(&req, 0, sizeof(req));
+ req.type = MSG_NOTI;
+ req.layer = rqst->layer;
+ req.key = rqst->key;
+ req.val = rqst->val;
+
+ r = serialz_request(&req, &data, &len);
+ if (r == -1)
+ return;
+
+ for (l = noti->clients; l; l = g_list_next(l)) {
+ struct bxt_client *cli = l->data;
+
+ r = proto_send(cli->fd, req.type, data, len);
+ if (r == -1)
+ bxt_err("send notis: cli %p error %d", cli, errno);
+ }
+
+ free(data);
+}
+
+static void proc_set(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+
+ assert(rqst);
+ assert(resp);
+
+ r = direct_set(rqst->layer, rqst->key, rqst->val);
+ if (r == -1) {
+ resp->res = errno;
+ return;
+ }
+ resp->res = 0;
+
+ send_notis(cli->bxtd, rqst);
+}
+
+static void proc_get(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+ struct buxton_value *val;
+
+ assert(rqst);
+ assert(resp);
+
+ val = calloc(1, sizeof(*val));
+ if (!val) {
+ resp->res = ENOMEM;
+ return;
+ }
+
+ r = direct_get(rqst->layer, rqst->key, val);
+ if (r == -1) {
+ free(val);
+ resp->res = errno;
+ return;
+ }
+
+ resp->res = 0;
+ resp->val = val;
+}
+
+static void proc_list(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+
+ assert(rqst);
+ assert(resp);
+
+ r = direct_list(rqst->layer, &resp->names, &resp->nmlen);
+ resp->res = (r == -1) ? errno : 0;
+}
+
+static void proc_create(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+
+ assert(cli);
+ assert(rqst);
+ assert(resp);
+
+ if (cli->cred.uid != 0) {
+ resp->res = EPERM;
+ return;
+ }
+
+ r = direct_create(rqst->layer, rqst->key, rqst->rpriv, rqst->wpriv,
+ rqst->val);
+ resp->res = (r == -1) ? errno : 0;
+}
+
+static void proc_unset(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+
+ assert(cli);
+ assert(rqst);
+ assert(resp);
+
+ if (cli->cred.uid != 0) {
+ resp->res = EPERM;
+ return;
+ }
+
+ r = direct_unset(rqst->layer, rqst->key);
+ resp->res = (r == -1) ? errno : 0;
+}
+
+static void add_cli(struct bxt_noti *noti, struct bxt_client *client)
+{
+ GList *l;
+
+ for (l = noti->clients; l; l = g_list_next(l)) {
+ if (l->data == client)
+ return;
+ }
+
+ noti->clients = g_list_append(noti->clients, client);
+ client->notilist = g_list_append(client->notilist, noti);
+ bxt_dbg("proc notify: noti %p '%s' client %p added",
+ noti, noti->layer_key, client);
+}
+
+static void proc_notify(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+ char *lykey;
+ struct bxt_noti *noti;
+
+ assert(cli);
+ assert(rqst);
+ assert(resp);
+
+ assert(rqst->layer);
+ assert(rqst->key);
+
+ r = direct_check(rqst->layer, rqst->key);
+ if (r == -1) {
+ resp->res = errno;
+ return;
+ }
+
+ lykey = get_search_key_u(rqst->layer, rqst->key);
+ if (!lykey) {
+ resp->res = errno;
+ return;
+ }
+
+ noti = g_hash_table_lookup(cli->bxtd->notis, lykey);
+ if (!noti) {
+ noti = calloc(1, sizeof(*noti));
+ if (!noti) {
+ resp->res = errno;
+ return;
+ }
+ noti->layer_key = lykey;
+
+ g_hash_table_insert(cli->bxtd->notis, noti->layer_key, noti);
+ bxt_dbg("proc notify: noti %p '%s' added", noti, lykey);
+ } else {
+ free(lykey);
+ }
+
+ add_cli(noti, cli);
+ resp->res = 0;
+}
+
+static void proc_unnotify(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ char *lykey;
+ struct bxt_noti *noti;
+
+ assert(cli);
+ assert(rqst);
+ assert(resp);
+
+ assert(rqst->layer);
+ assert(rqst->key);
+
+ lykey = get_search_key_u(rqst->layer, rqst->key);
+ if (!lykey) {
+ resp->res = errno;
+ return;
+ }
+
+ noti = g_hash_table_lookup(cli->bxtd->notis, lykey);
+
+ free(lykey);
+
+ if (!noti) {
+ resp->res = ENOENT;
+ return;
+ }
+
+ cli->notilist = g_list_remove(cli->notilist, noti);
+ noti->clients = g_list_remove(noti->clients, cli);
+ bxt_dbg("proc notify: noti %p '%s' client %p deleted",
+ noti, noti->layer_key, cli);
+
+ if (!noti->clients) /* no client */
+ g_hash_table_remove(cli->bxtd->notis, lykey);
+
+ resp->res = 0;
+}
+
+static void proc_set_priv(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+ enum buxton_priv_type type;
+
+ assert(cli);
+ assert(rqst);
+ assert(resp);
+
+ if (cli->cred.uid != 0) {
+ resp->res = EPERM;
+ return;
+ }
+
+ if (rqst->type == MSG_SET_WP)
+ type = BUXTON_PRIV_WRITE;
+ else
+ type = BUXTON_PRIV_READ;
+
+ r = direct_set_priv(rqst->layer, rqst->key, type, rqst->val->value.s);
+ resp->res = (r == -1) ? errno : 0;
+}
+
+static void proc_get_priv(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+ enum buxton_priv_type type;
+ struct buxton_value *val;
+
+ assert(rqst);
+ assert(resp);
+
+ val = calloc(1, sizeof(*val));
+ if (!val) {
+ resp->res = ENOMEM;
+ return;
+ }
+
+ if (rqst->type == MSG_GET_WP)
+ type = BUXTON_PRIV_WRITE;
+ else
+ type = BUXTON_PRIV_READ;
+
+ val->type = BUXTON_TYPE_PRIVILEGE;
+ r = direct_get_priv(rqst->layer, rqst->key, type, &val->value.s);
+ if (r == -1) {
+ free(val);
+ resp->res = errno;
+ return;
+ }
+
+ resp->res = 0;
+ resp->val = val;
+}
+
+typedef void (*proc_func)(struct bxt_client *cli,
+ struct request *, struct response *);
+
+static proc_func proc_funcs[MSG_MAX] = {
+ [MSG_SET] = proc_set,
+ [MSG_GET] = proc_get,
+ [MSG_LIST] = proc_list,
+ [MSG_CREAT] = proc_create,
+ [MSG_UNSET] = proc_unset,
+ [MSG_NOTIFY] = proc_notify,
+ [MSG_UNNOTIFY] = proc_unnotify,
+ [MSG_SET_WP] = proc_set_priv,
+ [MSG_SET_RP] = proc_set_priv,
+ [MSG_GET_WP] = proc_get_priv,
+ [MSG_GET_RP] = proc_get_priv,
+};
+
+static void proc_msg(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ assert(cli);
+ assert(rqst);
+ assert(resp);
+
+ if (rqst->type <= MSG_UNKNOWN || rqst->type >= MSG_MAX) {
+ bxt_err("proc msg: invalid type %d", rqst->type);
+ resp->res = EINVAL;
+ return;
+ }
+
+ assert(rqst->layer);
+ if (cli->cred.uid != 0 && cli->cred.uid != rqst->layer->uid) {
+ /* Only root can access other user's */
+ resp->res = EPERM;
+ return;
+ }
+
+ if (!proc_funcs[rqst->type]) {
+ bxt_err("proc msg: %d not supported", rqst->type);
+ resp->res = ENOTSUP;
+ return;
+ }
+
+ proc_funcs[rqst->type](cli, rqst, resp);
+}
+
+static void cyn_cb(struct bxt_client *cli, enum buxton_cynara_res res,
+ void *data)
+{
+ struct request *rqst = data;
+ struct response resp;
+
+ assert(rqst);
+
+ memset(&resp, 0, sizeof(resp));
+ resp.type = rqst->type;
+ resp.msgid = rqst->msgid;
+
+ switch (res) {
+ case BUXTON_CYNARA_ALLOWED:
+ proc_msg(cli, rqst, &resp);
+ break;
+ case BUXTON_CYNARA_DENIED:
+ default:
+ resp.res = EPERM;
+ break;
+ }
+
+ send_res(cli, &resp);
+
+ free_response(&resp);
+ free_request(rqst);
+ free(rqst);
+}
+
+static int check_priv(struct bxt_client *cli, struct request *rqst)
+{
+ int r;
+ enum buxton_priv_type type;
+ char *priv;
+
+ assert(cli);
+ assert(rqst);
+
+ switch (rqst->type) {
+ case MSG_SET:
+ case MSG_GET:
+ case MSG_NOTIFY:
+ if (rqst->type == MSG_SET)
+ type = BUXTON_PRIV_WRITE;
+ else
+ type = BUXTON_PRIV_READ;
+
+ r = direct_get_priv(rqst->layer, rqst->key, type, &priv);
+ if (r == -1) {
+ r = BUXTON_CYNARA_ERROR;
+ break;
+ }
+
+ bxt_dbg("priv '%s'", priv);
+
+ r = buxton_cynara_check(cli, cli->label, "", cli->cred.uid,
+ priv, cyn_cb, rqst);
+ free(priv);
+ break;
+ default:
+ r = BUXTON_CYNARA_ALLOWED;
+ break;
+ }
+
+ return r;
+}
+
+static int proc_serialized_msg(struct bxt_client *cli, uint8_t *data, int len)
+{
+ int r;
+ struct request *rqst;
+ struct response resp;
+
+ rqst = calloc(1, sizeof(*rqst));
+ if (!rqst)
+ return -1;
+
+ r = deserialz_request(data, len, rqst);
+ if (r == -1) {
+ free(rqst);
+ return -1;
+ }
+
+ r = check_priv(cli, rqst);
+
+ /* wait for cynara response, rqst should be freed in callback */
+ if (r == BUXTON_CYNARA_UNKNOWN)
+ return 0;
+
+ memset(&resp, 0, sizeof(resp));
+
+ resp.type = rqst->type;
+ resp.msgid = rqst->msgid;
+
+ if (r != BUXTON_CYNARA_ALLOWED)
+ resp.res = r == BUXTON_CYNARA_DENIED ? EPERM : errno;
+ else
+ proc_msg(cli, rqst, &resp);
+
+ send_res(cli, &resp);
+
+ free_response(&resp);
+ free_request(rqst);
+ free(rqst);
+
+ return 0;
+}
+
+static int proc_client_msg(struct bxt_client *cli)
+{
+ int r;
+ uint8_t *data;
+ int len;
+ enum message_type type;
+
+ r = proto_recv(cli->fd, &type, &data, &len);
+ if (r == -1)
+ return -1;
+
+ switch (type) {
+ case MSG_SET:
+ case MSG_GET:
+ case MSG_CREAT:
+ case MSG_UNSET:
+ case MSG_LIST:
+ case MSG_NOTIFY:
+ case MSG_UNNOTIFY:
+ case MSG_SET_WP:
+ case MSG_SET_RP:
+ case MSG_GET_WP:
+ case MSG_GET_RP:
+ r = proc_serialized_msg(cli, data, len);
+ break;
+ case MSG_NOTI:
+ default:
+ bxt_err("proc msg: Invalid message type %d", type);
+ r = -1;
+ break;
+ }
+
+ free(data);
+
+ return r;
+}
+
+static gboolean client_cb(gint fd, GIOCondition cond, gpointer data)
+{
+ int r;
+ struct bxt_client *cli = data;
+
+ assert(cli);
+
+ bxt_dbg("Client %d: cond %x", fd, cond);
+
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ if (cond & (G_IO_ERR | G_IO_NVAL))
+ bxt_err("Client %d: IO error", fd);
+
+ cli->fd_id = 0;
+ g_idle_add(del_client, cli);
+ return G_SOURCE_REMOVE;
+ }
+
+ if (cli->cred.pid == 0) {
+ sock_get_client_cred(fd, &cli->cred);
+ sock_get_client_label(fd, &cli->label);
+ }
+
+ r = proc_client_msg(cli);
+ if (r == -1) {
+ cli->fd_id = 0;
+ g_idle_add(del_client, cli);
+ return G_SOURCE_REMOVE;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void add_client(struct bxt_daemon *bxtd, int fd)
+{
+ int r;
+ struct bxt_client *cli;
+
+ r = sock_set_client(fd);
+ if (r == -1) {
+ close(fd);
+ return;
+ }
+
+ cli = calloc(1, sizeof(*cli));
+ if (!cli) {
+ bxt_err("Client %d: %d", fd, errno);
+ close(fd);
+ return;
+ }
+
+ cli->fd = fd;
+ cli->bxtd = bxtd;
+
+ cli->fd_id = g_unix_fd_add(fd,
+ G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ client_cb, cli);
+
+ g_hash_table_insert(bxtd->clients, cli, cli);
+ bxt_dbg("Client %p added, fd %d", cli, fd);
+}
+
+static gboolean accept_cb(gint fd, GIOCondition cond, gpointer data)
+{
+ struct bxt_daemon *bxtd = data;
+ int cfd;
+ struct sockaddr sa;
+ socklen_t addrlen;
+
+ assert(bxtd);
+
+ bxt_dbg("Accept: fd %d cond %x", fd, cond);
+
+ cfd = accept(fd, (struct sockaddr *)&sa, &addrlen);
+ if (cfd == -1) {
+ bxt_err("Accept: %d", errno);
+ return G_SOURCE_CONTINUE;
+ }
+
+ add_client(bxtd, cfd);
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void bxt_exit(struct bxt_daemon *bxtd)
+{
+ buxton_cynara_exit();
+
+ if (bxtd->notis)
+ g_hash_table_destroy(bxtd->notis);
+
+ if (bxtd->clients)
+ g_hash_table_destroy(bxtd->clients);
+
+ if (bxtd->loop)
+ g_main_loop_unref(bxtd->loop);
+
+ if (bxtd->sk != -1)
+ close(bxtd->sk);
+
+ direct_exit();
+
+ if (bxtd->sigfd != -1)
+ close(bxtd->sigfd);
+}
+
+static int bxt_init(struct bxt_daemon *bxtd, const char *confpath)
+{
+ int r;
+
+ assert(bxtd);
+
+ bxtd->clients = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ (GDestroyNotify)free_client, NULL);
+ if (!bxtd->clients)
+ return -1;
+
+ bxtd->notis = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify)free_noti);
+ if (!bxtd->notis)
+ return -1;
+
+ bxtd->sigfd = create_sigfd();
+ g_unix_fd_add(bxtd->sigfd, G_IO_IN, signal_cb, bxtd);
+
+ r = direct_init(MODULE_DIR, confpath);
+ if (r == -1)
+ return -1;
+
+ bxtd->sk = sock_get_server(SOCKPATH);
+ if (!bxtd->sk == -1)
+ return -1;
+
+ bxtd->sk_id = g_unix_fd_add(bxtd->sk, G_IO_IN, accept_cb, bxtd);
+
+ buxton_cynara_init();
+
+ bxtd->loop = g_main_loop_new(NULL, FALSE);
+
+ return 0;
+}
+
+int start_daemon(struct bxt_daemon *bxtd, const char *confpath)
+{
+ int r;
+
+ assert(bxtd);
+
+ if (!confpath)
+ confpath = CONFPATH;
+
+ r = bxt_init(bxtd, confpath);
+ if (r == -1) {
+ bxt_exit(bxtd);
+ return EXIT_FAILURE;
+ }
+
+ g_main_loop_run(bxtd->loop);
+ bxt_exit(bxtd);
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/daemon/daemon.h b/daemon/daemon.h
new file mode 100644
index 0000000..ddcb0ca
--- /dev/null
+++ b/daemon/daemon.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+struct bxt_daemon {
+ GMainLoop *loop;
+ int sigfd;
+
+ int sk; /* server socket */
+ guint sk_id; /* source ID for sk */
+
+ GHashTable *clients; /* struct bxt_client */
+ GHashTable *notis; /* struct bxt_noti */
+};
+
+struct bxt_client {
+ int fd;
+ guint fd_id;
+
+ struct ucred cred;
+ char *label;
+
+ GList *notilist; /* struct bxt_noti */
+
+ struct bxt_daemon *bxtd;
+};
+
+int start_daemon(struct bxt_daemon *bxtd, const char *confpath);
+
diff --git a/daemon/main.c b/daemon/main.c
new file mode 100644
index 0000000..1f5d8b1
--- /dev/null
+++ b/daemon/main.c
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "daemon.h"
+
+static const struct option const opts[] = {
+ { "config-file", required_argument, NULL, 'c' },
+ { "foreground", no_argument, NULL, 'f' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+};
+
+static void usage(const char *name)
+{
+ printf(" Usage: %s [OPTION]\n\n", name);
+ printf(" -c, --config-file=[FILE] Path to configuration file\n");
+ printf(" -f, --foreground Don't daemonize\n");
+ printf(" -h, --help Display this help message\n");
+
+ exit(EXIT_FAILURE);
+}
+
+int daemonize(void)
+{
+ pid_t p;
+ int fd;
+ int r;
+
+ p = fork();
+ if (p == -1) {
+ perror("fork");
+ return -1;
+ }
+
+ /* parent exit */
+ if (p)
+ exit(EXIT_SUCCESS);
+
+ /* child process */
+ r = chdir("/");
+ if (r == -1)
+ fprintf(stderr, "chdir failed: %d\n", errno);
+
+ umask(022);
+ setsid();
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd == -1)
+ return 0;
+
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+
+ if (fd > STDERR_FILENO)
+ close(fd);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char const *confpath;
+ int fg;
+ int r;
+ struct bxt_daemon bxtd = {
+ .sigfd = -1,
+ .sk = -1,
+ };
+
+ fg = 0;
+ confpath = NULL;
+
+ while (optind < argc) {
+ int c;
+
+ c = getopt_long(argc, argv, "c:fh", opts, NULL);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'c':
+ confpath = optarg;
+ break;
+ case 'f':
+ fg = 1;
+ break;
+ case 'h':
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (!fg) {
+ r = daemonize();
+ if (r == -1)
+ return EXIT_FAILURE;
+ }
+
+ return start_daemon(&bxtd, confpath);
+}
+
diff --git a/daemon/socks.c b/daemon/socks.c
new file mode 100644
index 0000000..2156a7b
--- /dev/null
+++ b/daemon/socks.c
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <systemd/sd-daemon.h>
+
+#include "log.h"
+
+#include "socks.h"
+
+#define SOCKET_TIMEOUT 5 /* seconds */
+
+static int smack_not_supported;
+
+static int sock_create(const char *path)
+{
+ int r;
+ int fd;
+ struct sockaddr_un sa;
+
+ assert(path && *path);
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ bxt_err("Socket '%s': socket %d", path, errno);
+ return -1;
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, path, sizeof(sa.sun_path));
+ sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
+
+ r = unlink(sa.sun_path);
+ if (r == -1 && errno != ENOENT) {
+ bxt_err("Socket '%s': unlink %d", path, errno);
+ close(fd);
+ return -1;
+ }
+
+ r = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
+ if (r == -1) {
+ bxt_err("Socket '%s': bind %d", path, errno);
+ close(fd);
+ return -1;
+ }
+
+ chmod(sa.sun_path, 0666);
+
+ r = listen(fd, 128);
+ if (r == -1) {
+ bxt_err("Socket '%s': listen %d", path, errno);
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int sock_get_server(const char *path)
+{
+ int n;
+ int i;
+ int r;
+ int fd;
+
+ if (!path || !*path) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ n = sd_listen_fds(0);
+ if (n < 0) {
+ bxt_err("sd_listen_fds: %d", n);
+ return -1;
+ }
+
+ if (n == 0)
+ return sock_create(path);
+
+ fd = -1;
+ for (i = SD_LISTEN_FDS_START; i < SD_LISTEN_FDS_START + n; i++) {
+ r = sd_is_socket_unix(i, SOCK_STREAM, -1, path, 0);
+ if (r > 0) {
+ fd = i;
+ break;
+ }
+ }
+
+ if (fd == -1) {
+ bxt_err("Socket '%s' is not passed", path);
+ return sock_create(path);
+ }
+
+ return fd;
+}
+
+int sock_set_client(int fd)
+{
+ int r;
+ struct timeval tv;
+ int on;
+
+ r = fcntl(fd, F_SETFL, O_NONBLOCK);
+ if (r == -1) {
+ bxt_err("Client %d: set NONBLOCK: %d", fd, errno);
+ return -1;
+ }
+
+ /* need SO_PRIORITY ? */
+
+ tv.tv_sec = SOCKET_TIMEOUT;
+ tv.tv_usec = 0;
+ r = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv,
+ sizeof(struct timeval));
+ if (r == -1) {
+ bxt_err("Client %d: set SO_RCVTIMEO: %d", fd, errno);
+ return -1;
+ }
+
+ on = 1;
+ r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+ if (r == -1)
+ bxt_err("Client %d: set SO_PASSCRED: %d", fd, errno);
+
+ return 0;
+}
+
+int sock_get_client_cred(int fd, struct ucred *cred)
+{
+ int r;
+ socklen_t len;
+
+ if (fd < 0 || !cred) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = sizeof(*cred);
+ r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &len);
+ if (r == -1) {
+ bxt_err("Client %d: get SO_PEERCRED: %d", fd, errno);
+ return -1;
+ }
+
+ bxt_dbg("Client %d: pid %d uid %u gid %u", fd,
+ cred->pid, cred->uid, cred->gid);
+
+ return 0;
+}
+
+int sock_get_client_label(int fd, char **label)
+{
+ int r;
+ socklen_t len;
+ char *l;
+
+ if (fd < 0 || !label) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (smack_not_supported) {
+ *label = NULL;
+ return 0;
+ }
+
+ len = 0;
+ r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, NULL, &len);
+ if (r == 0) {
+ /* Never reach here */
+ bxt_err("Client %d: get SO_PEERSEC: 0", fd);
+ *label = NULL;
+ smack_not_supported = 1;
+ return 0;
+ }
+
+ if (errno == ENOPROTOOPT) {
+ smack_not_supported = 1;
+ *label = NULL;
+ return 0;
+ }
+
+ if (errno != ERANGE) {
+ bxt_err("Client %d: get SO_PEERSEC: %d", fd, errno);
+ return -1;
+ }
+
+ l = calloc(1, len + 1);
+ if (!l)
+ return -1;
+
+ r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, l, &len);
+ if (r == -1) {
+ bxt_err("Cleint %d: get SO_PEERSEC: %d", fd, errno);
+ free(l);
+ return -1;
+ }
+
+ bxt_dbg("Client %d: Label '%s'", fd, l);
+
+ *label = l;
+
+ return 0;
+}
+
diff --git a/daemon/socks.h b/daemon/socks.h
new file mode 100644
index 0000000..46043ce
--- /dev/null
+++ b/daemon/socks.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#define _GNU_SOURCE
+#include <sys/socket.h>
+
+int sock_get_server(const char *path);
+
+int sock_set_client(int fd);
+int sock_get_client_cred(int fd, struct ucred *cred);
+int sock_get_client_label(int fd, char **label);
+