summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
commit74aa0bc6779af38018a03fd2cf4419fe85917904 (patch)
tree9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/lib
parentInitial commit. (diff)
downloadsssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz
sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/certmap/sss_cert_content_common.c281
-rw-r--r--src/lib/certmap/sss_cert_content_crypto.c1132
-rw-r--r--src/lib/certmap/sss_cert_digest_crypto.c158
-rw-r--r--src/lib/certmap/sss_certmap.c1337
-rw-r--r--src/lib/certmap/sss_certmap.doxy.in3
-rw-r--r--src/lib/certmap/sss_certmap.exports23
-rw-r--r--src/lib/certmap/sss_certmap.h195
-rw-r--r--src/lib/certmap/sss_certmap.pc.in11
-rw-r--r--src/lib/certmap/sss_certmap_attr_names.c134
-rw-r--r--src/lib/certmap/sss_certmap_int.h502
-rw-r--r--src/lib/certmap/sss_certmap_krb5_match.c547
-rw-r--r--src/lib/certmap/sss_certmap_ldap_mapping.c654
-rw-r--r--src/lib/cifs_idmap_sss/cifs_idmap_sss.c337
-rw-r--r--src/lib/idmap/sss_idmap.c1613
-rw-r--r--src/lib/idmap/sss_idmap.doxy.in1883
-rw-r--r--src/lib/idmap/sss_idmap.exports66
-rw-r--r--src/lib/idmap/sss_idmap.h962
-rw-r--r--src/lib/idmap/sss_idmap.pc.in11
-rw-r--r--src/lib/idmap/sss_idmap_conv.c569
-rw-r--r--src/lib/idmap/sss_idmap_private.h84
-rw-r--r--src/lib/ipa_hbac/hbac_evaluator.c520
-rw-r--r--src/lib/ipa_hbac/ipa_hbac.doxy.in1883
-rw-r--r--src/lib/ipa_hbac/ipa_hbac.exports20
-rw-r--r--src/lib/ipa_hbac/ipa_hbac.h344
-rw-r--r--src/lib/ipa_hbac/ipa_hbac.pc.in11
-rw-r--r--src/lib/sifp/sss_sifp.c473
-rw-r--r--src/lib/sifp/sss_sifp.h564
-rw-r--r--src/lib/sifp/sss_sifp_attrs.c317
-rw-r--r--src/lib/sifp/sss_sifp_common.c183
-rw-r--r--src/lib/sifp/sss_sifp_dbus.c275
-rw-r--r--src/lib/sifp/sss_sifp_dbus.h174
-rw-r--r--src/lib/sifp/sss_sifp_parser.c723
-rw-r--r--src/lib/sifp/sss_sifp_private.h112
-rw-r--r--src/lib/sifp/sss_sifp_utils.c90
-rw-r--r--src/lib/sifp/sss_simpleifp.doxy.in1539
-rw-r--r--src/lib/sifp/sss_simpleifp.exports56
-rw-r--r--src/lib/sifp/sss_simpleifp.pc.in12
-rw-r--r--src/lib/winbind_idmap_sss/libdlopen-test-winbind-idmap.c31
-rw-r--r--src/lib/winbind_idmap_sss/winbind_idmap_sss.c220
-rw-r--r--src/lib/winbind_idmap_sss/winbind_idmap_sss.h117
40 files changed, 18166 insertions, 0 deletions
diff --git a/src/lib/certmap/sss_cert_content_common.c b/src/lib/certmap/sss_cert_content_common.c
new file mode 100644
index 0000000..d95cc3a
--- /dev/null
+++ b/src/lib/certmap/sss_cert_content_common.c
@@ -0,0 +1,281 @@
+/*
+ SSSD - certificate handling utils
+ The calls defined here should be usable outside of SSSD as well, e.g. in
+ libsss_certmap.
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2017
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+
+#include "lib/certmap/sss_certmap_int.h"
+
+int get_short_name(TALLOC_CTX *mem_ctx, const char *full_name,
+ char delim, char **short_name)
+{
+ char *at;
+ char *s;
+
+ if (full_name == NULL || delim == '\0' || short_name == NULL) {
+ return EINVAL;
+ }
+
+ at = strchr(full_name, delim);
+ if (at != NULL) {
+ s = talloc_strndup(mem_ctx, full_name, (at - full_name));
+ } else {
+ s = talloc_strdup(mem_ctx, full_name);
+ }
+ if (s == NULL) {
+ return ENOMEM;
+ }
+
+ *short_name = s;
+
+ return 0;
+}
+
+int add_to_san_list(TALLOC_CTX *mem_ctx, bool is_bin,
+ enum san_opt san_opt, const uint8_t *data, size_t len,
+ struct san_list **item)
+{
+ struct san_list *i;
+
+ if (data == NULL || len == 0 || san_opt == SAN_INVALID) {
+ return EINVAL;
+ }
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ return ENOMEM;
+ }
+
+ i->san_opt = san_opt;
+ if (is_bin) {
+ i->bin_val = talloc_memdup(i, data, len);
+ i->bin_val_len = len;
+ } else {
+ i->val = talloc_strndup(i, (const char *) data, len);
+ }
+ if (i->val == NULL) {
+ talloc_free(i);
+ return ENOMEM;
+ }
+
+ *item = i;
+
+ return 0;
+}
+
+int add_principal_to_san_list(TALLOC_CTX *mem_ctx, enum san_opt san_opt,
+ const char *princ, struct san_list **item)
+{
+ struct san_list *i = NULL;
+ int ret;
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ return ENOMEM;
+ }
+ i->san_opt = san_opt;
+
+ i->val = talloc_strdup(i, princ);
+ if (i->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = get_short_name(i, i->val, '@', &(i->short_name));
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *item = i;
+ } else {
+ talloc_free(i);
+ }
+
+ return ret;
+}
+
+int rdn_list_2_dn_str(TALLOC_CTX *mem_ctx, const char *conversion,
+ const char **rdn_list, char **result)
+{
+ char *str = NULL;
+ size_t c;
+ int ret;
+ char *conv = NULL;
+
+ str = talloc_strdup(mem_ctx, "");
+ if (str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ if (conversion == NULL || strcmp(conversion, "nss_ldap") == 0
+ || strcmp(conversion, "nss") == 0) {
+ for (c = 0; rdn_list[c] != NULL; c++);
+ while (c != 0) {
+ c--;
+ str = talloc_asprintf_append(str, "%s%s",
+ (rdn_list[c + 1] == NULL) ? "" : ",",
+ rdn_list[c]);
+ if (str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ };
+ } else if (strcmp(conversion, "ad_ldap") == 0) {
+ for (c = 0; rdn_list[c] != NULL; c++);
+ while (c != 0) {
+ c--;
+ conv = check_ad_attr_name(str, rdn_list[c]);
+ str = talloc_asprintf_append(str, "%s%s",
+ (rdn_list[c + 1] == NULL) ? "" : ",",
+ conv == NULL ? rdn_list[c] : conv);
+ talloc_free(conv);
+ conv = NULL;
+ if (str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ };
+ } else if (strcmp(conversion, "nss_x500") == 0) {
+ for (c = 0; rdn_list[c] != NULL; c++) {
+ str = talloc_asprintf_append(str, "%s%s", (c == 0) ? "" : ",",
+ rdn_list[c]);
+ if (str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ } else if (strcmp(conversion, "ad_x500") == 0
+ || strcmp(conversion, "ad") == 0) {
+ for (c = 0; rdn_list[c] != NULL; c++) {
+ conv = check_ad_attr_name(str, rdn_list[c]);
+ str = talloc_asprintf_append(str, "%s%s",
+ (c == 0) ? "" : ",",
+ conv == NULL ? rdn_list[c] : conv);
+ talloc_free(conv);
+ conv = NULL;
+ if (str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ } else {
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *result = str;
+ } else {
+ talloc_free(str);
+ }
+
+ return ret;
+}
+
+int rdn_list_2_component(TALLOC_CTX *mem_ctx, const char *conversion,
+ const char **rdn_list, char **result)
+{
+ int ret;
+ char *sep;
+ char *attr_name = NULL;
+ int32_t number = 0;
+ char *res = NULL;
+ size_t rdn_count;
+ size_t idx;
+ int i;
+
+ ret = check_attr_name_and_or_number(mem_ctx, conversion, &attr_name, &number);
+ if (ret != 0) {
+ return ret;
+ }
+
+ for (rdn_count = 0; rdn_list[rdn_count] != NULL; rdn_count++);
+
+ if (abs(number) > rdn_count || rdn_count == 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (number == 0 && attr_name != NULL) {
+ for (i = rdn_count-1; i >= 0; i--) {
+ sep = strchr(rdn_list[i], '=');
+ if (sep == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+ if (strlen(attr_name) == (sep - rdn_list[i])
+ && strncasecmp(attr_name, rdn_list[i],
+ (sep - rdn_list[i])) == 0) {
+ res = talloc_strdup(mem_ctx, sep + 1);
+ break;
+ }
+ }
+ } else {
+ if (number == 0) {
+ idx = rdn_count - 1;
+ } else {
+ if (number > 0) {
+ idx = rdn_count - number;
+ } else {
+ idx = number * (-1) - 1;
+ }
+ }
+
+ sep = strchr(rdn_list[idx], '=');
+ if (sep == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+ if (attr_name != NULL) {
+ if (strlen(attr_name) != (sep - rdn_list[idx])
+ || strncasecmp(attr_name, rdn_list[idx],
+ (sep - rdn_list[idx])) != 0) {
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ res = talloc_strdup(mem_ctx, sep + 1);
+ }
+
+ if (res == NULL) {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ talloc_free(attr_name);
+
+ if (ret == 0) {
+ *result = res;
+ }
+
+ return ret;
+}
diff --git a/src/lib/certmap/sss_cert_content_crypto.c b/src/lib/certmap/sss_cert_content_crypto.c
new file mode 100644
index 0000000..6141aa7
--- /dev/null
+++ b/src/lib/certmap/sss_cert_content_crypto.c
@@ -0,0 +1,1132 @@
+/*
+ SSSD - certificate handling utils - OpenSSL version
+ The calls defined here should be usable outside of SSSD as well, e.g. in
+ libsss_certmap.
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2017
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <talloc.h>
+#include <openssl/x509v3.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/err.h>
+#include <openssl/stack.h>
+#include <openssl/safestack.h>
+
+#include "util/crypto/sss_crypto.h"
+#include "util/cert.h"
+#include "lib/certmap/sss_certmap.h"
+#include "lib/certmap/sss_certmap_int.h"
+
+/* backward compatible macros for OpenSSL < 1.1 */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#define ASN1_STRING_get0_data(o) ASN1_STRING_data(o)
+#define X509_get_extension_flags(o) ((o)->ex_flags)
+#define X509_get_key_usage(o) ((o)->ex_kusage)
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+#define OID_NTDS_CA_SECURITY_EXT "1.3.6.1.4.1.311.25.2"
+#define OID_NTDS_OBJECTSID "1.3.6.1.4.1.311.25.2.1"
+
+typedef struct PrincipalName_st {
+ ASN1_INTEGER *name_type;
+ STACK_OF(ASN1_GENERALSTRING) *name_string;
+} PrincipalName;
+
+ASN1_SEQUENCE(PrincipalName) = {
+ ASN1_EXP(PrincipalName, name_type, ASN1_INTEGER, 0),
+ ASN1_EXP_SEQUENCE_OF(PrincipalName, name_string, ASN1_GENERALSTRING, 1)
+} ASN1_SEQUENCE_END(PrincipalName)
+
+IMPLEMENT_ASN1_FUNCTIONS(PrincipalName)
+
+typedef struct KRB5PrincipalName_st {
+ ASN1_STRING *realm;
+ PrincipalName *principal_name;
+} KRB5PrincipalName;
+
+ASN1_SEQUENCE(KRB5PrincipalName) = {
+ ASN1_EXP(KRB5PrincipalName, realm, ASN1_GENERALSTRING, 0),
+ ASN1_EXP(KRB5PrincipalName, principal_name, PrincipalName, 1)
+} ASN1_SEQUENCE_END(KRB5PrincipalName)
+
+IMPLEMENT_ASN1_FUNCTIONS(KRB5PrincipalName)
+
+/* Microsoft's CA Security Extension as described in section 2.2.2.7.7.4 of
+ * [MS-WCCE]: Windows Client Certificate Enrollment Protocol
+ * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/e563cff8-1af6-4e6f-a655-7571ca482e71
+ */
+typedef struct NTDS_OBJECTSID_st {
+ ASN1_OBJECT *type_id;
+ ASN1_OCTET_STRING *value;
+} NTDS_OBJECTSID;
+
+ASN1_SEQUENCE(NTDS_OBJECTSID) = {
+ ASN1_SIMPLE(NTDS_OBJECTSID, type_id, ASN1_OBJECT),
+ ASN1_EXP(NTDS_OBJECTSID, value, ASN1_OCTET_STRING, 0)
+} ASN1_SEQUENCE_END(NTDS_OBJECTSID)
+
+IMPLEMENT_ASN1_FUNCTIONS(NTDS_OBJECTSID)
+
+typedef struct NTDS_CA_SECURITY_EXT_st {
+#define NTDS_CA_SECURITY_EXT_OBJECTSID 0
+ int type;
+ union {
+ NTDS_OBJECTSID *sid;
+ } d;
+} NTDS_CA_SECURITY_EXT;
+
+ASN1_CHOICE(NTDS_CA_SECURITY_EXT) = {
+ ASN1_IMP(NTDS_CA_SECURITY_EXT, d.sid, NTDS_OBJECTSID, NTDS_CA_SECURITY_EXT_OBJECTSID)
+} ASN1_CHOICE_END(NTDS_CA_SECURITY_EXT)
+
+IMPLEMENT_ASN1_FUNCTIONS(NTDS_CA_SECURITY_EXT)
+
+typedef STACK_OF(NTDS_CA_SECURITY_EXT) NTDS_CA_SECURITY_EXTS;
+
+ASN1_ITEM_TEMPLATE(NTDS_CA_SECURITY_EXTS) =
+ ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, SecExts, NTDS_CA_SECURITY_EXT)
+ASN1_ITEM_TEMPLATE_END(NTDS_CA_SECURITY_EXTS)
+
+IMPLEMENT_ASN1_FUNCTIONS(NTDS_CA_SECURITY_EXTS)
+
+enum san_opt openssl_name_type_to_san_opt(int type)
+{
+ switch (type) {
+ case GEN_OTHERNAME:
+ return SAN_OTHER_NAME;
+ case GEN_EMAIL:
+ return SAN_RFC822_NAME;
+ case GEN_DNS:
+ return SAN_DNS_NAME;
+ case GEN_X400:
+ return SAN_X400_ADDRESS;
+ case GEN_DIRNAME:
+ return SAN_DIRECTORY_NAME;
+ case GEN_EDIPARTY:
+ return SAN_EDIPART_NAME;
+ case GEN_URI:
+ return SAN_URI;
+ case GEN_IPADD:
+ return SAN_IP_ADDRESS;
+ case GEN_RID:
+ return SAN_REGISTERED_ID;
+ default:
+ return SAN_INVALID;
+ }
+}
+
+static int add_string_other_name_to_san_list(TALLOC_CTX *mem_ctx,
+ enum san_opt san_opt,
+ OTHERNAME *other_name,
+ struct san_list **item)
+{
+ struct san_list *i = NULL;
+ int ret;
+ char oid_buf[128]; /* FIXME: any other size ?? */
+ int len;
+ unsigned char *p;
+
+ len = OBJ_obj2txt(oid_buf, sizeof(oid_buf), other_name->type_id, 1);
+ if (len <= 0) {
+ return EINVAL;
+ }
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ return ENOMEM;
+ }
+ i->san_opt = san_opt;
+
+ i->other_name_oid = talloc_strndup(i, oid_buf, len);
+ if (i->other_name_oid == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ len = i2d_ASN1_TYPE(other_name->value, NULL);
+ if (len <= 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ i->bin_val = talloc_size(mem_ctx, len);
+ if (i->bin_val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* i2d_TYPE increment the second argument so that it points to the end of
+ * the written data hence we cannot use i->bin_val directly. */
+ p = i->bin_val;
+ i->bin_val_len = i2d_ASN1_TYPE(other_name->value, &p);
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *item = i;
+ } else {
+ talloc_free(i);
+ }
+
+ return ret;
+}
+
+static int add_nt_princ_to_san_list(TALLOC_CTX *mem_ctx,
+ enum san_opt san_opt,
+ GENERAL_NAME *current,
+ struct san_list **item)
+{
+ struct san_list *i = NULL;
+ int ret;
+ OTHERNAME *other_name = current->d.otherName;
+
+ if (ASN1_TYPE_get(other_name->value) != V_ASN1_UTF8STRING) {
+ return EINVAL;
+ }
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ return ENOMEM;
+ }
+ i->san_opt = san_opt;
+
+ i->val = talloc_strndup(i,
+ (const char *) ASN1_STRING_get0_data(
+ other_name->value->value.utf8string),
+ ASN1_STRING_length(other_name->value->value.utf8string));
+ if (i->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = get_short_name(i, i->val, '@', &(i->short_name));
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *item = i;
+ } else {
+ talloc_free(i);
+ }
+
+ return ret;
+}
+
+void *ASN1_TYPE_unpack_sequence(const ASN1_ITEM *it, const ASN1_TYPE *t)
+{
+ if (t == NULL || t->type != V_ASN1_SEQUENCE || t->value.sequence == NULL)
+ return NULL;
+ return ASN1_item_unpack(t->value.sequence, it);
+}
+
+static int add_pkinit_princ_to_san_list(TALLOC_CTX *mem_ctx,
+ enum san_opt san_opt,
+ GENERAL_NAME *current,
+ struct san_list **item)
+{
+ struct san_list *i = NULL;
+ int ret;
+ KRB5PrincipalName *princ = NULL;
+ size_t c;
+ const unsigned char *p;
+ const ASN1_STRING *oct;
+ ASN1_GENERALSTRING *name_comp;
+
+ oct = current->d.otherName->value->value.sequence;
+ p = oct->data;
+ princ = d2i_KRB5PrincipalName(NULL, &p, oct->length);
+ if (princ == NULL) {
+ return EINVAL;
+ }
+
+ if (princ->realm == NULL
+ || princ->principal_name == NULL
+ || princ->principal_name->name_string == NULL
+ || sk_ASN1_GENERALSTRING_num(princ->principal_name->name_string)
+ == 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ i->san_opt = san_opt;
+
+ i->val = talloc_strdup(i, "");
+ if (i->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0;
+ c < sk_ASN1_GENERALSTRING_num(princ->principal_name->name_string);
+ c++) {
+
+ if (c > 0) {
+ i->val = talloc_strdup_append(i->val, "/");
+ if (i->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ name_comp = sk_ASN1_GENERALSTRING_value(
+ princ->principal_name->name_string, c);
+ i->val = talloc_strndup_append(i->val,
+ (const char *) ASN1_STRING_get0_data(name_comp),
+ ASN1_STRING_length(name_comp));
+ if (i->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ i->val = talloc_asprintf_append(i->val, "@%.*s",
+ ASN1_STRING_length(princ->realm),
+ ASN1_STRING_get0_data(princ->realm));
+ if (i->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = get_short_name(i, i->val, '@', &(i->short_name));
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ KRB5PrincipalName_free(princ);
+ if (ret == 0) {
+ *item = i;
+ } else {
+ talloc_free(i);
+ }
+
+ return ret;
+}
+
+static int add_ip_to_san_list(TALLOC_CTX *mem_ctx, enum san_opt san_opt,
+ const uint8_t *data, size_t len,
+ struct san_list **item)
+{
+ struct san_list *i = NULL;
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ return ENOMEM;
+ }
+ i->san_opt = san_opt;
+
+ i->val = talloc_strndup(i, (const char *) data, len);
+ if (i->val == NULL) {
+ talloc_free(i);
+ return ENOMEM;
+ }
+
+ *item = i;
+ return 0;
+}
+
+static int get_rdn_list(TALLOC_CTX *mem_ctx, X509_NAME *name,
+ const char ***rdn_list)
+{
+ int ret;
+ size_t c;
+ const char **list = NULL;
+ X509_NAME_ENTRY *e;
+ ASN1_STRING *rdn_str;
+ ASN1_OBJECT *rdn_name;
+ BIO *bio_mem = NULL;
+ char *tmp_str;
+ long tmp_str_size;
+
+ int nid;
+ const char *sn;
+
+ bio_mem = BIO_new(BIO_s_mem());
+ if (bio_mem == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ list = talloc_zero_array(mem_ctx, const char *,
+ X509_NAME_entry_count(name) + 1);
+ if (list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; c < X509_NAME_entry_count(name); c++) {
+ e = X509_NAME_get_entry(name, c);
+ rdn_str = X509_NAME_ENTRY_get_data(e);
+
+ ret = ASN1_STRING_print_ex(bio_mem, rdn_str, ASN1_STRFLGS_RFC2253);
+ if (ret < 0) {
+ ret = EIO;
+ goto done;
+ }
+
+ tmp_str_size = BIO_get_mem_data(bio_mem, &tmp_str);
+ if (tmp_str_size == 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ rdn_name = X509_NAME_ENTRY_get_object(e);
+ nid = OBJ_obj2nid(rdn_name);
+ sn = OBJ_nid2sn(nid);
+
+ list[c] = talloc_asprintf(list, "%s=%.*s", openssl_2_nss_attr_name(sn),
+ (int) tmp_str_size, tmp_str);
+ ret = BIO_reset(bio_mem);
+ if (ret != 1) {
+ /* BIO_reset() for BIO_s_mem returns 1 for sucess */
+ ret = ENOMEM;
+ goto done;
+ }
+ if (list[c] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ BIO_free_all(bio_mem);
+ if (ret == 0) {
+ *rdn_list = list;
+ } else {
+ talloc_free(list);
+ }
+
+ return ret;
+}
+
+static int add_rdn_list_to_san_list(TALLOC_CTX *mem_ctx,
+ enum san_opt san_opt,
+ X509_NAME *name,
+ struct san_list **item)
+{
+ struct san_list *i = NULL;
+ int ret;
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ return ENOMEM;
+ }
+ i->san_opt = san_opt;
+
+ ret = get_rdn_list(i, name, &(i->rdn_list));
+ if (ret != 0) {
+ talloc_free(i);
+ return ret;
+ }
+
+ *item = i;
+ return 0;
+}
+
+static int add_oid_to_san_list(TALLOC_CTX *mem_ctx,
+ enum san_opt san_opt,
+ ASN1_OBJECT *oid,
+ struct san_list **item)
+{
+ struct san_list *i = NULL;
+ char oid_buf[128]; /* FIXME: any other size ?? */
+ int len;
+
+ len = OBJ_obj2txt(oid_buf, sizeof(oid_buf), oid, 1);
+ if (len <= 0) {
+ return EINVAL;
+ }
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ return ENOMEM;
+ }
+ i->san_opt = san_opt;
+
+ i->val = talloc_strndup(i, oid_buf, len);
+ if (i->val == NULL) {
+ talloc_free(i);
+ return ENOMEM;
+ }
+
+ *item = i;
+ return 0;
+}
+
+/* Due to CVE-2023-0286 the type of the x400Address member of the
+ * GENERAL_NAME struct was changed from ASN1_TYPE to ASN1_STRING. The
+ * following code tries to make sure that the x400Address can be extracted from
+ * the certificate in either case. */
+static int get_x400address_data(TALLOC_CTX *mem_ctx, GENERAL_NAME *current,
+ unsigned char **_data, int *_len)
+{
+ int ret;
+ unsigned char *data = NULL;
+ int len;
+
+#ifdef HAVE_X400ADDRESS_STRING
+ len = ASN1_STRING_length(current->d.x400Address);
+ if (len <= 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ data = (unsigned char *) talloc_strndup(mem_ctx,
+ (const char *) ASN1_STRING_get0_data(current->d.x400Address),
+ len);
+ if (data == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* just make sure we have the right length in case the original
+ * x400Address contained some unexpected \0-bytes. */
+ len = strlen((char *) data);
+#else
+ unsigned char *p;
+
+ len = i2d_ASN1_TYPE(current->d.x400Address, NULL);
+
+ if (len <= 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ data = talloc_size(mem_ctx, len);
+ if (data == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* i2d_ASN1_TYPE increment the second argument so that it points to the end
+ * of the written data hence we cannot use data directly. */
+ p = data;
+ len = i2d_ASN1_TYPE(current->d.x400Address, &p);
+#endif
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ if (_data != NULL) {
+ *_data = data;
+ }
+ if (_len != NULL) {
+ *_len = len;
+ }
+ } else {
+ talloc_free(data);
+ }
+
+ return ret;
+}
+static int get_san(TALLOC_CTX *mem_ctx, X509 *cert, struct san_list **san_list)
+{
+ STACK_OF(GENERAL_NAME) *extsan = NULL;
+ GENERAL_NAME *current;
+ size_t c;
+ int ret;
+ int crit;
+ struct san_list *list = NULL;
+ struct san_list *item = NULL;
+ struct san_list *item_s = NULL;
+ struct san_list *item_p = NULL;
+ struct san_list *item_pb = NULL;
+ int len;
+ unsigned char *data;
+ unsigned char *p;
+
+ extsan = X509_get_ext_d2i(cert, NID_subject_alt_name, &crit, NULL);
+ if (extsan == NULL) {
+ if (crit == -1) { /* extension could not be found */
+ return EOK;
+ } else {
+ return EINVAL;
+ }
+ }
+
+ for (c = 0; c < sk_GENERAL_NAME_num(extsan); c++) {
+ current = sk_GENERAL_NAME_value(extsan, c);
+ switch (current->type) {
+ case GEN_OTHERNAME:
+ ret = add_string_other_name_to_san_list(mem_ctx,
+ SAN_STRING_OTHER_NAME,
+ current->d.otherName,
+ &item_s);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item_s);
+
+ item_p = NULL;
+ if (strcmp(item_s->other_name_oid, NT_PRINCIPAL_OID) == 0) {
+ ret = add_nt_princ_to_san_list(mem_ctx, SAN_NT, current,
+ &item_p);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item_p);
+ } else if (strcmp(item_s->other_name_oid, PKINIT_OID) == 0) {
+ ret = add_pkinit_princ_to_san_list(mem_ctx, SAN_PKINIT,
+ current, &item_p);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item_p);
+ }
+
+ if (item_p != NULL) {
+ ret = add_principal_to_san_list(mem_ctx, SAN_PRINCIPAL,
+ item_p->val, &item_pb);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item_pb);
+ }
+
+ break;
+ case GEN_EMAIL:
+ ret = add_to_san_list(mem_ctx, false,
+ openssl_name_type_to_san_opt(current->type),
+ ASN1_STRING_get0_data(current->d.rfc822Name),
+ ASN1_STRING_length(current->d.rfc822Name),
+ &item);
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = get_short_name(item, item->val, '@', &(item->short_name));
+ if (ret != 0) {
+ goto done;
+ }
+
+ DLIST_ADD(list, item);
+ break;
+ case GEN_DNS:
+ ret = add_to_san_list(mem_ctx, false,
+ openssl_name_type_to_san_opt(current->type),
+ ASN1_STRING_get0_data(current->d.dNSName),
+ ASN1_STRING_length(current->d.dNSName),
+ &item);
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = get_short_name(item, item->val, '.', &(item->short_name));
+ if (ret != 0) {
+ goto done;
+ }
+
+ DLIST_ADD(list, item);
+ break;
+ case GEN_URI:
+ ret = add_to_san_list(mem_ctx, false,
+ openssl_name_type_to_san_opt(current->type),
+ ASN1_STRING_get0_data(current->d.uniformResourceIdentifier),
+ ASN1_STRING_length(current->d.uniformResourceIdentifier),
+ &item);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item);
+ break;
+ case GEN_IPADD:
+ ret = add_ip_to_san_list(mem_ctx,
+ openssl_name_type_to_san_opt(current->type),
+ ASN1_STRING_get0_data(current->d.iPAddress),
+ ASN1_STRING_length(current->d.iPAddress),
+ &item);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item);
+ break;
+ case GEN_DIRNAME:
+ ret = add_rdn_list_to_san_list(mem_ctx,
+ openssl_name_type_to_san_opt(current->type),
+ current->d.directoryName, &item);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item);
+ break;
+ case GEN_RID:
+ ret = add_oid_to_san_list(mem_ctx,
+ openssl_name_type_to_san_opt(current->type),
+ current->d.registeredID, &item);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item);
+ break;
+ case GEN_X400:
+ ret = get_x400address_data(mem_ctx, current, &data, &len);
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = add_to_san_list(mem_ctx, true,
+ openssl_name_type_to_san_opt(current->type),
+ data, len, &item);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item);
+ break;
+ case GEN_EDIPARTY:
+ len = i2d_EDIPARTYNAME(current->d.ediPartyName, NULL);
+ if (len <= 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ data = talloc_size(mem_ctx, len);
+ if (data == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* i2d_EDIPARTYNAME increment the second argument so that it points
+ * to the end of the written data hence we cannot use data directly.
+ */
+ p = data;
+ len = i2d_EDIPARTYNAME(current->d.ediPartyName, &p);
+
+ ret = add_to_san_list(mem_ctx, true,
+ openssl_name_type_to_san_opt(current->type),
+ data, len, &item);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item);
+ break;
+ default:
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ GENERAL_NAMES_free(extsan);
+
+ if (ret == EOK) {
+ *san_list = list;
+ }
+
+ return ret;
+}
+
+static int get_sid_ext(TALLOC_CTX *mem_ctx, X509 *cert, const char **_sid)
+{
+ int ret;
+ ASN1_OBJECT *sid_ext_oid = NULL;
+ ASN1_OBJECT *sid_oid = NULL;
+ int idx;
+ X509_EXTENSION *ext = NULL;
+ const unsigned char *p;
+ NTDS_CA_SECURITY_EXTS *sec_exts = NULL;
+ NTDS_CA_SECURITY_EXT *current;
+ char *sid = NULL;
+ const ASN1_OCTET_STRING *ext_data = NULL;
+ size_t c;
+
+ sid_ext_oid = OBJ_txt2obj(OID_NTDS_CA_SECURITY_EXT, 1);
+ if (sid_ext_oid == NULL) {
+ return EIO;
+ }
+
+ idx = X509_get_ext_by_OBJ(cert, sid_ext_oid, -1);
+ ASN1_OBJECT_free(sid_ext_oid);
+ if (idx == -1) {
+ /* Extension most probably not available, no error. */
+ return 0;
+ }
+
+ ext = X509_get_ext(cert, idx);
+ if (ext == NULL) {
+ return EINVAL;
+ }
+
+ ext_data = X509_EXTENSION_get_data(ext);
+ if (ext_data == NULL) {
+ return EINVAL;
+ }
+
+ p = ext_data->data;
+ sec_exts = d2i_NTDS_CA_SECURITY_EXTS(NULL, &p, ext_data->length);
+ if (sec_exts == NULL) {
+ return EIO;
+ }
+
+ ret = EINVAL;
+ for (c = 0; c < OPENSSL_sk_num((const OPENSSL_STACK *) sec_exts); c++) {
+ current = (NTDS_CA_SECURITY_EXT *)
+ OPENSSL_sk_value((const OPENSSL_STACK *) sec_exts, c);
+ /* Only handle NTDS_CA_SECURITY_EXT_OBJECTSID. So far no other types
+ * are defined. */
+ if (current->type == NTDS_CA_SECURITY_EXT_OBJECTSID) {
+ if (sid != NULL) {
+ /* second SID found, currently not expected */
+ talloc_free(sid);
+ ret = EINVAL;
+ goto done;
+ }
+
+ sid_oid = OBJ_txt2obj(OID_NTDS_OBJECTSID, 1);
+ if (sid_oid == NULL) {
+ ret = EIO;
+ goto done;
+ }
+ if (current->d.sid->type_id == NULL
+ || OBJ_cmp(current->d.sid->type_id, sid_oid) != 0) {
+ /* Unexpected OID */
+ ret = EINVAL;
+ goto done;
+ }
+
+ sid = talloc_strndup(mem_ctx, (char *) current->d.sid->value->data,
+ current->d.sid->value->length);
+ if (sid == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = 0;
+ }
+ }
+
+done:
+ NTDS_CA_SECURITY_EXTS_free(sec_exts);
+ ASN1_OBJECT_free(sid_oid);
+
+ if (ret == 0) {
+ *_sid = sid;
+ }
+ return ret;
+}
+
+static int get_extended_key_usage_oids(TALLOC_CTX *mem_ctx,
+ X509 *cert,
+ const char ***_oids)
+{
+ const char **oids_list = NULL;
+ size_t c;
+ int ret;
+ char oid_buf[128]; /* FIXME: any other size ?? */
+ int len;
+ EXTENDED_KEY_USAGE *extusage = NULL;
+ int crit;
+ size_t eku_count = 0;
+
+ extusage = X509_get_ext_d2i(cert, NID_ext_key_usage, &crit, NULL);
+ if (extusage == NULL) {
+ if (crit == -1) { /* extension could not be found */
+ eku_count = 0;
+ } else {
+ return EINVAL;
+ }
+ } else {
+ eku_count = sk_ASN1_OBJECT_num(extusage);
+ }
+
+ oids_list = talloc_zero_array(mem_ctx, const char *, eku_count + 1);
+ if (oids_list == NULL) {
+ return ENOMEM;
+ }
+
+ for (c = 0; c < eku_count; c++) {
+ len = OBJ_obj2txt(oid_buf, sizeof(oid_buf),
+ sk_ASN1_OBJECT_value(extusage, c), 1);
+ if (len < 0) {
+ return EIO;
+ }
+
+ oids_list[c] = talloc_strndup(oids_list, oid_buf, len);
+ if (oids_list[c] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ sk_ASN1_OBJECT_pop_free(extusage, ASN1_OBJECT_free);
+ if (ret == 0) {
+ *_oids = oids_list;
+ } else {
+ talloc_free(oids_list);
+ }
+
+ return ret;
+}
+
+static int get_serial_number(TALLOC_CTX *mem_ctx, X509 *cert,
+ uint8_t **serial_number,
+ size_t *serial_number_size,
+ const char **serial_number_dec_str)
+{
+ const ASN1_INTEGER *serial;
+ BIGNUM *bn = NULL;
+ size_t size;
+ uint8_t *buf = NULL;
+ int ret;
+ char *tmp_str = NULL;
+
+ serial = X509_get0_serialNumber(cert);
+ bn = ASN1_INTEGER_to_BN(serial, NULL);
+ if (bn == NULL) {
+ *serial_number_size = 0;
+ *serial_number = NULL;
+ *serial_number_dec_str = NULL;
+ return 0;
+ }
+
+ /* The serial number MUST be a positive integer. */
+ if (BN_is_zero(bn) || BN_is_negative(bn)) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ size = BN_num_bytes(bn);
+ if (size == 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ tmp_str = BN_bn2dec(bn);
+ if (tmp_str == NULL) {
+ ret = EIO;
+ goto done;
+ }
+
+ buf = talloc_size(mem_ctx, size);
+ if (buf == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = BN_bn2bin(bn, buf);
+ if (ret != size) {
+ ret = EIO;
+ goto done;
+ }
+
+ *serial_number_dec_str = talloc_strdup(mem_ctx, tmp_str);
+ if (*serial_number_dec_str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ *serial_number = buf;
+ *serial_number_size = size;
+
+ ret = 0;
+
+done:
+ if (ret != 0) {
+ talloc_free(buf);
+ }
+ BN_free(bn);
+ OPENSSL_free(tmp_str);
+
+ return ret;
+}
+
+static int get_subject_key_id(TALLOC_CTX *mem_ctx, X509 *cert,
+ uint8_t **subject_key_id,
+ size_t *subject_key_id_size)
+{
+ const ASN1_OCTET_STRING *ski;
+ size_t size = 0;
+ uint8_t *buf;
+
+ ski = X509_get0_subject_key_id(cert);
+ if (ski != NULL) {
+ size = ASN1_STRING_length(ski);
+ }
+ if (size == 0) {
+ *subject_key_id_size = 0;
+ *subject_key_id = NULL;
+ return 0;
+ }
+
+ buf = talloc_memdup(mem_ctx, ASN1_STRING_get0_data(ski), size);
+ if (buf == NULL) {
+ return ENOMEM;
+ }
+
+ *subject_key_id = buf;
+ *subject_key_id_size = size;
+
+ return 0;
+}
+
+int sss_cert_get_content(TALLOC_CTX *mem_ctx,
+ const uint8_t *der_blob, size_t der_size,
+ struct sss_cert_content **content)
+{
+ int ret;
+ struct sss_cert_content *cont = NULL;
+ X509 *cert = NULL;
+ const unsigned char *der;
+ BIO *bio_mem = NULL;
+ X509_NAME *tmp_name;
+
+ if (der_blob == NULL || der_size == 0) {
+ return EINVAL;
+ }
+
+ cont = talloc_zero(mem_ctx, struct sss_cert_content);
+ if (cont == NULL) {
+ return ENOMEM;
+ }
+
+ bio_mem = BIO_new(BIO_s_mem());
+ if (bio_mem == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ der = (const unsigned char *) der_blob;
+ cert = d2i_X509(NULL, &der, (int) der_size);
+ if (cert == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ tmp_name = X509_get_issuer_name(cert);
+
+ ret = get_rdn_list(cont, tmp_name, &cont->issuer_rdn_list);
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = rdn_list_2_dn_str(cont, NULL, cont->issuer_rdn_list,
+ &cont->issuer_str);
+ if (ret != 0) {
+ goto done;
+ }
+
+ tmp_name = X509_get_subject_name(cert);
+
+ ret = get_rdn_list(cont, tmp_name, &cont->subject_rdn_list);
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = rdn_list_2_dn_str(cont, NULL, cont->subject_rdn_list,
+ &cont->subject_str);
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = X509_check_purpose(cert, -1, -1);
+ if (ret < 0) {
+ ret = EIO;
+ goto done;
+ }
+ if ((X509_get_extension_flags(cert) & EXFLAG_KUSAGE)) {
+ cont->key_usage = X509_get_key_usage(cert);
+ } else {
+ /* According to X.509 https://www.itu.int/rec/T-REC-X.509-201610-I
+ * section 13.3.2 "Certificate match" "keyUsage matches if all of the
+ * bits set in the presented value are also set in the key usage
+ * extension in the stored attribute value, or if there is no key
+ * usage extension in the stored attribute value;". So we set all bits
+ * in our key_usage to make sure everything matches is keyUsage is not
+ * set in the certificate.
+ *
+ * Please note that NSS currently
+ * (https://bugzilla.mozilla.org/show_bug.cgi?id=549952) does not
+ * support 'decipherOnly' and will only use 0xff in this case. To have
+ * a consistent behavior with both libraries we will use UINT32_MAX
+ * for NSS as well. Since comparisons should be always done with a
+ * bit-wise and-operation the difference should not matter. */
+ cont->key_usage = UINT32_MAX;
+ }
+
+ ret = get_extended_key_usage_oids(cont, cert,
+ &(cont->extended_key_usage_oids));
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = get_san(cont, cert, &(cont->san_list));
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = get_serial_number(cont, cert, &(cont->serial_number),
+ &(cont->serial_number_size),
+ &(cont->serial_number_dec_str));
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = get_subject_key_id(cont, cert, &(cont->subject_key_id),
+ &(cont->subject_key_id_size));
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = get_sid_ext(cont, cert, &(cont->sid_ext));
+ if (ret != 0) {
+ goto done;
+ }
+
+ cont->cert_der = talloc_memdup(cont, der_blob, der_size);
+ if (cont->cert_der == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cont->cert_der_size = der_size;
+
+ ret = EOK;
+
+done:
+
+ X509_free(cert);
+ BIO_free_all(bio_mem);
+ CRYPTO_cleanup_all_ex_data();
+
+ if (ret == EOK) {
+ *content = cont;
+ } else {
+ talloc_free(cont);
+ }
+
+ return ret;
+}
diff --git a/src/lib/certmap/sss_cert_digest_crypto.c b/src/lib/certmap/sss_cert_digest_crypto.c
new file mode 100644
index 0000000..e2118f5
--- /dev/null
+++ b/src/lib/certmap/sss_cert_digest_crypto.c
@@ -0,0 +1,158 @@
+/*
+ SSSD - digest functions - OpenSSL version
+ The calls defined here should be usable outside of SSSD as well, e.g. in
+ libsss_certmap.
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2022
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <talloc.h>
+#include <openssl/opensslv.h>
+#include <openssl/objects.h>
+#include <openssl/evp.h>
+
+#include "lib/certmap/sss_certmap_int.h"
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+#define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
+#define EVP_MD_free(ctx)
+#define EVP_MD_fetch(ctx, algorithm, properties) discard_const(EVP_get_digestbyname(algorithm))
+#endif
+
+#define DATA_STEP_SIZE 30
+
+struct get_digest_data {
+ const char **list;
+ size_t size;
+ size_t idx;
+ int error;
+};
+
+static void get_digest_helper(const OBJ_NAME *name, void *arg)
+{
+ struct get_digest_data *data = (struct get_digest_data *) arg;
+ EVP_MD *md = NULL;
+
+ if (data->error != 0) {
+ return;
+ }
+
+ /* All digest names are expected to start with a lower case letter. For
+ * compatibility(?) with older version the digest list might contain some
+ * RSA based signature algorithms, e.g. RSA-SHA1, which should be ignored
+ * here. */
+ if (name == NULL || name->name == NULL || islower(*name->name) == 0
+ || strstr(name->name, "RSA") != NULL
+ || strstr(name->name, "rsa") != NULL) {
+ return;
+ }
+
+ /* check if digest can be fetched. */
+ md = EVP_MD_fetch(NULL, name->name, NULL);
+ if (md == NULL) {
+ return;
+ }
+ EVP_MD_free(md);
+
+ data->list[data->idx] = talloc_strdup(data->list, name->name);
+ if (data->list[data->idx] == NULL) {
+ data->error = ENOMEM;
+ return;
+ }
+
+ data->idx++;
+ if (data->idx == data->size - 1) {
+ data->size += DATA_STEP_SIZE;
+ data->list = talloc_realloc(data->list, data->list,
+ const char *, data->size);
+ if (data->list == NULL) {
+ data->error = ENOMEM;
+ return;
+ }
+ }
+ data->list[data->idx] = NULL;
+}
+
+
+int get_digest_list(TALLOC_CTX *mem_ctx, const char ***digest_list)
+{
+ struct get_digest_data data = { 0 };
+
+ data.size = DATA_STEP_SIZE;
+ data.list = talloc_array(mem_ctx, const char *, data.size);
+ if (data.list == NULL) {
+ return ENOMEM;
+ }
+
+ OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \
+ | OPENSSL_INIT_ADD_ALL_DIGESTS \
+ | OPENSSL_INIT_LOAD_CONFIG, NULL);
+
+ OBJ_NAME_do_all(OBJ_NAME_TYPE_MD_METH, get_digest_helper, &data);
+ if (data.error != 0) {
+ talloc_free(data.list);
+ return data.error;
+ }
+
+ *digest_list = data.list;
+
+ return 0;
+}
+
+int get_hash(TALLOC_CTX *mem_ctx, const uint8_t *blob, size_t blob_size,
+ const char *digest, bool upper, bool colon, bool reverse,
+ char **out)
+{
+ int ret;
+ EVP_MD *md = NULL;
+ unsigned char md_value[EVP_MAX_MD_SIZE];
+ unsigned int md_len;
+ char *tmp_str = NULL;
+
+ md = EVP_MD_fetch(NULL, digest, NULL);
+ if (md == NULL) {
+ return EINVAL;
+ }
+
+ ret = EVP_Digest(blob, blob_size, md_value, &md_len, md, NULL);
+ if (ret != 1) {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = bin_to_hex(mem_ctx, upper, colon, reverse, md_value, md_len,
+ &tmp_str);
+ if (ret != 0) {
+ goto done;
+ }
+
+ *out = tmp_str;
+ ret = 0;
+
+done:
+ if (ret != 0) {
+ talloc_free(tmp_str);
+ }
+ EVP_MD_free(md);
+
+ return ret;
+}
diff --git a/src/lib/certmap/sss_certmap.c b/src/lib/certmap/sss_certmap.c
new file mode 100644
index 0000000..50cb5b1
--- /dev/null
+++ b/src/lib/certmap/sss_certmap.c
@@ -0,0 +1,1337 @@
+/*
+ SSSD
+
+ Library for rule based certificate to user mapping
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <ctype.h>
+
+#include "util/util.h"
+#include "util/cert.h"
+#include "util/crypto/sss_crypto.h"
+#include "lib/certmap/sss_certmap.h"
+#include "lib/certmap/sss_certmap_int.h"
+
+int debug_level;
+void sss_debug_fn(const char *file,
+ long line,
+ const char *function,
+ int level,
+ const char *format, ...)
+{
+ return;
+}
+
+int bin_to_hex(TALLOC_CTX *mem_ctx, bool upper_case, bool colon_sep,
+ bool reverse, uint8_t *buf, size_t len, char **out)
+{
+ char *o;
+ size_t c;
+ const char *fmt = NULL;
+ size_t s;
+ size_t chop_end = 0;
+
+ if (len == 0 || buf == NULL) {
+ return EINVAL;
+ }
+
+ if (upper_case) {
+ if (colon_sep) {
+ fmt = "%02X:";
+ s = 3;
+ chop_end =1;
+ } else {
+ fmt = "%02X";
+ s = 2;
+ }
+ } else {
+ if (colon_sep) {
+ fmt = "%02x:";
+ s = 3;
+ chop_end =1;
+ } else {
+ fmt = "%02x";
+ s = 2;
+ }
+ }
+
+ o = talloc_size(mem_ctx, (len * s) + 1);
+ if (o == NULL) {
+ return ENOMEM;
+ }
+
+ for (c = 0; c < len; c++) {
+ if (reverse) {
+ snprintf(o+(c*s), s+1, fmt, buf[len -1 -c]);
+ } else {
+ snprintf(o+(c*s), s+1, fmt, buf[c]);
+ }
+ }
+ o[(len * s) - chop_end] = '\0';
+
+ *out = o;
+
+ return 0;
+}
+
+static int get_type_prefix(TALLOC_CTX *mem_ctx, const char *match_rule,
+ char **type, const char **rule_start)
+{
+ const char *c;
+ char *delim;
+
+ *type = NULL;
+ *rule_start = match_rule;
+
+ delim = strchr(match_rule, ':');
+ if (delim == NULL) {
+ /* no type prefix found */
+ return 0;
+ }
+
+ /* rule starts with ':', empty type */
+ if (delim == match_rule) {
+ *rule_start = delim + 1;
+ return EOK;
+ }
+
+ for (c = match_rule; c < delim; c++) {
+ /* type prefix may only contain digits and upper-case ASCII characters */
+ if (!(isascii(*c) && (isdigit(*c) || isupper(*c)))) {
+ /* no type prefix found */
+ return 0;
+ }
+ }
+
+ *rule_start = delim + 1;
+ *type = talloc_strndup(mem_ctx, match_rule, (delim - match_rule));
+ if (*type == NULL) {
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+static int parse_match_rule(struct sss_certmap_ctx *ctx, const char *match_rule,
+ struct krb5_match_rule **parsed_match_rule)
+{
+ int ret;
+ char *type;
+ const char *rule_start;
+
+ ret = get_type_prefix(ctx, match_rule, &type, &rule_start);
+ if (ret != EOK) {
+ CM_DEBUG(ctx, "Failed to read rule type.");
+ goto done;
+ }
+
+ if (type == NULL || strcmp(type, "KRB5") == 0) {
+ ret = parse_krb5_match_rule(ctx, rule_start, parsed_match_rule);
+ if (ret != EOK) {
+ CM_DEBUG(ctx, "Failed to parse KRB5 matching rule.");
+ goto done;
+ }
+ } else {
+ CM_DEBUG(ctx, "Unsupported matching rule type.");
+ ret = ESRCH;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(type);
+
+ return ret;
+}
+
+static int parse_mapping_rule(struct sss_certmap_ctx *ctx,
+ const char *mapping_rule,
+ struct ldap_mapping_rule **parsed_mapping_rule)
+{
+ int ret;
+ char *type;
+ const char *rule_start;
+
+ ret = get_type_prefix(ctx, mapping_rule, &type, &rule_start);
+ if (ret != EOK) {
+ CM_DEBUG(ctx, "Failed to read rule type.");
+ goto done;
+ }
+
+ if (type == NULL || strcmp(type, "LDAP") == 0) {
+ ctx->mapv = mapv_ldap;
+ ret = parse_ldap_mapping_rule(ctx, rule_start, parsed_mapping_rule);
+ if (ret != EOK) {
+ CM_DEBUG(ctx, "Failed to parse LDAP mapping rule.");
+ goto done;
+ }
+ } else if (strcmp(type, "LDAPU1") == 0) {
+ ctx->mapv = mapv_ldapu1;
+ ret = parse_ldap_mapping_rule(ctx, rule_start, parsed_mapping_rule);
+ if (ret != EOK) {
+ CM_DEBUG(ctx, "Failed to parse LDAPU1 mapping rule.");
+ goto done;
+ }
+ } else {
+ CM_DEBUG(ctx, "Unsupported mapping rule type.");
+ ret = ESRCH;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(type);
+
+ return ret;
+}
+
+int sss_certmap_add_rule(struct sss_certmap_ctx *ctx,
+ uint32_t priority, const char *match_rule,
+ const char *map_rule, const char **domains)
+{
+ size_t c;
+ int ret;
+ struct match_map_rule *rule;
+ struct TALLOC_CTX *tmp_ctx;
+ struct priority_list *p;
+ struct priority_list *p_new;
+ struct krb5_match_rule *parsed_match_rule;
+ struct ldap_mapping_rule *parsed_mapping_rule;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ rule = talloc_zero(tmp_ctx, struct match_map_rule);
+ if (rule == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ rule->priority = priority;
+
+ if (match_rule == NULL) {
+ match_rule = DEFAULT_MATCH_RULE;
+ }
+ ret = parse_match_rule(ctx, match_rule, &parsed_match_rule);
+ if (ret == 0) {
+ rule->parsed_match_rule = talloc_steal(rule, parsed_match_rule);
+ rule->match_rule = talloc_strdup(rule, match_rule);
+ if (rule->match_rule == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else if (ret == ESRCH) {
+ /* report unsupported rules */
+ goto done;
+ } else {
+ goto done;
+ }
+
+ if (map_rule == NULL) {
+ map_rule = DEFAULT_MAP_RULE;
+ }
+ ret = parse_mapping_rule(ctx, map_rule, &parsed_mapping_rule);
+ if (ret == 0) {
+ rule->parsed_mapping_rule = talloc_steal(rule, parsed_mapping_rule);
+ rule->map_rule = talloc_strdup(rule, map_rule);
+ if (rule->map_rule == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else if (ret == ESRCH) {
+ /* report unsupported rules */
+ goto done;
+ } else {
+ goto done;
+ }
+
+ if (domains != NULL && *domains != NULL) {
+ for (c = 0; domains[c] != NULL; c++);
+ rule->domains = talloc_zero_array(rule, char *, c + 1);
+ if (rule->domains == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ for (c = 0; domains[c] != NULL; c++) {
+ rule->domains[c] = talloc_strdup(rule->domains, domains[c]);
+ if (rule->domains[c] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ if (ctx->prio_list == NULL) {
+ ctx->prio_list = talloc_zero(ctx, struct priority_list);
+ if (ctx->prio_list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ctx->prio_list->priority = rule->priority;
+ ctx->prio_list->rule_list = rule;
+ } else {
+ for (p = ctx->prio_list; p != NULL && p->priority < rule->priority;
+ p = p->next);
+ if (p != NULL && p->priority == priority) {
+ DLIST_ADD(p->rule_list, rule);
+ } else {
+ p_new = talloc_zero(ctx, struct priority_list);
+ if (p_new == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ p_new->priority = rule->priority;
+ p_new->rule_list = rule;
+
+ if (p == NULL) {
+ DLIST_ADD_END(ctx->prio_list, p_new, struct priority_list *);
+ } else if (p->prev == NULL) {
+ DLIST_ADD(ctx->prio_list, p_new);
+ } else {
+ DLIST_ADD_AFTER(ctx->prio_list, p_new, p->prev);
+ }
+ }
+ }
+
+ talloc_steal(ctx, rule);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static int expand_cert(struct sss_certmap_ctx *ctx,
+ struct parsed_template *parsed_template,
+ struct sss_cert_content *cert_content,
+ char **expanded)
+{
+ int ret;
+ char *tmp_str = NULL;
+ const char *dgst = NULL;
+ bool upper = false;
+ bool colon = false;
+ bool reverse = false;
+
+ if (parsed_template->conversion == NULL
+ || strcmp(parsed_template->conversion, "bin") == 0) {
+ ret = bin_to_ldap_filter_value(ctx, cert_content->cert_der,
+ cert_content->cert_der_size, &tmp_str);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "bin conversion failed.");
+ goto done;
+ }
+ } else if (strcmp(parsed_template->conversion, "base64") == 0) {
+ tmp_str = sss_base64_encode(ctx, cert_content->cert_der,
+ cert_content->cert_der_size);
+ if (tmp_str == NULL) {
+ CM_DEBUG(ctx, "base64 conversion failed.");
+ ret = ENOMEM;
+ goto done;
+ }
+ } else if (check_digest_conversion(parsed_template->conversion,
+ ctx->digest_list, &dgst,
+ &upper, &colon, &reverse) == 0) {
+ ret = get_hash(ctx, cert_content->cert_der, cert_content->cert_der_size,
+ dgst, upper, colon, reverse, &tmp_str);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to generate digest of certificate.");
+ goto done;
+ }
+ } else {
+ CM_DEBUG(ctx, "Unsupported conversion.");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *expanded = tmp_str;
+ } else {
+ talloc_free(tmp_str);
+ }
+
+ return ret;
+}
+
+static int expand_bin_number_array(struct sss_certmap_ctx *ctx,
+ struct parsed_template *parsed_template,
+ uint8_t *bin_number,
+ size_t bin_number_size,
+ const char *bin_number_dec_str,
+ char **expanded)
+{
+ int ret;
+ char *tmp_str = NULL;
+ bool dec = false;
+ bool upper = false;
+ bool colon = false;
+ bool reverse = false;
+
+ if (bin_number == NULL || bin_number_size == 0) {
+ CM_DEBUG(ctx, "Missing data for conversion.");
+ ret = ENOENT;
+ goto done;
+ }
+
+ ret = check_hex_conversion(parsed_template->conversion, true,
+ &dec, &upper, &colon, &reverse);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Unsupported conversion.");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (dec) {
+ if (bin_number_dec_str != NULL) {
+ tmp_str = talloc_strdup(ctx, bin_number_dec_str);
+ if (tmp_str == NULL) {
+ CM_DEBUG(ctx, "Failed to copy binary number string.");
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = 0;
+ } else {
+ CM_DEBUG(ctx, "Missing string for 'dec' conversion.");
+ ret = ENOENT;
+ goto done;
+ }
+ } else {
+ ret = bin_to_hex(ctx, upper, colon, reverse,
+ bin_number, bin_number_size, &tmp_str);
+ }
+ if (ret != 0) {
+ CM_DEBUG(ctx, "%s conversion failed.", parsed_template->conversion);
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *expanded = tmp_str;
+ } else {
+ talloc_free(tmp_str);
+ }
+
+ return ret;
+}
+
+static int expand_san_blob(struct sss_certmap_ctx *ctx, enum san_opt san_opt,
+ struct san_list *san_list, char **expanded)
+{
+ struct san_list *item;
+ char *exp;
+ int ret;
+
+ DLIST_FOR_EACH(item, san_list) {
+ if (item->san_opt == san_opt) {
+ ret = bin_to_ldap_filter_value(ctx, item->bin_val,
+ item->bin_val_len, &exp);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "bin conversion failed.");
+ return ret;
+ }
+
+ *expanded = exp;
+ return 0;
+ }
+ }
+
+ return ENOENT;
+}
+
+static int expand_san_string(struct sss_certmap_ctx *ctx, enum san_opt san_opt,
+ struct san_list *san_list, const char *attr_name,
+ char **expanded)
+{
+ struct san_list *item;
+ char *exp;
+
+ DLIST_FOR_EACH(item, san_list) {
+ if (item->san_opt == san_opt) {
+ if (attr_name == NULL) {
+ exp = talloc_strdup(ctx, item->val);
+ } else if (strcasecmp(attr_name, "short_name") == 0) {
+ exp = talloc_strdup(ctx, item->short_name);
+ } else {
+ CM_DEBUG(ctx, "Unsupported attribute name [%s].", attr_name);
+ return EINVAL;
+ }
+
+ if (exp == NULL) {
+ return ENOMEM;
+ }
+
+ *expanded = exp;
+ return 0;
+ }
+ }
+
+ return ENOENT;
+}
+
+static int expand_san_rdn_list(struct sss_certmap_ctx *ctx,
+ enum san_opt san_opt,
+ struct san_list *san_list,
+ const char *conversion,
+ char **expanded)
+{
+ struct san_list *item;
+ char *exp;
+ int ret;
+
+ DLIST_FOR_EACH(item, san_list) {
+ if (item->san_opt == san_opt) {
+ ret = rdn_list_2_dn_str(ctx, conversion, item->rdn_list, &exp);
+ if (ret != 0) {
+ return ret;
+ }
+
+ *expanded = exp;
+ return 0;
+ }
+ }
+
+ return ENOENT;
+}
+
+
+static int expand_san(struct sss_certmap_ctx *ctx,
+ struct parsed_template *parsed_template,
+ struct san_list *san_list,
+ char **expanded)
+{
+ int ret;
+
+ if (strcmp("subject_rfc822_name", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_RFC822_NAME, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_dns_name", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_DNS_NAME, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_x400_address", parsed_template->name) == 0) {
+ ret = expand_san_blob(ctx, SAN_X400_ADDRESS, san_list, expanded);
+ } else if (strcmp("subject_directory_name", parsed_template->name) == 0) {
+ ret = expand_san_rdn_list(ctx, SAN_DIRECTORY_NAME, san_list,
+ parsed_template->conversion, expanded);
+ } else if (strcmp("subject_ediparty_name", parsed_template->name) == 0) {
+ ret = expand_san_blob(ctx, SAN_EDIPART_NAME, san_list, expanded);
+ } else if (strcmp("subject_uri", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_URI, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_ip_address", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_IP_ADDRESS, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_registered_id", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_REGISTERED_ID, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_pkinit_principal", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_PKINIT, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_nt_principal", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_NT, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_principal", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_PRINCIPAL, san_list,
+ parsed_template->attr_name, expanded);
+ } else {
+ CM_DEBUG(ctx, "Unsupported template name [%s].n",
+ parsed_template->name);
+ ret = EINVAL;
+ }
+
+ return ret;
+}
+
+static int expand_sid(struct sss_certmap_ctx *ctx, const char *attr_name,
+ const char *sid, char **expanded)
+{
+ char *exp;
+ char *sep;
+
+ if (attr_name == NULL) {
+ exp = talloc_strdup(ctx, sid);
+ } else if (strcasecmp(attr_name, "rid") == 0) {
+ sep = strrchr(sid, '-');
+ if (sep == NULL || sep[1] == '\0') {
+ CM_DEBUG(ctx, "Unsupported SID string [%s].", sid);
+ return EINVAL;
+ }
+ exp = talloc_strdup(ctx, sep+1);
+ } else {
+ CM_DEBUG(ctx, "Unsupported attribute name [%s].", attr_name);
+ return EINVAL;
+ }
+
+ if (exp == NULL) {
+ return ENOMEM;
+ }
+
+ *expanded = exp;
+ return 0;
+}
+
+static int expand_template(struct sss_certmap_ctx *ctx,
+ struct parsed_template *parsed_template,
+ struct sss_cert_content *cert_content,
+ bool sanitize,
+ char **expanded)
+{
+ int ret;
+ char *exp = NULL;
+ char *exp_sanitized = NULL;
+
+ if (strcmp("issuer_dn", parsed_template->name) == 0) {
+ ret = rdn_list_2_dn_str(ctx, parsed_template->conversion,
+ cert_content->issuer_rdn_list, &exp);
+ } else if (strcmp("subject_dn", parsed_template->name) == 0) {
+ ret = rdn_list_2_dn_str(ctx, parsed_template->conversion,
+ cert_content->subject_rdn_list, &exp);
+ } else if (strcmp("subject_key_id", parsed_template->name) == 0) {
+ ret = expand_bin_number_array(ctx, parsed_template,
+ cert_content->subject_key_id,
+ cert_content->subject_key_id_size,
+ NULL, &exp);
+ } else if (strcmp("issuer_dn_component", parsed_template->name) == 0) {
+ ret = rdn_list_2_component(ctx, parsed_template->attr_name,
+ cert_content->issuer_rdn_list, &exp);
+ } else if (strcmp("subject_dn_component", parsed_template->name) == 0) {
+ ret = rdn_list_2_component(ctx, parsed_template->attr_name,
+ cert_content->subject_rdn_list, &exp);
+ } else if (strncmp("subject_", parsed_template->name, 8) == 0) {
+ ret = expand_san(ctx, parsed_template, cert_content->san_list, &exp);
+ } else if (strcmp("cert", parsed_template->name) == 0) {
+ /* cert blob is already sanitized */
+ sanitize = false;
+ ret = expand_cert(ctx, parsed_template, cert_content, &exp);
+ } else if (strcmp("serial_number", parsed_template->name) == 0) {
+ ret = expand_bin_number_array(ctx, parsed_template,
+ cert_content->serial_number,
+ cert_content->serial_number_size,
+ cert_content->serial_number_dec_str,
+ &exp);
+ } else if (strcmp("sid", parsed_template->name) == 0) {
+ ret = expand_sid(ctx, parsed_template->attr_name,
+ cert_content->sid_ext, &exp);
+ } else {
+ CM_DEBUG(ctx, "Unsupported template name.");
+ ret = EINVAL;
+ goto done;
+ }
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to expand [%s] template.", parsed_template->name);
+ goto done;
+ }
+
+ if (exp == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (sanitize) {
+ ret = sss_filter_sanitize(ctx, exp, &exp_sanitized);
+ if (ret != EOK) {
+ CM_DEBUG(ctx, "Failed to sanitize expanded template.");
+ goto done;
+ }
+ talloc_free(exp);
+ exp = exp_sanitized;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *expanded = exp;
+ } else {
+ talloc_free(exp);
+ }
+
+ return ret;
+}
+
+static int get_filter(struct sss_certmap_ctx *ctx,
+ struct ldap_mapping_rule *parsed_mapping_rule,
+ struct sss_cert_content *cert_content, bool sanitize,
+ char **filter)
+{
+ struct ldap_mapping_rule_comp *comp;
+ char *result = NULL;
+ char *expanded = NULL;
+ int ret;
+
+ result = talloc_strdup(ctx, "");
+ if (result == NULL) {
+ return ENOMEM;
+ }
+
+ for (comp = parsed_mapping_rule->list; comp != NULL; comp = comp->next) {
+ if (comp->type == comp_string) {
+ result = talloc_strdup_append(result, comp->val);
+ } else if (comp->type == comp_template) {
+ ret = expand_template(ctx, comp->parsed_template, cert_content,
+ sanitize, &expanded);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to expanded template.");
+ goto done;
+ }
+
+ result = talloc_strdup_append(result, expanded);
+ talloc_free(expanded);
+ expanded = NULL;
+ if (result == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ ret = EINVAL;
+ CM_DEBUG(ctx, "Unsupported component type.");
+ goto done;
+ }
+ }
+
+ ret = 0;
+done:
+ talloc_free(expanded);
+ if (ret == 0) {
+ *filter = result;
+ } else {
+ talloc_free(result);
+ }
+
+ return ret;
+}
+
+static bool check_san_regexp(struct sss_certmap_ctx *ctx,
+ enum san_opt san_opt, regex_t regexp,
+ struct san_list *san_list)
+{
+ struct san_list *item;
+ bool match = false;
+ int ret;
+ char *tmp_str = NULL;
+
+ DLIST_FOR_EACH(item, san_list) {
+ if (item->san_opt == san_opt) {
+ if (item->san_opt == SAN_DIRECTORY_NAME) {
+ /* use LDAP order for matching */
+ ret = rdn_list_2_dn_str(ctx, NULL, item->rdn_list, &tmp_str);
+ if (ret != 0 || tmp_str == NULL) {
+ return false;
+ }
+ match = (regexec(&regexp, tmp_str, 0, NULL, 0) == 0);
+ talloc_free(tmp_str);
+ } else {
+ match = (item->val != NULL
+ && regexec(&regexp, item->val, 0, NULL, 0) == 0);
+ }
+ if (!match) {
+ return false;
+ }
+ }
+ }
+
+ return match;
+}
+
+static bool check_san_blob(enum san_opt san_opt,
+ uint8_t *bin_val, size_t bin_val_len,
+ struct san_list *san_list)
+{
+ struct san_list *item;
+ bool match = false;
+
+ if (bin_val == NULL || bin_val_len == 0) {
+ return false;
+ }
+
+ DLIST_FOR_EACH(item, san_list) {
+ if (item->san_opt == san_opt) {
+ match = (item->bin_val != NULL && item->bin_val_len != 0
+ && memmem(item->bin_val, item->bin_val_len,
+ bin_val, bin_val_len) != NULL);
+ if (!match) {
+ return false;
+ }
+ }
+ }
+
+ return match;
+}
+
+static bool check_san_str_other_name(enum san_opt san_opt,
+ const char *str_other_name_oid,
+ regex_t regexp,
+ struct san_list *san_list)
+{
+ struct san_list *item;
+ bool match = false;
+ char *tmp_str;
+
+ if (str_other_name_oid == NULL) {
+ return false;
+ }
+
+ DLIST_FOR_EACH(item, san_list) {
+ if (item->san_opt == san_opt
+ && strcmp(item->other_name_oid, str_other_name_oid) == 0) {
+ match = false;
+ if (item->bin_val != NULL && item->bin_val_len != 0) {
+ tmp_str = talloc_strndup(item, (char *) item->bin_val,
+ item->bin_val_len);
+ if (tmp_str != NULL) {
+ match = (regexec(&regexp, tmp_str, 0, NULL, 0) == 0);
+ }
+ talloc_free(tmp_str);
+ }
+ if (!match) {
+ return false;
+ }
+ }
+ }
+
+ return match;
+}
+
+static bool do_san_match(struct sss_certmap_ctx *ctx,
+ struct component_list *comp,
+ struct san_list *san_list)
+{
+ switch (comp->san_opt) {
+ case SAN_OTHER_NAME:
+ return check_san_blob(SAN_STRING_OTHER_NAME,
+ comp->bin_val, comp->bin_val_len,
+ san_list);
+ break;
+ case SAN_X400_ADDRESS:
+ case SAN_EDIPART_NAME:
+ return check_san_blob(comp->san_opt, comp->bin_val, comp->bin_val_len,
+ san_list);
+ break;
+ case SAN_RFC822_NAME:
+ case SAN_DNS_NAME:
+ case SAN_DIRECTORY_NAME:
+ case SAN_URI:
+ case SAN_IP_ADDRESS:
+ case SAN_REGISTERED_ID:
+ case SAN_PKINIT:
+ case SAN_NT:
+ case SAN_PRINCIPAL:
+ return check_san_regexp(ctx, comp->san_opt, comp->regexp, san_list);
+ break;
+ case SAN_STRING_OTHER_NAME:
+ return check_san_str_other_name(comp->san_opt, comp->str_other_name_oid,
+ comp->regexp, san_list);
+ break;
+ default:
+ CM_DEBUG(ctx, "Unsupported SAN option [%d].", comp->san_opt);
+ return false;
+ }
+}
+
+static int do_match(struct sss_certmap_ctx *ctx,
+ struct krb5_match_rule *parsed_match_rule,
+ struct sss_cert_content *cert_content)
+{
+ struct component_list *comp;
+ bool match = false;
+ size_t c;
+
+ if (parsed_match_rule == NULL || cert_content == NULL) {
+ return EINVAL;
+ }
+
+ /* Issuer */
+ for (comp = parsed_match_rule->issuer; comp != NULL; comp = comp->next) {
+ match = (cert_content->issuer_str != NULL
+ && regexec(&(comp->regexp), cert_content->issuer_str,
+ 0, NULL, 0) == 0);
+ if (match && parsed_match_rule->r == relation_or) {
+ /* match */
+ return 0;
+ } else if (!match && parsed_match_rule->r == relation_and) {
+ /* no match */
+ return ENOENT;
+ }
+
+ }
+
+ /* Subject */
+ for (comp = parsed_match_rule->subject; comp != NULL; comp = comp->next) {
+ match = (cert_content->subject_str != NULL
+ && regexec(&(comp->regexp), cert_content->subject_str,
+ 0, NULL, 0) == 0);
+ if (match && parsed_match_rule->r == relation_or) {
+ /* match */
+ return 0;
+ } else if (!match && parsed_match_rule->r == relation_and) {
+ /* no match */
+ return ENOENT;
+ }
+
+ }
+
+ /* Key Usage */
+ for (comp = parsed_match_rule->ku; comp != NULL; comp = comp->next) {
+ match = ((cert_content->key_usage & comp->ku) == comp->ku);
+ if (match && parsed_match_rule->r == relation_or) {
+ /* match */
+ return 0;
+ } else if (!match && parsed_match_rule->r == relation_and) {
+ /* no match */
+ return ENOENT;
+ }
+ }
+
+ /* Extended Key Usage */
+ for (comp = parsed_match_rule->eku; comp != NULL; comp = comp->next) {
+ for (c = 0; comp->eku_oid_list[c] != NULL; c++) {
+ match = string_in_list(comp->eku_oid_list[c],
+ discard_const(
+ cert_content->extended_key_usage_oids),
+ true);
+ if (match && parsed_match_rule->r == relation_or) {
+ /* match */
+ return 0;
+ } else if (!match && parsed_match_rule->r == relation_and) {
+ /* no match */
+ return ENOENT;
+ }
+ }
+ }
+
+ /* SAN */
+ for (comp = parsed_match_rule->san; comp != NULL; comp = comp->next) {
+ match = do_san_match(ctx, comp, cert_content->san_list);
+ if (match && parsed_match_rule->r == relation_or) {
+ /* match */
+ return 0;
+ } else if (!match && parsed_match_rule->r == relation_and) {
+ /* no match */
+ return ENOENT;
+ }
+ }
+
+ if (match) {
+ /* match */
+ return 0;
+ }
+
+ /* no match */
+ return ENOENT;
+}
+
+int sss_certmap_match_cert(struct sss_certmap_ctx *ctx,
+ const uint8_t *der_cert, size_t der_size)
+{
+ int ret;
+ struct match_map_rule *r;
+ struct priority_list *p;
+ struct sss_cert_content *cert_content = NULL;
+
+ ret = sss_cert_get_content(ctx, der_cert, der_size, &cert_content);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to get certificate content.");
+ return ret;
+ }
+
+ if (ctx->prio_list == NULL) {
+ /* Match all certificates if there are no rules applied */
+ ret = 0;
+ goto done;
+ }
+
+ for (p = ctx->prio_list; p != NULL; p = p->next) {
+ for (r = p->rule_list; r != NULL; r = r->next) {
+ ret = do_match(ctx, r->parsed_match_rule, cert_content);
+ if (ret == 0) {
+ /* match */
+ goto done;
+ }
+ }
+ }
+
+ ret = ENOENT;
+done:
+ talloc_free(cert_content);
+
+ return ret;
+}
+
+static int expand_mapping_rule_ex(struct sss_certmap_ctx *ctx,
+ const uint8_t *der_cert, size_t der_size,
+ bool sanitize,
+ char **_filter, char ***_domains)
+{
+ int ret;
+ struct match_map_rule *r;
+ struct priority_list *p;
+ struct sss_cert_content *cert_content = NULL;
+ char *filter = NULL;
+ char **domains = NULL;
+ size_t c;
+
+ if (_filter == NULL || _domains == NULL) {
+ return EINVAL;
+ }
+
+ ret = sss_cert_get_content(ctx, der_cert, der_size, &cert_content);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to get certificate content [%d].", ret);
+ return ret;
+ }
+
+ if (ctx->prio_list == NULL) {
+ if (ctx->default_mapping_rule == NULL) {
+ CM_DEBUG(ctx, "No matching or mapping rules available.");
+ return EINVAL;
+ }
+
+ ret = get_filter(ctx, ctx->default_mapping_rule, cert_content, sanitize,
+ &filter);
+ goto done;
+ }
+
+ for (p = ctx->prio_list; p != NULL; p = p->next) {
+ for (r = p->rule_list; r != NULL; r = r->next) {
+ ret = do_match(ctx, r->parsed_match_rule, cert_content);
+ if (ret == 0) {
+ /* match */
+ ret = get_filter(ctx, r->parsed_mapping_rule, cert_content,
+ sanitize, &filter);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to get filter");
+ goto done;
+ }
+
+ if (r->domains != NULL) {
+ for (c = 0; r->domains[c] != NULL; c++);
+ domains = talloc_zero_array(ctx, char *, c + 1);
+ if (domains == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; r->domains[c] != NULL; c++) {
+ domains[c] = talloc_strdup(domains, r->domains[c]);
+ if (domains[c] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ ret = 0;
+ goto done;
+ }
+ }
+ }
+
+ ret = ENOENT;
+
+done:
+ talloc_free(cert_content);
+ if (ret == 0) {
+ *_filter = filter;
+ *_domains = domains;
+ } else {
+ talloc_free(filter);
+ talloc_free(domains);
+ }
+
+ return ret;
+}
+
+int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx,
+ const uint8_t *der_cert, size_t der_size,
+ char **_filter, char ***_domains)
+{
+ return expand_mapping_rule_ex(ctx, der_cert, der_size, true,
+ _filter, _domains);
+}
+
+int sss_certmap_expand_mapping_rule(struct sss_certmap_ctx *ctx,
+ const uint8_t *der_cert, size_t der_size,
+ char **_expanded, char ***_domains)
+{
+ return expand_mapping_rule_ex(ctx, der_cert, der_size, false,
+ _expanded, _domains);
+}
+
+int sss_certmap_init(TALLOC_CTX *mem_ctx,
+ sss_certmap_ext_debug *debug, void *debug_priv,
+ struct sss_certmap_ctx **ctx)
+{
+ int ret;
+
+ if (ctx == NULL) {
+ return EINVAL;
+ }
+
+ *ctx = talloc_zero(mem_ctx, struct sss_certmap_ctx);
+ if (*ctx == NULL) {
+ return ENOMEM;
+ }
+
+ (*ctx)->debug = debug;
+ (*ctx)->debug_priv = debug_priv;
+
+ ret = parse_mapping_rule(*ctx, DEFAULT_MAP_RULE,
+ &((*ctx)->default_mapping_rule));
+ if (ret != 0) {
+ CM_DEBUG((*ctx), "Failed to parse default mapping rule.");
+ talloc_free(*ctx);
+ *ctx = NULL;
+ return ret;
+ }
+
+ ret = get_digest_list(*ctx, &((*ctx)->digest_list));
+ if (ret != 0) {
+ CM_DEBUG((*ctx), "Failed to get digest list.");
+ talloc_free(*ctx);
+ *ctx = NULL;
+ return ret;
+ }
+
+ return EOK;
+}
+
+void sss_certmap_free_ctx(struct sss_certmap_ctx *ctx)
+{
+ talloc_free(ctx);
+}
+
+void sss_certmap_free_filter_and_domains(char *filter, char **domains)
+{
+ talloc_free(filter);
+ talloc_free(domains);
+}
+
+static const char *sss_eku_oid2name(const char *oid)
+{
+ size_t c;
+
+ for (c = 0; sss_ext_key_usage[c].name != NULL; c++) {
+ if (strcmp(sss_ext_key_usage[c].oid, oid) == 0) {
+ return sss_ext_key_usage[c].name;
+ }
+ }
+
+ return NULL;
+}
+
+struct parsed_template san_parsed_template[] = {
+ { NULL, NULL, NULL }, /* SAN_OTHER_NAME handled separately */
+ { "subject_rfc822_name", NULL, NULL},
+ { "subject_dns_name", NULL, NULL},
+ { "subject_x400_address", NULL, NULL},
+ { "subject_directory_name", NULL, NULL},
+ { "subject_ediparty_name", NULL, NULL},
+ { "subject_uri", NULL, NULL},
+ { "subject_ip_address", NULL, NULL},
+ { "subject_registered_id", NULL, NULL},
+ { "subject_pkinit_principal", NULL, NULL},
+ { "subject_nt_principal", NULL, NULL},
+ { "subject_principal", NULL, NULL},
+ { NULL, NULL, NULL }, /* SAN_STRING_OTHER_NAME handled separately */
+ { NULL, NULL, NULL } /* SAN_END */
+};
+
+static int sss_cert_dump_content(TALLOC_CTX *mem_ctx,
+ struct sss_cert_content *c,
+ char **content_str)
+{
+ char *out = NULL;
+ size_t o;
+ struct san_list *s;
+ struct sss_certmap_ctx *ctx = NULL;
+ char *expanded = NULL;
+ int ret;
+ int ret2;
+ char *b64 = NULL;
+ const char *eku_str = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *hex = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_certmap_init(tmp_ctx, NULL, NULL, &ctx);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = ENOMEM; /* default error code for upcoming memory allocation issues */
+ out = talloc_strdup(tmp_ctx, "sss cert content (format might change):\n");
+ if (out == NULL) goto done;
+
+ out = talloc_asprintf_append(out, "Issuer: %s\n", c->issuer_str != NULL
+ ? c->issuer_str
+ : "- not available -");
+ if (out == NULL) goto done;
+ out = talloc_asprintf_append(out, "Subject: %s\n", c->subject_str != NULL
+ ? c->subject_str
+ : "- not available -");
+ if (out == NULL) goto done;
+
+ out = talloc_asprintf_append(out, "Key Usage: %u(0x%04x)", c->key_usage,
+ c->key_usage);
+ if (out == NULL) goto done;
+
+ if (c->key_usage != 0) {
+ out = talloc_asprintf_append(out, " (");
+ if (out == NULL) goto done;
+ for (o = 0; sss_key_usage[o].name != NULL; o++) {
+ if ((c->key_usage & sss_key_usage[o].flag) != 0) {
+ out = talloc_asprintf_append(out, "%s%s",
+ o == 0 ? "" : ",",
+ sss_key_usage[o].name);
+ if (out == NULL) goto done;
+ }
+ }
+ out = talloc_asprintf_append(out, ")");
+ if (out == NULL) goto done;
+ }
+ out = talloc_asprintf_append(out, "\n");
+ if (out == NULL) goto done;
+
+ for (o = 0; c->extended_key_usage_oids[o] != NULL; o++) {
+ eku_str = sss_eku_oid2name(c->extended_key_usage_oids[o]);
+ out = talloc_asprintf_append(out, "Extended Key Usage #%zu: %s%s%s%s\n",
+ o, c->extended_key_usage_oids[o],
+ eku_str == NULL ? "" : " (",
+ eku_str == NULL ? "" : eku_str,
+ eku_str == NULL ? "" : ")");
+ if (out == NULL) goto done;
+ }
+
+ if (c->serial_number_size != 0) {
+ ret2 = bin_to_hex(out, false, true, false, c->serial_number,
+ c->serial_number_size, &hex);
+ if (ret2 == 0) {
+ out = talloc_asprintf_append(out, "Serial Number: %s (%s)\n", hex,
+ c->serial_number_dec_str);
+ talloc_free(hex);
+ } else {
+ out = talloc_asprintf_append(out,
+ "Serial Number: -- conversion failed --\n");
+ }
+ } else {
+ out = talloc_asprintf_append(out, "Serial Number: -- missing --\n");
+ }
+ if (out == NULL) goto done;
+
+ if (c->subject_key_id_size != 0) {
+ ret2 = bin_to_hex(out, false, true, false, c->subject_key_id,
+ c->subject_key_id_size, &hex);
+ if (ret2 == 0) {
+ out = talloc_asprintf_append(out, "Subject Key ID: %s\n", hex);
+ talloc_free(hex);
+ } else {
+ out = talloc_asprintf_append(out,
+ "Subject Key ID: -- conversion failed --\n");
+ }
+ } else {
+ out = talloc_asprintf_append(out, "Subject Key ID: -- missing --\n");
+ }
+ if (out == NULL) goto done;
+
+ out = talloc_asprintf_append(out, "SID: %s\n", c->sid_ext == NULL
+ ? "SID extension not available"
+ : c->sid_ext);
+ if (out == NULL) goto done;
+
+ DLIST_FOR_EACH(s, c->san_list) {
+ out = talloc_asprintf_append(out, "SAN type: %s\n",
+ s->san_opt < SAN_END
+ ? sss_san_names[s->san_opt].name
+ : "- unsupported -");
+ if (out == NULL) goto done;
+
+ if (san_parsed_template[s->san_opt].name != NULL) {
+ ret = expand_san(ctx, &san_parsed_template[s->san_opt], c->san_list,
+ &expanded);
+ if (ret != EOK) {
+ goto done;
+ }
+ out = talloc_asprintf_append(out, " %s=%s\n\n",
+ san_parsed_template[s->san_opt].name,
+ expanded);
+ talloc_free(expanded);
+ if (out == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else if (s->san_opt == SAN_STRING_OTHER_NAME) {
+ b64 = sss_base64_encode(tmp_ctx, s->bin_val, s->bin_val_len);
+ out = talloc_asprintf_append(out, " %s=%s\n\n", s->other_name_oid,
+ b64 != NULL ? b64
+ : "- cannot encode -");
+ talloc_free(b64);
+ if (out == NULL) goto done;
+ }
+ }
+
+ *content_str = talloc_steal(mem_ctx, out);
+
+ ret = EOK;
+
+done:
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sss_certmap_display_cert_content(TALLOC_CTX *mem_cxt,
+ const uint8_t *der_cert, size_t der_size,
+ char **desc)
+{
+ int ret;
+ struct sss_cert_content *content = NULL;
+
+ ret = sss_cert_get_content(mem_cxt, der_cert, der_size, &content);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sss_cert_dump_content(mem_cxt, content, desc);
+ talloc_free(content);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/src/lib/certmap/sss_certmap.doxy.in b/src/lib/certmap/sss_certmap.doxy.in
new file mode 100644
index 0000000..e8959e2
--- /dev/null
+++ b/src/lib/certmap/sss_certmap.doxy.in
@@ -0,0 +1,3 @@
+PROJECT_NAME = sss_certmap
+OUTPUT_DIRECTORY = certmap_doc
+INPUT = @abs_top_srcdir@/src/lib/certmap/sss_certmap.h
diff --git a/src/lib/certmap/sss_certmap.exports b/src/lib/certmap/sss_certmap.exports
new file mode 100644
index 0000000..7d76677
--- /dev/null
+++ b/src/lib/certmap/sss_certmap.exports
@@ -0,0 +1,23 @@
+SSS_CERTMAP_0.0 {
+ global:
+ sss_certmap_init;
+ sss_certmap_free_ctx;
+ sss_certmap_err_msg;
+ sss_certmap_add_rule;
+ sss_certmap_match_cert;
+ sss_certmap_get_search_filter;
+ sss_cert_get_content;
+ sss_certmap_free_filter_and_domains;
+ local:
+ *;
+};
+
+SSS_CERTMAP_0.1 {
+ global:
+ sss_certmap_display_cert_content;
+} SSS_CERTMAP_0.0;
+
+SSS_CERTMAP_0.2 {
+ global:
+ sss_certmap_expand_mapping_rule;
+} SSS_CERTMAP_0.1;
diff --git a/src/lib/certmap/sss_certmap.h b/src/lib/certmap/sss_certmap.h
new file mode 100644
index 0000000..058d4f9
--- /dev/null
+++ b/src/lib/certmap/sss_certmap.h
@@ -0,0 +1,195 @@
+/*
+ SSSD
+
+ Library for rule based certificate to user mapping
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SSS_CERTMAP_H_
+#define _SSS_CERTMAP_H_
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <talloc.h>
+
+/**
+ * @defgroup sss_certmap Allow rule-based mapping of certificates to users
+ * Libsss_certmap provides a mechanism to map X509 certificate to users based
+ * on rules.
+ * @{
+ */
+
+/**
+ * Opaque type for the idmap context
+ */
+struct sss_certmap_ctx;
+
+/**
+ * Lowest priority of a rule
+ */
+#define SSS_CERTMAP_MIN_PRIO UINT32_MAX
+
+/**
+ * Typedef for external debug callback
+ */
+typedef void (sss_certmap_ext_debug)(void *pvt,
+ const char *file, long line,
+ const char *function,
+ const char *format, ...);
+/**
+ * @brief Initialize certmap context
+ *
+ * @param[in] mem_ctx Talloc memory context, may be NULL
+ * @param[in] debug Callback to handle debug output, may be NULL
+ * @param[in] debug_priv Private data for debugging callback, may be NULL
+ * @param[out] ctx New certmap context
+ *
+ * @return
+ * - 0: success
+ * - ENOMEM: failed to allocate internal Talloc context
+ * - EINVAL: ctx is NULL
+ */
+int sss_certmap_init(TALLOC_CTX *mem_ctx,
+ sss_certmap_ext_debug *debug, void *debug_priv,
+ struct sss_certmap_ctx **ctx);
+
+/**
+ * @brief Free certmap context
+ *
+ * @param[in] ctx certmap context previously initialized with
+ * @ref sss_certmap_init, may be NULL
+ */
+void sss_certmap_free_ctx(struct sss_certmap_ctx *ctx);
+
+/**
+ * @brief Add a rule to the certmap context
+ *
+ * @param[in] ctx certmap context previously initialized with
+ * @ref sss_certmap_init
+ * @param[in] priority priority of the rule, 0 is the hightest priority, the
+ * lowest is SSS_CERTMAP_MIN_PRIO
+ * @param[in] match_rule String with the matching rule
+ * @param[in] map_rule String with the mapping rule
+ * @param[in] domains NULL-terminated string array with a list of domains
+ * the rule should be valid for, i.e. only this domains
+ * should be searched for matching users
+ *
+ * @return
+ * - 0: success
+ */
+int sss_certmap_add_rule(struct sss_certmap_ctx *ctx,
+ uint32_t priority, const char *match_rule,
+ const char *map_rule, const char **domains);
+
+/**
+ * @brief Check if a certificate matches any of the applied rules
+ *
+ * @param[in] ctx certmap context previously initialized with
+ * @ref sss_certmap_init
+ * @param[in] der_cert binary blob with the DER encoded certificate
+ * @param[in] der_size size of the certificate blob
+ *
+ * @return
+ * - 0: certificate matches a rule
+ * - ENOENT: certificate does not match
+ * - EINVAL: internal error
+ */
+int sss_certmap_match_cert(struct sss_certmap_ctx *ctx,
+ const uint8_t *der_cert, size_t der_size);
+
+/**
+ * @brief Get the LDAP filter string for a certificate
+ *
+ * @param[in] ctx certmap context previously initialized with
+ * @ref sss_certmap_init
+ * @param[in] der_cert binary blob with the DER encoded certificate
+ * @param[in] der_size size of the certificate blob
+ * @param[out] filter LDAP filter string, expanded templates are sanitized,
+ * caller should free the data by calling
+ * sss_certmap_free_filter_and_domains
+ * @param[out] domains NULL-terminated array of strings with the domains the
+ * rule applies, caller should free the data by calling
+ * sss_certmap_free_filter_and_domains
+ *
+ * @return
+ * - 0: certificate matches a rule
+ * - ENOENT: certificate does not match
+ * - EINVAL: internal error
+ */
+int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx,
+ const uint8_t *der_cert, size_t der_size,
+ char **filter, char ***domains);
+
+/**
+ * @brief Expand the mapping rule by replacing the templates
+ *
+ * @param[in] ctx certmap context previously initialized with
+ * @ref sss_certmap_init
+ * @param[in] der_cert binary blob with the DER encoded certificate
+ * @param[in] der_size size of the certificate blob
+ * @param[out] expanded expanded mapping rule, templates are filled in
+ * verbatim in contrast to sss_certmap_get_search_filter,
+ * caller should free the data by
+ * calling sss_certmap_free_filter_and_domains
+ * @param[out] domains NULL-terminated array of strings with the domains the
+ * rule applies, caller should free the data by calling
+ * sss_certmap_free_filter_and_domains
+ *
+ * @return
+ * - 0: certificate matches a rule
+ * - ENOENT: certificate does not match
+ * - EINVAL: internal error
+ */
+int sss_certmap_expand_mapping_rule(struct sss_certmap_ctx *ctx,
+ const uint8_t *der_cert, size_t der_size,
+ char **_expanded, char ***_domains);
+/**
+ * @brief Free data returned by @ref sss_certmap_get_search_filter
+ * and @ref sss_certmap_expand_mapping_rule
+ *
+ * @param[in] filter LDAP filter strings returned by
+ * sss_certmap_get_search_filter
+ * @param[in] domains string array of domains returned by
+ * sss_certmap_get_search_filter
+ */
+void sss_certmap_free_filter_and_domains(char *filter, char **domains);
+
+/**
+ * @brief Get a string with the content of the certificate used by the library
+ *
+ * @param[in] mem_ctx Talloc memory context, may be NULL
+ * @param[in] der_cert binary blob with the DER encoded certificate
+ * @param[in] der_size size of the certificate blob
+ * @param[out] desc Multiline string showing the certificate content
+ * which is used by libsss_certmap
+ *
+ * @return
+ * - 0: success
+ * - EINVAL: certificate cannot be parsed
+ * - ENOMEM: memory allocation failure
+ */
+int sss_certmap_display_cert_content(TALLOC_CTX *mem_cxt,
+ const uint8_t *der_cert, size_t der_size,
+ char **desc);
+
+/**
+ * @}
+ */
+#endif /* _SSS_CERTMAP_H_ */
diff --git a/src/lib/certmap/sss_certmap.pc.in b/src/lib/certmap/sss_certmap.pc.in
new file mode 100644
index 0000000..561ccb8
--- /dev/null
+++ b/src/lib/certmap/sss_certmap.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: sss_certmap
+Description: SSS certificate mapping library
+Version: @VERSION@
+Libs: -L${libdir} -lsss_certmap
+Cflags:
+URL: https://github.com/SSSD/sssd/
diff --git a/src/lib/certmap/sss_certmap_attr_names.c b/src/lib/certmap/sss_certmap_attr_names.c
new file mode 100644
index 0000000..65c0f91
--- /dev/null
+++ b/src/lib/certmap/sss_certmap_attr_names.c
@@ -0,0 +1,134 @@
+/*
+ SSSD
+
+ Library for rule based certificate to user mapping - Attribute name
+ mapping for different implementations
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* NSS data taken from nss-utils:nss/lib/util/secoid.c and
+ * nss:nss/lib/certdb/alg1485.c */
+
+/* AD data taken from
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa376556%28v=vs.85%29.aspx
+ * and wine source code dlls/crypt32/oid.c  and include/wincrypt.h . */
+
+/* OpenSSL data taken from include/openssl/obj_mac.h */
+
+#include <stdbool.h>
+#include <string.h>
+#include <talloc.h>
+
+struct oid_attr_name_map {
+ bool nss_ad_differ;
+ bool nss_openssl_differ;
+ const char *oid;
+ const char *nss;
+ const char *ad;
+ const char *openssl;
+} oid_attr_name_map[] = {
+ { false, false, "2.5.4.3", "CN", "CN", "CN"},
+ { true, false, "2.5.4.8", "ST", "S", "ST"},
+ { false, false, "2.5.4.10", "O", "O", "O"},
+ { false, false, "2.5.4.11", "OU", "OU", "OU"},
+ { false, false, "2.5.4.46", "dnQualifier", "dnQualifier", "dnQualifier"},
+ { false, false, "2.5.4.6", "C", "C", "C"},
+ { true, false, "2.5.4.5", "serialNumber", "SERIALNUMBER", "serialNumber"},
+ { false, false, "2.5.4.7", "L", "L", "L"},
+ { true, false, "2.5.4.12", "title", "T", "title"},
+ { false, false, "2.5.4.4", "SN", "SN", "SN"},
+ { true, true, "2.5.4.42", "givenName", "G", "GN"},
+ { true, false, "2.5.4.43", "initials", "I", "initials"},
+ { true, false, "2.5.4.44", "generationQualifier", "OID.2.5.4.44", "generationQualifier"},
+ { false, false, "0.9.2342.19200300.100.1.25", "DC", "DC", "DC"},
+ { true, true, "0.9.2342.19200300.100.1.3", "MAIL", "OID,0.9.2342.19200300.100.1.3", "mail"},
+ { true, false, "0.9.2342.19200300.100.1.1", "UID", "OID.0.9.2342.19200300.100.1.1", "UID"},
+ { true, true, "2.5.4.13", "OID.2.5.4.13", "Description", "description"},
+ { true, false, "2.5.4.16", "postalAddress", "OID.2.5.4.16", "postalAddress"},
+ { true, false, "2.5.4.17", "postalCode", "PostalCode", "postalCode"},
+ { true, false, "2.5.4.18", "postOfficeBox", "POBox", "postOfficeBox"},
+ { true, false, "2.5.4.51", "houseIdentifier", "OID.2.5.4.51", "houseIdentifier"},
+ { false, true, "1.2.840.113549.1.9.1", "E", "E", "emailAddress"},
+ { false, true, "2.5.4.9", "STREET", "STREET", "street"},
+ { true, false, "2.5.4.65", "pseudonym", "OID.2.5.4.65", "pseudonym"},
+ { true, false, "2.5.4.15", "businessCategory", "OID.2.5.4.15", "businessCategory"},
+ { true, false, "2.5.4.41", "name", "OID.2.5.4.41", "name"},
+
+ { false, false, NULL, NULL, NULL, NULL}
+};
+
+char *check_ad_attr_name(TALLOC_CTX *mem_ctx, const char *rdn)
+{
+ char *p;
+ size_t c;
+ size_t len;
+
+ if (rdn == NULL) {
+ return NULL;
+ }
+
+ p = strchr(rdn, '=');
+ if (p == NULL) {
+ return NULL;
+ }
+
+ len = p - rdn;
+ if (len == 0) {
+ return NULL;
+ }
+
+ for (c = 0; oid_attr_name_map[c].oid != NULL; c++) {
+ if (!oid_attr_name_map[c].nss_ad_differ) {
+ continue;
+ }
+
+ if (strlen(oid_attr_name_map[c].nss) != len
+ || strncmp(rdn, oid_attr_name_map[c].nss, len) != 0) {
+ continue;
+ }
+
+ return talloc_asprintf(mem_ctx, "%s%s", oid_attr_name_map[c].ad, p);
+ }
+
+ return NULL;
+}
+
+const char *openssl_2_nss_attr_name(const char *attr)
+{
+ size_t c;
+
+ if (attr == NULL) {
+ return NULL;
+ }
+
+ for (c = 0; oid_attr_name_map[c].oid != NULL; c++) {
+ if (!oid_attr_name_map[c].nss_openssl_differ) {
+ continue;
+ }
+
+ if (strcmp(attr, oid_attr_name_map[c].openssl) != 0) {
+ continue;
+ }
+
+ return oid_attr_name_map[c].nss;
+ }
+
+ return attr;
+}
diff --git a/src/lib/certmap/sss_certmap_int.h b/src/lib/certmap/sss_certmap_int.h
new file mode 100644
index 0000000..77727d8
--- /dev/null
+++ b/src/lib/certmap/sss_certmap_int.h
@@ -0,0 +1,502 @@
+/*
+ SSSD
+
+ Library for rule based certificate to user mapping
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SSS_CERTMAP_INT_H__
+#define __SSS_CERTMAP_INT_H__
+
+#include <sys/types.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <talloc.h>
+
+#include "lib/certmap/sss_certmap.h"
+
+#define CM_DEBUG(cm_ctx, format, ...) do { \
+ if (cm_ctx != NULL && cm_ctx->debug != NULL) { \
+ cm_ctx->debug(cm_ctx->debug_priv, __FILE__, __LINE__, __FUNCTION__, \
+ format, ##__VA_ARGS__); \
+ } \
+} while (0)
+
+#define DEFAULT_MATCH_RULE "<KU>digitalSignature<EKU>clientAuth"
+#define DEFAULT_MAP_RULE "LDAP:(userCertificate;binary={cert!bin})"
+
+#define PKINIT_OID "1.3.6.1.5.2.2"
+#define NT_PRINCIPAL_OID "1.3.6.1.4.1.311.20.2.3"
+
+enum san_opt {
+ SAN_OTHER_NAME = 0,
+ SAN_RFC822_NAME,
+ SAN_DNS_NAME,
+ SAN_X400_ADDRESS,
+ SAN_DIRECTORY_NAME,
+ SAN_EDIPART_NAME,
+ SAN_URI,
+ SAN_IP_ADDRESS,
+ SAN_REGISTERED_ID,
+ SAN_PKINIT,
+ SAN_NT,
+ SAN_PRINCIPAL,
+ SAN_STRING_OTHER_NAME,
+
+ SAN_END,
+ SAN_INVALID
+};
+
+/* KRB5 matching rule */
+enum relation_type {
+ relation_none = 0,
+ relation_and,
+ relation_or
+};
+
+struct component_list {
+ char *val;
+ regex_t regexp;
+ uint32_t ku;
+ const char **eku_oid_list;
+ enum san_opt san_opt;
+ char *str_other_name_oid;
+ uint8_t *bin_val;
+ size_t bin_val_len;
+ struct component_list *prev;
+ struct component_list *next;
+};
+
+struct krb5_match_rule {
+ enum relation_type r;
+ struct component_list *issuer;
+ struct component_list *subject;
+ struct component_list *ku;
+ struct component_list *eku;
+ struct component_list *san;
+};
+
+enum comp_type {
+ comp_none = 0,
+ comp_string,
+ comp_template
+};
+
+struct parsed_template {
+ const char *name;
+ const char *attr_name;
+ const char *conversion;
+};
+
+struct ldap_mapping_rule_comp {
+ enum comp_type type;
+ char *val;
+ struct parsed_template *parsed_template;
+ struct ldap_mapping_rule_comp *prev;
+ struct ldap_mapping_rule_comp *next;
+};
+
+struct ldap_mapping_rule {
+ struct ldap_mapping_rule_comp *list;
+};
+
+struct match_map_rule {
+ uint32_t priority;
+ char *match_rule;
+ struct krb5_match_rule *parsed_match_rule;
+ char *map_rule;
+ struct ldap_mapping_rule *parsed_mapping_rule;
+ char **domains;
+ struct match_map_rule *prev;
+ struct match_map_rule *next;
+};
+
+struct priority_list {
+ uint32_t priority;
+ struct match_map_rule *rule_list;
+ struct priority_list *prev;
+ struct priority_list *next;
+};
+
+enum mapping_rule_version {
+ mapv_ldap = 0,
+ mapv_ldapu1
+};
+
+struct sss_certmap_ctx {
+ struct priority_list *prio_list;
+ sss_certmap_ext_debug *debug;
+ void *debug_priv;
+ struct ldap_mapping_rule *default_mapping_rule;
+ enum mapping_rule_version mapv;
+ const char **digest_list;
+};
+
+struct san_list {
+ enum san_opt san_opt;
+ char *val;
+ uint8_t *bin_val;
+ size_t bin_val_len;
+ char *other_name_oid;
+ char *short_name;
+ const char **rdn_list;
+ struct san_list *prev;
+ struct san_list *next;
+};
+
+/* key usage flags, see RFC 3280 section 4.2.1.3 */
+#define SSS_KU_DIGITAL_SIGNATURE 0x0080
+#define SSS_KU_NON_REPUDIATION 0x0040
+#define SSS_KU_KEY_ENCIPHERMENT 0x0020
+#define SSS_KU_DATA_ENCIPHERMENT 0x0010
+#define SSS_KU_KEY_AGREEMENT 0x0008
+#define SSS_KU_KEY_CERT_SIGN 0x0004
+#define SSS_KU_CRL_SIGN 0x0002
+#define SSS_KU_ENCIPHER_ONLY 0x0001
+#define SSS_KU_DECIPHER_ONLY 0x8000
+
+struct sss_key_usage {
+ const char *name;
+ uint32_t flag;
+};
+
+extern const struct sss_key_usage sss_key_usage[];
+
+struct sss_ext_key_usage {
+ const char *name;
+ const char *oid;
+};
+
+extern const struct sss_ext_key_usage sss_ext_key_usage[];
+
+struct sss_san_name {
+ const char *name;
+ enum san_opt san_opt;
+ bool is_string;
+};
+
+extern const struct sss_san_name sss_san_names[];
+
+struct sss_cert_content {
+ char *issuer_str;
+ const char **issuer_rdn_list;
+ char *subject_str;
+ const char **subject_rdn_list;
+ uint32_t key_usage;
+ const char **extended_key_usage_oids;
+ struct san_list *san_list;
+
+ uint8_t *cert_der;
+ size_t cert_der_size;
+
+ uint8_t *serial_number;
+ size_t serial_number_size;
+ const char *serial_number_dec_str;
+
+ uint8_t *subject_key_id;
+ size_t subject_key_id_size;
+
+ const char *sid_ext;
+};
+
+/**
+ * @brief Extract various attributes from a binary X.509 certificate
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[in] der_blob Binary DER encoded X.509 certificate
+ * @param[in] der_size Length of the binary certificate
+ * @param[out] content Struct with parsed certificate data
+ *
+ * @return
+ * - 0: success
+ * - EINVAL: invalid input
+ * - ENOMEM: memory allocation error
+ */
+int sss_cert_get_content(TALLOC_CTX *mem_ctx,
+ const uint8_t *der_blob, size_t der_size,
+ struct sss_cert_content **content);
+
+/**
+ * @brief Translate an RDN with NSS attribute names into AD names
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[in] rdn RDN input string
+ *
+ * @return
+ * - NULL in case of an error
+ * - RDN string with AD attribute name and the same value as the input
+ */
+char *check_ad_attr_name(TALLOC_CTX *mem_ctx, const char *rdn);
+
+/**
+ * @brief Translate OpenSSL attribute name to NSS name
+ *
+ * @param[in] attr OpenSSL attribute name
+ *
+ * @return
+ * - NSS attribute name, most of the time it will be the same but there are
+ * some differences like e.g. 'GN' vs 'givenName'
+ */
+char *openssl_2_nss_attr_name(const char *attr);
+
+/**
+ * @brief Parse matching rule
+ *
+ * @param[in] ctx Certmap context
+ * @param[in] rule_start Matching rule string
+ * @param[out] mapping_rule Parsed rule struct
+ *
+ * @return
+ * - 0: success
+ * - ENOMEM: memory allocation failure
+ * - EINVAL: invalid input
+ */
+int parse_krb5_match_rule(struct sss_certmap_ctx *ctx,
+ const char *rule_start,
+ struct krb5_match_rule **match_rule);
+
+/**
+ * @brief Check hex conversion string
+ *
+ * @param[in] inp Conversion string
+ * @param[in] dec_allowed Flag to indicate if 'dec' for decimal output is
+ * allowed in the conversion string
+ * @param[out] _dec Flag to indicate decimal output
+ * @param[out] _upper Upper flag found in the conversion string
+ * @param[out] _colon Colon flag found in the conversion string
+ * @param[out] _reverse Reverse flag found in the conversion string
+ *
+ * @return
+ * - 0: success
+ * - EINVAL: invalid input
+ */
+int check_hex_conversion(const char *inp, bool dec_allowed, bool *_dec,
+ bool *_upper, bool *_colon, bool *_reverse);
+
+/**
+ * @brief Check digest conversion string
+ *
+ * @param[in] inp Conversion string
+ * @param[in] digest_list List of know digest/hash functions
+ * @param[out] _dgst Name of digest found in the conversion string
+ * @param[out] _upper Upper flag found in the conversion string
+ * @param[out] _colon Colon flag found in the conversion string
+ * @param[out] _reverse Reverse flag found in the conversion string
+ *
+ * @return
+ * - 0: success
+ * - EINVAL: invalid input
+ */
+int check_digest_conversion(const char *inp, const char **digest_list,
+ const char **_dgst, bool *_upper, bool *_colon,
+ bool *_reverse);
+
+/**
+ * @brief Parse mapping rule
+ *
+ * @param[in] ctx Certmap context
+ * @param[in] rule_start Mapping rule string
+ * @param[out] mapping_rule Parsed rule struct
+ *
+ * @return
+ * - 0: success
+ * - ENOMEM: memory allocation failure
+ * - EINVAL: invalid input
+ */
+int parse_ldap_mapping_rule(struct sss_certmap_ctx *ctx,
+ const char *rule_start,
+ struct ldap_mapping_rule **mapping_rule);
+
+/**
+ * @brief Split attribute selector option
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[in] inp Attribute selector string in the format:
+ * - attr_name
+ * - [number]
+ * - attr_name[number]
+ * The number 0 is not allowed in the input
+ * @param[out] _attr_name Attribute name from the input if present, NULL if
+ * there is no name in the input
+ * @param[out] _number Number from the input if present, 0 if there is no
+ * number in the input
+ *
+ * @return
+ * - 0: success
+ * - ENOMEM: memory allocation failure
+ * - EINVAL: invalid input
+ */
+int check_attr_name_and_or_number(TALLOC_CTX *mem_ctx, const char *inp,
+ char **_attr_name, int32_t *_number);
+
+/**
+ * @brief Get the short name from a fully-qulified name
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[in] full_name Fully-qulified name in the format
+ * "short-name""delimiter""suffix"
+ * @param[in] delim Delimiter character
+ * @param[out] short_name Resulting short name
+ *
+ * @return
+ * - 0: success
+ * - ENOMEM: memory allocation failure
+ * - EINVAL: invalid input
+ */
+int get_short_name(TALLOC_CTX *mem_ctx, const char *full_name,
+ char delim, char **short_name);
+
+/**
+ * @brief Add generic data to a new san_list item
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[in] is_bin Binary data not a string
+ * @param[in] san_opt Type of Subject alternative name (SAN)
+ * @param[in] data Data
+ * @param[in] len Length of data
+ * @param[out] san_list New san_list item
+ *
+ * @return
+ * - 0: success
+ * - ENOMEM: memory allocation failure
+ * - EINVAL: invalid input
+ */
+int add_to_san_list(TALLOC_CTX *mem_ctx, bool is_bin,
+ enum san_opt san_opt, const uint8_t *data, size_t len,
+ struct san_list **item);
+
+/**
+ * @brief Add a principal and the related short name to a new san_list item
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[in] san_opt Type of Subject alternative name (SAN)
+ * @param[in] princ String representation of the principal
+ * @param[out] item New san_list item
+ *
+ * @return
+ * - 0: success
+ * - ENOMEM: memory allocation failure
+ */
+int add_principal_to_san_list(TALLOC_CTX *mem_ctx, enum san_opt san_opt,
+ const char *princ, struct san_list **item);
+
+/**
+ * @brief Get DN specified by 'conversion' from 'rdn_list'
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[in] conversion Specifies how the DN should be formatted:
+ * - (ad|ad_x500)|ad_ldap|nss_x500|(nss|nss_ldap)
+ * where 'x500' means most specific RDN comes last and
+ * 'ldap' most specific RDN comes first. 'ad' and
+ * 'nss' specify if the attributes names should be
+ * translated in the way Active Directory or the NSS
+ * library is using them.
+ * @param[in] rdn_list String array with the the individual RDNs of a DN
+ * starting with the most specific component
+ * @param[out] result The resulting DN
+ *
+ * @return
+ * - 0: success
+ * - EINVAL: unsupported 'conversion'
+ * - ENOMEM: memory allocation failure
+ */
+int rdn_list_2_dn_str(TALLOC_CTX *mem_ctx, const char *conversion,
+ const char **rdn_list, char **result);
+
+/**
+ * @brief Get attribute value specified by 'conversion' from 'rdn_list'
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[in] conversion Selection by position and/or name, format:
+ * - name
+ * - [pos]
+ * - name[pos]
+ * where 'name' is an attribute name and pos to
+ * position of the attribute in the DN starting with
+ * 1, negative numbers will start at the least
+ * specific component of the DN
+ * @param[in] rdn_list String array with the the individual RDNs of a DN
+ * starting with the most specific component
+ * @param[out] result The selected attribute value
+ *
+ * @return
+ * - 0: success
+ * - ENOMEM: memory allocation failure
+ * - EINVAL: unsupported 'conversion' or 'rdn_list'
+ * - EIO: no value could be returned
+ */
+int rdn_list_2_component(TALLOC_CTX *mem_ctx, const char *conversion,
+ const char **rdn_list, char **result);
+
+/**
+ * @brief Get list of supported has/digest types
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[out] digest_list Resulting list of hash type names
+ *
+ * @return
+ * - 0: success
+ * - ENOMEM: memory allocation failure
+ */
+int get_digest_list(TALLOC_CTX *mem_ctx, const char ***digest_list);
+
+/**
+ * @brief Calculate the digest/hash of some binary data and return it as hex
+ * string
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[in] blob Binary data to calculate the hash of
+ * @param[in] blob_size Length of binary data
+ * @param[in] digest Type of hash/digest
+ * @param[in] upper_case Use upper-case letters in hex string
+ * @param[in] colon_sep Seperate each byte in the hex string with a ':'
+ * @param[in] reverse Start at the end of the binary blob
+ * @param[out] out Resulting hex string
+ *
+ * @return
+ * - 0: success
+ * - EINVAL: invalid hash type
+ * - EIO: error while calculating the hash
+ * - ENOMEM: memory allocation failure
+ */
+int get_hash(TALLOC_CTX *mem_ctx, const uint8_t *blob, size_t blob_size,
+ const char *digest, bool upper, bool colon, bool reverse,
+ char **out);
+
+/**
+ * @brief Convert a binary blob into a hex string
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[in] upper_case Use upper-case letters in hex string
+ * @param[in] colon_sep Seperate each byte in the hex string with a ':'
+ * @param[in] reverse Start at the end of the binary blob
+ * @param[in] buf Start of the binary blob
+ * @param[in] len Length of the binary blob
+ * @param[out] out Resulting hex string
+ *
+ * @return
+ * - 0: success
+ * - EINVAL: invalid buffer or length
+ * - ENOMEM: memory allocation failure
+ */
+int bin_to_hex(TALLOC_CTX *mem_ctx, bool upper_case, bool colon_sep,
+ bool reverse, uint8_t *buf, size_t len, char **out);
+#endif /* __SSS_CERTMAP_INT_H__ */
diff --git a/src/lib/certmap/sss_certmap_krb5_match.c b/src/lib/certmap/sss_certmap_krb5_match.c
new file mode 100644
index 0000000..ab566ac
--- /dev/null
+++ b/src/lib/certmap/sss_certmap_krb5_match.c
@@ -0,0 +1,547 @@
+/*
+ SSSD
+
+ Library for rule based certificate to user mapping - KRB5 matching rules
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <ctype.h>
+
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+#include "lib/certmap/sss_certmap.h"
+#include "lib/certmap/sss_certmap_int.h"
+
+const struct sss_key_usage sss_key_usage[] = {
+ {"digitalSignature" , SSS_KU_DIGITAL_SIGNATURE},
+ {"nonRepudiation" , SSS_KU_NON_REPUDIATION},
+ {"keyEncipherment" , SSS_KU_KEY_ENCIPHERMENT},
+ {"dataEncipherment" , SSS_KU_DATA_ENCIPHERMENT},
+ {"keyAgreement" , SSS_KU_KEY_AGREEMENT},
+ {"keyCertSign" , SSS_KU_KEY_CERT_SIGN},
+ {"cRLSign" , SSS_KU_CRL_SIGN},
+ {"encipherOnly" , SSS_KU_ENCIPHER_ONLY},
+ {"decipherOnly" , SSS_KU_DECIPHER_ONLY},
+ {NULL ,0}
+};
+
+const struct sss_ext_key_usage sss_ext_key_usage[] = {
+ /* RFC 3280 section 4.2.1.13 */
+ {"serverAuth", "1.3.6.1.5.5.7.3.1"},
+ {"clientAuth", "1.3.6.1.5.5.7.3.2"},
+ {"codeSigning", "1.3.6.1.5.5.7.3.3"},
+ {"emailProtection", "1.3.6.1.5.5.7.3.4"},
+ {"timeStamping", "1.3.6.1.5.5.7.3.8"},
+ {"OCSPSigning", "1.3.6.1.5.5.7.3.9"},
+
+ /* RFC 4556 section 3.2.2 */
+ {"KPClientAuth", "1.3.6.1.5.2.3.4"},
+ {"pkinit", "1.3.6.1.5.2.3.4"},
+
+ /* https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography*/
+ {"msScLogin", "1.3.6.1.4.1.311.20.2.2"},
+
+ {NULL ,0}
+};
+
+const struct sss_san_name sss_san_names[] = {
+ /* https://www.ietf.org/rfc/rfc3280.txt section 4.2.1.7 */
+ {"otherName", SAN_OTHER_NAME, false},
+ {"rfc822Name", SAN_RFC822_NAME, true},
+ {"dNSName", SAN_DNS_NAME, true},
+ {"x400Address", SAN_X400_ADDRESS, false},
+ {"directoryName", SAN_DIRECTORY_NAME, true},
+ {"ediPartyName", SAN_EDIPART_NAME, false},
+ {"uniformResourceIdentifier", SAN_URI, true},
+ {"iPAddress", SAN_IP_ADDRESS, true},
+ {"registeredID", SAN_REGISTERED_ID, true},
+ /* https://www.ietf.org/rfc/rfc4556.txt section 3.2.2 */
+ {"pkinitSAN", SAN_PKINIT, true},
+ /* https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography */
+ {"ntPrincipalName", SAN_NT, true},
+ /* both previous principal types */
+ {"Principal", SAN_PRINCIPAL, true},
+ {"stringOtherName", SAN_STRING_OTHER_NAME, true},
+ {NULL, SAN_END, false}
+};
+
+static bool is_dotted_decimal(const char *s, size_t len)
+{
+ size_t c = 0;
+ bool has_dot = false;
+
+ if (s == NULL || !isdigit(s[c++])) {
+ return false;
+ }
+
+ while ((len == 0 && s[c] != '\0') || (len != 0 && c < len)) {
+ if (s[c] != '.' && !isdigit(s[c])) {
+ return false;
+ }
+ if (!has_dot && s[c] == '.') {
+ has_dot = true;
+ }
+ c++;
+ }
+
+ return (has_dot && isdigit(s[c - 1]));
+}
+
+static int component_list_destructor(void *data)
+{
+ struct component_list *comp = talloc_get_type(data, struct component_list);
+
+ if (comp != NULL) {
+ regfree(&(comp->regexp));
+ }
+
+ return 0;
+}
+
+/*
+ * The syntax of the MIT Kerberos style matching rules is:
+ * [KRB5:][relation-operator]component-rule ...
+ *
+ * where:
+ *
+ * relation-operator
+ * can be either &&, meaning all component rules must match, or ||,
+ * meaning only one component rule must match. The default is &&.
+ *
+ * component-rule
+ * can be one of the following. Note that there is no punctuation or whitespace between component rules.
+ * <SUBJECT>regular-expression
+ * <ISSUER>regular-expression
+ * <SAN>regular-expression
+ * <EKU>extended-key-usage
+ * <KU>key-usage
+ *
+ * see man sss-certmap for more details
+ *
+ */
+
+static int get_comp_value(TALLOC_CTX *mem_ctx,
+ struct sss_certmap_ctx *ctx,
+ const char **cur,
+ struct component_list **_comp)
+
+{
+ struct component_list *comp = NULL;
+ const char *end;
+ int ret;
+
+ comp = talloc_zero(mem_ctx, struct component_list);
+ if (comp == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_set_destructor((TALLOC_CTX *) comp, component_list_destructor);
+
+ end = strchr(*cur, '<');
+
+ if (end == NULL) {
+ comp->val = talloc_strdup(comp, *cur);
+ } else {
+ comp->val = talloc_strndup(comp, *cur, end - *cur);
+ }
+ if (comp->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ if (*(comp->val) == '\0') {
+ CM_DEBUG(ctx, "Missing component value.");
+ ret = EINVAL;
+ goto done;
+ }
+
+ *cur += strlen(comp->val);
+ *_comp = comp;
+ ret = 0;
+
+done:
+ if (ret != 0) {
+ talloc_free(comp);
+ }
+
+ return ret;
+}
+
+static int parse_krb5_get_eku_value(TALLOC_CTX *mem_ctx,
+ struct sss_certmap_ctx *ctx,
+ const char **cur,
+ struct component_list **_comp)
+{
+ struct component_list *comp = NULL;
+ int ret;
+ char **eku_list;
+ size_t c;
+ size_t k;
+ const char *o;
+ size_t e = 0;
+ int eku_list_size;
+
+ ret = get_comp_value(mem_ctx, ctx, cur, &comp);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to parse regexp.");
+ goto done;
+ }
+
+ ret = split_on_separator(mem_ctx, comp->val, ',', true, true,
+ &eku_list, &eku_list_size);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to split list.");
+ goto done;
+ }
+
+ comp->eku_oid_list = talloc_zero_array(comp, const char *,
+ eku_list_size + 1);
+ if (comp->eku_oid_list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; eku_list[c] != NULL; c++) {
+ for (k = 0; sss_ext_key_usage[k].name != NULL; k++) {
+ if (strcasecmp(eku_list[c], sss_ext_key_usage[k].name) == 0) {
+ comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list,
+ sss_ext_key_usage[k].oid);
+ if (comp->eku_oid_list[e] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ e++;
+ break;
+ }
+ }
+
+ if (sss_ext_key_usage[k].name == NULL) {
+ /* check for an dotted-decimal OID */
+ if (*(eku_list[c]) != '.') {
+ o = eku_list[c];
+ if (is_dotted_decimal(o, 0)) {
+ /* looks like a OID, only '.' and digits */
+ comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list,
+ eku_list[c]);
+ if (comp->eku_oid_list[e] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ e++;
+ continue;
+ }
+ }
+ CM_DEBUG(ctx, "No matching extended key usage found.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ if (e == 0) {
+ talloc_free(comp->eku_oid_list);
+ comp->eku_oid_list = NULL;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *_comp = comp;
+ } else {
+ talloc_free(comp);
+ }
+
+ return ret;
+}
+
+static int parse_krb5_get_ku_value(TALLOC_CTX *mem_ctx,
+ struct sss_certmap_ctx *ctx,
+ const char **cur,
+ struct component_list **_comp)
+{
+ struct component_list *comp = NULL;
+ int ret;
+ char **ku_list;
+ size_t c;
+ size_t k;
+
+ ret = get_comp_value(mem_ctx, ctx, cur, &comp);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to get value.");
+ goto done;
+ }
+
+ ret = split_on_separator(mem_ctx, comp->val, ',', true, true,
+ &ku_list, NULL);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to split list.");
+ goto done;
+ }
+
+ for (c = 0; ku_list[c] != NULL; c++) {
+ for (k = 0; sss_key_usage[k].name != NULL; k++) {
+ if (strcasecmp(ku_list[c], sss_key_usage[k].name) == 0) {
+ comp->ku |= sss_key_usage[k].flag;
+ break;
+ }
+ }
+
+ if (sss_key_usage[k].name == NULL) {
+ /* FIXME: add check for numerical ku */
+ CM_DEBUG(ctx, "No matching key usage found.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *_comp = comp;
+ } else {
+ talloc_free(comp);
+ }
+
+ return ret;
+}
+
+static int parse_krb5_get_component_value(TALLOC_CTX *mem_ctx,
+ struct sss_certmap_ctx *ctx,
+ const char **cur,
+ struct component_list **_comp)
+{
+ struct component_list *comp = NULL;
+ int ret;
+
+ ret = get_comp_value(mem_ctx, ctx, cur, &comp);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to parse regexp.");
+ goto done;
+ }
+
+ ret = regcomp(&(comp->regexp), comp->val, REG_EXTENDED);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to parse regexp.");
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *_comp = comp;
+ } else {
+ talloc_free(comp);
+ }
+
+ return ret;
+}
+
+static int parse_krb5_get_san_option(TALLOC_CTX *mem_ctx,
+ struct sss_certmap_ctx *ctx,
+ const char **cur,
+ enum san_opt *option,
+ char **str_other_name_oid)
+{
+ char *end;
+ size_t c;
+ size_t len;
+
+ end = strchr(*cur, '>');
+ if (end == NULL) {
+ CM_DEBUG(ctx, "Failed to parse SAN option.");
+ return EINVAL;
+ }
+
+ len = end - *cur;
+
+ if (len == 0) {
+ c= SAN_PRINCIPAL;
+ } else {
+ for (c = 0; sss_san_names[c].name != NULL; c++) {
+ if (strncasecmp(*cur, sss_san_names[c].name, len) == 0) {
+ break;
+ }
+ }
+ if (sss_san_names[c].name == NULL) {
+ if (is_dotted_decimal(*cur, len)) {
+ c = SAN_STRING_OTHER_NAME;
+ *str_other_name_oid = talloc_strndup(mem_ctx, *cur, len);
+ if (*str_other_name_oid == NULL) {
+ CM_DEBUG(ctx, "talloc_strndup failed.");
+ return ENOMEM;
+ }
+ } else {
+ CM_DEBUG(ctx, "Unknown SAN option.");
+ return EINVAL;
+ }
+ }
+ }
+
+ *option = sss_san_names[c].san_opt;
+ *cur = end + 1;
+
+ return 0;
+}
+
+static int parse_krb5_get_san_value(TALLOC_CTX *mem_ctx,
+ struct sss_certmap_ctx *ctx,
+ const char **cur,
+ struct component_list **_comp)
+{
+ struct component_list *comp = NULL;
+ enum san_opt san_opt = SAN_PRINCIPAL;
+ int ret;
+ char *str_other_name_oid = NULL;
+
+ if (*(*cur - 1) == ':') {
+ ret = parse_krb5_get_san_option(mem_ctx, ctx, cur, &san_opt,
+ &str_other_name_oid);
+ if (ret != 0) {
+ goto done;
+ }
+ }
+
+ if (sss_san_names[san_opt].is_string) {
+ ret = parse_krb5_get_component_value(mem_ctx, ctx, cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+ } else {
+ ret = get_comp_value(mem_ctx, ctx, cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+
+ if (comp->val != NULL) {
+ comp->bin_val = sss_base64_decode(comp, comp->val,
+ &comp->bin_val_len);
+ if (comp->bin_val == NULL || comp->bin_val_len == 0) {
+ CM_DEBUG(ctx, "Base64 decode failed.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ }
+ comp->san_opt = san_opt;
+
+done:
+ if (ret == 0) {
+ comp->str_other_name_oid = talloc_steal(comp, str_other_name_oid);
+ *_comp = comp;
+ } else {
+ talloc_free(comp);
+ talloc_free(str_other_name_oid);
+ }
+
+ return ret;
+}
+
+int parse_krb5_match_rule(struct sss_certmap_ctx *ctx,
+ const char *rule_start,
+ struct krb5_match_rule **match_rule)
+{
+ const char *cur;
+ struct krb5_match_rule *rule;
+ struct component_list *comp;
+ int ret;
+
+ rule = talloc_zero(ctx, struct krb5_match_rule);
+ if (rule == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cur = rule_start;
+ /* check relation */
+ if (strncmp(cur, "&&", 2) == 0) {
+ rule->r = relation_and;
+ cur += 2;
+ } else if (strncmp(cur, "||", 2) == 0) {
+ rule->r = relation_or;
+ cur += 2;
+ } else {
+ rule->r = relation_and;
+ }
+
+ while (*cur != '\0') {
+ /* new component must start with '<' */
+ if (*cur != '<') {
+ CM_DEBUG(ctx, "Invalid KRB5 matching rule.");
+ ret = EINVAL;
+ goto done;
+ }
+ cur++;
+
+ if (strncmp(cur, "ISSUER>", 7) == 0) {
+ cur += 7;
+ ret = parse_krb5_get_component_value(rule, ctx, &cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(rule->issuer, comp);
+ } else if (strncmp(cur, "SUBJECT>", 8) == 0) {
+ cur += 8;
+ ret = parse_krb5_get_component_value(rule, ctx, &cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(rule->subject, comp);
+ } else if (strncmp(cur, "KU>", 3) == 0) {
+ cur += 3;
+ ret = parse_krb5_get_ku_value(rule, ctx, &cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(rule->ku, comp);
+ } else if (strncmp(cur, "EKU>", 4) == 0) {
+ cur += 4;
+ ret = parse_krb5_get_eku_value(rule, ctx, &cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(rule->eku, comp);
+ } else if (strncmp(cur, "SAN>", 4) == 0
+ || strncmp(cur, "SAN:", 4) == 0) {
+ cur += 4;
+ ret = parse_krb5_get_san_value(rule, ctx, &cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(rule->san, comp);
+ } else {
+ CM_DEBUG(ctx, "Invalid KRB5 matching rule.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *match_rule = rule;
+ } else {
+ talloc_free(rule);
+ }
+
+ return ret;
+}
diff --git a/src/lib/certmap/sss_certmap_ldap_mapping.c b/src/lib/certmap/sss_certmap_ldap_mapping.c
new file mode 100644
index 0000000..354b031
--- /dev/null
+++ b/src/lib/certmap/sss_certmap_ldap_mapping.c
@@ -0,0 +1,654 @@
+/*
+ SSSD
+
+ Library for rule based certificate to user mapping - LDAP mapping rules
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <talloc.h>
+
+#include "util/dlinklist.h"
+#include "util/strtonum.h"
+#include "lib/certmap/sss_certmap.h"
+#include "lib/certmap/sss_certmap_int.h"
+
+#define HEX_CONVERSION "hex_conversion"
+#define HEX_DEC_CONVERSION "hex_dec_conversion"
+#define READ_DIGESTS_FROM_CTX "read_digests_from_ctx"
+#define ATTR_NAME_AND_OR_NUMBER "attr_name_an_or_number"
+
+struct template_table {
+ const char *name;
+ const char **attr_name;
+ const char **conversion;
+};
+
+const char *empty[] = {NULL};
+const char *name_attr[] = {"short_name", NULL};
+const char *attr_name_and_or_number[] = { ATTR_NAME_AND_OR_NUMBER, NULL};
+const char *x500_conv[] = {"ad_x500", "ad", "ad_ldap",
+ "nss_x500", "nss", "nss_ldap", NULL};
+const char *bin_conv[] = {"bin", "base64", NULL};
+const char *hex_conv[] = {HEX_CONVERSION, NULL};
+const char *dec_hex_conv[] = {HEX_DEC_CONVERSION, NULL};
+const char *digest_conv[] = { READ_DIGESTS_FROM_CTX, NULL};
+const char *sid_rid_attr[] = {"rid", NULL};
+
+struct template_table template_table_base[] = {
+ {"issuer_dn", empty, x500_conv},
+ {"subject_dn", empty, x500_conv},
+ {"cert", empty, bin_conv},
+ {"subject_rfc822_name", name_attr, empty},
+ {"subject_dns_name", name_attr, empty},
+ {"subject_x400_address", empty, empty},
+ {"subject_directory_name", empty, empty},
+ {"subject_ediparty_name", empty, empty},
+ {"subject_uri", empty, empty},
+ {"subject_ip_address", empty, empty},
+ {"subject_registered_id", empty, empty},
+ {"subject_pkinit_principal", name_attr, empty},
+ {"subject_nt_principal", name_attr, empty},
+ {"subject_principal", name_attr, empty},
+ {NULL, NULL, NULL}};
+
+struct template_table template_table_u1[] = {
+ {"serial_number", empty, dec_hex_conv},
+ {"subject_key_id", empty, hex_conv},
+ {"cert", empty, digest_conv},
+ {"subject_dn_component", attr_name_and_or_number, empty},
+ {"issuer_dn_component", attr_name_and_or_number, empty},
+ {"sid", sid_rid_attr, empty},
+ {NULL, NULL, NULL}};
+
+int check_attr_name_and_or_number(TALLOC_CTX *mem_ctx, const char *inp,
+ char **_attr_name, int32_t *_number)
+{
+ int ret;
+ char *sep;
+ char *end;
+ char *endptr = NULL;
+ char *attr_name = NULL;
+ int32_t number = 0;
+
+ if (inp == NULL) {
+ attr_name = NULL;
+ number = 0;
+ ret = 0;
+ goto done;
+ }
+
+ sep = strchr(inp, '[');
+ if (sep == NULL) {
+ attr_name = talloc_strdup(mem_ctx, inp);
+ if (attr_name == NULL) {
+ return ENOMEM;
+ }
+ number = 0;
+ } else {
+ end = strchr(sep, ']');
+ if (end == NULL || end == (sep + 1) || *(end + 1) != '\0') {
+ return EINVAL;
+ }
+
+ number = strtoint32(sep+1, &endptr, 10);
+ if (errno !=0 || number == 0 || *endptr != ']') {
+ return EINVAL;
+ }
+
+ if (sep == inp) {
+ attr_name = NULL;
+ } else {
+ attr_name = talloc_strndup(mem_ctx, inp, sep - inp);
+ if (attr_name == NULL) {
+ return ENOMEM;
+ }
+ }
+
+ }
+
+ ret = 0;
+
+done:
+
+ if (ret == 0) {
+ if (_attr_name != NULL) {
+ *_attr_name = attr_name;
+ }
+ if (_number != NULL) {
+ *_number = number;
+ }
+ }
+ return ret;
+}
+
+int check_hex_conversion(const char *inp, bool dec_allowed, bool *_dec,
+ bool *_upper, bool *_colon, bool *_reverse)
+{
+ int ret;
+ char *sep;
+ bool dec = false;
+ bool upper = false;
+ bool colon = false;
+ bool reverse = false;
+ char *c;
+
+ if (inp == NULL) {
+ ret = 0;
+ goto done;
+ }
+
+ sep = strchr(inp, '_');
+ /* We expect either 'hex' or 'dec' as full string or 'hex' before '_'*/
+ if ((sep == NULL && strlen(inp) != 3)
+ || (sep != NULL && (sep - inp) != 3)) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (strncasecmp(inp, "hex", 3) != 0) {
+ if (dec_allowed && sep == NULL && strncasecmp(inp, "dec", 3) == 0) {
+ dec = true;
+ } else {
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ if (sep != NULL) {
+ for (c = sep + 1; *c != '\0'; c++) {
+ switch(*c) {
+ case 'u':
+ case 'U':
+ upper = true;
+ break;
+ case 'c':
+ case 'C':
+ colon = true;
+ break;
+ case 'r':
+ case 'R':
+ reverse = true;
+ break;
+ default:
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ }
+
+ ret = 0;
+done:
+ if (ret == 0) {
+ if (_dec != NULL) {
+ *_dec = dec;
+ }
+ if (_upper != NULL) {
+ *_upper = upper;
+ }
+ if (_colon != NULL) {
+ *_colon = colon;
+ }
+ if (_reverse != NULL) {
+ *_reverse = reverse;
+ }
+ }
+
+ return ret;
+}
+
+int check_digest_conversion(const char *inp, const char **digest_list,
+ const char **_dgst, bool *_upper, bool *_colon,
+ bool *_reverse)
+{
+ int ret;
+ char *sep;
+ size_t d;
+ int cmp;
+ bool upper = false;
+ bool colon = false;
+ bool reverse = false;
+ char *c;
+ size_t len = 0;
+
+ sep = strchr(inp, '_');
+ if (sep != NULL) {
+ len = sep - inp;
+ }
+
+ for (d = 0; digest_list[d] != NULL; d++) {
+ if (sep == NULL) {
+ cmp = strcasecmp(digest_list[d], inp);
+ } else {
+ if (strlen(digest_list[d]) != len) {
+ continue;
+ }
+ cmp = strncasecmp(digest_list[d], inp, len);
+ }
+
+ if (cmp == 0) {
+ break;
+ }
+ }
+
+ if (digest_list[d] == NULL) {
+ return EINVAL;
+ }
+
+ if (sep != NULL) {
+ for (c = sep + 1; *c != '\0'; c++) {
+ switch(*c) {
+ case 'u':
+ case 'U':
+ upper = true;
+ break;
+ case 'c':
+ case 'C':
+ colon = true;
+ break;
+ case 'r':
+ case 'R':
+ reverse = true;
+ break;
+ default:
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ }
+
+ ret = 0;
+done:
+ if (ret == 0) {
+ if (_dgst != NULL) {
+ *_dgst = digest_list[d];
+ }
+ if (_upper != NULL) {
+ *_upper = upper;
+ }
+ if (_colon != NULL) {
+ *_colon = colon;
+ }
+ if (_reverse != NULL) {
+ *_reverse = reverse;
+ }
+ }
+
+ return ret;
+}
+
+static int check_parsed_template(struct sss_certmap_ctx *ctx,
+ struct template_table *template_table,
+ struct parsed_template *parsed)
+{
+ size_t n;
+ size_t a;
+ size_t c;
+ bool attr_name_valid = false;
+ bool conversion_valid = false;
+
+ for (n = 0; template_table[n].name != NULL; n++) {
+ if (strcmp(template_table[n].name, parsed->name) != 0) {
+ continue;
+ }
+
+ if (parsed->attr_name != NULL) {
+ for (a = 0; template_table[n].attr_name[a] != NULL; a++) {
+ if (strcmp(template_table[n].attr_name[a],
+ parsed->attr_name) == 0) {
+ attr_name_valid = true;
+ break;
+ }
+ }
+
+ if (!attr_name_valid && template_table[n].attr_name[0] != NULL
+ && strcmp(template_table[n].attr_name[0],
+ ATTR_NAME_AND_OR_NUMBER) == 0) {
+ if (check_attr_name_and_or_number(ctx, parsed->attr_name, NULL,
+ NULL) == 0) {
+ attr_name_valid = true;
+ }
+ }
+ } else {
+ attr_name_valid = true;
+ }
+
+ if (parsed->conversion != NULL) {
+ for (c = 0; template_table[n].conversion[c] != NULL; c++) {
+ if (strcmp(template_table[n].conversion[c],
+ parsed->conversion) == 0) {
+ conversion_valid = true;
+ break;
+ }
+ }
+
+ if (!conversion_valid && template_table[n].conversion[0] != NULL
+ && strcmp(template_table[n].conversion[0],
+ HEX_DEC_CONVERSION) == 0) {
+ if (check_hex_conversion(parsed->conversion, true,
+ NULL, NULL, NULL, NULL) == 0) {
+ conversion_valid = true;
+ }
+ }
+ if (!conversion_valid && template_table[n].conversion[0] != NULL
+ && strcmp(template_table[n].conversion[0],
+ HEX_CONVERSION) == 0) {
+ if (check_hex_conversion(parsed->conversion, false,
+ NULL, NULL, NULL, NULL) == 0) {
+ conversion_valid = true;
+ }
+ }
+ if (!conversion_valid && template_table[n].conversion[0] != NULL
+ && strcmp(template_table[n].conversion[0],
+ READ_DIGESTS_FROM_CTX) == 0) {
+ if (check_digest_conversion(parsed->conversion,
+ ctx->digest_list,
+ NULL, NULL, NULL, NULL) == 0) {
+ conversion_valid = true;
+ }
+ }
+ } else {
+ conversion_valid = true;
+ }
+
+ if (attr_name_valid && conversion_valid) {
+ return 0;
+ }
+ }
+
+ return EINVAL;
+}
+
+static int parse_template(TALLOC_CTX *mem_ctx, struct sss_certmap_ctx *ctx,
+ const char *template,
+ struct parsed_template **parsed_template)
+{
+ int ret;
+ struct parsed_template *parsed = NULL;
+ const char *dot;
+ const char *excl;
+ const char *p;
+
+ parsed = talloc_zero(mem_ctx, struct parsed_template);
+ if (parsed == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* A '.' indicates that a specifier will follow which indicates which part
+ * of the value should be added. */
+ dot = strchr(template, '.');
+ if (dot != NULL) {
+ p = strchr(dot + 1, '.');
+ if (p != NULL) {
+ CM_DEBUG(ctx, "Only one '.' allowed in template.");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (dot == template) {
+ CM_DEBUG(ctx, "Missing name in template.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ /* A '!' indicates that a conversion specifier will follow which indicates
+ * how the output should be formatted. */
+ excl = strchr(template, '!');
+ if (excl != NULL) {
+ p = strchr(excl + 1, '!');
+ if (p != NULL) {
+ CM_DEBUG(ctx, "Only one '!' allowed in template.");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (excl == template) {
+ CM_DEBUG(ctx, "Missing name in template.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ if (excl != NULL && excl[1] != '\0') {
+ parsed->conversion = talloc_strdup(parsed, excl + 1);
+ if (parsed->conversion == NULL) {
+ CM_DEBUG(ctx, "Memory allocation failed.");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (dot != NULL && dot[1] != '\0' && dot[1] != '!') {
+ if (excl == NULL) {
+ parsed->attr_name = talloc_strdup(parsed, dot + 1);
+ } else {
+ parsed->attr_name = talloc_strndup(parsed, dot + 1,
+ (excl - dot - 1));
+ }
+ if (parsed->attr_name == NULL) {
+ CM_DEBUG(ctx, "Memory allocation failed.");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (dot != NULL) {
+ parsed->name = talloc_strndup(parsed, template, (dot - template));
+ } else if (excl != NULL) {
+ parsed->name = talloc_strndup(parsed, template, (excl - template));
+ } else {
+ parsed->name = talloc_strdup(parsed, template);
+ }
+ if (parsed->name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* If the template cannot be found in the base table,
+ * check LDAPU1 as well. */
+ ret = check_parsed_template(ctx, template_table_base, parsed);
+ if (ret != 0) {
+ if (ctx->mapv == mapv_ldapu1) {
+ ret = check_parsed_template(ctx, template_table_u1, parsed);
+ }
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Parse template [%s] invalid.", template);
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *parsed_template = parsed;
+ } else {
+ talloc_free(parsed);
+ }
+
+ return ret;
+}
+
+static int add_comp(struct sss_certmap_ctx *ctx, struct ldap_mapping_rule *rule,
+ const char *string, enum comp_type type)
+{
+ int ret;
+ struct ldap_mapping_rule_comp *comp;
+
+ comp = talloc_zero(rule, struct ldap_mapping_rule_comp);
+ if (comp == NULL) {
+ return ENOMEM;
+ }
+
+ comp->type = type;
+ comp->val = talloc_strdup(comp, string);
+ if (comp->val == NULL) {
+ talloc_free(comp);
+ return ENOMEM;
+ }
+
+ if (type == comp_template) {
+ ret = parse_template(comp, ctx, string, &comp->parsed_template);
+ if (ret != 0) {
+ talloc_free(comp);
+ return ret;
+ }
+ }
+
+ DLIST_ADD_END(rule->list, comp, struct ldap_mapping_rule_comp *);
+
+ return 0;
+}
+
+static int add_string(struct sss_certmap_ctx *ctx,
+ struct ldap_mapping_rule *rule, const char *string)
+{
+ return add_comp(ctx, rule, string, comp_string);
+}
+
+static int add_template(struct sss_certmap_ctx *ctx,
+ struct ldap_mapping_rule *rule, const char *string)
+{
+ return add_comp(ctx, rule, string, comp_template);
+}
+
+int parse_ldap_mapping_rule(struct sss_certmap_ctx *ctx,
+ const char *rule_start,
+ struct ldap_mapping_rule **mapping_rule)
+{
+ size_t c;
+ const char *cur;
+ char *tmp_string = NULL;
+ size_t tmp_string_size;
+ struct ldap_mapping_rule *rule = NULL;
+ int ret;
+ bool in_template = false;
+
+ rule = talloc_zero(ctx, struct ldap_mapping_rule);
+ if (rule == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tmp_string_size = strlen(rule_start) + 1;
+ tmp_string = talloc_zero_size(ctx, tmp_string_size);
+ if (tmp_string == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cur = rule_start;
+ c = 0;
+
+ while (*cur != '\0') {
+ if (c > tmp_string_size) {
+ CM_DEBUG(ctx, "Cannot parse mapping rule.");
+ ret = EIO;
+ goto done;
+ }
+ switch (*cur) {
+ case '{':
+ if (in_template) {
+ CM_DEBUG(ctx, "'{' not allowed in templates.");
+ ret = EINVAL;
+ goto done;
+ }
+ if (cur[1] == '{') {
+ /* Add only a single '{' to the output */
+ tmp_string[c] = '{';
+ c++;
+ cur += 2;
+ } else {
+ if (c != 0) {
+ ret = add_string(ctx, rule, tmp_string);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to add string.");
+ ret = EINVAL;
+ goto done;
+ }
+ memset(tmp_string, 0, tmp_string_size);
+ c = 0;
+ }
+ cur++;
+ in_template = true;
+ }
+ break;
+ case '}':
+ if (cur[1] == '}') {
+ if (in_template) {
+ CM_DEBUG(ctx, "'}}' not allowed in templates.");
+ ret = EINVAL;
+ goto done;
+ } else {
+ /* Add only a single '}' to the output */
+ tmp_string[c] = '}';
+ c++;
+ cur += 2;
+ }
+ } else {
+ ret = add_template(ctx, rule, tmp_string);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to add template.");
+ ret = EINVAL;
+ goto done;
+ }
+ memset(tmp_string, 0, tmp_string_size);
+ c = 0;
+ cur++;
+ in_template = false;
+ }
+ break;
+ default:
+ tmp_string[c] = *cur;
+ c++;
+ cur++;
+ }
+ }
+ if (in_template) {
+ CM_DEBUG(ctx, "Rule ended inside template.");
+ ret = EINVAL;
+ goto done;
+ }
+ if (c != 0) {
+ ret = add_string(ctx, rule, tmp_string);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to add string.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *mapping_rule = rule;
+ } else {
+ talloc_free(rule);
+ }
+
+ talloc_free(tmp_string);
+
+ return ret;
+}
diff --git a/src/lib/cifs_idmap_sss/cifs_idmap_sss.c b/src/lib/cifs_idmap_sss/cifs_idmap_sss.c
new file mode 100644
index 0000000..6434f4b
--- /dev/null
+++ b/src/lib/cifs_idmap_sss/cifs_idmap_sss.c
@@ -0,0 +1,337 @@
+/*
+ Authors:
+ Benjamin Franzke <benjaminfranzke@googlemail.com>
+
+ Copyright (C) 2013 Benjamin Franzke
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* TODO: Support of [all] samba's Unix SIDs:
+ * Users: S-1-22-1-%UID
+ * Groups: S-1-22-2-%GID
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <stdarg.h>
+
+#include <cifsidmap.h>
+
+#include "lib/idmap/sss_idmap.h"
+#include "sss_client/idmap/sss_nss_idmap.h"
+
+#ifdef DEBUG
+#include <syslog.h>
+#define debug(str, ...) \
+ syslog(0, "%s: " str "\n", \
+ __FUNCTION__, ##__VA_ARGS__)
+#else
+#define debug(...) do { } while(0)
+#endif
+
+struct sssd_ctx {
+ struct sss_idmap_ctx *idmap;
+ const char **errmsg;
+};
+
+#define ctx_set_error(ctx, error) \
+ do { \
+ *ctx->errmsg = error; \
+ debug("%s", error ? error : ""); \
+ } while (0);
+
+int cifs_idmap_init_plugin(void **handle, const char **errmsg)
+{
+ struct sssd_ctx *ctx;
+ enum idmap_error_code err;
+
+ if (handle == NULL || errmsg == NULL)
+ return EINVAL;
+
+ ctx = malloc(sizeof *ctx);
+ if (!ctx) {
+ *errmsg = "Failed to allocate context";
+ return -1;
+ }
+ ctx->errmsg = errmsg;
+ ctx_set_error(ctx, NULL);
+
+ err = sss_idmap_init(NULL, NULL, NULL, &ctx->idmap);
+ if (err != IDMAP_SUCCESS) {
+ ctx_set_error(ctx, idmap_error_string(err));
+ free(ctx);
+ return -1;
+ }
+
+ *handle = ctx;
+ return 0;
+}
+
+void cifs_idmap_exit_plugin(void *handle)
+{
+ struct sssd_ctx *ctx = handle;
+
+ debug("exit");
+
+ if (ctx == NULL)
+ return;
+
+ sss_idmap_free(ctx->idmap);
+
+ free(ctx);
+}
+
+
+/* Test with `getcifsacl file` on client. */
+int cifs_idmap_sid_to_str(void *handle, const struct cifs_sid *csid,
+ char **name)
+{
+ struct sssd_ctx *ctx = handle;
+ enum idmap_error_code iderr;
+ char *sid;
+ enum sss_id_type id_type;
+ int err;
+
+ iderr = sss_idmap_bin_sid_to_sid(ctx->idmap, (const uint8_t *) csid,
+ sizeof(*csid), &sid);
+ if (iderr != IDMAP_SUCCESS) {
+ ctx_set_error(ctx, idmap_error_string(iderr));
+ *name = NULL;
+ return -1;
+ }
+
+ debug("sid: %s", sid);
+
+ err = sss_nss_getnamebysid(sid, name, &id_type);
+ if (err != 0) {
+ ctx_set_error(ctx, strerror(err));
+ *name = NULL;
+ return -err;
+ }
+
+ /* FIXME: Map Samba Unix SIDs? (sid->id and use getpwuid)? */
+
+ debug("name: %s", *name);
+
+ return 0;
+}
+
+static int sid_to_cifs_sid(struct sssd_ctx *ctx, const char *sid,
+ struct cifs_sid *csid)
+{
+ uint8_t *bsid = NULL;
+ enum idmap_error_code err;
+ size_t length;
+
+ err = sss_idmap_sid_to_bin_sid(ctx->idmap,
+ sid, &bsid, &length);
+ if (err != IDMAP_SUCCESS) {
+ ctx_set_error(ctx, idmap_error_string(err));
+ return -1;
+ }
+ if (length > sizeof(struct cifs_sid)) {
+ ctx_set_error(ctx, "too large sid length");
+ free(bsid);
+ return -1;
+ }
+
+ memcpy(csid, bsid, length);
+ sss_idmap_free_bin_sid(ctx->idmap, bsid);
+
+ return 0;
+}
+
+/* Test with setcifsacl -a */
+int cifs_idmap_str_to_sid(void *handle, const char *name,
+ struct cifs_sid *csid)
+{
+ struct sssd_ctx *ctx = handle;
+ int err;
+ enum sss_id_type id_type;
+ char *sid = NULL;
+ int success = 0;
+
+ debug("%s", name);
+
+ err = sss_nss_getsidbyname(name, &sid, &id_type);
+ if (err != 0) {
+ /* Might be a raw string representation of SID,
+ * try converting that before returning an error. */
+ if (sid_to_cifs_sid(ctx, name, csid) == 0)
+ return 0;
+
+ ctx_set_error(ctx, strerror(err));
+ return -err;
+ }
+
+ if (sid_to_cifs_sid(ctx, sid, csid) != 0)
+ success = -1;
+
+ free(sid);
+
+ return success;
+}
+
+static int samba_unix_sid_to_id(const char *sid, struct cifs_uxid *cuxid)
+{
+ id_t id;
+ uint8_t type;
+
+ if (sscanf(sid, "S-1-22-%hhu-%u", &type, &id) != 2)
+ return -1;
+
+ switch (type) {
+ case 1:
+ cuxid->type = CIFS_UXID_TYPE_UID;
+ cuxid->id.uid = id;
+ break;
+ case 2:
+ cuxid->type = CIFS_UXID_TYPE_GID;
+ cuxid->id.gid = id;
+ break;
+ default:
+ cuxid->type = CIFS_UXID_TYPE_UNKNOWN;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int sss_sid_to_id(struct sssd_ctx *ctx, const char *sid,
+ struct cifs_uxid *cuxid)
+{
+ int err;
+ enum sss_id_type id_type;
+ uint32_t uid;
+
+ err = sss_nss_getidbysid(sid, &uid, &id_type);
+ if (err != 0) {
+ ctx_set_error(ctx, strerror(err));
+ return -1;
+ }
+ cuxid->id.uid = (uid_t)uid;
+
+ switch (id_type) {
+ case SSS_ID_TYPE_UID:
+ cuxid->type = CIFS_UXID_TYPE_UID;
+ break;
+ case SSS_ID_TYPE_GID:
+ cuxid->type = CIFS_UXID_TYPE_GID;
+ break;
+ case SSS_ID_TYPE_BOTH:
+ cuxid->type = CIFS_UXID_TYPE_BOTH;
+ break;
+ case SSS_ID_TYPE_NOT_SPECIFIED:
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * cifs_idmap_sids_to_ids - convert struct cifs_sids to struct cifs_uxids
+ * usecase: mount.cifs -o sec=krb5,multiuser,cifsacl,nounix
+ * test: ls -n on mounted share
+ */
+int cifs_idmap_sids_to_ids(void *handle, const struct cifs_sid *csid,
+ const size_t num, struct cifs_uxid *cuxid)
+{
+ struct sssd_ctx *ctx = handle;
+ enum idmap_error_code err;
+ int success = -1;
+ size_t i;
+ char *sid;
+
+ debug("num: %zd", num);
+
+ if (num > UINT_MAX) {
+ ctx_set_error(ctx, "num is too large.");
+ return EINVAL;
+ }
+
+ for (i = 0; i < num; ++i) {
+ err = sss_idmap_bin_sid_to_sid(ctx->idmap, (const uint8_t *) &csid[i],
+ sizeof(csid[i]), &sid);
+ if (err != IDMAP_SUCCESS) {
+ ctx_set_error(ctx, idmap_error_string(err));
+ continue;
+ }
+
+ cuxid[i].type = CIFS_UXID_TYPE_UNKNOWN;
+
+ if (sss_sid_to_id(ctx, sid, &cuxid[i]) == 0 ||
+ samba_unix_sid_to_id(sid, &cuxid[i]) == 0) {
+
+ debug("setting uid of %s to %d", sid, cuxid[i].id.uid);
+ success = 0;
+ }
+
+ free(sid);
+ }
+
+ return success;
+}
+
+
+int cifs_idmap_ids_to_sids(void *handle, const struct cifs_uxid *cuxid,
+ const size_t num, struct cifs_sid *csid)
+{
+ struct sssd_ctx *ctx = handle;
+ int err, success = -1;
+ char *sid;
+ enum sss_id_type id_type;
+ size_t i;
+
+ debug("num ids: %zd", num);
+
+ if (num > UINT_MAX) {
+ ctx_set_error(ctx, "num is too large.");
+ return EINVAL;
+ }
+
+ for (i = 0; i < num; ++i) {
+ switch (cuxid[i].type) {
+ case CIFS_UXID_TYPE_UID:
+ err = sss_nss_getsidbyuid((uint32_t)cuxid[i].id.uid,
+ &sid, &id_type);
+ break;
+ case CIFS_UXID_TYPE_GID:
+ err = sss_nss_getsidbygid((uint32_t)cuxid[i].id.gid,
+ &sid, &id_type);
+ break;
+ default:
+ err = sss_nss_getsidbyid((uint32_t)cuxid[i].id.uid, &sid, &id_type);
+ }
+ if (err != 0) {
+ ctx_set_error(ctx, strerror(err));
+ csid[i].revision = 0;
+ /* FIXME: would it be safe to map *any* uid/gids unknown by sssd to
+ * SAMBA's UNIX SIDs? */
+ continue;
+ }
+
+ if (sid_to_cifs_sid(ctx, sid, csid) == 0)
+ success = 0;
+ else
+ csid[i].revision = 0;
+ free(sid);
+ }
+
+ return success;
+}
diff --git a/src/lib/idmap/sss_idmap.c b/src/lib/idmap/sss_idmap.c
new file mode 100644
index 0000000..7ad0565
--- /dev/null
+++ b/src/lib/idmap/sss_idmap.c
@@ -0,0 +1,1613 @@
+/*
+ SSSD
+
+ ID-mapping library
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include "lib/idmap/sss_idmap.h"
+#include "lib/idmap/sss_idmap_private.h"
+#include "shared/murmurhash3.h"
+
+#define SID_FMT "%s-%"PRIu32
+#define SID_STR_MAX_LEN 1024
+
+/* Hold all parameters for unix<->sid mapping relevant for
+ * given slice. */
+struct idmap_range_params {
+ uint32_t min_id;
+ uint32_t max_id;
+ char *range_id;
+
+ uint32_t first_rid;
+ struct idmap_range_params *next;
+};
+
+struct idmap_domain_info {
+ char *name;
+ char *sid;
+ struct idmap_range_params range_params;
+ struct idmap_domain_info *next;
+ bool external_mapping;
+
+ struct idmap_range_params *helpers;
+ bool auto_add_ranges;
+ bool helpers_owner;
+
+ idmap_store_cb cb;
+ void *pvt;
+};
+
+static void *default_alloc(size_t size, void *pvt)
+{
+ return malloc(size);
+}
+
+static void default_free(void *ptr, void *pvt)
+{
+ free(ptr);
+}
+
+static char *idmap_strdup(struct sss_idmap_ctx *ctx, const char *str)
+{
+ char *new = NULL;
+ size_t len;
+
+ CHECK_IDMAP_CTX(ctx, NULL);
+
+ len = strlen(str) + 1;
+
+ new = ctx->alloc_func(len, ctx->alloc_pvt);
+ if (new == NULL) {
+ return NULL;
+ }
+
+ memcpy(new, str, len);
+
+ return new;
+}
+
+static bool ranges_eq(const struct idmap_range_params *a,
+ const struct idmap_range_params *b)
+{
+ if (a == NULL || b == NULL) {
+ return false;
+ }
+
+ if (a->first_rid == b->first_rid
+ && a->min_id == b->min_id
+ && a->max_id == b->max_id) {
+ return true;
+ }
+
+ return false;
+}
+
+static enum idmap_error_code
+construct_range(struct sss_idmap_ctx *ctx,
+ const struct idmap_range_params *src,
+ char *id,
+ struct idmap_range_params **_dst)
+{
+ struct idmap_range_params *dst;
+
+ if (src == NULL || id == NULL || _dst == NULL) {
+ return IDMAP_ERROR;
+ }
+
+ dst = ctx->alloc_func(sizeof(struct idmap_range_params), ctx->alloc_pvt);
+ if (dst == NULL) {
+ return IDMAP_OUT_OF_MEMORY;
+ }
+
+ dst->min_id = src->min_id;
+ dst->max_id = src->max_id;
+ dst->first_rid = src->first_rid;
+ dst->next = NULL;
+ dst->range_id = id;
+
+ *_dst = dst;
+ return IDMAP_SUCCESS;
+}
+
+static bool id_is_in_range(uint32_t id,
+ struct idmap_range_params *rp,
+ uint32_t *rid)
+{
+ if (id == 0 || rp == NULL) {
+ return false;
+ }
+
+ if (id >= rp->min_id && id <= rp->max_id) {
+ if (rid != NULL) {
+ *rid = rp->first_rid + (id - rp->min_id);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+const char *idmap_error_string(enum idmap_error_code err)
+{
+ switch (err) {
+ case IDMAP_SUCCESS:
+ return "IDMAP operation successful";
+ break;
+ case IDMAP_NOT_IMPLEMENTED:
+ return "IDMAP Function is not yet implemented";
+ break;
+ case IDMAP_ERROR:
+ return "IDMAP general error";
+ break;
+ case IDMAP_OUT_OF_MEMORY:
+ return "IDMAP operation ran out of memory";
+ break;
+ case IDMAP_NO_DOMAIN:
+ return "IDMAP domain not found";
+ break;
+ case IDMAP_CONTEXT_INVALID:
+ return "IDMAP context is invalid";
+ break;
+ case IDMAP_SID_INVALID:
+ return "IDMAP SID is invalid";
+ break;
+ case IDMAP_SID_UNKNOWN:
+ return "IDMAP SID not found";
+ break;
+ case IDMAP_NO_RANGE:
+ return "IDMAP range not found";
+ break;
+ case IDMAP_BUILTIN_SID:
+ return "IDMAP SID from BUILTIN domain";
+ break;
+ case IDMAP_OUT_OF_SLICES:
+ return "IDMAP not more free slices";
+ break;
+ case IDMAP_COLLISION:
+ return "IDMAP new range collides with existing one";
+ break;
+ case IDMAP_EXTERNAL:
+ return "IDMAP ID managed externally";
+ break;
+ case IDMAP_NAME_UNKNOWN:
+ return "IDMAP domain with the given name not found";
+ break;
+ default:
+ return "IDMAP unknown error code";
+ }
+}
+
+bool is_domain_sid(const char *sid)
+{
+ const char *p;
+ long long a;
+ char *endptr;
+ size_t c;
+
+ if (sid == NULL || strncmp(sid, DOM_SID_PREFIX, DOM_SID_PREFIX_LEN) != 0) {
+ return false;
+ }
+
+ p = sid + DOM_SID_PREFIX_LEN;
+ c = 0;
+
+ do {
+ errno = 0;
+ a = strtoull(p, &endptr, 10);
+ if (errno != 0 || a > UINT32_MAX) {
+ return false;
+ }
+
+ if (*endptr == '-') {
+ p = endptr + 1;
+ } else if (*endptr != '\0') {
+ return false;
+ }
+ c++;
+ } while(c < 3 && *endptr != '\0');
+
+ if (c != 3 || *endptr != '\0') {
+ return false;
+ }
+
+ return true;
+}
+
+enum idmap_error_code sss_idmap_init(idmap_alloc_func *alloc_func,
+ void *alloc_pvt,
+ idmap_free_func *free_func,
+ struct sss_idmap_ctx **_ctx)
+{
+ struct sss_idmap_ctx *ctx;
+
+ if (alloc_func == NULL) {
+ alloc_func = default_alloc;
+ }
+
+ ctx = alloc_func(sizeof(struct sss_idmap_ctx), alloc_pvt);
+ if (ctx == NULL) {
+ return IDMAP_OUT_OF_MEMORY;
+ }
+ memset(ctx, 0, sizeof(struct sss_idmap_ctx));
+
+ ctx->alloc_func = alloc_func;
+ ctx->alloc_pvt = alloc_pvt;
+ ctx->free_func = (free_func == NULL) ? default_free : free_func;
+
+ /* Set default values. */
+ ctx->idmap_opts.autorid_mode = SSS_IDMAP_DEFAULT_AUTORID;
+ ctx->idmap_opts.idmap_lower = SSS_IDMAP_DEFAULT_LOWER;
+ ctx->idmap_opts.idmap_upper = SSS_IDMAP_DEFAULT_UPPER;
+ ctx->idmap_opts.rangesize = SSS_IDMAP_DEFAULT_RANGESIZE;
+ ctx->idmap_opts.extra_slice_init = SSS_IDMAP_DEFAULT_EXTRA_SLICE_INIT;
+
+ *_ctx = ctx;
+
+ return IDMAP_SUCCESS;
+}
+
+static void free_helpers(struct sss_idmap_ctx *ctx,
+ struct idmap_range_params *helpers,
+ bool helpers_owner)
+{
+ struct idmap_range_params *it = helpers;
+ struct idmap_range_params *tmp;
+
+ if (helpers_owner == false) {
+ return;
+ }
+
+ while (it != NULL) {
+ tmp = it->next;
+
+ ctx->free_func(it->range_id, ctx->alloc_pvt);
+ ctx->free_func(it, ctx->alloc_pvt);
+
+ it = tmp;
+ }
+}
+
+static struct idmap_range_params*
+get_helper_by_id(struct idmap_range_params *helpers, const char *id)
+{
+ struct idmap_range_params *it;
+
+ for (it = helpers; it != NULL; it = it->next) {
+ if (strcmp(it->range_id, id) == 0) {
+ return it;
+ }
+ }
+
+ return NULL;
+}
+
+static void sss_idmap_free_domain(struct sss_idmap_ctx *ctx,
+ struct idmap_domain_info *dom)
+{
+ if (ctx == NULL || dom == NULL) {
+ return;
+ }
+
+ ctx->free_func(dom->range_params.range_id, ctx->alloc_pvt);
+
+ free_helpers(ctx, dom->helpers, dom->helpers_owner);
+
+ ctx->free_func(dom->name, ctx->alloc_pvt);
+ ctx->free_func(dom->sid, ctx->alloc_pvt);
+ ctx->free_func(dom, ctx->alloc_pvt);
+}
+
+enum idmap_error_code sss_idmap_free(struct sss_idmap_ctx *ctx)
+{
+ struct idmap_domain_info *dom;
+ struct idmap_domain_info *next;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ next = ctx->idmap_domain_info;
+ while (next) {
+ dom = next;
+ next = dom->next;
+ sss_idmap_free_domain(ctx, dom);
+ }
+
+ ctx->free_func(ctx, ctx->alloc_pvt);
+
+ return IDMAP_SUCCESS;
+}
+
+static enum idmap_error_code sss_idmap_free_ptr(struct sss_idmap_ctx *ctx,
+ void *ptr)
+{
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ if (ptr != NULL) {
+ ctx->free_func(ptr, ctx->alloc_pvt);
+ }
+
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code sss_idmap_free_sid(struct sss_idmap_ctx *ctx,
+ char *sid)
+{
+ return sss_idmap_free_ptr(ctx, sid);
+}
+
+enum idmap_error_code sss_idmap_free_dom_sid(struct sss_idmap_ctx *ctx,
+ struct sss_dom_sid *dom_sid)
+{
+ return sss_idmap_free_ptr(ctx, dom_sid);
+}
+
+enum idmap_error_code sss_idmap_free_smb_sid(struct sss_idmap_ctx *ctx,
+ struct dom_sid *smb_sid)
+{
+ return sss_idmap_free_ptr(ctx, smb_sid);
+}
+
+enum idmap_error_code sss_idmap_free_bin_sid(struct sss_idmap_ctx *ctx,
+ uint8_t *bin_sid)
+{
+ return sss_idmap_free_ptr(ctx, bin_sid);
+}
+
+static bool check_overlap(struct idmap_range_params *range,
+ id_t min, id_t max)
+{
+ return ((range->min_id <= min && range->max_id >= max)
+ || (range->min_id >= min && range->min_id <= max)
+ || (range->max_id >= min && range->max_id <= max));
+}
+
+static bool check_dom_overlap(struct idmap_range_params *prim_range,
+ /* struct idmap_range_params *sec_ranges, */
+ id_t min,
+ id_t max)
+{
+ return check_overlap(prim_range, min, max);
+}
+
+enum idmap_error_code sss_idmap_calculate_range(struct sss_idmap_ctx *ctx,
+ const char *range_id,
+ id_t *slice_num,
+ struct sss_idmap_range *_range)
+{
+ id_t max_slices;
+ id_t orig_slice;
+ id_t new_slice = 0;
+ id_t min;
+ id_t max;
+ id_t idmap_lower;
+ id_t idmap_upper;
+ id_t rangesize;
+ bool autorid_mode;
+ uint32_t hash_val;
+ struct idmap_domain_info *dom;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ idmap_lower = ctx->idmap_opts.idmap_lower;
+ idmap_upper = ctx->idmap_opts.idmap_upper;
+ rangesize = ctx->idmap_opts.rangesize;
+ autorid_mode = ctx->idmap_opts.autorid_mode;
+
+ max_slices = (idmap_upper - idmap_lower) / rangesize;
+
+ if (slice_num && *slice_num != -1) {
+ /* The slice is being set explicitly.
+ * This may happen at system startup when we're loading
+ * previously-determined slices. In the future, we may also
+ * permit configuration to select the slice for a domain
+ * explicitly.
+ */
+ new_slice = *slice_num;
+ min = (rangesize * new_slice) + idmap_lower;
+ max = min + rangesize - 1;
+ for (dom = ctx->idmap_domain_info; dom != NULL; dom = dom->next) {
+ if (check_dom_overlap(&dom->range_params,min, max)) {
+ /* This range overlaps one already registered
+ * Fail, because the slice was manually configured
+ */
+ return IDMAP_COLLISION;
+ }
+ }
+ } else {
+ /* If slice is -1, we're being asked to pick a new slice */
+
+ if (autorid_mode) {
+ /* In autorid compatibility mode, always start at 0 and find the
+ * first free value.
+ */
+ orig_slice = 0;
+ } else {
+ /* Hash the range identifier string */
+ hash_val = murmurhash3(range_id, strlen(range_id), 0xdeadbeef);
+
+ /* Now get take the modulus of the hash val and the max_slices
+ * to determine its optimal position in the range.
+ */
+ new_slice = hash_val % max_slices;
+ orig_slice = new_slice;
+ }
+
+ min = (rangesize * new_slice) + idmap_lower;
+ max = min + rangesize - 1;
+ /* Verify that this slice is not already in use */
+ do {
+ for (dom = ctx->idmap_domain_info; dom != NULL; dom = dom->next) {
+
+ if (check_dom_overlap(&dom->range_params,
+ min, max)) {
+ /* This range overlaps one already registered
+ * We'll try the next available slot
+ */
+ new_slice++;
+ if (new_slice >= max_slices) {
+ /* loop around to the beginning if necessary */
+ new_slice = 0;
+ }
+
+ min = (rangesize * new_slice) + idmap_lower;
+ max = min + rangesize - 1;
+ break;
+ }
+ }
+
+ /* Keep trying until dom is NULL (meaning we got to the end
+ * without matching) or we have run out of slices and gotten
+ * back to the first one we tried.
+ */
+ } while (dom && new_slice != orig_slice);
+
+ if (dom) {
+ /* We looped all the way through and found no empty slots */
+ return IDMAP_OUT_OF_SLICES;
+ }
+ }
+
+ _range->min = (rangesize * new_slice) + idmap_lower;
+ _range->max = _range->min + rangesize - 1;
+
+ if (slice_num) {
+ *slice_num = new_slice;
+ }
+
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code sss_idmap_check_collision_ex(const char *o_name,
+ const char *o_sid,
+ struct sss_idmap_range *o_range,
+ uint32_t o_first_rid,
+ const char *o_range_id,
+ bool o_external_mapping,
+ const char *n_name,
+ const char *n_sid,
+ struct sss_idmap_range *n_range,
+ uint32_t n_first_rid,
+ const char *n_range_id,
+ bool n_external_mapping)
+{
+ bool names_equal;
+ bool sids_equal;
+
+ /* TODO: if both ranges have the same ID check if an update is
+ * needed. */
+
+ /* Check if ID ranges overlap.
+ * ID ranges with external mapping may overlap. */
+ if ((!n_external_mapping && !o_external_mapping)
+ && ((n_range->min >= o_range->min
+ && n_range->min <= o_range->max)
+ || (n_range->max >= o_range->min
+ && n_range->max <= o_range->max))) {
+ return IDMAP_COLLISION;
+ }
+
+ names_equal = (strcasecmp(n_name, o_name) == 0);
+ sids_equal = ((n_sid == NULL && o_sid == NULL)
+ || (n_sid != NULL && o_sid != NULL
+ && strcasecmp(n_sid, o_sid) == 0));
+
+ /* check if domain name and SID are consistent */
+ if ((names_equal && !sids_equal) || (!names_equal && sids_equal)) {
+ return IDMAP_COLLISION;
+ }
+
+ /* check if external_mapping is consistent */
+ if (names_equal && sids_equal
+ && n_external_mapping != o_external_mapping) {
+ return IDMAP_COLLISION;
+ }
+
+ /* check if RID ranges overlap */
+ if (names_equal && sids_equal
+ && n_external_mapping == false
+ && n_first_rid >= o_first_rid
+ && n_first_rid <= o_first_rid + (o_range->max - o_range->min)) {
+ return IDMAP_COLLISION;
+ }
+
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code sss_idmap_check_collision(struct sss_idmap_ctx *ctx,
+ char *n_name, char *n_sid,
+ struct sss_idmap_range *n_range,
+ uint32_t n_first_rid,
+ char *n_range_id,
+ bool n_external_mapping)
+{
+ struct idmap_domain_info *dom;
+ enum idmap_error_code err;
+ struct sss_idmap_range range;
+
+ for (dom = ctx->idmap_domain_info; dom != NULL; dom = dom->next) {
+
+ range.min = dom->range_params.min_id;
+ range.max = dom->range_params.max_id;
+
+ err = sss_idmap_check_collision_ex(dom->name, dom->sid,
+ &range,
+ dom->range_params.first_rid,
+ dom->range_params.range_id,
+ dom->external_mapping,
+ n_name, n_sid, n_range, n_first_rid,
+ n_range_id, n_external_mapping);
+ if (err != IDMAP_SUCCESS) {
+ return err;
+ }
+ }
+ return IDMAP_SUCCESS;
+}
+
+static enum
+idmap_error_code dom_check_collision(struct idmap_domain_info *dom_list,
+ struct idmap_domain_info *new_dom)
+{
+ struct idmap_domain_info *dom;
+ enum idmap_error_code err;
+ struct sss_idmap_range range;
+ struct sss_idmap_range new_dom_range = { new_dom->range_params.min_id,
+ new_dom->range_params.max_id };
+
+ for (dom = dom_list; dom != NULL; dom = dom->next) {
+ range.min = dom->range_params.min_id;
+ range.max = dom->range_params.max_id;
+
+ err = sss_idmap_check_collision_ex(dom->name, dom->sid,
+ &range,
+ dom->range_params.first_rid,
+ dom->range_params.range_id,
+ dom->external_mapping,
+ new_dom->name, new_dom->sid,
+ &new_dom_range,
+ new_dom->range_params.first_rid,
+ new_dom->range_params.range_id,
+ new_dom->external_mapping);
+ if (err != IDMAP_SUCCESS) {
+ return err;
+ }
+ }
+ return IDMAP_SUCCESS;
+}
+
+static char*
+generate_sec_slice_name(struct sss_idmap_ctx *ctx,
+ const char *domain_sid, uint32_t rid)
+{
+ const char *SEC_SLICE_NAME_FMT = "%s-%"PRIu32;
+ char *slice_name;
+ int len, len2;
+
+ len = snprintf(NULL, 0, SEC_SLICE_NAME_FMT, domain_sid, rid);
+ if (len <= 0) {
+ return NULL;
+ }
+
+ slice_name = ctx->alloc_func(len + 1, ctx->alloc_pvt);
+ if (slice_name == NULL) {
+ return NULL;
+ }
+
+ len2 = snprintf(slice_name, len + 1, SEC_SLICE_NAME_FMT, domain_sid,
+ rid);
+ if (len != len2) {
+ ctx->free_func(slice_name, ctx->alloc_pvt);
+ return NULL;
+ }
+
+ return slice_name;
+}
+
+static enum idmap_error_code
+generate_slice(struct sss_idmap_ctx *ctx, char *slice_name, uint32_t first_rid,
+ struct idmap_range_params **_slice)
+{
+ struct idmap_range_params *slice;
+ struct sss_idmap_range tmp_range;
+ enum idmap_error_code err;
+
+ slice = ctx->alloc_func(sizeof(struct idmap_range_params), ctx->alloc_pvt);
+ if (slice == NULL) {
+ return IDMAP_OUT_OF_MEMORY;
+ }
+
+ slice->next = NULL;
+
+ err = sss_idmap_calculate_range(ctx, slice_name, NULL, &tmp_range);
+ if (err != IDMAP_SUCCESS) {
+ ctx->free_func(slice, ctx->alloc_pvt);
+ return err;
+ }
+
+ slice->min_id = tmp_range.min;
+ slice->max_id = tmp_range.max;
+ slice->range_id = slice_name;
+ slice->first_rid = first_rid;
+
+ *_slice = slice;
+ return IDMAP_SUCCESS;
+}
+
+static enum idmap_error_code
+get_helpers(struct sss_idmap_ctx *ctx,
+ const char *domain_sid,
+ uint32_t first_rid,
+ struct idmap_range_params **_sec_slices)
+{
+ struct idmap_range_params *prev = NULL;
+ struct idmap_range_params *sec_slices = NULL;
+ static enum idmap_error_code err;
+ struct idmap_range_params *slice;
+ char *secondary_name;
+
+ for (int i = 0; i < ctx->idmap_opts.extra_slice_init; i++) {
+ secondary_name = generate_sec_slice_name(ctx, domain_sid, first_rid);
+ if (secondary_name == NULL) {
+ err = IDMAP_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ err = generate_slice(ctx, secondary_name, first_rid, &slice);
+ if (err != IDMAP_SUCCESS) {
+ goto fail;
+ }
+
+ first_rid += ctx->idmap_opts.rangesize;
+
+ if (prev != NULL) {
+ prev->next = slice;
+ }
+
+ if (sec_slices == NULL) {
+ sec_slices = slice;
+ }
+
+ prev = slice;
+ }
+
+ *_sec_slices = sec_slices;
+ return IDMAP_SUCCESS;
+
+fail:
+ ctx->free_func(secondary_name, ctx->alloc_pvt);
+
+ /* Free already generated helpers. */
+ free_helpers(ctx, sec_slices, true);
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_add_domain_ex(struct sss_idmap_ctx *ctx,
+ const char *domain_name,
+ const char *domain_sid,
+ struct sss_idmap_range *range,
+ const char *range_id,
+ uint32_t rid,
+ bool external_mapping)
+{
+ struct idmap_domain_info *dom = NULL;
+ enum idmap_error_code err;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ if (domain_name == NULL) {
+ return IDMAP_NO_DOMAIN;
+ }
+
+ if (range == NULL) {
+ return IDMAP_NO_RANGE;
+ }
+
+ /* For algorithmic mapping a valid domain SID is required, for external
+ * mapping it may be NULL, but if set it should be valid. */
+ if ((!external_mapping && !is_domain_sid(domain_sid))
+ || (external_mapping
+ && domain_sid != NULL
+ && !is_domain_sid(domain_sid))) {
+ return IDMAP_SID_INVALID;
+ }
+
+ dom = ctx->alloc_func(sizeof(struct idmap_domain_info), ctx->alloc_pvt);
+ if (dom == NULL) {
+ return IDMAP_OUT_OF_MEMORY;
+ }
+ memset(dom, 0, sizeof(struct idmap_domain_info));
+
+ dom->name = idmap_strdup(ctx, domain_name);
+ if (dom->name == NULL) {
+ err = IDMAP_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ if (domain_sid != NULL) {
+ dom->sid = idmap_strdup(ctx, domain_sid);
+ if (dom->sid == NULL) {
+ err = IDMAP_OUT_OF_MEMORY;
+ goto fail;
+ }
+ }
+
+ dom->range_params.min_id = range->min;
+ dom->range_params.max_id = range->max;
+
+ if (range_id != NULL) {
+ dom->range_params.range_id = idmap_strdup(ctx, range_id);
+ if (dom->range_params.range_id == NULL) {
+ err = IDMAP_OUT_OF_MEMORY;
+ goto fail;
+ }
+ }
+
+ dom->range_params.first_rid = rid;
+ dom->external_mapping = external_mapping;
+
+ err = dom_check_collision(ctx->idmap_domain_info, dom);
+ if (err != IDMAP_SUCCESS) {
+ goto fail;
+ }
+
+ dom->next = ctx->idmap_domain_info;
+ ctx->idmap_domain_info = dom;
+
+ return IDMAP_SUCCESS;
+
+fail:
+ sss_idmap_free_domain(ctx, dom);
+
+ return err;
+}
+
+enum idmap_error_code
+sss_idmap_add_auto_domain_ex(struct sss_idmap_ctx *ctx,
+ const char *domain_name,
+ const char *domain_sid,
+ struct sss_idmap_range *range,
+ const char *range_id,
+ uint32_t rid,
+ bool external_mapping,
+ idmap_store_cb cb,
+ void *pvt)
+{
+ enum idmap_error_code err;
+
+ err = sss_idmap_add_domain_ex(ctx, domain_name, domain_sid, range,
+ range_id, rid, external_mapping);
+ if (err != IDMAP_SUCCESS) {
+ return err;
+ }
+
+ if (external_mapping) {
+ /* There's no point in generating secondary ranges if external_mapping
+ is enabled. */
+ ctx->idmap_domain_info->auto_add_ranges = false;
+ return IDMAP_SUCCESS;
+ }
+
+ if ((range->max - range->min + 1) != ctx->idmap_opts.rangesize) {
+ /* Range of primary slice is not equal to the value of
+ ldap_idmap_range_size option. */
+ return IDMAP_ERROR;
+ }
+
+ /* No additional secondary ranges should be added if no sec ranges are
+ predeclared. */
+ if (ctx->idmap_opts.extra_slice_init == 0) {
+ ctx->idmap_domain_info->auto_add_ranges = false;
+ return IDMAP_SUCCESS;
+ }
+
+ /* Add size of primary slice for first_rid of secondary slices. */
+ rid += ctx->idmap_opts.rangesize;
+ err = get_helpers(ctx, domain_sid, rid,
+ &ctx->idmap_domain_info->helpers);
+ if (err == IDMAP_SUCCESS) {
+ ctx->idmap_domain_info->auto_add_ranges = true;
+ ctx->idmap_domain_info->helpers_owner = true;
+ } else {
+ /* Running out of slices for secondary mapping is a non-fatal
+ * problem. */
+ if (err == IDMAP_OUT_OF_SLICES) {
+ err = IDMAP_SUCCESS;
+ }
+ ctx->idmap_domain_info->auto_add_ranges = false;
+ }
+
+ ctx->idmap_domain_info->cb = cb;
+ ctx->idmap_domain_info->pvt = pvt;
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_add_domain(struct sss_idmap_ctx *ctx,
+ const char *domain_name,
+ const char *domain_sid,
+ struct sss_idmap_range *range)
+{
+ return sss_idmap_add_domain_ex(ctx, domain_name, domain_sid, range, NULL,
+ 0, false);
+}
+
+static bool sss_idmap_sid_is_builtin(const char *sid)
+{
+ if (strncmp(sid, "S-1-5-32-", 9) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+static bool parse_rid(const char *sid, size_t dom_prefix_len, long long *_rid)
+{
+ long long rid;
+ char *endptr;
+
+ errno = 0;
+ /* Use suffix of sid - part after domain and following '-' */
+ rid = strtoull(sid + dom_prefix_len + 1, &endptr, 10);
+ if (errno != 0 || rid > UINT32_MAX || *endptr != '\0') {
+ return false;
+ }
+
+ *_rid = rid;
+ return true;
+}
+
+static bool is_sid_from_dom(const char *dom_sid, const char *sid,
+ size_t *_dom_sid_len)
+{
+ size_t dom_sid_len;
+
+ if (dom_sid == NULL) {
+ return false;
+ }
+
+ dom_sid_len = strlen(dom_sid);
+ *_dom_sid_len = dom_sid_len;
+
+ if (strlen(sid) < dom_sid_len || sid[dom_sid_len] != '-') {
+ return false;
+ }
+
+ return strncmp(sid, dom_sid, dom_sid_len) == 0;
+}
+
+static bool comp_id(struct idmap_range_params *range_params, long long rid,
+ uint32_t *_id)
+{
+ uint32_t id;
+
+ if (rid >= range_params->first_rid
+ && ((UINT32_MAX - range_params->min_id) >
+ (rid - range_params->first_rid))) {
+ id = range_params->min_id + (rid - range_params->first_rid);
+ if (id <= range_params->max_id) {
+ *_id = id;
+ return true;
+ }
+ }
+ return false;
+}
+
+static enum idmap_error_code
+get_range(struct sss_idmap_ctx *ctx,
+ struct idmap_range_params *helpers,
+ const char *dom_sid,
+ long long rid,
+ struct idmap_range_params **_range)
+{
+ char *secondary_name = NULL;
+ enum idmap_error_code err;
+ int first_rid;
+ struct idmap_range_params *range;
+ struct idmap_range_params *helper;
+
+ first_rid = (rid / ctx->idmap_opts.rangesize) * ctx->idmap_opts.rangesize;
+
+ secondary_name = generate_sec_slice_name(ctx, dom_sid, first_rid);
+ if (secondary_name == NULL) {
+ err = IDMAP_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ helper = get_helper_by_id(helpers, secondary_name);
+ if (helper != NULL) {
+ /* Utilize helper's range. */
+ err = construct_range(ctx, helper, secondary_name, &range);
+ } else {
+ /* Have to generate a whole new range. */
+ err = generate_slice(ctx, secondary_name, first_rid, &range);
+ }
+
+ if (err != IDMAP_SUCCESS) {
+ goto error;
+ }
+
+ *_range = range;
+ return IDMAP_SUCCESS;
+
+error:
+ ctx->free_func(secondary_name, ctx->alloc_pvt);
+ return err;
+}
+
+static enum idmap_error_code
+spawn_dom(struct sss_idmap_ctx *ctx,
+ struct idmap_domain_info *parent,
+ struct idmap_range_params *range)
+{
+ struct sss_idmap_range tmp;
+ static enum idmap_error_code err;
+ struct idmap_domain_info *it;
+
+ tmp.min = range->min_id;
+ tmp.max = range->max_id;
+
+ err = sss_idmap_add_domain_ex(ctx,
+ parent->name,
+ parent->sid,
+ &tmp, range->range_id,
+ range->first_rid, false);
+ if (err != IDMAP_SUCCESS) {
+ return err;
+ }
+
+ it = ctx->idmap_domain_info;
+ while (it != NULL) {
+ /* Find the newly added domain. */
+ if (ranges_eq(&it->range_params, range)) {
+
+ /* Share helpers. */
+ it->helpers = parent->helpers;
+ it->auto_add_ranges = parent->auto_add_ranges;
+
+ /* Share call back for storing domains */
+ it->cb = parent->cb;
+ it->pvt = parent->pvt;
+ break;
+ }
+
+ it = it->next;
+ }
+
+ if (it == NULL) {
+ /* Failed to find just added domain. */
+ return IDMAP_ERROR;
+ }
+
+ /* Store mapping for newly created domain. */
+ if (it->cb != NULL) {
+ err = it->cb(it->name,
+ it->sid,
+ it->range_params.range_id,
+ it->range_params.min_id,
+ it->range_params.max_id,
+ it->range_params.first_rid,
+ it->pvt);
+ if (err != IDMAP_SUCCESS) {
+ return err;
+ }
+ }
+
+ return IDMAP_SUCCESS;
+}
+
+static enum idmap_error_code
+add_dom_for_sid(struct sss_idmap_ctx *ctx,
+ struct idmap_domain_info *matched_dom,
+ const char *sid,
+ uint32_t *_id)
+{
+ enum idmap_error_code err;
+ long long rid;
+ struct idmap_range_params *range = NULL;
+
+ if (parse_rid(sid, strlen(matched_dom->sid), &rid) == false) {
+ err = IDMAP_SID_INVALID;
+ goto done;
+ }
+
+ err = get_range(ctx, matched_dom->helpers, matched_dom->sid, rid, &range);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = spawn_dom(ctx, matched_dom, range);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ if (!comp_id(range, rid, _id)) {
+ err = IDMAP_ERROR;
+ goto done;
+ }
+
+ err = IDMAP_SUCCESS;
+
+done:
+ if (range != NULL) {
+ ctx->free_func(range->range_id, ctx->alloc_pvt);
+ }
+ ctx->free_func(range, ctx->alloc_pvt);
+ return err;
+}
+
+enum idmap_error_code sss_idmap_sid_to_unix(struct sss_idmap_ctx *ctx,
+ const char *sid,
+ uint32_t *_id)
+{
+ struct idmap_domain_info *idmap_domain_info;
+ struct idmap_domain_info *matched_dom = NULL;
+ size_t dom_len;
+ long long rid;
+
+ if (sid == NULL || _id == NULL) {
+ return IDMAP_ERROR;
+ }
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ idmap_domain_info = ctx->idmap_domain_info;
+
+ if (sss_idmap_sid_is_builtin(sid)) {
+ return IDMAP_BUILTIN_SID;
+ }
+
+ /* Try primary slices */
+ while (idmap_domain_info != NULL) {
+
+ if (is_sid_from_dom(idmap_domain_info->sid, sid, &dom_len)) {
+
+ if (idmap_domain_info->external_mapping == true) {
+ return IDMAP_EXTERNAL;
+ }
+
+ if (parse_rid(sid, dom_len, &rid) == false) {
+ return IDMAP_SID_INVALID;
+ }
+
+ if (comp_id(&idmap_domain_info->range_params, rid, _id)) {
+ return IDMAP_SUCCESS;
+ }
+
+ matched_dom = idmap_domain_info;
+ }
+
+ idmap_domain_info = idmap_domain_info->next;
+ }
+
+ if (matched_dom != NULL && matched_dom->auto_add_ranges) {
+ return add_dom_for_sid(ctx, matched_dom, sid, _id);
+ }
+
+ return matched_dom ? IDMAP_NO_RANGE : IDMAP_NO_DOMAIN;
+}
+
+enum idmap_error_code sss_idmap_check_sid_unix(struct sss_idmap_ctx *ctx,
+ const char *sid,
+ uint32_t id)
+{
+ struct idmap_domain_info *idmap_domain_info;
+ size_t dom_len;
+ bool no_range = false;
+
+ if (sid == NULL) {
+ return IDMAP_ERROR;
+ }
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ if (ctx->idmap_domain_info == NULL) {
+ return IDMAP_NO_DOMAIN;
+ }
+
+ idmap_domain_info = ctx->idmap_domain_info;
+
+ if (sss_idmap_sid_is_builtin(sid)) {
+ return IDMAP_BUILTIN_SID;
+ }
+
+ while (idmap_domain_info != NULL) {
+ if (idmap_domain_info->sid != NULL) {
+ dom_len = strlen(idmap_domain_info->sid);
+ if (strlen(sid) > dom_len && sid[dom_len] == '-'
+ && strncmp(sid, idmap_domain_info->sid, dom_len) == 0) {
+
+ if (id >= idmap_domain_info->range_params.min_id
+ && id <= idmap_domain_info->range_params.max_id) {
+ return IDMAP_SUCCESS;
+ }
+
+ no_range = true;
+ }
+ }
+
+ idmap_domain_info = idmap_domain_info->next;
+ }
+
+ return no_range ? IDMAP_NO_RANGE : IDMAP_SID_UNKNOWN;
+}
+
+static enum idmap_error_code generate_sid(struct sss_idmap_ctx *ctx,
+ const char *dom_sid,
+ uint32_t rid,
+ char **_sid)
+{
+ char *sid;
+ int len;
+ int ret;
+
+ len = snprintf(NULL, 0, SID_FMT, dom_sid, rid);
+ if (len <= 0 || len > SID_STR_MAX_LEN) {
+ return IDMAP_ERROR;
+ }
+
+ sid = ctx->alloc_func(len + 1, ctx->alloc_pvt);
+ if (sid == NULL) {
+ return IDMAP_OUT_OF_MEMORY;
+ }
+
+ ret = snprintf(sid, len + 1, SID_FMT, dom_sid, rid);
+ if (ret != len) {
+ ctx->free_func(sid, ctx->alloc_pvt);
+ return IDMAP_ERROR;
+ }
+
+ *_sid = sid;
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code sss_idmap_unix_to_sid(struct sss_idmap_ctx *ctx,
+ uint32_t id,
+ char **_sid)
+{
+ struct idmap_domain_info *idmap_domain_info;
+ uint32_t rid;
+ enum idmap_error_code err;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ idmap_domain_info = ctx->idmap_domain_info;
+
+ while (idmap_domain_info != NULL) {
+ if (id_is_in_range(id, &idmap_domain_info->range_params, &rid)) {
+
+ if (idmap_domain_info->external_mapping == true
+ || idmap_domain_info->sid == NULL) {
+ return IDMAP_EXTERNAL;
+ }
+
+ return generate_sid(ctx, idmap_domain_info->sid, rid, _sid);
+ }
+
+ idmap_domain_info = idmap_domain_info->next;
+ }
+
+ /* Check secondary ranges. */
+ idmap_domain_info = ctx->idmap_domain_info;
+ while (idmap_domain_info != NULL) {
+
+ for (struct idmap_range_params *it = idmap_domain_info->helpers;
+ it != NULL;
+ it = it->next) {
+
+ if (idmap_domain_info->helpers_owner == false) {
+ /* Checking helpers on owner is sufficient. */
+ continue;
+ }
+
+ if (id_is_in_range(id, it, &rid)) {
+
+ if (idmap_domain_info->external_mapping == true
+ || idmap_domain_info->sid == NULL) {
+ return IDMAP_EXTERNAL;
+ }
+
+ err = spawn_dom(ctx, idmap_domain_info, it);
+ if (err != IDMAP_SUCCESS) {
+ return err;
+ }
+
+ return generate_sid(ctx, idmap_domain_info->sid, rid, _sid);
+ }
+ }
+
+ idmap_domain_info = idmap_domain_info->next;
+ }
+
+ return IDMAP_NO_DOMAIN;
+}
+
+enum idmap_error_code sss_idmap_dom_sid_to_unix(struct sss_idmap_ctx *ctx,
+ struct sss_dom_sid *dom_sid,
+ uint32_t *id)
+{
+ enum idmap_error_code err;
+ char *sid;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ err = sss_idmap_dom_sid_to_sid(ctx, dom_sid, &sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_sid_to_unix(ctx, sid, id);
+
+done:
+ ctx->free_func(sid, ctx->alloc_pvt);
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_bin_sid_to_unix(struct sss_idmap_ctx *ctx,
+ uint8_t *bin_sid,
+ size_t length,
+ uint32_t *id)
+{
+ enum idmap_error_code err;
+ char *sid;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ err = sss_idmap_bin_sid_to_sid(ctx, bin_sid, length, &sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_sid_to_unix(ctx, sid, id);
+
+done:
+ ctx->free_func(sid, ctx->alloc_pvt);
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_smb_sid_to_unix(struct sss_idmap_ctx *ctx,
+ struct dom_sid *smb_sid,
+ uint32_t *id)
+{
+ enum idmap_error_code err;
+ char *sid;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ err = sss_idmap_smb_sid_to_sid(ctx, smb_sid, &sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_sid_to_unix(ctx, sid, id);
+
+done:
+ ctx->free_func(sid, ctx->alloc_pvt);
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_check_dom_sid_to_unix(struct sss_idmap_ctx *ctx,
+ struct sss_dom_sid *dom_sid,
+ uint32_t id)
+{
+ enum idmap_error_code err;
+ char *sid;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ err = sss_idmap_dom_sid_to_sid(ctx, dom_sid, &sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_check_sid_unix(ctx, sid, id);
+
+done:
+ ctx->free_func(sid, ctx->alloc_pvt);
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_check_bin_sid_unix(struct sss_idmap_ctx *ctx,
+ uint8_t *bin_sid,
+ size_t length,
+ uint32_t id)
+{
+ enum idmap_error_code err;
+ char *sid;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ err = sss_idmap_bin_sid_to_sid(ctx, bin_sid, length, &sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_check_sid_unix(ctx, sid, id);
+
+done:
+ ctx->free_func(sid, ctx->alloc_pvt);
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_check_smb_sid_unix(struct sss_idmap_ctx *ctx,
+ struct dom_sid *smb_sid,
+ uint32_t id)
+{
+ enum idmap_error_code err;
+ char *sid;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ err = sss_idmap_smb_sid_to_sid(ctx, smb_sid, &sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_check_sid_unix(ctx, sid, id);
+
+done:
+ ctx->free_func(sid, ctx->alloc_pvt);
+
+ return err;
+}
+enum idmap_error_code sss_idmap_unix_to_dom_sid(struct sss_idmap_ctx *ctx,
+ uint32_t id,
+ struct sss_dom_sid **_dom_sid)
+{
+ enum idmap_error_code err;
+ char *sid = NULL;
+ struct sss_dom_sid *dom_sid = NULL;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ err = sss_idmap_unix_to_sid(ctx, id, &sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_sid_to_dom_sid(ctx, sid, &dom_sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ *_dom_sid = dom_sid;
+ err = IDMAP_SUCCESS;
+
+done:
+ ctx->free_func(sid, ctx->alloc_pvt);
+ if (err != IDMAP_SUCCESS) {
+ ctx->free_func(dom_sid, ctx->alloc_pvt);
+ }
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_unix_to_bin_sid(struct sss_idmap_ctx *ctx,
+ uint32_t id,
+ uint8_t **_bin_sid,
+ size_t *_length)
+{
+ enum idmap_error_code err;
+ char *sid = NULL;
+ uint8_t *bin_sid = NULL;
+ size_t length;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ err = sss_idmap_unix_to_sid(ctx, id, &sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_sid_to_bin_sid(ctx, sid, &bin_sid, &length);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ *_bin_sid = bin_sid;
+ *_length = length;
+ err = IDMAP_SUCCESS;
+
+done:
+ ctx->free_func(sid, ctx->alloc_pvt);
+ if (err != IDMAP_SUCCESS) {
+ ctx->free_func(bin_sid, ctx->alloc_pvt);
+ }
+
+ return err;
+
+}
+
+enum idmap_error_code
+sss_idmap_ctx_set_autorid(struct sss_idmap_ctx *ctx, bool use_autorid)
+{
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+ ctx->idmap_opts.autorid_mode = use_autorid;
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code
+sss_idmap_ctx_set_lower(struct sss_idmap_ctx *ctx, id_t lower)
+{
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+ ctx->idmap_opts.idmap_lower = lower;
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code
+sss_idmap_ctx_set_upper(struct sss_idmap_ctx *ctx, id_t upper)
+{
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+ ctx->idmap_opts.idmap_upper = upper;
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code
+sss_idmap_ctx_set_rangesize(struct sss_idmap_ctx *ctx, id_t rangesize)
+{
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+ ctx->idmap_opts.rangesize = rangesize;
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code
+sss_idmap_ctx_set_extra_slice_init(struct sss_idmap_ctx *ctx,
+ int extra_slice_init)
+{
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+ ctx->idmap_opts.extra_slice_init = extra_slice_init;
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code
+sss_idmap_ctx_get_autorid(struct sss_idmap_ctx *ctx, bool *_autorid)
+{
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+ *_autorid = ctx->idmap_opts.autorid_mode;
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code
+sss_idmap_ctx_get_lower(struct sss_idmap_ctx *ctx, id_t *_lower)
+{
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+ *_lower = ctx->idmap_opts.idmap_lower;
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code
+sss_idmap_ctx_get_upper(struct sss_idmap_ctx *ctx, id_t *_upper)
+{
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+ *_upper = ctx->idmap_opts.idmap_upper;
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code
+sss_idmap_ctx_get_rangesize(struct sss_idmap_ctx *ctx, id_t *_rangesize)
+{
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+ *_rangesize = ctx->idmap_opts.rangesize;
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code
+sss_idmap_domain_has_algorithmic_mapping(struct sss_idmap_ctx *ctx,
+ const char *dom_sid,
+ bool *has_algorithmic_mapping)
+{
+ struct idmap_domain_info *idmap_domain_info;
+ size_t len;
+ size_t dom_sid_len;
+
+ if (dom_sid == NULL) {
+ return IDMAP_SID_INVALID;
+ }
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ if (ctx->idmap_domain_info == NULL) {
+ return IDMAP_NO_DOMAIN;
+ }
+
+ idmap_domain_info = ctx->idmap_domain_info;
+
+ while (idmap_domain_info != NULL) {
+ if (idmap_domain_info->sid != NULL) {
+ len = strlen(idmap_domain_info->sid);
+ dom_sid_len = strlen(dom_sid);
+ if (((dom_sid_len > len && dom_sid[len] == '-')
+ || dom_sid_len == len)
+ && strncmp(dom_sid, idmap_domain_info->sid, len) == 0) {
+
+ *has_algorithmic_mapping = !idmap_domain_info->external_mapping;
+ return IDMAP_SUCCESS;
+
+ }
+ }
+
+ idmap_domain_info = idmap_domain_info->next;
+ }
+
+ return IDMAP_SID_UNKNOWN;
+}
+
+enum idmap_error_code
+sss_idmap_domain_by_name_has_algorithmic_mapping(struct sss_idmap_ctx *ctx,
+ const char *dom_name,
+ bool *has_algorithmic_mapping)
+{
+ struct idmap_domain_info *idmap_domain_info;
+
+ if (dom_name == NULL) {
+ return IDMAP_ERROR;
+ }
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ if (ctx->idmap_domain_info == NULL) {
+ return IDMAP_NO_DOMAIN;
+ }
+
+ idmap_domain_info = ctx->idmap_domain_info;
+
+ while (idmap_domain_info != NULL) {
+ if (idmap_domain_info->name != NULL
+ && strcmp(dom_name, idmap_domain_info->name) == 0) {
+
+ *has_algorithmic_mapping = !idmap_domain_info->external_mapping;
+ return IDMAP_SUCCESS;
+ }
+
+ idmap_domain_info = idmap_domain_info->next;
+ }
+
+ return IDMAP_NAME_UNKNOWN;
+}
diff --git a/src/lib/idmap/sss_idmap.doxy.in b/src/lib/idmap/sss_idmap.doxy.in
new file mode 100644
index 0000000..833498b
--- /dev/null
+++ b/src/lib/idmap/sss_idmap.doxy.in
@@ -0,0 +1,1883 @@
+# Doxyfile 1.8.3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME = sss_idmap
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @PACKAGE_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = idmap_doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip. Note that you specify absolute paths here, but also
+# relative paths, which will be relative from the directory where doxygen is
+# started.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension,
+# and language is one of the parsers supported by doxygen: IDL, Java,
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
+# C++. For instance to make doxygen treat .inc files as Fortran files (default
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the
+# files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
+# Disable only in case of backward compatibilities issues.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented classes,
+# or namespaces to their corresponding documentation. Such a link can be
+# prevented in individual cases by by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES (the
+# default) will make doxygen replace the get and set methods by a property in
+# the documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+SYMBOL_CACHE_SIZE = 0
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = NO
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = YES
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if section-label ... \endif
+# and \cond section-label ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path. Do not use
+# file names with spaces, bibtex cannot handle them.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = @abs_top_srcdir@/src/lib/idmap/sss_idmap.h
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS = *.cpp \
+ *.cc \
+ *.c \
+ *.h \
+ *.hh \
+ *.hpp \
+ *.dox
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a UNIX file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS = */.git/* \
+ */.svn/* \
+ */cmake/* \
+ */build/*
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page (index.html).
+# This can be useful if you have a project on for instance GitHub and want reuse
+# the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C, C++ and Fortran comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If left blank doxygen will
+# generate a default style sheet. Note that it is recommended to use
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
+# tag will in the future become obsolete.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
+# user-defined cascading style sheet that is included after the standard
+# style sheets created by doxygen. Using this option one can overrule
+# certain style aspects. This is preferred over using HTML_STYLESHEET
+# since it does not replace the standard style sheet and is therefor more
+# robust against future updates. Doxygen will copy the style sheet file to
+# the output directory.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
+# and will result in a full expanded tree by default.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
+# identify the documentation publisher. This should be a reverse domain-name
+# style string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW = NONE
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
+# SVG. The default value is HTML-CSS, which is slower, but has the best
+# compatibility.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.
+# However, it is strongly recommended to install a local
+# copy of MathJax from http://www.mathjax.org before deployment.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript.
+# There are two flavours of web server based search depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools.
+# See the manual for details.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain
+# the search results. Doxygen ships with an example indexer (doxyindexer) and
+# search engine (doxysearch.cgi) which are based on the open source search engine
+# library Xapian. See the manual for configuration details.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will returned the search results when EXTERNAL_SEARCH is enabled.
+# Doxygen ships with an example search engine (doxysearch) which is based on
+# the open source search engine library Xapian. See the manual for configuration
+# details.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through other
+# doxygen projects that are not otherwise connected via tags files, but are
+# all added to the same search index. Each project needs to have a tag file set
+# via GENERATE_TAGFILE. The search mapping then maps the name of the tag file
+# to a relative location where the documentation can be found,
+# similar to the
+# TAGFILES option but without actually processing the tag file.
+# The format is: EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = DOXYGEN
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
+# doxygen is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
+# exceeded by 50% before the limit is enforced.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/src/lib/idmap/sss_idmap.exports b/src/lib/idmap/sss_idmap.exports
new file mode 100644
index 0000000..8406777
--- /dev/null
+++ b/src/lib/idmap/sss_idmap.exports
@@ -0,0 +1,66 @@
+SSS_IDMAP_0.4 {
+
+ # public functions
+ global:
+
+ sss_idmap_init;
+ sss_idmap_ctx_set_autorid;
+ sss_idmap_ctx_set_lower;
+ sss_idmap_ctx_set_upper;
+ sss_idmap_ctx_set_rangesize;
+ sss_idmap_ctx_get_autorid;
+ sss_idmap_ctx_get_lower;
+ sss_idmap_ctx_get_upper;
+ sss_idmap_ctx_get_rangesize;
+ sss_idmap_calculate_range;
+ sss_idmap_add_domain;
+ sss_idmap_add_domain_ex;
+ sss_idmap_check_collision;
+ sss_idmap_check_collision_ex;
+ sss_idmap_sid_to_unix;
+ sss_idmap_dom_sid_to_unix;
+ sss_idmap_bin_sid_to_unix;
+ sss_idmap_smb_sid_to_unix;
+ sss_idmap_check_sid_unix;
+ sss_idmap_check_dom_sid_to_unix;
+ sss_idmap_check_bin_sid_unix;
+ sss_idmap_check_smb_sid_unix;
+ sss_idmap_unix_to_sid;
+ sss_idmap_unix_to_dom_sid;
+ sss_idmap_unix_to_bin_sid;
+ sss_idmap_free;
+ sss_idmap_free_sid;
+ sss_idmap_free_dom_sid;
+ sss_idmap_free_smb_sid;
+ sss_idmap_free_bin_sid;
+ idmap_error_string;
+ is_domain_sid;
+ sss_idmap_domain_has_algorithmic_mapping;
+ sss_idmap_domain_by_name_has_algorithmic_mapping;
+ sss_idmap_bin_sid_to_dom_sid;
+ sss_idmap_bin_sid_to_sid;
+ sss_idmap_dom_sid_to_bin_sid;
+ sss_idmap_sid_to_bin_sid;
+ sss_idmap_dom_sid_to_sid;
+ sss_idmap_sid_to_dom_sid;
+ sss_idmap_sid_to_smb_sid;
+ sss_idmap_smb_sid_to_sid;
+ sss_idmap_dom_sid_to_smb_sid;
+ sss_idmap_smb_sid_to_dom_sid;
+ sss_idmap_bin_sid_to_smb_sid;
+ sss_idmap_smb_sid_to_bin_sid;
+
+ # everything else is local
+ local:
+ *;
+};
+
+SSS_IDMAP_0.5 {
+
+ # public functions
+ global:
+
+ sss_idmap_ctx_set_extra_slice_init;
+ sss_idmap_add_auto_domain_ex;
+
+} SSS_IDMAP_0.4;
diff --git a/src/lib/idmap/sss_idmap.h b/src/lib/idmap/sss_idmap.h
new file mode 100644
index 0000000..9c27a16
--- /dev/null
+++ b/src/lib/idmap/sss_idmap.h
@@ -0,0 +1,962 @@
+/*
+ SSSD
+
+ ID-mapping library
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SSS_IDMAP_H_
+#define SSS_IDMAP_H_
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#define DOM_SID_PREFIX "S-1-5-21-"
+#define DOM_SID_PREFIX_LEN (sizeof(DOM_SID_PREFIX) - 1)
+
+/**
+ * @defgroup sss_idmap Map Unix UIDs and GIDs to SIDs and back
+ * Libsss_idmap provides a mechanism to translate a SID to a UNIX UID or GID
+ * or the other way round.
+ * @{
+ */
+
+/**
+ * Error codes used by libsss_idmap
+ */
+enum idmap_error_code {
+ /** Success */
+ IDMAP_SUCCESS = 0,
+
+ /** Function is not yet implemented */
+ IDMAP_NOT_IMPLEMENTED,
+
+ /** General error */
+ IDMAP_ERROR,
+
+ /** Ran out of memory during processing */
+ IDMAP_OUT_OF_MEMORY,
+
+ /** No domain added */
+ IDMAP_NO_DOMAIN,
+
+ /** The provided idmap context is invalid */
+ IDMAP_CONTEXT_INVALID,
+
+ /** The provided SID is invalid */
+ IDMAP_SID_INVALID,
+
+ /** The provided SID was not found */
+ IDMAP_SID_UNKNOWN,
+
+ /** The provided UID or GID could not be mapped */
+ IDMAP_NO_RANGE,
+
+ /** The provided SID is a built-in one */
+ IDMAP_BUILTIN_SID,
+
+ /** No more free slices */
+ IDMAP_OUT_OF_SLICES,
+
+ /** New domain collides with existing one */
+ IDMAP_COLLISION,
+
+ /** External source should be consulted for idmapping */
+ IDMAP_EXTERNAL,
+
+ /** The provided name was not found */
+ IDMAP_NAME_UNKNOWN,
+
+ /** Sentinel to indicate the end of the error code list, not returned by
+ * any call */
+ IDMAP_ERR_LAST
+};
+
+/**
+ * Typedef for memory allocation functions
+ */
+typedef void *(idmap_alloc_func)(size_t size, void *pvt);
+typedef void (idmap_free_func)(void *ptr, void *pvt);
+
+/**
+ * Typedef for storing mappings of dynamically created domains
+ */
+typedef enum idmap_error_code (*idmap_store_cb)(const char *dom_name,
+ const char *dom_sid,
+ const char *range_id,
+ uint32_t min_id,
+ uint32_t max_id,
+ uint32_t first_rid,
+ void *pvt);
+
+/**
+ * Structure for id ranges
+ * FIXME: this struct might change when it is clear how ranges are handled on
+ * the server side
+ */
+struct sss_idmap_range {
+ uint32_t min;
+ uint32_t max;
+};
+
+/**
+ * Opaque type for SIDs
+ */
+struct sss_dom_sid;
+
+/**
+ * Opaque type for the idmap context
+ */
+struct sss_idmap_ctx;
+
+/**
+ * Placeholder for Samba's struct dom_sid. Consumers of libsss_idmap should
+ * include an appropriate Samba header file to define struct dom_sid. We use
+ * it here to avoid a hard dependency on Samba devel packages.
+ */
+struct dom_sid;
+
+/**
+ * @brief Initialize idmap context
+ *
+ * @param[in] alloc_func Function to allocate memory for the context, if
+ * NULL malloc() id used
+ * @param[in] alloc_pvt Private data for allocation routine
+ * @param[in] free_func Function to free the memory the context, if
+ * NULL free() id used
+ * @param[out] ctx idmap context
+ *
+ * @return
+ * - #IDMAP_OUT_OF_MEMORY: Insufficient memory to create the context
+ */
+enum idmap_error_code sss_idmap_init(idmap_alloc_func *alloc_func,
+ void *alloc_pvt,
+ idmap_free_func *free_func,
+ struct sss_idmap_ctx **ctx);
+
+/**
+ * @brief Set/unset autorid compatibility mode
+ *
+ * @param[in] ctx idmap context
+ * @param[in] use_autorid If true, autorid compatibility mode will be used
+ */
+enum idmap_error_code
+sss_idmap_ctx_set_autorid(struct sss_idmap_ctx *ctx, bool use_autorid);
+
+/**
+ * @brief Set the lower bound of the range of POSIX IDs
+ *
+ * @param[in] ctx idmap context
+ * @param[in] lower lower bound of the range
+ */
+enum idmap_error_code
+sss_idmap_ctx_set_lower(struct sss_idmap_ctx *ctx, id_t lower);
+
+/**
+ * @brief Set the upper bound of the range of POSIX IDs
+ *
+ * @param[in] ctx idmap context
+ * @param[in] upper upper bound of the range
+ */
+enum idmap_error_code
+sss_idmap_ctx_set_upper(struct sss_idmap_ctx *ctx, id_t upper);
+
+/**
+ * @brief Set the range size of POSIX IDs available for single domain
+ *
+ * @param[in] ctx idmap context
+ * @param[in] rangesize range size of IDs
+ */
+enum idmap_error_code
+sss_idmap_ctx_set_rangesize(struct sss_idmap_ctx *ctx, id_t rangesize);
+
+/**
+ * @brief Set the number of secondary slices available for domain
+ *
+ * @param[in] ctx idmap context
+ * @param[in] extra_slice_init number of secondary slices to be generated
+ * at startup
+ */
+enum idmap_error_code
+sss_idmap_ctx_set_extra_slice_init(struct sss_idmap_ctx *ctx,
+ int extra_slice_init);
+
+/**
+ * @brief Check if autorid compatibility mode is set
+ *
+ * @param[in] ctx idmap context
+ * @param[out] _autorid true if autorid is used
+ */
+enum idmap_error_code
+sss_idmap_ctx_get_autorid(struct sss_idmap_ctx *ctx, bool *_autorid);
+
+/**
+ * @brief Get the lower bound of the range of POSIX IDs
+ *
+ * @param[in] ctx idmap context
+ * @param[out] _lower returned lower bound
+ */
+enum idmap_error_code
+sss_idmap_ctx_get_lower(struct sss_idmap_ctx *ctx, id_t *_lower);
+
+/**
+ * @brief Get the upper bound of the range of POSIX IDs
+ *
+ * @param[in] ctx idmap context
+ * @param[out] _upper returned upper bound
+ */
+enum idmap_error_code
+sss_idmap_ctx_get_upper(struct sss_idmap_ctx *ctx, id_t *_upper);
+
+/**
+ * @brief Get the range size of POSIX IDs available for single domain
+ *
+ * @param[in] ctx idmap context
+ * @param[out] rangesize returned range size
+ */
+enum idmap_error_code
+sss_idmap_ctx_get_rangesize(struct sss_idmap_ctx *ctx, id_t *rangesize);
+
+/**
+ * @brief Calculate new range of available POSIX IDs
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] dom_sid Zero-terminated string representation of the domain
+ * SID (S-1-15-.....)
+ * @param[in,out] slice_num Slice number to be used. Set this pointer to NULL or
+ * the addressed value to -1 to calculate slice number
+ * automatically. The calculated value will be
+ * returned in this parameter.
+ * @param[out] range Structure containing upper and lower bound of the
+ * range of POSIX IDs
+ *
+ * @return
+ * - #IDMAP_OUT_OF_SLICES: Cannot calculate new range because all slices are
+ * used.
+ */
+enum idmap_error_code sss_idmap_calculate_range(struct sss_idmap_ctx *ctx,
+ const char *dom_sid,
+ id_t *slice_num,
+ struct sss_idmap_range *range);
+
+/**
+ * @brief Add a domain to the idmap context
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] domain_name Zero-terminated string with the domain name
+ * @param[in] domain_sid Zero-terminated string representation of the domain
+ * SID (S-1-15-.....)
+ * @param[in] range TBD Some information about the id ranges of this
+ * domain
+ *
+ * @return
+ * - #IDMAP_OUT_OF_MEMORY: Insufficient memory to store the data in the idmap
+ * context
+ * - #IDMAP_SID_INVALID: Invalid SID provided
+ * - #IDMAP_NO_DOMAIN: No domain domain name given
+ * - #IDMAP_COLLISION: New domain collides with existing one
+ */
+enum idmap_error_code sss_idmap_add_domain(struct sss_idmap_ctx *ctx,
+ const char *domain_name,
+ const char *domain_sid,
+ struct sss_idmap_range *range);
+
+/**
+ * @brief Add a domain with the first mappable RID to the idmap context
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] domain_name Zero-terminated string with the domain name
+ * @param[in] domain_sid Zero-terminated string representation of the domain
+ * SID (S-1-15-.....)
+ * @param[in] range TBD Some information about the id ranges of this
+ * domain
+ * @param[in] range_id optional unique identifier of a range, it is needed
+ * to allow updates at runtime
+ * @param[in] rid The RID that should be mapped to the first ID of the
+ * given range.
+ * @param[in] external_mapping If set to true the ID will not be mapped
+ * algorithmically, but the *_to_unix and *_unix_to_*
+ * calls will return IDMAP_EXTERNAL to instruct the
+ * caller to check external sources. For a single
+ * domain all ranges must be of the same type. It is
+ * not possible to mix algorithmic and external
+ * mapping.
+ *
+ * @return
+ * - #IDMAP_OUT_OF_MEMORY: Insufficient memory to store the data in the idmap
+ * context
+ * - #IDMAP_SID_INVALID: Invalid SID provided
+ * - #IDMAP_NO_DOMAIN: No domain domain name given
+ * - #IDMAP_COLLISION: New domain collides with existing one
+ */
+enum idmap_error_code sss_idmap_add_domain_ex(struct sss_idmap_ctx *ctx,
+ const char *domain_name,
+ const char *domain_sid,
+ struct sss_idmap_range *range,
+ const char *range_id,
+ uint32_t rid,
+ bool external_mapping);
+
+/**
+ * @brief Add a domain with the first mappable RID to the idmap context and
+ * generate automatically secondary slices
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] domain_name Zero-terminated string with the domain name
+ * @param[in] domain_sid Zero-terminated string representation of the domain
+ * SID (S-1-15-.....)
+ * @param[in] range TBD Some information about the id ranges of this
+ * domain
+ * @param[in] range_id optional unique identifier of a range, it is needed
+ * to allow updates at runtime
+ * @param[in] rid The RID that should be mapped to the first ID of the
+ * given range.
+ * @param[in] external_mapping If set to true the ID will not be mapped
+ * algorithmically, but the *_to_unix and *_unix_to_*
+ * calls will return IDMAP_EXTERNAL to instruct the
+ * caller to check external sources. For a single
+ * domain all ranges must be of the same type. It is
+ * not possible to mix algorithmic and external
+ * mapping.
+ * @param[in] cb The callback for storing mapping of dynamically
+ * created domains.
+ * @param[in] pvt Private data for callback cb.
+ *
+ * @return
+ * - #IDMAP_OUT_OF_MEMORY: Insufficient memory to store the data in the idmap
+ * context
+ * - #IDMAP_SID_INVALID: Invalid SID provided
+ * - #IDMAP_NO_DOMAIN: No domain domain name given
+ * - #IDMAP_COLLISION: New domain collides with existing one
+ */
+enum idmap_error_code
+sss_idmap_add_auto_domain_ex(struct sss_idmap_ctx *ctx,
+ const char *domain_name,
+ const char *domain_sid,
+ struct sss_idmap_range *range,
+ const char *range_id,
+ uint32_t rid,
+ bool external_mapping,
+ idmap_store_cb cb,
+ void *pvt);
+
+/**
+ * @brief Check if a new range would collide with any existing one
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] n_name Zero-terminated string with the domain name the new
+ * range should belong to
+ * @param[in] n_sid Zero-terminated string representation of the domain
+ * SID (S-1-15-.....) the new range sould belong to
+ * @param[in] n_range The new id range
+ * @param[in] n_range_id unique identifier of the new range, it is needed
+ * to allow updates at runtime, may be NULL
+ * @param[in] n_first_rid The RID that should be mapped to the first ID of the
+ * new range.
+ * @param[in] n_external_mapping Mapping type of the new range
+ *
+ * @return
+ * - #IDMAP_COLLISION: New range collides with existing one
+ */
+enum idmap_error_code sss_idmap_check_collision(struct sss_idmap_ctx *ctx,
+ char *n_name, char *n_sid,
+ struct sss_idmap_range *n_range,
+ uint32_t n_first_rid,
+ char *n_range_id,
+ bool n_external_mapping);
+
+/**
+ * @brief Check if two ranges would collide
+ *
+ * @param[in] o_name Zero-terminated string with the domain name the
+ * first range should belong to
+ * @param[in] o_sid Zero-terminated string representation of the domain
+ * SID (S-1-15-.....) the first range sould belong to
+ * @param[in] o_range The first id range
+ * @param[in] o_range_id unique identifier of the first range, it is needed
+ * to allow updates at runtime, may be NULL
+ * @param[in] o_first_rid The RID that should be mapped to the first ID of the
+ * first range.
+ * @param[in] o_external_mapping Mapping type of the first range
+ * @param[in] n_name Zero-terminated string with the domain name the
+ * second range should belong to
+ * @param[in] n_sid Zero-terminated string representation of the domain
+ * SID (S-1-15-.....) the second range sould belong to
+ * @param[in] n_range The second id range
+ * @param[in] n_range_id unique identifier of the second range, it is needed
+ * to allow updates at runtime, may be NULL
+ * @param[in] n_first_rid The RID that should be mapped to the first ID of the
+ * second range.
+ * @param[in] n_external_mapping Mapping type of the second range
+ *
+ * @return
+ * - #IDMAP_COLLISION: New range collides with existing one
+ */
+enum idmap_error_code sss_idmap_check_collision_ex(const char *o_name,
+ const char *o_sid,
+ struct sss_idmap_range *o_range,
+ uint32_t o_first_rid,
+ const char *o_range_id,
+ bool o_external_mapping,
+ const char *n_name,
+ const char *n_sid,
+ struct sss_idmap_range *n_range,
+ uint32_t n_first_rid,
+ const char *n_range_id,
+ bool n_external_mapping);
+/**
+ * @brief Translate SID to a unix UID or GID
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] sid Zero-terminated string representation of the SID
+ * @param[out] id Returned unix UID or GID
+ *
+ * @return
+ * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context
+ * - #IDMAP_SID_INVALID: Invalid SID provided
+ * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the
+ * idmap context
+ * - #IDMAP_EXTERNAL: external source is authoritative for mapping
+ */
+enum idmap_error_code sss_idmap_sid_to_unix(struct sss_idmap_ctx *ctx,
+ const char *sid,
+ uint32_t *id);
+
+/**
+ * @brief Translate a SID stucture to a unix UID or GID
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] dom_sid SID structure
+ * @param[out] id Returned unix UID or GID
+ *
+ * @return
+ * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context
+ * - #IDMAP_SID_INVALID: Invalid SID provided
+ * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the
+ * idmap context
+ * - #IDMAP_EXTERNAL: external source is authoritative for mapping
+ */
+enum idmap_error_code sss_idmap_dom_sid_to_unix(struct sss_idmap_ctx *ctx,
+ struct sss_dom_sid *dom_sid,
+ uint32_t *id);
+
+/**
+ * @brief Translate a binary SID to a unix UID or GID
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] bin_sid Array with the binary SID
+ * @param[in] length Size of the array containing the binary SID
+ * @param[out] id Returned unix UID or GID
+ *
+ * @return
+ * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context
+ * - #IDMAP_SID_INVALID: Invalid SID provided
+ * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the
+ * idmap context
+ * - #IDMAP_EXTERNAL: external source is authoritative for mapping
+ */
+enum idmap_error_code sss_idmap_bin_sid_to_unix(struct sss_idmap_ctx *ctx,
+ uint8_t *bin_sid,
+ size_t length,
+ uint32_t *id);
+
+/**
+ * @brief Translate a Samba dom_sid stucture to a unix UID or GID
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] smb_sid Samba dom_sid structure
+ * @param[out] id Returned unix UID or GID
+ *
+ * @return
+ * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context
+ * - #IDMAP_SID_INVALID: Invalid SID provided
+ * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the
+ * idmap context
+ * - #IDMAP_EXTERNAL: external source is authoritative for mapping
+ */
+enum idmap_error_code sss_idmap_smb_sid_to_unix(struct sss_idmap_ctx *ctx,
+ struct dom_sid *smb_sid,
+ uint32_t *id);
+
+/**
+ * @brief Check if a SID and a unix UID or GID belong to the same range
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] sid Zero-terminated string representation of the SID
+ * @param[in] id Unix UID or GID
+ *
+ * @return
+ * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context
+ * - #IDMAP_SID_INVALID: Invalid SID provided
+ * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the
+ * idmap context
+ * - #IDMAP_NO_RANGE No matching ID range found
+ */
+enum idmap_error_code sss_idmap_check_sid_unix(struct sss_idmap_ctx *ctx,
+ const char *sid,
+ uint32_t id);
+
+/**
+ * @brief Check if a SID structure and a unix UID or GID belong to the same range
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] dom_sid SID structure
+ * @param[in] id Unix UID or GID
+ *
+ * @return
+ * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context
+ * - #IDMAP_SID_INVALID: Invalid SID provided
+ * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the
+ * idmap context
+ * - #IDMAP_NO_RANGE No matching ID range found
+ */
+enum idmap_error_code sss_idmap_check_dom_sid_unix(struct sss_idmap_ctx *ctx,
+ struct sss_dom_sid *dom_sid,
+ uint32_t id);
+
+/**
+ * @brief Check if a binary SID and a unix UID or GID belong to the same range
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] bin_sid Array with the binary SID
+ * @param[in] length Size of the array containing the binary SID
+ * @param[in] id Unix UID or GID
+ *
+ * @return
+ * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context
+ * - #IDMAP_SID_INVALID: Invalid SID provided
+ * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the
+ * idmap context
+ * - #IDMAP_NO_RANGE No matching ID range found
+ */
+enum idmap_error_code sss_idmap_check_bin_sid_unix(struct sss_idmap_ctx *ctx,
+ uint8_t *bin_sid,
+ size_t length,
+ uint32_t id);
+
+/**
+ * @brief Check if a Samba dom_sid structure and a unix UID or GID belong to
+ * the same range
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] smb_sid Samba dom_sid structure
+ * @param[in] id Unix UID or GID
+ *
+ * @return
+ * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context
+ * - #IDMAP_SID_INVALID: Invalid SID provided
+ * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the
+ * idmap context
+ * - #IDMAP_NO_RANGE No matching ID range found
+ */
+enum idmap_error_code sss_idmap_check_smb_sid_unix(struct sss_idmap_ctx *ctx,
+ struct dom_sid *smb_sid,
+ uint32_t id);
+
+/**
+ * @brief Translate unix UID or GID to a SID
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] id unix UID or GID
+ * @param[out] sid Zero-terminated string representation of the SID, must be
+ * freed if not needed anymore
+ *
+ * @return
+ * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context
+ * - #IDMAP_NO_RANGE: The provided ID cannot be found in the domains added
+ * to the idmap context
+ * - #IDMAP_EXTERNAL: external source is authoritative for mapping
+ */
+enum idmap_error_code sss_idmap_unix_to_sid(struct sss_idmap_ctx *ctx,
+ uint32_t id,
+ char **sid);
+
+/**
+ * @brief Translate unix UID or GID to a SID structure
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] id unix UID or GID
+ * @param[out] dom_sid SID structure, must be freed if not needed anymore
+ *
+ * @return
+ * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context
+ * - #IDMAP_NO_RANGE: The provided ID cannot be found in the domains added
+ * to the idmap context
+ * - #IDMAP_EXTERNAL: external source is authoritative for mapping
+ */
+enum idmap_error_code sss_idmap_unix_to_dom_sid(struct sss_idmap_ctx *ctx,
+ uint32_t id,
+ struct sss_dom_sid **dom_sid);
+
+/**
+ * @brief Translate unix UID or GID to a binary SID
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] id unix UID or GID
+ * @param[out] bin_sid Array with the binary SID,
+ * must be freed if not needed anymore
+ * @param[out] length size of the array containing the binary SID
+ *
+ * @return
+ * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context
+ * - #IDMAP_NO_RANGE: The provided ID cannot be found in the domains added
+ * to the idmap context
+ * - #IDMAP_EXTERNAL: external source is authoritative for mapping
+ */
+enum idmap_error_code sss_idmap_unix_to_bin_sid(struct sss_idmap_ctx *ctx,
+ uint32_t id,
+ uint8_t **bin_sid,
+ size_t *length);
+
+/**
+ * @brief Free all the allocated memory of the idmap context
+ *
+ * @param[in] ctx Idmap context
+ *
+ * @return
+ * - #IDMAP_CONTEXT_INVALID: Provided context is invalid
+ */
+enum idmap_error_code sss_idmap_free(struct sss_idmap_ctx *ctx);
+
+/**
+ * @brief Free mapped SID.
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] sid SID to be freed.
+ *
+ * @return
+ * - #IDMAP_CONTEXT_INVALID: Provided context is invalid
+ */
+enum idmap_error_code sss_idmap_free_sid(struct sss_idmap_ctx *ctx,
+ char *sid);
+
+/**
+ * @brief Free mapped domain SID.
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] dom_sid Domain SID to be freed.
+ *
+ * @return
+ * - #IDMAP_CONTEXT_INVALID: Provided context is invalid
+ */
+enum idmap_error_code sss_idmap_free_dom_sid(struct sss_idmap_ctx *ctx,
+ struct sss_dom_sid *dom_sid);
+
+/**
+ * @brief Free mapped Samba SID.
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] smb_sid Samba SID to be freed.
+ *
+ * @return
+ * - #IDMAP_CONTEXT_INVALID: Provided context is invalid
+ */
+enum idmap_error_code sss_idmap_free_smb_sid(struct sss_idmap_ctx *ctx,
+ struct dom_sid *smb_sid);
+
+/**
+ * @brief Free mapped binary SID.
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] bin_sid Binary SID to be freed.
+ *
+ * @return
+ * - #IDMAP_CONTEXT_INVALID: Provided context is invalid
+ */
+enum idmap_error_code sss_idmap_free_bin_sid(struct sss_idmap_ctx *ctx,
+ uint8_t *bin_sid);
+
+/**
+ * @brief Translate error code to a string
+ *
+ * @param[in] err Idmap error code
+ *
+ * @return
+ * - Error description as a zero-terminated string
+ */
+const char *idmap_error_string(enum idmap_error_code err);
+
+/**
+ * @brief Check if given string can be used as domain SID
+ *
+ * @param[in] str String to check
+ *
+ * @return
+ * - true: String can be used as domain SID
+ * - false: String can not be used as domain SID
+ */
+bool is_domain_sid(const char *str);
+
+/**
+ * @brief Check if a domain is configured with algorithmic mapping
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] dom_sid SID string, can be either a domain SID
+ * or an object SID
+ * @param[out] has_algorithmic_mapping Boolean value indicating if the given
+ * domain is configured for algorithmic
+ * mapping or not.
+ *
+ * @return
+ * - #IDMAP_SUCCESS: Domain for the given SID was found and
+ * has_algorithmic_mapping is set accordingly
+ * - #IDMAP_SID_INVALID: Provided SID is invalid
+ * - #IDMAP_CONTEXT_INVALID: Provided idmap context is invalid
+ * - #IDMAP_NO_DOMAIN: No domains are available in the idmap context
+ * - #IDMAP_SID_UNKNOWN: No domain with the given SID was found in the
+ * idmap context
+ */
+enum idmap_error_code
+sss_idmap_domain_has_algorithmic_mapping(struct sss_idmap_ctx *ctx,
+ const char *dom_sid,
+ bool *has_algorithmic_mapping);
+
+/**
+ * @brief Check if a domain is configured with algorithmic mapping
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] dom_name Name of the domain
+ * @param[out] has_algorithmic_mapping Boolean value indicating if the given
+ * domain is configured for algorithmic
+ * mapping or not.
+ *
+ * @return
+ * - #IDMAP_SUCCESS: Domain for the given name was found and
+ * has_algorithmic_mapping is set accordingly
+ * - #IDMAP_ERROR: Provided name is invalid
+ * - #IDMAP_CONTEXT_INVALID: Provided idmap context is invalid
+ * - #IDMAP_NO_DOMAIN: No domains are available in the idmap context
+ * - #IDMAP_NAME_UNKNOWN: No domain with the given name was found in the
+ * idmap context
+ */
+enum idmap_error_code
+sss_idmap_domain_by_name_has_algorithmic_mapping(struct sss_idmap_ctx *ctx,
+ const char *dom_name,
+ bool *has_algorithmic_mapping);
+
+/**
+ * @brief Convert binary SID to SID structure
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] bin_sid Array with the binary SID
+ * @param[in] length Size of the array containing the binary SID
+ * @param[out] dom_sid SID structure,
+ * must be freed if not needed anymore
+ *
+ * @return
+ * - #IDMAP_SID_INVALID: Given SID is invalid
+ * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result
+ */
+enum idmap_error_code sss_idmap_bin_sid_to_dom_sid(struct sss_idmap_ctx *ctx,
+ const uint8_t *bin_sid,
+ size_t length,
+ struct sss_dom_sid **dom_sid);
+
+/**
+ * @brief Convert binary SID to SID string
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] bin_sid Array with the binary SID
+ * @param[in] length Size of the array containing the binary SID
+ * @param[out] sid Zero-terminated string representation of the SID,
+ * must be freed if not needed anymore
+ *
+ * @return
+ * - #IDMAP_SID_INVALID: Given SID is invalid
+ * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result
+ */
+enum idmap_error_code sss_idmap_bin_sid_to_sid(struct sss_idmap_ctx *ctx,
+ const uint8_t *bin_sid,
+ size_t length,
+ char **sid);
+
+/**
+ * @brief Convert SID structure to binary SID
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] dom_sid SID structure
+ * @param[out] bin_sid Array with the binary SID,
+ * must be freed if not needed anymore
+ * @param[out] length Size of the array containing the binary SID
+ *
+ * @return
+ * - #IDMAP_SID_INVALID: Given SID is invalid
+ * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result
+ */
+enum idmap_error_code sss_idmap_dom_sid_to_bin_sid(struct sss_idmap_ctx *ctx,
+ struct sss_dom_sid *dom_sid,
+ uint8_t **bin_sid,
+ size_t *length);
+
+/**
+ * @brief Convert SID string to binary SID
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] sid Zero-terminated string representation of the SID
+ * @param[out] bin_sid Array with the binary SID,
+ * must be freed if not needed anymore
+ * @param[out] length Size of the array containing the binary SID
+ *
+ * @return
+ * - #IDMAP_SID_INVALID: Given SID is invalid
+ * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result
+ */
+enum idmap_error_code sss_idmap_sid_to_bin_sid(struct sss_idmap_ctx *ctx,
+ const char *sid,
+ uint8_t **bin_sid,
+ size_t *length);
+
+/**
+ * @brief Convert SID structure to SID string
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] dom_sid SID structure
+ * @param[out] sid Zero-terminated string representation of the SID,
+ * must be freed if not needed anymore
+ *
+ * @return
+ * - #IDMAP_SID_INVALID: Given SID is invalid
+ * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result
+ */
+enum idmap_error_code sss_idmap_dom_sid_to_sid(struct sss_idmap_ctx *ctx,
+ struct sss_dom_sid *dom_sid,
+ char **sid);
+
+/**
+ * @brief Convert SID string to SID structure
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] sid Zero-terminated string representation of the SID
+ * @param[out] dom_sid SID structure,
+ * must be freed if not needed anymore
+ *
+ * @return
+ * - #IDMAP_SID_INVALID: Given SID is invalid
+ * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result
+ */
+enum idmap_error_code sss_idmap_sid_to_dom_sid(struct sss_idmap_ctx *ctx,
+ const char *sid,
+ struct sss_dom_sid **dom_sid);
+
+/**
+ * @brief Convert SID string to Samba dom_sid structure
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] sid Zero-terminated string representation of the SID
+ * @param[out] smb_sid Samba dom_sid structure,
+ * must be freed if not needed anymore
+ *
+ * @return
+ * - #IDMAP_SID_INVALID: Given SID is invalid
+ * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result
+ */
+enum idmap_error_code sss_idmap_sid_to_smb_sid(struct sss_idmap_ctx *ctx,
+ const char *sid,
+ struct dom_sid **smb_sid);
+
+/**
+ * @brief Convert Samba dom_sid structure to SID string
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] smb_sid Samba dom_sid structure
+ * @param[out] sid Zero-terminated string representation of the SID,
+ * must be freed if not needed anymore
+ *
+ * @return
+ * - #IDMAP_SID_INVALID: Given SID is invalid
+ * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result
+ */
+enum idmap_error_code sss_idmap_smb_sid_to_sid(struct sss_idmap_ctx *ctx,
+ struct dom_sid *smb_sid,
+ char **sid);
+
+/**
+ * @brief Convert SID stucture to Samba dom_sid structure
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] dom_sid SID structure
+ * @param[out] smb_sid Samba dom_sid structure,
+ * must be freed if not needed anymore
+ *
+ * @return
+ * - #IDMAP_SID_INVALID: Given SID is invalid
+ * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result
+ */
+enum idmap_error_code sss_idmap_dom_sid_to_smb_sid(struct sss_idmap_ctx *ctx,
+ struct sss_dom_sid *dom_sid,
+ struct dom_sid **smb_sid);
+
+/**
+ * @brief Convert Samba dom_sid structure to SID structure
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] smb_sid Samba dom_sid structure
+ * @param[out] dom_sid SID structure,
+ * must be freed if not needed anymore
+ *
+ * @return
+ * - #IDMAP_SID_INVALID: Given SID is invalid
+ * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result
+ */
+enum idmap_error_code sss_idmap_smb_sid_to_dom_sid(struct sss_idmap_ctx *ctx,
+ struct dom_sid *smb_sid,
+ struct sss_dom_sid **dom_sid);
+
+/**
+ * @brief Convert binary SID to Samba dom_sid structure
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] bin_sid Array with the binary SID
+ * @param[in] length Size of the array containing the binary SID
+ * @param[out] smb_sid Samba dom_sid structure,
+ * must be freed if not needed anymore
+ *
+ * @return
+ * - #IDMAP_SID_INVALID: Given SID is invalid
+ * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result
+ */
+enum idmap_error_code sss_idmap_bin_sid_to_smb_sid(struct sss_idmap_ctx *ctx,
+ const uint8_t *bin_sid,
+ size_t length,
+ struct dom_sid **smb_sid);
+
+/**
+ * @brief Convert Samba dom_sid structure to binary SID
+ *
+ * @param[in] ctx Idmap context
+ * @param[in] smb_sid Samba dom_sid structure
+ * @param[out] bin_sid Array with the binary SID,
+ * must be freed if not needed anymore
+ * @param[out] length Size of the array containing the binary SID
+ *
+ * @return
+ * - #IDMAP_SID_INVALID: Given SID is invalid
+ * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result
+ */
+enum idmap_error_code sss_idmap_smb_sid_to_bin_sid(struct sss_idmap_ctx *ctx,
+ struct dom_sid *smb_sid,
+ uint8_t **bin_sid,
+ size_t *length);
+/**
+ * @}
+ */
+#endif /* SSS_IDMAP_H_ */
diff --git a/src/lib/idmap/sss_idmap.pc.in b/src/lib/idmap/sss_idmap.pc.in
new file mode 100644
index 0000000..eda1f64
--- /dev/null
+++ b/src/lib/idmap/sss_idmap.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: sss_idmap
+Description: SSS idmap (SID <-> uid,gid) library
+Version: @VERSION@
+Libs: -L${libdir} -lsss_idmap
+Cflags:
+URL: https://github.com/SSSD/sssd/
diff --git a/src/lib/idmap/sss_idmap_conv.c b/src/lib/idmap/sss_idmap_conv.c
new file mode 100644
index 0000000..fd06b23
--- /dev/null
+++ b/src/lib/idmap/sss_idmap_conv.c
@@ -0,0 +1,569 @@
+/*
+ SSSD
+
+ ID-mapping library - conversion utilities
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "lib/idmap/sss_idmap.h"
+#include "lib/idmap/sss_idmap_private.h"
+#include "util/util.h"
+#include "util/sss_endian.h"
+
+#define SID_ID_AUTHS 6
+#define SID_SUB_AUTHS 15
+struct sss_dom_sid {
+ uint8_t sid_rev_num;
+ int8_t num_auths; /* [range(0,15)] */
+ uint8_t id_auth[SID_ID_AUTHS]; /* highest order byte has index 0 */
+ uint32_t sub_auths[SID_SUB_AUTHS]; /* host byte-order */
+};
+
+enum idmap_error_code sss_idmap_bin_sid_to_dom_sid(struct sss_idmap_ctx *ctx,
+ const uint8_t *bin_sid,
+ size_t length,
+ struct sss_dom_sid **_dom_sid)
+{
+ enum idmap_error_code err;
+ struct sss_dom_sid *dom_sid;
+ size_t i = 0;
+ size_t p = 0;
+ uint32_t val;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ if (length > sizeof(struct sss_dom_sid)) return IDMAP_SID_INVALID;
+
+ dom_sid = ctx->alloc_func(sizeof(struct sss_dom_sid), ctx->alloc_pvt);
+ if (dom_sid == NULL) {
+ return IDMAP_OUT_OF_MEMORY;
+ }
+ memset(dom_sid, 0, sizeof(struct sss_dom_sid));
+
+ /* Safely copy in the SID revision number */
+ dom_sid->sid_rev_num = (uint8_t) *(bin_sid + p);
+ p++;
+
+ /* Safely copy in the number of sub auth values */
+ dom_sid->num_auths = (uint8_t) *(bin_sid + p);
+ p++;
+
+ /* Make sure we aren't being told to read more bin_sid
+ * than can fit in the structure
+ */
+ if (dom_sid->num_auths > SID_SUB_AUTHS) {
+ err = IDMAP_SID_INVALID;
+ goto done;
+ }
+
+ /* Safely copy in the id_auth values */
+ for (i = 0; i < SID_ID_AUTHS; i++) {
+ dom_sid->id_auth[i] = (uint8_t) *(bin_sid + p);
+ p++;
+ }
+
+ /* Safely copy in the sub_auths values */
+ for (i = 0; i < dom_sid->num_auths; i++) {
+ /* SID sub auth values in Active Directory are stored little-endian,
+ * we store them in host order */
+ SAFEALIGN_COPY_UINT32(&val, bin_sid + p, &p);
+ dom_sid->sub_auths[i] = le32toh(val);
+ }
+
+ *_dom_sid = dom_sid;
+ err = IDMAP_SUCCESS;
+
+done:
+ if (err != IDMAP_SUCCESS) {
+ ctx->free_func(dom_sid, ctx->alloc_pvt);
+ }
+ return err;
+}
+
+enum idmap_error_code sss_idmap_dom_sid_to_bin_sid(struct sss_idmap_ctx *ctx,
+ struct sss_dom_sid *dom_sid,
+ uint8_t **_bin_sid,
+ size_t *_length)
+{
+ enum idmap_error_code err;
+ uint8_t *bin_sid;
+ size_t length;
+ size_t i = 0;
+ size_t p = 0;
+ uint32_t val;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ if (dom_sid->num_auths > SID_SUB_AUTHS) {
+ return IDMAP_SID_INVALID;
+ }
+
+ length = 2 + SID_ID_AUTHS + dom_sid->num_auths * 4;
+
+ bin_sid = ctx->alloc_func(length, ctx->alloc_pvt);
+ if (bin_sid == NULL) {
+ return IDMAP_OUT_OF_MEMORY;
+ }
+
+ bin_sid[p] = dom_sid->sid_rev_num;
+ p++;
+
+ bin_sid[p] = dom_sid->num_auths;
+ p++;
+
+ for (i = 0; i < SID_ID_AUTHS; i++) {
+ bin_sid[p] = dom_sid->id_auth[i];
+ p++;
+ }
+
+ for (i = 0; i < dom_sid->num_auths; i++) {
+ if (p + sizeof(uint32_t) > length) {
+ err = IDMAP_SID_INVALID;
+ goto done;
+ }
+ val = htole32(dom_sid->sub_auths[i]);
+ SAFEALIGN_COPY_UINT32(bin_sid + p, &val, &p);
+ }
+
+ *_bin_sid = bin_sid;
+ *_length = length;
+
+ err = IDMAP_SUCCESS;
+done:
+ if (err != IDMAP_SUCCESS) {
+ ctx->free_func(bin_sid, ctx->alloc_pvt);
+ }
+ return err;
+}
+
+enum idmap_error_code sss_idmap_dom_sid_to_sid(struct sss_idmap_ctx *ctx,
+ struct sss_dom_sid *dom_sid,
+ char **_sid)
+{
+ enum idmap_error_code err;
+ char *sid_buf;
+ size_t sid_buf_len;
+ char *p;
+ int nc;
+ int8_t i;
+ uint32_t id_auth_val = 0;
+
+ if (dom_sid->num_auths > SID_SUB_AUTHS) {
+ return IDMAP_SID_INVALID;
+ }
+
+ sid_buf_len = 25 + dom_sid->num_auths * 11;
+ sid_buf = ctx->alloc_func(sid_buf_len, ctx->alloc_pvt);
+ if (sid_buf == NULL) {
+ return IDMAP_OUT_OF_MEMORY;
+ }
+ memset(sid_buf, 0, sid_buf_len);
+
+ /* Only 32bits are used for the string representation */
+ id_auth_val = (dom_sid->id_auth[2] << 24) +
+ (dom_sid->id_auth[3] << 16) +
+ (dom_sid->id_auth[4] << 8) +
+ (dom_sid->id_auth[5]);
+
+ nc = snprintf(sid_buf, sid_buf_len, "S-%u-%lu", dom_sid->sid_rev_num,
+ (unsigned long) id_auth_val);
+ if (nc < 0 || nc >= sid_buf_len) {
+ err = IDMAP_SID_INVALID;
+ goto done;
+ }
+
+
+ /* Loop through the sub-auths, if any, prepending a hyphen
+ * for each one.
+ */
+ p = sid_buf;
+ for (i = 0; i < dom_sid->num_auths ; i++) {
+ p += nc;
+ sid_buf_len -= nc;
+
+ nc = snprintf(p, sid_buf_len, "-%lu",
+ (unsigned long) dom_sid->sub_auths[i]);
+ if (nc < 0 || nc >= sid_buf_len) {
+ err = IDMAP_SID_INVALID;
+ goto done;
+ }
+ }
+
+ *_sid = sid_buf;
+ err = IDMAP_SUCCESS;
+
+done:
+ if (err != IDMAP_SUCCESS) {
+ ctx->free_func(sid_buf, ctx->alloc_pvt);
+ }
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_sid_to_dom_sid(struct sss_idmap_ctx *ctx,
+ const char *sid,
+ struct sss_dom_sid **_dom_sid)
+{
+ enum idmap_error_code err;
+ unsigned long ul;
+ char *r;
+ char *end;
+ struct sss_dom_sid *dom_sid;
+
+ CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
+
+ if (sid == NULL || (sid[0] != 'S' && sid[0] != 's') || sid[1] != '-') {
+ return IDMAP_SID_INVALID;
+ }
+
+ dom_sid = ctx->alloc_func(sizeof(struct sss_dom_sid), ctx->alloc_pvt);
+ if (dom_sid == NULL) {
+ return IDMAP_OUT_OF_MEMORY;
+ }
+ memset(dom_sid, 0, sizeof(struct sss_dom_sid));
+
+
+ if (!isdigit(sid[2])) {
+ err = IDMAP_SID_INVALID;
+ goto done;
+ }
+ errno = 0;
+ ul = strtoul(sid + 2, &r, 10);
+ if (errno != 0 || r == NULL || *r != '-' || ul > UINT8_MAX) {
+ err = IDMAP_SID_INVALID;
+ goto done;
+ }
+ dom_sid->sid_rev_num = (uint8_t) ul;
+ r++;
+
+ if (!isdigit(*r)) {
+ err = IDMAP_SID_INVALID;
+ goto done;
+ }
+ errno = 0;
+ ul = strtoul(r, &r, 10);
+ if (errno != 0 || r == NULL || ul > UINT32_MAX) {
+ err = IDMAP_SID_INVALID;
+ goto done;
+ }
+
+ /* id_auth in the string should always be <2^32 in decimal */
+ /* store values in the same order as the binary representation */
+ dom_sid->id_auth[0] = 0;
+ dom_sid->id_auth[1] = 0;
+ dom_sid->id_auth[2] = (ul & 0xff000000) >> 24;
+ dom_sid->id_auth[3] = (ul & 0x00ff0000) >> 16;
+ dom_sid->id_auth[4] = (ul & 0x0000ff00) >> 8;
+ dom_sid->id_auth[5] = (ul & 0x000000ff);
+
+ if (*r == '\0') {
+ /* no sub auths given */
+ err = IDMAP_SUCCESS;
+ goto done;
+ }
+
+ if (*r != '-') {
+ err = IDMAP_SID_INVALID;
+ goto done;
+ }
+
+ do {
+ if (dom_sid->num_auths >= SID_SUB_AUTHS) {
+ err = IDMAP_SID_INVALID;
+ goto done;
+ }
+
+ r++;
+ if (!isdigit(*r)) {
+ err = IDMAP_SID_INVALID;
+ goto done;
+ }
+
+ errno = 0;
+ ul = strtoul(r, &end, 10);
+ if (errno != 0 || ul > UINT32_MAX || end == NULL ||
+ (*end != '\0' && *end != '-')) {
+ err = IDMAP_SID_INVALID;
+ goto done;
+ }
+
+ dom_sid->sub_auths[dom_sid->num_auths++] = ul;
+
+ r = end;
+ } while (*r != '\0');
+
+ err = IDMAP_SUCCESS;
+
+done:
+ if (err != IDMAP_SUCCESS) {
+ ctx->free_func(dom_sid, ctx->alloc_pvt);
+ } else {
+ *_dom_sid = dom_sid;
+ }
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_sid_to_bin_sid(struct sss_idmap_ctx *ctx,
+ const char *sid,
+ uint8_t **_bin_sid,
+ size_t *_length)
+{
+ enum idmap_error_code err;
+ struct sss_dom_sid *dom_sid = NULL;
+ size_t length;
+ uint8_t *bin_sid = NULL;
+
+ err = sss_idmap_sid_to_dom_sid(ctx, sid, &dom_sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_dom_sid_to_bin_sid(ctx, dom_sid, &bin_sid, &length);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ *_length = length;
+ *_bin_sid = bin_sid;
+ err = IDMAP_SUCCESS;
+
+done:
+ ctx->free_func(dom_sid, ctx->alloc_pvt);
+ if (err != IDMAP_SUCCESS) {
+ ctx->free_func(bin_sid, ctx->alloc_pvt);
+ }
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_bin_sid_to_sid(struct sss_idmap_ctx *ctx,
+ const uint8_t *bin_sid,
+ size_t length,
+ char **_sid)
+{
+ enum idmap_error_code err;
+ struct sss_dom_sid *dom_sid = NULL;
+ char *sid = NULL;
+
+ err = sss_idmap_bin_sid_to_dom_sid(ctx, bin_sid, length, &dom_sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_dom_sid_to_sid(ctx, dom_sid, &sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ *_sid = sid;
+ err = IDMAP_SUCCESS;
+
+done:
+ ctx->free_func(dom_sid, ctx->alloc_pvt);
+ if (err != IDMAP_SUCCESS) {
+ ctx->free_func(sid, ctx->alloc_pvt);
+ }
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_sid_to_smb_sid(struct sss_idmap_ctx *ctx,
+ const char *sid,
+ struct dom_sid **_smb_sid)
+{
+ enum idmap_error_code err;
+ struct sss_dom_sid *dom_sid = NULL;
+ struct dom_sid *smb_sid = NULL;
+
+ err = sss_idmap_sid_to_dom_sid(ctx, sid, &dom_sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_dom_sid_to_smb_sid(ctx, dom_sid, &smb_sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ *_smb_sid = smb_sid;
+ err = IDMAP_SUCCESS;
+
+done:
+ ctx->free_func(dom_sid, ctx->alloc_pvt);
+ if (err != IDMAP_SUCCESS) {
+ ctx->free_func(smb_sid, ctx->alloc_pvt);
+ }
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_smb_sid_to_sid(struct sss_idmap_ctx *ctx,
+ struct dom_sid *smb_sid,
+ char **_sid)
+{
+ enum idmap_error_code err;
+ struct sss_dom_sid *dom_sid = NULL;
+ char *sid = NULL;
+
+ err = sss_idmap_smb_sid_to_dom_sid(ctx, smb_sid, &dom_sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_dom_sid_to_sid(ctx, dom_sid, &sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ *_sid = sid;
+ err = IDMAP_SUCCESS;
+
+done:
+ ctx->free_func(dom_sid, ctx->alloc_pvt);
+ if (err != IDMAP_SUCCESS) {
+ ctx->free_func(sid, ctx->alloc_pvt);
+ }
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_dom_sid_to_smb_sid(struct sss_idmap_ctx *ctx,
+ struct sss_dom_sid *dom_sid,
+ struct dom_sid **_smb_sid)
+{
+ struct dom_sid *smb_sid;
+ size_t c;
+
+ smb_sid = ctx->alloc_func(sizeof(struct dom_sid), ctx->alloc_pvt);
+ if (smb_sid == NULL) {
+ return IDMAP_OUT_OF_MEMORY;
+ }
+ memset(smb_sid, 0, sizeof(struct dom_sid));
+
+ smb_sid->sid_rev_num = dom_sid->sid_rev_num;
+ smb_sid->num_auths = dom_sid->num_auths;
+ for (c = 0; c < SID_ID_AUTHS; c++) {
+ smb_sid->id_auth[c] = dom_sid->id_auth[c];
+ }
+ for (c = 0; c < SID_SUB_AUTHS; c++) {
+ smb_sid->sub_auths[c] = dom_sid->sub_auths[c];
+ }
+
+ *_smb_sid = smb_sid;
+
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code sss_idmap_smb_sid_to_dom_sid(struct sss_idmap_ctx *ctx,
+ struct dom_sid *smb_sid,
+ struct sss_dom_sid **_dom_sid)
+{
+ struct sss_dom_sid *dom_sid;
+ size_t c;
+
+ dom_sid = ctx->alloc_func(sizeof(struct sss_dom_sid), ctx->alloc_pvt);
+ if (dom_sid == NULL) {
+ return IDMAP_OUT_OF_MEMORY;
+ }
+ memset(dom_sid, 0, sizeof(struct sss_dom_sid));
+
+ dom_sid->sid_rev_num = smb_sid->sid_rev_num;
+ dom_sid->num_auths = smb_sid->num_auths;
+ for (c = 0; c < SID_ID_AUTHS; c++) {
+ dom_sid->id_auth[c] = smb_sid->id_auth[c];
+ }
+ for (c = 0; c < SID_SUB_AUTHS; c++) {
+ dom_sid->sub_auths[c] = smb_sid->sub_auths[c];
+ }
+
+ *_dom_sid = dom_sid;
+
+ return IDMAP_SUCCESS;
+}
+
+enum idmap_error_code sss_idmap_bin_sid_to_smb_sid(struct sss_idmap_ctx *ctx,
+ const uint8_t *bin_sid,
+ size_t length,
+ struct dom_sid **_smb_sid)
+{
+ enum idmap_error_code err;
+ struct sss_dom_sid *dom_sid = NULL;
+ struct dom_sid *smb_sid = NULL;
+
+ err = sss_idmap_bin_sid_to_dom_sid(ctx, bin_sid, length, &dom_sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_dom_sid_to_smb_sid(ctx, dom_sid, &smb_sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ *_smb_sid = smb_sid;
+ err = IDMAP_SUCCESS;
+
+done:
+ ctx->free_func(dom_sid, ctx->alloc_pvt);
+ if (err != IDMAP_SUCCESS) {
+ ctx->free_func(smb_sid, ctx->alloc_pvt);
+ }
+
+ return err;
+}
+
+enum idmap_error_code sss_idmap_smb_sid_to_bin_sid(struct sss_idmap_ctx *ctx,
+ struct dom_sid *smb_sid,
+ uint8_t **_bin_sid,
+ size_t *_length)
+{
+ enum idmap_error_code err;
+ struct sss_dom_sid *dom_sid = NULL;
+ uint8_t *bin_sid = NULL;
+ size_t length;
+
+ err = sss_idmap_smb_sid_to_dom_sid(ctx, smb_sid, &dom_sid);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ err = sss_idmap_dom_sid_to_bin_sid(ctx, dom_sid, &bin_sid, &length);
+ if (err != IDMAP_SUCCESS) {
+ goto done;
+ }
+
+ *_bin_sid = bin_sid;
+ *_length = length;
+ err = IDMAP_SUCCESS;
+
+done:
+ ctx->free_func(dom_sid, ctx->alloc_pvt);
+ if (err != IDMAP_SUCCESS) {
+ ctx->free_func(bin_sid, ctx->alloc_pvt);
+ }
+
+ return err;
+}
diff --git a/src/lib/idmap/sss_idmap_private.h b/src/lib/idmap/sss_idmap_private.h
new file mode 100644
index 0000000..15300d1
--- /dev/null
+++ b/src/lib/idmap/sss_idmap_private.h
@@ -0,0 +1,84 @@
+/*
+ SSSD
+
+ ID-mapping library - private headers
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SSS_IDMAP_PRIVATE_H_
+#define SSS_IDMAP_PRIVATE_H_
+
+#define SSS_IDMAP_DEFAULT_LOWER 200000
+#define SSS_IDMAP_DEFAULT_UPPER 2000200000
+#define SSS_IDMAP_DEFAULT_RANGESIZE 200000
+#define SSS_IDMAP_DEFAULT_AUTORID false
+#define SSS_IDMAP_DEFAULT_EXTRA_SLICE_INIT 10
+
+#define CHECK_IDMAP_CTX(ctx, ret) do { \
+ if (ctx == NULL || ctx->alloc_func == NULL || ctx->free_func == NULL) { \
+ return ret; \
+ } \
+} while(0)
+
+struct sss_idmap_opts {
+ /* true if autorid compatibility mode is used */
+ bool autorid_mode;
+
+ /* smallest available id (for all domains) */
+ id_t idmap_lower;
+
+ /* highest available id (for all domains) */
+ id_t idmap_upper;
+
+ /* number of available UIDs (for single domain) */
+ id_t rangesize;
+
+ /* maximal number of secondary slices */
+ int extra_slice_init;
+};
+
+struct sss_idmap_ctx {
+ idmap_alloc_func *alloc_func;
+ void *alloc_pvt;
+ idmap_free_func *free_func;
+ struct sss_idmap_opts idmap_opts;
+ struct idmap_domain_info *idmap_domain_info;
+};
+
+/* This is a copy of the definition in the samba gen_ndr/security.h header
+ * file. We use it here to be able to offer conversions form struct dom_sid to
+ * string or binary representation since those are not made available by
+ * public samba libraries.
+ *
+ * If the definition ever changes on the samba side we have to adopt the
+ * change. But chances are very low that this will ever happen since e.g. this
+ * struct is also defined in public documentation from Microsoft. See e.g.
+ * section 2.4.2.3 of "[MS-DTYP]: Windows Data Types"
+ * http://msdn.microsoft.com/en-us/library/cc230364(v=prot.10)
+ */
+
+struct dom_sid {
+ uint8_t sid_rev_num;
+ int8_t num_auths;
+ uint8_t id_auth[6];
+ uint32_t sub_auths[15];
+};
+
+#endif /* SSS_IDMAP_PRIVATE_H_ */
diff --git a/src/lib/ipa_hbac/hbac_evaluator.c b/src/lib/ipa_hbac/hbac_evaluator.c
new file mode 100644
index 0000000..ce13bd5
--- /dev/null
+++ b/src/lib/ipa_hbac/hbac_evaluator.c
@@ -0,0 +1,520 @@
+/*
+ SSSD
+
+ IPA Backend Module -- Access control
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2011 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h" /* for HAVE_FUNCTION_ATTRIBUTE_FORMAT in "ipa_hbac.h" */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "ipa_hbac.h"
+#include "sss_utf8.h"
+
+#ifndef HAVE_ERRNO_T
+#define HAVE_ERRNO_T
+typedef int errno_t;
+#endif
+
+#ifndef EOK
+#define EOK 0
+#endif
+
+/* HBAC logging system */
+
+/* debug macro */
+#define HBAC_DEBUG(level, format, ...) do { \
+ if (hbac_debug_fn != NULL) { \
+ hbac_debug_fn(__FILE__, __LINE__, __FUNCTION__, \
+ level, format, ##__VA_ARGS__); \
+ } \
+} while (0)
+
+/* static pointer to external logging function */
+static hbac_debug_fn_t hbac_debug_fn = NULL;
+
+/* setup function for external logging function */
+void hbac_enable_debug(hbac_debug_fn_t external_debug_fn)
+{
+ hbac_debug_fn = external_debug_fn;
+}
+
+/* auxiliary function for hbac_request_element logging */
+static void hbac_request_element_debug_print(struct hbac_request_element *el,
+ const char *label);
+
+/* auxiliary function for hbac_eval_req logging */
+static void hbac_req_debug_print(struct hbac_eval_req *req);
+
+/* auxiliary function for hbac_rule_element logging */
+static void hbac_rule_element_debug_print(struct hbac_rule_element *el,
+ const char *label);
+
+/* auxiliary function for hbac_rule logging */
+static void hbac_rule_debug_print(struct hbac_rule *rule);
+
+
+/* Placeholder structure for future HBAC time-based
+ * evaluation rules
+ */
+struct hbac_time_rules {
+ int not_yet_implemented;
+};
+
+enum hbac_eval_result_int {
+ HBAC_EVAL_MATCH_ERROR = -1,
+ HBAC_EVAL_MATCHED,
+ HBAC_EVAL_UNMATCHED
+};
+
+static bool hbac_rule_element_is_complete(struct hbac_rule_element *el)
+{
+ if (el == NULL) return false;
+ if (el->category == HBAC_CATEGORY_ALL) return true;
+
+ if (el->names == NULL && el->groups == NULL) return false;
+
+ if ((el->names && el->names[0] != NULL)
+ || (el->groups && el->groups[0] != NULL))
+ return true;
+
+ /* If other categories are added, handle them here */
+
+ return false;
+}
+
+bool hbac_rule_is_complete(struct hbac_rule *rule, uint32_t *missing_attrs)
+{
+ bool complete = true;
+
+ *missing_attrs = 0;
+
+ if (rule == NULL) {
+ /* No rule passed in? */
+ return false;
+ }
+
+ /* Make sure we have all elements */
+ if (!hbac_rule_element_is_complete(rule->users)) {
+ complete = false;
+ *missing_attrs |= HBAC_RULE_ELEMENT_USERS;
+ }
+
+ if (!hbac_rule_element_is_complete(rule->services)) {
+ complete = false;
+ *missing_attrs |= HBAC_RULE_ELEMENT_SERVICES;
+ }
+
+ if (!hbac_rule_element_is_complete(rule->targethosts)) {
+ complete = false;
+ *missing_attrs |= HBAC_RULE_ELEMENT_TARGETHOSTS;
+ }
+
+ if (!hbac_rule_element_is_complete(rule->srchosts)) {
+ complete = false;
+ *missing_attrs |= HBAC_RULE_ELEMENT_SOURCEHOSTS;
+ }
+
+ return complete;
+}
+
+enum hbac_eval_result_int hbac_evaluate_rule(struct hbac_rule *rule,
+ struct hbac_eval_req *hbac_req,
+ enum hbac_error_code *error);
+
+enum hbac_eval_result hbac_evaluate(struct hbac_rule **rules,
+ struct hbac_eval_req *hbac_req,
+ struct hbac_info **info)
+{
+ uint32_t i;
+
+ enum hbac_error_code ret;
+ enum hbac_eval_result result = HBAC_EVAL_DENY;
+ enum hbac_eval_result_int intermediate_result;
+
+ HBAC_DEBUG(HBAC_DBG_INFO, "[< hbac_evaluate()\n");
+ hbac_req_debug_print(hbac_req);
+
+ if (info) {
+ *info = malloc(sizeof(struct hbac_info));
+ if (!*info) {
+ HBAC_DEBUG(HBAC_DBG_ERROR, "Out of memory.\n");
+ return HBAC_EVAL_OOM;
+ }
+ (*info)->code = HBAC_ERROR_UNKNOWN;
+ (*info)->rule_name = NULL;
+ }
+
+ for (i = 0; rules[i]; i++) {
+ hbac_rule_debug_print(rules[i]);
+ intermediate_result = hbac_evaluate_rule(rules[i], hbac_req, &ret);
+ if (intermediate_result == HBAC_EVAL_UNMATCHED) {
+ /* This rule did not match at all. Skip it */
+ HBAC_DEBUG(HBAC_DBG_INFO, "The rule [%s] did not match.\n",
+ rules[i]->name);
+ continue;
+ } else if (intermediate_result == HBAC_EVAL_MATCHED) {
+ HBAC_DEBUG(HBAC_DBG_INFO, "ALLOWED by rule [%s].\n", rules[i]->name);
+ result = HBAC_EVAL_ALLOW;
+ if (info) {
+ (*info)->code = HBAC_SUCCESS;
+ (*info)->rule_name = strdup(rules[i]->name);
+ if (!(*info)->rule_name) {
+ HBAC_DEBUG(HBAC_DBG_ERROR, "Out of memory.\n");
+ result = HBAC_EVAL_ERROR;
+ (*info)->code = HBAC_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ break;
+ } else {
+ /* An error occurred processing this rule */
+ HBAC_DEBUG(HBAC_DBG_ERROR,
+ "Error %d occurred during evaluating of rule [%s].\n",
+ ret, rules[i]->name);
+ result = HBAC_EVAL_ERROR;
+ if (info) {
+ (*info)->code = ret;
+ (*info)->rule_name = strdup(rules[i]->name);
+ }
+ /* Explicitly not checking the result of strdup(), since if
+ * it's NULL, we can't do anything anyway.
+ */
+ goto done;
+ }
+ }
+
+ /* If we've reached the end of the loop, we have either set the
+ * result to ALLOW explicitly or we'll stick with the default DENY.
+ */
+done:
+
+ HBAC_DEBUG(HBAC_DBG_INFO, "hbac_evaluate() >]\n");
+ return result;
+}
+
+static errno_t hbac_evaluate_element(struct hbac_rule_element *rule_el,
+ struct hbac_request_element *req_el,
+ bool *matched);
+
+enum hbac_eval_result_int hbac_evaluate_rule(struct hbac_rule *rule,
+ struct hbac_eval_req *hbac_req,
+ enum hbac_error_code *error)
+{
+ errno_t ret;
+ bool matched;
+
+ if (!rule->enabled) {
+ HBAC_DEBUG(HBAC_DBG_INFO, "Rule [%s] is not enabled\n", rule->name);
+ return HBAC_EVAL_UNMATCHED;
+ }
+
+ /* Make sure we have all elements */
+ if (!rule->users
+ || !rule->services
+ || !rule->targethosts
+ || !rule->srchosts) {
+ HBAC_DEBUG(HBAC_DBG_INFO,
+ "Rule [%s] cannot be parsed, some elements are empty\n",
+ rule->name);
+ *error = HBAC_ERROR_UNPARSEABLE_RULE;
+ return HBAC_EVAL_MATCH_ERROR;
+ }
+
+ /* Check users */
+ ret = hbac_evaluate_element(rule->users,
+ hbac_req->user,
+ &matched);
+ if (ret != EOK) {
+ HBAC_DEBUG(HBAC_DBG_ERROR,
+ "Cannot parse user elements of rule [%s]\n", rule->name);
+ *error = HBAC_ERROR_UNPARSEABLE_RULE;
+ return HBAC_EVAL_MATCH_ERROR;
+ } else if (!matched) {
+ return HBAC_EVAL_UNMATCHED;
+ }
+
+ /* Check services */
+ ret = hbac_evaluate_element(rule->services,
+ hbac_req->service,
+ &matched);
+ if (ret != EOK) {
+ HBAC_DEBUG(HBAC_DBG_ERROR,
+ "Cannot parse service elements of rule [%s]\n", rule->name);
+ *error = HBAC_ERROR_UNPARSEABLE_RULE;
+ return HBAC_EVAL_MATCH_ERROR;
+ } else if (!matched) {
+ return HBAC_EVAL_UNMATCHED;
+ }
+
+ /* Check target hosts */
+ ret = hbac_evaluate_element(rule->targethosts,
+ hbac_req->targethost,
+ &matched);
+ if (ret != EOK) {
+ HBAC_DEBUG(HBAC_DBG_ERROR,
+ "Cannot parse targethost elements of rule [%s]\n",
+ rule->name);
+ *error = HBAC_ERROR_UNPARSEABLE_RULE;
+ return HBAC_EVAL_MATCH_ERROR;
+ } else if (!matched) {
+ return HBAC_EVAL_UNMATCHED;
+ }
+
+ /* Check source hosts */
+ ret = hbac_evaluate_element(rule->srchosts,
+ hbac_req->srchost,
+ &matched);
+ if (ret != EOK) {
+ HBAC_DEBUG(HBAC_DBG_ERROR,
+ "Cannot parse srchost elements of rule [%s]\n",
+ rule->name);
+ *error = HBAC_ERROR_UNPARSEABLE_RULE;
+ return HBAC_EVAL_MATCH_ERROR;
+ } else if (!matched) {
+ return HBAC_EVAL_UNMATCHED;
+ }
+ return HBAC_EVAL_MATCHED;
+}
+
+static errno_t hbac_evaluate_element(struct hbac_rule_element *rule_el,
+ struct hbac_request_element *req_el,
+ bool *matched)
+{
+ size_t i, j;
+ const uint8_t *rule_name;
+ const uint8_t *req_name;
+ int ret;
+
+ if (rule_el->category & HBAC_CATEGORY_ALL) {
+ *matched = true;
+ return EOK;
+ }
+
+ /* First check the name list */
+ if (rule_el->names) {
+ for (i = 0; rule_el->names[i]; i++) {
+ if (req_el->name != NULL) {
+ rule_name = (const uint8_t *) rule_el->names[i];
+ req_name = (const uint8_t *) req_el->name;
+
+ /* Do a case-insensitive comparison. */
+ ret = sss_utf8_case_eq(rule_name, req_name);
+ if (ret != EOK && ret != ENOMATCH) {
+ return ret;
+ } else if (ret == EOK) {
+ *matched = true;
+ return EOK;
+ }
+ }
+ }
+ }
+
+ if (rule_el->groups) {
+ /* Not found in the name list
+ * Check for group membership
+ */
+ for (i = 0; rule_el->groups[i]; i++) {
+ rule_name = (const uint8_t *) rule_el->groups[i];
+
+ for (j = 0; req_el->groups[j]; j++) {
+ req_name = (const uint8_t *) req_el->groups[j];
+
+ /* Do a case-insensitive comparison. */
+ ret = sss_utf8_case_eq(rule_name, req_name);
+ if (ret != EOK && ret != ENOMATCH) {
+ return ret;
+ } else if (ret == EOK) {
+ *matched = true;
+ return EOK;
+ }
+ }
+ }
+ }
+
+ /* Not found in groups either */
+ *matched = false;
+ return EOK;
+}
+
+const char *hbac_result_string(enum hbac_eval_result result)
+{
+ switch (result) {
+ case HBAC_EVAL_ALLOW:
+ return "HBAC_EVAL_ALLOW";
+ case HBAC_EVAL_DENY:
+ return "HBAC_EVAL_DENY";
+ case HBAC_EVAL_ERROR:
+ return "HBAC_EVAL_ERROR";
+ case HBAC_EVAL_OOM:
+ return "Could not allocate memory for hbac_info object";
+ }
+ return "HBAC_EVAL_ERROR";
+}
+
+void hbac_free_info(struct hbac_info *info)
+{
+ if (info == NULL) return;
+
+ free(info->rule_name);
+ free(info);
+}
+
+const char *hbac_error_string(enum hbac_error_code code)
+{
+ switch (code) {
+ case HBAC_SUCCESS:
+ return "Success";
+ case HBAC_ERROR_NOT_IMPLEMENTED:
+ return "Function is not yet implemented";
+ case HBAC_ERROR_OUT_OF_MEMORY:
+ return "Out of memory";
+ case HBAC_ERROR_UNPARSEABLE_RULE:
+ return "Rule could not be evaluated";
+ case HBAC_ERROR_UNKNOWN:
+ default:
+ return "Unknown error code";
+ }
+}
+
+static void hbac_request_element_debug_print(struct hbac_request_element *el,
+ const char *label)
+{
+ int i;
+
+ if (el) {
+ if (el->name) {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s [%s]\n", label, el->name);
+ }
+
+ if (el->groups) {
+ if (el->groups[0]) {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s_group:\n", label);
+ for (i = 0; el->groups[i]; i++) {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t\t[%s]\n", el->groups[i]);
+ }
+ } else {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s_group (none)\n", label);
+ }
+ }
+ } else {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\t%s (none)\n", label);
+ }
+}
+
+static void hbac_req_debug_print(struct hbac_eval_req *req)
+{
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\tREQUEST:\n");
+ if (req) {
+ struct tm *local_time = NULL;
+ size_t ret;
+ const size_t buff_size = 100;
+ char time_buff[buff_size];
+
+ hbac_request_element_debug_print(req->service, "service");
+ hbac_request_element_debug_print(req->user, "user");
+ hbac_request_element_debug_print(req->targethost, "targethost");
+ hbac_request_element_debug_print(req->srchost, "srchost");
+
+ local_time = localtime(&req->request_time);
+ if (local_time == NULL) {
+ return;
+ }
+
+ ret = strftime(time_buff, buff_size, "%Y-%m-%d %H:%M:%S", local_time);
+ if (ret <= 0) {
+ return;
+ }
+
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\t\trequest time %s\n", time_buff);
+ } else {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\tRequest is EMPTY.\n");
+ }
+}
+
+static void hbac_rule_element_debug_print(struct hbac_rule_element *el,
+ const char *label)
+{
+ int i;
+
+ if (el) {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\t\tcategory [%#x] [%s]\n", el->category,
+ (el->category == HBAC_CATEGORY_ALL) ? "ALL" : "NONE");
+
+ if (el->names) {
+ if (el->names[0]) {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s_names:\n", label);
+ for (i = 0; el->names[i]; i++) {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t\t[%s]\n", el->names[i]);
+ }
+ } else {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s_names (none)\n", label);
+ }
+ }
+
+ if (el->groups) {
+ if (el->groups[0]) {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s_groups:\n", label);
+ for (i = 0; el->groups[i]; i++) {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t\t[%s]\n", el->groups[i]);
+ }
+ } else {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\t\t%s_groups (none)\n", label);
+ }
+ }
+ }
+}
+
+static void hbac_rule_debug_print(struct hbac_rule *rule)
+{
+ if (rule) {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\tRULE [%s] [%s]:\n",
+ rule->name, (rule->enabled) ? "ENABLED" : "DISABLED");
+ if (rule->services) {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\tservices:\n");
+ hbac_rule_element_debug_print(rule->services, "services");
+ } else {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\tservices (none)\n");
+ }
+
+ if (rule->users) {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\tusers:\n");
+ hbac_rule_element_debug_print(rule->users, "users");
+ } else {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\tusers (none)\n");
+ }
+
+ if (rule->targethosts) {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\ttargethosts:\n");
+ hbac_rule_element_debug_print(rule->targethosts, "targethosts");
+ } else {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\ttargethosts (none)\n");
+ }
+
+ if (rule->srchosts) {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\tsrchosts:\n");
+ hbac_rule_element_debug_print(rule->srchosts, "srchosts");
+ } else {
+ HBAC_DEBUG(HBAC_DBG_TRACE, "\tsrchosts (none)\n");
+ }
+ }
+}
diff --git a/src/lib/ipa_hbac/ipa_hbac.doxy.in b/src/lib/ipa_hbac/ipa_hbac.doxy.in
new file mode 100644
index 0000000..d1e9f99
--- /dev/null
+++ b/src/lib/ipa_hbac/ipa_hbac.doxy.in
@@ -0,0 +1,1883 @@
+# Doxyfile 1.8.3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME = ipa_hbac
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @PACKAGE_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = hbac_doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip. Note that you specify absolute paths here, but also
+# relative paths, which will be relative from the directory where doxygen is
+# started.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension,
+# and language is one of the parsers supported by doxygen: IDL, Java,
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
+# C++. For instance to make doxygen treat .inc files as Fortran files (default
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the
+# files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
+# Disable only in case of backward compatibilities issues.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented classes,
+# or namespaces to their corresponding documentation. Such a link can be
+# prevented in individual cases by by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES (the
+# default) will make doxygen replace the get and set methods by a property in
+# the documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+SYMBOL_CACHE_SIZE = 0
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = NO
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = YES
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if section-label ... \endif
+# and \cond section-label ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path. Do not use
+# file names with spaces, bibtex cannot handle them.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = @abs_top_srcdir@/src/lib/ipa_hbac/ipa_hbac.h
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS = *.cpp \
+ *.cc \
+ *.c \
+ *.h \
+ *.hh \
+ *.hpp \
+ *.dox
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS = */.git/* \
+ */.svn/* \
+ */cmake/* \
+ */build/*
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page (index.html).
+# This can be useful if you have a project on for instance GitHub and want reuse
+# the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C, C++ and Fortran comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If left blank doxygen will
+# generate a default style sheet. Note that it is recommended to use
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
+# tag will in the future become obsolete.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
+# user-defined cascading style sheet that is included after the standard
+# style sheets created by doxygen. Using this option one can overrule
+# certain style aspects. This is preferred over using HTML_STYLESHEET
+# since it does not replace the standard style sheet and is therefor more
+# robust against future updates. Doxygen will copy the style sheet file to
+# the output directory.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
+# and will result in a full expanded tree by default.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
+# identify the documentation publisher. This should be a reverse domain-name
+# style string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW = NONE
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
+# SVG. The default value is HTML-CSS, which is slower, but has the best
+# compatibility.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.
+# However, it is strongly recommended to install a local
+# copy of MathJax from http://www.mathjax.org before deployment.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript.
+# There are two flavours of web server based search depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools.
+# See the manual for details.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain
+# the search results. Doxygen ships with an example indexer (doxyindexer) and
+# search engine (doxysearch.cgi) which are based on the open source search engine
+# library Xapian. See the manual for configuration details.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will returned the search results when EXTERNAL_SEARCH is enabled.
+# Doxygen ships with an example search engine (doxysearch) which is based on
+# the open source search engine library Xapian. See the manual for configuration
+# details.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through other
+# doxygen projects that are not otherwise connected via tags files, but are
+# all added to the same search index. Each project needs to have a tag file set
+# via GENERATE_TAGFILE. The search mapping then maps the name of the tag file
+# to a relative location where the documentation can be found,
+# similar to the
+# TAGFILES option but without actually processing the tag file.
+# The format is: EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = DOXYGEN
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
+# doxygen is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
+# exceeded by 50% before the limit is enforced.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/src/lib/ipa_hbac/ipa_hbac.exports b/src/lib/ipa_hbac/ipa_hbac.exports
new file mode 100644
index 0000000..abdcc5f
--- /dev/null
+++ b/src/lib/ipa_hbac/ipa_hbac.exports
@@ -0,0 +1,20 @@
+IPA_HBAC_0.0.1 {
+
+ # public functions
+ global:
+
+ hbac_evaluate;
+ hbac_result_string;
+ hbac_error_string;
+ hbac_free_info;
+ hbac_rule_is_complete;
+
+ # everything else is local
+ local:
+ *;
+};
+
+IPA_HBAC_0.1.0 {
+ global:
+ hbac_enable_debug;
+} IPA_HBAC_0.0.1;
diff --git a/src/lib/ipa_hbac/ipa_hbac.h b/src/lib/ipa_hbac/ipa_hbac.h
new file mode 100644
index 0000000..f9d339c
--- /dev/null
+++ b/src/lib/ipa_hbac/ipa_hbac.h
@@ -0,0 +1,344 @@
+/*
+ SSSD
+
+ IPA Backend Module -- Access control
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef IPA_HBAC_H_
+#define IPA_HBAC_H_
+
+/**
+ * @defgroup ipa_hbac Host-Based Access Control Resolver
+ * Libipa_hbac provides a mechanism to validate FreeIPA
+ * HBAC rules as well as evaluate whether they apply to
+ * a particular user login attempt.
+ *
+ * Libipa_hbac is case-insensitive and compatible with
+ * UTF-8.
+ * @{
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+
+/** Debug levels for HBAC. */
+enum hbac_debug_level {
+ HBAC_DBG_FATAL, /** Fatal failure (not used). */
+ HBAC_DBG_ERROR, /** Serious failure (out of memory, for example). */
+ HBAC_DBG_WARNING, /** Warnings (not used). */
+ HBAC_DBG_INFO, /** HBAC allow/disallow info. */
+ HBAC_DBG_TRACE /** Verbose description of rules. */
+};
+
+#ifdef HAVE_FUNCTION_ATTRIBUTE_FORMAT
+#define HBAC_ATTRIBUTE_PRINTF(a1, a2) __attribute__((format(printf, a1, a2)))
+#else
+#define HBAC_ATTRIBUTE_PRINTF(a1, a2)
+#endif
+
+/**
+ * Function pointer to HBAC external debugging function.
+ */
+typedef void (*hbac_debug_fn_t)(const char *file, int line,
+ const char *function,
+ enum hbac_debug_level, const char *format,
+ ...) HBAC_ATTRIBUTE_PRINTF(5, 6);
+
+/**
+ * HBAC uses external_debug_fn for logging messages.
+ * @param[in] external_debug_fn Pointer to external logging function.
+ */
+void hbac_enable_debug(hbac_debug_fn_t external_debug_fn);
+
+/** Result of HBAC evaluation */
+enum hbac_eval_result {
+ /** An error occurred
+ * See the #hbac_info for more details
+ */
+ HBAC_EVAL_ERROR = -1,
+
+ /** Evaluation grants access */
+ HBAC_EVAL_ALLOW,
+
+ /** Evaluation denies access */
+ HBAC_EVAL_DENY,
+
+ /** Evaluation failed due to lack of memory
+ * #hbac_info is not available
+ */
+ HBAC_EVAL_OOM
+};
+
+/**
+ * No service category specified
+ */
+#define HBAC_CATEGORY_NULL 0x0000
+
+/**
+ * Rule should apply to all
+ */
+#define HBAC_CATEGORY_ALL 0x0001
+
+/**
+ * Opaque type contained in hbac_evaluator.c
+ */
+struct hbac_time_rules;
+
+/**
+ * Component of an HBAC rule
+ *
+ * Components can be one of users, target hosts,
+ * source hosts, or services.
+ */
+struct hbac_rule_element {
+ /**
+ * Category for this element
+ *
+ * This value is a bitmask.
+ * See #HBAC_CATEGORY_NULL and
+ * #HBAC_CATEGORY_ALL
+ */
+ uint32_t category;
+
+ /**
+ * List of explicit members of this rule component
+ *
+ * - Users: usernames
+ * - Hosts: hostnames
+ * - Services: PAM service names
+ */
+ const char **names;
+
+ /**
+ * List of group members of this rule component
+ *
+ * - Users: user groups (POSIX or non-POSIX)
+ * - Hosts: hostgroups
+ * - Services: PAM service groups.
+ */
+ const char **groups;
+};
+
+/**
+ * HBAC rule object for evaluation
+ */
+struct hbac_rule {
+ const char *name;
+ bool enabled;
+
+ /**
+ * Services and service groups
+ * for which this rule applies
+ */
+ struct hbac_rule_element *services;
+
+ /**
+ * Users and groups for which this
+ * rule applies
+ */
+ struct hbac_rule_element *users;
+
+ /**
+ * Target hosts for which this rule apples
+ */
+ struct hbac_rule_element *targethosts;
+
+ /**
+ * Source hosts for which this rule applies
+ */
+ struct hbac_rule_element *srchosts;
+
+ /**
+ * For future use
+ */
+ struct hbac_time_rules *timerules;
+};
+
+/**
+ * Component of an HBAC request
+ */
+struct hbac_request_element {
+ /**
+ * List of explicit members of this request component
+ *
+ * - Users: usernames
+ * - Hosts: hostnames
+ * - Services: PAM service names
+ */
+ const char *name;
+
+ /**
+ * List of group members of this request component
+ *
+ * - Users: user groups (POSIX or non-POSIX)
+ * - Hosts: hostgroups
+ * - Services: PAM service groups.
+ */
+ const char **groups;
+};
+
+/**
+ * Request object for an HBAC rule evaluation
+ *
+ *
+ */
+struct hbac_eval_req {
+ /** This is a list of service DNs to check,
+ * it must consist of the actual service
+ * requested, as well as all parent groups
+ * containing that service.
+ */
+ struct hbac_request_element *service;
+
+ /** This is a list of user DNs to check,
+ * it must consist of the actual user
+ * requested, as well as all parent groups
+ * containing that user.
+ */
+ struct hbac_request_element *user;
+
+ /** This is a list of target hosts to check,
+ * it must consist of the actual target host
+ * requested, as well as all parent groups
+ * containing that target host.
+ */
+ struct hbac_request_element *targethost;
+
+ /** This is a list of source hosts to check,
+ * it must consist of the actual source host
+ * requested, as well as all parent groups
+ * containing that source host.
+ */
+ struct hbac_request_element *srchost;
+
+ /** For future use */
+ time_t request_time;
+};
+
+/**
+ * Error code returned by the evaluator
+ */
+enum hbac_error_code {
+ /** Unexpected error */
+ HBAC_ERROR_UNKNOWN = -1,
+
+ /** Successful evaluation */
+ HBAC_SUCCESS,
+
+ /** Function is not yet implemented */
+ HBAC_ERROR_NOT_IMPLEMENTED,
+
+ /** Ran out of memory during processing */
+ HBAC_ERROR_OUT_OF_MEMORY,
+
+ /** Parse error while evaluating rule */
+ HBAC_ERROR_UNPARSEABLE_RULE
+};
+
+/** Extended information */
+struct hbac_info {
+ /**
+ * If the hbac_eval_result was HBAC_EVAL_ERROR,
+ * this will be an error code.
+ * Otherwise it will be HBAC_SUCCESS
+ */
+ enum hbac_error_code code;
+
+ /**
+ * Specify the name of the rule that matched or
+ * threw an error
+ */
+ char *rule_name;
+};
+
+
+/**
+ * @brief Evaluate an authorization request against a set of HBAC rules
+ *
+ * @param[in] rules A NULL-terminated list of rules to evaluate against
+ * @param[in] hbac_req A user authorization request
+ * @param[out] info Extended information (including the name of the
+ * rule that allowed access (or caused a parse error)
+ * @return
+ * - #HBAC_EVAL_ERROR: An error occurred
+ * - #HBAC_EVAL_ALLOW: Access is granted
+ * - #HBAC_EVAL_DENY: Access is denied
+ * - #HBAC_EVAL_OOM: Insufficient memory to complete the evaluation
+ */
+enum hbac_eval_result hbac_evaluate(struct hbac_rule **rules,
+ struct hbac_eval_req *hbac_req,
+ struct hbac_info **info);
+
+/**
+ * @brief Display result of hbac evaluation in human-readable form
+ * @param[in] result Return value of #hbac_evaluate
+ * @return English string describing the evaluation result
+ */
+const char *hbac_result_string(enum hbac_eval_result result);
+
+/**
+ * @brief Display error description
+ * @param code Error code returned in #hbac_info
+ * @return English string describing the error
+ */
+const char *hbac_error_string(enum hbac_error_code code);
+
+/**
+ * @brief Function to safely free #hbac_info returned by #hbac_evaluate
+ * @param info #hbac_info returned by #hbac_evaluate
+ */
+void hbac_free_info(struct hbac_info *info);
+
+/** User element */
+#define HBAC_RULE_ELEMENT_USERS 0x01
+
+/** Service element */
+#define HBAC_RULE_ELEMENT_SERVICES 0x02
+
+/** Target host element */
+#define HBAC_RULE_ELEMENT_TARGETHOSTS 0x04
+
+/** Source host element */
+#define HBAC_RULE_ELEMENT_SOURCEHOSTS 0x08
+
+/**
+ * @brief Evaluate whether an HBAC rule contains all necessary elements
+ *
+ * @param[in] rule An HBAC rule to evaluate
+ * @param[out] missing_attrs A list of attributes missing from the rule
+ * This is a bitmask that may contain one or more
+ * of #HBAC_RULE_ELEMENT_USERS,
+ * #HBAC_RULE_ELEMENT_SERVICES,
+ * #HBAC_RULE_ELEMENT_TARGETHOSTS and
+ * #HBAC_RULE_ELEMENT_SOURCEHOSTS
+ *
+ * @return True if the rule contains all mandatory attributes
+ *
+ * @note This function does not care if the rule is enabled or disabled
+ */
+bool hbac_rule_is_complete(struct hbac_rule *rule, uint32_t *missing_attrs);
+
+/**
+ * @}
+ */
+#endif /* IPA_HBAC_H_ */
diff --git a/src/lib/ipa_hbac/ipa_hbac.pc.in b/src/lib/ipa_hbac/ipa_hbac.pc.in
new file mode 100644
index 0000000..3345dd6
--- /dev/null
+++ b/src/lib/ipa_hbac/ipa_hbac.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: ipa_hbac
+Description: FreeIPA HBAC Evaluator library
+Version: @VERSION@
+Libs: -L${libdir} -lipa_hbac
+Cflags:
+URL: https://github.com/SSSD/sssd/
diff --git a/src/lib/sifp/sss_sifp.c b/src/lib/sifp/sss_sifp.c
new file mode 100644
index 0000000..3dad40f
--- /dev/null
+++ b/src/lib/sifp/sss_sifp.c
@@ -0,0 +1,473 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2014 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <dbus/dbus.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib/sifp/sss_sifp.h"
+#include "lib/sifp/sss_sifp_dbus.h"
+#include "lib/sifp/sss_sifp_private.h"
+
+#define DBUS_IFACE_PROP "org.freedesktop.DBus.Properties"
+
+static void * default_alloc(size_t size, void *pvt)
+{
+ return malloc(size);
+}
+
+static void default_free(void *ptr, void *pvt)
+{
+ free(ptr);
+}
+
+static DBusMessage * sss_sifp_create_prop_msg(const char *object_path,
+ const char *method)
+{
+ return sss_sifp_create_message(object_path, DBUS_IFACE_PROP, method);
+}
+
+sss_sifp_error
+sss_sifp_init(sss_sifp_ctx **_ctx)
+{
+ return sss_sifp_init_ex(NULL, default_alloc, default_free, _ctx);
+}
+
+sss_sifp_error
+sss_sifp_init_ex(void *alloc_pvt,
+ sss_sifp_alloc_func *alloc_func,
+ sss_sifp_free_func *free_func,
+ sss_sifp_ctx **_ctx)
+{
+ sss_sifp_ctx *ctx = NULL;
+ DBusConnection *conn = NULL;
+ DBusError dbus_error;
+ sss_sifp_error ret;
+
+ if (_ctx == NULL || alloc_func == NULL || free_func == NULL) {
+ return SSS_SIFP_INVALID_ARGUMENT;
+ }
+
+ dbus_error_init(&dbus_error);
+
+ ctx = alloc_func(sizeof(sss_sifp_ctx), alloc_pvt);
+ if (ctx == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ ctx->conn = NULL;
+ ctx->alloc_fn = alloc_func;
+ ctx->free_fn = free_func;
+ ctx->alloc_pvt = alloc_pvt;
+ ctx->io_error = alloc_func(sizeof(DBusError), alloc_pvt);
+ if (ctx->io_error == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ *_ctx = ctx;
+
+ dbus_error_init(ctx->io_error);
+
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error);
+ if (dbus_error_is_set(&dbus_error)) {
+ sss_sifp_set_io_error(ctx, &dbus_error);
+ ret = SSS_SIFP_IO_ERROR;
+ goto done;
+ }
+
+ ctx->conn = conn;
+
+ ret = SSS_SIFP_OK;
+
+done:
+ if (ret != SSS_SIFP_OK) {
+ sss_sifp_free(&ctx);
+ }
+
+ dbus_error_free(&dbus_error);
+ return ret;
+}
+
+const char *
+sss_sifp_get_last_io_error_name(sss_sifp_ctx *ctx)
+{
+ if (ctx == NULL) {
+ return "Invalid sss_sifp context";
+ }
+
+ if (!dbus_error_is_set(ctx->io_error)) {
+ return NULL;
+ }
+
+ return ctx->io_error->name;
+}
+
+const char *
+sss_sifp_get_last_io_error_message(sss_sifp_ctx *ctx)
+{
+ if (ctx == NULL) {
+ return "Invalid sss_sifp context";
+ }
+
+ if (!dbus_error_is_set(ctx->io_error)) {
+ return NULL;
+ }
+
+ return ctx->io_error->message;
+}
+
+const char *
+sss_sifp_strerr(sss_sifp_error error)
+{
+ switch (error) {
+ case SSS_SIFP_OK:
+ return "Success";
+ case SSS_SIFP_OUT_OF_MEMORY:
+ return "Out of memory";
+ case SSS_SIFP_INVALID_ARGUMENT:
+ return "Invalid argument";
+ case SSS_SIFP_IO_ERROR:
+ return "Communication error";
+ case SSS_SIFP_INTERNAL_ERROR:
+ return "Internal error";
+ case SSS_SIFP_NOT_SUPPORTED:
+ return "Not supported";
+ case SSS_SIFP_ATTR_MISSING:
+ return "Attribute does not exist";
+ case SSS_SIFP_ATTR_NULL:
+ return "Attribute does not have any value set";
+ case SSS_SIFP_INCORRECT_TYPE:
+ return "Incorrect type";
+ case SSS_SIFP_ERROR_SENTINEL:
+ return "Invalid error code";
+ }
+
+ return "Invalid error code";
+}
+
+sss_sifp_error
+sss_sifp_fetch_attr(sss_sifp_ctx *ctx,
+ const char *object_path,
+ const char *interface,
+ const char *name,
+ sss_sifp_attr ***_attrs)
+{
+ DBusMessage *msg = NULL;
+ DBusMessage *reply = NULL;
+ dbus_bool_t bret;
+ sss_sifp_error ret;
+
+ if (ctx == NULL || object_path == NULL || interface == NULL
+ || name == NULL || _attrs == NULL) {
+ return SSS_SIFP_INVALID_ARGUMENT;
+ }
+
+ /* Message format:
+ * In: string:interface
+ * In: string:attribute
+ * Out: variant(misc:value)
+ */
+
+ msg = sss_sifp_create_prop_msg(object_path, "Get");
+ if (msg == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ bret = dbus_message_append_args(msg, DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID);
+ if (!bret) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ ret = sss_sifp_send_message(ctx, msg, &reply);
+ if (ret != SSS_SIFP_OK) {
+ goto done;
+ }
+
+ ret = sss_sifp_parse_attr(ctx, name, reply, _attrs);
+
+done:
+ if (msg != NULL) {
+ dbus_message_unref(msg);
+ }
+
+ if (reply != NULL) {
+ dbus_message_unref(reply);
+ }
+
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_fetch_all_attrs(sss_sifp_ctx *ctx,
+ const char *object_path,
+ const char *interface,
+ sss_sifp_attr ***_attrs)
+{
+ DBusMessage *msg = NULL;
+ DBusMessage *reply = NULL;
+ dbus_bool_t bret;
+ sss_sifp_error ret;
+
+ if (ctx == NULL || object_path == NULL || interface == NULL
+ || _attrs == NULL) {
+ return SSS_SIFP_INVALID_ARGUMENT;
+ }
+
+ msg = sss_sifp_create_prop_msg(object_path, "GetAll");
+ if (msg == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ bret = dbus_message_append_args(msg, DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_INVALID);
+ if (!bret) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ ret = sss_sifp_send_message(ctx, msg, &reply);
+ if (ret != SSS_SIFP_OK) {
+ goto done;
+ }
+
+ ret = sss_sifp_parse_attr_list(ctx, reply, _attrs);
+
+done:
+ if (msg != NULL) {
+ dbus_message_unref(msg);
+ }
+
+ if (reply != NULL) {
+ dbus_message_unref(reply);
+ }
+
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_fetch_object(sss_sifp_ctx *ctx,
+ const char *object_path,
+ const char *interface,
+ sss_sifp_object **_object)
+{
+ sss_sifp_object *object = NULL;
+ sss_sifp_attr **attrs = NULL;
+ const char *name = NULL;
+ sss_sifp_error ret;
+
+ if (ctx == NULL || object_path == NULL || interface == NULL
+ || _object == NULL) {
+ return SSS_SIFP_INVALID_ARGUMENT;
+ }
+
+ ret = sss_sifp_fetch_all_attrs(ctx, object_path, interface, &attrs);
+ if (ret != SSS_SIFP_OK) {
+ goto done;
+ }
+
+ ret = sss_sifp_find_attr_as_string(attrs, "name", &name);
+ if (ret != SSS_SIFP_OK) {
+ goto done;
+ }
+
+ object = _alloc_zero(ctx, sss_sifp_object, 1);
+ if (object == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ object->attrs = attrs;
+
+ object->name = sss_sifp_strdup(ctx, name);
+ if (object->name == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ object->object_path = sss_sifp_strdup(ctx, object_path);
+ if (object->object_path == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ object->interface = sss_sifp_strdup(ctx, interface);
+ if (object->interface == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ *_object = object;
+
+ ret = SSS_SIFP_OK;
+
+done:
+ if (ret != SSS_SIFP_OK) {
+ sss_sifp_free_object(ctx, &object);
+ }
+
+ return ret;
+}
+
+void
+sss_sifp_free(sss_sifp_ctx **_ctx)
+{
+ sss_sifp_ctx *ctx = NULL;
+
+ if (_ctx == NULL || *_ctx == NULL) {
+ return;
+ }
+
+ ctx = *_ctx;
+
+ if (ctx->conn != NULL) {
+ dbus_connection_unref(ctx->conn);
+ }
+
+ if (ctx->io_error != NULL) {
+ dbus_error_free(ctx->io_error);
+ _free(ctx, ctx->io_error);
+ }
+
+ _free(ctx, ctx);
+ *_ctx = NULL;
+
+ return;
+}
+
+void
+sss_sifp_free_attrs(sss_sifp_ctx *ctx,
+ sss_sifp_attr ***_attrs)
+{
+ sss_sifp_attr **attrs = NULL;
+ unsigned int i, j;
+
+ if (_attrs == NULL || *_attrs == NULL) {
+ return;
+ }
+
+ attrs = *_attrs;
+
+ for (i = 0; attrs[i] != NULL; i++) {
+ switch (attrs[i]->type) {
+ case SSS_SIFP_ATTR_TYPE_BOOL:
+ _free(ctx, attrs[i]->data.boolean);
+ break;
+ case SSS_SIFP_ATTR_TYPE_INT16:
+ _free(ctx, attrs[i]->data.int16);
+ break;
+ case SSS_SIFP_ATTR_TYPE_UINT16:
+ _free(ctx, attrs[i]->data.uint16);
+ break;
+ case SSS_SIFP_ATTR_TYPE_INT32:
+ _free(ctx, attrs[i]->data.int32);
+ break;
+ case SSS_SIFP_ATTR_TYPE_UINT32:
+ _free(ctx, attrs[i]->data.uint32);
+ break;
+ case SSS_SIFP_ATTR_TYPE_INT64:
+ _free(ctx, attrs[i]->data.int64);
+ break;
+ case SSS_SIFP_ATTR_TYPE_UINT64:
+ _free(ctx, attrs[i]->data.uint64);
+ break;
+ case SSS_SIFP_ATTR_TYPE_STRING:
+ for (j = 0; j < attrs[i]->num_values; j++) {
+ _free(ctx, attrs[i]->data.str[j]);
+ }
+ _free(ctx, attrs[i]->data.str);
+ break;
+ case SSS_SIFP_ATTR_TYPE_STRING_DICT:
+ if (attrs[i]->data.str_dict != NULL) {
+ hash_destroy(attrs[i]->data.str_dict);
+ }
+ attrs[i]->data.str_dict = NULL;
+ break;
+ }
+ _free(ctx, attrs[i]->name);
+ _free(ctx, attrs[i]);
+ }
+
+ _free(ctx, attrs);
+
+ *_attrs = NULL;
+}
+
+void
+sss_sifp_free_object(sss_sifp_ctx *ctx,
+ sss_sifp_object **_object)
+{
+ sss_sifp_object *object = NULL;
+
+ if (_object == NULL || *_object == NULL) {
+ return;
+ }
+
+ object = *_object;
+
+ sss_sifp_free_attrs(ctx, &object->attrs);
+ _free(ctx, object->object_path);
+ _free(ctx, object->interface);
+ _free(ctx, object->name);
+ _free(ctx, object);
+
+ *_object = NULL;
+}
+
+void
+sss_sifp_free_string(sss_sifp_ctx *ctx,
+ char **_str)
+{
+ if (_str == NULL || *_str == NULL) {
+ return;
+ }
+
+ _free(ctx, *_str);
+
+ *_str = NULL;
+}
+
+void
+sss_sifp_free_string_array(sss_sifp_ctx *ctx,
+ char ***_str_array)
+{
+ char **str_array = NULL;
+ int i;
+
+ if (_str_array == NULL || *_str_array == NULL) {
+ return;
+ }
+
+ str_array = *_str_array;
+
+ for (i = 0; str_array[i] != NULL; i++) {
+ _free(ctx, str_array[i]);
+ }
+
+ _free(ctx, str_array);
+
+ *_str_array = NULL;
+}
diff --git a/src/lib/sifp/sss_sifp.h b/src/lib/sifp/sss_sifp.h
new file mode 100644
index 0000000..95a7518
--- /dev/null
+++ b/src/lib/sifp/sss_sifp.h
@@ -0,0 +1,564 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2014 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SSS_SIFP_H_
+#define SSS_SIFP_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <dhash.h>
+
+/**
+ * @defgroup sss_simpleifp Simple interface to SSSD InfoPipe responder.
+ * Libsss_simpleifp provides a synchronous interface to simplify basic
+ * communication with SSSD InfoPipe responder.
+ *
+ * This interface is not a full replacement for the complete D-Bus API and it
+ * provides only access to the most common tasks like fetching attributes
+ * of SSSD objects.
+ *
+ * If there is a need for a more sophisticated communication with the SSSD
+ * InfoPipe responder a D-Bus API of your choice should be used.
+ *
+ * @{
+ */
+
+/** SSSD InfoPipe bus address */
+#define SSS_SIFP_ADDRESS "org.freedesktop.sssd.infopipe"
+
+/* Backwards-compatible address */
+#define SSS_SIFP_IFP SSS_SIFP_ADDRESS
+
+/* Backwards-compatible interface definitions */
+#define SSS_SIFP_IFACE_IFP SSS_SIFP_IFP
+#define SSS_SIFP_IFACE_COMPONENTS "org.freedesktop.sssd.infopipe.Components"
+#define SSS_SIFP_IFACE_SERVICES "org.freedesktop.sssd.infopipe.Services"
+#define SSS_SIFP_IFACE_DOMAINS "org.freedesktop.sssd.infopipe.Domains"
+#define SSS_SIFP_IFACE_USERS "org.freedesktop.sssd.infopipe.Users"
+#define SSS_SIFP_IFACE_GROUPS "org.freedesktop.sssd.infopipe.Groups"
+
+/**
+ * SSSD InfoPipe object path.
+ * Look at InfoPipe introspection and SSSD documentation for more objects.
+ */
+#define SSS_SIFP_PATH "/org/freedesktop/sssd/infopipe"
+
+/**
+ * SSSD InfoPipe object path.
+ * Look at InfoPipe introspection and SSSD documentation for more interfaces.
+ */
+#define SSS_SIFP_IFACE "org.freedesktop.sssd.infopipe"
+
+/**
+ * Opaque libsss_sifp context. One context shall not be used by multiple
+ * threads. Each thread needs to create and use its own context.
+ *
+ * @see sss_sifp_init
+ * @see sss_sifp_init_ex
+ */
+typedef struct sss_sifp_ctx sss_sifp_ctx;
+
+/**
+ * Typedef for memory allocation functions
+ */
+typedef void (sss_sifp_free_func)(void *ptr, void *pvt);
+typedef void *(sss_sifp_alloc_func)(size_t size, void *pvt);
+
+/**
+ * Error codes used by libsss_sifp
+ */
+typedef enum sss_sifp_error {
+ /** Success */
+ SSS_SIFP_OK = 0,
+
+ /** Ran out of memory during processing */
+ SSS_SIFP_OUT_OF_MEMORY,
+
+ /** Invalid argument */
+ SSS_SIFP_INVALID_ARGUMENT,
+
+ /**
+ * Input/output error
+ *
+ * @see sss_sifp_get_last_io_error() to get more information
+ */
+ SSS_SIFP_IO_ERROR,
+
+ /** Internal error */
+ SSS_SIFP_INTERNAL_ERROR,
+
+ /** Operation not supported */
+ SSS_SIFP_NOT_SUPPORTED,
+
+ /** Attribute does not exist */
+ SSS_SIFP_ATTR_MISSING,
+
+ /** Attribute does not have any value set */
+ SSS_SIFP_ATTR_NULL,
+
+ /** Incorrect attribute type */
+ SSS_SIFP_INCORRECT_TYPE,
+
+ /** Always last */
+ SSS_SIFP_ERROR_SENTINEL
+} sss_sifp_error;
+
+/**
+ * D-Bus object attribute
+ */
+typedef struct sss_sifp_attr sss_sifp_attr;
+
+/**
+ * D-Bus object
+ */
+typedef struct sss_sifp_object {
+ char *name;
+ char *object_path;
+ char *interface;
+ sss_sifp_attr **attrs;
+} sss_sifp_object;
+
+/**
+ * @brief Initialize sss_sifp context using default allocator (malloc)
+ *
+ * @param[out] _ctx sss_sifp context
+ */
+sss_sifp_error
+sss_sifp_init(sss_sifp_ctx **_ctx);
+
+/**
+ * @brief Initialize sss_sifp context
+ *
+ * @param[in] alloc_pvt Private data for allocation routine
+ * @param[in] alloc_func Function to allocate memory for the context, if
+ * NULL malloc() is used
+ * @param[in] free_func Function to free the memory of the context, if
+ * NULL free() is used
+ * @param[out] _ctx sss_sifp context
+ */
+sss_sifp_error
+sss_sifp_init_ex(void *alloc_pvt,
+ sss_sifp_alloc_func *alloc_func,
+ sss_sifp_free_func *free_func,
+ sss_sifp_ctx **_ctx);
+
+/**
+ * @brief Return last error name from underlying D-Bus communication
+ *
+ * @param[in] ctx sss_sifp context
+ * @return Error message or NULL if no error occurred during last D-Bus call.
+ */
+const char *
+sss_sifp_get_last_io_error_name(sss_sifp_ctx *ctx);
+
+/**
+ * @brief Return last error message from underlying D-Bus communication
+ *
+ * @param[in] ctx sss_sifp context
+ * @return Error message or NULL if no error occurred during last D-Bus call.
+ */
+const char *
+sss_sifp_get_last_io_error_message(sss_sifp_ctx *ctx);
+
+/**
+ * @brief Translate error code into human readable message.
+ *
+ * @param[in] error sss_sifp error code
+ * @return Error message.
+ */
+const char *
+sss_sifp_strerr(sss_sifp_error error);
+
+/**
+ * @brief Fetch selected attributes of given object.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in] object_path D-Bus object path
+ * @param[in] interface D-Bus interface
+ * @param[in] name Name of desired attribute
+ * @param[out] _attrs List of acquired attributes
+ */
+sss_sifp_error
+sss_sifp_fetch_attr(sss_sifp_ctx *ctx,
+ const char *object_path,
+ const char *interface,
+ const char *name,
+ sss_sifp_attr ***_attrs);
+
+/**
+ * @brief Fetch all attributes of given object.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in] object_path D-Bus object path
+ * @param[in] interface D-Bus interface
+ * @param[out] _attrs Acquired attributes
+ */
+sss_sifp_error
+sss_sifp_fetch_all_attrs(sss_sifp_ctx *ctx,
+ const char *object_path,
+ const char *interface,
+ sss_sifp_attr ***_attrs);
+
+/**
+ * @brief Fetch D-Bus object.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in] object_path D-Bus object path
+ * @param[in] interface D-Bus interface
+ * @param[out] _object Object and its attributes
+ */
+sss_sifp_error
+sss_sifp_fetch_object(sss_sifp_ctx *ctx,
+ const char *object_path,
+ const char *interface,
+ sss_sifp_object **_object);
+
+/**
+ * @brief Find attribute in list and return its value.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _value Output value
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_bool(sss_sifp_attr **attrs,
+ const char *name,
+ bool *_value);
+
+/**
+ * @brief Find attribute in list and return its value.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _value Output value
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_int16(sss_sifp_attr **attrs,
+ const char *name,
+ int16_t *_value);
+
+/**
+ * @brief Find attribute in list and return its value.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _value Output value
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_uint16(sss_sifp_attr **attrs,
+ const char *name,
+ uint16_t *_value);
+
+/**
+ * @brief Find attribute in list and return its value.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _value Output value
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_int32(sss_sifp_attr **attrs,
+ const char *name,
+ int32_t *_value);
+
+/**
+ * @brief Find attribute in list and return its value.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _value Output value
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_uint32(sss_sifp_attr **attrs,
+ const char *name,
+ uint32_t *_value);
+
+/**
+ * @brief Find attribute in list and return its value.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _value Output value
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_int64(sss_sifp_attr **attrs,
+ const char *name,
+ int64_t *_value);
+
+/**
+ * @brief Find attribute in list and return its value.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _value Output value
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_uint64(sss_sifp_attr **attrs,
+ const char *name,
+ uint64_t *_value);
+
+/**
+ * @brief Find attribute in list and return its value.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _value Output value
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_string(sss_sifp_attr **attrs,
+ const char *name,
+ const char **_value);
+
+/**
+ * @brief Find attribute in list and return its value.
+ *
+ * The dictionary is stored in dhash table, the values
+ * are pointers to NULL-terminated string array.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _value Output value
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_string_dict(sss_sifp_attr **attrs,
+ const char *name,
+ hash_table_t **_value);
+
+/**
+ * @brief Find attribute in list and return its values.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _num_values Number of values in the array
+ * @param[out] _value Output array
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_bool_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ bool **_value);
+
+/**
+ * @brief Find attribute in list and return its values.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _num_values Number of values in the array
+ * @param[out] _value Output array
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_int16_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ int16_t **_value);
+
+/**
+ * @brief Find attribute in list and return its values.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _num_values Number of values in the array
+ * @param[out] _value Output array
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_uint16_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ uint16_t **_value);
+
+/**
+ * @brief Find attribute in list and return its values.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _num_values Number of values in the array
+ * @param[out] _value Output array
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_int32_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ int32_t **_value);
+
+/**
+ * @brief Find attribute in list and return its values.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _num_values Number of values in the array
+ * @param[out] _value Output array
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_uint32_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ uint32_t **_value);
+
+/**
+ * @brief Find attribute in list and return its values.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _num_values Number of values in the array
+ * @param[out] _value Output array
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_int64_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ int64_t **_value);
+
+/**
+ * @brief Find attribute in list and return its values.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _num_values Number of values in the array
+ * @param[out] _value Output array
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_uint64_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ uint64_t **_value);
+
+/**
+ * @brief Find attribute in list and return its values.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ * @param[out] _num_values Number of values in the array
+ * @param[out] _value Output array
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_string_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ const char * const **_value);
+
+/**
+ * @brief Free sss_sifp context and set it to NULL.
+ *
+ * @param[in,out] _ctx sss_sifp context
+ */
+void
+sss_sifp_free(sss_sifp_ctx **_ctx);
+
+/**
+ * @brief Free attribute list and set it to NULL.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in,out] _attrs Attributes
+ */
+void
+sss_sifp_free_attrs(sss_sifp_ctx *ctx,
+ sss_sifp_attr ***_attrs);
+
+/**
+ * @brief Free sss_sifp object and set it to NULL.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in,out] _object Object
+ */
+void
+sss_sifp_free_object(sss_sifp_ctx *ctx,
+ sss_sifp_object **_object);
+
+/**
+ * @brief Free string and set it to NULL.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in,out] _str String
+ */
+void
+sss_sifp_free_string(sss_sifp_ctx *ctx,
+ char **_str);
+
+/**
+ * @brief Free array of strings and set it to NULL.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in,out] _str_array Array of strings
+ */
+void
+sss_sifp_free_string_array(sss_sifp_ctx *ctx,
+ char ***_str_array);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup common Most common use cases of SSSD InfoPipe responder.
+ * @{
+ */
+
+/**
+ * @brief List names of available domains.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[out] _domains List of domain names
+ */
+sss_sifp_error
+sss_sifp_list_domains(sss_sifp_ctx *ctx,
+ char ***_domains);
+
+/**
+ * @brief Fetch all information about domain by name.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in] name Domain name
+ * @param[out] _domain Domain object
+ */
+sss_sifp_error
+sss_sifp_fetch_domain_by_name(sss_sifp_ctx *ctx,
+ const char *name,
+ sss_sifp_object **_domain);
+
+/**
+ * @brief Fetch all information about user by uid.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in] uid User ID
+ * @param[out] _user User object
+ */
+sss_sifp_error
+sss_sifp_fetch_user_by_uid(sss_sifp_ctx *ctx,
+ uid_t uid,
+ sss_sifp_object **_user);
+
+/**
+ * @brief Fetch all information about user by name.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in] name User name
+ * @param[out] _user User object
+ */
+sss_sifp_error
+sss_sifp_fetch_user_by_name(sss_sifp_ctx *ctx,
+ const char *name,
+ sss_sifp_object **_user);
+
+/**
+ * @}
+ */
+
+#endif /* SSS_SIFP_H_ */
diff --git a/src/lib/sifp/sss_sifp_attrs.c b/src/lib/sifp/sss_sifp_attrs.c
new file mode 100644
index 0000000..8bc9c5d
--- /dev/null
+++ b/src/lib/sifp/sss_sifp_attrs.c
@@ -0,0 +1,317 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2014 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+
+#include "lib/sifp/sss_sifp.h"
+#include "lib/sifp/sss_sifp_private.h"
+
+#define GET_ATTR(attrs, name, rtype, field, out, ret) do { \
+ sss_sifp_attr *attr = sss_sifp_find_attr(attrs, name); \
+ \
+ if (attr == NULL) { \
+ ret = SSS_SIFP_ATTR_MISSING; \
+ break; \
+ } \
+ \
+ if (attr->type != rtype) { \
+ ret = SSS_SIFP_INCORRECT_TYPE; \
+ break; \
+ } \
+ \
+ if (attr->data.field == NULL) { \
+ ret = SSS_SIFP_ATTR_NULL; \
+ break; \
+ } \
+ \
+ out = attr->data.field[0]; \
+ \
+ ret = SSS_SIFP_OK; \
+} while (0)
+
+#define GET_ATTR_ARRAY(attrs, name, rtype, field, out_num, out_val, ret) \
+do { \
+ sss_sifp_attr *attr = sss_sifp_find_attr(attrs, name); \
+ \
+ if (attr == NULL) { \
+ ret = SSS_SIFP_ATTR_MISSING; \
+ break; \
+ } \
+ \
+ if (attr->type != rtype) { \
+ ret = SSS_SIFP_INCORRECT_TYPE; \
+ break; \
+ } \
+ \
+ if (attr->data.field == NULL) { \
+ out_num = 0; \
+ out_val = NULL; \
+ ret = SSS_SIFP_ATTR_NULL; \
+ break; \
+ } \
+ \
+ out_num = attr->num_values; \
+ out_val = attr->data.field; \
+ \
+ ret = SSS_SIFP_OK; \
+} while (0)
+
+static sss_sifp_attr *sss_sifp_find_attr(sss_sifp_attr **attrs,
+ const char *name)
+{
+ int i;
+
+ if (attrs == NULL || name == NULL) {
+ return NULL;
+ }
+
+ for (i = 0; attrs[i] != NULL; i++) {
+ if (strcmp(attrs[i]->name, name) == 0) {
+ return attrs[i];
+ }
+ }
+
+ return NULL;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_bool(sss_sifp_attr **attrs,
+ const char *name,
+ bool *_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR(attrs, name, SSS_SIFP_ATTR_TYPE_BOOL, boolean, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_int16(sss_sifp_attr **attrs,
+ const char *name,
+ int16_t *_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR(attrs, name, SSS_SIFP_ATTR_TYPE_INT16, int16, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_uint16(sss_sifp_attr **attrs,
+ const char *name,
+ uint16_t *_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR(attrs, name, SSS_SIFP_ATTR_TYPE_UINT16, uint16, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_int32(sss_sifp_attr **attrs,
+ const char *name,
+ int32_t *_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR(attrs, name, SSS_SIFP_ATTR_TYPE_INT32, int32, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_uint32(sss_sifp_attr **attrs,
+ const char *name,
+ uint32_t *_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR(attrs, name, SSS_SIFP_ATTR_TYPE_UINT32, uint32, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_int64(sss_sifp_attr **attrs,
+ const char *name,
+ int64_t *_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR(attrs, name, SSS_SIFP_ATTR_TYPE_INT64, int64, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_uint64(sss_sifp_attr **attrs,
+ const char *name,
+ uint64_t *_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR(attrs, name, SSS_SIFP_ATTR_TYPE_UINT64, uint64, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_string(sss_sifp_attr **attrs,
+ const char *name,
+ const char **_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ const char *value = NULL;
+
+ GET_ATTR(attrs, name, SSS_SIFP_ATTR_TYPE_STRING, str, value, ret);
+
+ if (ret == SSS_SIFP_ATTR_NULL) {
+ *_value = NULL;
+ return ret;
+ }
+
+ *_value = value;
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_string_dict(sss_sifp_attr **attrs,
+ const char *name,
+ hash_table_t **_value)
+{
+ sss_sifp_attr *attr = sss_sifp_find_attr(attrs, name);
+
+ if (attr == NULL) {
+ return SSS_SIFP_ATTR_MISSING;
+ }
+
+ if (attr->type != SSS_SIFP_ATTR_TYPE_STRING_DICT) {
+ return SSS_SIFP_INCORRECT_TYPE;
+ }
+
+ if (attr->data.str_dict == NULL) {
+ *_value = NULL;
+ return SSS_SIFP_ATTR_NULL;
+ }
+
+ *_value = attr->data.str_dict;
+
+ return SSS_SIFP_OK;
+}
+
+/**
+ * @brief Find attribute in list and return its values.
+ *
+ * @param[in] attrs Attributes
+ * @param[in] name Name of the attribute to find
+ *
+ * @return Attribute values or NULL if it is not found.
+ */
+sss_sifp_error
+sss_sifp_find_attr_as_bool_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ bool **_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR_ARRAY(attrs, name, SSS_SIFP_ATTR_TYPE_BOOL, boolean,
+ *_num_values, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_int16_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ int16_t **_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR_ARRAY(attrs, name, SSS_SIFP_ATTR_TYPE_INT16, int16,
+ *_num_values, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_uint16_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ uint16_t **_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR_ARRAY(attrs, name, SSS_SIFP_ATTR_TYPE_UINT16, uint16,
+ *_num_values, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_int32_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ int32_t **_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR_ARRAY(attrs, name, SSS_SIFP_ATTR_TYPE_INT32, int32,
+ *_num_values, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_uint32_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ uint32_t **_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR_ARRAY(attrs, name, SSS_SIFP_ATTR_TYPE_UINT32, uint32,
+ *_num_values, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_int64_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ int64_t **_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR_ARRAY(attrs, name, SSS_SIFP_ATTR_TYPE_INT64, int64,
+ *_num_values, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_uint64_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ uint64_t **_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ GET_ATTR_ARRAY(attrs, name, SSS_SIFP_ATTR_TYPE_UINT64, uint64,
+ *_num_values, *_value, ret);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_find_attr_as_string_array(sss_sifp_attr **attrs,
+ const char *name,
+ unsigned int *_num_values,
+ const char * const **_value)
+{
+ sss_sifp_error ret = SSS_SIFP_ATTR_MISSING;
+ char **value = NULL;
+
+ GET_ATTR_ARRAY(attrs, name, SSS_SIFP_ATTR_TYPE_STRING, str,
+ *_num_values, value, ret);
+
+ if (ret == SSS_SIFP_OK || ret == SSS_SIFP_ATTR_NULL) {
+ *_value = (const char * const *)value;
+ }
+
+ return ret;
+}
diff --git a/src/lib/sifp/sss_sifp_common.c b/src/lib/sifp/sss_sifp_common.c
new file mode 100644
index 0000000..9dade5e
--- /dev/null
+++ b/src/lib/sifp/sss_sifp_common.c
@@ -0,0 +1,183 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2014 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <dbus/dbus.h>
+
+#include "lib/sifp/sss_sifp.h"
+#include "lib/sifp/sss_sifp_dbus.h"
+#include "lib/sifp/sss_sifp_private.h"
+#include "responder/ifp/ifp_iface/ifp_iface.h"
+
+#define SSS_SIFP_ATTR_NAME "name"
+
+static sss_sifp_error
+sss_sifp_fetch_object_by_attr(sss_sifp_ctx *ctx,
+ const char *path,
+ const char *iface_find,
+ const char *iface_object,
+ const char *method,
+ int attr_type,
+ const void *attr,
+ sss_sifp_object **_object)
+{
+ sss_sifp_object *object = NULL;
+ char *object_path = NULL;
+ sss_sifp_error ret;
+
+ if (method == NULL || attr == NULL || attr_type == DBUS_TYPE_INVALID) {
+ return SSS_SIFP_INVALID_ARGUMENT;
+ }
+
+ ret = sss_sifp_invoke_find_ex(ctx, path, iface_find, method, &object_path,
+ attr_type, attr, DBUS_TYPE_INVALID);
+ if (ret != SSS_SIFP_OK) {
+ goto done;
+ }
+
+ ret = sss_sifp_fetch_object(ctx, object_path, iface_object, &object);
+ if (ret != SSS_SIFP_OK) {
+ goto done;
+ }
+
+ *_object = object;
+
+ ret = SSS_SIFP_OK;
+
+done:
+ sss_sifp_free_string(ctx, &object_path);
+
+ return ret;
+}
+
+static sss_sifp_error
+sss_sifp_fetch_object_by_name(sss_sifp_ctx *ctx,
+ const char *path,
+ const char *iface_find,
+ const char *iface_object,
+ const char *method,
+ const char *name,
+ sss_sifp_object **_object)
+{
+ return sss_sifp_fetch_object_by_attr(ctx, path, iface_find, iface_object,
+ method, DBUS_TYPE_STRING, &name,
+ _object);
+}
+
+sss_sifp_error
+sss_sifp_list_domains(sss_sifp_ctx *ctx,
+ char ***_domains)
+{
+ sss_sifp_attr **attrs = NULL;
+ char **object_paths = NULL;
+ char **domains = NULL;
+ const char *name = NULL;
+ unsigned int size;
+ unsigned int i;
+ sss_sifp_error ret;
+
+ if (_domains == NULL) {
+ return SSS_SIFP_INVALID_ARGUMENT;
+ }
+
+ ret = sss_sifp_invoke_list_ex(ctx, IFP_PATH, "org.freedesktop.sssd.infopipe", "Domains",
+ &object_paths, DBUS_TYPE_INVALID);
+ if (ret != SSS_SIFP_OK) {
+ goto done;
+ }
+
+ /* calculate number of paths acquired and allocate memory for domains */
+ for (size = 0; object_paths[size] != NULL; size++);
+
+ domains = _alloc_zero(ctx, char *, size + 1);
+ if (domains == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ /* fetch domain name */
+ for (i = 0; i < size; i++) {
+ ret = sss_sifp_fetch_attr(ctx, object_paths[i], "org.freedesktop.sssd.infopipe.Domains",
+ SSS_SIFP_ATTR_NAME, &attrs);
+ if (ret != SSS_SIFP_OK) {
+ goto done;
+ }
+
+ ret = sss_sifp_find_attr_as_string(attrs, SSS_SIFP_ATTR_NAME, &name);
+ if (ret != SSS_SIFP_OK) {
+ goto done;
+ }
+
+ domains[i] = sss_sifp_strdup(ctx, name);
+ if (domains[i] == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ sss_sifp_free_attrs(ctx, &attrs);
+ }
+
+ domains[i] = NULL;
+
+ *_domains = domains;
+
+ ret = SSS_SIFP_OK;
+
+done:
+ sss_sifp_free_attrs(ctx, &attrs);
+ sss_sifp_free_string_array(ctx, &object_paths);
+
+ if (ret != SSS_SIFP_OK) {
+ sss_sifp_free_string_array(ctx, &domains);
+ }
+
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_fetch_domain_by_name(sss_sifp_ctx *ctx,
+ const char *name,
+ sss_sifp_object **_domain)
+{
+ return sss_sifp_fetch_object_by_name(ctx, IFP_PATH, "org.freedesktop.sssd.infopipe",
+ "org.freedesktop.sssd.infopipe.Domains", "DomainByName",
+ name, _domain);
+}
+
+sss_sifp_error
+sss_sifp_fetch_user_by_uid(sss_sifp_ctx *ctx,
+ uid_t uid,
+ sss_sifp_object **_user)
+{
+ uint64_t _uid = uid;
+
+ return sss_sifp_fetch_object_by_attr(ctx, IFP_PATH_USERS, "org.freedesktop.sssd.infopipe.Users",
+ "org.freedesktop.sssd.infopipe.Users.User", "ByID",
+ DBUS_TYPE_UINT64, &_uid, _user);
+}
+
+sss_sifp_error
+sss_sifp_fetch_user_by_name(sss_sifp_ctx *ctx,
+ const char *name,
+ sss_sifp_object **_user)
+{
+ return sss_sifp_fetch_object_by_name(ctx, IFP_PATH_USERS, "org.freedesktop.sssd.infopipe.Users",
+ "org.freedesktop.sssd.infopipe.Users.User", "ByName",
+ name, _user);
+}
diff --git a/src/lib/sifp/sss_sifp_dbus.c b/src/lib/sifp/sss_sifp_dbus.c
new file mode 100644
index 0000000..2906c5a
--- /dev/null
+++ b/src/lib/sifp/sss_sifp_dbus.c
@@ -0,0 +1,275 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2014 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <dbus/dbus.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib/sifp/sss_sifp.h"
+#include "lib/sifp/sss_sifp_dbus.h"
+#include "lib/sifp/sss_sifp_private.h"
+
+static sss_sifp_error sss_sifp_ifp_call(sss_sifp_ctx *ctx,
+ const char *object_path,
+ const char *interface,
+ const char *method,
+ int first_arg_type,
+ va_list ap,
+ DBusMessage **_reply)
+{
+ DBusMessage *msg = NULL;
+ sss_sifp_error ret;
+ dbus_bool_t bret;
+
+ if (object_path == NULL || interface == NULL || method == NULL) {
+ return SSS_SIFP_INVALID_ARGUMENT;
+ }
+
+ msg = sss_sifp_create_message(object_path, interface, method);
+ if (msg == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ if (first_arg_type != DBUS_TYPE_INVALID) {
+ bret = dbus_message_append_args_valist(msg, first_arg_type, ap);
+ if (!bret) {
+ ret = SSS_SIFP_IO_ERROR;
+ goto done;
+ }
+ }
+
+ ret = sss_sifp_send_message(ctx, msg, _reply);
+
+done:
+ if (msg != NULL) {
+ dbus_message_unref(msg);
+ }
+
+ return ret;
+}
+
+DBusMessage *
+sss_sifp_create_message(const char *object_path,
+ const char *interface,
+ const char *method)
+{
+ return dbus_message_new_method_call(SSS_SIFP_ADDRESS, object_path,
+ interface, method);
+}
+
+sss_sifp_error
+sss_sifp_send_message(sss_sifp_ctx *ctx,
+ DBusMessage *msg,
+ DBusMessage **_reply)
+{
+ return sss_sifp_send_message_ex(ctx, msg, 5000, _reply);
+}
+
+sss_sifp_error
+sss_sifp_send_message_ex(sss_sifp_ctx *ctx,
+ DBusMessage *msg,
+ int timeout,
+ DBusMessage **_reply)
+{
+ DBusMessage *reply = NULL;
+ DBusError dbus_error;
+ sss_sifp_error ret;
+
+ if (ctx == NULL || msg == NULL) {
+ return SSS_SIFP_INVALID_ARGUMENT;
+ }
+
+ dbus_error_init(&dbus_error);
+
+ reply = dbus_connection_send_with_reply_and_block(ctx->conn, msg,
+ timeout, &dbus_error);
+ if (dbus_error_is_set(&dbus_error)) {
+ sss_sifp_set_io_error(ctx, &dbus_error);
+ ret = SSS_SIFP_IO_ERROR;
+ goto done;
+ }
+
+ if (_reply == NULL) {
+ dbus_message_unref(reply);
+ } else {
+ *_reply = reply;
+ }
+
+ ret = SSS_SIFP_OK;
+
+done:
+ dbus_error_free(&dbus_error);
+ return ret;
+}
+
+static sss_sifp_error
+sss_sifp_invoke_list_va(sss_sifp_ctx *ctx,
+ const char *object_path,
+ const char *interface,
+ const char *method,
+ char ***_object_paths,
+ int first_arg_type,
+ va_list ap)
+{
+ DBusMessage *reply = NULL;
+ char *dbus_method = NULL;
+ sss_sifp_error ret;
+
+ if (ctx == NULL || method == NULL || _object_paths == NULL) {
+ return SSS_SIFP_INVALID_ARGUMENT;
+ }
+
+ dbus_method = sss_sifp_strcat(ctx, "List", method);
+ if (dbus_method == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ ret = sss_sifp_ifp_call(ctx, object_path, interface, dbus_method,
+ first_arg_type, ap, &reply);
+ if (ret != SSS_SIFP_OK) {
+ goto done;
+ }
+
+ ret = sss_sifp_parse_object_path_list(ctx, reply, _object_paths);
+
+done:
+ sss_sifp_free_string(ctx, &dbus_method);
+
+ if (reply != NULL) {
+ dbus_message_unref(reply);
+ }
+
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_invoke_list_ex(sss_sifp_ctx *ctx,
+ const char *object_path,
+ const char *interface,
+ const char *method,
+ char ***_object_paths,
+ int first_arg_type,
+ ...)
+{
+ va_list ap;
+ sss_sifp_error ret;
+
+ va_start(ap, first_arg_type);
+ ret = sss_sifp_invoke_list_va(ctx, object_path, interface, method,
+ _object_paths, first_arg_type, ap);
+ va_end(ap);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_invoke_list(sss_sifp_ctx *ctx,
+ const char *method,
+ char ***_object_paths,
+ int first_arg_type,
+ ...)
+{
+ va_list ap;
+ sss_sifp_error ret;
+
+ va_start(ap, first_arg_type);
+ ret = sss_sifp_invoke_list_ex(ctx, SSS_SIFP_PATH, SSS_SIFP_IFACE, method,
+ _object_paths, first_arg_type, ap);
+ va_end(ap);
+ return ret;
+}
+
+static sss_sifp_error
+sss_sifp_invoke_find_va(sss_sifp_ctx *ctx,
+ const char *object_path,
+ const char *interface,
+ const char *method,
+ char **_object_path,
+ int first_arg_type,
+ va_list ap)
+{
+ DBusMessage *reply = NULL;
+ char *dbus_method = NULL;
+ sss_sifp_error ret;
+
+ if (ctx == NULL || method == NULL || _object_path == NULL) {
+ return SSS_SIFP_INVALID_ARGUMENT;
+ }
+
+ dbus_method = sss_sifp_strcat(ctx, "Find", method);
+ if (dbus_method == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ ret = sss_sifp_ifp_call(ctx, object_path, interface, dbus_method,
+ first_arg_type, ap, &reply);
+ if (ret != SSS_SIFP_OK) {
+ goto done;
+ }
+
+ ret = sss_sifp_parse_object_path(ctx, reply, _object_path);
+
+done:
+ sss_sifp_free_string(ctx, &dbus_method);
+
+ if (reply != NULL) {
+ dbus_message_unref(reply);
+ }
+
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_invoke_find_ex(sss_sifp_ctx *ctx,
+ const char *object_path,
+ const char *interface,
+ const char *method,
+ char **_object_path,
+ int first_arg_type,
+ ...)
+{
+ va_list ap;
+ sss_sifp_error ret;
+
+ va_start(ap, first_arg_type);
+ ret = sss_sifp_invoke_find_va(ctx, object_path, interface, method,
+ _object_path, first_arg_type, ap);
+ va_end(ap);
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_invoke_find(sss_sifp_ctx *ctx,
+ const char *method,
+ char **_object_path,
+ int first_arg_type,
+ ...)
+{
+ va_list ap;
+ sss_sifp_error ret;
+
+ va_start(ap, first_arg_type);
+ ret = sss_sifp_invoke_find_va(ctx, SSS_SIFP_PATH, SSS_SIFP_IFACE, method,
+ _object_path, first_arg_type, ap);
+ va_end(ap);
+ return ret;
+}
diff --git a/src/lib/sifp/sss_sifp_dbus.h b/src/lib/sifp/sss_sifp_dbus.h
new file mode 100644
index 0000000..875d781
--- /dev/null
+++ b/src/lib/sifp/sss_sifp_dbus.h
@@ -0,0 +1,174 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2014 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SSS_SIFP_DBUS_H_
+#define SSS_SIFP_DBUS_H_
+
+#include <sss_sifp.h>
+#include <dbus/dbus.h>
+
+/**
+ * @defgroup sss_sifp_dbus Advanced InfoPipe method calls.
+ *
+ * Functions in this module provide a way to reuse sss_sifp connection
+ * to the SSSD's InfoPipe responder.
+ *
+ * This allows the caller to send more sophisticated messages to the InfoPipe
+ * and to use both sss_sifp and D-Bus without the need of maintaining two
+ * separate D-Bus connections.
+ *
+ * However, these functions require the caller to understand the D-Bus
+ * bindings from libdbus.
+ *
+ * @{
+ */
+
+/**
+ * @brief Create a new method call message for SSSD InfoPipe bus.
+ *
+ * @param[in] object_path D-Bus object path
+ * @param[in] interface D-Bus interface
+ * @param[in] method D-Bus method
+ *
+ * @return D-Bus message.
+ */
+DBusMessage *
+sss_sifp_create_message(const char *object_path,
+ const char *interface,
+ const char *method);
+
+/**
+ * @brief Send D-Bus message to SSSD InfoPipe bus with 5 seconds timeout.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in] msg D-Bus message
+ * @param[in] _reply D-Bus reply, may be NULL if the caller is not interested
+ *
+ * @return D-Bus message.
+ */
+sss_sifp_error
+sss_sifp_send_message(sss_sifp_ctx *ctx,
+ DBusMessage *msg,
+ DBusMessage **_reply);
+
+/**
+ * @brief Send D-Bus message to SSSD InfoPipe bus.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in] msg D-Bus message
+ * @param[in] timeout Timeout
+ * @param[in] _reply D-Bus reply, may be NULL if the caller is not interested
+ *
+ * @return D-Bus message.
+ */
+sss_sifp_error
+sss_sifp_send_message_ex(sss_sifp_ctx *ctx,
+ DBusMessage *msg,
+ int timeout,
+ DBusMessage **_reply);
+
+/**
+ * @brief List objects that satisfies given conditions. This routine will
+ * invoke List<method> D-Bus method on given interface and object path. If
+ * no interface or object path is given, /org/freedesktop/sssd/infopipe and
+ * org.freedesktop.sssd.infopipe is used. Arguments to this method are given
+ * as standard variadic D-Bus arguments.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in] object_path D-Bus object path
+ * @param[in] interface D-Bus interface
+ * @param[in] method D-Bus method to call without the 'List' prefix
+ * @param[out] _object_paths List of object paths
+ * @param[in] first_arg_type Type of the first D-Bus argument
+ * @param[in] ... D-Bus arguments
+ */
+sss_sifp_error
+sss_sifp_invoke_list_ex(sss_sifp_ctx *ctx,
+ const char *object_path,
+ const char *interface,
+ const char *method,
+ char ***_object_paths,
+ int first_arg_type,
+ ...);
+
+/**
+ * @brief List objects that satisfies given conditions. This routine will
+ * invoke List<method> D-Bus method on SSSD InfoPipe interface. Arguments
+ * to this method are given as standard variadic D-Bus arguments.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in] method D-Bus method to call without the 'List' prefix
+ * @param[out] _object_paths List of object paths
+ * @param[in] first_arg_type Type of the first D-Bus argument
+ * @param[in] ... D-Bus arguments
+ */
+sss_sifp_error
+sss_sifp_invoke_list(sss_sifp_ctx *ctx,
+ const char *method,
+ char ***_object_paths,
+ int first_arg_type,
+ ...);
+
+/**
+ * @brief Find single object that satisfies given conditions. This routine will
+ * invoke Find<method> D-Bus method on given interface and object path. If
+ * no interface or object path is given, /org/freedesktop/sssd/infopipe and
+ * org.freedesktop.sssd.infopipe is used. Arguments to this method are given
+ * as standard variadic D-Bus arguments.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in] object_path D-Bus object path
+ * @param[in] interface D-Bus interface
+ * @param[in] method D-Bus method to call without the 'Find' prefix
+ * @param[out] _object_path Object path
+ * @param[in] first_arg_type Type of the first D-Bus argument
+ * @param[in] ... D-Bus arguments
+ */
+sss_sifp_error
+sss_sifp_invoke_find_ex(sss_sifp_ctx *ctx,
+ const char *object_path,
+ const char *interface,
+ const char *method,
+ char **_object_path,
+ int first_arg_type,
+ ...);
+
+/**
+ * @brief Find single object that satisfies given conditions. This routine will
+ * invoke Find<method> D-Bus method on SSSD InfoPipe interface. Arguments
+ * to this method are given as standard variadic D-Bus arguments.
+ *
+ * @param[in] ctx sss_sifp context
+ * @param[in] method D-Bus method to call without the 'Find' prefix
+ * @param[out] _object_path Object path
+ * @param[in] first_arg_type Type of the first D-Bus argument
+ * @param[in] ... D-Bus arguments
+ */
+sss_sifp_error
+sss_sifp_invoke_find(sss_sifp_ctx *ctx,
+ const char *method,
+ char **_object_path,
+ int first_arg_type,
+ ...);
+
+/**
+ * @}
+ */
+#endif /* SSS_SIFP_DBUS_H_ */
diff --git a/src/lib/sifp/sss_sifp_parser.c b/src/lib/sifp/sss_sifp_parser.c
new file mode 100644
index 0000000..150bd56
--- /dev/null
+++ b/src/lib/sifp/sss_sifp_parser.c
@@ -0,0 +1,723 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2014 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <dbus/dbus.h>
+#include <string.h>
+#include <dhash.h>
+
+#include "lib/sifp/sss_sifp.h"
+#include "lib/sifp/sss_sifp_private.h"
+
+#define check_dbus_arg(iter, type, ret, done) do { \
+ if (dbus_message_iter_get_arg_type((iter)) != (type)) { \
+ ret = SSS_SIFP_INTERNAL_ERROR; \
+ goto done; \
+ } \
+} while (0)
+
+#define parse_basic(ctx, iter, ret, attr_type, dbus_type, \
+ data_type, field, done) \
+do { \
+ dbus_type val; \
+ dbus_message_iter_get_basic(iter, &val); \
+ attr->type = attr_type; \
+ attr->data.field = _alloc_zero(ctx, data_type, 1); \
+ \
+ if (attr->data.field == NULL) { \
+ ret = SSS_SIFP_OUT_OF_MEMORY; \
+ goto done; \
+ } \
+ \
+ attr->data.field[0] = val; \
+ attr->num_values = 1; \
+ \
+ ret = SSS_SIFP_OK; \
+} while (0)
+
+#define parse_array(ctx, iter, ret, attr_type, dbus_type, \
+ data_type, field, done) \
+do { \
+ dbus_type val; \
+ unsigned int i; \
+ \
+ attr->type = attr_type; \
+ if (attr->num_values == 0) { \
+ attr->data.field = NULL; \
+ ret = SSS_SIFP_OK; \
+ goto done; \
+ } \
+ \
+ attr->data.field = _alloc_zero(ctx, data_type, attr->num_values); \
+ if (attr->data.field == NULL) { \
+ ret = SSS_SIFP_OUT_OF_MEMORY; \
+ goto done; \
+ } \
+ \
+ for (i = 0; i < attr->num_values; i++) { \
+ dbus_message_iter_get_basic(iter, &val); \
+ attr->data.field[i] = val; \
+ \
+ if (!dbus_message_iter_next(iter) && i + 1 < attr->num_values) { \
+ ret = SSS_SIFP_INTERNAL_ERROR; \
+ goto done; \
+ } \
+ } \
+ \
+ ret = SSS_SIFP_OK; \
+} while (0)
+
+static unsigned int
+sss_sifp_get_array_length(DBusMessageIter *iter)
+{
+ DBusMessageIter array_iter;
+ unsigned int size;
+
+ dbus_message_iter_recurse(iter, &array_iter);
+
+ if (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_INVALID) {
+ return 0;
+ }
+
+ size = 0;
+ do {
+ size++;
+ } while (dbus_message_iter_next(&array_iter));
+
+ return size;
+}
+
+static void hash_delete_cb(hash_entry_t *item,
+ hash_destroy_enum type,
+ void *pvt)
+{
+ sss_sifp_ctx *ctx = (sss_sifp_ctx*)pvt;
+ char **values = (char**)(item->value.ptr);
+ int i;
+
+ if (values == NULL) {
+ return;
+ }
+
+ for (i = 0; values[i] != NULL; i++) {
+ _free(ctx, values[i]);
+ values[i] = NULL;
+ }
+
+ _free(ctx, values);
+ item->value.ptr = NULL;
+}
+
+static sss_sifp_error
+sss_sifp_parse_dict(sss_sifp_ctx *ctx,
+ DBusMessageIter *iter,
+ hash_table_t *table)
+{
+ DBusMessageIter dict_iter;
+ DBusMessageIter array_iter;
+ sss_sifp_error ret;
+ hash_key_t table_key = {0};
+ hash_value_t table_value;
+ const char *key = NULL;
+ const char *value = NULL;
+ char **values = NULL;
+ unsigned int i;
+ unsigned int num_values;
+ int hret;
+
+ dbus_message_iter_recurse(iter, &dict_iter);
+
+ /* get the key */
+ check_dbus_arg(&dict_iter, DBUS_TYPE_STRING, ret, done);
+ dbus_message_iter_get_basic(&dict_iter, &key);
+
+ table_key.type = HASH_KEY_STRING;
+ table_key.str = sss_sifp_strdup(ctx, key);
+ if (table_key.str == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ if (!dbus_message_iter_next(&dict_iter)) {
+ ret = SSS_SIFP_INTERNAL_ERROR;
+ goto done;
+ }
+
+ /* now read the value */
+ switch (dbus_message_iter_get_arg_type(&dict_iter)) {
+ case DBUS_TYPE_STRING:
+ dbus_message_iter_get_basic(&dict_iter, &value);
+ values = _alloc_zero(ctx, char *, 2);
+ if (values == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ values[0] = sss_sifp_strdup(ctx, value);
+ if (values[0] == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ values[1] = NULL;
+
+ ret = SSS_SIFP_OK;
+ break;
+ case DBUS_TYPE_ARRAY:
+ num_values = sss_sifp_get_array_length(&dict_iter);
+ if (num_values == 0) {
+ values = NULL;
+ ret = SSS_SIFP_OK;
+ goto done;
+ }
+
+ if (dbus_message_iter_get_element_type(&dict_iter)
+ != DBUS_TYPE_STRING) {
+ ret = SSS_SIFP_NOT_SUPPORTED;
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&dict_iter, &array_iter);
+
+ values = _alloc_zero(ctx, char*, num_values + 1);
+ if (values == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < num_values; i++) {
+ dbus_message_iter_get_basic(&array_iter, &value);
+ values[i] = sss_sifp_strdup(ctx, value);
+ if (values[i] == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ dbus_message_iter_next(&array_iter);
+ }
+
+ ret = SSS_SIFP_OK;
+ break;
+ default:
+ ret = SSS_SIFP_NOT_SUPPORTED;
+ break;
+ }
+
+ table_value.type = HASH_VALUE_PTR;
+ table_value.ptr = values;
+
+ hret = hash_enter(table, &table_key, &table_value);
+ if (hret == HASH_ERROR_NO_MEMORY) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ } else if (hret != HASH_SUCCESS) {
+ ret = SSS_SIFP_INTERNAL_ERROR;
+ }
+
+done:
+ if (table_key.str != NULL) {
+ _free(ctx, table_key.str);
+ }
+
+ if (ret != SSS_SIFP_OK) {
+ if (values != NULL) {
+ for (i = 0; values[i] != NULL; i++) {
+ _free(ctx, values[i]);
+ }
+ _free(ctx, values);
+ }
+ }
+
+ return ret;
+}
+
+static sss_sifp_error
+sss_sifp_parse_basic(sss_sifp_ctx *ctx,
+ DBusMessageIter *iter,
+ sss_sifp_attr *attr)
+{
+ sss_sifp_error ret;
+
+ switch (dbus_message_iter_get_arg_type(iter)) {
+ case DBUS_TYPE_BOOLEAN:
+ parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_BOOL,
+ dbus_bool_t, bool, boolean, done);
+ break;
+ case DBUS_TYPE_INT16:
+ parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_INT16,
+ int16_t, int16_t, int16, done);
+ break;
+ case DBUS_TYPE_UINT16:
+ parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_UINT16,
+ uint16_t, uint16_t, uint16, done);
+ break;
+ case DBUS_TYPE_INT32:
+ parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_INT32,
+ int32_t, int32_t, int32, done);
+ break;
+ case DBUS_TYPE_UINT32:
+ parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_UINT32,
+ uint32_t, uint32_t, uint32, done);
+ break;
+ case DBUS_TYPE_INT64:
+ parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_INT64,
+ int64_t, int64_t, int64, done);
+ break;
+ case DBUS_TYPE_UINT64:
+ parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_UINT64,
+ uint64_t, uint64_t, uint64, done);
+ break;
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ {
+ const char *val = NULL;
+
+ dbus_message_iter_get_basic(iter, &val);
+
+ attr->type = SSS_SIFP_ATTR_TYPE_STRING;
+ attr->data.str = _alloc_zero(ctx, char*, 1);
+ if (attr->data.str == NULL) { \
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ attr->data.str[0] = sss_sifp_strdup(ctx, val);
+ if (attr->data.str[0] == NULL) {
+ _free(ctx, attr->data.str);
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ attr->num_values = 1;
+
+ ret = SSS_SIFP_OK;
+ break;
+ }
+ default:
+ ret = SSS_SIFP_INVALID_ARGUMENT;
+ break;
+ }
+
+done:
+ return ret;
+}
+
+static sss_sifp_error
+sss_sifp_parse_array(sss_sifp_ctx *ctx,
+ DBusMessageIter *iter,
+ sss_sifp_attr *attr)
+{
+ DBusMessageIter array_iter;
+ sss_sifp_error ret;
+ int hret;
+
+ attr->num_values = sss_sifp_get_array_length(iter);
+ dbus_message_iter_recurse(iter, &array_iter);
+
+ switch (dbus_message_iter_get_element_type(iter)) {
+ case DBUS_TYPE_BOOLEAN:
+ parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_BOOL,
+ dbus_bool_t, bool, boolean, done);
+ break;
+ case DBUS_TYPE_INT16:
+ parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_INT16,
+ int16_t, int16_t, int16, done);
+ break;
+ case DBUS_TYPE_UINT16:
+ parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_UINT16,
+ uint16_t, uint16_t, uint16, done);
+ break;
+ case DBUS_TYPE_INT32:
+ parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_INT32,
+ int32_t, int32_t, int32, done);
+ break;
+ case DBUS_TYPE_UINT32:
+ parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_UINT32,
+ uint32_t, uint32_t, uint32, done);
+ break;
+ case DBUS_TYPE_INT64:
+ parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_INT64,
+ int64_t, int64_t, int64, done);
+ break;
+ case DBUS_TYPE_UINT64:
+ parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_UINT64,
+ uint64_t, uint64_t, uint64, done);
+ break;
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH: ;
+ const char *val;
+ unsigned int i;
+
+ attr->type = SSS_SIFP_ATTR_TYPE_STRING;
+ if (attr->num_values == 0) {
+ attr->data.str = NULL;
+ ret = SSS_SIFP_OK;
+ goto done;
+ }
+
+ attr->data.str = _alloc_zero(ctx, char *, attr->num_values);
+ if (attr->data.str == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < attr->num_values; i++) {
+ dbus_message_iter_get_basic(&array_iter, &val);
+ attr->data.str[i] = sss_sifp_strdup(ctx, val);
+ if (attr->data.str[i] == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ if (!dbus_message_iter_next(&array_iter)
+ && i + 1 < attr->num_values) {
+ ret = SSS_SIFP_INTERNAL_ERROR;
+ goto done;
+ }
+ }
+
+ ret = SSS_SIFP_OK;
+ break;
+ case DBUS_TYPE_DICT_ENTRY:
+ attr->type = SSS_SIFP_ATTR_TYPE_STRING_DICT;
+ if (attr->num_values == 0) {
+ attr->data.str_dict = NULL;
+ ret = SSS_SIFP_OK;
+ goto done;
+ }
+
+ hret = hash_create_ex(0, &(attr->data.str_dict), 0, 0, 0, 0,
+ ctx->alloc_fn, ctx->free_fn, ctx->alloc_pvt,
+ hash_delete_cb, ctx);
+ if (hret != HASH_SUCCESS) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < attr->num_values; i++) {
+ ret = sss_sifp_parse_dict(ctx, &array_iter, attr->data.str_dict);
+ if (ret != SSS_SIFP_OK) {
+ _free(ctx, attr->data.str_dict);
+ goto done;
+ }
+
+ if (!dbus_message_iter_next(&array_iter)
+ && i + 1 < attr->num_values) {
+ ret = SSS_SIFP_INTERNAL_ERROR;
+ goto done;
+ }
+ }
+
+ ret = SSS_SIFP_OK;
+ break;
+ default:
+ ret = SSS_SIFP_INVALID_ARGUMENT;
+ break;
+ }
+
+done:
+ if (ret != SSS_SIFP_OK) {
+ if (attr->type == SSS_SIFP_ATTR_TYPE_STRING && attr->data.str != NULL) {
+ for (unsigned int i = 0;
+ (i < attr->num_values) && (attr->data.str[i] != NULL);
+ i++) {
+ _free(ctx, attr->data.str[i]);
+ }
+ _free(ctx, attr->data.str);
+ } else if (attr->type == SSS_SIFP_ATTR_TYPE_STRING_DICT
+ && attr->data.str_dict != NULL) {
+ hash_destroy(attr->data.str_dict);
+ attr->data.str_dict = NULL;
+ }
+ }
+
+ return ret;
+}
+
+static sss_sifp_error
+sss_sifp_parse_variant(sss_sifp_ctx *ctx,
+ DBusMessageIter *iter,
+ sss_sifp_attr *attr)
+{
+ DBusMessageIter variant_iter;
+ sss_sifp_error ret;
+ int type;
+
+ check_dbus_arg(iter, DBUS_TYPE_VARIANT, ret, done);
+
+ dbus_message_iter_recurse(iter, &variant_iter);
+
+ type = dbus_message_iter_get_arg_type(&variant_iter);
+ if (dbus_type_is_basic(type)) {
+ ret = sss_sifp_parse_basic(ctx, &variant_iter, attr);
+ } else {
+ /* container types */
+ switch (type) {
+ /* case DBUS_TYPE_DICT_ENTRY may only be contained within an array
+ * in variant */
+ case DBUS_TYPE_ARRAY:
+ ret = sss_sifp_parse_array(ctx, &variant_iter, attr);
+ break;
+ default:
+ ret = SSS_SIFP_NOT_SUPPORTED;
+ break;
+ }
+ }
+
+done:
+ return ret;
+}
+
+/**
+ * DBusMessage format:
+ * variant:value
+ *
+ * Iterator has to point to the variant but not inside the variant.
+ */
+static sss_sifp_error
+sss_sifp_parse_single_attr(sss_sifp_ctx *ctx,
+ const char *name,
+ DBusMessageIter *iter,
+ sss_sifp_attr **_attr)
+{
+ sss_sifp_attr *attr = NULL;
+ sss_sifp_error ret;
+
+ attr = _alloc_zero(ctx, sss_sifp_attr, 1);
+ if (attr == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ attr->name = sss_sifp_strdup(ctx, name);
+ if (attr->name == NULL) {
+ _free(ctx, attr);
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ ret = sss_sifp_parse_variant(ctx, iter, attr);
+ if (ret != SSS_SIFP_OK) {
+ _free(ctx, attr->name);
+ _free(ctx, attr);
+ }
+
+ *_attr = attr;
+
+done:
+ return ret;
+}
+
+/**
+ * DBusMessage format:
+ * variant:value
+ */
+sss_sifp_error
+sss_sifp_parse_attr(sss_sifp_ctx *ctx,
+ const char *name,
+ DBusMessage *msg,
+ sss_sifp_attr ***_attrs)
+{
+ sss_sifp_attr **attrs = NULL;
+ DBusMessageIter iter;
+ sss_sifp_error ret;
+
+ dbus_message_iter_init(msg, &iter);
+
+ attrs = _alloc_zero(ctx, sss_sifp_attr *, 2);
+ if (attrs == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ ret = sss_sifp_parse_single_attr(ctx, name, &iter, &attrs[0]);
+ if (ret != SSS_SIFP_OK) {
+ goto done;
+ }
+
+ *_attrs = attrs;
+
+ ret = SSS_SIFP_OK;
+
+done:
+ if (ret != SSS_SIFP_OK) {
+ sss_sifp_free_attrs(ctx, &attrs);
+ }
+
+ return ret;
+}
+
+/**
+ * DBusMessage format:
+ * array of dict_entry(string:attr_name, variant:value)
+ */
+sss_sifp_error
+sss_sifp_parse_attr_list(sss_sifp_ctx *ctx,
+ DBusMessage *msg,
+ sss_sifp_attr ***_attrs)
+{
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ DBusMessageIter dict_iter;
+ sss_sifp_attr **attrs = NULL;
+ const char *name = NULL;
+ unsigned int num_values;
+ sss_sifp_error ret;
+ unsigned int i;
+
+ dbus_message_iter_init(msg, &iter);
+
+ check_dbus_arg(&iter, DBUS_TYPE_ARRAY, ret, done);
+
+ if (dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
+ ret = SSS_SIFP_INTERNAL_ERROR;
+ goto done;
+ }
+
+ num_values = sss_sifp_get_array_length(&iter);
+ attrs = _alloc_zero(ctx, sss_sifp_attr *, num_values + 1);
+ if (attrs == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &array_iter);
+
+ for (i = 0; i < num_values; i++) {
+ dbus_message_iter_recurse(&array_iter, &dict_iter);
+
+ /* get the key */
+ check_dbus_arg(&dict_iter, DBUS_TYPE_STRING, ret, done);
+ dbus_message_iter_get_basic(&dict_iter, &name);
+
+ if (!dbus_message_iter_next(&dict_iter)) {
+ ret = SSS_SIFP_INTERNAL_ERROR;
+ goto done;
+ }
+
+ /* now read the value */
+ check_dbus_arg(&dict_iter, DBUS_TYPE_VARIANT, ret, done);
+
+ ret = sss_sifp_parse_single_attr(ctx, name, &dict_iter, &attrs[i]);
+ if (ret != SSS_SIFP_OK) {
+ goto done;
+ }
+
+ dbus_message_iter_next(&array_iter);
+ }
+
+ *_attrs = attrs;
+ ret = SSS_SIFP_OK;
+
+done:
+ if (ret != SSS_SIFP_OK) {
+ sss_sifp_free_attrs(ctx, &attrs);
+ }
+
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_parse_object_path(sss_sifp_ctx *ctx,
+ DBusMessage *msg,
+ char **_object_path)
+{
+ char *object_path = NULL;
+ const char *dbus_path = NULL;
+ DBusError dbus_error;
+ dbus_bool_t bret;
+ sss_sifp_error ret;
+
+ dbus_error_init(&dbus_error);
+
+ bret = dbus_message_get_args(msg, &dbus_error,
+ DBUS_TYPE_OBJECT_PATH, &dbus_path,
+ DBUS_TYPE_INVALID);
+ if (!bret) {
+ sss_sifp_set_io_error(ctx, &dbus_error);
+ ret = SSS_SIFP_IO_ERROR;
+ goto done;
+ }
+
+ object_path = sss_sifp_strdup(ctx, dbus_path);
+ if (object_path == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ *_object_path = object_path;
+ ret = SSS_SIFP_OK;
+
+done:
+ dbus_error_free(&dbus_error);
+
+ return ret;
+}
+
+sss_sifp_error
+sss_sifp_parse_object_path_list(sss_sifp_ctx *ctx,
+ DBusMessage *msg,
+ char ***_object_paths)
+{
+ char **object_paths = NULL;
+ char **dbus_paths = NULL;
+ int num_paths;
+ DBusError dbus_error;
+ dbus_bool_t bret;
+ sss_sifp_error ret;
+ int i;
+
+ dbus_error_init(&dbus_error);
+
+ bret = dbus_message_get_args(msg, &dbus_error,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
+ &dbus_paths, &num_paths,
+ DBUS_TYPE_INVALID);
+ if (!bret) {
+ sss_sifp_set_io_error(ctx, &dbus_error);
+ ret = SSS_SIFP_IO_ERROR;
+ goto done;
+ }
+
+ object_paths = _alloc_zero(ctx, char *, num_paths + 1);
+ if (object_paths == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < num_paths; i++) {
+ object_paths[i] = sss_sifp_strdup(ctx, dbus_paths[i]);
+ if (object_paths[i] == NULL) {
+ ret = SSS_SIFP_OUT_OF_MEMORY;
+ goto done;
+ }
+ }
+
+ *_object_paths = object_paths;
+ ret = SSS_SIFP_OK;
+
+done:
+ dbus_error_free(&dbus_error);
+ dbus_free_string_array(dbus_paths);
+
+ if (ret != SSS_SIFP_OK && object_paths != NULL) {
+ sss_sifp_free_string_array(ctx, &object_paths);
+ }
+
+ return ret;
+}
diff --git a/src/lib/sifp/sss_sifp_private.h b/src/lib/sifp/sss_sifp_private.h
new file mode 100644
index 0000000..9af8f7b
--- /dev/null
+++ b/src/lib/sifp/sss_sifp_private.h
@@ -0,0 +1,112 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2014 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SSS_SIFP_PRIVATE_H_
+#define SSS_SIFP_PRIVATE_H_
+
+#include <dbus/dbus.h>
+#include "lib/sifp/sss_sifp.h"
+
+void *sss_sifp_alloc_zero(sss_sifp_ctx *ctx, size_t size, size_t num);
+
+#define _alloc_zero(ctx, type, num) sss_sifp_alloc_zero(ctx, sizeof(type), num)
+
+#define _free(ctx, var) \
+ do { \
+ ctx->free_fn((var), ctx->alloc_pvt); \
+ (var) = NULL; \
+ } while (0)
+
+struct sss_sifp_ctx {
+ DBusConnection *conn;
+ sss_sifp_alloc_func *alloc_fn;
+ sss_sifp_free_func *free_fn;
+ void *alloc_pvt;
+
+ DBusError *io_error;
+};
+
+enum sss_sifp_attr_type {
+ SSS_SIFP_ATTR_TYPE_BOOL,
+ SSS_SIFP_ATTR_TYPE_INT16,
+ SSS_SIFP_ATTR_TYPE_UINT16,
+ SSS_SIFP_ATTR_TYPE_INT32,
+ SSS_SIFP_ATTR_TYPE_UINT32,
+ SSS_SIFP_ATTR_TYPE_INT64,
+ SSS_SIFP_ATTR_TYPE_UINT64,
+ SSS_SIFP_ATTR_TYPE_STRING,
+ SSS_SIFP_ATTR_TYPE_STRING_DICT
+};
+
+/**
+ * D-Bus object attribute
+ */
+struct sss_sifp_attr {
+ char *name;
+ enum sss_sifp_attr_type type;
+ unsigned int num_values;
+ union {
+ bool *boolean;
+ int16_t *int16;
+ uint16_t *uint16;
+ int32_t *int32;
+ uint32_t *uint32;
+ int64_t *int64;
+ uint64_t *uint64;
+ char **str;
+ hash_table_t *str_dict;
+ } data;
+};
+
+void
+sss_sifp_set_io_error(sss_sifp_ctx *ctx,
+ DBusError *error);
+
+char *
+sss_sifp_strdup(sss_sifp_ctx *ctx,
+ const char *str);
+
+char *
+sss_sifp_strcat(sss_sifp_ctx *ctx,
+ const char *str1,
+ const char *str2);
+
+sss_sifp_error
+sss_sifp_parse_attr(sss_sifp_ctx *ctx,
+ const char *name,
+ DBusMessage *msg,
+ sss_sifp_attr ***_attrs);
+
+sss_sifp_error
+sss_sifp_parse_attr_list(sss_sifp_ctx *ctx,
+ DBusMessage *msg,
+ sss_sifp_attr ***_attrs);
+
+sss_sifp_error
+sss_sifp_parse_object_path(sss_sifp_ctx *ctx,
+ DBusMessage *msg,
+ char **_object_path);
+
+sss_sifp_error
+sss_sifp_parse_object_path_list(sss_sifp_ctx *ctx,
+ DBusMessage *msg,
+ char ***_object_paths);
+
+#endif /* SSS_SIFP_PRIVATE_H_ */
diff --git a/src/lib/sifp/sss_sifp_utils.c b/src/lib/sifp/sss_sifp_utils.c
new file mode 100644
index 0000000..36cbb83
--- /dev/null
+++ b/src/lib/sifp/sss_sifp_utils.c
@@ -0,0 +1,90 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2014 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <dbus/dbus.h>
+#include <string.h>
+
+#include "lib/sifp/sss_sifp.h"
+#include "lib/sifp/sss_sifp_private.h"
+
+void *sss_sifp_alloc_zero(sss_sifp_ctx *ctx, size_t size, size_t num)
+{
+ void *addr = ctx->alloc_fn(size * num, ctx->alloc_pvt);
+
+ if (addr == NULL) {
+ return NULL;
+ }
+
+ memset(addr, '\0', size * num);
+
+ return addr;
+}
+
+void sss_sifp_set_io_error(sss_sifp_ctx *ctx, DBusError *error)
+{
+ dbus_error_free(ctx->io_error);
+ dbus_error_init(ctx->io_error);
+ dbus_set_error(ctx->io_error, error->name, "%s", error->message);
+}
+
+char * sss_sifp_strdup(sss_sifp_ctx *ctx, const char *str)
+{
+ char *result = NULL;
+ size_t str_len;
+
+ if (str == NULL) {
+ return NULL;
+ }
+
+ str_len = strlen(str);
+ result = _alloc_zero(ctx, char, str_len + 1);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ memcpy(result, str, str_len);
+
+ return result;
+}
+
+char * sss_sifp_strcat(sss_sifp_ctx *ctx, const char *str1, const char *str2)
+{
+ char *result = NULL;
+
+ if (str1 == NULL) {
+ return sss_sifp_strdup(ctx, str2);
+ }
+
+ if (str2 == NULL) {
+ return sss_sifp_strdup(ctx, str1);
+ }
+
+ size_t len = strlen(str1) + strlen(str2) + 1;
+
+ result = _alloc_zero(ctx, char, len);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ strcat(result, str1);
+ strcat(result, str2);
+
+ return result;
+}
diff --git a/src/lib/sifp/sss_simpleifp.doxy.in b/src/lib/sifp/sss_simpleifp.doxy.in
new file mode 100644
index 0000000..0270ada
--- /dev/null
+++ b/src/lib/sifp/sss_simpleifp.doxy.in
@@ -0,0 +1,1539 @@
+# Doxyfile 1.6.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = sss_simpleifp
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @PACKAGE_VERSION@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = sss_simpleifp_doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = NO
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = YES
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = @abs_top_srcdir@/src/lib/sifp/sss_sifp.h
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.cpp \
+ *.cc \
+ *.c \
+ *.h \
+ *.hh \
+ *.hpp \
+ *.dox
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS = */.git/* \
+ */.svn/* \
+ */cmake/* \
+ */build/*
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated
+# HTML page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = NO
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NONE
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# When the SEARCHENGINE tag is enable doxygen will generate a search box for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP) or Qt help (GENERATE_QHP)
+# there is already a search function so this one should typically
+# be disabled.
+
+SEARCHENGINE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = DOXYGEN
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/src/lib/sifp/sss_simpleifp.exports b/src/lib/sifp/sss_simpleifp.exports
new file mode 100644
index 0000000..f491092
--- /dev/null
+++ b/src/lib/sifp/sss_simpleifp.exports
@@ -0,0 +1,56 @@
+SSS_SIMPLEIFP_0.0 {
+
+ # public functions
+ global:
+
+ sss_sifp_init;
+ sss_sifp_init_ex;
+ sss_sifp_get_last_io_error_name;
+ sss_sifp_get_last_io_error_message;
+ sss_sifp_create_message;
+ sss_sifp_send_message;
+ sss_sifp_send_message_ex;
+ sss_sifp_fetch_attr;
+ sss_sifp_fetch_all_attrs;
+ sss_sifp_fetch_object;
+ sss_sifp_invoke_list;
+ sss_sifp_invoke_find;
+ sss_sifp_find_attr_as_bool;
+ sss_sifp_find_attr_as_int16;
+ sss_sifp_find_attr_as_uint16;
+ sss_sifp_find_attr_as_int32;
+ sss_sifp_find_attr_as_uint32;
+ sss_sifp_find_attr_as_int64;
+ sss_sifp_find_attr_as_uint64;
+ sss_sifp_find_attr_as_string;
+ sss_sifp_find_attr_as_string_dict;
+ sss_sifp_find_attr_as_bool_array;
+ sss_sifp_find_attr_as_int16_array;
+ sss_sifp_find_attr_as_uint16_array;
+ sss_sifp_find_attr_as_int32_array;
+ sss_sifp_find_attr_as_uint32_array;
+ sss_sifp_find_attr_as_int64_array;
+ sss_sifp_find_attr_as_uint64_array;
+ sss_sifp_find_attr_as_string_array;
+ sss_sifp_free;
+ sss_sifp_free_attrs;
+ sss_sifp_free_object;
+ sss_sifp_free_string;
+ sss_sifp_free_string_array;
+ sss_sifp_list_domains;
+ sss_sifp_fetch_domain_by_name;
+ sss_sifp_fetch_user_by_uid;
+ sss_sifp_fetch_user_by_name;
+
+ # everything else is local
+ local:
+ *;
+};
+
+SSS_SIMPLEIFP_0.1 {
+ # public functions
+ global:
+ sss_sifp_strerr;
+ sss_sifp_invoke_list_ex;
+ sss_sifp_invoke_find_ex;
+} SSS_SIMPLEIFP_0.0;
diff --git a/src/lib/sifp/sss_simpleifp.pc.in b/src/lib/sifp/sss_simpleifp.pc.in
new file mode 100644
index 0000000..51ca31f
--- /dev/null
+++ b/src/lib/sifp/sss_simpleifp.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: sss_simpleifp
+Description: A library that simplifies work with the InfoPipe responder
+Version: @VERSION@
+Requires: dbus-1, dhash
+Libs: -L@libdir@ -lsss_simpleifp
+Cflags: -I${includedir}
+URL: https://github.com/SSSD/sssd/
diff --git a/src/lib/winbind_idmap_sss/libdlopen-test-winbind-idmap.c b/src/lib/winbind_idmap_sss/libdlopen-test-winbind-idmap.c
new file mode 100644
index 0000000..94e8719
--- /dev/null
+++ b/src/lib/winbind_idmap_sss/libdlopen-test-winbind-idmap.c
@@ -0,0 +1,31 @@
+/*
+ SSSD
+
+ ID-mapping plugin for winbind - helper library for dlopen test
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "lib/winbind_idmap_sss/winbind_idmap_sss.h"
+
+NTSTATUS smb_register_idmap(int version, const char *name,
+ struct idmap_methods *methods)
+{
+ return NT_STATUS_OK;
+}
diff --git a/src/lib/winbind_idmap_sss/winbind_idmap_sss.c b/src/lib/winbind_idmap_sss/winbind_idmap_sss.c
new file mode 100644
index 0000000..5837532
--- /dev/null
+++ b/src/lib/winbind_idmap_sss/winbind_idmap_sss.c
@@ -0,0 +1,220 @@
+/*
+ SSSD
+
+ ID-mapping plugin for winbind
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+#include <errno.h>
+
+#include "lib/winbind_idmap_sss/winbind_idmap_sss.h"
+#include "sss_client/idmap/sss_nss_idmap.h"
+#include "lib/idmap/sss_idmap.h"
+#include "util/util_sss_idmap.h"
+
+struct idmap_sss_ctx {
+ struct sss_idmap_ctx *idmap_ctx;
+};
+
+static NTSTATUS idmap_sss_initialize(struct idmap_domain *dom)
+{
+ struct idmap_sss_ctx *ctx;
+ enum idmap_error_code err;
+
+ if (dom == NULL) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ ctx = talloc_zero(dom, struct idmap_sss_ctx);
+ if (ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ err = sss_idmap_init(sss_idmap_talloc, ctx, sss_idmap_talloc_free,
+ &ctx->idmap_ctx);
+ if (err != IDMAP_SUCCESS) {
+ talloc_free(ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+#if SMB_IDMAP_INTERFACE_VERSION == 6
+ dom->query_user = NULL;
+#endif
+
+ dom->private_data = ctx;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_sss_unixids_to_sids(struct idmap_domain *dom,
+ struct id_map **map)
+{
+ size_t c;
+ int ret;
+ char *sid_str;
+ enum sss_id_type id_type;
+ struct dom_sid *sid;
+ enum idmap_error_code err;
+ struct idmap_sss_ctx *ctx;
+
+ if (dom == NULL) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_sss_ctx);
+ if (ctx == NULL) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ for (c = 0; map[c]; c++) {
+ map[c]->status = ID_UNKNOWN;
+ }
+
+ for (c = 0; map[c]; c++) {
+ switch (map[c]->xid.type) {
+ case ID_TYPE_UID:
+ ret = sss_nss_getsidbyuid(map[c]->xid.id, &sid_str, &id_type);
+ break;
+ case ID_TYPE_GID:
+ ret = sss_nss_getsidbygid(map[c]->xid.id, &sid_str, &id_type);
+ break;
+ default:
+ ret = sss_nss_getsidbyid(map[c]->xid.id, &sid_str, &id_type);
+ }
+ if (ret != 0) {
+ if (ret == ENOENT) {
+ map[c]->status = ID_UNMAPPED;
+ }
+ continue;
+ }
+
+ switch (id_type) {
+ case SSS_ID_TYPE_UID:
+ map[c]->xid.type = ID_TYPE_UID;
+ break;
+ case SSS_ID_TYPE_GID:
+ map[c]->xid.type = ID_TYPE_GID;
+ break;
+ case SSS_ID_TYPE_BOTH:
+ map[c]->xid.type = ID_TYPE_BOTH;
+ break;
+ default:
+ free(sid_str);
+ continue;
+ }
+
+ err = sss_idmap_sid_to_smb_sid(ctx->idmap_ctx, sid_str, &sid);
+ free(sid_str);
+ if (err != IDMAP_SUCCESS) {
+ continue;
+ }
+
+ memcpy(map[c]->sid, sid, sizeof(struct dom_sid));
+ sss_idmap_free_smb_sid(ctx->idmap_ctx, sid);
+
+ map[c]->status = ID_MAPPED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_sss_sids_to_unixids(struct idmap_domain *dom,
+ struct id_map **map)
+{
+ size_t c;
+ int ret;
+ char *sid_str;
+ enum sss_id_type id_type;
+ enum idmap_error_code err;
+ struct idmap_sss_ctx *ctx;
+ uint32_t id;
+
+ if (dom == NULL) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_sss_ctx);
+ if (ctx == NULL) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ for (c = 0; map[c]; c++) {
+ map[c]->status = ID_UNKNOWN;
+ }
+
+ for (c = 0; map[c]; c++) {
+ err = sss_idmap_smb_sid_to_sid(ctx->idmap_ctx, map[c]->sid, &sid_str);
+ if (err != IDMAP_SUCCESS) {
+ continue;
+ }
+
+ ret = sss_nss_getidbysid(sid_str, &id, &id_type);
+ sss_idmap_free_sid(ctx->idmap_ctx, sid_str);
+ if (ret != 0) {
+ if (ret == ENOENT) {
+ map[c]->status = ID_UNMAPPED;
+ }
+ continue;
+ }
+
+ switch (id_type) {
+ case SSS_ID_TYPE_UID:
+ map[c]->xid.type = ID_TYPE_UID;
+ break;
+ case SSS_ID_TYPE_GID:
+ map[c]->xid.type = ID_TYPE_GID;
+ break;
+ case SSS_ID_TYPE_BOTH:
+ map[c]->xid.type = ID_TYPE_BOTH;
+ break;
+ default:
+ continue;
+ }
+
+ map[c]->xid.id = id;
+
+ map[c]->status = ID_MAPPED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static struct idmap_methods sss_methods = {
+ .init = idmap_sss_initialize,
+ .unixids_to_sids = idmap_sss_unixids_to_sids,
+ .sids_to_unixids = idmap_sss_sids_to_unixids,
+};
+
+#if SMB_IDMAP_INTERFACE_VERSION == 5
+NTSTATUS idmap_sss_init(void)
+#elif SMB_IDMAP_INTERFACE_VERSION == 6
+NTSTATUS idmap_sss_init(TALLOC_CTX *ctx)
+#else
+#error Unexpected Samba idmpa inferface version
+#endif
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "sss", &sss_methods);
+}
+
+NTSTATUS samba_init_module(void)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "sss", &sss_methods);
+}
diff --git a/src/lib/winbind_idmap_sss/winbind_idmap_sss.h b/src/lib/winbind_idmap_sss/winbind_idmap_sss.h
new file mode 100644
index 0000000..7880083
--- /dev/null
+++ b/src/lib/winbind_idmap_sss/winbind_idmap_sss.h
@@ -0,0 +1,117 @@
+/*
+ SSSD
+
+ ID-mapping plugin for winbind
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WINBIND_SSS_IDMAP_H_
+#define _WINBIND_SSS_IDMAP_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <core/ntstatus.h>
+#include <ndr.h>
+#include <gen_ndr/security.h>
+
+#include "config.h"
+
+/* The following definitions are taken from the Samba header files
+ * - winbindd/idmap_proto.h
+ * - idmap.d
+ * - gen_ndr/idmap.h
+ * and can be removed if the related Samba header files become public headers
+ * or if this plugin is build inside the Samba source tree. */
+
+enum id_type {
+ ID_TYPE_NOT_SPECIFIED,
+ ID_TYPE_UID,
+ ID_TYPE_GID,
+ ID_TYPE_BOTH
+};
+
+struct unixid {
+ uint32_t id;
+ enum id_type type;
+};
+
+enum id_mapping {
+ ID_UNKNOWN,
+ ID_MAPPED,
+ ID_UNMAPPED,
+ ID_EXPIRED
+};
+
+struct id_map {
+ struct dom_sid *sid;
+ struct unixid xid;
+ enum id_mapping status;
+};
+
+#ifndef SMB_IDMAP_INTERFACE_VERSION
+#error Missing Samba idmap interface version
+#endif
+
+#if SMB_IDMAP_INTERFACE_VERSION == 6
+struct wbint_userinfo;
+#endif
+
+struct idmap_domain {
+ const char *name;
+#if SMB_IDMAP_INTERFACE_VERSION == 6 && defined(SMB_IDMAP_DOMAIN_HAS_DOM_SID)
+ /*
+ * dom_sid is currently only initialized in the unixids_to_sids request,
+ * so don't rely on this being filled out everywhere!
+ */
+ struct dom_sid dom_sid;
+#endif
+ struct idmap_methods *methods;
+#if SMB_IDMAP_INTERFACE_VERSION == 6
+ NTSTATUS (*query_user)(struct idmap_domain *domain,
+ struct wbint_userinfo *info);
+#endif
+ uint32_t low_id;
+ uint32_t high_id;
+ bool read_only;
+ void *private_data;
+};
+
+/* Filled out by IDMAP backends */
+struct idmap_methods {
+
+ /* Called when backend is first loaded */
+ NTSTATUS (*init)(struct idmap_domain *dom);
+
+ /* Map an array of uids/gids to SIDs. The caller specifies
+ the uid/gid and type. Gets back the SID. */
+ NTSTATUS (*unixids_to_sids)(struct idmap_domain *dom, struct id_map **ids);
+
+ /* Map an arry of SIDs to uids/gids. The caller sets the SID
+ and type and gets back a uid or gid. */
+ NTSTATUS (*sids_to_unixids)(struct idmap_domain *dom, struct id_map **ids);
+
+ /* Allocate a Unix-ID. */
+ NTSTATUS (*allocate_id)(struct idmap_domain *dom, struct unixid *id);
+};
+
+NTSTATUS smb_register_idmap(int version, const char *name,
+ struct idmap_methods *methods);
+#endif /* _WINBIND_SSS_IDMAP_H_ */