diff options
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/afb-context.c | 15 | ||||
-rw-r--r-- | src/afb-context.h | 4 | ||||
-rw-r--r-- | src/afb-export.c | 3 | ||||
-rw-r--r-- | src/afb-session.c | 51 | ||||
-rw-r--r-- | src/afb-stub-ws.c | 3 | ||||
-rw-r--r-- | src/afb-token.c | 182 | ||||
-rw-r--r-- | src/afb-token.h | 28 | ||||
-rw-r--r-- | src/afb-ws-json1.c | 5 | ||||
-rw-r--r-- | src/main-afb-daemon.c | 3 |
10 files changed, 275 insertions, 20 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index abc81c8b..0d5e7122 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -104,6 +104,7 @@ SET(AFB_LIB_SOURCES afb-socket.c afb-stub-ws.c afb-supervision.c + afb-token.c afb-trace.c afb-websock.c afb-ws-client.c diff --git a/src/afb-context.c b/src/afb-context.c index fd394939..6057e69d 100644 --- a/src/afb-context.c +++ b/src/afb-context.c @@ -50,12 +50,19 @@ void afb_context_init(struct afb_context *context, struct afb_session *session, init_context(context, afb_session_addref(session), token); } +void afb_context_init_validated(struct afb_context *context, struct afb_session *session) +{ + afb_context_init(context, session, NULL); + context->validated = 1; +} + void afb_context_subinit(struct afb_context *context, struct afb_context *super) { context->session = super->session; context->flags = 0; context->super = super; context->api_key = NULL; + context->token = NULL; context->validated = super->validated; } @@ -75,6 +82,14 @@ int afb_context_connect(struct afb_context *context, const char *uuid, const cha return 0; } +int afb_context_connect_validated(struct afb_context *context, const char *uuid) +{ + int rc = afb_context_connect(context, uuid, NULL); + if (!rc) + context->validated = 1; + return rc; +} + void afb_context_disconnect(struct afb_context *context) { if (context->session && !context->super) { diff --git a/src/afb-context.h b/src/afb-context.h index bec1d35c..21af4b23 100644 --- a/src/afb-context.h +++ b/src/afb-context.h @@ -18,10 +18,12 @@ #pragma once struct afb_session; +struct afb_token; struct afb_context { struct afb_session *session; + struct afb_token *token; const void *api_key; struct afb_context *super; union { @@ -39,8 +41,10 @@ struct afb_context }; extern void afb_context_init(struct afb_context *context, struct afb_session *session, const char *token); +extern void afb_context_init_validated(struct afb_context *context, struct afb_session *session); extern void afb_context_subinit(struct afb_context *context, struct afb_context *super); extern int afb_context_connect(struct afb_context *context, const char *uuid, const char *token); +extern int afb_context_connect_validated(struct afb_context *context, const char *uuid); extern void afb_context_disconnect(struct afb_context *context); extern const char *afb_context_sent_token(struct afb_context *context); extern const char *afb_context_sent_uuid(struct afb_context *context); diff --git a/src/afb-export.c b/src/afb-export.c index 38ef4021..3cf1fc94 100644 --- a/src/afb-export.c +++ b/src/afb-export.c @@ -1952,7 +1952,6 @@ void afb_export_process_xreq(struct afb_export *export, struct afb_xreq *xreq) void afb_export_context_init(struct afb_export *export, struct afb_context *context) { - afb_context_init(context, export->session, NULL); - context->validated = 1; + afb_context_init_validated(context, export->session); } diff --git a/src/afb-session.c b/src/afb-session.c index 61aa3f61..fe5aa4af 100644 --- a/src/afb-session.c +++ b/src/afb-session.c @@ -28,6 +28,7 @@ #include "afb-session.h" #include "afb-hook.h" +#include "afb-token.h" #include "verbose.h" #include "pearson.h" #include "uuid.h" @@ -68,7 +69,7 @@ struct afb_session uint8_t autoclose: 1; /**< close the session when unreferenced */ uint8_t notinset: 1; /**< session removed from the set of sessions */ uuid_stringz_t uuid; /**< long term authentication of remote client */ - uuid_stringz_t token; /**< short term authentication of remote client */ + struct afb_token *token;/**< short term authentication of remote client */ }; /** @@ -79,14 +80,14 @@ static struct { int max; /**< maximum count of sessions */ int timeout; /**< common initial timeout */ struct afb_session *heads[HEADCOUNT]; /**< sessions */ - uuid_stringz_t initok; /**< common initial token */ + struct afb_token *initok;/**< common initial token */ pthread_mutex_t mutex; /**< declare a mutex to protect hash table */ } sessions = { .count = 0, .max = 10, .timeout = 3600, .heads = { 0 }, - .initok = { 0 }, + .initok = 0, .mutex = PTHREAD_MUTEX_INITIALIZER }; @@ -203,6 +204,7 @@ static void session_destroy (struct afb_session *session) afb_hook_session_destroy(session); #endif pthread_mutex_destroy(&session->mutex); + afb_token_unref(session->token); free(session->lang); free(session); } @@ -249,12 +251,13 @@ static struct afb_session *session_add(const char *uuid, int timeout, time_t now pthread_mutex_init(&session->mutex, NULL); session->refcount = 1; strcpy(session->uuid, uuid); - strcpy(session->token, sessions.initok); + session->token = afb_token_addref(sessions.initok); session->timeout = timeout; session_update_expiration(session, now); /* add */ if (sessionset_add(session, hashidx)) { + afb_token_unref(session->token); free(session); return NULL; } @@ -309,6 +312,9 @@ static time_t sessionset_cleanup (int force) */ int afb_session_init (int max_session_count, int timeout, const char *initok) { + int rc; + uuid_stringz_t uuid; + /* check parameters */ if (initok && strlen(initok) >= sizeof sessions.initok) { ERROR("initial token '%s' too long (max length %d)", @@ -322,10 +328,16 @@ int afb_session_init (int max_session_count, int timeout, const char *initok) sessionset_cleanup(1); sessions.max = max_session_count; sessions.timeout = timeout; - if (initok == NULL) - uuid_new_stringz(sessions.initok); - else - strcpy(sessions.initok, initok); + if (initok == NULL) { + uuid_new_stringz(uuid); + initok = uuid; + } + sessions.initok = 0; + if (*initok) { + rc = afb_token_get(&sessions.initok, initok); + if (rc < 0) + return rc; + } sessionset_unlock(); return 0; } @@ -367,7 +379,7 @@ void afb_session_purge() */ const char *afb_session_initial_token() { - return sessions.initok; + return sessions.initok ? afb_token_string(sessions.initok) : ""; } /* Searchs the session of 'uuid' */ @@ -528,7 +540,7 @@ int afb_session_check_token (struct afb_session *session, const char *token) session_lock(session); r = !session->closed && session->expiration >= NOW - && !(session->token[0] && strcmp (token, session->token)); + && !(session->token && strcmp(token, afb_session_token(session))); session_unlock(session); return r; } @@ -536,12 +548,23 @@ int afb_session_check_token (struct afb_session *session, const char *token) /* generate a new token and update client context */ void afb_session_new_token (struct afb_session *session) { + int rc; + uuid_stringz_t uuid; + struct afb_token *previous, *next; session_lock(session); - uuid_new_stringz(session->token); - session_update_expiration(session, NOW); + uuid_new_stringz(uuid); + rc = afb_token_get(&next, uuid); + if (rc < 0) + ERROR("can't renew token"); + else { + previous = session->token; + session->token = next; + session_update_expiration(session, NOW); #if WITH_AFB_HOOK - afb_hook_session_renew(session); + afb_hook_session_renew(session); #endif + afb_token_unref(previous); + } session_unlock(session); } @@ -554,7 +577,7 @@ const char *afb_session_uuid (struct afb_session *session) /* Returns the token of 'session' */ const char *afb_session_token (struct afb_session *session) { - return session->token; + return afb_token_string(session->token); } /** diff --git a/src/afb-stub-ws.c b/src/afb-stub-ws.c index 6a52f6fc..3e9ede2f 100644 --- a/src/afb-stub-ws.c +++ b/src/afb-stub-ws.c @@ -523,9 +523,8 @@ static void server_on_call_cb(void *closure, struct afb_proto_ws_call *call, con wreq->call = call; /* init the context */ - if (afb_context_connect(&wreq->xreq.context, sessionid, NULL) < 0) + if (afb_context_connect_validated(&wreq->xreq.context, sessionid) < 0) goto unconnected; - wreq->xreq.context.validated = 1; server_record_session(stubws, wreq->xreq.context.session); if (wreq->xreq.context.created) afb_session_set_autoclose(wreq->xreq.context.session, 1); diff --git a/src/afb-token.c b/src/afb-token.c new file mode 100644 index 00000000..b81a87df --- /dev/null +++ b/src/afb-token.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2015-2019 "IoT.bzh" + * Author "Fulup Ar Foll" + * Author: José Bollo <jose.bollo@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <errno.h> + +#include "afb-token.h" + +/** + * structure for recording a token + */ +struct afb_token +{ + /** link to the next token of the list */ + struct afb_token *next; + + /** reference of the token */ + uint16_t refcount; + + /** local numeric id of the token */ + uint16_t id; + + /** string value of the token */ + char text[]; +}; + +struct tokenset +{ + struct afb_token *first; + pthread_mutex_t mutex; + uint16_t idgen; +}; + +static struct tokenset tokenset = { + .first = 0, + .mutex = PTHREAD_MUTEX_INITIALIZER, + .idgen = 0 +}; + +static struct afb_token *searchid(uint16_t id) +{ + struct afb_token *r = tokenset.first; + while (r && r->id != id) + r = r->next; + return r; +} + +/** + * Get a token for the given value + * + * @param token address to return the pointer to the gotten token + * @param tokenstring string value of the token to get + * @return 0 in case of success or a -errno like negative code + */ +int afb_token_get(struct afb_token **token, const char *tokenstring) +{ + int rc; + struct afb_token *tok; + size_t length; + + /* get length of the token string */ + length = + strlen(tokenstring); + + /* concurrency */ + pthread_mutex_lock(&tokenset.mutex); + + /* search the token */ + tok = tokenset.first; + while (tok && memcmp(tokenstring, tok->text, length)) + tok = tok->next; + + /* search done */ + if (tok) { + /* found */ + tok = afb_token_addref(tok); + rc = 0; + } else { + /* not found, create */ + tok = malloc(length + sizeof *tok); + if (!tok) + /* creation failed */ + rc = -ENOMEM; + else { + while(!++tokenset.idgen || searchid(tokenset.idgen)); + tok->next = tokenset.first; + tokenset.first = tok; + tok->id = tokenset.idgen; + tok->refcount = 1; + memcpy(tok->text, tokenstring, length); + rc = 0; + } + } + pthread_mutex_unlock(&tokenset.mutex); + *token = tok; + return rc; +} + +/** + * Add a reference count to the given token + * + * @param token the token to reference + * @return the token with the reference added + */ +struct afb_token *afb_token_addref(struct afb_token *token) +{ + if (token) + __atomic_add_fetch(&token->refcount, 1, __ATOMIC_RELAXED); + return token; +} + +/** + * Remove a reference to the given token and clean the memory if needed + * + * @param token the token that is unreferenced + */ +void afb_token_unref(struct afb_token *token) +{ + struct afb_token **pt; + if (token && !__atomic_sub_fetch(&token->refcount, 1, __ATOMIC_RELAXED)) { + pthread_mutex_lock(&tokenset.mutex); + pt = &tokenset.first; + while (*pt && *pt != token) + if (*pt) + *pt = token->next; + pthread_mutex_unlock(&tokenset.mutex); + free(token); + } +} + +/** + * Check whether the token is valid or not + * + * @param token the token to check + * @return a boolean value: 0 if not valid, 1 if valid + */ +int afb_token_check(struct afb_token *token) +{ + /* TODO */ + return 1; +} + +/** + * Get the string value of the token + * + * @param token the token whose string value is queried + * @return the string value of the token + */ +const char *afb_token_string(const struct afb_token *token) +{ + return token->text; +} + +/** + * Get the "local" numeric id of the token + * + * @param token the token whose id is queried + * @return the numeric id of the token + */ +uint16_t afb_token_id(const struct afb_token *token) +{ + return token->id; +} diff --git a/src/afb-token.h b/src/afb-token.h new file mode 100644 index 00000000..69b0fa05 --- /dev/null +++ b/src/afb-token.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016-2019 "IoT.bzh" + * Author: José Bollo <jose.bollo@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +struct afb_token; + +extern int afb_token_get(struct afb_token **token, const char *tokenstring); +extern struct afb_token *afb_token_addref(struct afb_token *token); +extern void afb_token_unref(struct afb_token *token); + +extern int afb_token_check(struct afb_token *token); +extern const char *afb_token_string(const struct afb_token *token); +extern uint16_t afb_token_id(const struct afb_token *token); diff --git a/src/afb-ws-json1.c b/src/afb-ws-json1.c index c7991934..d0cca2bd 100644 --- a/src/afb-ws-json1.c +++ b/src/afb-ws-json1.c @@ -34,6 +34,7 @@ #include "afb-xreq.h" #include "afb-context.h" #include "afb-evt.h" +#include "afb-token.h" #include "systemd.h" #include "verbose.h" @@ -62,6 +63,7 @@ struct afb_ws_json1 void (*cleanup)(void*); void *cleanup_closure; struct afb_session *session; + struct afb_token *token; struct afb_evt_listener *listener; struct afb_wsj1 *wsj1; struct afb_cred *cred; @@ -121,6 +123,7 @@ struct afb_ws_json1 *afb_ws_json1_create(struct fdev *fdev, struct afb_apiset *a result->cleanup = cleanup; result->cleanup_closure = cleanup_closure; result->session = afb_session_addref(context->session); + result->token = afb_token_addref(context->token); result->new_session = context->created != 0; if (result->session == NULL) goto error2; @@ -141,6 +144,7 @@ error4: afb_wsj1_unref(result->wsj1); error3: afb_session_unref(result->session); + afb_token_unref(result->token); error2: free(result); error: @@ -161,6 +165,7 @@ void afb_ws_json1_unref(struct afb_ws_json1 *ws) afb_wsj1_unref(ws->wsj1); if (ws->cleanup != NULL) ws->cleanup(ws->cleanup_closure); + afb_token_unref(ws->token); afb_session_unref(ws->session); afb_cred_unref(ws->cred); afb_apiset_unref(ws->apiset); diff --git a/src/main-afb-daemon.c b/src/main-afb-daemon.c index 3421be81..165d1400 100644 --- a/src/main-afb-daemon.c +++ b/src/main-afb-daemon.c @@ -659,8 +659,7 @@ static void startup_call_current(struct startup_req *sreq) json = strchr(verb, ':'); if (json) { afb_xreq_init(&sreq->xreq, &startup_xreq_itf); - afb_context_init(&sreq->xreq.context, sreq->session, NULL); - sreq->xreq.context.validated = 1; + afb_context_init_validated(&sreq->xreq.context, sreq->session); sreq->api = strndup(api, verb - api); sreq->verb = strndup(verb + 1, json - verb - 1); sreq->xreq.request.called_api = sreq->api; |