aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJose Bollo <jose.bollo@iot.bzh>2018-09-19 14:52:03 +0200
committerJose Bollo <jose.bollo@iot.bzh>2018-09-20 23:39:57 +0200
commit4123ffb189acd0c18cddd0df75a58cbc3276ad80 (patch)
treea393e83dce5f57c7db0ab5475841c86829468ebe
parent7ae3477d84ae13c5e9ef9bb1980a8f70f8b7e9bf (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.initial4
-rw-r--r--src/.gitignore5
-rw-r--r--src/CMakeLists.txt16
-rw-r--r--src/Makefile26
-rw-r--r--src/cache.c155
-rw-r--r--src/cache.h3
-rw-r--r--src/cyn.c27
-rw-r--r--src/cyn.h17
-rw-r--r--src/db.c517
-rw-r--r--src/db.h14
-rw-r--r--src/lib-compat.c35
-rw-r--r--src/main-cynadm.c297
-rw-r--r--src/main-cynarad.c61
-rw-r--r--src/main-test-old-cynara.c (renamed from src/test-lib-compat.c)0
-rw-r--r--src/queue.c55
-rw-r--r--src/queue.h3
-rw-r--r--src/rcyn-client.c63
-rw-r--r--src/rcyn-client.h6
-rw-r--r--src/rcyn-protocol.txt15
-rw-r--r--src/rcyn-server.c105
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
diff --git a/src/cyn.c b/src/cyn.c
index 85deb4e..914bf52 100644
--- a/src/cyn.c
+++ b/src/cyn.c
@@ -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;
}
diff --git a/src/cyn.h b/src/cyn.h
index df08a5a..379deef 100644
--- a/src/cyn.h
+++ b/src/cyn.h
@@ -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,
diff --git a/src/db.c b/src/db.c
index f5f46da..f77b76f 100644
--- a/src/db.c
+++ b/src/db.c
@@ -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;
}
diff --git a/src/db.h b/src/db.h
index 0639833..192e215 100644
--- a/src/db.h
+++ b/src/db.h
@@ -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
-