diff options
author | José Bollo <jose.bollo@iot.bzh> | 2018-09-11 11:28:34 +0200 |
---|---|---|
committer | Jose Bollo <jose.bollo@iot.bzh> | 2018-09-17 13:17:56 +0200 |
commit | 7ae3477d84ae13c5e9ef9bb1980a8f70f8b7e9bf (patch) | |
tree | 46c1ddda9a4dcc4782ceab8deb2f891ad3b0f2c8 | |
parent | 11654afcb5753a54a033db12e1ed4a19b3f7c86e (diff) |
Make it work for AGL
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
-rw-r--r-- | CMakeLists.txt | 27 | ||||
-rw-r--r-- | cynara.initial | 4 | ||||
-rw-r--r-- | src/CMakeLists.txt | 29 | ||||
-rw-r--r-- | src/cache.c | 241 | ||||
-rw-r--r-- | src/cache.h | 17 | ||||
-rw-r--r-- | src/cyn.c | 94 | ||||
-rw-r--r-- | src/cyn.h | 22 | ||||
-rw-r--r-- | src/db.c | 110 | ||||
-rw-r--r-- | src/db.h | 32 | ||||
-rw-r--r-- | src/fbuf.c | 162 | ||||
-rw-r--r-- | src/fbuf.h | 40 | ||||
-rw-r--r-- | src/lib-compat.c | 43 | ||||
-rw-r--r-- | src/main-cynarad.c | 488 | ||||
-rw-r--r-- | src/prot.c | 17 | ||||
-rw-r--r-- | src/prot.h | 17 | ||||
-rw-r--r-- | src/queue.c | 17 | ||||
-rw-r--r-- | src/queue.h | 17 | ||||
-rw-r--r-- | src/rcyn-client.c | 72 | ||||
-rw-r--r-- | src/rcyn-client.h | 17 | ||||
-rw-r--r-- | src/rcyn-protocol.c | 17 | ||||
-rw-r--r-- | src/rcyn-protocol.h | 17 | ||||
-rw-r--r-- | src/rcyn-server.c | 256 | ||||
-rw-r--r-- | src/rcyn-server.h | 50 | ||||
-rw-r--r-- | src/socket.c | 2 | ||||
-rw-r--r-- | src/test-lib-compat.c | 19 | ||||
-rw-r--r-- | systemd/CMakeLists.txt | 32 | ||||
-rw-r--r-- | systemd/cynara-admin.socket.in | 2 | ||||
-rw-r--r-- | systemd/cynara-check.socket.in | 2 | ||||
-rw-r--r-- | systemd/cynara.service | 11 |
29 files changed, 1591 insertions, 283 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a3c9f1..d4ed7c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,13 +23,16 @@ PROJECT(cynara C) SET(PROJECT_NAME "Cynara") SET(PROJECT_PRETTY_NAME "Permission database") SET(PROJECT_DESCRIPTION "Secured permission database for applications") -SET(PROJECT_VERSION "1.99.RC1") +SET(PROJECT_VERSION "1.99.99") set(PROJECT_URL "https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/cynara.git;a=summary") INCLUDE(FindPkgConfig) INCLUDE(CheckIncludeFiles) INCLUDE(CheckLibraryExists) INCLUDE(GNUInstallDirs) +if(NOT CMAKE_INSTALL_FULL_RUNSTATEDIR) + set(CMAKE_INSTALL_FULL_RUNSTATEDIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/run") +endif() INCLUDE(CTest) ########################################################################### @@ -41,6 +44,13 @@ set(CYNARA_SOVERSION 1.99) add_definitions(-DCYNARA_VERSION="${CYNARA_VERSION}") set(SYSTEMD ON CACHE BOOL "should use systemd") +set(DEFAULT_DB_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/cynara" + CACHE PATH "directory path of the database") +set(DEFAULT_SOCKET_DIR "${CMAKE_INSTALL_FULL_RUNSTATEDIR}" + CACHE PATH "directory path of the sockets") +set(DEFAULT_CONFIG_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/security" + CACHE PATH "directory of configuration") +set(DEFAULT_INIT_FILE "${DEFAULT_CONFIG_DIR}/cynara.initial") ########################################################################### @@ -65,18 +75,15 @@ set(CMAKE_C_FLAGS_CCOV "-g -O2 --coverage") if(SYSTEMD) PKG_CHECK_MODULES(libsystemd REQUIRED libsystemd>=222) -endif() - -if(SYSTEMD) - set(SOCKET_DIR "/run/platform" - CACHE PATH "path of the socket system directories") - set(SYSTEMD_UNIT_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/systemd/system" - CACHE PATH "Path to systemd system unit files") - set(CHECK_SOCKET_SPEC "unix:${SOCKET_DIR}/cynara.check") - set(ADMIN_SOCKET_SPEC "unix:${SOCKET_DIR}/cynara.admin") add_subdirectory(systemd) endif() + add_subdirectory(include) add_subdirectory(src) add_subdirectory(pkgconfig) +install(FILES + ${CMAKE_CURRENT_SOURCE_DIR}/cynara.initial + DESTINATION + ${DEFAULT_CONFIG_DIR} +) diff --git a/cynara.initial b/cynara.initial new file mode 100644 index 0000000..96054d3 --- /dev/null +++ b/cynara.initial @@ -0,0 +1,4 @@ +# initial database for cynara +System * * * 1 +User * * * 1 + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fa3724c..4363234 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,7 @@ set(SERVER_SOURCES cyn.c db.c fbuf.c + main-cynarad.c prot.c queue.c rcyn-protocol.c @@ -40,6 +41,14 @@ set(LIB_SOURCES # build and install cynarad ########################################### add_executable(cynarad ${SERVER_SOURCES}) +target_compile_definitions(cynarad PRIVATE + DEFAULT_DB_DIR="${DEFAULT_DB_DIR}" + DEFAULT_SOCKET_DIR="${DEFAULT_SOCKET_DIR}" + DEFAULT_INIT_FILE="${DEFAULT_INIT_FILE}" + RCYN_DEFAULT_CHECK_SOCKET_SPEC="unix:${DEFAULT_SOCKET_DIR}/cynara.check" + RCYN_DEFAULT_ADMIN_SOCKET_SPEC="unix:${DEFAULT_SOCKET_DIR}/cynara.admin" +) +target_link_libraries(cynarad cap) install(TARGETS cynarad RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) @@ -47,6 +56,10 @@ install(TARGETS cynarad # build and install libcynara ########################################### ADD_LIBRARY(cynara SHARED ${LIB_SOURCES}) +target_compile_definitions(cynara PRIVATE + RCYN_DEFAULT_CHECK_SOCKET_SPEC="unix:${DEFAULT_SOCKET_DIR}/cynara.check" + RCYN_DEFAULT_ADMIN_SOCKET_SPEC="unix:${DEFAULT_SOCKET_DIR}/cynara.admin" +) SET_TARGET_PROPERTIES(cynara PROPERTIES VERSION ${CYNARA_VERSION} SOVERSION ${CYNARA_SOVERSION}) @@ -71,25 +84,9 @@ INSTALL(TARGETS test-cynara ########################################### if(SYSTEMD) - target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_CHECK_SOCKET_SPEC="sd:check") - target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_ADMIN_SOCKET_SPEC="sd:admin") target_compile_definitions(cynarad PRIVATE WITH_SYSTEMD_ACTIVATION) target_link_libraries(cynarad ${libsystemd_LDFLAGS} ${libsystemd_LINK_LIBRARIES}) target_include_directories(cynarad PRIVATE ${libsystemd_INCLUDE_DIRS}) target_compile_options(cynarad PRIVATE ${libsystemd_CFLAGS}) -else() - if(CHECK_SOCKET_SPEC) - target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_CHECK_SOCKET_SPEC="${CHECK_SOCKET_SPEC}") - endif() - if(ADMIN_SOCKET_SPEC) - target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_ADMIN_SOCKET_SPEC="${ADMIN_SOCKET_SPEC}") - endif() -endif() - -if(CHECK_SOCKET_SPEC) - target_compile_definitions(cynara PRIVATE RCYN_DEFAULT_CHECK_SOCKET_SPEC="${CHECK_SOCKET_SPEC}") -endif() -if(ADMIN_SOCKET_SPEC) - target_compile_definitions(cynara PRIVATE RCYN_DEFAULT_ADMIN_SOCKET_SPEC="${ADMIN_SOCKET_SPEC}") endif() diff --git a/src/cache.c b/src/cache.c index c2db00d..8d15728 100644 --- a/src/cache.c +++ b/src/cache.c @@ -1,17 +1,48 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 <stdint.h> #include <string.h> #include <errno.h> +#include <ctype.h> #include "cache.h" +/** + * The cache structure is a blob of memory ('content') + * of 'count' bytes of only 'used' bytes. + * That blob containts at sequence of records of variable length + * Each records holds the following values in that given order: + * - length: 2 bytes unsigned integer LSB first, MSB second + * - hit count: 1 byte unsigned integer + * - value: 1 byte unsigned integer + * - client: zero terminated string + * - session: zero terminated string + * - user: zero terminated string + * - permission: zero terminated string + * - + */ struct cache { - uint32_t begin; uint32_t used; uint32_t count; - char content[1]; + uint8_t content[1]; }; static @@ -20,57 +51,129 @@ lenat( cache_t *cache, uint32_t pos ) { - uint32_t p, n; - char c; + return ((uint32_t)cache->content[pos]) | (((uint32_t)cache->content[pos + 1]) << 8); +} - p = pos + 1; - if (p >= cache->count) - p -= cache->count; - n = 4; - while (n--) { - do { - c = cache->content[p++]; - if (p >= cache->count) - p -= cache->count; - } while(c); - } - return (p > pos ? p : (p + cache->count)) - pos; +static +void +drop_at( + cache_t *cache, + uint32_t pos +) { + uint32_t e, l; + + l = lenat(cache, pos); + e = pos + l; + cache->used -= l; + if (cache->used > e) + memmove(&cache->content[pos], &cache->content[e], cache->used - e); } static void -drop_one( +drop_lre( cache_t *cache ) { - uint32_t l = lenat(cache, cache->begin); - cache->used -= l; - cache->begin += l; - if (cache->begin > cache->count) - cache->begin -= cache->count; + uint32_t found = 0, iter = 0; + uint8_t hmin = 255, hint; + + while (iter < cache->used) { + hint = cache->content[iter + 2]; + if (hint < hmin) + found = iter; + iter += lenat(cache, iter); + } + if (found < cache->used) + drop_at(cache, found); } static void -addc( +hit( cache_t *cache, - char c + uint32_t pos ) { - uint32_t pos; - if (cache->used == cache->count) - drop_one(cache); - pos = cache->begin + cache->used++; - if (pos > cache->count) - pos -= cache->count; - cache->content[pos < cache->count ? pos : pos - cache->count] = c; + uint32_t iter = 0; + uint8_t hint; + + while (iter < cache->used) { + if (iter == pos) + hint = 255; + else { + hint = cache->content[iter + 2]; + if (hint) + hint--; + } + cache->content[iter + 2] = hint; + iter += lenat(cache, iter); + } } static -void -adds( +const char* +cmpi( + const char *head, + const char *other +) { + char c; + while(toupper(c = *head++) == toupper(*other++)) + if (!c) + return head; + return 0; +} + +static +const char* +cmp( + const char *head, + const char *other +) { + char c; + while((c = *head++) == *other++) + if (!c) + return head; + return 0; +} + + +static +int +match( + const char *head, + const char *client, + const char *session, + const char *user, + const char *permission +) { + head = cmp(head, client); + if (head) + head = cmp(head, session); + if (head) + head = cmp(head, user); + if (head) + head = cmpi(head, permission); + return !!head; +} + +static +uint32_t +search( cache_t *cache, - const char *s + const char *client, + const char *session, + const char *user, + const char *permission ) { - do { addc(cache, *s); } while(*s++); + char *txt; + uint32_t iter = 0; + + while (iter < cache->used) { + txt = (char*)&cache->content[iter + 4]; + if (match(txt, client, session, user, permission)) + return iter; + iter += lenat(cache, iter); + } + return iter; } int @@ -82,17 +185,35 @@ cache_put( const char *permission, int value ) { - if (cache == NULL - || strlen(client) + strlen(session) - + strlen(user) + strlen(permission) - + 5 > cache->count) + uint32_t pos; + size_t size, scli, sses, susr, sper; + + if (cache == NULL || value < 0 || value > 255) return -EINVAL; - addc(cache, (char)value); - adds(cache, client); - adds(cache, session); - adds(cache, user); - adds(cache, permission); + pos = search(cache, client, session, user, permission); + if (pos < cache->used) + cache->content[pos + 3] = (uint8_t)value; + else { + scli = strlen(client); + sses = strlen(session); + susr = strlen(user); + sper = strlen(permission); + size = scli + sses + susr + sper + 8; + if (size > 65535) + return -EINVAL; + if (size > cache->count) + return -ENOMEM; + while(cache->used + (uint32_t)size > cache->count) + drop_lre(cache); + pos = cache->used; + cache->content[pos + 0] = (uint8_t)(size & 255); + cache->content[pos + 1] = (uint8_t)((size >> 8) & 255); + cache->content[pos + 2] = (uint8_t)255; + cache->content[pos + 3] = (uint8_t)value; + stpcpy(1 + stpcpy(1 + stpcpy(1 + stpcpy((char*)&cache->content[pos + 4], client), session), user), permission); + cache->used += (uint32_t)size; + } return 0; } @@ -104,6 +225,13 @@ cache_search( const char *user, const char *permission ) { + uint32_t pos; + + pos = search(cache, client, session, user, permission); + if (pos < cache->used) { + hit(cache, pos); + return (int)cache->content[pos + 3]; + } return -ENOENT; } @@ -111,10 +239,8 @@ void cache_clear( cache_t *cache ) { - if (cache) { + if (cache) cache->used = 0; - cache->begin = 0; - } } int @@ -124,29 +250,18 @@ cache_resize( ) { cache_t *c = *cache, *nc; - while (c && c->used > newsize) - drop_one(c); + if (c) + while (c->used > newsize) + drop_lre(c); - nc = malloc(newsize - 1 + sizeof *c); + nc = realloc(c, newsize - 1 + sizeof *c); if (nc == NULL) return -ENOMEM; - nc->begin = 0; nc->count = newsize; - if (!c || c->used == 0) + if (!c) nc->used = 0; - else { - if (c->begin + c->used <= c->count) - memcpy(&nc->content[0], &c->content[c->begin], c->used); - else { - memcpy(&nc->content[0], &c->content[c->begin], c->count - c->begin); - memcpy(&nc->content[c->count - c->begin], &c->content[0], c->used + c->begin - c->count); - } - - nc->used = c->used; - } *cache = nc; - free(c); return 0; } diff --git a/src/cache.h b/src/cache.h index 2f3a9a1..5cd827b 100644 --- a/src/cache.h +++ b/src/cache.h @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 struct cache; @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 @@ -68,27 +85,15 @@ static int changed( ) { - int rc = db_sync(); + int rc; struct callback *c; + rc = db_sync(); for (c = observers; c ; c = c->next) c->callback(c->closure); return rc; } -int -cyn_init( -) { - /* TODO: paths? */ - int rc = db_open("/var/lib/cynara/cynara.names", "/var/lib/cynara/cynara.rules"); - if (rc == 0 && db_is_empty()) { - /* TODO: init? */ - rc = db_set("System", "*", "*", "*", 1); - db_sync(); - } - return rc; -} - /** enter critical recoverable section */ int cyn_enter( @@ -155,10 +160,17 @@ cyn_leave( return -EPERM; lock = &lock; - if (commit) - rc = queue_play() ?: changed(); - else + if (!commit) rc = 0; + else { + rc = queue_play(); + if (rc < 0) + db_recover(); + else { + rc = db_backup(); + changed(); + } + } queue_clear(); e = awaiters; @@ -187,10 +199,9 @@ cyn_set( const char *permission, uint32_t value ) { - if (lock && lock != &lock) - return queue_set(client, session, user, permission, value); - - return db_set(client, session, user, permission, value) ?: changed(); + if (!lock) + return -EPERM; + return queue_set(client, session, user, permission, value); } int @@ -200,10 +211,27 @@ cyn_drop( const char *user, const char *permission ) { - if (lock && lock != &lock) - return queue_drop(client, session, user, permission); + if (!lock) + return -EPERM; + return queue_drop(client, session, user, permission); +} - return db_drop(client, session, user, permission) ?: changed(); +void +cyn_list( + void *closure, + void (*callback)( + void *closure, + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value), + const char *client, + const char *session, + const char *user, + const char *permission +) { + db_for_all(closure, callback, client, session, user, permission); } int @@ -224,24 +252,6 @@ cyn_test( return rc; } -void -cyn_list( - void *closure, - void (*callback)( - void *closure, - const char *client, - const char *session, - const char *user, - const char *permission, - uint32_t value), - const char *client, - const char *session, - const char *user, - const char *permission -) { - db_for_all(closure, callback, client, session, user, permission); -} - int cyn_check_async( void (*check_cb)(void *closure, uint32_t value), @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 @@ -6,11 +23,6 @@ #define ASK 2 #define DEFAULT DENY -extern -int -cyn_init( -); - /** enter critical recoverable section */ extern int @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 @@ -7,9 +24,9 @@ #include <stdint.h> #include <stdbool.h> #include <stdalign.h> +#include <stdio.h> #include <string.h> #include <errno.h> -#include <syslog.h> #include "fbuf.h" #include "db.h" @@ -49,6 +66,10 @@ typedef struct session session_t; * - rules: the rules based on name indexes as 32bits indexes * These files are normally in /var/lib/cynara */ +#if !defined(DEFAULT_DB_DIR) +# define DEFAULT_DB_DIR "/var/lib/cynara" +#endif +static const char db_default_directory[] = DEFAULT_DB_DIR; /** the file for the names */ static fbuf_t fnames; @@ -161,7 +182,7 @@ db_get_name_index( if (!(up & 1023)) { p = realloc(names_sorted, (up + 1024) * sizeof *names_sorted); if (p == NULL) { - syslog(LOG_ERR, "out of memory"); + fprintf(stderr, "out of memory"); return -1; } names_sorted = p; @@ -199,7 +220,7 @@ init_names( p = realloc(ns, all * sizeof *ns); if (p == NULL) { free(ns); - syslog(LOG_ERR, "out of memory"); + fprintf(stderr, "out of memory"); goto error; } ns = p; @@ -228,7 +249,7 @@ init_names( return 0; bad_file: - syslog(LOG_ERR, "bad file %s", fnames.name); + fprintf(stderr, "bad file %s", fnames.name); errno = ENOEXEC; error: return -1; @@ -392,21 +413,53 @@ init_rules( sessions.count = (frules.used - uuidlen) / sizeof *sessions.rules; } +/** open a fbuf */ +static +int +open_identify( + fbuf_t *fb, + const char *directory, + const char *name, + const char *id, + uint32_t idlen +) { + int rc; + char *file, *backup; + + rc = asprintf(&file, "%s/%s", directory, name); + if (rc < 0) + rc = -ENOMEM; + else { + rc = asprintf(&backup, "%s~", file); + if (rc < 0) + rc = -ENOMEM; + else { + rc = fbuf_open_identify(fb, file, backup, id, idlen); + free(backup); + } + free(file); + } + return rc; +} + /** open the database for files 'names' and 'rules' (can be NULL) */ int db_open( - const char *names, - const char *rules + const char *directory ) { int rc; + /* provide default directory */ + if (directory == NULL) + directory = db_default_directory; + /* open the names */ - rc = fbuf_open_identify(&fnames, names ?: "cynara.names", uuid_names_v1, uuidlen); + rc = open_identify(&fnames, directory, "cynara.names", uuid_names_v1, uuidlen); if (rc < 0) goto error; /* open the rules */ - rc = fbuf_open_identify(&frules, rules ?: "cynara.rules", uuid_rules_v1, uuidlen); + rc = open_identify(&frules, directory, "cynara.rules", uuid_rules_v1, uuidlen); if (rc < 0) goto error; @@ -450,6 +503,47 @@ db_sync( return rc; } +/** make a backup of the database */ +int +db_backup( +) { + int rc; + + assert(fnames.name && frules.name); + rc = fbuf_backup(&fnames); + if (rc == 0) + rc = fbuf_backup(&frules); + return rc; +} + +/** recover the database from latest backup */ +int +db_recover( +) { + int rc; + + assert(fnames.name && frules.name); + + rc = fbuf_recover(&fnames); + if (rc < 0) + goto error; + + rc = fbuf_recover(&frules); + if (rc < 0) + goto error; + + rc = init_names(); + if (rc < 0) + goto error; + + init_rules(); + return 0; +error: + fprintf(stderr, "db recovering impossible: %m"); + exit(5); + return rc; +} + /** enumerate */ void db_for_all( @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 MAX_NAME_LENGTH 32767 @@ -6,8 +23,7 @@ extern int db_open( - const char *names, - const char *rules + const char *directory ); /** close the database */ @@ -50,6 +66,18 @@ db_get_name_index( bool needed ); +/** make a backup of the database */ +extern +int +db_backup( +); + +/** recover the database from latest backup */ +extern +int +db_recover( +); + /** enumerate */ extern void @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 @@ -6,6 +23,7 @@ #include <stdlib.h> #include <stdint.h> #include <stdbool.h> +#include <stdio.h> #include <string.h> #include <limits.h> #include <unistd.h> @@ -14,7 +32,6 @@ #include <sys/file.h> #include <sys/types.h> #include <sys/stat.h> -#include <syslog.h> #include "fbuf.h" @@ -31,15 +48,27 @@ get_asz( int fbuf_open( fbuf_t *fb, - const char *name + const char *name, + const char *backup ) { struct stat st; - int fd, rc; + int fd, fdback, rc; uint32_t sz, asz; void *buffer; + /* open the backup */ + if (backup == NULL) + fdback = -1; + else { + fdback = open(backup, O_RDWR|O_CREAT, 0600); + if (fdback < 0) + goto error; + rc = flock(fdback, LOCK_EX|LOCK_NB); + if (rc < 0) + goto error; + } + /* open the file */ - //fd = open(name, O_RDWR|O_CREAT|O_SYNC|O_DIRECT, 0600); fd = open(name, O_RDWR|O_CREAT, 0600); if (fd < 0) goto error; @@ -71,12 +100,17 @@ fbuf_open( if (read(fd, buffer, (size_t)sz) != (ssize_t)sz) goto error4; + /* save name */ + fb->name = strdup(name); + if (fb->name == NULL) + goto error4; + /* done */ - fb->name = name; fb->buffer = buffer; fb->saved = fb->used = fb->size = sz; fb->capacity = asz; fb->fd = fd; + fb->backup = fdback; return 0; error4: @@ -86,9 +120,14 @@ error3: error2: close(fd); error: - syslog(LOG_ERR, "can't open file %s: %m", name); + rc = -errno; + fprintf(stderr, "can't open file %s: %m", name); + if (fdback >= 0) { + flock(fdback, LOCK_UN); + close(fdback); + } memset(fb, 0, sizeof *fb); - return -errno; + return rc; } /** close the file 'fb' */ @@ -96,9 +135,14 @@ void fbuf_close( fbuf_t *fb ) { + free(fb->name); free(fb->buffer); flock(fb->fd, LOCK_UN); close(fb->fd); + if (fb->backup >= 0) { + flock(fb->backup, LOCK_UN); + close(fb->backup); + } memset(fb, 0, sizeof *fb); } @@ -110,25 +154,19 @@ fbuf_write( uint32_t count, uint32_t offset ) { - off_t rco; ssize_t rcs; /* don't call me for nothing */ assert(count); - /* set write position */ - rco = lseek(fb->fd, (off_t)offset, SEEK_SET); - if (rco != (off_t)offset) - goto error; - /* effective write */ - rcs = write(fb->fd, buffer, (size_t)count); + rcs = pwrite(fb->fd, buffer, (size_t)count, (off_t)offset); if (rcs != (ssize_t)count) goto error; return 0; error: - syslog(LOG_ERR, "write of file %s failed: %m", fb->name); + fprintf(stderr, "write of file %s failed: %m", fb->name); return -errno; } @@ -167,7 +205,7 @@ fbuf_sync( return 0; error: - syslog(LOG_ERR, "sync of file %s failed: %m", fb->name); + fprintf(stderr, "sync of file %s failed: %m", fb->name); return -errno; } @@ -184,7 +222,7 @@ fbuf_ensure_capacity( capacity = get_asz(count); buffer = realloc(fb->buffer, capacity); if (buffer == NULL) { - syslog(LOG_ERR, "alloc %u for file %s failed: %m", capacity, fb->name); + fprintf(stderr, "alloc %u for file %s failed: %m", capacity, fb->name); return -ENOMEM; } fb->buffer = buffer; @@ -254,7 +292,7 @@ fbuf_identify( /* bad identification */ errno = ENOKEY; - syslog(LOG_ERR, "identification of file %s failed: %m", fb->name); + fprintf(stderr, "identification of file %s failed: %m", fb->name); return -ENOKEY; } @@ -263,12 +301,13 @@ int fbuf_open_identify( fbuf_t *fb, const char *name, + const char *backup, const char *id, uint32_t idlen ) { int rc; - rc = fbuf_open(fb, name); + rc = fbuf_open(fb, name, backup); if (rc == 0) { rc = fbuf_identify(fb, id, idlen); if (rc < 0) @@ -277,3 +316,88 @@ fbuf_open_identify( return rc; } + +/* On versions of glibc before 2.27, we must invoke copy_file_range() + using syscall(2) */ +#include <features.h> +#if (__GLIBC__ < 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 27) +#include <syscall.h> +static +loff_t +copy_file_range( + int fd_in, + loff_t *off_in, + int fd_out, + loff_t *off_out, + size_t len, + unsigned int flags +) { + loff_t rc; + + rc = syscall(__NR_copy_file_range, fd_in, off_in, fd_out, + off_out, len, flags); + return rc; +} +#endif + + +/** make a backup */ +int +fbuf_backup( + fbuf_t *fb +) { + int rc; + size_t sz; + ssize_t wsz; + loff_t ino, outo; + + ino = outo = 0; + sz = fb->size; + for (;;) { + wsz = copy_file_range(fb->fd, &ino, fb->backup, &outo, sz, 0); + if (wsz < 0) { + if (errno != EINTR) + return -errno; + } else { + sz -= (size_t)wsz; + if (sz == 0) { + rc = ftruncate(fb->backup, outo); + if (rc == 0) + rc = fsync(fb->backup); + return rc < 0 ? -errno : 0; + } + } + } +} + +/** recover from latest backup */ +int +fbuf_recover( + fbuf_t *fb +) { + ssize_t ssz; + struct stat st; + int rc; + + /* get the size */ + rc = fstat(fb->backup, &st); + if (rc < 0) + return -errno; + + /* ensure space */ + if (st.st_size > UINT32_MAX) + return -EFBIG; + rc = fbuf_ensure_capacity(fb, (uint32_t)st.st_size); + if (rc < 0) + return rc; + + /* read it */ + ssz = pread(fb->backup, fb->buffer, (size_t)st.st_size, 0); + if (ssz < 0) + return -errno; + + fb->used = (uint32_t)st.st_size; + fb->saved = fb->size = 0; /* ensure rewrite of restored data */ + return 0; +} + @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 /** @@ -6,7 +23,7 @@ struct fbuf { /** filename for messages */ - const char *name; + char *name; /** in memory copy of the file */ void *buffer; @@ -25,18 +42,22 @@ struct fbuf /** opened file descriptor for the file */ int fd; + + /** opened file descriptor for the backup */ + int backup; }; /** short type */ typedef struct fbuf fbuf_t; -/** open in 'fb' the file of 'name' */ +/** open in 'fb' the file of 'name' and optionnal 'backup' name */ extern int fbuf_open( fbuf_t *fb, - const char *name + const char *name, + const char *backup ); /** close the file 'fb' */ @@ -105,7 +126,20 @@ int fbuf_open_identify( fbuf_t *fb, const char *name, + const char *backup, const char *id, uint32_t idlen ); +extern +int +fbuf_backup( + fbuf_t *fb +); + +extern +int +fbuf_recover( + fbuf_t *fb +); + diff --git a/src/lib-compat.c b/src/lib-compat.c index 5c6c4e2..794487d 100644 --- a/src/lib-compat.c +++ b/src/lib-compat.c @@ -1,4 +1,21 @@ /* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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. + */ + +/* cynara_admin_initialize(&m_CynaraAdmin), cynara_admin_finish(m_CynaraAdmin); cynara_admin_set_policies(m_CynaraAdmin, pp_policies.data()), @@ -321,20 +338,26 @@ int cynara_admin_list_policies_descriptions(struct cynara_admin *p_cynara_admin, } /************************************* CLIENT-ASYNC **************************************/ +struct cynara_async_configuration { uint32_t szcache; }; int cynara_async_configuration_create(cynara_async_configuration **pp_conf) { - *pp_conf = (cynara_async_configuration*)pp_conf; + *pp_conf = malloc(sizeof(cynara_async_configuration)); + if (*pp_conf == NULL) + return CYNARA_API_OUT_OF_MEMORY; + (*pp_conf)->szcache = 0; return CYNARA_API_SUCCESS; } void cynara_async_configuration_destroy(cynara_async_configuration *p_conf) { + free(p_conf); } int cynara_async_configuration_set_cache_size(cynara_async_configuration *p_conf, size_t cache_size) { + p_conf->szcache = cache_size > 1000000 ? 1000000 : (uint32_t)cache_size; return CYNARA_API_SUCCESS; } @@ -385,7 +408,7 @@ int cynara_async_initialize(cynara_async **pp_cynara, const cynara_async_configu if (p_cynara == NULL) ret = CYNARA_API_OUT_OF_MEMORY; else { - ret = from_status(rcyn_open(&p_cynara->rcyn, rcyn_Check, 0)); + ret = from_status(rcyn_open(&p_cynara->rcyn, rcyn_Check, p_conf ? p_conf->szcache : 0)); if (ret != CYNARA_API_SUCCESS) free(p_cynara); else { @@ -437,7 +460,7 @@ static void reqcb(void *closure, int status) *p = req->next; if (!req->canceled) - req->callback(req->id, CYNARA_CALL_CAUSE_ANSWER, status, req->user_response_data); + req->callback(req->id, CYNARA_CALL_CAUSE_ANSWER, from_check_status(status), req->user_response_data); free(req); } @@ -506,24 +529,32 @@ int cynara_async_cancel_request(cynara_async *p_cynara, cynara_check_id check_id /************************************* CLIENT **************************************/ +struct cynara_configuration { uint32_t szcache; }; + int cynara_configuration_create(cynara_configuration **pp_conf) { - *pp_conf = (cynara_configuration*)pp_conf; + *pp_conf = malloc(sizeof(cynara_configuration)); + if (*pp_conf == NULL) + return CYNARA_API_OUT_OF_MEMORY; + (*pp_conf)->szcache = 0; return CYNARA_API_SUCCESS; } void cynara_configuration_destroy(cynara_configuration *p_conf) { + free(p_conf); } -int cynara_configuration_set_cache_size(cynara_configuration *p_conf, size_t cache_size) +int cynara_configuration_set_cache_size(cynara_configuration *p_conf, + size_t cache_size) { + p_conf->szcache = cache_size > 1000000 ? 1000000 : (uint32_t)cache_size; return CYNARA_API_SUCCESS; } int cynara_initialize(cynara **pp_cynara, const cynara_configuration *p_conf) { - return from_status(rcyn_open((rcyn_t**)pp_cynara, rcyn_Check, 0)); + return from_status(rcyn_open((rcyn_t**)pp_cynara, rcyn_Check, p_conf ? p_conf->szcache : 0)); } int cynara_finish(cynara *p_cynara) diff --git a/src/main-cynarad.c b/src/main-cynarad.c new file mode 100644 index 0000000..ba7d71c --- /dev/null +++ b/src/main-cynarad.c @@ -0,0 +1,488 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <limits.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/capability.h> + +#if defined(WITH_SYSTEMD_ACTIVATION) +#include <systemd/sd-daemon.h> +#endif + +#include "db.h" +#include "cyn.h" +#include "rcyn-server.h" + +#if !defined(DEFAULT_DB_DIR) +# define DEFAULT_DB_DIR "/var/lib/cynara" +#endif +#if !defined(DEFAULT_SOCKET_DIR) +# define DEFAULT_SOCKET_DIR "/var/run/cynara" +#endif +#if !defined(DEFAULT_INIT_FILE) +# define DEFAULT_INIT_FILE "/etc/security/cynara.initial" +#endif +#if !defined(DEFAULT_CYNARA_USER) +# define DEFAULT_CYNARA_USER NULL +#endif +#if !defined(DEFAULT_CYNARA_GROUP) +# define DEFAULT_CYNARA_GROUP NULL +#endif + +#define _DBDIR_ 'd' +#define _GROUP_ 'g' +#define _HELP_ 'h' +#define _INIT_ 'i' +#define _MAKEDBDIR_ 'm' +#define _MAKESOCKDIR_ 'M' +#define _OWNSOCKDIR_ 'O' +#define _OWNDBDIR_ 'o' +#define _SOCKETDIR_ 'S' +#define _SYSTEMD_ 's' +#define _USER_ 'u' +#define _VERSION_ 'v' + +static +const char +shortopts[] = "d:g:hi:mMOoS:u:v" +#if defined(WITH_SYSTEMD_ACTIVATION) + "s" +#endif +; + +static +const struct option +longopts[] = { + { "dbdir", 1, NULL, _DBDIR_ }, + { "group", 1, NULL, _GROUP_ }, + { "help", 0, NULL, _HELP_ }, + { "init", 1, NULL, _INIT_ }, + { "make-db-dir", 0, NULL, _MAKEDBDIR_ }, + { "make-socket-dir", 0, NULL, _MAKESOCKDIR_ }, + { "own-db-dir", 0, NULL, _OWNDBDIR_ }, + { "own-socket-dir", 0, NULL, _OWNSOCKDIR_ }, + { "socketdir", 1, NULL, _SOCKETDIR_ }, +#if defined(WITH_SYSTEMD_ACTIVATION) + { "systemd", 0, NULL, _SYSTEMD_ }, +#endif + { "user", 1, NULL, _USER_ }, + { "version", 0, NULL, _VERSION_ }, + { NULL, 0, NULL, 0 } +}; + +static +const char +helptxt[] = + "\n" + "usage: cynarad [options]...\n" + "\n" + "otpions:\n" +#if defined(WITH_SYSTEMD_ACTIVATION) + " -s, --systemd socket activation by systemd\n" +#endif + " -u, --user xxx set the user\n" + " -g, --group xxx set the group\n" + " -i, --init xxx initialize if needed the database with content of file xxx\n" + "\n" + " -b, --dbdir xxx set the directory of database\n" + " (default: "DEFAULT_DB_DIR")\n" + " -m, --make-db-dir make the database directory\n" + " -o, --own-db-dir set user and group on database directory\n" + "\n" + " -S, --socketdir xxx set the base xxx for sockets\n" + " (default: "DEFAULT_SOCKET_DIR")\n" + " -M, --make-socket-dir make the socket directory\n" + " -O, --own-socket-dir set user and group on socket directory\n" + "\n" + " -h, --help print this help and exit\n" + " -v, --version print the version and exit\n" + "\n" +; + +static +const char +versiontxt[] = + "cynarad version 1.99.99\n" +; + +static int isid(const char *text); +static void ensure_directory(const char *path, int uid, int gid); +static void initdb(const char *path); +int main(int ac, char **av) +{ + int opt; + int rc; + int makesockdir = 0; + int makedbdir = 0; + int owndbdir = 0; + int ownsockdir = 0; + int help = 0; + int version = 0; + int error = 0; + int systemd = 0; + int uid = -1; + int gid = -1; + const char *init = NULL; + const char *dbdir = NULL; + const char *socketdir = NULL; + const char *user = NULL; + const char *group = NULL; + struct passwd *pw; + struct group *gr; + cap_t caps = { 0 }; + rcyn_server_t *server; + char *spec_socket_admin, *spec_socket_check; + + /* scan arguments */ + for (;;) { + opt = getopt_long(ac, av, shortopts, longopts, NULL); + if (opt == -1) + break; + + switch(opt) { + case _DBDIR_: + dbdir = optarg; + break; + case _GROUP_: + group = optarg; + break; + case _HELP_: + help = 1; + break; + case _INIT_: + init = optarg; + break; + case _MAKEDBDIR_: + makedbdir = 1; + break; + case _MAKESOCKDIR_: + makesockdir = 1; + break; + case _OWNSOCKDIR_: + ownsockdir = 1; + break; + case _OWNDBDIR_: + owndbdir = 1; + break; + case _SOCKETDIR_: + socketdir = optarg; + break; +#if defined(WITH_SYSTEMD_ACTIVATION) + case _SYSTEMD_: + systemd = 1; + break; +#endif + case _USER_: + user = optarg; + break; + case _VERSION_: + version = 1; + break; + default: + error = 1; + break; + } + } + + /* handles help, version, error */ + if (help | version) { + fprintf(stdout, "%s", help ? helptxt : versiontxt); + return 0; + } + if (error) + return 1; + if (systemd && (socketdir || makesockdir)) { + fprintf(stderr, "can't set options --systemd and --%s together\n", + socketdir ? "socketdir" : "make-socket-dir"); + return 1; + } + + /* set the defaults */ + dbdir = dbdir ?: DEFAULT_DB_DIR; + socketdir = socketdir ?: DEFAULT_SOCKET_DIR; + user = user ?: DEFAULT_CYNARA_USER; + group = group ?: DEFAULT_CYNARA_GROUP; + init = init ?: DEFAULT_INIT_FILE; + + /* compute socket specs */ + spec_socket_admin = spec_socket_check = NULL; + if (systemd) { + spec_socket_admin = strdup("sd:admin"); + spec_socket_check = strdup("sd:check"); + } else { + rc = asprintf(&spec_socket_admin, "unix:%s/cynara.admin", socketdir); + rc = asprintf(&spec_socket_check, "unix:%s/cynara.check", socketdir); + } + if (spec_socket_admin == NULL || spec_socket_check == NULL) { + fprintf(stderr, "can't make socket paths\n"); + return 1; + } + + /* compute user and group */ + if (user) { + uid = isid(user); + if (uid < 0) { + pw = getpwnam(user); + if (pw == NULL) { + fprintf(stderr, "can not find user '%s'\n", user); + return -1; + } + uid = pw->pw_uid; + gid = pw->pw_gid; + } + } + if (group) { + gid = isid(group); + if (gid < 0) { + gr = getgrnam(group); + if (gr == NULL) { + fprintf(stderr, "can not find group '%s'\n", group); + return -1; + } + gid = gr->gr_gid; + } + } + + /* handle directories */ + if (makedbdir) + ensure_directory(dbdir, owndbdir ? uid : -1, owndbdir ? gid : -1); + if (makesockdir && socketdir[0] != '@') + ensure_directory(socketdir, ownsockdir ? uid : -1, ownsockdir ? gid : -1); + + /* drop privileges */ + if (gid >= 0) { + rc = setgid(gid); + if (rc < 0) { + fprintf(stderr, "can not change group: %m\n"); + return -1; + } + } + if (uid >= 0) { + rc = setuid(uid); + if (rc < 0) { + fprintf(stderr, "can not change user: %m\n"); + return -1; + } + } + cap_clear(caps); + rc = cap_set_proc(caps); + + /* connection to the database */ + rc = db_open(dbdir); + if (rc < 0) { + fprintf(stderr, "can not open database: %m\n"); + return 1; + } + + /* initialisation of the database */ + if (db_is_empty()) { + initdb(init); + if (rc == 0) + rc = db_sync(); + if (rc == 0) + rc = db_backup(); + if (rc < 0) { + fprintf(stderr, "can't initialise database: %m\n"); + return 1; + } + } + + /* initialize server */ + signal(SIGPIPE, SIG_IGN); /* avoid SIGPIPE! */ + rc = rcyn_server_create(&server, spec_socket_admin, spec_socket_check); + if (rc < 0) { + fprintf(stderr, "can't initialise server: %m\n"); + return 1; + } + + /* ready ! */ +#if defined(WITH_SYSTEMD_ACTIVATION) + if (systemd) + sd_notify(0, "READY=1"); +#endif + + /* serve */ + rc = rcyn_server_serve(server); + return rc ? 3 : 0; +} + +/** returns the value of the id for 'text' (positive) or a negative value (-1) */ +static int isid(const char *text) +{ + long long int value = 0; + while(*text && value < INT_MAX) + if (*text < '0' || *text > '9' || value >= INT_MAX) + return -1; + else + value = 10 * value + (*text++ - '0'); + return value <= INT_MAX ? (int)value : -1; +} + +/** returns a pointer to the first last / of the path if it is meaningful */ +static char *enddir(char *path) +{ + /* + * / -> NULL + * /xxx -> NULL + * /xxx/ -> NULL + * /xxx/y -> /y + * /xxx//y -> //y + */ + char *c = NULL, *r = NULL, *i = path; + for(;;) { + while(*i == '/') + i++; + if (*i) + r = c; + while(*i != '/') + if (!*i++) + return r; + c = i; + } +} + +/** ensure that 'path' is a directory for the user and group */ +static void ensuredir(char *path, int length, int uid, int gid) +{ + struct stat st; + int rc, n; + char *e; + + n = length; + for(;;) { + path[n] = 0; + rc = mkdir(path, 0755); + if (rc == 0 || errno == EEXIST) { + /* exists */ + if (n == length) { + rc = stat(path, &st); + if (rc < 0) { + fprintf(stderr, "can not check %s: %m\n", path); + exit(1); + } else if ((st.st_mode & S_IFMT) != S_IFDIR) { + fprintf(stderr, "not a directory %s: %m\n", path); + exit(1); + } + /* set ownership */ + if ((uid != st.st_uid && uid >= 0) || (gid != st.st_gid && gid >= 0)) { + rc = chown(path, uid, gid); + if (rc < 0) { + fprintf(stderr, "can not own directory %s for uid=%d & gid=%d: %m\n", path, uid, gid); + exit(1); + } + } + return; + } + path[n] = '/'; + n = (int)strlen(path); + } else if (errno == ENOENT) { + /* a part of the path doesn't exist, try to create it */ + e = enddir(path); + if (!e) { + /* can't create it because at root */ + fprintf(stderr, "can not ensure directory %s\n", path); + exit(1); + } + n = (int)(e - path); + } else { + fprintf(stderr, "can not ensure directory %s: %m\n", path); + exit(1); + } + } +} + +/** ensure that 'path' is a directory for the user and group */ +static void ensure_directory(const char *path, int uid, int gid) +{ + size_t l; + char *p; + + l = strlen(path); + if (l > INT_MAX) { + /* ?!?!?!? *#@! */ + fprintf(stderr, "path toooooo long\n"); + exit(1); + } + p = strndupa(path, l); + ensuredir(p, (int)l, uid, gid); +} + +/** initialize the database from file of 'path' */ +static void initdb(const char *path) +{ + int rc, lino, x; + char *item[10]; + char buffer[2048]; + FILE *f; + + f = fopen(path, "r"); + if (f == NULL) { + fprintf(stderr, "can't open file %s\n", path); + exit(1); + } + + lino = 0; + while(fgets(buffer, sizeof buffer, f)) { + lino++; + item[0] = strtok(buffer, " \t\n\r"); + if (item[0] && item[0][0] != '#') { + item[1] = strtok(NULL, " \t\n\r"); + item[2] = strtok(NULL, " \t\n\r"); + item[3] = strtok(NULL, " \t\n\r"); + item[4] = strtok(NULL, " \t\n\r"); + item[5] = strtok(NULL, " \t\n\r"); + if (item[1] == NULL || item[2] == NULL + || item[3] == NULL || item[4] == NULL) { + fprintf(stderr, "field missing (%s:%d)\n", path, lino); + exit(1); + } else if (item[5] != NULL && item[5][0] != '#') { + fprintf(stderr, "extra field (%s:%d)\n", path, lino); + exit(1); + } + x = isid(item[4]); + if (x < 0) { + fprintf(stderr, "bad value (%s:%d)\n", path, lino); + exit(1); + } + rc = db_set(item[0], item[1], item[2], item[3], x); + if (rc < 0) { + fprintf(stderr, "can't set (%s:%d)\n", path, lino); + exit(1); + } + } + } + if (!feof(f)) { + fprintf(stderr, "error while reading file %s\n", path); + exit(1); + } + fclose(f); +} + @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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> @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 struct prot; diff --git a/src/queue.c b/src/queue.c index 16b0a0f..e2fb84c 100644 --- a/src/queue.c +++ b/src/queue.c @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 <stdint.h> #include <stdbool.h> #include <stdlib.h> diff --git a/src/queue.h b/src/queue.h index 70518c4..1937ee1 100644 --- a/src/queue.h +++ b/src/queue.h @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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. + */ + extern int diff --git a/src/rcyn-client.c b/src/rcyn-client.c index f712ca6..e1f7841 100644 --- a/src/rcyn-client.c +++ b/src/rcyn-client.c @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 <stdbool.h> @@ -106,7 +123,7 @@ flushw( if (rc == -EAGAIN) { pfd.fd = rcyn->fd; pfd.events = POLLOUT; - do { rc = poll(&pfd, 1, 0); } while (rc < 0 && errno == EINTR); + do { rc = poll(&pfd, 1, -1); } while (rc < 0 && errno == EINTR); if (rc < 0) rc = -errno; } @@ -170,7 +187,7 @@ wait_input( pfd.fd = rcyn->fd; pfd.events = POLLIN; - do { rc = poll(&pfd, 1, 0); } while (rc < 0 && errno == EINTR); + do { rc = poll(&pfd, 1, -1); } while (rc < 0 && errno == EINTR); return rc < 0 ? -errno : 0; } @@ -284,7 +301,7 @@ async( int op, uint32_t events ) { - return rcyn->async.controlcb + return rcyn->async.controlcb && rcyn->fd >= 0 ? rcyn->async.controlcb(rcyn->async.closure, op, rcyn->fd, events) : 0; } @@ -344,6 +361,13 @@ connection( return rc; } +static +int +ensure_opened( + rcyn_t *rcyn +) { + return rcyn->fd < 0 ? connection(rcyn) : 0; +} /************************************************************************************/ @@ -375,17 +399,12 @@ rcyn_open( rcyn->async.closure = 0; rcyn->async.requests = NULL; - /* connection */ - rc = connection(rcyn); - if (rc < 0) - goto error3; + /* lazy connection */ + rcyn->fd = -1; /* done */ return 0; -error3: - free(rcyn->cache); - prot_destroy(rcyn->prot); error2: free(rcyn); error: @@ -414,6 +433,9 @@ rcyn_enter( return -EPERM; if (rcyn->async.requests != NULL) return -EINPROGRESS; + rc = ensure_opened(rcyn); + if (rc < 0) + return rc; rc = putx(rcyn, _enter_, NULL); if (rc >= 0) @@ -432,6 +454,9 @@ rcyn_leave( return -EPERM; if (rcyn->async.requests != NULL) return -EINPROGRESS; + rc = ensure_opened(rcyn); + if (rc < 0) + return rc; rc = putx(rcyn, _leave_, commit ? _commit_ : NULL/*default: rollback*/, NULL); if (rc >= 0) @@ -453,6 +478,9 @@ check_or_test( if (rcyn->async.requests != NULL) return -EINPROGRESS; + rc = ensure_opened(rcyn); + if (rc < 0) + return rc; /* ensure there is no clear cache pending */ flushr(rcyn); @@ -514,6 +542,9 @@ rcyn_set( return -EPERM; if (rcyn->async.requests != NULL) return -EINPROGRESS; + rc = ensure_opened(rcyn); + if (rc < 0) + return rc; snprintf(val, sizeof val, "%u", (unsigned)value); rc = putx(rcyn, _set_, client, session, user, permission, val, NULL); @@ -545,6 +576,9 @@ rcyn_get( return -EPERM; if (rcyn->async.requests != NULL) return -EINPROGRESS; + rc = ensure_opened(rcyn); + if (rc < 0) + return rc; rc = putx(rcyn, _get_, client, session, user, permission, NULL); if (rc >= 0) { @@ -577,6 +611,9 @@ rcyn_drop( return -EPERM; if (rcyn->async.requests != NULL) return -EINPROGRESS; + rc = ensure_opened(rcyn); + if (rc < 0) + return rc; rc = putx(rcyn, _drop_, client, session, user, permission, NULL); if (rc >= 0) @@ -645,6 +682,7 @@ rcyn_async_process( int rc; const char *first; asreq_t *ar; + const char *client, *session, *user, *permission; for (;;) { /* non blocking wait for a reply */ @@ -670,6 +708,13 @@ rcyn_async_process( /* emit the asynchronous answer */ rcyn->async.requests = ar->next; rc = status_check(rcyn); + if (rc >= 0) { + client = (const char*)(ar + 1); + session = &client[1 + strlen(client)]; + user = &session[1 + strlen(session)]; + permission = &user[1 + strlen(user)]; + cache_put(rcyn->cache, client, session, user, permission, rc); + } ar->callback(ar->closure, rc); free(ar); } @@ -691,8 +736,12 @@ rcyn_async_check( int rc; asreq_t **pr, *ar; + rc = ensure_opened(rcyn); + if (rc < 0) + return rc; + /* allocate */ - ar = malloc(sizeof *ar); + ar = malloc(sizeof *ar + strlen(client) + strlen(session) + strlen(user) + strlen(permission) + 4); if (ar == NULL) return -ENOMEM; @@ -700,6 +749,7 @@ rcyn_async_check( ar->next = NULL; ar->callback = callback; ar->closure = closure; + stpcpy(1 + stpcpy(1 + stpcpy(1 + stpcpy((char*)(ar + 1), client), session), user), permission); /* send the request */ rc = putx(rcyn, simple ? _test_ : _check_, diff --git a/src/rcyn-client.h b/src/rcyn-client.h index 6706820..a4104ee 100644 --- a/src/rcyn-client.h +++ b/src/rcyn-client.h @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 diff --git a/src/rcyn-protocol.c b/src/rcyn-protocol.c index c656570..868e81f 100644 --- a/src/rcyn-protocol.c +++ b/src/rcyn-protocol.c @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 "rcyn-protocol.h" const char diff --git a/src/rcyn-protocol.h b/src/rcyn-protocol.h index 8906194..631c0b7 100644 --- a/src/rcyn-protocol.h +++ b/src/rcyn-protocol.h @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 extern const char diff --git a/src/rcyn-server.c b/src/rcyn-server.c index 4fc365e..2fff5db 100644 --- a/src/rcyn-server.c +++ b/src/rcyn-server.c @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 <stdbool.h> @@ -11,17 +28,15 @@ #include <fcntl.h> #include <errno.h> #include <poll.h> +#include <limits.h> #include <sys/epoll.h> #include <sys/types.h> #include <sys/socket.h> -#if defined(WITH_SYSTEMD_ACTIVATION) -#include <systemd/sd-daemon.h> -#endif - #include "prot.h" #include "cyn.h" #include "rcyn-protocol.h" +#include "rcyn-server.h" #include "socket.h" typedef enum rcyn_type { @@ -29,6 +44,7 @@ typedef enum rcyn_type { rcyn_Admin } rcyn_type_t; + /** structure for using epoll easily */ typedef struct pollitem pollitem_t; struct pollitem @@ -37,10 +53,10 @@ struct pollitem void (*handler)(pollitem_t *pollitem, uint32_t events, int pollfd); /** data */ - union { - void *closure; - int fd; - }; + void *closure; + + /** file */ + int fd; }; static @@ -48,12 +64,11 @@ int pollitem_do( pollitem_t *pollitem, uint32_t events, - int fd, int pollfd, int op ) { struct epoll_event ev = { .events = events, .data.ptr = pollitem }; - return epoll_ctl(pollfd, op, fd, &ev); + return epoll_ctl(pollfd, op, pollitem->fd, &ev); } static @@ -61,10 +76,9 @@ int pollitem_add( pollitem_t *pollitem, uint32_t events, - int fd, int pollfd ) { - return pollitem_do(pollitem, events, fd, pollfd, EPOLL_CTL_ADD); + return pollitem_do(pollitem, events, pollfd, EPOLL_CTL_ADD); } #if 0 @@ -73,20 +87,19 @@ int pollitem_mod( pollitem_t *pollitem, uint32_t events, - int fd, int pollfd ) { - return pollitem_do(pollitem, events, fd, pollfd, EPOLL_CTL_MOD); + return pollitem_do(pollitem, events, pollfd, EPOLL_CTL_MOD); } #endif static int pollitem_del( - int fd, + pollitem_t *pollitem, int pollfd ) { - return pollitem_do(NULL, 0, fd, pollfd, EPOLL_CTL_DEL); + return pollitem_do(pollitem, 0, pollfd, EPOLL_CTL_DEL); } @@ -96,9 +109,6 @@ struct client /** a protocol structure */ prot_t *prot; - /** the in/out file descriptor */ - int fd; - /** type of client */ rcyn_type_t type; @@ -125,6 +135,22 @@ struct client }; typedef struct client client_t; +/** structure for servers */ +struct rcyn_server +{ + /** the pollfd to use */ + int pollfd; + + /** is stopped ? */ + int stopped; + + /** the admin socket */ + pollitem_t admin; + + /** the check socket */ + pollitem_t check; +}; + /** * Check 'arg' against 'value' beginning at offset accepting it if 'arg' prefixes 'value' * Returns 1 if matching or 0 if not. @@ -157,9 +183,9 @@ flushw( rc = prot_should_write(cli->prot); while (rc) { - rc = prot_write(cli->prot, cli->fd); + rc = prot_write(cli->prot, cli->pollitem.fd); if (rc == -EAGAIN) { - pfd.fd = cli->fd; + pfd.fd = cli->pollitem.fd; pfd.events = POLLOUT; do { rc = poll(&pfd, 1, 0); } while (rc < 0 && errno == EINTR); } @@ -418,7 +444,7 @@ destroy_client( bool closefds ) { if (closefds) - close(cli->fd); + close(cli->pollitem.fd); if (cli->entering) cyn_enter_async_cancel(entercb, cli); if (cli->entered) @@ -446,7 +472,7 @@ on_client_event( /* possible input */ if (events & EPOLLIN) { - nr = prot_read(cli->prot, cli->fd); + nr = prot_read(cli->prot, cli->pollitem.fd); if (nr <= 0) goto terminate; nargs = prot_get(cli->prot, &args); @@ -462,7 +488,7 @@ on_client_event( /* terminate the client session */ terminate: - pollitem_del(cli->fd, pollfd); + pollitem_del(&cli->pollitem, pollfd); destroy_client(cli, true); } @@ -495,7 +521,6 @@ create_client( goto error3; /* records the file descriptor */ - cli->fd = fd; cli->type = type; cli->version = 0; /* version not set */ cli->relax = true; /* relax on error */ @@ -505,6 +530,7 @@ create_client( cli->checked = false; /* no check made */ cli->pollitem.handler = on_client_event; cli->pollitem.closure = cli; + cli->pollitem.fd = fd; return 0; error3: prot_destroy(cli->prot); @@ -559,7 +585,7 @@ on_server_event( } /* add the client to the epolling */ - rc = pollitem_add(&cli->pollitem, EPOLLIN, fd, pollfd); + rc = pollitem_add(&cli->pollitem, EPOLLIN, pollfd); if (rc < 0) { fprintf(stderr, "can't poll client connection: %s\n", strerror(-rc)); destroy_client(cli, 1); @@ -589,66 +615,159 @@ on_admin_server_event( on_server_event(pollitem, events, pollfd, rcyn_Admin); } -int main(int ac, char **av) -{ - int rc; - pollitem_t pi_admin, pi_check, *pi; - int pollfd, fd; - struct epoll_event ev; - const char *check_spec = rcyn_default_check_socket_spec; - const char *admin_spec = rcyn_default_admin_socket_spec; +/** destroy a server */ +void +rcyn_server_destroy( + rcyn_server_t *server +) { + if (server) { + if (server->pollfd >= 0) + close(server->pollfd); + if (server->admin.fd >= 0) + close(server->admin.fd); + if (server->check.fd >= 0) + close(server->check.fd); + free(server); + } +} - /* - * future possible options: - * - strict/relax - * - databse name(s) - * - socket name - * - policy - */ +/** create a server */ +int +rcyn_server_create( + rcyn_server_t **server, + const char *admin_socket_spec, + const char *check_socket_spec +) { + rcyn_server_t *srv; + int rc; - /* connection to the database */ - rc = cyn_init(); - if (rc < 0) { - fprintf(stderr, "can't initialise database: %m\n"); - return 1; + /* allocate the structure */ + *server = srv = malloc(sizeof *srv); + if (srv == NULL) { + rc = -ENOMEM; + fprintf(stderr, "can't alloc memory: %m\n"); + goto error; } /* create the polling fd */ - pollfd = epoll_create1(EPOLL_CLOEXEC); - if (pollfd < 0) { + srv->admin.fd = srv->check.fd = -1; + srv->pollfd = epoll_create1(EPOLL_CLOEXEC); + if (srv->pollfd < 0) { + rc = -errno; fprintf(stderr, "can't create polling: %m\n"); - return 1; + goto error2; } /* create the admin server socket */ - fd = socket_open(admin_spec, 1); - if (fd < 0) { - fprintf(stderr, "can't create admin server socket: %m\n"); - return 1; + admin_socket_spec = admin_socket_spec ?: rcyn_default_admin_socket_spec; + srv->admin.fd = socket_open(admin_socket_spec, 1); + if (srv->admin.fd < 0) { + rc = -errno; + fprintf(stderr, "can't create admin server socket %s: %m\n", admin_socket_spec); + goto error2; } /* add the server to pollfd */ - pi_admin.handler = on_admin_server_event; - pi_admin.fd = fd; - rc = pollitem_add(&pi_admin, EPOLLIN, fd, pollfd); + srv->admin.handler = on_admin_server_event; + srv->admin.closure = srv; + rc = pollitem_add(&srv->admin, EPOLLIN, srv->pollfd); if (rc < 0) { + rc = -errno; fprintf(stderr, "can't poll admin server: %m\n"); - return 1; + goto error2; } /* create the server socket */ - fd = socket_open(check_spec, 1); - if (fd < 0) { - fprintf(stderr, "can't create check server socket: %m\n"); - return 1; + check_socket_spec = check_socket_spec ?: rcyn_default_check_socket_spec; + srv->check.fd = socket_open(check_socket_spec, 1); + if (srv->check.fd < 0) { + rc = -errno; + fprintf(stderr, "can't create check server socket %s: %m\n", check_socket_spec); + goto error2; } /* add the server to pollfd */ - pi_check.handler = on_check_server_event; - pi_check.fd = fd; - rc = pollitem_add(&pi_check, EPOLLIN, fd, pollfd); + srv->check.handler = on_check_server_event; + srv->check.closure = srv; + rc = pollitem_add(&srv->check, EPOLLIN, srv->pollfd); if (rc < 0) { + rc = -errno; fprintf(stderr, "can't poll check server: %m\n"); + goto error2; + } + return 0; + +error2: + if (srv->pollfd >= 0) + close(srv->pollfd); + if (srv->admin.fd >= 0) + close(srv->admin.fd); + if (srv->check.fd >= 0) + close(srv->check.fd); + free(srv); +error: + *server = NULL; + return rc; +} + +/** stop the server */ +void +rcyn_server_stop( + rcyn_server_t *server, + int status +) { + server->stopped = status ?: INT_MIN; +} + +/** create a server */ +int +rcyn_server_serve( + rcyn_server_t *server +) { + int rc; + struct epoll_event ev; + pollitem_t *pi; + + /* process inputs */ + server->stopped = 0; + while(!server->stopped) { + rc = epoll_wait(server->pollfd, &ev, 1, -1); + if (rc == 1) { + pi = ev.data.ptr; + pi->handler(pi, ev.events, server->pollfd); + } + } + return server->stopped == INT_MIN ? 0 : server->stopped; +} + +#if 0 +#if defined(WITH_SYSTEMD_ACTIVATION) +#include <systemd/sd-daemon.h> +#endif + + +int main(int ac, char **av) +{ + int rc; + const char *check_spec = rcyn_default_check_socket_spec; + const char *admin_spec = rcyn_default_admin_socket_spec; + rcyn_server_t *server; + + /* connection to the database */ + rc = cyn_init( + "/var/lib/cynara/cynara.names", + "/var/lib/cynara/cynara.rules", + (const char**)((const char *[]){ "System", "*", "*", "*", "1", NULL }) + ); + if (rc < 0) { + fprintf(stderr, "can't initialise database: %m\n"); + return 1; + } + + /* create the server */ + rc = rcyn_server_create(&server, admin_spec, check_spec); + if (rc < 0) { + fprintf(stderr, "can't initialise server: %m\n"); return 1; } @@ -658,12 +777,7 @@ int main(int ac, char **av) #endif /* process inputs */ - for(;;) { - rc = epoll_wait(pollfd, &ev, 1, -1); - if (rc == 1) { - pi = ev.data.ptr; - pi->handler(pi, ev.events, pollfd); - } - } + rcyn_server_serve(server); } +#endif diff --git a/src/rcyn-server.h b/src/rcyn-server.h new file mode 100644 index 0000000..2908c52 --- /dev/null +++ b/src/rcyn-server.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 + +struct rcyn_server; +typedef struct rcyn_server rcyn_server_t; + +extern +void +rcyn_server_destroy( + rcyn_server_t *server +); + +extern +int +rcyn_server_create( + rcyn_server_t **server, + const char *admin_socket_spec, + const char *check_socket_spec +); + +extern +int +rcyn_server_serve( + rcyn_server_t *server +); + +extern +void +rcyn_server_stop( + rcyn_server_t *server, + int status +); + diff --git a/src/socket.c b/src/socket.c index 3ab0408..b908cf0 100644 --- a/src/socket.c +++ b/src/socket.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2018 "IoT.bzh" + * Copyright (C) 2018 "IoT.bzh" * Author José Bollo <jose.bollo@iot.bzh> * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/test-lib-compat.c b/src/test-lib-compat.c index 627c208..9f9ea1b 100644 --- a/src/test-lib-compat.c +++ b/src/test-lib-compat.c @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 <stdint.h> #include <stdbool.h> #include <stdio.h> @@ -55,7 +72,7 @@ void ckex(int rc, int type, int line, const char *x) printf("ERROR(%d) %s by %s line %d\n", rc, buffer, x, line); exit(1); } - printf("SUCCESS %s\n", x); + printf("SUCCESS[%d] %s\n", rc, x); } int is(const char *first, const char *second, int mincount) diff --git a/systemd/CMakeLists.txt b/systemd/CMakeLists.txt index f8116a2..3fde22f 100644 --- a/systemd/CMakeLists.txt +++ b/systemd/CMakeLists.txt @@ -1,25 +1,23 @@ -# Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved +########################################################################### +# Copyright (C) 2018 "IoT.bzh" # -# 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 +# author: José Bollo <jose.bollo@iot.bzh> # -# http://www.apache.org/licenses/LICENSE-2.0 +# 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 # -# 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. -# -# @file CMakeLists.txt -# @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com> +# 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. +########################################################################### -SET(CYNARA_ADMIN_SOCKET_GROUP - "security_fw" - CACHE STRING - "Group to apply on administrative sockets") +set(SYSTEMD_UNIT_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/systemd/system" + CACHE PATH "Path to systemd system unit files") CONFIGURE_FILE(cynara-admin.socket.in cynara-admin.socket @ONLY) CONFIGURE_FILE(cynara-check.socket.in cynara-check.socket @ONLY) diff --git a/systemd/cynara-admin.socket.in b/systemd/cynara-admin.socket.in index ebc59c6..25c3b2c 100644 --- a/systemd/cynara-admin.socket.in +++ b/systemd/cynara-admin.socket.in @@ -1,6 +1,6 @@ [Socket] FileDescriptorName=admin -ListenStream=@SOCKET_DIR@/cynara.admin +ListenStream=@DEFAULT_SOCKET_DIR@/cynara.admin SocketMode=0600 SmackLabelIPIn=@ SmackLabelIPOut=@ diff --git a/systemd/cynara-check.socket.in b/systemd/cynara-check.socket.in index 1139d2f..b0606e0 100644 --- a/systemd/cynara-check.socket.in +++ b/systemd/cynara-check.socket.in @@ -1,6 +1,6 @@ [Socket] FileDescriptorName=check -ListenStream=@SOCKET_DIR@/cynara.check +ListenStream=@DEFAULT_SOCKET_DIR@/cynara.check SocketMode=0666 SmackLabelIPIn=* SmackLabelIPOut=@ diff --git a/systemd/cynara.service b/systemd/cynara.service index e124b91..9cacce6 100644 --- a/systemd/cynara.service +++ b/systemd/cynara.service @@ -4,8 +4,7 @@ Requires=afm-system-setup.service After=afm-system-setup.service [Service] -ExecStartPre=+-/usr/bin/sh -c 'if test ! -d /var/lib/cynara; then mkdir -p /var/lib/cynara; chown cynara:cynara /var/lib/cynara; chsmack -a System /var/lib/cynara; fi' -ExecStart=/usr/bin/cynarad +ExecStart=/usr/bin/cynarad --systemd --user cynara --group cynara --make-db-dir --own-db-dir Type=notify @@ -15,11 +14,11 @@ Restart=always Sockets=cynara-admin.socket Sockets=cynara-check.socket - -UMask=0000 -User=cynara -Group=cynara SmackProcessLabel=System + +#UMask=0000 +#User=cynara +#Group=cynara #NoNewPrivileges=true #Environment="CYNARA_LOG_LEVEL=LOG_DEBUG" |