Initial commit
authorJose Bollo <jose.bollo@iot.bzh>
Mon, 10 Sep 2018 10:00:18 +0000 (12:00 +0200)
committerJose Bollo <jose.bollo@iot.bzh>
Mon, 10 Sep 2018 10:00:18 +0000 (12:00 +0200)
Signed-off-by: Jose Bollo <jose.bollo@iot.bzh>
58 files changed:
.gitignore [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
LICENSE [new file with mode: 0644]
include/CMakeLists.txt [new file with mode: 0644]
include/cynara/cynara-admin-types.h [new file with mode: 0644]
include/cynara/cynara-admin.h [new file with mode: 0644]
include/cynara/cynara-agent.h [new file with mode: 0644]
include/cynara/cynara-client-async.h [new file with mode: 0644]
include/cynara/cynara-client-plugin.h [new file with mode: 0644]
include/cynara/cynara-client.h [new file with mode: 0644]
include/cynara/cynara-creds-commons.h [new file with mode: 0644]
include/cynara/cynara-creds-dbus.h [new file with mode: 0644]
include/cynara/cynara-creds-gdbus.h [new file with mode: 0644]
include/cynara/cynara-creds-self.h [new file with mode: 0644]
include/cynara/cynara-creds-socket.h [new file with mode: 0644]
include/cynara/cynara-error.h [new file with mode: 0644]
include/cynara/cynara-limits.h [new file with mode: 0644]
include/cynara/cynara-monitor.h [new file with mode: 0644]
include/cynara/cynara-plugin.h [new file with mode: 0644]
include/cynara/cynara-policy-types.h [new file with mode: 0644]
include/cynara/cynara-session.h [new file with mode: 0644]
pkgconfig/CMakeLists.txt [new file with mode: 0644]
pkgconfig/cynara.pc.in [new file with mode: 0644]
pkgconfig/link.pc [new symlink]
sample [new file with mode: 0644]
src/.gitignore [new file with mode: 0644]
src/CMakeLists.txt [new file with mode: 0644]
src/Makefile [new file with mode: 0644]
src/cache.c [new file with mode: 0644]
src/cache.h [new file with mode: 0644]
src/cyn.c [new file with mode: 0644]
src/cyn.h [new file with mode: 0644]
src/db.c [new file with mode: 0644]
src/db.h [new file with mode: 0644]
src/export.map [new file with mode: 0644]
src/fbuf.c [new file with mode: 0644]
src/fbuf.h [new file with mode: 0644]
src/lib-compat.c [new file with mode: 0644]
src/prot.c [new file with mode: 0644]
src/prot.h [new file with mode: 0644]
src/queue.c [new file with mode: 0644]
src/queue.h [new file with mode: 0644]
src/rcyn-client.c [new file with mode: 0644]
src/rcyn-client.h [new file with mode: 0644]
src/rcyn-protocol.c [new file with mode: 0644]
src/rcyn-protocol.h [new file with mode: 0644]
src/rcyn-protocol.txt [new file with mode: 0644]
src/rcyn-server.c [new file with mode: 0644]
src/socket.c [new file with mode: 0644]
src/socket.h [new file with mode: 0644]
src/test-lib-compat.c [new file with mode: 0644]
systemd/CMakeLists.txt [new file with mode: 0644]
systemd/cynara-admin.socket.in [new file with mode: 0644]
systemd/cynara-check.socket.in [new file with mode: 0644]
systemd/cynara.service [new file with mode: 0644]
systemd/cynara.target [new file with mode: 0644]
systemd/sockets.target.wants/cynara-admin.socket [new symlink]
systemd/sockets.target.wants/cynara-check.socket [new symlink]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..ac33634
--- /dev/null
@@ -0,0 +1,2 @@
+build/
+.*.sw*
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8a3c9f1
--- /dev/null
@@ -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)
+
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -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 (file)
index 0000000..6553f89
--- /dev/null
@@ -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 (file)
index 0000000..95d27b9
--- /dev/null
@@ -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 (file)
index 0000000..a0ab0c9
--- /dev/null
@@ -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 (file)
index 0000000..3e6d4f0
--- /dev/null
@@ -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 (file)
index 0000000..78a1313
--- /dev/null
@@ -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 (file)
index 0000000..70000e6
--- /dev/null
@@ -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 (file)
index 0000000..d1388ec
--- /dev/null
@@ -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 (file)
index 0000000..81b9784
--- /dev/null
@@ -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 (file)
index 0000000..04af96f
--- /dev/null
@@ -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 (file)
index 0000000..2334e45
--- /dev/null
@@ -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 (file)
index 0000000..580a19d
--- /dev/null
@@ -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 (file)
index 0000000..fe5cbdb
--- /dev/null
@@ -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 (file)
index 0000000..8316f1d
--- /dev/null
@@ -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 (file)
index 0000000..f4e98c1
--- /dev/null
@@ -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 (file)
index 0000000..ce7b8d0
--- /dev/null
@@ -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 (file)
index 0000000..747966f
--- /dev/null
@@ -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 (file)
index 0000000..bd74b5a
--- /dev/null
@@ -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 (file)
index 0000000..1c76bce
--- /dev/null
@@ -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 (file)
index 0000000..f32ad70
--- /dev/null
@@ -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 (file)
index 0000000..6a39979
--- /dev/null
@@ -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 (symlink)
index 0000000..17d4d5d
--- /dev/null
@@ -0,0 +1 @@
+cynara.pc
\ No newline at end of file
diff --git a/sample b/sample
new file mode 100644 (file)
index 0000000..92535a7
--- /dev/null
+++ b/sample
@@ -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 (file)
index 0000000..d551f05
--- /dev/null
@@ -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 (file)
index 0000000..fa3724c
--- /dev/null
@@ -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 (file)
index 0000000..f5969c3
--- /dev/null
@@ -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 (file)
index 0000000..c2db00d
--- /dev/null
@@ -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 (file)
index 0000000..2f3a9a1
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..84467af
--- /dev/null
@@ -0,0 +1,9 @@
+{
+global:
+       rcyn_*;
+       cynara_*;
+local:
+       *;
+};
+
+
diff --git a/src/fbuf.c b/src/fbuf.c
new file mode 100644 (file)
index 0000000..6d4d41c
--- /dev/null
@@ -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 (file)
index 0000000..3bbdbde
--- /dev/null
@@ -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 (file)
index 0000000..5c6c4e2
--- /dev/null
@@ -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 (file)
index 0000000..db0e41e
--- /dev/null
@@ -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 (file)
index 0000000..a1ded3b
--- /dev/null
@@ -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 (file)
index 0000000..16b0a0f
--- /dev/null
@@ -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 (file)
index 0000000..70518c4
--- /dev/null
@@ -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 (file)
index 0000000..f712ca6
--- /dev/null
@@ -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 (file)
index 0000000..6706820
--- /dev/null
@@ -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 (file)
index 0000000..c656570
--- /dev/null
@@ -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 (file)
index 0000000..8906194
--- /dev/null
@@ -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 (file)
index 0000000..134d05e
--- /dev/null
@@ -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 (file)
index 0000000..4fc365e
--- /dev/null
@@ -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 (file)
index 0000000..3ab0408
--- /dev/null
@@ -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 (file)
index 0000000..b5989e1
--- /dev/null
@@ -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 (file)
index 0000000..627c208
--- /dev/null
@@ -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 (file)
index 0000000..f8116a2
--- /dev/null
@@ -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 (file)
index 0000000..ebc59c6
--- /dev/null
@@ -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 (file)
index 0000000..1139d2f
--- /dev/null
@@ -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 (file)
index 0000000..e124b91
--- /dev/null
@@ -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 (file)
index 0000000..9b2dee4
--- /dev/null
@@ -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 (symlink)
index 0000000..3d0b1ce
--- /dev/null
@@ -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 (symlink)
index 0000000..921ca66
--- /dev/null
@@ -0,0 +1 @@
+../cynara-check.socket
\ No newline at end of file