summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJose Bollo <jose.bollo@iot.bzh>2019-05-15 09:08:08 +0200
committerJose Bollo <jose.bollo@iot.bzh>2019-05-22 15:57:34 +0200
commit2b70c9ff631b0a2f0c7ec771e2f390a1d677af1e (patch)
treeab64408bd8a443536e380aef03fed37df1a3dbf6
parentc451f53b4f3acd2157f9c7e7365ecc5663e9ada7 (diff)
Refactor agent mechanism
Agent are now named and called when the value returned is prefixed by name+colon. For example, the agent 'me' receives the requests 'request' and the asked key for the rule: * * * * me:request forever Add the always available AGENT-AT implementation. The AGENT-AT handles the value prefix @: to re-ask the database with a query derived from the value. Example: the rule * * 1001 * @:%c:%s:OWNER:%p forever if selected for the query key {client=C, session=S, user=1001, permission=P} will produce the evaluation of the key {client=C, session=S, user=OWNER, permission=P} The values @: are structured as 4 field separated by colons (:). The sequences %c, %s, %u, %p, %% and %: are substituted by client, session, user, permission, % and :, with values coming from the original request. Change-Id: I7043845292f13f9c269a71cfabc4715330eaff34 Signed-off-by: José Bollo <jose.bollo@iot.bzh>
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/agent-at.c126
-rw-r--r--src/agent-at.h23
-rw-r--r--src/anydb.c3
-rw-r--r--src/cyn.c267
-rw-r--r--src/cyn.h20
-rw-r--r--src/main-cynadm.c2
-rw-r--r--src/main-cynarad.c4
-rw-r--r--src/prot.c1
-rw-r--r--src/rcyn-server.c9
10 files changed, 358 insertions, 98 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e8071e8..9b09a89 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -17,6 +17,7 @@
###########################################################################
set(SERVER_SOURCES
+ agent-at.c
anydb.c
cyn.c
db.c
diff --git a/src/agent-at.c b/src/agent-at.c
new file mode 100644
index 0000000..f671fc7
--- /dev/null
+++ b/src/agent-at.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+
+#include "data.h"
+#include "cyn.h"
+
+static
+size_t
+parse(
+ const char *spec,
+ const data_key_t *rkey,
+ data_key_t *key,
+ char *buffer,
+ size_t szbuf
+) {
+ size_t iout, ikey, inf;
+ const char *val;
+
+ iout = 0;
+ for (ikey = 0 ; ikey < 4 ; ikey++) {
+ inf = iout;
+ while(*spec) {
+ if (*spec == ':' && ikey < 3) {
+ spec++;
+ break;
+ }
+ if (*spec == '%' && spec[1]) {
+ switch(spec[1]) {
+ case 'c':
+ val = rkey->client;
+ break;
+ case 's':
+ val = rkey->session;
+ break;
+ case 'u':
+ val = rkey->user;
+ break;
+ case 'p':
+ val = rkey->permission;
+ break;
+ default:
+ val = 0;
+ }
+ if (val) {
+ while (*val) {
+ if (iout < szbuf)
+ buffer[iout] = *val;
+ iout++;
+ val++;
+ }
+ } else {
+ if (spec[1] != ':' && spec[1] != '%') {
+ if (iout < szbuf)
+ buffer[iout] = '%';
+ iout++;
+ }
+ if (iout < szbuf)
+ buffer[iout] = spec[1];
+ iout++;
+ }
+ spec += 2;
+ } else {
+ if (iout < szbuf)
+ buffer[iout] = *spec;
+ iout++;
+ spec++;
+ }
+ }
+ if (inf == iout)
+ val = 0;
+ else {
+ val = &buffer[inf];
+ if (iout < szbuf)
+ buffer[iout] = 0;
+ iout++;
+ }
+ if (key)
+ ((const char**)key)[ikey] = val;
+ }
+ return iout;
+}
+
+static
+int
+agent_at_cb(
+ const char *name,
+ void *agent_closure,
+ const data_key_t *key,
+ const char *value,
+ on_result_cb_t *on_result_cb,
+ void *on_result_closure
+) {
+ data_key_t atkey;
+ char *block;
+ size_t size;
+
+ size = parse(value, key, 0, 0, 0);
+ block = alloca(size);
+ parse(value, key, &atkey, block, size);
+ return cyn_test_async(on_result_cb, on_result_closure, &atkey);
+}
+
+int
+agent_at_activate(
+) {
+ return cyn_agent_add("@", agent_at_cb, 0);
+}
diff --git a/src/agent-at.h b/src/agent-at.h
new file mode 100644
index 0000000..7bbffc0
--- /dev/null
+++ b/src/agent-at.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+extern
+int
+agent_at_activate(
+);
diff --git a/src/anydb.c b/src/anydb.c
index 995cd0a..d522c96 100644
--- a/src/anydb.c
+++ b/src/anydb.c
@@ -456,9 +456,6 @@ anydb_test(
if (s.score) {
value->value = string(db, s.idxval);
value->expire = s.expire;
- } else {
- value->value = NULL;
- value->expire = 0;
}
return s.score;
}
diff --git a/src/cyn.c b/src/cyn.c
index e08c262..f853a53 100644
--- a/src/cyn.c
+++ b/src/cyn.c
@@ -22,7 +22,6 @@
#include <string.h>
#include <errno.h>
-
#include "data.h"
#include "db.h"
#include "queue.h"
@@ -35,44 +34,36 @@ struct callback
void *any_cb;
on_enter_cb_t *on_enter_cb;
on_change_cb_t *on_change_cb;
- agent_cb_t *agent_cb;
};
void *closure;
};
-struct async_check
+struct agent
{
- struct async_check *next;
- on_result_cb_t *on_result_cb;
+ struct agent *next;
+ agent_cb_t *agent_cb;
void *closure;
- data_key_t key;
- struct callback *next_agent;
+ uint8_t len;
+ char name[];
};
/** locking critical section */
static const void *lock;
static struct callback *awaiters;
static struct callback *observers;
-static struct callback *agents;
-static struct async_check *asynchecks;
+static struct agent *agents;
static
int
delcb(
void *callback,
void *closure,
- struct callback **head,
- struct async_check *achecks
+ struct callback **head
) {
struct callback *c;
while((c = *head)) {
if (c->any_cb == callback && c->closure == closure) {
- while (achecks) {
- if (achecks->next_agent == c)
- achecks->next_agent = c->next;
- achecks = achecks->next;
- }
*head = c->next;
free(c);
return 1;
@@ -130,7 +121,7 @@ cyn_enter_async_cancel(
on_enter_cb_t *enter_cb,
void *closure
) {
- return delcb(enter_cb, closure, &awaiters, 0);
+ return delcb(enter_cb, closure, &awaiters);
}
int
@@ -146,7 +137,7 @@ cyn_on_change_remove(
on_change_cb_t *on_change_cb,
void *closure
) {
- return delcb(on_change_cb, closure, &observers, 0);
+ return delcb(on_change_cb, closure, &observers);
}
/** leave critical recoverable section */
@@ -229,19 +220,63 @@ cyn_list(
db_for_all(closure, callback, key);
}
-int
-cyn_test(
- const data_key_t *key,
+static
+data_value_t *
+default_value(
data_value_t *value
) {
- int rc;
+ value->value = DEFAULT;
+ value->expire = 0;
+ return value;
+}
- rc = db_test(key, value);
- if (rc <= 0) {
- value->value = DEFAULT;
- value->expire = 0;
- }
- return rc;
+static
+struct agent *
+search_agent(
+ const char *name,
+ uint8_t len,
+ struct agent ***ppprev
+) {
+ struct agent *it, **pprev;
+
+ pprev = &agents;
+ while((it = *pprev)
+ && (len != it->len || memcmp(it->name, name, (size_t)len)))
+ pprev = &it->next;
+ *ppprev = pprev;
+ return it;
+}
+
+static
+struct agent *
+required_agent(
+ const char *value
+) {
+ struct agent **pprev;
+ uint8_t len;
+
+ for (len = 0 ; len < UINT8_MAX && value[len] ; len++)
+ if (value[len] == ':')
+ return search_agent(value, len, &pprev);
+ return 0;
+}
+
+struct async_check
+{
+ on_result_cb_t *on_result_cb;
+ void *closure;
+ data_key_t key;
+ data_value_t value;
+ int decount;
+};
+
+static
+void
+async_reply(
+ struct async_check *achk
+) {
+ achk->on_result_cb(achk->closure, &achk->value);
+ free(achk);
}
static
@@ -249,61 +284,76 @@ void
async_on_result(
void *closure,
const data_value_t *value
-) {
- struct async_check *achk = closure, **pac;
- struct callback *agent;
- int rc;
- data_value_t v;
-
- if (!value) {
- agent = achk->next_agent;
- while (agent) {
- achk->next_agent = agent->next;
- rc = agent->agent_cb(
- agent->closure,
- &achk->key,
- async_on_result,
- closure);
- if (!rc)
- return;
- agent = achk->next_agent;
- }
- v.value = DEFAULT;
- v.expire = 0;
- value = &v;
- }
+);
- achk->on_result_cb(achk->closure, value);
- pac = &asynchecks;
- while (*pac != achk)
- pac = &(*pac)->next;
- *pac = achk->next;
- free(achk);
+static
+void
+async_call_agent(
+ struct agent *agent,
+ struct async_check *achk
+) {
+ int rc = agent->agent_cb(
+ agent->name,
+ agent->closure,
+ &achk->key,
+ &achk->value.value[agent->len + 1],
+ async_on_result,
+ achk);
+ if (rc < 0)
+ async_reply(achk);
}
+static
+void
+async_on_result(
+ void *closure,
+ const data_value_t *value
+) {
+ struct async_check *achk = closure;
+ struct agent *agent;
+
+ achk->value = *value;
+ agent = required_agent(value->value);
+ if (agent && achk->decount) {
+ achk->decount--;
+ async_call_agent(agent, achk);
+ } else
+ async_reply(achk);
+}
+static
int
-cyn_check_async(
+async_check_or_test(
on_result_cb_t *on_result_cb,
void *closure,
- const data_key_t *key
+ const data_key_t *key,
+ int agentdeep
) {
+ int rc;
data_value_t value;
size_t szcli, szses, szuse, szper;
struct async_check *achk;
+ struct agent *agent;
void *ptr;
- cyn_test(key, &value);
- if (!strcmp(value.value, ALLOW) || !strcmp(value.value, DENY)) {
+ /* get the direct value */
+ rc = db_test(key, &value);
+
+ /* on error or missing result */
+ if (rc <= 0) {
+ default_value(&value);
on_result_cb(closure, &value);
- return 0;
+ return rc;
}
- if (!agents) {
+ /* if not an agent or agent not required */
+ agent = required_agent(value.value);
+ if (!agent || !agentdeep) {
on_result_cb(closure, &value);
- return 0;
+ return rc;
}
+ /* allocate asynchronous query */
szcli = key->client ? 1 + strlen(key->client) : 0;
szses = key->session ? 1 + strlen(key->session) : 0;
szuse = key->user ? 1 + strlen(key->user) : 0;
@@ -314,56 +364,111 @@ cyn_check_async(
return -ENOMEM;
}
- ptr = achk;
+ /* init the structure */
+ ptr = &achk[1];
achk->on_result_cb = on_result_cb;
achk->closure = closure;
if (!key->client)
achk->key.client = 0;
else {
achk->key.client = ptr;
- memcpy(ptr, key->client, szcli);
- ptr += szcli;
+ ptr = mempcpy(ptr, key->client, szcli);
}
if (!key->session)
achk->key.session = 0;
else {
achk->key.session = ptr;
- memcpy(ptr, key->session, szses);
- ptr += szses;
+ ptr = mempcpy(ptr, key->session, szses);
}
if (!key->user)
achk->key.user = 0;
else {
achk->key.user = ptr;
- memcpy(ptr, key->user, szuse);
- ptr += szuse;
+ ptr = mempcpy(ptr, key->user, szuse);
}
if (!key->permission)
achk->key.permission = 0;
else {
achk->key.permission = ptr;
- memcpy(ptr, key->permission, szper);
+ ptr = mempcpy(ptr, key->permission, szper);
}
- achk->next_agent = agents;
- achk->next = asynchecks;
- asynchecks = achk;
- async_on_result(achk, 0);
+ achk->value = value;
+ achk->decount = agentdeep;
+
+ /* call the agent */
+ async_call_agent(agent, achk);
return 0;
}
int
+cyn_test_async(
+ on_result_cb_t *on_result_cb,
+ void *closure,
+ const data_key_t *key
+) {
+ return async_check_or_test(on_result_cb, closure, key, 0);
+}
+
+int
+cyn_check_async(
+ on_result_cb_t *on_result_cb,
+ void *closure,
+ const data_key_t *key
+) {
+ return async_check_or_test(on_result_cb, closure, key, 10);
+}
+
+int
cyn_agent_add(
+ const char *name,
agent_cb_t *agent_cb,
void *closure
) {
- return addcb(agent_cb, closure, &agents);
+ struct agent *agent, **pprev;
+ size_t length;
+ uint8_t len;
+
+ length = strlen(name);
+ if (length <= 0 || length > UINT8_MAX)
+ return -EINVAL;
+ len = (uint8_t)length++;
+
+ agent = search_agent(name, len, &pprev);
+ if (agent)
+ return -EEXIST;
+
+ agent = malloc(sizeof *agent + length);
+ if (!agent)
+ return -ENOMEM;
+
+ agent->next = 0;
+ agent->agent_cb = agent_cb;
+ agent->closure = closure;
+ agent->len = len;
+ memcpy(agent->name, name, length);
+ *pprev = agent;
+
+ return 0;
}
int
cyn_agent_remove(
- agent_cb_t *agent_cb,
- void *closure
+ const char *name
) {
- return delcb(agent_cb, closure, &agents, asynchecks);
-}
+ struct agent *agent, **pprev;
+ size_t length;
+ uint8_t len;
+ length = strlen(name);
+ if (length <= 0 || length > UINT8_MAX)
+ return -EINVAL;
+ len = (uint8_t)length;
+
+ agent = search_agent(name, len, &pprev);
+ if (!agent)
+ return -ENOENT;
+
+ *pprev = agent->next;
+ free(agent);
+ return 0;
+}
diff --git a/src/cyn.h b/src/cyn.h
index 8f8893a..2dbfa62 100644
--- a/src/cyn.h
+++ b/src/cyn.h
@@ -28,8 +28,10 @@ typedef void (list_cb_t)(
const data_value_t *value);
typedef int (agent_cb_t)(
+ const char *name,
void *agent_closure,
const data_key_t *key,
+ const char *value,
on_result_cb_t *on_result_cb,
void *on_result_closure);
@@ -90,13 +92,6 @@ cyn_drop(
);
extern
-int
-cyn_test(
- const data_key_t *key,
- data_value_t *value
-);
-
-extern
void
cyn_list(
void *closure,
@@ -104,6 +99,13 @@ cyn_list(
const data_key_t *key
);
+int
+cyn_test_async(
+ on_result_cb_t *on_result_cb,
+ void *closure,
+ const data_key_t *key
+);
+
extern
int
cyn_check_async(
@@ -115,6 +117,7 @@ cyn_check_async(
extern
int
cyn_agent_add(
+ const char *name,
agent_cb_t *agent_cb,
void *closure
);
@@ -122,7 +125,6 @@ cyn_agent_add(
extern
int
cyn_agent_remove(
- agent_cb_t *agent_cb,
- void *closure
+ const char *name
);
diff --git a/src/main-cynadm.c b/src/main-cynadm.c
index a8607b4..53e722a 100644
--- a/src/main-cynadm.c
+++ b/src/main-cynadm.c
@@ -370,6 +370,8 @@ int do_check(int ac, char **av, int (*f)(rcyn_t*,const rcyn_key_t*))
fprintf(stdout, "denied\n");
else if (rc == -ENOENT && f == rcyn_cache_check)
fprintf(stdout, "not in cache!\n");
+ else if (rc == -EEXIST)
+ fprintf(stderr, "denied but an entry exist\n");
else
fprintf(stderr, "error %s\n", strerror(-rc));
}
diff --git a/src/main-cynarad.c b/src/main-cynarad.c
index 9c51df7..75b0169 100644
--- a/src/main-cynarad.c
+++ b/src/main-cynarad.c
@@ -41,6 +41,7 @@
#include "rcyn-server.h"
#include "rcyn-protocol.h"
#include "dbinit.h"
+#include "agent-at.h"
#if !defined(DEFAULT_DB_DIR)
# define DEFAULT_DB_DIR "/var/lib/cynara"
@@ -234,6 +235,9 @@ int main(int ac, char **av)
group = group ?: DEFAULT_CYNARA_GROUP;
init = init ?: DEFAULT_INIT_FILE;
+ /* activate the agents */
+ agent_at_activate();
+
/* compute socket specs */
spec_socket_admin = spec_socket_check = spec_socket_agent = 0;
if (systemd) {
diff --git a/src/prot.c b/src/prot.c
index 711d084..06edb2f 100644
--- a/src/prot.c
+++ b/src/prot.c
@@ -99,7 +99,6 @@ buf_put_car(
return 0;
}
-
/**
* Put the 'string' into the 'buf' escaping it at need
* returns:
diff --git a/src/rcyn-server.c b/src/rcyn-server.c
index 14a9243..951e655 100644
--- a/src/rcyn-server.c
+++ b/src/rcyn-server.c
@@ -159,8 +159,8 @@ putx(
va_list l;
int rc;
- va_start(l, cli);
n = 0;
+ va_start(l, cli);
p = va_arg(l, const char *);
while (p) {
if (n == MAXARGS)
@@ -250,15 +250,16 @@ checkcb(
) {
client_t *cli = closure;
char text[30];
+ const char *etxt;
if (!value)
putx(cli, DEFAULT, "0", NULL);
else {
- exp2txt(value->expire, text, sizeof text);
+ etxt = exp2txt(value->expire, text, sizeof text);
if (strcmp(value->value, ALLOW) && strcmp(value->value, DENY))
- putx(cli, _done_, value, text, NULL);
+ putx(cli, _done_, value->value, etxt, NULL);
else
- putx(cli, value->value, text, NULL);
+ putx(cli, value->value, etxt, NULL);
}
flushw(cli);
}