diff options
author | Jose Bollo <jose.bollo@iot.bzh> | 2018-09-19 14:52:03 +0200 |
---|---|---|
committer | Jose Bollo <jose.bollo@iot.bzh> | 2018-09-20 23:39:57 +0200 |
commit | 4123ffb189acd0c18cddd0df75a58cbc3276ad80 (patch) | |
tree | a393e83dce5f57c7db0ab5475841c86829468ebe | |
parent | 7ae3477d84ae13c5e9ef9bb1980a8f70f8b7e9bf (diff) |
Add session, value as string and expiration
The DB now records sessions. It has expiration
management (currently coarse: ~16s, see db.c)
that is propagated to caches.
Values are now strings. Default values are
"yes" and "no" for allowed or denied permissions.
new program: cynadm
Signed-off-by: Jose Bollo <jose.bollo@iot.bzh>
-rw-r--r-- | cynara.initial | 4 | ||||
-rw-r--r-- | src/.gitignore | 5 | ||||
-rw-r--r-- | src/CMakeLists.txt | 16 | ||||
-rw-r--r-- | src/Makefile | 26 | ||||
-rw-r--r-- | src/cache.c | 155 | ||||
-rw-r--r-- | src/cache.h | 3 | ||||
-rw-r--r-- | src/cyn.c | 27 | ||||
-rw-r--r-- | src/cyn.h | 17 | ||||
-rw-r--r-- | src/db.c | 517 | ||||
-rw-r--r-- | src/db.h | 14 | ||||
-rw-r--r-- | src/lib-compat.c | 35 | ||||
-rw-r--r-- | src/main-cynadm.c | 297 | ||||
-rw-r--r-- | src/main-cynarad.c | 61 | ||||
-rw-r--r-- | src/main-test-old-cynara.c (renamed from src/test-lib-compat.c) | 0 | ||||
-rw-r--r-- | src/queue.c | 55 | ||||
-rw-r--r-- | src/queue.h | 3 | ||||
-rw-r--r-- | src/rcyn-client.c | 63 | ||||
-rw-r--r-- | src/rcyn-client.h | 6 | ||||
-rw-r--r-- | src/rcyn-protocol.txt | 15 | ||||
-rw-r--r-- | src/rcyn-server.c | 105 |
20 files changed, 943 insertions, 481 deletions
diff --git a/cynara.initial b/cynara.initial index 96054d3..aa58c4b 100644 --- a/cynara.initial +++ b/cynara.initial @@ -1,4 +1,4 @@ # initial database for cynara -System * * * 1 -User * * * 1 +System * * * yes always +User * * * yes always diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index d551f05..0000000 --- a/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -cynarad -test-cynara -cynara.names -cynara.rules -.*.sw* diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4363234..549d401 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,11 +72,19 @@ INSTALL(TARGETS cynara LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}) INSTALL(FILES rcyn-client.h DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/cynara) ########################################### -# build and install test-cynara +# build and install cynadm ########################################### -ADD_EXECUTABLE(test-cynara test-lib-compat.c) -TARGET_LINK_LIBRARIES(test-cynara cynara) -INSTALL(TARGETS test-cynara +ADD_EXECUTABLE(cynadm main-cynadm.c) +TARGET_LINK_LIBRARIES(cynadm cynara) +INSTALL(TARGETS cynadm + RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) + +########################################### +# build and install test-old-cynara +########################################### +ADD_EXECUTABLE(test-old-cynara main-test-old-cynara.c) +TARGET_LINK_LIBRARIES(test-old-cynara cynara) +INSTALL(TARGETS test-old-cynara RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) ########################################### diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index f5969c3..0000000 --- a/src/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -.PHONY: all clean - -srcssrv = db.c fbuf.c queue.c cyn.c prot.c rcyn-protocol.c socket.c - -srcscli = prot.c rcyn-client.c rcyn-protocol.c lib-compat.c cache.c socket.c - -incssrv = db.h fbuf.h cyn.h prot.h rcyn-protocol.h socket.h - -incscli = prot.h rcyn-client.h rcyn-protocol.h cache.h socket.h - -defs = -DRCYN_DEFAULT_CHECK_SOCKET_SPEC=\"tcp:localhost:5555\" \ - -DRCYN_DEFAULT_ADMIN_SOCKET_SPEC=\"tcp:localhost:4444\" - -bins = cynarad test-cynara - -all: $(bins) - -clean: - rm cynara.names cynara.rules $(bins) 2>/dev/null || true - -cynarad: rcyn-server.c $(srcssrv) $(incssrv) - gcc -o cynarad -g -Wall rcyn-server.c $(srcssrv) $(defs) - -test-cynara: test-lib-compat.c $(srcscli) $(incscli) - gcc -o test-cynara -I../include -g -Wall test-lib-compat.c $(srcscli) $(defs) - diff --git a/src/cache.c b/src/cache.c index 8d15728..9bbe667 100644 --- a/src/cache.c +++ b/src/cache.c @@ -18,26 +18,48 @@ #include <stdlib.h> #include <stdint.h> +#include <stdalign.h> #include <string.h> #include <errno.h> +#include <time.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 + * A cache item header + * + * Each item is followed by values in that given order: * - client: zero terminated string * - session: zero terminated string * - user: zero terminated string * - permission: zero terminated string * - */ +struct item +{ + /** expiration */ + time_t expire; + + /** length of the cache entry including this header */ + uint16_t length; + + /** hit indicator */ + uint8_t hit; + + /** value to store */ + int8_t value; + + /** fake ending character */ + char strings; +}; +typedef struct item item_t; + +/** + * 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 + */ struct cache { uint32_t used; @@ -46,12 +68,13 @@ struct cache }; static -uint32_t -lenat( +inline +item_t * +itemat( cache_t *cache, uint32_t pos ) { - return ((uint32_t)cache->content[pos]) | (((uint32_t)cache->content[pos + 1]) << 8); + return (item_t*)(&cache->content[pos]); } static @@ -62,7 +85,7 @@ drop_at( ) { uint32_t e, l; - l = lenat(cache, pos); + l = itemat(cache, pos)->length; e = pos + l; cache->used -= l; if (cache->used > e) @@ -75,13 +98,15 @@ drop_lre( cache_t *cache ) { uint32_t found = 0, iter = 0; - uint8_t hmin = 255, hint; + uint8_t hmin = 255, hit; + item_t *item; while (iter < cache->used) { - hint = cache->content[iter + 2]; - if (hint < hmin) + item = itemat(cache, iter); + hit = item->hit; + if (hit < hmin) found = iter; - iter += lenat(cache, iter); + iter += item->length; } if (found < cache->used) drop_at(cache, found); @@ -91,21 +116,23 @@ static void hit( cache_t *cache, - uint32_t pos + item_t *target ) { uint32_t iter = 0; - uint8_t hint; + uint8_t hit; + item_t *item; while (iter < cache->used) { - if (iter == pos) - hint = 255; + item = itemat(cache, iter); + if (item == target) + hit = 255; else { - hint = cache->content[iter + 2]; - if (hint) - hint--; + hit = item->hit; + if (hit) + hit--; } - cache->content[iter + 2] = hint; - iter += lenat(cache, iter); + item->hit = hit; + iter += item->length; } } @@ -135,7 +162,6 @@ cmp( return 0; } - static int match( @@ -156,7 +182,7 @@ match( } static -uint32_t +item_t* search( cache_t *cache, const char *client, @@ -164,16 +190,24 @@ search( const char *user, const char *permission ) { - char *txt; - uint32_t iter = 0; + time_t now; + item_t *item, *found; + uint32_t iter; + found = NULL; + now = time(NULL); + 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); + item = itemat(cache, iter); + if (item->expire && item->expire < now) + drop_at(cache, iter); + else { + if (match(&item->strings, client, session, user, permission)) + found = item; + iter += item->length; + } } - return iter; + return found; } int @@ -183,37 +217,40 @@ cache_put( const char *session, const char *user, const char *permission, - int value + int value, + time_t expire ) { - uint32_t pos; - size_t size, scli, sses, susr, sper; + uint16_t length; + item_t *item; + size_t size; - if (cache == NULL || value < 0 || value > 255) + if (cache == NULL || value < -128 || value > 127) return -EINVAL; - 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; + item = search(cache, client, session, user, permission); + if (item == NULL) { + /* create an item */ + size = (size_t)(&((item_t*)0)->strings) + + strlen(client) + + strlen(session) + + strlen(user) + + strlen(permission); + size = (size + alignof(item_t) - 1) & ~(alignof(item_t) - 1); if (size > 65535) return -EINVAL; - if (size > cache->count) + length = (uint16_t)size; + if (length > cache->count) return -ENOMEM; - while(cache->used + (uint32_t)size > cache->count) + while(cache->used + length > 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); + item = itemat(cache, cache->used); + item->length = length; + stpcpy(1 + stpcpy(1 + stpcpy(1 + stpcpy(&item->strings, client), session), user), permission); cache->used += (uint32_t)size; } + item->expire = expire; + item->hit = 255; + item->value = (int8_t)value; return 0; } @@ -225,12 +262,12 @@ cache_search( const char *user, const char *permission ) { - uint32_t pos; + item_t *item; - pos = search(cache, client, session, user, permission); - if (pos < cache->used) { - hit(cache, pos); - return (int)cache->content[pos + 3]; + item = search(cache, client, session, user, permission); + if (item) { + hit(cache, item); + return (int)item->value; } return -ENOENT; } diff --git a/src/cache.h b/src/cache.h index 5cd827b..bd56601 100644 --- a/src/cache.h +++ b/src/cache.h @@ -38,7 +38,8 @@ cache_put( const char *session, const char *user, const char *permission, - int value + int value, + time_t expire ); extern @@ -88,6 +88,7 @@ changed( int rc; struct callback *c; + db_cleanup(0); rc = db_sync(); for (c = observers; c ; c = c->next) c->callback(c->closure); @@ -197,11 +198,12 @@ cyn_set( const char *session, const char *user, const char *permission, - uint32_t value + const char *value, + time_t expire ) { if (!lock) return -EPERM; - return queue_set(client, session, user, permission, value); + return queue_set(client, session, user, permission, value, expire); } int @@ -225,7 +227,8 @@ cyn_list( const char *session, const char *user, const char *permission, - uint32_t value), + const char *value, + time_t expire), const char *client, const char *session, const char *user, @@ -240,11 +243,12 @@ cyn_test( const char *session, const char *user, const char *permission, - uint32_t *value + const char **value, + time_t *expire ) { int rc; - rc = db_test(client, session, user, permission, value); + rc = db_test(client, session, user, permission, value, expire); if (rc <= 0) *value = DEFAULT; else @@ -254,24 +258,25 @@ cyn_test( int cyn_check_async( - void (*check_cb)(void *closure, uint32_t value), + void (*check_cb)(void *closure, const char *value, time_t expire), void *closure, const char *client, const char *session, const char *user, const char *permission ) { - uint32_t value; + const char *value; + time_t expire; - cyn_test(client, session, user, permission, &value); - if (value == ALLOW || value == DENY) { - check_cb(closure, value); + cyn_test(client, session, user, permission, &value, &expire); + if (!strcmp(value, ALLOW) || !strcmp(value, DENY)) { + check_cb(closure, value, expire); return 0; } /* TODO: try to resolve AGENT?? */ - check_cb(closure, value); + check_cb(closure, value, expire); return 0; } @@ -18,9 +18,9 @@ #pragma once -#define DENY 0 -#define ALLOW 1 -#define ASK 2 +#define DENY "no" +#define ALLOW "yes" +#define ASK "ask" #define DEFAULT DENY /** enter critical recoverable section */ @@ -45,7 +45,8 @@ cyn_set( const char *session, const char *user, const char *permission, - uint32_t value + const char *value, + time_t expire ); extern @@ -64,7 +65,8 @@ cyn_test( const char *session, const char *user, const char *permission, - uint32_t *value + const char **value, + time_t *expire ); extern @@ -77,7 +79,8 @@ cyn_list( const char *session, const char *user, const char *permission, - uint32_t value), + const char *value, + time_t expire), const char *client, const char *session, const char *user, @@ -87,7 +90,7 @@ cyn_list( extern int cyn_check_async( - void (*check_cb)(void *closure, uint32_t value), + void (*check_cb)(void *closure, const char *value, time_t expire), void *closure, const char *client, const char *session, @@ -26,11 +26,13 @@ #include <stdalign.h> #include <stdio.h> #include <string.h> +#include <time.h> #include <errno.h> #include "fbuf.h" #include "db.h" +#define NOEXPIRE 0 #define NOIDX 0 #define ANYIDX 40 @@ -39,26 +41,45 @@ #define WIDEIDX 42 #define WIDESTR "*" -/** - * A rule is a set of 4 integers +/* + * for the first version, save enougth time up to 4149 + * 4149 = 1970 + (4294967296 * 16) / (365 * 24 * 60 * 60) + * + * in the next version, time will be relative to a stored base */ -struct rule -{ - uint32_t client, user, permission, value; -}; -typedef struct rule rule_t; +#define exp2time(x) (((time_t)(x)) << 4) +#define time2expl(x) ((uint32_t)((x) >> 4)) +#define time2exph(x) time2expl((x) + 15) /** - * Sessions + * A rule is a set of 32 bits integers */ -struct session +struct rule { - struct session *next, *prev; - rule_t *rules; - const char *name; - uint32_t count; + union { + uint32_t ids[5]; + struct { + /** client string id */ + uint32_t client; + + /** session string id */ + uint32_t session; + + /** user string id */ + uint32_t user; + + /** permission string id */ + uint32_t permission; + + /** value string id */ + uint32_t value; + }; + }; + + /** expiration */ + uint32_t expire; }; -typedef struct session session_t; +typedef struct rule rule_t; /* * The cynara-agl database is made of 2 memory mapped files: @@ -92,12 +113,11 @@ static uint32_t names_count; /** the name indexes sorted */ static uint32_t *names_sorted; -/** the sessions */ -static session_t sessions = { - .next = &sessions, - .prev = &sessions, - .name = WIDESTR -}; +/** count of rules */ +static uint32_t rules_count; + +/** the rules */ +static rule_t *rules; /** return the name of 'index' */ static @@ -273,134 +293,77 @@ is_any_or_wide( return is_any(text) || 0 == strcmp(text, WIDESTR); } -/** get in 'session' the session for 'name' and create it if 'needed' */ +/** set the 'value' to the rule at 'index' */ static -int -get_session( - const char *name, - bool needed, - session_t **session +void +touch_at( + uint32_t index ) { - session_t *s; - size_t len; - - /* start on ANY sessions */ - s = &sessions; - if (is_any_or_wide(name)) - goto found; - - /* look to other sessions */ - s = s->next; - while(s != &sessions) { - if (!strcmp(s->name, name)) - goto found; - s = s->next; - } - - /* not found */ - if (!needed) { - errno = ENOENT; - return -1; - } - - /* check length */ - len = strnlen(name, MAX_NAME_LENGTH + 1); - if (len > MAX_NAME_LENGTH) { - errno = EINVAL; - return -1; - } + uint32_t pos; - /* create it */ - s = malloc(sizeof * s + len + 1); - if (s == NULL) - return -1; /* out of memory */ - - /* init new session */ - s->rules = NULL; - s->count = 0; - s->name = strcpy((char*)(s + 1), name); - s->next = &sessions; - s->prev = sessions.prev; - sessions.prev = s; - s->prev->next = s; -found: - *session = s; - return 0; + pos = (uint32_t)(((void*)&rules[index]) - frules.buffer); + if (pos < frules.saved) + frules.saved = pos; } -/** for 'session' set the value the rule at 'index' */ +/** set the 'value' to the rule at 'index' */ static void -session_set_at( - session_t *session, +set_at( uint32_t index, - uint32_t value + uint32_t value, + uint32_t expire ) { - uint32_t pos; - - assert(index < session->count); - session->rules[index].value = value; - if (session == &sessions) { - pos = (uint32_t)(((void*)&session->rules[index]) - frules.buffer); - if (pos < frules.saved) - frules.saved = pos; - } + assert(index < rules_count); + rules[index].value = value; + rules[index].expire = expire; + touch_at(index); } -/** drop of 'session' the rule at 'index' */ +/** drop the rule at 'index' */ static void -session_drop_at( - session_t *session, +drop_at( uint32_t index ) { uint32_t pos; - assert(index < session->count); - if (index < --session->count) - session->rules[index] = session->rules[session->count]; - if (session == &sessions) { - pos = (uint32_t)(((void*)&session->rules[index]) - frules.buffer); - if (pos < frules.saved) - frules.saved = pos; - pos = (uint32_t)(((void*)&session->rules[session->count]) - frules.buffer); - frules.used = pos; - } + assert(index < rules_count); + if (index < --rules_count) + rules[index] = rules[rules_count]; + pos = (uint32_t)(((void*)&rules[rules_count]) - frules.buffer); + frules.used = pos; + touch_at(index); } -/** add to 'session' the rule 'client' x 'user' x 'permission' x 'value' */ +/** add the rule 'client' x 'session' x 'user' x 'permission' x 'value' */ static int -session_add( - session_t *session, +add_rule( uint32_t client, + uint32_t session, uint32_t user, uint32_t permission, - uint32_t value + uint32_t value, + uint32_t expire ) { int rc; uint32_t c; rule_t *rule; - if (session == &sessions) { - c = frules.used + (uint32_t)sizeof *rule; - rc = fbuf_ensure_capacity(&frules, c); - if (rc) - return rc; - frules.used = c; - session->rules = (rule_t*)(frules.buffer + uuidlen); - } else { - c = session->count + 32 - (session->count & 31); - rule = realloc(session->rules, c * sizeof *rule); - if (rule == NULL) - return -ENOMEM; - session->rules = rule; - } - rule = &session->rules[session->count++]; + c = frules.used + (uint32_t)sizeof *rule; + rc = fbuf_ensure_capacity(&frules, c); + if (rc) + return rc; + rules = (rule_t*)(frules.buffer + uuidlen); + rule = &rules[rules_count++]; rule->client = client; + rule->session = session; rule->user = user; rule->permission = permission; rule->value = value; + rule->expire = expire; + frules.used = c; return 0; } @@ -409,8 +372,8 @@ static void init_rules( ) { - sessions.rules = (rule_t*)(frules.buffer + uuidlen); - sessions.count = (frules.used - uuidlen) / sizeof *sessions.rules; + rules = (rule_t*)(frules.buffer + uuidlen); + rules_count = (frules.used - uuidlen) / sizeof *rules; } /** open a fbuf */ @@ -487,7 +450,7 @@ db_close( bool db_is_empty( ) { - return !sessions.count; + return !rules_count; } /** synchronize db on files */ @@ -554,46 +517,35 @@ db_for_all( const char *session, const char *user, const char *permission, - uint32_t value), + const char *value, + time_t expire), const char *client, const char *session, const char *user, const char *permission ) { - uint32_t ucli, uusr, i; - bool anyperm, anysession; - session_t *ses; + uint32_t ucli, uusr, uses, i; + bool anyperm; if (db_get_name_index(&ucli, client, false) + || db_get_name_index(&uses, session, false) || db_get_name_index(&uusr, user, false)) return; /* nothing to do! */ anyperm = is_any(permission); - anysession = is_any(session); - if (anysession) - ses = &sessions; - else { - if (get_session(session, false, &ses)) - return; /* ignore if no session */ - } - for(;;) { - for (i = 0; i < ses->count; i++) { - if ((ucli == ANYIDX || ucli == ses->rules[i].client) - && (uusr == ANYIDX || uusr == ses->rules[i].user) - && (anyperm || !strcasecmp(permission, name_at(ses->rules[i].permission)))) { - callback(closure, - name_at(ses->rules[i].client), - ses->name, - name_at(ses->rules[i].user), - name_at(ses->rules[i].permission), - ses->rules[i].value); - } + for (i = 0; i < rules_count; i++) { + if ((ucli == ANYIDX || ucli == rules[i].client) + && (uses == ANYIDX || uses == rules[i].session) + && (uusr == ANYIDX || uusr == rules[i].user) + && (anyperm || !strcasecmp(permission, name_at(rules[i].permission)))) { + callback(closure, + name_at(rules[i].client), + name_at(rules[i].session), + name_at(rules[i].user), + name_at(rules[i].permission), + name_at(rules[i].value), + exp2time(rules[i].expire)); } - if (!anysession) - break; - ses = ses->next; - if (ses == &sessions) - break; } } @@ -605,37 +557,24 @@ db_drop( const char *user, const char *permission ) { - uint32_t ucli, uusr, i; - bool anyperm, anysession; - session_t *ses; + uint32_t ucli, uses, uusr, i; + bool anyperm; if (db_get_name_index(&ucli, client, false) + || db_get_name_index(&uses, session, false) || db_get_name_index(&uusr, user, false)) return 0; /* nothing to do! */ anyperm = is_any(permission); - anysession = is_any(session); - if (anysession) - ses = &sessions; - else { - if (get_session(session, false, &ses)) - return 0; /* ignore if no session */ - } - for(;;) { - i = 0; - while (i < ses->count) { - if ((ucli == ANYIDX || ucli == ses->rules[i].client) - && (uusr == ANYIDX || uusr == ses->rules[i].user) - && (anyperm || !strcasecmp(permission, name_at(ses->rules[i].permission)))) - session_drop_at(ses, i); - else - i++; - } - if (!anysession) - break; - ses = ses->next; - if (ses == &sessions) - break; + i = 0; + while (i < rules_count) { + if ((ucli == ANYIDX || ucli == rules[i].client) + && (uses == ANYIDX || uses == rules[i].session) + && (uusr == ANYIDX || uusr == rules[i].user) + && (anyperm || !strcasecmp(permission, name_at(rules[i].permission)))) + drop_at(i); + else + i++; } return 0; } @@ -647,11 +586,11 @@ db_set( const char *session, const char *user, const char *permission, - uint32_t value + const char *value, + time_t expire ) { int rc; - uint32_t ucli, uusr, uperm, i; - session_t *ses; + uint32_t ucli, uses, uusr, uperm, uval, i; /* normalize */ client = is_any_or_wide(client) ? WIDESTR : client; @@ -659,26 +598,28 @@ db_set( user = is_any_or_wide(user) ? WIDESTR : user; permission = is_any_or_wide(permission) ? WIDESTR : permission; - /* get the session */ - rc = get_session(session, true, &ses); - if (rc) - goto error; - /* get/create strings */ rc = db_get_name_index(&ucli, client, true); if (rc) goto error; + rc = db_get_name_index(&uses, session, true); + if (rc) + goto error; rc = db_get_name_index(&uusr, user, true); if (rc) goto error; + rc = db_get_name_index(&uval, value, true); + if (rc) + goto error; /* search the existing rule */ - for (i = 0 ; i < ses->count ; i++) { - if (ucli == ses->rules[i].client - && uusr == ses->rules[i].user - && !strcasecmp(permission, name_at(ses->rules[i].permission))) { + for (i = 0; i < rules_count; i++) { + if (ucli == rules[i].client + && uses == rules[i].session + && uusr == rules[i].user + && !strcasecmp(permission, name_at(rules[i].permission))) { /* found */ - session_set_at(ses, i, value); + set_at(i, uval, time2exph(expire)); return 0; } } @@ -688,7 +629,7 @@ db_set( if (rc) goto error; - rc = session_add(ses, ucli, uusr, uperm, value); + rc = add_rule(ucli, uses, uusr, uperm, uval, time2exph(expire)); return 0; error: @@ -702,11 +643,11 @@ db_test( const char *session, const char *user, const char *permission, - uint32_t *value + const char **value, + time_t *expire ) { - uint32_t ucli, uusr, i, val, score, sc; - session_t *ses; - rule_t *rule; + uint32_t ucli, uses, uusr, i, score, sc, now; + rule_t *rule, *found; /* check */ client = is_any_or_wide(client) ? WIDESTR : client; @@ -715,41 +656,203 @@ db_test( permission = is_any_or_wide(permission) ? WIDESTR : permission; /* search the items */ - val = score = 0; -#define NOIDX 0 if (db_get_name_index(&ucli, client, false)) ucli = NOIDX; + if (db_get_name_index(&uses, session, false)) + uses = NOIDX; if (db_get_name_index(&uusr, user, false)) uusr = NOIDX; - /* get the session */ - if (get_session(session, false, &ses)) - ses = &sessions; - -retry: /* search the existing rule */ - for (i = 0 ; i < ses->count ; i++) { - rule = &ses->rules[i]; - if ((ucli == rule->client || WIDEIDX == rule->client) + now = time2expl(time(NULL)); + found = NULL; + score = 0; + for (i = 0 ; i < rules_count ; i++) { + rule = &rules[i]; + if ((!rule->expire || rule->expire >= now) + && (ucli == rule->client || WIDEIDX == rule->client) + && (uses == rule->session || WIDEIDX == rule->session) && (uusr == rule->user || WIDEIDX == rule->user) && (WIDEIDX == rule->permission || !strcasecmp(permission, name_at(rule->permission)))) { /* found */ - sc = 1 + (rule->client != WIDEIDX) + sc = 1 + (rule->client != WIDEIDX) + (rule->session != WIDEIDX) + (rule->user != WIDEIDX) + (rule->permission != WIDEIDX); if (sc > score) { score = sc; - val = rule->value; + found = rule; } } } - if (!score && ses != &sessions) { - ses = &sessions; - goto retry; + if (!found) { + *value = NULL; + *expire = 0; + return 0; + } + *value = name_at(found->value); + *expire = exp2time(found->expire); + return 1; +} + +typedef struct gc gc_t; +struct gc +{ + uint32_t *befores; + uint32_t *afters; +}; + +/** compare indexes. used by qsort and bsearch */ +static +int +cmpidxs( + const void *pa, + const void *pb +) { + uint32_t a = *(const uint32_t*)pa; + uint32_t b = *(const uint32_t*)pb; + return a < b ? -1 : a != b; +} + +static +void +gc_mark( + gc_t *gc, + uint32_t idx +) { + uint32_t *p = bsearch(&idx, gc->befores, names_count, sizeof *gc->befores, cmpidxs); + assert(p != NULL); + gc->afters[p - gc->befores] = 1; +} + +static +uint32_t +gc_after( + gc_t *gc, + uint32_t idx +) { + uint32_t *p = bsearch(&idx, gc->befores, names_count, sizeof *gc->befores, cmpidxs); + assert(p != NULL); + return gc->afters[p - gc->befores]; +} + +static +int +gc_init( + gc_t *gc +) { + gc->befores = malloc((sizeof *gc->befores + sizeof *gc->afters) * names_count); + if (gc->befores == NULL) + return -ENOMEM; + + names_count = names_count; + memcpy(gc->befores, names_sorted, names_count * sizeof *gc->befores); + qsort(gc->befores, names_count, sizeof *gc->befores, cmpidxs); + gc->afters = &gc->befores[names_count]; + memset(gc->afters, 0, names_count * sizeof *gc->afters); + + gc_mark(gc, ANYIDX); + gc_mark(gc, WIDEIDX); + return 0; +} + +static +void +gc_end( + gc_t *gc +) { + free(gc->befores); +} + +static +int +gc_pack( + gc_t *gc +) { + uint32_t i, j, n, next, prev; + char *strings; + + /* skip the unchanged initial part */ + n = names_count; + i = 0; + while (i < n && gc->afters[i]) + i++; + + /* at end means no change */ + if (i == n) + return 0; + + /* pack the strings */ + strings = fnames.buffer; + j = i; + memcpy(gc->afters, gc->befores, j * sizeof *gc->afters); + next = gc->befores[i++]; + fnames.saved = next; + while (i < n) { + if (gc->afters[i]) { + gc->befores[j] = prev = gc->befores[i]; + gc->afters[j++] = next; + while ((strings[next++] = strings[prev++])); + } + i++; + } + fnames.used = next; + names_count = j; + memcpy(names_sorted, gc->afters, j * sizeof *gc->afters); + qsort(names_sorted, j, sizeof *names_sorted, cmpnames); + + return 1; +} + +int +db_cleanup( +) { + int rc, chg; + uint32_t idbef, idaft, i, j, now; + gc_t gc; + rule_t *rule; + + /* init garbage collector */ + rc= gc_init(&gc); + if (rc < 0) + return rc; + + /* default now */ + now = time2expl(time(NULL)); + + /* remove expired entries and mark string ids of remaining ones */ + i = 0; + while (i < rules_count) { + rule = &rules[i]; + if (rule->expire && now >= rule->expire) + drop_at(i); + else { + for (j = 0 ; j < 5 ; j++) + gc_mark(&gc, rule->ids[j]); + i++; + } + } + + /* pack the strings */ + if (gc_pack(&gc)) { + /* replace the ids if changed */ + i = 0; + while (i < rules_count) { + rule = &rules[i]; + for (chg = j = 0 ; j < 5 ; j++) { + idbef = rule->ids[j]; + idaft = gc_after(&gc, idbef); + rule->ids[j] = idaft; + chg += idbef != idaft; + } + if (chg) + touch_at(i); + i++; + } } - if (score) - *value = val; - return score > 0; + /* terminate */ + gc_end(&gc); + + return 0; } @@ -89,7 +89,8 @@ db_for_all( const char *session, const char *user, const char *permission, - uint32_t value), + const char *value, + time_t expire), const char *client, const char *session, const char *user, @@ -114,7 +115,8 @@ db_set( const char *session, const char *user, const char *permission, - uint32_t value + const char *value, + time_t expire ); /** check rules */ @@ -125,6 +127,12 @@ db_test( const char *session, const char *user, const char *permission, - uint32_t *value + const char **value, + time_t *expire +); + +/** cleanup the base */ +int +db_cleanup( ); diff --git a/src/lib-compat.c b/src/lib-compat.c index 794487d..b9934af 100644 --- a/src/lib-compat.c +++ b/src/lib-compat.c @@ -77,26 +77,25 @@ static int from_check_status(int rc) return rc; } -static int from_value(uint32_t value) +static int from_value(const char *value) { - switch(value) { - case 0: return CYNARA_ADMIN_DENY; - case 1: return CYNARA_ADMIN_ALLOW; - case 2: return CYNARA_ADMIN_ASK; - } - return (int)value; + if (!strcmp(value, "yes")) + return CYNARA_ADMIN_ALLOW; + if (!strcmp(value, "ask")) + return CYNARA_ADMIN_ASK; + return CYNARA_ADMIN_DENY; } -static uint32_t to_value(int value) +static const char *to_value(int value) { switch(value) { - case CYNARA_ADMIN_DENY: return 0; - case CYNARA_ADMIN_NONE: return 0; - case CYNARA_ADMIN_BUCKET: return 0; - case CYNARA_ADMIN_ALLOW: return 1; - case CYNARA_ADMIN_ASK: return 2; + case CYNARA_ADMIN_DENY: + case CYNARA_ADMIN_NONE: + case CYNARA_ADMIN_BUCKET: return "no"; + case CYNARA_ADMIN_ALLOW: return "yes"; + case CYNARA_ADMIN_ASK: return "ask"; } - return (uint32_t)value; + return "?"; } /************************************ ERROR ****************************************/ @@ -173,7 +172,7 @@ int cynara_admin_set_policies(struct cynara_admin *p_cynara_admin, p->client, "*", p->user, p->privilege); else if (p->result != CYNARA_ADMIN_BUCKET && p->result != CYNARA_ADMIN_NONE) rc = rcyn_set((rcyn_t*)p_cynara_admin, - p->client, "*", p->user, p->privilege, to_value(p->result)); + p->client, "*", p->user, p->privilege, to_value(p->result), 0); p = *++policies; } rc2 = rcyn_leave((rcyn_t*)p_cynara_admin, rc == 0); @@ -189,7 +188,8 @@ static void check_cb( const char *session, const char *user, const char *permission, - uint32_t value + const char *value, + time_t expire ) { *((int*)closure) = from_value(value); } @@ -219,7 +219,8 @@ static void list_cb( const char *session, const char *user, const char *permission, - uint32_t value + const char *value, + time_t expire ) { struct list_data *data = closure; struct cynara_admin_policy *pol; diff --git a/src/main-cynadm.c b/src/main-cynadm.c new file mode 100644 index 0000000..e5318a1 --- /dev/null +++ b/src/main-cynadm.c @@ -0,0 +1,297 @@ +/* + * 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> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <signal.h> + +#include "rcyn-client.h" + +rcyn_t *rcyn; + +char buffer[4000]; +char *str[40]; +int nstr; + +static const int MIN = 60; +static const int HOUR = 60*MIN; +static const int DAY = 24*HOUR; +static const int YEAR = 365*DAY; + +const char *client, *session, *user, *permission, *value; +time_t expire; +int txt2experr; + +time_t txt2exp(const char *txt) +{ + time_t r, x; + + txt2experr = 0; + if (!strcmp(txt, "always")) + return 0; + + r = time(NULL); + while(*txt) { + x = 0; + while('0' <= *txt && *txt <= '9') + x = 10 * x + (time_t)(*txt++ - '0'); + switch(*txt) { + case 'y': r += x * YEAR; txt++; break; + case 'd': r += x *= DAY; txt++; break; + case 'h': r += x *= HOUR; txt++; break; + case 'm': r += x *= MIN; txt++; break; + case 's': txt++; /*@fallthrough@*/ + case 0: r += x; break; + default: txt2experr = 1; return -1; + } + } + return r; +} + +const char *exp2txt(time_t expire) +{ + static char buffer[200]; + int n; + + if (!expire) + return "always"; + expire -= time(NULL); + n = 0; + if (expire >= YEAR) { + n += snprintf(&buffer[n], sizeof buffer - n, "%lldy", + (long long)(expire / YEAR)); + expire = expire % YEAR; + } + if (expire >= DAY) { + n += snprintf(&buffer[n], sizeof buffer - n, "%lldd", + (long long)(expire / DAY)); + expire = expire % DAY; + } + if (expire >= HOUR) { + n += snprintf(&buffer[n], sizeof buffer - n, "%lldh", + (long long)(expire / HOUR)); + expire = expire % HOUR; + } + if (expire >= MIN) { + n += snprintf(&buffer[n], sizeof buffer - n, "%lldm", + (long long)(expire / MIN)); + expire = expire % MIN; + } + if (expire) { + n += snprintf(&buffer[n], sizeof buffer - n, "%llds", + (long long)expire); + } + return buffer; +} + +int plink(int ac, char **av, int *used, int maxi) +{ + int r = 0; + + if (maxi < ac) + ac = maxi; + while (r < ac && strcmp(av[r], ";")) + r++; + + *used = r + (r < ac); + return r; +} + +int get_csupve(int ac, char **av, int *used, const char *def) +{ + int n = plink(ac, av, used, 7); + + client = n > 1 ? av[1] : def; + session = n > 2 ? av[2] : def; + user = n > 3 ? av[3] : def; + permission = n > 4 ? av[4] : def; + value = n > 5 ? av[5] : "no"; + expire = n > 6 ? txt2exp(av[6]) : 0; + + return client && session && user && permission && value && !txt2experr ? 0 : -EINVAL; +} + +int get_csup(int ac, char **av, int *used, const char *def) +{ + int n = plink(ac, av, used, 5); + + client = n > 1 ? av[1] : def; + session = n > 2 ? av[2] : def; + user = n > 3 ? av[3] : def; + permission = n > 4 ? av[4] : def; + + return client && session && user && permission ? 0 : -EINVAL; +} + +void listcb(void *closure, const char *client, const char *session, + const char *user, const char *permission, + const char *value, time_t expire) +{ + int *p = closure; + const char *e = exp2txt(expire); + fprintf(stdout, "%s\t%s\t%s\t%s\t%s\t%s\n", client, session, user, permission, value, e); + (*p)++; +} + +int do_list(int ac, char **av) +{ + int count, uc, rc; + + rc = get_csup(ac, av, &uc, "#"); + if (rc == 0) { + count = 0; + rc = rcyn_get(rcyn, client, session, user, permission, listcb, &count); + if (rc < 0) + fprintf(stderr, "error %s\n", strerror(-rc)); + else + fprintf(stdout, "%d entries found\n", count); + } + return uc; +} + +int do_set(int ac, char **av) +{ + int uc, rc; + + rc = get_csupve(ac, av, &uc, "*"); + if (rc == 0) + rc = rcyn_enter(rcyn); + if (rc == 0) { + rc = rcyn_set(rcyn, client, session, user, permission, value, expire); + rcyn_leave(rcyn, !rc); + } + if (rc < 0) + fprintf(stderr, "error %s\n", strerror(-rc)); + return uc; +} + +int do_drop(int ac, char **av) +{ + int uc, rc; + + rc = get_csup(ac, av, &uc, "#"); + if (rc == 0) + rc = rcyn_enter(rcyn); + if (rc == 0) { + rc = rcyn_drop(rcyn, client, session, user, permission); + rcyn_leave(rcyn, !rc); + } + if (rc < 0) + fprintf(stderr, "error %s\n", strerror(-rc)); + return uc; +} + +int do_check(int ac, char **av, int (*f)(rcyn_t*,const char*,const char*,const char*,const char*)) +{ + int uc, rc; + + rc = get_csup(ac, av, &uc, NULL); + if (rc == 0) { + rc = f(rcyn, client, session, user, permission); + if (rc > 0) + fprintf(stdout, "allowed\n"); + else if (rc == 0) + fprintf(stdout, "denied\n"); + else + fprintf(stderr, "error %s\n", strerror(-rc)); + } + return uc; +} + +int do_any(int ac, char **av) +{ + if (!ac) + return 0; + + if (!strcmp(av[0], "list")) + return do_list(ac, av); + + if (!strcmp(av[0], "set")) + return do_set(ac, av); + + if (!strcmp(av[0], "drop")) + return do_drop(ac, av); + + if (!strcmp(av[0], "check")) + return do_check(ac, av, rcyn_check); + + if (!strcmp(av[0], "test")) + return do_check(ac, av, rcyn_test); + + if (!strcmp(av[0], "cache")) + return do_check(ac, av, rcyn_cache_check); + + if (!strcmp(av[0], "clear")) { + rcyn_cache_clear(rcyn); + return 1; + } + + return 0; +} + +void do_all(int ac, char **av) +{ + int rc; + + while(ac) { + rc = do_any(ac, av); + if (rc <= 0) + exit(1); + ac -= rc; + av += rc; + } +} + +int main(int ac, char **av) +{ + int rc; + + signal(SIGPIPE, SIG_IGN); /* avoid SIGPIPE! */ + rc = rcyn_open(&rcyn, rcyn_Admin, 5000); + if (rc < 0) { + fprintf(stderr, "initialisation failed: %s\n", strerror(-rc)); + return 1; + } + if (ac > 1) { + do_all(ac - 1, av + 1); + return 0; + } + + for(;;) { + if (!fgets(buffer, sizeof buffer, stdin)) + break; + + str[nstr = 0] = strtok(buffer, " \t\n"); + while(str[nstr]) + str[++nstr] = strtok(NULL, " \t\n"); + + ac = 0; + while(ac < nstr) { + rc = do_any(nstr - ac, &str[ac]); + if (rc <= 0) + exit(1); + ac += rc; + } + } + return 0; +} + diff --git a/src/main-cynarad.c b/src/main-cynarad.c index ba7d71c..5744672 100644 --- a/src/main-cynarad.c +++ b/src/main-cynarad.c @@ -23,6 +23,7 @@ #include <stdio.h> #include <limits.h> #include <string.h> +#include <time.h> #include <getopt.h> #include <unistd.h> #include <pwd.h> @@ -106,24 +107,24 @@ helptxt[] = "\n" "otpions:\n" #if defined(WITH_SYSTEMD_ACTIVATION) - " -s, --systemd socket activation by systemd\n" + " -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" + " -i, --init xxx initialize if needed the database with file xxx\n" + " (default: "DEFAULT_INIT_FILE"\n" " -b, --dbdir xxx set the directory of database\n" - " (default: "DEFAULT_DB_DIR")\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" + " (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" + " -h, --help print this help and exit\n" + " -v, --version print the version and exit\n" "\n" ; @@ -435,12 +436,42 @@ static void ensure_directory(const char *path, int uid, int gid) ensuredir(p, (int)l, uid, gid); } +time_t txt2exp(const char *txt) +{ + static const int MIN = 60; + static const int HOUR = 60*MIN; + static const int DAY = 24*HOUR; + static const int YEAR = 365*DAY; + + time_t r, x; + + if (!strcmp(txt, "always")) + return 0; + r = time(NULL); + while(*txt) { + x = 0; + while('0' <= *txt && *txt <= '9') + x = 10 * x + (time_t)(*txt++ - '0'); + switch(*txt) { + case 'y': r += x * YEAR; txt++; break; + case 'd': r += x *= DAY; txt++; break; + case 'h': r += x *= HOUR; txt++; break; + case 'm': r += x *= MIN; txt++; break; + case 's': txt++; /*@fallthrough@*/ + case 0: r += x; break; + default: return -1; + } + } + return r; +} + /** initialize the database from file of 'path' */ static void initdb(const char *path) { - int rc, lino, x; + int rc, lino; char *item[10]; char buffer[2048]; + time_t expire; FILE *f; f = fopen(path, "r"); @@ -459,20 +490,22 @@ static void initdb(const char *path) item[3] = strtok(NULL, " \t\n\r"); item[4] = strtok(NULL, " \t\n\r"); item[5] = strtok(NULL, " \t\n\r"); + item[6] = strtok(NULL, " \t\n\r"); if (item[1] == NULL || item[2] == NULL - || item[3] == NULL || item[4] == NULL) { + || item[3] == NULL || item[4] == NULL + || item[5] == NULL) { fprintf(stderr, "field missing (%s:%d)\n", path, lino); exit(1); - } else if (item[5] != NULL && item[5][0] != '#') { + } else if (item[6] != NULL && item[6][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); + expire = txt2exp(item[5]); + if (expire < 0) { + fprintf(stderr, "bad expiration %s (%s:%d)\n", item[5], path, lino); exit(1); } - rc = db_set(item[0], item[1], item[2], item[3], x); + rc = db_set(item[0], item[1], item[2], item[3], item[4], expire); if (rc < 0) { fprintf(stderr, "can't set (%s:%d)\n", path, lino); exit(1); diff --git a/src/test-lib-compat.c b/src/main-test-old-cynara.c index 9f9ea1b..9f9ea1b 100644 --- a/src/test-lib-compat.c +++ b/src/main-test-old-cynara.c diff --git a/src/queue.c b/src/queue.c index e2fb84c..0699bb1 100644 --- a/src/queue.c +++ b/src/queue.c @@ -55,16 +55,8 @@ qread( static bool -qget_char( - char *value -) { - return qread(value, sizeof *value); -} - -static -bool -qget_uint32( - uint32_t *value +qget_time( + time_t *value ) { return qread(value, sizeof *value); } @@ -109,16 +101,8 @@ qwrite( static bool -qput_char( - char value -) { - return qwrite(&value, sizeof value); -} - -static -bool -qput_uint32( - uint32_t value +qput_time( + time_t value ) { return qwrite(&value, sizeof value); } @@ -144,11 +128,11 @@ queue_drop( const char *user, const char *permission ) { - return qput_char(DROP) - && qput_string(client) + return qput_string(client) && qput_string(session) && qput_string(user) && qput_string(permission) + && qput_string(0) ? 0 : -(errno = ENOMEM); } @@ -158,14 +142,15 @@ queue_set( const char *session, const char *user, const char *permission, - uint32_t value + const char *value, + time_t expire ) { - return qput_char(SET) - && qput_string(client) + return qput_string(client) && qput_string(session) && qput_string(user) && qput_string(permission) - && qput_uint32(value) + && qput_string(value) + && qput_time(expire) ? 0 : -(errno = ENOMEM); } @@ -180,26 +165,28 @@ int queue_play( ) { int rc, rc2; - char op; const char *client; const char *session; const char *user; const char *permission; - uint32_t value; + const char *value; + time_t expire; rc = 0; queue.read = 0; while (queue.read < queue.write) { rc2 = -EINVAL; - if (qget_char(&op) - && qget_string(&client) + if (qget_string(&client) && qget_string(&session) && qget_string(&user) - && qget_string(&permission)) { - if (op == DROP) + && qget_string(&permission) + && qget_string(&value)) { + if (!value[0]) rc2 = db_drop(client, session, user, permission); - else if (qget_uint32(&value)) - rc2 = db_set(client, session, user, permission, value); + else { + if (qget_time(&expire)) + rc2 = db_set(client, session, user, permission, value, expire); + } } if (rc2 != 0 && rc == 0) rc = rc2; diff --git a/src/queue.h b/src/queue.h index 1937ee1..a9fc454 100644 --- a/src/queue.h +++ b/src/queue.h @@ -32,7 +32,8 @@ queue_set( const char *session, const char *user, const char *permission, - uint32_t value + const char *value, + time_t expire ); extern diff --git a/src/rcyn-client.c b/src/rcyn-client.c index e1f7841..47a5798 100644 --- a/src/rcyn-client.c +++ b/src/rcyn-client.c @@ -261,11 +261,29 @@ status_done( static int status_check( - rcyn_t *rcyn + rcyn_t *rcyn, + time_t *expire ) { - return !strcmp(rcyn->reply.fields[0], _yes_) ? 1 - : !strcmp(rcyn->reply.fields[0], _no_) ? 0 - : -EEXIST; + int rc, exp; + + if (!strcmp(rcyn->reply.fields[0], _yes_)) { + rc = 1; + exp = 1; + } else if (!strcmp(rcyn->reply.fields[0], _no_)) { + rc = 0; + exp = 1; + } else if (!strcmp(rcyn->reply.fields[0], _done_)) { + rc = -EEXIST; + exp = 2; + } else { + rc = -EPROTO; + exp = rcyn->reply.count; + } + if (exp < rcyn->reply.count) + *expire = strtoll(rcyn->reply.fields[exp], NULL, 10); + else + *expire = 0; + return rc; } static @@ -366,6 +384,8 @@ int ensure_opened( rcyn_t *rcyn ) { + if (rcyn->fd >= 0 && write(rcyn->fd, NULL, 0) < 0) + disconnection(rcyn); return rcyn->fd < 0 ? connection(rcyn) : 0; } @@ -475,6 +495,7 @@ check_or_test( const char *action ) { int rc; + time_t expire; if (rcyn->async.requests != NULL) return -EINPROGRESS; @@ -496,9 +517,9 @@ check_or_test( /* get the response */ rc = wait_pending_reply(rcyn); if (rc >= 0) { - rc = status_check(rcyn); + rc = status_check(rcyn, &expire); if (rc >= 0) - cache_put(rcyn->cache, client, session, user, permission, rc); + cache_put(rcyn->cache, client, session, user, permission, rc, expire); } } return rc; @@ -523,7 +544,7 @@ rcyn_test( const char *user, const char *permission ) { - return check_or_test(rcyn, client, session, user, permission, _check_); + return check_or_test(rcyn, client, session, user, permission, _test_); } int @@ -533,9 +554,11 @@ rcyn_set( const char *session, const char *user, const char *permission, - int value + const char *value, + time_t expire ) { - char val[30]; + char text[30]; + const char *exp; int rc; if (rcyn->type != rcyn_Admin) @@ -546,8 +569,13 @@ rcyn_set( if (rc < 0) return rc; - snprintf(val, sizeof val, "%u", (unsigned)value); - rc = putx(rcyn, _set_, client, session, user, permission, val, NULL); + if (!expire) + exp = NULL; + else { + snprintf(text, sizeof text, "%lld", (long long)expire); + exp = text; + } + rc = putx(rcyn, _set_, client, session, user, permission, value, exp, NULL); if (rc >= 0) rc = wait_done(rcyn); return rc; @@ -566,7 +594,8 @@ rcyn_get( const char *session, const char *user, const char *permission, - uint32_t value + const char *value, + time_t expire ), void *closure ) { @@ -583,13 +612,14 @@ rcyn_get( rc = putx(rcyn, _get_, client, session, user, permission, NULL); if (rc >= 0) { rc = wait_reply(rcyn, true); - while (rc == 6 && !strcmp(rcyn->reply.fields[0], _item_)) { + while ((rc == 6 || rc == 7) && !strcmp(rcyn->reply.fields[0], _item_)) { callback(closure, rcyn->reply.fields[1], rcyn->reply.fields[2], rcyn->reply.fields[3], rcyn->reply.fields[4], - (uint32_t)atoi(rcyn->reply.fields[5])); + rcyn->reply.fields[5], + rc == 6 ? 0 : (time_t)strtoll(rcyn->reply.fields[6], NULL, 10)); rc = wait_reply(rcyn, true); } rc = status_done(rcyn); @@ -683,6 +713,7 @@ rcyn_async_process( const char *first; asreq_t *ar; const char *client, *session, *user, *permission; + time_t expire; for (;;) { /* non blocking wait for a reply */ @@ -707,13 +738,13 @@ rcyn_async_process( /* emit the asynchronous answer */ rcyn->async.requests = ar->next; - rc = status_check(rcyn); + rc = status_check(rcyn, &expire); 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); + cache_put(rcyn->cache, client, session, user, permission, rc, expire); } ar->callback(ar->closure, rc); free(ar); diff --git a/src/rcyn-client.h b/src/rcyn-client.h index a4104ee..611f843 100644 --- a/src/rcyn-client.h +++ b/src/rcyn-client.h @@ -81,7 +81,8 @@ rcyn_set( const char *session, const char *user, const char *permission, - int value + const char *value, + time_t expire ); extern @@ -98,7 +99,8 @@ rcyn_get( const char *session, const char *user, const char *permission, - uint32_t value + const char *value, + time_t expire ), void *closure ); diff --git a/src/rcyn-protocol.txt b/src/rcyn-protocol.txt index 134d05e..7a35ab9 100644 --- a/src/rcyn-protocol.txt +++ b/src/rcyn-protocol.txt @@ -13,12 +13,12 @@ invalidate cache: test a permission: c->s test CLIENT SESSION USER PERMISSION - s->c yes|no|VALUE [CACHING] + s->c [yes|no|done VALUE] [EXPIRE] check a permission: c->s check CLIENT SESSION USER PERMISSION - s->c yes|no|VALUE [CACHING] + s->c [yes|no|done VALUE] [EXPIRE] erase (admin): @@ -27,13 +27,13 @@ erase (admin): set (admin): - c->s set CLIENT SESSION USER PERMISSION VALUE + c->s set CLIENT SESSION USER PERMISSION VALUE [EXPIRE] s->c done|error ... list permissions (admin): c->s get CLIENT SESSION USER PERMISSION - s->c item CLIENT SESSION USER PERMISSION VALUE + s->c item CLIENT SESSION USER PERMISSION VALUE [EXPIRE] s->c ... s->c done @@ -65,15 +65,14 @@ asking (agent ask CLIENT SESSION USER PERMISSION): c->s g(et) CLIENT SESSION USER PERMISSION c->s l(eave) [commit|rollback] c->s r(cyn) - c->s s(et) CLIENT SESSION USER PERMISSION VALUE + c->s s(et) CLIENT SESSION USER PERMISSION VALUE EXPIRE c->s t(est) CLIENT SESSION USER PERMISSION s->c clear s->c done s->c done [CLIENT SESSION USER PERMISSION VALUE] s->c done|error ... - s->c item CLIENT SESSION USER PERMISSION VALUE - s->c yes|no - s->c yes|no|VALUE [CACHING] + s->c item CLIENT SESSION USER PERMISSION VALUE EXPIRE + s->c done VALUE EXPIRE diff --git a/src/rcyn-server.c b/src/rcyn-server.c index 2fff5db..13f3a2e 100644 --- a/src/rcyn-server.c +++ b/src/rcyn-server.c @@ -277,26 +277,37 @@ entercb( send_done(cli); } +/** translate optional expire value */ +static +const char * +exp2txt( + time_t expire, + char *buffer, + size_t bufsz +) { + if (!expire) + return NULL; + + /* TODO: check size */ + snprintf(buffer, bufsz, "%lld", (long long)expire); + return buffer; +} + /** callback of checking */ static void checkcb( void *closure, - uint32_t value + const char *value, + time_t expire ) { client_t *cli = closure; - const char *a; - char val[30]; - - if (value == DENY) - a = _no_; - else if (value == ALLOW) - a = _yes_; - else { - snprintf(val, sizeof val, "%d", value); - a = val; - } - putx(cli, a, NULL); + char text[30]; + + if (strcmp(value, ALLOW) && strcmp(value, DENY)) + putx(cli, _done_, value, exp2txt(expire, text, sizeof text), NULL); + else + putx(cli, value, exp2txt(expire, text, sizeof text), NULL); flushw(cli); } @@ -309,12 +320,14 @@ getcb( const char *session, const char *user, const char *permission, - uint32_t value + const char *value, + time_t expire ) { client_t *cli = closure; - char val[30]; - snprintf(val, sizeof val, "%d", value); - putx(cli, _item_, client, session, user, permission, val, NULL); + char text[30]; + + putx(cli, _item_, client, session, user, permission, + value, exp2txt(expire, text, sizeof text), NULL); } /** handle a request */ @@ -326,7 +339,8 @@ onrequest( const char *args[] ) { int rc; - uint32_t value; + const char *value; + time_t expire; /* just ignore empty lines */ if (count == 0) @@ -397,21 +411,25 @@ onrequest( } break; case 's': /* set */ - if (ckarg(args[0], _set_, 1) && count == 6) { + if (ckarg(args[0], _set_, 1) && (count == 6 || count == 7)) { if (cli->type != rcyn_Admin) break; if (!cli->entered) break; - value = (uint32_t)atol(args[5]); - rc = cyn_set(args[1], args[2], args[3], args[4], value); + if (count == 6) + expire = 0; + else + expire = strtoll(args[6], NULL, 10); + rc = cyn_set(args[1], args[2], args[3], args[4], args[5], expire); send_done_or_error(cli, rc); return; } break; case 't': /* test */ if (ckarg(args[0], _test_, 1) && count == 5) { - cyn_test(args[1], args[2], args[3], args[4], &value); - checkcb(cli, value); + cli->checked = 1; + cyn_test(args[1], args[2], args[3], args[4], &value, &expire); + checkcb(cli, value, expire); return; } break; @@ -740,44 +758,3 @@ rcyn_server_serve( 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; - } - - /* ready ! */ -#if defined(WITH_SYSTEMD_ACTIVATION) - sd_notify(0, "READY=1"); -#endif - - /* process inputs */ - rcyn_server_serve(server); -} -#endif - |