diff options
author | Jose Bollo <jose.bollo@iot.bzh> | 2019-05-15 09:08:08 +0200 |
---|---|---|
committer | Jose Bollo <jose.bollo@iot.bzh> | 2019-05-22 15:57:34 +0200 |
commit | 2b70c9ff631b0a2f0c7ec771e2f390a1d677af1e (patch) | |
tree | ab64408bd8a443536e380aef03fed37df1a3dbf6 | |
parent | c451f53b4f3acd2157f9c7e7365ecc5663e9ada7 (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.txt | 1 | ||||
-rw-r--r-- | src/agent-at.c | 126 | ||||
-rw-r--r-- | src/agent-at.h | 23 | ||||
-rw-r--r-- | src/anydb.c | 3 | ||||
-rw-r--r-- | src/cyn.c | 267 | ||||
-rw-r--r-- | src/cyn.h | 20 | ||||
-rw-r--r-- | src/main-cynadm.c | 2 | ||||
-rw-r--r-- | src/main-cynarad.c | 4 | ||||
-rw-r--r-- | src/prot.c | 1 | ||||
-rw-r--r-- | src/rcyn-server.c | 9 |
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; } @@ -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; +} @@ -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) { @@ -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); } |