diff options
Diffstat (limited to 'daemon')
-rw-r--r-- | daemon/CMakeLists.txt | 30 | ||||
-rw-r--r-- | daemon/cynara.c | 348 | ||||
-rw-r--r-- | daemon/cynara.h | 46 | ||||
-rw-r--r-- | daemon/daemon.c | 877 | ||||
-rw-r--r-- | daemon/daemon.h | 51 | ||||
-rw-r--r-- | daemon/main.c | 133 | ||||
-rw-r--r-- | daemon/socks.c | 232 | ||||
-rw-r--r-- | daemon/socks.h | 29 |
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); + |