diff options
58 files changed, 9509 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac33634 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +.*.sw* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8a3c9f1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,82 @@ +########################################################################### +# 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. +########################################################################### + +CMAKE_MINIMUM_REQUIRED(VERSION 3.0) + +PROJECT(cynara C) + +SET(PROJECT_NAME "Cynara") +SET(PROJECT_PRETTY_NAME "Permission database") +SET(PROJECT_DESCRIPTION "Secured permission database for applications") +SET(PROJECT_VERSION "1.99.RC1") +set(PROJECT_URL "https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/cynara.git;a=summary") + +INCLUDE(FindPkgConfig) +INCLUDE(CheckIncludeFiles) +INCLUDE(CheckLibraryExists) +INCLUDE(GNUInstallDirs) +INCLUDE(CTest) + +########################################################################### +# possible settings + +set(CYNARA_VERSION ${PROJECT_VERSION}) +set(CYNARA_SOVERSION 1.99) + +add_definitions(-DCYNARA_VERSION="${CYNARA_VERSION}") + +set(SYSTEMD ON CACHE BOOL "should use systemd") + +########################################################################### + +include_directories(include) +link_libraries(-Wl,--as-needed -Wl,--gc-sections -Wl,--no-undefined) + +add_compile_options(-Wall -Wextra -Wconversion) +add_compile_options(-Wno-unused-parameter) # frankly not using a parameter does it care? +add_compile_options(-Wno-sign-compare -Wno-sign-conversion) +add_compile_options(-Werror=maybe-uninitialized) +add_compile_options(-Werror=implicit-function-declaration) +add_compile_options(-ffunction-sections -fdata-sections) +add_compile_options(-fPIC) +add_compile_options(-g) + +set(CMAKE_C_FLAGS_PROFILING "-g -O2 -pg -Wp,-U_FORTIFY_SOURCE") +set(CMAKE_C_FLAGS_DEBUG "-g -ggdb -Wp,-U_FORTIFY_SOURCE") +set(CMAKE_C_FLAGS_RELEASE "-g -O2") +set(CMAKE_C_FLAGS_CCOV "-g -O2 --coverage") + +########################################################################### + +if(SYSTEMD) + PKG_CHECK_MODULES(libsystemd REQUIRED libsystemd>=222) +endif() + +if(SYSTEMD) + set(SOCKET_DIR "/run/platform" + CACHE PATH "path of the socket system directories") + set(SYSTEMD_UNIT_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/systemd/system" + CACHE PATH "Path to systemd system unit files") + set(CHECK_SOCKET_SPEC "unix:${SOCKET_DIR}/cynara.check") + set(ADMIN_SOCKET_SPEC "unix:${SOCKET_DIR}/cynara.admin") + add_subdirectory(systemd) +endif() +add_subdirectory(include) +add_subdirectory(src) +add_subdirectory(pkgconfig) + @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 0000000..6553f89 --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,19 @@ +########################################################################### +# 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. +########################################################################### + +INSTALL(DIRECTORY cynara DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}) diff --git a/include/cynara/cynara-admin-types.h b/include/cynara/cynara-admin-types.h new file mode 100644 index 0000000..95d27b9 --- /dev/null +++ b/include/cynara/cynara-admin-types.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * \file src/include/cynara-admin-types.h + * \author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com> + * \author Aleksander Zdyb <a.zdyb@samsung.com> + * \author Zofia Abramowska <z.abramowska@samsung.com> + * \author Oskar Switalski <o.switalski@samsung.com> + * \version 1.0 + * \brief This file contains structs and consts for cynara admin. + */ + +#ifndef CYNARA_ADMIN_TYPES_H +#define CYNARA_ADMIN_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \struct cynara_admin_policy + * \brief Defines single policy + */ +struct cynara_admin_policy { + char *bucket; /**< Name of bucket, in which policy is placed */ + + char *client; /**< Identifier of client (application) */ + char *user; /**< Identifier of user */ + char *privilege; /**< Privilege name */ + + int result; /**< Result of policy */ + char *result_extra; /**< Not always used, may contain some additional result data + like e.g. name of bucket in case result == CYNARA_ADMIN_BUCKET */ +}; + +/** + * \struct cynara_admin_policy_descr + * \brief Describes policy of type given with result + */ + +struct cynara_admin_policy_descr { + int result; /**< Result of policy to describe */ + char *name; /**< Descriptive name of given policy result */ +}; + +/** + * \name Wildcard + * Can replace client, user or privilege name. + * WILDCARD matches any string during check procedure from libcynara-client. + */ +#define CYNARA_ADMIN_WILDCARD "*" + +/** + * \name Name of Default Bucket + * Default bucket - the one that check starts in. + * Default bucket cannot be removed, although its default policy + * (which originally is set to DENY) can be changed. + */ +#define CYNARA_ADMIN_DEFAULT_BUCKET "" + +/** + * \name Any + * Can replace client, user or privilege name. + * ANY matches any string (including WILDCARD) during: + * + * * policy removal with cynara_admin_erase() function + * * listing policies from a single bucket. + * + * Using ANY as default policy for bucket or as policy type of inserted policy record + * is forbidden and will cause CYNARA_API_INVALID_PARAM error. + */ +#define CYNARA_ADMIN_ANY "#" + +#ifdef __cplusplus +} +#endif + +#endif /* CYNARA_ADMIN_TYPES_H */ diff --git a/include/cynara/cynara-admin.h b/include/cynara/cynara-admin.h new file mode 100644 index 0000000..a0ab0c9 --- /dev/null +++ b/include/cynara/cynara-admin.h @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * \file src/include/cynara-admin.h + * \author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com> + * \author Zofia Abramowska <z.abramowska@samsung.com> + * \author Oskar Switalski <o.switalski@samsung.com> + * \version 1.0 + * \brief This file contains administration APIs of cynara available with libcynara-admin. + * \example cynara-admin.example + */ + +#ifndef CYNARA_ADMIN_H +#define CYNARA_ADMIN_H + +#include <cynara/cynara-admin-types.h> +#include <cynara/cynara-error.h> +#include <cynara/cynara-limits.h> +#include <cynara/cynara-policy-types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \struct cynara_admin + * Forward declaration of structure allowing initialization of library + * and usage of all libcynara-admin API functions + */ +struct cynara_admin; + +/** + * \brief Initialize cynara-admin structure. + * + * \par Description: + * Initialize cynara-admin library. + * Creates structure used in following API calls. + * + * \par Purpose: + * This function must be invoked prior to other admin API calls. It creates structure needed by + * other cynara-admin library API functions. + * + * \par Typical use case: + * Once before a service can call other cynara-admin library functions. + * + * \par Method of function operation: + * This API initializes inner library structures and in case of success creates cynara_admin + * structure and stores pointer to this structure at memory address passed in pp_cynara_admin + * parameter. + * + * \par Sync (or) async: + * This is a synchronous API. + * + * \par Important notes: + * Structure cynara_admin created by cynara_admin_initialize() call should be released with + * cynara_admin_finish(). + * + * \param[out] pp_cynara_admin address of pointer for created cynara_admin structure. + * + * \return CYNARA_API_SUCCESS on success, or error code otherwise. + */ +int cynara_admin_initialize(struct cynara_admin **pp_cynara_admin); + +/** + * \brief Release cynara-admin structure. + * + * \par Description: + * Destroys structure created with cynara_admin_initialize() function. + * + * \par Purpose: + * This API should be used to clean up after usage of cynara-admin library. + * + * \par Typical use case: + * Function should be called once, when done with cynara-admin library API usage. + * + * \par Method of function operation: + * This API releases inner library structures and destroys cynara_admin structure. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Important notes: + * No invocations of cynara-admin library API functions are allowed after call to + * cynara_admin_finish(). + * + * \param[in] p_cynara_admin cynara_admin structure created in cynara_admin_initialize(). + * + * \return CYNARA_API_SUCCESS on success, or error code otherwise. + */ +int cynara_admin_finish(struct cynara_admin *p_cynara_admin); + +/** + * \brief Insert, update or delete policies in cynara database. + * + * \par Description: + * Manages policies in cynara. + * + * \par Purpose: + * This API should be used to insert, update or delete policies in cynara. + * + * \par Typical use case: + * Enables privileged services to alter policies by adding, updating or removing records. + * + * \par Method of function operation: + * \parblock + * Policies are arranged into buckets. Every policy is defined in context of some bucket identified + * with bucket field (string). A bucket consists of policies identified with tripple: (client, user, + * privilege), which is a (unique) key within considered bucket. + * + * Every policy can be one of two types: simple or bucket-pointing policy. + * + * * Simple policies have result field with value of CYNARA_ADMIN_DENY or CYNARA_ADMIN_ALLOW. + * result_extra field should be NULL in this case. + * * Bucket-pointing policies have result field with value of CYNARA_ADMIN_BUCKET and name of + * bucket they point to in result_extra field. + * + * + * Type of operation, which is run for every record (single policy) is defined by result field in + * cynara_admin_policy structure. + * + * * In case of CYNARA_ADMIN_DENY or CYNARA_ADMIN_ALLOW a simple policy is updated or inserted into + * cynara database. + * * In case of CYNARA_ADMIN_BUCKET, a bucket-pointing policy is updated or inserted into cynara + * database. + * * In case of CYNARA_ADMIN_DELETE, a policy is removed from cynara database. + * + * One call of cynara_admin_set_policies() can manage many different policies in different buckets. + * However, considered buckets must exist before referring to them in policies. + * \endparblock + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Important notes: + * \parblock + * When plugin API will be specified, there will be more valid types to pass as result. + * Numerical values of defines CYNARA_ADMIN_... may change, so usage of defines names is strongly + * recommended. + * + * Policies size cannot exceed CYNARA_MAX_VECTOR_SIZE excluding last null element and string members + * length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be returned. + * \endparblock + * + * \param[in] p_cynara_admin cynara admin structure. + * \param[in] policies NULL terminated array of pointers to policy structures. + * + * \return CYNARA_API_SUCCESS on success, or error code otherwise. + */ +int cynara_admin_set_policies(struct cynara_admin *p_cynara_admin, + const struct cynara_admin_policy *const *policies); + +/** + * \brief Add, remove or update buckets in cynara database. + * + * \par Description: + * Adds new, updates or removes existing bucket for policies in cynara. + * + * \par Purpose: + * This API should be used to add, remove or update buckets. + * + * \par Typical use case: + * Enables privileged services to alter policies database by adding, updating or removing buckets. + * + * \par Method of function operation: + * \parblock + * Every bucket has a default policy. During search, if no policy matches the searched key (client, + * user, privilege), default policy is returned. + + * Operation run on a single bucket defined with bucket parameter. + + * Operation parameter defines what should happen with bucket. In case of: + * * CYNARA_ADMIN_DENY, a bucket is inserted or updated with CYNARA_ADMIN_DENY default policy; + * * CYNARA_ADMIN_ALLOW, a bucket is inserted or updated with CYNARA_ADMIN_ALLOW default policy; + * * CYNARA_ADMIN_DELETE, a bucket is removed with all policies that were kept in it. + * \endparblock + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Important notes: + * \parblock + * When plugin API will be specified, there will be more valid types to pass as operation / default + * policy. Numerical values of defines CYNARA_ADMIN_... may change, so usages of provided consts is + * strongly recommended. + * + * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be + * returned. + * + * Default bucket identified with CYNARA_ADMIN_DEFAULT_BUCKET exists always. Its default policy + * is preset to DENY (can be altered, however). Default bucket cannot be removed. + * + * Extra parameter will be used to pass additional data to cynara extensions to build more complex + * policies, such as ALLOW but for 5 minutes only, or ALLOW if user confirms. + * \endparblock + * + * \param[in] p_cynara_admin cynara admin structure. + * \param[in] bucket bucket name + * \param[in] operation type of operation (default policy or CYNARA_ADMIN_DELETE) + * \param[in] extra additional data for default policy (will be available with cynara extensions) + * + * \return CYNARA_API_SUCCESS on success, or error code otherwise. + */ +int cynara_admin_set_bucket(struct cynara_admin *p_cynara_admin, const char *bucket, int operation, + const char *extra); + +/** + * \brief Raw check client and user access for given privilege without using plugins extensions. + * + * \par Description: + * Raw check client and user access for given privilege without using plugins extensions. + * + * \par Purpose: + * This API should be used to check type of matching policy for check request + * + * \par Typical use case: + * Administrator of cynara want to know, what would cynara return to client, if asked about given + * access. + * + * \par Method of function operation: + * \parblock + * Function works almost the same way as cynara_check() client function. + * The differences are: + * * user must specify bucket, from which search would be started (in case of cynara_check() + * it is always the default bucket) + * * user can specify if search should be recursive: disabling recursive check will constrain search + * to single bucket only, ignoring all policies leading to other buckets (in case of + * cynara_check() search is always recursive) + * * when matching policy in cynara is found, its result is returned without being interpreted by + * plugin extensions. + * \endparblock + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Important notes: + * \parblock + * (*result_extra) may be set to NULL, if extra data are not used in matched policy + * If (*result_extra) is not NULL, it contains a string allocated by cynara admin library + * with malloc(3) function and must be released with free(3) function. + * + * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be + * returned. + * \endparblock + * + * \param[in] p_cynara_admin cynara admin structure. + * \param[in] start_bucket name of bucket where search would start. + * \param[in] recursive FALSE (== 0) : single bucket search; + * TRUE (!= 0) : search does not ignore policies leading to another buckets. + * \param[in] client application or process identifier. + * \param[in] user user running client. + * \param[in] privilege privilege that is a subject of a check. + * \param[out] result placeholder for matched policy type. + * \param[out] result_extra placeholder for matched policy additional data (see Important Notes!). + * + * \return CYNARA_API_SUCCESS on success, or error code otherwise. + */ +int cynara_admin_check(struct cynara_admin *p_cynara_admin, + const char *start_bucket, const int recursive, + const char *client, const char *user, const char *privilege, + int *result, char **result_extra); + +/** + * \brief Lists policies from single bucket in cynara database. + * + * \par Description: + * Lists filtered cynara policies from single bucket. + * + * \par Purpose: + * This API should be used to list policies from single bucket. + * + * \par Typical use case: + * List all policies matching defined filter. + * + * \par Method of function operation: + * \parblock + * Policies are arranged into buckets. Every bucket contains set of policies. Each of policies are + * identified with triple {client, user, privilege}. Function lists all policies from single bucket + * with matching client, user and privilege names. + * + * CYNARA_ADMIN_ANY can be used to match any client, user or privilege, e.g. + * + * List with parameters: {client = CYNARA_ADMIN_ANY, user = "alice", privilege = CYNARA_ADMIN_ANY} + * will match all policies related to "alice", so will match {"app1", "alice", "gps"} and + * {CYNARA_ADMIN_WILDCARD, "alice", "sms"}, but won't match {"app3", CYNARA_ADMIN_WILDCARD, "call"}. + * + * List with parameters: {client = "calculator", user = CYNARA_ADMIN_WILDCARD, + * privilege = CYNARA_ADMIN_ANY} will match {"calculator", CYNARA_ADMIN_WILDCARD, "sms"} but won't + * match {CYNARA_ADMIN_WILDCARD, CYNARA_ADMIN_WILDCARD, "sms"} nor {"calculator", "bob", "sms"} + * + * Matching policies are returned as NULL terminated array of pointers to cynara_admin_policy + * structures. + * + * If any of: bucket, client, user, privilege, policies is NULL then CYNARA_API_INVALID_PARAM + * is returned. + * + * If there is no bucket with given name CYNARA_API_BUCKET_NOT_FOUND is returned. + * + * In case of successful call CYNARA_API_SUCCESS is returned and *policies points to newly created + * array of pointers to struct cynara_admin_policy. It is responsibility of caller to release: + * + * * all non-NULL const char* pointers in all cynara_admin_policy structures; + * * all pointers to cynara_admin_policy structures kept in *policies array; + * * *policies array itself. + * + * All allocation made by cynara admin library are done with malloc(3) function and must be released + * with free(3) function. + * \endparblock + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Important notes: + * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be + * returned. + * + * \param[in] p_cynara_admin cynara admin structure. + * \param[in] bucket name. + * \param[in] client filter for client name. + * \param[in] user filter for user name. + * \param[in] privilege filter for privilege. + * \param[out] policies placeholder for NULL terminated array of pointers to policy structures. + * + * \return CYNARA_API_SUCCESS on success, or error code otherwise. + */ +int cynara_admin_list_policies(struct cynara_admin *p_cynara_admin, const char *bucket, + const char *client, const char *user, const char *privilege, + struct cynara_admin_policy ***policies); + +/** + * \brief Erase policies matching filter from cynara database. + * + * \par Description: + * Erase policies matching filter from cynara database. + * + * \par Purpose: + * This API should be used to erase multiple policies with some common key part, + * e.g. all policies related to given user. + * + * \par Typical use case: + * Erase all policies matching defined filter. + * + * \par Method of function operation: + * \parblock + * Policies are arranged into buckets. Every bucket contains set of policies. Each of policies are + * identified with triple {client, user, privilege}. Function erases all policies with matching + * client, user and privilege names. + * + * There are two modes: + * * non-recursive (recursive parameter set to 0) - when policies are erased only from single bucket + * * recursive (recursive parameter set to 1) when policies are removed from given start_bucket and + * all nested buckets. + * + * CYNARA_ADMIN_ANY can be used to match any client, user or privilege, e.g. + * + * Erase with parameters: {client = CYNARA_ADMIN_ANY, user = "alice", privilege = CYNARA_ADMIN_ANY} + * will match all policies related to "alice", so will match {"app1", "alice", "gps"} and + * {CYNARA_ADMIN_WILDCARD, "alice", "sms"}, but won't match {"app3", CYNARA_ADMIN_WILDCARD, "call"}. + * + * Erase with parameters: {client = "calculator", user = CYNARA_ADMIN_WILDCARD, + * privilege = CYNARA_ADMIN_ANY} will match {"calculator", CYNARA_ADMIN_WILDCARD, "sms"} but won't + * match {CYNARA_ADMIN_WILDCARD, CYNARA_ADMIN_WILDCARD, "sms"} nor {"calculator", "bob", "sms"} + * + * If any of: start_bucket, client, user, privilege, policies is NULL then CYNARA_API_INVALID_PARAM + * is returned. + * + * If there is no bucket with given name CYNARA_API_BUCKET_NOT_FOUND is returned. + * + * In case of successful call CYNARA_API_SUCCESS is returned. + * \endparblock + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Important notes: + * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be + * returned. + * + * \param[in] p_cynara_admin cynara admin structure. + * \param[in] start_bucket name of bucket where erase would start. + * \param[in] recursive FALSE (== 0) : erase is not recursive (single bucket erase); \n + * TRUE (!= 0) : erase follows all policies leading to nested buckets + * \param[in] client filter for client name. + * \param[in] user filter for user name. + * \param[in] privilege filter for privilege. + * + * \return CYNARA_API_SUCCESS on success, or error code otherwise. + */ +int cynara_admin_erase(struct cynara_admin *p_cynara_admin, + const char *start_bucket, int recursive, + const char *client, const char *user, const char *privilege); + +/** + * \brief Lists available policies with their name description. + * + * \par Description: + * + * Lists available cynara policy results with name description. + * + * \par Purpose: + * This API should be used to list all available policy results + * (also from cynara extension plugins). + * + * \par Typical use case: + * Gathering information about possible policy results and presenting them to user (using name + * attribute of description). Result can be passed to cynara_admin_set_policies(). + * + * \par Method of function operation: + * \parblock + * Policies are based on policy result number. Policies can be built in (like primitives: ALLOW, + * DENY...) or can be loaded from cynara plugin extensions. This API gives possibility of checking, + * which of these result exist in current cynara server and can be presented to user in a readable + * way (of course additional translation may be needed). + * + * Descriptions of existing policy results are returned as NULL terminated array of pointers of + * cynara_admin_policy_descr structures. + * + * Example output could be {{0, "Deny"}, {11, "AskUser"}, {65535, "Allow"}, NULL} + * + * In case of successful call CYNARA_API_SUCCESS is returned and *descriptions points + * to newly created array of pointers to struct cynara_admin_policy_descr. It is responsibility + * of caller to release: + * + * * all non-NULL char* pointers in all cynara_admin_policy_descr structures; + * * all pointers to cynara_admin_policy_descr structures kept in *descriptions array; + * * *descriptions array itself. + * + * All allocation made by cynara admin library are done with malloc(3) function and must be released + * with free(3) function. + * \endparblock + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \param[in] p_cynara_admin cynara admin structure. + * \param[out] descriptions placeholder for NULL terminated array of pointers of + * description structures. + * + * \return CYNARA_API_SUCCESS on success, or error code otherwise. + */ +int cynara_admin_list_policies_descriptions(struct cynara_admin *p_cynara_admin, + struct cynara_admin_policy_descr ***descriptions); + +#ifdef __cplusplus +} +#endif + +#endif /* CYNARA_ADMIN_H */ diff --git a/include/cynara/cynara-agent.h b/include/cynara/cynara-agent.h new file mode 100644 index 0000000..3e6d4f0 --- /dev/null +++ b/include/cynara/cynara-agent.h @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/include/cynara-agent.h + * @author Adam Malinowski <a.malinowsk2@partner.samsung.com> + * @author Oskar Switalski <o.switalski@samsung.com> + * @version 1.0 + * @brief This file contains agent APIs available with libcynara-agent. + */ + +#ifndef CYNARA_AGENT_H +#define CYNARA_AGENT_H + +#include <stdint.h> + +#include <cynara/cynara-error.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint16_t cynara_agent_req_id; +typedef struct cynara_agent cynara_agent; + +/** + * \enum cynara_agent_msg_type + * Values specifying type of message. + * + * \var cynara_agent_msg_type::CYNARA_MSG_TYPE_ACTION + * Message of this type indicates its content is a request for performing an action or response to + * such request. + * + * \var cynara_agent_msg_type::CYNARA_MSG_TYPE_CANCEL + * Message of this type indicates its content is a request for canceling action or response to such + * request. + */ +typedef enum { + CYNARA_MSG_TYPE_ACTION, + CYNARA_MSG_TYPE_CANCEL +} cynara_agent_msg_type; + +/** + * \par Description: + * Initialize cynara-agent structure. + * Create structure used in following API calls. + * + * \par Purpose: + * This API must be used prior to calling other agent API functions. + * + * \par Typical use case: + * Once before other agent API functions are called. + * + * \par Method of function operation: + * This API initializes inner library structures and in case of success returns cynara_agent + * structure. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * Structure cynara_agent created by this function should be released with cynara_agent_finish(). + * + * \param[out] pp_cynara_agent Place holder for created cynara_agent structure. + * \param[in] p_agent_type Type (name) of agent used by cynara for communication agent<->plugin. + * + * \return CYNARA_API_SUCCESS on success, or error code on error. + */ +int cynara_agent_initialize(cynara_agent **pp_cynara_agent, const char *p_agent_type); + +/** + * \par Description: + * Destroy structure created with cynara_agent_initialize(). + * + * \par Purpose: + * This API should be used to clean up after usage of cynara-agent library. + * + * \par Typical use case: + * Once after connection to cynara is not needed. + * + * \par Method of function operation: + * This API releases inner library structure and destroys cynara_agent structure. Connection to + * cynara service is closed. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * No other call to cynara-agent library should be made after call to cynara_agent_finish() except + * cynara_agent_initialize(). + * + * \param[in] p_cynara_agent cynara_agent structure. If NULL, then the call has no effect. + */ +int cynara_agent_finish(cynara_agent *p_cynara_agent); + +/** + * \par Description: + * Get request from cynara service. + * + * \par Purpose: + * This API should be used to get request from cynara service. Request is generated by corresponding + * plugin loaded into cynara service. + * + * \par Typical use case: + * Agent waits for request from cynara service. Request may be either ask for performing agent + * specific action or ask for canceling such action. Agent calls this function when is ready to + * perform or cancel an action. + * + * \par Method of function operation: + * \parblock + * Function reads data incoming from cynara service and if at least one complete request is ready + * then returns with CYNARA_API_SUCCESS code. Request type, request id and specific + * plugin data are stored in given arguments. Function returns exactly one request. If there are + * more then one requests ready to get then one must call this function multiple times. + * + * This function is blocking which means that if there is no request from cynara service it will not + * return. On success, buffer for plugin specific data is allocated and size is set. Developer is + * responsible for freeing this memory using free() function. + * \endparblock + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * \parblock + * Call to cynara_agent_get_request() needs cynara_agent structure to be created first. + * Use cynara_agent_initialize() before calling this function. + * + * After CYNARA_API_ACCESS_DENIED error is returned agent should be terminated or at least should + * not invoke neither cynara_agent_get_request() nor cynara_agent_put_response() functions. + * \endparblock + * + * \param[in] p_cynara_agent cynara_agent structure. + * \param[out] req_type Request type, demand an action or cancel this action. + * \param[out] req_id Request identifier used to pair with answer #cynara_agent_put_response() and + * cancel request. + * \param[out] data Plugin specific data. Buffer is allocated in this function and developer is + * responsible for freeing it using free() function. Buffer is filled with data + * from corresponding plugin. If there is no enough memory for data + * CYNARA_API_OUT_OF_MEMORY is returned and all arguments remain untouched. + * \param[out] data_size Size of plugin data (bytes count). In case of out of memory this value + * stays untouched. + * + * \return CYNARA_API_SUCCESS on successfully read request, + * CYNARA_API_INTERRUPTED when cynara_agent_cancel_waiting() is called during waiting, + * or negative error code otherwise. + */ +int cynara_agent_get_request(cynara_agent *p_cynara_agent, cynara_agent_msg_type *req_type, + cynara_agent_req_id *req_id, void **data, size_t *data_size); + +/** + * \par Description: + * Send response to cynara service. + * + * \par Purpose: + * This API should be used to send response to cynara service. + * + * \par Typical use case: + * Agent calls this function when is ready to answer request for action or cancel request. + * + * \par Method of function operation: + * Function sends data to cynara service. Data contains answer for previously got question. + * Answer may be of type CYNARA_MSG_TYPE_ACTION or CYNARA_MSG_TYPE_CANCEL. Type is + * CYNARA_MSG_TYPE_ACTION when request for an action was processed and answer is ready, or + * CYNARA_MSG_TYPE_CANCEL when processing request for an action was interrupted by cancel request. + * Agent must send exactly one response per one request and cancel. If request is processed before + * cancel message arrives the agent sends action response. If cancel arrives before action request + * is processed then agent sends cancel response and drops processing related action. + * Request id in response must be the same as request id in corresponding request. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * Call to cynara_agent_get_request() needs cynara_agent structure to be created first. + * Use cynara_agent_initialize() before calling this function. Also successful call to + * cynara_agent_get_request() is needed before calling this function. + * + * \param[in] p_cynara_agent cynara_agent structure. + * \param[in] resp_type Response type - see Method of operation for details. + * \param[in] req_id Request identifier obtained from request. + * \param[in] data Plugin specific data. If necessary agent should fill this buffer with data + * directed to plugin. + * \param[in] data_size Size of plugin data (bytes count). + * + * \return CYNARA_API_SUCCESS on successfully read request, or negative error code otherwise. + */ +int cynara_agent_put_response(cynara_agent *p_cynara_agent, const cynara_agent_msg_type resp_type, + const cynara_agent_req_id req_id, const void *data, + const size_t data_size); + +/** + * \par Description: + * Break from waiting for cynara service request using cynara_agent_get_request(). + * + * \par Purpose: + * This API should be used when cynara_agent_get_request() is blocked and before calling + * cynara_agent_finish(). + * + * \par Typical use case: + * Agent calls this API, when it wants to gracefully quit. + * + * \par Method of function operation: + * Function notifies cynara_agent_get_request() to stop waiting for request from cynara. + * Then cynara_agent_get_request() returns CYNARA_API_INTERRUPTED and no request is fetched. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function can be used only together with cynara_agent_get_request(), otherwise should + * be treaded as NOT thread safe. + * + * \par Important notes: + * Call to cynara_agent_cancel_waiting() needs cynara_agent structure to be created first and + * cynara_agent_get_request() running. + * Use cynara_agent_initialize() before calling this function. + * + * \param[in] p_cynara_agent cynara_agent structure. + * \return CYNARA_API_SUCCESS on successful waiting cancel, or negative error code otherwise. + */ +int cynara_agent_cancel_waiting(cynara_agent *p_cynara_agent); + +#ifdef __cplusplus +} +#endif + +#endif /* CYNARA_AGENT_H */ diff --git a/include/cynara/cynara-client-async.h b/include/cynara/cynara-client-async.h new file mode 100644 index 0000000..78a1313 --- /dev/null +++ b/include/cynara/cynara-client-async.h @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/include/cynara-client-async.h + * @author Zofia Abramowska <z.abramowska@samsung.com> + * @author Marcin Niesluchowski <m.niesluchow@samsung.com> + * @author Oskar Switalski <o.switalski@samsung.com> + * @version 1.0 + * @brief This file contains asynchronous client APIs of Cynara available + * with libcynara-client-asynchronous. + * @example cynara-client-async.example + */ + +#ifndef CYNARA_CLIENT_ASYNC_H +#define CYNARA_CLIENT_ASYNC_H + +#include <stddef.h> +#include <stdint.h> + +#include <cynara/cynara-error.h> +#include <cynara/cynara-limits.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint16_t cynara_check_id; +typedef struct cynara_async cynara_async; +typedef struct cynara_async_configuration cynara_async_configuration; + +/** + * \enum cynara_async_status + * Values indicating the status of connected cynara socket. + * + * \var cynara_async_status::CYNARA_STATUS_FOR_READ + * Wait for read events on socket. + * + * \var cynara_async_status::CYNARA_STATUS_FOR_RW + * Wait for both read and write events on socket. + */ +typedef enum { + CYNARA_STATUS_FOR_READ, + CYNARA_STATUS_FOR_RW +} cynara_async_status; + +/** + * \enum cynara_async_call_cause + * Values indicating the reason of cynara_response_callback call. + * + * \var cynara_async_call_cause::CYNARA_CALL_CAUSE_ANSWER + * Callback was called due to response to previous cynara_async_create_request() or + * cynara_async_create_simple_request() call. + * + * \var cynara_async_call_cause::CYNARA_CALL_CAUSE_CANCEL + * Callback was called due to request cancellation with cynara_async_cancel_request() call. + * + * \var cynara_async_call_cause::CYNARA_CALL_CAUSE_FINISH + * Callback was called due to cynara_async_finish() call. + * + * \var cynara_async_call_cause::CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE + * Callback was called due to service not available. + */ +typedef enum { + CYNARA_CALL_CAUSE_ANSWER, + CYNARA_CALL_CAUSE_CANCEL, + CYNARA_CALL_CAUSE_FINISH, + CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE +} cynara_async_call_cause; + +/** + * \brief Response_callback is registered once in cynara_async_create_request() or + * cynara_async_create_simple_request() and will be triggered exactly once in 4 kinds of situations: + * + * -# after response is received from cynara service (CYNARA_CALL_CAUSE_ANSWER) + * -# when request is canceled with cynara_async_cancel_request() (CYNARA_CALL_CAUSE_CANCEL) + * -# when request was pending for response, but cynara_async_finish() was called + * (CYNARA_CALL_CAUSE_FINISH) + * -# when connection to cynara service was broken and cannot be established again - probably cynara + * is unoperational (CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE) + * + * API functions called during this callback with CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE + * or CYNARA_CALL_CAUSE_FINISH cause will return CYNARA_API_OPERATION_NOT_ALLOWED. + * cynara_async_finish() will be ignored if called from within this callback. + * + * \param[in] check_id Number identifying check request. Number is generated in + * cynara_async_create_request() or cynara_async_create_simple_request() + * and returned to user. It can be used to match response with sent request. + * Number is valid since cynara_async_create_request() call or + * cynara_async_create_simple_request() call till callback call. + * After that the number can be reused by cynara to run new request. + * \param[in] cause Cause of triggering this callback. + * \param[in] response Response for created request. Should be ignored if cause is not + * an answer to request (cause != CYNARA_CALL_CAUSE_ANSWER). + * \param[in] user_response_data User specific data - passed to cynara library in + * cynara_async_cancel_request() is being only remembered by library. + * Cynara library does not take any actions on this pointer, + * except for giving it back to user in cynara_response_callback. + * After that cynara forgets this data. + */ +typedef void (*cynara_response_callback) (cynara_check_id check_id, cynara_async_call_cause cause, + int response, void *user_response_data); + +/** + * \brief Callback used by cynara async API when status of cynara socket is changed in + * cynara_async_initialize(), cynara_async_check_cache(), cynara_async_create_request(), + * cynara_async_create_simple_request(), cynara_async_process(), cynara_async_cancel_request() + * or cynara_async_finish(). + * + * File descriptor changes every time cynara library connects or disconnects cynara service. + * Status change is triggered when check_request or cancel needs to be send to + * cynara service or sending data has finished and there is nothing more to send to cynara + * service. + * + * Note, that provided file descriptor is used internally by libcynara-client-async + * so user should not use it in other way than waiting on it in event loop. + * In particular user should not write to, read from or close this fd. + * CYNARA_API_OPERATION_NOT_ALLOWED will be returned for every api function called in this callback. + * cynara_async_finish() will be ignored if called from within this callback. + * + * \param[in] old_fd Old descriptor which should be unregistered from event loop, + * Special value -1 is used when callback is called after first + * successful connect. + * \param[in] new_fd New descriptor which should be registered in event loop, + * Special value -1 is used when cynara_async_finish() is called and + * cynara is disconnected. In this case status should be ignored. + * \param[in] status Status indicating which events should be awaited on socket + * \param[in] user_status_data User specific data - passed to cynara library in + * cynara_async_initialize() is being only remembered by library. + * Cynara library does not take any actions on this pointer, + * except for giving it back to user in cynara_status_callback. + * Data should be valid at least until cynara_async_finish() is called. + */ +typedef void (*cynara_status_callback) (int old_fd, int new_fd, cynara_async_status status, + void *user_status_data); + +/** + * \par Description: + * Initialize cynara_async_configuration. Create structure used in following configuration + * API calls. + * + * \par Purpose: + * For configuration parameter to be used in cynara async initialization function, this API must be + * called before any other cynara async configuration API function. + * It will create cynara_async_configuration structure, an optional parameter of cynara async + * initialization. + * + * \par Typical use case: + * Once before setting parameters of cynara async configuration and passing to + * cynara_async_initialize(). + * + * \par Method of function operation: + * This API initializes inner library structures and in case of success returns pointer + * to created cynara_async_configuration structure. + * + * \par Sync (or) Async: + * This as a synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * Structure cynara_async_configuration created by cynara_async_configuration_create() call + * should be released with cynara_async_configuration_destroy(). + * Structure cynara_async_configuration should be destroyed after passing it to + * cynara_async_initialize(). + * + * \param[out] pp_conf Placeholder for created cynara_async_configuration structure. + * + * \return CYNARA_API_SUCCESS on success + * \return negative error code on error + */ +int cynara_async_configuration_create(cynara_async_configuration **pp_conf); + +/** + * \par Description: + * Release cynara_async_configuration structure created with cynara_async_configuration_create(). + * + * \par Purpose: + * This API should be used to clean up after usage of cynara_async_configuration. + * + * \par Typical use case: + * Once cynara_async_configuration is not needed. + * + * \par Method of function operation: + * This API releases inner library structure and destroys cynara_async_configuration structure. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \param[in] p_conf cynara_async_configuration structure. If NULL, the call has no effect. + */ +void cynara_async_configuration_destroy(cynara_async_configuration *p_conf); + +/** + * \par Description: + * Set client cache size. + * + * \par Purpose: + * This API is used to change default number of cached responses returned from cynara. + * + * \par Typical use case: + * Once before setting parameters of cynara async configuration and passing to + * cynara_async_initialize(). + * + * \par Method of function operation: + * This API initializes cache with given capacity. + * + * \par Sync (or) Async: + * This as a synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * After passing cynara_async_configuration to cynara_async_initialize() calling this API will have + * no effect. + * + * \param[in] p_conf cynara_async_configuration structure pointer. + * \param[in] cache_size Cache size to be set. + * + * \return CYNARA_API_SUCCESS on success + * \return negative error code on error + */ +int cynara_async_configuration_set_cache_size(cynara_async_configuration *p_conf, + size_t cache_size); + +/** + * \par Description: + * Initialize cynara-async-client library with given configuration. Create structure used in + * following API calls and register callback and user_status_data for + * further cynara async API calls. + * + * \par Purpose: + * This API must be used prior to calling any other cynara async API function. + * It will create cynara_async structure required for any other cynara async API calls. + * + * \par Typical use case: + * Once before entering event loop and before any other cynara async API is called. + * + * \par Method of function operation: + * This API initializes inner library structures and in case of success + * returns cynara_async structure. + * + * \par Sync (or) Async: + * This is an synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * Structure cynara_async created by cynara_async_initialize() call should be released + * with cynara_async_finish(). + * + * \param[out] pp_cynara Placeholder for created cynara_async structure. + * \param[in] p_conf Configuration for cynara-async-client library. + * NULL should be used for default configuration. + * Configuration management functions will be added later. + * Configuration will be able to specify e.g. size of cache used by library + * for holding checks results. + * \param[in] callback Function called when connection is started. + * If NULL, no callback will be called, when status changes. + * \param[in] user_status_data User specific data, passed to callback is being only remembered + * by library. Cynara library does not take any actions on this pointer, + * except for giving it back to user in cynara_status_callback. + * Data should be valid at least until cynara_async_finish() is called. + * Can be NULL. + * + * \return CYNARA_API_SUCCESS on success + * \return negative error code on error + */ +int cynara_async_initialize(cynara_async **pp_cynara, const cynara_async_configuration *p_conf, + cynara_status_callback callback, void *user_status_data); + +/** + * \par Description: + * Release cynara-async-client library and destroy structure created with cynara_async_initialize(). + * + * \par Purpose: + * This API should be used to clean up after usage of cynara-async-client library. + * + * \par Typical use case: + * Once after connection to cynara is not needed. + * + * \par Method of function operation: + * This API releases inner library structure and destroys cynara_async structure. Connection to + * cynara server is closed. Upon disconnecting this will trigger cynara_status_callback callback + * with -1 as new_fd param so client can unregister file descriptor connected with cynara. It will + * also trigger cynara_response_callback callback for each created request with + * cynara_async_call_cause::CYNARA_CALL_CAUSE_FINISH as cause param. + * + * \par Sync (or) Async: + * This is an asynchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * No other call to cynara-async-client library should be made after call to cynara_async_finish(). + * cynara_async_finish() called from within cynara_response_callback or cynara_status_callback will + * be ignored. + * + * \param[in] p_cynara cynara_async structure. If NULL, then the call has no effect. + */ +void cynara_async_finish(cynara_async *p_cynara); + +/** + * \par Description: + * Check access to given privilege for specified user, client and client_session in cache. + * + * \par Purpose: + * This API should be used to check if cache holds information about access of user, + * running application identified as clients to a privilege. + * This API should be used for fast check in cache. + * + * \par Typical use case: + * A service wants to check in cache, if a client requesting access to some privilege + * has proper rights. + * + * \par Method of function operation: + * Client (a process / application) requesting access to a privilege is running as some user. + * For such triple (client, user, privilege) a cache is checked. + * If cache is invalid it is cleared and call returns same as access not found. + * Additional parameter client_session + * may be used to distinguish between client session (e.g. for allowing access only for this + * particular application launch). Empty string "" can be used, when session differentiation + * is not needed. + * + * \par Sync (or) Async: + * This is an synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * \parblock + * Call to cynara_async_check_cache() needs cynara_async structure to be created first. + * Use cynara_async_initialize() before calling this function. cynara_async_check_cache() called + * from within cynara_status_callback or cynara_response_callback with + * CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE or CYNARA_CALL_CAUSE_FINISH cause will return + * CYNARA_API_OPERATION_NOT_ALLOWED. + * + * Socket status may occasionally be changed to CYNARA_STATUS_FOR_RW, to ensure + * that cynara_async_process() is triggered in event loop after socket is ready to send + * monitor logs to Cynara service. + * + * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be + * returned. + * \endparblock + * + * \param[in] p_cynara cynara_async structure. + * \param[in] client Application or process identifier. + * \param[in] client_session Client defined session. + * \param[in] user User of running client. + * \param[in] privilege Privilege that is a subject of a check. + * + * \return CYNARA_API_ACCESS_ALLOWED on checked access allowed + * \return CYNARA_API_ACCESS_DENIED on checked access denied + * \return CYNARA_API_CACHE_MISS on access not in cache + * \return other negative error code on error + */ +int cynara_async_check_cache(cynara_async *p_cynara, const char *client, const char *client_session, + const char *user, const char *privilege); + +/** + * \par Description: + * Creates access check request to cynara service for client, user accessing given privilege. + * Set callback and user_response_data to be called and passed when request processing is finished. + * + * \par Purpose: + * This API should be used to create check request for client identified by a triple + * (client, user, privilege) in custom defined session. + * Response can be received with cynara_async_process(). + * Check id is returned to pair request with response for canceling purposes. + * + * \par Typical use case: + * When cynara_async_check_cache() returned CYNARA_API_CACHE_MISS, so cynara service + * has to be asked, if a client requesting access to some privilege has proper rights. + * To receive matching response client sets callback and specifies arbitrary data to be passed + * to this callback. + * + * \par Method of function operation: + * \parblock + * Client (a process / application) requesting access to a privilege is running as some user. + * For such triple (client, user, privilege) a request event is created and added to pending + * requests for cynara_async_process() to process. + * + * Socket status will be changed to CYNARA_STATUS_FOR_RW, to ensure that cynara_async_process() + * will be triggered in event loop after socket is ready to send request to cynara service. + * After request is sent and there is nothing more to send to cynara service, status will change + * back to CYNARA_STATUS_FOR_READ. Status changes are delivered with cynara_status_callback. + * When function is successfully called unique check_id is returned. It is used for matching + * generated request with response, that will be received by registered callback. + * + * Because check_id is coded as 16-bit unsigned integer, there can be only 2^16 = 65536 pending + * requests. When response callback is called either because of getting answer or because + * of cancel check_id used for that request is released and can be reused by cynara library. + * When maximum of pending requests is reached cynara_async_create_request() fails with + * CYNARA_API_MAX_PENDING_REQUESTS error code. + * \endparblock + * + * \par Sync (or) Async: + * This is an asynchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * \parblock + * Call cynara_async_create_request() needs cynara_async structure to be created first with + * cynara_async_initialize(). + * Call cynara_async_cancel_request() to cancel pending request. + * Call cynara_async_process() to receive response. + * + * It is guaranteed that if cynara_async_create_request() function succeeds (CYNARA_API_SUCCESS) + * a callback will be called exactly once and that it will receive user_response_data. + * + * If function fails (returns negative error code) request won't be generated and won't be pending, + * callback function won't be ever called and user_response_data won't be remembered by library. + * + * Also no check_id will be generated and *p_check_id value should be ignored. + * cynara_async_create_request() called from within cynara_status_callback or + * cynara_response_callback with CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE or CYNARA_CALL_CAUSE_FINISH + * cause will return CYNARA_API_OPERATION_NOT_ALLOWED. + * + * String length cannot exceed CYNARA_MAX_ID_LENGTH ,otherwise CYNARA_API_INVALID_PARAM will be + * returned. + * \endparblock + * + * \param[in] p_cynara cynara_async structure. + * \param[in] client Application or process identifier. + * \param[in] client_session Client defined session. + * \param[in] user User of running client. + * \param[in] privilege Privilege that is a subject of a check. + * \param[out] p_check_id Placeholder for check id. If NULL, then no check_id is returned. + * \param[in] callback Function called when matching response is received. + * If NULL then no callback will be called when response, cancel, finish + * or service not availble error happens. + * \param[in] user_response_data User specific data, passed to callback is being only remembered + * by library. Cynara library does not take any actions on this pointer, + * except for giving it back to user in cynara_response_callback. + * Can be NULL. + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_MAX_PENDING_REQUESTS on too much pending requests + * \return other negative error code on error + */ +int cynara_async_create_request(cynara_async *p_cynara, const char *client, + const char *client_session, const char *user, const char *privilege, + cynara_check_id *p_check_id, cynara_response_callback callback, + void *user_response_data); + +/** + * \par Description: + * Creates simple access check request to cynara service for (potential) permission to take some + * action or access a resource. + * Set callback and user_response_data to be called and passed when request processing is finished. + * + * \par Purpose: + * This API should be used for a quick check if a user running application identified as client + * has access to a given privilege. + * Response can be received with cynara_async_process(). + * Check id is returned to pair request with response for canceling purposes. + * + * \par Typical use case: + * An application may use this API to check if it has (potential) permission to take some action + * or access resource in future (permissions may rarely change). The typical use would be to disable + * or hide some of functionalities if they probably could not be used anyways. + * + * \par Method of function operation: + * \parblock + * This function is very similar to cynara_async_create_request() with the difference, that in case + * of answer not being one of CYNARA_API_PERMISSION_DENIED or CYNARA_API_PERMISSION_ALLOWED, + * no external application will be consulted. Instead, CYNARA_API_ACCESS_NOT_RESOLVED is returned + * by a callback, meaning, that only creating full request through cynara_async_create_request() API + * would yield eventual answer. + * + * If access permission cannot be acquired without usage of external agents, callback can be + * called with CYNARA_CALL_CAUSE_ANSWER and response value being CYNARA_API_ACCESS_NOT_RESOLVED. + * \endparblock + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \par Important notes: + * \parblock + * Call cynara_async_create_simple_request() needs cynara_async structure to be created first + * with cynara_async_initialize(). + * Call cynara_async_cancel_request() to cancel pending request. + * Call cynara_async_process() to send request and receive response. + * + * The answer will be taken from cynara's database without consulting any external applications. + * If the answer cannot be resolved in one of CYNARA_API_ACCESS_ALLOWED or + * CYNARA_API_ACCESS_DENIED without communicating with external application, response returned + * through callback will have value CYNARA_API_ACCESS_NOT_RESOLVED. + * + * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be + * returned. + * \endparblock + * + * \param[in] p_cynara cynara_async structure. + * \param[in] client Application or process identifier. + * \param[in] client_session Client defined session. + * \param[in] user User of running client. + * \param[in] privilege Privilege that is a subject of a check. + * \param[out] p_check_id Placeholder for check id. If NULL, then no check_id is returned. + * \param[in] callback Function called when matching response is received. + * If NULL then no callback will be called when response, cancel, finish + * or service not availble error happens. + * \param[in] user_response_data User specific data, passed to callback is being only stored by + * library. Cynara library does not take any actions on this pointer, except for giving + * it back to user in cynara_response_callback. + * Can be NULL. + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_MAX_PENDING_REQUESTS on too much pending requests + * \return other negative error code on error + */ +int cynara_async_create_simple_request(cynara_async *p_cynara, const char *client, + const char *client_session, const char *user, + const char *privilege, cynara_check_id *p_check_id, + cynara_response_callback callback, void *user_response_data); + +/** + * \par Description: + * Process events that appeared on cynara socket. + * + * \par Purpose: + * Process events after they appear on cynara socket. + * + * \par Typical use case: + * After request was queued with cynara_async_create_request() or + * cynara_async_create_simple_request() this API will return response. + * When event loop will return readiness on cynara socket, client should use this API. + * + * \par Method of function operation: + * \parblock + * This API sends pending requests, receives all responses and reacts when cynara + * has disconnected. If cynara has disconnected all values in cache become invalid. During these + * operations status of cynara socket may change, so cynara_status_callback callback will be + * triggered to indicate these changes. cynara_response_callback callback will be triggered with + * cynara_async_call_cause::CYNARA_CALL_CAUSE_ANSWER as cause param when response is available. + * + * If cynara has disconnected it will be triggered with + * cynara_async_call_cause::CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE as cause param. + * \endparblock + * + * \par Sync (or) Async: + * This is an asynchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * Call to cynara_async_process() requires initialized cynara_async structure. For this use + * cynara_async_initialize(). cynara_async_process() called from within cynara_status_callback or + * cynara_response_callback with CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE or CYNARA_CALL_CAUSE_FINISH + * cause will return CYNARA_API_OPERATION_NOT_ALLOWED. + * + * \param[in] p_cynara cynara_async structure. + * + * \return CYNARA_API_SUCCESS on success + * \return negative error code on error + */ +int cynara_async_process(cynara_async *p_cynara); + +/** + * \par Description: + * Cancel request created by cynara_async_create_request() or cynara_async_create_simple_request(). + * + * \par Purpose: + * This API should be used to cancel pending check request, + * created by cynara_async_create_request() or cynara_async_create_simple_request(). + * + * \par Typical use case: + * When cynara client is no longer interested in receiving an answer. + * Same check_id value should be used to identify proper request as was generated during + * request creation with cynara_async_create_request() or cynara_async_create_simple_request(). + * + * \par Method of function operation: + * \parblock + * Cancels request created by cynara_async_create_request() or cynara_async_create_simple_request() + * call. + * + * cynara_status_callback callback may be triggered to be able to send cancel to cynara. + * cynara_response_callback callback will be triggered with with + * cynara_async_call_cause::CYNARA_CALL_CAUSE_CANCEL as cause param. + * + * If given id is not valid (was not requested or response callback was already delivered) + * cynara_async_cancel_request() returns CYNARA_API_INVALID_PARAM. + * \endparblock + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * Call to cynara_async_cancel_request() needs cynara_async structure to be created first. For this + * use cynara_async_initialize(). cynara_async_cancel_request() called from within + * cynara_status_callback or cynara_response_callback with CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE + * or CYNARA_CALL_CAUSE_FINISH cause will return CYNARA_API_OPERATION_NOT_ALLOWED. + * + * \param[in] p_cynara cynara_async structure. + * \param[in] check_id Check id to be cancelled + * + * \return CYNARA_API_SUCCESS on success + * \return negative error code on error + */ +int cynara_async_cancel_request(cynara_async *p_cynara, cynara_check_id check_id); + +#ifdef __cplusplus +} +#endif + +#endif /* CYNARA_CLIENT_ASYNC_H */ diff --git a/include/cynara/cynara-client-plugin.h b/include/cynara/cynara-client-plugin.h new file mode 100644 index 0000000..70000e6 --- /dev/null +++ b/include/cynara/cynara-client-plugin.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/include/cynara-client-plugin.h + * @author Zofia Abramowska <z.abramowska@samsung.com> + * @author Oskar Switalski <o.switalski@samsung.com> + * @version 1.0 + * @brief This file defines cynara client side of external plugin interface - + * ClientPluginInterface. + */ + +#ifndef CYNARACLIENTPLUGIN_H_ +#define CYNARACLIENTPLUGIN_H_ + +#include <memory> + +#include <plugin/ExternalPluginInterface.h> +#include <types/ClientSession.h> +#include <types/PolicyResult.h> + +namespace Cynara { + +class ClientPluginInterface; +typedef std::shared_ptr<ClientPluginInterface> ClientPluginInterfacePtr; + +/** + * A class defining external plugins interface. + * + * These plugins work inside of cynara client library. They interpret + * PolicyResult returned by cynara in terms of: + * * cacheability - tells, whether value should be cached (for e.g. policyType like + * ALLOW_ONCE should not be cached) + * * usability - whether cache entry can still be used (for e.g. policy allowing access for + * given type) + * * value - translates PolicyResult to CYNARA_API_ACCESS_ALLOWED or CYNARA_API_ACCESS_DENIED + * + * Plugin implementing ClientPluginInterface must implement ExternalPluginInterface. + * Creation/destruction functions with signatures compatible to Cynara::create_t and + * Cynara::destroy_t must be provided as factories of ClientPluginInterface. + */ +class ClientPluginInterface : public ExternalPluginInterface { +public: + /** + * Return entry cacheability + */ + virtual bool isCacheable(const ClientSession &session, const PolicyResult &result) = 0; + /** + * Return entry usability + */ + virtual bool isUsable(const ClientSession &session, const ClientSession &prevSession, + bool &updateSession, PolicyResult &result) = 0; + /** + * Translate PolicyResult to CYNARA_API_ACCESS_ALLOWED or CYNARA_API_ACCESS_DENIED + */ + virtual int toResult(const ClientSession &session, PolicyResult &result) = 0; + + virtual ~ClientPluginInterface() {}; +}; + +} + +#endif // CYNARACLIENTPLUGIN_H_ diff --git a/include/cynara/cynara-client.h b/include/cynara/cynara-client.h new file mode 100644 index 0000000..d1388ec --- /dev/null +++ b/include/cynara/cynara-client.h @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/include/cynara-client.h + * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com> + * @author Zofia Abramowska <z.abramowska@samsung.com> + * @author Oskar Switalski <o.switalski@samsung.com> + * @version 1.0 + * @brief This file contains client APIs of Cynara available with libcynara-client. + * @example cynara-client.example + */ + +#ifndef CYNARA_CLIENT_H +#define CYNARA_CLIENT_H + +#include <stddef.h> + +#include <cynara/cynara-error.h> +#include <cynara/cynara-limits.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct cynara cynara; +typedef struct cynara_configuration cynara_configuration; + +/** + * \par Description: + * Initialize cynara_configuration. Create structure used in following configuration + * API calls. + * + * \par Purpose: + * For configuration parameter to be used in cynara_initialize() function, this API must be + * called before any other cynara configuration API function. + * It will create cynara_configuration structure, an optional parameter of cynara initialization. + * + * \par Typical use case: + * Once before setting parameters of cynara configuration and passing to + * cynara_initialize(). + * + * \par Method of function operation: + * This API initializes inner library structures and in case of success returns pointer + * to created cynara_configuration structure. + * + * \par Sync (or) Async: + * This as a synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * Structure cynara_configuration created by cynara_configuration_create() call + * should be released with cynara_configuration_destroy(). + * Structure cynara_configuration should be destroyed after passing it to + * cynara_initialize(). + * + * \param[out] pp_conf Placeholder for created cynara_configuration structure. + * + * \return CYNARA_API_SUCCESS on success + * or negative error code on error. + */ +int cynara_configuration_create(cynara_configuration **pp_conf); + +/** + * \par Description: + * Release cynara_configuration structure created with cynara_configuration_create(). + * + * \par Purpose: + * This API should be used to clean up after usage of cynara_configuration. + * + * \par Typical use case: + * Once cynara_configuration is not needed. + * + * \par Method of function operation: + * This API releases inner library structure and destroys cynara_configuration structure. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \param[in] p_conf cynara_configuration structure. If NULL, the call has no effect. + */ +void cynara_configuration_destroy(cynara_configuration *p_conf); + +/** + * \par Description: + * Set client cache size. + * + * \par Purpose: + * This API is used to change default number of cached responses returned from cynara. + * + * \par Typical use case: + * Once after cynara_configuration is created with cynara_configuration_create() + * and before passing configuration to cynara_initialize(). + * + * \par Method of function operation: + * This API initializes cache with given capacity. + * + * \par Sync (or) Async: + * This as a synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into protected critical section. + * + * \par Important notes: + * After passing cynara_configuration to cynara_initialize() calling this API will have + * no effect. + * + * \param[in] p_conf cynara_configuration structure pointer. + * \param[in] cache_size Cache size to be set. + * + * \return CYNARA_API_SUCCESS on success + * or negative error code on error. + */ +int cynara_configuration_set_cache_size(cynara_configuration *p_conf, size_t cache_size); + +/** + * \par Description: + * Initialize cynara-client library with given configuration. + * Create structure used in following API calls. + * + * \par Purpose: + * This API must be used by prior calling cynara_check() function. + * + * \par Typical use case: + * Once before a service can call cynara_check(). + * + * \par Method of function operation: + * This API initializes inner library structures and in case of success + * creates and returns cynara structure. + * + * \par Sync (or) Async: + * This is a Synchronous API. + * + * \par Thread-safeness: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \par Important notes: + * Structure cynara created by cynara_initialize() call should be released with cynara_finish(). + * + * \param[out] pp_cynara Place holder for created cynara structure. + * \param[in] p_conf Configuration for cynara-client library. NULL for default parameters. + * [TODO define and describe functions for custom parameters]. + * + * \return CYNARA_API_SUCCESS on success, or error code on error. + */ +int cynara_initialize(cynara **pp_cynara, const cynara_configuration *p_conf); + +/** + * \par Description: + * Release cynara-client library and destroy structure created with cynara_initialize(). + * + * \par Purpose: + * This API should be used to clean up after usage of cynara-client library. + * + * \par Typical use case: + * Once after last call to cynara_check(). + * + * \par Method of function operation: + * This API releases inner library structures and destroys cynara structure. + * + * \par Sync (or) Async: + * This is a Synchronous API. + * + * \par Thread-safeness: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \par Important notes: + * No other call to libcynara-client should be made after call to cynara_finish(). + * + * \param[in] p_cynara Cynara structure. + * + * \return CYNARA_API_SUCCESS on success, or error code on error. + */ +int cynara_finish(cynara *p_cynara); + +/** + * \par Description: + * Check client, user access for given privilege. + * + * \par Purpose: + * This API should be used to check if a user running application identified as client + * has access to a privilege. + * + * \par Typical use case: + * A service want to ask trusted process (Cynara), if a client demanding access to some privilege + * has proper rights. + * + * \par Method of function operation: + * Client (a process / application) demanding access to a privilege is running as some user. + * For such triple an access to a privilege is checked by calling cynara. + * Depending on defined policy, an external application may be launched to ask user a question, + * e.g. if [s]he wants to allow client to use a privilege. Additional parameter client_session + * may be used to distinguish between client session (e.g. for allowing access only for this + * particular application launch). + * + * \par Sync (or) Async: + * This is a Synchronous API. + * + * \par Thread-safeness: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \par Important notes: + * \parblock + * An external application may be launched to allow user interaction in granting or denying access. + * + * Call to cynara_check() needs cynara structure to be created first with call to + * cynara_initialize(). + * + * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be + * returned. + * \endparblock + * + * \param[in] p_cynara Cynara structure. + * \param[in] client Application or process identifier. + * \param[in] client_session Session of client (connection, launch). + * \param[in] user User running client. + * \param[in] privilege Privilege that is a subject of a check.. + * + * \return CYNARA_API_ACCESS_ALLOWED on access allowed, CYNARA_API_ACCESS_DENIED on access denial + * or other error code on error. + */ +int cynara_check(cynara *p_cynara, const char *client, const char *client_session, const char *user, + const char *privilege); + +/** + * \par Description: + * Check for (potential) permission to take some action or access a resource. + * + * \par Purpose: + * This API should be used for a quick check if a user running application identified as client + * has access to a given privilege. + * + * \par Typical use case: + * An application may use this API to check, if it has (potential) permission to take some action + * or access resource in future (permissions may rarely change). The typical use would be to disable + * or hide some of functionalities, if they probably could not be used anyways. + * + * \par Method of function operation: + * This function is very similar to cynara_check() with the difference, that in case of answer not + * being one of CYNARA_API_ACCESS_DENIED or CYNARA_API_ACCESS_ALLOWED, no external application will + * be consulted. Instead, CYNARA_API_ACCESS_NOT_RESOLVED is returned, meaning, that only running + * full cynara_check() API would yield eventual answer. + * Similarly, like in cynara_check(), argument client_session can be used to distinguish client + * sessions and grant possibility to yield answer from cache. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \par Important notes: + * \parblock + * The answer will be taken from Cynara's database without consulting any external applications. + * If the answer cannot be resolved in one of CYNARA_API_PERMISSION_DENIED or + * CYNARA_API_PERMISSION_ALLOWED without communicating with external application the API will return + * CYNARA_API_ACCESS_NOT_RESOLVED. + * + * Call to cynara_simple_check() needs cynara structure to be created first with call to + * cynara_initialize(). String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise + * CYNARA_API_INVALID_PARAM will be returned. + * \endparblock + * + * \param[in] p_cynara Cynara structure. + * \param[in] client Application or process identifier. + * \param[in] client_session Session of client (connection, launch). + * \param[in] user User running client. + * \param[in] privilege Privilege that is a subject of a check. + * + * \return CYNARA_API_ACCESS_ALLOWED on access allowed, CYNARA_API_ACCESS_DENIED on access denial, + * CYNARA_API_ACCESS_NOT_RESOLVED when decision is not known without usage of external plugins or + * agents or negative error code on error. + */ +int cynara_simple_check(cynara *p_cynara, const char *client, const char *client_session, + const char *user, const char *privilege); + +#ifdef __cplusplus +} +#endif + +#endif /* CYNARA_CLIENT_H */ diff --git a/include/cynara/cynara-creds-commons.h b/include/cynara/cynara-creds-commons.h new file mode 100644 index 0000000..81b9784 --- /dev/null +++ b/include/cynara/cynara-creds-commons.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/include/cynara-creds-commons.h + * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com> + * @author Radoslaw Bartosiak <r.bartosiak@samsung.com> + * @author Aleksander Zdyb <a.zdyb@samsung.com> + * @author Oskar Switalski <o.switalski@samsung.com> + * @version 1.0 + * @brief This file contains common APIs for Cynara credentials helper. + */ + +#ifndef CYNARA_CREDS_COMMONS_H +#define CYNARA_CREDS_COMMONS_H + +#include <cynara/cynara-error.h> + +enum cynara_client_creds { + CLIENT_METHOD_SMACK, + CLIENT_METHOD_PID, + + CLIENT_METHOD_DEFAULT = 0xFFFF, +}; + +enum cynara_user_creds { + USER_METHOD_UID, + USER_METHOD_GID, + + USER_METHOD_DEFAULT = 0xFFFF, +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \par Description: + * Gets the system default method value for client feature used in cynara-creds. + * + * \par Purpose: + * Functions cynara_creds_dbus_get_client() and cynara_creds_socket_get_client() take a method + * parameter, which determines a kind of process feature (i.e PID, SMACK label) returned by them. + * The described function provides implementation for obtaining a system default value + * for this parameter. + * + * \par Typical use case: + * The function might be called before cynara_creds_dbus_get_client() + * and cynara_creds_socket_get_client(), when functions shall be invoked with system default + * value of method parameter. + * + * \par Method of function operation: + * When this function is called for the first time it reads and returns the value of client_default + * parameter from /etc/cynara/creds.conf file. + * Returned value is cached so subsequent calls will not consult file again but use cached value. + * This also means that after the initial call any changes in the file will be ignored for the + * remaining lifetime of the process. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is thread-safe. + * + * \param[out] method Placeholder for system default client feature + * (like CLIENT_METHOD_SMACK, CLIENT_METHOD_PID) + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or + * there are errors in configuration file + * \return CYNARA_API_OUT_OF_MEMORY if there is error in memory allocation + * \return CYNARA_API_UNKNOWN_ERROR if there is other error + * + */ +int cynara_creds_get_default_client_method(enum cynara_client_creds *method); + +/** + * \par Description: + * Gets the system default method value for user feature used in cynara-creds. + * + * \par Purpose: + * Functions cynara_creds_dbus_get_user() and cynara_creds_socket_get_user() take a method + * parameter, which determines a kind of process feature (i.e UID, GID) returned by them. + * The described function provides implementation for obtaining a system default value + * for this parameter. + * + * \par Typical use case: + * The function might be called before cynara_creds_dbus_get_user() + * and cynara_creds_socket_get_user() when functions shall be invoked with system default + * value of method parameter. + * + * \par Method of function operation: + * When this function is called for the first time it reads and returns the value of user_default + * parameter from /etc/cynara/creds.conf file. + * Returned value is cached so subsequent calls will not consult file again but use cached value. + * This also means that after the initial call any changes in the file will be ignored for the + * remaining lifetime of the process. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is thread-safe. + * + * \param[out] method Placeholder for system default user feature + * (like USER_METHOD_UID, USER_METHOD_GID) + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or + * there are errors in configuration file + * \return CYNARA_API_OUT_OF_MEMORY if there is error in memory allocation + * \return CYNARA_API_UNKNOWN_ERROR if there is other error + */ +int cynara_creds_get_default_user_method(enum cynara_user_creds *method); + +#ifdef __cplusplus +} +#endif + +#endif /* CYNARA_CREDS_COMMONS_H */ diff --git a/include/cynara/cynara-creds-dbus.h b/include/cynara/cynara-creds-dbus.h new file mode 100644 index 0000000..04af96f --- /dev/null +++ b/include/cynara/cynara-creds-dbus.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/include/cynara-creds-dbus.h + * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com> + * @author Radoslaw Bartosiak <r.bartosiak@samsung.com> + * @author Oskar Switalski <o.switalski@samsung.com> + * @version 1.0 + * @brief This file contains Cynara credentials helper APIs for dbus clients. + * @example cynara-creds-dbus.example + */ + +#ifndef CYNARA_CREDS_DBUS_H +#define CYNARA_CREDS_DBUS_H + +#include <dbus/dbus.h> +#include <sys/types.h> + +#include <cynara/cynara-creds-commons.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \par Description: + * Creates a client identification string with given method. Client is a process identified by the + * unique name at the other side of the dbus connection. + * + * \par Purpose: + * Client identification string is required for cynara_check() and cynara_async_create_request() + * functions. + * + * \par Typical use case: + * The function is called before the call of cynara_check() or cynara_async_create_request() + * function. Returned string is used as client parameter in cynara_check() or + * cynara_async_create_request() function. String is released with free() function when it is no + * longer needed. + * + * \par Method of function operation: + * The function generates client string by calling a method from DBus Interface + * ("org.freedesktop.DBus") which is placed on system bus ("org.freedesktop.DBus"). + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \par Important notes: + * Memory for returned client string is obtained with malloc(), and should be freed with free(). + * Allocated string is returned only, when function succeeds. + * If method is CLIENT_METHOD_DEFAULT then it will be chosen based on Cynara configuration file. + * + * \param[in] connection DBus connection to a bus. It manages incomming and outgoing messages + * \param[in] uniqueName DBus identifier of the client + * \param[in] method Method of client identifier creation + * \param[out] client Placeholder for allocated string containing client id + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_INVALID_PARAM when client is NULL or uniqueName or client has wrong + * value (i.e NULL or non-existing) + * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported + * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or + * there are errors in configuration file + * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory + */ +int cynara_creds_dbus_get_client(DBusConnection *connection, const char *uniqueName, + enum cynara_client_creds method, char **client); + +/** + * \par Description: + * Creates a user identification string with given method. User is an executor of process + * at the other side of socket. + * + * \par Purpose: + * User identification string is required for cynara_check() and cynara_async_create_request() + * functions. + * + * \par Typical use case: + * The function is called before the call of cynara_check() or cynara_async_create_request() + * function. Returned string is used as user parameter in cynara_check() or + * cynara_async_create_request() function. String is released with free() function when it is no + * longer needed. + * + * \par Method of function operation: + * The function generates user string by calling a method from DBus Interface + * ("org.freedesktop.DBus") which is placed on system bus ("org.freedesktop.DBus"). + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \par Important notes: + * Memory for returned user string is obtained with malloc(), and should be freed with free(). + * Allocated string is returned only, when function succeeds. + * If method is USER_METHOD_DEFAULT then it will be chosen based on Cynara configuration file. + * + * \param[in] connection DBus connection to a bus. It manages incomming and outgoing messages + * \param[in] uniqueName DBus identifier of the client invoked by the user + * \param[in] method Method of client identifier creation + * \param[out] user Placeholder for allocated string containing user id + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_INVALID_PARAM when user is NULL or connection is not valid DBus connection or + * uniqueName does not represent a process conected to the DBus + * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported + * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or + * there are errors in configuration file + * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory + */ +int cynara_creds_dbus_get_user(DBusConnection *connection, const char *uniqueName, + enum cynara_user_creds method, char **user); + +/** + * \par Description: + * Return PID of a proces identified by the unique name at the other side of the dbus connection. + * + * \par Purpose: + * PID may be used for client_session creation with cynara_helper_session_from_pid() function + * from libcynara-helper-session library. Client_session is needed for cynara_check() + * and cynara_async_create_request() functions. + * + * \par Typical use case: + * The function is called before the call of cynara_helper_session_from_pid() function. + * + * \par Method of function operation: + * The function reads PID of the peer by calling a method from DBus Interface + * ("org.freedesktop.DBus") which is placed on system bus ("org.freedesktop.DBus") + * with "GetConnectionUnixProcessID" argument. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \param[in] connection DBus connection to a bus. It manages incomming and outgoing messages + * \param[in] uniqueName DBus identifier of the client invoked by the user + * \param[out] pid Placeholder for PID returned by function + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_INVALID_PARAM when socket_fd is not valid connected socket descriptor + * \return CYNARA_API_UNKNOWN_ERROR when system function fails in incredible situation + * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory + */ +int cynara_creds_dbus_get_pid(DBusConnection *connection, const char *uniqueName, pid_t *pid); + +#ifdef __cplusplus +} +#endif + +#endif /* CYNARA_CREDS_DBUS_H */ diff --git a/include/cynara/cynara-creds-gdbus.h b/include/cynara/cynara-creds-gdbus.h new file mode 100644 index 0000000..2334e45 --- /dev/null +++ b/include/cynara/cynara-creds-gdbus.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/include/cynara-creds-gdbus.h + * @author Jacek Bukarewicz <j.bukarewicz@samsung.com> + * @author Oskar Switalski <o.switalski@samsung.com> + * @version 1.0 + * @brief This file contains Cynara credentials helper APIs for gdbus clients. + * @example cynara-creds-gdbus.example + */ + +#ifndef CYNARA_CREDS_GDBUS_H +#define CYNARA_CREDS_GDBUS_H + +#include <gio/gio.h> +#include <sys/types.h> + +#include <cynara/cynara-creds-commons.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \par Description: + * Creates a client identification string with given method. Client is a process identified by the + * unique name at the other side of the dbus connection. + * + * \par Purpose: + * Client identification string is required for cynara_check() and cynara_async_create_request() + * functions. + * + * \par Typical use case: + * The function is called before the call of cynara_check() or cynara_async_create_request() + * function. Returned string is used as client parameter in cynara_check() or + * cynara_async_create_request() function. String is released with g_free() function when it is no + * longer needed. + * + * \par Method of function operation: + * The function generates client string by calling a method from DBus Interface + * ("org.freedesktop.DBus") which is placed on system bus ("org.freedesktop.DBus"). + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \par Important notes: + * Memory for returned user string should be freed with g_free(). + * Allocated string is returned only, when function succeeds. + * If method is CLIENT_METHOD_DEFAULT then it will be chosen based on Cynara configuration file. + * + * \param[in] connection DBus connection to a bus. It manages incomming and outgoing messages + * \param[in] uniqueName DBus identifier of the client + * \param[in] method Method of client identifier creation + * \param[out] client Placeholder for allocated string containing client id + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_INVALID_PARAM when client is NULL or uniqueName or client has wrong + * value (i.e NULL or non-existing) + * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or + * there are errors in configuration file + * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported + */ +int cynara_creds_gdbus_get_client(GDBusConnection *connection, const gchar *uniqueName, + enum cynara_client_creds method, gchar **client); + +/** + * \par Description: + * Creates a user identification string with given method. User is an executor of process + * at the other side of socket. + * + * \par Purpose: + * User identification string is required for cynara_check() and cynara_async_create_request() + * functions. + * + * \par Typical use case: + * The function is called before the call of cynara_check() or cynara_async_create_request() + * function. Returned string is used as user parameter in cynara_check() or + * cynara_async_create_request() function. String is released with g_free() function when it is no + * longer needed. + * + * \par Method of function operation: + * The function generates user string by calling a method from DBus Interface + * ("org.freedesktop.DBus") which is placed on system bus ("org.freedesktop.DBus"). + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \par Important notes: + * Memory for returned user string should be freed with g_free(). + * Allocated string is returned only, when function succeeds. + * If method is USER_METHOD_DEFAULT then it will be chosen based on Cynara configuration file. + * + * \param[in] connection DBus connection to a bus. It manages incomming and outgoing messages + * \param[in] uniqueName DBus identifier of the client invoked by the user + * \param[in] method Method of client identifier creation + * \param[out] user Placeholder for allocated string containing user id + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_INVALID_PARAM when user is NULL or connection is not valid DBus connection or + * uniqueName does not represent a process conected to the DBus + * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or + * there are errors in configuration file + * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported + */ +int cynara_creds_gdbus_get_user(GDBusConnection *connection, const gchar *uniqueName, + enum cynara_user_creds method, gchar **user); + +/** + * \par Description: + * Return PID of a process identified by the unique name at the other side of the dbus connection. + * + * \par Purpose: + * PID may be used for client_session creation with cynara_helper_session_from_pid() function + * from libcynara-helper-session library. Client_session is needed for cynara_check() + * and cynara_async_create_request() functions. + * + * \par Typical use case: + * The function is called before the call of cynara_helper_session_from_pid() function. + * + * \par Method of function operation: + * The function reads PID of the peer by calling a method from DBus Interface + * ("org.freedesktop.DBus") which is placed on system bus ("org.freedesktop.DBus") + * with "GetConnectionUnixProcessID" argument. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \param[in] connection DBus connection to a bus. It manages incomming and outgoing messages + * \param[in] uniqueName DBus identifier of the client invoked by the user + * \param[out] pid Placeholder for PID returned by function + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_INVALID_PARAM when one of parameters is not valid + * \return CYNARA_API_UNKNOWN_ERROR when function fails because of unknown error + */ +int cynara_creds_gdbus_get_pid(GDBusConnection *connection, const gchar *uniqueName, pid_t *pid); + +#ifdef __cplusplus +} +#endif + +#endif /* CYNARA_CREDS_GDBUS_H */ diff --git a/include/cynara/cynara-creds-self.h b/include/cynara/cynara-creds-self.h new file mode 100644 index 0000000..580a19d --- /dev/null +++ b/include/cynara/cynara-creds-self.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/include/cynara-creds-self.h + * @author Zofia Abramowska <z.abramowska@samsung.com> + * @version 1.0 + * @brief This file contains Cynara credentials helper APIs for current process + * @example cynara-creds-self.example + */ + +#ifndef CYNARA_CREDS_SELF_H +#define CYNARA_CREDS_SELF_H + +#include <sys/types.h> + +#include <cynara/cynara-creds-commons.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \par Description: + * Creates a client identification string with given method. Client is a current process. + * + * \par Purpose: + * Client identification string is required for cynara_check() and cynara_async_create_request() + * functions. + * + * \par Typical use case: + * The function is called before the call of cynara_check() or cynara_async_create_request() + * function. Returned string is used as client parameter in cynara_check() or + * cynara_async_create_request() function. String is released with free() function when it is no + * longer needed. + * + * \par Method of function operation: + * The function generates client string using current process context. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \par Important notes: + * Memory for returned client string is obtained with malloc(), and should be freed with free(). + * Allocated string is returned only, when function succeeds. + * If method is CLIENT_METHOD_DEFAULT then it will be chosen based on Cynara configuration file. + * + * \param[in] method Method of client identifier creation + * \param[out] client Placeholder for allocated string containing client id + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_INVALID_PARAM when client is NULL + * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported + * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or + * there are errors in configuration file + * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory + */ +int cynara_creds_self_get_client(enum cynara_client_creds method, char **client); + +/** + * \par Description: + * Creates a user identification string with given method. User is current process. + * + * \par Purpose: + * User identification string is required for cynara_check() and cynara_async_create_request() + * functions. + * + * \par Typical use case: + * The function is called before the call of cynara_check() or cynara_async_create_request() + * function. Returned string is used as user parameter in cynara_check() or + * cynara_async_create_request() function. String is released with free() function when it is no + * longer needed. + * + * \par Method of function operation: + * The function generates user string using current process context. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \par Important notes: + * Memory for returned user string is obtained with malloc(), and should be freed with free(). + * Allocated string is returned only, when function succeeds. + * If method is USER_METHOD_DEFAULT then it will be chosen based on Cynara configuration file. + * + * \param[in] method Method of user identifier creation + * \param[out] user Placeholder for allocated string containing user id + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_INVALID_PARAM when user is NULL + * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported + * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or + * there are errors in configuration file + * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory + */ +int cynara_creds_self_get_user(enum cynara_user_creds method, char **user); + +#ifdef __cplusplus +} +#endif + +#endif /* CYNARA_CREDS_SELF_H */ diff --git a/include/cynara/cynara-creds-socket.h b/include/cynara/cynara-creds-socket.h new file mode 100644 index 0000000..fe5cbdb --- /dev/null +++ b/include/cynara/cynara-creds-socket.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/include/cynara-creds-socket.h + * @author Radoslaw Bartosiak <r.bartosiak@samsung.com> + * @author Aleksander Zdyb <a.zdyb@samsung.com> + * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com> + * @author Oskar Switalski <o.switalski@samsung.com> + * @version 1.0 + * @brief This file contains Cynara credentials helper APIs for socket clients. + * @example cynara-creds-socket.example + */ + +#ifndef CYNARA_CREDS_SOCKET_H +#define CYNARA_CREDS_SOCKET_H + +#include <sys/types.h> + +#include <cynara/cynara-creds-commons.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \par Description: + * Creates a client identification string with given method. Client is a process at the other + * side of socket. + * + * \par Purpose: + * Client identification string is required for cynara_check() and cynara_async_create_request() + * functions. + * + * \par Typical use case: + * The function is called before the call of cynara_check() or cynara_async_create_request() + * function. Returned string is used as client parameter in cynara_check() or + * cynara_async_create_request() function. String is released with free() function when it is no + * longer needed. + * + * \par Method of function operation: + * The function generates client string using SO_PEERCRED on socket. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \par Important notes: + * Memory for returned client string is obtained with malloc(), and should be freed with free(). + * Allocated string is returned only, when function succeeds. + * If method is CLIENT_METHOD_DEFAULT then it will be chosen based on Cynara configuration file. + * + * \param[in] socket_fd Descriptor of open connected UNIX socket + * \param[in] method Method of client identifier creation + * \param[out] client Placeholder for allocated string containing client id + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_INVALID_PARAM when client is NULL or socket_fd is not valid connected socket + * descriptor + * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported + * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or + * there are errors in configuration file + * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory + */ +int cynara_creds_socket_get_client(int socket_fd, enum cynara_client_creds method, char **client); + +/** + * \par Description: + * Creates a user identification string with given method. User is an executor of process + * at the other side of socket. + * + * \par Purpose: + * User identification string is required for cynara_check() and cynara_async_create_request() + * functions. + * + * \par Typical use case: + * The function is called before the call of cynara_check() or cynara_async_create_request() + * function. Returned string is used as user parameter in cynara_check() or + * cynara_async_create_request() function. String is released with free() function when it is no + * longer needed. + * + * \par Method of function operation: + * The function generates user string using SO_PEERCRED on socket. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \par Important notes: + * Memory for returned user string is obtained with malloc(), and should be freed with free(). + * Allocated string is returned only, when function succeeds. + * If method is USER_METHOD_DEFAULT then it will be chosen based on Cynara configuration file. + * + * \param[in] socket_fd Descriptor of open connected UNIX socket + * \param[in] method Method of user identifier creation + * \param[out] user Placeholder for allocated string containing user id + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_INVALID_PARAM when user is NULL or socket_fd is not valid connected socket + * descriptor + * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported + * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or + * there are errors in configuration file + * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory + */ +int cynara_creds_socket_get_user(int socket_fd, enum cynara_user_creds method, char **user); + +/** + * \par Description: + * Return PID of process at the other side of socket. + * + * \par Purpose: + * PID may be used for client_session creation with cynara_session_from_pid() function + * from libcynara-session library. Client_session is needed for cynara_check() + * and cynara_async_create_request() functions. + * + * \par Typical use case: + * The function is called before the call of cynara_session_from_pid() function. + * + * \par Method of function operation: + * The function reads PID of peer using SO_PEERCRED on socket. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is NOT thread-safe. If functions from described API are called by multithreaded + * application from different threads, they must be put into mutex protected critical section. + * + * \param[in] socket_fd Descriptor of open connected UNIX socket + * \param[out] pid Placeholder for pid + * + * \return CYNARA_API_SUCCESS on success + * \return CYNARA_API_INVALID_PARAM when socket_fd is not valid connected socket descriptor + * \return CYNARA_API_UNKNOWN_ERROR when system function fails in incredible situation + */ +int cynara_creds_socket_get_pid(int socket_fd, pid_t *pid); + +#ifdef __cplusplus +} +#endif + +#endif /* CYNARA_CREDS_SOCKET_H */ diff --git a/include/cynara/cynara-error.h b/include/cynara/cynara-error.h new file mode 100644 index 0000000..8316f1d --- /dev/null +++ b/include/cynara/cynara-error.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/include/cynara-error.h + * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com> + * @author Zofia Abramowska <z.abramowska@samsung.com> + * @author Radoslaw Bartosiak <r.bartosiak@samsung.com> + * @author Aleksander Zdyb <a.zdyb@samsung.com> + * @version 1.0 + * @brief This file contains error codes returned by APIs of Cynara. + */ + +#ifndef CYNARA_ERROR_H +#define CYNARA_ERROR_H + +#include <stddef.h> + +/** + * \name Return Codes + * Exported by the foundation API. Return codes beginning with negative codes indicate an error. + * @{ +*/ + +/*! \brief indicating that API call was interrupted by user*/ +#define CYNARA_API_INTERRUPTED 4 + +/*! \brief indicating access that cannot be resolved without further actions*/ +#define CYNARA_API_ACCESS_NOT_RESOLVED 3 + +/*! \brief indicating access that was checked is allowed */ +#define CYNARA_API_ACCESS_ALLOWED 2 + +/*! \brief indicating that access that was checked is denied */ +#define CYNARA_API_ACCESS_DENIED 1 + +/*! \brief indicating the result of the one specific API is successful */ +#define CYNARA_API_SUCCESS 0 + +/*! \brief indicating that value is not present in cache */ +#define CYNARA_API_CACHE_MISS -1 + +/*! \brief indicating that pending requests reached maximum */ +#define CYNARA_API_MAX_PENDING_REQUESTS -2 + +/*! \brief indicating system is running out of memory state */ +#define CYNARA_API_OUT_OF_MEMORY -3 + +/*! \brief indicating the API's parameter is malformed */ +#define CYNARA_API_INVALID_PARAM -4 + +/*! \brief indicating that service is not available */ +#define CYNARA_API_SERVICE_NOT_AVAILABLE -5 + +/*! \brief indicating that provided method is not supported by library */ +#define CYNARA_API_METHOD_NOT_SUPPORTED -6 + +/*! \brief cynara service does not allow to perform requested operation */ +#define CYNARA_API_OPERATION_NOT_ALLOWED -7 + +/*! \brief cynara service failed to perform requested operation */ +#define CYNARA_API_OPERATION_FAILED -8 + +/*! \brief cynara service hasn't found requested bucket */ +#define CYNARA_API_BUCKET_NOT_FOUND -9 + +/*! \brief indicating an unknown error */ +#define CYNARA_API_UNKNOWN_ERROR -10 + +/*! \brief indicating configuration error */ +#define CYNARA_API_CONFIGURATION_ERROR -11 + +/*! \brief indicating invalid parameter in command-line */ +#define CYNARA_API_INVALID_COMMANDLINE_PARAM -12 + +/*! \brief indicating that provided buffer is too short */ +#define CYNARA_API_BUFFER_TOO_SHORT -13 + +/*! \brief indicating that database is corrupted */ +#define CYNARA_API_DATABASE_CORRUPTED -14 + +/*! \brief indicating that user doesn't have enough permission to perform action */ +#define CYNARA_API_PERMISSION_DENIED -15 +/** @}*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \par Description: + * Returns the error string in the user-supplied buffer buf of length buflen. + * + * \par Purpose: + * This function populates passed argument buf with a string that describes the error code + * passed in the argument errnum, possibly using the LC_MESSAGES part of the current locale + * to select the appropriate language. + * + * \par Typical use case: + * Once after any of API functions returns negative value (if the error message is to be presented + * to the user). + * + * \par Method of function operation: + * This function copies error string to memory pointed by argument buf along with termination + * character ('\0'). + * + * \par Sync (or) async: + * This is a synchronous API. + * + * \par Thread-safety: + * This function is thread-safe as long as setlocale() is not invoked concurrently. + * + * \par Important notes: + * This function copies error string to memory pointed by argument buf only if the buffer is + * of sufficient size (indicated by argument buflen). User is responsible for allocating sufficient + * memory for both error string and termination character ('\0'). + * Moreover, the user must not invoke setlocale() concurrently with this function, because results + * are unspecified. + * + * \param[in] errnum error number + * \param[out] buf buffer for error message + * \param[in] buflen buffer size + * + * \return CYNARA_API_SUCCESS on success, CYNARA_API_BUFFER_TOO_SHORT, if error message couldn't fit + * into allocated buffer, or CYNARA_API_INVALID_PARAM if errnum is not a valid error number + * or argument buf is NULL. + * + * \brief Obtain error message from error number. + */ +int cynara_strerror(int errnum, char *buf, size_t buflen); + +#ifdef __cplusplus +} +#endif + +#endif /* CYNARA_ERROR_H */ diff --git a/include/cynara/cynara-limits.h b/include/cynara/cynara-limits.h new file mode 100644 index 0000000..f4e98c1 --- /dev/null +++ b/include/cynara/cynara-limits.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015-2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/include/cynara-limits.h + * @author Rafal Krypa <r.krypa@samsung.com> + * @version 1.0 + * @brief This file contains limits for parameter sizes of Cynara APIs. + */ + +#ifndef CYNARA_LIMITS_H +#define CYNARA_LIMITS_H + +/*! \brief Maximum length of text identifier accepted by Cynara */ +#define CYNARA_MAX_ID_LENGTH 4096 + +/*! \brief Maximum size of vector accepted by Cynara */ +#define CYNARA_MAX_VECTOR_SIZE 4096 + +/*! \brief Maximum size of monitor entries buffer accepted by Cynara */ +#define CYNARA_MAX_MONITOR_BUFFER_SIZE 65535 + +#endif /* CYNARA_LIMITS_H */ diff --git a/include/cynara/cynara-monitor.h b/include/cynara/cynara-monitor.h new file mode 100644 index 0000000..ce7b8d0 --- /dev/null +++ b/include/cynara/cynara-monitor.h @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/include/cynara-monitor.h + * @author Aleksander Zdyb <a.zdyb@samsung.com> + * @version 1.0 + * @brief This file contains client APIs of Cynara monitoring. + */ + +#ifndef CYNARA_MONITOR_H +#define CYNARA_MONITOR_H + +#include <stddef.h> +#include <time.h> + +#include <cynara/cynara-error.h> +#include <cynara/cynara-limits.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct cynara_monitor_configuration cynara_monitor_configuration; +typedef struct cynara_monitor cynara_monitor; +typedef struct cynara_monitor_entry cynara_monitor_entry; + +/** + * \par Description: + * Initializes cynara_monitor_configuration. Creates structure used in following configuration + * API calls. + * + * \par Purpose: + * For configuration parameter to be used in cynara_monitor_initialize() function, this API must be + * called before any other Cynara monitor configuration API function. + * It will create cynara_monitor_configuration structure, an optional parameter + * of Cynara monitor initialization. + * + * \par Typical use case: + * Once before setting parameters of Cynara monitor configuration and passing to + * cynara_monitor_initialize(). + * + * \par Method of function operation: + * This API initializes inner library structures and in case of success returns pointer + * to created cynara_monitor_configuration structure. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safety: + * \parblock + * This function is NOT thread-safe. If this function is called simultaneously with other functions + * from described API in different threads, they must be put into protected critical section. + * + * \par Important notes: + * Structure cynara_monitor_configuration created by cynara_monitor_configuration_create() call + * should be released with cynara_monitor_configuration_destroy(). + * Structure cynara_monitor_configuration should be destroyed after passing it to + * cynara_monitor_initialize(). + * \endparblock + * + * \param[out] pp_conf Placeholder for created cynara_monitor_configuration structure. + * + * \return CYNARA_API_SUCCESS on success + * or negative error code on error. + */ +int cynara_monitor_configuration_create(cynara_monitor_configuration **pp_conf); + +/** + * \par Description: + * Releases cynara_monitor_configuration structure created + * with cynara_monitor_configuration_create(). + * + * \par Purpose: + * This API should be used to clean up after usage of cynara_monitor_configuration. + * + * \par Typical use case: + * Once cynara_monitor_configuration is not needed. + * + * \par Method of function operation: + * This API releases inner library structure and destroys cynara_monitor_configuration structure. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If this function is called simultaneously with other functions + * from described API in different threads, they must be put into protected critical section. + * + * \param[in] p_conf cynara_monitor_configuration structure. If NULL, the call has no effect. + */ +void cynara_monitor_configuration_destroy(cynara_monitor_configuration *p_conf); + +/** + * \par Description: + * Set monitor entries buffer size. + * + * \par Purpose: + * This API is used to change default number of monitor entries stored on server side + * before returning them to client of libcynara-monitor. Buffer size cannot exceed value + * of CYNARA_MAX_MONITOR_BUFFER_SIZE, otherwise CYNARA_API_INPUT_PARAM will be returned. + * + * \par Typical use case: + * Once after cynara_configuration is created with cynara_monitor_configuration_create() + * and before passing configuration to cynara_monitor_configuration(). + * + * \par Method of function operation: + * This API initializes buffer with given capacity. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. If this function is called simultaneously with other functions + * from described API in different threads, they must be put into protected critical section. + * + * \par Important notes: + * After passing cynara_configuration to cynara_monitor_initialize() calling this API will have + * no effect. + * + * If buffer size is not set with cynara_monitor_configuration_set_buffer_size(), + * a default size will be taken. + * \endparblock + * + * \param[in] p_conf cynara_monitor_configuration structure pointer. + * \param[in] buffer_size buffer size to be set. + * + * \return CYNARA_API_SUCCESS on success + * or negative error code on error. + */ +int cynara_monitor_configuration_set_buffer_size(cynara_monitor_configuration *p_conf, + size_t buffer_size); + +/** + * \par Description: + * Initializes cynara-monitor library with given configuration. + * Creates structure used in following API calls. + * + * \par Purpose: + * This API must be used by prior calling cynara_monitor_entries_get() function. + * + * \par Typical use case: + * Once before a service can call cynara_monitor_entries_get(). + * + * \par Method of function operation: + * This API initializes inner library structures and in case of success + * creates cynara_monitor structure. + * + * \par Sync (or) Async: + * This is a Synchronous API. + * + * \par Thread-safeness: + * This function is NOT thread-safe. If this function is called simultaneously with other functions + * from described API in different threads, they must be put into protected critical section. + * + * \par Important notes: + * Structure cynara_monitor created by cynara_monitor_initialize() call should be released + * with cynara_monitor_finish(). + * + * \param[out] pp_cynara_monitor Placeholder for created cynara_monitor structure. + * \param[in] p_conf Configuration for cynara-monitor library. NULL for default parameters. + * + * \return CYNARA_API_SUCCESS on success, or error code on error. + */ +int cynara_monitor_initialize(cynara_monitor **pp_cynara_monitor, + const cynara_monitor_configuration *p_conf); + +/** + * \par Description: + * Releases cynara-monitor library and destroys structure created with cynara_monitor_initialize(). + * + * \par Purpose: + * This API should be used to clean up after the usage of cynara-monitor library. + * + * \par Typical use case: + * Once after last call to cynara_monitor_entries_get(). + * + * \par Method of function operation: + * This API releases inner library structures and destroys cynara_monitor structure. + * + * \par Sync (or) Async: + * This is a Synchronous API. + * + * \par Thread-safeness: + * This function is NOT thread-safe. If this function is called simultaneously with other functions + * from described API in different threads, they must be put into protected critical section. + * + * \par Important notes: + * No other call to libcynara-monitor should be made after call to cynara_monitor_finish(). + * This function cannot be called, if cynara_monitor_entries_get() haven't returned yet. + * + * \param[in] p_cynara_monitor Cynara monitor structure. + * + * \return CYNARA_API_SUCCESS on success, or error code on error. + */ +int cynara_monitor_finish(cynara_monitor *p_cynara_monitor); + +/** + * \brief Returns monitor entries. + * + * \par Description: + * + * Returns all available monitor entries right after the size of buffer reaches set limit. + * + * \par Purpose: + * This API should be used to get available monitor entries. + * + * \par Typical use case: + * Gathering information about possible accesses checked in Cynara by individual services. + * + * \par Method of function operation: + * \parblock + * Each call to cynara_check() made by individual services are logged. The logs can be obtained + * by privileged clients with usage of libcynara-monitor (this API). + * + * In case of successful call CYNARA_API_SUCCESS is returned and *monitor_entries points + * to newly created array of pointers to cynara_monitor_entry. It is responsibility + * of the caller to release entries with cynara_monitor_entries_free(). + * + * The function blocks until the size of buffer reaches set limit or cynara_monitor_entries_flush() + * is called from another thread. + * \endparblock + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safeness: + * This function is thread-safe. + * + * \par Important notes: + * Although this function is thread-safe, no multiple calls are allowed. + * + * \param[in] p_cynara_monitor cynara_monitor structure. + * \param[out] monitor_entries placeholder for NULL terminated array of pointers to + * monitor_entries structures. + * + * \return CYNARA_API_SUCCESS on success, or error code otherwise. + */ +int cynara_monitor_entries_get(cynara_monitor *p_cynara_monitor, + cynara_monitor_entry ***monitor_entries); + +/** + * \brief Makes cynara_monitor_entries_get() return immediately. + * + * \par Description: + * + * When called from another thread, makes cynara_monitor_entries_get() return immediately. + * + * \par Purpose: + * This API should be used to make cynara_monitor_entries_get() return immediately. + * + * \par Typical use case: + * User of libcynara-monitor wants to immediately get get monitor entries and/or make + * cynara_monitor_entries_get() return without waiting for full batch of entries. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safeness: + * This function is thread-safe. + * + * \param[in] p_cynara_monitor cynara_monitor structure. + * + * \return CYNARA_API_SUCCESS on success, or error code otherwise. + */ +int cynara_monitor_entries_flush(cynara_monitor *p_cynara_monitor); + +/** + * \brief Releases monitor entries. + * + * \par Description: + * + * Releases monitor entries obtained with cynara_monitor_entries_get(). + * + * \par Purpose: + * This API should be used to release array of cynara_monitor_entry obtained + * with cynara_monitor_entries_get. + * + * \par Typical use case: + * When user obtained all needed data from the entries and won't be accessing the + * memory any more. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safeness: + * This function is thread-safe. + * + * \param[in] monitor_entries NULL terminated array of cynara_monitor_entry to be released. + */ +void cynara_monitor_entries_free(cynara_monitor_entry **monitor_entries); + +/** + * \brief Gets client from monitor_entry + * + * \par Description: + * Gives access to client field of monitor_entry given as a parameter. + * Please refer to cynara_check() in cynara-client.h for more information on client field. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safeness: + * This function is thread-safe. + * + * \par Important notes: + * The returned pointer is valid as long as given monitor_entry is. + * + * \param[in] monitor_entry cynara_monitor_entry to be accessed. + * + * \return valid const char * pointer or NULL in case of error. + */ +const char *cynara_monitor_entry_get_client(const cynara_monitor_entry *monitor_entry); + +/** + * \brief Gets user from monitor_entry + * + * \par Description: + * Gives access to user field of monitor_entry given as a parameter. + * Please refer to cynara_check() in cynara-client.h for more information on user field. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safeness: + * This function is thread-safe. + * + * \par Important notes: + * The returned pointer is valid as long as given monitor_entry is. + * + * \param[in] monitor_entry cynara_monitor_entry to be accessed. + * + * \return valid const char * pointer or NULL in case of error. + */ +const char *cynara_monitor_entry_get_user(const cynara_monitor_entry *monitor_entry); + +/** + * \brief Gets privilege from monitor_entry + * + * \par Description: + * Gives access to privilege field of monitor_entry given as a parameter. + * Please refer to cynara_check() in cynara-client.h for more information on privilege field. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safeness: + * This function is thread-safe. + * + * \par Important notes: + * The returned pointer is valid as long as given monitor_entry is. + * + * \param[in] monitor_entry cynara_monitor_entry to be accessed. + * + * \return valid const char * pointer or NULL in case of error. + */ +const char *cynara_monitor_entry_get_privilege(const cynara_monitor_entry *monitor_entry); + +/** + * \brief Gets result from monitor_entry + * + * \par Description: + * Gives access to result field of monitor_entry given as a parameter. + * In case of successful get, the result is one of CYNARA_API_ACCESS_ALLOWED + * or CYNARA_API_ACCESS_DENIED. In case of error a negative code error is returned. + * Please refer to cynara-error.h for more information. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safeness: + * This function is thread-safe. + * + * \param[in] monitor_entry cynara_monitor_entry to be accessed. + * + * \return CYNARA_API_ACCESS_ALLOWED on access allowed, CYNARA_API_ACCESS_DENIED on access denial + * or other error code on error. + */ +int cynara_monitor_entry_get_result(const cynara_monitor_entry *monitor_entry); + +/** + * \brief Gets timestamp from monitor_entry + * + * \par Description: + * Gives access to timestamp field of monitor_entry given as a parameter. + * This is the time of cynara_check() being invoked. Please refer to cynara-client.h + * for more information about cynara_check(). + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safeness: + * This function is thread-safe. + * + * \par Important notes: + * The returned pointer is valid as long as given monitor_entry is. + * + * \param[in] monitor_entry cynara_monitor_entry to be accessed. + * + * \return valid const istruct timspec * pointer or NULL in case of error. + */ +const struct timespec *cynara_monitor_entry_get_timestamp( + const cynara_monitor_entry *monitor_entry); + +#ifdef __cplusplus +} +#endif + +#endif /* CYNARA_MONITOR_H */ diff --git a/include/cynara/cynara-plugin.h b/include/cynara/cynara-plugin.h new file mode 100644 index 0000000..747966f --- /dev/null +++ b/include/cynara/cynara-plugin.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ +/** + * @file src/include/cynara-plugin.h + * @author Zofia Abramowska <z.abramowska@samsung.com> + * @author Oskar Switalski <o.switalski@samsung.com> + * @version 1.0 + * @brief This file defines cynara service side of external plugin interface - + * ServicePluginInterface + */ + +#ifndef CYNARA_PLUGIN_H_ +#define CYNARA_PLUGIN_H_ + +#include <memory> +#include <string> +#include <vector> + +#include <log/log.h> +#include <plugin/ExternalPluginInterface.h> +#include <types/PolicyResult.h> +#include <types/PolicyType.h> + +namespace Cynara { + +//These typedefs will be defined in external headers +typedef std::string PluginData; +typedef std::string AgentType; + +class ServicePluginInterface; +typedef std::shared_ptr<ServicePluginInterface> ServicePluginInterfacePtr; + +/** + * A class defining external plugins interface. + * These plugins work inside of cynara and either can produce + * response through check instantly or require communication + * with given type of agent. Agent must be registered through + * cynara-agent API. + * + * Plugin implementing ServicePluginInterface must implement ExternalPluginInterface. + * Creation/destruction functions with signatures compatible to Cynara::create_t and + * Cynara::destroy_t must be provided as factories of ServicePluginInterface. + */ + +class ServicePluginInterface : public ExternalPluginInterface { +public: + /** + * Enum indicating status of calling plugin method. + */ + enum class PluginStatus { + SUCCESS, /**< update() finished successfully */ + ANSWER_READY, /**< check() returns answer immediately through argument */ + ANSWER_NOTREADY, /**< check() cannot return answer immediately, + communication with agent is required */ + ERROR /**< either check() or update() fails */ + }; + + /** + * Asks plugin, what kind of permission does client, user and privilege has. + * + * @param[in] client + * @param[in] user + * @param[in] privilege + * @param[out] result Immediate response (if available) + * @param[out] requiredAgent When ANSWER_NOTREADY, required AgentType to communicate with + * @param[out] pluginData Additional data, that will be passed to agent + * @return PluginStatus In case of success - either ANSWER_READY or ANSWER_NOTREADY, + * in case of error - ERROR + */ + virtual PluginStatus check(const std::string &client, const std::string &user, + const std::string &privilege, PolicyResult &result, + AgentType &requiredAgent, PluginData &pluginData) noexcept = 0; + + /** + * Updates response returned by agent + * @param[in] client + * @param[in] user + * @param[in] privilege + * @param[in] agentData Additional data, passed from agent + * @param[out] result Response interpreted from agent + * @return PluginStatus In case of success - SUCCESS, in case of error - ERROR + */ + virtual PluginStatus update(const std::string &client, const std::string &user, + const std::string &privilege, const PluginData &agentData, + PolicyResult &result) noexcept = 0; + + virtual ~ServicePluginInterface() {}; + +}; + +} // namespace Cynara + +#endif /* CYNARA_PLUGIN_H_ */ diff --git a/include/cynara/cynara-policy-types.h b/include/cynara/cynara-policy-types.h new file mode 100644 index 0000000..bd74b5a --- /dev/null +++ b/include/cynara/cynara-policy-types.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * \file src/include/cynara-policy-types.h + * \author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com> + * \version 1.0 + * \brief This file contains policy types / operations definitions. + */ + +#ifndef CYNARA_POLICY_TYPES_H +#define CYNARA_POLICY_TYPES_H + +/** + * \name Operation Codes + * Operation codes that define action type to be taken + * @{ + */ + +/*! \brief a policy or bucket should be removed */ +#define CYNARA_ADMIN_DELETE -1 + +/*! \brief set policy result or bucket's default policy to DENY */ +#define CYNARA_ADMIN_DENY 0 + +/*! \brief set bucket's default policy to NONE */ +#define CYNARA_ADMIN_NONE 1 + +/*! \brief set policy to point into another bucket */ +#define CYNARA_ADMIN_BUCKET 0xFFFE + +/*! \brief set policy result or bucket's default policy to ALLOW */ +#define CYNARA_ADMIN_ALLOW 0xFFFF +/** @}*/ + +#endif /* CYNARA_POLICY_TYPES_H */ diff --git a/include/cynara/cynara-session.h b/include/cynara/cynara-session.h new file mode 100644 index 0000000..1c76bce --- /dev/null +++ b/include/cynara/cynara-session.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/include/cynara-session.h + * @author Aleksander Zdyb <a.zdyb@samsung.com> + * @author Radoslaw Bartosiak <r.bartosiak@samsung.com.pl> + * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com> + * @author Oskar Switalski <o.switalski@samsung.com> + * @version 1.0 + * @brief This file contains Cynara session helper APIs. + * @example cynara-session.example + */ + +#ifndef CYNARA_SESSION_H +#define CYNARA_SESSION_H + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \par Description: + * Creates a client session string based on pid and time of creation of client process + * + * \par Purpose: + * This function can be used to create session string identifier used in cynara_check() + * and cynara_async_create_request() functions defined in client libraries. + * + * \par Typical use case: + * The function is called before the call of cynara_check() or cynara_async_create_request() + * function. Returned string is used as client_session param in cynara_check() or + * cynara_async_create_request() function. String is released with free() function. + * + * \par Method of function operation: + * The function generates client session based on the pid and start time of the client process. + * Time is acquired from /proc/PID directory. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread safety: + * This function is thread-safe. + * + * \par Important notes: + * Memory for returned string is obtained with malloc(), and should be freed with free(). + * + * \param[in] client_pid client application process identifier (PID). + * + * \return session string on success + * \return NULL on error + */ +char *cynara_session_from_pid(pid_t client_pid); + +#ifdef __cplusplus +} +#endif + +#endif /* CYNARA_SESSION_H */ diff --git a/pkgconfig/CMakeLists.txt b/pkgconfig/CMakeLists.txt new file mode 100644 index 0000000..f32ad70 --- /dev/null +++ b/pkgconfig/CMakeLists.txt @@ -0,0 +1,41 @@ +########################################################################### +# 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. +########################################################################### + +configure_file(cynara.pc.in cynara.pc @ONLY) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cynara.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + +foreach(item cynara-admin.pc + cynara-agent.pc + cynara-client-async.pc + cynara-client.pc + cynara-commons.pc + cynara-creds-commons.pc + cynara-creds-dbus.pc + cynara-creds-gdbus.pc + cynara-creds-self.pc + cynara-creds-socket.pc + cynara-monitor.pc + cynara-plugin.pc + cynara-session.pc) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/link.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + RENAME ${item}) +endforeach(item) + diff --git a/pkgconfig/cynara.pc.in b/pkgconfig/cynara.pc.in new file mode 100644 index 0000000..6a39979 --- /dev/null +++ b/pkgconfig/cynara.pc.in @@ -0,0 +1,5 @@ +Name: cynara +Description: cynara package +Version: @CYNARA_VERSION@ +Libs: -L@CMAKE_INSTALL_FULL_LIBDIR@ -lcynara +Cflags: -I@CMAKE_INSTALL_FULL_INCLUDEDIR@ -I@CMAKE_INSTALL_FULL_INCLUDEDIR@/cynara diff --git a/pkgconfig/link.pc b/pkgconfig/link.pc new file mode 120000 index 0000000..17d4d5d --- /dev/null +++ b/pkgconfig/link.pc @@ -0,0 +1 @@ +cynara.pc
\ No newline at end of file @@ -0,0 +1,80 @@ +set System * * * 1 +set User::App::aftest * * urn:AGL:permission::public:hidden 1 +set User::App::aftest * * urn:AGL:permission::public:no-htdocs 1 +set User::App::agl-identity-service * * urn:AGL:permission::public:hidden 1 +set User::App::agl-identity-service * * urn:AGL:permission::system:run-by-default 1 +set User::App::agl-service-audio-4a * * http://tizen.org/privilege/internal/dbus 1 +set User::App::agl-service-audio-4a * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-audio-4a * * urn:AGL:permission::system:run-by-default 1 +set User::App::agl-service-audio-4a * * urn:AGL:permission:UNICENS:public:monitor 1 +set User::App::agl-service-bluetooth-pbap * * http://tizen.org/privilege/internal/dbus 1 +set User::App::agl-service-bluetooth-pbap * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-bluetooth-pbap * * urn:AGL:permission::public:no-htdocs 1 +set User::App::agl-service-bluetooth * * http://tizen.org/privilege/internal/dbus 1 +set User::App::agl-service-bluetooth * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-bluetooth * * urn:AGL:permission::system:run-by-default 1 +set User::App::agl-service-geoclue * * http://tizen.org/privilege/internal/dbus 1 +set User::App::agl-service-geoclue * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-geoclue * * urn:AGL:permission::public:no-htdocs 1 +set User::App::agl-service-geoclue * * urn:AGL:permission::system:run-by-default 1 +set User::App::agl-service-geofence * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-geofence * * urn:AGL:permission::public:no-htdocs 1 +set User::App::agl-service-gps * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-gps * * urn:AGL:permission::public:no-htdocs 1 +set User::App::agl-service-iiodevices * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-iiodevices * * urn:AGL:permission::public:no-htdocs 1 +set User::App::agl-service-mediaplayer * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-mediaplayer * * urn:AGL:permission::public:no-htdocs 1 +set User::App::agl-service-mediascanner * * http://tizen.org/privilege/internal/dbus 1 +set User::App::agl-service-mediascanner * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-mediascanner * * urn:AGL:permission::public:no-htdocs 1 +set User::App::agl-service-network * * http://tizen.org/privilege/internal/dbus 1 +set User::App::agl-service-network * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-network * * urn:AGL:permission::system:run-by-default 1 +set User::App::agl-service-nfc * * http://tizen.org/privilege/internal/dbus 1 +set User::App::agl-service-nfc * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-nfc * * urn:AGL:permission::public:no-htdocs 1 +set User::App::agl-service-nfc * * urn:AGL:permission::system:run-by-default 1 +set User::App::agl-service-radio * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-radio * * urn:AGL:permission::public:no-htdocs 1 +set User::App::agl-service-radio * * urn:AGL:permission:audio:public:audiostream 1 +set User::App::agl-service-steering-wheel * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-unicens * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-unicens * * urn:AGL:permission::system:run-by-default 1 +set User::App::agl-service-weather * * urn:AGL:permission::public:hidden 1 +set User::App::agl-service-weather * * urn:AGL:permission::public:no-htdocs 1 +set User::App::dashboard * * urn:AGL:permission::public:no-htdocs 1 +set User::App::homescreen-service-2017 * * http://tizen.org/privilege/internal/dbus 1 +set User::App::homescreen-service-2017 * * urn:AGL:permission::public:hidden 1 +set User::App::homescreen-service-2017 * * urn:AGL:permission::system:run-by-default 1 +set User::App::homescreen * * http://tizen.org/privilege/internal/dbus 1 +set User::App::homescreen * * urn:AGL:permission::public:no-htdocs 1 +set User::App::homescreen * * urn:AGL:permission::system:run-by-default 1 +set User::App::hvac * * urn:AGL:permission::public:no-htdocs 1 +set User::App::launcher * * http://tizen.org/privilege/internal/dbus 1 +set User::App::launcher * * urn:AGL:permission::public:no-htdocs 1 +set User::App::launcher * * urn:AGL:permission::system:run-by-default 1 +set User::App::low-can-service * * urn:AGL:permission::public:hidden 1 +set User::App::low-can-service * * urn:AGL:permission::public:no-htdocs 1 +set User::App::low-can-service * * urn:AGL:permission::system:run-by-default 1 +set User::App::mediaplayer * * urn:AGL:permission::public:no-htdocs 1 +set User::App::mixer * * urn:AGL:permission::public:4a-audio-mixer 1 +set User::App::mixer * * urn:AGL:permission::public:no-htdocs 1 +set User::App::naviapi-binding-service * * http://tizen.org/privilege/internal/dbus 1 +set User::App::naviapi-binding-service * * urn:AGL:permission::public:hidden 1 +set User::App::navigation * * http://tizen.org/privilege/internal/dbus 1 +set User::App::navigation * * urn:AGL:permission::public:no-htdocs 1 +set User::App::persistence-binding * * urn:AGL:permission::public:hidden 1 +set User::App::persistence-binding * * urn:AGL:permission::system:run-by-default 1 +set User::App::phone * * http://tizen.org/privilege/internal/dbus 1 +set User::App::phone * * urn:AGL:permission::public:no-htdocs 1 +set User::App::poi * * http://tizen.org/privilege/internal/dbus 1 +set User::App::poi * * urn:AGL:permission::public:no-htdocs 1 +set User::App::radio * * urn:AGL:permission::public:no-htdocs 1 +set User::App::settings * * urn:AGL:permission::public:no-htdocs 1 +set User::App::signal-composer * * urn:AGL:permission::public:hidden 1 +set User::App::signal-composer * * urn:AGL:permission::public:no-htdocs 1 +set User::App::windowmanager-service-2017 * * urn:AGL:permission::public:hidden 1 +set User::App::windowmanager-service-2017 * * urn:AGL:permission::system:run-by-default 1 +set User * * * 1 + diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..d551f05 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,5 @@ +cynarad +test-cynara +cynara.names +cynara.rules +.*.sw* diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..fa3724c --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,95 @@ +########################################################################### +# 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. +########################################################################### + +set(SERVER_SOURCES + cyn.c + db.c + fbuf.c + prot.c + queue.c + rcyn-protocol.c + rcyn-server.c + socket.c +) + +set(LIB_SOURCES + cache.c + lib-compat.c + prot.c + rcyn-client.c + rcyn-protocol.c + socket.c +) + +########################################### +# build and install cynarad +########################################### +add_executable(cynarad ${SERVER_SOURCES}) +install(TARGETS cynarad + RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) + +########################################### +# build and install libcynara +########################################### +ADD_LIBRARY(cynara SHARED ${LIB_SOURCES}) +SET_TARGET_PROPERTIES(cynara PROPERTIES + VERSION ${CYNARA_VERSION} + SOVERSION ${CYNARA_SOVERSION}) +TARGET_LINK_LIBRARIES(cynara + -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/export.map + -Wl,--as-needed + -Wl,--gc-sections +) +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 +########################################### +ADD_EXECUTABLE(test-cynara test-lib-compat.c) +TARGET_LINK_LIBRARIES(test-cynara cynara) +INSTALL(TARGETS test-cynara + RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) + +########################################### +# alterations for SYSTEMD and socket specs +########################################### + +if(SYSTEMD) + target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_CHECK_SOCKET_SPEC="sd:check") + target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_ADMIN_SOCKET_SPEC="sd:admin") + target_compile_definitions(cynarad PRIVATE WITH_SYSTEMD_ACTIVATION) + target_link_libraries(cynarad ${libsystemd_LDFLAGS} ${libsystemd_LINK_LIBRARIES}) + target_include_directories(cynarad PRIVATE ${libsystemd_INCLUDE_DIRS}) + target_compile_options(cynarad PRIVATE ${libsystemd_CFLAGS}) +else() + if(CHECK_SOCKET_SPEC) + target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_CHECK_SOCKET_SPEC="${CHECK_SOCKET_SPEC}") + endif() + if(ADMIN_SOCKET_SPEC) + target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_ADMIN_SOCKET_SPEC="${ADMIN_SOCKET_SPEC}") + endif() +endif() + +if(CHECK_SOCKET_SPEC) + target_compile_definitions(cynara PRIVATE RCYN_DEFAULT_CHECK_SOCKET_SPEC="${CHECK_SOCKET_SPEC}") +endif() +if(ADMIN_SOCKET_SPEC) + target_compile_definitions(cynara PRIVATE RCYN_DEFAULT_ADMIN_SOCKET_SPEC="${ADMIN_SOCKET_SPEC}") +endif() + diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..f5969c3 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,26 @@ +.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 new file mode 100644 index 0000000..c2db00d --- /dev/null +++ b/src/cache.c @@ -0,0 +1,163 @@ + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include "cache.h" + +struct cache +{ + uint32_t begin; + uint32_t used; + uint32_t count; + char content[1]; +}; + +static +uint32_t +lenat( + cache_t *cache, + uint32_t pos +) { + uint32_t p, n; + char c; + + p = pos + 1; + if (p >= cache->count) + p -= cache->count; + n = 4; + while (n--) { + do { + c = cache->content[p++]; + if (p >= cache->count) + p -= cache->count; + } while(c); + } + return (p > pos ? p : (p + cache->count)) - pos; +} + +static +void +drop_one( + cache_t *cache +) { + uint32_t l = lenat(cache, cache->begin); + cache->used -= l; + cache->begin += l; + if (cache->begin > cache->count) + cache->begin -= cache->count; +} + +static +void +addc( + cache_t *cache, + char c +) { + uint32_t pos; + if (cache->used == cache->count) + drop_one(cache); + pos = cache->begin + cache->used++; + if (pos > cache->count) + pos -= cache->count; + cache->content[pos < cache->count ? pos : pos - cache->count] = c; +} + +static +void +adds( + cache_t *cache, + const char *s +) { + do { addc(cache, *s); } while(*s++); +} + +int +cache_put( + cache_t *cache, + const char *client, + const char *session, + const char *user, + const char *permission, + int value +) { + if (cache == NULL + || strlen(client) + strlen(session) + + strlen(user) + strlen(permission) + + 5 > cache->count) + return -EINVAL; + + addc(cache, (char)value); + adds(cache, client); + adds(cache, session); + adds(cache, user); + adds(cache, permission); + return 0; +} + +int +cache_search( + cache_t *cache, + const char *client, + const char *session, + const char *user, + const char *permission +) { + return -ENOENT; +} + +void +cache_clear( + cache_t *cache +) { + if (cache) { + cache->used = 0; + cache->begin = 0; + } +} + +int +cache_resize( + cache_t **cache, + uint32_t newsize +) { + cache_t *c = *cache, *nc; + + while (c && c->used > newsize) + drop_one(c); + + nc = malloc(newsize - 1 + sizeof *c); + if (nc == NULL) + return -ENOMEM; + + nc->begin = 0; + nc->count = newsize; + if (!c || c->used == 0) + nc->used = 0; + else { + if (c->begin + c->used <= c->count) + memcpy(&nc->content[0], &c->content[c->begin], c->used); + else { + memcpy(&nc->content[0], &c->content[c->begin], c->count - c->begin); + memcpy(&nc->content[c->count - c->begin], &c->content[0], c->used + c->begin - c->count); + } + + nc->used = c->used; + } + *cache = nc; + free(c); + return 0; +} + +int +cache_create( + cache_t **cache, + uint32_t size +) { + *cache = NULL; + return cache_resize(cache, size); +} + + + diff --git a/src/cache.h b/src/cache.h new file mode 100644 index 0000000..2f3a9a1 --- /dev/null +++ b/src/cache.h @@ -0,0 +1,48 @@ +#pragma once + +struct cache; +typedef struct cache cache_t; + +extern +int +cache_search( + cache_t *cache, + const char *client, + const char *session, + const char *user, + const char *permission +); + +extern +int +cache_put( + cache_t *cache, + const char *client, + const char *session, + const char *user, + const char *permission, + int value +); + +extern +void +cache_clear( + cache_t *cache +); + +extern +int +cache_resize( + cache_t **cache, + uint32_t newsize +); + +extern +int +cache_create( + cache_t **cache, + uint32_t size +); + + + diff --git a/src/cyn.c b/src/cyn.c new file mode 100644 index 0000000..4eb6d9e --- /dev/null +++ b/src/cyn.c @@ -0,0 +1,267 @@ +#define _GNU_SOURCE + + +#include <assert.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> + + +#include "db.h" +#include "queue.h" +#include "cyn.h" + +struct callback +{ + struct callback *next; + void (*callback)(void *closure); + void *closure; +}; + +/** locking critical section */ +static const void *lock; +static struct callback *awaiters; +static struct callback *observers; + +static +int +delcb( + void (*callback)(void *closure), + void *closure, + struct callback **head +) { + struct callback *c; + + while((c = *head)) { + if (c->callback == callback && c->closure == closure) { + *head = c->next; + free(c); + return 1; + } + head = &c->next; + } + return 0; +} + +static +int +addcb( + void (*callback)(void *closure), + void *closure, + struct callback **head +) { + struct callback *c; + + c = malloc(sizeof *c); + if (c == NULL) + return -(errno = ENOMEM); + c->callback = callback; + c->closure = closure; + c->next = *head; + *head = c; + return 0; +} + +static +int +changed( +) { + int rc = db_sync(); + struct callback *c; + + for (c = observers; c ; c = c->next) + c->callback(c->closure); + return rc; +} + +int +cyn_init( +) { + /* TODO: paths? */ + int rc = db_open("/var/lib/cynara/cynara.names", "/var/lib/cynara/cynara.rules"); + if (rc == 0 && db_is_empty()) { + /* TODO: init? */ + rc = db_set("System", "*", "*", "*", 1); + db_sync(); + } + return rc; +} + +/** enter critical recoverable section */ +int +cyn_enter( + const void *magic +) { + if (lock) + return -EBUSY; + lock = magic; + return 0; +} + +int +cyn_enter_async( + void (*enter_cb)(void *closure), + void *closure +) { + if (lock) + return addcb(enter_cb, closure, &awaiters); + + lock = closure; + enter_cb(closure); + return 0; +} + +int +cyn_enter_async_cancel( + void (*enter_cb)(void *closure), + void *closure +) { + return delcb(enter_cb, closure, &awaiters); +} + +int +cyn_on_change_add( + void (*on_change_cb)(void *closure), + void *closure +) { + return addcb(on_change_cb, closure, &observers); +} + + +int +cyn_on_change_remove( + void (*on_change_cb)(void *closure), + void *closure +) { + return delcb(on_change_cb, closure, &observers); +} + +/** leave critical recoverable section */ +int +cyn_leave( + const void *magic, + bool commit +) { + int rc; + struct callback *e, **p; + + if (!magic) + return -EINVAL; + if (!lock) + return -EALREADY; + if (lock != magic) + return -EPERM; + + lock = &lock; + if (commit) + rc = queue_play() ?: changed(); + else + rc = 0; + queue_clear(); + + e = awaiters; + if (!e) + lock = 0; + else { + p = &awaiters; + while(e->next) { + p = &e->next; + e = *p; + } + *p = NULL; + lock = e->closure; + e->callback(e->closure); + free(e); + } + + return rc; +} + +int +cyn_set( + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value +) { + if (lock && lock != &lock) + return queue_set(client, session, user, permission, value); + + return db_set(client, session, user, permission, value) ?: changed(); +} + +int +cyn_drop( + const char *client, + const char *session, + const char *user, + const char *permission +) { + if (lock && lock != &lock) + return queue_drop(client, session, user, permission); + + return db_drop(client, session, user, permission) ?: changed(); +} + +int +cyn_test( + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t *value +) { + int rc; + + rc = db_test(client, session, user, permission, value); + if (rc <= 0) + *value = DEFAULT; + else + rc = 0; + return rc; +} + +void +cyn_list( + void *closure, + void (*callback)( + void *closure, + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value), + const char *client, + const char *session, + const char *user, + const char *permission +) { + db_for_all(closure, callback, client, session, user, permission); +} + +int +cyn_check_async( + void (*check_cb)(void *closure, uint32_t value), + void *closure, + const char *client, + const char *session, + const char *user, + const char *permission +) { + uint32_t value; + + cyn_test(client, session, user, permission, &value); + if (value == ALLOW || value == DENY) { + check_cb(closure, value); + return 0; + } + + /* TODO: try to resolve AGENT?? */ + + check_cb(closure, value); + return 0; +} + diff --git a/src/cyn.h b/src/cyn.h new file mode 100644 index 0000000..0a061c2 --- /dev/null +++ b/src/cyn.h @@ -0,0 +1,113 @@ + +#pragma once + +#define DENY 0 +#define ALLOW 1 +#define ASK 2 +#define DEFAULT DENY + +extern +int +cyn_init( +); + +/** enter critical recoverable section */ +extern +int +cyn_enter( + const void *magic +); + +/** leave critical recoverable section */ +extern +int +cyn_leave( + const void *magic, + bool commit +); + +extern +int +cyn_set( + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value +); + +extern +int +cyn_drop( + const char *client, + const char *session, + const char *user, + const char *permission +); + +extern +int +cyn_test( + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t *value +); + +extern +void +cyn_list( + void *closure, + void (*callback)( + void *closure, + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value), + const char *client, + const char *session, + const char *user, + const char *permission +); + +extern +int +cyn_check_async( + void (*check_cb)(void *closure, uint32_t value), + void *closure, + const char *client, + const char *session, + const char *user, + const char *permission +); + +extern +int +cyn_enter_async( + void (*enter_cb)(void *closure), + void *closure +); + +extern +int +cyn_enter_async_cancel( + void (*enter_cb)(void *closure), + void *closure +); + +extern +int +cyn_on_change_add( + void (*on_change_cb)(void *closure), + void *closure +); + +extern +int +cyn_on_change_remove( + void (*on_change_cb)(void *closure), + void *closure +); + diff --git a/src/db.c b/src/db.c new file mode 100644 index 0000000..c7efdca --- /dev/null +++ b/src/db.c @@ -0,0 +1,661 @@ + +#define _GNU_SOURCE + + +#include <assert.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdalign.h> +#include <string.h> +#include <errno.h> +#include <syslog.h> + +#include "fbuf.h" +#include "db.h" + +#define NOIDX 0 + +#define ANYIDX 40 +#define ANYSTR "#" + +#define WIDEIDX 42 +#define WIDESTR "*" + +/** + * A rule is a set of 4 integers + */ +struct rule +{ + uint32_t client, user, permission, value; +}; +typedef struct rule rule_t; + +/** + * Sessions + */ +struct session +{ + struct session *next, *prev; + rule_t *rules; + const char *name; + uint32_t count; +}; +typedef struct session session_t; + +/* + * The cynara-agl database is made of 2 memory mapped files: + * - names: the zero terminated names + * - rules: the rules based on name indexes as 32bits indexes + * These files are normally in /var/lib/cynara + */ + +/** the file for the names */ +static fbuf_t fnames; + +/** the file for the rules */ +static fbuf_t frules; + +/** identification of names version 1 (uuidgen --sha1 -n @url -N urn:AGL:cynara:db:names:1) */ +static const char uuid_names_v1[] = "e9481f9e-b2f4-5716-90cf-c286d98d1868\n--\n"; + +/** identification of rules version 1 (uuidgen --sha1 -n @url -N urn:AGL:cynara:db:rules:1) */ +static const char uuid_rules_v1[] = "8f7a5b21-48b1-57af-96c9-d5d7192be370\n--\n"; + +/** length of the identification */ +static const int uuidlen = 40; + +/** count of names */ +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 +}; + +/** return the name of 'index' */ +static +const char* +name_at( + uint32_t index +) { + return (const char*)(fnames.buffer + index); +} + +/** compare names. used by qsort and bsearch */ +static +int +cmpnames( + const void *pa, + const void *pb +) { + uint32_t a = *(const uint32_t*)pa; + uint32_t b = *(const uint32_t*)pb; + return strcmp(name_at(a), name_at(b)); +} + +/** search the index of 'name' and create it if 'needed' */ +int +db_get_name_index( + uint32_t *index, + const char *name, + bool needed +) { + uint32_t lo, up, m, i, *p; + int c; + const char *n; + size_t len; + + /* special names */ + if (!name || !name[0]) + name = ANYSTR; + + /* dichotomic search */ + lo = 0; + up = names_count; + while(lo < up) { + m = (lo + up) >> 1; + i = names_sorted[m]; + n = name_at(i); + c = strcmp(n, name); + + if (c == 0) { + /* found */ + *index = i; + return 0; + } + + /* dichotomic iteration */ + if (c < 0) + lo = m + 1; + else + up = m; + } + + /* 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; + } + + /* add the name in the file */ + i = fnames.used; + c = fbuf_append(&fnames, name, 1 + (uint32_t)len); + if (c < 0) + return c; + + /* add the name in sorted array */ + up = names_count; + if (!(up & 1023)) { + p = realloc(names_sorted, (up + 1024) * sizeof *names_sorted); + if (p == NULL) { + syslog(LOG_ERR, "out of memory"); + return -1; + } + names_sorted = p; + } + memmove(&names_sorted[lo + 1], &names_sorted[lo], (up - lo) * sizeof *names_sorted); + names_count = up + 1; + *index = names_sorted[lo] = i; + return 0; +} + +/** initialize names */ +static +int +init_names( +) { + int rc; + uint32_t pos, len, *ns, *p, all, nc; + + all = 0; + nc = 0; + ns = NULL; + + /* iterate over names */ + pos = uuidlen; + while (pos < fnames.saved) { + /* get name length */ + len = (uint32_t)strlen(name_at(pos)); + if (pos + len <= pos || pos + len > fnames.saved) { + free(ns); + goto bad_file; + } + /* store the position */ + if (all <= nc) { + all += 1024; + p = realloc(ns, all * sizeof *ns); + if (p == NULL) { + free(ns); + syslog(LOG_ERR, "out of memory"); + goto error; + } + ns = p; + } + ns[nc++] = pos; + /* next */ + pos += len + 1; + } + + /* sort and record */ + qsort(ns, nc, sizeof *ns, cmpnames); + names_sorted = ns; + names_count = nc; + + /* predefined symbols */ + rc = db_get_name_index(&pos, ANYSTR, true); + if (rc < 0) + goto error; + if (pos != ANYIDX) + goto bad_file; + rc = db_get_name_index(&pos, WIDESTR, true); + if (rc < 0) + goto error; + if (pos != WIDEIDX) + goto bad_file; + + return 0; +bad_file: + syslog(LOG_ERR, "bad file %s", fnames.name); + errno = ENOEXEC; +error: + return -1; +} + +/** check whether the 'text' fit ANYSTR, NULL or "" */ +static +bool +is_any( + const char *text +) { + return text == NULL || text[0] == 0 || 0 == strcmp(text, ANYSTR); +} + +/** check whether the 'text' fit ANYSTR, WIDESTR, NULL or "" */ +static +bool +is_any_or_wide( + const char *text +) { + return is_any(text) || 0 == strcmp(text, WIDESTR); +} + +/** get in 'session' the session for 'name' and create it if 'needed' */ +static +int +get_session( + const char *name, + bool needed, + session_t **session +) { + 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; + } + + /* 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; +} + +/** for 'session' set the value the rule at 'index' */ +static +void +session_set_at( + session_t *session, + uint32_t index, + uint32_t value +) { + 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; + } +} + +/** drop of 'session' the rule at 'index' */ +static +void +session_drop_at( + session_t *session, + 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; + } +} + +/** add to 'session' the rule 'client' x 'user' x 'permission' x 'value' */ +static +int +session_add( + session_t *session, + uint32_t client, + uint32_t user, + uint32_t permission, + uint32_t value +) { + 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++]; + rule->client = client; + rule->user = user; + rule->permission = permission; + rule->value = value; + return 0; +} + +/** init the rules from the file */ +static +void +init_rules( +) { + sessions.rules = (rule_t*)(frules.buffer + uuidlen); + sessions.count = (frules.used - uuidlen) / sizeof *sessions.rules; +} + +/** open the database for files 'names' and 'rules' (can be NULL) */ +int +db_open( + const char *names, + const char *rules +) { + int rc; + + /* open the names */ + rc = fbuf_open_identify(&fnames, names ?: "cynara.names", uuid_names_v1, uuidlen); + if (rc < 0) + goto error; + + /* open the rules */ + rc = fbuf_open_identify(&frules, rules ?: "cynara.rules", uuid_rules_v1, uuidlen); + if (rc < 0) + goto error; + + /* connect internals */ + rc = init_names(); + if (rc < 0) + goto error; + + init_rules(); + return 0; +error: + return -1; +} + +/** close the database */ +void +db_close( +) { + assert(fnames.name && frules.name); + fbuf_close(&fnames); + fbuf_close(&frules); +} + +/** is the database empty */ +bool +db_is_empty( +) { + return !sessions.count; +} + +/** synchronize db on files */ +int +db_sync( +) { + int rc; + + assert(fnames.name && frules.name); + rc = fbuf_sync(&fnames); + if (rc == 0) + rc = fbuf_sync(&frules); + return rc; +} + +/** enumerate */ +void +db_for_all( + void *closure, + void (*callback)( + void *closure, + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value), + const char *client, + const char *session, + const char *user, + const char *permission +) { + uint32_t ucli, uusr, i; + bool anyperm, anysession; + session_t *ses; + + if (db_get_name_index(&ucli, client, 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); + } + } + if (!anysession) + break; + ses = ses->next; + if (ses == &sessions) + break; + } +} + +/** drop rules */ +int +db_drop( + const char *client, + const char *session, + const char *user, + const char *permission +) { + uint32_t ucli, uusr, i; + bool anyperm, anysession; + session_t *ses; + + if (db_get_name_index(&ucli, client, 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; + } + return 0; +} + +/** set rules */ +int +db_set( + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value +) { + int rc; + uint32_t ucli, uusr, uperm, i; + session_t *ses; + + /* normalize */ + client = is_any_or_wide(client) ? WIDESTR : client; + session = is_any_or_wide(session) ? WIDESTR : session; + 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(&uusr, user, 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))) { + /* found */ + session_set_at(ses, i, value); + return 0; + } + } + + /* create the rule */ + rc = db_get_name_index(&uperm, permission, true); + if (rc) + goto error; + + rc = session_add(ses, ucli, uusr, uperm, value); + + return 0; +error: + return rc; +} + +/** check rules */ +int +db_test( + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t *value +) { + uint32_t ucli, uusr, i, val, score, sc; + session_t *ses; + rule_t *rule; + + /* check */ + client = is_any_or_wide(client) ? WIDESTR : client; + session = is_any_or_wide(session) ? WIDESTR : session; + user = is_any_or_wide(user) ? WIDESTR : user; + 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(&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) + && (uusr == rule->user || WIDEIDX == rule->user) + && (WIDEIDX == rule->permission + || !strcasecmp(permission, name_at(rule->permission)))) { + /* found */ + sc = 1 + (rule->client != WIDEIDX) + + (rule->user != WIDEIDX) + (rule->permission != WIDEIDX); + if (sc > score) { + score = sc; + val = rule->value; + } + } + } + if (!score && ses != &sessions) { + ses = &sessions; + goto retry; + } + + if (score) + *value = val; + return score > 0; +} + diff --git a/src/db.h b/src/db.h new file mode 100644 index 0000000..ce56e8c --- /dev/null +++ b/src/db.h @@ -0,0 +1,102 @@ +#pragma once + +#define MAX_NAME_LENGTH 32767 + +/** open the database for files 'names' and 'rules' (can be NULL) */ +extern +int +db_open( + const char *names, + const char *rules +); + +/** close the database */ +extern +void +db_close( +); + +/** is the database empty */ +extern +bool +db_is_empty( +); + +/** sync the database */ +extern +int +db_sync( +); + +/** enter critical recoverable section */ +extern +int +db_enter( +); + +/** leave critical recoverable section */ +extern +int +db_leave( + bool commit +); + +/** get an index for a name */ +extern +int +db_get_name_index( + uint32_t *index, + const char *name, + bool needed +); + +/** enumerate */ +extern +void +db_for_all( + void *closure, + void (*callback)( + void *closure, + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value), + const char *client, + const char *session, + const char *user, + const char *permission +); + +/** erase rules */ +extern +int +db_drop( + const char *client, + const char *session, + const char *user, + const char *permission +); + +/** set rules */ +extern +int +db_set( + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value +); + +/** check rules */ +extern +int +db_test( + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t *value +); + diff --git a/src/export.map b/src/export.map new file mode 100644 index 0000000..84467af --- /dev/null +++ b/src/export.map @@ -0,0 +1,9 @@ +{ +global: + rcyn_*; + cynara_*; +local: + *; +}; + + diff --git a/src/fbuf.c b/src/fbuf.c new file mode 100644 index 0000000..6d4d41c --- /dev/null +++ b/src/fbuf.c @@ -0,0 +1,279 @@ + +#define _GNU_SOURCE + + +#include <assert.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <limits.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <syslog.h> + +#include "fbuf.h" + +/** compute the size to allocate for ensuring 'sz' bytes */ +static +uint32_t +get_asz( + uint32_t sz +) { + return (sz & 0xfffffc00) + 0x000004cf; +} + +/** open in 'fb' the file of 'name' */ +int +fbuf_open( + fbuf_t *fb, + const char *name +) { + struct stat st; + int fd, rc; + uint32_t sz, asz; + void *buffer; + + /* open the file */ + //fd = open(name, O_RDWR|O_CREAT|O_SYNC|O_DIRECT, 0600); + fd = open(name, O_RDWR|O_CREAT, 0600); + if (fd < 0) + goto error; + + /* lock it */ + rc = flock(fd, LOCK_EX|LOCK_NB); + if (rc < 0) + goto error2; + + /* get file stat */ + rc = fstat(fd, &st); + if (rc < 0) + goto error3; + + /* check file size */ + if ((off_t)INT32_MAX < st.st_size) { + errno = EFBIG; + goto error3; + } + + /* compute allocation size */ + sz = (uint32_t)st.st_size; + asz = get_asz(sz); + buffer = malloc(asz); + if (buffer == NULL) + goto error3; + + /* read the file */ + if (read(fd, buffer, (size_t)sz) != (ssize_t)sz) + goto error4; + + /* done */ + fb->name = name; + fb->buffer = buffer; + fb->saved = fb->used = fb->size = sz; + fb->capacity = asz; + fb->fd = fd; + return 0; + +error4: + free(buffer); +error3: + flock(fd, LOCK_UN); +error2: + close(fd); +error: + syslog(LOG_ERR, "can't open file %s: %m", name); + memset(fb, 0, sizeof *fb); + return -errno; +} + +/** close the file 'fb' */ +void +fbuf_close( + fbuf_t *fb +) { + free(fb->buffer); + flock(fb->fd, LOCK_UN); + close(fb->fd); + memset(fb, 0, sizeof *fb); +} + +/** write to file 'fb' at 'offset' the 'count' bytes pointed by 'buffer' */ +int +fbuf_write( + fbuf_t *fb, + const void *buffer, + uint32_t count, + uint32_t offset +) { + off_t rco; + ssize_t rcs; + + /* don't call me for nothing */ + assert(count); + + /* set write position */ + rco = lseek(fb->fd, (off_t)offset, SEEK_SET); + if (rco != (off_t)offset) + goto error; + + /* effective write */ + rcs = write(fb->fd, buffer, (size_t)count); + if (rcs != (ssize_t)count) + goto error; + + return 0; +error: + syslog(LOG_ERR, "write of file %s failed: %m", fb->name); + return -errno; +} + +/** write to file 'fb' the unsaved bytes and flush the content to the file */ +int +fbuf_sync( + fbuf_t *fb +) { + int rc; + bool changed = false; + + /* write unsaved bytes */ + if (fb->used > fb->saved) { + rc = fbuf_write(fb, fb->buffer + fb->saved, fb->used - fb->saved, fb->saved); + if (rc < 0) + return rc; + fb->saved = fb->used; + changed = true; + } + + /* truncate on needed */ + if (fb->used < fb->size) { + rc = ftruncate(fb->fd, (off_t)fb->used); + if (rc < 0) + goto error; + changed = true; + } + fb->size = fb->used; + + /* force synchronisation of the file */ + if (changed) { + rc = fsync(fb->fd); + if (rc < 0) + goto error; + } + + return 0; +error: + syslog(LOG_ERR, "sync of file %s failed: %m", fb->name); + return -errno; +} + +/** allocate enough memory in 'fb' to store 'count' bytes */ +int +fbuf_ensure_capacity( + fbuf_t *fb, + uint32_t count +) { + uint32_t capacity; + void *buffer; + + if (count > fb->capacity) { + capacity = get_asz(count); + buffer = realloc(fb->buffer, capacity); + if (buffer == NULL) { + syslog(LOG_ERR, "alloc %u for file %s failed: %m", capacity, fb->name); + return -ENOMEM; + } + fb->buffer = buffer; + fb->capacity = capacity; + } + return 0; +} + +/** put at 'offset' in the memory of 'fb' the 'count' bytes pointed by 'buffer' */ +int +fbuf_put( + fbuf_t *fb, + const void *buffer, + uint32_t count, + uint32_t offset +) { + int rc; + uint32_t end = offset + count; + + /* don't call me for nothing */ + assert(count); + + /* grow as necessary */ + if (end > fb->used) { + rc = fbuf_ensure_capacity(fb, end); + if (rc < 0) + return rc; + fb->used = end; + } + + /* copy the data */ + memcpy(fb->buffer + offset, buffer, count); + + /* write the data to the disk */ + if (offset < fb->saved) + fb->saved = offset; + return 0; +} + +/** append at end in the memory of 'fb' the 'count' bytes pointed by 'buffer' */ +int +fbuf_append( + fbuf_t *fb, + const void *buffer, + uint32_t count +) { + /* don't call me for nothing */ + assert(count); + + return fbuf_put(fb, buffer, count, fb->used); +} + +/** check or make identification of file 'fb' by 'id' of 'len' */ +int +fbuf_identify( + fbuf_t *fb, + const char *id, + uint32_t idlen +) { + /* init if empty */ + if (fb->saved == 0 && fb->used == 0) + return fbuf_append(fb, id, idlen); + + /* check if not empty */ + if (fb->saved >= idlen && !memcmp(fb->buffer, id, idlen)) + return 0; + + /* bad identification */ + errno = ENOKEY; + syslog(LOG_ERR, "identification of file %s failed: %m", fb->name); + return -ENOKEY; +} + +/** check or make identification by 'uuid' of file 'fb' */ +int +fbuf_open_identify( + fbuf_t *fb, + const char *name, + const char *id, + uint32_t idlen +) { + int rc; + + rc = fbuf_open(fb, name); + if (rc == 0) { + rc = fbuf_identify(fb, id, idlen); + if (rc < 0) + fbuf_close(fb); + } + return rc; +} + diff --git a/src/fbuf.h b/src/fbuf.h new file mode 100644 index 0000000..3bbdbde --- /dev/null +++ b/src/fbuf.h @@ -0,0 +1,111 @@ +#pragma once + +/** + * A fbuf records file data and access + */ +struct fbuf +{ + /** filename for messages */ + const char *name; + + /** in memory copy of the file */ + void *buffer; + + /** size saved to the file */ + uint32_t saved; + + /** size currently used */ + uint32_t used; + + /** size currently allocated */ + uint32_t capacity; + + /** size of the file */ + uint32_t size; + + /** opened file descriptor for the file */ + int fd; +}; + +/** short type */ +typedef struct fbuf fbuf_t; + + +/** open in 'fb' the file of 'name' */ +extern +int +fbuf_open( + fbuf_t *fb, + const char *name +); + +/** close the file 'fb' */ +extern +void +fbuf_close( + fbuf_t *fb +); + +/** write to file 'fb' at 'offset' the 'count' bytes pointed by 'buffer' */ +extern +int +fbuf_write( + fbuf_t *fb, + const void *buffer, + uint32_t count, + uint32_t offset +); + +/** write to file 'fb' the unsaved bytes and flush the content to the file */ +extern +int +fbuf_sync( + fbuf_t *fb +); + +/** allocate enough memory in 'fb' to store 'count' bytes */ +extern +int +fbuf_ensure_capacity( + fbuf_t *fb, + uint32_t count +); + +/** put at 'offset' in the memory of 'fb' the 'count' bytes pointed by 'buffer' */ +extern +int +fbuf_put( + fbuf_t *fb, + const void *buffer, + uint32_t count, + uint32_t offset +); + +/** append at end in the memory of 'fb' the 'count' bytes pointed by 'buffer' */ +extern +int +fbuf_append( + fbuf_t *fb, + const void *buffer, + uint32_t count +); + +/** check or make identification of file 'fb' by 'id' of 'len' */ +extern +int +fbuf_identify( + fbuf_t *fb, + const char *id, + uint32_t idlen +); + +/** check or make identification by 'uuid' of file 'fb' */ +extern +int +fbuf_open_identify( + fbuf_t *fb, + const char *name, + const char *id, + uint32_t idlen +); + diff --git a/src/lib-compat.c b/src/lib-compat.c new file mode 100644 index 0000000..5c6c4e2 --- /dev/null +++ b/src/lib-compat.c @@ -0,0 +1,651 @@ +/* +cynara_admin_initialize(&m_CynaraAdmin), +cynara_admin_finish(m_CynaraAdmin); +cynara_admin_set_policies(m_CynaraAdmin, pp_policies.data()), +cynara_admin_list_policies(m_CynaraAdmin, bucketName.c_str(), appId.c_str(), +cynara_admin_erase(m_CynaraAdmin, bucketName.c_str(), static_cast<int>(recursive), +cynara_admin_check(m_CynaraAdmin, bucket.c_str(), recursive, label.c_str(), + +cynara_initialize(&m_Cynara, nullptr), +cynara_finish(m_Cynara); +cynara_check(m_Cynara, +*/ +#define _GNU_SOURCE + +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/epoll.h> + +#include <cynara/cynara-admin.h> +#include <cynara/cynara-client.h> +#include <cynara/cynara-client-async.h> +#include <cynara/cynara-creds-commons.h> + +#ifndef CYNARA_ADMIN_ASK +# define CYNARA_ADMIN_ASK 11 +#endif + +#include "rcyn-client.h" + +/******************** ADMIN ********************************/ + +static int from_status(int rc) +{ + switch (-rc) { + case 0: rc = CYNARA_API_SUCCESS; break; + case ENOMEM: rc = CYNARA_API_OUT_OF_MEMORY; break; + case ENOTSUP: rc = CYNARA_API_METHOD_NOT_SUPPORTED; break; + case ENOENT: rc = CYNARA_API_CACHE_MISS; break; + default: rc = CYNARA_API_UNKNOWN_ERROR; break; + } + return rc; +} + +static int from_check_status(int rc) +{ + switch (rc) { + case 0: rc = CYNARA_API_ACCESS_DENIED; break; + case 1: rc = CYNARA_API_ACCESS_ALLOWED; break; + case -EEXIST: rc = CYNARA_API_ACCESS_NOT_RESOLVED; break; + default: rc = from_status(rc); break; + } + return rc; +} + +static int from_value(uint32_t value) +{ + switch(value) { + case 0: return CYNARA_ADMIN_DENY; + case 1: return CYNARA_ADMIN_ALLOW; + case 2: return CYNARA_ADMIN_ASK; + } + return (int)value; +} + +static uint32_t 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; + } + return (uint32_t)value; +} + +/************************************ ERROR ****************************************/ + +static const struct { + int num; + const char *text; +} error_descriptions[] = { + { CYNARA_API_INTERRUPTED, "API call was interrupted by user" }, + { CYNARA_API_ACCESS_NOT_RESOLVED, "access cannot be resolved without further actions" }, + { CYNARA_API_ACCESS_ALLOWED, "access that was checked is allowed" }, + { CYNARA_API_ACCESS_DENIED, "access that was checked is denied" }, + { CYNARA_API_SUCCESS, "successful" }, + { CYNARA_API_CACHE_MISS, "value is not present in cache" }, + { CYNARA_API_MAX_PENDING_REQUESTS, "pending requests reached maximum" }, + { CYNARA_API_OUT_OF_MEMORY, "system is running out of memory" }, + { CYNARA_API_INVALID_PARAM, "parameter is malformed" }, + { CYNARA_API_SERVICE_NOT_AVAILABLE, "service is not available" }, + { CYNARA_API_METHOD_NOT_SUPPORTED, "method is not supported by library" }, + { CYNARA_API_OPERATION_NOT_ALLOWED, "not allowed to perform requested operation" }, + { CYNARA_API_OPERATION_FAILED, "failed to perform requested operation" }, + { CYNARA_API_BUCKET_NOT_FOUND, "service hasn't found requested bucket" }, + { CYNARA_API_UNKNOWN_ERROR, "unknown error" }, + { CYNARA_API_CONFIGURATION_ERROR, "configuration error" }, + { CYNARA_API_INVALID_COMMANDLINE_PARAM, "invalid parameter in command-line" }, + { CYNARA_API_BUFFER_TOO_SHORT, "provided buffer is too short" }, + { CYNARA_API_DATABASE_CORRUPTED, "database is corrupted" }, + { CYNARA_API_PERMISSION_DENIED, "user doesn't have enough permission to perform action" }, +}; + +int cynara_strerror(int errnum, char *buf, size_t buflen) +{ + int i = (int)(sizeof error_descriptions / sizeof *error_descriptions); + while(i) { + if (error_descriptions[--i].num == errnum) { + if (strlen(error_descriptions[i].text) >= buflen) + return CYNARA_API_BUFFER_TOO_SHORT; + if (buf == NULL) + break; + strcpy(buf, error_descriptions[i].text); + return CYNARA_API_SUCCESS; + } + } + return CYNARA_API_INVALID_PARAM; +} + +/******************** ADMIN ********************************/ + +struct cynara_admin; + +int cynara_admin_initialize(struct cynara_admin **pp_cynara_admin) +{ + return from_status(rcyn_open((rcyn_t**)pp_cynara_admin, rcyn_Admin, 0)); +} + +int cynara_admin_finish(struct cynara_admin *p_cynara_admin) +{ + rcyn_close((rcyn_t*)p_cynara_admin); + return CYNARA_API_SUCCESS; +} + +int cynara_admin_set_policies(struct cynara_admin *p_cynara_admin, + const struct cynara_admin_policy *const *policies) +{ + int rc, rc2; + const struct cynara_admin_policy *p; + + rc = rcyn_enter((rcyn_t*)p_cynara_admin); + if (rc == 0) { + p = *policies; + while (rc == 0 && p != NULL) { + if (p->result == CYNARA_ADMIN_DELETE) + rc = rcyn_drop((rcyn_t*)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 = *++policies; + } + rc2 = rcyn_leave((rcyn_t*)p_cynara_admin, rc == 0); + if (rc == 0) + rc = rc2; + } + return rc; +} + +static void check_cb( + void *closure, + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value +) { + *((int*)closure) = from_value(value); +} + +int cynara_admin_check(struct cynara_admin *p_cynara_admin, + const char *start_bucket, const int recursive, + const char *client, const char *user, const char *privilege, + int *result, char **result_extra) +{ + if (result_extra) + *result_extra = NULL; + *result = CYNARA_ADMIN_DENY; + return from_status(rcyn_get((rcyn_t*)p_cynara_admin, client, "*", user, privilege, check_cb, result)); +} + +struct list_data +{ + struct cynara_admin_policy **policies; + const char *bucket; + unsigned count; + int error; +}; + +static void list_cb( + void *closure, + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value +) { + struct list_data *data = closure; + struct cynara_admin_policy *pol; + + if (data->error) + return; + + pol = calloc(1, sizeof *pol); + if (pol == NULL) + goto error; + + pol->bucket = strdup(data->bucket ?: ""); + pol->client = strdup(client); + pol->user = strdup(user); + pol->privilege = strdup(permission); + if (pol->bucket == NULL || pol->client == NULL || pol->user == NULL || pol->privilege == NULL) + goto error; + + pol->result = from_value(value); + pol->result_extra = 0; + closure = realloc(data->policies, (data->count + 1) * sizeof *data->policies); + if (closure == NULL) + goto error; + + (data->policies = closure)[data->count++] = pol; + return; +error: + if (pol) { + free(pol->bucket); + free(pol->client); + free(pol->user); + free(pol->privilege); + free(pol); + } + data->error = -ENOMEM; + +} + +int cynara_admin_list_policies(struct cynara_admin *p_cynara_admin, const char *bucket, + const char *client, const char *user, const char *privilege, + struct cynara_admin_policy ***policies) +{ + int rc; + struct list_data data; + + data.policies = NULL; + data.bucket = bucket && strcmp(bucket, "#") && strcmp(bucket, "*") ? bucket : NULL; + data.count = 0; + data.error = 0; + rc = rcyn_get((rcyn_t*)p_cynara_admin, client, "*", user, privilege, list_cb, &data); + if (rc == 0 && data.error != 0) + rc = data.error; + if (rc == 0 && !data.error) { + if ((*policies = realloc(data.policies, (data.count + 1) * sizeof *data.policies)) != NULL) + policies[0][data.count] = NULL; + else + rc = -ENOMEM; + } + if (rc) { + while(data.count) + free(data.policies[--data.count]); + free(data.policies); + *policies = NULL; + } + return from_status(rc); +} + +int cynara_admin_erase(struct cynara_admin *p_cynara_admin, + const char *start_bucket, int recursive, + const char *client, const char *user, const char *privilege) +{ + int rc, rc2; + + rc = rcyn_enter((rcyn_t*)p_cynara_admin); + if (rc == 0) { + rc = rcyn_drop((rcyn_t*)p_cynara_admin, + client, "*", user, privilege); + rc2 = rcyn_leave((rcyn_t*)p_cynara_admin, rc == 0); + if (rc == 0) + rc = rc2; + } + return from_status(rc); +} + + +int cynara_admin_list_policies_descriptions(struct cynara_admin *p_cynara_admin, + struct cynara_admin_policy_descr ***descriptions) +{ + struct cynara_admin_policy_descr **d = malloc(4 * sizeof *d), *s; + if (d) { + d[0] = malloc(sizeof *s); + d[1] = malloc(sizeof *s); + d[2] = malloc(sizeof *s); + d[3] = NULL; + if (d[0] != NULL && d[1] != NULL && d[2] != NULL) { + d[0]->name = strdup("Deny"); + d[1]->name = strdup("AskUser"); + d[2]->name = strdup("Allow"); + if (d[0]->name != NULL && d[1]->name != NULL && d[2]->name != NULL) { + d[0]->result = CYNARA_ADMIN_DENY; + d[1]->result = CYNARA_ADMIN_ASK; + d[2]->result = CYNARA_ADMIN_ALLOW; + *descriptions = d; + return CYNARA_API_SUCCESS; + } + free(d[0]->name); + free(d[1]->name); + free(d[2]->name); + } + free(d[0]); + free(d[1]); + free(d[2]); + } + *descriptions = NULL; + return CYNARA_API_OUT_OF_MEMORY; +} + +/************************************* CLIENT-ASYNC **************************************/ + +int cynara_async_configuration_create(cynara_async_configuration **pp_conf) +{ + *pp_conf = (cynara_async_configuration*)pp_conf; + return CYNARA_API_SUCCESS; +} + +void cynara_async_configuration_destroy(cynara_async_configuration *p_conf) +{ +} + +int cynara_async_configuration_set_cache_size(cynara_async_configuration *p_conf, + size_t cache_size) +{ + return CYNARA_API_SUCCESS; +} + +struct reqasync +{ + struct reqasync *next; + cynara_async *cynasync; + cynara_response_callback callback; + void *user_response_data; + cynara_check_id id; + bool canceled; +}; + +struct cynara_async +{ + rcyn_t *rcyn; + cynara_status_callback callback; + void *user_status_data; + struct reqasync *reqs; + cynara_check_id ids; +}; + +static int async_control_cb(void *closure, int op, int fd, uint32_t events) +{ + cynara_async *p_cynara = closure; + cynara_async_status s = (events & EPOLLOUT) ? CYNARA_STATUS_FOR_RW : CYNARA_STATUS_FOR_READ; + switch(op) { + case EPOLL_CTL_ADD: + p_cynara->callback(-1, fd, s, p_cynara->user_status_data); + break; + case EPOLL_CTL_MOD: + p_cynara->callback(fd, fd, s, p_cynara->user_status_data); + break; + case EPOLL_CTL_DEL: + p_cynara->callback(fd, -1, 0, p_cynara->user_status_data); + break; + } + return 0; +} + +int cynara_async_initialize(cynara_async **pp_cynara, const cynara_async_configuration *p_conf, + cynara_status_callback callback, void *user_status_data) +{ + int ret; + cynara_async *p_cynara; + + p_cynara = malloc(sizeof *p_cynara); + if (p_cynara == NULL) + ret = CYNARA_API_OUT_OF_MEMORY; + else { + ret = from_status(rcyn_open(&p_cynara->rcyn, rcyn_Check, 0)); + if (ret != CYNARA_API_SUCCESS) + free(p_cynara); + else { + p_cynara->callback = callback; + p_cynara->user_status_data = user_status_data; + p_cynara->reqs = NULL; + p_cynara->ids = 0; + rcyn_async_setup(p_cynara->rcyn, async_control_cb, p_cynara); + *pp_cynara = p_cynara; + } + } + return ret; +} + +void cynara_async_finish(cynara_async *p_cynara) +{ + struct reqasync *req; + + for(req = p_cynara->reqs ; req ; req = req->next) { + if (!req->canceled) { + req->callback(req->id, CYNARA_CALL_CAUSE_FINISH, 0, req->user_response_data); + req->canceled = true; + } + } + + rcyn_close(p_cynara->rcyn); + + while((req = p_cynara->reqs)) { + p_cynara->reqs = req->next; + free(req); + } + free(p_cynara); +} + +int cynara_async_check_cache(cynara_async *p_cynara, const char *client, const char *client_session, + const char *user, const char *privilege) +{ + return from_check_status(rcyn_cache_check(p_cynara->rcyn, client, client_session,user, privilege)); +} + +static void reqcb(void *closure, int status) +{ + struct reqasync *req = closure, **p; + + p = &req->cynasync->reqs; + while(*p && *p != req) + p = &(*p)->next; + if (*p) + *p = req->next; + + if (!req->canceled) + req->callback(req->id, CYNARA_CALL_CAUSE_ANSWER, status, req->user_response_data); + + free(req); +} + +static int create_reqasync(cynara_async *p_cynara, const char *client, + const char *client_session, const char *user, const char *privilege, + cynara_check_id *p_check_id, cynara_response_callback callback, + void *user_response_data, bool simple) +{ + int rc; + struct reqasync *req; + + req = malloc(sizeof *req); + if (req == NULL) + return CYNARA_API_OUT_OF_MEMORY; + + req->next = p_cynara->reqs; + req->cynasync = p_cynara; + req->callback = callback; + req->user_response_data = user_response_data; + req->id = ++p_cynara->ids; + req->canceled = false; + + rc = rcyn_async_check(p_cynara->rcyn, client, client_session, user, privilege, simple, reqcb, req); + if (rc == 0) + p_cynara->reqs = req; + else + free(req); + return from_status(rc); +} + +int cynara_async_create_request(cynara_async *p_cynara, const char *client, + const char *client_session, const char *user, const char *privilege, + cynara_check_id *p_check_id, cynara_response_callback callback, + void *user_response_data) +{ + return create_reqasync(p_cynara, client, client_session, user, privilege, p_check_id, callback, user_response_data, false); +} + +int cynara_async_create_simple_request(cynara_async *p_cynara, const char *client, + const char *client_session, const char *user, + const char *privilege, cynara_check_id *p_check_id, + cynara_response_callback callback, void *user_response_data) +{ + return create_reqasync(p_cynara, client, client_session, user, privilege, p_check_id, callback, user_response_data, true); +} + + +int cynara_async_process(cynara_async *p_cynara) +{ + return rcyn_async_process(p_cynara->rcyn); +} + +int cynara_async_cancel_request(cynara_async *p_cynara, cynara_check_id check_id) +{ + struct reqasync *req = p_cynara->reqs; + + while(req && req->id != check_id) + req = req->next; + if (req && !req->canceled) { + req->canceled = true; + req->callback(req->id, CYNARA_CALL_CAUSE_CANCEL, 0, req->user_response_data); + } + return CYNARA_API_SUCCESS; +} + +/************************************* CLIENT **************************************/ + +int cynara_configuration_create(cynara_configuration **pp_conf) +{ + *pp_conf = (cynara_configuration*)pp_conf; + return CYNARA_API_SUCCESS; +} + +void cynara_configuration_destroy(cynara_configuration *p_conf) +{ +} + +int cynara_configuration_set_cache_size(cynara_configuration *p_conf, size_t cache_size) +{ + return CYNARA_API_SUCCESS; +} + +int cynara_initialize(cynara **pp_cynara, const cynara_configuration *p_conf) +{ + return from_status(rcyn_open((rcyn_t**)pp_cynara, rcyn_Check, 0)); +} + +int cynara_finish(cynara *p_cynara) +{ + rcyn_close((rcyn_t*)p_cynara); + return CYNARA_API_SUCCESS; +} + +int cynara_check(cynara *p_cynara, const char *client, const char *client_session, const char *user, + const char *privilege) +{ + return from_check_status(rcyn_check((rcyn_t*)p_cynara, client, client_session, user, privilege)); +} + +int cynara_simple_check(cynara *p_cynara, const char *client, const char *client_session, + const char *user, const char *privilege) +{ + return from_check_status(rcyn_test((rcyn_t*)p_cynara, client, client_session, user, privilege)); +} + +/************************************* CREDS... & SESSION *********************************/ +#define MAX_LABEL_LENGTH 1024 + +#if !defined(DEFAULT_PEERSEC_LABEL) +# define DEFAULT_PEERSEC_LABEL "NoLabel" +#endif + +int cynara_creds_get_default_client_method(enum cynara_client_creds *method) +{ + *method = CLIENT_METHOD_SMACK; + return CYNARA_API_SUCCESS; +} + +int cynara_creds_get_default_user_method(enum cynara_user_creds *method) +{ + *method = USER_METHOD_UID; + return CYNARA_API_SUCCESS; +} + +int cynara_creds_self_get_client(enum cynara_client_creds method, char **client) +{ + char label[MAX_LABEL_LENGTH + 1]; + int len, fd; + + label[0] = 0; + fd = open("/proc/self/current/attr", O_RDONLY); + if (fd >= 0) { + len = (int)read(fd, label, sizeof label - 1); + label[len >= 0 ? len : 0] = 0; + close(fd); + } + return (*client = strdup(label[0] ? label : DEFAULT_PEERSEC_LABEL)) + ? CYNARA_API_SUCCESS : CYNARA_API_OUT_OF_MEMORY; +} + +int cynara_creds_self_get_user(enum cynara_user_creds method, char **user) +{ + return asprintf(user, "%ld", (long)getuid()) > 0 + ? CYNARA_API_SUCCESS : CYNARA_API_OUT_OF_MEMORY; +} + +int cynara_creds_socket_get_client(int fd, enum cynara_client_creds method, char **client) +{ + int rc; + socklen_t length; + struct ucred ucred; + char label[MAX_LABEL_LENGTH + 1]; + + /* get the credentials */ + length = (socklen_t)(sizeof ucred); + rc = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &length); + if (rc < 0 || length != (socklen_t)(sizeof ucred) || !~ucred.uid) + return CYNARA_API_OPERATION_FAILED; + + /* get the security label */ + length = (socklen_t)(sizeof label); + rc = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, label, &length); + if (rc < 0 || length > (socklen_t)(sizeof label)) + return CYNARA_API_OPERATION_FAILED; + + return (*client = strdup(label)) + ? CYNARA_API_SUCCESS : CYNARA_API_OUT_OF_MEMORY; +} + + + +int cynara_creds_socket_get_user(int fd, enum cynara_user_creds method, char **user) +{ + int rc; + socklen_t length; + struct ucred ucred; + + /* get the credentials */ + length = (socklen_t)(sizeof ucred); + rc = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &length); + if (rc < 0 || length != (socklen_t)(sizeof ucred) || !~ucred.uid) + return CYNARA_API_OPERATION_FAILED; + return asprintf(user, "%ld", (long)ucred.uid) > 0 + ? CYNARA_API_SUCCESS : CYNARA_API_OUT_OF_MEMORY; +} + + + +int cynara_creds_socket_get_pid(int fd, pid_t *pid) +{ + int rc; + socklen_t length; + struct ucred ucred; + + /* get the credentials */ + length = (socklen_t)(sizeof ucred); + rc = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &length); + if (rc < 0 || length != (socklen_t)(sizeof ucred) || !~ucred.uid) + return CYNARA_API_OPERATION_FAILED; + *pid = ucred.pid; + return CYNARA_API_SUCCESS; +} + +char *cynara_session_from_pid(pid_t client_pid) +{ + char *r; + + return asprintf(&r, "%ld", (long)client_pid) < 0 ? NULL : r; +} + diff --git a/src/prot.c b/src/prot.c new file mode 100644 index 0000000..db0e41e --- /dev/null +++ b/src/prot.c @@ -0,0 +1,440 @@ +#define _GNU_SOURCE + +#include <stdlib.h> +#include <limits.h> +#include <stdarg.h> +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/uio.h> + +#include "prot.h" + +/** the structure buf is generic the meaning of pos/count is not fixed */ +struct buf +{ + /** a position */ + unsigned pos; + + /** a count */ + unsigned count; + + /** a fixed size content */ + char content[MAXBUFLEN]; +}; +typedef struct buf buf_t; + +/** structure for recording received fields */ +struct fields +{ + /** count of field (negative if invalid) */ + int count; + + /** the fields as strings */ + const char *fields[MAXARGS]; +}; +typedef struct fields fields_t; + +/** structure for handling the protocol */ +struct prot +{ + /** input buf, pos is the scanning position */ + buf_t inbuf; + + /** output buf, pos is to be written position */ + buf_t outbuf; + + /** the fields */ + fields_t fields; +}; + + +/** + * Put the 'count' in 'fields' to the 'buf' + * returns: + * - 0 on success + * - -EINVAL if the count of fields is too big + * - -ECANCELED if there is not enought space in the buffer + */ +static +int +buf_put_fields( + buf_t *buf, + unsigned count, + const char **fields +) { + unsigned ifield, pos, remain; + const char *t; + char c; + + /* check the count of fields */ + if (count > MAXARGS) + return -EINVAL; + + /* get the writing position and the free count */ + pos = buf->pos + buf->count; + if (pos >= MAXBUFLEN) + pos -= MAXBUFLEN; + remain = MAXBUFLEN - buf->count; + + /* put all fields */ + for (ifield = 0 ; ifield < count ; ifield++) { + /* prepend the field separator if needed */ + if (ifield) { + if (!remain--) + goto cancel; + buf->content[pos++] = FS; + if (pos == MAXBUFLEN) + pos = 0; + } + /* put the field if any (NULL aliases "") */ + t = fields[ifield]; + if (t) { + /* put all chars of the field */ + while((c = *t++)) { + /* escape special characters */ + if (c == FS || c == RS || c == ESC) { + if (!remain--) + goto cancel; + buf->content[pos++] = ESC; + if (pos == MAXBUFLEN) + pos = 0; + } + /* put the char */ + if (!remain--) + goto cancel; + buf->content[pos++] = c; + if (pos == MAXBUFLEN) + pos = 0; + } + } + } + + /* put the end indicator */ + if (!remain--) + goto cancel; + buf->content[pos] = RS; + + /* record the new values */ + buf->count = MAXBUFLEN - remain; + return 0; + +cancel: + return -ECANCELED; +} + +/** + * write the content of 'buf' to 'fd' + */ +static +int +buf_write( + buf_t *buf, + int fd +) { + int n; + unsigned count; + ssize_t rc; + struct iovec vec[2]; + + /* get the count of byte to write (avoid int overflow) */ + count = buf->count > INT_MAX ? INT_MAX : buf->count; + + /* calling it with nothing to write is an error */ + if (count == 0) + return -ENODATA; + + /* prepare the iovec */ + vec[0].iov_base = buf->content + buf->pos; + if (buf->pos + count <= MAXBUFLEN) { + vec[0].iov_len = count; + n = 1; + } else { + vec[0].iov_len = MAXBUFLEN - buf->pos; + vec[1].iov_base = buf->content; + vec[1].iov_len = count - vec[0].iov_len; + n = 2; + } + + /* write the buffers */ + do { + rc = writev(fd, vec, n); + } while(rc < 0 && errno == EINTR); + + /* check error */ + if (rc < 0) + rc = -errno; + else { + /* update the state */ + buf->count -= (unsigned)rc; + buf->pos += (unsigned)rc; + if (buf->pos >= MAXBUFLEN) + buf->pos -= MAXBUFLEN; + } + + return (int)rc; +} + +/* get the 'fields' from 'buf' */ +static +void +buf_get_fields( + buf_t *buf, + fields_t *fields +) { + char c; + unsigned read, write; + + /* advance the pos after the end */ + assert(buf->content[buf->pos] == RS); + buf->pos++; + + /* init first field */ + fields->fields[fields->count = 0] = buf->content; + read = write = 0; + for (;;) { + c = buf->content[read++]; + switch(c) { + case FS: /* field separator */ + buf->content[write++] = 0; + if (fields->count >= MAXARGS) + return; + fields->fields[++fields->count] = &buf->content[write]; + break; + case RS: /* end of line (record separator) */ + buf->content[write] = 0; + fields->count += (write > 0); + return; + case ESC: /* escaping */ + c = buf->content[read++]; + if (c != FS && c != RS && c != ESC) + buf->content[write++] = ESC; + buf->content[write++] = c; + break; + default: /* other characters */ + buf->content[write++] = c; + break; + } + } +} + +/** + * Advance pos of 'buf' until end of record RS found in buffer. + * return 1 if found or 0 if not found + */ +static +int +buf_scan_end_record( + buf_t *buf +) { + unsigned nesc; + + /* search the next RS */ + while(buf->pos < buf->count) { + if (buf->content[buf->pos] == RS) { + /* check whether RS is escaped */ + nesc = 0; + while (buf->pos > nesc && buf->content[buf->pos - (nesc + 1)] == ESC) + nesc++; + if ((nesc & 1) == 0) + return 1; /* not escaped */ + } + buf->pos++; + } + return 0; +} + +/** remove chars of 'buf' until pos */ +static +void +buf_crop( + buf_t *buf +) { + buf->count -= buf->pos; + if (buf->count) + memmove(buf->content, buf->content + buf->pos, buf->count); + buf->pos = 0; +} + +/** read input 'buf' from 'fd' */ +static +int +inbuf_read( + buf_t *buf, + int fd +) { + ssize_t szr; + int rc; + + if (buf->count == MAXBUFLEN) + return -ENOBUFS; + + do { + szr = read(fd, buf->content + buf->count, MAXBUFLEN - buf->count); + } while(szr < 0 && errno == EINTR); + if (szr >= 0) + buf->count += (unsigned)(rc = (int)szr); + else if (szr < 0) + rc = -(errno == EWOULDBLOCK ? EAGAIN : errno); + + return rc; +} + +/** + * create the prot structure in 'prot' + * Return 0 in case of success or -ENOMEM in case of error + */ +int +prot_create( + prot_t **prot +) { + prot_t *p; + + /* allocation of the structure */ + *prot = p = malloc(sizeof *p); + if (p == NULL) + return -ENOMEM; + + /* initialisation of the structure */ + prot_reset(p); + + /* terminate */ + return 0; +} + +/** + * Destroys the protocol 'prot' + */ +void +prot_destroy( + prot_t *prot +) { + free(prot); +} + +/** + * reset the protocol 'prot' + */ +void +prot_reset( + prot_t *prot +) { + /* initialisation of the structure */ + prot->inbuf.pos = prot->inbuf.count = 0; + prot->outbuf.pos = prot->outbuf.count = 0; + prot->fields.count = -1; +} + +/** + * Put protocol encoded 'count' 'fields' to the output buffer + * returns: + * - 0 on success + * - -EINVAL if the count of fields is too big + * - -ECANCELED if there is not enought space in the buffer + */ +int +prot_put( + prot_t *prot, + unsigned count, + const char **fields +) { + return buf_put_fields(&prot->outbuf, count, fields); +} + +/** + * Put protocol encoded fields until NULL found to the output buffer + * returns: + * - 0 on success + * - -EINVAL if the count of fields is too big + * - -ECANCELED if there is not enought space in the buffer + */ +int +prot_putx( + prot_t *prot, + ... +) { + const char *p, *fields[MAXARGS]; + unsigned n; + va_list l; + + va_start(l, prot); + n = 0; + p = va_arg(l, const char *); + while (p) { + if (n == MAXARGS) + return -EINVAL; + fields[n++] = p; + p = va_arg(l, const char *); + } + va_end(l); + return prot_put(prot, n, fields); +} + +/** + * Check whether write should be done or not + * Returns 1 if there is something to write or 0 otherwise + */ +int +prot_should_write( + prot_t *prot +) { + return prot->outbuf.count > 0; +} + +/** + * Write the content to write and return either the count + * of bytes written or an error code (negative). Note that + * the returned value tries to be the same as those returned + * by "man 2 write". The only exception is -ENODATA that is + * returned if there is nothing to be written. + */ +int +prot_write( + prot_t *prot, + int fdout +) { + return buf_write(&prot->outbuf, fdout); +} + +int +prot_can_read( + prot_t *prot +) { + return prot->inbuf.count < MAXBUFLEN; +} + +int +prot_read( + prot_t *prot, + int fdin +) { + return inbuf_read(&prot->inbuf, fdin); +} + +int +prot_get( + prot_t *prot, + const char ***fields +) { + if (prot->fields.count < 0) { + if (!buf_scan_end_record(&prot->inbuf)) + return -EAGAIN; + buf_get_fields(&prot->inbuf, &prot->fields); + } + if (fields) + *fields = prot->fields.fields; + return (int)prot->fields.count; +} + +void +prot_next( + prot_t *prot +) { + if (prot->fields.count >= 0) { + buf_crop(&prot->inbuf); + prot->fields.count = -1; + } +} + + diff --git a/src/prot.h b/src/prot.h new file mode 100644 index 0000000..a1ded3b --- /dev/null +++ b/src/prot.h @@ -0,0 +1,85 @@ +#pragma once + +struct prot; +typedef struct prot prot_t; + +#define MAXBUFLEN 2000 +#define MAXARGS 20 +#define FS ' ' +#define RS '\n' +#define ESC '\\' + + +extern +int +prot_create( + prot_t **prot +); + +extern +void +prot_destroy( + prot_t *prot +); + +extern +void +prot_reset( + prot_t *prot +); + +extern +int +prot_put( + prot_t *prot, + unsigned count, + const char **fields +); + +extern +int +prot_putx( + prot_t *prot, + ... +); + +extern +int +prot_should_write( + prot_t *prot +); + +extern +int +prot_write( + prot_t *prot, + int fdout +); + +extern +int +prot_can_read( + prot_t *prot +); + +extern +int +prot_read( + prot_t *prot, + int fdin +); + +extern +int +prot_get( + prot_t *prot, + const char ***fields +); + +extern +void +prot_next( + prot_t *prot +); + + diff --git a/src/queue.c b/src/queue.c new file mode 100644 index 0000000..16b0a0f --- /dev/null +++ b/src/queue.c @@ -0,0 +1,192 @@ +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "db.h" + +#define DROP 0 +#define SET 1 + +/** + * Queue + */ +struct queue +{ + uint32_t read, write, capacity; + void *queue; +}; +typedef struct queue queue_t; + +/** the queue */ +static queue_t queue; + +static +bool +qread( + void *data, + uint32_t length +) { + if (queue.read + length > queue.write) + return false; + + memcpy(data, queue.queue + queue.read, length); + queue.read += length; + return true; +} + +static +bool +qget_char( + char *value +) { + return qread(value, sizeof *value); +} + +static +bool +qget_uint32( + uint32_t *value +) { + return qread(value, sizeof *value); +} + +static +bool +qget_string( + const char **text +) { + char *p = queue.queue + queue.read; + uint32_t length = (uint32_t)strlen(p); + if (queue.read + length >= queue.write) + return false; + *text = p; + queue.read += length + 1; + return true; +} + +static +bool +qwrite( + const void *data, + uint32_t length +) { + uint32_t c; + void *b; + + c = queue.capacity; + while (c < queue.write + length) + c += 4096; + if (c != queue.capacity) { + b = realloc(queue.queue, c); + if (b == NULL) + return false; + queue.queue = b; + queue.capacity = c; + } + memcpy(queue.queue + queue.write, data, length); + queue.write += length; + return true; +} + +static +bool +qput_char( + char value +) { + return qwrite(&value, sizeof value); +} + +static +bool +qput_uint32( + uint32_t value +) { + return qwrite(&value, sizeof value); +} + +static +bool +qput_string( + const char *text +) { + size_t len; + text = text ?: ""; + /* check length */ + len = strnlen(text, MAX_NAME_LENGTH + 1); + if (len > MAX_NAME_LENGTH) + return false; + return qwrite(text, 1 + (uint32_t)len); +} + +int +queue_drop( + const char *client, + const char *session, + const char *user, + const char *permission +) { + return qput_char(DROP) + && qput_string(client) + && qput_string(session) + && qput_string(user) + && qput_string(permission) + ? 0 : -(errno = ENOMEM); +} + +int +queue_set( + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value +) { + return qput_char(SET) + && qput_string(client) + && qput_string(session) + && qput_string(user) + && qput_string(permission) + && qput_uint32(value) + ? 0 : -(errno = ENOMEM); +} + + +void +queue_clear( +) { + queue.write = 0; +} + +int +queue_play( +) { + int rc, rc2; + char op; + const char *client; + const char *session; + const char *user; + const char *permission; + uint32_t value; + + rc = 0; + queue.read = 0; + while (queue.read < queue.write) { + rc2 = -EINVAL; + if (qget_char(&op) + && qget_string(&client) + && qget_string(&session) + && qget_string(&user) + && qget_string(&permission)) { + if (op == DROP) + rc2 = db_drop(client, session, user, permission); + else if (qget_uint32(&value)) + rc2 = db_set(client, session, user, permission, value); + } + if (rc2 != 0 && rc == 0) + rc = rc2; + } + return rc; +} + diff --git a/src/queue.h b/src/queue.h new file mode 100644 index 0000000..70518c4 --- /dev/null +++ b/src/queue.h @@ -0,0 +1,31 @@ + +extern +int +queue_drop( + const char *client, + const char *session, + const char *user, + const char *permission +); + +extern +int +queue_set( + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value +); + +extern +void +queue_clear( +); + +extern +int +queue_play( +); + + diff --git a/src/rcyn-client.c b/src/rcyn-client.c new file mode 100644 index 0000000..f712ca6 --- /dev/null +++ b/src/rcyn-client.c @@ -0,0 +1,722 @@ +#define _GNU_SOURCE + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <assert.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/epoll.h> + +#include "prot.h" +#include "rcyn-protocol.h" +#include "rcyn-client.h" +#include "cache.h" +#include "socket.h" + +#define MIN_CACHE_SIZE 400 + +struct asreq; +typedef struct asreq asreq_t; + +/** recording of asynchronous requests */ +struct asreq +{ + /** link to the next pending request */ + asreq_t *next; + + /** callback function */ + void (*callback)( + void *closure, + int status); + + /** closure of the callback */ + void *closure; +}; + +struct rcyn; +typedef struct rcyn rcyn_t; + +/** + * structure recording a rcyn client + */ +struct rcyn +{ + /** file descriptor of the socket */ + int fd; + + /** count of pending requests */ + int pending; + + /** type of link */ + rcyn_type_t type; + + /** protocol manager object */ + prot_t *prot; + + /** cache object */ + cache_t *cache; + + /** copy of the reply */ + struct { + /** count of fields of the reply */ + int count; + + /** fields (or fields) of the reply */ + const char **fields; + } reply; + + /** async */ + struct { + /** control callback */ + rcyn_async_ctl_t controlcb; + + /** closure */ + void *closure; + + /** reqests */ + asreq_t *requests; + } async; +}; + +static void disconnection(rcyn_t *rcyn); + +/** + * Flush the write buffer + */ +static +int +flushw( + rcyn_t *rcyn +) { + int rc; + struct pollfd pfd; + + rc = prot_should_write(rcyn->prot); + while (rc) { + rc = prot_write(rcyn->prot, rcyn->fd); + if (rc == -EAGAIN) { + pfd.fd = rcyn->fd; + pfd.events = POLLOUT; + do { rc = poll(&pfd, 1, 0); } while (rc < 0 && errno == EINTR); + if (rc < 0) + rc = -errno; + } + if (rc < 0) { + break; + } + rc = prot_should_write(rcyn->prot); + } + return rc; +} + +/** + * Put the command made of arguments ... + * Increment the count of pending requests. + * Return 0 in case of success or a negative number on error. + */ +static +int +putx( + rcyn_t *rcyn, + ... +) { + const char *p, *fields[MAXARGS]; + unsigned n; + va_list l; + int rc; + + /* reconstruct the array of arguments */ + va_start(l, rcyn); + n = 0; + p = va_arg(l, const char *); + while (p && n < MAXARGS) { + fields[n++] = p; + p = va_arg(l, const char *); + } + va_end(l); + + /* put it to the output buffer */ + rc = prot_put(rcyn->prot, n, fields); + if (rc == -ECANCELED) { + /* not enough room in the buffer, flush it */ + rc = flushw(rcyn); + if (rc == 0) + rc = prot_put(rcyn->prot, n, fields); + } + /* client always flushes */ + if (rc == 0) { + rcyn->pending++; + rc = flushw(rcyn); + } + return rc; +} + +static +int +wait_input( + rcyn_t *rcyn +) { + int rc; + struct pollfd pfd; + + pfd.fd = rcyn->fd; + pfd.events = POLLIN; + do { rc = poll(&pfd, 1, 0); } while (rc < 0 && errno == EINTR); + return rc < 0 ? -errno : 0; +} + +static +int +get_reply( + rcyn_t *rcyn +) { + int rc; + + prot_next(rcyn->prot); + rc = rcyn->reply.count = prot_get(rcyn->prot, &rcyn->reply.fields); + if (rc <= 0) + return rc; + if (0 != strcmp(rcyn->reply.fields[0], _clear_)) { + if (0 != strcmp(rcyn->reply.fields[0], _item_)) + rcyn->pending--; + return rc; + } + cache_clear(rcyn->cache); + return rcyn->reply.count = 0; +} + +static +int +wait_reply( + rcyn_t *rcyn, + bool block +) { + int rc; + + for(;;) { + prot_next(rcyn->prot); + rc = get_reply(rcyn); + if (rc > 0) + return rc; + if (rc < 0) { + rc = prot_read(rcyn->prot, rcyn->fd); + while (rc <= 0) { + if (rc == 0) + return -(errno = EPIPE); + if (rc == -EAGAIN && block) + rc = wait_input(rcyn); + if (rc < 0) + return rc; + rc = prot_read(rcyn->prot, rcyn->fd); + } + } + } +} + +static +int +flushr( + rcyn_t *rcyn +) { + int rc; + + do { rc = wait_reply(rcyn, false); } while(rc > 0); + return rc; +} + +static +int +status_done( + rcyn_t *rcyn +) { + return strcmp(rcyn->reply.fields[0], _done_) ? -ECANCELED : 0; +} + +static +int +status_check( + rcyn_t *rcyn +) { + return !strcmp(rcyn->reply.fields[0], _yes_) ? 1 + : !strcmp(rcyn->reply.fields[0], _no_) ? 0 + : -EEXIST; +} + +static +int +wait_pending_reply( + rcyn_t *rcyn +) { + int rc; + for (;;) { + rc = wait_reply(rcyn, true); + if (rc < 0) + return rc; + if (rc > 0 && rcyn->pending == 0) + return rc; + } +} + +static +int +wait_done( + rcyn_t *rcyn +) { + int rc = wait_pending_reply(rcyn); + if (rc > 0) + rc = status_done(rcyn); + return rc; +} + +static +int +async( + rcyn_t *rcyn, + int op, + uint32_t events +) { + return rcyn->async.controlcb + ? rcyn->async.controlcb(rcyn->async.closure, op, rcyn->fd, events) + : 0; +} + +static +void +disconnection( + rcyn_t *rcyn +) { + if (rcyn->fd >= 0) { + async(rcyn, EPOLL_CTL_DEL, 0); + close(rcyn->fd); + rcyn->fd = -1; + } +} + +static +int +connection( + rcyn_t *rcyn +) { + int rc; + const char *spec; + + /* socket spec */ + switch(rcyn->type) { + default: + case rcyn_Check: spec = rcyn_default_check_socket_spec; break; + case rcyn_Admin: spec = rcyn_default_admin_socket_spec; break; + } + + /* init the client */ + rcyn->pending = 0; + rcyn->reply.count = -1; + cache_clear(rcyn->cache); + prot_reset(rcyn->prot); + rcyn->fd = socket_open(spec, 0); + if (rcyn->fd < 0) + return -errno; + + /* negociate the protocol */ + rc = putx(rcyn, _rcyn_, "1", NULL); + if (rc >= 0) { + rc = wait_pending_reply(rcyn); + if (rc >= 0) { + rc = -EPROTO; + if (rcyn->reply.count == 2 + && 0 == strcmp(rcyn->reply.fields[0], _yes_) + && 0 == strcmp(rcyn->reply.fields[1], "1")) { + rc = async(rcyn, EPOLL_CTL_ADD, EPOLLIN); + if (rc >= 0) + return 0; + } + } + } + disconnection(rcyn); + return rc; +} + + +/************************************************************************************/ + +int +rcyn_open( + rcyn_t **prcyn, + rcyn_type_t type, + uint32_t cache_size +) { + rcyn_t *rcyn; + int rc; + + /* allocate the structure */ + *prcyn = rcyn = malloc(sizeof *rcyn); + if (rcyn == NULL) { + rc = -ENOMEM; + goto error; + } + + /* create a protocol object */ + rc = prot_create(&rcyn->prot); + if (rc < 0) + goto error2; + + /* record type and weakly create cache */ + cache_create(&rcyn->cache, cache_size < MIN_CACHE_SIZE ? MIN_CACHE_SIZE : cache_size); + rcyn->type = type; + rcyn->async.controlcb = NULL; + rcyn->async.closure = 0; + rcyn->async.requests = NULL; + + /* connection */ + rc = connection(rcyn); + if (rc < 0) + goto error3; + + /* done */ + return 0; + +error3: + free(rcyn->cache); + prot_destroy(rcyn->prot); +error2: + free(rcyn); +error: + *prcyn = NULL; + return rc; +} + +void +rcyn_close( + rcyn_t *rcyn +) { + rcyn_async_setup(rcyn, NULL, NULL); + disconnection(rcyn); + prot_destroy(rcyn->prot); + free(rcyn->cache); + free(rcyn); +} + +int +rcyn_enter( + rcyn_t *rcyn +) { + int rc; + + if (rcyn->type != rcyn_Admin) + return -EPERM; + if (rcyn->async.requests != NULL) + return -EINPROGRESS; + + rc = putx(rcyn, _enter_, NULL); + if (rc >= 0) + rc = wait_done(rcyn); + return rc; +} + +int +rcyn_leave( + rcyn_t *rcyn, + bool commit +) { + int rc; + + if (rcyn->type != rcyn_Admin) + return -EPERM; + if (rcyn->async.requests != NULL) + return -EINPROGRESS; + + rc = putx(rcyn, _leave_, commit ? _commit_ : NULL/*default: rollback*/, NULL); + if (rc >= 0) + rc = wait_done(rcyn); + return rc; +} + +static +int +check_or_test( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission, + const char *action +) { + int rc; + + if (rcyn->async.requests != NULL) + return -EINPROGRESS; + + /* ensure there is no clear cache pending */ + flushr(rcyn); + + /* check cache item */ + rc = cache_search(rcyn->cache, client, session, user, permission); + if (rc >= 0) + return rc; + + /* send the request */ + rc = putx(rcyn, action, client, session, user, permission, NULL); + if (rc >= 0) { + /* get the response */ + rc = wait_pending_reply(rcyn); + if (rc >= 0) { + rc = status_check(rcyn); + if (rc >= 0) + cache_put(rcyn->cache, client, session, user, permission, rc); + } + } + return rc; +} + +int +rcyn_check( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission +) { + return check_or_test(rcyn, client, session, user, permission, _check_); +} + +int +rcyn_test( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission +) { + return check_or_test(rcyn, client, session, user, permission, _check_); +} + +int +rcyn_set( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission, + int value +) { + char val[30]; + int rc; + + if (rcyn->type != rcyn_Admin) + return -EPERM; + if (rcyn->async.requests != NULL) + return -EINPROGRESS; + + snprintf(val, sizeof val, "%u", (unsigned)value); + rc = putx(rcyn, _set_, client, session, user, permission, val, NULL); + if (rc >= 0) + rc = wait_done(rcyn); + return rc; +} + +int +rcyn_get( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission, + void (*callback)( + void *closure, + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value + ), + void *closure +) { + int rc; + + if (rcyn->type != rcyn_Admin) + return -EPERM; + if (rcyn->async.requests != NULL) + return -EINPROGRESS; + + 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_)) { + 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])); + rc = wait_reply(rcyn, true); + } + rc = status_done(rcyn); + } + return rc; +} + +int +rcyn_drop( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission +) { + int rc; + + if (rcyn->type != rcyn_Admin) + return -EPERM; + if (rcyn->async.requests != NULL) + return -EINPROGRESS; + + rc = putx(rcyn, _drop_, client, session, user, permission, NULL); + if (rc >= 0) + rc = wait_done(rcyn); + return rc; +} + +/************************************************************************************/ + +int +rcyn_cache_resize( + rcyn_t *rcyn, + uint32_t size +) { + return cache_resize(&rcyn->cache, size < MIN_CACHE_SIZE ? MIN_CACHE_SIZE : size); +} + +void +rcyn_cache_clear( + rcyn_t *rcyn +) { + cache_clear(rcyn->cache); +} + +int +rcyn_cache_check( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission +) { + return cache_search(rcyn->cache, client, session, user, permission); +} + + +/************************************************************************************/ + +int +rcyn_async_setup( + rcyn_t *rcyn, + rcyn_async_ctl_t controlcb, + void *closure +) { + asreq_t *ar; + + /* cancel pending requests */ + while((ar = rcyn->async.requests) != NULL) { + rcyn->async.requests = ar->next; + ar->callback(ar->closure, -ECANCELED); + free(ar); + } + /* remove existing polling */ + async(rcyn, EPOLL_CTL_DEL, 0); + /* records new data */ + rcyn->async.controlcb = controlcb; + rcyn->async.closure = closure; + /* record to polling */ + return async(rcyn, EPOLL_CTL_ADD, EPOLLIN); +} + +int +rcyn_async_process( + rcyn_t *rcyn +) { + int rc; + const char *first; + asreq_t *ar; + + for (;;) { + /* non blocking wait for a reply */ + rc = wait_reply(rcyn, false); + if (rc < 0) + return rc == -EAGAIN ? 0 : rc; + + /* skip empty replies */ + if (rc == 0) + continue; + + /* skip done/error replies */ + first = rcyn->reply.fields[0]; + if (!strcmp(first, _done_) + || !strcmp(first, _error_)) + continue; + + /* ignore unexpected answers */ + ar = rcyn->async.requests; + if (ar == NULL) + continue; + + /* emit the asynchronous answer */ + rcyn->async.requests = ar->next; + rc = status_check(rcyn); + ar->callback(ar->closure, rc); + free(ar); + } +} + +int +rcyn_async_check( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission, + bool simple, + void (*callback)( + void *closure, + int status), + void *closure +) { + int rc; + asreq_t **pr, *ar; + + /* allocate */ + ar = malloc(sizeof *ar); + if (ar == NULL) + return -ENOMEM; + + /* init */ + ar->next = NULL; + ar->callback = callback; + ar->closure = closure; + + /* send the request */ + rc = putx(rcyn, simple ? _test_ : _check_, + client, session, user, permission, NULL); + if (rc >= 0) + rc = flushw(rcyn); + if (rc < 0) { + free(ar); + return rc; + } + + /* record the request */ + pr = &rcyn->async.requests; + while(*pr != NULL) + pr = &(*pr)->next; + *pr = ar; + return 0; +} + + diff --git a/src/rcyn-client.h b/src/rcyn-client.h new file mode 100644 index 0000000..6706820 --- /dev/null +++ b/src/rcyn-client.h @@ -0,0 +1,149 @@ + +#pragma once + +typedef enum rcyn_type { + rcyn_Check, + rcyn_Admin +} rcyn_type_t; + +struct rcyn; +typedef struct rcyn rcyn_t; + +extern +int +rcyn_open( + rcyn_t **rcyn, + rcyn_type_t type, + uint32_t cache_size +); + +extern +void +rcyn_close( + rcyn_t *rcyn +); + +extern +int +rcyn_enter( + rcyn_t *rcyn +); + +extern +int +rcyn_leave( + rcyn_t *rcyn, + bool commit +); + +extern +int +rcyn_check( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission +); + +extern +int +rcyn_test( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission +); + +extern +int +rcyn_set( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission, + int value +); + +extern +int +rcyn_get( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission, + void (*callback)( + void *closure, + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value + ), + void *closure +); + +extern +int +rcyn_drop( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission +); + +extern +void +rcyn_cache_clear( + rcyn_t *rcyn +); + +extern +int +rcyn_cache_check( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission +); + +typedef int (*rcyn_async_ctl_t)( + void *closure, + int op, + int fd, + uint32_t events); + +extern +int +rcyn_async_setup( + rcyn_t *rcyn, + rcyn_async_ctl_t controlcb, + void *closure +); + +extern +int +rcyn_async_process( + rcyn_t *rcyn +); + +extern +int +rcyn_async_check( + rcyn_t *rcyn, + const char *client, + const char *session, + const char *user, + const char *permission, + bool test, + void (*callback)( + void *closure, + int status), + void *closure +); + diff --git a/src/rcyn-protocol.c b/src/rcyn-protocol.c new file mode 100644 index 0000000..c656570 --- /dev/null +++ b/src/rcyn-protocol.c @@ -0,0 +1,33 @@ +#include "rcyn-protocol.h" + +const char + _check_[] = "check", + _drop_[] = "drop", + _enter_[] = "enter", + _get_[] = "get", + _leave_[] = "leave", + _rcyn_[] = "rcyn", + _set_[] = "set", + _test_[] = "test"; + +const char + _commit_[] = "commit", + _rollback_[] = "rollback"; + +const char + _clear_[] = "clear", + _done_[] = "done", + _error_[] = "error", + _item_[] = "item", + _no_[] = "no", + _yes_[] = "yes"; + +#if !defined(RCYN_DEFAULT_CHECK_SOCKET_SPEC) +# define RCYN_DEFAULT_CHECK_SOCKET_SPEC "unix:/run/platform/cynara.check" +#endif +#if !defined(RCYN_DEFAULT_ADMIN_SOCKET_SPEC) +# define RCYN_DEFAULT_ADMIN_SOCKET_SPEC "unix:/run/platform/cynara.admin" +#endif +const char + rcyn_default_check_socket_spec[] = RCYN_DEFAULT_CHECK_SOCKET_SPEC, + rcyn_default_admin_socket_spec[] = RCYN_DEFAULT_ADMIN_SOCKET_SPEC; diff --git a/src/rcyn-protocol.h b/src/rcyn-protocol.h new file mode 100644 index 0000000..8906194 --- /dev/null +++ b/src/rcyn-protocol.h @@ -0,0 +1,28 @@ +#pragma once + +extern const char + _check_[], + _drop_[], + _enter_[], + _get_[], + _leave_[], + _rcyn_[], + _set_[], + _test_[]; + +extern const char + _commit_[], + _rollback_[]; + +extern const char + _clear_[], + _done_[], + _error_[], + _item_[], + _no_[], + _yes_[]; + +extern const char + rcyn_default_check_socket_spec[], + rcyn_default_admin_socket_spec[]; + diff --git a/src/rcyn-protocol.txt b/src/rcyn-protocol.txt new file mode 100644 index 0000000..134d05e --- /dev/null +++ b/src/rcyn-protocol.txt @@ -0,0 +1,79 @@ +protocol +======== + +hello: + + c->s rcyn 1 + s->c yes 1 + +invalidate cache: + + s->c clear + +test a permission: + + c->s test CLIENT SESSION USER PERMISSION + s->c yes|no|VALUE [CACHING] + +check a permission: + + c->s check CLIENT SESSION USER PERMISSION + s->c yes|no|VALUE [CACHING] + +erase (admin): + + c->s drop CLIENT SESSION USER PERMISSION + s->c done|error ... + +set (admin): + + c->s set CLIENT SESSION USER PERMISSION VALUE + s->c done|error ... + +list permissions (admin): + + c->s get CLIENT SESSION USER PERMISSION + s->c item CLIENT SESSION USER PERMISSION VALUE + s->c ... + s->c done + +enter critical (admin) + + c->s enter + s->c done + +leave critical (admin) + + c->s leave [commit|rollback] + s->c done|error ... + +register agent (agent): + + c->s agent NAME [ARGS...] + s->c done|error ... + +asking (agent ask CLIENT SESSION USER PERMISSION): + + s->c ask CLIENT SESSION USER PERMISSION + c->s done | ([yes|no] [always|session|one-time]) + + +---------------------------------------------------------- + c->s c(heck) CLIENT SESSION USER PERMISSION + c->s d(rop) CLIENT SESSION USER PERMISSION + c->s e(nter) + 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 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] + + diff --git a/src/rcyn-server.c b/src/rcyn-server.c new file mode 100644 index 0000000..4fc365e --- /dev/null +++ b/src/rcyn-server.c @@ -0,0 +1,669 @@ +#define _GNU_SOURCE + +#include <stdbool.h> +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <poll.h> +#include <sys/epoll.h> +#include <sys/types.h> +#include <sys/socket.h> + +#if defined(WITH_SYSTEMD_ACTIVATION) +#include <systemd/sd-daemon.h> +#endif + +#include "prot.h" +#include "cyn.h" +#include "rcyn-protocol.h" +#include "socket.h" + +typedef enum rcyn_type { + rcyn_Check, + rcyn_Admin +} rcyn_type_t; + +/** structure for using epoll easily */ +typedef struct pollitem pollitem_t; +struct pollitem +{ + /** callback on event */ + void (*handler)(pollitem_t *pollitem, uint32_t events, int pollfd); + + /** data */ + union { + void *closure; + int fd; + }; +}; + +static +int +pollitem_do( + pollitem_t *pollitem, + uint32_t events, + int fd, + int pollfd, + int op +) { + struct epoll_event ev = { .events = events, .data.ptr = pollitem }; + return epoll_ctl(pollfd, op, fd, &ev); +} + +static +int +pollitem_add( + pollitem_t *pollitem, + uint32_t events, + int fd, + int pollfd +) { + return pollitem_do(pollitem, events, fd, pollfd, EPOLL_CTL_ADD); +} + +#if 0 +static +int +pollitem_mod( + pollitem_t *pollitem, + uint32_t events, + int fd, + int pollfd +) { + return pollitem_do(pollitem, events, fd, pollfd, EPOLL_CTL_MOD); +} +#endif + +static +int +pollitem_del( + int fd, + int pollfd +) { + return pollitem_do(NULL, 0, fd, pollfd, EPOLL_CTL_DEL); +} + + +/** structure that represents a rcyn client */ +struct client +{ + /** a protocol structure */ + prot_t *prot; + + /** the in/out file descriptor */ + int fd; + + /** type of client */ + rcyn_type_t type; + + /** the version of the protocol used */ + unsigned version: 4; + + /** is relaxed version of the protocol */ + unsigned relax: 1; + + /** is the actual link invalid or valid */ + unsigned invalid: 1; + + /** enter/leave status, record if entered */ + unsigned entered: 1; + + /** enter/leave status, record if entring pending */ + unsigned entering: 1; + + /** indicate if some check were made */ + unsigned checked: 1; + + /** polling callback */ + pollitem_t pollitem; +}; +typedef struct client client_t; + +/** + * Check 'arg' against 'value' beginning at offset accepting it if 'arg' prefixes 'value' + * Returns 1 if matching or 0 if not. + */ +static +bool +ckarg( + const char *arg, + const char *value, + unsigned offset +) { + while(arg[offset]) + if (arg[offset] == value[offset]) + offset++; + else + return false; + return true; +} + +/** + * Flush the write buffer + */ +static +int +flushw( + client_t *cli +) { + int rc; + struct pollfd pfd; + + rc = prot_should_write(cli->prot); + while (rc) { + rc = prot_write(cli->prot, cli->fd); + if (rc == -EAGAIN) { + pfd.fd = cli->fd; + pfd.events = POLLOUT; + do { rc = poll(&pfd, 1, 0); } while (rc < 0 && errno == EINTR); + } + if (rc < 0) { + break; + } + rc = prot_should_write(cli->prot); + } + return rc; +} + +/** + * Send a reply to client + */ +static +int +putx( + client_t *cli, + ... +) { + const char *p, *fields[MAXARGS]; + unsigned n; + va_list l; + int rc; + + va_start(l, cli); + n = 0; + p = va_arg(l, const char *); + while (p) { + if (n == MAXARGS) + return -EINVAL; + fields[n++] = p; + p = va_arg(l, const char *); + } + va_end(l); + rc = prot_put(cli->prot, n, fields); + if (rc == -ECANCELED) { + rc = flushw(cli); + if (rc == 0) + rc = prot_put(cli->prot, n, fields); + } + return rc; +} + +/** emit a simple done reply and flush */ +static +void +send_done( + client_t *cli +) { + putx(cli, _done_, NULL); + flushw(cli); +} + +/** emit a simple error reply and flush */ +static +void +send_error( + client_t *cli, + const char *errorstr +) { + putx(cli, _error_, errorstr, NULL); + flushw(cli); +} + +/** emit a simple done/error reply */ +static +void +send_done_or_error( + client_t *cli, + int status +) { + if (status >= 0) + send_done(cli); + else + send_error(cli, NULL); +} + +/** callback of entering */ +static +void +entercb( + void *closure +) { + client_t *cli = closure; + + cli->entered = true; + cli->entering = false; + send_done(cli); +} + +/** callback of checking */ +static +void +checkcb( + void *closure, + uint32_t value +) { + 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); + flushw(cli); +} + +/** callback of getting list of entries */ +static +void +getcb( + void *closure, + const char *client, + const char *session, + const char *user, + const char *permission, + uint32_t value +) { + client_t *cli = closure; + char val[30]; + snprintf(val, sizeof val, "%d", value); + putx(cli, _item_, client, session, user, permission, val, NULL); +} + +/** handle a request */ +static +void +onrequest( + client_t *cli, + int count, + const char *args[] +) { + int rc; + uint32_t value; + + /* just ignore empty lines */ + if (count == 0) + return; + + /* version hand-shake */ + if (!cli->version) { + if (!ckarg(args[0], _rcyn_, 0) || count != 2 || !ckarg(args[1], "1", 0)) + goto invalid; + putx(cli, _yes_, "1", NULL); + flushw(cli); + cli->version = 1; + return; + } + + switch(args[0][0]) { + case 'c': /* check */ + if (ckarg(args[0], _check_, 1) && count == 5) { + cli->checked = 1; + cyn_check_async(checkcb, cli, args[1], args[2], args[3], args[4]); + return; + } + break; + case 'd': /* drop */ + if (ckarg(args[0], _drop_, 1) && count == 5) { + if (cli->type != rcyn_Admin) + break; + if (!cli->entered) + break; + rc = cyn_drop(args[1], args[2], args[3], args[4]); + send_done_or_error(cli, rc); + return; + } + break; + case 'e': /* enter */ + if (ckarg(args[0], _enter_, 1) && count == 1) { + if (cli->type != rcyn_Admin) + break; + if (cli->entered || cli->entering) + break; + cli->entering = true; + /* TODO: remove from polling until entered? */ + cyn_enter_async(entercb, cli); + return; + } + break; + case 'g': /* get */ + if (ckarg(args[0], _get_, 1) && count == 5) { + if (cli->type != rcyn_Admin) + break; + cyn_list(cli, getcb, args[1], args[2], args[3], args[4]); + send_done(cli); + return; + } + break; + case 'l': /* leave */ + if (ckarg(args[0], _leave_, 1) && count <= 2) { + if (cli->type != rcyn_Admin) + break; + if (count == 2 && !ckarg(args[1], _commit_, 0) && !ckarg(args[1], _rollback_, 0)) + break; + if (!cli->entered) + break; + rc = cyn_leave(cli, count == 2 && ckarg(args[1], _commit_, 0)); + cli->entered = false; + send_done_or_error(cli, rc); + return; + } + break; + case 's': /* set */ + if (ckarg(args[0], _set_, 1) && count == 6) { + 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); + 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); + return; + } + break; + } +invalid: /* invalid rest detected */ + send_error(cli, "invalid"); + if (!cli->relax) + cli->invalid = 1; +} + +/** on change callback, emits a clear for caching */ +static +void +onchange( + void *closure +) { + client_t *cli = closure; + if (cli->checked) { + cli->checked = false; + putx(cli, _clear_, NULL); + flushw(cli); + } +} + +/** destroy a client */ +static +void +destroy_client( + client_t *cli, + bool closefds +) { + if (closefds) + close(cli->fd); + if (cli->entering) + cyn_enter_async_cancel(entercb, cli); + if (cli->entered) + cyn_leave(cli, false); + cyn_on_change_remove(onchange, cli); + prot_destroy(cli->prot); + free(cli); +} + +/** handle client requests */ +static +void +on_client_event( + pollitem_t *pollitem, + uint32_t events, + int pollfd +) { + int nargs, nr; + const char **args; + client_t *cli = pollitem->closure; + + /* is it a hangup? */ + if (events & EPOLLHUP) + goto terminate; + + /* possible input */ + if (events & EPOLLIN) { + nr = prot_read(cli->prot, cli->fd); + if (nr <= 0) + goto terminate; + nargs = prot_get(cli->prot, &args); + while (nargs >= 0) { + onrequest(cli, nargs, args); + if (cli->invalid && !cli->relax) + goto terminate; + prot_next(cli->prot); + nargs = prot_get(cli->prot, &args); + } + } + return; + + /* terminate the client session */ +terminate: + pollitem_del(cli->fd, pollfd); + destroy_client(cli, true); +} + +/** create a client */ +static +int +create_client( + client_t **pcli, + int fd, + rcyn_type_t type +) { + client_t *cli; + int rc; + + /* allocate the object */ + *pcli = cli = calloc(1, sizeof *cli); + if (cli == NULL) { + rc = -ENOMEM; + goto error; + } + + /* create protocol object */ + rc = prot_create(&cli->prot); + if (rc < 0) + goto error2; + + /* monitor change and caching */ + rc = cyn_on_change_add(onchange, cli); + if (rc < 0) + goto error3; + + /* records the file descriptor */ + cli->fd = fd; + cli->type = type; + cli->version = 0; /* version not set */ + cli->relax = true; /* relax on error */ + cli->invalid = false; /* not invalid */ + cli->entered = false; /* not entered */ + cli->entering = false; /* not entering */ + cli->checked = false; /* no check made */ + cli->pollitem.handler = on_client_event; + cli->pollitem.closure = cli; + return 0; +error3: + prot_destroy(cli->prot); +error2: + free(cli); +error: + *pcli = NULL; + return rc; +} + +/** handle server events */ +static +void +on_server_event( + pollitem_t *pollitem, + uint32_t events, + int pollfd, + rcyn_type_t type +) { + int servfd = pollitem->fd; + int fd, rc; + struct sockaddr saddr; + socklen_t slen; + client_t *cli; + + /* is it a hangup? it shouldn't! */ + if (events & EPOLLHUP) { + fprintf(stderr, "unexpected server socket closing\n"); + exit(2); + } + + /* EPOLLIN is the only expected event but asserting makes fear */ + if (!(events & EPOLLIN)) + return; + + /* accept the connection */ + slen = (socklen_t)sizeof saddr; + fd = accept(servfd, &saddr, &slen); + if (fd < 0) { + fprintf(stderr, "can't accept connection: %m\n"); + return; + } + fcntl(fd, F_SETFD, FD_CLOEXEC); + fcntl(fd, F_SETFL, O_NONBLOCK); + + /* create a client for the connection */ + rc = create_client(&cli, fd, type); + if (rc < 0) { + fprintf(stderr, "can't create client connection: %s\n", strerror(-rc)); + close(fd); + return; + } + + /* add the client to the epolling */ + rc = pollitem_add(&cli->pollitem, EPOLLIN, fd, pollfd); + if (rc < 0) { + fprintf(stderr, "can't poll client connection: %s\n", strerror(-rc)); + destroy_client(cli, 1); + return; + } +} + +/** handle check server events */ +static +void +on_check_server_event( + pollitem_t *pollitem, + uint32_t events, + int pollfd +) { + on_server_event(pollitem, events, pollfd, rcyn_Check); +} + +/** handle admin server events */ +static +void +on_admin_server_event( + pollitem_t *pollitem, + uint32_t events, + int pollfd +) { + on_server_event(pollitem, events, pollfd, rcyn_Admin); +} + +int main(int ac, char **av) +{ + int rc; + pollitem_t pi_admin, pi_check, *pi; + int pollfd, fd; + struct epoll_event ev; + const char *check_spec = rcyn_default_check_socket_spec; + const char *admin_spec = rcyn_default_admin_socket_spec; + + /* + * future possible options: + * - strict/relax + * - databse name(s) + * - socket name + * - policy + */ + + /* connection to the database */ + rc = cyn_init(); + if (rc < 0) { + fprintf(stderr, "can't initialise database: %m\n"); + return 1; + } + + /* create the polling fd */ + pollfd = epoll_create1(EPOLL_CLOEXEC); + if (pollfd < 0) { + fprintf(stderr, "can't create polling: %m\n"); + return 1; + } + + /* create the admin server socket */ + fd = socket_open(admin_spec, 1); + if (fd < 0) { + fprintf(stderr, "can't create admin server socket: %m\n"); + return 1; + } + + /* add the server to pollfd */ + pi_admin.handler = on_admin_server_event; + pi_admin.fd = fd; + rc = pollitem_add(&pi_admin, EPOLLIN, fd, pollfd); + if (rc < 0) { + fprintf(stderr, "can't poll admin server: %m\n"); + return 1; + } + + /* create the server socket */ + fd = socket_open(check_spec, 1); + if (fd < 0) { + fprintf(stderr, "can't create check server socket: %m\n"); + return 1; + } + + /* add the server to pollfd */ + pi_check.handler = on_check_server_event; + pi_check.fd = fd; + rc = pollitem_add(&pi_check, EPOLLIN, fd, pollfd); + if (rc < 0) { + fprintf(stderr, "can't poll check server: %m\n"); + return 1; + } + + /* ready ! */ +#if defined(WITH_SYSTEMD_ACTIVATION) + sd_notify(0, "READY=1"); +#endif + + /* process inputs */ + for(;;) { + rc = epoll_wait(pollfd, &ev, 1, -1); + if (rc == 1) { + pi = ev.data.ptr; + pi->handler(pi, ev.events, pollfd); + } + } +} + diff --git a/src/socket.c b/src/socket.c new file mode 100644 index 0000000..3ab0408 --- /dev/null +++ b/src/socket.c @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2015-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. + */ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <endian.h> +#include <netdb.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> + +#if defined(WITH_SYSTEMD_ACTIVATION) +#include <systemd/sd-daemon.h> +#endif + +#include "socket.h" + +#define BACKLOG 30 + +/******************************************************************************/ + +/** + * known types + */ +enum type { + /** type internet */ + Type_Inet, + + /** type systemd */ + Type_Systemd, + + /** type Unix */ + Type_Unix +}; + +/** + * Structure for known entries + */ +struct entry +{ + /** the known prefix */ + const char *prefix; + + /** the type of the entry */ + unsigned type: 2; + + /** should not set SO_REUSEADDR for servers */ + unsigned noreuseaddr: 1; + + /** should not call listen for servers */ + unsigned nolisten: 1; +}; + +/** + * The known entries with the default one at the first place + */ +static struct entry entries[] = { + { + .prefix = "tcp:", + .type = Type_Inet + }, + { + .prefix = "sd:", + .type = Type_Systemd, + .noreuseaddr = 1, + .nolisten = 1 + }, + { + .prefix = "unix:", + .type = Type_Unix + } +}; + +/******************************************************************************/ + +/** + * open a unix domain socket for client or server + * + * @param spec the specification of the path (prefix with @ for abstract) + * @param server 0 for client, server otherwise + * + * @return the file descriptor number of the socket or -1 in case of error + */ +static int open_unix(const char *spec, int server) +{ + int fd, rc, abstract; + struct sockaddr_un addr; + size_t length; + + abstract = spec[0] == '@'; + + /* check the length */ + length = strlen(spec); + if (length >= 108) { + errno = ENAMETOOLONG; + return -1; + } + + /* remove the file on need */ + if (server && !abstract) + unlink(spec); + + /* create a socket */ + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return fd; + + /* prepare address */ + memset(&addr, 0, sizeof addr); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, spec); + if (abstract) + addr.sun_path[0] = 0; /* implement abstract sockets */ + + if (server) { + rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr)); + } else { + rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr)); + } + if (rc < 0) { + close(fd); + return rc; + } + return fd; +} + +/** + * open a tcp socket for client or server + * + * @param spec the specification of the host:port/... + * @param server 0 for client, server otherwise + * + * @return the file descriptor number of the socket or -1 in case of error + */ +static int open_tcp(const char *spec, int server) +{ + int rc, fd; + const char *service, *host, *tail; + struct addrinfo hint, *rai, *iai; + + /* scan the uri */ + tail = strchrnul(spec, '/'); + service = strchr(spec, ':'); + if (service == NULL || tail < service) { + errno = EINVAL; + return -1; + } + host = strndupa(spec, service++ - spec); + service = strndupa(service, tail - service); + + /* get addr */ + memset(&hint, 0, sizeof hint); + hint.ai_family = AF_INET; + hint.ai_socktype = SOCK_STREAM; + rc = getaddrinfo(host, service, &hint, &rai); + if (rc != 0) { + errno = EINVAL; + return -1; + } + + /* get the socket */ + iai = rai; + while (iai != NULL) { + fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol); + if (fd >= 0) { + if (server) { + rc = bind(fd, iai->ai_addr, iai->ai_addrlen); + } else { + rc = connect(fd, iai->ai_addr, iai->ai_addrlen); + } + if (rc == 0) { + freeaddrinfo(rai); + return fd; + } + close(fd); + } + iai = iai->ai_next; + } + freeaddrinfo(rai); + return -1; +} + +/** + * open a systemd socket for server + * + * @param spec the specification of the systemd name + * + * @return the file descriptor number of the socket or -1 in case of error + */ +static int open_systemd(const char *spec) +{ +#if defined(WITH_SYSTEMD_ACTIVATION) + char **names; + int fd = -1; + int c = sd_listen_fds_with_names(0, &names); + if (c < 0) + errno = -c; + else if (names) { + for (c = 0 ; names[c] ; c++) { + if (!strcmp(names[c], spec)) + fd = SD_LISTEN_FDS_START + c; + free(names[c]); + } + free(names); + } + if (fd < 0 && '0' <= *spec && *spec <= '9') + fd = SD_LISTEN_FDS_START + atoi(spec); + return fd; +#else + errno = EAFNOSUPPORT; + return -1; +#endif +} + +/******************************************************************************/ + +/** + * Get the entry of the uri by searching to its prefix + * + * @param uri the searched uri + * @param offset where to store the prefix length + * + * @return the found entry or the default one + */ +static struct entry *get_entry(const char *uri, int *offset) +{ + int l, i = (int)(sizeof entries / sizeof * entries); + + for (;;) { + if (!i) { + l = 0; + break; + } + i--; + l = (int)strlen(entries[i].prefix); + if (!strncmp(uri, entries[i].prefix, l)) + break; + } + + *offset = l; + return &entries[i]; +} + +/** + * open socket for client or server + * + * @param uri the specification of the socket + * @param server 0 for client, server otherwise + * + * @return the file descriptor number of the socket or -1 in case of error + */ +int socket_open(const char *uri, int server) +{ + int fd, rc, offset; + struct entry *e; + + /* search for the entry */ + e = get_entry(uri, &offset); + + /* get the names */ + uri += offset; + + /* open the socket */ + switch (e->type) { + case Type_Unix: + fd = open_unix(uri, server); + break; + case Type_Inet: + fd = open_tcp(uri, server); + break; + case Type_Systemd: + if (server) + fd = open_systemd(uri); + else { + errno = EINVAL; + fd = -1; + } + break; + default: + errno = EAFNOSUPPORT; + fd = -1; + break; + } + if (fd < 0) + return -1; + + /* set it up */ + fcntl(fd, F_SETFD, FD_CLOEXEC); + fcntl(fd, F_SETFL, O_NONBLOCK); + if (server) { + if (!e->noreuseaddr) { + rc = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc); + } + if (!e->nolisten) + listen(fd, BACKLOG); + } + return fd; +} + diff --git a/src/socket.h b/src/socket.h new file mode 100644 index 0000000..b5989e1 --- /dev/null +++ b/src/socket.h @@ -0,0 +1,21 @@ +/* + * 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 socket_open(const char *uri, int server); + diff --git a/src/test-lib-compat.c b/src/test-lib-compat.c new file mode 100644 index 0000000..627c208 --- /dev/null +++ b/src/test-lib-compat.c @@ -0,0 +1,256 @@ +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/epoll.h> + +#include <cynara/cynara-admin.h> +#include <cynara/cynara-client-async.h> +#include <cynara/cynara-client.h> + +#define STD 0 +#define TEST 1 +#define CACHE 2 + +#define STRFY(x) #x +#define CKEX(x) ckex((x),STD,__LINE__,STRFY(x)) +#define TEEX(x) ckex((x),TEST,__LINE__,STRFY(x)) +#define CAEX(x) ckex((x),CACHE,__LINE__,STRFY(x)) + +struct cynara_admin *admin; +struct cynara_async_configuration *aconf; +struct cynara_async *aclient; +struct cynara_configuration *conf; +struct cynara *client; +char buffer[4000]; +char *str[40]; +int nstr; +int pollfd; + +#define BUCKET "BUCK" + +void ckex(int rc, int type, int line, const char *x) +{ + int err = 1; + switch(type) { + case STD: + err = (rc != CYNARA_API_SUCCESS); + break; + case TEST: + err = (rc != CYNARA_API_ACCESS_DENIED + && rc != CYNARA_API_ACCESS_ALLOWED + && rc != CYNARA_API_ACCESS_NOT_RESOLVED); + break; + case CACHE: + err = (rc != CYNARA_API_ACCESS_DENIED + && rc != CYNARA_API_ACCESS_ALLOWED + && rc != CYNARA_API_ACCESS_NOT_RESOLVED + && rc != CYNARA_API_CACHE_MISS); + break; + } + if (err) { + char buffer[200]; + cynara_strerror(rc, buffer, 200); + printf("ERROR(%d) %s by %s line %d\n", rc, buffer, x, line); + exit(1); + } + printf("SUCCESS %s\n", x); +} + +int is(const char *first, const char *second, int mincount) +{ + return nstr >= mincount + 2 + && !strcmp(first, str[0]) + && !strcmp(second, str[1]); +} + +void adm_list(char *cli, char *usr, char *perm) +{ + int i; + struct cynara_admin_policy **policies; + CKEX(cynara_admin_list_policies(admin, BUCKET, cli, usr, perm, &policies)); + i = 0; + while(policies[i]) { + printf("%s %s %s %s %d %s\n", + policies[i]->bucket, + policies[i]->client, + policies[i]->user, + policies[i]->privilege, + policies[i]->result, + policies[i]->result_extra ?: ""); + free(policies[i]->bucket); + free(policies[i]->client); + free(policies[i]->user); + free(policies[i]->privilege); + free(policies[i]->result_extra); + free(policies[i]); + i++; + } + free(policies); +} + +void adm_check(char *cli, char *usr, char *perm) +{ + char *rs; + int ri; + + CKEX(cynara_admin_check(admin, BUCKET, 1, cli, usr, perm, &ri, &rs)); + printf("got %d %s \n", ri, rs ?: "NULL"); +} + +void adm_set() +{ + struct cynara_admin_policy **policies, *p; + int n, i; + + n = (nstr - 2) / 4; + policies = malloc((1 + n) * sizeof *policies + n * sizeof **policies); + policies[n] = NULL; + p = (struct cynara_admin_policy*)(&policies[n + 1]); + for (i = 0 ; i < n ; i++, p++) { + policies[i] = p; + p->bucket = BUCKET; + p->client = str[2 + i * 4 + 0]; + p->user = str[2 + i * 4 + 1]; + p->privilege = str[2 + i * 4 + 2]; + p->result = atoi(str[2 + i * 4 + 3]); + p->result_extra = NULL; + } + CKEX(cynara_admin_set_policies(admin, (const struct cynara_admin_policy *const *)policies)); + free(policies); +} + +void adm_erase(char *cli, char *usr, char *perm) +{ + CKEX(cynara_admin_erase(admin, BUCKET, 1, cli, usr, perm)); +} + +void adm_desc() +{ + int i; + struct cynara_admin_policy_descr **d; + CKEX(cynara_admin_list_policies_descriptions(admin, &d)); + i = 0; + while(d[i]) { + printf("desc[%d] %d -> %s\n", i, d[i]->result, d[i]->name); + free(d[i]->name); + free(d[i]); + i++; + } + free(d); +} + +void asy_cache(char *cli, char *ses, char *usr, char *perm) +{ + CAEX(cynara_async_check_cache(aclient, cli, ses, usr, perm)); +} + +void asyncb(cynara_check_id check_id, cynara_async_call_cause cause, + int response, void *user_response_data) +{ + printf("RECEIVE %d %d\n", cause, response); +} + +void asy_check(char *cli, char *ses, char *usr, char *perm, int simple) +{ + if (simple) + CKEX(cynara_async_create_simple_request(aclient, cli, ses, usr, perm, NULL, asyncb, NULL)); + else + CKEX(cynara_async_create_request(aclient, cli, ses, usr, perm, NULL, asyncb, NULL)); +} + +void syn_check(char *cli, char *ses, char *usr, char *perm, int simple) +{ + if (simple) + TEEX(cynara_simple_check(client, cli, ses, usr, perm)); + else + TEEX(cynara_check(client, cli, ses, usr, perm)); +} + +void asyncstscb(int old_fd, int new_fd, cynara_async_status status, void *data) +{ + struct epoll_event ev; + + ev.data.fd = new_fd; + ev.events = (status == CYNARA_STATUS_FOR_RW ? EPOLLOUT : 0)|EPOLLIN; + if (old_fd == new_fd) { + if (new_fd != -1) + epoll_ctl(pollfd, EPOLL_CTL_MOD, new_fd, &ev); + } else { + if (old_fd != -1) + epoll_ctl(pollfd, EPOLL_CTL_DEL, old_fd, &ev); + if (new_fd != -1) + epoll_ctl(pollfd, EPOLL_CTL_ADD, new_fd, &ev); + } +} + +int main(int ac, char **av) +{ + struct epoll_event ev; + + pollfd = epoll_create(10); + ev.data.fd = 0; + ev.events = EPOLLIN; + epoll_ctl(pollfd, EPOLL_CTL_ADD, 0, &ev); + + CKEX(cynara_admin_initialize(&admin)); + + CKEX(cynara_async_configuration_create(&aconf)); + CKEX(cynara_async_configuration_set_cache_size(aconf, 1000)); + CKEX(cynara_async_initialize(&aclient, aconf, asyncstscb, NULL)); + cynara_async_configuration_destroy(aconf); + + CKEX(cynara_configuration_create(&conf)); + CKEX(cynara_configuration_set_cache_size(conf, 1000)); + CKEX(cynara_initialize(&client, conf)); + cynara_configuration_destroy(conf); + + for(;;) { + epoll_wait(pollfd, &ev, 1, -1); + + if (ev.data.fd) { + cynara_async_process(aclient); + continue; + } + + if (!fgets(buffer, sizeof buffer, stdin)) + break; + + str[nstr = 0] = strtok(buffer, " \t\n"); + while(str[nstr]) + str[++nstr] = strtok(NULL, " \t\n"); + + if (is("admin", "listall", 0)) + adm_list("#", "#", "#"); + else if (is("admin", "list", 3)) + adm_list(str[2], str[3], str[4]); + else if (is("admin", "check", 3)) + adm_check(str[2], str[3], str[4]); + else if (is("admin", "set", 4)) + adm_set(); + else if (is("admin", "erase", 3)) + adm_erase(str[2], str[3], str[4]); + else if (is("admin", "desc", 0)) + adm_desc(); + else if (is("async", "cache", 4)) + asy_cache(str[2], str[3], str[4], str[5]); + else if (is("async", "check", 4)) + asy_check(str[2], str[3], str[4], str[5], 0); + else if (is("async", "test", 4)) + asy_check(str[2], str[3], str[4], str[5], 1); + else if (is("sync", "check", 4)) + syn_check(str[2], str[3], str[4], str[5], 0); + else if (is("sync", "test", 4)) + syn_check(str[2], str[3], str[4], str[5], 1); + else if (nstr > 0 && !strcmp(str[0], "exit")) + break; + else if (nstr > 0 && str[0][0] != '#') + printf("ERROR bad input\n"); + } + + cynara_finish(client); + cynara_async_finish(aclient); + cynara_admin_finish(admin); +} + diff --git a/systemd/CMakeLists.txt b/systemd/CMakeLists.txt new file mode 100644 index 0000000..f8116a2 --- /dev/null +++ b/systemd/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved +# +# 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. +# +# @file CMakeLists.txt +# @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com> +# + +SET(CYNARA_ADMIN_SOCKET_GROUP + "security_fw" + CACHE STRING + "Group to apply on administrative sockets") + +CONFIGURE_FILE(cynara-admin.socket.in cynara-admin.socket @ONLY) +CONFIGURE_FILE(cynara-check.socket.in cynara-check.socket @ONLY) + +INSTALL(FILES + ${CMAKE_CURRENT_SOURCE_DIR}/cynara.service + ${CMAKE_CURRENT_SOURCE_DIR}/cynara.target + ${CMAKE_CURRENT_BINARY_DIR}/cynara-admin.socket + ${CMAKE_CURRENT_BINARY_DIR}/cynara-check.socket + DESTINATION + ${SYSTEMD_UNIT_DIR} +) + +INSTALL(DIRECTORY + ${CMAKE_CURRENT_SOURCE_DIR}/sockets.target.wants + DESTINATION + ${SYSTEMD_UNIT_DIR} +) + + diff --git a/systemd/cynara-admin.socket.in b/systemd/cynara-admin.socket.in new file mode 100644 index 0000000..ebc59c6 --- /dev/null +++ b/systemd/cynara-admin.socket.in @@ -0,0 +1,15 @@ +[Socket] +FileDescriptorName=admin +ListenStream=@SOCKET_DIR@/cynara.admin +SocketMode=0600 +SmackLabelIPIn=@ +SmackLabelIPOut=@ + +Service=cynara.service + +[Unit] +Wants=cynara.target +Before=cynara.target + +[Install] +WantedBy=sockets.target diff --git a/systemd/cynara-check.socket.in b/systemd/cynara-check.socket.in new file mode 100644 index 0000000..1139d2f --- /dev/null +++ b/systemd/cynara-check.socket.in @@ -0,0 +1,15 @@ +[Socket] +FileDescriptorName=check +ListenStream=@SOCKET_DIR@/cynara.check +SocketMode=0666 +SmackLabelIPIn=* +SmackLabelIPOut=@ + +Service=cynara.service + +[Unit] +Wants=cynara.target +Before=cynara.target + +[Install] +WantedBy=sockets.target diff --git a/systemd/cynara.service b/systemd/cynara.service new file mode 100644 index 0000000..e124b91 --- /dev/null +++ b/systemd/cynara.service @@ -0,0 +1,29 @@ +[Unit] +Description=Cynara service +Requires=afm-system-setup.service +After=afm-system-setup.service + +[Service] +ExecStartPre=+-/usr/bin/sh -c 'if test ! -d /var/lib/cynara; then mkdir -p /var/lib/cynara; chown cynara:cynara /var/lib/cynara; chsmack -a System /var/lib/cynara; fi' +ExecStart=/usr/bin/cynarad + +Type=notify + +KillMode=process +TimeoutStopSec=3 +Restart=always + +Sockets=cynara-admin.socket +Sockets=cynara-check.socket + +UMask=0000 +User=cynara +Group=cynara +SmackProcessLabel=System +#NoNewPrivileges=true + +#Environment="CYNARA_LOG_LEVEL=LOG_DEBUG" +#Environment="CYNARA_AUDIT_LEVEL=ALL" + +[Install] +WantedBy=multi-user.target diff --git a/systemd/cynara.target b/systemd/cynara.target new file mode 100644 index 0000000..9b2dee4 --- /dev/null +++ b/systemd/cynara.target @@ -0,0 +1,4 @@ +[Unit] +Description=cynara sockets +DefaultDependencies=true + diff --git a/systemd/sockets.target.wants/cynara-admin.socket b/systemd/sockets.target.wants/cynara-admin.socket new file mode 120000 index 0000000..3d0b1ce --- /dev/null +++ b/systemd/sockets.target.wants/cynara-admin.socket @@ -0,0 +1 @@ +../cynara-admin.socket
\ No newline at end of file diff --git a/systemd/sockets.target.wants/cynara-check.socket b/systemd/sockets.target.wants/cynara-check.socket new file mode 120000 index 0000000..921ca66 --- /dev/null +++ b/systemd/sockets.target.wants/cynara-check.socket @@ -0,0 +1 @@ +../cynara-check.socket
\ No newline at end of file |