summaryrefslogtreecommitdiffstats
path: root/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/util')
-rw-r--r--src/util/atomic_io.c102
-rw-r--r--src/util/atomic_io.h53
-rw-r--r--src/util/auth_utils.h44
-rw-r--r--src/util/authtok-utils.c277
-rw-r--r--src/util/authtok-utils.h185
-rw-r--r--src/util/authtok.c1234
-rw-r--r--src/util/authtok.h523
-rw-r--r--src/util/backup_file.c120
-rw-r--r--src/util/become_user.c212
-rw-r--r--src/util/cert.h50
-rw-r--r--src/util/cert/cert_common.c138
-rw-r--r--src/util/cert/libcrypto/cert.c425
-rw-r--r--src/util/cert_derb64_to_ldap_filter.c113
-rw-r--r--src/util/check_file.c105
-rw-r--r--src/util/child_common.c955
-rw-r--r--src/util/child_common.h145
-rw-r--r--src/util/crypto/libcrypto/crypto_base64.c133
-rw-r--r--src/util/crypto/libcrypto/crypto_hmac_sha1.c49
-rw-r--r--src/util/crypto/libcrypto/crypto_obfuscate.c313
-rw-r--r--src/util/crypto/libcrypto/crypto_prng.c42
-rw-r--r--src/util/crypto/libcrypto/crypto_sha512crypt.c382
-rw-r--r--src/util/crypto/libcrypto/sss_openssl.h39
-rw-r--r--src/util/crypto/sss_crypto.h63
-rw-r--r--src/util/debug.c519
-rw-r--r--src/util/debug.h204
-rw-r--r--src/util/debug_backtrace.c331
-rw-r--r--src/util/dlinklist.h155
-rw-r--r--src/util/domain_info_utils.c1044
-rw-r--r--src/util/file_watch.c326
-rw-r--r--src/util/file_watch.h53
-rw-r--r--src/util/files.c892
-rw-r--r--src/util/find_uid.c381
-rw-r--r--src/util/find_uid.h36
-rw-r--r--src/util/inotify.c609
-rw-r--r--src/util/inotify.h61
-rw-r--r--src/util/io.c98
-rw-r--r--src/util/memory.c97
-rw-r--r--src/util/mmap_cache.h161
-rw-r--r--src/util/murmurhash3.c116
-rw-r--r--src/util/nscd.c146
-rw-r--r--src/util/nss_dl_load.c98
-rw-r--r--src/util/nss_dl_load.h122
-rw-r--r--src/util/pac_utils.c156
-rw-r--r--src/util/probes.h46
-rw-r--r--src/util/refcount.c92
-rw-r--r--src/util/refcount.h63
-rw-r--r--src/util/safe-format-string.c309
-rw-r--r--src/util/safe-format-string.h81
-rw-r--r--src/util/selinux.c122
-rw-r--r--src/util/server.c794
-rw-r--r--src/util/session_recording.c127
-rw-r--r--src/util/session_recording.h87
-rw-r--r--src/util/signal.c89
-rw-r--r--src/util/sss_chain_id.c60
-rw-r--r--src/util/sss_chain_id.h37
-rw-r--r--src/util/sss_chain_id_tevent.c138
-rw-r--r--src/util/sss_chain_id_tevent.h29
-rw-r--r--src/util/sss_cli_cmd.c244
-rw-r--r--src/util/sss_cli_cmd.h28
-rw-r--r--src/util/sss_endian.h57
-rw-r--r--src/util/sss_format.h76
-rw-r--r--src/util/sss_ini.c969
-rw-r--r--src/util/sss_ini.h164
-rw-r--r--src/util/sss_iobuf.c452
-rw-r--r--src/util/sss_iobuf.h201
-rw-r--r--src/util/sss_krb5.c1384
-rw-r--r--src/util/sss_krb5.h207
-rw-r--r--src/util/sss_ldap.c499
-rw-r--r--src/util/sss_ldap.h99
-rw-r--r--src/util/sss_log.c126
-rw-r--r--src/util/sss_nss.c233
-rw-r--r--src/util/sss_nss.h42
-rw-r--r--src/util/sss_pam_data.c205
-rw-r--r--src/util/sss_pam_data.h99
-rw-r--r--src/util/sss_ptr_hash.c395
-rw-r--r--src/util/sss_ptr_hash.h145
-rw-r--r--src/util/sss_ptr_list.c173
-rw-r--r--src/util/sss_ptr_list.h120
-rw-r--r--src/util/sss_python.c60
-rw-r--r--src/util/sss_python.h68
-rw-r--r--src/util/sss_regexp.c190
-rw-r--r--src/util/sss_regexp.h83
-rw-r--r--src/util/sss_selinux.c255
-rw-r--r--src/util/sss_selinux.h54
-rw-r--r--src/util/sss_semanage.c480
-rw-r--r--src/util/sss_sockets.c436
-rw-r--r--src/util/sss_sockets.h42
-rw-r--r--src/util/sss_ssh.c271
-rw-r--r--src/util/sss_ssh.h56
-rw-r--r--src/util/sss_tc_utf8.c82
-rw-r--r--src/util/sss_time.c76
-rw-r--r--src/util/sss_utf8.c81
-rw-r--r--src/util/sss_utf8.h44
-rw-r--r--src/util/string_utils.c252
-rw-r--r--src/util/strtonum.c77
-rw-r--r--src/util/strtonum.h32
-rw-r--r--src/util/user_info_msg.c57
-rw-r--r--src/util/user_info_msg.h33
-rw-r--r--src/util/usertools.c891
-rw-r--r--src/util/util.c1103
-rw-r--r--src/util/util.h902
-rw-r--r--src/util/util_creds.h84
-rw-r--r--src/util/util_errors.c194
-rw-r--r--src/util/util_errors.h204
-rw-r--r--src/util/util_ext.c389
-rw-r--r--src/util/util_lock.c91
-rw-r--r--src/util/util_preauth.c86
-rw-r--r--src/util/util_sss_idmap.c32
-rw-r--r--src/util/util_sss_idmap.h28
-rw-r--r--src/util/util_watchdog.c290
-rw-r--r--src/util/well_known_sids.c478
111 files changed, 26800 insertions, 0 deletions
diff --git a/src/util/atomic_io.c b/src/util/atomic_io.c
new file mode 100644
index 0000000..7cc4304
--- /dev/null
+++ b/src/util/atomic_io.c
@@ -0,0 +1,102 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@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 <stdint.h>
+
+#include "util/atomic_io.h"
+
+/* based on code from libssh <http://www.libssh.org> */
+ssize_t sss_atomic_io_s(int fd, void *buf, size_t n, bool do_read)
+{
+ char *b = buf;
+ size_t pos = 0;
+ ssize_t res;
+ struct pollfd pfd;
+
+ pfd.fd = fd;
+ pfd.events = do_read ? POLLIN : POLLOUT;
+
+ while (n > pos) {
+ if (do_read) {
+ res = read(fd, b + pos, n - pos);
+ } else {
+ res = write(fd, b + pos, n - pos);
+ }
+ switch (res) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ (void) poll(&pfd, 1, -1);
+ continue;
+ }
+ return -1;
+ case 0:
+ /* read returns 0 on end-of-file */
+ errno = do_read ? 0 : EPIPE;
+ return pos;
+ default:
+ pos += (size_t) res;
+ }
+ }
+
+ return pos;
+}
+
+ssize_t sss_atomic_write_safe_s(int fd, void *buf, size_t len)
+{
+ uint32_t ulen = len;
+ ssize_t ret;
+
+ ret = sss_atomic_write_s(fd, &ulen, sizeof(uint32_t));
+ if (ret != sizeof(uint32_t)) {
+ errno = errno == 0 ? EIO : errno;
+ return -1;
+ }
+
+ return sss_atomic_write_s(fd, buf, len);
+}
+
+ssize_t sss_atomic_read_safe_s(int fd, void *buf, size_t buf_len, size_t *_len)
+{
+ uint32_t ulen = (uint32_t)-1;
+ ssize_t ret;
+
+ ret = sss_atomic_read_s(fd, &ulen, sizeof(uint32_t));
+ if (ret != sizeof(uint32_t)) {
+ errno = errno == 0 ? EIO : errno;
+ return -1;
+ }
+
+ if (ulen > buf_len) {
+ if (_len != NULL) {
+ *_len = ulen;
+ }
+ errno = ERANGE;
+ return -1;
+ }
+
+ if (_len != NULL) {
+ *_len = ulen;
+ }
+
+ return sss_atomic_read_s(fd, buf, ulen);
+}
diff --git a/src/util/atomic_io.h b/src/util/atomic_io.h
new file mode 100644
index 0000000..fee0998
--- /dev/null
+++ b/src/util/atomic_io.h
@@ -0,0 +1,53 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@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 __SSSD_ATOMIC_IO_H__
+#define __SSSD_ATOMIC_IO_H__
+
+#include <unistd.h>
+#include <stdbool.h>
+#include <poll.h>
+#include <errno.h>
+
+/* Performs a read or write operation in an manner that is seemingly atomic
+ * to the caller.
+ *
+ * Please note that the function does not perform any asynchronous operation
+ * so the operation might potentially block
+ */
+ssize_t sss_atomic_io_s(int fd, void *buf, size_t n, bool do_read);
+
+#define sss_atomic_read_s(fd, buf, n) sss_atomic_io_s(fd, buf, n, true)
+#define sss_atomic_write_s(fd, buf, n) sss_atomic_io_s(fd, buf, n, false)
+
+/**
+ * Write length of the buffer then the buffer itself.
+ *
+ * (uint32_t)length + buffer
+ */
+ssize_t sss_atomic_write_safe_s(int fd, void *buf, size_t len);
+
+/**
+ * First, read uint32_t as a message length, then read the rest of the message
+ * expecting given length. The exact length is returned in _len parameter.
+ */
+ssize_t sss_atomic_read_safe_s(int fd, void *buf, size_t max_len, size_t *_len);
+
+#endif /* __SSSD_ATOMIC_IO_H__ */
diff --git a/src/util/auth_utils.h b/src/util/auth_utils.h
new file mode 100644
index 0000000..8883c5c
--- /dev/null
+++ b/src/util/auth_utils.h
@@ -0,0 +1,44 @@
+/*
+ SSSD
+
+ Authentication utility functions
+
+ Authors:
+ Jakub Hrozek <jhrozek@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 <errno.h>
+#include <security/pam_appl.h>
+
+static inline int cached_login_pam_status(int auth_res)
+{
+ switch (auth_res) {
+ case EOK:
+ return PAM_SUCCESS;
+ case ERR_ACCOUNT_UNKNOWN:
+ return PAM_AUTHINFO_UNAVAIL;
+ case ERR_NO_CACHED_CREDS:
+ case ERR_CACHED_CREDS_EXPIRED:
+ case ERR_AUTH_DENIED:
+ return PAM_PERM_DENIED;
+ case ERR_AUTH_FAILED:
+ return PAM_AUTH_ERR;
+ default:
+ return PAM_SYSTEM_ERR;
+ }
+}
diff --git a/src/util/authtok-utils.c b/src/util/authtok-utils.c
new file mode 100644
index 0000000..ac3c8bb
--- /dev/null
+++ b/src/util/authtok-utils.c
@@ -0,0 +1,277 @@
+/*
+ SSSD - auth utils helpers
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2015
+
+ 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/>.
+*/
+
+/* This file is use by SSSD clients and the main daemons. Please do not add
+ * code which is specific to only one of them. */
+
+#include <errno.h>
+
+#include "sss_client/sss_cli.h"
+#include "sss_client/pam_message.h"
+
+errno_t sss_auth_pack_2fa_blob(const char *fa1, size_t fa1_len,
+ const char *fa2, size_t fa2_len,
+ uint8_t *buf, size_t buf_len,
+ size_t *_2fa_blob_len)
+{
+ size_t c;
+ uint32_t tmp_uint32_t;
+
+ if (fa1 == NULL || *fa1 == '\0' || fa1_len > UINT32_MAX
+ || fa2 == NULL || *fa2 == '\0' || fa2_len > UINT32_MAX
+ || (buf == NULL && buf_len != 0)) {
+ return EINVAL;
+ }
+
+ if (fa1_len == 0) {
+ fa1_len = strlen(fa1);
+ } else {
+ if (fa1[fa1_len] != '\0') {
+ return EINVAL;
+ }
+ }
+
+ if (fa2_len == 0) {
+ fa2_len = strlen(fa2);
+ } else {
+ if (fa2[fa2_len] != '\0') {
+ return EINVAL;
+ }
+ }
+
+ *_2fa_blob_len = fa1_len + fa2_len + 2 + 2 * sizeof(uint32_t);
+ if (buf == NULL || buf_len < *_2fa_blob_len) {
+ return EAGAIN;
+ }
+
+ c = 0;
+ tmp_uint32_t = (uint32_t) fa1_len + 1;
+ SAFEALIGN_COPY_UINT32(buf, &tmp_uint32_t, &c);
+ tmp_uint32_t = (uint32_t) fa2_len + 1;
+ SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c);
+
+ memcpy(buf + c, fa1, fa1_len + 1);
+ c += fa1_len + 1;
+
+ memcpy(buf + c, fa2, fa2_len + 1);
+
+ return 0;
+}
+
+errno_t sss_auth_passkey_calc_size(const char *uv,
+ const char *key,
+ const char *pin,
+ size_t *_passkey_buf_len)
+{
+ size_t len = 0;
+
+ if (uv == NULL || key == NULL) {
+ return EINVAL;
+ }
+
+ len += strlen(key) + 1;
+ len += strlen(uv) + 1;
+
+ if (pin != NULL) {
+ len += strlen(pin) + 1;
+ }
+
+ *_passkey_buf_len = len;
+
+ return EOK;
+}
+
+errno_t sss_auth_pack_passkey_blob(uint8_t *buf,
+ const char *uv,
+ const char *key,
+ const char *pin)
+{
+ size_t len = 0;
+ size_t key_len;
+ size_t uv_len;
+ size_t pin_len;
+
+ if (uv == NULL || key == NULL) {
+ return EINVAL;
+ }
+
+ uv_len = strlen(uv) + 1;
+ memcpy(buf + len, uv, uv_len);
+ len += uv_len;
+
+ key_len = strlen(key) + 1;
+ memcpy(buf + len, key, key_len);
+ len += key_len;
+
+ /* Add provided PIN */
+ if (pin != NULL) {
+ pin_len = strlen(pin) + 1;
+ /* User verification is false */
+ } else {
+ pin = "";
+ pin_len = 0;
+ }
+ memcpy(buf + len, pin, pin_len);
+
+ return EOK;
+}
+
+errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
+ const char *token_name, size_t token_name_len,
+ const char *module_name, size_t module_name_len,
+ const char *key_id, size_t key_id_len,
+ const char *label, size_t label_len,
+ uint8_t *buf, size_t buf_len,
+ size_t *_sc_blob_len)
+{
+ size_t c;
+ uint32_t tmp_uint32_t;
+
+ if (pin_len > UINT32_MAX || token_name_len > UINT32_MAX
+ || module_name_len > UINT32_MAX
+ || (pin_len != 0 && pin == NULL)
+ || (token_name_len != 0 && token_name == NULL)
+ || (module_name_len != 0 && module_name == NULL)
+ || (key_id_len != 0 && key_id == NULL)
+ || (label_len != 0 && label == NULL)) {
+ return EINVAL;
+ }
+
+ /* A missing pin is ok in the case of a reader with a keyboard */
+ if (pin == NULL) {
+ pin = "";
+ pin_len = 0;
+ }
+
+ if (token_name == NULL) {
+ token_name = "";
+ token_name_len = 0;
+ }
+
+ if (module_name == NULL) {
+ module_name = "";
+ module_name_len = 0;
+ }
+
+ if (key_id == NULL) {
+ key_id = "";
+ key_id_len = 0;
+ }
+
+ if (label == NULL) {
+ label = "";
+ label_len = 0;
+ }
+
+ /* len should not include the trailing \0 */
+ if (pin_len == 0 || pin[pin_len - 1] == '\0') {
+ pin_len = strlen(pin);
+ }
+
+ if (token_name_len == 0 || token_name[token_name_len - 1] == '\0') {
+ token_name_len = strlen(token_name);
+ }
+
+ if (module_name_len == 0 || module_name[module_name_len - 1] == '\0') {
+ module_name_len = strlen(module_name);
+ }
+
+ if (key_id_len == 0 || key_id[key_id_len - 1] == '\0') {
+ key_id_len = strlen(key_id);
+ }
+
+ if (label_len == 0 || label[label_len - 1] == '\0') {
+ label_len = strlen(label);
+ }
+
+ *_sc_blob_len = pin_len + token_name_len + module_name_len + key_id_len
+ + label_len + 5 + 5 * sizeof(uint32_t);
+ if (buf == NULL || buf_len < *_sc_blob_len) {
+ return EAGAIN;
+ }
+
+ c = 0;
+ tmp_uint32_t = (uint32_t) pin_len + 1;
+ SAFEALIGN_COPY_UINT32(buf, &tmp_uint32_t, &c);
+ tmp_uint32_t = (uint32_t) token_name_len + 1;
+ SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c);
+ tmp_uint32_t = (uint32_t) module_name_len + 1;
+ SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c);
+ tmp_uint32_t = (uint32_t) key_id_len + 1;
+ SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c);
+ tmp_uint32_t = (uint32_t) label_len + 1;
+ SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c);
+
+ memcpy(buf + c, pin, pin_len);
+ buf[c + pin_len] = '\0';
+ c += pin_len + 1;
+
+ memcpy(buf + c, token_name, token_name_len);
+ buf[c + token_name_len] = '\0';
+ c += token_name_len + 1;
+
+ memcpy(buf + c, module_name, module_name_len);
+ buf[c + module_name_len] = '\0';
+ c += module_name_len + 1;
+
+ memcpy(buf + c, key_id, key_id_len);
+ buf[c + key_id_len] = '\0';
+ c += key_id_len +1;
+
+ memcpy(buf + c, label, label_len);
+ buf[c + label_len] = '\0';
+
+ return 0;
+}
+
+const char *sss_auth_get_pin_from_sc_blob(uint8_t *blob, size_t blob_len)
+{
+ size_t c = 0;
+ uint32_t pin_len;
+ uint32_t token_name_len;
+ uint32_t module_name_len;
+ uint32_t key_id_len;
+ uint32_t label_len;
+
+ if (blob == NULL || blob_len == 0) {
+ return NULL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&pin_len, blob, &c);
+ if (pin_len == 0) {
+ return NULL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&token_name_len, blob + c, &c);
+ SAFEALIGN_COPY_UINT32(&module_name_len, blob + c, &c);
+ SAFEALIGN_COPY_UINT32(&key_id_len, blob + c, &c);
+ SAFEALIGN_COPY_UINT32(&label_len, blob + c, &c);
+
+ if (blob_len != 5 * sizeof(uint32_t) + pin_len + token_name_len
+ + module_name_len + key_id_len
+ + label_len) {
+ return NULL;
+ }
+
+ if (blob[c + pin_len - 1] != '\0') {
+ return NULL;
+ }
+
+ return (const char *) blob + c;
+}
diff --git a/src/util/authtok-utils.h b/src/util/authtok-utils.h
new file mode 100644
index 0000000..3a7e7c1
--- /dev/null
+++ b/src/util/authtok-utils.h
@@ -0,0 +1,185 @@
+/*
+ SSSD - auth utils helpers
+
+ Copyright (C) Sumit Bose <simo@redhat.com> 2015
+
+ 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 __AUTHTOK_UTILS_H__
+#define __AUTHTOK_UTILS_H__
+
+#include <talloc.h>
+
+#include "sss_client/sss_cli.h"
+#include "sss_client/pam_message.h"
+
+/**
+ * @brief Fill memory buffer with Smartcard authentication blob
+ *
+ * @param[in] pin PIN, null terminated
+ * @param[in] pin_len Length of the PIN, if 0
+ * strlen() will be called internally
+ * @param[in] token_name Token name, null terminated
+ * @param[in] token_name_len Length of the token name, if 0
+ * strlen() will be called internally
+ * @param[in] module_name Name of PKCS#11 module, null terminated
+ * @param[in] module_name_len Length of the module name, if 0
+ * strlen() will be called internally
+ * @param[in] key_id Key ID of the certificate
+ * @param[in] key_id_len Length of the key id of the certificate, if 0
+ * strlen() will be called internally
+ * @param[in] label Label of the certificate
+ * @param[in] label_len Length of the label of the certificate, if 0
+ * strlen() will be called internally
+ * @param[in] buf memory buffer of size buf_len, may be NULL
+ * @param[in] buf_len size of memory buffer buf
+ *
+ * @param[out] _sc_blob len size of the Smartcard authentication blob
+ *
+ * @return EOK on success
+ * EINVAL if input data is not consistent
+ * EAGAIN if provided buffer is too small, _sc_blob_len
+ * contains the size needed to store the SC blob
+ */
+errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
+ const char *token_name, size_t token_name_len,
+ const char *module_name, size_t module_name_len,
+ const char *key_id, size_t key_id_len,
+ const char *label, size_t label_len,
+ uint8_t *buf, size_t buf_len,
+ size_t *_sc_blob_len);
+/**
+ * @brief Fill memory buffer with 2FA blob
+ *
+ * @param[in] fa1 First authentication factor, null terminated
+ * @param[in] fa1_len Length of the first authentication factor, if 0
+ * strlen() will be called internally
+ * @param[in] fa2 Second authentication factor, null terminated
+ * @param[in] fa2_len Length of the second authentication factor, if 0
+ * strlen() will be called internally
+ * @param[in] buf memory buffer of size buf_len
+ * @param[in] buf_len size of memory buffer buf
+ *
+ * @param[out] _2fa_blob_len size of the 2FA blob
+ *
+ * @return EOK on success
+ * EINVAL if input data is not consistent
+ * EAGAIN if provided buffer is too small, _2fa_blob_len
+ * contains the size needed to store the 2FA blob
+ */
+errno_t sss_auth_pack_2fa_blob(const char *fa1, size_t fa1_len,
+ const char *fa2, size_t fa2_len,
+ uint8_t *buf, size_t buf_len,
+ size_t *_2fa_blob_len);
+
+/**
+ * @brief Extract 2FA data from memory buffer
+ *
+ * @param[in] mem_ctx Talloc memory context to allocate the 2FA data on
+ * @param[in] blob Memory buffer containing the 2FA data
+ * @param[in] blob_len Size of the memory buffer
+ * @param[out] _fa1 First authentication factor, null terminated
+ * @param[out] _fa1_len Length of the first authentication factor
+ * @param[out] _fa2 Second authentication factor, null terminated
+ * @param[out] _fa2_len Length of the second authentication factor
+ *
+ * @return EOK on success
+ * EINVAL if input data is not consistent
+ * EINVAL if no memory can be allocated
+ */
+errno_t sss_auth_unpack_2fa_blob(TALLOC_CTX *mem_ctx,
+ const uint8_t *blob, size_t blob_len,
+ char **fa1, size_t *_fa1_len,
+ char **fa2, size_t *_fa2_len);
+
+/**
+ * @brief Extract SC data from memory buffer
+ *
+ * @param[in] mem_ctx Talloc memory context to allocate the 2FA
+ * data on
+ * @param[in] blob Memory buffer containing the 2FA data
+ * @param[in] blob_len Size of the memory buffer
+ * @param[out] _pin PIN, null terminated
+ * @param[out] _pin_len Length of the PIN
+ * @param[out] _token_name Token name, null terminated
+ * @param[out] _token_name_len Length of the token name
+ * @param[out] _module_name Name of PKCS#11 module, null terminated
+ * @param[out] _module_name_len Length of the module name
+ * @param[out] _key_id Key ID of the certificate, null terminated
+ * @param[out] _key_id_len Length of the key ID
+ * @param[out] _labe l Label of the certificate, null terminated
+ * @param[out] _label_len Length of the label
+ *
+ * @return EOK on success
+ * EINVAL if input data is not consistent
+ * EINVAL if no memory can be allocated
+ */
+errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
+ const uint8_t *blob, size_t blob_len,
+ char **pin, size_t *_pin_len,
+ char **token_name, size_t *_token_name_len,
+ char **module_name, size_t *_module_name_len,
+ char **key_id, size_t *_key_id_len,
+ char **label, size_t *_label_len);
+
+/**
+ * @brief Return a pointer to the PIN string in the memory buffer
+ *
+ * @param[in] blob Memory buffer containing the 2FA data
+ * @param[in] blob_len Size of the memory buffer
+ *
+ * @return pointer to 0-terminate PIN string in the memory buffer
+ */
+const char *sss_auth_get_pin_from_sc_blob(uint8_t *blob, size_t blob_len);
+
+/**
+ * @brief Fill memory buffer with Passkey authentication blob
+ *
+ * @param[in] buf Memory buffer containing the Passkey data
+ * @param[in] uv User verification, "true" or "false"
+ * @param[in] key Hash table key used to lookup Passkey data
+ * in the PAM responder.
+ * @param[in] pin PIN provided by the user. Can be set to
+ * NULL if no PIN is provided (user verification false)
+ *
+ * @param[out] _passkey_buf_len len size of the Passkey authentication blob
+ *
+ * @return EOK on success
+ * EINVAL if input data is not valid
+ */
+errno_t sss_auth_pack_passkey_blob(uint8_t *buf,
+ const char *uv,
+ const char *key,
+ const char *pin);
+/**
+ * @brief Calculate size of Passkey authentication data
+ *
+ * @param[in] uv User verification, "true" or "false"
+ * @param[in] key Hash table key used to lookup Passkey data
+ * in the PAM responder.
+ * @param[in] pin PIN provided by the user. Can be
+ * Set to NULL if no PIN is
+ * provided (user verification false)
+ *
+ * @param[out] _passkey_buf_len len size of the Passkey authentication blob
+ *
+ * @return EOK on success
+ * EINVAL if input data is not valid
+ */
+errno_t sss_auth_passkey_calc_size(const char *uv,
+ const char *key,
+ const char *pin,
+ size_t *_passkey_buf_len);
+#endif /* __AUTHTOK_UTILS_H__ */
diff --git a/src/util/authtok.c b/src/util/authtok.c
new file mode 100644
index 0000000..6a6e74e
--- /dev/null
+++ b/src/util/authtok.c
@@ -0,0 +1,1234 @@
+/*
+ SSSD - auth utils
+
+ Copyright (C) Simo Sorce <simo@redhat.com> 2012
+
+ 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 "authtok.h"
+
+struct sss_auth_token {
+ enum sss_authtok_type type;
+ uint8_t *data;
+ size_t length;
+};
+
+const char *sss_authtok_type_to_str(enum sss_authtok_type type)
+{
+ switch (type) {
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ return "No authentication token available";
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ return "Password";
+ case SSS_AUTHTOK_TYPE_CCFILE:
+ return "Path to a Kerberos credential cache file";
+ case SSS_AUTHTOK_TYPE_2FA:
+ return "Two factors";
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ return "Smart Card PIN";
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ return "Smart Card PIN will be entered at the card reader";
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ return "Two factors in a single string";
+ case SSS_AUTHTOK_TYPE_OAUTH2:
+ return "OAuth2";
+ case SSS_AUTHTOK_TYPE_PASSKEY:
+ return "Passkey";
+ case SSS_AUTHTOK_TYPE_PASSKEY_KRB:
+ return "Passkey kerberos";
+ case SSS_AUTHTOK_TYPE_PASSKEY_REPLY:
+ return "Passkey kerberos reply";
+ }
+
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unknown authtok type %d\n", type);
+ return "-unknown-";
+}
+
+enum sss_authtok_type sss_authtok_get_type(struct sss_auth_token *tok)
+{
+ return tok->type;
+}
+
+size_t sss_authtok_get_size(struct sss_auth_token *tok)
+{
+ if (!tok) {
+ return 0;
+ }
+ switch (tok->type) {
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ case SSS_AUTHTOK_TYPE_CCFILE:
+ case SSS_AUTHTOK_TYPE_2FA:
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ case SSS_AUTHTOK_TYPE_OAUTH2:
+ case SSS_AUTHTOK_TYPE_PASSKEY:
+ case SSS_AUTHTOK_TYPE_PASSKEY_KRB:
+ case SSS_AUTHTOK_TYPE_PASSKEY_REPLY:
+ return tok->length;
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ return 0;
+ }
+
+ return EINVAL;
+}
+
+uint8_t *sss_authtok_get_data(struct sss_auth_token *tok)
+{
+ if (!tok) {
+ return NULL;
+ }
+ return tok->data;
+}
+
+errno_t sss_authtok_get_password(struct sss_auth_token *tok,
+ const char **pwd, size_t *len)
+{
+ if (!tok) {
+ return EFAULT;
+ }
+ switch (tok->type) {
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ return ENOENT;
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ *pwd = (const char *)tok->data;
+ if (len) {
+ *len = tok->length - 1;
+ }
+ return EOK;
+ case SSS_AUTHTOK_TYPE_CCFILE:
+ case SSS_AUTHTOK_TYPE_2FA:
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ case SSS_AUTHTOK_TYPE_OAUTH2:
+ case SSS_AUTHTOK_TYPE_PASSKEY:
+ case SSS_AUTHTOK_TYPE_PASSKEY_KRB:
+ case SSS_AUTHTOK_TYPE_PASSKEY_REPLY:
+ return EACCES;
+ }
+
+ return EINVAL;
+}
+
+errno_t sss_authtok_get_ccfile(struct sss_auth_token *tok,
+ const char **ccfile, size_t *len)
+{
+ if (!tok) {
+ return EINVAL;
+ }
+ switch (tok->type) {
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ return ENOENT;
+ case SSS_AUTHTOK_TYPE_CCFILE:
+ *ccfile = (const char *)tok->data;
+ if (len) {
+ *len = tok->length - 1;
+ }
+ return EOK;
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ case SSS_AUTHTOK_TYPE_2FA:
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ case SSS_AUTHTOK_TYPE_OAUTH2:
+ case SSS_AUTHTOK_TYPE_PASSKEY:
+ case SSS_AUTHTOK_TYPE_PASSKEY_KRB:
+ case SSS_AUTHTOK_TYPE_PASSKEY_REPLY:
+ return EACCES;
+ }
+
+ return EINVAL;
+}
+
+errno_t sss_authtok_get_2fa_single(struct sss_auth_token *tok,
+ const char **str, size_t *len)
+{
+ if (!tok) {
+ return EINVAL;
+ }
+ switch (tok->type) {
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ return ENOENT;
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ *str = (const char *)tok->data;
+ if (len) {
+ *len = tok->length - 1;
+ }
+ return EOK;
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ case SSS_AUTHTOK_TYPE_2FA:
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_CCFILE:
+ case SSS_AUTHTOK_TYPE_OAUTH2:
+ case SSS_AUTHTOK_TYPE_PASSKEY:
+ case SSS_AUTHTOK_TYPE_PASSKEY_KRB:
+ case SSS_AUTHTOK_TYPE_PASSKEY_REPLY:
+ return EACCES;
+ }
+
+ return EINVAL;
+}
+
+errno_t sss_authtok_get_oauth2(struct sss_auth_token *tok,
+ const char **str, size_t *len)
+{
+ if (!tok) {
+ return EINVAL;
+ }
+ switch (tok->type) {
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ return ENOENT;
+ case SSS_AUTHTOK_TYPE_OAUTH2:
+ *str = (const char *)tok->data;
+ if (len) {
+ *len = tok->length - 1;
+ }
+ return EOK;
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ case SSS_AUTHTOK_TYPE_2FA:
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_CCFILE:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ case SSS_AUTHTOK_TYPE_PASSKEY:
+ case SSS_AUTHTOK_TYPE_PASSKEY_KRB:
+ case SSS_AUTHTOK_TYPE_PASSKEY_REPLY:
+ return EACCES;
+ }
+
+ return EINVAL;
+}
+
+errno_t sss_authtok_get_passkey_reply(struct sss_auth_token *tok,
+ const char **str, size_t *len)
+{
+ if (!tok) {
+ return EINVAL;
+ }
+ switch (tok->type) {
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ return ENOENT;
+ case SSS_AUTHTOK_TYPE_PASSKEY_REPLY:
+ *str = (const char *)tok->data;
+ if (len) {
+ *len = tok->length - 1;
+ }
+ return EOK;
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ case SSS_AUTHTOK_TYPE_2FA:
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_CCFILE:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ case SSS_AUTHTOK_TYPE_OAUTH2:
+ case SSS_AUTHTOK_TYPE_PASSKEY:
+ case SSS_AUTHTOK_TYPE_PASSKEY_KRB:
+ return EACCES;
+ }
+
+ return EINVAL;
+}
+
+errno_t sss_authtok_get_passkey_pin(struct sss_auth_token *tok,
+ const char **_pin, size_t *_len)
+{
+ int ret;
+ const char *pin = NULL;
+ const char *key = NULL;
+ const char *prompt = NULL;
+ size_t pin_len = 0;
+
+ if (!tok) {
+ return EINVAL;
+ }
+ switch (tok->type) {
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ return ENOENT;
+ case SSS_AUTHTOK_TYPE_PASSKEY:
+ *_pin = (const char *)tok->data;
+ if (_len) {
+ *_len = tok->length - 1;
+ }
+ return EOK;
+ case SSS_AUTHTOK_TYPE_PASSKEY_KRB:
+ ret = sss_authtok_get_passkey(NULL, tok, &prompt, &key, &pin, &pin_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_passkey failed.\n");
+ return ret;
+ }
+
+ *_pin = pin;
+ *_len = pin_len;
+
+ return EOK;
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ case SSS_AUTHTOK_TYPE_2FA:
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_CCFILE:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ case SSS_AUTHTOK_TYPE_OAUTH2:
+ case SSS_AUTHTOK_TYPE_PASSKEY_REPLY:
+ return EACCES;
+ }
+
+ return EINVAL;
+}
+static errno_t sss_authtok_set_string(struct sss_auth_token *tok,
+ enum sss_authtok_type type,
+ const char *context_name,
+ const char *str, size_t len)
+{
+ size_t size;
+
+ if (len == 0) {
+ len = strlen(str);
+ } else {
+ while (len > 0 && str[len - 1] == '\0') len--;
+ }
+
+ if (len == 0) {
+ /* we do not allow zero length typed tokens */
+ return EINVAL;
+ }
+
+ size = len + 1;
+
+ tok->data = talloc_named(tok, size, "%s", context_name);
+ if (!tok->data) {
+ return ENOMEM;
+ }
+ memcpy(tok->data, str, len);
+ tok->data[len] = '\0';
+ tok->type = type;
+ tok->length = size;
+
+ return EOK;
+
+}
+
+void sss_authtok_set_empty(struct sss_auth_token *tok)
+{
+ if (!tok) {
+ return;
+ }
+ switch (tok->type) {
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ return;
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ case SSS_AUTHTOK_TYPE_2FA:
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ case SSS_AUTHTOK_TYPE_PASSKEY:
+ case SSS_AUTHTOK_TYPE_PASSKEY_KRB:
+ case SSS_AUTHTOK_TYPE_PASSKEY_REPLY:
+ case SSS_AUTHTOK_TYPE_OAUTH2:
+ sss_erase_mem_securely(tok->data, tok->length);
+ break;
+ case SSS_AUTHTOK_TYPE_CCFILE:
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ break;
+ }
+
+ tok->type = SSS_AUTHTOK_TYPE_EMPTY;
+ talloc_zfree(tok->data);
+ tok->length = 0;
+}
+
+errno_t sss_authtok_set_password(struct sss_auth_token *tok,
+ const char *password, size_t len)
+{
+ sss_authtok_set_empty(tok);
+
+ return sss_authtok_set_string(tok, SSS_AUTHTOK_TYPE_PASSWORD,
+ "password", password, len);
+}
+
+errno_t sss_authtok_set_ccfile(struct sss_auth_token *tok,
+ const char *ccfile, size_t len)
+{
+ sss_authtok_set_empty(tok);
+
+ return sss_authtok_set_string(tok, SSS_AUTHTOK_TYPE_CCFILE,
+ "ccfile", ccfile, len);
+}
+
+errno_t sss_authtok_set_2fa_single(struct sss_auth_token *tok,
+ const char *str, size_t len)
+{
+ sss_authtok_set_empty(tok);
+
+ return sss_authtok_set_string(tok, SSS_AUTHTOK_TYPE_2FA_SINGLE,
+ "2fa_single", str, len);
+}
+
+errno_t sss_authtok_set_oauth2(struct sss_auth_token *tok,
+ const char *str, size_t len)
+{
+ sss_authtok_set_empty(tok);
+
+ return sss_authtok_set_string(tok, SSS_AUTHTOK_TYPE_OAUTH2,
+ "oauth2", str, len);
+}
+
+errno_t sss_authtok_set_passkey_reply(struct sss_auth_token *tok,
+ const char *str, size_t len)
+{
+ sss_authtok_set_empty(tok);
+
+ return sss_authtok_set_string(tok, SSS_AUTHTOK_TYPE_PASSKEY_REPLY,
+ "passkey kerberos reply", str, len);
+}
+
+errno_t sss_authtok_set_passkey(struct sss_auth_token *tok,
+ const char *str, size_t len)
+{
+ sss_authtok_set_empty(tok);
+
+ return sss_authtok_set_string(tok, SSS_AUTHTOK_TYPE_PASSKEY,
+ "passkey", str, len);
+}
+
+static errno_t sss_authtok_set_2fa_from_blob(struct sss_auth_token *tok,
+ const uint8_t *data, size_t len);
+static errno_t sss_authtok_set_passkey_from_blob(struct sss_auth_token *tok,
+ const uint8_t *data, size_t len);
+
+errno_t sss_authtok_set(struct sss_auth_token *tok,
+ enum sss_authtok_type type,
+ const uint8_t *data, size_t len)
+{
+ switch (type) {
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ return sss_authtok_set_password(tok, (const char *)data, len);
+ case SSS_AUTHTOK_TYPE_CCFILE:
+ return sss_authtok_set_ccfile(tok, (const char *)data, len);
+ case SSS_AUTHTOK_TYPE_2FA:
+ return sss_authtok_set_2fa_from_blob(tok, data, len);
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ return sss_authtok_set_sc_from_blob(tok, data, len);
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ return sss_authtok_set_sc_from_blob(tok, data, len);
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ return sss_authtok_set_2fa_single(tok, (const char *) data, len);
+ case SSS_AUTHTOK_TYPE_OAUTH2:
+ return sss_authtok_set_oauth2(tok, (const char *) data, len);
+ case SSS_AUTHTOK_TYPE_PASSKEY_KRB:
+ return sss_authtok_set_passkey_from_blob(tok, data, len);
+ case SSS_AUTHTOK_TYPE_PASSKEY:
+ return sss_authtok_set_passkey(tok, (const char *) data, len);
+ case SSS_AUTHTOK_TYPE_PASSKEY_REPLY:
+ return sss_authtok_set_passkey_reply(tok, (const char *)data, len);
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ sss_authtok_set_empty(tok);
+ return EOK;
+ }
+
+ return EINVAL;
+}
+
+errno_t sss_authtok_copy(struct sss_auth_token *src,
+ struct sss_auth_token *dst)
+{
+ if (!src || !dst) {
+ return EINVAL;
+ }
+ sss_authtok_set_empty(dst);
+
+ if (src->type == SSS_AUTHTOK_TYPE_EMPTY) {
+ return EOK;
+ }
+
+ dst->data = talloc_memdup(dst, src->data, src->length);
+ if (!dst->data) {
+ return ENOMEM;
+ }
+ dst->length = src->length;
+ dst->type = src->type;
+
+ return EOK;
+}
+
+static int sss_auth_token_destructor(struct sss_auth_token *tok)
+{
+ if (tok != NULL) {
+ sss_erase_talloc_mem_securely(tok->data);
+ }
+ return 0;
+}
+
+struct sss_auth_token *sss_authtok_new(TALLOC_CTX *mem_ctx)
+{
+ struct sss_auth_token *token;
+
+ token = talloc_zero(mem_ctx, struct sss_auth_token);
+ if (token == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
+ }
+
+ talloc_set_destructor(token, sss_auth_token_destructor);
+
+ return token;
+}
+
+
+void sss_authtok_wipe_password(struct sss_auth_token *tok)
+{
+ if (!tok || tok->type != SSS_AUTHTOK_TYPE_PASSWORD) {
+ return;
+ }
+
+ sss_erase_mem_securely(tok->data, tok->length);
+}
+
+errno_t sss_auth_unpack_2fa_blob(TALLOC_CTX *mem_ctx,
+ const uint8_t *blob, size_t blob_len,
+ char **fa1, size_t *_fa1_len,
+ char **fa2, size_t *_fa2_len)
+{
+ size_t c;
+ uint32_t fa1_len;
+ uint32_t fa2_len;
+
+ if (blob_len < 2 * sizeof(uint32_t)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n");
+ return EINVAL;
+ }
+
+ c = 0;
+ SAFEALIGN_COPY_UINT32(&fa1_len, blob, &c);
+ SAFEALIGN_COPY_UINT32(&fa2_len, blob + c, &c);
+
+ if (blob_len != 2 * sizeof(uint32_t) + fa1_len + fa2_len) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n");
+ return EINVAL;
+ }
+
+ if (fa1_len != 0) {
+ *fa1 = talloc_strndup(mem_ctx, (const char *) blob + c, fa1_len);
+ if (*fa1 == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ return ENOMEM;
+ }
+ } else {
+ *fa1 = NULL;
+ }
+
+ if (fa2_len != 0) {
+ *fa2 = talloc_strndup(mem_ctx, (const char *) blob + c + fa1_len,
+ fa2_len);
+ if (*fa2 == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ talloc_free(*fa1);
+ return ENOMEM;
+ }
+ } else {
+ *fa2 = NULL;
+ }
+
+ /* Re-calculate length for the case where \0 was missing in the blob */
+ *_fa1_len = (*fa1 == NULL) ? 0 : strlen(*fa1);
+ *_fa2_len = (*fa2 == NULL) ? 0 : strlen(*fa2);
+
+ return EOK;
+}
+
+static errno_t sss_authtok_set_2fa_from_blob(struct sss_auth_token *tok,
+ const uint8_t *data, size_t len)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ char *fa1;
+ size_t fa1_len;
+ char *fa2;
+ size_t fa2_len;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_auth_unpack_2fa_blob(tmp_ctx, data, len, &fa1, &fa1_len,
+ &fa2, &fa2_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_auth_unpack_2fa_blob failed.\n");
+ goto done;
+ }
+
+ ret = sss_authtok_set_2fa(tok, fa1, fa1_len, fa2, fa2_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_set_2fa failed.\n");
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+
+ if (ret != EOK) {
+ sss_authtok_set_empty(tok);
+ }
+
+ return ret;
+}
+
+errno_t sss_authtok_get_2fa(struct sss_auth_token *tok,
+ const char **fa1, size_t *fa1_len,
+ const char **fa2, size_t *fa2_len)
+{
+ size_t c;
+ uint32_t tmp_uint32_t;
+
+ if (tok->type != SSS_AUTHTOK_TYPE_2FA) {
+ return (tok->type == SSS_AUTHTOK_TYPE_EMPTY) ? ENOENT : EACCES;
+ }
+
+ if (tok->length < 2 * sizeof(uint32_t)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n");
+ return EINVAL;
+ }
+
+ c = 0;
+ SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data, &c);
+ *fa1_len = tmp_uint32_t - 1;
+ SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c);
+ *fa2_len = tmp_uint32_t - 1;
+
+ if (*fa1_len == 0 || *fa2_len == 0
+ || tok->length != 2 * sizeof(uint32_t) + *fa1_len + *fa2_len + 2) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n");
+ return EINVAL;
+ }
+
+ if (tok->data[c + *fa1_len] != '\0'
+ || tok->data[c + *fa1_len + 1 + *fa2_len] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing terminating null character.\n");
+ return EINVAL;
+ }
+
+ *fa1 = (const char *) tok->data + c;
+ *fa2 = (const char *) tok->data + c + *fa1_len + 1;
+
+ return EOK;
+}
+
+errno_t sss_authtok_set_2fa(struct sss_auth_token *tok,
+ const char *fa1, size_t fa1_len,
+ const char *fa2, size_t fa2_len)
+{
+ int ret;
+ size_t needed_size;
+
+ if (tok == NULL) {
+ return EINVAL;
+ }
+
+ sss_authtok_set_empty(tok);
+
+ ret = sss_auth_pack_2fa_blob(fa1, fa1_len, fa2, fa2_len, NULL, 0,
+ &needed_size);
+ if (ret != EAGAIN) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_auth_pack_2fa_blob unexpectedly returned [%d].\n", ret);
+ return EINVAL;
+ }
+
+ tok->data = talloc_size(tok, needed_size);
+ if (tok->data == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
+ return ENOMEM;
+ }
+
+ ret = sss_auth_pack_2fa_blob(fa1, fa1_len, fa2, fa2_len, tok->data,
+ needed_size, &needed_size);
+ if (ret != EOK) {
+ talloc_free(tok->data);
+ DEBUG(SSSDBG_OP_FAILURE, "sss_auth_pack_2fa_blob failed.\n");
+ return ret;
+ }
+ tok->length = needed_size;
+ tok->type = SSS_AUTHTOK_TYPE_2FA;
+
+ return EOK;
+}
+
+errno_t sss_auth_unpack_passkey_blob(TALLOC_CTX *mem_ctx,
+ const uint8_t *blob,
+ char **_prompt,
+ char **_key,
+ char **_pin)
+{
+ size_t len = 0;
+ char *prompt;
+ char *key;
+ char *pin;
+
+ prompt = talloc_strdup(mem_ctx, (const char *) blob + len);
+ if (prompt == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup prompt failed.\n");
+ return ENOMEM;
+ }
+ len += strlen(prompt) + 1;
+
+ key = talloc_strdup(mem_ctx, (const char *) blob + len);
+ if (key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup key failed.\n");
+ talloc_free(prompt);
+ return ENOMEM;
+ }
+ len += strlen(key) + 1;
+
+ pin = talloc_strdup(mem_ctx, (const char *) blob + len);
+ if (pin == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup pin failed.\n");
+ talloc_free(prompt);
+ talloc_free(key);
+ return ENOMEM;
+ }
+
+ *_prompt = prompt;
+ *_key = key;
+ *_pin = pin;
+
+ return EOK;
+}
+
+errno_t sss_authtok_set_passkey_krb(struct sss_auth_token *tok,
+ const char *prompt,
+ const char *key,
+ const char *pin)
+{
+ int ret;
+ size_t needed_size;
+
+ if (tok == NULL) {
+ return EINVAL;
+ }
+
+ sss_authtok_set_empty(tok);
+
+ ret = sss_auth_passkey_calc_size(prompt, key, pin, &needed_size);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_auth_calc_size failed [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ tok->data = talloc_size(tok, needed_size);
+ if (tok->data == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
+ return ENOMEM;
+ }
+
+ ret = sss_auth_pack_passkey_blob(tok->data, prompt, key, pin);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_auth_pack_passkey_blob unexpectedly returned [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ return EINVAL;
+ }
+
+ tok->length = needed_size;
+ tok->type = SSS_AUTHTOK_TYPE_PASSKEY_KRB;
+
+ return EOK;
+}
+
+static errno_t sss_authtok_set_passkey_from_blob(struct sss_auth_token *tok,
+ const uint8_t *data, size_t len)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ char *prompt;
+ char *key;
+ char *pin;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_auth_unpack_passkey_blob(tmp_ctx, data, &prompt, &key,
+ &pin);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_auth_unpack_passkey_blob returned [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sss_authtok_set_passkey_krb(tok, prompt, key, pin);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_set_passkey_krb returned [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+
+ if (ret != EOK) {
+ sss_authtok_set_empty(tok);
+ }
+
+ return ret;
+}
+
+errno_t sss_authtok_get_passkey(TALLOC_CTX *mem_ctx,
+ struct sss_auth_token *tok,
+ const char **_prompt,
+ const char **_key,
+ const char **_pin,
+ size_t *_pin_len)
+{
+ char *prompt;
+ char *key;
+ char *pin;
+ size_t pin_len = 0;
+ errno_t ret;
+
+ if (!tok) {
+ return EFAULT;
+ }
+ if (tok->type != SSS_AUTHTOK_TYPE_PASSKEY_KRB) {
+ return (tok->type == SSS_AUTHTOK_TYPE_EMPTY) ? ENOENT : EACCES;
+ }
+
+ ret = sss_auth_unpack_passkey_blob(mem_ctx, tok->data, &prompt, &key,
+ &pin);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_auth_unpack_passkey_blob returned [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ pin_len = strlen(pin);
+
+ *_prompt = prompt;
+ *_key = key;
+ *_pin = pin;
+ *_pin_len = pin_len;
+done:
+ return ret;
+
+}
+
+errno_t sss_authtok_set_sc(struct sss_auth_token *tok,
+ enum sss_authtok_type type,
+ const char *pin, size_t pin_len,
+ const char *token_name, size_t token_name_len,
+ const char *module_name, size_t module_name_len,
+ const char *key_id, size_t key_id_len,
+ const char *label, size_t label_len)
+{
+ int ret;
+ size_t needed_size;
+
+ if (type != SSS_AUTHTOK_TYPE_SC_PIN
+ && type != SSS_AUTHTOK_TYPE_SC_KEYPAD) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid type [%d].\n", type);
+ return EINVAL;
+ }
+
+ sss_authtok_set_empty(tok);
+
+ ret = sss_auth_pack_sc_blob(pin, pin_len, token_name, token_name_len,
+ module_name, module_name_len,
+ key_id, key_id_len, label, label_len, NULL, 0,
+ &needed_size);
+ if (ret != EAGAIN) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_auth_pack_sc_blob failed.\n");
+ return ret;
+ }
+
+ tok->data = talloc_size(tok, needed_size);
+ if (tok->data == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
+ return ENOMEM;
+ }
+
+ ret = sss_auth_pack_sc_blob(pin, pin_len, token_name, token_name_len,
+ module_name, module_name_len,
+ key_id, key_id_len, label, label_len, tok->data,
+ needed_size, &needed_size);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_auth_pack_sc_blob failed.\n");
+ talloc_free(tok->data);
+ return ret;
+ }
+
+ tok->length = needed_size;
+ tok->type = type;
+
+ return EOK;
+}
+
+errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok,
+ const uint8_t *data,
+ size_t len)
+{
+ int ret;
+ char *pin = NULL;
+ size_t pin_len;
+ char *token_name = NULL;
+ size_t token_name_len;
+ char *module_name = NULL;
+ size_t module_name_len;
+ char *key_id = NULL;
+ size_t key_id_len;
+ char *label = NULL;
+ size_t label_len;
+ TALLOC_CTX *tmp_ctx;
+
+ if (tok == NULL) {
+ return EFAULT;
+ }
+ if (data == NULL || len == 0) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_auth_unpack_sc_blob(tmp_ctx, data, len, &pin, &pin_len,
+ &token_name, &token_name_len,
+ &module_name, &module_name_len,
+ &key_id, &key_id_len, &label, &label_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_auth_unpack_sc_blob failed.\n");
+ goto done;
+ }
+
+ ret = sss_authtok_set_sc(tok, SSS_AUTHTOK_TYPE_SC_PIN, pin, pin_len,
+ token_name, token_name_len,
+ module_name, module_name_len,
+ key_id, key_id_len, label, label_len);
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t sss_authtok_set_sc_pin(struct sss_auth_token *tok, const char *pin,
+ size_t len)
+{
+ if (tok == NULL) {
+ return EFAULT;
+ }
+ if (pin == NULL) {
+ return EINVAL;
+ }
+
+ return sss_authtok_set_sc(tok, SSS_AUTHTOK_TYPE_SC_PIN, pin, len,
+ NULL, 0, NULL, 0, NULL, 0, NULL, 0);
+}
+
+errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **_pin,
+ size_t *len)
+{
+ int ret;
+ const char *pin = NULL;
+ size_t pin_len;
+
+ if (!tok) {
+ return EFAULT;
+ }
+ switch (tok->type) {
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ return ENOENT;
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ ret = sss_authtok_get_sc(tok, &pin, &pin_len,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
+ return ret;
+ }
+
+ *_pin = pin;
+ if (len) {
+ *len = pin_len;
+ }
+ return EOK;
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ case SSS_AUTHTOK_TYPE_CCFILE:
+ case SSS_AUTHTOK_TYPE_2FA:
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ case SSS_AUTHTOK_TYPE_OAUTH2:
+ case SSS_AUTHTOK_TYPE_PASSKEY:
+ case SSS_AUTHTOK_TYPE_PASSKEY_KRB:
+ case SSS_AUTHTOK_TYPE_PASSKEY_REPLY:
+ return EACCES;
+ }
+
+ return EINVAL;
+}
+
+void sss_authtok_set_sc_keypad(struct sss_auth_token *tok)
+{
+ if (tok == NULL) {
+ return;
+ }
+
+ sss_authtok_set_empty(tok);
+
+ tok->type = SSS_AUTHTOK_TYPE_SC_KEYPAD;
+}
+
+errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
+ const uint8_t *blob, size_t blob_len,
+ char **pin, size_t *_pin_len,
+ char **token_name, size_t *_token_name_len,
+ char **module_name, size_t *_module_name_len,
+ char **key_id, size_t *_key_id_len,
+ char **label, size_t *_label_len)
+{
+ size_t c;
+ uint32_t pin_len;
+ uint32_t token_name_len;
+ uint32_t module_name_len;
+ uint32_t key_id_len;
+ uint32_t label_len;
+
+ c = 0;
+
+ if (blob == NULL || blob_len == 0) {
+ pin_len = 0;
+ token_name_len = 0;
+ module_name_len = 0;
+ key_id_len = 0;
+ label_len = 0;
+ } else if (blob_len > 0
+ && strnlen((const char *) blob, blob_len) == blob_len - 1) {
+ pin_len = blob_len;
+ token_name_len = 0;
+ module_name_len = 0;
+ key_id_len = 0;
+ label_len = 0;
+ } else {
+ if (blob_len < 5 * sizeof(uint32_t)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n");
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&pin_len, blob, &c);
+ SAFEALIGN_COPY_UINT32(&token_name_len, blob + c, &c);
+ SAFEALIGN_COPY_UINT32(&module_name_len, blob + c, &c);
+ SAFEALIGN_COPY_UINT32(&key_id_len, blob + c, &c);
+ SAFEALIGN_COPY_UINT32(&label_len, blob + c, &c);
+
+ if (blob_len != 5 * sizeof(uint32_t) + pin_len + token_name_len
+ + module_name_len + key_id_len
+ + label_len) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n");
+ return EINVAL;
+ }
+ }
+
+ if (pin_len != 0) {
+ *pin = talloc_strndup(mem_ctx, (const char *) blob + c, pin_len);
+ if (*pin == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ return ENOMEM;
+ }
+ } else {
+ *pin = NULL;
+ }
+
+ if (token_name_len != 0) {
+ *token_name = talloc_strndup(mem_ctx, (const char *) blob + c + pin_len,
+ token_name_len);
+ if (*token_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ talloc_free(*pin);
+ return ENOMEM;
+ }
+ } else {
+ *token_name = NULL;
+ }
+
+ if (module_name_len != 0) {
+ *module_name = talloc_strndup(mem_ctx,
+ (const char *) blob + c + pin_len
+ + token_name_len,
+ module_name_len);
+ if (*module_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ talloc_free(*pin);
+ talloc_free(*token_name);
+ return ENOMEM;
+ }
+ } else {
+ *module_name = NULL;
+ }
+
+ if (key_id_len != 0) {
+ *key_id = talloc_strndup(mem_ctx,
+ (const char *) blob + c + pin_len
+ + token_name_len
+ + module_name_len,
+ key_id_len);
+ if (*key_id == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ talloc_free(*pin);
+ talloc_free(*token_name);
+ talloc_free(*module_name);
+ return ENOMEM;
+ }
+ } else {
+ *key_id = NULL;
+ }
+
+ if (label_len != 0) {
+ *label = talloc_strndup(mem_ctx,
+ (const char *) blob + c + pin_len
+ + token_name_len
+ + module_name_len
+ + key_id_len,
+ label_len);
+ if (*label == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ talloc_free(*pin);
+ talloc_free(*token_name);
+ talloc_free(*module_name);
+ talloc_free(*key_id);
+ return ENOMEM;
+ }
+ } else {
+ *label = NULL;
+ }
+
+ /* Re-calculate length for the case where \0 was missing in the blob */
+ if (_pin_len != NULL) {
+ *_pin_len = (*pin == NULL) ? 0 : strlen(*pin);
+ }
+ if (_token_name_len != NULL) {
+ *_token_name_len = (*token_name == NULL) ? 0 : strlen(*token_name);
+ }
+ if (_module_name_len != NULL) {
+ *_module_name_len = (*module_name == NULL) ? 0 : strlen(*module_name);
+ }
+
+ if (_key_id_len != NULL) {
+ *_key_id_len = (*key_id == NULL) ? 0 : strlen(*key_id);
+ }
+
+ if (_label_len != NULL) {
+ *_label_len = (*label == NULL) ? 0 : strlen(*label);
+ }
+
+ return EOK;
+}
+
+errno_t sss_authtok_get_sc(struct sss_auth_token *tok,
+ const char **_pin, size_t *_pin_len,
+ const char **_token_name, size_t *_token_name_len,
+ const char **_module_name, size_t *_module_name_len,
+ const char **_key_id, size_t *_key_id_len,
+ const char **_label, size_t *_label_len)
+{
+ size_t c = 0;
+ size_t pin_len;
+ size_t token_name_len;
+ size_t module_name_len;
+ size_t key_id_len;
+ size_t label_len;
+ uint32_t tmp_uint32_t;
+
+ if (!tok) {
+ return EFAULT;
+ }
+
+ if (tok->type != SSS_AUTHTOK_TYPE_SC_PIN
+ && tok->type != SSS_AUTHTOK_TYPE_SC_KEYPAD) {
+ return (tok->type == SSS_AUTHTOK_TYPE_EMPTY) ? ENOENT : EACCES;
+ }
+
+ if (tok->length < 5 * sizeof(uint32_t)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n");
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data, &c);
+ pin_len = tmp_uint32_t - 1;
+ SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c);
+ token_name_len = tmp_uint32_t - 1;
+ SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c);
+ module_name_len = tmp_uint32_t -1;
+ SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c);
+ key_id_len = tmp_uint32_t -1;
+ SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c);
+ label_len = tmp_uint32_t -1;
+
+ if (tok->length != 5 * sizeof(uint32_t) + 5 + pin_len + token_name_len
+ + module_name_len + key_id_len
+ + label_len) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n");
+ return EINVAL;
+ }
+
+ if (_pin != NULL) {
+ *_pin = (const char *) tok->data + c;
+ }
+ if (_pin_len != NULL) {
+ *_pin_len = pin_len;
+ }
+
+ if (_token_name != NULL) {
+ *_token_name = (const char *) tok->data + c + pin_len + 1;
+ }
+ if (_token_name_len != NULL) {
+ *_token_name_len = token_name_len;
+ }
+
+ if (_module_name != NULL) {
+ *_module_name = (const char *) tok->data + c + pin_len + 1
+ + token_name_len + 1;
+ }
+ if (_module_name_len != NULL) {
+ *_module_name_len = module_name_len;
+ }
+
+ if (_key_id != NULL) {
+ *_key_id = (const char *) tok->data + c + pin_len + 1
+ + token_name_len + 1 + module_name_len + 1;
+ }
+ if (_key_id_len != NULL) {
+ *_key_id_len = key_id_len;
+ }
+
+ if (_label != NULL) {
+ *_label = (const char *) tok->data + c + pin_len + 1
+ + token_name_len + 1 + module_name_len + 1
+ + key_id_len + 1;
+ }
+ if (_label_len != NULL) {
+ *_label_len = label_len;
+ }
+
+ return EOK;
+}
diff --git a/src/util/authtok.h b/src/util/authtok.h
new file mode 100644
index 0000000..f9cc79c
--- /dev/null
+++ b/src/util/authtok.h
@@ -0,0 +1,523 @@
+/*
+ SSSD - auth utils
+
+ Copyright (C) Simo Sorce <simo@redhat.com> 2012
+
+ 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 __AUTHTOK_H__
+#define __AUTHTOK_H__
+
+#include "util/util.h"
+#include "util/authtok-utils.h"
+#include "sss_client/sss_cli.h"
+
+#define IS_SC_AUTHTOK(tok) ( \
+ sss_authtok_get_type((tok)) == SSS_AUTHTOK_TYPE_SC_PIN \
+ || sss_authtok_get_type((tok)) == SSS_AUTHTOK_TYPE_SC_KEYPAD)
+
+
+/* Use sss_authtok_* accessor functions instead of struct sss_auth_token
+ */
+struct sss_auth_token;
+
+/**
+ * @brief Converts token type to string for debugging purposes.
+ *
+ * @param type Tonen type
+ *
+ * @return Token type string representation
+ */
+const char *sss_authtok_type_to_str(enum sss_authtok_type type);
+
+/**
+ * @brief Returns the token type
+ *
+ * @param tok A pointer to an sss_auth_token
+ *
+ * @return An sss_authtok_type (empty, password, ...)
+ */
+enum sss_authtok_type sss_authtok_get_type(struct sss_auth_token *tok);
+
+/**
+ * @brief Returns the token size
+ *
+ * @param tok A pointer to an sss_auth_token
+ *
+ * @return The current size of the token payload
+ */
+size_t sss_authtok_get_size(struct sss_auth_token *tok);
+
+/**
+ * @brief Get the data buffer
+ *
+ * @param tok A pointer to an sss_auth_token
+ *
+ * @return A pointer to the token payload
+ */
+uint8_t *sss_authtok_get_data(struct sss_auth_token *tok);
+
+/**
+ * @brief Returns a const string if the auth token is of type
+ SSS_AUTHTOK_TYPE_PASSWORD, otherwise it returns an error
+ *
+ * @param tok A pointer to an sss_auth_token
+ * @param pwd A pointer to a const char *, that will point to a null
+ * terminated string
+ * @param len The length of the password string
+ *
+ * @return EOK on success
+ * ENOENT if the token is empty
+ * EACCESS if the token is not a password token
+ */
+errno_t sss_authtok_get_password(struct sss_auth_token *tok,
+ const char **pwd, size_t *len);
+
+/**
+ * @brief Set a password into an auth token, replacing any previous data
+ *
+ * @param tok A pointer to an sss_auth_token structure to change, also
+ * used as a memory context to allocate the internal data.
+ * @param password A string
+ * @param len The length of the string or, if 0 is passed,
+ * then strlen(password) will be used internally.
+ *
+ * @return EOK on success
+ * ENOMEM on error
+ */
+errno_t sss_authtok_set_password(struct sss_auth_token *tok,
+ const char *password, size_t len);
+
+/**
+ * @brief Returns a const string if the auth token is of type
+ SSS_AUTHTOK_TYPE_CCFILE, otherwise it returns an error
+ *
+ * @param tok A pointer to an sss_auth_token
+ * @param ccfile A pointer to a const char *, that will point to a null
+ * terminated string, also used as a memory context use to allocate the internal data
+ * @param len The length of the string
+ *
+ * @return EOK on success
+ * ENOENT if the token is empty
+ * EACCESS if the token is not a password token
+ */
+errno_t sss_authtok_get_ccfile(struct sss_auth_token *tok,
+ const char **ccfile, size_t *len);
+
+/**
+ * @brief Set a cc file name into an auth token, replacing any previous data
+ *
+ * @param tok A pointer to an sss_auth_token structure to change, also
+ * used as a memory context to allocate the internal data.
+ * @param ccfile A null terminated string
+ * @param len The length of the string
+ *
+ * @return EOK on success
+ * ENOMEM on error
+ */
+errno_t sss_authtok_set_ccfile(struct sss_auth_token *tok,
+ const char *ccfile, size_t len);
+
+/**
+ * @brief Resets an auth token to the empty status
+ *
+ * @param tok A pointer to an sss_auth_token structure to reset
+ *
+ * NOTE: This function uses sss_erase_mem_securely() on the payload if the type
+ * is SSS_AUTHTOK_TYPE_PASSWORD
+ */
+void sss_authtok_set_empty(struct sss_auth_token *tok);
+
+/**
+ * @brief Set an auth token by type, replacing any previous data
+ *
+ * @param tok A pointer to an sss_auth_token structure to change, also
+ * used as a memory context to allocate the internal data.
+ * @param type A valid authtok type
+ * @param data A data pointer
+ * @param len The length of the data
+ *
+ * @return EOK on success
+ * ENOMEM or EINVAL on error
+ */
+errno_t sss_authtok_set(struct sss_auth_token *tok,
+ enum sss_authtok_type type,
+ const uint8_t *data, size_t len);
+
+/**
+ * @brief Copy an auth token from source to destination
+ *
+ * @param src The source auth token
+ * @param dst The destination auth token, also used as a memory context
+ * to allocate dst internal data.
+ *
+ * @return EOK on success
+ * ENOMEM on error
+ */
+errno_t sss_authtok_copy(struct sss_auth_token *src,
+ struct sss_auth_token *dst);
+
+/**
+ * @brief Uses sss_erase_mem_securely to wipe the password from memory
+ * if the authtoken contains a password, otherwise does nothing.
+ *
+ * @param tok A pointer to an sss_auth_token structure to change
+ *
+ * NOTE: This function should only be used in destructors or similar
+ * functions where freeing the actual string is unsafe and where it can
+ * be guaranteed that the auth token will not be used anymore.
+ * Use sss_authtok_set_empty() in normal circumstances.
+ */
+void sss_authtok_wipe_password(struct sss_auth_token *tok);
+
+/**
+ * @brief Create new empty struct sss_auth_token.
+ *
+ * @param mem_ctx A memory context use to allocate the internal data
+ * @return A pointer to new empty struct sss_auth_token
+ * NULL in case of failure
+ *
+ * NOTE: This function is the only way, how to create new empty
+ * struct sss_auth_token.
+ */
+struct sss_auth_token *sss_authtok_new(TALLOC_CTX *mem_ctx);
+
+/**
+ * @brief Set authtoken with 2FA data
+ *
+ * @param tok A pointer to an sss_auth_token structure to change, also
+ * used as a memory context to allocate the internal data.
+ * @param[in] fa1 First authentication factor, null terminated
+ * @param[in] fa1_len Length of the first authentication factor, if 0
+ * strlen() will be called internally
+ * @param[in] fa2 Second authentication factor, null terminated
+ * @param[in] fa2_len Length of the second authentication factor, if 0
+ * strlen() will be called internally
+ *
+ * @return EOK on success
+ * ENOMEM if memory allocation failed
+ * EINVAL if input data is not consistent
+ */
+errno_t sss_authtok_set_2fa(struct sss_auth_token *tok,
+ const char *fa1, size_t fa1_len,
+ const char *fa2, size_t fa2_len);
+
+/**
+ * @brief Get 2FA factors from authtoken
+ *
+ * @param tok A pointer to an sss_auth_token structure to change, also
+ * used as a memory context to allocate the internal data.
+ * @param[out] fa1 A pointer to a const char *, that will point to a
+ * null terminated string holding the first
+ * authentication factor, may not be modified or freed
+ * @param[out] fa1_len Length of the first authentication factor
+ * @param[out] fa2 A pointer to a const char *, that will point to a
+ * null terminated string holding the second
+ * authentication factor, may not be modified or freed
+ * @param[out] fa2_len Length of the second authentication factor
+ *
+ * @return EOK on success
+ * ENOMEM if memory allocation failed
+ * EINVAL if input data is not consistent
+ * ENOENT if the token is empty
+ * EACCESS if the token is not a 2FA token
+ */
+errno_t sss_authtok_get_2fa(struct sss_auth_token *tok,
+ const char **fa1, size_t *fa1_len,
+ const char **fa2, size_t *fa2_len);
+
+/**
+ * @brief Set a Smart Card PIN into an auth token, replacing any previous data
+ *
+ * @param tok A pointer to an sss_auth_token structure to change, also
+ * used as a memory context to allocate the internal data.
+ * @param pin A string
+ * @param len The length of the string or, if 0 is passed,
+ * then strlen(password) will be used internally.
+ *
+ * @return EOK on success
+ * ENOMEM on error
+ */
+errno_t sss_authtok_set_sc_pin(struct sss_auth_token *tok, const char *pin,
+ size_t len);
+
+/**
+ * @brief Returns a Smart Card PIN as const string if the auth token is of
+ * type SSS_AUTHTOK_TYPE_SC_PIN, otherwise it returns an error
+ *
+ * @param tok A pointer to an sss_auth_token
+ * @param pin A pointer to a const char *, that will point to a null
+ * terminated string
+ * @param len The length of the pin string
+ *
+ * @return EOK on success
+ * ENOENT if the token is empty
+ * EACCESS if the token is not a Smart Card PIN token
+ */
+errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **pin,
+ size_t *len);
+
+/**
+ * @brief Sets an auth token to type SSS_AUTHTOK_TYPE_SC_KEYPAD, replacing any
+ * previous data
+ *
+ * @param tok A pointer to an sss_auth_token structure to change, also
+ * used as a memory context to allocate the internal data.
+ */
+void sss_authtok_set_sc_keypad(struct sss_auth_token *tok);
+
+/**
+ * @brief Set complete Smart Card authentication blob including PKCS#11 token
+ * name, module name and key id.
+ *
+ * @param tok A pointer to an sss_auth_token
+ * @param type Authentication token type, may be
+ * SSS_AUTHTOK_TYPE_SC_PIN or SSS_AUTHTOK_TYPE_SC_KEYPAD
+ * @param pin A pointer to a const char *, that will point to a null
+ * terminated string containing the PIN
+ * @param pin_len The length of the pin string, if set to 0 it will be
+ * calculated
+ * @param token_name A pointer to a const char *, that will point to a null
+ * terminated string containing the PKCS#11 token name
+ * @param token_name_len The length of the token name string, if set to 0 it
+ * will be calculated
+ * @param module_name A pointer to a const char *, that will point to a null
+ * terminated string containing the PKCS#11 module name
+ * @param module_name_len The length of the module name string, if set to 0 it
+ * will be calculated
+ * @param key_id A pointer to a const char *, that will point to a null
+ * terminated string containing the PKCS#11 key id
+ * @param key_id_len The length of the key id string, if set to 0 it will be
+ * calculated
+ * @param label A pointer to a const char *, that will point to a null
+ * terminated string containing the PKCS#11 label
+ * @param label_len The length of the label string, if set to 0 it will be
+ * calculated
+ *
+ * @return EOK on success
+ * EINVAL unexpected or inval input
+ * ENOMEM memory allocation error
+ */
+errno_t sss_authtok_set_sc(struct sss_auth_token *tok,
+ enum sss_authtok_type type,
+ const char *pin, size_t pin_len,
+ const char *token_name, size_t token_name_len,
+ const char *module_name, size_t module_name_len,
+ const char *key_id, size_t key_id_len,
+ const char *label, size_t label_len);
+/**
+ * @brief Set a Smart Card authentication data, replacing any previous data
+ *
+ * @param tok A pointer to an sss_auth_token structure to change, also
+ * used as a memory context to allocate the internal data.
+ * @param data Smart Card authentication data blob
+ * @param len The length of the blob
+ *
+ * @return EOK on success
+ * ENOMEM on error
+ */
+errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok,
+ const uint8_t *data,
+ size_t len);
+
+/**
+ * @brief Get complete Smart Card authtoken data
+ *
+ * @param tok A pointer to an sss_auth_token structure
+ * @param[out] _pin A pointer to a const char *, that will point to
+ * a null terminated string holding the PIN,
+ * may not be modified or freed
+ * @param[out] _pin__len Length of the PIN
+ * @param[out] _token_name A pointer to a const char *, that will point to
+ * a null terminated string holding the PKCS#11
+ * token name, may not be modified or freed
+ * @param[out] _token_name_len Length of the PKCS#11 token name
+ * @param[out] _module_name A pointer to a const char *, that will point to
+ * a null terminated string holding the PKCS#11
+ * module name, may not be modified or freed
+ * @param[out] _module_name_len Length of the PKCS#11 module name
+ * @param[out] _key_id A pointer to a const char *, that will point to
+ * a null terminated string holding the PKCS#11
+ * key id, may not be modified or freed
+ * @param[out] _key_id_len Length of the PKCS#11 key id
+ * @param[out] _label A pointer to a const char *, that will point to
+ * a null terminated string holding the PKCS#11
+ * label, may not be modified or freed
+ * @param[out] _label_len Length of the PKCS#11 label
+ *
+ * Any of the output pointers may be NULL if the caller does not need the
+ * specific item.
+ *
+ * @return EOK on success
+ * EFAULT missing token
+ * EINVAL if input data is not consistent
+ * ENOENT if the token is empty
+ * EACCESS if the token is not a Smart Card token
+ */
+errno_t sss_authtok_get_sc(struct sss_auth_token *tok,
+ const char **_pin, size_t *_pin_len,
+ const char **_token_name, size_t *_token_name_len,
+ const char **_module_name, size_t *_module_name_len,
+ const char **_key_id, size_t *_key_id_len,
+ const char **_label, size_t *_label_len);
+
+
+/**
+ * @brief Returns a const string if the auth token is of type
+ SSS_AUTHTOK_TYPE_2FA_SINGLE, otherwise it returns an error
+ *
+ * @param tok A pointer to an sss_auth_token
+ * @param pwd A pointer to a const char *, that will point to a null
+ * terminated string
+ * @param len The length of the credential string
+ *
+ * @return EOK on success
+ * ENOENT if the token is empty
+ * EACCESS if the token is not a password token
+ */
+errno_t sss_authtok_get_2fa_single(struct sss_auth_token *tok,
+ const char **str, size_t *len);
+
+/**
+ * @brief Set a 2FA credentials in a single strings into an auth token,
+ * replacing any previous data
+ *
+ * @param tok A pointer to an sss_auth_token structure to change, also
+ * used as a memory context to allocate the internal data.
+ * @param str A string where the two authentication factors are
+ * concatenated together
+ * @param len The length of the string or, if 0 is passed,
+ * then strlen(password) will be used internally.
+ *
+ * @return EOK on success
+ * ENOMEM on error
+ */
+errno_t sss_authtok_set_2fa_single(struct sss_auth_token *tok,
+ const char *str, size_t len);
+
+/**
+ * @brief Returns a const string if the auth token is of type
+ SSS_AUTHTOK_TYPE_OAUTH2, otherwise it returns an error
+ *
+ * @param tok A pointer to an sss_auth_token
+ * @param pwd A pointer to a const char *, that will point to a null
+ * terminated string
+ * @param len The length of the credential string
+ *
+ * @return EOK on success
+ * ENOENT if the token is empty
+ * EACCESS if the token is not a password token
+ */
+errno_t sss_authtok_get_oauth2(struct sss_auth_token *tok,
+ const char **str, size_t *len);
+
+/**
+ * @brief Set one-time password into an auth token, replacing any previous data.
+ *
+ * @param tok A pointer to an sss_auth_token structure to change, also
+ * used as a memory context to allocate the internal data.
+ * @param str A string that holds the one-time password.
+ * @param len The length of the string or, if 0 is passed,
+ * then strlen(password) will be used internally.
+ *
+ * @return EOK on success
+ * ENOMEM on error
+ */
+errno_t sss_authtok_set_oauth2(struct sss_auth_token *tok,
+ const char *str, size_t len);
+/**
+ * @brief Returns a const string if the auth token is of type
+ SSS_AUTHTOK_TYPE_PASSKEY_REPLY, otherwise it returns an error
+ *
+ * @param tok A pointer to an sss_auth_token
+ * @param str A string that holds the passkey assertion data
+ * @param len The length of the credential string
+ *
+ * @return EOK on success
+ * ENOENT if the token is empty
+ * EACCESS if the token is not a passkey token
+ */
+errno_t sss_authtok_set_passkey_reply(struct sss_auth_token *tok,
+ const char *str, size_t len);
+/**
+ * @brief Returns a const string if the auth token is of type
+ SSS_AUTHTOK_TYPE_PASSKEY_REPLY, otherwise it returns an error
+ *
+ * @param tok A pointer to an sss_auth_token
+ * @param str A pointer to a const char *, that will point to a null
+ * terminated string
+ * @param len The length of the credential string
+ *
+ * @return EOK on success
+ * ENOENT if the token is empty
+ * EACCESS if the token is not a password token
+ */
+errno_t sss_authtok_get_passkey_reply(struct sss_auth_token *tok,
+ const char **str, size_t *len);
+
+/**
+ * @brief Returns a const string if the auth token is of type
+ SSS_AUTHTOK_TYPE_PASSKEY, otherwise it returns an error
+ *
+ * @param mem_ctx Parent talloc context to attach to
+ * @param tok A pointer to an sss_auth_token
+ * @param prompt A pointer to a const char *, that will point to a null
+ * terminated string
+ * @param key A pointer to a const char *, that will point to a null
+ * terminated string
+ * @param pin A pointer to a const char *, that will point to a null
+ * terminated string
+ *
+ * @return EOK on success
+ * ENOENT if the token is empty
+ * EACCESS if the token is not a password token
+ */
+errno_t sss_authtok_get_passkey(TALLOC_CTX *mem_ctx,
+ struct sss_auth_token *tok,
+ const char **_prompt, const char **_key,
+ const char **_pin, size_t *_pin_len);
+/**
+ * @brief Returns a const string if the auth token is of type
+ SSS_AUTHTOK_TYPE_PASSKEY, otherwise it returns the error code
+ *
+ * @param tok A pointer to an sss_auth_token
+ * @param pwd A pointer to a const char *, that will point to a null
+ * terminated string
+ * @param len The length of the credential string
+ *
+ * @return EOK on success
+ * ENOENT if the token is empty
+ * EACCESS if the token is not a password token
+ */
+errno_t sss_authtok_get_passkey_pin(struct sss_auth_token *tok,
+ const char **pin, size_t *len);
+
+/**
+ * @brief Set passkey kerberos preauth credentials into an auth token,
+ * replacing any previous data.
+ *
+ * @param tok A pointer to an sss_auth_token structure to change, also
+ * used as a memory context to allocate the internal data.
+ * @param pin A string that holds the passkey PIN.
+ * @param len The length of the string or, if 0 is passed,
+ * then strlen(password) will be used internally.
+ *
+ * @return EOK on success
+ * ENOMEM on error
+ */
+errno_t sss_authtok_set_passkey_krb(struct sss_auth_token *tok,
+ const char *prompt, const char *key,
+ const char *pin);
+#endif /* __AUTHTOK_H__ */
diff --git a/src/util/backup_file.c b/src/util/backup_file.c
new file mode 100644
index 0000000..a164a86
--- /dev/null
+++ b/src/util/backup_file.c
@@ -0,0 +1,120 @@
+/*
+ SSSD
+
+ Backup files
+
+ Copyright (C) Simo Sorce 2009
+
+ 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 "util/util.h"
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#define BUFFER_SIZE 65536
+
+int backup_file(const char *src_file, int dbglvl)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ char buf[BUFFER_SIZE];
+ int src_fd = -1;
+ int dst_fd = -1;
+ char *dst_file;
+ ssize_t numread;
+ ssize_t written;
+ int ret, i;
+
+ src_fd = open(src_file, O_RDONLY);
+ if (src_fd < 0) {
+ ret = errno;
+ DEBUG(dbglvl, "Error (%d [%s]) opening source file %s\n",
+ ret, strerror(ret), src_file);
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* try a few times to come up with a new backup file, then give up */
+ for (i = 0; i < 10; i++) {
+ if (i == 0) {
+ dst_file = talloc_asprintf(tmp_ctx, "%s.bak", src_file);
+ } else {
+ dst_file = talloc_asprintf(tmp_ctx, "%s.bak%d", src_file, i);
+ }
+ if (!dst_file) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ errno = 0;
+ dst_fd = open(dst_file, O_CREAT|O_EXCL|O_WRONLY, 0600);
+ ret = errno;
+
+ if (dst_fd >= 0) break;
+
+ if (ret != EEXIST) {
+ DEBUG(dbglvl, "Error (%d [%s]) opening destination file %s\n",
+ ret, strerror(ret), dst_file);
+ goto done;
+ }
+ }
+ if (ret != 0) {
+ DEBUG(dbglvl, "Error (%d [%s]) opening destination file %s\n",
+ ret, strerror(ret), dst_file);
+ goto done;
+ }
+
+ /* copy file contents */
+ while (1) {
+ errno = 0;
+ numread = sss_atomic_read_s(src_fd, buf, BUFFER_SIZE);
+ if (numread < 0) {
+ ret = errno;
+ DEBUG(dbglvl, "Error (%d [%s]) reading from source %s\n",
+ ret, strerror(ret), src_file);
+ goto done;
+ }
+ if (numread == 0) break;
+
+ errno = 0;
+ written = sss_atomic_write_s(dst_fd, buf, numread);
+ if (written == -1) {
+ ret = errno;
+ DEBUG(dbglvl, "Error (%d [%s]) writing to destination %s\n",
+ ret, strerror(ret), dst_file);
+ goto done;
+ }
+
+ if (written != numread) {
+ DEBUG(dbglvl, "Wrote %zd bytes expected %zd bytes\n",
+ written, numread);
+ ret = EIO;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (src_fd != -1) close(src_fd);
+ if (dst_fd != -1) close(dst_fd);
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/util/become_user.c b/src/util/become_user.c
new file mode 100644
index 0000000..c3f726d
--- /dev/null
+++ b/src/util/become_user.c
@@ -0,0 +1,212 @@
+/*
+ SSSD
+
+ Kerberos 5 Backend Module -- Utilities
+
+ Authors:
+ Sumit Bose <sbose@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/>.
+*/
+
+#include "util/util.h"
+#include <grp.h>
+
+errno_t become_user(uid_t uid, gid_t gid)
+{
+ uid_t cuid;
+ int ret;
+
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Trying to become user [%"SPRIuid"][%"SPRIgid"].\n", uid, gid);
+
+ /* skip call if we already are the requested user */
+ cuid = geteuid();
+ if (uid == cuid) {
+ DEBUG(SSSDBG_FUNC_DATA, "Already user [%"SPRIuid"].\n", uid);
+ return EOK;
+ }
+
+ /* drop supplementary groups first */
+ ret = setgroups(0, NULL);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "setgroups failed [%d][%s].\n", ret, strerror(ret));
+ return ret;
+ }
+
+ /* change GID so that root cannot be regained (changes saved GID too) */
+ ret = setresgid(gid, gid, gid);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "setresgid failed [%d][%s].\n", ret, strerror(ret));
+ return ret;
+ }
+
+ /* change UID so that root cannot be regained (changes saved UID too) */
+ /* this call also takes care of dropping CAP_SETUID, so this is a PNR */
+ ret = setresuid(uid, uid, uid);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "setresuid failed [%d][%s].\n", ret, strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+struct sss_creds {
+ uid_t uid;
+ gid_t gid;
+ int num_gids;
+ gid_t gids[];
+};
+
+errno_t restore_creds(struct sss_creds *saved_creds);
+
+/* This is a reversible version of become_user, and returns the saved
+ * credentials so that creds can be switched back calling restore_creds */
+errno_t switch_creds(TALLOC_CTX *mem_ctx,
+ uid_t uid, gid_t gid,
+ int num_gids, gid_t *gids,
+ struct sss_creds **saved_creds)
+{
+ struct sss_creds *ssc = NULL;
+ int size;
+ int ret;
+ uid_t myuid;
+ uid_t mygid;
+
+ DEBUG(SSSDBG_FUNC_DATA, "Switch user to [%d][%d].\n", uid, gid);
+
+ myuid = geteuid();
+ mygid = getegid();
+
+ if (saved_creds) {
+ /* save current user credentials */
+ size = getgroups(0, NULL);
+ if (size == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ssc = talloc_size(mem_ctx,
+ (sizeof(struct sss_creds) + size * sizeof(gid_t)));
+ if (!ssc) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Allocation failed!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ ssc->num_gids = size;
+
+ size = getgroups(ssc->num_gids, ssc->gids);
+ if (size == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n",
+ ret, strerror(ret));
+ /* free ssc immediately otherwise the code will try to restore
+ * wrong creds */
+ talloc_zfree(ssc);
+ goto done;
+ }
+
+ /* we care only about effective ids */
+ ssc->uid = myuid;
+ ssc->gid = mygid;
+ }
+
+ /* if we are regaining root, set EUID first so that we have CAP_SETUID back,
+ * and the other calls work too, otherwise call it last so that we can
+ * change groups before we loose CAP_SETUID */
+ if (uid == 0) {
+ ret = setresuid(0, 0, 0);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "setresuid failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+ }
+
+ /* TODO: use libcap-ng if we need to get/set capabilities too? */
+
+ if (myuid == uid && mygid == gid) {
+ DEBUG(SSSDBG_FUNC_DATA, "Already user [%"SPRIuid"].\n", uid);
+ talloc_zfree(ssc);
+ return EOK;
+ }
+
+ /* try to setgroups first should always work if CAP_SETUID is set,
+ * otherwise it will always fail, failure is not critical though as
+ * generally we only really care about UID and at most primary GID */
+ ret = setgroups(num_gids, gids);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "setgroups failed [%d][%s].\n", ret, strerror(ret));
+ }
+
+ /* change GID now, (leaves saved GID to current, so we can restore) */
+ ret = setresgid(-1, gid, -1);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "setresgid failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+
+ if (uid != 0) {
+ /* change UID, (leaves saved UID to current, so we can restore) */
+ ret = setresuid(-1, uid, -1);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "setresuid failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ if (ret) {
+ /* attempt to restore creds first */
+ restore_creds(ssc);
+ talloc_free(ssc);
+ } else if (saved_creds) {
+ *saved_creds = ssc;
+ }
+ return ret;
+}
+
+errno_t restore_creds(struct sss_creds *saved_creds)
+{
+ if (saved_creds == NULL) {
+ /* In case save_creds was saved with the UID already dropped */
+ return EOK;
+ }
+
+ return switch_creds(saved_creds,
+ saved_creds->uid,
+ saved_creds->gid,
+ saved_creds->num_gids,
+ saved_creds->gids, NULL);
+}
diff --git a/src/util/cert.h b/src/util/cert.h
new file mode 100644
index 0000000..0c95865
--- /dev/null
+++ b/src/util/cert.h
@@ -0,0 +1,50 @@
+/*
+ SSSD - certificate handling utils - openssl version
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2015
+
+ 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 <stdint.h>
+#include <talloc.h>
+
+#include "util/util.h"
+
+#ifndef __CERT_H__
+#define __CERT_H__
+
+errno_t sss_cert_der_to_pem(TALLOC_CTX *mem_ctx, const uint8_t *der_blob,
+ size_t der_size, char **pem, size_t *pem_size);
+
+errno_t sss_cert_pem_to_der(TALLOC_CTX *mem_ctx, const char *pem,
+ uint8_t **der_blob, size_t *der_size);
+
+errno_t sss_cert_derb64_to_pem(TALLOC_CTX *mem_ctx, const char *derb64,
+ char **pem, size_t *pem_size);
+
+errno_t sss_cert_pem_to_derb64(TALLOC_CTX *mem_ctx, const char *pem,
+ char **derb64);
+
+errno_t bin_to_ldap_filter_value(TALLOC_CTX *mem_ctx,
+ const uint8_t *blob, size_t blob_size,
+ char **_str);
+
+errno_t get_ssh_key_from_cert(TALLOC_CTX *mem_ctx,
+ uint8_t *der_blob, size_t der_size,
+ uint8_t **key_blob, size_t *key_size);
+
+errno_t get_ssh_key_from_derb64(TALLOC_CTX *mem_ctx, const char *derb64,
+ uint8_t **key_blob, size_t *key_size);
+#endif /* __CERT_H__ */
diff --git a/src/util/cert/cert_common.c b/src/util/cert/cert_common.c
new file mode 100644
index 0000000..610d755
--- /dev/null
+++ b/src/util/cert/cert_common.c
@@ -0,0 +1,138 @@
+/*
+ SSSD - certificate handling utils
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2015
+
+ 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 "util/util.h"
+#include "util/cert.h"
+#include "util/crypto/sss_crypto.h"
+
+errno_t sss_cert_derb64_to_pem(TALLOC_CTX *mem_ctx, const char *derb64,
+ char **pem, size_t *pem_size)
+{
+ int ret;
+ unsigned char *der;
+ size_t der_size;
+
+ if (derb64 == NULL) {
+ return EINVAL;
+ }
+
+ der = sss_base64_decode(mem_ctx, derb64, &der_size);
+ if (der == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+ return EINVAL;
+ }
+
+ ret = sss_cert_der_to_pem(mem_ctx, der, der_size, pem, pem_size);
+ talloc_free(der);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_cert_der_to_pem failed.\n");
+ }
+
+ return ret;
+}
+
+errno_t sss_cert_pem_to_derb64(TALLOC_CTX *mem_ctx, const char *pem,
+ char **derb64)
+{
+ int ret;
+ uint8_t *der;
+ size_t der_size;
+
+ ret = sss_cert_pem_to_der(mem_ctx, pem, &der, &der_size);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_cert_pem_to_der failed.\n");
+ return ret;
+ }
+
+ *derb64 = sss_base64_encode(mem_ctx, der, der_size);
+ talloc_free(der);
+ if (*derb64 == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_encode failed.\n");
+ return EINVAL;
+ }
+
+ return EOK;
+}
+
+errno_t bin_to_ldap_filter_value(TALLOC_CTX *mem_ctx,
+ const uint8_t *blob, size_t blob_size,
+ char **_str)
+{
+ int ret;
+ size_t c;
+ size_t len;
+ char *str = NULL;
+ char *p;
+
+ if (blob == NULL || blob_size == 0 || _str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing input parameter.\n");
+ return EINVAL;
+ }
+
+ len = (blob_size * 3) + 1;
+ str = talloc_size(mem_ctx, len);
+ if (str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
+ return ENOMEM;
+ }
+ str[len - 1] = '\0';
+
+ p = str;
+ for (c = 0; c < blob_size; c++) {
+ ret = snprintf(p, 4, "\\%02x", blob[c]);
+ if (ret != 3) {
+ DEBUG(SSSDBG_OP_FAILURE, "snprintf failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ p += 3;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *_str = str;
+ } else {
+ talloc_free(str);
+ }
+
+ return ret;
+}
+
+errno_t get_ssh_key_from_derb64(TALLOC_CTX *mem_ctx, const char *derb64,
+ uint8_t **key_blob, size_t *key_size)
+{
+ int ret;
+ uint8_t *der_blob;
+ size_t der_size;
+
+ der_blob = sss_base64_decode(mem_ctx, derb64, &der_size);
+ if (der_blob == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+ return EIO;
+ }
+
+ ret = get_ssh_key_from_cert(mem_ctx, der_blob, der_size,
+ key_blob, key_size);
+ talloc_free(der_blob);
+
+ return ret;
+}
diff --git a/src/util/cert/libcrypto/cert.c b/src/util/cert/libcrypto/cert.c
new file mode 100644
index 0000000..61908c7
--- /dev/null
+++ b/src/util/cert/libcrypto/cert.c
@@ -0,0 +1,425 @@
+/*
+ SSSD - certificate handling utils - OpenSSL version
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2015
+
+ 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 <openssl/x509.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+
+#include "util/util.h"
+#include "util/sss_endian.h"
+
+errno_t sss_cert_der_to_pem(TALLOC_CTX *mem_ctx, const uint8_t *der_blob,
+ size_t der_size, char **pem, size_t *pem_size)
+{
+ X509 *x509 = NULL;
+ BIO *bio_mem = NULL;
+ const unsigned char *d;
+ int ret;
+ long p_size;
+ char *p;
+
+ if (der_blob == NULL || der_size == 0) {
+ return EINVAL;
+ }
+
+ d = (const unsigned char *) der_blob;
+
+ x509 = d2i_X509(NULL, &d, (int) der_size);
+ if (x509 == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "d2i_X509 failed.\n");
+ return EINVAL;
+ }
+
+ bio_mem = BIO_new(BIO_s_mem());
+ if (bio_mem == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "BIO_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = PEM_write_bio_X509(bio_mem, x509);
+ if (ret != 1) {
+ DEBUG(SSSDBG_OP_FAILURE, "PEM_write_bio_X509 failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ p_size = BIO_get_mem_data(bio_mem, &p);
+ if (p_size == 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected PEM size [%ld].\n", p_size);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (pem != NULL) {
+ *pem = talloc_strndup(mem_ctx, p, p_size);
+ if (*pem == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_memdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (pem_size != NULL) {
+ *pem_size = p_size;
+ }
+
+ ret = EOK;
+
+done:
+ X509_free(x509);
+ BIO_free_all(bio_mem);
+
+ return ret;
+}
+
+errno_t sss_cert_pem_to_der(TALLOC_CTX *mem_ctx, const char *pem,
+ uint8_t **_der_blob, size_t *_der_size)
+{
+ X509 *x509 = NULL;
+ BIO *bio_mem = NULL;
+ int ret;
+ unsigned char *buf;
+ int buf_size;
+ uint8_t *der_blob;
+ size_t der_size;
+
+ if (pem == NULL) {
+ return EINVAL;
+ }
+
+ bio_mem = BIO_new(BIO_s_mem());
+ if (bio_mem == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "BIO_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = BIO_puts(bio_mem, pem);
+ if (ret <= 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "BIO_puts failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ x509 = PEM_read_bio_X509(bio_mem, NULL, NULL, NULL);
+ if (x509 == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "PEM_read_bio_X509 failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ buf_size = i2d_X509(x509, NULL);
+ if (buf_size <= 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "i2d_X509 failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ if (_der_blob != NULL) {
+ buf = talloc_size(mem_ctx, buf_size);
+ if (buf == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ der_blob = buf;
+
+ der_size = i2d_X509(x509, &buf);
+ if (der_size != buf_size) {
+ talloc_free(der_blob);
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "i2d_X509 size mismatch between two calls.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ *_der_blob = der_blob;
+ }
+
+ if (_der_size != NULL) {
+ *_der_size = buf_size;
+ }
+
+ ret = EOK;
+
+done:
+ X509_free(x509);
+ BIO_free_all(bio_mem);
+
+ return ret;
+
+}
+
+/* SSH EC keys are defined in https://tools.ietf.org/html/rfc5656 */
+#define ECDSA_SHA2_HEADER "ecdsa-sha2-"
+/* Looks like OpenSSH currently only supports the following 3 required
+ * curves. */
+#define IDENTIFIER_NISTP256 "nistp256"
+#define IDENTIFIER_NISTP384 "nistp384"
+#define IDENTIFIER_NISTP521 "nistp521"
+
+static errno_t ec_pub_key_to_ssh(TALLOC_CTX *mem_ctx, EVP_PKEY *cert_pub_key,
+ uint8_t **key_blob, size_t *key_size)
+{
+ int ret;
+ size_t c;
+ uint8_t *buf = NULL;
+ size_t buf_len;
+ EC_KEY *ec_key = NULL;
+ const EC_GROUP *ec_group = NULL;
+ const EC_POINT *ec_public_key = NULL;
+ BN_CTX *bn_ctx = NULL;
+ int key_len;
+ const char *identifier = NULL;
+ int identifier_len;
+ const char *header = NULL;
+ int header_len;
+
+ ec_key = EVP_PKEY_get1_EC_KEY(cert_pub_key);
+ if (ec_key == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ec_group = EC_KEY_get0_group(ec_key);
+
+ switch(EC_GROUP_get_curve_name(ec_group)) {
+ case NID_X9_62_prime256v1:
+ identifier = IDENTIFIER_NISTP256;
+ header = ECDSA_SHA2_HEADER IDENTIFIER_NISTP256;
+ break;
+ case NID_secp384r1:
+ identifier = IDENTIFIER_NISTP384;
+ header = ECDSA_SHA2_HEADER IDENTIFIER_NISTP384;
+ break;
+ case NID_secp521r1:
+ identifier = IDENTIFIER_NISTP521;
+ header = ECDSA_SHA2_HEADER IDENTIFIER_NISTP521;
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported curve [%s]\n",
+ OBJ_nid2sn(EC_GROUP_get_curve_name(ec_group)));
+ ret = EINVAL;
+ goto done;
+ }
+
+ header_len = strlen(header);
+ identifier_len = strlen(identifier);
+
+ ec_public_key = EC_KEY_get0_public_key(ec_key);
+
+ bn_ctx = BN_CTX_new();
+ if (bn_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "BN_CTX_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ key_len = EC_POINT_point2oct(ec_group, ec_public_key,
+ POINT_CONVERSION_UNCOMPRESSED, NULL, 0, bn_ctx);
+ if (key_len == 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "EC_POINT_point2oct failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ buf_len = header_len + identifier_len + key_len + 3 * sizeof(uint32_t);
+ buf = talloc_size(mem_ctx, buf_len * sizeof(uint8_t));
+ if (buf == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ c = 0;
+
+ SAFEALIGN_SET_UINT32(buf, htobe32(header_len), &c);
+ safealign_memcpy(&buf[c], header, header_len, &c);
+
+ SAFEALIGN_SET_UINT32(&buf[c], htobe32(identifier_len), &c);
+ safealign_memcpy(&buf[c], identifier , identifier_len, &c);
+
+ SAFEALIGN_SET_UINT32(&buf[c], htobe32(key_len), &c);
+
+ if (EC_POINT_point2oct(ec_group, ec_public_key,
+ POINT_CONVERSION_UNCOMPRESSED, buf + c, key_len,
+ bn_ctx)
+ != key_len) {
+ DEBUG(SSSDBG_OP_FAILURE, "EC_POINT_point2oct failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ *key_size = buf_len;
+ *key_blob = buf;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(buf);
+ }
+
+ BN_CTX_free(bn_ctx);
+ EC_KEY_free(ec_key);
+
+ return ret;
+}
+
+
+#define SSH_RSA_HEADER "ssh-rsa"
+#define SSH_RSA_HEADER_LEN (sizeof(SSH_RSA_HEADER) - 1)
+
+static errno_t rsa_pub_key_to_ssh(TALLOC_CTX *mem_ctx, EVP_PKEY *cert_pub_key,
+ uint8_t **key_blob, size_t *key_size)
+{
+ int ret;
+ size_t c;
+ size_t size;
+ uint8_t *buf = NULL;
+ const BIGNUM *n;
+ const BIGNUM *e;
+ int modulus_len;
+ unsigned char modulus[OPENSSL_RSA_MAX_MODULUS_BITS/8];
+ int exponent_len;
+ unsigned char exponent[OPENSSL_RSA_MAX_PUBEXP_BITS/8];
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ const RSA *rsa_pub_key = NULL;
+ rsa_pub_key = EVP_PKEY_get0_RSA(cert_pub_key);
+ if (rsa_pub_key == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ RSA_get0_key(rsa_pub_key, &n, &e, NULL);
+#else
+ n = cert_pub_key->pkey.rsa->n;
+ e = cert_pub_key->pkey.rsa->e;
+#endif
+ modulus_len = BN_bn2bin(n, modulus);
+ exponent_len = BN_bn2bin(e, exponent);
+
+ size = SSH_RSA_HEADER_LEN + 3 * sizeof(uint32_t)
+ + modulus_len
+ + exponent_len
+ + 1; /* see comment about missing 00 below */
+ if (exponent[0] & 0x80)
+ size++;
+
+ buf = talloc_size(mem_ctx, size);
+ if (buf == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ c = 0;
+
+ SAFEALIGN_SET_UINT32(buf, htobe32(SSH_RSA_HEADER_LEN), &c);
+ safealign_memcpy(&buf[c], SSH_RSA_HEADER, SSH_RSA_HEADER_LEN, &c);
+ if (exponent[0] & 0x80){
+ SAFEALIGN_SET_UINT32(&buf[c], htobe32(exponent_len+1), &c);
+ SAFEALIGN_SETMEM_VALUE(&buf[c], '\0', unsigned char, &c);
+ } else {
+ SAFEALIGN_SET_UINT32(&buf[c], htobe32(exponent_len), &c);
+ }
+ safealign_memcpy(&buf[c], exponent, exponent_len, &c);
+
+ /* Adding missing 00 which AFAIK is added to make sure
+ * the bigint is handled as positive number */
+ /* TODO: make a better check if 00 must be added or not, e.g. ... & 0x80)
+ */
+ SAFEALIGN_SET_UINT32(&buf[c], htobe32(modulus_len + 1), &c);
+ SAFEALIGN_SETMEM_VALUE(&buf[c], '\0', unsigned char, &c);
+ safealign_memcpy(&buf[c], modulus, modulus_len, &c);
+
+ *key_blob = buf;
+ *key_size = size;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(buf);
+ }
+
+ return ret;
+}
+
+errno_t get_ssh_key_from_cert(TALLOC_CTX *mem_ctx,
+ const uint8_t *der_blob, size_t der_size,
+ uint8_t **key_blob, size_t *key_size)
+{
+ int ret;
+ const unsigned char *d;
+ X509 *cert = NULL;
+ EVP_PKEY *cert_pub_key = NULL;
+
+ if (der_blob == NULL || der_size == 0) {
+ return EINVAL;
+ }
+
+ d = (const unsigned char *) der_blob;
+
+ cert = d2i_X509(NULL, &d, (int) der_size);
+ if (cert == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "d2i_X509 failed.\n");
+ return EINVAL;
+ }
+
+ cert_pub_key = X509_get_pubkey(cert);
+ if (cert_pub_key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "X509_get_pubkey failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ switch (EVP_PKEY_base_id(cert_pub_key)) {
+ case EVP_PKEY_RSA:
+ ret = rsa_pub_key_to_ssh(mem_ctx, cert_pub_key, key_blob, key_size);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "rsa_pub_key_to_ssh failed.\n");
+ goto done;
+ }
+ break;
+ case EVP_PKEY_EC:
+ ret = ec_pub_key_to_ssh(mem_ctx, cert_pub_key, key_blob, key_size);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "rsa_pub_key_to_ssh failed.\n");
+ goto done;
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Expected RSA or EC public key, found unsupported [%d].\n",
+ EVP_PKEY_base_id(cert_pub_key));
+ ret = EINVAL;
+ goto done;
+ }
+
+done:
+
+ EVP_PKEY_free(cert_pub_key);
+ X509_free(cert);
+
+ return ret;
+}
diff --git a/src/util/cert_derb64_to_ldap_filter.c b/src/util/cert_derb64_to_ldap_filter.c
new file mode 100644
index 0000000..838320a
--- /dev/null
+++ b/src/util/cert_derb64_to_ldap_filter.c
@@ -0,0 +1,113 @@
+/*
+ SSSD - helper function with dependency to libsss_certmap.so
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2020
+
+ 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 "util/util.h"
+#include "util/cert.h"
+#include "util/crypto/sss_crypto.h"
+#include "lib/certmap/sss_certmap.h"
+
+errno_t sss_cert_derb64_to_ldap_filter(TALLOC_CTX *mem_ctx, const char *derb64,
+ const char *attr_name,
+ struct sss_certmap_ctx *certmap_ctx,
+ struct sss_domain_info *dom,
+ char **ldap_filter)
+{
+ int ret;
+ unsigned char *der;
+ size_t der_size;
+ char *val;
+ char *filter = NULL;
+ char **domains = NULL;
+ size_t c;
+
+ if (derb64 == NULL || attr_name == NULL) {
+ return EINVAL;
+ }
+
+ der = sss_base64_decode(mem_ctx, derb64, &der_size);
+ if (der == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+ return EINVAL;
+ }
+
+ if (certmap_ctx == NULL) {
+ ret = bin_to_ldap_filter_value(mem_ctx, der, der_size, &val);
+ talloc_free(der);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "bin_to_ldap_filter_value failed.\n");
+ return ret;
+ }
+
+ *ldap_filter = talloc_asprintf(mem_ctx, "(%s=%s)", attr_name, val);
+ talloc_free(val);
+ if (*ldap_filter == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ return ENOMEM;
+ }
+ } else {
+ ret = sss_certmap_get_search_filter(certmap_ctx, der, der_size,
+ &filter, &domains);
+ talloc_free(der);
+ if (ret != 0) {
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Certificate does not match matching-rules.\n");
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_certmap_get_search_filter failed.\n");
+ }
+ } else {
+ if (domains == NULL) {
+ if (IS_SUBDOMAIN(dom)) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Rule applies only to local domain.\n");
+ ret = ENOENT;
+ }
+ } else {
+ for (c = 0; domains[c] != NULL; c++) {
+ if (strcasecmp(dom->name, domains[c]) == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Rule applies to current domain [%s].\n",
+ dom->name);
+ ret = EOK;
+ break;
+ }
+ }
+ if (domains[c] == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Rule does not apply to current domain [%s].\n",
+ dom->name);
+ ret = ENOENT;
+ }
+ }
+ }
+
+ if (ret == EOK) {
+ *ldap_filter = talloc_strdup(mem_ctx, filter);
+ if (*ldap_filter == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ }
+ }
+ sss_certmap_free_filter_and_domains(filter, domains);
+ return ret;
+ }
+
+ return EOK;
+}
diff --git a/src/util/check_file.c b/src/util/check_file.c
new file mode 100644
index 0000000..2203a41
--- /dev/null
+++ b/src/util/check_file.c
@@ -0,0 +1,105 @@
+/*
+ SSSD
+
+ Check file permissions
+
+ Authors:
+ Sumit Bose <sbose@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/>.
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "util/util.h"
+
+static errno_t perform_checks(const char *filename,
+ struct stat *stat_buf,
+ uid_t uid, gid_t gid,
+ mode_t mode, mode_t mask);
+
+errno_t check_file(const char *filename,
+ uid_t uid, uid_t gid, mode_t mode, mode_t mask,
+ struct stat *caller_stat_buf, bool follow_symlink)
+{
+ int ret;
+ struct stat local_stat_buf;
+ struct stat *stat_buf;
+
+ if (caller_stat_buf == NULL) {
+ stat_buf = &local_stat_buf;
+ } else {
+ stat_buf = caller_stat_buf;
+ }
+
+ if (follow_symlink) {
+ ret = stat(filename, stat_buf);
+ } else {
+ ret = lstat(filename, stat_buf);
+ }
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_TRACE_FUNC, "lstat for '%s' failed: [%d][%s].\n",
+ filename, ret, strerror(ret));
+ return ret;
+ }
+
+ return perform_checks(filename, stat_buf, uid, gid, mode, mask);
+}
+
+static errno_t perform_checks(const char *filename,
+ struct stat *stat_buf,
+ uid_t uid, gid_t gid,
+ mode_t mode, mode_t mask)
+{
+ mode_t st_mode;
+
+ if (mask) {
+ st_mode = stat_buf->st_mode & mask;
+ } else {
+ st_mode = stat_buf->st_mode & (S_IFMT|ALLPERMS);
+ }
+
+ if ((mode & S_IFMT) != (st_mode & S_IFMT)) {
+ DEBUG(SSSDBG_TRACE_LIBS, "File '%s' is not of the right type.\n",
+ filename);
+ return EINVAL;
+ }
+
+ if ((st_mode & ALLPERMS) != (mode & ALLPERMS)) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "File '%s' has the wrong (bit masked) mode [%.7o], "
+ "expected [%.7o].\n", filename,
+ (st_mode & ALLPERMS), (mode & ALLPERMS));
+ return EINVAL;
+ }
+
+ if (uid != (uid_t)(-1) && stat_buf->st_uid != uid) {
+ DEBUG(SSSDBG_TRACE_LIBS, "File '%s' must be owned by uid [%d].\n",
+ filename, uid);
+ return EINVAL;
+ }
+
+ if (gid != (gid_t)(-1) && stat_buf->st_gid != gid) {
+ DEBUG(SSSDBG_TRACE_LIBS, "File '%s' must be owned by gid [%d].\n",
+ filename, gid);
+ return EINVAL;
+ }
+
+ return EOK;
+}
diff --git a/src/util/child_common.c b/src/util/child_common.c
new file mode 100644
index 0000000..1dbf95b
--- /dev/null
+++ b/src/util/child_common.c
@@ -0,0 +1,955 @@
+/*
+ SSSD
+
+ Common helper functions to be used in child processes
+
+ Authors:
+ Sumit Bose <sbose@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/>.
+*/
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <tevent.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <sys/prctl.h>
+
+#include "util/util.h"
+#include "util/find_uid.h"
+#include "db/sysdb.h"
+#include "util/child_common.h"
+
+struct sss_sigchild_ctx {
+ struct tevent_context *ev;
+ hash_table_t *children;
+ int options;
+};
+
+struct sss_child_ctx {
+ pid_t pid;
+ sss_child_fn_t cb;
+ void *pvt;
+ struct sss_sigchild_ctx *sigchld_ctx;
+};
+
+static errno_t child_debug_init(const char *logfile, int *debug_fd);
+
+static void sss_child_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data);
+
+errno_t sss_sigchld_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sss_sigchild_ctx **child_ctx)
+{
+ errno_t ret;
+ struct sss_sigchild_ctx *sigchld_ctx;
+ struct tevent_signal *tes;
+
+ sigchld_ctx = talloc_zero(mem_ctx, struct sss_sigchild_ctx);
+ if (!sigchld_ctx) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "fatal error initializing sss_sigchild_ctx\n");
+ return ENOMEM;
+ }
+ sigchld_ctx->ev = ev;
+
+ ret = sss_hash_create(sigchld_ctx, 0, &sigchld_ctx->children);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "fatal error initializing children hash table: [%s]\n",
+ strerror(ret));
+ talloc_free(sigchld_ctx);
+ return ret;
+ }
+
+ BlockSignals(false, SIGCHLD);
+ tes = tevent_add_signal(ev, sigchld_ctx, SIGCHLD, SA_SIGINFO,
+ sss_child_handler, sigchld_ctx);
+ if (tes == NULL) {
+ talloc_free(sigchld_ctx);
+ return EIO;
+ }
+
+ *child_ctx = sigchld_ctx;
+ return EOK;
+}
+
+static int sss_child_destructor(void *ptr)
+{
+ struct sss_child_ctx *child_ctx;
+ hash_key_t key;
+ int error;
+
+ child_ctx = talloc_get_type(ptr, struct sss_child_ctx);
+ key.type = HASH_KEY_ULONG;
+ key.ul = child_ctx->pid;
+
+ error = hash_delete(child_ctx->sigchld_ctx->children, &key);
+ if (error != HASH_SUCCESS && error != HASH_ERROR_KEY_NOT_FOUND) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "failed to delete child_ctx from hash table [%d]: %s\n",
+ error, hash_error_string(error));
+ }
+
+ return 0;
+}
+
+errno_t sss_child_register(TALLOC_CTX *mem_ctx,
+ struct sss_sigchild_ctx *sigchld_ctx,
+ pid_t pid,
+ sss_child_fn_t cb,
+ void *pvt,
+ struct sss_child_ctx **child_ctx)
+{
+ struct sss_child_ctx *child;
+ hash_key_t key;
+ hash_value_t value;
+ int error;
+
+ child = talloc_zero(mem_ctx, struct sss_child_ctx);
+ if (child == NULL) {
+ return ENOMEM;
+ }
+
+ child->pid = pid;
+ child->cb = cb;
+ child->pvt = pvt;
+ child->sigchld_ctx = sigchld_ctx;
+
+ key.type = HASH_KEY_ULONG;
+ key.ul = pid;
+
+ value.type = HASH_VALUE_PTR;
+ value.ptr = child;
+
+ error = hash_enter(sigchld_ctx->children, &key, &value);
+ if (error != HASH_SUCCESS) {
+ talloc_free(child);
+ return ENOMEM;
+ }
+
+ talloc_set_destructor((TALLOC_CTX *) child, sss_child_destructor);
+
+ *child_ctx = child;
+ return EOK;
+}
+
+struct sss_child_cb_pvt {
+ struct sss_child_ctx *child_ctx;
+ int wait_status;
+};
+
+static void sss_child_invoke_cb(struct tevent_context *ev,
+ struct tevent_immediate *imm,
+ void *pvt)
+{
+ struct sss_child_cb_pvt *cb_pvt;
+ struct sss_child_ctx *child_ctx;
+ hash_key_t key;
+ int error;
+
+ cb_pvt = talloc_get_type(pvt, struct sss_child_cb_pvt);
+ child_ctx = cb_pvt->child_ctx;
+
+ key.type = HASH_KEY_ULONG;
+ key.ul = child_ctx->pid;
+
+ error = hash_delete(child_ctx->sigchld_ctx->children, &key);
+ if (error != HASH_SUCCESS && error != HASH_ERROR_KEY_NOT_FOUND) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "failed to delete child_ctx from hash table [%d]: %s\n",
+ error, hash_error_string(error));
+ }
+
+ if (child_ctx->cb) {
+ child_ctx->cb(child_ctx->pid, cb_pvt->wait_status, child_ctx->pvt);
+ }
+
+ talloc_free(imm);
+}
+
+static void sss_child_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ struct sss_sigchild_ctx *sigchld_ctx;
+ struct tevent_immediate *imm;
+ struct sss_child_cb_pvt *invoke_pvt;
+ struct sss_child_ctx *child_ctx;
+ hash_key_t key;
+ hash_value_t value;
+ int error;
+ int wait_status;
+ pid_t pid;
+
+ sigchld_ctx = talloc_get_type(private_data, struct sss_sigchild_ctx);
+ key.type = HASH_KEY_ULONG;
+
+ do {
+ do {
+ errno = 0;
+ pid = waitpid(-1, &wait_status, WNOHANG | sigchld_ctx->options);
+ } while (pid == -1 && errno == EINTR);
+
+ if (pid == -1) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "waitpid failed [%d]: %s\n", errno, strerror(errno));
+ return;
+ } else if (pid == 0) continue;
+
+ key.ul = pid;
+ error = hash_lookup(sigchld_ctx->children, &key, &value);
+ if (error == HASH_SUCCESS) {
+ child_ctx = talloc_get_type(value.ptr, struct sss_child_ctx);
+
+ imm = tevent_create_immediate(child_ctx);
+ if (imm == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Out of memory invoking SIGCHLD callback\n");
+ return;
+ }
+
+ invoke_pvt = talloc_zero(child_ctx, struct sss_child_cb_pvt);
+ if (invoke_pvt == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "out of memory invoking SIGCHLD callback\n");
+ return;
+ }
+ invoke_pvt->child_ctx = child_ctx;
+ invoke_pvt->wait_status = wait_status;
+
+ tevent_schedule_immediate(imm, sigchld_ctx->ev,
+ sss_child_invoke_cb, invoke_pvt);
+ } else if (error == HASH_ERROR_KEY_NOT_FOUND) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "BUG: waitpid() returned [%d] but it was not in the table. "
+ "This could be due to a linked library creating processes "
+ "without registering them with the sigchld handler\n",
+ pid);
+ /* We will simply ignore this and return to the loop
+ * This will prevent a zombie, but may cause unexpected
+ * behavior in the code that was trying to handle this
+ * pid.
+ */
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "SIGCHLD hash table error [%d]: %s\n",
+ error, hash_error_string(error));
+ /* This is bad, but we should try to check for other
+ * children anyway, to avoid potential zombies.
+ */
+ }
+ } while (pid != 0);
+}
+
+struct sss_child_ctx_old {
+ struct tevent_signal *sige;
+ pid_t pid;
+ int child_status;
+ sss_child_callback_t cb;
+ void *pvt;
+};
+
+static void child_sig_handler(struct tevent_context *ev,
+ struct tevent_signal *sige, int signum,
+ int count, void *__siginfo, void *pvt);
+
+int child_handler_setup(struct tevent_context *ev, int pid,
+ sss_child_callback_t cb, void *pvt,
+ struct sss_child_ctx_old **_child_ctx)
+{
+ struct sss_child_ctx_old *child_ctx;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Setting up signal handler up for pid [%d]\n", pid);
+
+ child_ctx = talloc_zero(ev, struct sss_child_ctx_old);
+ if (child_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ child_ctx->sige = tevent_add_signal(ev, child_ctx, SIGCHLD, SA_SIGINFO,
+ child_sig_handler, child_ctx);
+ if(!child_ctx->sige) {
+ /* Error setting up signal handler */
+ talloc_free(child_ctx);
+ return ENOMEM;
+ }
+
+ child_ctx->pid = pid;
+ child_ctx->cb = cb;
+ child_ctx->pvt = pvt;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Signal handler set up for pid [%d]\n", pid);
+
+ if (_child_ctx != NULL) {
+ *_child_ctx = child_ctx;
+ }
+
+ return EOK;
+}
+
+void child_handler_destroy(struct sss_child_ctx_old *ctx)
+{
+ errno_t ret;
+
+ /* We still want to wait for the child to finish, but the caller is not
+ * interested in the result anymore (e.g. timeout was reached). */
+ ctx->cb = NULL;
+ ctx->pvt = NULL;
+
+ ret = kill(ctx->pid, SIGKILL);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE, "kill failed [%d][%s].\n", ret, strerror(ret));
+ }
+}
+
+/* Async communication with the child process via a pipe */
+
+struct _write_pipe_state {
+ int fd;
+ uint8_t *buf;
+ size_t len;
+ bool safe;
+ ssize_t written;
+};
+
+static void _write_pipe_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *pvt);
+
+static struct tevent_req *_write_pipe_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ uint8_t *buf,
+ size_t len,
+ bool safe,
+ int fd)
+{
+ struct tevent_req *req;
+ struct _write_pipe_state *state;
+ struct tevent_fd *fde;
+
+ req = tevent_req_create(mem_ctx, &state, struct _write_pipe_state);
+ if (req == NULL) return NULL;
+
+ state->fd = fd;
+ state->buf = buf;
+ state->len = len;
+ state->safe = safe;
+ state->written = 0;
+
+ fde = tevent_add_fd(ev, state, fd, TEVENT_FD_WRITE,
+ _write_pipe_handler, req);
+ if (fde == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_fd failed.\n");
+ goto fail;
+ }
+
+ return req;
+
+fail:
+ talloc_zfree(req);
+ return NULL;
+}
+
+static void _write_pipe_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct _write_pipe_state *state;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct _write_pipe_state);
+
+ if (flags & TEVENT_FD_READ) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "_write_pipe_done called with TEVENT_FD_READ,"
+ " this should not happen.\n");
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ errno = 0;
+ if (state->safe) {
+ state->written = sss_atomic_write_safe_s(state->fd, state->buf, state->len);
+ } else {
+ state->written = sss_atomic_write_s(state->fd, state->buf, state->len);
+ }
+ if (state->written == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "write failed [%d][%s].\n", ret, strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->len != state->written) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Wrote %zd bytes, expected %zu\n",
+ state->written, state->len);
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "All data has been sent!\n");
+ tevent_req_done(req);
+ return;
+}
+
+static int _write_pipe_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+struct tevent_req *write_pipe_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ uint8_t *buf,
+ size_t len,
+ int fd)
+{
+ return _write_pipe_send(mem_ctx, ev, buf, len, false, fd);
+}
+
+int write_pipe_recv(struct tevent_req *req)
+{
+ return _write_pipe_recv(req);
+}
+
+struct tevent_req *write_pipe_safe_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ uint8_t *buf,
+ size_t len,
+ int fd)
+{
+ return _write_pipe_send(mem_ctx, ev, buf, len, true, fd);
+}
+
+int write_pipe_safe_recv(struct tevent_req *req)
+{
+ return _write_pipe_recv(req);
+}
+
+struct _read_pipe_state {
+ int fd;
+ uint8_t *buf;
+ size_t len;
+ bool safe;
+};
+
+static void _read_pipe_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *pvt);
+
+static struct tevent_req *_read_pipe_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ bool safe,
+ int fd)
+{
+ struct tevent_req *req;
+ struct _read_pipe_state *state;
+ struct tevent_fd *fde;
+
+ req = tevent_req_create(mem_ctx, &state, struct _read_pipe_state);
+ if (req == NULL) return NULL;
+
+ state->fd = fd;
+ state->buf = NULL;
+ state->len = 0;
+ state->safe = safe;
+
+ fde = tevent_add_fd(ev, state, fd, TEVENT_FD_READ,
+ _read_pipe_handler, req);
+ if (fde == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_fd failed.\n");
+ goto fail;
+ }
+
+ return req;
+
+fail:
+ talloc_zfree(req);
+ return NULL;
+}
+
+static void _read_pipe_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct _read_pipe_state *state;
+ ssize_t size;
+ errno_t err;
+ uint8_t *buf;
+ size_t len = 0;
+
+ state = tevent_req_data(req, struct _read_pipe_state);
+
+ if (flags & TEVENT_FD_WRITE) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "_read_pipe_done called with "
+ "TEVENT_FD_WRITE, this should not happen.\n");
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ buf = talloc_array(state, uint8_t, CHILD_MSG_CHUNK);
+ if (buf == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ if (state->safe) {
+ size = sss_atomic_read_safe_s(state->fd, buf, CHILD_MSG_CHUNK, &len);
+ if (size == -1 && errno == ERANGE) {
+ buf = talloc_realloc(state, buf, uint8_t, len);
+ if(!buf) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ size = sss_atomic_read_s(state->fd, buf, len);
+ }
+ } else {
+ size = sss_atomic_read_s(state->fd, buf, CHILD_MSG_CHUNK);
+ }
+ if (size == -1) {
+ err = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "read failed [%d][%s].\n", err, strerror(err));
+ tevent_req_error(req, err);
+ return;
+ } else if (size > 0) {
+ state->buf = talloc_realloc(state, state->buf, uint8_t,
+ state->len + size);
+ if(!state->buf) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ safealign_memcpy(&state->buf[state->len], buf,
+ size, &state->len);
+
+ if (state->len == len) {
+ DEBUG(SSSDBG_TRACE_FUNC, "All data received\n");
+ tevent_req_done(req);
+ }
+ return;
+
+ } else if (size == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "EOF received, client finished\n");
+ tevent_req_done(req);
+ return;
+
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "unexpected return value of read [%zd].\n", size);
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+}
+
+static errno_t _read_pipe_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **buf,
+ ssize_t *len)
+{
+ struct _read_pipe_state *state;
+ state = tevent_req_data(req, struct _read_pipe_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *buf = talloc_steal(mem_ctx, state->buf);
+ *len = state->len;
+
+ return EOK;
+}
+
+struct tevent_req *read_pipe_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int fd)
+{
+ return _read_pipe_send(mem_ctx, ev, false, fd);
+}
+
+errno_t read_pipe_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **_buf,
+ ssize_t *_len)
+{
+ return _read_pipe_recv(req, mem_ctx, _buf, _len);
+}
+
+struct tevent_req *read_pipe_safe_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int fd)
+{
+ return _read_pipe_send(mem_ctx, ev, true, fd);
+}
+
+errno_t read_pipe_safe_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **_buf,
+ ssize_t *_len)
+{
+ return _read_pipe_recv(req, mem_ctx, _buf, _len);
+}
+
+static void child_invoke_callback(struct tevent_context *ev,
+ struct tevent_immediate *imm,
+ void *pvt);
+static void child_sig_handler(struct tevent_context *ev,
+ struct tevent_signal *sige, int signum,
+ int count, void *__siginfo, void *pvt)
+{
+ int ret, err;
+ struct sss_child_ctx_old *child_ctx;
+ struct tevent_immediate *imm;
+
+ if (count <= 0) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "SIGCHLD handler called with invalid child count\n");
+ return;
+ }
+
+ child_ctx = talloc_get_type(pvt, struct sss_child_ctx_old);
+ DEBUG(SSSDBG_TRACE_LIBS, "Waiting for child [%d].\n", child_ctx->pid);
+
+ errno = 0;
+ ret = waitpid(child_ctx->pid, &child_ctx->child_status, WNOHANG);
+
+ if (ret == -1) {
+ err = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "waitpid failed [%d][%s].\n", err, strerror(err));
+ } else if (ret == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "waitpid did not find a child with changed status.\n");
+ } else {
+ if (WIFEXITED(child_ctx->child_status)) {
+ if (WEXITSTATUS(child_ctx->child_status) != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "child [%d] failed with status [%d].\n", ret,
+ WEXITSTATUS(child_ctx->child_status));
+ } else {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "child [%d] finished successfully.\n", ret);
+ }
+ } else if (WIFSIGNALED(child_ctx->child_status)) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "child [%d] was terminated by signal [%d].\n", ret,
+ WTERMSIG(child_ctx->child_status));
+ } else {
+ if (WIFSTOPPED(child_ctx->child_status)) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "child [%d] was stopped by signal [%d].\n", ret,
+ WSTOPSIG(child_ctx->child_status));
+ }
+ if (WIFCONTINUED(child_ctx->child_status) == true) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "child [%d] was resumed by delivery of SIGCONT.\n",
+ ret);
+ }
+
+ return;
+ }
+
+ /* Invoke the callback in a tevent_immediate handler
+ * so that it is safe to free the tevent_signal *
+ */
+ imm = tevent_create_immediate(child_ctx);
+ if (imm == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Out of memory invoking sig handler callback\n");
+ return;
+ }
+
+ tevent_schedule_immediate(imm, ev, child_invoke_callback,
+ child_ctx);
+ }
+
+ return;
+}
+
+static void child_invoke_callback(struct tevent_context *ev,
+ struct tevent_immediate *imm,
+ void *pvt)
+{
+ struct sss_child_ctx_old *child_ctx =
+ talloc_get_type(pvt, struct sss_child_ctx_old);
+ if (child_ctx->cb) {
+ child_ctx->cb(child_ctx->child_status, child_ctx->sige, child_ctx->pvt);
+ }
+
+ /* Stop monitoring for this child */
+ talloc_free(child_ctx);
+}
+
+static errno_t prepare_child_argv(TALLOC_CTX *mem_ctx,
+ int child_debug_fd,
+ const char *binary,
+ const char *extra_argv[],
+ bool extra_args_only,
+ char ***_argv)
+{
+ /*
+ * program name, debug_level, debug_timestamps,
+ * debug_microseconds, PR_SET_DUMPABLE and NULL
+ */
+ uint_t argc = 6;
+ char ** argv = NULL;
+ errno_t ret = EINVAL;
+ size_t i;
+
+ if (extra_args_only) {
+ argc = 2; /* program name and NULL */
+ }
+
+ /* Save the current state in case an interrupt changes it */
+ bool child_debug_timestamps = debug_timestamps;
+ bool child_debug_microseconds = debug_microseconds;
+
+ if (!extra_args_only) {
+ argc++;
+ }
+
+ if (extra_argv) {
+ for (i = 0; extra_argv[i]; i++) argc++;
+ }
+
+ /*
+ * program name, debug_level, debug_timestamps,
+ * debug_microseconds and NULL
+ */
+ argv = talloc_array(mem_ctx, char *, argc);
+ if (argv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_array failed.\n");
+ return ENOMEM;
+ }
+
+ argv[--argc] = NULL;
+
+ /* Add extra_attrs first */
+ if (extra_argv) {
+ for (i = 0; extra_argv[i]; i++) {
+ argv[--argc] = talloc_strdup(argv, extra_argv[i]);
+ if (argv[argc] == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ }
+ }
+
+ if (!extra_args_only) {
+ argv[--argc] = talloc_asprintf(argv, "--debug-level=%#.4x",
+ debug_level);
+ if (argv[argc] == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ if (sss_logger == FILES_LOGGER) {
+ argv[--argc] = talloc_asprintf(argv, "--debug-fd=%d",
+ child_debug_fd);
+ if (argv[argc] == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ } else {
+ argv[--argc] = talloc_asprintf(argv, "--logger=%s",
+ sss_logger_str[sss_logger]);
+ if (argv[argc] == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ }
+
+ argv[--argc] = talloc_asprintf(argv, "--debug-timestamps=%d",
+ child_debug_timestamps);
+ if (argv[argc] == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ argv[--argc] = talloc_asprintf(argv, "--debug-microseconds=%d",
+ child_debug_microseconds);
+ if (argv[argc] == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ argv[--argc] = talloc_asprintf(argv, "--dumpable=%d",
+ prctl(PR_GET_DUMPABLE));
+ if (argv[argc] == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ }
+
+ argv[--argc] = talloc_strdup(argv, binary);
+ if (argv[argc] == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ if (argc != 0) {
+ ret = EINVAL;
+ goto fail;
+ }
+
+ *_argv = argv;
+
+ return EOK;
+
+fail:
+ talloc_free(argv);
+ return ret;
+}
+
+void exec_child_ex(TALLOC_CTX *mem_ctx,
+ int *pipefd_to_child, int *pipefd_from_child,
+ const char *binary, const char *logfile,
+ const char *extra_argv[], bool extra_args_only,
+ int child_in_fd, int child_out_fd)
+{
+ int ret;
+ errno_t err;
+ char **argv;
+ int debug_fd = -1;
+
+ if (logfile) {
+ ret = child_debug_init(logfile, &debug_fd);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "child_debug_init() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ debug_fd = STDERR_FILENO;
+ }
+
+ close(pipefd_to_child[1]);
+ ret = dup2(pipefd_to_child[0], child_in_fd);
+ if (ret == -1) {
+ err = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "dup2 failed [%d][%s].\n", err, strerror(err));
+ exit(EXIT_FAILURE);
+ }
+
+ close(pipefd_from_child[0]);
+ ret = dup2(pipefd_from_child[1], child_out_fd);
+ if (ret == -1) {
+ err = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "dup2 failed [%d][%s].\n", err, strerror(err));
+ exit(EXIT_FAILURE);
+ }
+
+ ret = prepare_child_argv(mem_ctx, debug_fd,
+ binary, extra_argv, extra_args_only,
+ &argv);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "prepare_child_argv() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ execv(binary, argv);
+ err = errno;
+ DEBUG(SSSDBG_OP_FAILURE, "execv failed [%d][%s].\n", err, strerror(err));
+ exit(EXIT_FAILURE);
+}
+
+void exec_child(TALLOC_CTX *mem_ctx,
+ int *pipefd_to_child, int *pipefd_from_child,
+ const char *binary, const char *logfile)
+{
+ exec_child_ex(mem_ctx, pipefd_to_child, pipefd_from_child,
+ binary, logfile, NULL, false,
+ STDIN_FILENO, STDOUT_FILENO);
+}
+
+int child_io_destructor(void *ptr)
+{
+ int ret;
+ struct child_io_fds *io = talloc_get_type(ptr, struct child_io_fds);
+ if (io == NULL) return EOK;
+
+ if (io->write_to_child_fd != -1) {
+ ret = close(io->write_to_child_fd);
+ io->write_to_child_fd = -1;
+ if (ret != EOK) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "close failed [%d][%s].\n", ret, strerror(ret));
+ }
+ }
+
+ if (io->read_from_child_fd != -1) {
+ ret = close(io->read_from_child_fd);
+ io->read_from_child_fd = -1;
+ if (ret != EOK) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "close failed [%d][%s].\n", ret, strerror(ret));
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t child_debug_init(const char *logfile, int *debug_fd)
+{
+ int ret;
+ FILE *debug_filep;
+
+ if (debug_fd == NULL) {
+ return EOK;
+ }
+
+ if (sss_logger == FILES_LOGGER && *debug_fd == -1) {
+ ret = open_debug_file_ex(logfile, &debug_filep, false);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error setting up logging (%d) [%s]\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ *debug_fd = fileno(debug_filep);
+ if (*debug_fd == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "fileno failed [%d][%s]\n", ret, strerror(ret));
+ return ret;
+ }
+ }
+
+ return EOK;
+}
diff --git a/src/util/child_common.h b/src/util/child_common.h
new file mode 100644
index 0000000..aa13e17
--- /dev/null
+++ b/src/util/child_common.h
@@ -0,0 +1,145 @@
+/*
+ SSSD
+
+ Common helper functions to be used in child processes
+
+ Authors:
+ Sumit Bose <sbose@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 __CHILD_COMMON_H__
+#define __CHILD_COMMON_H__
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <tevent.h>
+
+#include "util/util.h"
+
+#define IN_BUF_SIZE 2048
+#define CHILD_MSG_CHUNK 1024
+
+#define SIGTERM_TO_SIGKILL_TIME 2
+
+struct response {
+ uint8_t *buf;
+ size_t size;
+};
+
+struct io_buffer {
+ uint8_t *data;
+ size_t size;
+};
+
+struct child_io_fds {
+ int read_from_child_fd;
+ int write_to_child_fd;
+ pid_t pid;
+ bool child_exited;
+ bool in_use;
+};
+
+/* COMMON SIGCHLD HANDLING */
+typedef void (*sss_child_fn_t)(int pid, int wait_status, void *pvt);
+
+struct sss_sigchild_ctx;
+struct sss_child_ctx;
+
+/* Create a new child context to manage callbacks */
+errno_t sss_sigchld_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sss_sigchild_ctx **child_ctx);
+
+errno_t sss_child_register(TALLOC_CTX *mem_ctx,
+ struct sss_sigchild_ctx *sigchld_ctx,
+ pid_t pid,
+ sss_child_fn_t cb,
+ void *pvt,
+ struct sss_child_ctx **child_ctx);
+
+/* Callback to be invoked when a sigchld handler is called.
+ * The tevent_signal * associated with the handler will be
+ * freed automatically when this function returns.
+ */
+typedef void (*sss_child_callback_t)(int child_status,
+ struct tevent_signal *sige,
+ void *pvt);
+
+struct sss_child_ctx_old;
+
+/* Set up child termination signal handler */
+int child_handler_setup(struct tevent_context *ev, int pid,
+ sss_child_callback_t cb, void *pvt,
+ struct sss_child_ctx_old **_child_ctx);
+
+/* Destroy child termination signal handler */
+void child_handler_destroy(struct sss_child_ctx_old *ctx);
+
+/* Async communication with the child process via a pipe */
+struct tevent_req *write_pipe_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ uint8_t *buf,
+ size_t len,
+ int fd);
+int write_pipe_recv(struct tevent_req *req);
+
+struct tevent_req *read_pipe_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int fd);
+errno_t read_pipe_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **_buf,
+ ssize_t *_len);
+
+/* Include buffer length in a message header, read does not wait for EOF. */
+struct tevent_req *write_pipe_safe_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ uint8_t *buf,
+ size_t len,
+ int fd);
+int write_pipe_safe_recv(struct tevent_req *req);
+
+struct tevent_req *read_pipe_safe_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int fd);
+errno_t read_pipe_safe_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **_buf,
+ ssize_t *_len);
+
+/* The pipes to communicate with the child must be nonblocking */
+void fd_nonblocking(int fd);
+
+/* Never returns EOK, ether returns an error, or doesn't return on success */
+void exec_child_ex(TALLOC_CTX *mem_ctx,
+ int *pipefd_to_child, int *pipefd_from_child,
+ const char *binary, const char *logfile,
+ const char *extra_argv[], bool extra_args_only,
+ int child_in_fd, int child_out_fd);
+
+/* Same as exec_child_ex() except child_in_fd is set to STDIN_FILENO and
+ * child_out_fd is set to STDOUT_FILENO and extra_argv is always NULL.
+ */
+void exec_child(TALLOC_CTX *mem_ctx,
+ int *pipefd_to_child, int *pipefd_from_child,
+ const char *binary, const char *logfile);
+
+int child_io_destructor(void *ptr);
+
+#endif /* __CHILD_COMMON_H__ */
diff --git a/src/util/crypto/libcrypto/crypto_base64.c b/src/util/crypto/libcrypto/crypto_base64.c
new file mode 100644
index 0000000..11a0648
--- /dev/null
+++ b/src/util/crypto/libcrypto/crypto_base64.c
@@ -0,0 +1,133 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@redhat.com>
+ George McCollister <george.mccollister@gmail.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 "util/util.h"
+#include "util/crypto/sss_crypto.h"
+
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+
+char *sss_base64_encode(TALLOC_CTX *mem_ctx,
+ const unsigned char *in,
+ size_t insize)
+{
+ char *b64encoded = NULL, *outbuf = NULL;
+ int i, j, b64size;
+ BIO *bmem, *b64;
+
+ b64 = BIO_new(BIO_f_base64());
+ if (!b64) return NULL;
+
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ bmem = BIO_new(BIO_s_mem());
+ if (!bmem) goto done;
+
+ b64 = BIO_push(b64, bmem);
+
+ BIO_write(b64, in, insize);
+
+ (void) BIO_flush(b64);
+
+ b64size = BIO_get_mem_data(bmem, &b64encoded);
+ if (b64encoded) {
+ outbuf = talloc_array(mem_ctx, char, b64size+1);
+ if (outbuf == NULL) goto done;
+
+ for (i=0, j=0; i < b64size; i++) {
+ if (b64encoded[i] == '\n' || b64encoded[i] == '\r') {
+ continue;
+ }
+ outbuf[j++] = b64encoded[i];
+ }
+ outbuf[j++] = '\0';
+ }
+
+done:
+ BIO_free_all(b64);
+ return outbuf;
+}
+
+unsigned char *sss_base64_decode(TALLOC_CTX *mem_ctx,
+ const char *in,
+ size_t *outsize)
+{
+ unsigned char *outbuf = NULL;
+ unsigned char *b64decoded = NULL;
+ unsigned char inbuf[512];
+ char * in_dup;
+ int size, inlen = strlen(in);
+ BIO *bmem, *b64, *bmem_out;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ in_dup = talloc_size(tmp_ctx, inlen+1);
+ if (!in_dup) goto done;
+ memcpy(in_dup, in, inlen+1);
+
+ b64 = BIO_new(BIO_f_base64());
+ if (!b64) goto done;
+
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+
+ bmem = BIO_new_mem_buf(in_dup, -1);
+ if (!bmem) {
+ BIO_free(b64);
+ goto done;
+ }
+
+ b64 = BIO_push(b64, bmem);
+
+ bmem_out = BIO_new(BIO_s_mem());
+ if (!bmem_out) {
+ BIO_free_all(b64);
+ goto done;
+ }
+
+ while((inlen = BIO_read(b64, inbuf, 512)) > 0)
+ BIO_write(bmem_out, inbuf, inlen);
+
+ (void) BIO_flush(bmem_out);
+
+ size = BIO_get_mem_data(bmem_out, &b64decoded);
+
+ if (b64decoded) {
+ outbuf = talloc_memdup(mem_ctx, b64decoded, size);
+ if (!outbuf) {
+ BIO_free_all(b64);
+ BIO_free(bmem_out);
+ goto done;
+ }
+
+ *outsize = size;
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot get decoded data\n");
+ }
+ BIO_free_all(b64);
+ BIO_free(bmem_out);
+
+done:
+ talloc_free(tmp_ctx);
+ return outbuf;
+}
diff --git a/src/util/crypto/libcrypto/crypto_hmac_sha1.c b/src/util/crypto/libcrypto/crypto_hmac_sha1.c
new file mode 100644
index 0000000..9b072ad
--- /dev/null
+++ b/src/util/crypto/libcrypto/crypto_hmac_sha1.c
@@ -0,0 +1,49 @@
+/*
+ Copyright (C) 2019 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 <openssl/hmac.h>
+
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+
+
+int sss_hmac_sha1(const unsigned char *key, size_t key_len,
+ const unsigned char *in, size_t in_len,
+ unsigned char *out)
+{
+ unsigned int res_len = 0;
+ unsigned char md[EVP_MAX_MD_SIZE];
+
+ if ((key == NULL) || (key_len == 0) || (key_len > INT_MAX)
+ || (in == NULL) || (in_len == 0) || (in_len > INT_MAX)
+ || (out == NULL)) {
+ return EINVAL;
+ }
+
+ if (!HMAC(EVP_sha1(), key, (int)key_len, in, (int)in_len, md, &res_len)) {
+ return EINVAL;
+ }
+
+ if (res_len != SSS_SHA1_LENGTH) {
+ return EINVAL;
+ }
+
+ memcpy(out, md, SSS_SHA1_LENGTH);
+
+ return EOK;
+}
diff --git a/src/util/crypto/libcrypto/crypto_obfuscate.c b/src/util/crypto/libcrypto/crypto_obfuscate.c
new file mode 100644
index 0000000..2cef61b
--- /dev/null
+++ b/src/util/crypto/libcrypto/crypto_obfuscate.c
@@ -0,0 +1,313 @@
+/*
+ SSSD
+
+ Password obfuscation logic
+
+ Authors:
+ George McCollister <george.mccollister@gmail.com>
+
+ Copyright (C) George McCollister 2012
+
+ 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/>.
+*/
+
+/*
+ * READ ME:
+ *
+ * Please note that password obfuscation does not improve security in any
+ * way. It is just a mechanism to make the password human-unreadable.
+ */
+
+#include "config.h"
+#include <talloc.h>
+#include <errno.h>
+
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+#define OBF_BUFFER_SENTINEL "\0\1\2\3"
+#define OBF_BUFFER_SENTINEL_SIZE 4
+
+struct crypto_mech_data {
+ const EVP_CIPHER * (*cipher)(void);
+ uint16_t keylen;
+ uint16_t bsize;
+};
+
+static struct crypto_mech_data cmdata[] = {
+ /* AES with automatic padding, 256b key, 128b block */
+ { EVP_aes_256_cbc, 32, 16 },
+ /* sentinel */
+ { 0, 0, 0 }
+};
+
+static struct crypto_mech_data *get_crypto_mech_data(enum obfmethod meth)
+{
+ if (meth >= NUM_OBFMETHODS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported cipher type\n");
+ return NULL;
+ }
+ return &cmdata[meth];
+}
+
+int sss_password_encrypt(TALLOC_CTX *mem_ctx, const char *password, int plen,
+ enum obfmethod meth, char **obfpwd)
+{
+ int ret;
+ EVP_CIPHER_CTX *ctx;
+ struct crypto_mech_data *mech_props;
+ TALLOC_CTX *tmp_ctx = NULL;
+ unsigned char *keybuf;
+ unsigned char *ivbuf;
+ unsigned char *cryptotext;
+ int ct_maxsize;
+ int ctlen = 0;
+ int digestlen = 0;
+ int result_len;
+
+ unsigned char *obfbuf;
+ size_t obufsize = 0;
+ size_t p = 0;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ mech_props = get_crypto_mech_data(meth);
+ if (mech_props == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ keybuf = talloc_array(tmp_ctx, unsigned char, mech_props->keylen);
+ if (keybuf == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ivbuf = talloc_array(tmp_ctx, unsigned char, mech_props->bsize);
+ if (ivbuf == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_generate_csprng_buffer((uint8_t *)keybuf, mech_props->keylen);
+ if (ret != EOK) {
+ goto done;
+ }
+ ret = sss_generate_csprng_buffer((uint8_t *)ivbuf, mech_props->bsize);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* cryptotext buffer must be at least len(plaintext)+blocksize */
+ ct_maxsize = plen + (mech_props->bsize);
+ cryptotext = talloc_array(tmp_ctx, unsigned char, ct_maxsize);
+ if (!cryptotext) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (!EVP_EncryptInit_ex(ctx, mech_props->cipher(), 0, keybuf, ivbuf)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failure to initialize cipher contex\n");
+ ret = EIO;
+ goto done;
+ }
+
+ /* sample data we'll encrypt and decrypt */
+ if (!EVP_EncryptUpdate(ctx, cryptotext, &ctlen, (const unsigned char *)password, plen)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot execute the encryption operation\n");
+ ret = EIO;
+ goto done;
+ }
+
+ if (!EVP_EncryptFinal_ex(ctx, cryptotext + ctlen, &digestlen)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot finialize the encryption operation\n");
+ ret = EIO;
+ goto done;
+ }
+
+ result_len = ctlen + digestlen;
+ if (result_len < 0 || result_len > UINT16_MAX) {
+ ret = ERANGE;
+ goto done;
+ }
+
+ /* Pack the obfuscation buffer */
+ /* The buffer consists of:
+ * uint16_t the type of the cipher
+ * uint16_t length of the cryptotext in bytes (clen)
+ * uint8_t[klen] key
+ * uint8_t[blen] IV
+ * uint8_t[clen] cryptotext
+ * 4 bytes of "sentinel" denoting end of the buffer
+ */
+ obufsize = sizeof(uint16_t) + sizeof(uint16_t) +
+ mech_props->keylen + mech_props->bsize +
+ result_len + OBF_BUFFER_SENTINEL_SIZE;
+ obfbuf = talloc_array(tmp_ctx, unsigned char, obufsize);
+ if (!obfbuf) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Writing method: %d\n", meth);
+ SAFEALIGN_SET_UINT16(&obfbuf[p], meth, &p);
+ DEBUG(SSSDBG_TRACE_FUNC, "Writing bufsize: %d\n", result_len);
+ SAFEALIGN_SET_UINT16(&obfbuf[p], result_len, &p);
+ safealign_memcpy(&obfbuf[p], keybuf, mech_props->keylen, &p);
+ safealign_memcpy(&obfbuf[p], ivbuf, mech_props->bsize, &p);
+ safealign_memcpy(&obfbuf[p], cryptotext, result_len, &p);
+ safealign_memcpy(&obfbuf[p], OBF_BUFFER_SENTINEL,
+ OBF_BUFFER_SENTINEL_SIZE, &p);
+
+ /* Base64 encode the resulting buffer */
+ *obfpwd = sss_base64_encode(mem_ctx, obfbuf, obufsize);
+ if (*obfpwd == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ EVP_CIPHER_CTX_free(ctx);
+ return ret;
+}
+
+int sss_password_decrypt(TALLOC_CTX *mem_ctx, char *b64encoded,
+ char **password)
+{
+ int ret;
+ EVP_CIPHER_CTX *ctx;
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct crypto_mech_data *mech_props;
+
+ int plainlen;
+ int digestlen;
+ unsigned char *obfbuf = NULL;
+ size_t obflen;
+ char *pwdbuf;
+
+ /* for unmarshaling data */
+ uint16_t meth;
+ uint16_t ctsize;
+ size_t p = 0;
+ unsigned char *cryptotext;
+ unsigned char *keybuf;
+ unsigned char *ivbuf;
+ unsigned char sentinel_check[OBF_BUFFER_SENTINEL_SIZE];
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Base64 decode the incoming buffer */
+ obfbuf = sss_base64_decode(tmp_ctx, b64encoded, &obflen);
+ if (!obfbuf) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* unpack obfuscation buffer */
+ SAFEALIGN_COPY_UINT16_CHECK(&meth, obfbuf+p, obflen, &p);
+ DEBUG(SSSDBG_TRACE_FUNC, "Read method: %d\n", meth);
+ SAFEALIGN_COPY_UINT16_CHECK(&ctsize, obfbuf+p, obflen, &p);
+ DEBUG(SSSDBG_TRACE_FUNC, "Read bufsize: %d\n", ctsize);
+
+ mech_props = get_crypto_mech_data(meth);
+ if (mech_props == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* check that we got sane mechanism properties and cryptotext size */
+ memcpy(sentinel_check,
+ obfbuf + p + mech_props->keylen + mech_props->bsize + ctsize,
+ OBF_BUFFER_SENTINEL_SIZE);
+ if (memcmp(sentinel_check, OBF_BUFFER_SENTINEL, OBF_BUFFER_SENTINEL_SIZE) != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Obfuscation buffer seems corrupt, aborting\n");
+ ret = EFAULT;
+ goto done;
+ }
+
+ /* copy out key, ivbuf and cryptotext */
+ keybuf = talloc_array(tmp_ctx, unsigned char, mech_props->keylen);
+ if (keybuf == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ safealign_memcpy(keybuf, obfbuf+p, mech_props->keylen, &p);
+
+ ivbuf = talloc_array(tmp_ctx, unsigned char, mech_props->bsize);
+ if (ivbuf == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ safealign_memcpy(ivbuf, obfbuf+p, mech_props->bsize, &p);
+
+ cryptotext = talloc_array(tmp_ctx, unsigned char, ctsize);
+ if (cryptotext == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ safealign_memcpy(cryptotext, obfbuf+p, ctsize, &p);
+
+ pwdbuf = talloc_array(tmp_ctx, char, ctsize);
+ if (!pwdbuf) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (!EVP_DecryptInit_ex(ctx, mech_props->cipher(), 0, keybuf, ivbuf)) {
+ ret = EIO;
+ goto done;
+ }
+
+ /* sample data we'll encrypt and decrypt */
+ if (!EVP_DecryptUpdate(ctx, (unsigned char *)pwdbuf, &plainlen, cryptotext, ctsize)) {
+ ret = EIO;
+ goto done;
+ }
+
+ if (!EVP_DecryptFinal_ex(ctx, (unsigned char *)pwdbuf + plainlen, &digestlen)) {
+ ret = EIO;
+ goto done;
+ }
+
+ *password = talloc_move(mem_ctx, &pwdbuf);
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ EVP_CIPHER_CTX_free(ctx);
+ return ret;
+}
diff --git a/src/util/crypto/libcrypto/crypto_prng.c b/src/util/crypto/libcrypto/crypto_prng.c
new file mode 100644
index 0000000..32e5b60
--- /dev/null
+++ b/src/util/crypto/libcrypto/crypto_prng.c
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) Red Hat 2019
+
+ 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 <limits.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <openssl/rand.h>
+
+#include "util/util_errors.h"
+#include "util/crypto/sss_crypto.h"
+
+int sss_generate_csprng_buffer(uint8_t *buf, size_t size)
+{
+ if ((buf == NULL) || (size > INT_MAX)) {
+ return EINVAL;
+ }
+
+ if (RAND_bytes((unsigned char *)buf, (int)size) == 1) {
+ return EOK;
+ }
+
+ return EAGAIN;
+}
diff --git a/src/util/crypto/libcrypto/crypto_sha512crypt.c b/src/util/crypto/libcrypto/crypto_sha512crypt.c
new file mode 100644
index 0000000..c816d26
--- /dev/null
+++ b/src/util/crypto/libcrypto/crypto_sha512crypt.c
@@ -0,0 +1,382 @@
+/* This file is based on nss_sha512crypt.c which is based on the work of
+ * Ulrich Drepper (http://people.redhat.com/drepper/SHA-crypt.txt).
+ *
+ * libcrypto is used to provide SHA512 and random number generation.
+ * (http://www.openssl.org/docs/crypto/crypto.html).
+ *
+ * Sumit Bose <sbose@redhat.com>
+ * George McCollister <georgem@novatech-llc.com>
+ */
+/* SHA512-based UNIX crypt implementation.
+ Released into the Public Domain by Ulrich Drepper <drepper@redhat.com>. */
+
+#include "config.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include "util/util.h"
+#include "util/sss_endian.h"
+#include "util/crypto/sss_crypto.h"
+
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+#include "sss_openssl.h"
+
+
+/* Define our magic string to mark salt for SHA512 "encryption" replacement. */
+const char sha512_salt_prefix[] = "$6$";
+#define SALT_PREF_SIZE (sizeof(sha512_salt_prefix) - 1)
+
+/* Prefix for optional rounds specification. */
+const char sha512_rounds_prefix[] = "rounds=";
+#define ROUNDS_SIZE (sizeof(sha512_rounds_prefix) - 1)
+
+#define SALT_LEN_MAX 16
+#define ROUNDS_DEFAULT 5000
+#define ROUNDS_MIN 1000
+#define ROUNDS_MAX 999999999
+
+/* Table with characters for base64 transformation. */
+const char b64t[64] =
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+/* base64 conversion function */
+static inline void b64_from_24bit(char **dest, size_t *len, size_t n,
+ uint8_t b2, uint8_t b1, uint8_t b0)
+{
+ uint32_t w;
+ size_t i;
+
+ if (*len < n) n = *len;
+
+ w = (b2 << 16) | (b1 << 8) | b0;
+ for (i = 0; i < n; i++) {
+ (*dest)[i] = b64t[w & 0x3f];
+ w >>= 6;
+ }
+
+ *len -= i;
+ *dest += i;
+}
+
+static int sha512_crypt_r(const char *key,
+ const char *salt,
+ char *buffer, size_t buflen)
+{
+ unsigned char temp_result[64];
+ unsigned char alt_result[64];
+ size_t rounds = ROUNDS_DEFAULT;
+ bool rounds_custom = false;
+ EVP_MD_CTX *alt_ctx = NULL;
+ EVP_MD_CTX *ctx;
+ size_t salt_len;
+ size_t key_len;
+ size_t cnt;
+ char *p_bytes = NULL;
+ char *s_bytes = NULL;
+ int p1, p2, p3, pt, n;
+ unsigned int part;
+ char *cp;
+ int ret;
+
+ /* Find beginning of salt string. The prefix should normally always be
+ * present. Just in case it is not. */
+ if (strncmp(salt, sha512_salt_prefix, SALT_PREF_SIZE) == 0) {
+ /* Skip salt prefix. */
+ salt += SALT_PREF_SIZE;
+ }
+
+ if (strncmp(salt, sha512_rounds_prefix, ROUNDS_SIZE) == 0) {
+ unsigned long int srounds;
+ const char *num;
+ char *endp;
+
+ num = salt + ROUNDS_SIZE;
+ errno = 0;
+ srounds = strtoul(num, &endp, 10);
+ if (!errno && (*endp == '$')) {
+ salt = endp + 1;
+ if (srounds < ROUNDS_MIN) srounds = ROUNDS_MIN;
+ if (srounds > ROUNDS_MAX) srounds = ROUNDS_MAX;
+ rounds = srounds;
+ rounds_custom = true;
+ }
+ }
+
+ salt_len = MIN(strcspn(salt, "$"), SALT_LEN_MAX);
+ key_len = strlen(key);
+
+ ctx = EVP_MD_CTX_new();
+ if (ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ alt_ctx = EVP_MD_CTX_new();
+ if (alt_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Prepare for the real work. */
+ if (!EVP_DigestInit_ex(ctx, EVP_sha512(), NULL)) {
+ ret = EIO;
+ goto done;
+ }
+
+ /* Add the key string. */
+ EVP_DigestUpdate(ctx, (const unsigned char *)key, key_len);
+
+ /* The last part is the salt string. This must be at most 16
+ * characters and it ends at the first `$' character (for
+ * compatibility with existing implementations). */
+ EVP_DigestUpdate(ctx, (const unsigned char *)salt, salt_len);
+
+ /* Compute alternate SHA512 sum with input KEY, SALT, and KEY.
+ * The final result will be added to the first context. */
+ if (!EVP_DigestInit_ex(alt_ctx, EVP_sha512(), NULL)) {
+ ret = EIO;
+ goto done;
+ }
+
+ /* Add key. */
+ EVP_DigestUpdate(alt_ctx, (const unsigned char *)key, key_len);
+
+ /* Add salt. */
+ EVP_DigestUpdate(alt_ctx, (const unsigned char *)salt, salt_len);
+
+ /* Add key again. */
+ EVP_DigestUpdate(alt_ctx, (const unsigned char *)key, key_len);
+
+ /* Now get result of this (64 bytes) and add it to the other context. */
+ EVP_DigestFinal_ex(alt_ctx, alt_result, &part);
+
+ /* Add for any character in the key one byte of the alternate sum. */
+ for (cnt = key_len; cnt > 64; cnt -= 64) {
+ EVP_DigestUpdate(ctx, alt_result, 64);
+ }
+ EVP_DigestUpdate(ctx, alt_result, cnt);
+
+ /* Take the binary representation of the length of the key and for every
+ * 1 add the alternate sum, for every 0 the key. */
+ for (cnt = key_len; cnt > 0; cnt >>= 1) {
+ if ((cnt & 1) != 0) {
+ EVP_DigestUpdate(ctx, alt_result, 64);
+ } else {
+ EVP_DigestUpdate(ctx, (const unsigned char *)key, key_len);
+ }
+ }
+
+ /* Create intermediate result. */
+ EVP_DigestFinal_ex(ctx, alt_result, &part);
+
+ /* Start computation of P byte sequence. */
+ if (!EVP_DigestInit_ex(alt_ctx, EVP_sha512(), NULL)) {
+ ret = EIO;
+ goto done;
+ }
+
+ /* For every character in the password add the entire password. */
+ for (cnt = 0; cnt < key_len; cnt++) {
+ EVP_DigestUpdate(alt_ctx, (const unsigned char *)key, key_len);
+ }
+
+ /* Finish the digest. */
+ EVP_DigestFinal_ex(alt_ctx, temp_result, &part);
+
+ /* Create byte sequence P. */
+ cp = p_bytes = alloca(key_len);
+ for (cnt = key_len; cnt >= 64; cnt -= 64) {
+ cp = mempcpy(cp, temp_result, 64);
+ }
+ memcpy(cp, temp_result, cnt);
+
+ /* Start computation of S byte sequence. */
+ if (!EVP_DigestInit_ex(alt_ctx, EVP_sha512(), NULL)) {
+ ret = EIO;
+ goto done;
+ }
+
+ for (cnt = 0; cnt < 16 + alt_result[0]; cnt++) {
+ EVP_DigestUpdate(alt_ctx, (const unsigned char *)salt, salt_len);
+ }
+
+ /* Finish the digest. */
+ EVP_DigestFinal_ex(alt_ctx, temp_result, &part);
+
+ /* Create byte sequence S. */
+ cp = s_bytes = alloca(salt_len);
+ for (cnt = salt_len; cnt >= 64; cnt -= 64) {
+ cp = mempcpy(cp, temp_result, 64);
+ }
+ memcpy(cp, temp_result, cnt);
+
+ /* Repeatedly run the collected hash value through SHA512 to burn CPU cycles. */
+ for (cnt = 0; cnt < rounds; cnt++) {
+
+ if (!EVP_DigestInit_ex(ctx, EVP_sha512(), NULL)) {
+ ret = EIO;
+ goto done;
+ }
+
+ /* Add key or last result. */
+ if ((cnt & 1) != 0) {
+ EVP_DigestUpdate(ctx, (const unsigned char *)p_bytes, key_len);
+ } else {
+ EVP_DigestUpdate(ctx, alt_result, 64);
+ }
+
+ /* Add salt for numbers not divisible by 3. */
+ if (cnt % 3 != 0) {
+ EVP_DigestUpdate(ctx, (const unsigned char *)s_bytes, salt_len);
+ }
+
+ /* Add key for numbers not divisible by 7. */
+ if (cnt % 7 != 0) {
+ EVP_DigestUpdate(ctx, (const unsigned char *)p_bytes, key_len);
+ }
+
+ /* Add key or last result. */
+ if ((cnt & 1) != 0) {
+ EVP_DigestUpdate(ctx, alt_result, 64);
+ } else {
+ EVP_DigestUpdate(ctx, (const unsigned char *)p_bytes, key_len);
+ }
+
+ /* Create intermediate result. */
+ EVP_DigestFinal_ex(ctx, alt_result, &part);
+ }
+
+ /* Now we can construct the result string.
+ * It consists of three parts. */
+ if (buflen <= SALT_PREF_SIZE) {
+ ret = ERANGE;
+ goto done;
+ }
+
+ cp = memcpy(buffer, sha512_salt_prefix, SALT_PREF_SIZE);
+ cp += SALT_PREF_SIZE;
+ buflen -= SALT_PREF_SIZE;
+
+ if (rounds_custom) {
+ n = snprintf(cp, buflen, "%s%zu$",
+ sha512_rounds_prefix, rounds);
+ if (n < 0 || n >= buflen) {
+ ret = ERANGE;
+ goto done;
+ }
+ cp += n;
+ buflen -= n;
+ }
+
+ if (buflen <= salt_len + 1) {
+ ret = ERANGE;
+ goto done;
+ }
+ cp = stpncpy(cp, salt, salt_len);
+ *cp++ = '$';
+ buflen -= salt_len + 1;
+
+ /* fuzzyfill the base 64 string */
+ p1 = 0;
+ p2 = 21;
+ p3 = 42;
+ for (n = 0; n < 21; n++) {
+ b64_from_24bit(&cp, &buflen, 4, alt_result[p1], alt_result[p2], alt_result[p3]);
+ if (buflen == 0) {
+ ret = ERANGE;
+ goto done;
+ }
+ pt = p1;
+ p1 = p2 + 1;
+ p2 = p3 + 1;
+ p3 = pt + 1;
+ }
+ /* 64th and last byte */
+ b64_from_24bit(&cp, &buflen, 2, 0, 0, alt_result[p3]);
+ if (buflen == 0) {
+ ret = ERANGE;
+ goto done;
+ }
+
+ *cp = '\0';
+ ret = EOK;
+
+done:
+ /* Clear the buffer for the intermediate result so that people attaching
+ * to processes or reading core dumps cannot get any information. We do it
+ * in this way to clear correct_words[] inside the SHA512 implementation
+ * as well. */
+ EVP_MD_CTX_free(ctx);
+ EVP_MD_CTX_free(alt_ctx);
+ if (p_bytes != NULL) {
+ sss_erase_mem_securely(p_bytes, key_len);
+ }
+ if (s_bytes != NULL) {
+ sss_erase_mem_securely(s_bytes, salt_len);
+ }
+ sss_erase_mem_securely(temp_result, sizeof(temp_result));
+ sss_erase_mem_securely(alt_result, sizeof(alt_result));
+
+ return ret;
+}
+
+int s3crypt_sha512(TALLOC_CTX *memctx,
+ const char *key, const char *salt, char **_hash)
+{
+ char *hash;
+ int hlen = (sizeof (sha512_salt_prefix) - 1
+ + sizeof (sha512_rounds_prefix) + 9 + 1
+ + strlen (salt) + 1 + 86 + 1);
+ int ret;
+
+ hash = talloc_size(memctx, hlen);
+ if (!hash) return ENOMEM;
+
+ ret = sha512_crypt_r(key, salt, hash, hlen);
+ if (ret) return ret;
+
+ *_hash = hash;
+ return ret;
+}
+
+#define SALT_RAND_LEN 12
+
+int s3crypt_gen_salt(TALLOC_CTX *memctx, char **_salt)
+{
+ uint8_t rb[SALT_RAND_LEN];
+ char *salt, *cp;
+ size_t slen;
+ int ret;
+
+ ret = sss_generate_csprng_buffer(rb, SALT_RAND_LEN);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ salt = talloc_size(memctx, SALT_LEN_MAX + 1);
+ if (!salt) {
+ return ENOMEM;
+ }
+
+ slen = SALT_LEN_MAX;
+ cp = salt;
+ b64_from_24bit(&cp, &slen, 4, rb[0], rb[1], rb[2]);
+ b64_from_24bit(&cp, &slen, 4, rb[3], rb[4], rb[5]);
+ b64_from_24bit(&cp, &slen, 4, rb[6], rb[7], rb[8]);
+ b64_from_24bit(&cp, &slen, 4, rb[9], rb[10], rb[11]);
+ *cp = '\0';
+
+ *_salt = salt;
+
+ return EOK;
+}
diff --git a/src/util/crypto/libcrypto/sss_openssl.h b/src/util/crypto/libcrypto/sss_openssl.h
new file mode 100644
index 0000000..a2e2d85
--- /dev/null
+++ b/src/util/crypto/libcrypto/sss_openssl.h
@@ -0,0 +1,39 @@
+/*
+ Authors:
+ Lukas Slebodnik <lslebodn@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 _SSS_LIBCRYTPO_SSS_OPENSSL_H_
+#define _SSS_LIBCRYTPO_SSS_OPENSSL_H_
+
+#include <openssl/evp.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+
+/* EVP_MD_CTX_create and EVP_MD_CTX_destroy are deprecated macros
+ * in openssl-1.1 but openssl-1.0 does not know anything about
+ * newly added functions EVP_MD_CTX_new, EVP_MD_CTX_free in 1.1
+ */
+
+# define EVP_MD_CTX_new() EVP_MD_CTX_create()
+# define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy((ctx))
+
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+
+#endif /* _SSS_LIBCRYTPO_SSS_OPENSSL_H_ */
diff --git a/src/util/crypto/sss_crypto.h b/src/util/crypto/sss_crypto.h
new file mode 100644
index 0000000..db6b154
--- /dev/null
+++ b/src/util/crypto/sss_crypto.h
@@ -0,0 +1,63 @@
+/*
+ Copyright (C) 2009-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 _SSS_CRYPTO_H_
+#define _SSS_CRYPTO_H_
+
+#include <talloc.h>
+#include <stdint.h>
+
+/* Guaranteed either to fill given buffer with crypto strong random data
+ * or to fail with error code (for example in the case of the lack of
+ * proper entropy)
+ * Suitable to be used in security relevant context.
+ */
+int sss_generate_csprng_buffer(uint8_t *buf, size_t size);
+
+int s3crypt_sha512(TALLOC_CTX *mmectx,
+ const char *key, const char *salt, char **_hash);
+int s3crypt_gen_salt(TALLOC_CTX *memctx, char **_salt);
+
+/* Methods of obfuscation. */
+enum obfmethod {
+ AES_256,
+ NUM_OBFMETHODS
+};
+
+char *sss_base64_encode(TALLOC_CTX *mem_ctx,
+ const unsigned char *in,
+ size_t insize);
+
+unsigned char *sss_base64_decode(TALLOC_CTX *mem_ctx,
+ const char *in,
+ size_t *outsize);
+
+#define SSS_SHA1_LENGTH 20
+
+int sss_hmac_sha1(const unsigned char *key,
+ size_t key_len,
+ const unsigned char *in,
+ size_t in_len,
+ unsigned char *out);
+
+int sss_password_encrypt(TALLOC_CTX *mem_ctx, const char *password, int plen,
+ enum obfmethod meth, char **obfpwd);
+
+int sss_password_decrypt(TALLOC_CTX *mem_ctx, char *b64encoded,
+ char **password);
+
+#endif /* _SSS_CRYPTO_H_ */
diff --git a/src/util/debug.c b/src/util/debug.c
new file mode 100644
index 0000000..7dcf64f
--- /dev/null
+++ b/src/util/debug.c
@@ -0,0 +1,519 @@
+/*
+ Authors:
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#ifdef WITH_JOURNALD
+#include <systemd/sd-journal.h>
+#endif
+
+#include "util/util.h"
+
+/* from debug_backtrace.h */
+void sss_debug_backtrace_init(void);
+void sss_debug_backtrace_vprintf(int level, const char *format, va_list ap);
+void sss_debug_backtrace_printf(int level, const char *format, ...);
+void sss_debug_backtrace_endmsg(const char *file, long line, int level);
+
+const char *debug_prg_name = "sssd";
+
+int debug_level = SSSDBG_UNRESOLVED;
+int debug_timestamps = SSSDBG_TIMESTAMP_UNRESOLVED;
+int debug_microseconds = SSSDBG_MICROSECONDS_UNRESOLVED;
+enum sss_logger_t sss_logger = STDERR_LOGGER;
+const char *debug_log_file = "sssd";
+FILE *_sss_debug_file;
+uint64_t debug_chain_id;
+/* Default value says 'BUG' because this is just a precautionary measure and
+ * it is expected value is always set explicitly by every SSSD process. 'BUG'
+ * should make any potential oversight more prominent in the logs. */
+const char *debug_chain_id_fmt = "BUG%lu %s";
+
+const char *sss_logger_str[] = {
+ [STDERR_LOGGER] = "stderr",
+ [FILES_LOGGER] = "files",
+#ifdef WITH_JOURNALD
+ [JOURNALD_LOGGER] = "journald",
+#endif
+ NULL,
+};
+
+
+/* this function isn't static to let access from debug-tests.c */
+void sss_set_logger(const char *logger)
+{
+ if (logger == NULL) {
+ sss_logger = STDERR_LOGGER;
+#ifdef WITH_JOURNALD
+ sss_logger = JOURNALD_LOGGER;
+#endif
+ } else if (strcmp(logger, "stderr") == 0) {
+ sss_logger = STDERR_LOGGER;
+ } else if (strcmp(logger, "files") == 0) {
+ sss_logger = FILES_LOGGER;
+#ifdef WITH_JOURNALD
+ } else if (strcmp(logger, "journald") == 0) {
+ sss_logger = JOURNALD_LOGGER;
+#endif
+ } else {
+ /* unexpected value */
+ fprintf(stderr, "Unexpected logger: %s\nExpected: ", logger);
+ fprintf(stderr, "%s", sss_logger_str[STDERR_LOGGER]);
+ fprintf(stderr, "|%s", sss_logger_str[FILES_LOGGER]);
+#ifdef WITH_JOURNALD
+ fprintf(stderr, "|%s", sss_logger_str[JOURNALD_LOGGER]);
+#endif
+ fprintf(stderr, "\n");
+ sss_logger = STDERR_LOGGER;
+ }
+}
+
+static int _sss_open_debug_file(void)
+{
+ return open_debug_file_ex(NULL, NULL, true);
+}
+
+void _sss_debug_init(int dbg_lvl, const char *logger)
+{
+ if (dbg_lvl != SSSDBG_INVALID) {
+ debug_level = debug_convert_old_level(dbg_lvl);
+ } else {
+ debug_level = SSSDBG_UNRESOLVED;
+ }
+
+ sss_set_logger(logger);
+
+ /* if 'FILES_LOGGER' is requested then open log file, if it wasn't
+ * initialized before via set_debug_file_from_fd().
+ */
+ if ((sss_logger == FILES_LOGGER) && (_sss_debug_file == NULL)) {
+ if (_sss_open_debug_file() != 0) {
+ ERROR("Error opening log file, falling back to stderr\n");
+ sss_logger = STDERR_LOGGER;
+ }
+ }
+
+ sss_debug_backtrace_init();
+}
+
+errno_t set_debug_file_from_fd(const int fd)
+{
+ FILE *dummy;
+ errno_t ret;
+
+ errno = 0;
+ dummy = fdopen(fd, "a");
+ if (dummy == NULL) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "fdopen failed [%d][%s].\n", ret, strerror(ret));
+ sss_log(SSS_LOG_ERR,
+ "Could not open debug file descriptor [%d]. "
+ "Debug messages will not be written to the file "
+ "for this child process [%s][%s]\n",
+ fd, debug_prg_name, strerror(ret));
+ return ret;
+ }
+
+ _sss_debug_file = dummy;
+
+ return EOK;
+}
+
+int get_fd_from_debug_file(void)
+{
+ if (_sss_debug_file == NULL) {
+ return STDERR_FILENO;
+ }
+
+ return fileno(_sss_debug_file);
+}
+
+int debug_convert_old_level(int old_level)
+{
+ if ((old_level != 0) && !(old_level & 0x000F))
+ return old_level;
+
+ int new_level = SSSDBG_FATAL_FAILURE;
+
+ if (old_level <= 0)
+ return new_level;
+
+ if (old_level >= 1)
+ new_level |= SSSDBG_CRIT_FAILURE;
+
+ if (old_level >= 2)
+ new_level |= SSSDBG_OP_FAILURE;
+
+ if (old_level >= 3)
+ new_level |= SSSDBG_MINOR_FAILURE;
+
+ if (old_level >= 4)
+ new_level |= SSSDBG_CONF_SETTINGS;
+
+ if (old_level >= 5)
+ new_level |= SSSDBG_FUNC_DATA;
+
+ if (old_level >= 6)
+ new_level |= SSSDBG_TRACE_FUNC;
+
+ if (old_level >= 7)
+ new_level |= SSSDBG_TRACE_LIBS;
+
+ if (old_level >= 8)
+ new_level |= SSSDBG_TRACE_INTERNAL;
+
+ if (old_level >= 9)
+ new_level |= SSSDBG_TRACE_ALL | SSSDBG_BE_FO | SSSDBG_PERF_STAT;
+
+ if (old_level >= 10)
+ new_level |= SSSDBG_TRACE_LDB;
+
+ return new_level;
+}
+
+
+#ifdef WITH_JOURNALD
+static errno_t journal_send(const char *file,
+ long line,
+ const char *function,
+ int level,
+ const char *format,
+ va_list ap)
+{
+ errno_t ret;
+ int res;
+ char *message = NULL;
+ char *code_file = NULL;
+ char *code_line = NULL;
+ const char *domain;
+
+ /* First, evaluate the message to be sent */
+ ret = vasprintf(&message, format, ap);
+ if (ret == -1) {
+ /* ENOMEM, just return */
+ return ENOMEM;
+ }
+
+ res = asprintf(&code_file, "CODE_FILE=%s", file);
+ if (res == -1) {
+ ret = ENOMEM;
+ goto journal_done;
+ }
+
+ res = asprintf(&code_line, "CODE_LINE=%ld", line);
+ if (res == -1) {
+ ret = ENOMEM;
+ goto journal_done;
+ }
+
+ /* If this log message was sent from a provider,
+ * track the domain.
+ */
+ domain = getenv(SSS_DOM_ENV);
+ if (domain == NULL) {
+ domain = "";
+ }
+
+ /* Send the log message to journald, specifying the
+ * source code location and other tracking data.
+ */
+ res = sd_journal_send_with_location(
+ code_file, code_line, function,
+ "MESSAGE=%s", message,
+ "PRIORITY=%i", LOG_DEBUG,
+ "SSSD_DOMAIN=%s", domain,
+ "SSSD_PRG_NAME=sssd[%s]", debug_prg_name,
+ "SSSD_DEBUG_LEVEL=%x", level,
+ NULL);
+ ret = -res;
+
+journal_done:
+ free(code_line);
+ free(code_file);
+ free(message);
+ return ret;
+}
+#endif /* WiTH_JOURNALD */
+
+void sss_vdebug_fn(const char *file,
+ long line,
+ const char *function,
+ int level,
+ int flags,
+ const char *format,
+ va_list ap)
+{
+ static time_t last_time;
+ static char last_time_str[128];
+ struct timeval tv;
+ struct tm tm;
+ time_t t;
+
+#ifdef WITH_JOURNALD
+ char chain_id_fmt_fixed[256];
+ char *chain_id_fmt_dyn = NULL;
+ char *result_fmt;
+ errno_t ret;
+ va_list ap_fallback;
+
+ if (sss_logger == JOURNALD_LOGGER) {
+ if (!DEBUG_IS_SET(level)) return; /* no debug backtrace with journald atm */
+ /* If we are not outputting logs to files, we should be sending them
+ * to journald.
+ * NOTE: on modern systems, this is where stdout/stderr will end up
+ * from system services anyway. The only difference here is that we
+ * can also provide extra structuring data to make it more easily
+ * searchable.
+ */
+ va_copy(ap_fallback, ap);
+ if (debug_chain_id > 0 && debug_chain_id_fmt != NULL) {
+ result_fmt = chain_id_fmt_fixed;
+ ret = snprintf(chain_id_fmt_fixed, sizeof(chain_id_fmt_fixed),
+ debug_chain_id_fmt, debug_chain_id, format);
+ if (ret < 0) {
+ va_end(ap_fallback);
+ return;
+ } else if (ret >= sizeof(chain_id_fmt_fixed)) {
+ ret = asprintf(&chain_id_fmt_dyn, debug_chain_id_fmt,
+ debug_chain_id, format);
+ if (ret < 0) {
+ va_end(ap_fallback);
+ return;
+ }
+ result_fmt = chain_id_fmt_dyn;
+ }
+
+ ret = journal_send(file, line, function, level, result_fmt, ap);
+ free(chain_id_fmt_dyn);
+ } else {
+ ret = journal_send(file, line, function, level, format, ap);
+ }
+ if (ret != EOK) {
+ /* Emergency fallback, send to STDERR */
+ vfprintf(stderr, format, ap_fallback);
+ fflush(stderr);
+ }
+ va_end(ap_fallback);
+ return;
+ }
+#endif
+
+ if (debug_timestamps == SSSDBG_TIMESTAMP_ENABLED) {
+ if (debug_microseconds == SSSDBG_MICROSECONDS_ENABLED) {
+ gettimeofday(&tv, NULL);
+ t = tv.tv_sec;
+ } else {
+ t = time(NULL);
+ }
+ if (t != last_time) {
+ last_time = t;
+ localtime_r(&t, &tm);
+ snprintf(last_time_str, sizeof(last_time_str),
+ "(%d-%02d-%02d %2d:%02d:%02d",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ }
+ if (debug_microseconds == SSSDBG_MICROSECONDS_ENABLED) {
+ sss_debug_backtrace_printf(level, "%s:%.6ld): ",
+ last_time_str, tv.tv_usec);
+ } else {
+ sss_debug_backtrace_printf(level, "%s): ", last_time_str);
+ }
+ }
+
+ sss_debug_backtrace_printf(level, "[%s] [%s] (%#.4x): ",
+ debug_prg_name, function, level);
+
+ if (debug_chain_id > 0 && debug_chain_id_fmt != NULL) {
+ sss_debug_backtrace_printf(level, debug_chain_id_fmt, debug_chain_id, "");
+ }
+
+ sss_debug_backtrace_vprintf(level, format, ap);
+
+ if (flags & APPEND_LINE_FEED) {
+ sss_debug_backtrace_printf(level, "\n");
+ }
+ sss_debug_backtrace_endmsg(file, line, level);
+}
+
+void sss_debug_fn(const char *file,
+ long line,
+ const char *function,
+ int level,
+ const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ sss_vdebug_fn(file, line, function, level, 0, format, ap);
+ va_end(ap);
+}
+
+/* In cases SSSD used to run as the root user, but runs as the SSSD user now,
+ * we need to chown the log files
+ */
+int chown_debug_file(const char *filename,
+ uid_t uid, gid_t gid)
+{
+ char *logpath;
+ const char *log_file;
+ errno_t ret;
+
+ if (filename == NULL) {
+ log_file = debug_log_file;
+ } else {
+ log_file = filename;
+ }
+
+ ret = asprintf(&logpath, "%s/%s.log", LOG_PATH, log_file);
+ if (ret == -1) {
+ return ENOMEM;
+ }
+
+ ret = chown(logpath, uid, gid);
+ free(logpath);
+ if (ret != 0) {
+ ret = errno;
+ if (ret == ENOENT) {
+ /* Log does not exist. We might log to journald
+ * or starting for first time.
+ * It's not a failure. */
+ return EOK;
+ }
+
+ DEBUG(SSSDBG_FATAL_FAILURE, "chown failed for [%s]: [%d]\n",
+ log_file, ret);
+ return ret;
+ }
+
+ return EOK;
+}
+
+int open_debug_file_ex(const char *filename, FILE **filep, bool want_cloexec)
+{
+ FILE *f = NULL;
+ char *logpath;
+ const char *log_file;
+ mode_t old_umask;
+ int ret;
+ int debug_fd;
+ int flags;
+
+ if (filename == NULL) {
+ log_file = debug_log_file;
+ } else {
+ log_file = filename;
+ }
+
+ ret = asprintf(&logpath, "%s/%s.log", LOG_PATH, log_file);
+ if (ret == -1) {
+ return ENOMEM;
+ }
+
+ if (_sss_debug_file && !filep) fclose(_sss_debug_file);
+
+ old_umask = umask(SSS_DFL_UMASK);
+ errno = 0;
+ f = fopen(logpath, "a");
+ if (f == NULL) {
+ sss_log(SSS_LOG_EMERG, "Could not open file [%s]. Error: [%d][%s]\n",
+ logpath, errno, strerror(errno));
+ free(logpath);
+ return EIO;
+ }
+ umask(old_umask);
+
+ debug_fd = fileno(f);
+ if (debug_fd == -1) {
+ fclose(f);
+ free(logpath);
+ return EIO;
+ }
+
+ if(want_cloexec) {
+ flags = fcntl(debug_fd, F_GETFD, 0);
+ (void) fcntl(debug_fd, F_SETFD, flags | FD_CLOEXEC);
+ }
+
+ if (filep == NULL) {
+ _sss_debug_file = f;
+ } else {
+ *filep = f;
+ }
+ free(logpath);
+ return EOK;
+}
+
+int rotate_debug_files(void)
+{
+ int ret;
+ errno_t error;
+
+ if (sss_logger != FILES_LOGGER) return EOK;
+
+ if (_sss_debug_file != NULL) {
+ do {
+ error = 0;
+ ret = fclose(_sss_debug_file);
+ if (ret != 0) {
+ error = errno;
+ }
+
+ /* Check for EINTR, which means we should retry
+ * because the system call was interrupted by a
+ * signal
+ */
+ } while (error == EINTR);
+
+ if (error != 0) {
+ /* Even if we were unable to close the debug log, we need to make
+ * sure that we open up a new one. Log rotation will remove the
+ * current file, so all debug messages will be disappearing.
+ *
+ * We should write an error to the syslog warning of the resource
+ * leak and then proceed with opening the new file.
+ */
+ sss_log(SSS_LOG_ALERT, "Could not close debug file [%s]. [%d][%s]\n",
+ debug_log_file, error, strerror(error));
+ sss_log(SSS_LOG_ALERT, "Attempting to open new file anyway. "
+ "Be aware that this is a resource leak\n");
+ }
+ }
+
+ _sss_debug_file = NULL;
+
+ return _sss_open_debug_file();
+}
+
+void _sss_talloc_log_fn(const char *message)
+{
+ DEBUG(SSSDBG_FATAL_FAILURE, "%s\n", message);
+}
diff --git a/src/util/debug.h b/src/util/debug.h
new file mode 100644
index 0000000..54f9d11
--- /dev/null
+++ b/src/util/debug.h
@@ -0,0 +1,204 @@
+/*
+ Authors:
+ Simo Sorce <ssorce@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 __SSSD_DEBUG_H__
+#define __SSSD_DEBUG_H__
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "util/util_errors.h"
+
+#define SSSDBG_TIMESTAMP_UNRESOLVED -1
+#define SSSDBG_TIMESTAMP_DISABLED 0
+#define SSSDBG_TIMESTAMP_ENABLED 1
+#define SSSDBG_TIMESTAMP_DEFAULT SSSDBG_TIMESTAMP_ENABLED
+
+#define SSSDBG_MICROSECONDS_UNRESOLVED -1
+#define SSSDBG_MICROSECONDS_DISABLED 0
+#define SSSDBG_MICROSECONDS_ENABLED 1
+#define SSSDBG_MICROSECONDS_DEFAULT SSSDBG_MICROSECONDS_DISABLED
+
+
+enum sss_logger_t {
+ STDERR_LOGGER = 0,
+ FILES_LOGGER,
+#ifdef WITH_JOURNALD
+ JOURNALD_LOGGER,
+#endif
+};
+
+extern const char *sss_logger_str[]; /* mapping: sss_logger_t -> string */
+extern const char *debug_prg_name;
+extern int debug_level;
+extern int debug_timestamps;
+extern int debug_microseconds;
+extern enum sss_logger_t sss_logger;
+extern const char *debug_log_file; /* only file name, excluding path */
+
+
+/* converts log level from "old" notation and opens log file if needed */
+#define DEBUG_INIT(dbg_lvl, logger) do { \
+ _sss_debug_init(dbg_lvl, logger); \
+ talloc_set_log_fn(_sss_talloc_log_fn); \
+} while (0)
+
+/* CLI tools shall debug to stderr */
+#define DEBUG_CLI_INIT(dbg_lvl) do { \
+ DEBUG_INIT(dbg_lvl, sss_logger_str[STDERR_LOGGER]); \
+} while (0)
+
+void sss_debug_backtrace_enable(bool enable);
+
+/* debug_convert_old_level() converts "old" style decimal notation
+ * to bitmask composed of SSSDBG_*
+ * Used explicitly, for example, while processing user input
+ * in sssctl_logs.
+ */
+int debug_convert_old_level(int old_level);
+
+/* set_debug_file_from_fd() is used by *_child processes as those
+ * don't manage logs files on their own but instead receive fd arg
+ * on command line.
+ */
+errno_t set_debug_file_from_fd(const int fd);
+
+/* get_fd_from_debug_file() is used to redirect STDERR_FILENO
+ * to currently open log file fd while running external helpers
+ * (e.g. nsupdate, ipa_get_keytab)
+ */
+int get_fd_from_debug_file(void);
+
+/* chown_debug_file() uses 'debug_log_file' in case 'filename == NULL' */
+int chown_debug_file(const char *filename, uid_t uid, gid_t gid);
+
+/* open_debug_file_ex() is used to open log file for *_child processes */
+int open_debug_file_ex(const char *filename, FILE **filep, bool want_cloexec);
+
+int rotate_debug_files(void);
+
+#define SSS_DOM_ENV "_SSS_DOM"
+
+/* 0x0800 isn't used for historical reasons */
+#define SSSDBG_FATAL_FAILURE 0x0010 /* level 0 */
+#define SSSDBG_CRIT_FAILURE 0x0020 /* level 1 */
+#define SSSDBG_OP_FAILURE 0x0040 /* level 2 */
+#define SSSDBG_MINOR_FAILURE 0x0080 /* level 3 */
+#define SSSDBG_CONF_SETTINGS 0x0100 /* level 4 */
+#define SSSDBG_FUNC_DATA 0x0200 /* level 5 */
+#define SSSDBG_TRACE_FUNC 0x0400 /* level 6 */
+#define SSSDBG_TRACE_LIBS 0x1000 /* level 7 */
+#define SSSDBG_TRACE_INTERNAL 0x2000 /* level 8 */
+#define SSSDBG_TRACE_ALL 0x4000 /* level 9 */
+#define SSSDBG_BE_FO 0x8000 /* level 9 */
+#define SSSDBG_TRACE_LDB 0x10000 /* level 10 */
+#define SSSDBG_PERF_STAT 0x20000 /* level 9 */
+
+/* IMPORTANT_INFO will be logged if any of bits >= OP_FAILURE are on: */
+#define SSSDBG_IMPORTANT_INFO (SSSDBG_OP_FAILURE|SSSDBG_MINOR_FAILURE|\
+ SSSDBG_CONF_SETTINGS|SSSDBG_FUNC_DATA|\
+ SSSDBG_TRACE_FUNC|SSSDBG_TRACE_LIBS|\
+ SSSDBG_TRACE_INTERNAL|SSSDBG_TRACE_ALL|\
+ SSSDBG_BE_FO|SSSDBG_TRACE_LDB|SSSDBG_PERF_STAT)
+
+#define SSSDBG_INVALID -1
+#define SSSDBG_UNRESOLVED 0
+#define SSSDBG_DEFAULT (SSSDBG_FATAL_FAILURE|SSSDBG_CRIT_FAILURE|SSSDBG_OP_FAILURE)
+#define SSSDBG_TOOLS_DEFAULT (SSSDBG_FATAL_FAILURE)
+
+
+/** \def DEBUG(level, format, ...)
+ \brief macro to generate debug messages
+
+ \param level the debug level, please use one of the SSSDBG_* macros
+ \param format the debug message format string, should result in a
+ newline-terminated message
+ \param ... the debug message format arguments
+*/
+#define DEBUG(level, format, ...) do { \
+ sss_debug_fn(__FILE__, __LINE__, __FUNCTION__, \
+ level, \
+ format, ##__VA_ARGS__); \
+} while (0)
+
+
+/* SSSD_*_OPTS are used as 'poptOption' entries */
+#define SSSD_LOGGER_OPTS \
+ {"logger", '\0', POPT_ARG_STRING, &opt_logger, 0, \
+ _("Set logger"), "stderr|files|journald"},
+
+#define SSSD_DEBUG_OPTS \
+ {"debug-level", 'd', POPT_ARG_INT, &debug_level, 0, \
+ _("Debug level"), NULL}, \
+ {"debug-timestamps", 0, POPT_ARG_INT, &debug_timestamps, 0, \
+ _("Add debug timestamps"), NULL}, \
+ {"debug-microseconds", 0, POPT_ARG_INT, &debug_microseconds, 0, \
+ _("Show timestamps with microseconds"), NULL},
+
+
+#define PRINT(fmt, ...) fprintf(stdout, gettext(fmt), ##__VA_ARGS__)
+#define ERROR(fmt, ...) fprintf(stderr, gettext(fmt), ##__VA_ARGS__)
+
+
+#ifdef HAVE_FUNCTION_ATTRIBUTE_FORMAT
+#define SSS_ATTRIBUTE_PRINTF(a1, a2) __attribute__((format (printf, a1, a2)))
+#else
+#define SSS_ATTRIBUTE_PRINTF(a1, a2)
+#endif
+
+/* sss_*debug_fn() are rarely needed to be used explicitly
+ * (common example: provision of a logger function to 3rd party lib)
+ * For normal logs use DEBUG() instead.
+ */
+void sss_vdebug_fn(const char *file,
+ long line,
+ const char *function,
+ int level,
+ int flags,
+ const char *format,
+ va_list ap);
+void sss_debug_fn(const char *file,
+ long line,
+ const char *function,
+ int level,
+ const char *format, ...) SSS_ATTRIBUTE_PRINTF(5, 6);
+
+#define APPEND_LINE_FEED 0x1 /* can be used as a sss_vdebug_fn() flag */
+
+/* Checks whether level is set in generic debug_level.
+ Rarely needed to be used explicitly as everything
+ should go to backtrace buffer anyway (regardless debug_level)
+ Deciding if "--verbose" should be passed to `adcli` child process
+ is one of usage examples.
+ */
+#define DEBUG_IS_SET(level) (debug_level & (level) || \
+ (debug_level == SSSDBG_UNRESOLVED && \
+ (level & (SSSDBG_FATAL_FAILURE | \
+ SSSDBG_CRIT_FAILURE))))
+
+
+/* not to be used explictly, use 'DEBUG_INIT' instead */
+void _sss_debug_init(int dbg_lvl, const char *logger);
+void _sss_talloc_log_fn(const char *msg);
+
+#endif /* __SSSD_DEBUG_H__ */
diff --git a/src/util/debug_backtrace.c b/src/util/debug_backtrace.c
new file mode 100644
index 0000000..e376f81
--- /dev/null
+++ b/src/util/debug_backtrace.c
@@ -0,0 +1,331 @@
+/*
+ Copyright (C) 2021 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 <stdbool.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "util/debug.h"
+
+extern FILE *_sss_debug_file;
+
+
+static const unsigned SSS_DEBUG_BACKTRACE_DEFAULT_SIZE = 100*1024; /* bytes */
+static const unsigned SSS_DEBUG_BACKTRACE_LEVEL = SSSDBG_BE_FO;
+
+/* Size of locations history to keep to avoid duplicating backtraces */
+#define SSS_DEBUG_BACKTRACE_LOCATIONS 5
+
+
+/* -->
+ * ring buffer = [*******t...\n............e000]
+ * where:
+ * "t" - 'tail', "e" - 'end'
+ * "......" - "old" part of buffer
+ * "******" - "new" part of buffer
+ * "000" - unoccupied space
+ */
+static struct {
+ bool enabled;
+ bool initialized;
+ int size;
+ char *buffer; /* buffer start */
+ char *end; /* end data border */
+ char *tail; /* tail of "current" message */
+
+ /* locations where last backtraces happened */
+ struct {
+ const char *file;
+ long line;
+ } locations[SSS_DEBUG_BACKTRACE_LOCATIONS];
+ unsigned last_location_idx;
+} _bt;
+
+
+static inline bool _all_levels_enabled(void);
+static inline bool _backtrace_is_enabled(int level);
+static inline bool _is_trigger_level(int level);
+static void _store_location(const char *file, long line);
+static bool _is_recent_location(const char *file, long line);
+static void _backtrace_vprintf(const char *format, va_list ap);
+static void _backtrace_printf(const char *format, ...);
+static void _backtrace_dump(void);
+static inline void _debug_vprintf(const char *format, va_list ap);
+static inline void _debug_fwrite(const char *ptr, const char *end);
+static inline void _debug_fflush(void);
+
+
+void sss_debug_backtrace_init(void)
+{
+ _bt.size = SSS_DEBUG_BACKTRACE_DEFAULT_SIZE;
+ _bt.buffer = (char *)malloc(_bt.size);
+ if (!_bt.buffer) {
+ ERROR("Failed to allocate debug backtrace buffer, feature is off\n");
+ return;
+ }
+
+ _bt.end = _bt.buffer;
+ _bt.tail = _bt.buffer;
+
+ _bt.enabled = true;
+ _bt.initialized = true;
+
+ /* locations[] & last_location_idx are zero-initialized */
+
+ _backtrace_printf(" * ");
+}
+
+
+void sss_debug_backtrace_enable(bool enable)
+{
+ _bt.enabled = enable;
+}
+
+
+void sss_debug_backtrace_vprintf(int level, const char *format, va_list ap)
+{
+ va_list ap_copy;
+
+ /* Potential optimization: only print to file here if backtrace is disabled,
+ * otherwise always print message to backtrace only and then copy message
+ * from backtrace to file in sss_debug_backtrace_endmsg().
+ * This saves va_copy and another round of format parsing inside printf but
+ * results in a little bit less readable output.
+ */
+ if (DEBUG_IS_SET(level)) {
+ va_copy(ap_copy, ap);
+ _debug_vprintf(format, ap_copy);
+ va_end(ap_copy);
+ }
+
+ if (_backtrace_is_enabled(level)) {
+ _backtrace_vprintf(format, ap);
+ }
+}
+
+
+void sss_debug_backtrace_printf(int level, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ sss_debug_backtrace_vprintf(level, format, ap);
+ va_end(ap);
+}
+
+
+void sss_debug_backtrace_endmsg(const char *file, long line, int level)
+{
+ if (DEBUG_IS_SET(level)) {
+ _debug_fflush();
+ }
+
+ if (_backtrace_is_enabled(level)) {
+ if (_is_trigger_level(level)) {
+ if (!_is_recent_location(file, line)) {
+ _backtrace_dump();
+ _store_location(file, line);
+ } else {
+ fprintf(_sss_debug_file ? _sss_debug_file : stderr,
+ " * ... skipping repetitive backtrace ...\n");
+ /* and reset */
+ _bt.end = _bt.buffer;
+ _bt.tail = _bt.buffer;
+ }
+ }
+ _backtrace_printf(" * ");
+ }
+}
+
+
+
+
+/* ********** Helpers ********** */
+
+
+static inline void _debug_vprintf(const char *format, va_list ap)
+{
+ vfprintf(_sss_debug_file ? _sss_debug_file : stderr, format, ap);
+}
+
+
+static inline void _debug_fwrite(const char *begin, const char *end)
+{
+ if (end <= begin) {
+ return;
+ }
+ size_t size = (end - begin);
+ fwrite_unlocked(begin, size, 1, _sss_debug_file ? _sss_debug_file : stderr);
+}
+
+
+static inline void _debug_fflush(void)
+{
+ fflush(_sss_debug_file ? _sss_debug_file : stderr);
+}
+
+
+ /* does 'level' trigger backtrace dump? */
+static inline bool _is_trigger_level(int level)
+{
+ return ((level <= SSSDBG_OP_FAILURE) &&
+ (level <= debug_level));
+}
+
+
+/* checks if global 'debug_level' has all levels up to 9 enabled */
+static inline bool _all_levels_enabled(void)
+{
+ static const unsigned all_levels =
+ SSSDBG_FATAL_FAILURE|SSSDBG_CRIT_FAILURE|SSSDBG_OP_FAILURE|
+ SSSDBG_MINOR_FAILURE|SSSDBG_CONF_SETTINGS|SSSDBG_FUNC_DATA|
+ SSSDBG_TRACE_FUNC|SSSDBG_TRACE_LIBS|SSSDBG_TRACE_INTERNAL|
+ SSSDBG_TRACE_ALL|SSSDBG_BE_FO;
+
+ return ((debug_level & all_levels) == all_levels);
+}
+
+
+/* should message of this 'level' go to backtrace? */
+static inline bool _backtrace_is_enabled(int level)
+{
+ /* Store message in backtrace buffer if: */
+ return (_bt.initialized && /* backtrace is initialized */
+ _bt.enabled && /* backtrace is enabled */
+ sss_logger != STDERR_LOGGER &&
+ !_all_levels_enabled() && /* generic log doesn't cover everything */
+ level <= SSS_DEBUG_BACKTRACE_LEVEL); /* skip SSSDBG_TRACE_LDB */
+}
+
+
+static void _store_location(const char *file, long line)
+{
+ _bt.last_location_idx = (_bt.last_location_idx + 1) % SSS_DEBUG_BACKTRACE_LOCATIONS;
+ /* __FILE__ is a character string literal with static storage duration. */
+ _bt.locations[_bt.last_location_idx].file = file;
+ _bt.locations[_bt.last_location_idx].line = line;
+}
+
+
+static bool _is_recent_location(const char *file, long line)
+{
+ for (unsigned idx = 0; idx < SSS_DEBUG_BACKTRACE_LOCATIONS; ++idx) {
+ if ((line == _bt.locations[idx].line) &&
+ (_bt.locations[idx].file != NULL) &&
+ (strcmp(file, _bt.locations[idx].file) == 0)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/* prints to buffer */
+static void _backtrace_vprintf(const char *format, va_list ap)
+{
+ int buff_tail_size = _bt.size - (_bt.tail - _bt.buffer);
+ int written;
+
+ /* make sure there is at least 1kb available to avoid truncation;
+ * putting a sane limit on the size of single message (1kb in a worst case)
+ * makes logic simpler and avoids performance hit
+ */
+ if (buff_tail_size < 1024) {
+ /* let's wrap */
+ _bt.end = _bt.tail;
+ _bt.tail = _bt.buffer;
+ buff_tail_size = _bt.size;
+ }
+
+ written = vsnprintf(_bt.tail, buff_tail_size, format, ap);
+ if (written >= buff_tail_size) {
+ /* message is > 1kb, just discard */
+ return;
+ }
+
+ _bt.tail += written;
+ if (_bt.tail > _bt.end) {
+ _bt.end = _bt.tail;
+ }
+}
+
+
+static void _backtrace_printf(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ _backtrace_vprintf(format, ap);
+ va_end(ap);
+}
+
+
+static bool _bt_empty(const char *begin, const char *end)
+{
+ int counter = 0;
+
+ while (begin < end) {
+ if (*begin == '\n') {
+ counter++;
+ if (counter == 2) {
+ /* there is least one line in addition to trigger msg */
+ return false;
+ }
+ }
+ begin++;
+ }
+
+ return true;
+}
+
+
+static void _backtrace_dump(void)
+{
+ const char *start = NULL;
+ static const char *start_marker =
+ "********************** PREVIOUS MESSAGE WAS TRIGGERED BY THE FOLLOWING BACKTRACE:\n";
+ static const char *end_marker =
+ "********************** BACKTRACE DUMP ENDS HERE *********************************\n\n";
+
+ if (_bt.end > _bt.tail) {
+ /* there is something in the "old" part, but don't start mid message */
+ start = _bt.tail + 1;
+ while ((start < _bt.end) && (*start != '\n')) start++;
+ if (start >= _bt.end) start = NULL;
+ }
+
+ if (!start) {
+ /* do we have anything to dump at all? */
+ if (_bt_empty(_bt.buffer, _bt.tail)) {
+ return;
+ }
+ }
+
+ fprintf(_sss_debug_file ? _sss_debug_file : stderr, "%s", start_marker);
+
+ if (start) {
+ _debug_fwrite(start + 1, _bt.end); /* dump "old" part of buffer */
+ }
+ _debug_fwrite(_bt.buffer, _bt.tail); /* dump "new" part of buffer */
+
+ fprintf(_sss_debug_file ? _sss_debug_file : stderr, "%s", end_marker);
+ _debug_fflush();
+
+ _bt.end = _bt.buffer;
+ _bt.tail = _bt.buffer;
+}
diff --git a/src/util/dlinklist.h b/src/util/dlinklist.h
new file mode 100644
index 0000000..017c604
--- /dev/null
+++ b/src/util/dlinklist.h
@@ -0,0 +1,155 @@
+/*
+ Unix SMB/CIFS implementation.
+ some simple double linked list macros
+ Copyright (C) Andrew Tridgell 1998
+
+ 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/>.
+*/
+
+/* To use these macros you must have a structure containing a next and
+ prev pointer */
+
+#ifndef _DLINKLIST_H
+#define _DLINKLIST_H
+
+
+/* hook into the front of the list */
+#define DLIST_ADD(list, p) \
+do { \
+ if (!(list)) { \
+ (list) = (p); \
+ (p)->next = (p)->prev = NULL; \
+ } else { \
+ (list)->prev = (p); \
+ (p)->next = (list); \
+ (p)->prev = NULL; \
+ (list) = (p); \
+ } \
+} while (0)
+
+/* remove an element from a list - element doesn't have to be in list. */
+#define DLIST_REMOVE(list, p) \
+do { \
+ if ((p) == (list)) { \
+ (list) = (p)->next; \
+ if (list) { \
+ (list)->prev = NULL; \
+ } \
+ } else { \
+ if ((p)->prev) { \
+ (p)->prev->next = (p)->next; \
+ } \
+ if ((p)->next) { \
+ (p)->next->prev = (p)->prev; \
+ } \
+ } \
+ if ((p) != (list)) { \
+ (p)->next = (p)->prev = NULL; \
+ } \
+} while (0)
+
+/* promote an element to the top of the list */
+#define DLIST_PROMOTE(list, p) \
+do { \
+ DLIST_REMOVE(list, p); \
+ DLIST_ADD(list, p); \
+} while (0)
+
+/* hook into the end of the list - needs a tmp pointer */
+#define DLIST_ADD_END(list, p, type) \
+do { \
+ if (!(list)) { \
+ (list) = (p); \
+ (p)->next = (p)->prev = NULL; \
+ } else { \
+ type tmp; \
+ for (tmp = (list); tmp->next; tmp = tmp->next) { \
+ /* no op */ \
+ } \
+ tmp->next = (p); \
+ (p)->next = NULL; \
+ (p)->prev = tmp; \
+ } \
+} while (0)
+
+/* insert 'p' after the given element 'el' in a list. If el is NULL then
+ this is the same as a DLIST_ADD() */
+#define DLIST_ADD_AFTER(list, p, el) \
+do { \
+ if (!(list) || !(el)) { \
+ DLIST_ADD(list, p); \
+ } else { \
+ p->prev = el; \
+ p->next = el->next; \
+ el->next = p; \
+ if (p->next) { \
+ p->next->prev = p; \
+ } \
+ } \
+} while (0)
+
+/* demote an element to the end of the list, needs a tmp pointer */
+#define DLIST_DEMOTE(list, p, type) \
+do { \
+ DLIST_REMOVE(list, p); \
+ DLIST_ADD_END(list, p, type); \
+} while (0)
+
+/* concatenate two lists - putting all elements of the 2nd list at the
+ end of the first list */
+#define DLIST_CONCATENATE(list1, list2, type) \
+do { \
+ if (!(list1)) { \
+ (list1) = (list2); \
+ } else { \
+ type tmp; \
+ for (tmp = (list1); tmp->next; tmp = tmp->next) { \
+ /* no op */ \
+ } \
+ tmp->next = (list2); \
+ if (list2) { \
+ (list2)->prev = tmp; \
+ } \
+ } \
+} while (0)
+
+/* insert all elements from list2 after the given element 'el' in the
+ * first list */
+#define DLIST_ADD_LIST_AFTER(list1, el, list2, type) \
+do { \
+ if (!(list1) || !(el) || !(list2)) { \
+ DLIST_CONCATENATE(list1, list2, type); \
+ } else { \
+ type tmp; \
+ for (tmp = (list2); tmp->next; tmp = tmp->next) { \
+ /* no op */ \
+ } \
+ (list2)->prev = (el); \
+ tmp->next = (el)->next; \
+ (el)->next = (list2); \
+ if (tmp->next != NULL) { \
+ tmp->next->prev = tmp; \
+ } \
+ } \
+} while (0);
+
+#define DLIST_FOR_EACH(p, list) \
+ for ((p) = (list); (p) != NULL; (p) = (p)->next)
+
+#define DLIST_FOR_EACH_SAFE(p, q, list) \
+ for ((p) = (list), (q) = (p) != NULL ? (p)->next : NULL; \
+ (p) != NULL; \
+ (p) = (q), (q) = (p) != NULL ? (p)->next : NULL)
+
+#endif /* _DLINKLIST_H */
diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
new file mode 100644
index 0000000..a6fae06
--- /dev/null
+++ b/src/util/domain_info_utils.c
@@ -0,0 +1,1044 @@
+/*
+ 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 <ctype.h>
+#include <utime.h>
+
+#include "confdb/confdb.h"
+#include "db/sysdb.h"
+#include "util/util.h"
+
+struct sss_domain_info *get_domains_head(struct sss_domain_info *domain)
+{
+ struct sss_domain_info *dom = NULL;
+
+ /* get to the top level domain */
+ for (dom = domain; dom->parent != NULL; dom = dom->parent);
+
+ return dom;
+}
+
+struct sss_domain_info *get_next_domain(struct sss_domain_info *domain,
+ uint32_t gnd_flags)
+{
+ struct sss_domain_info *dom;
+ bool descend = gnd_flags & (SSS_GND_DESCEND | SSS_GND_SUBDOMAINS);
+ bool include_disabled = gnd_flags & SSS_GND_INCLUDE_DISABLED;
+ bool only_subdomains = gnd_flags & SSS_GND_SUBDOMAINS;
+
+ dom = domain;
+ while (dom) {
+ if (descend && dom->subdomains) {
+ dom = dom->subdomains;
+ } else if (dom->next && only_subdomains && IS_SUBDOMAIN(dom)) {
+ dom = dom->next;
+ } else if (dom->next && !only_subdomains) {
+ dom = dom->next;
+ } else if (descend && !only_subdomains && IS_SUBDOMAIN(dom)
+ && dom->parent->next) {
+ dom = dom->parent->next;
+ } else {
+ dom = NULL;
+ }
+
+ if (dom) {
+ if (sss_domain_get_state(dom) == DOM_DISABLED
+ && !include_disabled) {
+ continue;
+ } else {
+ /* Next domain found. */
+ break;
+ }
+ }
+ }
+
+ return dom;
+}
+
+bool subdomain_enumerates(struct sss_domain_info *parent,
+ const char *sd_name)
+{
+ if (parent->sd_enumerate == NULL
+ || parent->sd_enumerate[0] == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Subdomain_enumerate not set\n");
+ return false;
+ }
+
+ if (strcasecmp(parent->sd_enumerate[0], "all") == 0) {
+ return true;
+ } else if (strcasecmp(parent->sd_enumerate[0], "none") == 0) {
+ return false;
+ } else {
+ for (int i=0; parent->sd_enumerate[i]; i++) {
+ if (strcasecmp(parent->sd_enumerate[i], sd_name) == 0) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+struct sss_domain_info *find_domain_by_name_ex(struct sss_domain_info *domain,
+ const char *name,
+ bool match_any,
+ uint32_t gnd_flags)
+{
+ struct sss_domain_info *dom = domain;
+
+ if (name == NULL) {
+ return NULL;
+ }
+
+ if (!(gnd_flags & SSS_GND_INCLUDE_DISABLED)) {
+ while (dom && sss_domain_get_state(dom) == DOM_DISABLED) {
+ dom = get_next_domain(dom, gnd_flags);
+ }
+ }
+
+ while (dom) {
+ if (strcasecmp(dom->name, name) == 0 ||
+ ((match_any == true) && (dom->flat_name != NULL) &&
+ (strcasecmp(dom->flat_name, name) == 0))) {
+ return dom;
+ }
+ dom = get_next_domain(dom, gnd_flags);
+ }
+
+ return NULL;
+}
+
+struct sss_domain_info *find_domain_by_name(struct sss_domain_info *domain,
+ const char *name,
+ bool match_any)
+{
+ return find_domain_by_name_ex(domain, name, match_any, SSS_GND_DESCEND);
+}
+
+struct sss_domain_info *find_domain_by_sid(struct sss_domain_info *domain,
+ const char *sid)
+{
+ struct sss_domain_info *dom = domain;
+ size_t sid_len;
+ size_t dom_sid_len;
+
+ if (sid == NULL) {
+ return NULL;
+ }
+
+ sid_len = strlen(sid);
+
+ while (dom && sss_domain_get_state(dom) == DOM_DISABLED) {
+ dom = get_next_domain(dom, SSS_GND_DESCEND);
+ }
+
+ while (dom) {
+ if (dom->domain_id != NULL) {
+ dom_sid_len = strlen(dom->domain_id);
+
+ if (strncasecmp(dom->domain_id, sid, dom_sid_len) == 0) {
+ if (dom_sid_len == sid_len) {
+ /* sid is domain sid */
+ return dom;
+ }
+
+ /* sid is object sid, check if domain sid is align with
+ * sid first subauthority component */
+ if (sid[dom_sid_len] == '-') {
+ return dom;
+ }
+ }
+ }
+
+ dom = get_next_domain(dom, SSS_GND_DESCEND);
+ }
+
+ return NULL;
+}
+
+struct sss_domain_info*
+sss_get_domain_by_sid_ldap_fallback(struct sss_domain_info *domain,
+ const char* sid)
+{
+ /* LDAP provider doesn't know about sub-domains and hence can only
+ * have one configured domain
+ */
+ if (strcmp(domain->provider, "ldap") == 0) {
+ return domain;
+ } else {
+ return find_domain_by_sid(get_domains_head(domain), sid);
+ }
+}
+
+struct sss_domain_info *
+find_domain_by_object_name_ex(struct sss_domain_info *domain,
+ const char *object_name, bool strict,
+ uint32_t gnd_flags)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_domain_info *dom = NULL;
+ char *domainname = NULL;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return NULL;
+ }
+
+ ret = sss_parse_internal_fqname(tmp_ctx, object_name,
+ NULL, &domainname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to parse name '%s' [%d]: %s\n",
+ object_name, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (domainname == NULL) {
+ if (strict) {
+ dom = NULL;
+ } else {
+ dom = domain;
+ }
+ } else {
+ dom = find_domain_by_name_ex(domain, domainname, true, gnd_flags);
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return dom;
+}
+
+struct sss_domain_info *
+find_domain_by_object_name(struct sss_domain_info *domain,
+ const char *object_name)
+{
+ return find_domain_by_object_name_ex(domain, object_name, false,
+ SSS_GND_DESCEND);
+}
+
+errno_t sssd_domain_init(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *cdb,
+ const char *domain_name,
+ const char *db_path,
+ struct sss_domain_info **_domain)
+{
+ int ret;
+ struct sss_domain_info *dom;
+ struct sysdb_ctx *sysdb;
+
+ ret = confdb_get_domain(cdb, domain_name, &dom);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error retrieving domain configuration.\n");
+ return ret;
+ }
+
+ if (dom->sysdb != NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Sysdb context already initialized.\n");
+ return EEXIST;
+ }
+
+ ret = sysdb_domain_init(mem_ctx, dom, db_path, &sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error opening cache database.\n");
+ return ret;
+ }
+
+ dom->sysdb = talloc_steal(dom, sysdb);
+
+ *_domain = dom;
+
+ return EOK;
+}
+
+static errno_t
+sss_krb5_touch_config(void)
+{
+ const char *config = NULL;
+ errno_t ret;
+
+ config = getenv("KRB5_CONFIG");
+ if (config == NULL) {
+ config = KRB5_CONF_PATH;
+ }
+
+ ret = utime(config, NULL);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to change mtime of \"%s\" "
+ "[%d]: %s\n", config, ret, strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t sss_get_domain_mappings_content(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ char **content)
+{
+ int ret;
+ char *o = NULL;
+ struct sss_domain_info *dom;
+ struct sss_domain_info *parent_dom;
+ char *uc_parent = NULL;
+ char *uc_forest = NULL;
+ char *parent_capaths = NULL;
+ bool capaths_started = false;
+
+ if (domain == NULL || content == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing parameter.\n");
+ return EINVAL;
+ }
+
+ o = talloc_strdup(mem_ctx, "[domain_realm]\n");
+ if (o == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Start with the parent domain */
+ if (domain->realm != NULL) {
+ o = talloc_asprintf_append(o, ".%s = %s\n%s = %s\n",
+ domain->name, domain->realm, domain->name,
+ domain->realm);
+ if (o == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf_append failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ /* This loops skips the starting parent and starts right with the first
+ * subdomain, if any. */
+ for (dom = get_next_domain(domain, SSS_GND_DESCEND);
+ dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */
+ dom = get_next_domain(dom, 0)) {
+ if (dom->realm != NULL) {
+ o = talloc_asprintf_append(o, ".%s = %s\n%s = %s\n",
+ dom->name, dom->realm, dom->name, dom->realm);
+ if (o == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf_append failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ parent_dom = domain;
+ uc_parent = get_uppercase_realm(mem_ctx, parent_dom->name);
+ if (uc_parent == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_uppercase_realm failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (dom = get_next_domain(domain, SSS_GND_DESCEND);
+ dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */
+ dom = get_next_domain(dom, 0)) {
+
+ if (dom->forest == NULL) {
+ continue;
+ }
+
+ talloc_free(uc_forest);
+ uc_forest = get_uppercase_realm(mem_ctx, dom->forest);
+ if (uc_forest == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_uppercase_realm failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (!capaths_started) {
+ o = talloc_asprintf_append(o, "[capaths]\n");
+ if (o == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf_append failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ capaths_started = true;
+ }
+
+ o = talloc_asprintf_append(o, "%s = {\n %s = %s\n}\n",
+ dom->realm, uc_parent, uc_forest);
+ if (o == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf_append failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (parent_capaths == NULL) {
+ parent_capaths = talloc_asprintf(mem_ctx, " %s = %s\n", dom->realm,
+ uc_forest);
+ } else {
+ parent_capaths = talloc_asprintf_append(parent_capaths,
+ " %s = %s\n", dom->realm,
+ uc_forest);
+ }
+ if (parent_capaths == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "talloc_asprintf/talloc_asprintf_append failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (parent_capaths != NULL) {
+ o = talloc_asprintf_append(o, "%s = {\n%s}\n", uc_parent,
+ parent_capaths);
+ if (o == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf_append failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(parent_capaths);
+ talloc_free(uc_parent);
+ talloc_free(uc_forest);
+
+ if (ret == EOK) {
+ *content = o;
+ } else {
+ talloc_free(o);
+ }
+
+ return ret;
+}
+
+errno_t
+sss_write_domain_mappings(struct sss_domain_info *domain)
+{
+ errno_t ret;
+ errno_t err;
+ TALLOC_CTX *tmp_ctx;
+ const char *mapping_file;
+ char *sanitized_domain;
+ char *tmp_file = NULL;
+ int fd = -1;
+ mode_t old_mode;
+ FILE *fstream = NULL;
+ int i;
+ char *content = NULL;
+
+ if (domain == NULL || domain->name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No domain name provided\n");
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ ret = sss_get_domain_mappings_content(tmp_ctx, domain, &content);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_get_domain_mappings_content failed.\n");
+ goto done;
+ }
+
+ sanitized_domain = talloc_strdup(tmp_ctx, domain->name);
+ if (sanitized_domain == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+ return ENOMEM;
+ }
+
+ /* only alpha-numeric chars, dashes and underscores are allowed in
+ * krb5 include directory */
+ for (i = 0; sanitized_domain[i] != '\0'; i++) {
+ if (!isalnum(sanitized_domain[i])
+ && sanitized_domain[i] != '-' && sanitized_domain[i] != '_') {
+ sanitized_domain[i] = '_';
+ }
+ }
+
+ mapping_file = talloc_asprintf(tmp_ctx, "%s/domain_realm_%s",
+ KRB5_MAPPING_DIR, sanitized_domain);
+ if (!mapping_file) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_FUNC_DATA, "Mapping file for domain [%s] is [%s]\n",
+ domain->name, mapping_file);
+
+ tmp_file = get_hidden_tmp_path(tmp_ctx, mapping_file);
+ if (tmp_file == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ old_mode = umask(SSS_DFL_UMASK);
+ fd = mkstemp(tmp_file);
+ umask(old_mode);
+ if (fd < 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "creating the temp file [%s] for domain-realm mappings "
+ "failed.\n", tmp_file);
+ ret = EIO;
+ talloc_zfree(tmp_ctx);
+ goto done;
+ }
+
+ fstream = fdopen(fd, "a");
+ if (!fstream) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE, "fdopen failed [%d]: %s\n",
+ ret, strerror(ret));
+ ret = close(fd);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "fclose failed [%d][%s].\n", ret, strerror(ret));
+ /* Nothing to do here, just report the failure */
+ }
+ ret = EIO;
+ goto done;
+ }
+
+ ret = fprintf(fstream, "%s", content);
+ if (ret < 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "fprintf failed\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = fclose(fstream);
+ fstream = NULL;
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "fclose failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+
+ ret = rename(tmp_file, mapping_file);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "rename failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+
+ talloc_zfree(tmp_file);
+
+ ret = chmod(mapping_file, 0644);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "fchmod failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ err = sss_krb5_touch_config();
+ if (err != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to change last modification time "
+ "of krb5.conf. Created mappings may not be loaded.\n");
+ /* Ignore */
+ }
+
+ if (fstream) {
+ err = fclose(fstream);
+ if (err != 0) {
+ err = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "fclose failed [%d][%s].\n", err, strerror(err));
+ /* Nothing to do here, just report the failure */
+ }
+ }
+
+ if (tmp_file) {
+ err = unlink(tmp_file);
+ if (err < 0) {
+ err = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not remove file [%s]: [%d]: %s\n",
+ tmp_file, err, strerror(err));
+ }
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/* Save domain names, do not descend. */
+errno_t get_dom_names(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *start_dom,
+ char ***_dom_names,
+ int *_dom_names_count)
+{
+ struct sss_domain_info *dom;
+ TALLOC_CTX *tmp_ctx;
+ char **dom_names;
+ size_t count, i;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* get count of domains*/
+ count = 0;
+ dom = start_dom;
+ while (dom) {
+ count++;
+ dom = get_next_domain(dom, 0);
+ }
+
+ dom_names = talloc_array(tmp_ctx, char*, count);
+ if (dom_names == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* copy names */
+ i = 0;
+ dom = start_dom;
+ while (dom) {
+ dom_names[i] = talloc_strdup(dom_names, dom->name);
+ if (dom_names[i] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ dom = get_next_domain(dom, 0);
+ i++;
+ }
+
+ if (_dom_names != NULL ) {
+ *_dom_names = talloc_steal(mem_ctx, dom_names);
+ }
+
+ if (_dom_names_count != NULL ) {
+ *_dom_names_count = count;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+char *get_hidden_tmp_path(TALLOC_CTX *mem_ctx, const char *path)
+{
+ const char *s;
+
+ if (path == NULL) {
+ return NULL;
+ }
+
+ s = strrchr(path, '/');
+ if (s == NULL) {
+ /* No path, just file name */
+ return talloc_asprintf(mem_ctx, ".%sXXXXXX", path);
+ } else if ( *(s + 1) == '\0') {
+ /* '/' is the last character, there is no filename */
+ DEBUG(SSSDBG_OP_FAILURE, "Missing file name in [%s].\n", path);
+ return NULL;
+ }
+
+ return talloc_asprintf(mem_ctx, "%.*s.%sXXXXXX", (int)(s - path + 1),
+ path, s+1);
+}
+
+static errno_t sss_write_krb5_snippet_common(const char *file_name,
+ const char *content)
+{
+ int ret;
+ errno_t err;
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *tmp_file = NULL;
+ int fd = -1;
+ mode_t old_mode;
+ ssize_t written;
+ size_t size;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ tmp_file = get_hidden_tmp_path(tmp_ctx, file_name);
+ if (tmp_file == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ old_mode = umask(SSS_DFL_UMASK);
+ fd = mkstemp(tmp_file);
+ umask(old_mode);
+ if (fd < 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "creating the temp file [%s] for "
+ "krb5 config snippet failed.\n", tmp_file);
+ ret = EIO;
+ talloc_zfree(tmp_ctx);
+ goto done;
+ }
+
+ size = strlen(content);
+ written = sss_atomic_write_s(fd, discard_const(content), size);
+ close(fd);
+ if (written == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "write failed [%d][%s]\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (written != size) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Wrote %zd bytes expected %zu\n", written, size);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = chmod(tmp_file, 0644);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "chmod failed [%d][%s].\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = rename(tmp_file, file_name);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "rename failed [%d][%s].\n", ret, sss_strerror(ret));
+ goto done;
+ }
+ tmp_file = NULL;
+
+done:
+ if (tmp_file != NULL) {
+ err = unlink(tmp_file);
+ if (err == -1) {
+ err = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not remove file [%s]: [%d]: %s\n",
+ tmp_file, err, sss_strerror(err));
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+#define LOCALAUTH_PLUGIN_CONFIG \
+"[plugins]\n" \
+" localauth = {\n" \
+" module = sssd:"APP_MODULES_PATH"/sssd_krb5_localauth_plugin.so\n" \
+" }\n"
+
+static errno_t sss_write_krb5_localauth_snippet(const char *path)
+{
+#ifdef HAVE_KRB5_LOCALAUTH_PLUGIN
+ int ret;
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char *file_name;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ file_name = talloc_asprintf(tmp_ctx, "%s/localauth_plugin", path);
+ if (file_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_FUNC_DATA, "File for localauth plugin configuration is [%s]\n",
+ file_name);
+
+ ret = sss_write_krb5_snippet_common(file_name, LOCALAUTH_PLUGIN_CONFIG);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_write_krb5_snippet_common failed.\n");
+ goto done;
+ }
+
+done:
+
+ talloc_free(tmp_ctx);
+ return ret;
+
+#else
+ DEBUG(SSSDBG_TRACE_ALL, "Kerberos localauth plugin not available.\n");
+ return EOK;
+#endif
+}
+
+static errno_t sss_write_krb5_libdefaults_snippet(const char *path,
+ bool canonicalize,
+ bool udp_limit)
+{
+ int ret;
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char *file_name;
+ char *file_contents;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ file_name = talloc_asprintf(tmp_ctx, "%s/krb5_libdefaults", path);
+ if (file_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_FUNC_DATA, "File for KRB5 libdefaults configuration is [%s]\n",
+ file_name);
+
+ file_contents = talloc_strdup(tmp_ctx, "[libdefaults]\n");
+ if (file_contents == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "talloc_asprintf failed while creating the content\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (canonicalize == true) {
+ file_contents = talloc_asprintf_append(file_contents,
+ " canonicalize = true\n");
+ if (file_contents == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "talloc_asprintf failed while appending to the content\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (udp_limit == true) {
+ file_contents = talloc_asprintf_append(file_contents,
+ " udp_preference_limit = 0\n");
+ if (file_contents == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "talloc_asprintf failed while appending to the content\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = sss_write_krb5_snippet_common(file_name, file_contents);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_write_krb5_snippet_common failed.\n");
+ goto done;
+ }
+
+done:
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sss_write_krb5_conf_snippet(const char *path, bool canonicalize,
+ bool udp_limit)
+{
+ errno_t ret;
+ errno_t err;
+
+ if (path != NULL && (*path == '\0' || strcasecmp(path, "none") == 0)) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Empty path, nothing to do.\n");
+ return EOK;
+ }
+
+ if (path == NULL || *path != '/') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid or missing path [%s]-\n",
+ path == NULL ? "missing" : path);
+ return EINVAL;
+ }
+
+ ret = sss_write_krb5_localauth_snippet(path);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_write_krb5_localauth_snippet failed.\n");
+ goto done;
+ }
+
+ ret = sss_write_krb5_libdefaults_snippet(path, canonicalize, udp_limit);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_write_krb5_libdefaults_snippet failed.\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ err = sss_krb5_touch_config();
+ if (err != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to change last modification time "
+ "of krb5.conf. Created mappings may not be loaded.\n");
+ /* Ignore */
+ }
+
+ return ret;
+}
+
+static const char *domain_state_str(struct sss_domain_info *dom)
+{
+ switch (dom->state) {
+ case DOM_ACTIVE:
+ return "Active";
+ case DOM_DISABLED:
+ return "Disabled";
+ case DOM_INACTIVE:
+ return "Inactive";
+ case DOM_INCONSISTENT:
+ return "Inconsistent";
+ }
+ return "Unknown";
+}
+
+enum sss_domain_state sss_domain_get_state(struct sss_domain_info *dom)
+{
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Domain %s is %s\n", dom->name, domain_state_str(dom));
+ return dom->state;
+}
+
+void sss_domain_set_state(struct sss_domain_info *dom,
+ enum sss_domain_state state)
+{
+ dom->state = state;
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Domain %s is %s\n", dom->name, domain_state_str(dom));
+}
+
+#ifdef BUILD_FILES_PROVIDER
+bool sss_domain_fallback_to_nss(struct sss_domain_info *dom)
+{
+ return dom->fallback_to_nss;
+}
+#endif
+
+bool sss_domain_is_forest_root(struct sss_domain_info *dom)
+{
+ return (dom->forest_root == dom);
+}
+
+char *subdomain_create_conf_path_from_str(TALLOC_CTX *mem_ctx,
+ const char *parent_name,
+ const char *subdom_name)
+{
+ return talloc_asprintf(mem_ctx, CONFDB_DOMAIN_PATH_TMPL "/%s",
+ parent_name, subdom_name);
+}
+
+char *subdomain_create_conf_path(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *subdomain)
+{
+ if (!IS_SUBDOMAIN(subdomain)) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "The domain \"%s\" is not a subdomain.\n",
+ subdomain->name);
+ return NULL;
+ }
+
+ return subdomain_create_conf_path_from_str(mem_ctx,
+ subdomain->parent->name,
+ subdomain->name);
+}
+
+const char *sss_domain_type_str(struct sss_domain_info *dom)
+{
+ if (dom == NULL) {
+ return "BUG: Invalid domain";
+ }
+ switch (dom->type) {
+ case DOM_TYPE_POSIX:
+ return "POSIX";
+ case DOM_TYPE_APPLICATION:
+ return "Application";
+ }
+ return "Unknown";
+}
+
+void sss_domain_info_set_output_fqnames(struct sss_domain_info *domain,
+ bool output_fqnames)
+{
+ domain->output_fqnames = output_fqnames;
+}
+
+bool sss_domain_info_get_output_fqnames(struct sss_domain_info *domain)
+{
+ return domain->output_fqnames;
+}
+
+bool sss_domain_is_mpg(struct sss_domain_info *domain)
+{
+ return domain->mpg_mode == MPG_ENABLED;
+}
+
+bool sss_domain_is_hybrid(struct sss_domain_info *domain)
+{
+ return domain->mpg_mode == MPG_HYBRID;
+}
+
+enum sss_domain_mpg_mode get_domain_mpg_mode(struct sss_domain_info *domain)
+{
+ return domain->mpg_mode;
+}
+
+const char *str_domain_mpg_mode(enum sss_domain_mpg_mode mpg_mode)
+{
+ switch (mpg_mode) {
+ case MPG_ENABLED:
+ return "true";
+ case MPG_DISABLED:
+ return "false";
+ case MPG_HYBRID:
+ return "hybrid";
+ case MPG_DEFAULT:
+ return "default";
+ }
+
+ return NULL;
+}
+
+enum sss_domain_mpg_mode str_to_domain_mpg_mode(const char *str_mpg_mode)
+{
+ if (strcasecmp(str_mpg_mode, "FALSE") == 0) {
+ return MPG_DISABLED;
+ } else if (strcasecmp(str_mpg_mode, "TRUE") == 0) {
+ return MPG_ENABLED;
+ } else if (strcasecmp(str_mpg_mode, "HYBRID") == 0) {
+ return MPG_HYBRID;
+ } else if (strcasecmp(str_mpg_mode, "DEFAULT") == 0) {
+ return MPG_DEFAULT;
+ }
+
+
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Invalid value for %s\n, assuming disabled",
+ SYSDB_SUBDOMAIN_MPG);
+ return MPG_DISABLED;
+}
diff --git a/src/util/file_watch.c b/src/util/file_watch.c
new file mode 100644
index 0000000..d19fdcc
--- /dev/null
+++ b/src/util/file_watch.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2022, Red Hat Inc.
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include "util/inotify.h"
+#include "util/util.h"
+#include "util/file_watch.h"
+
+
+
+#define MISSING_FILE_POLL_TIME 10 /* seconds */
+#define FILE_WATCH_POLL_INTERVAL 5 /* seconds */
+
+
+struct file_watch_ctx {
+ struct tevent_context *ev;
+ const char *filename;
+ bool use_inotify;
+
+ struct config_file_inotify_check {
+ struct snotify_ctx *snctx;
+ } inotify_check;
+
+ struct config_file_poll_check {
+ struct tevent_timer *timer;
+ time_t modified;
+ } poll_check;
+
+ fw_callback cb;
+ void *cb_arg;
+};
+
+
+static void poll_watched_file(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t, void *ptr);
+
+static errno_t create_poll_timer(struct file_watch_ctx *fw_ctx)
+{
+ struct timeval tv;
+
+ tv = tevent_timeval_current_ofs(FILE_WATCH_POLL_INTERVAL, 0);
+
+ fw_ctx->poll_check.timer = tevent_add_timer(fw_ctx->ev,
+ fw_ctx,
+ tv,
+ poll_watched_file,
+ fw_ctx);
+ if (!fw_ctx->poll_check.timer) {
+ return EIO;
+ }
+
+ return EOK;
+}
+
+static void poll_watched_file(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ int ret, err;
+ struct stat file_stat;
+ struct file_watch_ctx *fw_ctx;
+
+ fw_ctx = talloc_get_type(ptr, struct file_watch_ctx);
+
+ ret = stat(fw_ctx->filename, &file_stat);
+ if (ret < 0) {
+ err = errno;
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Could not stat file [%s]. Error [%d:%s]\n",
+ fw_ctx->filename, err, strerror(err));
+ return;
+ }
+
+ if (file_stat.st_mtime != fw_ctx->poll_check.modified) {
+ /* Parse the file and invoke the callback */
+ /* Note: this will fire if the modification time changes into the past
+ * as well as the future.
+ */
+ DEBUG(SSSDBG_TRACE_INTERNAL, "File [%s] changed\n", fw_ctx->filename);
+ fw_ctx->poll_check.modified = file_stat.st_mtime;
+
+ /* Tell the caller the file changed */
+ fw_ctx->cb(fw_ctx->filename, fw_ctx->cb_arg);
+ }
+
+ ret = create_poll_timer(fw_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Error: File [%s] no longer monitored for changes!\n",
+ fw_ctx->filename);
+ }
+}
+
+
+static int watched_file_inotify_cb(const char *filename,
+ uint32_t flags,
+ void *pvt)
+{
+ static char received[PATH_MAX + 1];
+ static char expected[PATH_MAX + 1];
+ struct file_watch_ctx *fw_ctx;
+ char *res;
+
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Received inotify notification for %s\n", filename);
+
+ fw_ctx = talloc_get_type(pvt, struct file_watch_ctx);
+ if (fw_ctx == NULL) {
+ return EINVAL;
+ }
+
+ res = realpath(fw_ctx->filename, expected);
+ if (res == NULL) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Normalization failed for expected %s. Skipping the callback.\n",
+ fw_ctx->filename);
+ goto done;
+ }
+
+ res = realpath(filename, received);
+ if (res == NULL) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Normalization failed for received %s. Skipping the callback.\n",
+ filename);
+ goto done;
+ }
+
+ if (strcmp(expected, received) == 0) {
+ if (access(received, F_OK) == 0) {
+ fw_ctx->cb(received, fw_ctx->cb_arg);
+ } else {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "File %s is missing. Skipping the callback.\n", filename);
+ }
+ }
+
+done:
+ return EOK;
+}
+
+
+static int try_inotify(struct file_watch_ctx *fw_ctx)
+{
+#ifdef HAVE_INOTIFY
+ struct snotify_ctx *snctx;
+ /* We will queue the file for update in one second.
+ * This way, if there is a script writing to the file
+ * repeatedly, we won't be attempting to update multiple
+ * times.
+ */
+ struct timeval delay = { .tv_sec = 1, .tv_usec = 0 };
+
+ snctx = snotify_create(fw_ctx, fw_ctx->ev, SNOTIFY_WATCH_DIR,
+ fw_ctx->filename, &delay,
+ IN_DELETE_SELF | IN_CLOSE_WRITE | IN_MOVE_SELF | \
+ IN_CREATE | IN_MOVED_TO | IN_IGNORED,
+ watched_file_inotify_cb, fw_ctx);
+ if (snctx == NULL) {
+ return EIO;
+ }
+
+ return EOK;
+#else
+ return EINVAL;
+#endif /* HAVE_INOTIFY */
+}
+
+static errno_t fw_watch_file_poll(struct file_watch_ctx *fw_ctx)
+{
+ struct stat file_stat;
+ int ret, err;
+
+ ret = stat(fw_ctx->filename, &file_stat);
+ if (ret < 0) {
+ err = errno;
+ if (err == ENOENT) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "File [%s] is missing. Will try again later.\n",
+ fw_ctx->filename);
+ } else {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Could not stat file [%s]. Error [%d:%s]\n",
+ fw_ctx->filename, err, strerror(err));
+ }
+ return err;
+ }
+
+ fw_ctx->poll_check.modified = file_stat.st_mtime;
+
+ if(!fw_ctx->poll_check.timer) {
+ ret = create_poll_timer(fw_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Cannot create poll timer\n");
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+
+static int watch_file(struct file_watch_ctx *fw_ctx)
+{
+ int ret = EOK;
+ bool use_inotify;
+
+ use_inotify = fw_ctx->use_inotify;
+ if (use_inotify) {
+ ret = try_inotify(fw_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Falling back to polling\n");
+ use_inotify = false;
+ }
+ }
+
+ if (!use_inotify) {
+ ret = fw_watch_file_poll(fw_ctx);
+ }
+
+ return ret;
+}
+
+
+static void set_file_watching(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *data)
+{
+ int ret;
+ struct file_watch_ctx *fw_ctx = talloc_get_type(data, struct file_watch_ctx);
+
+ ret = watch_file(fw_ctx);
+ if (ret == EOK) {
+ if (access(fw_ctx->filename, F_OK) == 0) {
+ fw_ctx->cb(fw_ctx->filename, fw_ctx->cb_arg);
+ }
+ } else if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "%s missing. Waiting for it to appear.\n",
+ fw_ctx->filename);
+ tv = tevent_timeval_current_ofs(MISSING_FILE_POLL_TIME, 0);
+ te = tevent_add_timer(fw_ctx->ev, fw_ctx, tv, set_file_watching, fw_ctx);
+ if (te == NULL) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "tevent_add_timer failed. %s will be ignored.\n",
+ fw_ctx->filename);
+ }
+ } else {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "watch_file failed. %s will be ignored: [%i] %s\n",
+ fw_ctx->filename, ret, sss_strerror(ret));
+ }
+}
+
+
+struct file_watch_ctx *fw_watch_file(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *filename,
+ bool use_inotify,
+ fw_callback cb,
+ void *cb_arg)
+{
+ int ret;
+ struct timeval tv;
+ struct file_watch_ctx *fw_ctx;
+
+ if (ev == NULL || filename == NULL || cb == NULL) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Invalid parameter\n");
+ return NULL;
+ }
+
+ fw_ctx = talloc_zero(mem_ctx, struct file_watch_ctx);
+ if (fw_ctx == NULL) {
+ DEBUG(SSSDBG_IMPORTANT_INFO, "Failed to allocate the context\n");
+ return NULL;
+ }
+
+ fw_ctx->ev = ev;
+ fw_ctx->use_inotify = use_inotify;
+ fw_ctx->cb = cb;
+ fw_ctx->cb_arg = cb_arg;
+ fw_ctx->filename = talloc_strdup(fw_ctx, filename);
+ if (fw_ctx->filename == NULL) {
+ DEBUG(SSSDBG_IMPORTANT_INFO, "talloc_strdup() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Watch for changes to the requested file, and retry periodically
+ * if the file does not exist */
+ tv = tevent_timeval_current_ofs(0, 0); // Not actually used
+ set_file_watching(fw_ctx->ev, NULL, tv, fw_ctx);
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(fw_ctx);
+ fw_ctx = NULL;
+ }
+
+ return fw_ctx;
+}
diff --git a/src/util/file_watch.h b/src/util/file_watch.h
new file mode 100644
index 0000000..7f81852
--- /dev/null
+++ b/src/util/file_watch.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 Red Hat Inc.
+ *
+ * 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 _FILE_WATCH_H
+#define _FILE_WATCH_H
+
+#include <stdbool.h>
+#include <talloc.h>
+#include <tevent.h>
+
+
+typedef void (*fw_callback)(const char *filename, void *arg);
+struct file_watch_ctx;
+
+/*
+ * This function configures the watching of a file. When the file is created
+ * or modified, the provided callback function is invoked.
+ *
+ * If the file exists at the moment this function is called, the callback
+ * will be invoked once. A first processing of the file can be done in that
+ * case. If the file does not exist at that moment, the callback will be
+ * invoked when the file is created.
+ *
+ * inotify will be used to watch the file unless 'use_notify' is set to 'false'
+ * in sssd.conf or inotify fails (not installed). In those two cases, the
+ * file state will be polled every 10 seconds when the file doesn't exist
+ * to detect its creation, and every 5 seconds when the file exists to detect
+ * changes.
+ */
+struct file_watch_ctx *fw_watch_file(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *filename,
+ bool use_inotify,
+ fw_callback cb,
+ void *cb_arg);
+
+
+
+#endif /* _FILE_WATCH_H */
diff --git a/src/util/files.c b/src/util/files.c
new file mode 100644
index 0000000..5b7fbc8
--- /dev/null
+++ b/src/util/files.c
@@ -0,0 +1,892 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@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/>.
+*/
+
+/*
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (c) 1991 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2001, Marek Michałkiewicz
+ * Copyright (c) 2003 - 2006, Tomasz Kłoczko
+ * Copyright (c) 2007 - 2008, Nicolas François
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <talloc.h>
+
+#include "util/util.h"
+
+struct copy_ctx {
+ const char *src_orig;
+ const char *dst_orig;
+ dev_t src_dev;
+ uid_t uid;
+ gid_t gid;
+};
+
+static int sss_timeat_set(int dir_fd, const char *path,
+ const struct stat *statp,
+ int flags)
+{
+ int ret;
+
+#ifdef HAVE_UTIMENSAT
+ struct timespec timebuf[2];
+
+ timebuf[0] = statp->st_atim;
+ timebuf[1] = statp->st_mtim;
+
+ ret = utimensat(dir_fd, path, timebuf, flags);
+#else
+ struct timeval tv[2];
+
+ tv[0].tv_sec = statp->st_atime;
+ tv[0].tv_usec = 0;
+ tv[1].tv_sec = statp->st_mtime;
+ tv[1].tv_usec = 0;
+
+ ret = futimesat(dir_fd, path, tv);
+#endif
+ if (ret == -1) {
+ return errno;
+ }
+
+ return EOK;
+}
+
+static int sss_futime_set(int fd, const struct stat *statp)
+{
+ int ret;
+
+#ifdef HAVE_FUTIMENS
+ struct timespec timebuf[2];
+
+ timebuf[0] = statp->st_atim;
+ timebuf[1] = statp->st_mtim;
+ ret = futimens(fd, timebuf);
+#else
+ struct timeval tv[2];
+
+ tv[0].tv_sec = statp->st_atime;
+ tv[0].tv_usec = 0;
+ tv[1].tv_sec = statp->st_mtime;
+ tv[1].tv_usec = 0;
+
+ ret = futimes(fd, tv);
+#endif
+ if (ret == -1) {
+ return errno;
+ }
+
+ return EOK;
+}
+
+/* wrapper in order not to create a temporary context in
+ * every iteration */
+static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
+ int parent_fd,
+ const char *dir_name,
+ dev_t parent_dev,
+ bool keep_root_dir);
+
+int sss_remove_tree(const char *root)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = remove_tree_with_ctx(tmp_ctx, AT_FDCWD, root, 0, false);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sss_remove_subtree(const char *root)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = remove_tree_with_ctx(tmp_ctx, AT_FDCWD, root, 0, true);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/*
+ * The context is not freed in case of error
+ * because this is a recursive function, will be freed when we
+ * reach the top level remove_tree() again
+ */
+static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
+ int parent_fd,
+ const char *dir_name,
+ dev_t parent_dev,
+ bool keep_root_dir)
+{
+ struct dirent *result;
+ struct stat statres;
+ DIR *rootdir = NULL;
+ int ret, err;
+ int dir_fd;
+ int log_level;
+
+ dir_fd = sss_openat_cloexec(parent_fd, dir_name,
+ O_RDONLY | O_DIRECTORY | O_NOFOLLOW, &ret);
+ if (dir_fd == -1) {
+ ret = errno;
+ if (ret == ENOENT) {
+ log_level = SSSDBG_TRACE_FUNC;
+ } else {
+ log_level = SSSDBG_MINOR_FAILURE;
+ }
+ DEBUG(log_level, "Cannot open %s: [%d]: %s\n",
+ dir_name, ret, strerror(ret));
+ return ret;
+ }
+
+ rootdir = fdopendir(dir_fd);
+ if (rootdir == NULL) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot open directory: [%d][%s]\n", ret, strerror(ret));
+ close(dir_fd);
+ goto fail;
+ }
+
+ while ((result = readdir(rootdir)) != NULL) {
+ if (strcmp(result->d_name, ".") == 0 ||
+ strcmp(result->d_name, "..") == 0) {
+ continue;
+ }
+
+ ret = fstatat(dir_fd, result->d_name,
+ &statres, AT_SYMLINK_NOFOLLOW);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "stat failed: [%d][%s]\n", ret, strerror(ret));
+ goto fail;
+ }
+
+ if (S_ISDIR(statres.st_mode)) {
+ /* if directory, recursively descend, but check if on the same FS */
+ if (parent_dev && parent_dev != statres.st_dev) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Directory %s is on different filesystem, "
+ "will not follow\n", result->d_name);
+ ret = EFAULT;
+ goto fail;
+ }
+
+ ret = remove_tree_with_ctx(mem_ctx, dir_fd, result->d_name,
+ statres.st_dev, false);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Removing subdirectory failed: [%d][%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+ } else {
+ ret = unlinkat(dir_fd, result->d_name, 0);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Removing file failed '%s': [%d][%s]\n",
+ result->d_name, ret, strerror(ret));
+ goto fail;
+ }
+ }
+ }
+
+ ret = closedir(rootdir);
+ rootdir = NULL;
+ if (ret != 0) {
+ ret = errno;
+ goto fail;
+ }
+
+ if (!keep_root_dir) {
+ /* Remove also root directory. */
+ ret = unlinkat(parent_fd, dir_name, AT_REMOVEDIR);
+ if (ret == -1) {
+ ret = errno;
+ }
+ }
+
+ ret = EOK;
+fail:
+ if (rootdir) { /* clean up on abnormal exit but retain return code */
+ err = closedir(rootdir);
+ if (err) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "closedir failed, bad dirp?\n");
+ }
+ }
+ return ret;
+}
+
+static char *talloc_readlinkat(TALLOC_CTX *mem_ctx, int dir_fd,
+ const char *filename)
+{
+ size_t size = 1024;
+ ssize_t nchars;
+ char *buffer;
+ char *new_buffer;
+
+ buffer = talloc_array(mem_ctx, char, size);
+ if (!buffer) {
+ return NULL;
+ }
+
+ while (1) {
+ nchars = readlinkat(dir_fd, filename, buffer, size);
+ if (nchars < 0) {
+ talloc_free(buffer);
+ return NULL;
+ }
+
+ if ((size_t) nchars < size) {
+ /* The buffer was large enough */
+ break;
+ }
+
+ /* Try again with a bigger buffer */
+ size *= 2;
+ new_buffer = talloc_realloc(mem_ctx, buffer, char, size);
+ if (!new_buffer) {
+ talloc_free(buffer);
+ return NULL;
+ }
+ buffer = new_buffer;
+ }
+
+ /* readlink does not nul-terminate */
+ buffer[nchars] = '\0';
+ return buffer;
+}
+
+static int
+copy_symlink(int src_dir_fd,
+ int dst_dir_fd,
+ const char *file_name,
+ const char *full_path,
+ const struct stat *statp,
+ uid_t uid, gid_t gid)
+{
+ char *buf;
+ errno_t ret;
+
+ buf = talloc_readlinkat(NULL, src_dir_fd, file_name);
+ if (!buf) {
+ return ENOMEM;
+ }
+
+ ret = selinux_file_context(full_path);
+ if (ret != 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to set SELinux context for [%s]\n", full_path);
+ /* Not fatal */
+ }
+
+ ret = symlinkat(buf, dst_dir_fd, file_name);
+ talloc_free(buf);
+ if (ret == -1) {
+ ret = errno;
+ if (ret == EEXIST) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "symlink pointing to already exists at '%s'\n", full_path);
+ return EOK;
+ }
+
+ DEBUG(SSSDBG_CRIT_FAILURE, "symlinkat failed: %s\n", strerror(ret));
+ return ret;
+ }
+
+ ret = fchownat(dst_dir_fd, file_name,
+ uid, gid, AT_SYMLINK_NOFOLLOW);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "fchownat failed: %s\n", strerror(ret));
+ return ret;
+ }
+
+ ret = sss_timeat_set(dst_dir_fd, file_name, statp,
+ AT_SYMLINK_NOFOLLOW);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "utimensat failed [%d]: %s\n",
+ ret, strerror(ret));
+ /* Do not fail */
+ }
+
+ return EOK;
+}
+
+static int
+copy_file_contents(int ifd,
+ int ofd,
+ mode_t mode,
+ uid_t uid, gid_t gid)
+{
+ errno_t ret;
+ char buf[1024];
+ ssize_t cnt, written;
+
+ while ((cnt = sss_atomic_read_s(ifd, buf, sizeof(buf))) != 0) {
+ if (cnt == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot read() from source file: [%d][%s].\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ errno = 0;
+ written = sss_atomic_write_s(ofd, buf, cnt);
+ if (written == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot write() to destination file: [%d][%s].\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ if (written != cnt) {
+ ret = EINVAL;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Wrote %zd bytes, expected %zd\n", written, cnt);
+ goto done;
+ }
+ }
+
+ /* Set the ownership; permissions are still
+ * restrictive. */
+ ret = fchown(ofd, uid, gid);
+ if (ret == -1 && errno != EPERM) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Error changing owner: %s\n",
+ strerror(ret));
+ goto done;
+ }
+
+ /* Set the desired mode. */
+ ret = fchmod(ofd, mode);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE, "Error changing mode: %s\n",
+ strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+
+/* Copy bytes from input file descriptor ifd into file named
+ * dst_named under directory with dest_dir_fd. Own the new file
+ * by uid/gid
+ */
+static int
+copy_file(int ifd,
+ int dest_dir_fd,
+ const char *file_name,
+ const char *full_path,
+ const struct stat *statp,
+ uid_t uid, gid_t gid)
+{
+ int ofd = -1;
+ errno_t ret;
+
+ ret = selinux_file_context(full_path);
+ if (ret != 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to set SELinux context for [%s]\n", full_path);
+ /* Not fatal */
+ }
+
+ /* Start with absolutely restrictive permissions */
+ ofd = openat(dest_dir_fd, file_name,
+ O_EXCL | O_CREAT | O_WRONLY | O_NOFOLLOW,
+ 0);
+ if (ofd < 0 && errno != EEXIST) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot open() destination file '%s': [%d][%s].\n",
+ full_path, ret, strerror(ret));
+ goto done;
+ }
+
+ ret = copy_file_contents(ifd, ofd, statp->st_mode, uid, gid);
+ if (ret != EOK) goto done;
+
+
+ ret = sss_futime_set(ofd, statp);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "sss_futime_set failed [%d]: %s\n",
+ ret, strerror(ret));
+ /* Do not fail */
+ }
+ ret = EOK;
+
+done:
+ if (ofd != -1) close(ofd);
+ return ret;
+}
+
+int
+sss_copy_file_secure(const char *src,
+ const char *dest,
+ mode_t mode,
+ uid_t uid, gid_t gid,
+ bool force)
+{
+ int ifd = -1;
+ int ofd = -1;
+ int dest_flags = 0;
+ errno_t ret;
+
+ ret = selinux_file_context(dest);
+ if (ret != 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to set SELinux context for [%s]\n", dest);
+ /* Not fatal */
+ }
+
+ /* Start with absolutely restrictive permissions */
+ dest_flags = O_CREAT | O_WRONLY | O_NOFOLLOW;
+ if (!force) {
+ dest_flags |= O_EXCL;
+ }
+
+ ofd = open(dest, dest_flags, mode);
+ if (ofd < 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot open() destination file '%s': [%d][%s].\n",
+ dest, errno, strerror(errno));
+ goto done;
+ }
+
+ ifd = sss_open_cloexec(src, O_RDONLY | O_NOFOLLOW, &ret);
+ if (ifd < 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot open() source file '%s': [%d][%s].\n",
+ src, ret, strerror(ret));
+ goto done;
+ }
+
+ ret = copy_file_contents(ifd, ofd, mode, uid, gid);
+
+done:
+ if (ifd != -1) close(ifd);
+ if (ofd != -1) close(ofd);
+ return ret;
+}
+
+static errno_t
+copy_dir(struct copy_ctx *cctx,
+ int src_dir_fd, const char *src_dir_path,
+ int dest_parent_fd, const char *dest_dir_name,
+ const char *dest_dir_path,
+ mode_t mode,
+ const struct stat *src_dir_stat);
+
+static errno_t
+copy_entry(struct copy_ctx *cctx,
+ int src_dir_fd,
+ const char *src_dir_path,
+ int dest_dir_fd,
+ const char *dest_dir_path,
+ const char *ent_name)
+{
+ char *src_ent_path = NULL;
+ char *dest_ent_path = NULL;
+ int ifd = -1;
+ errno_t ret;
+ struct stat st;
+
+ /* Build the path of the source file or directory and its
+ * corresponding member in the new tree. */
+ src_ent_path = talloc_asprintf(cctx, "%s/%s", src_dir_path, ent_name);
+ dest_ent_path = talloc_asprintf(cctx, "%s/%s", dest_dir_path, ent_name);
+ if (!src_ent_path || !dest_ent_path) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Open the input entry first, then we can fstat() it and be
+ * certain that it is still the same file. O_NONBLOCK protects
+ * us against FIFOs and perhaps side-effects of the open() of a
+ * device file if there ever was one here, and doesn't matter
+ * for regular files or directories. */
+ ifd = sss_openat_cloexec(src_dir_fd, ent_name,
+ O_RDONLY | O_NOFOLLOW | O_NONBLOCK, &ret);
+ if (ifd == -1 && ret != ELOOP) {
+ /* openat error */
+ DEBUG(SSSDBG_CRIT_FAILURE, "openat failed on '%s': %s\n",
+ src_ent_path, strerror(ret));
+ goto done;
+ } else if (ifd == -1 && ret == ELOOP) {
+ /* Should be a symlink.. */
+ ret = fstatat(src_dir_fd, ent_name, &st, AT_SYMLINK_NOFOLLOW);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "fstatat failed on '%s': %s\n",
+ src_ent_path, strerror(ret));
+ goto done;
+ }
+
+ /* Handle symlinks */
+ ret = copy_symlink(src_dir_fd, dest_dir_fd, ent_name,
+ dest_ent_path, &st, cctx->uid, cctx->gid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot copy '%s' to '%s'\n",
+ src_ent_path, dest_ent_path);
+ }
+ goto done;
+ }
+
+ ret = fstat(ifd, &st);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "couldn't stat '%s': %s\n", src_ent_path, strerror(ret));
+ goto done;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ /* If it's a directory, descend into it. */
+ ret = copy_dir(cctx, ifd, src_ent_path,
+ dest_dir_fd, ent_name,
+ dest_ent_path, st.st_mode & 07777,
+ &st);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Couldn't recursively copy '%s' to '%s': %s\n",
+ src_ent_path, dest_ent_path, strerror(ret));
+ goto done;
+ }
+ } else if (S_ISREG(st.st_mode)) {
+ /* Copy a regular file */
+ ret = copy_file(ifd, dest_dir_fd, ent_name, dest_ent_path,
+ &st, cctx->uid, cctx->gid);
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot copy '%s' to '%s'\n",
+ src_ent_path, dest_ent_path);
+ goto done;
+ }
+ } else {
+ /* Is a special file */
+ DEBUG(SSSDBG_FUNC_DATA, "'%s' is a special file, skipping.\n",
+ src_ent_path);
+ }
+
+ ret = EOK;
+done:
+ talloc_free(src_ent_path);
+ talloc_free(dest_ent_path);
+ if (ifd != -1) close(ifd);
+ return ret;
+}
+
+static errno_t
+copy_dir(struct copy_ctx *cctx,
+ int src_dir_fd, const char *src_dir_path,
+ int dest_parent_fd, const char *dest_dir_name,
+ const char *dest_dir_path,
+ mode_t mode,
+ const struct stat *src_dir_stat)
+{
+ errno_t ret;
+ errno_t dret;
+ int dest_dir_fd = -1;
+ DIR *dir = NULL;
+ struct dirent *ent;
+
+ if (!dest_dir_path) {
+ return EINVAL;
+ }
+
+ dir = fdopendir(src_dir_fd);
+ if (dir == NULL) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Error reading '%s': %s\n", src_dir_path, strerror(ret));
+ goto done;
+ }
+
+ /* Create the directory. It starts owned by us (presumably root), with
+ * fairly restrictive permissions that still allow us to use the
+ * directory.
+ * */
+ errno = 0;
+ ret = mkdirat(dest_parent_fd, dest_dir_name, S_IRWXU);
+ if (ret == -1 && errno != EEXIST) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Error reading '%s': %s\n", dest_dir_path, strerror(ret));
+ goto done;
+ }
+
+ dest_dir_fd = sss_openat_cloexec(dest_parent_fd, dest_dir_name,
+ O_RDONLY | O_DIRECTORY | O_NOFOLLOW, &ret);
+ if (dest_dir_fd == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Error opening '%s': %s\n", dest_dir_path, strerror(ret));
+ goto done;
+ }
+
+ while ((ent = readdir(dir)) != NULL) {
+ /* Iterate through each item in the directory. */
+ /* Skip over self and parent hard links. */
+ if (strcmp(ent->d_name, ".") == 0 ||
+ strcmp(ent->d_name, "..") == 0) {
+ continue;
+ }
+
+ ret = copy_entry(cctx,
+ src_dir_fd, src_dir_path,
+ dest_dir_fd, dest_dir_path,
+ ent->d_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not copy [%s] to [%s]\n",
+ src_dir_path, dest_dir_path);
+ goto done;
+ }
+ }
+
+ /* Set the ownership on the directory. Permissions are still
+ * fairly restrictive. */
+ ret = fchown(dest_dir_fd, cctx->uid, cctx->gid);
+ if (ret == -1 && errno != EPERM) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Error changing owner of '%s': %s\n",
+ dest_dir_path, strerror(ret));
+ goto done;
+ }
+
+ /* Set the desired mode. Do this explicitly to preserve S_ISGID and
+ * other bits. Do this after chown, because chown is permitted to
+ * reset these bits. */
+ ret = fchmod(dest_dir_fd, mode);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Error setting mode of '%s': %s\n",
+ dest_dir_path, strerror(ret));
+ goto done;
+ }
+
+ sss_futime_set(dest_dir_fd, src_dir_stat);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "sss_futime_set failed [%d]: %s\n",
+ ret, strerror(ret));
+ /* Do not fail */
+ }
+
+ ret = EOK;
+done:
+ if (dir) {
+ dret = closedir(dir);
+ if (dret != 0) {
+ dret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to close directory: %s.\n", strerror(dret));
+ }
+ }
+
+ if (dest_dir_fd != -1) {
+ close(dest_dir_fd);
+ }
+ return ret;
+}
+
+/* NOTE:
+ * For several reasons, including the fact that we copy even special files
+ * (pipes, etc) from the skeleton directory, the skeldir needs to be trusted
+ */
+int sss_copy_tree(const char *src_root,
+ const char *dst_root,
+ mode_t mode_root,
+ uid_t uid, gid_t gid)
+{
+ int ret = EOK;
+ struct copy_ctx *cctx = NULL;
+ int fd = -1;
+ struct stat s_src;
+
+ fd = sss_open_cloexec(src_root, O_RDONLY | O_DIRECTORY, &ret);
+ if (fd == -1) {
+ goto fail;
+ }
+
+ ret = fstat(fd, &s_src);
+ if (ret == -1) {
+ ret = errno;
+ goto fail;
+ }
+
+ cctx = talloc_zero(NULL, struct copy_ctx);
+ if (!cctx) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ cctx->src_orig = src_root;
+ cctx->dst_orig = dst_root;
+ cctx->src_dev = s_src.st_dev;
+ cctx->uid = uid;
+ cctx->gid = gid;
+
+ ret = copy_dir(cctx, fd, src_root, AT_FDCWD,
+ dst_root, dst_root, mode_root, &s_src);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "copy_dir failed: [%d][%s]\n", ret, strerror(ret));
+ goto fail;
+ }
+
+fail:
+ if (fd != -1) close(fd);
+ reset_selinux_file_context();
+ talloc_free(cctx);
+ return ret;
+}
+
+int sss_create_dir(const char *parent_dir_path,
+ const char *dir_name,
+ mode_t mode,
+ uid_t uid, gid_t gid)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *dir_path;
+ int ret = EOK;
+ int parent_dir_fd = -1;
+ int dir_fd = -1;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ parent_dir_fd = sss_open_cloexec(parent_dir_path, O_RDONLY | O_DIRECTORY,
+ &ret);
+ if (parent_dir_fd == -1) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Cannot open() directory '%s' [%d]: %s\n",
+ parent_dir_path, ret, sss_strerror(ret));
+ goto fail;
+ }
+
+ dir_path = talloc_asprintf(tmp_ctx, "%s/%s", parent_dir_path, dir_name);
+ if (dir_path == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ errno = 0;
+ ret = mkdirat(parent_dir_fd, dir_name, mode);
+ if (ret == -1) {
+ if (errno == EEXIST) {
+ ret = EOK;
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Directory '%s' already created!\n", dir_path);
+ } else {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Error reading '%s': %s\n", parent_dir_path, strerror(ret));
+ goto fail;
+ }
+ }
+
+ dir_fd = sss_open_cloexec(dir_path, O_RDONLY | O_DIRECTORY, &ret);
+ if (dir_fd == -1) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Cannot open() directory '%s' [%d]: %s\n",
+ dir_path, ret, sss_strerror(ret));
+ goto fail;
+ }
+
+ errno = 0;
+ ret = fchown(dir_fd, uid, gid);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to own the newly created directory '%s' [%d]: %s\n",
+ dir_path, ret, sss_strerror(ret));
+ goto fail;
+ }
+
+ ret = EOK;
+
+fail:
+ if (parent_dir_fd != -1) {
+ close(parent_dir_fd);
+ }
+ if (dir_fd != -1) {
+ close(dir_fd);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/util/find_uid.c b/src/util/find_uid.c
new file mode 100644
index 0000000..1b506df
--- /dev/null
+++ b/src/util/find_uid.c
@@ -0,0 +1,381 @@
+/*
+ SSSD
+
+ Create uid table
+
+ Authors:
+ Sumit Bose <sbose@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/>.
+*/
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <talloc.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <dhash.h>
+
+#include "util/find_uid.h"
+#include "util/util.h"
+#include "util/strtonum.h"
+
+#ifdef HAVE_SYSTEMD_LOGIN
+#include <systemd/sd-login.h>
+#endif
+
+#define PATHLEN (NAME_MAX + 14)
+#define BUFSIZE 4096
+
+static void *hash_talloc(const size_t size, void *pvt)
+{
+ return talloc_size(pvt, size);
+}
+
+static void hash_talloc_free(void *ptr, void *pvt)
+{
+ talloc_free(ptr);
+}
+
+static errno_t get_uid_from_pid(const pid_t pid, uid_t *uid, bool *is_systemd)
+{
+ int ret;
+ char path[PATHLEN];
+ struct stat stat_buf;
+ int fd;
+ char buf[BUFSIZE];
+ char *p;
+ char *e;
+ char *endptr;
+ uint32_t num=0;
+ errno_t error;
+
+ ret = snprintf(path, PATHLEN, "/proc/%d/status", pid);
+ if (ret < 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "snprintf failed\n");
+ return EINVAL;
+ } else if (ret >= PATHLEN) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "path too long?!?!\n");
+ return EINVAL;
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ error = errno;
+ if (error == ENOENT) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Proc file [%s] is not available anymore.\n",
+ path);
+ } else if (error == EPERM) {
+ /* case of hidepid=1 mount option for /proc */
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Proc file [%s] is not permissible.\n",
+ path);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "open failed [%s][%d][%s].\n", path, error, strerror(error));
+ }
+ return error;
+ }
+
+ ret = fstat(fd, &stat_buf);
+ if (ret == -1) {
+ error = errno;
+ if (error == ENOENT) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Proc file [%s] is not available anymore.\n",
+ path);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "fstat failed [%d][%s].\n", error, strerror(error));
+ }
+ goto fail_fd;
+ }
+
+ if (!S_ISREG(stat_buf.st_mode)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "not a regular file\n");
+ error = EINVAL;
+ goto fail_fd;
+ }
+
+ errno = 0;
+ ret = sss_atomic_read_s(fd, buf, BUFSIZE);
+ if (ret == -1) {
+ error = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "read failed [%d][%s].\n", error, strerror(error));
+ goto fail_fd;
+ }
+
+ /* Guarantee NULL-termination in case we read the full BUFSIZE somehow */
+ buf[BUFSIZE-1] = '\0';
+
+ ret = close(fd);
+ if (ret == -1) {
+ error = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "close failed [%d][%s].\n", error, strerror(error));
+ }
+
+ /* Get uid */
+ p = strstr(buf, "\nUid:\t");
+ if (p != NULL) {
+ p += 6;
+ e = strchr(p,'\t');
+ if (e == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "missing delimiter.\n");
+ return EINVAL;
+ } else {
+ *e = '\0';
+ }
+ num = (uint32_t) strtoint32(p, &endptr, 10);
+ error = errno;
+ if (error != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "strtol failed [%s].\n", strerror(error));
+ return error;
+ }
+ if (*endptr != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "uid contains extra characters\n");
+ return EINVAL;
+ }
+
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "format error\n");
+ return EINVAL;
+ }
+
+ /* Get process name. */
+ p = strstr(buf, "Name:\t");
+ if (p == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "format error\n");
+ return EINVAL;
+ }
+ p += 6;
+ e = strchr(p,'\n');
+ if (e == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "format error\n");
+ return EINVAL;
+ }
+ if (strncmp(p, "systemd", e-p) == 0 || strncmp(p, "(sd-pam)", e-p) == 0) {
+ *is_systemd = true;
+ } else {
+ *is_systemd = false;
+ }
+
+ *uid = num;
+
+ return EOK;
+
+fail_fd:
+ close(fd);
+ return error;
+}
+
+static errno_t name_to_pid(const char *name, pid_t *pid)
+{
+ long num;
+ char *endptr;
+ errno_t error;
+
+ errno = 0;
+ num = strtol(name, &endptr, 10);
+ error = errno;
+ if (error == ERANGE) {
+ perror("strtol");
+ return error;
+ }
+
+ if (*endptr != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pid string contains extra characters.\n");
+ return EINVAL;
+ }
+
+ if (num <= 0 || num >= INT_MAX) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pid out of range.\n");
+ return ERANGE;
+ }
+
+ *pid = num;
+
+ return EOK;
+}
+
+static int only_numbers(char *p)
+{
+ while(*p!='\0' && isdigit(*p)) ++p;
+ return *p;
+}
+
+static errno_t get_active_uid_linux(hash_table_t *table, uid_t search_uid)
+{
+ DIR *proc_dir = NULL;
+ struct dirent *dirent;
+ int ret, err;
+ pid_t pid = -1;
+ bool is_systemd;
+ uid_t uid;
+
+ hash_key_t key;
+ hash_value_t value;
+
+ proc_dir = opendir("/proc");
+ if (proc_dir == NULL) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot open proc dir.\n");
+ goto done;
+ };
+
+ errno = 0;
+ while ((dirent = readdir(proc_dir)) != NULL) {
+ if (only_numbers(dirent->d_name) != 0) {
+ continue;
+ }
+ ret = name_to_pid(dirent->d_name, &pid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "name_to_pid failed.\n");
+ goto done;
+ }
+
+ ret = get_uid_from_pid(pid, &uid, &is_systemd);
+ if (ret != EOK) {
+ /* Most probably this /proc entry disappeared.
+ Anyway, just skip it.
+ */
+ DEBUG(SSSDBG_TRACE_ALL, "get_uid_from_pid() failed.\n");
+ errno = 0;
+ continue;
+ }
+
+ if (is_systemd) {
+ /* Systemd process may linger for a while even when user.
+ * is logged out. Lets ignore it and focus only
+ * on non-systemd processes. */
+ continue;
+ }
+
+ if (table != NULL) {
+ key.type = HASH_KEY_ULONG;
+ key.ul = (unsigned long) uid;
+ value.type = HASH_VALUE_ULONG;
+ value.ul = (unsigned long) uid;
+
+ ret = hash_enter(table, &key, &value);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "cannot add to table [%s]\n", hash_error_string(ret));
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ if (uid == search_uid) {
+ ret = EOK;
+ goto done;
+ }
+ }
+
+ errno = 0;
+ }
+ if (errno != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "readdir failed.\n");
+ goto done;
+ }
+
+ ret = closedir(proc_dir);
+ proc_dir = NULL;
+ if (ret == -1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "closedir failed, watch out.\n");
+ }
+
+ if (table != NULL) {
+ ret = EOK;
+ } else {
+ ret = ENOENT;
+ }
+
+done:
+ if (proc_dir != NULL) {
+ err = closedir(proc_dir);
+ if (err) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "closedir failed, bad dirp?\n");
+ }
+ }
+ return ret;
+}
+
+errno_t get_uid_table(TALLOC_CTX *mem_ctx, hash_table_t **table)
+{
+#ifdef __linux__
+ int ret;
+
+ ret = hash_create_ex(0, table, 0, 0, 0, 0,
+ hash_talloc, hash_talloc_free, mem_ctx,
+ NULL, NULL);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "hash_create_ex failed [%s]\n", hash_error_string(ret));
+ return ENOMEM;
+ }
+
+ return get_active_uid_linux(*table, 0);
+#else
+ return ENOSYS;
+#endif
+}
+
+errno_t check_if_uid_is_active(uid_t uid, bool *result)
+{
+ int ret;
+
+#ifdef HAVE_SYSTEMD_LOGIN
+ ret = sd_uid_get_sessions(uid, 0, NULL);
+ if (ret > 0) {
+ *result = true;
+ return EOK;
+ }
+ if (ret == 0) {
+ *result = false;
+ }
+ if (ret < 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "systemd-login gave error %d: %s\n",
+ -ret, strerror(-ret));
+ }
+ /* fall back to the old method */
+#endif
+
+ ret = get_active_uid_linux(NULL, uid);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "get_active_uid_linux() failed.\n");
+ return ret;
+ }
+
+ if (ret == EOK) {
+ *result = true;
+ } else {
+ *result = false;
+ }
+
+ return EOK;
+}
diff --git a/src/util/find_uid.h b/src/util/find_uid.h
new file mode 100644
index 0000000..e01b3fc
--- /dev/null
+++ b/src/util/find_uid.h
@@ -0,0 +1,36 @@
+/*
+ SSSD
+
+ Create uid table
+
+ Authors:
+ Sumit Bose <sbose@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 __FIND_UID_H__
+#define __FIND_UID_H__
+
+#include <talloc.h>
+#include <sys/types.h>
+#include <dhash.h>
+
+#include "util/util.h"
+
+errno_t get_uid_table(TALLOC_CTX *mem_ctx, hash_table_t **table);
+errno_t check_if_uid_is_active(uid_t uid, bool *result);
+
+#endif /* __FIND_UID_H__ */
diff --git a/src/util/inotify.c b/src/util/inotify.c
new file mode 100644
index 0000000..a3c33ed
--- /dev/null
+++ b/src/util/inotify.c
@@ -0,0 +1,609 @@
+/*
+ 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 <talloc.h>
+#include <errno.h>
+#include <libgen.h>
+#include <limits.h>
+#include <sys/inotify.h>
+#include <sys/time.h>
+
+#include "util/inotify.h"
+#include "util/util.h"
+
+/* For parent directories, we want to know if a file was moved there or
+ * created there
+ */
+#define PARENT_DIR_MASK (IN_CREATE | IN_MOVED_TO)
+
+/* This structure is recreated if we need to rewatch the file and/or
+ * directory
+ */
+struct snotify_watch_ctx {
+ int inotify_fd; /* The inotify_fd */
+ struct tevent_fd *tfd; /* Activity on the fd */
+
+ struct snotify_ctx *snctx; /* Pointer up to the main snotify struct */
+
+ /* In case we're also watching the parent directory, otherwise -1.
+ * We keep the variable here and not in snctx so that we're able
+ * to catch even changes to the parent directory
+ */
+ int dir_wd;
+ /* The file watch */
+ int file_wd;
+};
+
+/* This is what we call when an event we're interested in arrives */
+struct snotify_cb_ctx {
+ snotify_cb_fn fn;
+ const char *fn_name;
+ uint32_t mask;
+ void *pvt;
+};
+
+/* One instance of a callback. We hoard the inotify notifications
+ * until timer fires in caught_flags
+ */
+struct snotify_dispatcher {
+ struct tevent_timer *te;
+ uint32_t caught_flags;
+};
+
+struct snotify_ctx {
+ struct tevent_context *ev;
+
+ /* The full path of the file we're watching,
+ * its file and directory components */
+ const char *filename;
+ const char *dir_name;
+ const char *base_name;
+
+ /* Private pointer passed to the callback */
+ struct snotify_cb_ctx cb;
+ /* A singleton callback dispatcher */
+ struct snotify_dispatcher *disp;
+
+ /* Internal snotify flags */
+ uint16_t snotify_flags;
+ /* The caller might decide to batch the updates and receive
+ * them all together with a delay
+ */
+ struct timeval delay;
+ /* We keep the structure that actually does the work
+ * separately to be able to reinitialize it when the
+ * file is recreated or moved to the directory
+ */
+ struct snotify_watch_ctx *wctx;
+};
+
+struct flg2str {
+ uint32_t flg;
+ const char *str;
+} flg_table[] = {
+ { 0x00000001, "IN_ACCESS" },
+ { 0x00000002, "IN_MODIFY" },
+ { 0x00000004, "IN_ATTRIB" },
+ { 0x00000008, "IN_CLOSE_WRITE" },
+ { 0x00000010, "IN_CLOSE_NOWRITE" },
+ { 0x00000020, "IN_OPEN" },
+ { 0x00000040, "IN_MOVED_FROM" },
+ { 0x00000080, "IN_MOVED_TO" },
+ { 0x00000100, "IN_CREATE" },
+ { 0x00000200, "IN_DELETE" },
+ { 0x00000400, "IN_DELETE_SELF" },
+ { 0x00000800, "IN_MOVE_SELF" },
+ { 0x00002000, "IN_UNMOUNT" },
+ { 0x00004000, "IN_Q_OVERFLOW" },
+ { 0x00008000, "IN_IGNORED" },
+ { 0x01000000, "IN_ONLYDIR" },
+ { 0x02000000, "IN_DONT_FOLLOW" },
+ { 0x04000000, "IN_EXCL_UNLINK" },
+ { 0x20000000, "IN_MASK_ADD" },
+ { 0x40000000, "IN_ISDIR" },
+ { 0x80000000, "IN_ONESHOT" },
+ { 0, NULL },
+};
+
+#if 0
+static void debug_flags(uint32_t flags, const char *file)
+{
+ char msgbuf[1024];
+ size_t total = 0;
+
+ if (!DEBUG_IS_SET(SSSDBG_TRACE_LIBS)) {
+ return;
+ }
+
+ for (int i = 0; flg_table[i].flg != 0; i++) {
+ if (flags & flg_table[i].flg) {
+ total += snprintf(msgbuf+total,
+ sizeof(msgbuf)-total,
+ "%s ", flg_table[i].str);
+ }
+ }
+
+ if (total == 0) {
+ snprintf(msgbuf, sizeof(msgbuf), "NONE\n");
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Inotify event: %s on %s\n", msgbuf, file);
+}
+#endif
+
+static void snotify_process_callbacks(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *ptr)
+{
+ struct snotify_ctx *snctx;
+
+ snctx = talloc_get_type(ptr, struct snotify_ctx);
+ if (snctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Bad pointer\n");
+ return;
+ }
+
+ snctx->cb.fn(snctx->filename,
+ snctx->disp->caught_flags,
+ snctx->cb.pvt);
+
+ talloc_zfree(snctx->disp);
+}
+
+static struct snotify_dispatcher *create_dispatcher(struct snotify_ctx *snctx)
+{
+ struct snotify_dispatcher *disp;
+ struct timeval tv;
+
+ disp = talloc_zero(snctx, struct snotify_dispatcher);
+ if (disp == NULL) {
+ return NULL;
+ }
+
+ gettimeofday(&tv, NULL);
+ tv.tv_sec += snctx->delay.tv_sec;
+ tv.tv_usec += snctx->delay.tv_usec;
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Running a timer with delay %ld.%ld\n",
+ (unsigned long) snctx->delay.tv_sec,
+ (unsigned long) snctx->delay.tv_usec);
+
+ disp->te = tevent_add_timer(snctx->ev, disp, tv,
+ snotify_process_callbacks,
+ snctx);
+ if (disp->te == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n");
+ talloc_free(disp);
+ return NULL;
+ }
+
+ return disp;
+}
+
+static struct snotify_dispatcher *get_dispatcher(struct snotify_ctx *snctx)
+{
+ if (snctx->disp != NULL) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Reusing existing dispatcher\n");
+ return snctx->disp;
+ }
+
+ return create_dispatcher(snctx);
+}
+
+static errno_t dispatch_event(struct snotify_ctx *snctx,
+ uint32_t ev_flags)
+{
+ struct snotify_dispatcher *disp;
+
+ if ((snctx->cb.mask & ev_flags) == 0) {
+ return EOK;
+ }
+
+ disp = get_dispatcher(snctx);
+ if (disp == NULL) {
+ return ENOMEM;
+ }
+
+ disp->caught_flags |= ev_flags;
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Dispatched an event with combined flags 0x%X\n",
+ disp->caught_flags);
+
+ snctx->disp = disp;
+ return EOK;
+}
+
+static errno_t process_dir_event(struct snotify_ctx *snctx,
+ const struct inotify_event *in_event)
+{
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_ALL, "inotify name: %s\n", in_event->name);
+ if (in_event->len == 0 \
+ || strcmp(in_event->name, snctx->base_name) != 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Not interested in %s\n", in_event->name);
+ return EOK;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "received notification for watched file [%s] under %s\n",
+ in_event->name, snctx->dir_name);
+
+ /* file the event for the file to see if the caller is interested in it */
+ ret = dispatch_event(snctx, in_event->mask);
+ if (ret == EOK) {
+ /* Tells the outer loop to re-initialize flags once the loop is finished.
+ * However, finish reading all the events first to make sure we don't
+ * miss any
+ */
+ return EAGAIN;
+ }
+
+ return ret;
+}
+
+static errno_t process_file_event(struct snotify_ctx *snctx,
+ const struct inotify_event *in_event)
+{
+ if (in_event->mask & IN_IGNORED) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Will reopen moved or deleted file %s\n", snctx->filename);
+ /* Notify caller of the event, don't quit */
+ return EAGAIN;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "received notification for watched file %s\n", snctx->filename);
+
+ return dispatch_event(snctx, in_event->mask);
+}
+
+static errno_t snotify_rewatch(struct snotify_ctx *snctx);
+
+static void snotify_internal_cb(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *data)
+{
+ char ev_buf[sizeof(struct inotify_event) + PATH_MAX];
+ const char *ptr;
+ const struct inotify_event *in_event;
+ struct snotify_ctx *snctx;
+ ssize_t len;
+ errno_t ret;
+ bool rewatch = false;
+
+ snctx = talloc_get_type(data, struct snotify_ctx);
+ if (snctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Bad pointer\n");
+ return;
+ }
+
+ while (1) {
+ len = read(snctx->wctx->inotify_fd, ev_buf, sizeof(ev_buf));
+ if (len == -1) {
+ ret = errno;
+ if (ret != EAGAIN) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot read inotify_event [%d]: %s\n",
+ ret, strerror(ret));
+ } else {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "All inotify events processed\n");
+ }
+ break;
+ }
+
+ if ((size_t) len < sizeof(struct inotify_event)) {
+ /* Did not even read the required amount of data, move on.. */
+ continue;
+ }
+
+ for (ptr = ev_buf;
+ ptr < ev_buf + len;
+ ptr += sizeof(struct inotify_event) + in_event->len) {
+
+ in_event = (const struct inotify_event *) ptr;
+
+#if 0
+ debug_flags(in_event->mask, in_event->name);
+#endif
+
+ if (snctx->wctx->dir_wd == in_event->wd) {
+ ret = process_dir_event(snctx, in_event);
+ } else if (snctx->wctx->file_wd == in_event->wd) {
+ ret = process_file_event(snctx, in_event);
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unknown watch %d\n", in_event->wd);
+ ret = EOK;
+ }
+
+ if (ret == EAGAIN) {
+ rewatch = true;
+ /* Continue with the loop and read all the events from
+ * this descriptor first, then rewatch when done
+ */
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to process inotify event\n");
+ }
+ }
+ }
+
+ if (rewatch) {
+ ret = snotify_rewatch(snctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to re-set watch");
+ }
+ }
+}
+
+static int watch_ctx_destructor(void *memptr)
+{
+ struct snotify_watch_ctx *wctx;
+
+ wctx = talloc_get_type(memptr, struct snotify_watch_ctx);
+ if (wctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Bad pointer\n");
+ return 1;
+ }
+
+ /* We don't need to close the watches explicitly. man 7 inotify says:
+ * When all file descriptors referring to an inotify instance
+ * have been closed (using close(2)), the underlying object
+ * and its resources are freed for reuse by the kernel; all
+ * associated watches are automatically freed.
+ */
+ if (wctx->inotify_fd != -1) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Closing inotify fd %d\n", wctx->inotify_fd);
+ close(wctx->inotify_fd);
+ }
+
+ return 0;
+}
+
+static errno_t resolve_filename(struct snotify_ctx *snctx,
+ const char *filename,
+ char *resolved,
+ size_t resolved_size)
+{
+ /* NOTE: The code below relies in the GNU extensions for realpath,
+ * which will store in 'resolved' the prefix of 'filename' that does
+ * not exists if realpath call fails and errno is set to ENOENT */
+ if (realpath(filename, resolved) == NULL) {
+ char fcopy[PATH_MAX + 1];
+ char *p;
+ struct stat st;
+
+ if (errno != ENOENT) {
+ return errno;
+ }
+
+ /* Check if the unique missing component is the basename. The
+ * dirname must exist to be notified watching the parent dir. */
+ strncpy(fcopy, filename, sizeof(fcopy) - 1);
+ fcopy[PATH_MAX] = '\0';
+
+ p = dirname(fcopy);
+ if (p == NULL) {
+ return EIO;
+ }
+
+ if (stat(p, &st) == -1) {
+ return errno;
+ }
+
+ /* The basedir exist, check the caller requested to watch it.
+ * Otherwise return error as never will be notified. */
+
+ if ((snctx->snotify_flags & SNOTIFY_WATCH_DIR) == 0) {
+ return ENOENT;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t copy_filenames(struct snotify_ctx *snctx,
+ const char *filename)
+{
+ char *p;
+ char resolved[PATH_MAX + 1];
+ char fcopy[PATH_MAX + 1];
+ errno_t ret;
+
+ ret = resolve_filename(snctx, filename, resolved, sizeof(resolved));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ strncpy(fcopy, resolved, sizeof(fcopy) - 1);
+ fcopy[PATH_MAX] = '\0';
+
+ p = dirname(fcopy);
+ if (p == NULL) {
+ return EIO;
+ }
+
+ snctx->dir_name = talloc_strdup(snctx, p);
+ if (snctx->dir_name == NULL) {
+ return ENOMEM;
+ }
+
+ strncpy(fcopy, resolved, sizeof(fcopy) - 1);
+ fcopy[PATH_MAX] = '\0';
+
+ p = basename(fcopy);
+ if (p == NULL) {
+ return EIO;
+ }
+
+ snctx->base_name = talloc_strdup(snctx, p);
+ if (snctx->base_name == NULL) {
+ return ENOMEM;
+ }
+
+ snctx->filename = talloc_strdup(snctx, resolved);
+ if (snctx->filename == NULL) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static struct snotify_watch_ctx *snotify_watch(struct snotify_ctx *snctx,
+ uint32_t mask)
+{
+ struct snotify_watch_ctx *wctx;
+ errno_t ret;
+
+ wctx = talloc_zero(snctx, struct snotify_watch_ctx);
+ if (wctx == NULL) {
+ return NULL;
+ }
+ wctx->inotify_fd = -1;
+ wctx->dir_wd = -1;
+ wctx->file_wd = -1;
+ wctx->snctx = snctx;
+ talloc_set_destructor((TALLOC_CTX *)wctx, watch_ctx_destructor);
+
+ wctx->inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+ if (wctx->inotify_fd == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "inotify_init1 failed: %d: %s\n", ret, strerror(ret));
+ goto fail;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Opened inotify fd %d\n", wctx->inotify_fd);
+
+ wctx->tfd = tevent_add_fd(snctx->ev, wctx, wctx->inotify_fd,
+ TEVENT_FD_READ, snotify_internal_cb,
+ snctx);
+ if (wctx->tfd == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot add tevent fd watch for %s\n",
+ snctx->filename);
+ goto fail;
+ }
+
+ wctx->file_wd = inotify_add_watch(wctx->inotify_fd, snctx->filename, mask);
+ if (wctx->file_wd == -1) {
+ ret = errno;
+ if (ret != ENOENT || (!(snctx->snotify_flags & SNOTIFY_WATCH_DIR))) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "inotify_add_watch failed [%d]: %s\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Opened file watch %d\n", wctx->file_wd);
+
+ if (snctx->snotify_flags & SNOTIFY_WATCH_DIR) {
+ /* Create a watch for the parent directory. This is useful for cases
+ * where we start watching a file before it's created, but still want
+ * a notification when the file is moved in
+ */
+ wctx->dir_wd = inotify_add_watch(wctx->inotify_fd,
+ snctx->dir_name, PARENT_DIR_MASK);
+ if (wctx->dir_wd == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "inotify_add_watch failed [%d]: %s\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Opened directory watch %d\n", wctx->dir_wd);
+ }
+
+ return wctx;
+
+fail:
+ talloc_free(wctx);
+ return NULL;
+}
+
+static errno_t snotify_rewatch(struct snotify_ctx *snctx)
+{
+ talloc_free(snctx->wctx);
+
+ snctx->wctx = snotify_watch(snctx, snctx->cb.mask);
+ if (snctx->wctx == NULL) {
+ return ENOMEM;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Recreated watch\n");
+ return EOK;
+}
+
+struct snotify_ctx *_snotify_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ uint16_t snotify_flags,
+ const char *filename,
+ struct timeval *delay,
+ uint32_t mask,
+ snotify_cb_fn fn,
+ const char *fn_name,
+ void *pvt)
+{
+ errno_t ret;
+ struct snotify_ctx *snctx;
+
+ snctx = talloc_zero(mem_ctx, struct snotify_ctx);
+ if (snctx == NULL) {
+ return NULL;
+ }
+
+ snctx->ev = ev;
+ snctx->snotify_flags = snotify_flags;
+ if (delay) {
+ snctx->delay.tv_sec = delay->tv_sec;
+ snctx->delay.tv_usec = delay->tv_usec;
+ }
+
+ snctx->cb.fn = fn;
+ snctx->cb.fn_name = fn_name;
+ snctx->cb.mask = mask;
+ snctx->cb.pvt = pvt;
+
+ ret = copy_filenames(snctx, filename);
+ if (ret != EOK) {
+ talloc_free(snctx);
+ return NULL;
+ }
+
+ snctx->wctx = snotify_watch(snctx, mask);
+ if (snctx->wctx == NULL) {
+ talloc_free(snctx);
+ return NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Added a watch for %s with inotify flags 0x%X "
+ "internal flags 0x%X "
+ "using function %s after delay %ld.%ld\n",
+ snctx->filename,
+ mask,
+ snotify_flags,
+ fn_name,
+ (unsigned long) snctx->delay.tv_sec,
+ (unsigned long) snctx->delay.tv_usec);
+
+ return snctx;
+}
diff --git a/src/util/inotify.h b/src/util/inotify.h
new file mode 100644
index 0000000..3592944
--- /dev/null
+++ b/src/util/inotify.h
@@ -0,0 +1,61 @@
+/*
+ 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 __INOTIFY_H_
+#define __INOTIFY_H_
+
+#include <talloc.h>
+#include <tevent.h>
+#include <sys/inotify.h>
+
+
+typedef int (*snotify_cb_fn)(const char *filename,
+ uint32_t caught_flags,
+ void *pvt);
+
+#define SNOTIFY_WATCH_DIR 0x0001
+
+/*
+ * Set up an inotify watch for file at filename. When an inotify
+ * event is caught, it must match the "mask" parameter. The watch
+ * would then call snotify_cb_fn() and include the caught flags.
+ *
+ * If snotify_flags includes SNOTIFY_WATCH_DIR, also the parent directory
+ * of this file would be watched to cover cases where the file might not
+ * exist when the watch is created.
+ *
+ * If you wish to batch inotify requests to avoid hammering the caller
+ * with several successive requests, use the delay parameter. The function
+ * would then only send invoke the callback after the delay and the caught
+ * flags would be OR-ed. By default, the callback is invoked immediately.
+ *
+ * Use the pvt parameter to pass a private context to the function
+ */
+struct snotify_ctx *_snotify_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ uint16_t snotify_flags,
+ const char *filename,
+ struct timeval *delay,
+ uint32_t mask,
+ snotify_cb_fn fn,
+ const char *fn_name,
+ void *pvt);
+
+#define snotify_create(mem_ctx, ev, snotify_flags, filename, delay, mask, fn, pvt) \
+ _snotify_create(mem_ctx, ev, snotify_flags, filename, delay, mask, fn, #fn, pvt);
+
+#endif /* __INOTIFY_H_ */
diff --git a/src/util/io.c b/src/util/io.c
new file mode 100644
index 0000000..4d442b4
--- /dev/null
+++ b/src/util/io.c
@@ -0,0 +1,98 @@
+/*
+ SSSD
+
+ io.c
+
+ Authors:
+ Lukas Slebodnik <lslebodn@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 "config.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "shared/io.h"
+
+/* CAUTION:
+ * This file have to be minimalist and cannot include DEBUG macros
+ * or header file util.h.
+ */
+
+int sss_open_cloexec(const char *pathname, int flags, int *ret)
+{
+ int fd;
+ int oflags;
+
+ oflags = flags;
+#ifdef O_CLOEXEC
+ oflags |= O_CLOEXEC;
+#endif
+
+ errno = 0;
+ fd = open(pathname, oflags);
+ if (fd == -1) {
+ if (ret) {
+ *ret = errno;
+ }
+ return -1;
+ }
+
+#ifndef O_CLOEXEC
+ int v;
+
+ v = fcntl(fd, F_GETFD, 0);
+ /* we ignore an error, it's not fatal and there is nothing we
+ * can do about it anyways */
+ (void)fcntl(fd, F_SETFD, v | FD_CLOEXEC);
+#endif
+
+ return fd;
+}
+
+int sss_openat_cloexec(int dir_fd, const char *pathname, int flags, int *ret)
+{
+ int fd;
+ int oflags;
+
+ oflags = flags;
+#ifdef O_CLOEXEC
+ oflags |= O_CLOEXEC;
+#endif
+
+ errno = 0;
+ fd = openat(dir_fd, pathname, oflags);
+ if (fd == -1) {
+ if (ret) {
+ *ret = errno;
+ }
+ return -1;
+ }
+
+#ifndef O_CLOEXEC
+ int v;
+
+ v = fcntl(fd, F_GETFD, 0);
+ /* we ignore an error, it's not fatal and there is nothing we
+ * can do about it anyways */
+ (void)fcntl(fd, F_SETFD, v | FD_CLOEXEC);
+#endif
+
+ return fd;
+}
diff --git a/src/util/memory.c b/src/util/memory.c
new file mode 100644
index 0000000..4fb8cfa
--- /dev/null
+++ b/src/util/memory.c
@@ -0,0 +1,97 @@
+/*
+ Authors:
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include <talloc.h>
+
+#include "util/util.h"
+
+
+#ifdef HAVE_EXPLICIT_BZERO
+
+#include <string.h>
+
+#else
+
+typedef void *(*_sss_memset_t)(void *, int, size_t);
+
+static volatile _sss_memset_t memset_func = memset;
+
+static void explicit_bzero(void *s, size_t n)
+{
+ memset_func(s, 0, n);
+}
+
+#endif
+
+
+int sss_erase_talloc_mem_securely(void *p)
+{
+ if (p == NULL) {
+ return 0;
+ }
+
+ size_t size = talloc_get_size(p);
+ if (size == 0) {
+ return 0;
+ }
+
+ explicit_bzero(p, size);
+
+ return 0;
+}
+
+void sss_erase_mem_securely(void *p, size_t size)
+{
+ if ((p == NULL) || (size == 0)) {
+ return;
+ }
+
+ explicit_bzero(p, size);
+}
+
+
+struct mem_holder {
+ void *mem;
+ void_destructor_fn_t *fn;
+};
+
+static int mem_holder_destructor(void *ptr)
+{
+ struct mem_holder *h;
+
+ h = talloc_get_type(ptr, struct mem_holder);
+ return h->fn(h->mem);
+}
+
+int sss_mem_attach(TALLOC_CTX *mem_ctx, void *ptr, void_destructor_fn_t *fn)
+{
+ struct mem_holder *h;
+
+ if (!ptr || !fn) return EINVAL;
+
+ h = talloc(mem_ctx, struct mem_holder);
+ if (!h) return ENOMEM;
+
+ h->mem = ptr;
+ h->fn = fn;
+ talloc_set_destructor((TALLOC_CTX *)h, mem_holder_destructor);
+
+ return EOK;
+}
diff --git a/src/util/mmap_cache.h b/src/util/mmap_cache.h
new file mode 100644
index 0000000..3f2b6c2
--- /dev/null
+++ b/src/util/mmap_cache.h
@@ -0,0 +1,161 @@
+/*
+ SSSD
+
+ Mmap Cache Common header
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2011
+
+ 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 _MMAP_CACHE_H_
+#define _MMAP_CACHE_H_
+
+#include "shared/murmurhash3.h"
+
+
+/* NOTE: all the code here assumes that writing a uint32_t nto mmapped
+ * memory is an atomic operation and can't be split in multiple
+ * non-atomic operations */
+typedef uint32_t rel_ptr_t;
+
+/* align macros */
+#define MC_8 sizeof(uint8_t)
+#define MC_32 sizeof(uint32_t)
+#define MC_64 sizeof(uint64_t)
+#define MC_ALIGN32(size) ( ((size) + MC_32 -1) & (~(MC_32 -1)) )
+#define MC_ALIGN64(size) ( ((size) + MC_64 -1) & (~(MC_64 -1)) )
+#define MC_HEADER_SIZE MC_ALIGN64(sizeof(struct sss_mc_header))
+
+#define MC_HT_SIZE(elems) ( (elems) * MC_32 )
+#define MC_HT_ELEMS(size) ( (size) / MC_32 )
+
+#define MC_PTR_ADD(ptr, bytes) (void *)((uint8_t *)(ptr) + (bytes))
+#define MC_PTR_DIFF(ptr, base) ((uint8_t *)(ptr) - (uint8_t *)(base))
+
+#define MC_INVALID_VAL64 ((uint64_t)-1)
+#define MC_INVALID_VAL32 ((uint32_t)-1)
+#define MC_INVALID_VAL8 ((uint8_t)-1)
+#define MC_INVALID_VAL MC_INVALID_VAL32
+
+/*
+ * 40 seem a good compromise for slot size
+ * 4 blocks are enough for the average passwd entry of 42 bytes
+ * passwd records have 84 bytes of overhead, 160 - 82 = 78 bytes
+ * 3 blocks can contain a very minimal entry, 120 - 82 = 38 bytes
+ *
+ * 3 blocks are enough for groups w/o users (private user groups)
+ * group records have 68 bytes of overhead, 120 - 66 = 54 bytes
+ */
+#define MC_SLOT_SIZE 40
+#define MC_SIZE_TO_SLOTS(len) (((len) + (MC_SLOT_SIZE - 1)) / MC_SLOT_SIZE)
+#define MC_PTR_TO_SLOT(base, ptr) (MC_PTR_DIFF(ptr, base) / MC_SLOT_SIZE)
+#define MC_SLOT_TO_PTR(base, slot, type) \
+ (type *)((base) + ((slot) * MC_SLOT_SIZE))
+
+#define MC_SLOT_WITHIN_BOUNDS(slot, dt_size) \
+ ((slot) < ((dt_size) / MC_SLOT_SIZE))
+
+#define MC_VALID_BARRIER(val) (((val) & 0xff000000) == 0xf0000000)
+
+#define MC_CHECK_RECORD_LENGTH(mc_ctx, rec) \
+ ((rec)->len >= MC_HEADER_SIZE && (rec)->len != MC_INVALID_VAL32 \
+ && ((rec)->len <= ((mc_ctx)->dt_size \
+ - MC_PTR_DIFF(rec, (mc_ctx)->data_table))))
+
+
+#define SSS_MC_MAJOR_VNO 1
+#define SSS_MC_MINOR_VNO 1
+
+#define SSS_MC_HEADER_UNINIT 0 /* after ftruncate or before reset */
+#define SSS_MC_HEADER_ALIVE 1 /* current and in use */
+#define SSS_MC_HEADER_RECYCLED 2 /* file was recycled, reopen asap */
+
+#pragma pack(1)
+struct sss_mc_header {
+ uint32_t b1; /* barrier 1 */
+ uint32_t major_vno; /* major version number */
+ uint32_t minor_vno; /* minor version number */
+ uint32_t status; /* database status */
+ uint32_t seed; /* random seed used to avoid collision attacks */
+ uint32_t dt_size; /* data table size */
+ uint32_t ft_size; /* free table size */
+ uint32_t ht_size; /* hash table size */
+ rel_ptr_t data_table; /* data table pointer relative to mmap base */
+ rel_ptr_t free_table; /* free table pointer relative to mmap base */
+ rel_ptr_t hash_table; /* hash table pointer relative to mmap base */
+ rel_ptr_t reserved; /* reserved for future changes */
+ uint32_t b2; /* barrier 2 */
+};
+
+struct sss_mc_rec {
+ uint32_t b1; /* barrier 1 */
+ uint32_t len; /* total record length including record data */
+ uint64_t expire; /* record expiration time (cast to time_t) */
+ rel_ptr_t next1; /* ptr of next record rel to data_table */
+ /* next1 is related to hash1 */
+ rel_ptr_t next2; /* ptr of next record rel to data_table */
+ /* next2 is related to hash2 */
+ uint32_t hash1; /* val of first hash (usually name of record) */
+ uint32_t hash2; /* val of second hash (usually id of record) */
+ uint32_t padding; /* padding & reserved for future changes */
+ uint32_t b2; /* barrier 2 - 32 bytes mark, fits a slot */
+ char data[0];
+};
+
+struct sss_mc_pwd_data {
+ rel_ptr_t name; /* ptr to name string, rel. to struct base addr */
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t strs_len; /* length of strs */
+ char strs[0]; /* concatenation of all passwd strings, each
+ * string is zero terminated ordered as follows:
+ * name, passwd, gecos, dir, shell */
+};
+
+struct sss_mc_grp_data {
+ rel_ptr_t name; /* ptr to name string, rel. to struct base addr */
+ uint32_t gid;
+ uint32_t members; /* number of members in strs */
+ uint32_t strs_len; /* length of strs */
+ char strs[0]; /* concatenation of all group strings, each
+ * string is zero terminated ordered as follows:
+ * name, passwd, member1, member2, ... */
+};
+
+struct sss_mc_initgr_data {
+ rel_ptr_t unique_name; /* ptr to unique name string, rel. to struct base addr */
+ rel_ptr_t name; /* ptr to raw name string, rel. to struct base addr */
+ rel_ptr_t strs; /* ptr to concatenation of all strings */
+ uint32_t strs_len; /* length of strs */
+ uint32_t data_len; /* all initgroups data len */
+ uint32_t num_groups; /* number of groups */
+ uint32_t gids[0]; /* array of all groups
+ * string with name and unique_name is stored
+ * after gids */
+};
+
+struct sss_mc_sid_data {
+ rel_ptr_t name; /* ptr to SID string, rel. to struct base addr */
+ uint32_t type; /* enum sss_id_type */
+ uint32_t id; /* gid or uid */
+ uint32_t populated_by; /* 0 - by_id(), 1 - by_uid/gid() lookup */
+ uint32_t sid_len; /* length of sid */
+ char sid[0];
+};
+
+#pragma pack()
+
+
+#endif /* _MMAP_CACHE_H_ */
diff --git a/src/util/murmurhash3.c b/src/util/murmurhash3.c
new file mode 100644
index 0000000..f8db9d2
--- /dev/null
+++ b/src/util/murmurhash3.c
@@ -0,0 +1,116 @@
+/* This file is based on the public domain MurmurHash3 from Austin Appleby:
+ * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
+ *
+ * We use only the 32 bit variant because the 2 produce different result while
+ * we need to produce the same result regardless of the architecture as
+ * clients can be both 64 or 32 bit at the same time.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "config.h"
+#include "shared/murmurhash3.h"
+#include "util/sss_endian.h"
+
+static uint32_t rotl(uint32_t x, int8_t r)
+{
+ return (x << r) | (x >> (32 - r));
+}
+
+/* slower than original but is endian neutral and handles platforms that
+ * do only aligned reads */
+__attribute__((always_inline))
+static inline uint32_t getblock(const uint8_t *p, int i)
+{
+ uint32_t r;
+ size_t size = sizeof(uint32_t);
+
+ memcpy(&r, &p[i * size], size);
+
+ return le32toh(r);
+}
+
+/*
+ * Finalization mix - force all bits of a hash block to avalanche
+ */
+
+__attribute__((always_inline))
+static inline uint32_t fmix(uint32_t h)
+{
+ h ^= h >> 16;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >> 16;
+
+ return h;
+}
+
+
+uint32_t murmurhash3(const char *key, int len, uint32_t seed)
+{
+ const uint8_t *blocks;
+ const uint8_t *tail;
+ int nblocks;
+ uint32_t h1;
+ uint32_t k1;
+ uint32_t c1;
+ uint32_t c2;
+ int i;
+
+ blocks = (const uint8_t *)key;
+ nblocks = len / 4;
+ h1 = seed;
+ c1 = 0xcc9e2d51;
+ c2 = 0x1b873593;
+
+ /* body */
+
+ for (i = 0; i < nblocks; i++) {
+
+ k1 = getblock(blocks, i);
+
+ k1 *= c1;
+ k1 = rotl(k1, 15);
+ k1 *= c2;
+
+ h1 ^= k1;
+ h1 = rotl(h1, 13);
+ h1 = h1 * 5 + 0xe6546b64;
+ }
+
+ /* tail */
+
+ tail = (const uint8_t *)key + nblocks * 4;
+
+ k1 = 0;
+
+ switch (len & 3) {
+ case 3:
+ k1 ^= tail[2] << 16;
+ SSS_ATTRIBUTE_FALLTHROUGH;
+ case 2:
+ k1 ^= tail[1] << 8;
+ SSS_ATTRIBUTE_FALLTHROUGH;
+ case 1:
+ k1 ^= tail[0];
+ k1 *= c1;
+ k1 = rotl(k1, 15);
+ k1 *= c2;
+ h1 ^= k1;
+ break;
+ default:
+ break;
+ }
+
+ /* finalization */
+
+ h1 ^= len;
+ h1 = fmix(h1);
+
+ return h1;
+}
+
+
diff --git a/src/util/nscd.c b/src/util/nscd.c
new file mode 100644
index 0000000..47a1c02
--- /dev/null
+++ b/src/util/nscd.c
@@ -0,0 +1,146 @@
+/*
+ SSSD
+
+ nscd.c
+
+ Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2010
+
+ 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"
+
+
+/* NSCD config file parse and check */
+static unsigned int sss_nscd_check_service(char* svc_name)
+{
+ struct sss_nscd_db {
+ const char *svc_type_name;
+ unsigned int nscd_service_flag;
+ };
+
+ int i;
+ unsigned int ret = 0;
+ struct sss_nscd_db db[] = {
+ { "passwd", 0x0001 },
+ { "group", 0x0010 },
+ { "netgroup", 0x0100 },
+ { "services", 0x1000 },
+ { NULL, 0 }
+ };
+
+ if (svc_name == NULL) {
+ return ret;
+ }
+
+ for (i = 0; db[i].svc_type_name != NULL; i++) {
+ if (!strcmp(db[i].svc_type_name, svc_name)) {
+
+ ret = db[i].nscd_service_flag;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+errno_t sss_nscd_parse_conf(const char *conf_path)
+{
+ FILE *fp;
+ int ret = EOK;
+ unsigned int occurred = 0;
+ char *line, *entry, *service, *enabled, *pad;
+ size_t linelen = 0;
+
+ fp = fopen(conf_path, "r");
+ if (fp == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Couldn't open NSCD configuration "
+ "file [%s]\n", conf_path);
+ return ENOENT;
+ }
+
+ while (getline(&line, &linelen, fp) != -1) {
+
+ pad = strchr(line, '#');
+ if (pad != NULL) {
+ *pad = '\0';
+ }
+
+ if (line[0] == '\n' || line[0] == '\0') continue;
+
+ entry = line;
+ while (isspace(*entry) && *entry != '\0') {
+ entry++;
+ }
+
+ pad = entry;
+ while (!isspace(*pad) && *pad != '\0') {
+ pad++;
+ }
+
+ service = pad;
+ while (isspace(*service) && *service != '\0') {
+ service++;
+ }
+
+ *pad = '\0';
+ pad = service;
+ while (!isspace(*pad) && *pad != '\0') {
+ pad++;
+ }
+
+ enabled = pad;
+ while (isspace(*enabled) && *enabled != '\0') {
+ enabled++;
+ }
+
+ *pad = '\0';
+ pad = enabled;
+ while (!isspace(*pad) && *pad != '\0') {
+ pad++;
+ }
+ *pad = '\0';
+
+ if (!strcmp(entry, "enable-cache") &&
+ !strcmp(enabled, "yes")) {
+
+ occurred |= sss_nscd_check_service(service);
+ }
+ };
+
+ ret = ferror(fp);
+ if (ret) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Reading NSCD configuration file [%s] "
+ "ended with failure [%d]: %s.\n",
+ conf_path, ret, strerror(ret));
+ ret = ENOENT;
+ goto done;
+ }
+
+ ret = EOK;
+ if (occurred != 0) {
+ ret = EEXIST;
+ goto done;
+ }
+
+done:
+ free(line);
+ fclose(fp);
+
+ return ret;
+}
diff --git a/src/util/nss_dl_load.c b/src/util/nss_dl_load.c
new file mode 100644
index 0000000..4421083
--- /dev/null
+++ b/src/util/nss_dl_load.c
@@ -0,0 +1,98 @@
+/*
+ Copyright (C) 2019 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 <dlfcn.h>
+#include <talloc.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include "util/util_errors.h"
+#include "util/debug.h"
+#include "nss_dl_load.h"
+
+
+#define NSS_FN_NAME "_nss_%s_%s"
+
+
+static void *proxy_dlsym(void *handle,
+ const char *name,
+ const char *libname)
+{
+ char *funcname;
+ void *funcptr;
+
+ funcname = talloc_asprintf(NULL, NSS_FN_NAME, libname, name);
+ if (funcname == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ return NULL;
+ }
+
+ funcptr = dlsym(handle, funcname);
+ talloc_free(funcname);
+
+ return funcptr;
+}
+
+
+errno_t sss_load_nss_symbols(struct sss_nss_ops *ops, const char *libname,
+ struct sss_nss_symbols *syms, size_t nsyms)
+{
+ errno_t ret;
+ char *libpath;
+ size_t i;
+
+ libpath = talloc_asprintf(NULL, "libnss_%s.so.2", libname);
+ if (libpath == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ return ENOMEM;
+ }
+
+ ops->dl_handle = dlopen(libpath, RTLD_NOW);
+ if (ops->dl_handle == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to load %s module, "
+ "error: %s\n", libpath, dlerror());
+ ret = ELIBACC;
+ goto out;
+ }
+
+ for (i = 0; i < nsyms; i++) {
+ *(syms[i].fptr) = proxy_dlsym(ops->dl_handle, syms[i].fname,
+ libname);
+
+ if (*(syms[i].fptr) == NULL) {
+ if (syms[i].mandatory) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Library '%s' did not provide "
+ "mandatory symbol '%s', error: %s.\n", libpath,
+ syms[i].fname, dlerror());
+ ret = ELIBBAD;
+ goto out;
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "Library '%s' did not provide "
+ "optional symbol '%s', error: %s.\n", libpath,
+ syms[i].fname, dlerror());
+ }
+ }
+ }
+
+ ret = EOK;
+
+out:
+ talloc_free(libpath);
+
+ return ret;
+}
diff --git a/src/util/nss_dl_load.h b/src/util/nss_dl_load.h
new file mode 100644
index 0000000..f1e882b
--- /dev/null
+++ b/src/util/nss_dl_load.h
@@ -0,0 +1,122 @@
+/*
+ Copyright (C) 2019 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 __SSSD_NSS_DL_LOAD_H__
+#define __SSSD_NSS_DL_LOAD_H__
+
+
+#include <nss.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netdb.h>
+#include "util/util_errors.h"
+#include "sss_client/nss_compat.h"
+
+
+struct sss_nss_ops {
+ enum nss_status (*getpwnam_r)(const char *name, struct passwd *result,
+ char *buffer, size_t buflen, int *errnop);
+ enum nss_status (*getpwuid_r)(uid_t uid, struct passwd *result,
+ char *buffer, size_t buflen, int *errnop);
+ enum nss_status (*setpwent)(void);
+ enum nss_status (*getpwent_r)(struct passwd *result,
+ char *buffer, size_t buflen, int *errnop);
+ enum nss_status (*endpwent)(void);
+
+ enum nss_status (*getgrnam_r)(const char *name, struct group *result,
+ char *buffer, size_t buflen, int *errnop);
+ enum nss_status (*getgrgid_r)(gid_t gid, struct group *result,
+ char *buffer, size_t buflen, int *errnop);
+ enum nss_status (*setgrent)(void);
+ enum nss_status (*getgrent_r)(struct group *result,
+ char *buffer, size_t buflen, int *errnop);
+ enum nss_status (*endgrent)(void);
+ enum nss_status (*initgroups_dyn)(const char *user, gid_t group,
+ long int *start, long int *size,
+ gid_t **groups, long int limit,
+ int *errnop);
+ enum nss_status (*setnetgrent)(const char *netgroup,
+ struct __netgrent *result);
+ enum nss_status (*getnetgrent_r)(struct __netgrent *result, char *buffer,
+ size_t buflen, int *errnop);
+ enum nss_status (*endnetgrent)(struct __netgrent *result);
+
+ /* Services */
+ enum nss_status (*getservbyname_r)(const char *name,
+ const char *protocol,
+ struct servent *result,
+ char *buffer, size_t buflen,
+ int *errnop);
+ enum nss_status (*getservbyport_r)(int port, const char *protocol,
+ struct servent *result,
+ char *buffer, size_t buflen,
+ int *errnop);
+ enum nss_status (*setservent)(void);
+ enum nss_status (*getservent_r)(struct servent *result,
+ char *buffer, size_t buflen,
+ int *errnop);
+ enum nss_status (*endservent)(void);
+
+ /* Hosts */
+ enum nss_status (*gethostbyname_r)(const char *name,
+ struct hostent *result,
+ char *buffer, size_t buflen,
+ int *errnop, int *h_errnop);
+ enum nss_status (*gethostbyname2_r)(const char *name, int af,
+ struct hostent *result,
+ char *buffer, size_t buflen,
+ int *errnop, int *h_errnop);
+ enum nss_status (*gethostbyaddr_r)(const void *addr, socklen_t addrlen,
+ int af, struct hostent *result,
+ char *buffer, size_t buflen,
+ int *errnop, int *h_errnop);
+ enum nss_status (*sethostent)(void);
+ enum nss_status (*gethostent_r)(struct hostent *ret,
+ char *buf, size_t buflen,
+ int *errnop, int *h_errnop);
+ enum nss_status (*endhostent)(void);
+
+ /* Networks */
+ enum nss_status (*getnetbyname_r)(const char *name,
+ struct netent *result,
+ char *buffer, size_t buflen,
+ int *errnop, int *h_errnop);
+ enum nss_status (*getnetbyaddr_r)(uint32_t addr, int type,
+ struct netent *result,
+ char *buffer, size_t buflen,
+ int *errnop, int *h_errnop);
+ enum nss_status (*setnetent)(void);
+ enum nss_status (*getnetent_r)(struct netent *ret,
+ char *buffer, size_t buflen,
+ int *errnop, int *h_errnop);
+ enum nss_status (*endnetent)(void);
+
+ void *dl_handle;
+};
+
+
+struct sss_nss_symbols {
+ void **fptr;
+ bool mandatory;
+ const char *fname;
+};
+
+errno_t sss_load_nss_symbols(struct sss_nss_ops *ops, const char *libname,
+ struct sss_nss_symbols *syms, size_t nsyms);
+
+
+#endif /* __SSSD_NSS_DL_LOAD_H__ */
diff --git a/src/util/pac_utils.c b/src/util/pac_utils.c
new file mode 100644
index 0000000..4499d8d
--- /dev/null
+++ b/src/util/pac_utils.c
@@ -0,0 +1,156 @@
+/*
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2022 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 <talloc.h>
+
+#include "config.h"
+
+#include "util/util.h"
+
+static errno_t check_check_pac_opt(const char *inp, uint32_t *check_pac_flags)
+{
+ int ret;
+ size_t c;
+ char **list = NULL;
+ uint32_t flags = 0;
+
+ if (strcasecmp(inp, CHECK_PAC_NO_CHECK_STR) == 0) {
+ flags = 0;
+ ret = EOK;
+ goto done;
+ }
+
+ ret = split_on_separator(NULL, inp, ',', true, true, &list, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to split pac_check value.\n");
+ goto done;
+ }
+
+ for (c = 0; list[c] != NULL; c++) {
+ if (strcasecmp(list[c], CHECK_PAC_NO_CHECK_STR) == 0) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "pac_check option [%s] can be only used alone.\n",
+ CHECK_PAC_NO_CHECK_STR);
+ ret = EINVAL;
+ goto done;
+ } else if (strcasecmp(list[c], CHECK_PAC_PRESENT_STR) == 0) {
+ flags |= CHECK_PAC_PRESENT;
+ } else if (strcasecmp(list[c], CHECK_PAC_CHECK_UPN_STR) == 0) {
+ flags |= CHECK_PAC_CHECK_UPN;
+ } else if (strcasecmp(list[c], CHECK_PAC_UPN_DNS_INFO_PRESENT_STR) == 0) {
+ flags |= CHECK_PAC_UPN_DNS_INFO_PRESENT;
+ flags |= CHECK_PAC_CHECK_UPN;
+ } else if (strcasecmp(list[c], CHECK_PAC_CHECK_UPN_DNS_INFO_EX_STR) == 0) {
+ flags |= CHECK_PAC_CHECK_UPN_DNS_INFO_EX;
+ } else if (strcasecmp(list[c], CHECK_PAC_UPN_DNS_INFO_EX_PRESENT_STR) == 0) {
+ flags |= CHECK_PAC_UPN_DNS_INFO_EX_PRESENT;
+ flags |= CHECK_PAC_CHECK_UPN_DNS_INFO_EX;
+ flags |= CHECK_PAC_UPN_DNS_INFO_PRESENT;
+ flags |= CHECK_PAC_CHECK_UPN;
+ } else if (strcasecmp(list[c], CHECK_PAC_CHECK_UPN_ALLOW_MISSING_STR) == 0) {
+ flags |= CHECK_PAC_CHECK_UPN_ALLOW_MISSING;
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "Unknown value [%s] for pac_check.\n",
+ list[c]);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ if ((flags & CHECK_PAC_CHECK_UPN_ALLOW_MISSING)
+ && !(flags & CHECK_PAC_CHECK_UPN)) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "pac_check option '%s' is set but '%s' is not set, this means "
+ "the UPN is not checked.\n",
+ CHECK_PAC_CHECK_UPN_ALLOW_MISSING_STR, CHECK_PAC_CHECK_UPN_STR);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(list);
+#ifndef HAVE_PAC_RESPONDER
+ if (ret == EOK && flags != 0) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "pac_check is configured but SSSD is build without PAC "
+ "responder, not all checks will be available.\n");
+ }
+#endif
+ if (ret == EOK && check_pac_flags != NULL) {
+ *check_pac_flags = flags;
+ }
+
+ return ret;
+}
+
+errno_t get_pac_check_config(struct confdb_ctx *cdb, uint32_t *pac_check_opts)
+{
+ int ret;
+ char *dummy;
+ struct sss_domain_info *dom;
+ struct sss_domain_info *domains = NULL;
+
+ ret = confdb_get_string(cdb, NULL, CONFDB_PAC_CONF_ENTRY,
+ CONFDB_PAC_CHECK, NULL, &dummy);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to get pac_check config option [%d][%s].\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (dummy == NULL) {
+ ret = confdb_get_domains(cdb, &domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to get domain list, cannot "
+ "determine pac_check defaults.\n");
+ return ret;
+ }
+
+ for (dom = domains; dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_DESCEND
+ |SSS_GND_INCLUDE_DISABLED)) {
+ if (strcasecmp(dom->provider, "ad") == 0
+ || strcasecmp(dom->provider, "ipa") == 0) {
+ break;
+ }
+ }
+
+ if (dom == NULL) {
+ /* No AD or IPA provider found */
+ dummy = talloc_strdup(NULL, CONFDB_PAC_CHECK_DEFAULT);
+ } else {
+ dummy = talloc_strdup(NULL, CONFDB_PAC_CHECK_IPA_AD_DEFAULT);
+ }
+ if (dummy == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy pac_check defaults.\n");
+ return ENOMEM;
+ }
+ }
+
+ ret = check_check_pac_opt(dummy, pac_check_opts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pac_check option [%s] is invalid.\n",
+ dummy);
+ }
+ talloc_free(dummy);
+
+ return ret;
+}
diff --git a/src/util/probes.h b/src/util/probes.h
new file mode 100644
index 0000000..effce49
--- /dev/null
+++ b/src/util/probes.h
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2015 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 __PROBES_H_
+#define __PROBES_H_
+
+#ifdef HAVE_SYSTEMTAP
+
+#include "stap_generated_probes.h"
+
+/* Probe expansion inspired by libvirt */
+#define PROBE_EXPAND(NAME, ...) NAME(__VA_ARGS__)
+
+#define PROBE(NAME, ...) do { \
+ if (SSSD_ ## NAME ## _ENABLED()) { \
+ PROBE_EXPAND(SSSD_ ## NAME, \
+ __VA_ARGS__); \
+ } \
+} while(0);
+
+/* Systemtap doesn't handle copying NULL strings well */
+#define PROBE_SAFE_STR(s) ((s) ? (s) : "")
+
+#else
+
+/* No systemtap, define empty macros */
+#define PROBE(NAME, ...) do { \
+} while(0);
+
+#endif
+
+#endif /* __PROBES_H_ */
diff --git a/src/util/refcount.c b/src/util/refcount.c
new file mode 100644
index 0000000..69873d3
--- /dev/null
+++ b/src/util/refcount.c
@@ -0,0 +1,92 @@
+/*
+ SSSD
+
+ Simple reference counting wrappers for talloc.
+
+ Authors:
+ Martin Nagy <mnagy@redhat.com>
+
+ Copyright (C) Red Hat, Inc 2009
+
+ 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 <talloc.h>
+
+#include "refcount.h"
+#include "util/util.h"
+
+struct wrapper {
+ int *refcount;
+ void *ptr;
+};
+
+static int
+refcount_destructor(struct wrapper *wrapper)
+{
+ (*wrapper->refcount)--;
+ if (*wrapper->refcount == 0) {
+ talloc_free(wrapper->ptr);
+ };
+
+ return 0;
+}
+
+void *
+_rc_alloc(const void *context, size_t size, size_t refcount_offset,
+ const char *type_name)
+{
+ struct wrapper *wrapper;
+ char *refcount_pos;
+
+ wrapper = talloc(context, struct wrapper);
+ if (wrapper == NULL) {
+ return NULL;
+ }
+
+ wrapper->ptr = talloc_named_const(NULL, size, type_name);
+ if (wrapper->ptr == NULL) {
+ talloc_free(wrapper);
+ return NULL;
+ };
+
+ refcount_pos = (char *)wrapper->ptr + refcount_offset;
+ wrapper->refcount = DISCARD_ALIGN(refcount_pos, int *);
+ *wrapper->refcount = 1;
+
+ talloc_set_destructor(wrapper, refcount_destructor);
+
+ return wrapper->ptr;
+}
+
+void *
+_rc_reference(const void *context, size_t refcount_offset, void *source)
+{
+ struct wrapper *wrapper;
+ char *refcount_pos;
+
+ wrapper = talloc(context, struct wrapper);
+ if (wrapper == NULL) {
+ return NULL;
+ }
+
+ wrapper->ptr = source;
+ refcount_pos = (char *)wrapper->ptr + refcount_offset;
+ wrapper->refcount = DISCARD_ALIGN(refcount_pos, int *);
+ (*wrapper->refcount)++;
+
+ talloc_set_destructor(wrapper, refcount_destructor);
+
+ return wrapper->ptr;
+}
diff --git a/src/util/refcount.h b/src/util/refcount.h
new file mode 100644
index 0000000..3dd71cf
--- /dev/null
+++ b/src/util/refcount.h
@@ -0,0 +1,63 @@
+/*
+ SSSD
+
+ Simple reference counting wrappers for talloc.
+
+ Authors:
+ Martin Nagy <mnagy@redhat.com>
+
+ Copyright (C) Red Hat, Inc 2009
+
+ 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 __REFCOUNT_H__
+#define __REFCOUNT_H__
+
+#include <stddef.h>
+
+#define REFCOUNT_MEMBER_NAME DO_NOT_TOUCH_THIS_MEMBER_refcount
+
+/*
+ * Include this member in your structure in order to be able to use it with
+ * the refcount_* functions.
+ */
+#define REFCOUNT_COMMON int REFCOUNT_MEMBER_NAME
+
+/*
+ * Allocate a new structure that uses reference counting. The resulting pointer
+ * returned. You must not free the returned pointer manually. It will be freed
+ * when 'ctx' is freed with talloc_free() and no other references are left.
+ */
+#define rc_alloc(ctx, type) \
+ (type *)_rc_alloc(ctx, sizeof(type), offsetof(type, REFCOUNT_MEMBER_NAME), \
+ #type)
+
+/*
+ * Increment the reference count of 'src' and return it back if we are
+ * successful. The reference count will be decremented after 'ctx' has been
+ * released by talloc_free(). The function will return NULL in case of failure.
+ */
+#define rc_reference(ctx, type, src) \
+ (type *)_rc_reference(ctx, offsetof(type, REFCOUNT_MEMBER_NAME), src)
+
+/*
+ * These functions should not be used directly. Use the above macros instead.
+ */
+void *_rc_alloc(const void *context, size_t size, size_t refcount_offset,
+ const char *type_name);
+void *_rc_reference(const void *context, size_t refcount_offset, void *source);
+
+
+#endif /* !__REFCOUNT_H__ */
diff --git a/src/util/safe-format-string.c b/src/util/safe-format-string.c
new file mode 100644
index 0000000..11532d4
--- /dev/null
+++ b/src/util/safe-format-string.c
@@ -0,0 +1,309 @@
+/*
+ * This file originated in the realmd project
+ *
+ * Copyright 2013 Red Hat Inc
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Stef Walter <stefw@redhat.com>
+ */
+
+/*
+ * Some snippets of code from gnulib, but have since been refactored
+ * to within an inch of their life...
+ *
+ * vsprintf with automatic memory allocation.
+ * Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2, 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
+ * Library General Public License for more details.
+ */
+
+#include "config.h"
+
+#include "safe-format-string.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+static void
+safe_padding (int count,
+ int *total,
+ void (* copy_fn) (void *, const char *, size_t),
+ void *data)
+{
+ char eight[] = " ";
+ int num;
+
+ while (count > 0) {
+ num = MIN (count, 8);
+ copy_fn (data, eight, num);
+ count -= num;
+ *total += num;
+ }
+}
+
+static void
+dummy_copy_fn (void *data,
+ const char *piece,
+ size_t len)
+{
+
+}
+
+int
+safe_format_string_cb (void (* copy_fn) (void *, const char *, size_t),
+ void *data,
+ const char *format,
+ const char * const args[],
+ int num_args)
+{
+ int at_arg = 0;
+ const char *cp;
+ int precision;
+ int width;
+ int len;
+ const char *value;
+ int total;
+ int left;
+ int i;
+
+ if (!copy_fn)
+ copy_fn = dummy_copy_fn;
+
+ total = 0;
+ cp = format;
+
+ while (*cp) {
+
+ /* Piece of raw string */
+ if (*cp != '%') {
+ len = strcspn (cp, "%");
+ copy_fn (data, cp, len);
+ total += len;
+ cp += len;
+ continue;
+ }
+
+ cp++;
+
+ /* An literal percent sign? */
+ if (*cp == '%') {
+ copy_fn (data, "%", 1);
+ total++;
+ cp++;
+ continue;
+ }
+
+ value = NULL;
+ left = 0;
+ precision = -1;
+ width = -1;
+
+ /* Test for positional argument. */
+ if (*cp >= '0' && *cp <= '9') {
+ /* Look-ahead parsing, otherwise skipped */
+ if (cp[strspn (cp, "0123456789")] == '$') {
+ unsigned int n = 0;
+ for (i = 0; i < 6 && *cp >= '0' && *cp <= '9'; i++, cp++) {
+ n = 10 * n + (*cp - '0');
+ }
+ /* Positional argument 0 is invalid. */
+ if (n == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* Positional argument N too high */
+ if (n > num_args) {
+ errno = EINVAL;
+ return -1;
+ }
+ value = args[n - 1];
+ cp++; /* $ */
+ }
+ }
+
+ /* Read the supported flags. */
+ for (; ; cp++) {
+ if (*cp == '-')
+ left = 1;
+ /* Supported but ignored */
+ else if (*cp != ' ')
+ break;
+ }
+
+ /* Parse the width. */
+ if (*cp >= '0' && *cp <= '9') {
+ width = 0;
+ for (i = 0; i < 6 && *cp >= '0' && *cp <= '9'; i++, cp++) {
+ width = 10 * width + (*cp - '0');
+ }
+ }
+
+ /* Parse the precision. */
+ if (*cp == '.') {
+ precision = 0;
+ for (i = 0, cp++; i < 6 && *cp >= '0' && *cp <= '9'; cp++, i++) {
+ precision = 10 * precision + (*cp - '0');
+ }
+ }
+
+ /* Read the conversion character. */
+ switch (*cp++) {
+ case 's':
+ /* Non-positional argument */
+ if (value == NULL) {
+ /* Too many arguments used */
+ if (at_arg == num_args) {
+ errno = EINVAL;
+ return -1;
+ }
+ value = args[at_arg++];
+ }
+ break;
+
+ /* No other conversion characters are supported */
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* How many characters are we printing? */
+ len = strlen (value);
+ if (precision >= 0)
+ len = MIN (precision, len);
+
+ /* Do we need padding? */
+ safe_padding (left ? 0 : width - len, &total, copy_fn, data);
+
+ /* The actual data */;
+ copy_fn (data, value, len);
+ total += len;
+
+ /* Do we need padding? */
+ safe_padding (left ? width - len : 0, &total, copy_fn, data);
+ }
+
+ return total;
+}
+
+static const char **
+valist_to_args (va_list va,
+ int *num_args)
+{
+ int alo_args;
+ const char **args;
+ const char *arg;
+ void *mem;
+
+ *num_args = alo_args = 0;
+ args = NULL;
+
+ for (;;) {
+ arg = va_arg (va, const char *);
+ if (arg == NULL)
+ break;
+ if (*num_args == alo_args) {
+ alo_args += 8;
+ mem = realloc (args, sizeof (const char *) * alo_args);
+ if (!mem) {
+ free (args);
+ return NULL;
+ }
+ args = mem;
+ }
+ args[(*num_args)++] = arg;
+ }
+
+ return args;
+}
+
+struct sprintf_ctx {
+ char *data;
+ size_t length;
+ size_t alloc;
+};
+
+static void
+snprintf_copy_fn (void *data,
+ const char *piece,
+ size_t length)
+{
+ struct sprintf_ctx *cx = data;
+
+ /* Don't copy if too much data */
+ if (cx->length > cx->alloc)
+ length = 0;
+ else if (cx->length + length > cx->alloc)
+ length = cx->alloc - cx->length;
+
+ if (length > 0)
+ memcpy (cx->data + cx->length, piece, length);
+
+ /* Null termination happens later */
+ cx->length += length;
+}
+
+int
+safe_format_string (char *str,
+ size_t len,
+ const char *format,
+ ...)
+{
+ struct sprintf_ctx cx;
+ int num_args;
+ va_list va;
+ const char **args;
+ int error = 0;
+ int ret;
+
+ cx.data = str;
+ cx.length = 0;
+ cx.alloc = len;
+
+ va_start (va, format);
+ args = valist_to_args (va, &num_args);
+ va_end (va);
+
+ if (args == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (len)
+ cx.data[0] = '\0';
+
+ ret = safe_format_string_cb (snprintf_copy_fn, &cx, format, args, num_args);
+ if (ret < 0) {
+ error = errno;
+ } else if (len > 0) {
+ cx.data[MIN (cx.length, len - 1)] = '\0';
+ }
+
+ free (args);
+
+ if (error)
+ errno = error;
+ return ret;
+}
diff --git a/src/util/safe-format-string.h b/src/util/safe-format-string.h
new file mode 100644
index 0000000..6d3ab5d
--- /dev/null
+++ b/src/util/safe-format-string.h
@@ -0,0 +1,81 @@
+/*
+ * This file originated in the realmd project
+ *
+ * Copyright 2013 Red Hat Inc
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Stef Walter <stefw@redhat.com>
+ */
+
+#include "config.h"
+
+#ifndef __SAFE_FORMAT_STRING_H__
+#define __SAFE_FORMAT_STRING_H__
+
+#include <stdlib.h>
+
+/*
+ * This is a neutered printf variant that can be used with user-provided
+ * format strings.
+ *
+ * Not only are the normal printf functions not safe to use on user-provided
+ * input (i.e.: can crash, be abused, etc.), they're also very brittle with
+ * regards to positional arguments: one must consume them all or printf will
+ * just abort(). This is because arguments of different sizes are accepted
+ * in the varargs. So obviously the positional code cannot know the offset
+ * of the relevant varargs if some are not consumed (i.e.: tagged with a
+ * field type).
+ *
+ * Thus the only accepted field type here is 's'. It's all we need.
+ *
+ * In general new code should use a better syntax than printf format strings
+ * for configuration options. This code is here to facilitate robust processing
+ * of the full_name_format syntax we already have, which has been documented as
+ * "printf(3) compatible".
+ *
+ * Features:
+ * - Only string 's' fields are supported
+ * - All the varargs should be strings, followed by a NULL argument
+ * - Both positional '%1$s' and non-positional '%s' are supported
+ * - Field widths '%8s' work as expected
+ * - Precision '%.8s' works, but precision cannot be read from a field
+ * - Left alignment flag is supported '%-8s'.
+ * - The space flag '% 8s' has no effect (it's the default for string fields).
+ * - No more than six digits are supported for widths, precisions, etc.
+ * - Percent signs are to be escaped as usual '%%'
+ *
+ * Use of other flags or field types will cause the relevant printf call to
+ * return -1. Using too many arguments or incorrect positional arguments
+ * will also cause the call to fail.
+ *
+ * Functions return -1 on failure and set errno. Otherwise they return
+ * the full length of the string that would be formatted, with the same
+ * semantics as snprintf().
+ */
+
+#ifndef GNUC_NULL_TERMINATED
+#if __GNUC__ >= 4
+#define GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+#else
+#define GNUC_NULL_TERMINATED
+#endif
+#endif
+
+int safe_format_string (char *str,
+ size_t len,
+ const char *format,
+ ...) GNUC_NULL_TERMINATED;
+
+int safe_format_string_cb (void (* callback) (void *data, const char *piece, size_t len),
+ void *data,
+ const char *format,
+ const char * const args[],
+ int num_args);
+
+#endif /* __SAFE_FORMAT_STRING_H__ */
diff --git a/src/util/selinux.c b/src/util/selinux.c
new file mode 100644
index 0000000..30d16ac
--- /dev/null
+++ b/src/util/selinux.c
@@ -0,0 +1,122 @@
+/*
+ SSSD
+
+ selinux.c
+
+ Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2010
+
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#endif
+
+#include "util/debug.h"
+
+#ifdef HAVE_SELINUX
+/*
+ * selinux_file_context - Set the security context before any file or
+ * directory creation.
+ *
+ * selinux_file_context () should be called before any creation of file,
+ * symlink, directory, ...
+ *
+ * Callers may have to Reset SELinux to create files with default
+ * contexts:
+ * reset_selinux_file_context();
+ */
+int selinux_file_context(const char *dst_name)
+{
+ struct selabel_handle *handle = NULL;
+ char *scontext = NULL;
+ char *pathname = NULL;
+ int ret;
+
+ if (is_selinux_enabled() != 1) {
+ return EOK;
+ }
+
+ pathname = realpath(dst_name, NULL);
+ if (pathname == NULL) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "realpath of %s failed [%d]: %s\n",
+ dst_name, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ /* Get the default security context for this file */
+ handle = selabel_open(SELABEL_CTX_FILE, NULL, 0);
+ if (handle == NULL) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create selabel context "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = selabel_lookup(handle, &scontext, pathname, 0);
+ if (ret < 0 && errno == ENOENT) {
+ scontext = NULL;
+ } else if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup selinux context "
+ "[%d]: %s", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ /* Set the security context for the next created file */
+ if (setfscreatecon(scontext) < 0) {
+ if (security_getenforce() != 0) {
+ ret = EFAULT;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ free(pathname);
+ freecon(scontext);
+
+ if (handle != NULL) {
+ selabel_close(handle);
+ }
+
+ return ret;
+}
+
+int reset_selinux_file_context(void)
+{
+ setfscreatecon(NULL);
+ return EOK;
+}
+
+#else /* HAVE_SELINUX */
+int selinux_file_context(const char *dst_name)
+{
+ return EOK;
+}
+
+int reset_selinux_file_context(void)
+{
+ return EOK;
+}
+#endif /* HAVE_SELINUX */
diff --git a/src/util/server.c b/src/util/server.c
new file mode 100644
index 0000000..34e8a6d
--- /dev/null
+++ b/src/util/server.c
@@ -0,0 +1,794 @@
+/*
+ SSSD
+
+ Servers setup routines
+
+ Copyright (C) Andrew Tridgell 1992-2005
+ Copyright (C) Martin Pool 2002
+ Copyright (C) Jelmer Vernooij 2002
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Simo Sorce 2008
+
+ 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 <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/prctl.h>
+#include <ldb.h>
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "util/sss_chain_id.h"
+#include "util/sss_chain_id_tevent.h"
+
+#ifdef HAVE_PRCTL
+#include <sys/prctl.h>
+#endif
+
+static TALLOC_CTX *autofree_ctx;
+
+static void server_atexit(void)
+{
+ talloc_zfree(autofree_ctx);
+}
+
+/*******************************************************************
+ Close the low 3 FDs and open dev/null in their place.
+********************************************************************/
+static void close_low_fds(void)
+{
+#ifndef VALGRIND
+ /* try and use up these file descriptors, so silly
+ library routines writing to stdout etc. won't cause havoc */
+ if (freopen ("/dev/null", "r", stdin) == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Can't freopen() stdin to /dev/null\n");
+ }
+ if (freopen ("/dev/null", "w", stdout) == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Can't freopen() stdout to /dev/null\n");
+ }
+ if (freopen ("/dev/null", "w", stderr) == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Can't freopen() stderr to /dev/null\n");
+ }
+#endif
+}
+
+static void daemon_parent_sigterm(int sig)
+{
+ _exit(0);
+}
+
+/**
+ Become a daemon, discarding the controlling terminal.
+**/
+
+static void become_daemon(void)
+{
+ pid_t pid, cpid;
+ int status;
+ int ret, error;
+
+ pid = fork();
+ if (pid == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_FATAL_FAILURE, "fork() failed: %d [%s]\n",
+ ret, strerror(ret));
+ sss_log(SSS_LOG_ERR, "can't start: fork() failed");
+ _exit(1);
+ }
+ if (pid != 0) {
+ /* Terminate parent process on demand so we can hold systemd
+ * or initd from starting next service until SSSD is initialized.
+ * We use signals directly here because we don't have a tevent
+ * context yet. */
+ CatchSignal(SIGTERM, daemon_parent_sigterm);
+
+ /* or exit when child process (i.e. sssd monitor) is terminated
+ * and return error in this case */
+ ret = 1;
+ do {
+ error = 0;
+ cpid = waitpid(pid, &status, 0);
+ if (cpid == -1) {
+ /* An error occurred while waiting */
+ error = errno;
+ if (error != EINTR) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Error [%d][%s] while waiting for child\n",
+ error, strerror(error));
+ /* Forcibly kill this child */
+ kill(pid, SIGKILL);
+ }
+ } else {
+ if (WIFEXITED(status)) {
+ /* return our exit code if available */
+ ret = WEXITSTATUS(status);
+ }
+ }
+ } while (error == EINTR);
+
+ _exit(ret);
+ }
+
+ /* create new session, process group and detach from the terminal */
+ if (setsid() == (pid_t) -1) {
+ ret = errno;
+ DEBUG(SSSDBG_FATAL_FAILURE, "setsid() failed: %d [%s]\n",
+ ret, strerror(ret));
+ sss_log(SSS_LOG_ERR, "can't start: setsid() failed");
+ _exit(1);
+ }
+
+ /* chdir to / to be sure we're not on a remote filesystem */
+ errno = 0;
+ if(chdir("/") == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_FATAL_FAILURE, "Cannot change directory (%d [%s])\n",
+ ret, strerror(ret));
+ }
+
+ /* Close FDs 0,1,2. Needed if started by rsh */
+ close_low_fds();
+}
+
+int check_pidfile(const char *file)
+{
+ char pid_str[32];
+ pid_t pid;
+ int fd;
+ int ret, err;
+ ssize_t len;
+ ssize_t pidlen = sizeof(pid_str) - 1;
+
+ fd = open(file, O_RDONLY, 0644);
+ err = errno;
+ if (fd != -1) {
+ errno = 0;
+ len = sss_atomic_read_s(fd, pid_str, pidlen);
+ ret = errno;
+ close(fd);
+ if (len == -1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "read failed [%d][%s].\n", ret, strerror(ret));
+ return EINVAL;
+ }
+
+ /* Ensure NULL-termination */
+ pid_str[len] = '\0';
+
+ /* let's check the pid */
+ pid = (pid_t)atoi(pid_str);
+ if (pid != 0) {
+ errno = 0;
+ ret = kill(pid, 0);
+ /* succeeded in signaling the process -> another sssd process */
+ if (ret == 0) {
+ return EEXIST;
+ }
+ if (ret != 0 && errno != ESRCH) {
+ err = errno;
+ return err;
+ }
+ }
+
+ /* nothing in the file or no process */
+ ret = unlink(file);
+ /* non-fatal failure */
+ if (ret != EOK) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to remove file: %s - %d [%s]!\n",
+ file, ret, sss_strerror(ret));
+ }
+ } else {
+ if (err != ENOENT) {
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int pidfile(const char *file)
+{
+ char pid_str[32];
+ int fd;
+ int ret, err;
+ size_t size;
+ ssize_t written;
+
+ ret = check_pidfile(file);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ fd = open(file, O_CREAT | O_WRONLY | O_EXCL, 0644);
+ err = errno;
+ if (fd == -1) {
+ return err;
+ }
+
+ memset(pid_str, 0, sizeof(pid_str));
+ snprintf(pid_str, sizeof(pid_str) -1, "%u\n", (unsigned int) getpid());
+ size = strlen(pid_str);
+
+ errno = 0;
+ written = sss_atomic_write_s(fd, pid_str, size);
+ err = errno;
+ close(fd);
+ if (written == -1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "write failed [%d][%s]\n", err, strerror(err));
+ return err;
+ }
+
+ if (written != size) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Wrote %zd bytes expected %zu\n", written, size);
+ return EIO;
+ }
+
+ return 0;
+}
+
+void orderly_shutdown(int status)
+{
+#if HAVE_GETPGRP
+ static int sent_sigterm;
+ int debug;
+
+ if (sent_sigterm == 0 && getpgrp() == getpid()) {
+ debug = is_socket_activated() ? SSSDBG_TRACE_INTERNAL
+ : SSSDBG_IMPORTANT_INFO;
+ DEBUG(debug, "SIGTERM: killing children\n");
+ sent_sigterm = 1;
+ kill(-getpgrp(), SIGTERM);
+ }
+#endif
+ DEBUG(SSSDBG_IMPORTANT_INFO, "Shutting down (status = %d)\n", status);
+ sss_log(SSS_LOG_INFO, "Shutting down (status = %d)", status);
+ exit(status);
+}
+
+static void default_quit(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ struct main_context *ctx = talloc_get_type(private_data, struct main_context);
+ talloc_free(ctx);
+
+ orderly_shutdown(0);
+}
+
+#ifndef HAVE_PRCTL
+static void sig_segv_abrt(int sig)
+{
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Received signal %s, shutting down\n", strsignal(sig));
+ orderly_shutdown(1);
+}
+#endif /* HAVE_PRCTL */
+
+/*
+ setup signal masks
+*/
+static void setup_signals(void)
+{
+ /* we are never interested in SIGPIPE */
+ BlockSignals(true, SIGPIPE);
+
+#if defined(SIGFPE)
+ /* we are never interested in SIGFPE */
+ BlockSignals(true, SIGFPE);
+#endif
+
+ /* We are no longer interested in USR1 */
+ BlockSignals(true, SIGUSR1);
+
+ /* We are no longer interested in SIGINT except for monitor */
+ BlockSignals(true, SIGINT);
+
+#if defined(SIGUSR2)
+ /* We are no longer interested in USR2 */
+ BlockSignals(true, SIGUSR2);
+#endif
+
+ /* POSIX demands that signals are inherited. If the invoking process has
+ * these signals masked, we will have problems, as we won't receive them. */
+ BlockSignals(false, SIGHUP);
+ BlockSignals(false, SIGTERM);
+
+#ifndef HAVE_PRCTL
+ /* If prctl is not defined on the system, try to handle
+ * some common termination signals gracefully */
+ CatchSignal(SIGSEGV, sig_segv_abrt);
+ CatchSignal(SIGABRT, sig_segv_abrt);
+#endif
+
+}
+
+/*
+ handle io on stdin
+*/
+static void server_stdin_handler(struct tevent_context *event_ctx,
+ struct tevent_fd *fde,
+ uint16_t flags, void *private)
+{
+ const char *binary_name = (const char *)private;
+ uint8_t c;
+
+ errno = 0;
+ if (sss_atomic_read_s(0, &c, 1) == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "%s: EOF on stdin - terminating\n",
+ binary_name);
+#if HAVE_GETPGRP
+ if (getpgrp() == getpid()) {
+ kill(-getpgrp(), SIGTERM);
+ }
+#endif
+ exit(0);
+ }
+}
+
+/*
+ main server helpers.
+*/
+
+int die_if_parent_died(void)
+{
+#ifdef HAVE_PRCTL
+ int ret;
+
+ errno = 0;
+ ret = prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE, "prctl failed [%d]: %s\n",
+ ret, strerror(ret));
+ return ret;
+ }
+#endif
+ return EOK;
+}
+
+struct logrotate_ctx {
+ struct confdb_ctx *confdb;
+ const char *confdb_path;
+};
+
+static void te_server_hup(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ errno_t ret;
+ struct logrotate_ctx *lctx =
+ talloc_get_type(private_data, struct logrotate_ctx);
+
+ DEBUG(SSSDBG_IMPORTANT_INFO, "Received SIGHUP. Rotating logfiles.\n");
+
+ ret = server_common_rotate_logs(lctx->confdb, lctx->confdb_path);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Could not reopen log file [%s]\n",
+ strerror(ret));
+ }
+}
+
+errno_t server_common_rotate_logs(struct confdb_ctx *confdb,
+ const char *conf_path)
+{
+ errno_t ret;
+ int old_debug_level = debug_level;
+
+ ret = rotate_debug_files();
+ if (ret) {
+ sss_log(SSS_LOG_ALERT, "Could not rotate debug files! [%d][%s]\n",
+ ret, strerror(ret));
+ return ret;
+ }
+
+ /* Get new debug level from the confdb */
+ ret = confdb_get_int(confdb, conf_path,
+ CONFDB_SERVICE_DEBUG_LEVEL,
+ old_debug_level,
+ &debug_level);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ /* Try to proceed with the old value */
+ debug_level = old_debug_level;
+ }
+
+ if (debug_level != old_debug_level) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Debug level changed to %#.4x\n", debug_level);
+ debug_level = debug_convert_old_level(debug_level);
+ }
+
+ return EOK;
+}
+
+errno_t generic_get_debug_level(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ void *pvt_data,
+ uint32_t *_debug_level)
+{
+ *_debug_level = debug_level;
+ return EOK;
+}
+
+errno_t generic_set_debug_level(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ void *pvt_data,
+ uint32_t new_debug_level)
+{
+ debug_level = new_debug_level;
+ return EOK;
+}
+
+static const char *get_db_path(void)
+{
+#ifdef UNIT_TESTING
+#ifdef TEST_DB_PATH
+ return TEST_DB_PATH;
+#else
+ #error "TEST_DB_PATH must be defined when unit testing server.c!"
+#endif /* TEST_DB_PATH */
+#else
+ return DB_PATH;
+#endif /* UNIT_TESTING */
+}
+
+static const char *get_pid_path(void)
+{
+#ifdef UNIT_TESTING
+#ifdef TEST_PID_PATH
+ return TEST_PID_PATH;
+#else
+ #error "TEST_PID_PATH must be defined when unit testing server.c!"
+#endif /* TEST_PID_PATH */
+#else
+ return PID_PATH;
+#endif
+}
+
+int server_setup(const char *name, bool is_responder,
+ int flags,
+ uid_t uid, gid_t gid,
+ const char *conf_entry,
+ struct main_context **main_ctx,
+ bool allow_sss_loop)
+{
+ struct tevent_context *event_ctx;
+ struct main_context *ctx;
+ uint16_t stdin_event_flags;
+ char *conf_db;
+ int ret = EOK;
+ bool dt;
+ bool dm;
+ bool backtrace_enabled;
+ struct tevent_signal *tes;
+ struct logrotate_ctx *lctx;
+ char *locale;
+ int watchdog_interval;
+ pid_t my_pid;
+ char *pidfile_name;
+ int cfg_debug_level = SSSDBG_INVALID;
+ bool dumpable = true;
+
+ if (is_responder) {
+ sss_chain_id_set_format(DEBUG_CHAIN_ID_FMT_CID);
+ } else {
+ sss_chain_id_set_format(DEBUG_CHAIN_ID_FMT_RID);
+ }
+
+ talloc_enable_null_tracking();
+
+ autofree_ctx = talloc_named_const(NULL, 0, "autofree_context");
+ if (autofree_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ atexit(server_atexit);
+
+ debug_prg_name = talloc_strdup(autofree_ctx, name);
+ if (!debug_prg_name) {
+ return ENOMEM;
+ }
+
+ my_pid = getpid();
+ ret = setpgid(my_pid, my_pid);
+ if (ret != EOK) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed setting process group: %s[%d]. "
+ "We might leak processes in case of failure\n",
+ sss_strerror(ret), ret);
+ }
+
+ if (!is_socket_activated()) {
+ ret = chown_debug_file(NULL, uid, gid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot chown the debug files, debugging might not work!\n");
+ }
+
+ ret = become_user(uid, gid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Cannot become user [%"SPRIuid"][%"SPRIgid"].\n", uid, gid);
+ return ret;
+ }
+ }
+
+ if (!allow_sss_loop) {
+ ret = setenv("_SSS_LOOPS", "NO", 0);
+ if (ret != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to set _SSS_LOOPS.\n");
+ return ret;
+ }
+ }
+ /* To make sure the domain cannot be set from the environment, unset the
+ * variable explicitly when setting up any server. Backends later set the
+ * value after reading domain from the configuration */
+ ret = unsetenv(SSS_DOM_ENV);
+ if (ret != 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unsetting "SSS_DOM_ENV" failed, journald "
+ "logging might not work as expected\n");
+ }
+
+ setup_signals();
+
+ /* we want default permissions on created files to be very strict */
+ umask(SSS_DFL_UMASK);
+
+ if (flags & FLAGS_DAEMON) {
+ DEBUG(SSSDBG_IMPORTANT_INFO, "Becoming a daemon.\n");
+ become_daemon();
+ }
+
+ if (flags & FLAGS_PID_FILE) {
+ pidfile_name = talloc_asprintf(NULL, "%s/%s.pid", get_pid_path(), name);
+ if (!pidfile_name) {
+ return ENOMEM;
+ }
+ ret = pidfile(pidfile_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error creating pidfile: %s! "
+ "(%d [%s])\n", pidfile_name, ret, strerror(ret));
+ talloc_free(pidfile_name);
+ return ret;
+ }
+ talloc_free(pidfile_name);
+ }
+
+ /* Set up locale */
+ locale = setlocale(LC_ALL, "");
+ if (locale == NULL) {
+ /* Just print debug message and continue */
+ DEBUG(SSSDBG_TRACE_FUNC, "Unable to set locale\n");
+ }
+
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ /* the event context is the top level structure.
+ * Everything else should hang off that */
+ event_ctx = tevent_context_init(autofree_ctx);
+ if (event_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "The event context initialization failed\n");
+ return 1;
+ }
+
+ ctx = talloc(event_ctx, struct main_context);
+ if (ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory, aborting!\n");
+ return ENOMEM;
+ }
+
+ ctx->parent_pid = getppid();
+ ctx->event_ctx = event_ctx;
+
+ /* Set up an event handler for a SIGINT */
+ tes = tevent_add_signal(event_ctx, event_ctx, SIGINT, 0,
+ default_quit, ctx);
+ if (tes == NULL) {
+ return EIO;
+ }
+
+ /* Set up an event handler for a SIGTERM */
+ tes = tevent_add_signal(event_ctx, event_ctx, SIGTERM, 0,
+ default_quit, ctx);
+ if (tes == NULL) {
+ return EIO;
+ }
+
+ conf_db = talloc_asprintf(ctx, "%s/%s",
+ get_db_path(), CONFDB_FILE);
+ if (conf_db == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory, aborting!\n");
+ return ENOMEM;
+ }
+
+ ret = confdb_init(ctx, &ctx->confdb_ctx, conf_db);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "The confdb initialization failed\n");
+ return ret;
+ }
+
+ if (debug_level == SSSDBG_UNRESOLVED) {
+ /* set debug level if any in conf_entry */
+ ret = confdb_get_int(ctx->confdb_ctx, conf_entry,
+ CONFDB_SERVICE_DEBUG_LEVEL,
+ SSSDBG_INVALID,
+ &cfg_debug_level);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) "
+ "[%s]\n", ret, strerror(ret));
+ return ret;
+ }
+
+ if (cfg_debug_level == SSSDBG_INVALID) {
+ /* Check for the `debug` alias */
+ ret = confdb_get_int(ctx->confdb_ctx, conf_entry,
+ CONFDB_SERVICE_DEBUG_LEVEL_ALIAS,
+ SSSDBG_DEFAULT,
+ &cfg_debug_level);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) "
+ "[%s]\n", ret, strerror(ret));
+ return ret;
+ }
+ }
+
+ debug_level = debug_convert_old_level(cfg_debug_level);
+ }
+
+ /* same for debug timestamps */
+ if (debug_timestamps == SSSDBG_TIMESTAMP_UNRESOLVED) {
+ ret = confdb_get_bool(ctx->confdb_ctx, conf_entry,
+ CONFDB_SERVICE_DEBUG_TIMESTAMPS,
+ SSSDBG_TIMESTAMP_DEFAULT,
+ &dt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) "
+ "[%s]\n", ret, strerror(ret));
+ return ret;
+ }
+ if (dt) debug_timestamps = SSSDBG_TIMESTAMP_ENABLED;
+ else debug_timestamps = SSSDBG_TIMESTAMP_DISABLED;
+ }
+
+ /* same for debug microseconds */
+ if (debug_microseconds == SSSDBG_MICROSECONDS_UNRESOLVED) {
+ ret = confdb_get_bool(ctx->confdb_ctx, conf_entry,
+ CONFDB_SERVICE_DEBUG_MICROSECONDS,
+ SSSDBG_MICROSECONDS_DEFAULT,
+ &dm);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) "
+ "[%s]\n", ret, strerror(ret));
+ return ret;
+ }
+ if (dm) debug_microseconds = SSSDBG_MICROSECONDS_ENABLED;
+ else debug_microseconds = SSSDBG_MICROSECONDS_DISABLED;
+ }
+
+ ret = confdb_get_bool(ctx->confdb_ctx, conf_entry,
+ CONFDB_SERVICE_DEBUG_BACKTRACE_ENABLED,
+ true,
+ &backtrace_enabled);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading %s from confdb (%d) [%s]\n",
+ CONFDB_SERVICE_DEBUG_BACKTRACE_ENABLED, ret, strerror(ret));
+ return ret;
+ }
+ sss_debug_backtrace_enable(backtrace_enabled);
+
+ /* before opening the log file set up log rotation */
+ lctx = talloc_zero(ctx, struct logrotate_ctx);
+ if (!lctx) return ENOMEM;
+
+ lctx->confdb = ctx->confdb_ctx;
+ lctx->confdb_path = conf_entry;
+
+ tes = tevent_add_signal(ctx->event_ctx, ctx, SIGHUP, 0,
+ te_server_hup, lctx);
+ if (tes == NULL) {
+ return EIO;
+ }
+
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Starting with debug level = %#.4x\n", debug_level);
+
+ /* Setup the internal watchdog */
+ ret = confdb_get_int(ctx->confdb_ctx, conf_entry,
+ CONFDB_DOMAIN_TIMEOUT,
+ 0, &watchdog_interval);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ return ret;
+ }
+
+ if ((flags & FLAGS_NO_WATCHDOG) == 0) {
+ ret = setup_watchdog(ctx->event_ctx, watchdog_interval);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Watchdog setup failed.\n");
+ return ret;
+ }
+ }
+
+ ret = confdb_get_bool(ctx->confdb_ctx,
+ CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_DUMPABLE,
+ true, /* default value */
+ &dumpable);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to determine "CONFDB_MONITOR_DUMPABLE"\n");
+ return ret;
+ }
+ ret = prctl(PR_SET_DUMPABLE, dumpable ? 1 : 0);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set PR_SET_DUMPABLE\n");
+ return ret;
+ } else if (!dumpable) {
+ DEBUG(SSSDBG_IMPORTANT_INFO, "Core dumps are disabled!\n");
+ }
+
+ sss_chain_id_setup(ctx->event_ctx);
+
+ sss_log(SSS_LOG_INFO, "Starting up");
+
+ DEBUG(SSSDBG_TRACE_FUNC, "CONFDB: %s\n", conf_db);
+
+ if (flags & FLAGS_INTERACTIVE) {
+ /* terminate when stdin goes away */
+ stdin_event_flags = TEVENT_FD_READ;
+ } else {
+ /* stay alive forever */
+ stdin_event_flags = 0;
+ }
+
+ /* catch EOF on stdin */
+#ifdef SIGTTIN
+ signal(SIGTTIN, SIG_IGN);
+#endif
+ tevent_add_fd(event_ctx, event_ctx, STDIN_FILENO, stdin_event_flags,
+ server_stdin_handler, discard_const(name));
+
+ *main_ctx = ctx;
+ return EOK;
+}
+
+void server_loop(struct main_context *main_ctx)
+{
+ /* wait for events - this is where the server sits for most of its
+ life */
+ tevent_loop_wait(main_ctx->event_ctx);
+
+ /* as everything hangs off this event context, freeing it
+ should initiate a clean shutdown of all services */
+ talloc_free(main_ctx->event_ctx);
+}
diff --git a/src/util/session_recording.c b/src/util/session_recording.c
new file mode 100644
index 0000000..743f42f
--- /dev/null
+++ b/src/util/session_recording.c
@@ -0,0 +1,127 @@
+/*
+ SSSD
+
+ Session recording utilities
+
+ Authors:
+ Nikolai Kondrashov <Nikolai.Kondrashov@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 "util/session_recording.h"
+#include "util/debug.h"
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+errno_t session_recording_conf_load(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *cdb,
+ struct session_recording_conf *pconf)
+{
+ int ret;
+ char *str;
+ struct stat s;
+
+ if (cdb == NULL || pconf == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* Read session_recording/scope option */
+ ret = confdb_get_string(cdb, mem_ctx, CONFDB_SESSION_RECORDING_CONF_ENTRY,
+ CONFDB_SESSION_RECORDING_SCOPE, "none", &str);
+ if (ret != EOK) goto done;
+ if (strcasecmp(str, "none") == 0) {
+ pconf->scope = SESSION_RECORDING_SCOPE_NONE;
+ } else if (strcasecmp(str, "some") == 0) {
+ pconf->scope = SESSION_RECORDING_SCOPE_SOME;
+ } else if (strcasecmp(str, "all") == 0) {
+ pconf->scope = SESSION_RECORDING_SCOPE_ALL;
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unknown value for session recording scope: %s\n",
+ str);
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* If session recording is enabled at all */
+ if (pconf->scope != SESSION_RECORDING_SCOPE_NONE) {
+ /* Check that the shell exists and is executable */
+ ret = stat(SESSION_RECORDING_SHELL, &s);
+ if (ret != 0) {
+ switch (errno) {
+ case ENOENT:
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Session recording shell \"%s\" not found\n",
+ SESSION_RECORDING_SHELL);
+ ret = EINVAL;
+ goto done;
+ case EOK:
+ if ((s.st_mode & 0111) != 0111) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Session recording shell \"%s\" is not executable\n",
+ SESSION_RECORDING_SHELL);
+ ret = EINVAL;
+ goto done;
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed checking for session recording shell "
+ "\"%s\": %s\n",
+ SESSION_RECORDING_SHELL, strerror(errno));
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ }
+
+ /* Read session_recording/users option */
+ ret = confdb_get_string_as_list(cdb, mem_ctx,
+ CONFDB_SESSION_RECORDING_CONF_ENTRY,
+ CONFDB_SESSION_RECORDING_USERS,
+ &pconf->users);
+ if (ret != EOK && ret != ENOENT) goto done;
+
+ /* Read session_recording/groups option */
+ ret = confdb_get_string_as_list(cdb, mem_ctx,
+ CONFDB_SESSION_RECORDING_CONF_ENTRY,
+ CONFDB_SESSION_RECORDING_GROUPS,
+ &pconf->groups);
+ if (ret != EOK && ret != ENOENT) goto done;
+
+ /* Read session_recording/exclude users option */
+ ret = confdb_get_string_as_list(cdb, mem_ctx,
+ CONFDB_SESSION_RECORDING_CONF_ENTRY,
+ CONFDB_SESSION_RECORDING_EXCLUDE_USERS,
+ &pconf->exclude_users);
+ if (ret != EOK && ret != ENOENT) goto done;
+
+ /* Read session_recording/exclude groups option */
+ ret = confdb_get_string_as_list(cdb, mem_ctx,
+ CONFDB_SESSION_RECORDING_CONF_ENTRY,
+ CONFDB_SESSION_RECORDING_EXCLUDE_GROUPS,
+ &pconf->exclude_groups);
+ if (ret != EOK && ret != ENOENT) goto done;
+
+ ret = EOK;
+done:
+ return ret;
+}
diff --git a/src/util/session_recording.h b/src/util/session_recording.h
new file mode 100644
index 0000000..2b77b44
--- /dev/null
+++ b/src/util/session_recording.h
@@ -0,0 +1,87 @@
+/*
+ SSSD
+
+ Session recording utilities
+
+ Authors:
+ Nikolai Kondrashov <Nikolai.Kondrashov@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 __SESSION_RECORDING_H__
+#define __SESSION_RECORDING_H__
+
+#include "confdb/confdb.h"
+#include "util/util_errors.h"
+
+/** Scope of users/groups whose session should be recorded */
+enum session_recording_scope {
+ SESSION_RECORDING_SCOPE_NONE, /**< None, no users/groups */
+ SESSION_RECORDING_SCOPE_SOME, /**< Some users/groups specified elsewhere */
+ SESSION_RECORDING_SCOPE_ALL /**< All users/groups */
+};
+
+/** Session recording configuration (from "session_recording" section) */
+struct session_recording_conf {
+ /**
+ * Session recording scope:
+ * whether to record nobody, everyone, or some users/groups
+ */
+ enum session_recording_scope scope;
+ /**
+ * NULL-terminated list of users whose session should be recorded.
+ * Can be NULL, meaning empty list. Only applicable if scope is "some".
+ */
+ char **users;
+ /**
+ * NULL-terminated list of groups, members of which should have their
+ * sessions recorded. Can be NULL, meaning empty list. Only applicable if
+ * scope is "some"
+ */
+ char **groups;
+ /**
+ * NULL-terminated list of users to be excluded from recording.
+ * Can be NULL, meaning empty list. Only applicable if scope is "all".
+ */
+ char **exclude_users;
+ /**
+ * NULL-terminated list of groups, members of which should be excluded
+ * from recording. Can be NULL, meaning empty list. Only applicable if
+ * scope is "all"
+ */
+ char **exclude_groups;
+};
+
+/**
+ * Load session recording configuration from configuration database.
+ *
+ * @param mem_ctx Memory context to allocate data with.
+ * @param cdb The configuration database connection object to retrieve
+ * data from.
+ * @param pconf Location for the loaded session recording configuration.
+ *
+ * @return Status code:
+ * ENOMEM - memory allocation failed,
+ * EINVAL - configuration was invalid,
+ * EIO - an I/O error occurred while communicating with the ConfDB.
+ */
+extern errno_t session_recording_conf_load(
+ TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *cdb,
+ struct session_recording_conf *pconf);
+
+#endif /* __SESSION_RECORDING_H__ */
diff --git a/src/util/signal.c b/src/util/signal.c
new file mode 100644
index 0000000..d77b79d
--- /dev/null
+++ b/src/util/signal.c
@@ -0,0 +1,89 @@
+/*
+ Unix SMB/CIFS implementation.
+ signal handling functions
+
+ Copyright (C) Andrew Tridgell 1998
+
+ 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 "util/util.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+/**
+ * @file
+ * @brief Signal handling
+ */
+
+/**
+ Block sigs.
+**/
+
+void BlockSignals(bool block, int signum)
+{
+#ifdef HAVE_SIGPROCMASK
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set,signum);
+ sigprocmask(block?SIG_BLOCK:SIG_UNBLOCK,&set,NULL);
+#elif defined(HAVE_SIGBLOCK)
+ if (block) {
+ sigblock(sigmask(signum));
+ } else {
+ sigsetmask(siggetmask() & ~sigmask(signum));
+ }
+#else
+ /* yikes! This platform can't block signals? */
+ static int done;
+ if (!done) {
+ DEBUG(SSSDBG_FATAL_FAILURE,"WARNING: No signal blocking available\n");
+ done=1;
+ }
+#endif
+}
+
+/**
+ Catch a signal. This should implement the following semantics:
+
+ 1) The handler remains installed after being called.
+ 2) The signal should be blocked during handler execution.
+**/
+
+void (*CatchSignal(int signum,void (*handler)(int )))(int)
+{
+#ifdef HAVE_SIGACTION
+ struct sigaction act;
+ struct sigaction oldact;
+
+ memset(&act, 0, sizeof(act));
+
+ act.sa_handler = handler;
+#ifdef SA_RESTART
+ /*
+ * We *want* SIGALRM to interrupt a system call.
+ */
+ if(signum != SIGALRM)
+ act.sa_flags = SA_RESTART;
+#endif
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask,signum);
+ sigaction(signum,&act,&oldact);
+ return oldact.sa_handler;
+#else /* !HAVE_SIGACTION */
+ /* FIXME: need to handle sigvec and systems with broken signal() */
+ return signal(signum, handler);
+#endif
+}
diff --git a/src/util/sss_chain_id.c b/src/util/sss_chain_id.c
new file mode 100644
index 0000000..9623b52
--- /dev/null
+++ b/src/util/sss_chain_id.c
@@ -0,0 +1,60 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2021 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 "util/sss_chain_id.h"
+#include "config.h"
+
+extern const char *debug_chain_id_fmt;
+
+#ifdef BUILD_CHAIN_ID
+void sss_chain_id_set_format(const char *fmt)
+{
+ debug_chain_id_fmt = fmt;
+}
+
+uint64_t sss_chain_id_set(uint64_t id)
+{
+ uint64_t old_id = debug_chain_id;
+ debug_chain_id = id;
+ return old_id;
+}
+
+uint64_t sss_chain_id_get(void)
+{
+ return debug_chain_id;
+}
+#else /* BUILD_CHAIN_ID not defined */
+
+void sss_chain_id_set_format(const char *fmt)
+{
+ return;
+}
+
+uint64_t sss_chain_id_set(uint64_t id)
+{
+ return 0;
+}
+
+uint64_t sss_chain_id_get(void)
+{
+ return 0;
+}
+
+#endif /* BUILD_CHAIN_ID */
diff --git a/src/util/sss_chain_id.h b/src/util/sss_chain_id.h
new file mode 100644
index 0000000..ec5e9fa
--- /dev/null
+++ b/src/util/sss_chain_id.h
@@ -0,0 +1,37 @@
+/*
+ Authors:
+ Justin Stephenson <jstephen@redhat.com>
+
+ Copyright (C) 2021 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_CHAIN_ID_
+#define _SSS_CHAIN_ID_
+
+#include <stdint.h>
+
+extern uint64_t debug_chain_id;
+
+/* Explicitly set new chain id. The old id is returned. */
+uint64_t sss_chain_id_set(uint64_t id);
+
+/* Get the current chain id. */
+uint64_t sss_chain_id_get(void);
+
+/* Set new debug chain id logging format. */
+void sss_chain_id_set_format(const char *fmt);
+
+#endif /* _SSS_CHAIN_ID_ */
diff --git a/src/util/sss_chain_id_tevent.c b/src/util/sss_chain_id_tevent.c
new file mode 100644
index 0000000..a68607d
--- /dev/null
+++ b/src/util/sss_chain_id_tevent.c
@@ -0,0 +1,138 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2021 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 "util/sss_chain_id.h"
+
+#include <tevent.h>
+
+#ifdef BUILD_CHAIN_ID
+
+static void sss_chain_id_trace_fde(struct tevent_fd *fde,
+ enum tevent_event_trace_point point,
+ void *private_data)
+{
+ switch (point) {
+ case TEVENT_EVENT_TRACE_ATTACH:
+ /* Assign the current chain id when the event is created. */
+ tevent_fd_set_tag(fde, debug_chain_id);
+ break;
+ case TEVENT_EVENT_TRACE_BEFORE_HANDLER:
+ /* Set the chain id when a handler is being called. */
+ debug_chain_id = tevent_fd_get_tag(fde);
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
+}
+
+static void sss_chain_id_trace_signal(struct tevent_signal *se,
+ enum tevent_event_trace_point point,
+ void *private_data)
+{
+ switch (point) {
+ case TEVENT_EVENT_TRACE_ATTACH:
+ /* Assign the current chain id when the event is created. */
+ tevent_signal_set_tag(se, debug_chain_id);
+ break;
+ case TEVENT_EVENT_TRACE_BEFORE_HANDLER:
+ /* Set the chain id when a handler is being called. */
+ debug_chain_id = tevent_signal_get_tag(se);
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
+}
+
+static void sss_chain_id_trace_timer(struct tevent_timer *timer,
+ enum tevent_event_trace_point point,
+ void *private_data)
+{
+ switch (point) {
+ case TEVENT_EVENT_TRACE_ATTACH:
+ /* Assign the current chain id when the event is created. */
+ tevent_timer_set_tag(timer, debug_chain_id);
+ break;
+ case TEVENT_EVENT_TRACE_BEFORE_HANDLER:
+ /* Set the chain id when a handler is being called. */
+ debug_chain_id = tevent_timer_get_tag(timer);
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
+}
+
+static void sss_chain_id_trace_immediate(struct tevent_immediate *im,
+ enum tevent_event_trace_point point,
+ void *private_data)
+{
+ switch (point) {
+ case TEVENT_EVENT_TRACE_ATTACH:
+ /* Assign the current chain id when the event is created. */
+ tevent_immediate_set_tag(im, debug_chain_id);
+ break;
+ case TEVENT_EVENT_TRACE_BEFORE_HANDLER:
+ /* Set the chain id when a handler is being called. */
+ debug_chain_id = tevent_immediate_get_tag(im);
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
+}
+
+static void sss_chain_id_trace_loop(enum tevent_trace_point point,
+ void *private_data)
+{
+ switch (point) {
+ case TEVENT_TRACE_AFTER_LOOP_ONCE:
+ /* Reset chain id when we got back to the loop. An event handler
+ * that set chain id was fired. This tracepoint represents a place
+ * after the event handler was finished, we need to restore chain
+ * id to 0 (out of request).
+ */
+ debug_chain_id = 0;
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
+}
+
+void sss_chain_id_setup(struct tevent_context *ev)
+{
+ tevent_set_trace_callback(ev, sss_chain_id_trace_loop, NULL);
+ tevent_set_trace_fd_callback(ev, sss_chain_id_trace_fde, NULL);
+ tevent_set_trace_signal_callback(ev, sss_chain_id_trace_signal, NULL);
+ tevent_set_trace_timer_callback(ev, sss_chain_id_trace_timer, NULL);
+ tevent_set_trace_immediate_callback(ev, sss_chain_id_trace_immediate, NULL);
+}
+
+#else /* BUILD_CHAIN_ID not defined */
+
+void sss_chain_id_setup(struct tevent_context *ev)
+{
+ return;
+}
+
+#endif /* BUILD_CHAIN_ID */
diff --git a/src/util/sss_chain_id_tevent.h b/src/util/sss_chain_id_tevent.h
new file mode 100644
index 0000000..547d271
--- /dev/null
+++ b/src/util/sss_chain_id_tevent.h
@@ -0,0 +1,29 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2021 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_CHAIN_ID_TEVENT_
+#define _SSS_CHAIN_ID_TEVENT_
+
+#include <tevent.h>
+
+/* Setup chain id tracking on tevent context. */
+void sss_chain_id_setup(struct tevent_context *ev);
+
+#endif /* _SSS_CHAIN_ID_TEVENT_ */
diff --git a/src/util/sss_cli_cmd.c b/src/util/sss_cli_cmd.c
new file mode 100644
index 0000000..0b743df
--- /dev/null
+++ b/src/util/sss_cli_cmd.c
@@ -0,0 +1,244 @@
+/*
+ SSSD - cmd2str util
+
+ Copyright (C) Petr Cech <pcech@redhat.com> 2015
+
+ 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 "sss_client/sss_cli.h"
+#include "util/sss_cli_cmd.h"
+#include "util/util.h"
+
+const char *sss_cmd2str(enum sss_cli_command cmd)
+{
+ switch (cmd) {
+ /* null */
+ case SSS_CLI_NULL:
+ return "SSS_CLI_NULL";
+
+ /* version */
+ case SSS_GET_VERSION:
+ return "SSS_GET_VERSION";
+
+ /* passwd */
+ case SSS_NSS_GETPWNAM:
+ return "SSS_NSS_GETPWNAM";
+ case SSS_NSS_GETPWUID:
+ return "SSS_NSS_GETPWUID";
+ case SSS_NSS_SETPWENT:
+ return "SSS_NSS_SETPWENT";
+ case SSS_NSS_GETPWENT:
+ return "SSS_NSS_GETPWENT";
+ case SSS_NSS_ENDPWENT:
+ return "SSS_NSS_ENDPWENT";
+
+ /* group */
+ case SSS_NSS_GETGRNAM:
+ return "SSS_NSS_GETGRNAM";
+ case SSS_NSS_GETGRGID:
+ return "SSS_NSS_GETGRGID";
+ case SSS_NSS_SETGRENT:
+ return "SSS_NSS_SETGRENT";
+ case SSS_NSS_GETGRENT:
+ return "SSS_NSS_GETGRENT";
+ case SSS_NSS_ENDGRENT:
+ return "SSS_NSS_ENDGRENT";
+ case SSS_NSS_INITGR:
+ return "SSS_NSS_INITGR";
+
+#if 0
+ /* aliases */
+ case SSS_NSS_GETALIASBYNAME:
+ return "SSS_NSS_GETALIASBYNAME";
+ case SSS_NSS_GETALIASBYPORT:
+ return "SSS_NSS_GETALIASBYPORT";
+ case SSS_NSS_SETALIASENT:
+ return "SSS_NSS_SETALIASENT";
+ case SSS_NSS_GETALIASENT:
+ return "SSS_NSS_GETALIASENT";
+ case SSS_NSS_ENDALIASENT:
+ return "SSS_NSS_ENDALIASENT";
+
+ /* ethers */
+ case SSS_NSS_GETHOSTTON:
+ return "SSS_NSS_GETHOSTTON";
+ case SSS_NSS_GETNTOHOST:
+ return "SSS_NSS_GETNTOHOST";
+ case SSS_NSS_SETETHERENT:
+ return "SSS_NSS_SETETHERENT";
+ case SSS_NSS_GETETHERENT:
+ return "SSS_NSS_GETETHERENT";
+ case SSS_NSS_ENDETHERENT:
+ return "SSS_NSS_ENDETHERENT";
+
+#endif
+ /* hosts */
+ case SSS_NSS_GETHOSTBYNAME:
+ return "SSS_NSS_GETHOSTBYNAME";
+ case SSS_NSS_GETHOSTBYNAME2:
+ return "SSS_NSS_GETHOSTBYNAME2";
+ case SSS_NSS_GETHOSTBYADDR:
+ return "SSS_NSS_GETHOSTBYADDR";
+ case SSS_NSS_SETHOSTENT:
+ return "SSS_NSS_SETHOSTENT";
+ case SSS_NSS_GETHOSTENT:
+ return "SSS_NSS_GETHOSTENT";
+ case SSS_NSS_ENDHOSTENT:
+ return "SSS_NSS_ENDHOSTENT";
+
+ /* netgroup */
+ case SSS_NSS_SETNETGRENT:
+ return "SSS_NSS_SETNETGRENT";
+ case SSS_NSS_GETNETGRENT:
+ return "SSS_NSS_GETNETGRENT";
+ case SSS_NSS_ENDNETGRENT:
+ return "SSS_NSS_ENDNETGRENT";
+
+ /* networks */
+ case SSS_NSS_GETNETBYNAME:
+ return "SSS_NSS_GETNETBYNAME";
+ case SSS_NSS_GETNETBYADDR:
+ return "SSS_NSS_GETNETBYADDR";
+ case SSS_NSS_SETNETENT:
+ return "SSS_NSS_SETNETENT";
+ case SSS_NSS_GETNETENT:
+ return "SSS_NSS_GETNETENT";
+ case SSS_NSS_ENDNETENT:
+ return "SSS_NSS_ENDNETENT";
+
+#if 0
+ /* protocols */
+ case SSS_NSS_GETPROTOBYNAME:
+ return "SSS_NSS_GETPROTOBYNAME";
+ case SSS_NSS_GETPROTOBYNUM:
+ return "SSS_NSS_GETPROTOBYNUM";
+ case SSS_NSS_SETPROTOENT:
+ return "SSS_NSS_SETPROTOENT";
+ case SSS_NSS_GETPROTOENT:
+ return "SSS_NSS_GETPROTOENT";
+ case SSS_NSS_ENDPROTOENT:
+ return "SSS_NSS_ENDPROTOENT";
+
+ /* rpc */
+ case SSS_NSS_GETRPCBYNAME:
+ return "SSS_NSS_GETRPCBYNAME";
+ case SSS_NSS_GETRPCBYNUM:
+ return "SSS_NSS_GETRPCBYNUM";
+ case SSS_NSS_SETRPCENT:
+ return "SSS_NSS_SETRPCENT";
+ case SSS_NSS_GETRPCENT:
+ return "SSS_NSS_GETRPCENT";
+ case SSS_NSS_ENDRPCENT:
+ return "SSS_NSS_ENDRPCENT";
+#endif
+
+ /* services */
+ case SSS_NSS_GETSERVBYNAME:
+ return "SSS_NSS_GETSERVBYNAME";
+ case SSS_NSS_GETSERVBYPORT:
+ return "SSS_NSS_GETSERVBYPORT";
+ case SSS_NSS_SETSERVENT:
+ return "SSS_NSS_SETSERVENT";
+ case SSS_NSS_GETSERVENT:
+ return "SSS_NSS_GETSERVENT";
+ case SSS_NSS_ENDSERVENT:
+ return "SSS_NSS_ENDSERVENT";
+
+#if 0
+ /* shadow */
+ case SSS_NSS_GETSPNAM:
+ return "SSS_NSS_GETSPNAM";
+ case SSS_NSS_GETSPUID:
+ return "SSS_NSS_GETSPUID";
+ case SSS_NSS_SETSPENT:
+ return "SSS_NSS_SETSPENT";
+ case SSS_NSS_GETSPENT:
+ return "SSS_NSS_GETSPENT";
+ case SSS_NSS_ENDSPENT:
+ return "SSS_NSS_ENDSPENT";
+#endif
+
+ /* SUDO */
+ case SSS_SUDO_GET_SUDORULES:
+ return "SSS_SUDO_GET_SUDORULES";
+ case SSS_SUDO_GET_DEFAULTS:
+ return "SSS_SUDO_GET_DEFAULTS";
+
+ /* autofs */
+ case SSS_AUTOFS_SETAUTOMNTENT:
+ return "SSS_AUTOFS_SETAUTOMNTENT";
+ case SSS_AUTOFS_GETAUTOMNTENT:
+ return "SSS_AUTOFS_GETAUTOMNTENT";
+ case SSS_AUTOFS_GETAUTOMNTBYNAME:
+ return "SSS_AUTOFS_GETAUTOMNTBYNAME";
+ case SSS_AUTOFS_ENDAUTOMNTENT:
+ return "SSS_AUTOFS_ENDAUTOMNTENT";
+
+ /* SSH */
+ case SSS_SSH_GET_USER_PUBKEYS:
+ return "SSS_SSH_GET_USER_PUBKEYS";
+ case SSS_SSH_GET_HOST_PUBKEYS:
+ return "SSS_SSH_GET_HOST_PUBKEYS";
+
+ /* PAM related calls */
+ case SSS_PAM_AUTHENTICATE:
+ return "SSS_PAM_AUTHENTICATE";
+ case SSS_PAM_SETCRED:
+ return "SSS_PAM_SETCRED";
+ case SSS_PAM_ACCT_MGMT:
+ return "SSS_PAM_ACCT_MGMT";
+ case SSS_PAM_OPEN_SESSION:
+ return "SSS_PAM_OPEN_SESSION";
+ case SSS_PAM_CLOSE_SESSION:
+ return "SSS_PAM_CLOSE_SESSION";
+ case SSS_PAM_CHAUTHTOK:
+ return "SSS_PAM_CHAUTHTOK";
+ case SSS_PAM_CHAUTHTOK_PRELIM:
+ return "SSS_PAM_CHAUTHTOK_PRELIM";
+ case SSS_CMD_RENEW:
+ return "SSS_CMD_RENEW";
+ case SSS_PAM_PREAUTH:
+ return "SSS_PAM_PREAUTH";
+
+ /* PAC responder calls */
+ case SSS_PAC_ADD_PAC_USER:
+ return "SSS_PAC_ADD_PAC_USER";
+
+ /* ID-SID mapping calls */
+ case SSS_NSS_GETSIDBYNAME:
+ return "SSS_NSS_GETSIDBYNAME";
+ case SSS_NSS_GETSIDBYID:
+ return "SSS_NSS_GETSIDBYID";
+ case SSS_NSS_GETNAMEBYSID:
+ return "SSS_NSS_GETNAMEBYSID";
+ case SSS_NSS_GETIDBYSID:
+ return "SSS_NSS_GETIDBYSID";
+ case SSS_NSS_GETORIGBYNAME:
+ return "SSS_NSS_GETORIGBYNAME";
+ case SSS_NSS_GETORIGBYUSERNAME:
+ return "SSS_NSS_GETORIGBYUSERNAME";
+ case SSS_NSS_GETORIGBYGROUPNAME:
+ return "SSS_NSS_GETORIGBYGROUPNAME";
+
+ /* SUBID ranges */
+ case SSS_NSS_GET_SUBID_RANGES:
+ return "SSS_NSS_GET_SUBID_RANGES";
+
+ default:
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Translation's string is missing for command [%#x].\n", cmd);
+ return "UNKNOWN COMMAND";
+ }
+}
diff --git a/src/util/sss_cli_cmd.h b/src/util/sss_cli_cmd.h
new file mode 100644
index 0000000..66ad076
--- /dev/null
+++ b/src/util/sss_cli_cmd.h
@@ -0,0 +1,28 @@
+/*
+ SSSD - cmd2str util
+
+ Copyright (C) Petr Cech <pcech@redhat.com> 2015
+
+ 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_CLI_CMD_H__
+#define __SSS_CLI_CMD_H__
+
+#include "sss_client/sss_cli.h"
+
+/* Translate sss_cli_command to human readable form. */
+const char *sss_cmd2str(enum sss_cli_command cmd);
+
+#endif /* __SSS_CLI_CMD_H__ */
diff --git a/src/util/sss_endian.h b/src/util/sss_endian.h
new file mode 100644
index 0000000..834c359
--- /dev/null
+++ b/src/util/sss_endian.h
@@ -0,0 +1,57 @@
+/*
+ SSSD
+
+ Authors:
+ Lukas Slebodnik <lslebodn@redhat.com>
+
+ Copyright (C) 2013 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_ENDIAN_H_
+#define SSS_ENDIAN_H_
+
+#ifdef HAVE_ENDIAN_H
+# include <endian.h>
+#elif defined(HAVE_SYS_ENDIAN_H)
+# include <sys/endian.h>
+#endif /* !HAVE_ENDIAN_H && !HAVE_SYS_ENDIAN_H */
+
+/* Endianness-compatibility for systems running older versions of glibc */
+
+#ifndef le32toh
+#ifndef HAVE_BYTESWAP_H
+#error missing le32toh and byteswap.h
+#else /* defined HAVE_BYTESWAP_H */
+#include <byteswap.h>
+
+/* support RHEL5 lack of definitions */
+/* Copied from endian.h on glibc 2.15 */
+#ifdef __USE_BSD
+/* Conversion interfaces. */
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+# define le32toh(x) (x)
+# define htole32(x) (x)
+# else
+# define le32toh(x) __bswap_32 (x)
+# define htole32(x) __bswap_32 (x)
+# endif
+#endif /* __USE_BSD */
+
+#endif /* HAVE_BYTESWAP_H */
+
+#endif /* le32toh */
+
+#endif /* SSS_ENDIAN_H_ */
diff --git a/src/util/sss_format.h b/src/util/sss_format.h
new file mode 100644
index 0000000..6f2735b
--- /dev/null
+++ b/src/util/sss_format.h
@@ -0,0 +1,76 @@
+/*
+ SSSD
+
+ sss_format.h
+
+ Authors:
+ Lukas Slebodnik <lslebodn@redhat.com>
+
+ Copyright (C) 2013 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_FORMAT_H__
+#define __SSS_FORMAT_H__
+
+#include "config.h"
+
+#include <inttypes.h>
+
+/* key_serial_t is defined in keyutils.h as typedef int32_t */
+#define SPRIkey_ser PRId32
+
+/* rlim_t is defined with conditional build as unsigned type.
+ * It seems that sizeof(rlim_t) is 8. It may be platform dependent, therefore
+ * the same format will be used like with uint64_t.
+ */
+#define SPRIrlim PRIu64
+
+#if SIZEOF_ID_T == 8
+# define SPRIid PRIu64
+#elif SIZEOF_ID_T == 4
+# define SPRIid PRIu32
+#else
+# error Unexpected sizeof id_t
+#endif /* SIZEOF_ID_T */
+
+#if SIZEOF_UID_T == 8
+# define SPRIuid PRIu64
+#elif SIZEOF_UID_T == 4
+# define SPRIuid PRIu32
+#else
+# error Unexpected sizeof uid_t
+#endif /* SIZEOF_UID_T */
+
+#if SIZEOF_GID_T == 8
+# define SPRIgid PRIu64
+#elif SIZEOF_GID_T == 4
+# define SPRIgid PRIu32
+#else
+# error Unexpected sizeof gid_t
+#endif /* SIZEOF_GID_T */
+
+#if SIZEOF_TIME_T == 8
+# define SPRItime PRId64
+#elif SIZEOF_TIME_T == 4
+# define SPRItime PRId32
+#else
+# error Unexpected sizeof time_t
+#endif /* SIZEOF_TIME_T */
+
+
+#endif /* __SSS_FORMAT_H__ */
diff --git a/src/util/sss_ini.c b/src/util/sss_ini.c
new file mode 100644
index 0000000..3f71b18
--- /dev/null
+++ b/src/util/sss_ini.c
@@ -0,0 +1,969 @@
+/*
+ SSSD
+
+ sss_ini.c
+
+ Authors:
+ Ondrej Kos <okos@redhat.com>
+
+ Copyright (C) 2013 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 <stdio.h>
+#include <errno.h>
+#include <talloc.h>
+
+#include "config.h"
+#include "util/util.h"
+#include "util/sss_ini.h"
+#include "confdb/confdb_setup.h"
+#include "confdb/confdb_private.h"
+
+#include "ini_configobj.h"
+#include "ini_config.h"
+
+struct sss_ini {
+ char **error_list;
+ struct ref_array *ra_success_list;
+ struct ref_array *ra_error_list;
+ struct ini_cfgobj *sssd_config;
+ struct value_obj *obj;
+ const struct stat *cstat;
+ struct ini_cfgfile *file;
+ bool main_config_exists;
+};
+
+#define sss_ini_get_sec_list ini_get_section_list
+#define sss_ini_get_attr_list ini_get_attribute_list
+#define sss_ini_get_const_string_config_value ini_get_const_string_config_value
+#define sss_ini_get_config_obj ini_get_config_valueobj
+
+
+static void sss_ini_free_error_messages(struct sss_ini *self)
+{
+ if (self != NULL) {
+ ini_config_free_errors(self->error_list);
+ self->error_list = NULL;
+ }
+}
+
+static void sss_ini_free_ra_messages(struct sss_ini *self)
+{
+ if (self != NULL) {
+ ref_array_destroy(self->ra_success_list);
+ self->ra_success_list = NULL;
+ ref_array_destroy(self->ra_error_list);
+ self->ra_error_list = NULL;
+ }
+}
+
+static void sss_ini_free_config(struct sss_ini *self)
+{
+ if (self != NULL && self->sssd_config != NULL) {
+ ini_config_destroy(self->sssd_config);
+ self->sssd_config = NULL;
+ }
+}
+
+/* Close file descriptor */
+
+static void sss_ini_close_file(struct sss_ini *self)
+{
+ if (self != NULL && self->file != NULL) {
+ ini_config_file_destroy(self->file);
+ self->file = NULL;
+ }
+}
+
+/* sss_ini destructor */
+
+static int sss_ini_destroy(struct sss_ini *self)
+{
+ sss_ini_free_error_messages(self);
+ sss_ini_free_ra_messages(self);
+ sss_ini_free_config(self);
+ sss_ini_close_file(self);
+ return 0;
+}
+
+/* Initialize data structure */
+
+struct sss_ini* sss_ini_new(TALLOC_CTX *mem_ctx)
+{
+ struct sss_ini *self;
+
+
+ self = talloc_zero(mem_ctx, struct sss_ini);
+ if (!self) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Not enough memory for sss_ini_data.\n");
+ return NULL;
+ }
+ talloc_set_destructor(self, sss_ini_destroy);
+ return self;
+}
+
+/* Open configuration file */
+
+static int sss_ini_config_file_open(struct sss_ini *self,
+ const char *config_file)
+{
+ int ret;
+
+ if (self == NULL) {
+ return EINVAL;
+ }
+
+ ret = ini_config_file_open(config_file,
+ INI_META_STATS,
+ &self->file);
+ self->main_config_exists = (ret != ENOENT);
+ return ret;
+}
+
+static int sss_ini_config_file_from_mem(struct sss_ini *self,
+ void *data_buf,
+ uint32_t data_len)
+{
+ if (self == NULL) {
+ return EINVAL;
+ }
+
+ return ini_config_file_from_mem(data_buf, strlen(data_buf),
+ &self->file);
+}
+
+/* Check configuration file permissions */
+
+static int sss_ini_access_check(struct sss_ini *self)
+{
+ if (!self->main_config_exists) {
+ return EOK;
+ }
+
+ return ini_config_access_check(self->file,
+ INI_ACCESS_CHECK_MODE |
+ INI_ACCESS_CHECK_UID |
+ INI_ACCESS_CHECK_GID,
+ 0, /* owned by root */
+ 0, /* owned by root */
+ S_IRUSR, /* r**------ */
+ ALLPERMS & ~(S_IWUSR|S_IXUSR));
+}
+
+
+
+/* Get cstat */
+
+int sss_ini_get_stat(struct sss_ini *self)
+{
+ self->cstat = ini_config_get_stat(self->file);
+
+ if (!self->cstat) return EIO;
+
+ return EOK;
+}
+
+
+
+/* Get mtime */
+
+int sss_ini_get_mtime(struct sss_ini *self,
+ size_t timestr_len,
+ char *timestr)
+{
+ return snprintf(timestr, timestr_len, "%llu",
+ (long long unsigned)self->cstat->st_mtime);
+}
+
+/* Get file_exists */
+
+bool sss_ini_exists(struct sss_ini *self)
+{
+ return self->main_config_exists;
+}
+
+/* Print ini_config errors */
+
+static void sss_ini_config_print_errors(char **error_list)
+{
+ unsigned count = 0;
+
+ if (!error_list) {
+ return;
+ }
+
+ while (error_list[count]) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "%s\n", error_list[count]);
+ count++;
+ }
+}
+
+static int sss_ini_parse(struct sss_ini *self)
+{
+ int ret;
+
+ if (!self) {
+ return EINVAL;
+ }
+
+ sss_ini_free_error_messages(self);
+ sss_ini_free_config(self);
+
+ /* Create config object */
+ ret = ini_config_create(&(self->sssd_config));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to create config object. Error %d.\n", ret);
+ return ret;
+ }
+
+ /* Parse file */
+ ret = ini_config_parse(self->file,
+ INI_STOP_ON_ANY,
+ INI_MV1S_OVERWRITE,
+ INI_PARSE_NOWRAP,
+ self->sssd_config);
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to parse configuration. Error %d.\n", ret);
+
+ if (ini_config_error_count(self->sssd_config)) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Errors detected while parsing: %s\n",
+ ini_config_get_filename(self->file));
+
+ ini_config_get_errors(self->sssd_config,
+ &(self->error_list));
+ }
+ }
+ return ret;
+}
+
+static int sss_ini_add_snippets(struct sss_ini *self,
+ const char *config_dir)
+{
+#ifdef HAVE_LIBINI_CONFIG_V1_3
+ int ret;
+ const char *patterns[] = { "^[^\\.].*\\.conf$", NULL };
+ const char *sections[] = { ".*", NULL };
+ uint32_t i = 0;
+ char *msg = NULL;
+ struct ini_cfgobj *modified_sssd_config = NULL;
+ struct access_check snip_check;
+
+ if (self == NULL || self->sssd_config == NULL || config_dir == NULL) {
+ return EINVAL;
+ }
+
+ sss_ini_free_ra_messages(self);
+
+ snip_check.flags = INI_ACCESS_CHECK_MODE | INI_ACCESS_CHECK_UID
+ | INI_ACCESS_CHECK_GID;
+ snip_check.uid = 0; /* owned by root */
+ snip_check.gid = 0; /* owned by root */
+ snip_check.mode = S_IRUSR; /* r**------ */
+ snip_check.mask = ALLPERMS & ~(S_IWUSR | S_IXUSR);
+
+ ret = ini_config_augment(self->sssd_config,
+ config_dir,
+ patterns,
+ sections,
+ &snip_check,
+ INI_STOP_ON_ANY,
+ INI_MV1S_OVERWRITE,
+ INI_PARSE_NOWRAP,
+ INI_MV2S_OVERWRITE,
+ &modified_sssd_config,
+ &self->ra_error_list,
+ &self->ra_success_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to augment configuration: Error %d",
+ ret);
+ }
+
+ while (ref_array_get(self->ra_success_list, i, &msg) != NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Config merge success: %s\n", msg);
+ i++;
+ }
+
+ i = 0;
+ while (ref_array_get(self->ra_error_list, i, &msg) != NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Config merge error: %s\n", msg);
+ i++;
+ }
+
+ /* switch config objects if there are no errors */
+ if (modified_sssd_config != NULL) {
+ ini_config_destroy(self->sssd_config);
+ self->sssd_config = modified_sssd_config;
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Using only main configuration file due to errors in merging\n");
+ }
+ return ret;
+
+#else /* HAVE_LIBINI_CONFIG_V1_3 */
+ return EOK;
+#endif /* ! HAVE_LIBINI_CONFIG_V1_3 */
+}
+
+struct ref_array *
+sss_ini_get_ra_success_list(struct sss_ini *self)
+{
+#ifdef HAVE_LIBINI_CONFIG_V1_3
+ return self->ra_success_list;
+#else
+ return NULL;
+#endif /* HAVE_LIBINI_CONFIG_V1_3 */
+}
+
+struct ref_array *
+sss_ini_get_ra_error_list(struct sss_ini *self)
+{
+#ifdef HAVE_LIBINI_CONFIG_V1_3
+ return self->ra_error_list;
+#else
+ return NULL;
+#endif /* HAVE_LIBINI_CONFIG_V1_3 */
+}
+
+/* Get configuration object */
+
+int sss_ini_get_cfgobj(struct sss_ini *self,
+ const char *section, const char *name)
+{
+ return sss_ini_get_config_obj(section,name, self->sssd_config,
+ INI_GET_FIRST_VALUE, &self->obj);
+}
+
+/* Check configuration object */
+
+int sss_ini_check_config_obj(struct sss_ini *self)
+{
+ if (self->obj == NULL) {
+ return ENOENT;
+ }
+
+ return EOK;
+}
+
+
+
+/* Get integer value */
+
+int sss_ini_get_int_config_value(struct sss_ini *self,
+ int strict, int def, int *error)
+{
+ return ini_get_int_config_value(self->obj, strict, def, error);
+}
+
+/* Get string value */
+
+const char *sss_ini_get_string_config_value(struct sss_ini *self,
+ int *error)
+{
+ return ini_get_string_config_value(self->obj, error);
+}
+
+/* Create LDIF */
+
+int sss_confdb_create_ldif(TALLOC_CTX *mem_ctx,
+ struct sss_ini *self,
+ const char *only_section,
+ const char **config_ldif)
+{
+ int ret, i, j;
+ char *ldif;
+ char *tmp_ldif;
+ char **sections;
+ int section_count;
+ char *dn;
+ char *tmp_dn;
+ char *sec_dn;
+ char **attrs;
+ int attr_count;
+ char *ldif_attr;
+ TALLOC_CTX *tmp_ctx;
+ size_t dn_size;
+ size_t ldif_len;
+ size_t attr_len;
+ struct value_obj *obj = NULL;
+ bool section_handled = true;
+
+ if (only_section != NULL) {
+ /* If the section is specified, we must handle it, either by adding
+ * its contents or by deleting the section if it doesn't exist
+ */
+ section_handled = false;
+ }
+
+ ldif_len = strlen(CONFDB_INTERNAL_LDIF);
+ ldif = talloc_array(mem_ctx, char, ldif_len+1);
+ if (!ldif) return ENOMEM;
+
+ tmp_ctx = talloc_new(ldif);
+ if (!tmp_ctx) {
+ ret = ENOMEM;
+ goto error;
+ }
+
+ memcpy(ldif, CONFDB_INTERNAL_LDIF, ldif_len);
+
+ /* Read in the collection and convert it to an LDIF */
+ /* Get the list of sections */
+ sections = sss_ini_get_sec_list(self->sssd_config,
+ &section_count, &ret);
+ if (ret != EOK) {
+ goto error;
+ }
+
+ for (i = 0; i < section_count; i++) {
+ const char *rdn = NULL;
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Processing config section [%s]\n", sections[i]);
+ ret = parse_section(tmp_ctx, sections[i], &sec_dn, &rdn);
+ if (ret != EOK) {
+ goto error;
+ }
+
+ if (only_section != NULL) {
+ if (strcasecmp(only_section, sections[i])) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Skipping section %s\n", sections[i]);
+ continue;
+ } else {
+ /* Mark the requested section as handled so that we don't
+ * try to re-add it later
+ */
+ section_handled = true;
+ }
+ }
+
+ dn = talloc_asprintf(tmp_ctx,
+ "dn: %s,cn=config\n"
+ "cn: %s\n",
+ sec_dn, rdn);
+ if (!dn) {
+ ret = ENOMEM;
+ free_section_list(sections);
+ goto error;
+ }
+ dn_size = strlen(dn);
+
+ /* Get all of the attributes and their values as LDIF */
+ attrs = sss_ini_get_attr_list(self->sssd_config, sections[i],
+ &attr_count, &ret);
+ if (ret != EOK) {
+ free_section_list(sections);
+ goto error;
+ }
+
+ for (j = 0; j < attr_count; j++) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Processing attribute [%s]\n", attrs[j]);
+ ret = sss_ini_get_config_obj(sections[i], attrs[j],
+ self->sssd_config,
+ INI_GET_FIRST_VALUE, &obj);
+ if (ret != EOK) goto error;
+
+ const char *value = sss_ini_get_const_string_config_value(obj, &ret);
+ if (ret != EOK) goto error;
+ if (value && value[0] == '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Attribute '%s' has empty value, ignoring\n",
+ attrs[j]);
+ continue;
+ }
+
+ ldif_attr = talloc_asprintf(tmp_ctx,
+ "%s: %s\n", attrs[j], value);
+ DEBUG(SSSDBG_TRACE_ALL, "%s\n", ldif_attr);
+
+ attr_len = strlen(ldif_attr);
+
+ tmp_dn = talloc_realloc(tmp_ctx, dn, char,
+ dn_size+attr_len+1);
+ if (!tmp_dn) {
+ ret = ENOMEM;
+ free_attribute_list(attrs);
+ free_section_list(sections);
+ goto error;
+ }
+ dn = tmp_dn;
+ memcpy(dn+dn_size, ldif_attr, attr_len+1);
+ dn_size += attr_len;
+ }
+
+ dn_size ++;
+ tmp_dn = talloc_realloc(tmp_ctx, dn, char,
+ dn_size+1);
+ if (!tmp_dn) {
+ ret = ENOMEM;
+ free_attribute_list(attrs);
+ free_section_list(sections);
+ goto error;
+ }
+ dn = tmp_dn;
+ dn[dn_size-1] = '\n';
+ dn[dn_size] = '\0';
+
+ DEBUG(SSSDBG_TRACE_ALL, "Section dn\n%s\n", dn);
+
+ tmp_ldif = talloc_realloc(mem_ctx, ldif, char,
+ ldif_len+dn_size+1);
+ if (!tmp_ldif) {
+ ret = ENOMEM;
+ free_attribute_list(attrs);
+ free_section_list(sections);
+ goto error;
+ }
+ ldif = tmp_ldif;
+ memcpy(ldif+ldif_len, dn, dn_size);
+ ldif_len += dn_size;
+
+ free_attribute_list(attrs);
+ talloc_free(dn);
+ }
+
+
+ if (only_section != NULL && section_handled == false) {
+ /* If only a single section was supposed to be
+ * handled, but it wasn't found in the INI file,
+ * create an LDIF that would remove the section
+ */
+ ret = parse_section(tmp_ctx, only_section, &sec_dn, NULL);
+ if (ret != EOK) {
+ goto error;
+ }
+
+ dn = talloc_asprintf(tmp_ctx,
+ "dn: %s,cn=config\n"
+ "changetype: delete\n\n",
+ sec_dn);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto error;
+ }
+ dn_size = strlen(dn);
+
+ tmp_ldif = talloc_realloc(mem_ctx, ldif, char,
+ ldif_len+dn_size+1);
+ if (!tmp_ldif) {
+ ret = ENOMEM;
+ goto error;
+ }
+
+ ldif = tmp_ldif;
+ memcpy(ldif+ldif_len, dn, dn_size);
+ ldif_len += dn_size;
+ }
+
+ ldif[ldif_len] = '\0';
+
+ free_section_list(sections);
+
+ *config_ldif = (const char *)ldif;
+ talloc_free(tmp_ctx);
+ return EOK;
+
+error:
+ talloc_free(ldif);
+ return ret;
+}
+
+#ifdef HAVE_LIBINI_CONFIG_V1_3
+static errno_t check_domain_inherit_from(char *cfg_section,
+ struct ini_cfgobj *config_obj,
+ struct ini_errobj *errobj)
+{
+ struct value_obj *vo = NULL;
+ int ret;
+
+ ret = ini_get_config_valueobj(cfg_section,
+ "inherit_from",
+ config_obj,
+ INI_GET_NEXT_VALUE,
+ &vo);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (vo != NULL) {
+ ret = ini_errobj_add_msg(errobj,
+ "Attribute 'inherit_from' is not "
+ "allowed in section '%s'.",
+ cfg_section);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+static errno_t check_domain_id_provider(char *cfg_section,
+ struct ini_cfgobj *config_obj,
+ struct ini_errobj *errobj)
+{
+ struct value_obj *vo = NULL;
+ const char *valid_values[] = { "ad", "ipa", "ldap", "proxy",
+#ifdef BUILD_FILES_PROVIDER
+ "files",
+#endif
+ NULL };
+ const char **valid_value;
+ const char *value;
+ int ret;
+
+ ret = ini_get_config_valueobj(cfg_section,
+ "id_provider",
+ config_obj,
+ INI_GET_NEXT_VALUE,
+ &vo);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (vo == NULL) {
+ ret = ini_errobj_add_msg(errobj,
+ "Attribute 'id_provider' is "
+ "missing in section '%s'.",
+ cfg_section);
+ } else {
+ value = sss_ini_get_const_string_config_value(vo, &ret);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ valid_value = valid_values;
+ while (*valid_value != NULL) {
+ if (strcmp(value, *valid_value) == 0) {
+ break;
+ }
+ valid_value++;
+ }
+ if (*valid_value == NULL) {
+ ret = ini_errobj_add_msg(errobj,
+ "Attribute 'id_provider' in section '%s' "
+ "has an invalid value: %s",
+ cfg_section, value);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+#define SECTION_IS_DOMAIN(s) \
+ (strncmp("domain/", s, strlen("domain/")) == 0)
+#define SECTION_DOMAIN_IS_SUBDOMAIN(s) \
+ (strchr(s + strlen("domain/"), '/') != NULL)
+
+/* Here we can put custom SSSD specific checks that can not be implemented
+ * using libini validators */
+static int custom_sssd_checks(const char *rule_name,
+ struct ini_cfgobj *rules_obj,
+ struct ini_cfgobj *config_obj,
+ struct ini_errobj *errobj,
+ void **data)
+{
+ char **cfg_sections = NULL;
+ int num_cfg_sections;
+ int ret;
+
+ /* Get all sections in configuration */
+ cfg_sections = ini_get_section_list(config_obj, &num_cfg_sections, &ret);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* Check a normal domain section (not application domains) */
+ for (int i = 0; i < num_cfg_sections; i++) {
+ if (SECTION_IS_DOMAIN(cfg_sections[i])) {
+ ret = check_domain_inherit_from(cfg_sections[i], config_obj, errobj);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (!SECTION_DOMAIN_IS_SUBDOMAIN(cfg_sections[i])) {
+ ret = check_domain_id_provider(cfg_sections[i], config_obj, errobj);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+ }
+ }
+
+ ret = EOK;
+done:
+ ini_free_section_list(cfg_sections);
+ return ret;
+}
+
+static int sss_ini_call_validators_errobj(struct sss_ini *data,
+ const char *rules_path,
+ struct ini_errobj *errobj)
+{
+ int ret;
+ struct ini_cfgobj *rules_cfgobj = NULL;
+ struct ini_validator custom_sssd = { "sssd_checks", custom_sssd_checks,
+ NULL };
+ struct ini_validator *sss_validators[] = { &custom_sssd, NULL };
+
+ ret = ini_rules_read_from_file(rules_path, &rules_cfgobj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to read sssd.conf schema %d [%s]\n", ret, strerror(ret));
+ goto done;
+ }
+
+ ret = ini_rules_check(rules_cfgobj, data->sssd_config, sss_validators, errobj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "ini_rules_check failed %d [%s]\n", ret, strerror(ret));
+ goto done;
+ }
+
+done:
+ if (rules_cfgobj) ini_config_destroy(rules_cfgobj);
+
+ return ret;
+}
+#endif /* HAVE_LIBINI_CONFIG_V1_3 */
+
+int sss_ini_call_validators(struct sss_ini *data,
+ const char *rules_path)
+{
+#ifdef HAVE_LIBINI_CONFIG_V1_3
+ int ret;
+ struct ini_errobj *errobj = NULL;
+
+ ret = ini_errobj_create(&errobj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to create error list\n");
+ goto done;
+ }
+
+ ret = sss_ini_call_validators_errobj(data,
+ rules_path,
+ errobj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to get errors from validators.\n");
+ goto done;
+ }
+
+ /* Do not error out when validators find some issue */
+ while (!ini_errobj_no_more_msgs(errobj)) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "%s\n", ini_errobj_get_msg(errobj));
+ ini_errobj_next(errobj);
+ }
+
+ ret = EOK;
+
+done:
+ ini_errobj_destroy(&errobj);
+ return ret;
+#else
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "libini_config does not support configuration file validataion\n");
+ return EOK;
+#endif /* HAVE_LIBINI_CONFIG_V1_3 */
+}
+
+int sss_ini_call_validators_strs(TALLOC_CTX *mem_ctx,
+ struct sss_ini *data,
+ const char *rules_path,
+ char ***_errors,
+ size_t *_num_errors)
+{
+#ifdef HAVE_LIBINI_CONFIG_V1_3
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ini_errobj *errobj = NULL;
+ int ret;
+ size_t num_errors;
+ char **errors = NULL;
+
+ if (_num_errors == NULL || _errors == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ini_errobj_create(&errobj);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sss_ini_call_validators_errobj(data,
+ rules_path,
+ errobj);
+ if (ret != EOK) {
+ goto done;
+ }
+ num_errors = ini_errobj_count(errobj);
+ if (num_errors == 0) {
+ *_num_errors = num_errors;
+ goto done;
+ }
+
+ errors = talloc_array(tmp_ctx, char *, num_errors);
+ if (errors == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (int i = 0; i < num_errors; i++) {
+ errors[i] = talloc_strdup(errors, ini_errobj_get_msg(errobj));
+ if (errors[i] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ini_errobj_next(errobj);
+ }
+
+ *_num_errors = num_errors;
+ *_errors = talloc_steal(mem_ctx, errors);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ ini_errobj_destroy(&errobj);
+
+ return ret;
+
+#else
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "libini_config does not support configuration file validation\n");
+
+ if (_num_errors == NULL || _errors == NULL) {
+ return EINVAL;
+ }
+
+ _num_errors = 0;
+ return EOK;
+#endif /* HAVE_LIBINI_CONFIG_V1_3 */
+}
+
+int sss_ini_open(struct sss_ini *self,
+ const char *config_file,
+ const char *fallback_cfg)
+{
+ int ret;
+
+ if (self == NULL) {
+ return EINVAL;
+ }
+
+ if (config_file != NULL) {
+ ret = sss_ini_config_file_open(self, config_file);
+ } else {
+ ret = ENOENT;
+ }
+
+ switch (ret) {
+ case EOK:
+ break;
+ case ENOENT:
+ DEBUG(SSSDBG_TRACE_FUNC, "No %s.\n", config_file);
+ if (fallback_cfg == NULL) {
+ return ret;
+ }
+
+ ret = sss_ini_config_file_from_mem(self,
+ discard_const(fallback_cfg),
+ strlen(fallback_cfg));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "sss_ini_config_file_from_mem failed. Error %d\n",
+ ret);
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "sss_ini_config_file_open failed: Error %d\n",
+ ret);
+ sss_ini_config_print_errors(self->error_list);
+ break;
+ }
+ return ret;
+}
+
+int sss_ini_read_sssd_conf(struct sss_ini *self,
+ const char *config_file,
+ const char *config_dir)
+{
+ errno_t ret;
+
+ if (self == NULL) {
+ return EINVAL;
+ }
+
+ ret = sss_ini_open(self, config_file, CONFDB_FALLBACK_CONFIG);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "The sss_ini_open failed %s: %d\n",
+ config_file,
+ ret);
+ return ERR_INI_OPEN_FAILED;
+ }
+
+ if (sss_ini_exists(self)) {
+ ret = sss_ini_access_check(self);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Permission check on config file failed.\n");
+ return ERR_INI_INVALID_PERMISSION;
+ }
+ } else {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "File %1$s does not exist.\n",
+ (config_file ? config_file : "NULL"));
+ }
+
+ ret = sss_ini_parse(self);
+ if (ret != EOK) {
+ sss_ini_config_print_errors(self->error_list);
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to parse configuration.\n");
+ return ERR_INI_PARSE_FAILED;
+ }
+
+ ret = sss_ini_add_snippets(self, config_dir);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Error while reading configuration directory.\n");
+ return ERR_INI_ADD_SNIPPETS_FAILED;
+ }
+
+ return ret;
+}
diff --git a/src/util/sss_ini.h b/src/util/sss_ini.h
new file mode 100644
index 0000000..4e3f67f
--- /dev/null
+++ b/src/util/sss_ini.h
@@ -0,0 +1,164 @@
+/*
+ SSSD
+
+ sss_ini.c
+
+ Authors:
+ Ondrej Kos <okos@redhat.com>
+
+ Copyright (C) 2013 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_INI_H__
+#define __SSS_INI_H__
+
+#include <stdbool.h>
+
+/**
+ * @brief INI data structure
+ */
+struct sss_ini;
+
+/**
+ * @brief create new ini data object
+ *
+ * @param[in] tmp_ctx talloc context
+ *
+ * @return
+ * - pointer to newly allocated and initialized structure
+ * - NULL in case of error
+ */
+struct sss_ini* sss_ini_new(TALLOC_CTX *tmp_ctx);
+
+
+/**
+ * @brief Open ini file or use fallback_cfg if file is not present. Include
+ * configuration snippets and perform access check.
+ *
+ * @param[in] self pointer to sss_ini structure
+ * @param[in] config_file ini file
+ * @param[in] config_dir directory containing ini files to be included
+ *
+ * @return
+ * - EOK - success
+ * - ERR_INI_OPEN_FAILED - sss_ini_open failed
+ * - ERR_INI_INVALID_PERMISSION - access check failed
+ * - ERR_INI_PARSE_FAILED - failed to parse configuration file
+ * - ERR_INI_ADD_SNIPPETS_FAILED - failed to add configuration snippets
+ */
+int sss_ini_read_sssd_conf(struct sss_ini *self,
+ const char *config_file,
+ const char *config_dir);
+
+/**
+ * @brief Open ini file or use fallback_cfg if file is not present
+ *
+ * @param[in] self pointer to sss_ini structure
+ * @param[in] config_file ini file
+ * @param[in] fallback_cfg string with ini content. This parameter is used
+ * when config_file doesn't exist or it is set to NULL
+ *
+ * @return error code
+ */
+int sss_ini_open(struct sss_ini *self,
+ const char *config_file,
+ const char *fallback_cfg);
+
+/**
+ * @brief Check whether sss_ini_open() reported that ini file is
+ * not present
+ *
+ * @param[in] self pointer to sss_ini structure
+ *
+ * @return
+ * - true we are using ini file
+ * - false file was not found
+ */
+bool sss_ini_exists(struct sss_ini *self);
+
+/**
+ * @brief get Cstat structure of the ini file
+ */
+int sss_ini_get_stat(struct sss_ini *self);
+
+/**
+ * @brief Get mtime of the ini file
+ */
+int sss_ini_get_mtime(struct sss_ini *self,
+ size_t timestr_len,
+ char *timestr);
+
+/**
+ * @brief Get pointer to list of snippet parsing errors
+ */
+struct ref_array *
+sss_ini_get_ra_error_list(struct sss_ini *self);
+
+/**
+ * @brief Get pointer to list of successfully merged snippet files
+ */
+struct ref_array *
+sss_ini_get_ra_success_list(struct sss_ini *self);
+
+/**
+ * @brief Get configuration object
+ */
+int sss_ini_get_cfgobj(struct sss_ini *self,
+ const char *section, const char *name);
+
+/**
+ * @brief Check configuration object
+ */
+int sss_ini_check_config_obj(struct sss_ini *self);
+
+/**
+ * @brief Get int value
+ */
+int sss_ini_get_int_config_value(struct sss_ini *self,
+ int strict, int def, int *error);
+
+/**
+ * @brief Get string value
+ */
+const char *sss_ini_get_string_config_value(struct sss_ini *self,
+ int *error);
+
+/**
+ * @brief Create LDIF
+ */
+int sss_confdb_create_ldif(TALLOC_CTX *mem_ctx,
+ struct sss_ini *self,
+ const char *only_section,
+ const char **config_ldif);
+
+/**
+ * @brief Validate sssd.conf if libini_config support it
+ */
+int sss_ini_call_validators(struct sss_ini *data,
+ const char *rules_path);
+
+/**
+ * @brief Get errors from validators in array of strings
+ */
+int sss_ini_call_validators_strs(TALLOC_CTX *mem_ctx,
+ struct sss_ini *data,
+ const char *rules_path,
+ char ***_strs,
+ size_t *_num_errors);
+
+#endif /* __SSS_INI_H__ */
diff --git a/src/util/sss_iobuf.c b/src/util/sss_iobuf.c
new file mode 100644
index 0000000..bd8d2f3
--- /dev/null
+++ b/src/util/sss_iobuf.c
@@ -0,0 +1,452 @@
+#include <talloc.h>
+
+#include "util/util.h"
+#include "util/sss_iobuf.h"
+
+/**
+ * @brief The iobuf structure that holds the data, its capacity and
+ * a pointer to the data.
+ *
+ * @see sss_iobuf_init_empty()
+ * @see sss_iobuf_init_readonly()
+ */
+struct sss_iobuf {
+ uint8_t *data; /* Start of the data buffer */
+
+ size_t dp; /* Data pointer */
+ size_t size; /* Current data buffer size */
+ size_t capacity; /* Maximum capacity */
+};
+
+struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx,
+ size_t size,
+ size_t capacity)
+{
+ struct sss_iobuf *iobuf;
+ uint8_t *buf;
+
+ iobuf = talloc_zero(mem_ctx, struct sss_iobuf);
+ if (iobuf == NULL) {
+ return NULL;
+ }
+
+ buf = talloc_array(iobuf, uint8_t, size);
+ if (buf == NULL) {
+ talloc_free(iobuf);
+ return NULL;
+ }
+
+ if (capacity == 0) {
+ capacity = SIZE_MAX / 2;
+ }
+
+ iobuf->data = buf;
+ iobuf->size = size;
+ iobuf->capacity = capacity;
+
+ return iobuf;
+}
+
+struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx,
+ const uint8_t *data,
+ size_t size)
+{
+ struct sss_iobuf *iobuf;
+
+ iobuf = sss_iobuf_init_empty(mem_ctx, size, size);
+ if (iobuf == NULL) {
+ return NULL;
+ }
+
+ if (data != NULL) {
+ memcpy(iobuf->data, data, size);
+ }
+
+ return iobuf;
+}
+
+struct sss_iobuf *sss_iobuf_init_steal(TALLOC_CTX *mem_ctx,
+ uint8_t *data,
+ size_t size)
+{
+ struct sss_iobuf *iobuf;
+
+ iobuf = talloc_zero(mem_ctx, struct sss_iobuf);
+ if (iobuf == NULL) {
+ return NULL;
+ }
+
+ iobuf->data = talloc_steal(iobuf, data);
+ iobuf->size = size;
+ iobuf->capacity = size;
+
+ return iobuf;
+}
+
+void sss_iobuf_cursor_reset(struct sss_iobuf *iobuf)
+{
+ iobuf->dp = 0;
+}
+
+size_t sss_iobuf_get_len(struct sss_iobuf *iobuf)
+{
+ if (iobuf == NULL) {
+ return 0;
+ }
+
+ return iobuf->dp;
+}
+
+size_t sss_iobuf_get_capacity(struct sss_iobuf *iobuf)
+{
+ if (iobuf == NULL) {
+ return 0;
+ }
+
+ return iobuf->capacity;
+}
+
+size_t sss_iobuf_get_size(struct sss_iobuf *iobuf)
+{
+ if (iobuf == NULL) {
+ return 0;
+ }
+
+ return iobuf->size;
+}
+
+uint8_t *sss_iobuf_get_data(struct sss_iobuf *iobuf)
+{
+ if (iobuf == NULL) {
+ return NULL;
+ }
+
+ return iobuf->data;
+}
+
+static size_t iobuf_get_len(struct sss_iobuf *iobuf)
+{
+ if (iobuf == NULL) {
+ return 0;
+ }
+
+ return (iobuf->size - iobuf->dp);
+}
+
+static errno_t ensure_bytes(struct sss_iobuf *iobuf,
+ size_t nbytes)
+{
+ size_t wantsize;
+ size_t newsize;
+ uint8_t *newdata;
+
+ if (iobuf == NULL) {
+ return EINVAL;
+ }
+
+ wantsize = iobuf->dp + nbytes;
+ if (wantsize <= iobuf->size) {
+ /* Enough space already */
+ return EOK;
+ }
+
+ /* Else, try to extend the iobuf */
+ if (wantsize > iobuf->capacity) {
+ /* We will never grow past capacity */
+ return ENOBUFS;
+ }
+
+ /* Double the size until we add at least nbytes, but stop if we double past capacity */
+ for (newsize = iobuf->size;
+ (newsize < wantsize) && (newsize < iobuf->capacity);
+ newsize *= 2)
+ ;
+
+ if (newsize > iobuf->capacity) {
+ newsize = iobuf->capacity;
+ }
+
+ newdata = talloc_realloc(iobuf, iobuf->data, uint8_t, newsize);
+ if (newdata == NULL) {
+ return ENOMEM;
+ }
+
+ iobuf->data = newdata;
+ iobuf->size = newsize;
+
+ return EOK;
+}
+
+static inline uint8_t *iobuf_ptr(struct sss_iobuf *iobuf)
+{
+ return iobuf->data + iobuf->dp;
+}
+
+errno_t sss_iobuf_read(struct sss_iobuf *iobuf,
+ size_t len,
+ uint8_t *_buf,
+ size_t *_read)
+{
+ size_t remaining;
+
+ if (iobuf == NULL || _buf == NULL) {
+ return EINVAL;
+ }
+
+ remaining = iobuf_get_len(iobuf);
+ if (len > remaining) {
+ len = remaining;
+ }
+
+ safealign_memcpy(_buf, iobuf_ptr(iobuf), len, &iobuf->dp);
+ if (_read != NULL) {
+ *_read = len;
+ }
+
+ return EOK;
+}
+
+errno_t sss_iobuf_read_len(struct sss_iobuf *iobuf,
+ size_t len,
+ uint8_t *_buf)
+{
+ size_t read_bytes;
+ errno_t ret;
+
+ ret = sss_iobuf_read(iobuf, len, _buf, &read_bytes);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (read_bytes != len) {
+ return ENOBUFS;
+ }
+
+ return EOK;
+}
+
+errno_t sss_iobuf_write_len(struct sss_iobuf *iobuf,
+ uint8_t *buf,
+ size_t len)
+{
+ errno_t ret;
+
+ if (iobuf == NULL || buf == NULL) {
+ return EINVAL;
+ }
+
+ ret = ensure_bytes(iobuf, len);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ safealign_memcpy(iobuf_ptr(iobuf), buf, len, &iobuf->dp);
+
+ return EOK;
+}
+
+errno_t sss_iobuf_read_varlen(TALLOC_CTX *mem_ctx,
+ struct sss_iobuf *iobuf,
+ uint8_t **_out,
+ size_t *_len)
+{
+ uint8_t *out;
+ uint32_t len;
+ size_t slen;
+ errno_t ret;
+
+ if (iobuf == NULL || _out == NULL || _len == NULL) {
+ return EINVAL;
+ }
+
+ ret = sss_iobuf_read_uint32(iobuf, &len);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (len == 0) {
+ *_out = NULL;
+ *_len = 0;
+ return EOK;
+ }
+
+ out = talloc_array(mem_ctx, uint8_t, len);
+ if (out == NULL) {
+ return ENOMEM;
+ }
+
+ slen = len;
+ ret = sss_iobuf_read_len(iobuf, slen, out);
+ if (ret != EOK) {
+ talloc_free(out);
+ return ret;
+ }
+
+ *_out = out;
+ *_len = slen;
+
+ return EOK;
+}
+
+errno_t sss_iobuf_write_varlen(struct sss_iobuf *iobuf,
+ uint8_t *data,
+ size_t len)
+{
+ errno_t ret;
+
+ if (iobuf == NULL || (data == NULL && len != 0)) {
+ return EINVAL;
+ }
+
+ ret = sss_iobuf_write_uint32(iobuf, len);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (len == 0) {
+ return EOK;
+ }
+
+ return sss_iobuf_write_len(iobuf, data, len);
+}
+
+errno_t sss_iobuf_read_iobuf(TALLOC_CTX *mem_ctx,
+ struct sss_iobuf *iobuf,
+ struct sss_iobuf **_out)
+{
+ struct sss_iobuf *out;
+ uint8_t *data;
+ size_t len;
+ errno_t ret;
+
+ ret = sss_iobuf_read_varlen(NULL, iobuf, &data, &len);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ out = sss_iobuf_init_steal(mem_ctx, data, len);
+ if (out == NULL) {
+ return ENOMEM;
+ }
+
+ *_out = out;
+
+ return EOK;
+}
+
+errno_t sss_iobuf_write_iobuf(struct sss_iobuf *iobuf,
+ struct sss_iobuf *data)
+{
+ return sss_iobuf_write_varlen(iobuf, data->data, data->size);
+}
+
+errno_t sss_iobuf_read_uint8(struct sss_iobuf *iobuf,
+ uint8_t *_val)
+{
+ SAFEALIGN_COPY_UINT8_CHECK(_val, iobuf_ptr(iobuf),
+ iobuf->capacity, &iobuf->dp);
+ return EOK;
+}
+
+errno_t sss_iobuf_read_uint32(struct sss_iobuf *iobuf,
+ uint32_t *_val)
+{
+ SAFEALIGN_COPY_UINT32_CHECK(_val, iobuf_ptr(iobuf),
+ iobuf->capacity, &iobuf->dp);
+ return EOK;
+}
+
+errno_t sss_iobuf_read_int32(struct sss_iobuf *iobuf,
+ int32_t *_val)
+{
+ SAFEALIGN_COPY_INT32_CHECK(_val, iobuf_ptr(iobuf),
+ iobuf->capacity, &iobuf->dp);
+ return EOK;
+}
+
+errno_t sss_iobuf_write_uint8(struct sss_iobuf *iobuf,
+ uint8_t val)
+{
+ errno_t ret;
+
+ ret = ensure_bytes(iobuf, sizeof(uint8_t));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ SAFEALIGN_SETMEM_UINT8(iobuf_ptr(iobuf), val, &iobuf->dp);
+ return EOK;
+}
+
+errno_t sss_iobuf_write_uint32(struct sss_iobuf *iobuf,
+ uint32_t val)
+{
+ errno_t ret;
+
+ ret = ensure_bytes(iobuf, sizeof(uint32_t));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ SAFEALIGN_SETMEM_UINT32(iobuf_ptr(iobuf), val, &iobuf->dp);
+ return EOK;
+}
+
+errno_t sss_iobuf_write_int32(struct sss_iobuf *iobuf,
+ int32_t val)
+{
+ errno_t ret;
+
+ ret = ensure_bytes(iobuf, sizeof(int32_t));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ SAFEALIGN_SETMEM_INT32(iobuf_ptr(iobuf), val, &iobuf->dp);
+ return EOK;
+}
+
+errno_t sss_iobuf_read_stringz(struct sss_iobuf *iobuf,
+ const char **_out)
+{
+ uint8_t *end;
+ size_t len;
+
+ if (iobuf == NULL) {
+ return EINVAL;
+ }
+
+ if (_out == NULL) {
+ return EINVAL;
+ }
+
+ *_out = NULL;
+
+ end = memchr(iobuf_ptr(iobuf), '\0', sss_iobuf_get_size(iobuf));
+ if (end == NULL) {
+ return EINVAL;
+ }
+
+ len = end + 1 - iobuf_ptr(iobuf);
+ if (sss_iobuf_get_size(iobuf) < len) {
+ return EINVAL;
+ }
+
+ *_out = (const char *) iobuf_ptr(iobuf);
+ iobuf->dp += len;
+ return EOK;
+}
+
+errno_t sss_iobuf_write_stringz(struct sss_iobuf *iobuf,
+ const char *str)
+{
+ if (iobuf == NULL || str == NULL) {
+ return EINVAL;
+ }
+
+ SAFEALIGN_MEMCPY_CHECK(iobuf_ptr(iobuf),
+ str, strlen(str)+1,
+ sss_iobuf_get_size(iobuf),
+ &iobuf->dp);
+ return EOK;
+}
diff --git a/src/util/sss_iobuf.h b/src/util/sss_iobuf.h
new file mode 100644
index 0000000..01f0f45
--- /dev/null
+++ b/src/util/sss_iobuf.h
@@ -0,0 +1,201 @@
+#ifndef __SSS_IOBUF_H_
+#define __SSS_IOBUF_H_
+
+#include <talloc.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "util/util_errors.h"
+
+struct sss_iobuf;
+
+/*
+ * @brief Allocate an empty IO buffer
+ *
+ * @param[in] mem_ctx The talloc context that owns the iobuf
+ *
+ * When this buffer is written into, but the capacity is exceeded, the write
+ * function will return an error.
+ *
+ * @param[in] mem_ctx The talloc context that owns the iobuf
+ * @param[in] size The size of the data buffer
+ * @param[in] capacity The maximum capacity the buffer can grow into.
+ * Use 0 for an 'unlimited' buffer that will grow
+ * until SIZE_MAX/2.
+ *
+ * @warning The allocated data storage will not be zeroed.
+ *
+ * @return The newly created buffer on success or NULL on an error.
+ *
+ */
+struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx,
+ size_t size,
+ size_t capacity);
+
+/*
+ * @brief Allocate an IO buffer with a fixed size
+ *
+ * This function is useful for parsing an input buffer from an existing
+ * buffer pointed to by data.
+ *
+ * The iobuf does not assume ownership of the data buffer in talloc terms,
+ * but copies the data instead.
+ *
+ * @param[in] mem_ctx The talloc context that owns the iobuf
+ * @param[in] data The data to initialize the IO buffer with. This
+ * data is copied into the iobuf-owned buffer.
+ * @param[in] size The size of the data buffer
+ *
+ * @warning The allocated data storage will not be zeroed.
+ *
+ * @return The newly created buffer on success or NULL on an error.
+ */
+struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx,
+ const uint8_t *data,
+ size_t size);
+
+/*
+ * @brief Allocate an IO buffer with a fixed size, stealing input data.
+ *
+ * This function is useful for parsing an input buffer from an existing
+ * buffer pointed to by data.
+ *
+ * The iobuf assumes ownership of the data buffer.
+ *
+ * @param[in] mem_ctx The talloc context that owns the iobuf
+ * @param[in] data The data to initialize the IO buffer with.
+ * @param[in] size The size of the data buffer
+ *
+ * @return The newly created buffer on success or NULL on an error.
+ */
+struct sss_iobuf *sss_iobuf_init_steal(TALLOC_CTX *mem_ctx,
+ uint8_t *data,
+ size_t size);
+
+/*
+ * @brief Reset internal cursor of the IO buffer (seek to the start)
+ */
+void sss_iobuf_cursor_reset(struct sss_iobuf *iobuf);
+
+/*
+ * @brief Returns the number of bytes currently stored in the iobuf
+ *
+ * @return The number of bytes (the data pointer offset)
+ */
+size_t sss_iobuf_get_len(struct sss_iobuf *iobuf);
+
+/*
+ * @brief Returns the capacity of the IO buffer
+ *
+ * @return The capacity of the IO buffer. Returns zero
+ * for an unlimited buffer.
+ */
+size_t sss_iobuf_get_capacity(struct sss_iobuf *iobuf);
+
+/*
+ * @brief Returns the current size of the IO buffer
+ */
+size_t sss_iobuf_get_size(struct sss_iobuf *iobuf);
+
+/*
+ * @brief Returns the data pointer of the IO buffer
+ */
+uint8_t *sss_iobuf_get_data(struct sss_iobuf *iobuf);
+
+/*
+ * @brief Read from an IO buffer
+ *
+ * Read up to len bytes from an IO buffer. It is not an error to request
+ * more bytes than the buffer actually has - the function will succeed, but
+ * return the actual number of bytes read. Reading from an empty buffer just
+ * returns zero bytes read.
+ *
+ * @param[in] iobuf The IO buffer to read from
+ * @param[in] len The maximum number of bytes to read
+ * @param[out] _buf The buffer to read data into from iobuf
+ * @param[out] _read The actual number of bytes read from IO buffer.
+ *
+ * @return EOK on success, errno otherwise
+ */
+errno_t sss_iobuf_read(struct sss_iobuf *iobuf,
+ size_t len,
+ uint8_t *_buf,
+ size_t *_read);
+
+/*
+ * @brief Read an exact number of bytes from an IO buffer
+ *
+ * Read exactly len bytes from an IO buffer. If the buffer contains fewer
+ * bytes than len, ENOBUFS is returned.
+ *
+ * @param[in] iobuf The IO buffer to read from
+ * @param[in] len The maximum number of bytes to read
+ * @param[out] _buf The buffer to read data into from iobuf
+ *
+ * @return EOK on success, errno otherwise
+ */
+errno_t sss_iobuf_read_len(struct sss_iobuf *iobuf,
+ size_t len,
+ uint8_t *_buf);
+
+/*
+ * @brief Write into an IO buffer
+ *
+ * Attempts to write len bytes into the iobuf. If the capacity is exceeded,
+ * the iobuf module tries to extend the buffer up to the maximum capacity.
+ *
+ * If reallocating the internal buffer fails, the data pointers are not
+ * touched.
+ *
+ * @param[in] iobuf The IO buffer to write to
+ * @param[in] buf The data to write into the buffer
+ * @param[in] len The number of bytes to write
+ *
+ * @return EOK on success, errno otherwise. Notably returns ENOBUFS if
+ * the buffer capacity is exceeded.
+ */
+errno_t sss_iobuf_write_len(struct sss_iobuf *iobuf,
+ uint8_t *buf,
+ size_t len);
+
+errno_t sss_iobuf_read_varlen(TALLOC_CTX *mem_ctx,
+ struct sss_iobuf *iobuf,
+ uint8_t **_out,
+ size_t *_len);
+
+errno_t sss_iobuf_write_varlen(struct sss_iobuf *iobuf,
+ uint8_t *data,
+ size_t len);
+
+errno_t sss_iobuf_read_iobuf(TALLOC_CTX *mem_ctx,
+ struct sss_iobuf *iobuf,
+ struct sss_iobuf **_out);
+
+errno_t sss_iobuf_write_iobuf(struct sss_iobuf *iobuf,
+ struct sss_iobuf *data);
+
+errno_t sss_iobuf_read_uint8(struct sss_iobuf *iobuf,
+ uint8_t *_val);
+
+errno_t sss_iobuf_write_uint8(struct sss_iobuf *iobuf,
+ uint8_t val);
+
+errno_t sss_iobuf_read_uint32(struct sss_iobuf *iobuf,
+ uint32_t *_val);
+
+errno_t sss_iobuf_write_uint32(struct sss_iobuf *iobuf,
+ uint32_t val);
+
+errno_t sss_iobuf_read_int32(struct sss_iobuf *iobuf,
+ int32_t *_val);
+
+errno_t sss_iobuf_write_int32(struct sss_iobuf *iobuf,
+ int32_t val);
+
+errno_t sss_iobuf_read_stringz(struct sss_iobuf *iobuf,
+ const char **_out);
+
+errno_t sss_iobuf_write_stringz(struct sss_iobuf *iobuf,
+ const char *str);
+
+#endif /* __SSS_IOBUF_H_ */
diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c
new file mode 100644
index 0000000..3f57e5b
--- /dev/null
+++ b/src/util/sss_krb5.c
@@ -0,0 +1,1384 @@
+/*
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2009-2010 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 <stdio.h>
+#include <errno.h>
+#include <talloc.h>
+#include <profile.h>
+
+#include "config.h"
+
+#include "util/sss_iobuf.h"
+#include "util/util.h"
+#include "util/util_errors.h"
+#include "util/sss_krb5.h"
+
+static char *
+sss_krb5_get_primary(TALLOC_CTX *mem_ctx,
+ const char *pattern,
+ const char *hostname)
+{
+ char *primary;
+ char *dot;
+ char *c;
+ char *shortname;
+
+ if (strcmp(pattern, "%S$") == 0) {
+ shortname = talloc_strdup(mem_ctx, hostname);
+ if (!shortname) return NULL;
+
+ dot = strchr(shortname, '.');
+ if (dot) {
+ *dot = '\0';
+ }
+
+ for (c=shortname; *c != '\0'; ++c) {
+ *c = toupper(*c);
+ }
+
+ /* The samAccountName is recommended to be less than 20 characters.
+ * This is only for users and groups. For machine accounts,
+ * the real limit is caused by NetBIOS protocol.
+ * NetBIOS names are limited to 16 (15 + $)
+ * https://support.microsoft.com/en-us/help/163409/netbios-suffixes-16th-character-of-the-netbios-name
+ */
+ primary = talloc_asprintf(mem_ctx, "%.15s$", shortname);
+ talloc_free(shortname);
+ return primary;
+ }
+
+ return talloc_asprintf(mem_ctx, pattern, hostname);
+}
+
+const char *sss_printable_keytab_name(krb5_context ctx, const char *keytab_name)
+{
+ /* sss_printable_keytab_name() output is expected to be used
+ for logging purposes only. Thus it is non-critical to provide
+ krb5_kt_default_name() with a buffer which is potentially less then
+ actual file path. 1024 is chosen to be 'large enough' to fit default
+ keytab name for any sensible configuration.
+ (And while it is tempting to use PATH_MAX here it would be misuse
+ of this posix limit.)
+ */
+ static char buff[1024];
+
+ if (keytab_name) {
+ return keytab_name;
+ }
+
+ if (krb5_kt_default_name(ctx, buff, sizeof(buff)) != 0) {
+ return "-default keytab-";
+ }
+
+ return buff;
+}
+
+errno_t select_principal_from_keytab(TALLOC_CTX *mem_ctx,
+ const char *hostname,
+ const char *desired_realm,
+ const char *keytab_name,
+ char **_principal,
+ char **_primary,
+ char **_realm)
+{
+ krb5_error_code kerr = 0;
+ krb5_context krb_ctx = NULL;
+ krb5_keytab keytab = NULL;
+ krb5_principal client_princ = NULL;
+ TALLOC_CTX *tmp_ctx;
+ char *primary = NULL;
+ char *realm = NULL;
+ int i = 0;
+ errno_t ret;
+ char *principal_string;
+ const char *realm_name;
+ int realm_len;
+ const char *error_message = NULL;
+
+ /**
+ * The %s conversion is passed as-is, the %S conversion is translated to
+ * "short host name"
+ *
+ * Priority of lookup:
+ * - our.hostname@REALM or host/our.hostname@REALM depending on the input
+ * - SHORT.HOSTNAME$@REALM (AD domain)
+ * - host/our.hostname@REALM
+ * - foobar$@REALM (AD domain)
+ * - host/foobar@REALM
+ * - host/foo@BAR
+ * - pick the first principal in the keytab
+ */
+ const char *primary_patterns[] = {"%s", "%S$", "host/%s", "*$", "host/*",
+ "host/*", NULL};
+ const char *realm_patterns[] = {"%s", "%s", "%s", "%s", "%s",
+ NULL, NULL};
+
+ DEBUG(SSSDBG_FUNC_DATA,
+ "trying to select the most appropriate principal from keytab\n");
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed\n");
+ return ENOMEM;
+ }
+
+ kerr = sss_krb5_init_context(&krb_ctx);
+ if (kerr) {
+ error_message = "Failed to init Kerberos context";
+ ret = EFAULT;
+ goto done;
+ }
+
+ if (keytab_name != NULL) {
+ kerr = krb5_kt_resolve(krb_ctx, keytab_name, &keytab);
+ } else {
+ kerr = krb5_kt_default(krb_ctx, &keytab);
+ }
+ if (kerr) {
+ const char *krb5_err_msg = sss_krb5_get_error_message(krb_ctx, kerr);
+ error_message = talloc_strdup(tmp_ctx, krb5_err_msg);
+ sss_krb5_free_error_message(krb_ctx, krb5_err_msg);
+ ret = EFAULT;
+ goto done;
+ }
+
+ if (!desired_realm) {
+ desired_realm = "*";
+ }
+ if (!hostname) {
+ hostname = "*";
+ }
+
+ do {
+ if (primary_patterns[i]) {
+ primary = sss_krb5_get_primary(tmp_ctx,
+ primary_patterns[i],
+ hostname);
+ if (primary == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ primary = NULL;
+ }
+ if (realm_patterns[i]) {
+ realm = talloc_asprintf(tmp_ctx, realm_patterns[i], desired_realm);
+ if (realm == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ realm = NULL;
+ }
+
+ kerr = find_principal_in_keytab(krb_ctx, keytab, primary, realm,
+ &client_princ);
+ talloc_zfree(primary);
+ talloc_zfree(realm);
+ if (kerr == 0) {
+ break;
+ }
+ if (client_princ != NULL) {
+ krb5_free_principal(krb_ctx, client_princ);
+ client_princ = NULL;
+ }
+ i++;
+ } while(primary_patterns[i-1] != NULL || realm_patterns[i-1] != NULL);
+
+ if (kerr == 0) {
+ if (_principal) {
+ kerr = krb5_unparse_name(krb_ctx, client_princ, &principal_string);
+ if (kerr) {
+ error_message = "krb5_unparse_name failed (_principal)";
+ ret = EINVAL;
+ goto done;
+ }
+
+ *_principal = talloc_strdup(mem_ctx, principal_string);
+ sss_krb5_free_unparsed_name(krb_ctx, principal_string);
+ if (!*_principal) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_FUNC_DATA, "Selected principal: %s\n", *_principal);
+ }
+
+ if (_primary) {
+ kerr = sss_krb5_unparse_name_flags(krb_ctx, client_princ,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+ &principal_string);
+ if (kerr) {
+ if (_principal) talloc_zfree(*_principal);
+ error_message = "krb5_unparse_name failed (_primary)";
+ ret = EINVAL;
+ goto done;
+ }
+
+ *_primary = talloc_strdup(mem_ctx, principal_string);
+ sss_krb5_free_unparsed_name(krb_ctx, principal_string);
+ if (!*_primary) {
+ if (_principal) talloc_zfree(*_principal);
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_FUNC_DATA, "Selected primary: %s\n", *_primary);
+ }
+
+ if (_realm) {
+ sss_krb5_princ_realm(krb_ctx, client_princ,
+ &realm_name,
+ &realm_len);
+ if (realm_len == 0) {
+ error_message = "sss_krb5_princ_realm failed";
+ if (_principal) talloc_zfree(*_principal);
+ if (_primary) talloc_zfree(*_primary);
+ ret = EINVAL;
+ goto done;
+ }
+
+ *_realm = talloc_asprintf(mem_ctx, "%.*s",
+ realm_len, realm_name);
+ if (!*_realm) {
+ if (_principal) talloc_zfree(*_principal);
+ if (_primary) talloc_zfree(*_primary);
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_FUNC_DATA, "Selected realm: %s\n", *_realm);
+ }
+
+ ret = EOK;
+ } else {
+ ret = ERR_KRB5_PRINCIPAL_NOT_FOUND;
+ }
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to read keytab [%s]: %s\n",
+ sss_printable_keytab_name(krb_ctx, keytab_name),
+ (error_message ? error_message : sss_strerror(ret)));
+
+ sss_log(SSS_LOG_ERR, "Failed to read keytab [%s]: %s\n",
+ sss_printable_keytab_name(krb_ctx, keytab_name),
+ (error_message ? error_message : sss_strerror(ret)));
+ }
+ if (keytab) krb5_kt_close(krb_ctx, keytab);
+ if (client_princ) krb5_free_principal(krb_ctx, client_princ);
+ if (krb_ctx) krb5_free_context(krb_ctx);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+enum matching_mode {MODE_NORMAL, MODE_PREFIX, MODE_POSTFIX};
+/**
+ * We only have primary and instances stored separately, we need to
+ * join them to one string and compare that string.
+ *
+ * @param ctx Kerberos context
+ * @param principal principal we want to match
+ * @param pattern_primary primary part of the principal we want to
+ * perform matching against. It is possible to use * wildcard
+ * at the beginning or at the end of the string. If NULL, it
+ * will act as "*"
+ * @param pattern_realm realm part of the principal we want to perform
+ * the matching against. If NULL, it will act as "*"
+ */
+static bool match_principal(krb5_context ctx,
+ krb5_principal principal,
+ const char *pattern_primary,
+ const char *pattern_realm)
+{
+ char *primary = NULL;
+ char *primary_str = NULL;
+ int primary_str_len = 0;
+ int tmp_len;
+ int len_diff;
+ const char *realm_name;
+ int realm_len;
+
+ enum matching_mode mode = MODE_NORMAL;
+ TALLOC_CTX *tmp_ctx;
+ bool ret = false;
+
+ sss_krb5_princ_realm(ctx, principal, &realm_name, &realm_len);
+ if (realm_len == 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "sss_krb5_princ_realm failed.\n");
+ return false;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed\n");
+ return false;
+ }
+
+ if (pattern_primary) {
+ tmp_len = strlen(pattern_primary);
+ if (pattern_primary[tmp_len-1] == '*') {
+ mode = MODE_PREFIX;
+ primary_str = talloc_strdup(tmp_ctx, pattern_primary);
+ primary_str[tmp_len-1] = '\0';
+ primary_str_len = tmp_len-1;
+ } else if (pattern_primary[0] == '*') {
+ mode = MODE_POSTFIX;
+ primary_str = talloc_strdup(tmp_ctx, pattern_primary+1);
+ primary_str_len = tmp_len-1;
+ }
+
+ sss_krb5_unparse_name_flags(ctx, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+ &primary);
+
+ len_diff = strlen(primary)-primary_str_len;
+
+ if ((mode == MODE_NORMAL &&
+ strcmp(primary, pattern_primary) != 0) ||
+ (mode == MODE_PREFIX &&
+ strncmp(primary, primary_str, primary_str_len) != 0) ||
+ (mode == MODE_POSTFIX &&
+ strcmp(primary+len_diff, primary_str) != 0)) {
+ goto done;
+ }
+ }
+
+ if (!pattern_realm || (realm_len == strlen(pattern_realm) &&
+ strncmp(realm_name, pattern_realm, realm_len) == 0)) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Principal matched to the sample (%s@%s).\n", pattern_primary,
+ pattern_realm);
+ ret = true;
+ }
+
+done:
+ free(primary);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+krb5_error_code find_principal_in_keytab(krb5_context ctx,
+ krb5_keytab keytab,
+ const char *pattern_primary,
+ const char *pattern_realm,
+ krb5_principal *princ)
+{
+ krb5_error_code kerr;
+ krb5_error_code kerr_dbg;
+ krb5_kt_cursor cursor;
+ krb5_keytab_entry entry;
+ bool principal_found = false;
+
+ memset(&cursor, 0, sizeof(cursor));
+ memset(&entry, 0, sizeof(entry));
+
+ kerr = krb5_kt_start_seq_get(ctx, keytab, &cursor);
+ if (kerr != 0) {
+ const char *krb5_err_msg = sss_krb5_get_error_message(ctx, kerr);
+ DEBUG(SSSDBG_CRIT_FAILURE, "krb5_kt_start_seq_get failed: %s\n",
+ krb5_err_msg);
+ sss_log(SSS_LOG_ERR, "krb5_kt_start_seq_get failed: %s\n",
+ krb5_err_msg);
+ sss_krb5_free_error_message(ctx, krb5_err_msg);
+ return kerr;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Trying to find principal %s@%s in keytab.\n", pattern_primary, pattern_realm);
+ while ((kerr = krb5_kt_next_entry(ctx, keytab, &entry, &cursor)) == 0) {
+ principal_found = match_principal(ctx, entry.principal, pattern_primary, pattern_realm);
+ if (principal_found) {
+ break;
+ }
+
+ kerr_dbg = sss_krb5_free_keytab_entry_contents(ctx, &entry);
+ if (kerr_dbg != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to free keytab entry.\n");
+ }
+ memset(&entry, 0, sizeof(entry));
+ }
+
+ /* Close the keytab here. Even though we're using cursors, the file
+ * handle is stored in the krb5_keytab structure, and it gets
+ * overwritten by other keytab calls, creating a leak. */
+ kerr_dbg = krb5_kt_end_seq_get(ctx, keytab, &cursor);
+ if (kerr_dbg != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "krb5_kt_end_seq_get failed.\n");
+ }
+
+ if (principal_found) {
+ kerr = krb5_copy_principal(ctx, entry.principal, princ);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "krb5_copy_principal failed.\n");
+ sss_log(SSS_LOG_ERR, "krb5_copy_principal failed.\n");
+ }
+ kerr_dbg = sss_krb5_free_keytab_entry_contents(ctx, &entry);
+ if (kerr_dbg != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to free keytab entry.\n");
+ }
+ } else {
+ /* If principal was not found then 'kerr' was set */
+ if (kerr != KRB5_KT_END) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Error while reading keytab using krb5_kt_next_entry()\n");
+ sss_log(SSS_LOG_ERR,
+ "Error while reading keytab using krb5_kt_next_entry()\n");
+ } else {
+ kerr = KRB5_KT_NOTFOUND;
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "No principal matching %s@%s found in keytab.\n",
+ pattern_primary, pattern_realm);
+ }
+ }
+
+ return kerr;
+}
+
+static const char *__SSS_KRB5_NO_ERR_MSG_AVAILABLE = "- no krb5 error message available -";
+
+const char *KRB5_CALLCONV sss_krb5_get_error_message(krb5_context ctx,
+ krb5_error_code ec)
+{
+#ifdef HAVE_KRB5_GET_ERROR_MESSAGE
+ return krb5_get_error_message(ctx, ec);
+#else
+ int size = sizeof("Kerberos error [XXXXXXXXXXXX]");
+ char *s = malloc(sizeof(char) * size);
+ if (s != NULL) {
+ int ret = snprintf(s, size, "Kerberos error [%12d]", ec);
+ if (ret < 0 || ret >= size) {
+ free(s);
+ s = NULL;
+ }
+ }
+ return (s ? s : __SSS_KRB5_NO_ERR_MSG_AVAILABLE);
+#endif
+}
+
+void KRB5_CALLCONV sss_krb5_free_error_message(krb5_context ctx, const char *s)
+{
+ if (s == __SSS_KRB5_NO_ERR_MSG_AVAILABLE) {
+ return;
+ }
+
+#ifdef HAVE_KRB5_GET_ERROR_MESSAGE
+ if (s != NULL) {
+ krb5_free_error_message(ctx, s);
+ }
+#else
+ free(s);
+#endif
+
+ return;
+}
+
+krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_alloc(
+ krb5_context context,
+ krb5_get_init_creds_opt **opt)
+{
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
+ return krb5_get_init_creds_opt_alloc(context, opt);
+#else
+ *opt = calloc(1, sizeof(krb5_get_init_creds_opt));
+ if (*opt == NULL) {
+ return ENOMEM;
+ }
+ krb5_get_init_creds_opt_init(*opt);
+
+ return 0;
+#endif
+}
+
+void KRB5_CALLCONV sss_krb5_get_init_creds_opt_free (krb5_context context,
+ krb5_get_init_creds_opt *opt)
+{
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
+ krb5_get_init_creds_opt_free(context, opt);
+#else
+ free(opt);
+#endif
+
+ return;
+}
+
+void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name)
+{
+#ifdef HAVE_KRB5_FREE_UNPARSED_NAME
+ if (name != NULL) {
+ krb5_free_unparsed_name(context, name);
+ }
+#else
+ if (name != NULL) {
+ memset(name, 0, strlen(name));
+ free(name);
+ }
+#endif
+}
+
+
+krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_expire_callback(
+ krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_expire_callback_func cb,
+ void *data)
+{
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_EXPIRE_CALLBACK
+ return krb5_get_init_creds_opt_set_expire_callback(context, opt, cb, data);
+#else
+ DEBUG(SSSDBG_FUNC_DATA,
+ "krb5_get_init_creds_opt_set_expire_callback not available.\n");
+ return 0;
+#endif
+}
+
+errno_t check_fast(const char *str, bool *use_fast)
+{
+#if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_FLAGS
+ if (strcasecmp(str, "never") == 0 ) {
+ *use_fast = false;
+ } else if (strcasecmp(str, "try") == 0 || strcasecmp(str, "demand") == 0) {
+ *use_fast = true;
+ } else {
+ sss_log(SSS_LOG_ALERT, "Unsupported value [%s] for option krb5_use_fast,"
+ "please use never, try, or demand.\n", str);
+ return EINVAL;
+ }
+
+ return EOK;
+#else
+ sss_log(SSS_LOG_ALERT, "This build of sssd does not support FAST. "
+ "Please remove option krb5_use_fast.\n");
+ return EINVAL;
+#endif
+}
+
+krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_fast_ccache_name(
+ krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ const char *fast_ccache_name)
+{
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE_NAME
+ return krb5_get_init_creds_opt_set_fast_ccache_name(context, opt,
+ fast_ccache_name);
+#else
+ DEBUG(SSSDBG_FUNC_DATA,
+ "krb5_get_init_creds_opt_set_fast_ccache_name not available.\n");
+ return 0;
+#endif
+}
+
+krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_fast_flags(
+ krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_flags flags)
+{
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_FLAGS
+ return krb5_get_init_creds_opt_set_fast_flags(context, opt, flags);
+#else
+ DEBUG(SSSDBG_FUNC_DATA,
+ "krb5_get_init_creds_opt_set_fast_flags not available.\n");
+ return 0;
+#endif
+}
+
+
+#ifndef HAVE_KRB5_UNPARSE_NAME_FLAGS
+#ifndef REALM_SEP
+#define REALM_SEP '@'
+#endif
+#ifndef COMPONENT_SEP
+#define COMPONENT_SEP '/'
+#endif
+
+static int
+sss_krb5_copy_component_quoting(char *dest, const krb5_data *src, int flags)
+{
+ int j;
+ const char *cp = src->data;
+ char *q = dest;
+ int length = src->length;
+
+ if (flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) {
+ memcpy(dest, src->data, src->length);
+ return src->length;
+ }
+
+ for (j=0; j < length; j++,cp++) {
+ int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) &&
+ !(flags & KRB5_PRINCIPAL_UNPARSE_SHORT);
+
+ switch (*cp) {
+ case REALM_SEP:
+ if (no_realm) {
+ *q++ = *cp;
+ break;
+ }
+ case COMPONENT_SEP:
+ case '\\':
+ *q++ = '\\';
+ *q++ = *cp;
+ break;
+ case '\t':
+ *q++ = '\\';
+ *q++ = 't';
+ break;
+ case '\n':
+ *q++ = '\\';
+ *q++ = 'n';
+ break;
+ case '\b':
+ *q++ = '\\';
+ *q++ = 'b';
+ break;
+ case '\0':
+ *q++ = '\\';
+ *q++ = '0';
+ break;
+ default:
+ *q++ = *cp;
+ }
+ }
+ return q - dest;
+}
+
+static int
+sss_krb5_component_length_quoted(const krb5_data *src, int flags)
+{
+ const char *cp = src->data;
+ int length = src->length;
+ int j;
+ int size = length;
+
+ if ((flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) == 0) {
+ int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) &&
+ !(flags & KRB5_PRINCIPAL_UNPARSE_SHORT);
+
+ for (j = 0; j < length; j++,cp++)
+ if ((!no_realm && *cp == REALM_SEP) ||
+ *cp == COMPONENT_SEP ||
+ *cp == '\0' || *cp == '\\' || *cp == '\t' ||
+ *cp == '\n' || *cp == '\b')
+ size++;
+ }
+
+ return size;
+}
+
+#endif /* HAVE_KRB5_UNPARSE_NAME_FLAGS */
+
+
+krb5_error_code
+sss_krb5_parse_name_flags(krb5_context context, const char *name, int flags,
+ krb5_principal *principal)
+{
+#ifdef HAVE_KRB5_PARSE_NAME_FLAGS
+ return krb5_parse_name_flags(context, name, flags, principal);
+#else
+ if (flags != 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "krb5_parse_name_flags not available on " \
+ "this plattform, names are parsed " \
+ "without flags. Some features like " \
+ "enterprise principals might not work " \
+ "as expected.\n");
+ }
+
+ return krb5_parse_name(context, name, principal);
+#endif
+}
+
+krb5_error_code
+sss_krb5_unparse_name_flags(krb5_context context, krb5_const_principal principal,
+ int flags, char **name)
+{
+#ifdef HAVE_KRB5_UNPARSE_NAME_FLAGS
+ return krb5_unparse_name_flags(context, principal, flags, name);
+#else
+ char *cp, *q;
+ int i;
+ int length;
+ krb5_int32 nelem;
+ unsigned int totalsize = 0;
+ char *default_realm = NULL;
+ krb5_error_code ret = 0;
+
+ if (name != NULL)
+ *name = NULL;
+
+ if (!principal || !name)
+ return KRB5_PARSE_MALFORMED;
+
+ if (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) {
+ /* omit realm if local realm */
+ krb5_principal_data p;
+
+ ret = krb5_get_default_realm(context, &default_realm);
+ if (ret != 0)
+ goto cleanup;
+
+ krb5_princ_realm(context, &p)->length = strlen(default_realm);
+ krb5_princ_realm(context, &p)->data = default_realm;
+
+ if (krb5_realm_compare(context, &p, principal))
+ flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM;
+ }
+
+ if ((flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) == 0) {
+ totalsize += sss_krb5_component_length_quoted(krb5_princ_realm(context,
+ principal),
+ flags);
+ totalsize++;
+ }
+
+ nelem = krb5_princ_size(context, principal);
+ for (i = 0; i < (int) nelem; i++) {
+ cp = krb5_princ_component(context, principal, i)->data;
+ totalsize += sss_krb5_component_length_quoted(krb5_princ_component(context, principal, i), flags);
+ totalsize++;
+ }
+ if (nelem == 0)
+ totalsize++;
+
+ *name = malloc(totalsize);
+
+ if (!*name) {
+ ret = ENOMEM;
+ goto cleanup;
+ }
+
+ q = *name;
+
+ for (i = 0; i < (int) nelem; i++) {
+ cp = krb5_princ_component(context, principal, i)->data;
+ length = krb5_princ_component(context, principal, i)->length;
+ q += sss_krb5_copy_component_quoting(q,
+ krb5_princ_component(context,
+ principal,
+ i),
+ flags);
+ *q++ = COMPONENT_SEP;
+ }
+
+ if (i > 0)
+ q--;
+ if ((flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) == 0) {
+ *q++ = REALM_SEP;
+ q += sss_krb5_copy_component_quoting(q, krb5_princ_realm(context, principal), flags);
+ }
+ *q++ = '\0';
+
+cleanup:
+ free(default_realm);
+
+ return ret;
+#endif /* HAVE_KRB5_UNPARSE_NAME_FLAGS */
+}
+
+void sss_krb5_get_init_creds_opt_set_canonicalize(krb5_get_init_creds_opt *opts,
+ int canonicalize)
+{
+ /* FIXME: The extra check for HAVE_KRB5_TICKET_TIMES is a workaround due to Heimdal
+ * defining krb5_get_init_creds_opt_set_canonicalize() with a different set of
+ * arguments. We should use a better configure check in the future.
+ */
+#if defined(HAVE_KRB5_GET_INIT_CREDS_OPT_SET_CANONICALIZE) && defined(HAVE_KRB5_TICKET_TIMES)
+ krb5_get_init_creds_opt_set_canonicalize(opts, canonicalize);
+#else
+ DEBUG(SSSDBG_OP_FAILURE, "Kerberos principal canonicalization is not available!\n");
+#endif
+}
+
+#ifdef HAVE_KRB5_PRINCIPAL_GET_REALM
+void sss_krb5_princ_realm(krb5_context context, krb5_const_principal princ,
+ const char **realm, int *len)
+{
+ const char *realm_str = krb5_principal_get_realm(context, princ);
+
+ if (realm_str != NULL) {
+ *realm = realm_str;
+ *len = strlen(realm_str);
+ } else {
+ *realm = NULL;
+ *len = 0;
+ }
+}
+#else
+void sss_krb5_princ_realm(krb5_context context, krb5_const_principal princ,
+ const char **realm, int *len)
+{
+ const krb5_data *data;
+
+ data = krb5_princ_realm(context, princ);
+ if (data) {
+ *realm = data->data;
+ *len = data->length;
+ } else {
+ *realm = NULL;
+ *len = 0;
+ }
+}
+#endif
+
+krb5_error_code
+sss_krb5_free_keytab_entry_contents(krb5_context context,
+ krb5_keytab_entry *entry)
+{
+#ifdef HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS
+ return krb5_free_keytab_entry_contents(context, entry);
+#else
+ return krb5_kt_free_entry(context, entry);
+#endif
+}
+
+
+#ifdef HAVE_KRB5_SET_TRACE_CALLBACK
+
+#ifndef HAVE_KRB5_TRACE_INFO
+/* krb5-1.10 had struct krb5_trace_info, 1.11 has type named krb5_trace_info */
+typedef struct krb5_trace_info krb5_trace_info;
+#endif /* HAVE_KRB5_TRACE_INFO */
+
+static void
+sss_child_krb5_trace_cb(krb5_context context,
+ const krb5_trace_info *info, void *data)
+{
+ if (info == NULL) {
+ /* Null info means destroy the callback data. */
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "%s\n", info->message);
+}
+
+errno_t
+sss_child_set_krb5_tracing(krb5_context ctx)
+{
+ return krb5_set_trace_callback(ctx, sss_child_krb5_trace_cb, NULL);
+}
+#else /* HAVE_KRB5_SET_TRACE_CALLBACK */
+errno_t
+sss_child_set_krb5_tracing(krb5_context ctx)
+{
+ DEBUG(SSSDBG_CONF_SETTINGS, "krb5 tracing is not available\n");
+ return 0;
+}
+#endif /* HAVE_KRB5_SET_TRACE_CALLBACK */
+
+krb5_error_code sss_krb5_find_authdata(krb5_context context,
+ krb5_authdata *const *ticket_authdata,
+ krb5_authdata *const *ap_req_authdata,
+ krb5_authdatatype ad_type,
+ krb5_authdata ***results)
+{
+#ifdef HAVE_KRB5_FIND_AUTHDATA
+ return krb5_find_authdata(context, ticket_authdata, ap_req_authdata,
+ ad_type, results);
+#else
+ return ENOTSUP;
+#endif
+}
+
+krb5_error_code sss_extract_pac(krb5_context ctx,
+ krb5_ccache ccache,
+ krb5_principal server_principal,
+ krb5_principal client_principal,
+ krb5_keytab keytab,
+ uint32_t check_pac_flags,
+ krb5_authdata ***_pac_authdata)
+{
+ krb5_error_code kerr;
+ krb5_creds mcred;
+ krb5_creds cred;
+ krb5_authdata **pac_authdata = NULL;
+ krb5_pac pac = NULL;
+ krb5_ticket *ticket = NULL;
+ krb5_keytab_entry entry;
+
+ memset(&entry, 0, sizeof(entry));
+ memset(&mcred, 0, sizeof(mcred));
+ memset(&cred, 0, sizeof(mcred));
+
+ mcred.server = server_principal;
+ mcred.client = client_principal;
+
+ kerr = krb5_cc_retrieve_cred(ctx, ccache, 0, &mcred, &cred);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_cc_retrieve_cred failed.\n");
+ goto done;
+ }
+
+ kerr = krb5_decode_ticket(&cred.ticket, &ticket);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_decode_ticket failed.\n");
+ goto done;
+ }
+
+ kerr = krb5_server_decrypt_ticket_keytab(ctx, keytab, ticket);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_server_decrypt_ticket_keytab failed.\n");
+ goto done;
+ }
+
+ kerr = sss_krb5_find_authdata(ctx,
+ ticket->enc_part2->authorization_data, NULL,
+ KRB5_AUTHDATA_WIN2K_PAC, &pac_authdata);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_find_authdata failed.\n");
+ goto done;
+ }
+
+ if (pac_authdata == NULL || pac_authdata[0] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "No PAC authdata available.\n");
+ if (check_pac_flags & CHECK_PAC_PRESENT) {
+ kerr = ERR_CHECK_PAC_FAILED;
+ } else {
+ kerr = ENOENT;
+ }
+ goto done;
+ }
+
+ if (pac_authdata[1] != NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "More than one PAC autdata found.\n");
+ kerr = EINVAL;
+ goto done;
+ }
+
+ kerr = krb5_pac_parse(ctx, pac_authdata[0]->contents,
+ pac_authdata[0]->length, &pac);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_pac_parse failed.\n");
+ goto done;
+ }
+
+ kerr = krb5_kt_get_entry(ctx, keytab, ticket->server,
+ ticket->enc_part.kvno, ticket->enc_part.enctype,
+ &entry);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_kt_get_entry failed.\n");
+ goto done;
+ }
+
+ kerr = krb5_pac_verify(ctx, pac, 0, NULL, &entry.key, NULL);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_pac_verify failed.\n");
+ goto done;
+ }
+
+ *_pac_authdata = pac_authdata;
+ kerr = 0;
+
+done:
+ if (kerr != 0) {
+ krb5_free_authdata(ctx, pac_authdata);
+ }
+ if (entry.magic != 0) {
+ krb5_free_keytab_entry_contents(ctx, &entry);
+ }
+ krb5_pac_free(ctx, pac);
+ if (ticket != NULL) {
+ krb5_free_ticket(ctx, ticket);
+ }
+
+ krb5_free_cred_contents(ctx, &cred);
+ return kerr;
+}
+
+char * sss_get_ccache_name_for_principal(TALLOC_CTX *mem_ctx,
+ krb5_context ctx,
+ krb5_principal principal,
+ const char *location)
+{
+#ifdef HAVE_KRB5_CC_COLLECTION
+ krb5_error_code kerr;
+ krb5_ccache tmp_cc = NULL;
+ char *tmp_ccname = NULL;
+ char *ret_ccname = NULL;
+
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Location: [%s]\n", location);
+
+ kerr = krb5_cc_set_default_name(ctx, location);
+ if (kerr != 0) {
+ KRB5_DEBUG(SSSDBG_MINOR_FAILURE, ctx, kerr);
+ return NULL;
+ }
+
+ kerr = krb5_cc_cache_match(ctx, principal, &tmp_cc);
+ if (kerr != 0) {
+ const char *err_msg = sss_krb5_get_error_message(ctx, kerr);
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "krb5_cc_cache_match failed: [%d][%s]\n", kerr, err_msg);
+ sss_krb5_free_error_message(ctx, err_msg);
+ return NULL;
+ }
+
+ kerr = krb5_cc_get_full_name(ctx, tmp_cc, &tmp_ccname);
+ if (kerr != 0) {
+ KRB5_DEBUG(SSSDBG_MINOR_FAILURE, ctx, kerr);
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL,
+ "tmp_ccname: [%s]\n", tmp_ccname);
+
+ ret_ccname = talloc_strdup(mem_ctx, tmp_ccname);
+ if (ret_ccname == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed (ENOMEM).\n");
+ }
+
+done:
+ if (tmp_cc != NULL) {
+ kerr = krb5_cc_close(ctx, tmp_cc);
+ if (kerr != 0) {
+ KRB5_DEBUG(SSSDBG_MINOR_FAILURE, ctx, kerr);
+ }
+ }
+ krb5_free_string(ctx, tmp_ccname);
+
+ return ret_ccname;
+#else
+ return NULL;
+#endif /* HAVE_KRB5_CC_COLLECTION */
+}
+
+krb5_error_code sss_krb5_kt_have_content(krb5_context context,
+ krb5_keytab keytab)
+{
+#ifdef HAVE_KRB5_KT_HAVE_CONTENT
+ return krb5_kt_have_content(context, keytab);
+#else
+ krb5_keytab_entry entry;
+ krb5_kt_cursor cursor;
+ krb5_error_code kerr;
+ krb5_error_code kerr_end;
+
+ kerr = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "krb5_kt_start_seq_get failed, assuming no entries.\n");
+ return KRB5_KT_NOTFOUND;
+ }
+
+ kerr = krb5_kt_next_entry(context, keytab, &entry, &cursor);
+ kerr_end = krb5_kt_end_seq_get(context, keytab, &cursor);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "krb5_kt_next_entry failed, assuming no entries.\n");
+ return KRB5_KT_NOTFOUND;
+ }
+ kerr = krb5_free_keytab_entry_contents(context, &entry);
+
+ if (kerr_end != 0) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "krb5_kt_end_seq_get failed, ignored.\n");
+ }
+ if (kerr != 0) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "krb5_free_keytab_entry_contents failed, ignored.\n");
+ }
+
+ return 0;
+#endif
+}
+
+#define KDC_PROXY_INDICATOR "https://"
+#define KDC_PROXY_INDICATOR_LEN (sizeof(KDC_PROXY_INDICATOR) - 1)
+
+bool sss_krb5_realm_has_proxy(const char *realm)
+{
+ krb5_context context = NULL;
+ krb5_error_code kerr;
+ struct _profile_t *profile = NULL;
+ const char *profile_path[4] = {"realms", NULL, "kdc", NULL};
+ char **list = NULL;
+ bool res = false;
+ size_t c;
+
+ if (realm == NULL) {
+ return false;
+ }
+
+ kerr = sss_krb5_init_context(&context);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_krb5_init_context failed.\n");
+ return false;
+ }
+
+ kerr = krb5_get_profile(context, &profile);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_get_profile failed.\n");
+ goto done;
+ }
+
+ profile_path[1] = realm;
+
+ kerr = profile_get_values(profile, profile_path, &list);
+ if (kerr == PROF_NO_RELATION || kerr == PROF_NO_SECTION) {
+ goto done;
+ } else if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "profile_get_values failed.\n");
+ goto done;
+ }
+
+ for (c = 0; list[c] != NULL; c++) {
+ if (strncasecmp(KDC_PROXY_INDICATOR, list[c],
+ KDC_PROXY_INDICATOR_LEN) == 0) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Found KDC Proxy indicator [%s] in [%s].\n",
+ KDC_PROXY_INDICATOR, list[c]);
+ res = true;
+ break;
+ }
+ }
+
+done:
+ profile_free_list(list);
+ profile_release(profile);
+ krb5_free_context(context);
+
+ return res;
+}
+
+static errno_t iobuf_read_uint32be(struct sss_iobuf *iobuf,
+ uint32_t *_val)
+{
+ uint32_t beval;
+ errno_t ret;
+
+ ret = sss_iobuf_read_uint32(iobuf, &beval);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_val = be32toh(beval);
+ return EOK;
+}
+
+static errno_t iobuf_write_uint32be(struct sss_iobuf *iobuf,
+ uint32_t val)
+{
+ uint32_t beval;
+
+ beval = htobe32(val);
+ return sss_iobuf_write_uint32(iobuf, beval);
+}
+
+static errno_t iobuf_get_len_bytes(TALLOC_CTX *mem_ctx,
+ struct sss_iobuf *iobuf,
+ uint32_t *_nbytes,
+ uint8_t **_bytes)
+{
+ errno_t ret;
+ uint32_t nbytes;
+ uint8_t *bytes = NULL;
+
+ ret = iobuf_read_uint32be(iobuf, &nbytes);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ bytes = talloc_zero_size(mem_ctx, nbytes);
+ if (bytes == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_iobuf_read_len(iobuf, nbytes, bytes);
+ if (ret != EOK) {
+ talloc_free(bytes);
+ return ret;
+ }
+
+ *_bytes = bytes;
+ *_nbytes = nbytes;
+ return EOK;
+}
+
+void get_krb5_data_from_cred(struct sss_iobuf *iobuf, krb5_data *k5data)
+{
+ k5data->data = (char *) sss_iobuf_get_data(iobuf);
+ k5data->length = sss_iobuf_get_size(iobuf);
+}
+
+static errno_t get_krb5_data(TALLOC_CTX *mem_ctx,
+ struct sss_iobuf *iobuf,
+ krb5_data *k5data)
+{
+ errno_t ret;
+ uint32_t nbytes;
+ uint8_t *bytes = NULL;
+
+ ret = iobuf_get_len_bytes(mem_ctx, iobuf, &nbytes, &bytes);
+ if (ret != EOK) {
+ talloc_free(bytes);
+ return ret;
+ }
+
+ k5data->data = (char *) bytes; /* FIXME - the cast is ugly */
+ k5data->length = nbytes;
+ return EOK;
+}
+
+static errno_t set_krb5_data(struct sss_iobuf *iobuf,
+ krb5_data *k5data)
+{
+ errno_t ret;
+
+ ret = iobuf_write_uint32be(iobuf, k5data->length);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (k5data->length > 0) {
+ ret = sss_iobuf_write_len(iobuf,
+ (uint8_t *) k5data->data,
+ k5data->length);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+/* FIXME - it would be nice if Kerberos exported these APIs.. */
+krb5_error_code sss_krb5_unmarshal_princ(TALLOC_CTX *mem_ctx,
+ struct sss_iobuf *iobuf,
+ krb5_principal *_princ)
+{
+ krb5_principal princ = NULL;
+ krb5_error_code ret;
+ uint32_t ncomps;
+
+ if (iobuf == NULL || _princ == NULL) {
+ return EINVAL;
+ }
+
+ princ = talloc_zero(mem_ctx, struct krb5_principal_data);
+ if (princ == NULL) {
+ return ENOMEM;
+ }
+
+ princ->magic = KV5M_PRINCIPAL;
+
+ ret = iobuf_read_uint32be(iobuf, (uint32_t *) &princ->type);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ ret = iobuf_read_uint32be(iobuf, &ncomps);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ if (ncomps > sss_iobuf_get_capacity(iobuf)) {
+ /* Sanity check to avoid large allocations */
+ ret = EINVAL;
+ goto fail;
+ }
+
+ if (ncomps != 0) {
+ princ->data = talloc_zero_array(princ, krb5_data, ncomps);
+ if (princ->data == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ princ->length = ncomps;
+ }
+
+ ret = get_krb5_data(princ, iobuf, &princ->realm);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ for (size_t i = 0; i < ncomps; i++) {
+ ret = get_krb5_data(princ->data, iobuf, &princ->data[i]);
+ if (ret != EOK) {
+ goto fail;
+ }
+ }
+
+ *_princ = princ;
+ return 0;
+
+fail:
+ talloc_free(princ);
+ return ret;
+}
+
+krb5_error_code sss_krb5_marshal_princ(krb5_principal princ,
+ struct sss_iobuf *iobuf)
+{
+ krb5_error_code ret;
+
+ if (iobuf == NULL || princ == NULL) {
+ return EINVAL;
+ }
+
+ ret = iobuf_write_uint32be(iobuf, princ->type);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = iobuf_write_uint32be(iobuf, princ->length);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = set_krb5_data(iobuf, &princ->realm);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ for (int i = 0; i < princ->length; i++) {
+ ret = set_krb5_data(iobuf, &princ->data[i]);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+ return EOK;
+}
+
+krb5_error_code sss_krb5_init_context(krb5_context *context)
+{
+ krb5_error_code kerr;
+ const char *msg;
+
+ kerr = krb5_init_context(context);
+ if (kerr != 0) {
+ /* It is safe to call (sss_)krb5_get_error_message() with NULL as first
+ * argument. */
+ msg = sss_krb5_get_error_message(NULL, kerr);
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to init Kerberos context [%s]\n", msg);
+ sss_log(SSS_LOG_CRIT, "Failed to init Kerberos context [%s]\n", msg);
+ sss_krb5_free_error_message(NULL, msg);
+ }
+
+ return kerr;
+}
+
+bool sss_krb5_creds_compare(krb5_context kctx, krb5_creds *a, krb5_creds *b)
+{
+ if (!krb5_principal_compare(kctx, a->client, b->client)) {
+ return false;
+ }
+
+ if (!krb5_principal_compare(kctx, a->server, b->server)) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h
new file mode 100644
index 0000000..a9254e2
--- /dev/null
+++ b/src/util/sss_krb5.h
@@ -0,0 +1,207 @@
+/*
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2009-2010 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_KRB5_H__
+#define __SSS_KRB5_H__
+
+#include "config.h"
+
+#include <stdbool.h>
+#include <talloc.h>
+
+#ifdef HAVE_KRB5_KRB5_H
+#include <krb5/krb5.h>
+#else
+#include <krb5.h>
+#endif
+
+#include "util/sss_iobuf.h"
+#include "util/util.h"
+#include <uuid/uuid.h>
+
+#define KRB5_CHILD_LOG_FILE "krb5_child"
+#define LDAP_CHILD_LOG_FILE "ldap_child"
+
+/* MIT Kerberos has the same hardcoded warning interval of 7 days. Due to the
+ * fact that using the expiration time of a Kerberos password with LDAP
+ * authentication is presumably a rare case a separate config option is not
+ * necessary. */
+#define KERBEROS_PWEXPIRE_WARNING_TIME (7 * 24 * 60 * 60)
+
+const char *sss_printable_keytab_name(krb5_context ctx, const char *keytab_name);
+
+#if defined HAVE_KRB5_CC_CACHE_MATCH && defined HAVE_KRB5_CC_GET_FULL_NAME
+#define HAVE_KRB5_CC_COLLECTION 1
+#endif
+
+const char * KRB5_CALLCONV sss_krb5_get_error_message (krb5_context,
+ krb5_error_code);
+
+void KRB5_CALLCONV sss_krb5_free_error_message(krb5_context, const char *);
+
+#define KRB5_DEBUG(level, errctx, krb5_error) do { \
+ const char *__krb5_error_msg; \
+ __krb5_error_msg = sss_krb5_get_error_message(errctx, krb5_error); \
+ DEBUG(level, "%d: [%d][%s]\n", __LINE__, krb5_error, __krb5_error_msg); \
+ sss_log(SSS_LOG_ERR, "%s", __krb5_error_msg); \
+ sss_krb5_free_error_message(errctx, __krb5_error_msg); \
+} while(0)
+
+krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_alloc(
+ krb5_context context,
+ krb5_get_init_creds_opt **opt);
+
+void KRB5_CALLCONV sss_krb5_get_init_creds_opt_free (krb5_context context,
+ krb5_get_init_creds_opt *opt);
+
+void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name);
+
+krb5_error_code find_principal_in_keytab(krb5_context ctx,
+ krb5_keytab keytab,
+ const char *pattern_primary,
+ const char *pattern_realm,
+ krb5_principal *princ);
+
+errno_t select_principal_from_keytab(TALLOC_CTX *mem_ctx,
+ const char *hostname,
+ const char *desired_realm,
+ const char *keytab_name,
+ char **_principal,
+ char **_primary,
+ char **_realm);
+
+#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_EXPIRE_CALLBACK
+typedef void
+(KRB5_CALLCONV *krb5_expire_callback_func)(krb5_context context, void *data,
+ krb5_timestamp password_expiration,
+ krb5_timestamp account_expiration,
+ krb5_boolean is_last_req);
+#endif
+krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_expire_callback(
+ krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_expire_callback_func cb,
+ void *data);
+
+errno_t check_fast(const char *str, bool *use_fast);
+
+krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_fast_ccache_name(
+ krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ const char *fast_ccache_name);
+
+krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_fast_flags(
+ krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_flags flags);
+
+#if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_FLAGS
+#define SSS_KRB5_FAST_REQUIRED KRB5_FAST_REQUIRED
+#else
+#define SSS_KRB5_FAST_REQUIRED 0
+#endif
+
+
+#ifndef HAVE_KRB5_PARSE_NAME_FLAGS
+#define KRB5_PRINCIPAL_PARSE_NO_REALM 0x1
+#define KRB5_PRINCIPAL_PARSE_REQUIRE_REALM 0x2
+#define KRB5_PRINCIPAL_PARSE_ENTERPRISE 0x4
+#endif
+krb5_error_code
+sss_krb5_parse_name_flags(krb5_context context, const char *name, int flags,
+ krb5_principal *principal);
+
+#ifndef HAVE_KRB5_UNPARSE_NAME_FLAGS
+#define KRB5_PRINCIPAL_UNPARSE_SHORT 0x1
+#define KRB5_PRINCIPAL_UNPARSE_NO_REALM 0x2
+#define KRB5_PRINCIPAL_UNPARSE_DISPLAY 0x4
+#endif
+krb5_error_code
+sss_krb5_unparse_name_flags(krb5_context context, krb5_const_principal principal,
+ int flags, char **name);
+
+void sss_krb5_get_init_creds_opt_set_canonicalize(krb5_get_init_creds_opt *opts,
+ int canonicalize);
+
+enum sss_krb5_cc_type {
+ SSS_KRB5_TYPE_FILE,
+#ifdef HAVE_KRB5_CC_COLLECTION
+ SSS_KRB5_TYPE_DIR,
+ SSS_KRB5_TYPE_KEYRING,
+#endif /* HAVE_KRB5_CC_COLLECTION */
+
+ SSS_KRB5_TYPE_UNKNOWN
+};
+
+/* === Compatibility routines for the Heimdal Kerberos implementation === */
+
+void sss_krb5_princ_realm(krb5_context context, krb5_const_principal princ,
+ const char **realm, int *len);
+
+krb5_error_code
+sss_krb5_free_keytab_entry_contents(krb5_context context,
+ krb5_keytab_entry *entry);
+
+#ifdef HAVE_KRB5_TICKET_TIMES
+typedef krb5_ticket_times sss_krb5_ticket_times;
+#elif defined(HAVE_KRB5_TIMES)
+typedef krb5_times sss_krb5_ticket_times;
+#endif
+
+/* Redirect libkrb5 tracing towards our DEBUG statements */
+errno_t sss_child_set_krb5_tracing(krb5_context ctx);
+
+krb5_error_code sss_krb5_find_authdata(krb5_context context,
+ krb5_authdata *const *ticket_authdata,
+ krb5_authdata *const *ap_req_authdata,
+ krb5_authdatatype ad_type,
+ krb5_authdata ***results);
+
+krb5_error_code sss_extract_pac(krb5_context ctx,
+ krb5_ccache ccache,
+ krb5_principal server_principal,
+ krb5_principal client_principal,
+ krb5_keytab keytab,
+ uint32_t check_pac_flags,
+ krb5_authdata ***_pac_authdata);
+
+char * sss_get_ccache_name_for_principal(TALLOC_CTX *mem_ctx,
+ krb5_context ctx,
+ krb5_principal principal,
+ const char *location);
+
+krb5_error_code sss_krb5_kt_have_content(krb5_context context,
+ krb5_keytab keytab);
+
+bool sss_krb5_realm_has_proxy(const char *realm);
+
+krb5_error_code sss_krb5_marshal_princ(krb5_principal princ,
+ struct sss_iobuf *iobuf);
+
+krb5_error_code sss_krb5_unmarshal_princ(TALLOC_CTX *mem_ctx,
+ struct sss_iobuf *iobuf,
+ krb5_principal *_princ);
+
+krb5_error_code sss_krb5_init_context(krb5_context *context);
+
+void get_krb5_data_from_cred(struct sss_iobuf *iobuf, krb5_data *k5data);
+
+bool sss_krb5_creds_compare(krb5_context kctx, krb5_creds *a, krb5_creds *b);
+#endif /* __SSS_KRB5_H__ */
diff --git a/src/util/sss_ldap.c b/src/util/sss_ldap.c
new file mode 100644
index 0000000..756574c
--- /dev/null
+++ b/src/util/sss_ldap.c
@@ -0,0 +1,499 @@
+/*
+ Authors:
+ Sumit Bose <sbose@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/>.
+*/
+
+#include "config.h"
+
+#include <fcntl.h>
+
+#include "util/util.h"
+#include "util/sss_sockets.h"
+#include "util/sss_ldap.h"
+
+#include "providers/ldap/sdap.h"
+
+const char* sss_ldap_err2string(int err)
+{
+ if (IS_SSSD_ERROR(err)) {
+ return sss_strerror(err);
+ } else {
+ return ldap_err2string(err);
+ }
+}
+
+int sss_ldap_get_diagnostic_msg(TALLOC_CTX *mem_ctx, LDAP *ld, char **_errmsg)
+{
+ char *errmsg = NULL;
+ int optret;
+
+ optret = ldap_get_option(ld, SDAP_DIAGNOSTIC_MESSAGE, (void*)&errmsg);
+ if (optret != LDAP_SUCCESS) {
+ return EINVAL;
+ }
+
+ *_errmsg = talloc_strdup(mem_ctx, errmsg ? errmsg : "unknown error");
+ ldap_memfree(errmsg);
+ if (*_errmsg == NULL) {
+ return ENOMEM;
+ }
+ return EOK;
+}
+
+int sss_ldap_control_create(const char *oid, int iscritical,
+ struct berval *value, int dupval,
+ LDAPControl **ctrlp)
+{
+#ifdef HAVE_LDAP_CONTROL_CREATE
+ return ldap_control_create(oid, iscritical, value, dupval, ctrlp);
+#else
+ LDAPControl *lc = NULL;
+
+ if (oid == NULL || ctrlp == NULL) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ lc = calloc(sizeof(LDAPControl), 1);
+ if (lc == NULL) {
+ return LDAP_NO_MEMORY;
+ }
+
+ lc->ldctl_oid = strdup(oid);
+ if (lc->ldctl_oid == NULL) {
+ free(lc);
+ return LDAP_NO_MEMORY;
+ }
+
+ if (value != NULL && value->bv_val != NULL) {
+ if (dupval == 0) {
+ lc->ldctl_value = *value;
+ } else {
+ ber_dupbv(&lc->ldctl_value, value);
+ if (lc->ldctl_value.bv_val == NULL) {
+ free(lc->ldctl_oid);
+ free(lc);
+ return LDAP_NO_MEMORY;
+ }
+ }
+ }
+
+ lc->ldctl_iscritical = iscritical;
+
+ *ctrlp = lc;
+
+ return LDAP_SUCCESS;
+#endif
+}
+
+#ifdef HAVE_LDAP_INIT_FD
+
+#define LDAP_PROTO_TCP 1 /* ldap:// */
+#define LDAP_PROTO_UDP 2 /* reserved */
+#define LDAP_PROTO_IPC 3 /* ldapi:// */
+#define LDAP_PROTO_EXT 4 /* user-defined socket/sockbuf */
+
+extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld);
+
+static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq);
+#endif
+
+struct sss_ldap_init_state {
+ LDAP *ldap;
+ int sd;
+ const char *uri;
+ bool use_udp;
+};
+
+static int sss_ldap_init_state_destructor(void *data)
+{
+ struct sss_ldap_init_state *state = (struct sss_ldap_init_state *)data;
+
+ if (state->ldap) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "calling ldap_unbind_ext for ldap:[%p] sd:[%d]\n",
+ state->ldap, state->sd);
+ ldap_unbind_ext(state->ldap, NULL, NULL);
+ }
+ if (state->sd != -1) {
+ DEBUG(SSSDBG_TRACE_FUNC, "closing socket [%d]\n", state->sd);
+ close(state->sd);
+ state->sd = -1;
+ }
+
+ return 0;
+}
+
+
+struct tevent_req *sss_ldap_init_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *uri,
+ struct sockaddr *addr,
+ int addr_len, int timeout)
+{
+ int ret = EOK;
+ struct tevent_req *req;
+ struct sss_ldap_init_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct sss_ldap_init_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n");
+ return NULL;
+ }
+
+ talloc_set_destructor((TALLOC_CTX *)state, sss_ldap_init_state_destructor);
+
+ state->ldap = NULL;
+ state->sd = -1;
+ state->uri = uri;
+ state->use_udp = strncmp(uri, "cldap", 5) == 0 ? true : false;
+
+#ifdef HAVE_LDAP_INIT_FD
+ struct tevent_req *subreq;
+
+ subreq = sssd_async_socket_init_send(state, ev, state->use_udp, addr,
+ addr_len, timeout);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_CRIT_FAILURE, "sssd_async_socket_init_send failed.\n");
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, sss_ldap_init_sys_connect_done, req);
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+#else
+ DEBUG(SSSDBG_MINOR_FAILURE, "ldap_init_fd not available, "
+ "will use ldap_initialize with uri [%s].\n", uri);
+ ret = ldap_initialize(&state->ldap, uri);
+ if (ret == LDAP_SUCCESS) {
+ tevent_req_done(req);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "ldap_initialize failed [%s].\n", sss_ldap_err2string(ret));
+ if (ret == LDAP_SERVER_DOWN) {
+ tevent_req_error(req, ETIMEDOUT);
+ } else {
+ tevent_req_error(req, EIO);
+ }
+ }
+#endif
+
+ tevent_req_post(req, ev);
+ return req;
+}
+
+#ifdef HAVE_LDAP_INIT_FD
+static errno_t unset_fcntl_flags(int fd, int fl_flags)
+{
+ errno_t ret;
+ int flags;
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "fcntl F_GETFL failed [%s].\n", strerror(ret));
+ return ret;
+ }
+
+ /* unset flags */
+ flags &= ~fl_flags;
+
+ ret = fcntl(fd, F_SETFL, flags);
+ if (ret != EOK) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "fcntl F_SETFL failed [%s].\n", strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sss_ldap_init_state *state = tevent_req_data(req,
+ struct sss_ldap_init_state);
+ char *tlserr;
+ int ret;
+ int lret;
+ int optret;
+ int ticks_before_install;
+ int ticks_after_install;
+
+ ret = sssd_async_socket_init_recv(subreq, &state->sd);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sssd_async_socket_init request failed: [%d]: %s.\n",
+ ret, sss_strerror(ret));
+ goto fail;
+ }
+
+ /* openldap < 2.5 does not correctly handle O_NONBLOCK during starttls for
+ * ldaps, so we need to remove the flag here. This is fine since I/O events
+ * are handled via tevent so we only read when there is data available.
+ *
+ * We need to keep O_NONBLOCK due to a bug in openldap to correctly perform
+ * a parallel CLDAP pings without timeout. See:
+ * https://bugs.openldap.org/show_bug.cgi?id=9328
+ *
+ * @todo remove this when the bug is fixed and we can put a hard requirement
+ * on newer openldap.
+ */
+ if (!state->use_udp) {
+ ret = unset_fcntl_flags(state->sd, O_NONBLOCK);
+ if (ret != EOK) {
+ goto fail;
+ }
+ }
+
+ /* Initialize LDAP handler */
+
+ lret = ldap_init_fd(state->sd,
+ state->use_udp ? LDAP_PROTO_UDP : LDAP_PROTO_TCP,
+ state->uri, &state->ldap);
+ if (lret != LDAP_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "ldap_init_fd failed: %s. [%d][%s]\n",
+ sss_ldap_err2string(lret), state->sd, state->uri);
+ ret = lret == LDAP_SERVER_DOWN ? ETIMEDOUT : EIO;
+ goto fail;
+ }
+
+ if (ldap_is_ldaps_url(state->uri)) {
+ ticks_before_install = get_watchdog_ticks();
+ lret = ldap_install_tls(state->ldap);
+ ticks_after_install = get_watchdog_ticks();
+ if (lret != LDAP_SUCCESS) {
+ if (lret == LDAP_LOCAL_ERROR) {
+ DEBUG(SSSDBG_FUNC_DATA, "TLS/SSL already in place.\n");
+ } else {
+
+ optret = sss_ldap_get_diagnostic_msg(state, state->ldap,
+ &tlserr);
+ if (optret == LDAP_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "ldap_install_tls failed: [%s] [%s]\n",
+ sss_ldap_err2string(lret), tlserr);
+ sss_log(SSS_LOG_ERR,
+ "Could not start TLS encryption. %s", tlserr);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "ldap_install_tls failed: [%s]\n",
+ sss_ldap_err2string(lret));
+ sss_log(SSS_LOG_ERR, "Could not start TLS encryption. "
+ "Check for certificate issues.");
+ }
+
+ if (ticks_after_install > ticks_before_install) {
+ ret = ERR_TLS_HANDSHAKE_INTERRUPTED;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Assuming %s\n",
+ sss_ldap_err2string(ret));
+ goto fail;
+ }
+
+ ret = EIO;
+ goto fail;
+ }
+ }
+ }
+
+ tevent_req_done(req);
+ return;
+
+fail:
+ tevent_req_error(req, ret);
+}
+#endif
+
+int sss_ldap_init_recv(struct tevent_req *req, LDAP **ldap, int *sd)
+{
+ struct sss_ldap_init_state *state = tevent_req_data(req,
+ struct sss_ldap_init_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ /* Everything went well therefore we do not want to release resources */
+ talloc_set_destructor(state, NULL);
+
+ *ldap = state->ldap;
+ *sd = state->sd;
+
+ return EOK;
+}
+
+/*
+ * _filter will contain combined filters from all possible search bases
+ * or NULL if it should be empty
+ */
+
+
+bool sss_ldap_dn_in_search_bases_len(TALLOC_CTX *mem_ctx,
+ const char *dn,
+ struct sdap_search_base **search_bases,
+ char **_filter,
+ int *_match_len)
+{
+ struct sdap_search_base *base;
+ int basedn_len, dn_len;
+ int len_diff;
+ int i, j;
+ bool base_confirmed = false;
+ bool comma_found = false;
+ bool backslash_found = false;
+ char *filter = NULL;
+ bool ret = false;
+ int match_len;
+
+ if (dn == NULL) {
+ DEBUG(SSSDBG_FUNC_DATA, "dn is NULL\n");
+ ret = false;
+ goto done;
+ }
+
+ if (search_bases == NULL) {
+ DEBUG(SSSDBG_FUNC_DATA, "search_bases is NULL\n");
+ ret = false;
+ goto done;
+ }
+
+ dn_len = strlen(dn);
+ for (i = 0; search_bases[i] != NULL; i++) {
+ base = search_bases[i];
+ basedn_len = strlen(base->basedn);
+
+ if (basedn_len > dn_len) {
+ continue;
+ }
+
+ len_diff = dn_len - basedn_len;
+ base_confirmed = (strncasecmp(&dn[len_diff], base->basedn, basedn_len) == 0);
+ if (!base_confirmed) {
+ continue;
+ }
+ match_len = basedn_len;
+
+ switch (base->scope) {
+ case LDAP_SCOPE_BASE:
+ /* dn > base? */
+ if (len_diff != 0) {
+ continue;
+ }
+ break;
+ case LDAP_SCOPE_ONELEVEL:
+ if (len_diff == 0) {
+ /* Base object doesn't belong to scope=one
+ * search */
+ continue;
+ }
+
+ comma_found = false;
+ for (j = 0; j < len_diff - 1; j++) { /* ignore comma before base */
+ if (dn[j] == '\\') {
+ backslash_found = true;
+ } else if (dn[j] == ',' && !backslash_found) {
+ comma_found = true;
+ break;
+ } else {
+ backslash_found = false;
+ }
+ }
+
+ /* it has at least one more level */
+ if (comma_found) {
+ continue;
+ }
+
+ break;
+ case LDAP_SCOPE_SUBTREE:
+ /* dn length >= base dn length && base_confirmed == true */
+ break;
+ default:
+ DEBUG(SSSDBG_FUNC_DATA, "Unsupported scope: %d\n", base->scope);
+ continue;
+ }
+
+ /*
+ * If we get here, the dn is valid.
+ * If no filter is set, than return true immediately.
+ * Append filter otherwise.
+ */
+ ret = true;
+ if (_match_len) {
+ *_match_len = match_len;
+ }
+
+ if (base->filter == NULL || _filter == NULL) {
+ goto done;
+ } else {
+ filter = talloc_strdup_append(filter, base->filter);
+ if (filter == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup_append() failed\n");
+ ret = false;
+ goto done;
+ }
+ }
+ }
+
+ if (_filter != NULL) {
+ if (filter != NULL) {
+ *_filter = talloc_asprintf(mem_ctx, "(|%s)", filter);
+ if (*_filter == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "talloc_asprintf_append() failed\n");
+ ret = false;
+ goto done;
+ }
+ } else {
+ *_filter = NULL;
+ }
+ }
+
+done:
+ talloc_free(filter);
+ return ret;
+}
+
+bool sss_ldap_dn_in_search_bases(TALLOC_CTX *mem_ctx,
+ const char *dn,
+ struct sdap_search_base **search_bases,
+ char **_filter)
+{
+ return sss_ldap_dn_in_search_bases_len(mem_ctx, dn, search_bases, _filter,
+ NULL);
+}
+
+char *sss_ldap_encode_ndr_uint32(TALLOC_CTX *mem_ctx, uint32_t flags)
+{
+ char hex[9]; /* 4 bytes in hex + terminating zero */
+ errno_t ret;
+
+ ret = snprintf(hex, 9, "%08x", flags);
+ if (ret != 8) {
+ return NULL;
+ }
+
+ return talloc_asprintf(mem_ctx, "\\%c%c\\%c%c\\%c%c\\%c%c",
+ hex[6], hex[7], hex[4], hex[5],
+ hex[2], hex[3], hex[0], hex[1]);
+}
diff --git a/src/util/sss_ldap.h b/src/util/sss_ldap.h
new file mode 100644
index 0000000..b5fc61f
--- /dev/null
+++ b/src/util/sss_ldap.h
@@ -0,0 +1,99 @@
+/*
+ Authors:
+ Sumit Bose <sbose@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 __SSS_LDAP_H__
+#define __SSS_LDAP_H__
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ldap.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#ifndef LDAP_CONTROL_PWEXPIRED
+#define LDAP_CONTROL_PWEXPIRED "2.16.840.1.113730.3.4.4"
+#endif
+
+#ifndef LDAP_CONTROL_PWEXPIRING
+#define LDAP_CONTROL_PWEXPIRING "2.16.840.1.113730.3.4.5"
+#endif
+
+#ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
+#define SDAP_DIAGNOSTIC_MESSAGE LDAP_OPT_DIAGNOSTIC_MESSAGE
+#else
+#ifdef LDAP_OPT_ERROR_STRING
+#define SDAP_DIAGNOSTIC_MESSAGE LDAP_OPT_ERROR_STRING
+#else
+#error No extended diagnostic message available
+#endif
+#endif
+
+const char* sss_ldap_err2string(int err);
+
+int sss_ldap_get_diagnostic_msg(TALLOC_CTX *mem_ctx,
+ LDAP *ld,
+ char **_errmsg);
+
+#ifndef LDAP_SERVER_ASQ_OID
+#define LDAP_SERVER_ASQ_OID "1.2.840.113556.1.4.1504"
+#endif /* LDAP_SERVER_ASQ_OID */
+
+#ifndef LDAP_SERVER_SD_OID
+#define LDAP_SERVER_SD_OID "1.2.840.113556.1.4.801"
+#endif /* LDAP_SERVER_SD_OID */
+
+
+/*
+ * The following four flags specify which security descriptor parts to retrieve
+ * during sd_search (see http://msdn.microsoft.com/en-us/library/aa366987.aspx)
+ */
+#define SECINFO_OWNER ( 0x00000001 )
+#define SECINFO_GROUP ( 0x00000002 )
+#define SECINFO_DACL ( 0x00000004 )
+#define SECINFO_SACL ( 0x00000008 )
+
+int sss_ldap_control_create(const char *oid, int iscritical,
+ struct berval *value, int dupval,
+ LDAPControl **ctrlp);
+
+struct tevent_req *sss_ldap_init_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *uri,
+ struct sockaddr *addr,
+ int addr_len, int timeout);
+
+int sss_ldap_init_recv(struct tevent_req *req, LDAP **ldap, int *sd);
+
+struct sdap_options;
+struct sdap_search_base;
+bool sss_ldap_dn_in_search_bases(TALLOC_CTX *mem_ctx,
+ const char *dn,
+ struct sdap_search_base **search_bases,
+ char **_filter);
+
+bool sss_ldap_dn_in_search_bases_len(TALLOC_CTX *mem_ctx,
+ const char *dn,
+ struct sdap_search_base **search_bases,
+ char **_filter,
+ int *_match_len);
+
+char *sss_ldap_encode_ndr_uint32(TALLOC_CTX *mem_ctx, uint32_t flags);
+
+#endif /* __SSS_LDAP_H__ */
diff --git a/src/util/sss_log.c b/src/util/sss_log.c
new file mode 100644
index 0000000..3c415d4
--- /dev/null
+++ b/src/util/sss_log.c
@@ -0,0 +1,126 @@
+/*
+ SSSD
+
+ sss_log.c
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2010 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 "util/util.h"
+
+#ifdef WITH_JOURNALD
+#include <systemd/sd-journal.h>
+#else /* WITH_JOURNALD */
+#include <syslog.h>
+#endif /* WITH_JOURNALD */
+
+static int sss_to_syslog(int priority)
+{
+ switch(priority) {
+ case SSS_LOG_EMERG:
+ return LOG_EMERG;
+ case SSS_LOG_ALERT:
+ return LOG_ALERT;
+ case SSS_LOG_CRIT:
+ return LOG_CRIT;
+ case SSS_LOG_ERR:
+ return LOG_ERR;
+ case SSS_LOG_WARNING:
+ return LOG_WARNING;
+ case SSS_LOG_NOTICE:
+ return LOG_NOTICE;
+ case SSS_LOG_INFO:
+ return LOG_INFO;
+ case SSS_LOG_DEBUG:
+ return LOG_DEBUG;
+ default:
+ /* If we've been passed an invalid priority, it's
+ * best to assume it's an emergency.
+ */
+ return LOG_EMERG;
+ }
+}
+
+static void sss_log_internal(int priority, int facility, const char *format,
+ va_list ap);
+
+void sss_log(int priority, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ sss_log_internal(priority, LOG_DAEMON, format, ap);
+ va_end(ap);
+}
+
+void sss_log_ext(int priority, int facility, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ sss_log_internal(priority, facility, format, ap);
+ va_end(ap);
+}
+
+
+
+#ifdef WITH_JOURNALD
+
+static void sss_log_internal(int priority, int facility, const char *format,
+ va_list ap)
+{
+ int syslog_priority;
+ int ret;
+ char *message;
+ const char *domain;
+
+ ret = vasprintf(&message, format, ap);
+
+ if (ret == -1) {
+ /* ENOMEM */
+ return;
+ }
+
+ domain = getenv(SSS_DOM_ENV);
+ if (domain == NULL) {
+ domain = "";
+ }
+
+ syslog_priority = sss_to_syslog(priority);
+ sd_journal_send("MESSAGE=%s", message,
+ "SSSD_DOMAIN=%s", domain,
+ "SSSD_PRG_NAME=sssd[%s]", debug_prg_name,
+ "PRIORITY=%i", syslog_priority,
+ "SYSLOG_FACILITY=%i", LOG_FAC(facility),
+ NULL);
+
+ free(message);
+}
+
+#else /* WITH_JOURNALD */
+
+static void sss_log_internal(int priority, int facility, const char *format,
+ va_list ap)
+{
+ int syslog_priority = sss_to_syslog(priority);
+
+ vsyslog(facility|syslog_priority, format, ap);
+}
+
+#endif /* WITH_JOURNALD */
diff --git a/src/util/sss_nss.c b/src/util/sss_nss.c
new file mode 100644
index 0000000..63fd069
--- /dev/null
+++ b/src/util/sss_nss.c
@@ -0,0 +1,233 @@
+/*
+ SSSD
+
+ Utility functions related to ID information
+
+ Copyright (C) Jan Zeleny <jzeleny@redhat.com> 2012
+
+ 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 "util/util.h"
+#include "util/sss_nss.h"
+
+char *expand_homedir_template(TALLOC_CTX *mem_ctx,
+ const char *template,
+ bool case_sensitive,
+ struct sss_nss_homedir_ctx *homedir_ctx)
+{
+ char *copy;
+ char *p;
+ char *n;
+ char *result = NULL;
+ char *res = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char *orig = NULL;
+ char *username = NULL;
+
+ if (template == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing template.\n");
+ return NULL;
+ }
+
+ if (homedir_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing home directory data.\n");
+ return NULL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return NULL;
+
+ copy = talloc_strdup(tmp_ctx, template);
+ if (copy == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
+ goto done;
+ }
+
+ result = talloc_strdup(tmp_ctx, "");
+ if (result == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
+ goto done;
+ }
+
+ p = copy;
+ while ( (n = strchr(p, '%')) != NULL) {
+ *n = '\0';
+ n++;
+ if ( *n == '\0' ) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "format error, single %% at the end of "
+ "the template.\n");
+ goto done;
+ }
+ switch( *n ) {
+ case 'u':
+ if (homedir_ctx->username == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot expand user name template because user name "
+ "is empty.\n");
+ goto done;
+ }
+ username = sss_output_name(tmp_ctx, homedir_ctx->username,
+ case_sensitive, 0);
+ if (username == NULL) {
+ goto done;
+ }
+
+ result = talloc_asprintf_append(result, "%s%s", p, username);
+ talloc_free(username);
+ break;
+
+ case 'l':
+ if (homedir_ctx->username == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot expand first letter of user name template "
+ "because user name is empty.\n");
+ goto done;
+ }
+ username = sss_output_name(tmp_ctx, homedir_ctx->username,
+ case_sensitive, 0);
+ if (username == NULL) {
+ goto done;
+ }
+
+ result = talloc_asprintf_append(result, "%s%c", p, username[0]);
+ talloc_free(username);
+ break;
+
+ case 'U':
+ if (homedir_ctx->uid == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot expand uid template "
+ "because uid is invalid.\n");
+ goto done;
+ }
+ result = talloc_asprintf_append(result, "%s%d", p,
+ homedir_ctx->uid);
+ break;
+
+ case 'd':
+ if (homedir_ctx->domain == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot expand domain name "
+ "template because domain name "
+ "is empty.\n");
+ goto done;
+ }
+ result = talloc_asprintf_append(result, "%s%s", p,
+ homedir_ctx->domain);
+ break;
+
+ case 'f':
+ if (homedir_ctx->domain == NULL
+ || homedir_ctx->username == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot expand fully qualified "
+ "name template because domain "
+ "or user name is empty.\n");
+ goto done;
+ }
+ username = sss_output_name(tmp_ctx, homedir_ctx->username,
+ case_sensitive, 0);
+ if (username == NULL) {
+ goto done;
+ }
+
+ result = talloc_asprintf_append(result, "%s%s@%s", p,
+ username, homedir_ctx->domain);
+ talloc_free(username);
+ break;
+
+ case 'o':
+ case 'h':
+ if (homedir_ctx->original == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Original home directory for %s is not available, "
+ "using empty string\n", homedir_ctx->username);
+ orig = "";
+ } else {
+ if (*n == 'o') {
+ orig = homedir_ctx->original;
+ } else {
+ orig = sss_tc_utf8_str_tolower(tmp_ctx,
+ homedir_ctx->original);
+ if (orig == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to lowercase the original home "
+ "directory.\n");
+ goto done;
+ }
+ }
+ }
+ result = talloc_asprintf_append(result, "%s%s", p, orig);
+ break;
+
+ case 'F':
+ if (homedir_ctx->flatname == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot expand domain name "
+ "template because domain flat "
+ "name is empty.\n");
+ goto done;
+ }
+ result = talloc_asprintf_append(result, "%s%s", p,
+ homedir_ctx->flatname);
+ break;
+
+ case 'H':
+ if (homedir_ctx->config_homedir_substr == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot expand home directory substring template "
+ "substring is empty.\n");
+ goto done;
+ }
+ result = talloc_asprintf_append(result, "%s%s", p,
+ homedir_ctx->config_homedir_substr);
+ break;
+
+ case 'P':
+ if (homedir_ctx->upn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot expand user principal name template "
+ "string is empty.\n");
+ goto done;
+ }
+ result = talloc_asprintf_append(result, "%s%s", p,
+ homedir_ctx->upn);
+ break;
+
+ case '%':
+ result = talloc_asprintf_append(result, "%s%%", p);
+ break;
+
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "format error, unknown template "
+ "[%%%c].\n", *n);
+ goto done;
+ }
+
+ if (result == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append failed.\n");
+ goto done;
+ }
+
+ p = n + 1;
+ }
+
+ result = talloc_asprintf_append(result, "%s", p);
+ if (result == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append failed.\n");
+ goto done;
+ }
+
+ res = talloc_move(mem_ctx, &result);
+done:
+ talloc_zfree(tmp_ctx);
+ return res;
+}
diff --git a/src/util/sss_nss.h b/src/util/sss_nss.h
new file mode 100644
index 0000000..2b8a5ae
--- /dev/null
+++ b/src/util/sss_nss.h
@@ -0,0 +1,42 @@
+/*
+ SSSD
+
+ Utility functions related to ID information
+
+ Copyright (C) Jan Zeleny <jzeleny@redhat.com> 2012
+
+ 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_NSS_H__
+#define __SSS_NSS_H__
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <talloc.h>
+
+struct sss_nss_homedir_ctx {
+ const char *username;
+ uint32_t uid;
+ const char *original;
+ const char *domain;
+ const char *flatname;
+ const char *config_homedir_substr;
+ const char *upn;
+};
+
+char *expand_homedir_template(TALLOC_CTX *mem_ctx, const char *template,
+ bool case_sensitive,
+ struct sss_nss_homedir_ctx *homedir_ctx);
+#endif
diff --git a/src/util/sss_pam_data.c b/src/util/sss_pam_data.c
new file mode 100644
index 0000000..f09b9c5
--- /dev/null
+++ b/src/util/sss_pam_data.c
@@ -0,0 +1,205 @@
+/*
+ SSSD
+
+ Utilities to for tha pam_data structure
+
+ Authors:
+ Sumit Bose <sbose@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/>.
+*/
+
+#include <security/pam_modules.h>
+
+#include "util/sss_pam_data.h"
+#include "util/sss_cli_cmd.h"
+
+#define PAM_SAFE_ITEM(item) item ? item : "not set"
+
+int pam_data_destructor(void *ptr)
+{
+ struct pam_data *pd = talloc_get_type(ptr, struct pam_data);
+
+ /* make sure to wipe any password from memory before freeing */
+ sss_authtok_wipe_password(pd->authtok);
+ sss_authtok_wipe_password(pd->newauthtok);
+
+ return 0;
+}
+
+struct pam_data *create_pam_data(TALLOC_CTX *mem_ctx)
+{
+ struct pam_data *pd;
+
+ pd = talloc_zero(mem_ctx, struct pam_data);
+ if (pd == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
+ goto failed;
+ }
+
+ pd->pam_status = PAM_SYSTEM_ERR;
+
+ pd->authtok = sss_authtok_new(pd);
+ if (pd->authtok == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
+ goto failed;
+ }
+
+ pd->newauthtok = sss_authtok_new(pd);
+ if (pd->newauthtok == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
+ goto failed;
+ }
+
+ talloc_set_destructor((TALLOC_CTX *) pd, pam_data_destructor);
+
+ return pd;
+
+failed:
+ talloc_free(pd);
+ return NULL;
+}
+
+errno_t copy_pam_data(TALLOC_CTX *mem_ctx, struct pam_data *src,
+ struct pam_data **dst)
+{
+ struct pam_data *pd = NULL;
+ errno_t ret;
+
+ pd = create_pam_data(mem_ctx);
+ if (pd == NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+
+ pd->cmd = src->cmd;
+ pd->priv = src->priv;
+
+ pd->domain = talloc_strdup(pd, src->domain);
+ if (pd->domain == NULL && src->domain != NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+ pd->user = talloc_strdup(pd, src->user);
+ if (pd->user == NULL && src->user != NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+ pd->service = talloc_strdup(pd, src->service);
+ if (pd->service == NULL && src->service != NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+ pd->tty = talloc_strdup(pd, src->tty);
+ if (pd->tty == NULL && src->tty != NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+ pd->ruser = talloc_strdup(pd, src->ruser);
+ if (pd->ruser == NULL && src->ruser != NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+ pd->rhost = talloc_strdup(pd, src->rhost);
+ if (pd->rhost == NULL && src->rhost != NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+
+ pd->cli_pid = src->cli_pid;
+ pd->client_id_num = src->client_id_num;
+
+ /* if structure pam_data was allocated on stack and zero initialized,
+ * than src->authtok and src->newauthtok are NULL, therefore
+ * instead of copying, new empty authtok will be created.
+ */
+ if (src->authtok) {
+ ret = sss_authtok_copy(src->authtok, pd->authtok);
+ if (ret) {
+ goto failed;
+ }
+ } else {
+ pd->authtok = sss_authtok_new(pd);
+ if (pd->authtok == NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+ }
+
+ if (src->newauthtok) {
+ ret = sss_authtok_copy(src->newauthtok, pd->newauthtok);
+ if (ret) {
+ goto failed;
+ }
+ } else {
+ pd->newauthtok = sss_authtok_new(pd);
+ if (pd->newauthtok == NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+ }
+
+ *dst = pd;
+
+ return EOK;
+
+failed:
+ talloc_free(pd);
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "copy_pam_data failed: (%d) %s.\n", ret, strerror(ret));
+ return ret;
+}
+
+void pam_print_data(int l, struct pam_data *pd)
+{
+ DEBUG(l, "command: %s\n", sss_cmd2str(pd->cmd));
+ DEBUG(l, "domain: %s\n", PAM_SAFE_ITEM(pd->domain));
+ DEBUG(l, "user: %s\n", PAM_SAFE_ITEM(pd->user));
+ DEBUG(l, "service: %s\n", PAM_SAFE_ITEM(pd->service));
+ DEBUG(l, "tty: %s\n", PAM_SAFE_ITEM(pd->tty));
+ DEBUG(l, "ruser: %s\n", PAM_SAFE_ITEM(pd->ruser));
+ DEBUG(l, "rhost: %s\n", PAM_SAFE_ITEM(pd->rhost));
+ DEBUG(l, "authtok type: %d (%s)\n",
+ sss_authtok_get_type(pd->authtok),
+ sss_authtok_type_to_str(sss_authtok_get_type(pd->authtok)));
+ DEBUG(l, "newauthtok type: %d (%s)\n",
+ sss_authtok_get_type(pd->newauthtok),
+ sss_authtok_type_to_str(sss_authtok_get_type(pd->newauthtok)));
+ DEBUG(l, "priv: %d\n", pd->priv);
+ DEBUG(l, "cli_pid: %d\n", pd->cli_pid);
+ DEBUG(l, "child_pid: %d\n", pd->child_pid);
+ DEBUG(l, "logon name: %s\n", PAM_SAFE_ITEM(pd->logon_name));
+ DEBUG(l, "flags: %d\n", pd->cli_flags);
+}
+
+int pam_add_response(struct pam_data *pd, enum response_type type,
+ int len, const uint8_t *data)
+{
+ struct response_data *new;
+
+ new = talloc(pd, struct response_data);
+ if (new == NULL) return ENOMEM;
+
+ new->type = type;
+ new->len = len;
+ new->data = talloc_memdup(new, data, len);
+ if (new->data == NULL) return ENOMEM;
+ new->do_not_send_to_client = false;
+ new->next = pd->resp_list;
+ pd->resp_list = new;
+
+ return EOK;
+}
diff --git a/src/util/sss_pam_data.h b/src/util/sss_pam_data.h
new file mode 100644
index 0000000..e9b90a8
--- /dev/null
+++ b/src/util/sss_pam_data.h
@@ -0,0 +1,99 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@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 _SSS_PAM_DATA_H_
+#define _SSS_PAM_DATA_H_
+
+#include "config.h"
+#include <stdbool.h>
+#include <stdint.h>
+#ifdef USE_KEYRING
+#include <sys/types.h>
+#include <keyutils.h>
+#endif
+
+#include "util/util_errors.h"
+#include "util/debug.h"
+#include "util/authtok.h"
+
+#define DEBUG_PAM_DATA(level, pd) do { \
+ pam_print_data(level, pd); \
+} while(0)
+
+struct response_data {
+ int32_t type;
+ int32_t len;
+ uint8_t *data;
+ bool do_not_send_to_client;
+ struct response_data *next;
+};
+
+struct pam_data {
+ int cmd;
+ char *domain;
+ char *user;
+ char *service;
+ char *tty;
+ char *ruser;
+ char *rhost;
+ char **requested_domains;
+ struct sss_auth_token *authtok;
+ struct sss_auth_token *newauthtok;
+ uint32_t cli_pid;
+ uint32_t child_pid;
+ char *logon_name;
+ uint32_t cli_flags;
+
+ int pam_status;
+ int response_delay;
+ struct response_data *resp_list;
+
+ bool offline_auth;
+ bool last_auth_saved;
+ int priv;
+ int account_locked;
+
+ uint32_t client_id_num;
+#ifdef USE_KEYRING
+ key_serial_t key_serial;
+#endif
+ bool passkey_local_done;
+};
+
+/**
+ * @brief Create new zero initialized struct pam_data.
+ *
+ * @param mem_ctx A memory context use to allocate the internal data
+ * @return A pointer to new struct pam_data
+ * NULL on error
+ *
+ * NOTE: This function should be the only way, how to create new empty
+ * struct pam_data, because this function automatically initialize sub
+ * structures and set destructor to created object.
+ */
+struct pam_data *create_pam_data(TALLOC_CTX *mem_ctx);
+errno_t copy_pam_data(TALLOC_CTX *mem_ctx, struct pam_data *old_pd,
+ struct pam_data **new_pd);
+void pam_print_data(int l, struct pam_data *pd);
+int pam_add_response(struct pam_data *pd,
+ enum response_type type,
+ int len, const uint8_t *data);
+
+#endif /* _SSS_PAM_DATA_H_ */
diff --git a/src/util/sss_ptr_hash.c b/src/util/sss_ptr_hash.c
new file mode 100644
index 0000000..115b0b9
--- /dev/null
+++ b/src/util/sss_ptr_hash.c
@@ -0,0 +1,395 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@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 <talloc.h>
+#include <dhash.h>
+
+#include "util/util.h"
+#include "util/sss_ptr_hash.h"
+
+static bool sss_ptr_hash_check_type(void *ptr, const char *type)
+{
+ void *type_ptr;
+
+ type_ptr = talloc_check_name(ptr, type);
+ if (type_ptr == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Invalid data type detected. Expected [%s], got [%s].\n",
+ type, talloc_get_name(ptr));
+ return false;
+ }
+
+ return true;
+}
+
+static int sss_ptr_hash_table_destructor(hash_table_t *table)
+{
+ sss_ptr_hash_delete_all(table, false);
+ return 0;
+}
+
+struct sss_ptr_hash_delete_data {
+ hash_delete_callback *callback;
+ void *pvt;
+};
+
+struct sss_ptr_hash_value {
+ hash_table_t *table;
+ const char *key;
+ void *payload;
+ bool delete_in_progress;
+};
+
+static int
+sss_ptr_hash_value_destructor(struct sss_ptr_hash_value *value)
+{
+ hash_key_t table_key;
+
+ /* Do not call hash_delete() if we got here from hash delete callback when
+ * the callback calls talloc_free(payload) which frees the value. This
+ * should not happen since talloc will avoid circular free but let's be
+ * over protective here. */
+ if (value->delete_in_progress) {
+ return 0;
+ }
+
+ value->delete_in_progress = true;
+ if (value->table && value->key) {
+ table_key.type = HASH_KEY_STRING;
+ table_key.str = discard_const_p(char, value->key);
+ if (hash_delete(value->table, &table_key) != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "failed to delete entry with key '%s'\n", value->key);
+ value->delete_in_progress = false;
+ }
+ }
+
+ return 0;
+}
+
+static struct sss_ptr_hash_value *
+sss_ptr_hash_value_create(hash_table_t *table,
+ const char *key,
+ void *talloc_ptr)
+{
+ struct sss_ptr_hash_value *value;
+
+ value = talloc_zero(talloc_ptr, struct sss_ptr_hash_value);
+ if (value == NULL) {
+ return NULL;
+ }
+
+ value->key = talloc_strdup(value, key);
+ if (value->key == NULL) {
+ talloc_free(value);
+ return NULL;
+ }
+
+ value->table = table;
+ value->payload = talloc_ptr;
+ talloc_set_destructor(value, sss_ptr_hash_value_destructor);
+
+ return value;
+}
+
+static void
+sss_ptr_hash_delete_cb(hash_entry_t *item,
+ hash_destroy_enum deltype,
+ void *pvt)
+{
+ struct sss_ptr_hash_delete_data *data;
+ struct sss_ptr_hash_value *value;
+ struct hash_entry_t callback_entry;
+
+ if (pvt == NULL) {
+ return;
+ }
+
+ value = talloc_get_type(item->value.ptr, struct sss_ptr_hash_value);
+ if (value == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid value!\n");
+ return;
+ }
+
+ /* Switch to the input value and call custom callback. */
+ data = talloc_get_type(pvt, struct sss_ptr_hash_delete_data);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid data!\n");
+ return;
+ }
+
+ callback_entry.key = item->key;
+ callback_entry.value.type = HASH_VALUE_PTR;
+ callback_entry.value.ptr = value->payload;
+
+ /* Delete the value in case this callback has been called directly
+ * from dhash (overwriting existing entry) instead of hash_delete()
+ * in value's destructor. */
+ if (!value->delete_in_progress) {
+ talloc_set_destructor(value, NULL);
+ talloc_free(value);
+ }
+
+ /* Even if execution is already in the context of
+ * talloc_free(payload) -> talloc_free(value) -> ...
+ * there still might be legitimate reasons to execute callback.
+ */
+ data->callback(&callback_entry, deltype, data->pvt);
+}
+
+hash_table_t *sss_ptr_hash_create(TALLOC_CTX *mem_ctx,
+ hash_delete_callback *del_cb,
+ void *del_cb_pvt)
+{
+ struct sss_ptr_hash_delete_data *data = NULL;
+ hash_table_t *table;
+ errno_t ret;
+
+ if (del_cb != NULL) {
+ data = talloc_zero(NULL, struct sss_ptr_hash_delete_data);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ data->callback = del_cb;
+ data->pvt = del_cb_pvt;
+ }
+
+ ret = sss_hash_create_ex(mem_ctx, 0, &table, 0, 0, 0, 0,
+ sss_ptr_hash_delete_cb, data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n",
+ ret, sss_strerror(ret));
+ talloc_free(data);
+ return NULL;
+ }
+
+ if (data != NULL) {
+ talloc_steal(table, data);
+ }
+
+ talloc_set_destructor(table, sss_ptr_hash_table_destructor);
+
+ return table;
+}
+
+errno_t _sss_ptr_hash_add(hash_table_t *table,
+ const char *key,
+ void *talloc_ptr,
+ const char *type,
+ bool override)
+{
+ struct sss_ptr_hash_value *value;
+ hash_value_t table_value;
+ hash_key_t table_key;
+ int hret;
+
+ if (table == NULL || key == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid input!\n");
+ return EINVAL;
+ }
+
+ if (!sss_ptr_hash_check_type(talloc_ptr, type)) {
+ return ERR_INVALID_DATA_TYPE;
+ }
+
+ table_key.type = HASH_KEY_STRING;
+ table_key.str = discard_const_p(char, key);
+
+ if (override == false && hash_has_key(table, &table_key)) {
+ return EEXIST;
+ }
+
+ value = sss_ptr_hash_value_create(table, key, talloc_ptr);
+ if (value == NULL) {
+ return ENOMEM;
+ }
+
+ table_value.type = HASH_VALUE_PTR;
+ table_value.ptr = value;
+
+ hret = hash_enter(table, &table_key, &table_value);
+ if (hret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add key %s!\n", key);
+ talloc_free(value);
+ return EIO;
+ }
+
+ return EOK;
+}
+
+static struct sss_ptr_hash_value *
+sss_ptr_hash_lookup_internal(hash_table_t *table,
+ const char *key)
+{
+ hash_value_t table_value;
+ hash_key_t table_key;
+ int hret;
+
+ table_key.type = HASH_KEY_STRING;
+ table_key.str = discard_const_p(char, key);
+
+ hret = hash_lookup(table, &table_key, &table_value);
+ if (hret == HASH_ERROR_KEY_NOT_FOUND) {
+ return NULL;
+ } else if (hret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to search hash table [%d]\n", hret);
+ return NULL;
+ }
+
+ /* Check value type. */
+ if (table_value.type != HASH_VALUE_PTR) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid value type found: %d\n",
+ table_value.type);
+ return NULL;
+ }
+
+ if (!sss_ptr_hash_check_type(table_value.ptr, "struct sss_ptr_hash_value")) {
+ return NULL;
+ }
+
+ return table_value.ptr;
+}
+
+void *_sss_ptr_hash_lookup(hash_table_t *table,
+ const char *key,
+ const char *type)
+{
+ struct sss_ptr_hash_value *value;
+
+ value = sss_ptr_hash_lookup_internal(table, key);
+ if (value == NULL || value->payload == NULL) {
+ return NULL;
+ }
+
+ if (!sss_ptr_hash_check_type(value->payload, type)) {
+ return NULL;
+ }
+
+ return value->payload;
+}
+
+void *_sss_ptr_get_value(hash_value_t *table_value,
+ const char *type)
+{
+ struct sss_ptr_hash_value *value;
+
+ /* Check value type. */
+ if (table_value->type != HASH_VALUE_PTR) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid value type found: %d\n",
+ table_value->type);
+ return NULL;
+ }
+
+ if (!sss_ptr_hash_check_type(table_value->ptr, "struct sss_ptr_hash_value")) {
+ return NULL;
+ }
+
+ value = table_value->ptr;
+
+ if (!sss_ptr_hash_check_type(value->payload, type)) {
+ return NULL;
+ }
+
+ return value->payload;
+}
+
+void sss_ptr_hash_delete(hash_table_t *table,
+ const char *key,
+ bool free_value)
+{
+ struct sss_ptr_hash_value *value;
+ void *payload = NULL;
+
+ if (table == NULL || key == NULL) {
+ return;
+ }
+
+ value = sss_ptr_hash_lookup_internal(table, key);
+ if (value == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to remove key '%s' from table\n", key);
+ return;
+ }
+
+ if (free_value) {
+ payload = value->payload;
+ }
+
+ talloc_free(value); /* this will call hash_delete() in value d-tor */
+
+ talloc_free(payload); /* it is safe to call talloc_free(NULL) */
+
+ return;
+}
+
+void sss_ptr_hash_delete_all(hash_table_t *table,
+ bool free_values)
+{
+ hash_value_t *content;
+ struct sss_ptr_hash_value *value;
+ void *payload = NULL;
+ unsigned long count;
+ unsigned long i;
+ int hret;
+
+ if (table == NULL) {
+ return;
+ }
+
+ hret = hash_values(table, &count, &content);
+ if (hret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get values [%d]\n", hret);
+ return;
+ }
+
+ for (i = 0; i < count; ++i) {
+ if ((content[i].type == HASH_VALUE_PTR) &&
+ sss_ptr_hash_check_type(content[i].ptr,
+ "struct sss_ptr_hash_value")) {
+ value = content[i].ptr;
+ if (free_values) {
+ payload = value->payload;
+ }
+ talloc_free(value);
+ if (free_values) {
+ talloc_free(payload); /* it's safe to call talloc_free(NULL) */
+ }
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unexpected type of table content, skipping");
+ }
+ }
+
+ talloc_free(content);
+
+ return;
+}
+
+bool sss_ptr_hash_has_key(hash_table_t *table,
+ const char *key)
+{
+ hash_key_t table_key;
+
+ table_key.type = HASH_KEY_STRING;
+ table_key.str = discard_const_p(char, key);
+
+ return hash_has_key(table, &table_key);
+}
diff --git a/src/util/sss_ptr_hash.h b/src/util/sss_ptr_hash.h
new file mode 100644
index 0000000..0889b17
--- /dev/null
+++ b/src/util/sss_ptr_hash.h
@@ -0,0 +1,145 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@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 _SSS_PTR_HASH_H_
+#define _SSS_PTR_HASH_H_
+
+#include <talloc.h>
+#include <dhash.h>
+
+#include "util/util.h"
+
+/**
+ * Create a new hash table with string key and talloc pointer value with
+ * possible custom delete callback @del_cb.
+ * Table will have destructor setup to wipe content.
+ * Never call hash_destroy(table) and hash_delete() explicitly but rather
+ * use talloc_free(table) and sss_ptr_hash_delete().
+ *
+ * A notes about @del_cb:
+ * - this callback must never modify hash table (i.e. add/del entries);
+ * - this callback is triggered when value is either explicitly removed
+ * from the table or simply freed (latter leads to removal of an entry
+ * from the table);
+ * - this callback is also triggered for every entry when table is freed
+ * entirely. In this case (deltype == HASH_TABLE_DESTROY) any table
+ * lookups / iteration are forbidden as table might be already invalidated.
+ */
+hash_table_t *sss_ptr_hash_create(TALLOC_CTX *mem_ctx,
+ hash_delete_callback *del_cb,
+ void *del_cb_pvt);
+
+/**
+ * Add a new value @talloc_ptr of type @type into the table.
+ *
+ * If the @key already exist in the table and @override is true,
+ * the value is overridden. Otherwise EEXIST error is returned.
+ *
+ * If talloc_ptr is freed the key and value are automatically
+ * removed from the hash table (del_cb that was set up during
+ * table creation is executed as a first step of this removal).
+ *
+ * @return EOK If the <@key, @talloc_ptr> pair was inserted.
+ * @return EEXIST If @key already exists and @override is false.
+ * @return Other errno code in case of an error.
+ */
+errno_t _sss_ptr_hash_add(hash_table_t *table,
+ const char *key,
+ void *talloc_ptr,
+ const char *type,
+ bool override);
+
+/**
+ * Add a new value @talloc_ptr of type @type into the table.
+ *
+ * If talloc_ptr is freed the key and value are automatically
+ * removed from the hash table.
+ *
+ * @return EOK If the <@key, @talloc_ptr> pair was inserted.
+ * @return EEXIST If @key already exists.
+ * @return Other errno code in case of an error.
+ */
+#define sss_ptr_hash_add(table, key, talloc_ptr, type) \
+ _sss_ptr_hash_add(table, key, talloc_ptr, #type, false)
+
+/**
+ * Add a new value @talloc_ptr of type @type into the table.
+ *
+ * If the @key already exists in the table, its value is
+ * overridden. If talloc_ptr is freed the key and value
+ * are automatically removed from the hash table.
+ *
+ * @return EOK If the <@key, @talloc_ptr> pair was inserted.
+ * @return Other errno code in case of an error.
+ */
+#define sss_ptr_hash_add_or_override(table, key, talloc_ptr, type) \
+ _sss_ptr_hash_add(table, key, talloc_ptr, #type, true)
+
+void *_sss_ptr_hash_lookup(hash_table_t *table,
+ const char *key,
+ const char *type);
+
+/**
+ * Lookup @key in the table and return its value as typed to @type.
+ * The type of the value must match with @type, otherwise NULL is returned.
+ *
+ * @return talloc_ptr If the value is found as type matches.
+ * @return NULL If the value is not found or if the type is invalid.
+ */
+#define sss_ptr_hash_lookup(table, key, type) \
+ (type *)_sss_ptr_hash_lookup(table, key, #type)
+
+void *_sss_ptr_get_value(hash_value_t *table_value,
+ const char *type);
+
+/**
+ * Obtain inserted talloc pointer from table value typed to @type.
+ * The type of the value must match with @type, otherwise NULL is returned.
+ *
+ * @return talloc_ptr If the value is found as type matches.
+ * @return NULL If the value is not found or if the type is invalid.
+ */
+#define sss_ptr_get_value(table_value, type) \
+ (type *)_sss_ptr_get_value(table_value, #type)
+
+/**
+ * Delete @key from table. If @free_value is true then also the value
+ * associated with @key is freed, otherwise it is left intact.
+ */
+void sss_ptr_hash_delete(hash_table_t *table,
+ const char *key,
+ bool free_value);
+
+/**
+ * Delete all keys from the table. If @free_value sis true then also
+ * the values associated with those keys are reed, otherwise
+ * they are left intact.
+ */
+void sss_ptr_hash_delete_all(hash_table_t *table,
+ bool free_values);
+
+/**
+ * @return true If @key is present in the table.
+ * @return false Otherwise.
+ */
+bool sss_ptr_hash_has_key(hash_table_t *table,
+ const char *key);
+
+#endif /* _SSS_PTR_HASH_H_ */
diff --git a/src/util/sss_ptr_list.c b/src/util/sss_ptr_list.c
new file mode 100644
index 0000000..5be9b50
--- /dev/null
+++ b/src/util/sss_ptr_list.c
@@ -0,0 +1,173 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2018 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 <talloc.h>
+
+#include "util/util.h"
+#include "util/dlinklist.h"
+#include "util/sss_ptr_list.h"
+
+struct sss_ptr_list {
+ struct sss_ptr_list_item *head;
+ bool in_destructor;
+ bool free_data;
+};
+
+struct sss_ptr_list_spy {
+ struct sss_ptr_list_item *item;
+};
+
+static int
+sss_ptr_list_spy_destructor(struct sss_ptr_list_spy *spy)
+{
+ spy->item->ptr = NULL;
+ spy->item->spy = NULL;
+ talloc_free(spy->item);
+ return 0;
+}
+
+static int
+sss_ptr_list_item_destructor(struct sss_ptr_list_item *item)
+{
+ if (item->spy != NULL) {
+ talloc_set_destructor(item->spy, NULL);
+ talloc_free(item->spy);
+ }
+
+ if (item->list == NULL) {
+ return 0;
+ }
+
+ if (item->list->free_data && item->ptr != NULL) {
+ talloc_free(item->ptr);
+ }
+
+ if (item->list->in_destructor) {
+ return 0;
+ }
+
+ DLIST_REMOVE(item->list->head, item);
+ return 0;
+}
+
+static int
+sss_ptr_list_destructor(struct sss_ptr_list *list)
+{
+ list->in_destructor = true;
+
+ return 0;
+}
+
+static struct sss_ptr_list_spy *
+sss_ptr_list_spy_create(struct sss_ptr_list_item *item, void *ptr)
+{
+ struct sss_ptr_list_spy *spy;
+
+ spy = talloc_zero(ptr, struct sss_ptr_list_spy);
+ if (spy == NULL) {
+ return NULL;
+ }
+
+ spy->item = item;
+
+ talloc_set_destructor(spy, sss_ptr_list_spy_destructor);
+
+ return spy;
+}
+
+struct sss_ptr_list *
+sss_ptr_list_create(TALLOC_CTX *mem_ctx, bool free_data_on_removal)
+{
+ struct sss_ptr_list *list;
+
+ list = talloc_zero(mem_ctx, struct sss_ptr_list);
+ if (list == NULL) {
+ return NULL;
+ }
+
+ list->free_data = free_data_on_removal;
+
+ talloc_set_destructor(list, sss_ptr_list_destructor);
+ return list;
+}
+
+errno_t
+sss_ptr_list_add(struct sss_ptr_list *list, void *ptr)
+{
+ struct sss_ptr_list_item *item;
+
+ item = talloc_zero(list, struct sss_ptr_list_item);
+ if (item == NULL) {
+ return ENOMEM;
+ }
+
+ item->ptr = ptr;
+ item->list = list;
+ item->spy = sss_ptr_list_spy_create(item, ptr);
+ if (item->spy == NULL) {
+ talloc_free(item);
+ return ENOMEM;
+ }
+
+ DLIST_ADD(list->head, item);
+
+ talloc_set_destructor(item, sss_ptr_list_item_destructor);
+
+ return EOK;
+}
+
+void
+sss_ptr_list_remove(struct sss_ptr_list *list, void *ptr)
+{
+ struct sss_ptr_list_item *item;
+
+ item = sss_ptr_list_find(list, ptr);
+ if (item == NULL) {
+ return;
+ }
+
+ talloc_free(item);
+}
+
+struct sss_ptr_list_item *
+sss_ptr_list_find(struct sss_ptr_list *list, void *ptr)
+{
+ struct sss_ptr_list_item *item;
+
+ DLIST_FOR_EACH(item, list->head) {
+ if (item->ptr == ptr) {
+ return item;
+ }
+ }
+
+ return NULL;
+}
+
+struct sss_ptr_list_item *
+sss_ptr_list_head(struct sss_ptr_list *list)
+{
+ return list->head;
+}
+
+bool
+sss_ptr_list_is_empty(struct sss_ptr_list *list)
+{
+ return list == NULL || list->head == NULL;
+}
diff --git a/src/util/sss_ptr_list.h b/src/util/sss_ptr_list.h
new file mode 100644
index 0000000..dc40ea3
--- /dev/null
+++ b/src/util/sss_ptr_list.h
@@ -0,0 +1,120 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2018 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_PTR_LIST_H_
+#define _SSS_PTR_LIST_H_
+
+#include <talloc.h>
+
+#include "util/util.h"
+#include "util/dlinklist.h"
+
+struct sss_ptr_list;
+struct sss_ptr_list_spy;
+
+struct sss_ptr_list_item {
+ void *ptr;
+
+ struct sss_ptr_list *list;
+ struct sss_ptr_list_spy *spy;
+ struct sss_ptr_list_item *prev;
+ struct sss_ptr_list_item *next;
+};
+
+/**
+ * Create new linked list.
+ *
+ * @param mem_ctx Memory context.
+ * @param free_data_on_removal If true than the stored pointer is freed when
+ * it is being removed from the list or when
+ * the list is freed.
+ * @return New list or NULL on failure.
+ */
+struct sss_ptr_list *
+sss_ptr_list_create(TALLOC_CTX *mem_ctx, bool free_data_on_removal);
+
+/**
+ * Obtain head of the list that can be used with DLIST_* macros.
+ *
+ * @return Head of the list or NULL if it is empty.
+ */
+struct sss_ptr_list_item *
+sss_ptr_list_head(struct sss_ptr_list *list);
+
+/**
+ * @return True if the list is empty, false otherwise.
+ */
+bool
+sss_ptr_list_is_empty(struct sss_ptr_list *list);
+
+/**
+ * Add new item (must be a talloc context) to the list.
+ *
+ * The list item will be automatically removed from the list if @ptr is freed.
+ * To remove the item from the list, call talloc_free(item).
+ *
+ * @param list Linked list.
+ * @param ptr Talloc pointer to add to the list.
+ *
+ * @return New EOK on success, other errno code on error.
+ */
+errno_t
+sss_ptr_list_add(struct sss_ptr_list *list, void *ptr);
+
+/**
+ * Remove stored pointer from the list.
+ *
+ * If @free_data_on_removal was true when creating the list, the pointer will
+ * be automatically freed.
+ *
+ * @param list Linked list.
+ * @param ptr Talloc pointer to remove from the list.
+ */
+void
+sss_ptr_list_remove(struct sss_ptr_list *list, void *ptr);
+
+/**
+ * Find pointer in the list and return list item containing it.
+ *
+ * @param list Linked list.
+ * @param ptr Talloc pointer to search for.
+ *
+ * @return List item if found, NULL otherwise..
+ */
+struct sss_ptr_list_item *
+sss_ptr_list_find(struct sss_ptr_list *list, void *ptr);
+
+/**
+ * Return value stored inside linked list item.
+ *
+ * @param item Linked list item.
+ * @param type Type to look for.
+ *
+ * @return The value.
+ */
+#define sss_ptr_list_value(item, type) \
+ talloc_get_type(item->ptr, type)
+
+#define SSS_PTR_LIST_FOR_EACH(list, value, type) \
+ for (struct sss_ptr_list_item *__item = sss_ptr_list_head(list); \
+ __item != NULL && (((value) = sss_ptr_list_value(__item, type)), 1); \
+ __item = __item->next)
+
+#endif /* _SSS_PTR_LIST_H_ */
diff --git a/src/util/sss_python.c b/src/util/sss_python.c
new file mode 100644
index 0000000..2b01c6e
--- /dev/null
+++ b/src/util/sss_python.c
@@ -0,0 +1,60 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@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 "src/util/sss_python.h"
+
+PyObject *
+sss_exception_with_doc(const char *name, const char *doc, PyObject *base,
+ PyObject *dict)
+{
+#if PY_VERSION_HEX >= 0x03080000
+ return PyErr_NewExceptionWithDoc(name, doc, base, dict);
+#elif PY_VERSION_HEX >= 0x02070000
+ return PyErr_NewExceptionWithDoc(discard_const_p(char, name),
+ discard_const_p(char, doc), base, dict);
+#else
+ int result;
+ PyObject *ret = NULL;
+ PyObject *mydict = NULL; /* points to the dict only if we create it */
+ PyObject *docobj;
+
+ if (dict == NULL) {
+ dict = mydict = PyDict_New();
+ if (dict == NULL) {
+ return NULL;
+ }
+ }
+
+ if (doc != NULL) {
+ docobj = PyString_FromString(doc);
+ if (docobj == NULL)
+ goto failure;
+ result = PyDict_SetItemString(dict, "__doc__", docobj);
+ Py_DECREF(docobj);
+ if (result < 0)
+ goto failure;
+ }
+
+ ret = PyErr_NewException(discard_const_p(char, name), base, dict);
+ failure:
+ Py_XDECREF(mydict);
+ return ret;
+#endif
+}
diff --git a/src/util/sss_python.h b/src/util/sss_python.h
new file mode 100644
index 0000000..ea38422
--- /dev/null
+++ b/src/util/sss_python.h
@@ -0,0 +1,68 @@
+#ifndef __SSS_PYTHON_H__
+#define __SSS_PYTHON_H__
+
+#include "config.h"
+
+#include <Python.h>
+#include <stdbool.h>
+
+#include "util/util.h"
+
+#if PY_VERSION_HEX < 0x02050000
+#define sss_py_const_p(type, value) discard_const_p(type, (value))
+#else
+#define sss_py_const_p(type, value) (value)
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+#define IS_PY3K
+
+#define MODINITERROR(module) do { \
+ Py_XDECREF(module); \
+ return NULL; \
+} while(0)
+
+#define PYNUMBER_CHECK(what) PyLong_Check(what)
+#define PYNUMBER_FROMLONG(what) PyLong_FromLong(what)
+#define PYNUMBER_ASLONG(what) PyLong_AsLong(what)
+#else /* PY_MAJOR_VERSION < 3 */
+#include <bytesobject.h>
+
+#define MODINITERROR(module) do { \
+ Py_XDECREF(module); \
+ return; \
+} while(0)
+
+#define PYNUMBER_CHECK(what) PyInt_Check(what)
+#define PYNUMBER_FROMLONG(what) PyInt_FromLong(what)
+#define PYNUMBER_ASLONG(what) PyInt_AsLong(what)
+#endif /* PY_MAJOR_VERSION < 3 */
+
+/* Exceptions compatibility */
+PyObject *
+sss_exception_with_doc(const char *name, const char *doc, PyObject *base,
+ PyObject *dict);
+
+/* Convenience macros */
+#define TYPE_READY(module, type, name) do { \
+ if (PyType_Ready(&type) < 0) { \
+ MODINITERROR(module); \
+ } \
+ Py_INCREF(&type); \
+ if (PyModule_AddObject(module, \
+ discard_const_p(char, name), \
+ (PyObject *) &type) == -1) { \
+ Py_XDECREF(&type); \
+ MODINITERROR(module); \
+ } \
+} while(0) \
+
+#define SAFE_SET(old, new) do { \
+ PyObject *__simple_set_tmp = NULL; \
+ __simple_set_tmp = old; \
+ Py_INCREF(new); \
+ old = new; \
+ Py_XDECREF(__simple_set_tmp); \
+} while(0)
+
+#endif /* __SSS_PYTHON_H__ */
diff --git a/src/util/sss_regexp.c b/src/util/sss_regexp.c
new file mode 100644
index 0000000..3fc1a7b
--- /dev/null
+++ b/src/util/sss_regexp.c
@@ -0,0 +1,190 @@
+/*
+ SSSD
+
+ Authors:
+ Tomas Halman <thalman@redhat.com>
+
+ Copyright (C) 2019 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 "util/sss_regexp.h"
+#include <string.h>
+#include "util/util_errors.h"
+#include "util/debug.h"
+
+#define SSS_REGEXP_OVEC_SIZE 30
+#define SSS_REGEXP_ERR_MSG_SIZE 120 /* 120 is recomended by pcre2 doc */
+
+#ifndef EOK
+#define EOK 0
+#endif
+
+/*
+ * sss_regexp with pcre2
+ */
+struct _sss_regexp_t {
+ pcre2_code *re;
+ pcre2_match_data *match_data;
+ char *matched_string;
+};
+
+static int sss_regexp_pcre2_destroy(sss_regexp_t *self)
+{
+ if (self->re) {
+ pcre2_code_free(self->re);
+ }
+ if (self->match_data) {
+ pcre2_match_data_free(self->match_data);
+ }
+ if (self->matched_string) {
+ pcre2_substring_free((PCRE2_UCHAR *)self->matched_string);
+ }
+ return 0;
+}
+
+static int sss_regexp_pcre2_compile(sss_regexp_t *self,
+ const char *pattern,
+ int options)
+{
+ int errorcode;
+ unsigned char errormsg[SSS_REGEXP_ERR_MSG_SIZE];
+ size_t erroroffset;
+
+ self->re = pcre2_compile((PCRE2_SPTR)pattern,
+ strlen(pattern),
+ options,
+ &errorcode,
+ &erroroffset,
+ NULL);
+ if (self->re == NULL) {
+ pcre2_get_error_message(errorcode,
+ errormsg,
+ SSS_REGEXP_ERR_MSG_SIZE);
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid Regular Expression pattern "
+ "at position %zu. (Error: %d [%s])\n", erroroffset, errorcode, errormsg);
+ return errorcode;
+ }
+ return EOK;
+}
+
+static int sss_regexp_pcre2_match(sss_regexp_t *self,
+ const char *subject,
+ int startoffset,
+ int options)
+{
+ if (!self->re) {
+ return SSS_REGEXP_ERROR_NOMATCH;
+ }
+ if (self->match_data) {
+ pcre2_match_data_free(self->match_data);
+ }
+ self->match_data = pcre2_match_data_create_from_pattern(self->re, NULL);
+ if (!self->match_data) {
+ return SSS_REGEXP_ERROR_NOMEMORY;
+ }
+ return pcre2_match(self->re,
+ (PCRE2_SPTR)subject,
+ strlen(subject),
+ startoffset,
+ options,
+ self->match_data,
+ NULL);
+}
+
+static int sss_regexp_pcre2_get_named_substring(sss_regexp_t *self,
+ const char *name,
+ const char **value)
+{
+ PCRE2_SIZE length;
+ int rc;
+
+ if (self->matched_string) {
+ pcre2_substring_free((PCRE2_UCHAR *)(self->matched_string));
+ self->matched_string = NULL;
+ }
+ rc = pcre2_substring_get_byname(self->match_data,
+ (PCRE2_SPTR)name,
+ (PCRE2_UCHAR **) &self->matched_string,
+ &length);
+ *value = self->matched_string;
+ return rc;
+}
+
+/*
+ * sss_regexp talloc destructor
+ */
+static int sss_regexp_destroy(sss_regexp_t *self)
+{
+ if (!self) return 0;
+ return sss_regexp_pcre2_destroy(self);
+}
+
+/*
+ * sss_regexp constructor
+ */
+int sss_regexp_new(TALLOC_CTX *mem_ctx,
+ const char *pattern,
+ int options,
+ sss_regexp_t **self_p)
+{
+ int ret;
+ sss_regexp_t *self = talloc_zero(mem_ctx, sss_regexp_t);
+ if (!self) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Not enough memory for sss_regexp_t.\n");
+ *self_p = NULL;
+ return SSS_REGEXP_ERROR_NOMEMORY;
+ }
+ talloc_set_destructor(self, sss_regexp_destroy);
+
+ ret = sss_regexp_pcre2_compile(self,
+ pattern,
+ options);
+ if (ret != EOK) {
+ talloc_free(self);
+ self = NULL;
+ }
+ *self_p = self;
+ return ret;
+}
+
+/*
+ * sss_regexp match function
+ */
+int sss_regexp_match(sss_regexp_t *self,
+ const char *subject,
+ int startoffset,
+ int options)
+{
+ if (!self || !self->re || !subject) return SSS_REGEXP_ERROR_NOMATCH;
+
+ return sss_regexp_pcre2_match(self, subject, startoffset, options);
+}
+
+
+/*
+ * sss_regexp get named substring
+ */
+int sss_regexp_get_named_substring(sss_regexp_t *self,
+ const char *name,
+ const char **value)
+{
+ if (!self || !self->re || !name) {
+ *value = NULL;
+ return SSS_REGEXP_ERROR_NOMATCH;
+ }
+
+ return sss_regexp_pcre2_get_named_substring(self, name, value);
+}
diff --git a/src/util/sss_regexp.h b/src/util/sss_regexp.h
new file mode 100644
index 0000000..2b29361
--- /dev/null
+++ b/src/util/sss_regexp.h
@@ -0,0 +1,83 @@
+/*
+ SSSD
+
+ Authors:
+ Tomas Halman <thalman@redhat.com>
+
+ Copyright (C) 2018 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_REGEXP_H_
+#define SSS_REGEXP_H_
+
+#include <stddef.h>
+#include <talloc.h>
+#include "config.h"
+
+/* regexp class */
+typedef struct _sss_regexp_t sss_regexp_t;
+
+#include <pcre2.h>
+#define SSS_REGEXP_ERROR_NOMATCH PCRE2_ERROR_NOMATCH
+#define SSS_REGEXP_ERROR_NOMEMORY PCRE2_ERROR_NOMEMORY
+#define SSS_REGEXP_NOTEMPTY PCRE2_NOTEMPTY
+#define SSS_REGEXP_EXTENDED PCRE2_EXTENDED
+#define SSS_REGEXP_DUPNAMES PCRE2_DUPNAMES
+
+/* how to use sss_regexp:
+ *
+ * int err;
+ * const char *found;
+ *
+ * sss_regexp_t *re
+ * err = sss_regexp_new (NULL, "#(?P<myname>.+)#", 0, &re);
+ * if (err != EOK) {
+ * goto fail;
+ * }
+ * int rc = sss_regexp_match (re,
+ * "a#findthis#b",
+ * 0,
+ * 0);
+ * if (rc != 0) { ... }
+ * rc = sss_regexp_get_named_substring (re, "myname", &found);
+ * ...
+ * talloc_free (re);
+ */
+
+/*
+ * Create new compiled regexp object.
+ */
+int sss_regexp_new(TALLOC_CTX *mem_ctx,
+ const char *pattern,
+ int options,
+ sss_regexp_t **self_p);
+
+/*
+ * Search subject with previously created regexp.
+ */
+int sss_regexp_match(sss_regexp_t *self,
+ const char *subject,
+ int startoffset,
+ int options);
+
+/*
+ * Get named substring from last sss_regexp_match.
+ */
+int sss_regexp_get_named_substring(sss_regexp_t *self,
+ const char *name,
+ const char **value);
+
+#endif /* SSS_REGEXP_H_ */
diff --git a/src/util/sss_selinux.c b/src/util/sss_selinux.c
new file mode 100644
index 0000000..165aeca
--- /dev/null
+++ b/src/util/sss_selinux.c
@@ -0,0 +1,255 @@
+/*
+ SSSD
+
+ SELinux-related utility functions
+
+ Authors:
+ Jan Zeleny <jzeleny@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 "util/sss_selinux.h"
+#include "util/sss_utf8.h"
+#include "db/sysdb_selinux.h"
+
+static bool match_entity(struct ldb_message_element *values,
+ struct ldb_message_element *sought_values)
+{
+ int i, j;
+
+ for (i = 0; i < values->num_values; i++) {
+ for (j = 0; j < sought_values->num_values; j++) {
+ if (values->values[i].length != sought_values->values[j].length) {
+ continue;
+ }
+
+ if (strncasecmp((char *)values->values[i].data,
+ (char *)sought_values->values[j].data,
+ values->values[i].length) == 0)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool sss_selinux_match(struct sysdb_attrs *usermap,
+ struct sysdb_attrs *user,
+ struct sysdb_attrs *host,
+ uint32_t *_priority)
+{
+ struct ldb_message_element *users_el = NULL;
+ struct ldb_message_element *usercat = NULL;
+ struct ldb_message_element *hosts_el = NULL;
+ struct ldb_message_element *hostcat = NULL;
+ struct ldb_message_element *dn;
+ struct ldb_message_element *memberof;
+ int i;
+ uint32_t priority = 0;
+ bool matched_name;
+ bool matched_group;
+ bool matched_category;
+ errno_t ret;
+
+ if (usermap == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "NULL given as usermap! Skipping ...\n");
+ return false;
+ }
+
+ /* Search for user and host related elements */
+ for (i = 0; i < usermap->num; i++) {
+ if (!strcasecmp(usermap->a[i].name, SYSDB_ORIG_MEMBER_USER)) {
+ users_el = &usermap->a[i];
+ } else if (!strcasecmp(usermap->a[i].name, SYSDB_ORIG_MEMBER_HOST)) {
+ hosts_el = &usermap->a[i];
+ } else if (!strcasecmp(usermap->a[i].name, SYSDB_USER_CATEGORY)) {
+ usercat = &usermap->a[i];
+ } else if (!strcasecmp(usermap->a[i].name, SYSDB_HOST_CATEGORY)) {
+ hostcat = &usermap->a[i];
+ }
+ }
+
+ if (user) {
+ ret = sysdb_attrs_get_el(user, SYSDB_ORIG_DN, &dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "User does not have origDN\n");
+ return false;
+ }
+ ret = sysdb_attrs_get_el(user, SYSDB_ORIG_MEMBEROF, &memberof);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "User does not have orig memberof, "
+ "therefore it can't match to any rule\n");
+ return false;
+ }
+
+ /**
+ * The rule won't match if user category != "all" and user map doesn't
+ * contain neither user nor any of his groups in memberUser attribute
+ */
+ matched_category = false;
+ if (usercat != NULL) {
+ for (i = 0; i < usercat->num_values; i++) {
+ if (strcasecmp((char *)usercat->values[i].data, "all") == 0) {
+ matched_category = true;
+ break;
+ }
+ }
+ }
+
+ if (!matched_category) {
+ if (users_el == NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "No users specified in the rule!\n");
+ return false;
+ } else {
+ matched_name = match_entity(users_el, dn);
+ matched_group = match_entity(users_el, memberof);
+ if (matched_name) {
+ priority |= SELINUX_PRIORITY_USER_NAME;
+ } else if (matched_group) {
+ priority |= SELINUX_PRIORITY_USER_GROUP;
+ } else {
+ DEBUG(SSSDBG_TRACE_ALL, "User did not match\n");
+ return false;
+ }
+ }
+ } else {
+ priority |= SELINUX_PRIORITY_USER_CAT;
+ }
+ }
+
+ if (host) {
+ ret = sysdb_attrs_get_el(host, SYSDB_ORIG_DN, &dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Host does not have origDN\n");
+ return false;
+ }
+ ret = sysdb_attrs_get_el(host, SYSDB_ORIG_MEMBEROF, &memberof);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Host does not have orig memberof, "
+ "therefore it can't match to any rule\n");
+ return false;
+ }
+
+ /**
+ * The rule won't match if host category != "all" and user map doesn't
+ * contain neither host nor any of its groups in memberHost attribute
+ */
+ matched_category = false;
+ if (hostcat != NULL) {
+ for (i = 0; i < hostcat->num_values; i++) {
+ if (strcasecmp((char *)hostcat->values[i].data, "all") == 0) {
+ matched_category = true;
+ break;
+ }
+ }
+ }
+ if (!matched_category) {
+ if (hosts_el == NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "No users specified in the rule!\n");
+ return false;
+ } else {
+ matched_name = match_entity(hosts_el, dn);
+ matched_group = match_entity(hosts_el, memberof);
+ if (matched_name) {
+ priority |= SELINUX_PRIORITY_HOST_NAME;
+ } else if (matched_group) {
+ priority |= SELINUX_PRIORITY_HOST_GROUP;
+ } else {
+ DEBUG(SSSDBG_TRACE_ALL, "Host did not match\n");
+ return false;
+ }
+ }
+ } else {
+ priority |= SELINUX_PRIORITY_HOST_CAT;
+ }
+ }
+
+ if (_priority != NULL) {
+ *_priority = priority;
+ }
+
+ return true;
+}
+
+errno_t sss_selinux_extract_user(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *username,
+ struct sysdb_attrs **_user_attrs)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char **attrs;
+ struct sysdb_attrs *user_attrs;
+ struct ldb_message *user_msg;
+
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ attrs = talloc_array(tmp_ctx, const char *, 3);
+ if (attrs == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ attrs[0] = SYSDB_ORIG_DN;
+ attrs[1] = SYSDB_ORIG_MEMBEROF;
+ attrs[2] = NULL;
+
+ ret = sysdb_search_user_by_name(tmp_ctx, domain, username, attrs,
+ &user_msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_name failed.\n");
+ goto done;
+ }
+
+ user_attrs = talloc_zero(tmp_ctx, struct sysdb_attrs);
+ if (user_attrs == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ user_attrs->a = talloc_steal(user_attrs, user_msg->elements);
+ user_attrs->num = user_msg->num_elements;
+
+ *_user_attrs = talloc_steal(mem_ctx, user_attrs);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+const char *sss_selinux_map_get_seuser(struct sysdb_attrs *usermap)
+{
+ int i;
+ const uint8_t *name;
+ const uint8_t *template = (const uint8_t *)SYSDB_SELINUX_USER;
+
+ for (i = 0; i < usermap->num; i++) {
+ name = (const uint8_t *)usermap->a[i].name;
+ if (sss_utf8_case_eq(name, template) == 0) {
+ return (const char *)usermap->a[i].values[0].data;
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/util/sss_selinux.h b/src/util/sss_selinux.h
new file mode 100644
index 0000000..8821e73
--- /dev/null
+++ b/src/util/sss_selinux.h
@@ -0,0 +1,54 @@
+/*
+ SSSD
+
+ SELinux-related utility functions
+
+ Authors:
+ Jan Zeleny <jzeleny@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_SELINUX_H_
+#define SSS_SELINUX_H_
+
+#include <talloc.h>
+#include <errno.h>
+
+#include <db/sysdb.h>
+
+#define SELINUX_PRIORITY_USER_CAT 1
+#define SELINUX_PRIORITY_USER_GROUP 2
+#define SELINUX_PRIORITY_USER_NAME 4
+/* According to specification, host has higher priority */
+#define SELINUX_PRIORITY_HOST_CAT 8
+#define SELINUX_PRIORITY_HOST_GROUP 16
+#define SELINUX_PRIORITY_HOST_NAME 32
+
+errno_t
+sss_selinux_extract_user(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *username,
+ struct sysdb_attrs **_user_attrs);
+
+bool sss_selinux_match(struct sysdb_attrs *usermap,
+ struct sysdb_attrs *user,
+ struct sysdb_attrs *host,
+ uint32_t *_priority);
+
+const char *sss_selinux_map_get_seuser(struct sysdb_attrs *usermap);
+
+#endif /* SSS_SELINUX_H_ */
diff --git a/src/util/sss_semanage.c b/src/util/sss_semanage.c
new file mode 100644
index 0000000..10a567c
--- /dev/null
+++ b/src/util/sss_semanage.c
@@ -0,0 +1,480 @@
+/*
+ SSSD
+
+ sss_semanage.c
+
+ Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2010
+
+ 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 <stdio.h>
+#if defined(HAVE_SEMANAGE) && defined(HAVE_SELINUX)
+#include <semanage/semanage.h>
+#include <selinux/selinux.h>
+#endif
+
+#include "util/util.h"
+
+#ifndef DEFAULT_SERANGE
+#define DEFAULT_SERANGE "s0"
+#endif
+
+#if defined(HAVE_SEMANAGE) && defined(HAVE_SELINUX)
+/* turn libselinux messages into SSSD DEBUG() calls */
+static void sss_semanage_error_callback(void *varg,
+ semanage_handle_t *handle,
+ const char *fmt, ...)
+{
+ int level = SSSDBG_INVALID;
+ va_list ap;
+
+ switch (semanage_msg_get_level(handle)) {
+ case SEMANAGE_MSG_ERR:
+ level = SSSDBG_CRIT_FAILURE;
+ break;
+ case SEMANAGE_MSG_WARN:
+ level = SSSDBG_MINOR_FAILURE;
+ break;
+ case SEMANAGE_MSG_INFO:
+ level = SSSDBG_TRACE_FUNC;
+ break;
+ }
+
+ va_start(ap, fmt);
+ sss_vdebug_fn(__FILE__, __LINE__, "libsemanage", level,
+ APPEND_LINE_FEED, fmt, ap);
+ va_end(ap);
+}
+
+static void sss_semanage_close(semanage_handle_t *handle)
+{
+ if (handle == NULL) {
+ return; /* semanage uses asserts */
+ }
+
+ if (semanage_is_connected(handle)) {
+ semanage_disconnect(handle);
+ }
+ semanage_handle_destroy(handle);
+}
+
+static int sss_is_selinux_managed(semanage_handle_t *handle)
+{
+ int ret;
+
+ if (handle == NULL) {
+ return EINVAL;
+ }
+
+ if (!is_selinux_enabled()) {
+ return ERR_SELINUX_NOT_MANAGED;
+ }
+
+ ret = semanage_is_managed(handle);
+ if (ret == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "SELinux policy not managed via libsemanage\n");
+ return ERR_SELINUX_NOT_MANAGED;
+ } else if (ret == -1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Call to semanage_is_managed failed\n");
+ return EIO;
+ }
+
+ return EOK;
+}
+
+static int sss_semanage_init(semanage_handle_t **_handle)
+{
+ int ret;
+ semanage_handle_t *handle = NULL;
+
+ handle = semanage_handle_create();
+ if (!handle) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux management handle\n");
+ ret = EIO;
+ goto done;
+ }
+
+ semanage_msg_set_callback(handle,
+ sss_semanage_error_callback,
+ NULL);
+
+ ret = sss_is_selinux_managed(handle);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = semanage_access_check(handle);
+ if (ret < SEMANAGE_CAN_READ) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot read SELinux policy store\n");
+ ret = EACCES;
+ goto done;
+ }
+
+ ret = semanage_connect(handle);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot estabilish SELinux management connection\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ sss_semanage_close(handle);
+ } else {
+ *_handle = handle;
+ }
+
+ return ret;
+}
+
+static int sss_semanage_user_add(semanage_handle_t *handle,
+ semanage_seuser_key_t *key,
+ const char *login_name,
+ const char *seuser_name,
+ const char *mls)
+{
+ int ret;
+ semanage_seuser_t *seuser = NULL;
+
+ ret = semanage_seuser_create(handle, &seuser);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot create SELinux login mapping for %s\n", login_name);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = semanage_seuser_set_name(handle, seuser, login_name);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not set name for %s\n", login_name);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = semanage_seuser_set_mlsrange(handle, seuser,
+ mls ? mls : DEFAULT_SERANGE);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not set serange for %s\n", login_name);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = semanage_seuser_set_sename(handle, seuser, seuser_name);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not set SELinux user for %s\n", login_name);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = semanage_seuser_modify_local(handle, key, seuser);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not add login mapping for %s\n", login_name);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ semanage_seuser_free(seuser);
+ return ret;
+}
+
+static int sss_semanage_user_mod(semanage_handle_t *handle,
+ semanage_seuser_key_t *key,
+ const char *login_name,
+ const char *seuser_name,
+ const char *mls)
+{
+ int ret;
+ semanage_seuser_t *seuser = NULL;
+
+ semanage_seuser_query(handle, key, &seuser);
+ if (seuser == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not query seuser for %s\n", login_name);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = semanage_seuser_set_mlsrange(handle, seuser,
+ mls ? mls : DEFAULT_SERANGE);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not set serange for %s\n", login_name);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = semanage_seuser_set_sename(handle, seuser, seuser_name);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not set sename for %s\n", login_name);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = semanage_seuser_modify_local(handle, key, seuser);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not modify login mapping for %s\n", login_name);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ semanage_seuser_free(seuser);
+ return ret;
+}
+
+int sss_seuser_exists(const char *linuxuser)
+{
+ int ret;
+ int exists;
+ semanage_seuser_key_t *sm_key = NULL;
+ semanage_handle_t *sm_handle = NULL;
+
+ ret = sss_semanage_init(&sm_handle);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = semanage_seuser_key_create(sm_handle, linuxuser, &sm_key);
+ if (ret < 0) {
+ sss_semanage_close(sm_handle);
+ return EIO;
+ }
+
+ ret = semanage_seuser_exists(sm_handle, sm_key, &exists);
+ semanage_seuser_key_free(sm_key);
+ sss_semanage_close(sm_handle);
+ if (ret < 0) {
+ return EIO;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "seuser exists: %s\n", exists ? "yes" : "no");
+
+ return exists ? EOK : ERR_SELINUX_USER_NOT_FOUND;
+}
+
+int sss_get_seuser(const char *linuxuser,
+ char **selinuxuser,
+ char **level)
+{
+ int ret;
+ semanage_handle_t *handle;
+
+ handle = semanage_handle_create();
+ if (handle == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux management handle\n");
+ return EIO;
+ }
+
+ semanage_msg_set_callback(handle,
+ sss_semanage_error_callback,
+ NULL);
+
+ /* We only needed the handle for this call. Close the handle right
+ * after it */
+ ret = sss_is_selinux_managed(handle);
+ sss_semanage_close(handle);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return getseuserbyname(linuxuser, selinuxuser, level);
+}
+
+int sss_set_seuser(const char *login_name, const char *seuser_name,
+ const char *mls)
+{
+ semanage_handle_t *handle = NULL;
+ semanage_seuser_key_t *key = NULL;
+ int ret;
+ int seuser_exists = 0;
+
+ if (seuser_name == NULL) {
+ /* don't care, just let system pick the defaults */
+ return EOK;
+ }
+
+ ret = sss_semanage_init(&handle);
+ if (ret == ERR_SELINUX_NOT_MANAGED) {
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux handle\n");
+ goto done;
+ }
+
+ ret = semanage_begin_transaction(handle);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot begin SELinux transaction\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = semanage_seuser_key_create(handle, login_name, &key);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux user key\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = semanage_seuser_exists(handle, key, &seuser_exists);
+ if (ret < 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot verify the SELinux user\n");
+ ret = EIO;
+ goto done;
+ }
+
+ if (seuser_exists) {
+ ret = sss_semanage_user_mod(handle, key, login_name, seuser_name,
+ mls);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot modify SELinux user mapping\n");
+ ret = EIO;
+ goto done;
+ }
+ } else {
+ ret = sss_semanage_user_add(handle, key, login_name, seuser_name,
+ mls);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add SELinux user mapping\n");
+ ret = EIO;
+ goto done;
+ }
+ }
+
+ ret = semanage_commit(handle);
+ if (ret < 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot commit SELinux transaction\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ if (key != NULL) {
+ semanage_seuser_key_free(key);
+ }
+ sss_semanage_close(handle);
+ return ret;
+}
+
+int sss_del_seuser(const char *login_name)
+{
+ semanage_handle_t *handle = NULL;
+ semanage_seuser_key_t *key = NULL;
+ int ret;
+ int exists = 0;
+
+ ret = sss_semanage_init(&handle);
+ if (ret == ERR_SELINUX_NOT_MANAGED) {
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux handle\n");
+ goto done;
+ }
+
+ ret = semanage_begin_transaction(handle);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot begin SELinux transaction\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = semanage_seuser_key_create(handle, login_name, &key);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux user key\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = semanage_seuser_exists(handle, key, &exists);
+ if (ret < 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot verify the SELinux user\n");
+ ret = EIO;
+ goto done;
+ }
+
+ if (!exists) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Login mapping for %s is not defined, OK if default mapping "
+ "was used\n", login_name);
+ ret = EOK; /* probably default mapping */
+ goto done;
+ }
+
+ ret = semanage_seuser_exists_local(handle, key, &exists);
+ if (ret < 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot verify the SELinux user\n");
+ ret = EIO;
+ goto done;
+ }
+
+ if (!exists) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Login mapping for %s is defined in policy, cannot be deleted\n",
+ login_name);
+ ret = ENOENT;
+ goto done;
+ }
+
+ ret = semanage_seuser_del_local(handle, key);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not delete login mapping for %s\n", login_name);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = semanage_commit(handle);
+ if (ret < 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot commit SELinux transaction\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ sss_semanage_close(handle);
+ return ret;
+}
+#else /* HAVE_SEMANAGE && HAVE_SELINUX */
+int sss_set_seuser(const char *login_name, const char *seuser_name,
+ const char *mls)
+{
+ return EOK;
+}
+
+int sss_del_seuser(const char *login_name)
+{
+ return EOK;
+}
+
+int sss_get_seuser(const char *linuxuser,
+ char **selinuxuser,
+ char **level)
+{
+ return EOK;
+}
+#endif /* HAVE_SEMANAGE */
diff --git a/src/util/sss_sockets.c b/src/util/sss_sockets.c
new file mode 100644
index 0000000..60312d5
--- /dev/null
+++ b/src/util/sss_sockets.c
@@ -0,0 +1,436 @@
+/*
+ SSSD
+
+ Socket utils
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2016
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2009
+
+ 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 <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include "util/util.h"
+
+
+static errno_t set_fcntl_flags(int fd, int fd_flags, int fl_flags)
+{
+ int ret;
+ int cur_flags;
+
+ ret = fcntl(fd, F_GETFD, 0);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "fcntl F_GETFD failed [%d][%s].\n", ret, strerror(ret));
+ return ret;
+ }
+ cur_flags = ret;
+
+ ret = fcntl(fd, F_SETFD, cur_flags | fd_flags);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "fcntl F_SETFD failed [%d][%s].\n", ret, strerror(ret));
+ return ret;
+ }
+
+ ret = fcntl(fd, F_GETFL, 0);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "fcntl F_GETFD failed [%d][%s].\n", ret, strerror(ret));
+ return ret;
+ }
+ cur_flags = ret;
+
+ ret = fcntl(fd, F_SETFL, cur_flags | fl_flags);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "fcntl F_SETFD failed [%d][%s].\n", ret, strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t set_fd_common_opts(int fd, int timeout)
+{
+ int dummy = 1;
+ int ret;
+ struct timeval tv;
+ unsigned int milli;
+ int domain;
+ int type;
+ socklen_t optlen = sizeof(int);
+
+ /* Get protocol domain. */
+ ret = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &optlen);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_FUNC_DATA, "Unable to get socket domain [%d]: %s.\n",
+ ret, strerror(ret));
+ /* Assume IPV6. */
+ domain = AF_INET6;
+ }
+
+ /* Get protocol type. */
+ ret = getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &optlen);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_FUNC_DATA, "Unable to get socket type [%d]: %s.\n",
+ ret, strerror(ret));
+ /* Assume TCP. */
+ type = SOCK_STREAM;
+ }
+
+ /* SO_KEEPALIVE and TCP_NODELAY are set by OpenLDAP client libraries but
+ * failures are ignored.*/
+ if (domain != AF_UNIX && type == SOCK_STREAM) {
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &dummy, sizeof(dummy));
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_FUNC_DATA,
+ "setsockopt SO_KEEPALIVE failed.[%d][%s].\n", ret,
+ strerror(ret));
+ }
+
+ ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &dummy, sizeof(dummy));
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_FUNC_DATA,
+ "setsockopt TCP_NODELAY failed.[%d][%s].\n", ret,
+ strerror(ret));
+ }
+ }
+
+ if (timeout > 0) {
+ /* Set socket read & write timeout */
+ tv = tevent_timeval_set(timeout, 0);
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_FUNC_DATA,
+ "setsockopt SO_RCVTIMEO failed.[%d][%s].\n", ret,
+ strerror(ret));
+ }
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_FUNC_DATA,
+ "setsockopt SO_SNDTIMEO failed.[%d][%s].\n", ret,
+ strerror(ret));
+ }
+
+ if (domain != AF_UNIX && type == SOCK_STREAM) {
+ milli = timeout * 1000; /* timeout in milliseconds */
+ ret = setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &milli,
+ sizeof(milli));
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_FUNC_DATA,
+ "setsockopt TCP_USER_TIMEOUT failed.[%d][%s].\n", ret,
+ strerror(ret));
+ }
+ }
+ }
+
+ return EOK;
+}
+
+
+struct sssd_async_connect_state {
+ struct tevent_fd *fde;
+ int fd;
+ socklen_t addr_len;
+ struct sockaddr_storage addr;
+};
+
+static void sssd_async_connect_done(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *priv);
+
+struct tevent_req *sssd_async_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int fd,
+ const struct sockaddr *addr,
+ socklen_t addr_len)
+{
+ struct tevent_req *req;
+ struct sssd_async_connect_state *state;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct sssd_async_connect_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n");
+ return NULL;
+ }
+
+ state->fd = fd;
+ state->addr_len = addr_len;
+ memcpy(&state->addr, addr, addr_len);
+
+ ret = connect(fd, addr, addr_len);
+ if (ret == EOK) {
+ goto done;
+ }
+
+ ret = errno;
+ switch (ret) {
+ case EINPROGRESS:
+ case EINTR:
+
+ /* Despite the connect() man page says waiting on a non-blocking
+ * connect should be done by checking for writability, we need to check
+ * also for readability.
+ * With TEVENT_FD_READ, connect fails much faster in offline mode with
+ * errno 113/No route to host.
+ */
+ state->fde = tevent_add_fd(ev, state, fd,
+ TEVENT_FD_READ | TEVENT_FD_WRITE,
+ sssd_async_connect_done, req);
+ if (state->fde == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_fd failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ return req;
+
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "connect failed [%d][%s].\n", ret, strerror(ret));
+ }
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sssd_async_connect_done(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *priv)
+{
+ struct tevent_req *req = talloc_get_type(priv, struct tevent_req);
+ struct sssd_async_connect_state *state =
+ tevent_req_data(req, struct sssd_async_connect_state);
+ int ret;
+
+ errno = 0;
+ ret = connect(state->fd, (struct sockaddr *) &state->addr,
+ state->addr_len);
+ if (ret == -1) {
+ ret = errno;
+ if (ret == EALREADY || ret == EINPROGRESS || ret == EINTR) {
+ return; /* Try again later */
+ }
+ }
+
+ talloc_zfree(fde);
+
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "connect failed [%d][%s].\n", ret, strerror(ret));
+ tevent_req_error(req, ret);
+ }
+}
+
+int sssd_async_connect_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+
+static void sssd_async_connect_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt)
+{
+ struct tevent_req *connection_request;
+
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "The connection timed out [ldap_network_timeout]\n");
+
+ connection_request = talloc_get_type(pvt, struct tevent_req);
+ tevent_req_error(connection_request, ETIMEDOUT);
+}
+
+
+struct sssd_async_socket_state {
+ struct tevent_timer *connect_timeout;
+ int sd;
+};
+
+static int sssd_async_socket_state_destructor(void *data);
+static void sssd_async_socket_init_done(struct tevent_req *subreq);
+
+struct tevent_req *sssd_async_socket_init_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ bool use_udp,
+ struct sockaddr *addr,
+ socklen_t addr_len, int timeout)
+{
+ struct sssd_async_socket_state *state;
+ struct tevent_req *req, *subreq;
+ struct timeval tv;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sssd_async_socket_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n");
+ return NULL;
+ }
+ state->sd = -1;
+
+ talloc_set_destructor((TALLOC_CTX *)state,
+ sssd_async_socket_state_destructor);
+
+ state->sd = socket(addr->sa_family, use_udp ? SOCK_DGRAM : SOCK_STREAM, 0);
+ if (state->sd == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "socket failed [%d][%s].\n", ret, strerror(ret));
+ goto fail;
+ }
+
+ ret = set_fd_common_opts(state->sd, timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "set_fd_common_opts failed.\n");
+ goto fail;
+ }
+
+ ret = set_fcntl_flags(state->sd, FD_CLOEXEC, O_NONBLOCK);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "setting fd flags failed.\n");
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Using file descriptor [%d] for the connection.\n", state->sd);
+
+ subreq = sssd_async_connect_send(state, ev, state->sd,
+ (struct sockaddr *) addr, addr_len);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_CRIT_FAILURE, "sssd_async_connect_send failed.\n");
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Setting %d seconds timeout [ldap_network_timeout] for connecting\n",
+ timeout);
+ tv = tevent_timeval_current_ofs(timeout, 0);
+
+ state->connect_timeout = tevent_add_timer(ev, subreq, tv,
+ sssd_async_connect_timeout,
+ subreq);
+ if (state->connect_timeout == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_timer failed.\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, sssd_async_socket_init_done, req);
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sssd_async_socket_init_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct sssd_async_socket_state *state =
+ tevent_req_data(req, struct sssd_async_socket_state);
+ int ret;
+
+ /* kill the timeout handler now that we got a reply */
+ talloc_zfree(state->connect_timeout);
+
+ ret = sssd_async_connect_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ if (ret == ETIMEDOUT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sdap_async_sys_connect request failed: [%d]: %s "
+ "[ldap_network_timeout].\n",
+ ret, sss_strerror(ret));
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sdap_async_sys_connect request failed: [%d]: %s.\n",
+ ret, sss_strerror(ret));
+ }
+ goto fail;
+ }
+
+ tevent_req_done(req);
+ return;
+
+fail:
+ tevent_req_error(req, ret);
+}
+
+int sssd_async_socket_init_recv(struct tevent_req *req, int *sd)
+{
+ struct sssd_async_socket_state *state =
+ tevent_req_data(req, struct sssd_async_socket_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ /* steal the sd and neutralize destructor actions */
+ *sd = state->sd;
+ state->sd = -1;
+
+ return EOK;
+}
+
+static int sssd_async_socket_state_destructor(void *data)
+{
+ struct sssd_async_socket_state *state =
+ talloc_get_type(data, struct sssd_async_socket_state);
+
+ if (state->sd != -1) {
+ DEBUG(SSSDBG_TRACE_FUNC, "closing socket [%d]\n", state->sd);
+ close(state->sd);
+ state->sd = -1;
+ }
+
+ return 0;
+}
diff --git a/src/util/sss_sockets.h b/src/util/sss_sockets.h
new file mode 100644
index 0000000..1fe5f0a
--- /dev/null
+++ b/src/util/sss_sockets.h
@@ -0,0 +1,42 @@
+/*
+ SSSD
+
+ Socket utils
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2016
+
+ 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_SOCKETS_H__
+#define __SSS_SOCKETS_H__
+
+errno_t set_fd_common_opts(int fd, int timeout);
+
+struct tevent_req *sssd_async_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int fd,
+ const struct sockaddr *addr,
+ socklen_t addr_len);
+int sssd_async_connect_recv(struct tevent_req *req);
+
+
+struct tevent_req *sssd_async_socket_init_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ bool use_udp,
+ struct sockaddr *addr,
+ socklen_t addr_len, int timeout);
+int sssd_async_socket_init_recv(struct tevent_req *req, int *sd);
+
+#endif /* __SSS_SOCKETS_H__ */
diff --git a/src/util/sss_ssh.c b/src/util/sss_ssh.c
new file mode 100644
index 0000000..8b60886
--- /dev/null
+++ b/src/util/sss_ssh.c
@@ -0,0 +1,271 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@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 <arpa/inet.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+#include "util/sss_ssh.h"
+
+errno_t
+sss_ssh_make_ent(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct sss_ssh_ent **result)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_ssh_ent *res = NULL;
+ errno_t ret;
+ const char *name;
+ struct ldb_message_element *el;
+ unsigned int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ if (!name) {
+ ret = EINVAL;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Host is missing name attribute\n");
+ goto done;
+ }
+
+ res = talloc_zero(tmp_ctx, struct sss_ssh_ent);
+ if (!res) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ res->name = talloc_strdup(res, name);
+ if (!res->name) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ el = ldb_msg_find_element(msg, SYSDB_SSH_PUBKEY);
+ if (el) {
+ res->num_pubkeys = el->num_values;
+
+ res->pubkeys = talloc_array(res, struct sss_ssh_pubkey,
+ res->num_pubkeys);
+ if (!res->pubkeys) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ res->pubkeys[i].data = sss_base64_decode(res->pubkeys,
+ (char *)el->values[i].data, &res->pubkeys[i].data_len);
+ if (!res->pubkeys[i].data) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ el = ldb_msg_find_element(msg, SYSDB_NAME_ALIAS);
+ if (el) {
+ res->num_aliases = el->num_values;
+
+ res->aliases = talloc_array(res, char *, res->num_aliases);
+ if (!res->aliases) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ res->aliases[i] = talloc_strdup(res->aliases,
+ (char *)el->values[i].data);
+ if (!res->aliases[i]) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ *result = talloc_steal(mem_ctx, res);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sss_ssh_get_pubkey_algorithm(TALLOC_CTX *mem_ctx,
+ struct sss_ssh_pubkey *pubkey,
+ char **result)
+{
+ size_t c = 0;
+ uint32_t algo_len;
+ char *algo;
+
+ if (pubkey->data_len < 5) {
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&algo_len, pubkey->data, &c);
+ algo_len = ntohl(algo_len);
+ if (algo_len < 1 || algo_len > 64 || algo_len > pubkey->data_len - 4) {
+ /* the maximum length of 64 is defined in RFC 4250 */
+ return EINVAL;
+ }
+
+ algo = talloc_zero_array(mem_ctx, char, algo_len+1);
+ if (!algo) {
+ return ENOMEM;
+ }
+
+ memcpy(algo, pubkey->data+c, algo_len);
+
+ *result = algo;
+ return EOK;
+}
+
+errno_t
+sss_ssh_format_pubkey(TALLOC_CTX *mem_ctx,
+ struct sss_ssh_pubkey *pubkey,
+ char **result)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ char *blob;
+ char *algo;
+ char *out = NULL;
+ size_t i, len;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (pubkey->data_len > 4 && memcmp(pubkey->data, "\0\0\0", 3) == 0) {
+ /* All valid public key blobs start with 3 null bytes (see RFC 4253
+ * section 6.6, RFC 4251 section 5 and RFC 4250 section 4.6)
+ */
+ blob = sss_base64_encode(tmp_ctx, pubkey->data, pubkey->data_len);
+ if (!blob) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_ssh_get_pubkey_algorithm(tmp_ctx, pubkey, &algo);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ out = talloc_asprintf(mem_ctx, "%s %s", algo, blob);
+ if (!out) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ /* Not a valid public key blob, so this must be a textual public key */
+ for (i = 0; i < pubkey->data_len; i++) {
+ if (pubkey->data[i] == '\0' ||
+ (pubkey->data[i] == '\n' && i != pubkey->data_len - 1) ||
+ pubkey->data[i] == '\r') {
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ len = pubkey->data_len;
+ if (pubkey->data[len - 1] == '\n') {
+ len--;
+ }
+
+ out = talloc_array(mem_ctx, char, len + 1);
+ if (out == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ memcpy(out, pubkey->data, len);
+ out[len] = '\0';
+ }
+
+ *result = out;
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sss_ssh_print_pubkey(struct sss_ssh_pubkey *pubkey)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *repr = NULL;
+ char *repr_break = NULL;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ssh_format_pubkey(tmp_ctx, pubkey, &repr);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_ssh_format_pubkey() failed (%d): %s\n",
+ ret, strerror(ret));
+ sss_log(SSS_LOG_ERR, "SSH key is malformed: %s\n", strerror(ret));
+ goto end;
+ }
+
+ /* OpenSSH expects a linebreak after each key */
+ repr_break = talloc_asprintf(tmp_ctx, "%s\n", repr);
+ talloc_zfree(repr);
+ if (repr_break == NULL) {
+ ret = ENOMEM;
+ goto end;
+ }
+
+ ret = sss_atomic_write_s(STDOUT_FILENO, repr_break, strlen(repr_break));
+ /* Avoid spiking memory with too many large keys */
+ talloc_zfree(repr_break);
+ if (ret < 0) {
+ ret = errno;
+ if (ret == EPIPE) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "SSHD closed the pipe before all keys could be written\n");
+ /* Return 0 so that openssh doesn't abort pubkey auth */
+ ret = 0;
+ goto end;
+ }
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_atomic_write_s() failed (%d): %s\n",
+ ret, strerror(ret));
+ goto end;
+ }
+
+ ret = EOK;
+
+ end:
+ talloc_zfree(tmp_ctx);
+
+ return ret;
+}
diff --git a/src/util/sss_ssh.h b/src/util/sss_ssh.h
new file mode 100644
index 0000000..d35ffb9
--- /dev/null
+++ b/src/util/sss_ssh.h
@@ -0,0 +1,56 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@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_SSH_H_
+#define _SSS_SSH_H_
+
+#define SSS_SSH_REQ_ALIAS 0x01
+#define SSS_SSH_REQ_DOMAIN 0x02
+#define SSS_SSH_REQ_MASK 0x03
+
+struct sss_ssh_pubkey {
+ uint8_t *data;
+ size_t data_len;
+};
+
+struct sss_ssh_ent {
+ char *name;
+
+ struct sss_ssh_pubkey *pubkeys;
+ size_t num_pubkeys;
+
+ char **aliases;
+ size_t num_aliases;
+};
+
+errno_t
+sss_ssh_make_ent(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct sss_ssh_ent **result);
+
+errno_t
+sss_ssh_format_pubkey(TALLOC_CTX *mem_ctx,
+ struct sss_ssh_pubkey *pubkey,
+ char **result);
+
+errno_t
+sss_ssh_print_pubkey(struct sss_ssh_pubkey *pubkey);
+
+#endif /* _SSS_SSH_H_ */
diff --git a/src/util/sss_tc_utf8.c b/src/util/sss_tc_utf8.c
new file mode 100644
index 0000000..75d5c71
--- /dev/null
+++ b/src/util/sss_tc_utf8.c
@@ -0,0 +1,82 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@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"
+
+#include <stdlib.h>
+#include <unistr.h>
+#include <unicase.h>
+
+#include <talloc.h>
+#include "util/util.h"
+
+/* Expects and returns NULL-terminated string;
+ * result must be freed with sss_utf8_free()
+ */
+static inline char *sss_utf8_tolower(const char *s)
+{
+ size_t llen;
+ return (char *)u8_tolower((const uint8_t *)s, strlen(s) + 1, NULL, NULL, NULL, &llen);
+}
+
+char *sss_tc_utf8_str_tolower(TALLOC_CTX *mem_ctx, const char *s)
+{
+ char *lower;
+ char *ret = NULL;
+
+ lower = sss_utf8_tolower(s);
+ if (lower) {
+ ret = talloc_strdup(mem_ctx, lower);
+ free(lower);
+ }
+
+ return ret;
+}
+
+errno_t sss_filter_sanitize_for_dom(TALLOC_CTX *mem_ctx,
+ const char *input,
+ struct sss_domain_info *dom,
+ char **sanitized,
+ char **lc_sanitized)
+{
+ int ret;
+
+ ret = sss_filter_sanitize(mem_ctx, input, sanitized);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_filter_sanitize failed.\n");
+ return ret;
+ }
+
+ if (dom->case_sensitive) {
+ *lc_sanitized = talloc_strdup(mem_ctx, *sanitized);
+ } else {
+ *lc_sanitized = sss_tc_utf8_str_tolower(mem_ctx, *sanitized);
+ }
+
+ if (*lc_sanitized == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "%s failed.\n",
+ dom->case_sensitive ?
+ "talloc_strdup" :
+ "sss_tc_utf8_str_tolower");
+ return ENOMEM;
+ }
+
+ return EOK;
+}
diff --git a/src/util/sss_time.c b/src/util/sss_time.c
new file mode 100644
index 0000000..bd985ab
--- /dev/null
+++ b/src/util/sss_time.c
@@ -0,0 +1,76 @@
+/*
+ SSSD - time utils
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2021
+
+ 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 "util/util.h"
+
+#define TV_TO_US(tv) ((tv).tv_sec * 1000000 + (tv).tv_usec)
+
+uint64_t get_start_time()
+{
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "gettimeofday failed.\n");
+ return 0;
+ }
+
+ return TV_TO_US(tv);
+}
+
+const char *sss_format_time(uint64_t us)
+{
+ static char out[128];
+ int ret;
+
+ if (us == 0) {
+ return "[- unavailable -]";
+ }
+
+ ret = snprintf(out, sizeof(out), "[%.3f] milliseconds", (double) us/1000);
+ if (ret < 0 || ret >= sizeof(out)) {
+ return "[- formatting error -]";
+ }
+
+ return out;
+}
+
+uint64_t get_spend_time_us(uint64_t st)
+{
+ struct timeval tv;
+ uint64_t time_now;
+
+ if (st == 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing start time.\n");
+ return 0;
+ }
+
+ if (gettimeofday(&tv, NULL) != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "gettimeofday failed.\n");
+ return 0;
+ }
+
+ time_now = TV_TO_US(tv);
+
+ if (st > time_now) {
+ DEBUG(SSSDBG_OP_FAILURE, "Start time in future.\n");
+ return 0;
+ }
+
+ return time_now - st;
+}
diff --git a/src/util/sss_utf8.c b/src/util/sss_utf8.c
new file mode 100644
index 0000000..ac2c1ff
--- /dev/null
+++ b/src/util/sss_utf8.c
@@ -0,0 +1,81 @@
+/*
+ SSSD
+
+ Authors:
+ 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"
+
+#include <string.h>
+#include <errno.h>
+
+#include <stdlib.h>
+#include <unistr.h>
+#include <unicase.h>
+
+#include "sss_utf8.h"
+
+bool sss_utf8_check(const uint8_t *s, size_t n)
+{
+ if (u8_check(s, n) == NULL) {
+ return true;
+ }
+ return false;
+}
+
+errno_t sss_utf8_case_eq(const uint8_t *s1, const uint8_t *s2)
+{
+
+ /* Do a case-insensitive comparison.
+ * The input must be encoded in UTF8.
+ * We have no way of knowing the language,
+ * so we'll pass NULL for the language and
+ * hope for the best.
+ */
+ int ret;
+ int resultp;
+ size_t n1, n2;
+ errno = 0;
+
+ n1 = u8_strlen(s1);
+ n2 = u8_strlen(s2);
+
+ ret = u8_casecmp(s1, n1,
+ s2, n2,
+ NULL, NULL,
+ &resultp);
+ if (ret < 0) {
+ /* An error occurred */
+ return errno;
+ }
+
+ if (resultp == 0) {
+ return EOK;
+ }
+ return ENOMATCH;
+}
+
+bool sss_string_equal(bool cs, const char *s1, const char *s2)
+{
+ if (cs) {
+ return strcmp(s1, s2) == 0;
+ }
+
+ return sss_utf8_case_eq((const uint8_t *)s1, (const uint8_t *)s2) == EOK;
+}
diff --git a/src/util/sss_utf8.h b/src/util/sss_utf8.h
new file mode 100644
index 0000000..2769f95
--- /dev/null
+++ b/src/util/sss_utf8.h
@@ -0,0 +1,44 @@
+/*
+ SSSD
+
+ Authors:
+ 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/>.
+*/
+
+#ifndef SSS_UTF8_H_
+#define SSS_UTF8_H_
+
+#ifndef ENOMATCH
+#define ENOMATCH -1
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "util/util_errors.h"
+
+bool sss_utf8_check(const uint8_t *s, size_t n);
+
+/* Returns EOK on match, ENOTUNIQ if comparison succeeds but
+ * does not match.
+ * May return other errno error codes on failure
+ */
+errno_t sss_utf8_case_eq(const uint8_t *s1, const uint8_t *s2);
+
+
+#endif /* SSS_UTF8_H_ */
diff --git a/src/util/string_utils.c b/src/util/string_utils.c
new file mode 100644
index 0000000..78966e3
--- /dev/null
+++ b/src/util/string_utils.c
@@ -0,0 +1,252 @@
+/*
+ SSSD
+
+ Authors:
+ Lukas Slebodnik <slebodnikl@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 "util/util.h"
+
+char *sss_replace_char(TALLOC_CTX *mem_ctx,
+ const char *in,
+ const char match,
+ const char sub)
+{
+ char *p;
+ char *out;
+
+ out = talloc_strdup(mem_ctx, in);
+ if (out == NULL) {
+ return NULL;
+ }
+
+ for (p = out; *p != '\0'; ++p) {
+ if (*p == match) {
+ *p = sub;
+ }
+ }
+
+ return out;
+}
+
+char * sss_replace_space(TALLOC_CTX *mem_ctx,
+ const char *orig_name,
+ const char subst)
+{
+ if (subst == '\0' || subst == ' ') {
+ return talloc_strdup(mem_ctx, orig_name);
+ }
+
+ if (strchr(orig_name, subst) != NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Input [%s] already contains replacement character [%c].\n",
+ orig_name, subst);
+ sss_log(SSS_LOG_CRIT,
+ "Name [%s] already contains replacement character [%c]. " \
+ "No replacement will be done.\n",
+ orig_name, subst);
+ return talloc_strdup(mem_ctx, orig_name);
+ }
+
+ return sss_replace_char(mem_ctx, orig_name, ' ', subst);
+}
+
+char * sss_reverse_replace_space(TALLOC_CTX *mem_ctx,
+ const char *orig_name,
+ const char subst)
+{
+ if (subst == '\0' || subst == ' ') {
+ return talloc_strdup(mem_ctx, orig_name);
+ }
+
+ if (strchr(orig_name, subst) != NULL && strchr(orig_name, ' ') != NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Input [%s] contains replacement character [%c] and space.\n",
+ orig_name, subst);
+ return talloc_strdup(mem_ctx, orig_name);
+ }
+
+ return sss_replace_char(mem_ctx, orig_name, subst, ' ');
+}
+
+errno_t guid_blob_to_string_buf(const uint8_t *blob, char *str_buf,
+ size_t buf_size)
+{
+ int ret;
+
+ if (blob == NULL || str_buf == NULL || buf_size < GUID_STR_BUF_SIZE) {
+ DEBUG(SSSDBG_OP_FAILURE, "Buffer too small.\n");
+ return EINVAL;
+ }
+
+ ret = snprintf(str_buf, buf_size,
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ blob[3], blob[2], blob[1], blob[0],
+ blob[5], blob[4],
+ blob[7], blob[6],
+ blob[8], blob[9],
+ blob[10], blob[11],blob[12], blob[13],blob[14], blob[15]);
+ if (ret != (GUID_STR_BUF_SIZE -1)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "snprintf failed.\n");
+ return EIO;
+ }
+
+ return EOK;
+}
+
+const char *get_last_x_chars(const char *str, size_t x)
+{
+ size_t len;
+
+ if (str == NULL) {
+ return NULL;
+ }
+
+ len = strlen(str);
+
+ if (len < x) {
+ return str;
+ }
+
+ return (str + len - x);
+}
+
+char **concatenate_string_array(TALLOC_CTX *mem_ctx,
+ char **arr1, size_t len1,
+ char **arr2, size_t len2)
+{
+ size_t i, j;
+ size_t new_size = len1 + len2;
+ char ** string_array = talloc_realloc(mem_ctx, arr1, char *, new_size + 1);
+ if (string_array == NULL) {
+ return NULL;
+ }
+
+ for (i=len1, j=0; i < new_size; ++i,++j) {
+ string_array[i] = talloc_steal(string_array,
+ arr2[j]);
+ }
+
+ string_array[i] = NULL;
+
+ return string_array;
+}
+
+errno_t mod_defaults_list(TALLOC_CTX *mem_ctx, const char **defaults_list,
+ char **mod_list, char ***_list)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ size_t mod_list_size;
+ const char **add_list;
+ const char **remove_list;
+ size_t c;
+ size_t ai = 0;
+ size_t ri = 0;
+ size_t j = 0;
+ char **list;
+ size_t expected_list_size = 0;
+ size_t defaults_list_size = 0;
+
+ for (defaults_list_size = 0;
+ defaults_list != NULL && defaults_list[defaults_list_size] != NULL;
+ defaults_list_size++);
+
+ for (mod_list_size = 0;
+ mod_list != NULL && mod_list[mod_list_size] != NULL;
+ mod_list_size++);
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ add_list = talloc_zero_array(tmp_ctx, const char *, mod_list_size + 1);
+ remove_list = talloc_zero_array(tmp_ctx, const char *, mod_list_size + 1);
+
+ if (add_list == NULL || remove_list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; mod_list != NULL && mod_list[c] != NULL; c++) {
+ switch (mod_list[c][0]) {
+ case '+':
+ add_list[ai] = mod_list[c] + 1;
+ ++ai;
+ break;
+ case '-':
+ remove_list[ri] = mod_list[c] + 1;
+ ++ri;
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE,
+ "The option "CONFDB_PAM_P11_ALLOWED_SERVICES" must start"
+ "with either '+' (for adding service) or '-' (for "
+ "removing service) got '%s'\n", mod_list[c]);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ expected_list_size = defaults_list_size + ai + 1;
+
+ list = talloc_zero_array(tmp_ctx, char *, expected_list_size);
+ if (list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; add_list[c] != NULL; ++c) {
+ if (string_in_list(add_list[c], discard_const(remove_list), false)) {
+ continue;
+ }
+
+ list[j] = talloc_strdup(list, add_list[c]);
+ if (list[j] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ j++;
+ }
+
+ for (c = 0; defaults_list != NULL && defaults_list[c] != NULL; ++c) {
+ if (string_in_list(defaults_list[c],
+ discard_const(remove_list), false)) {
+ continue;
+ }
+
+ list[j] = talloc_strdup(list, defaults_list[c]);
+ if (list[j] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ j++;
+ }
+
+ if (_list != NULL) {
+ *_list = talloc_steal(mem_ctx, list);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+
+ return ret;
+}
diff --git a/src/util/strtonum.c b/src/util/strtonum.c
new file mode 100644
index 0000000..8eda8ea
--- /dev/null
+++ b/src/util/strtonum.c
@@ -0,0 +1,77 @@
+/*
+ SSSD
+
+ SSSD Utility functions
+
+ Copyright (C) Stephen Gallagher <sgallagh@redhat.com> 2009
+
+ 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 <stdlib.h>
+#include <errno.h>
+#include "util/strtonum.h"
+
+int32_t strtoint32(const char *nptr, char **endptr, int base)
+{
+ long long ret = 0;
+
+ errno = 0;
+ ret = strtoll(nptr, endptr, base);
+
+ if (ret > INT32_MAX) {
+ errno = ERANGE;
+ return INT32_MAX;
+ }
+ else if (ret < INT32_MIN) {
+ errno = ERANGE;
+ return INT32_MIN;
+ }
+
+ /* If errno was set by strtoll, we'll pass it back as-is */
+ return (int32_t)ret;
+}
+
+
+uint32_t strtouint32(const char *nptr, char **endptr, int base)
+{
+ unsigned long long ret = 0;
+ errno = 0;
+ ret = strtoull(nptr, endptr, base);
+
+ if (ret > UINT32_MAX) {
+ errno = ERANGE;
+ return UINT32_MAX;
+ }
+
+ /* If errno was set by strtoll, we'll pass it back as-is */
+ return (uint32_t)ret;
+}
+
+
+uint16_t strtouint16(const char *nptr, char **endptr, int base)
+{
+ unsigned long long ret = 0;
+ errno = 0;
+ ret = strtoull(nptr, endptr, base);
+
+ if (ret > UINT16_MAX) {
+ errno = ERANGE;
+ return UINT16_MAX;
+ }
+
+ /* If errno was set by strtoll, we'll pass it back as-is */
+ return (uint16_t)ret;
+}
+
diff --git a/src/util/strtonum.h b/src/util/strtonum.h
new file mode 100644
index 0000000..ae493b5
--- /dev/null
+++ b/src/util/strtonum.h
@@ -0,0 +1,32 @@
+/*
+ SSSD
+
+ SSSD Utility functions
+
+ Copyright (C) Stephen Gallagher 2009
+
+ 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 _STRTONUM_H_
+#define _STRTONUM_H_
+
+#include <stdint.h>
+
+int32_t strtoint32(const char *nptr, char **endptr, int base);
+uint32_t strtouint32(const char *nptr, char **endptr, int base);
+
+uint16_t strtouint16(const char *nptr, char **endptr, int base);
+
+#endif /* _STRTONUM_H_ */
diff --git a/src/util/user_info_msg.c b/src/util/user_info_msg.c
new file mode 100644
index 0000000..1399544
--- /dev/null
+++ b/src/util/user_info_msg.c
@@ -0,0 +1,57 @@
+/*
+ SSSD
+
+ Pack user info messages
+
+ Authors:
+ Sumit Bose <sbose@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/>.
+*/
+
+#include "util/util.h"
+#include "util/user_info_msg.h"
+#include "sss_client/sss_cli.h"
+
+errno_t pack_user_info_chpass_error(TALLOC_CTX *mem_ctx,
+ const char *user_error_message,
+ size_t *resp_len,
+ uint8_t **_resp)
+{
+ uint32_t resp_type = SSS_PAM_USER_INFO_CHPASS_ERROR;
+ size_t err_len;
+ uint8_t *resp;
+ size_t p;
+
+ err_len = strlen(user_error_message);
+ *resp_len = 2 * sizeof(uint32_t) + err_len;
+ resp = talloc_size(mem_ctx, *resp_len);
+ if (resp == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
+ return ENOMEM;
+ }
+
+ p = 0;
+ SAFEALIGN_SET_UINT32(&resp[p], resp_type, &p);
+ SAFEALIGN_SET_UINT32(&resp[p], err_len, &p);
+ safealign_memcpy(&resp[p], user_error_message, err_len, &p);
+ if (p != *resp_len) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Size mismatch\n");
+ }
+
+ *_resp = resp;
+ return EOK;
+}
diff --git a/src/util/user_info_msg.h b/src/util/user_info_msg.h
new file mode 100644
index 0000000..c68d538
--- /dev/null
+++ b/src/util/user_info_msg.h
@@ -0,0 +1,33 @@
+/*
+ SSSD
+
+ Pack user info messages
+
+ Authors:
+ Sumit Bose <sbose@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 __USER_INFO_MSG_H__
+#define __USER_INFO_MSG_H__
+
+
+errno_t pack_user_info_chpass_error(TALLOC_CTX *mem_ctx,
+ const char *user_error_message,
+ size_t *len,
+ uint8_t **_resp);
+
+#endif /* __USER_INFO_MSG_H__ */
diff --git a/src/util/usertools.c b/src/util/usertools.c
new file mode 100644
index 0000000..8084760
--- /dev/null
+++ b/src/util/usertools.c
@@ -0,0 +1,891 @@
+/*
+ SSSD
+
+ User tools
+
+ Copyright (C) Stephen Gallagher <sgallagh@redhat.com> 2009
+
+ 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 <pwd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <talloc.h>
+#include <grp.h>
+
+#include "db/sysdb.h"
+#include "confdb/confdb.h"
+#include "util/strtonum.h"
+#include "util/util.h"
+#include "util/safe-format-string.h"
+#include "responder/common/responder.h"
+
+#define NAME_DOMAIN_PATTERN_OPTIONS (SSS_REGEXP_DUPNAMES | SSS_REGEXP_EXTENDED)
+
+/* Function returns given realm name as new uppercase string */
+char *get_uppercase_realm(TALLOC_CTX *memctx, const char *name)
+{
+ char *realm;
+ char *c;
+
+ realm = talloc_strdup(memctx, name);
+ if (!realm) {
+ return NULL;
+ }
+
+ c = realm;
+ while(*c != '\0') {
+ *c = toupper(*c);
+ c++;
+ }
+
+ return realm;
+}
+
+static errno_t get_id_provider_default_re(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *cdb,
+ const char *conf_path,
+ char **re_pattern)
+{
+ int ret;
+ size_t c;
+ char *id_provider = NULL;
+
+ struct provider_default_re {
+ const char *name;
+ const char *re;
+ } provider_default_re[] = {{"ipa", SSS_IPA_AD_DEFAULT_RE},
+ {"ad", SSS_IPA_AD_DEFAULT_RE},
+ {NULL, NULL}};
+
+ ret = confdb_get_string(cdb, mem_ctx, conf_path, CONFDB_DOMAIN_ID_PROVIDER,
+ NULL, &id_provider);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to read ID provider " \
+ "from conf db.\n");
+ goto done;
+ }
+
+ if (id_provider == NULL) {
+ *re_pattern = NULL;
+ } else {
+ for (c = 0; provider_default_re[c].name != NULL; c++) {
+ if (strcmp(id_provider, provider_default_re[c].name) == 0) {
+ *re_pattern = talloc_strdup(mem_ctx, provider_default_re[c].re);
+ if (*re_pattern == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ break;
+ }
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(id_provider);
+ return ret;
+}
+
+static errno_t sss_fqnames_init(struct sss_names_ctx *nctx, const char *fq_fmt)
+{
+ char *fq;
+
+ nctx->fq_fmt = talloc_strdup(nctx, fq_fmt);
+ if (nctx->fq_fmt == NULL) {
+ return ENOMEM;
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS, "Using fq format [%s].\n", nctx->fq_fmt);
+
+ /* Fail if the name specifier is missing, or if the format is
+ * invalid */
+ fq = sss_tc_fqname2 (nctx, nctx, "unused.example.com", "unused", "the-test-user");
+ if (fq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "The fq format is invalid [%s]\n", nctx->fq_fmt);
+ return EINVAL;
+ } else if (strstr (fq, "the-test-user") == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Username pattern not found in [%s]\n", nctx->fq_fmt);
+ return ENOENT;
+ }
+
+ talloc_free (fq);
+ return EOK;
+}
+
+int sss_names_init_from_args(TALLOC_CTX *mem_ctx, const char *re_pattern,
+ const char *fq_fmt, struct sss_names_ctx **out)
+{
+ struct sss_names_ctx *ctx;
+ int errval;
+ int ret;
+
+ ctx = talloc_zero(mem_ctx, struct sss_names_ctx);
+ if (!ctx) return ENOMEM;
+
+ ctx->re_pattern = talloc_strdup(ctx, re_pattern);
+ if (ctx->re_pattern == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS, "Using re [%s].\n", ctx->re_pattern);
+
+ ret = sss_fqnames_init(ctx, fq_fmt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not check the FQ names format"
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ errval = sss_regexp_new(ctx,
+ ctx->re_pattern,
+ NAME_DOMAIN_PATTERN_OPTIONS,
+ &(ctx->re));
+ if (errval != 0) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ *out = ctx;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(ctx);
+ }
+ return ret;
+}
+
+int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
+ const char *domain, struct sss_names_ctx **out)
+{
+ TALLOC_CTX *tmpctx = NULL;
+ char *conf_path = NULL;
+ char *re_pattern = NULL;
+ char *fq_fmt = NULL;
+ int ret;
+
+ tmpctx = talloc_new(NULL);
+ if (tmpctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (domain != NULL) {
+ conf_path = talloc_asprintf(tmpctx, CONFDB_DOMAIN_PATH_TMPL, domain);
+ if (conf_path == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = confdb_get_string(cdb, tmpctx, conf_path,
+ CONFDB_NAME_REGEX, NULL, &re_pattern);
+ if (ret != EOK) goto done;
+ }
+
+ /* If not found in the domain, look in globals */
+ if (re_pattern == NULL) {
+ ret = confdb_get_string(cdb, tmpctx, CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_NAME_REGEX, NULL, &re_pattern);
+ if (ret != EOK) goto done;
+ }
+
+ if (re_pattern == NULL && conf_path != NULL) {
+ ret = get_id_provider_default_re(tmpctx, cdb, conf_path, &re_pattern);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to get provider default regular " \
+ "expression for domain [%s].\n", domain);
+ goto done;
+ }
+ }
+
+ if (!re_pattern) {
+ re_pattern = talloc_strdup(tmpctx, SSS_DEFAULT_RE);
+ if (!re_pattern) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (conf_path != NULL) {
+ ret = confdb_get_string(cdb, tmpctx, conf_path,
+ CONFDB_FULL_NAME_FORMAT, NULL, &fq_fmt);
+ if (ret != EOK) goto done;
+ }
+
+ /* If not found in the domain, look in globals */
+ if (fq_fmt == NULL) {
+ ret = confdb_get_string(cdb, tmpctx, CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_FULL_NAME_FORMAT, NULL, &fq_fmt);
+ if (ret != EOK) goto done;
+ }
+
+ if (!fq_fmt) {
+ fq_fmt = talloc_strdup(tmpctx, CONFDB_DEFAULT_FULL_NAME_FORMAT);
+ if (!fq_fmt) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = sss_names_init_from_args(mem_ctx, re_pattern, fq_fmt, out);
+
+done:
+ talloc_free(tmpctx);
+ return ret;
+}
+
+int sss_ad_default_names_ctx(TALLOC_CTX *mem_ctx,
+ struct sss_names_ctx **_out)
+{
+ return sss_names_init_from_args(mem_ctx, SSS_IPA_AD_DEFAULT_RE,
+ CONFDB_DEFAULT_FULL_NAME_FORMAT,
+ _out);
+}
+
+int sss_parse_name(TALLOC_CTX *memctx,
+ struct sss_names_ctx *snctx,
+ const char *orig, char **_domain, char **_name)
+{
+ sss_regexp_t *re = snctx->re;
+ const char *result;
+ int ret;
+
+ ret = sss_regexp_match(re, orig, 0, SSS_REGEXP_NOTEMPTY);
+ if (ret == SSS_REGEXP_ERROR_NOMATCH) {
+ return ERR_REGEX_NOMATCH;
+ } else if (ret < 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "PCRE Matching error, %d\n", ret);
+ return EINVAL;
+ }
+
+ if (ret == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Too many matches, the pattern is invalid.\n");
+ }
+
+ if (_name != NULL) {
+ result = NULL;
+ ret = sss_regexp_get_named_substring(re, "name", &result);
+ if (ret < 0 || !result) {
+ DEBUG(SSSDBG_OP_FAILURE, "Name not found!\n");
+ return EINVAL;
+ }
+ *_name = talloc_strdup(memctx, result);
+ if (!*_name) return ENOMEM;
+ }
+
+ if (_domain != NULL) {
+ result = NULL;
+ ret = sss_regexp_get_named_substring(re, "domain", &result);
+ if (ret < 0 || !result) {
+ DEBUG(SSSDBG_FUNC_DATA, "Domain not provided!\n");
+ *_domain = NULL;
+ } else {
+ /* ignore "" string */
+ if (*result) {
+ *_domain = talloc_strdup(memctx, result);
+ if (!*_domain) return ENOMEM;
+ } else {
+ *_domain = NULL;
+ }
+ }
+ }
+
+ return EOK;
+}
+
+static struct sss_domain_info * match_any_domain_or_subdomain_name(
+ struct sss_domain_info *dom,
+ const char *dmatch)
+{
+ if (strcasecmp(dom->name, dmatch) == 0 ||
+ (dom->flat_name != NULL && strcasecmp(dom->flat_name, dmatch) == 0)) {
+ return dom;
+ }
+
+ return find_domain_by_name_ex(dom, dmatch, true, SSS_GND_SUBDOMAINS);
+}
+
+int sss_parse_name_for_domains(TALLOC_CTX *memctx,
+ struct sss_domain_info *domains,
+ const char *default_domain,
+ const char *orig, char **domain, char **name)
+{
+ struct sss_domain_info *dom, *match = NULL;
+ char *rdomain, *rname;
+ char *dmatch, *nmatch;
+ char *candidate_name = NULL;
+ char *candidate_domain = NULL;
+ bool name_mismatch = false;
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ rname = NULL;
+ rdomain = NULL;
+
+ for (dom = domains; dom != NULL; dom = get_next_domain(dom, 0)) {
+ ret = sss_parse_name(tmp_ctx, dom->names, orig, &dmatch, &nmatch);
+ if (ret == EOK) {
+ /*
+ * If the name matched without the domain part, make note of it.
+ * All the other domain expressions must agree on the domain-less
+ * name.
+ */
+ if (dmatch == NULL) {
+ if (candidate_name == NULL) {
+ candidate_name = nmatch;
+ } else if (strcasecmp(candidate_name, nmatch) != 0) {
+ name_mismatch = true;
+ }
+
+ /*
+ * If a domain was returned, then it must match the name of the
+ * domain that this expression was found on, or one of the
+ * subdomains.
+ */
+ } else {
+ match = match_any_domain_or_subdomain_name (dom, dmatch);
+ if (match != NULL) {
+ DEBUG(SSSDBG_FUNC_DATA, "name '%s' matched expression for "
+ "domain '%s', user is %s\n",
+ orig, match->name, nmatch);
+ rdomain = talloc_strdup(tmp_ctx, match->name);
+ if (rdomain == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ rname = nmatch;
+ break;
+ } else if (candidate_domain == NULL) {
+ candidate_domain = dmatch;
+ }
+ }
+
+ /* EINVAL is returned when name doesn't match */
+ } else if (ret != EINVAL) {
+ goto done;
+ }
+ }
+
+ if (rdomain == NULL && rname == NULL) {
+ if (candidate_name && !name_mismatch) {
+ DEBUG(SSSDBG_FUNC_DATA, "name '%s' matched without domain, " \
+ "user is %s\n", orig, nmatch);
+ rdomain = NULL;
+ if (default_domain != NULL) {
+ rdomain = talloc_strdup(tmp_ctx, default_domain);
+ if (rdomain == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (dom = domains; dom != NULL; dom = get_next_domain(dom, 0)) {
+ match = match_any_domain_or_subdomain_name(dom, rdomain);
+ if (match != NULL) {
+ break;
+ }
+ }
+ if (match == NULL) {
+ DEBUG(SSSDBG_FUNC_DATA, "default domain [%s] is currently " \
+ "not known\n", rdomain);
+ *domain = talloc_steal(memctx, rdomain);
+ ret = EAGAIN;
+ goto done;
+ }
+ DEBUG(SSSDBG_FUNC_DATA, "using default domain [%s]\n", rdomain);
+ }
+
+ rname = candidate_name;
+ } else if (candidate_domain) {
+ /* This branch is taken when the input matches the configured
+ * regular expression, but the domain is now known. Normally, this
+ * is the case with a FQDN of a user from subdomain that was not
+ * yet discovered
+ */
+ *domain = talloc_steal(memctx, candidate_domain);
+ ret = EAGAIN;
+ goto done;
+ }
+ }
+
+ if (rdomain == NULL && rname == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "name '%s' did not match any domain's expression\n", orig);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (domain != NULL) {
+ *domain = talloc_steal(memctx, rdomain);
+ }
+
+ if (name != NULL) {
+ *name = talloc_steal(memctx, rname);
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+char *
+sss_get_cased_name(TALLOC_CTX *mem_ctx,
+ const char *orig_name,
+ bool case_sensitive)
+{
+ return case_sensitive ? talloc_strdup(mem_ctx, orig_name) :
+ sss_tc_utf8_str_tolower(mem_ctx, orig_name);
+}
+
+errno_t
+sss_get_cased_name_list(TALLOC_CTX *mem_ctx, const char * const *orig,
+ bool case_sensitive, const char ***_cased)
+{
+ const char **out;
+ size_t num, i;
+
+ if (orig == NULL) {
+ *_cased = NULL;
+ return EOK;
+ }
+
+ for (num=0; orig[num]; num++); /* count the num of strings */
+
+ if (num == 0) {
+ *_cased = NULL;
+ return EOK;
+ }
+
+ out = talloc_array(mem_ctx, const char *, num + 1);
+ if (out == NULL) {
+ return ENOMEM;
+ }
+
+ for (i = 0; i < num; i++) {
+ out[i] = sss_get_cased_name(out, orig[i], case_sensitive);
+ if (out[i] == NULL) {
+ talloc_free(out);
+ return ENOMEM;
+ }
+ }
+
+ out[num] = NULL;
+ *_cased = out;
+ return EOK;
+}
+
+static inline const char *
+calc_flat_name(struct sss_domain_info *domain)
+{
+ const char *s;
+
+ s = domain->flat_name;
+ if (s == NULL) {
+ DEBUG(SSSDBG_FUNC_DATA, "Domain has no flat name set,"
+ "using domain name instead\n");
+ s = domain->name;
+ }
+
+ return s;
+}
+
+char *
+sss_tc_fqname(TALLOC_CTX *mem_ctx, struct sss_names_ctx *nctx,
+ struct sss_domain_info *domain, const char *name)
+{
+ if (domain == NULL || nctx == NULL) return NULL;
+
+ return sss_tc_fqname2 (mem_ctx, nctx, domain->name,
+ calc_flat_name (domain), name);
+}
+
+static void
+safe_talloc_callback (void *data,
+ const char *piece,
+ size_t len)
+{
+ char **output = data;
+ if (*output != NULL)
+ *output = talloc_strndup_append(*output, piece, len);
+}
+
+char *
+sss_tc_fqname2(TALLOC_CTX *mem_ctx, struct sss_names_ctx *nctx,
+ const char *domain_name, const char *flat_dom_name,
+ const char *name)
+{
+ const char *args[] = { name, domain_name, flat_dom_name, NULL };
+ char *output;
+
+ if (nctx == NULL) return NULL;
+
+ output = talloc_strdup(mem_ctx, "");
+ if (safe_format_string_cb(safe_talloc_callback, &output, nctx->fq_fmt, args, 3) < 0)
+ output = NULL;
+ else if (output == NULL)
+ errno = ENOMEM;
+ return output;
+}
+
+int
+sss_fqname(char *str, size_t size, struct sss_names_ctx *nctx,
+ struct sss_domain_info *domain, const char *name)
+{
+ if (domain == NULL || nctx == NULL) return -EINVAL;
+
+ return safe_format_string(str, size, nctx->fq_fmt,
+ name, domain->name, calc_flat_name (domain), NULL);
+}
+
+errno_t sss_user_by_name_or_uid(const char *input, uid_t *_uid, gid_t *_gid)
+{
+ uid_t uid;
+ errno_t ret;
+ char *endptr;
+ struct passwd *pwd;
+
+ /* Try if it's an ID first */
+ uid = strtouint32(input, &endptr, 10);
+ if ((errno != 0) || (*endptr != '\0') || (input == endptr)) {
+ ret = errno;
+ if (ret == ERANGE) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "UID [%s] is out of range.\n", input);
+ return ret;
+ }
+
+ /* Nope, maybe a username? */
+ pwd = getpwnam(input);
+ } else {
+ pwd = getpwuid(uid);
+ }
+
+ if (pwd == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "[%s] is neither a valid UID nor a user name which could be "
+ "resolved by getpwnam().\n", input);
+ return EINVAL;
+ }
+
+ if (_uid) {
+ *_uid = pwd->pw_uid;
+ }
+
+ if (_gid) {
+ *_gid = pwd->pw_gid;
+ }
+ return EOK;
+}
+
+/* Accepts fqname in the format shortname@domname only. */
+errno_t sss_parse_internal_fqname(TALLOC_CTX *mem_ctx,
+ const char *fqname,
+ char **_shortname,
+ char **_dom_name)
+{
+ errno_t ret;
+ char *separator;
+ char *shortname = NULL;
+ char *dom_name = NULL;
+ size_t shortname_len;
+ TALLOC_CTX *tmp_ctx;
+
+ if (fqname == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ separator = strrchr(fqname, '@');
+ if (separator == NULL || *(separator + 1) == '\0' || separator == fqname) {
+ /*The name does not contain name or domain component. */
+ ret = ERR_WRONG_NAME_FORMAT;
+ goto done;
+ }
+
+ if (_dom_name != NULL) {
+ dom_name = talloc_strdup(tmp_ctx, separator + 1);
+ if (dom_name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_dom_name = talloc_steal(mem_ctx, dom_name);
+ }
+
+ if (_shortname != NULL) {
+ shortname_len = strlen(fqname) - strlen(separator);
+ shortname = talloc_strndup(tmp_ctx, fqname, shortname_len);
+ if (shortname == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_shortname = talloc_steal(mem_ctx, shortname);
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/* Creates internal fqname in format shortname@domname.
+ * The domain portion is lowercased. */
+char *sss_create_internal_fqname(TALLOC_CTX *mem_ctx,
+ const char *shortname,
+ const char *dom_name)
+{
+ char *lc_dom_name;
+ char *fqname = NULL;
+
+ if (shortname == NULL || dom_name == NULL) {
+ /* Avoid allocating null@null */
+ return NULL;
+ }
+
+ lc_dom_name = sss_tc_utf8_str_tolower(mem_ctx, dom_name);
+ if (lc_dom_name == NULL) {
+ goto done;
+ }
+
+ fqname = talloc_asprintf(mem_ctx, "%s@%s", shortname, lc_dom_name);
+ talloc_free(lc_dom_name);
+done:
+ return fqname;
+}
+
+/* Creates a list of internal fqnames in format shortname@domname.
+ * The domain portion is lowercased. */
+char **sss_create_internal_fqname_list(TALLOC_CTX *mem_ctx,
+ const char * const *shortname_list,
+ const char *dom_name)
+{
+ char **fqname_list = NULL;
+ size_t c;
+
+ if (shortname_list == NULL || dom_name == NULL) {
+ /* Avoid allocating null@null */
+ return NULL;
+ }
+
+ for (c = 0; shortname_list[c] != NULL; c++);
+ fqname_list = talloc_zero_array(mem_ctx, char *, c+1);
+ if (fqname_list == NULL) {
+ talloc_free(fqname_list);
+ return NULL;
+ }
+
+ for (size_t i = 0; shortname_list[i] != NULL; i++) {
+ fqname_list[i] = sss_create_internal_fqname(fqname_list,
+ shortname_list[i],
+ dom_name);
+ if (fqname_list[i] == NULL) {
+ talloc_free(fqname_list);
+ return NULL;
+ }
+ }
+
+ return fqname_list;
+}
+
+char *sss_output_name(TALLOC_CTX *mem_ctx,
+ const char *name,
+ bool case_sensitive,
+ const char replace_space)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+ char *shortname;
+ char *outname = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return NULL;
+
+ ret = sss_parse_internal_fqname(tmp_ctx, name, &shortname, NULL);
+ if (ret == ERR_WRONG_NAME_FORMAT) {
+ /* There is no domain name. */
+ shortname = talloc_strdup(tmp_ctx, name);
+ if (shortname == NULL) {
+ goto done;
+ }
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_parse_internal_fqname failed\n");
+ goto done;
+ }
+
+ outname = sss_get_cased_name(tmp_ctx, shortname, case_sensitive);
+ if (outname == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_get_cased_name failed, skipping\n");
+ goto done;
+ }
+
+ outname = sss_replace_space(tmp_ctx, outname, replace_space);
+ if (outname == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_replace_space failed\n");
+ goto done;
+ }
+
+ outname = talloc_steal(mem_ctx, outname);
+done:
+ talloc_free(tmp_ctx);
+ return outname;
+}
+
+const char *
+sss_get_name_from_msg(struct sss_domain_info *domain,
+ struct ldb_message *msg)
+{
+ const char *name;
+
+ /* If domain has a view associated we return overridden name
+ * if possible. */
+ if (DOM_HAS_VIEWS(domain)) {
+ name = ldb_msg_find_attr_as_string(msg, OVERRIDE_PREFIX SYSDB_NAME,
+ NULL);
+ if (name != NULL) {
+ return name;
+ }
+ }
+
+ /* Otherwise we try to return name override from
+ * Default Truest View for trusted users. */
+ name = ldb_msg_find_attr_as_string(msg, SYSDB_DEFAULT_OVERRIDE_NAME, NULL);
+ if (name != NULL) {
+ return name;
+ }
+
+ /* If no override is found we return the original name. */
+ return ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+}
+
+int sss_output_fqname(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ char override_space,
+ char **_output_name)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+ char *output_name;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ output_name = sss_output_name(tmp_ctx, name, domain->case_preserve,
+ override_space);
+ if (output_name == NULL) {
+ ret = EIO;
+ goto done;
+ }
+
+ if (sss_domain_info_get_output_fqnames(domain) || domain->fqnames) {
+ output_name = sss_tc_fqname(tmp_ctx, domain->names,
+ domain, output_name);
+ if (output_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_tc_fqname failed\n");
+ ret = EIO;
+ goto done;
+ }
+ }
+
+ *_output_name = talloc_steal(mem_ctx, output_name);
+ ret = EOK;
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+void sss_sssd_user_uid_and_gid(uid_t *_uid, gid_t *_gid)
+{
+ uid_t sssd_uid;
+ gid_t sssd_gid;
+ errno_t ret;
+
+ ret = sss_user_by_name_or_uid(SSSD_USER, &sssd_uid, &sssd_gid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "failed to get sssd user (" SSSD_USER ") uid/gid, using root\n");
+ sssd_uid = 0;
+ sssd_gid = 0;
+ }
+
+ if (_uid != NULL) {
+ *_uid = sssd_uid;
+ }
+
+ if (_gid != NULL) {
+ *_gid = sssd_gid;
+ }
+}
+
+void sss_set_sssd_user_eid(void)
+{
+ uid_t uid;
+ gid_t gid;
+
+
+ if (geteuid() == 0) {
+ sss_sssd_user_uid_and_gid(&uid, &gid);
+
+ if (setegid(gid) != EOK) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Failed to set egid to %"SPRIgid": %s\n",
+ gid, sss_strerror(errno));
+ }
+ if (seteuid(uid) != EOK) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Failed to set euid to %"SPRIuid": %s\n",
+ uid, sss_strerror(errno));
+ }
+ }
+}
+
+void sss_restore_sssd_user_eid(void)
+{
+ if (getuid() == 0) {
+ if (seteuid(getuid()) != EOK) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Failed to restore euid: %s\n",
+ sss_strerror(errno));
+ }
+ if (setegid(getgid()) != EOK) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Failed to restore egid: %s\n",
+ sss_strerror(errno));
+ }
+ }
+}
diff --git a/src/util/util.c b/src/util/util.c
new file mode 100644
index 0000000..6546b60
--- /dev/null
+++ b/src/util/util.c
@@ -0,0 +1,1103 @@
+/*
+ Authors:
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include "config.h"
+#include <ctype.h>
+#include <netdb.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <talloc.h>
+#include <dhash.h>
+#include <time.h>
+
+#include "util/util.h"
+#include "util/sss_utf8.h"
+
+int socket_activated = 0;
+int dbus_activated = 0;
+
+static void free_args(char **args)
+{
+ int i;
+
+ if (args) {
+ for (i = 0; args[i]; i++) free(args[i]);
+ free(args);
+ }
+}
+
+/* parse a string into arguments.
+ * arguments are separated by a space
+ * '\' is an escape character and can be used only to escape
+ * itself or the white space.
+ */
+char **parse_args(const char *str)
+{
+ const char *p;
+ char **ret, **r;
+ char *tmp;
+ int num;
+ int i;
+ bool e, w;
+
+ tmp = malloc(strlen(str) + 1);
+ if (!tmp) return NULL;
+
+ ret = NULL;
+ num = 0;
+ i = 0;
+ e = false;
+ /* skip leading whitespaces */
+ w = true;
+ p = str;
+ while (*p) {
+ if (*p == '\\') {
+ w = false;
+ if (e) {
+ /* if we were already escaping, add a '\' literal */
+ tmp[i] = '\\';
+ i++;
+ e = false;
+ } else {
+ /* otherwise just start escaping */
+ e = true;
+ }
+ } else if (isspace(*p)) {
+ if (e) {
+ /* Add escaped whitespace literally */
+ tmp[i] = *p;
+ i++;
+ e = false;
+ } else if (w == false) {
+ /* If previous character was non-whitespace, arg break */
+ tmp[i] = '\0';
+ i++;
+ w = true;
+ }
+ /* previous char was whitespace as well, skip it */
+ } else {
+ w = false;
+ if (e) {
+ /* Prepend escaped chars with a literal \ */
+ tmp[i] = '\\';
+ i++;
+ e = false;
+ }
+ /* Copy character from the source string */
+ tmp[i] = *p;
+ i++;
+ }
+
+ p++;
+
+ /* check if this was the last char */
+ if (*p == '\0') {
+ if (e) {
+ tmp[i] = '\\';
+ i++;
+ e = false;
+ }
+ tmp[i] = '\0';
+ i++;
+ }
+
+ /* save token to result array */
+ if (i > 1 && tmp[i-1] == '\0') {
+ r = realloc(ret, (num + 2) * sizeof(char *));
+ if (!r) goto fail;
+ ret = r;
+ ret[num+1] = NULL;
+ ret[num] = strdup(tmp);
+ if (!ret[num]) goto fail;
+ num++;
+ i = 0;
+ }
+ }
+
+ free(tmp);
+ return ret;
+
+fail:
+ free(tmp);
+ free_args(ret);
+ return NULL;
+}
+
+const char **dup_string_list(TALLOC_CTX *memctx, const char **str_list)
+{
+ int i = 0;
+ int j = 0;
+ const char **dup_list;
+
+ if (!str_list) {
+ return NULL;
+ }
+
+ /* Find the size of the list */
+ while (str_list[i]) i++;
+
+ dup_list = talloc_array(memctx, const char *, i+1);
+ if (!dup_list) {
+ return NULL;
+ }
+
+ /* Copy the elements */
+ for (j = 0; j < i; j++) {
+ dup_list[j] = talloc_strdup(dup_list, str_list[j]);
+ if (!dup_list[j]) {
+ talloc_free(dup_list);
+ return NULL;
+ }
+ }
+
+ /* NULL-terminate the list */
+ dup_list[i] = NULL;
+
+ return dup_list;
+}
+
+/* Take two string lists (terminated on a NULL char*)
+ * and return up to three arrays of strings based on
+ * shared ownership.
+ *
+ * Pass NULL to any return type you don't care about
+ */
+errno_t diff_string_lists(TALLOC_CTX *memctx,
+ char **_list1,
+ char **_list2,
+ char ***_list1_only,
+ char ***_list2_only,
+ char ***_both_lists)
+{
+ int error;
+ errno_t ret;
+ int i;
+ int i2 = 0;
+ int i12 = 0;
+ hash_table_t *table;
+ hash_key_t key;
+ hash_value_t value;
+ char **list1 = NULL;
+ char **list2 = NULL;
+ char **list1_only = NULL;
+ char **list2_only = NULL;
+ char **both_lists = NULL;
+ unsigned long count;
+ hash_key_t *keys;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(memctx);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (!_list1) {
+ list1 = talloc_array(tmp_ctx, char *, 1);
+ if (!list1) {
+ talloc_free(tmp_ctx);
+ return ENOMEM;
+ }
+ list1[0] = NULL;
+ }
+ else {
+ list1 = _list1;
+ }
+
+ if (!_list2) {
+ list2 = talloc_array(tmp_ctx, char *, 1);
+ if (!list2) {
+ talloc_free(tmp_ctx);
+ return ENOMEM;
+ }
+ list2[0] = NULL;
+ }
+ else {
+ list2 = _list2;
+ }
+
+ error = hash_create(0, &table, NULL, NULL);
+ if (error != HASH_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return EIO;
+ }
+
+ key.type = HASH_KEY_STRING;
+ value.type = HASH_VALUE_UNDEF;
+
+ /* Add all entries from list 1 into a hash table */
+ i = 0;
+ while (list1[i]) {
+ key.str = talloc_strdup(tmp_ctx, list1[i]);
+ error = hash_enter(table, &key, &value);
+ if (error != HASH_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+ i++;
+ }
+
+ /* Iterate through list 2 and remove matching items */
+ i = 0;
+ while (list2[i]) {
+ key.str = talloc_strdup(tmp_ctx, list2[i]);
+ error = hash_delete(table, &key);
+ if (error == HASH_SUCCESS) {
+ if (_both_lists) {
+ /* String was present in both lists */
+ i12++;
+ both_lists = talloc_realloc(tmp_ctx, both_lists, char *, i12+1);
+ if (!both_lists) {
+ ret = ENOMEM;
+ goto done;
+ }
+ both_lists[i12-1] = talloc_strdup(both_lists, list2[i]);
+ if (!both_lists[i12-1]) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ both_lists[i12] = NULL;
+ }
+ }
+ else if (error == HASH_ERROR_KEY_NOT_FOUND) {
+ if (_list2_only) {
+ /* String was present only in list2 */
+ i2++;
+ list2_only = talloc_realloc(tmp_ctx, list2_only,
+ char *, i2+1);
+ if (!list2_only) {
+ ret = ENOMEM;
+ goto done;
+ }
+ list2_only[i2-1] = talloc_strdup(list2_only, list2[i]);
+ if (!list2_only[i2-1]) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ list2_only[i2] = NULL;
+ }
+ }
+ else {
+ /* An error occurred */
+ ret = EIO;
+ goto done;
+ }
+ i++;
+ }
+
+ /* Get the leftover entries in the hash table */
+ if (_list1_only) {
+ error = hash_keys(table, &count, &keys);
+ if (error != HASH_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ list1_only = talloc_array(tmp_ctx, char *, count+1);
+ if (!list1_only) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < count; i++) {
+ list1_only[i] = talloc_strdup(list1_only, keys[i].str);
+ if (!list1_only[i]) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ list1_only[count] = NULL;
+
+ free(keys);
+
+ *_list1_only = talloc_steal(memctx, list1_only);
+ }
+
+ if (_list2_only) {
+ if (list2_only) {
+ *_list2_only = talloc_steal(memctx, list2_only);
+ }
+ else {
+ *_list2_only = talloc_array(memctx, char *, 1);
+ if (!(*_list2_only)) {
+ ret = ENOMEM;
+ goto done;
+ }
+ *_list2_only[0] = NULL;
+ }
+ }
+
+ if (_both_lists) {
+ if (both_lists) {
+ *_both_lists = talloc_steal(memctx, both_lists);
+ }
+ else {
+ *_both_lists = talloc_array(memctx, char *, 1);
+ if (!(*_both_lists)) {
+ ret = ENOMEM;
+ goto done;
+ }
+ *_both_lists[0] = NULL;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ hash_destroy(table);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static void *hash_talloc(const size_t size, void *pvt)
+{
+ return talloc_size(pvt, size);
+}
+
+static void hash_talloc_free(void *ptr, void *pvt)
+{
+ talloc_free(ptr);
+}
+
+errno_t sss_hash_create_ex(TALLOC_CTX *mem_ctx,
+ unsigned long count,
+ hash_table_t **tbl,
+ unsigned int directory_bits,
+ unsigned int segment_bits,
+ unsigned long min_load_factor,
+ unsigned long max_load_factor,
+ hash_delete_callback *delete_callback,
+ void *delete_private_data)
+{
+ errno_t ret;
+ hash_table_t *table;
+ int hret;
+
+ TALLOC_CTX *internal_ctx;
+ internal_ctx = talloc_new(NULL);
+ if (!internal_ctx) {
+ return ENOMEM;
+ }
+
+ hret = hash_create_ex(count, &table, directory_bits, segment_bits,
+ min_load_factor, max_load_factor,
+ hash_talloc, hash_talloc_free, internal_ctx,
+ delete_callback, delete_private_data);
+ switch (hret) {
+ case HASH_SUCCESS:
+ /* Steal the table pointer onto the mem_ctx,
+ * then make the internal_ctx a child of
+ * table.
+ *
+ * This way, we can clean up the values when
+ * we talloc_free() the table
+ */
+ *tbl = talloc_steal(mem_ctx, table);
+ talloc_steal(table, internal_ctx);
+ return EOK;
+
+ case HASH_ERROR_NO_MEMORY:
+ ret = ENOMEM;
+ break;
+ default:
+ ret = EIO;
+ }
+
+ DEBUG(SSSDBG_FATAL_FAILURE, "Could not create hash table: [%d][%s]\n",
+ hret, hash_error_string(hret));
+
+ talloc_free(internal_ctx);
+ return ret;
+}
+
+errno_t sss_hash_create(TALLOC_CTX *mem_ctx, unsigned long count,
+ hash_table_t **tbl)
+{
+ return sss_hash_create_ex(mem_ctx, count, tbl, 0, 0, 0, 0, NULL, NULL);
+}
+
+char *
+sss_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr)
+{
+ return family == AF_INET6 ? talloc_asprintf(mem_ctx, "[%s]", addr) :
+ talloc_strdup(mem_ctx, addr);
+}
+
+/* out->len includes terminating '\0' */
+void to_sized_string(struct sized_string *out, const char *in)
+{
+ out->str = in;
+ if (out->str) {
+ out->len = strlen(out->str) + 1;
+ } else {
+ out->len = 0;
+ }
+}
+
+/* This function only removes first and last
+ * character if the first character was '['.
+ *
+ * NOTE: This means, that ipv6addr must NOT be followed
+ * by port number.
+ */
+errno_t
+remove_ipv6_brackets(char *ipv6addr)
+{
+ size_t len;
+
+ if (ipv6addr && ipv6addr[0] == '[') {
+ len = strlen(ipv6addr);
+ if (len < 3) {
+ return EINVAL;
+ }
+
+ memmove(ipv6addr, &ipv6addr[1], len - 2);
+ ipv6addr[len -2] = '\0';
+ }
+
+ return EOK;
+}
+
+errno_t add_string_to_list(TALLOC_CTX *mem_ctx, const char *string,
+ char ***list_p)
+{
+ size_t c;
+ char **old_list = NULL;
+ char **new_list = NULL;
+
+ if (string == NULL || list_p == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing string or list.\n");
+ return EINVAL;
+ }
+
+ old_list = *list_p;
+
+ if (old_list == NULL) {
+ /* If the input is a NULL list a new one is created with the new
+ * string and the terminating NULL element. */
+ c = 0;
+ new_list = talloc_array(mem_ctx, char *, 2);
+ } else {
+ for (c = 0; old_list[c] != NULL; c++);
+ /* Allocate one extra space for the new service and one for
+ * the terminating NULL
+ */
+ new_list = talloc_realloc(mem_ctx, old_list, char *, c + 2);
+ }
+
+ if (new_list == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array/talloc_realloc failed.\n");
+ return ENOMEM;
+ }
+
+ new_list[c] = talloc_strdup(new_list, string);
+ if (new_list[c] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ talloc_free(new_list);
+ return ENOMEM;
+ }
+
+ new_list[c + 1] = NULL;
+
+ *list_p = new_list;
+
+ return EOK;
+}
+
+errno_t del_string_from_list(const char *string,
+ char ***list_p, bool case_sensitive)
+{
+ char **list;
+ int(*compare)(const char *s1, const char *s2);
+
+ if (string == NULL || list_p == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing string or list.\n");
+ return EINVAL;
+ }
+
+ if (!string_in_list(string, *list_p, case_sensitive)) {
+ return ENOENT;
+ }
+
+ compare = case_sensitive ? strcmp : strcasecmp;
+ list = *list_p;
+ int matches = 0;
+ int index = 0;
+ while (list[index]) {
+ if (compare(string, list[index]) == 0) {
+ matches++;
+ TALLOC_FREE(list[index]);
+ } else if (matches) {
+ list[index - matches] = list[index];
+ list[index] = NULL;
+ }
+ index++;
+ }
+
+ return EOK;
+}
+
+int domain_to_basedn(TALLOC_CTX *memctx, const char *domain, char **basedn)
+{
+ const char *s;
+ char *dn;
+ char *p;
+ int l;
+
+ if (!domain || !basedn) {
+ return EINVAL;
+ }
+
+ s = domain;
+ dn = talloc_strdup(memctx, "dc=");
+
+ while ((p = strchr(s, '.'))) {
+ l = p - s;
+ dn = talloc_asprintf_append_buffer(dn, "%.*s,dc=", l, s);
+ if (!dn) {
+ return ENOMEM;
+ }
+ s = p + 1;
+ }
+ dn = talloc_strdup_append_buffer(dn, s);
+ if (!dn) {
+ return ENOMEM;
+ }
+
+ for (p=dn; *p; ++p) {
+ *p = tolower(*p);
+ }
+
+ *basedn = dn;
+ return EOK;
+}
+
+bool is_host_in_domain(const char *host, const char *domain)
+{
+ int diff = strlen(host) - strlen(domain);
+
+ if (diff == 0 && strcmp(host, domain) == 0) {
+ return true;
+ }
+
+ if (diff > 0 && strcmp(host + diff, domain) == 0 && host[diff - 1] == '.') {
+ return true;
+ }
+
+ return false;
+}
+
+#ifndef IN_LOOPBACK /* from <linux/in.h> */
+#define IN_LOOPBACK(a) ((((long int) (a)) & 0xff000000) == 0x7f000000)
+#endif
+/* addr is in network order for both IPv4 and IPv6 versions */
+bool check_ipv4_addr(struct in_addr *addr, uint8_t flags)
+{
+ char straddr[INET_ADDRSTRLEN];
+
+ if (inet_ntop(AF_INET, addr, straddr, INET_ADDRSTRLEN) == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "inet_ntop failed, won't log IP addresses\n");
+ snprintf(straddr, INET_ADDRSTRLEN, "unknown");
+ }
+
+ if ((flags & SSS_NO_MULTICAST) && IN_MULTICAST(ntohl(addr->s_addr))) {
+ DEBUG(SSSDBG_FUNC_DATA, "Multicast IPv4 address %s\n", straddr);
+ return false;
+ } else if ((flags & SSS_NO_LOOPBACK)
+ && IN_LOOPBACK(ntohl(addr->s_addr))) {
+ DEBUG(SSSDBG_FUNC_DATA, "Loopback IPv4 address %s\n", straddr);
+ return false;
+ } else if ((flags & SSS_NO_LINKLOCAL)
+ && (addr->s_addr & htonl(0xffff0000)) == htonl(0xa9fe0000)) {
+ /* 169.254.0.0/16 */
+ DEBUG(SSSDBG_FUNC_DATA, "Link-local IPv4 address %s\n", straddr);
+ return false;
+ } else if ((flags & SSS_NO_BROADCAST)
+ && addr->s_addr == htonl(INADDR_BROADCAST)) {
+ DEBUG(SSSDBG_FUNC_DATA, "Broadcast IPv4 address %s\n", straddr);
+ return false;
+ }
+
+ return true;
+}
+
+bool check_ipv6_addr(struct in6_addr *addr, uint8_t flags)
+{
+ char straddr[INET6_ADDRSTRLEN];
+
+ if (inet_ntop(AF_INET6, addr, straddr, INET6_ADDRSTRLEN) == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "inet_ntop failed, won't log IP addresses\n");
+ snprintf(straddr, INET6_ADDRSTRLEN, "unknown");
+ }
+
+ if ((flags & SSS_NO_LINKLOCAL) && IN6_IS_ADDR_LINKLOCAL(addr)) {
+ DEBUG(SSSDBG_FUNC_DATA, "Link local IPv6 address %s\n", straddr);
+ return false;
+ } else if ((flags & SSS_NO_LOOPBACK) && IN6_IS_ADDR_LOOPBACK(addr)) {
+ DEBUG(SSSDBG_FUNC_DATA, "Loopback IPv6 address %s\n", straddr);
+ return false;
+ } else if ((flags & SSS_NO_MULTICAST) && IN6_IS_ADDR_MULTICAST(addr)) {
+ DEBUG(SSSDBG_FUNC_DATA, "Multicast IPv6 address %s\n", straddr);
+ return false;
+ }
+
+ return true;
+}
+
+const char * const * get_known_services(void)
+{
+ static const char *svc[] = {"nss", "pam", "sudo", "autofs",
+ "ssh", "pac", "ifp", NULL };
+
+ return svc;
+}
+
+errno_t add_strings_lists_ex(TALLOC_CTX *mem_ctx,
+ const char **l1, const char **l2,
+ bool copy_strings, bool skip_dups,
+ const char ***_new_list)
+{
+ size_t c;
+ size_t n;
+ size_t l1_count = 0;
+ size_t l2_count = 0;
+ size_t new_count = 0;
+ const char **new;
+ int ret;
+
+ if (l1 != NULL) {
+ for (l1_count = 0; l1[l1_count] != NULL; l1_count++);
+ }
+
+ if (l2 != NULL) {
+ for (l2_count = 0; l2[l2_count] != NULL; l2_count++);
+ }
+
+ new_count = l1_count + l2_count;
+
+ new = talloc_zero_array(mem_ctx, const char *, new_count + 1);
+ if (new == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
+ return ENOMEM;
+ }
+
+ if (copy_strings || skip_dups) {
+ n = 0;
+ for(c = 0; c < l1_count; c++) {
+ if (skip_dups) {
+ if (string_in_list_size(l1[c], new, n, false)) {
+ continue;
+ }
+ }
+ if (copy_strings) {
+ new[n] = talloc_strdup(new, l1[c]);
+ if (new[n] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ new[n] = discard_const(l1[c]);
+ }
+ n++;
+ }
+ for(c = 0; c < l2_count; c++) {
+ if (skip_dups) {
+ if (string_in_list_size(l2[c], new, n, false)) {
+ continue;
+ }
+ }
+ if (copy_strings) {
+ new[n] = talloc_strdup(new, l2[c]);
+ if (new[n] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ new[n] = discard_const(l2[c]);
+ }
+ n++;
+ }
+ } else {
+ if (l1 != NULL) {
+ memcpy(new, l1, sizeof(char *) * l1_count);
+ }
+
+ if (l2 != NULL) {
+ memcpy(&new[l1_count], l2, sizeof(char *) * l2_count);
+ }
+ }
+
+ *_new_list = new;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(new);
+ }
+
+ return ret;
+}
+
+/* Set the nonblocking flag to the fd */
+errno_t sss_fd_nonblocking(int fd)
+{
+ int flags;
+ int ret;
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "F_GETFL failed [%d][%s].\n", ret, strerror(ret));
+ return ret;
+ }
+
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "F_SETFL failed [%d][%s].\n", ret, strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+/* Convert GeneralizedTime (http://en.wikipedia.org/wiki/GeneralizedTime)
+ * to unix time (seconds since epoch). Use UTC time zone.
+ */
+errno_t sss_utc_to_time_t(const char *str, const char *format, time_t *_unix_time)
+{
+ char *end;
+ struct tm tm;
+ size_t len;
+ time_t ut;
+
+ if (str == NULL) {
+ return EINVAL;
+ }
+
+ len = strlen(str);
+ if (str[len-1] != 'Z') {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "%s does not seem to be in UTZ time zone.\n", str);
+ return ERR_TIMESPEC_NOT_SUPPORTED;
+ }
+
+ memset(&tm, 0, sizeof(tm));
+
+ end = strptime(str, format, &tm);
+ /* not all characters from format were matched */
+ if (end == NULL) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "String [%s] failed to match format [%s].\n", str, format);
+ return EINVAL;
+ }
+
+ /* str is 'longer' than format */
+ if (*end != '\0') {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "String [%s] is longer than format [%s].\n", str, format);
+ return EINVAL;
+ }
+
+ ut = mktime(&tm);
+ if (ut == -1) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "mktime failed to convert [%s].\n", str);
+ return EINVAL;
+ }
+
+ tzset();
+ ut -= timezone;
+ *_unix_time = ut;
+ return EOK;
+}
+
+struct tmpfile_watch {
+ const char *filename;
+};
+
+static int unlink_dbg(const char *filename)
+{
+ errno_t ret;
+
+ ret = unlink(filename);
+ if (ret != 0) {
+ ret = errno;
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "File already removed: [%s]\n", filename);
+ return 0;
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot remove temporary file [%s] %d [%s]\n",
+ filename, ret, strerror(ret));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int unique_filename_destructor(void *memptr)
+{
+ struct tmpfile_watch *tw = talloc_get_type(memptr, struct tmpfile_watch);
+
+ if (tw == NULL || tw->filename == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Wrong private pointer\n");
+ return -1;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Unlinking [%s]\n", tw->filename);
+
+ return unlink_dbg(tw->filename);
+}
+
+static struct tmpfile_watch *tmpfile_watch_set(TALLOC_CTX *owner,
+ const char *filename)
+{
+ struct tmpfile_watch *tw = NULL;
+
+ tw = talloc_zero(owner, struct tmpfile_watch);
+ if (tw == NULL) {
+ return NULL;
+ }
+
+ tw->filename = talloc_strdup(tw, filename);
+ if (tw->filename == NULL) {
+ talloc_free(tw);
+ return NULL;
+ }
+
+ talloc_set_destructor((TALLOC_CTX *) tw,
+ unique_filename_destructor);
+ return tw;
+}
+
+int sss_unique_file_ex(TALLOC_CTX *owner,
+ char *path_tmpl,
+ mode_t file_umask,
+ errno_t *_err)
+{
+ size_t tmpl_len;
+ errno_t ret;
+ int fd = -1;
+ mode_t old_umask;
+ struct tmpfile_watch *tw = NULL;
+
+ tmpl_len = strlen(path_tmpl);
+ if (tmpl_len < 6 || strcmp(path_tmpl + (tmpl_len - 6), "XXXXXX") != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Template too short or doesn't end with XXXXXX!\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ old_umask = umask(file_umask);
+ fd = mkstemp(path_tmpl);
+ umask(old_umask);
+ if (fd == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE,
+ "mkstemp(\"%s\") failed [%d]: %s!\n",
+ path_tmpl, ret, strerror(ret));
+ goto done;
+ }
+
+ if (owner != NULL) {
+ tw = tmpfile_watch_set(owner, path_tmpl);
+ if (tw == NULL) {
+ unlink_dbg(path_tmpl);
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+done:
+ if (_err) {
+ *_err = ret;
+ }
+ return fd;
+}
+
+int sss_unique_file(TALLOC_CTX *owner,
+ char *path_tmpl,
+ errno_t *_err)
+{
+ return sss_unique_file_ex(owner, path_tmpl, SSS_DFL_UMASK, _err);
+}
+
+errno_t sss_unique_filename(TALLOC_CTX *owner, char *path_tmpl)
+{
+ int fd;
+ errno_t ret;
+
+ fd = sss_unique_file(owner, path_tmpl, &ret);
+ /* We only care about a unique file name */
+ if (fd >= 0) {
+ close(fd);
+ }
+
+ return ret;
+}
+
+bool is_user_or_group_name(const char *sudo_user_value)
+{
+ if (sudo_user_value == NULL) {
+ return false;
+ }
+
+ /* See man sudoers.ldap for explanation */
+ if (strcmp(sudo_user_value, "ALL") == 0) {
+ return false;
+ }
+
+ switch (sudo_user_value[0]) {
+ case '#': /* user id */
+ case '+': /* netgroup */
+ case '\0': /* empty value */
+ return false;
+ }
+
+ if (sudo_user_value[0] == '%') {
+ switch (sudo_user_value[1]) {
+ case '#': /* POSIX group ID */
+ case ':': /* non-POSIX group */
+ case '\0': /* empty value */
+ return false;
+ }
+ }
+
+ /* Now it's either a username or a groupname */
+ return true;
+}
+
+bool is_socket_activated(void)
+{
+#ifdef HAVE_SYSTEMD
+ return !!socket_activated;
+#else
+ return false;
+#endif
+}
+
+bool is_dbus_activated(void)
+{
+#ifdef HAVE_SYSTEMD
+ return !!dbus_activated;
+#else
+ return false;
+#endif
+}
+
+int sss_rand(void)
+{
+ static bool srand_done = false;
+
+ /* Coverity might complain here: "DC.WEAK_CRYPTO (CWE-327)"
+ * It is safe to ignore as this helper function is *NOT* intended
+ * to be used in security relevant context.
+ */
+ if (!srand_done) {
+ srand(time(NULL) * getpid());
+ srand_done = true;
+ }
+ return rand();
+}
+
+errno_t sss_canonicalize_ip_address(TALLOC_CTX *mem_ctx,
+ const char *address,
+ char **canonical_address)
+{
+ struct addrinfo hints;
+ struct addrinfo *result = NULL;
+ char buf[INET6_ADDRSTRLEN + 1];
+ int ret;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ ret = getaddrinfo(address, NULL, &hints, &result);
+ if (ret != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to canonicalize address [%s]: %s",
+ address, gai_strerror(ret));
+ return EINVAL;
+ }
+
+ ret = getnameinfo(result->ai_addr, result->ai_addrlen, buf, sizeof(buf),
+ NULL, 0, NI_NUMERICHOST);
+ freeaddrinfo(result);
+ if (ret != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to canonicalize address [%s]: %s",
+ address, gai_strerror(ret));
+ return EINVAL;
+ }
+
+ *canonical_address = talloc_strdup(mem_ctx, buf);
+ if (*canonical_address == NULL) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+/* According to the https://tools.ietf.org/html/rfc2181#section-11.
+ * practically no restrictions are imposed to a domain name per se.
+ *
+ * But since SSSD uses this name as a part of log file name,
+ * it is still required to avoid '/' as a safety measure.
+ */
+bool is_valid_domain_name(const char *domain)
+{
+ if (strchr(domain, '/') != NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Forbidden symbol '/' in the domain name '%s'\n", domain);
+ return false;
+ }
+
+ return true;
+}
+
+errno_t sss_getenv(TALLOC_CTX *mem_ctx,
+ const char *variable_name,
+ const char *default_value,
+ char **_value)
+{
+ char *value = getenv(variable_name);
+ if (value == NULL && default_value == NULL) {
+ return ENOENT;
+ }
+
+ *_value = talloc_strdup(mem_ctx, value != NULL ? value : default_value);
+ if (*_value == NULL) {
+ return ENOMEM;
+ }
+
+ return value != NULL ? EOK : ENOENT;
+}
diff --git a/src/util/util.h b/src/util/util.h
new file mode 100644
index 0000000..f3a4926
--- /dev/null
+++ b/src/util/util.h
@@ -0,0 +1,902 @@
+/*
+ Authors:
+ Simo Sorce <ssorce@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 __SSSD_UTIL_H__
+#define __SSSD_UTIL_H__
+
+#include "config.h"
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <libintl.h>
+#include <locale.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <limits.h>
+#include <sys/un.h>
+
+#include <talloc.h>
+#include <tevent.h>
+#include <ldb.h>
+#include <dhash.h>
+
+#include "confdb/confdb.h"
+#include "shared/io.h"
+#include "shared/safealign.h"
+#include "util/atomic_io.h"
+#include "util/util_errors.h"
+#include "util/sss_format.h"
+#include "util/sss_regexp.h"
+#include "util/debug.h"
+
+/* name of the monitor server instance */
+#define SSSD_MONITOR_NAME "sssd"
+#define SSSD_PIDFILE PID_PATH"/"SSSD_MONITOR_NAME".pid"
+#define MAX_PID_LENGTH 10
+
+#define _(STRING) gettext (STRING)
+
+#define ENUM_INDICATOR "*"
+
+/*
+ * CLEAR_MC_FLAG is a flag file used to notify NSS responder
+ * that SIGHUP signal it received was triggered by sss_cache
+ * as a call for memory cache clearing. During the procedure
+ * this file is deleted by NSS responder to notify back
+ * sss_cache that memory cache clearing was completed.
+ */
+#define CLEAR_MC_FLAG "clear_mc_flag"
+
+/* Default secure umask */
+#define SSS_DFL_UMASK 0177
+
+/* Secure mask with executable bit */
+#define SSS_DFL_X_UMASK 0077
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef ALLPERMS
+#define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)/* 07777 */
+#endif
+
+#define SSSD_MAIN_OPTS SSSD_DEBUG_OPTS
+
+#define SSSD_SERVER_OPTS(uid, gid) \
+ {"uid", 0, POPT_ARG_INT, &uid, 0, \
+ _("The user ID to run the server as"), NULL}, \
+ {"gid", 0, POPT_ARG_INT, &gid, 0, \
+ _("The group ID to run the server as"), NULL},
+
+extern int socket_activated;
+extern int dbus_activated;
+
+#ifdef HAVE_SYSTEMD
+#define SSSD_RESPONDER_OPTS \
+ { "socket-activated", 0, POPT_ARG_NONE, &socket_activated, 0, \
+ _("Informs that the responder has been socket-activated"), NULL }, \
+ { "dbus-activated", 0, POPT_ARG_NONE, &dbus_activated, 0, \
+ _("Informs that the responder has been dbus-activated"), NULL },
+#else
+#define SSSD_RESPONDER_OPTS
+#endif
+
+#define FLAGS_NONE 0x0000
+#define FLAGS_DAEMON 0x0001
+#define FLAGS_INTERACTIVE 0x0002
+#define FLAGS_PID_FILE 0x0004
+#define FLAGS_GEN_CONF 0x0008
+#define FLAGS_NO_WATCHDOG 0x0010
+
+enum sssd_exit_status {
+ CHILD_TIMEOUT_EXIT_CODE = 7,
+ CA_DB_NOT_FOUND_EXIT_CODE = 50,
+ SSS_WATCHDOG_EXIT_CODE = 70 /* to match EX_SOFTWARE in sysexits.h */
+};
+
+#define PIPE_INIT { -1, -1 }
+
+#define PIPE_FD_CLOSE(fd) do { \
+ if (fd != -1) { \
+ close(fd); \
+ fd = -1; \
+ } \
+} while(0);
+
+#define PIPE_CLOSE(p) do { \
+ PIPE_FD_CLOSE(p[0]); \
+ PIPE_FD_CLOSE(p[1]); \
+} while(0);
+
+#ifndef talloc_zfree
+#define talloc_zfree(ptr) do { talloc_free(discard_const(ptr)); ptr = NULL; } while(0)
+#endif
+
+#ifndef discard_const_p
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr)))
+#else
+# define discard_const_p(type, ptr) ((type *)(ptr))
+#endif
+#endif
+
+#define TEVENT_REQ_RETURN_ON_ERROR(req) do { \
+ enum tevent_req_state TRROEstate; \
+ uint64_t TRROEuint64; \
+ errno_t TRROEerr; \
+ \
+ if (tevent_req_is_error(req, &TRROEstate, &TRROEuint64)) { \
+ TRROEerr = (errno_t)TRROEuint64; \
+ switch (TRROEstate) { \
+ case TEVENT_REQ_USER_ERROR: \
+ if (TRROEerr == 0) { \
+ return ERR_INTERNAL; \
+ } \
+ return TRROEerr; \
+ case TEVENT_REQ_TIMED_OUT: \
+ return ETIMEDOUT; \
+ default: \
+ return ERR_INTERNAL; \
+ } \
+ } \
+} while (0)
+
+#define OUT_OF_ID_RANGE(id, min, max) \
+ (id == 0 || (min && (id < min)) || (max && (id > max)))
+
+#include "util/dlinklist.h"
+
+/* From sss_log.c */
+#define SSS_LOG_EMERG 0 /* system is unusable */
+#define SSS_LOG_ALERT 1 /* action must be taken immediately */
+#define SSS_LOG_CRIT 2 /* critical conditions */
+#define SSS_LOG_ERR 3 /* error conditions */
+#define SSS_LOG_WARNING 4 /* warning conditions */
+#define SSS_LOG_NOTICE 5 /* normal but significant condition */
+#define SSS_LOG_INFO 6 /* informational */
+#define SSS_LOG_DEBUG 7 /* debug-level messages */
+
+void sss_log(int priority, const char *format, ...) SSS_ATTRIBUTE_PRINTF(2, 3);
+void sss_log_ext(int priority, int facility, const char *format, ...) SSS_ATTRIBUTE_PRINTF(3, 4);
+
+/* from server.c */
+#define DEBUG_CHAIN_ID_FMT_RID "[RID#%"PRIu64"] %s"
+#define DEBUG_CHAIN_ID_FMT_CID "[CID#%"PRIu64"] %s"
+
+struct main_context {
+ struct tevent_context *event_ctx;
+ struct confdb_ctx *confdb_ctx;
+ pid_t parent_pid;
+};
+
+struct sbus_request;
+
+errno_t server_common_rotate_logs(struct confdb_ctx *confdb,
+ const char *conf_entry);
+errno_t generic_get_debug_level(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ void *pvt_data,
+ uint32_t *_debug_level);
+errno_t generic_set_debug_level(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ void *pvt_data,
+ uint32_t new_debug_level);
+int die_if_parent_died(void);
+int check_pidfile(const char *file);
+int pidfile(const char *file);
+int server_setup(const char *name, bool is_responder,
+ int flags,
+ uid_t uid, gid_t gid,
+ const char *conf_entry,
+ struct main_context **main_ctx,
+ bool allow_sss_loop);
+void server_loop(struct main_context *main_ctx);
+void orderly_shutdown(int status);
+
+/* from signal.c */
+void BlockSignals(bool block, int signum);
+void (*CatchSignal(int signum,void (*handler)(int )))(int);
+
+/* from memory.c */
+typedef int (void_destructor_fn_t)(void *);
+/* sssd_mem_attach
+ * This function will take a non-talloc pointer and "attach" it to a talloc
+ * memory context. It will accept a destructor for the original pointer
+ * so that when the parent memory context is freed, the non-talloc
+ * pointer will also be freed properly.
+ * Returns EOK in case of success.
+ */
+int sss_mem_attach(TALLOC_CTX *mem_ctx, void *ptr, void_destructor_fn_t *fn);
+
+/* sss_erase_talloc_mem_securely() function always returns 0 as an int value
+ * to make it possible to use it as talloc destructor.
+ */
+int sss_erase_talloc_mem_securely(void *p);
+void sss_erase_mem_securely(void *p, size_t size);
+
+/* from usertools.c */
+char *get_uppercase_realm(TALLOC_CTX *memctx, const char *name);
+
+struct sss_names_ctx {
+ char *re_pattern;
+ char *fq_fmt;
+
+ sss_regexp_t *re;
+};
+
+#define SSS_DEFAULT_RE "^((?P<name>.+)@(?P<domain>[^@]+)|(?P<name>[^@]+))$"
+
+#define SSS_IPA_AD_DEFAULT_RE "^(((?P<domain>[^\\\\]+)\\\\(?P<name>.+))|" \
+ "((?P<name>.+)@(?P<domain>[^@]+))|" \
+ "((?P<name>[^@\\\\]+)))$"
+
+/* initialize sss_names_ctx directly from arguments */
+int sss_names_init_from_args(TALLOC_CTX *mem_ctx,
+ const char *re_pattern,
+ const char *fq_fmt,
+ struct sss_names_ctx **out);
+
+/* initialize sss_names_ctx from domain configuration */
+int sss_names_init(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *cdb,
+ const char *domain,
+ struct sss_names_ctx **out);
+
+int sss_ad_default_names_ctx(TALLOC_CTX *mem_ctx,
+ struct sss_names_ctx **_out);
+
+int sss_parse_name(TALLOC_CTX *memctx,
+ struct sss_names_ctx *snctx,
+ const char *orig, char **_domain, char **_name);
+
+int sss_parse_name_for_domains(TALLOC_CTX *memctx,
+ struct sss_domain_info *domains,
+ const char *default_domain,
+ const char *orig, char **domain, char **name);
+
+char *
+sss_get_cased_name(TALLOC_CTX *mem_ctx, const char *orig_name,
+ bool case_sensitive);
+
+errno_t
+sss_get_cased_name_list(TALLOC_CTX *mem_ctx, const char * const *orig,
+ bool case_sensitive, const char ***_cased);
+
+/* Return fully-qualified name according to the fq_fmt. The name is allocated using
+ * talloc on top of mem_ctx
+ */
+char *
+sss_tc_fqname(TALLOC_CTX *mem_ctx, struct sss_names_ctx *nctx,
+ struct sss_domain_info *domain, const char *name);
+
+/* Return fully-qualified name according to the fq_fmt. The name is allocated using
+ * talloc on top of mem_ctx. In contrast to sss_tc_fqname() sss_tc_fqname2()
+ * expects the domain and flat domain name as separate arguments.
+ */
+char *
+sss_tc_fqname2(TALLOC_CTX *mem_ctx, struct sss_names_ctx *nctx,
+ const char *dom_name, const char *flat_dom_name,
+ const char *name);
+
+/* Return fully-qualified name formatted according to the fq_fmt. The buffer in "str" is
+ * "size" bytes long. Returns the number of bytes written on success or a negative
+ * value of failure.
+ *
+ * Pass a zero size to calculate the length that would be needed by the fully-qualified
+ * name.
+ */
+int
+sss_fqname(char *str, size_t size, struct sss_names_ctx *nctx,
+ struct sss_domain_info *domain, const char *name);
+
+
+/* Accepts fqname in the format shortname@domname only. */
+errno_t sss_parse_internal_fqname(TALLOC_CTX *mem_ctx,
+ const char *fqname,
+ char **_shortname,
+ char **_dom_name);
+
+/* Creates internal fqname in format shortname@domname.
+ * The domain portion is lowercased. */
+char *sss_create_internal_fqname(TALLOC_CTX *mem_ctx,
+ const char *shortname,
+ const char *dom_name);
+
+/* Creates internal fqnames list in format shortname@domname.
+ * The domain portion is lowercased. */
+char **sss_create_internal_fqname_list(TALLOC_CTX *mem_ctx,
+ const char * const *shortname_list,
+ const char *dom_name);
+
+/* Turn fqname into cased shortname with replaced space. */
+char *sss_output_name(TALLOC_CTX *mem_ctx,
+ const char *fqname,
+ bool case_sensitive,
+ const char replace_space);
+
+int sss_output_fqname(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ char override_space,
+ char **_output_name);
+
+const char *sss_get_name_from_msg(struct sss_domain_info *domain,
+ struct ldb_message *msg);
+
+/* from backup-file.c */
+int backup_file(const char *src, int dbglvl);
+
+/* check_file()
+ * Verify that a file has certain permissions and/or is of a certain
+ * file type. This function can be used to determine if a file is a
+ * symlink.
+ * Warning: use of this function implies a potential race condition
+ * Opening a file before or after checking it does NOT guarantee that
+ * it is still the same file. Additional checks should be performed
+ * on the caller_stat_buf to ensure that it has the same device and
+ * inode to minimize impact. Permission changes may have occurred,
+ * however.
+ */
+errno_t check_file(const char *filename,
+ uid_t uid, gid_t gid, mode_t mode, mode_t mask,
+ struct stat *caller_stat_buf, bool follow_symlink);
+
+/* from util.c */
+#define SSS_NO_LINKLOCAL 0x01
+#define SSS_NO_LOOPBACK 0x02
+#define SSS_NO_MULTICAST 0x04
+#define SSS_NO_BROADCAST 0x08
+
+#define SSS_NO_SPECIAL \
+ (SSS_NO_LINKLOCAL|SSS_NO_LOOPBACK|SSS_NO_MULTICAST|SSS_NO_BROADCAST)
+
+/* These two functions accept addr in network order */
+bool check_ipv4_addr(struct in_addr *addr, uint8_t check);
+bool check_ipv6_addr(struct in6_addr *addr, uint8_t check);
+
+/* Returns the canonical form of an IPv4 or IPv6 address */
+errno_t sss_canonicalize_ip_address(TALLOC_CTX *mem_ctx,
+ const char *address,
+ char **canonical_address);
+
+const char * const * get_known_services(void);
+
+errno_t sss_user_by_name_or_uid(const char *input, uid_t *_uid, gid_t *_gid);
+void sss_sssd_user_uid_and_gid(uid_t *_uid, gid_t *_gid);
+void sss_set_sssd_user_eid(void);
+void sss_restore_sssd_user_eid(void);
+
+int split_on_separator(TALLOC_CTX *mem_ctx, const char *str,
+ const char sep, bool trim, bool skip_empty,
+ char ***_list, int *size);
+
+char **parse_args(const char *str);
+
+errno_t sss_hash_create(TALLOC_CTX *mem_ctx,
+ unsigned long count,
+ hash_table_t **tbl);
+
+errno_t sss_hash_create_ex(TALLOC_CTX *mem_ctx,
+ unsigned long count,
+ hash_table_t **tbl,
+ unsigned int directory_bits,
+ unsigned int segment_bits,
+ unsigned long min_load_factor,
+ unsigned long max_load_factor,
+ hash_delete_callback *delete_callback,
+ void *delete_private_data);
+
+/* Returns true if sudoUser value is a username or a groupname */
+bool is_user_or_group_name(const char *sudo_user_value);
+
+/* Returns true if the responder has been socket-activated */
+bool is_socket_activated(void);
+
+/* Returns true if the responder has been dbus-activated */
+bool is_dbus_activated(void);
+
+/**
+ * @brief Add two list of strings
+ *
+ * Create a new NULL-terminated list of strings by adding two lists together.
+ *
+ * @param[in] mem_ctx Talloc memory context for the new list.
+ * @param[in] l1 First NULL-terminated list of strings.
+ * @param[in] l2 Second NULL-terminated list of strings.
+ * @param[in] copy_strings If set to 'true' the list items will be copied
+ * otherwise only the pointers to the items are
+ * copied.
+ * @param[in] skip_dups Whether the function should skip duplicate values.
+ * @param[out] new_list New NULL-terminated list of strings. Must be freed
+ * with talloc_free() by the caller. If copy_strings
+ * is 'true' the new elements will be freed as well.
+ */
+errno_t add_strings_lists_ex(TALLOC_CTX *mem_ctx,
+ const char **l1, const char **l2,
+ bool copy_strings, bool skip_dups,
+ const char ***_new_list);
+
+/**
+ * @overload errno_t add_strings_lists_ex(TALLOC_CTX *mem_ctx,
+ * const char **l1, const char **l2,
+ * bool copy_strings, bool skip_dups,
+ * const char ***_new_list)
+ */
+static inline errno_t add_strings_lists(TALLOC_CTX *mem_ctx,
+ const char **l1, const char **l2,
+ bool copy_strings,
+ const char ***_new_list)
+{
+ return add_strings_lists_ex(mem_ctx, l1, l2, copy_strings, false, _new_list);
+}
+
+
+/**
+ * @brief set file descriptor as nonblocking
+ *
+ * Set the O_NONBLOCK flag for the input fd
+ *
+ * @param[in] fd The file descriptor to set as nonblocking
+ *
+ * @return EOK on success, errno code otherwise
+ */
+errno_t sss_fd_nonblocking(int fd);
+
+/* Copy a NULL-terminated string list
+ * Returns NULL on out of memory error or invalid input
+ */
+const char **dup_string_list(TALLOC_CTX *memctx, const char **str_list);
+
+/* Take two string lists (terminated on a NULL char*)
+ * and return up to three arrays of strings based on
+ * shared ownership.
+ *
+ * Pass NULL to any return type you don't care about
+ */
+errno_t diff_string_lists(TALLOC_CTX *memctx,
+ char **string1,
+ char **string2,
+ char ***string1_only,
+ char ***string2_only,
+ char ***both_strings);
+
+/* Sanitize an input string (e.g. a username) for use in
+ * an LDAP/LDB filter
+ * Returns a newly-constructed string attached to mem_ctx
+ * It will fail only on an out of memory condition, where it
+ * will return ENOMEM.
+ */
+errno_t sss_filter_sanitize(TALLOC_CTX *mem_ctx,
+ const char *input,
+ char **sanitized);
+
+errno_t sss_filter_sanitize_ex(TALLOC_CTX *mem_ctx,
+ const char *input,
+ char **sanitized,
+ const char *ignore);
+
+errno_t sss_filter_sanitize_for_dom(TALLOC_CTX *mem_ctx,
+ const char *input,
+ struct sss_domain_info *dom,
+ char **sanitized,
+ char **lc_sanitized);
+
+/* Sanitize an input string (e.g. a DN) for use in
+ * an LDAP/LDB filter
+ *
+ * It is basically the same as sss_filter_sanitize(_ex),
+ * just extra spaces inside DN around '=' and ',' are removed
+ * before sanitizing other characters . According the documentation
+ * spaces in DN are allowed and some ldap servers can return them
+ * in isMemberOf or member attributes.
+ *
+ * (dc = my example, dc = com => dc=my\20example,dc=com)
+ *
+ * Returns a newly-constructed string attached to mem_ctx
+ * It will fail only on an out of memory condition, where it
+ * will return ENOMEM.
+ *
+ */
+errno_t sss_filter_sanitize_dn(TALLOC_CTX *mem_ctx,
+ const char *input,
+ char **sanitized);
+
+char *
+sss_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr);
+
+/* This function only removes first and last
+ * character if the first character was '['.
+ *
+ * NOTE: This means, that ipv6addr must NOT be followed
+ * by port number.
+ */
+errno_t
+remove_ipv6_brackets(char *ipv6addr);
+
+
+errno_t add_string_to_list(TALLOC_CTX *mem_ctx, const char *string,
+ char ***list_p);
+
+errno_t del_string_from_list(const char *string,
+ char ***list_p, bool case_sensitive);
+
+bool string_in_list(const char *string, char **list, bool case_sensitive);
+
+bool string_in_list_size(const char *string, const char **list, size_t size,
+ bool case_sensitive);
+
+int domain_to_basedn(TALLOC_CTX *memctx, const char *domain, char **basedn);
+
+bool is_host_in_domain(const char *host, const char *domain);
+
+bool is_valid_domain_name(const char *domain);
+
+/* This is simple wrapper around libc rand() intended to avoid calling srand()
+ * explicitly, thus *not* suitable to be used in security relevant context.
+ * If CS properties are desired (security relevant functionality/FIPS/etc) then
+ * use sss_crypto.h:sss_generate_csprng_buffer() instead!
+ */
+int sss_rand(void);
+
+/* from nscd.c */
+errno_t sss_nscd_parse_conf(const char *conf_path);
+
+/* from sss_tc_utf8.c */
+char *
+sss_tc_utf8_str_tolower(TALLOC_CTX *mem_ctx, const char *s);
+uint8_t *
+sss_tc_utf8_tolower(TALLOC_CTX *mem_ctx, const uint8_t *s, size_t len, size_t *_nlen);
+/* from sss_utf8.c */
+bool sss_string_equal(bool cs, const char *s1, const char *s2);
+
+/* len includes terminating '\0' */
+struct sized_string {
+ const char *str;
+ size_t len;
+};
+
+void to_sized_string(struct sized_string *out, const char *in);
+
+/* from domain_info.c */
+struct sss_domain_info *get_domains_head(struct sss_domain_info *domain);
+
+#define SSS_GND_DESCEND 0x01
+#define SSS_GND_INCLUDE_DISABLED 0x02
+/* Descend to sub-domains of current domain but do not go to next parent */
+#define SSS_GND_SUBDOMAINS 0x04
+#define SSS_GND_ALL_DOMAINS (SSS_GND_DESCEND | SSS_GND_INCLUDE_DISABLED)
+#define SSS_GND_ALL_SUBDOMAINS (SSS_GND_SUBDOMAINS | SSS_GND_INCLUDE_DISABLED)
+
+struct sss_domain_info *get_next_domain(struct sss_domain_info *domain,
+ uint32_t gnd_flags);
+struct sss_domain_info *find_domain_by_name(struct sss_domain_info *domain,
+ const char *name,
+ bool match_any);
+struct sss_domain_info *find_domain_by_name_ex(struct sss_domain_info *domain,
+ const char *name,
+ bool match_any,
+ uint32_t gnd_flags);
+struct sss_domain_info *find_domain_by_sid(struct sss_domain_info *domain,
+ const char *sid);
+enum sss_domain_state sss_domain_get_state(struct sss_domain_info *dom);
+void sss_domain_set_state(struct sss_domain_info *dom,
+ enum sss_domain_state state);
+#ifdef BUILD_FILES_PROVIDER
+bool sss_domain_fallback_to_nss(struct sss_domain_info *dom);
+#endif
+bool sss_domain_is_forest_root(struct sss_domain_info *dom);
+const char *sss_domain_type_str(struct sss_domain_info *dom);
+
+struct sss_domain_info*
+sss_get_domain_by_sid_ldap_fallback(struct sss_domain_info *domain,
+ const char* sid);
+
+struct sss_domain_info *
+find_domain_by_object_name(struct sss_domain_info *domain,
+ const char *object_name);
+
+struct sss_domain_info *
+find_domain_by_object_name_ex(struct sss_domain_info *domain,
+ const char *object_name, bool strict,
+ uint32_t gnd_flags);
+
+bool subdomain_enumerates(struct sss_domain_info *parent,
+ const char *sd_name);
+
+char *subdomain_create_conf_path_from_str(TALLOC_CTX *mem_ctx,
+ const char *parent_name,
+ const char *subdom_name);
+char *subdomain_create_conf_path(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *subdomain);
+
+errno_t sssd_domain_init(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *cdb,
+ const char *domain_name,
+ const char *db_path,
+ struct sss_domain_info **_domain);
+
+void sss_domain_info_set_output_fqnames(struct sss_domain_info *domain,
+ bool output_fqname);
+
+bool sss_domain_info_get_output_fqnames(struct sss_domain_info *domain);
+
+bool sss_domain_is_mpg(struct sss_domain_info *domain);
+
+bool sss_domain_is_hybrid(struct sss_domain_info *domain);
+
+enum sss_domain_mpg_mode get_domain_mpg_mode(struct sss_domain_info *domain);
+const char *str_domain_mpg_mode(enum sss_domain_mpg_mode mpg_mode);
+enum sss_domain_mpg_mode str_to_domain_mpg_mode(const char *str_mpg_mode);
+
+#define IS_SUBDOMAIN(dom) ((dom)->parent != NULL)
+
+#define DOM_HAS_VIEWS(dom) ((dom)->has_views)
+
+/* the directory domain - realm mappings and other krb5 config snippers are
+ * written to */
+#define KRB5_MAPPING_DIR PUBCONF_PATH"/krb5.include.d"
+
+errno_t sss_get_domain_mappings_content(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ char **content);
+
+errno_t sss_write_domain_mappings(struct sss_domain_info *domain);
+
+char *get_hidden_tmp_path(TALLOC_CTX *mem_ctx, const char *path);
+
+errno_t sss_write_krb5_conf_snippet(const char *path, bool canonicalize,
+ bool udp_limit);
+
+errno_t get_dom_names(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *start_dom,
+ char ***_dom_names,
+ int *_dom_names_count);
+
+__attribute__((always_inline))
+static inline bool is_domain_provider(struct sss_domain_info *domain,
+ const char *provider)
+{
+ return domain != NULL &&
+ domain->provider != NULL &&
+ strcasecmp(domain->provider, provider) == 0;
+}
+
+/* Returns true if the provider used for the passed domain is the "files"
+ * one. Otherwise returns false. */
+__attribute__((always_inline))
+static inline bool is_files_provider(struct sss_domain_info *domain)
+{
+#ifdef BUILD_FILES_PROVIDER
+ return domain != NULL &&
+ domain->provider != NULL &&
+ strcasecmp(domain->provider, "files") == 0;
+#else
+ return false;
+#endif
+}
+
+/* from util_lock.c */
+errno_t sss_br_lock_file(int fd, size_t start, size_t len,
+ int num_tries, useconds_t wait);
+
+#ifdef HAVE_PAC_RESPONDER
+#define BUILD_WITH_PAC_RESPONDER true
+#else
+#define BUILD_WITH_PAC_RESPONDER false
+#endif
+
+/* from well_known_sids.c */
+errno_t well_known_sid_to_name(const char *sid, const char **dom,
+ const char **name);
+
+errno_t name_to_well_known_sid(const char *dom, const char *name,
+ const char **sid);
+
+/* from string_utils.c */
+char *sss_replace_char(TALLOC_CTX *mem_ctx,
+ const char *in,
+ const char match,
+ const char sub);
+
+char * sss_replace_space(TALLOC_CTX *mem_ctx,
+ const char *orig_name,
+ const char replace_char);
+char * sss_reverse_replace_space(TALLOC_CTX *mem_ctx,
+ const char *orig_name,
+ const char replace_char);
+
+#define GUID_BIN_LENGTH 16
+/* 16 2-digit hex values + 4 dashes + terminating 0 */
+#define GUID_STR_BUF_SIZE (2 * GUID_BIN_LENGTH + 4 + 1)
+
+errno_t guid_blob_to_string_buf(const uint8_t *blob, char *str_buf,
+ size_t buf_size);
+
+const char *get_last_x_chars(const char *str, size_t x);
+
+char **concatenate_string_array(TALLOC_CTX *mem_ctx,
+ char **arr1, size_t len1,
+ char **arr2, size_t len2);
+
+errno_t mod_defaults_list(TALLOC_CTX *mem_ctx, const char **defaults_list,
+ char **mod_list, char ***_list);
+
+/* from become_user.c */
+errno_t become_user(uid_t uid, gid_t gid);
+struct sss_creds;
+errno_t switch_creds(TALLOC_CTX *mem_ctx,
+ uid_t uid, gid_t gid,
+ int num_gids, gid_t *gids,
+ struct sss_creds **saved_creds);
+errno_t restore_creds(struct sss_creds *saved_creds);
+
+/* from sss_semanage.c */
+/* Please note that libsemange relies on files and directories created with
+ * certain permissions. Therefore the caller should make sure the umask is
+ * not too restricted (especially when called from the daemon code).
+ */
+int sss_set_seuser(const char *login_name, const char *seuser_name,
+ const char *mlsrange);
+int sss_del_seuser(const char *login_name);
+int sss_get_seuser(const char *linuxuser,
+ char **selinuxuser,
+ char **level);
+int sss_seuser_exists(const char *linuxuser);
+
+/* convert time from generalized form to unix time */
+errno_t sss_utc_to_time_t(const char *str, const char *format, time_t *unix_time);
+
+/* Creates a unique file using mkstemp with provided umask. The template
+ * must end with XXXXXX. Returns the fd, sets _err to an errno value on error.
+ *
+ * Prefer using sss_unique_file() as it uses a secure umask internally.
+ */
+int sss_unique_file_ex(TALLOC_CTX *mem_ctx,
+ char *path_tmpl,
+ mode_t file_umask,
+ errno_t *_err);
+int sss_unique_file(TALLOC_CTX *owner,
+ char *path_tmpl,
+ errno_t *_err);
+
+/* Creates a unique filename using mkstemp with secure umask. The template
+ * must end with XXXXXX
+ *
+ * path_tmpl must be a talloc context. Destructor would be set on the filename
+ * so that it's guaranteed the file is removed.
+ */
+int sss_unique_filename(TALLOC_CTX *owner, char *path_tmpl);
+
+/* from util_watchdog.c */
+int setup_watchdog(struct tevent_context *ev, int interval);
+void teardown_watchdog(void);
+int get_watchdog_ticks(void);
+
+/* The arm_watchdog() and disarm_watchdog() calls will disable and re-enable
+ * the watchdog reset, respectively. This means that after arm_watchdog() is
+ * called the watchdog will not be resetted anymore and it will kill the
+ * process if disarm_watchdog() wasn't called before.
+ * Those calls should only be used when there is no other way to handle
+ * waiting request and recover into a stable state.
+ * Those calls cannot be nested, i.e. after calling arm_watchdog() it should
+ * not be called a second time in a different request because then
+ * disarm_watchdog() will disable the watchdog coverage for both. */
+void arm_watchdog(void);
+void disarm_watchdog(void);
+
+/* from files.c */
+int sss_remove_tree(const char *root);
+int sss_remove_subtree(const char *root);
+
+int sss_copy_tree(const char *src_root,
+ const char *dst_root,
+ mode_t mode_root,
+ uid_t uid, gid_t gid);
+
+int sss_copy_file_secure(const char *src,
+ const char *dest,
+ mode_t mode,
+ uid_t uid, gid_t gid,
+ bool force);
+
+int sss_create_dir(const char *parent_dir_path,
+ const char *dir_name,
+ mode_t mode,
+ uid_t uid, gid_t gid);
+
+/* from selinux.c */
+int selinux_file_context(const char *dst_name);
+int reset_selinux_file_context(void);
+
+/* from cert_derb64_to_ldap_filter.c */
+struct sss_certmap_ctx;
+errno_t sss_cert_derb64_to_ldap_filter(TALLOC_CTX *mem_ctx, const char *derb64,
+ const char *attr_name,
+ struct sss_certmap_ctx *certmap_ctx,
+ struct sss_domain_info *dom,
+ char **ldap_filter);
+
+
+/* from util_preauth.c */
+errno_t create_preauth_indicator(void);
+
+#ifdef SSSD_LIBEXEC_PATH
+#define P11_CHILD_LOG_FILE "p11_child"
+#define P11_CHILD_PATH SSSD_LIBEXEC_PATH"/p11_child"
+#define P11_CHILD_TIMEOUT_DEFAULT 10
+#define P11_WAIT_FOR_CARD_TIMEOUT_DEFAULT 60
+#define PASSKEY_CHILD_TIMEOUT_DEFAULT 15
+#define PASSKEY_CHILD_LOG_FILE "passkey_child"
+#define PASSKEY_CHILD_PATH SSSD_LIBEXEC_PATH"/passkey_child"
+
+#endif /* SSSD_LIBEXEC_PATH */
+
+#ifndef N_ELEMENTS
+#define N_ELEMENTS(arr) (sizeof(arr) / sizeof(arr[0]))
+#endif
+
+/* If variable is not set, it stores a copy of default_value (if not NULL)
+ * in _value but returns ENOENT so the information is propagated to the caller.
+ */
+errno_t sss_getenv(TALLOC_CTX *mem_ctx,
+ const char *variable_name,
+ const char *default_value,
+ char **_value);
+
+/* from sss_time.c */
+uint64_t get_start_time(void);
+
+const char *sss_format_time(uint64_t us);
+uint64_t get_spend_time_us(uint64_t st);
+
+/* from pac_utils.h */
+#define CHECK_PAC_NO_CHECK_STR "no_check"
+#define CHECK_PAC_PRESENT_STR "pac_present"
+#define CHECK_PAC_PRESENT (1 << 0)
+#define CHECK_PAC_CHECK_UPN_STR "check_upn"
+#define CHECK_PAC_CHECK_UPN (1 << 1)
+#define CHECK_PAC_UPN_DNS_INFO_PRESENT_STR "upn_dns_info_present"
+#define CHECK_PAC_UPN_DNS_INFO_PRESENT (1 << 2)
+#define CHECK_PAC_CHECK_UPN_DNS_INFO_EX_STR "check_upn_dns_info_ex"
+#define CHECK_PAC_CHECK_UPN_DNS_INFO_EX (1 << 3)
+#define CHECK_PAC_UPN_DNS_INFO_EX_PRESENT_STR "upn_dns_info_ex_present"
+#define CHECK_PAC_UPN_DNS_INFO_EX_PRESENT (1 << 4)
+#define CHECK_PAC_CHECK_UPN_ALLOW_MISSING_STR "check_upn_allow_missing"
+#define CHECK_PAC_CHECK_UPN_ALLOW_MISSING (1 << 5)
+
+errno_t get_pac_check_config(struct confdb_ctx *cdb, uint32_t *pac_check_opts);
+
+static inline struct timeval sss_tevent_timeval_current_ofs_time_t(time_t secs)
+{
+ uint32_t secs32 = (secs > UINT_MAX ? UINT_MAX : secs);
+ return tevent_timeval_current_ofs(secs32, 0);
+}
+#endif /* __SSSD_UTIL_H__ */
diff --git a/src/util/util_creds.h b/src/util/util_creds.h
new file mode 100644
index 0000000..994d0a9
--- /dev/null
+++ b/src/util/util_creds.h
@@ -0,0 +1,84 @@
+/*
+ Authors:
+ Simo Sorce <simo@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 __SSSD_UTIL_CREDS_H__
+#define __SSSD_UTIL_CREDS_H__
+
+/* following code comes from gss-proxy's gp_selinux.h file */
+#ifdef HAVE_SELINUX
+
+#include <selinux/context.h>
+typedef context_t SELINUX_CTX;
+#include <selinux/selinux.h>
+typedef char * SEC_CTX;
+
+#define SELINUX_context_new context_new
+#define SELINUX_context_free context_free
+#define SELINUX_context_str context_str
+#define SELINUX_context_type_get context_type_get
+#define SELINUX_context_user_get context_user_get
+#define SELINUX_context_role_get context_role_get
+#define SELINUX_context_range_get context_range_get
+#define SELINUX_getpeercon getpeercon
+#define SELINUX_freecon freecon
+
+#else /* not HAVE_SELINUX */
+
+typedef void * SELINUX_CTX;
+typedef void * SEC_CTX;
+
+#define SELINUX_context_new(x) NULL
+#define SELINUX_context_free(x) (x) = NULL
+#define SELINUX_context_dummy_get(x) "<SELinux not compiled in>"
+#define SELINUX_context_str SELINUX_context_dummy_get
+#define SELINUX_context_type_get SELINUX_context_dummy_get
+#define SELINUX_context_user_get SELINUX_context_dummy_get
+#define SELINUX_context_role_get SELINUX_context_dummy_get
+#define SELINUX_context_range_get SELINUX_context_dummy_get
+
+#include <errno.h>
+#define SELINUX_getpeercon(x, y) -1; do { \
+ *(y) = NULL; \
+ errno = ENOTSUP; \
+} while(0)
+
+#define SELINUX_freecon(x) (x) = NULL
+
+#endif /* done HAVE_SELINUX */
+
+#ifdef HAVE_UCRED
+#include <sys/socket.h>
+struct cli_creds {
+ struct ucred ucred;
+ SELINUX_CTX selinux_ctx;
+};
+
+#define cli_creds_get_uid(x) (x->ucred.uid)
+#define cli_creds_get_gid(x) (x->ucred.gid)
+
+#else /* not HAVE_UCRED */
+struct cli_creds {
+ SELINUX_CTX selinux_ctx;
+};
+#define cli_creds_get_uid(x) (-1)
+#define cli_creds_get_gid(x) (-1)
+#endif /* done HAVE_UCRED */
+
+#endif /* __SSSD_UTIL_CREDS_H__ */
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
new file mode 100644
index 0000000..a9e7cf7
--- /dev/null
+++ b/src/util/util_errors.c
@@ -0,0 +1,194 @@
+/*
+ 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/>.
+
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+*/
+
+#include "util/util.h"
+#include <ldb.h>
+
+struct err_string {
+ const char *msg;
+};
+
+struct err_string error_to_str[] = {
+ { "Invalid Error" }, /* ERR_INVALID */
+ { "Internal Error" }, /* ERR_INTERNAL */
+ { "SSSD is running" }, /* ERR_SSSD_RUNNING */
+ { "SSSD is not running" }, /* ERR_SSSD_NOT_RUNNING */
+ { "SSSD is offline" }, /* ERR_OFFLINE */
+ { "Terminated" }, /* ERR_TERMINATED */
+ { "Invalid data type" }, /* ERR_INVALID_DATA_TYPE */
+ { "DP target is not configured" }, /* ERR_MISSING_DP_TARGET */
+ { "Account Unknown" }, /* ERR_ACCOUNT_UNKNOWN */
+ { "No suitable principal found in keytab" }, /* ERR_KRB5_PRINCIPAL_NOT_FOUND */
+ { "Invalid credential type" }, /* ERR_INVALID_CRED_TYPE */
+ { "No credentials available" }, /* ERR_NO_CREDS */
+ { "Credentials are expired" }, /* ERR_CREDS_EXPIRED */
+ { "Credentials are expired, old ccache was removed" }, /* ERR_CREDS_EXPIRED_CCACHE */
+ { "Failure setting user credentials"}, /* ERR_CREDS_INVALID */
+ { "No cached credentials available" }, /* ERR_NO_CACHED_CREDS */
+ { "No matching credentials found" }, /* ERR_NO_MATCHING_CREDS */
+ { "Cached credentials are expired" }, /* ERR_CACHED_CREDS_EXPIRED */
+ { "Authentication Denied" }, /* ERR_AUTH_DENIED */
+ { "Authentication Failed" }, /* ERR_AUTH_FAILED */
+ { "Password Change Denied" }, /* ERR_CHPASS_DENIED */
+ { "Password Change Failed" }, /* ERR_CHPASS_FAILED */
+ { "Network I/O Error" }, /* ERR_NETWORK_IO */
+ { "Account Expired" }, /* ERR_ACCOUNT_EXPIRED */
+ { "Password Expired" }, /* ERR_PASSWORD_EXPIRED */
+ { "Password Expired (reject access)" }, /* ERR_PASSWORD_EXPIRED_REJECT */
+ { "Password Expired (warn user)" }, /* ERR_PASSWORD_EXPIRED_WARN */
+ { "Password Expired (ask for new password)" }, /* ERR_PASSWORD_EXPIRED_RENEW */
+ { "Host Access Denied" }, /* ERR_ACCESS_DENIED */
+ { "SRV record not found" }, /* ERR_SRV_NOT_FOUND */
+ { "SRV lookup error" }, /* ERR_SRV_LOOKUP_ERROR */
+ { "SRV lookup did not return any new server" }, /* ERR_SRV_DUPLICATES */
+ { "Dynamic DNS update failed" }, /* ERR_DYNDNS_FAILED */
+ { "Dynamic DNS update timed out" }, /* ERR_DYNDNS_TIMEOUT */
+ { "Dynamic DNS update not possible while offline" }, /* ERR_DYNDNS_OFFLINE */
+ { "Cannot parse input" }, /* ERR_INPUT_PARSE */
+ { "Entry not found" }, /* ERR_NOT_FOUND */
+ { "Domain not found" }, /* ERR_DOMAIN_NOT_FOUND */
+ { "No domain is enabled" }, /* ERR_NO_DOMAIN_ENABLED */
+ { "Malformed search filter" }, /* ERR_INVALID_FILTER, */
+ { "No POSIX attributes detected" }, /* ERR_NO_POSIX */
+ { "Extra attribute is a duplicate" }, /* ERR_DUP_EXTRA_ATTR */
+ { "Malformed extra attribute" }, /* ERR_INVALID_EXTRA_ATTR */
+ { "Cannot get bus message sender" }, /* ERR_SBUS_GET_SENDER_ERROR */
+ { "Bus message has no sender" }, /* ERR_SBUS_NO_SENDER */
+ { "Invalid SBUS path provided" }, /* ERR_SBUS_INVALID_PATH */
+ { "User/Group SIDs not found" }, /* ERR_NO_SIDS */
+ { "Bus method not supported" }, /* ERR_SBUS_NOSUP */
+ { "Cannot connect to system bus" }, /* ERR_NO_SYSBUS */
+ { "LDAP search returned a referral" }, /* ERR_REFERRAL */
+ { "Error setting SELinux user context" }, /* ERR_SELINUX_CONTEXT */
+ { "SELinux is not managed by libsemanage" }, /* ERR_SELINUX_NOT_MANAGED */
+ { "SELinux user does not exist" }, /* ERR_SELINUX_USER_NOT_FOUND */
+ { "Username format not allowed by re_expression" }, /* ERR_REGEX_NOMATCH */
+ { "Time specification not supported" }, /* ERR_TIMESPEC_NOT_SUPPORTED */
+ { "Invalid SSSD configuration detected" }, /* ERR_INVALID_CONFIG */
+ { "Malformed cache entry" }, /* ERR_MALFORMED_ENTRY */
+ { "Unexpected cache entry type" }, /* ERR_UNEXPECTED_ENTRY_TYPE */
+ { "Failed to resolve one of user groups" }, /* ERR_SIMPLE_GROUPS_MISSING */
+ { "Home directory is NULL" }, /* ERR_HOMEDIR_IS_NULL */
+ { "Unsupported trust direction" }, /* ERR_TRUST_NOT_SUPPORTED */
+ { "Retrieving keytab failed" }, /* ERR_IPA_GETKEYTAB_FAILED */
+ { "Trusted forest root unknown" }, /* ERR_TRUST_FOREST_UNKNOWN */
+ { "p11_child failed" }, /* ERR_P11_CHILD */
+ { "p11_child timeout" }, /* ERR_P11_CHILD_TIMEOUT */
+ { "PIN locked" }, /* ERR_P11_PIN_LOCKED */
+ { "passkey_child failed" }, /* ERR_PASSKEY_CHILD */
+ { "passkey_child timeout" }, /* ERR_PASSKEY_CHILD_TIMEOUT */
+ { "Address family not supported" }, /* ERR_ADDR_FAMILY_NOT_SUPPORTED */
+ { "Message sender is the bus" }, /* ERR_SBUS_SENDER_BUS */
+ { "Subdomain is inactive" }, /* ERR_SUBDOM_INACTIVE */
+ { "Account is locked" }, /* ERR_ACCOUNT_LOCKED */
+ { "AD renewal child failed" }, /* ERR_RENEWAL_CHILD */
+ { "SBUS request already handled" }, /* ERR_SBUS_REQUEST_HANDLED */
+ { "Sysdb version is too old" }, /* ERR_SYSDB_VERSION_TOO_OLD */
+ { "Sysdb version is too new" }, /* ERR_SYSDB_VERSION_TOO_NEW */
+ { "Domain has to timestamp cache" }, /* ERR_NO_TS */
+ { "No timestamp cache record" }, /* ERR_TS_CACHE_MISS */
+ { "Dereference threshold reached" }, /* ERR_DEREF_THRESHOLD */
+ { "The user is not handled by SSSD" }, /* ERR_NON_SSSD_USER */
+ { "The internal name format cannot be parsed" }, /* ERR_WRONG_NAME_FORMAT */
+ { "The maximum level of nested containers has been reached" }, /* ERR_SEC_INVALID_CONTAINERS_NEST_LEVEL */
+ { "The maximum number of stored secrets has been reached" }, /* ERR_SEC_INVALID_TOO_MANY_SECRETS */
+ { "The secret payload size is too large" }, /* ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE */
+ { "No authentication method available" }, /* ERR_NO_AUTH_METHOD_AVAILABLE */
+ { "Smartcard authentication not supported" }, /* ERR_SC_AUTH_NOT_SUPPORTED */
+ { "Malformed input KCM packet" }, /* ERR_KCM_MALFORMED_IN_PKT */
+ { "KCM operation not implemented" }, /* ERR_KCM_OP_NOT_IMPLEMENTED */
+ { "End of credential cache reached" }, /* ERR_KCM_CC_END */
+ { "Credential cache name not allowed" }, /* ERR_KCM_WRONG_CCNAME_FORMAT */
+ { "Cannot encode a JSON object to string" }, /* ERR_JSON_ENCODING */
+ { "Cannot decode a JSON object from string" }, /* ERR_JSON_DECODING */
+ { "Invalid certificate provided" }, /* ERR_INVALID_CERT */
+ { "Unable to initialize SSL" }, /* ERR_SSL_FAILURE */
+ { "Unable to verify peer" }, /* ERR_UNABLE_TO_VERIFY_PEER */
+ { "Unable to resolve host" }, /* ERR_UNABLE_TO_RESOLVE_HOST */
+ { "GetAccountDomain() not supported" }, /* ERR_GET_ACCT_DOM_NOT_SUPPORTED */
+ { "Subid ranges are not supported by this provider" }, /* ERR_GET_ACCT_SUBID_RANGES_NOT_SUPPORTED */
+ { "The last GetAccountDomain() result is still valid" }, /* ERR_GET_ACCT_DOM_CACHED */
+ { "ID is outside the allowed range" }, /* ERR_ID_OUTSIDE_RANGE */
+ { "Group ID is duplicated" }, /* ERR_GID_DUPLICATED */
+ { "Multiple objects were found when only one was expected" }, /* ERR_MULTIPLE_ENTRIES */
+ { "Unsupported range type" }, /* ERR_UNSUPPORTED_RANGE_TYPE */
+ { "proxy_child terminated by a signal" }, /* ERR_PROXY_CHILD_SIGNAL */
+ { "PAC check failed" }, /* ERR_CHECK_PAC_FAILED */
+
+ /* DBUS Errors */
+ { "Connection was killed on demand" }, /* ERR_SBUS_KILL_CONNECTION */
+ { "NULL string cannot be sent over D-Bus" }, /* ERR_SBUS_EMPTY_STRING */
+ { "Maximum number of connections was reached" }, /* ERR_SBUS_CONNECTION_LIMIT */
+ { "String contains invalid characters" }, /* ERR_SBUS_INVALID_STRING */
+ { "Unexpected argument type provided" }, /* ERR_SBUS_INVALID_TYPE */
+ { "Unknown service" }, /* ERR_SBUS_UNKNOWN_SERVICE */
+ { "Unknown interface" }, /* ERR_SBUS_UNKNOWN_INTERFACE */
+ { "Unknown property" }, /* ERR_SBUS_UNKNOWN_PROPERTY */
+ { "Unknown bus owner" }, /* ERR_SBUS_UNKNOWN_OWNER */
+ { "No reply was received" }, /* ERR_SBUS_NO_REPLY */
+
+ /* ini parsing errors */
+ { "Failed to open configuration" }, /* ERR_INI_OPEN_FAILED */
+ { "File ownership and permissions check failed" }, /* ERR_INI_INVALID_PERMISSION */
+ { "Error while parsing configuration file" }, /* ERR_INI_PARSE_FAILED */
+ { "Failed to add configuration snippets" }, /* ERR_INI_ADD_SNIPPETS_FAILED */
+
+ { "TLS handshake was interrupted"}, /* ERR_TLS_HANDSHAKE_INTERRUPTED */
+
+ { "Certificate authority file not found"}, /* ERR_CA_DB_NOT_FOUND */
+
+ { "ERR_LAST" } /* ERR_LAST */
+};
+
+
+const char *sss_strerror(errno_t error)
+{
+ if (IS_SSSD_ERROR(error)) {
+ return error_to_str[SSSD_ERR_IDX(error)].msg;
+ }
+
+ return strerror(error);
+}
+
+/* TODO: make a more complete and precise mapping */
+errno_t sss_ldb_error_to_errno(int ldberr)
+{
+ switch (ldberr) {
+ case LDB_SUCCESS:
+ return EOK;
+ case LDB_ERR_OPERATIONS_ERROR:
+ return EIO;
+ case LDB_ERR_NO_SUCH_OBJECT:
+ case LDB_ERR_NO_SUCH_ATTRIBUTE:
+ return ENOENT;
+ case LDB_ERR_BUSY:
+ return EBUSY;
+ case LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS:
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ return EEXIST;
+ case LDB_ERR_INVALID_ATTRIBUTE_SYNTAX:
+ return EINVAL;
+ default:
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "LDB returned unexpected error: [%i]\n",
+ ldberr);
+ return EFAULT;
+ }
+}
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
new file mode 100644
index 0000000..c3558a2
--- /dev/null
+++ b/src/util/util_errors.h
@@ -0,0 +1,204 @@
+/*
+ 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/>.
+
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+*/
+
+#ifndef __SSSD_UTIL_ERRORS_H__
+#define __SSSD_UTIL_ERRORS_H__
+
+#ifndef HAVE_ERRNO_T
+#define HAVE_ERRNO_T
+typedef int errno_t;
+#endif
+
+/*
+ * We define a specific number space so that we do not overlap with other
+ * generic errors returned by various libraries. This will make it easy
+ * to have functions that double check that what was returned was an SSSD
+ * specific error where it matters. For example we may want to ensure some
+ * particularly sensitive paths only return SSSD-specific errors as that
+ * will ensure all error conditions have been explicitly dealt with,
+ * and are not the result of assigning the wrong return result.
+ *
+ * Basic system errno errors can still be used, but when an error condition
+ * does not properly map to a system error we should use an SSSD specific one
+ */
+
+#define ERR_BASE 0x555D0000
+#define ERR_MASK 0x0000FFFF
+
+/* never use ERR_INVALID, it is used for catching and returning
+ * information on invalid error numbers */
+/* never use ERR_LAST, this represents the maximum error value available
+ * and is used to validate error codes */
+enum sssd_errors {
+ ERR_INVALID = ERR_BASE + 0,
+ ERR_INTERNAL,
+ ERR_SSSD_RUNNING,
+ ERR_SSSD_NOT_RUNNING,
+ ERR_OFFLINE,
+ ERR_TERMINATED,
+ ERR_INVALID_DATA_TYPE,
+ ERR_MISSING_DP_TARGET,
+ ERR_ACCOUNT_UNKNOWN,
+ ERR_KRB5_PRINCIPAL_NOT_FOUND,
+ ERR_INVALID_CRED_TYPE,
+ ERR_NO_CREDS,
+ ERR_CREDS_EXPIRED,
+ ERR_CREDS_EXPIRED_CCACHE,
+ ERR_CREDS_INVALID,
+ ERR_NO_CACHED_CREDS,
+ ERR_NO_MATCHING_CREDS,
+ ERR_CACHED_CREDS_EXPIRED,
+ ERR_AUTH_DENIED,
+ ERR_AUTH_FAILED,
+ ERR_CHPASS_DENIED,
+ ERR_CHPASS_FAILED,
+ ERR_NETWORK_IO,
+ ERR_ACCOUNT_EXPIRED,
+ ERR_PASSWORD_EXPIRED,
+ ERR_PASSWORD_EXPIRED_REJECT,
+ ERR_PASSWORD_EXPIRED_WARN,
+ ERR_PASSWORD_EXPIRED_RENEW,
+ ERR_ACCESS_DENIED,
+ ERR_SRV_NOT_FOUND,
+ ERR_SRV_LOOKUP_ERROR,
+ ERR_SRV_DUPLICATES,
+ ERR_DYNDNS_FAILED,
+ ERR_DYNDNS_TIMEOUT,
+ ERR_DYNDNS_OFFLINE,
+ ERR_INPUT_PARSE,
+ ERR_NOT_FOUND,
+ ERR_DOMAIN_NOT_FOUND,
+ ERR_NO_DOMAIN_ENABLED,
+ ERR_INVALID_FILTER,
+ ERR_NO_POSIX,
+ ERR_DUP_EXTRA_ATTR,
+ ERR_INVALID_EXTRA_ATTR,
+ ERR_SBUS_GET_SENDER_ERROR,
+ ERR_SBUS_NO_SENDER,
+ ERR_SBUS_INVALID_PATH,
+ ERR_NO_SIDS,
+ ERR_SBUS_NOSUP,
+ ERR_NO_SYSBUS,
+ ERR_REFERRAL,
+ ERR_SELINUX_CONTEXT,
+ ERR_SELINUX_NOT_MANAGED,
+ ERR_SELINUX_USER_NOT_FOUND,
+ ERR_REGEX_NOMATCH,
+ ERR_TIMESPEC_NOT_SUPPORTED,
+ ERR_INVALID_CONFIG,
+ ERR_MALFORMED_ENTRY,
+ ERR_UNEXPECTED_ENTRY_TYPE,
+ ERR_SIMPLE_GROUPS_MISSING,
+ ERR_HOMEDIR_IS_NULL,
+ ERR_TRUST_NOT_SUPPORTED,
+ ERR_IPA_GETKEYTAB_FAILED,
+ ERR_TRUST_FOREST_UNKNOWN,
+ ERR_P11_CHILD,
+ ERR_P11_CHILD_TIMEOUT,
+ ERR_P11_PIN_LOCKED,
+ ERR_PASSKEY_CHILD,
+ ERR_PASSKEY_CHILD_TIMEOUT,
+ ERR_ADDR_FAMILY_NOT_SUPPORTED,
+ ERR_SBUS_SENDER_BUS,
+ ERR_SUBDOM_INACTIVE,
+ ERR_ACCOUNT_LOCKED,
+ ERR_RENEWAL_CHILD,
+ ERR_SBUS_REQUEST_HANDLED,
+ ERR_SYSDB_VERSION_TOO_OLD,
+ ERR_SYSDB_VERSION_TOO_NEW,
+ ERR_NO_TS,
+ ERR_TS_CACHE_MISS,
+ ERR_DEREF_THRESHOLD,
+ ERR_NON_SSSD_USER,
+ ERR_WRONG_NAME_FORMAT,
+ ERR_SEC_INVALID_CONTAINERS_NEST_LEVEL,
+ ERR_SEC_INVALID_TOO_MANY_SECRETS,
+ ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE,
+ ERR_NO_AUTH_METHOD_AVAILABLE,
+ ERR_SC_AUTH_NOT_SUPPORTED,
+ ERR_KCM_MALFORMED_IN_PKT,
+ ERR_KCM_OP_NOT_IMPLEMENTED,
+ ERR_KCM_CC_END,
+ ERR_KCM_WRONG_CCNAME_FORMAT,
+ ERR_JSON_ENCODING,
+ ERR_JSON_DECODING,
+ ERR_INVALID_CERT,
+ ERR_SSL_FAILURE,
+ ERR_UNABLE_TO_VERIFY_PEER,
+ ERR_UNABLE_TO_RESOLVE_HOST,
+ ERR_GET_ACCT_DOM_NOT_SUPPORTED,
+ ERR_GET_ACCT_SUBID_RANGES_NOT_SUPPORTED,
+ ERR_GET_ACCT_DOM_CACHED,
+ ERR_ID_OUTSIDE_RANGE,
+ ERR_GID_DUPLICATED,
+ ERR_MULTIPLE_ENTRIES,
+ ERR_UNSUPPORTED_RANGE_TYPE,
+ ERR_PROXY_CHILD_SIGNAL,
+ ERR_CHECK_PAC_FAILED,
+
+ /* DBUS Errors */
+ ERR_SBUS_KILL_CONNECTION,
+ ERR_SBUS_EMPTY_STRING,
+ ERR_SBUS_CONNECTION_LIMIT,
+ ERR_SBUS_INVALID_STRING,
+ ERR_SBUS_INVALID_TYPE,
+ ERR_SBUS_UNKNOWN_SERVICE,
+ ERR_SBUS_UNKNOWN_INTERFACE,
+ ERR_SBUS_UNKNOWN_PROPERTY,
+ ERR_SBUS_UNKNOWN_OWNER,
+ ERR_SBUS_NO_REPLY,
+
+ /* ini parsing errors */
+ ERR_INI_OPEN_FAILED,
+ ERR_INI_INVALID_PERMISSION,
+ ERR_INI_PARSE_FAILED,
+ ERR_INI_ADD_SNIPPETS_FAILED,
+
+ ERR_TLS_HANDSHAKE_INTERRUPTED,
+
+ ERR_CA_DB_NOT_FOUND,
+
+ ERR_LAST /* ALWAYS LAST */
+};
+
+#define SSSD_ERR_BASE(err) ((err) & ~ERR_MASK)
+#define SSSD_ERR_IDX(err) ((err) & ERR_MASK)
+#define IS_SSSD_ERROR(err) \
+ (((err) > 0) && (SSSD_ERR_BASE(err) == ERR_BASE) && ((err) <= ERR_LAST))
+
+#define ERR_OK 0
+/* Backwards compat */
+#ifndef EOK
+#define EOK ERR_OK
+#endif
+
+/**
+ * @brief return a string describing the error number like strerror()
+ *
+ * @param error An errno_t number, can be an SSSD error or a system error
+ *
+ * @return A statically allocated string.
+ */
+const char *sss_strerror(errno_t error);
+
+/* return ldb error converted to an errno */
+errno_t sss_ldb_error_to_errno(int ldberr);
+
+#endif /* __SSSD_UTIL_ERRORS_H__ */
diff --git a/src/util/util_ext.c b/src/util/util_ext.c
new file mode 100644
index 0000000..c9839a9
--- /dev/null
+++ b/src/util/util_ext.c
@@ -0,0 +1,389 @@
+/*
+ SSSD helper calls - can be used by libraries for external use as well
+
+ Authors:
+ Simo Sorce <ssorce@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 <talloc.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+
+#define EOK 0
+
+#ifndef HAVE_ERRNO_T
+#define HAVE_ERRNO_T
+typedef int errno_t;
+#endif
+
+int split_on_separator(TALLOC_CTX *mem_ctx, const char *str,
+ const char sep, bool trim, bool skip_empty,
+ char ***_list, int *size)
+{
+ int ret;
+ const char *substr_end = str;
+ const char *substr_begin = str;
+ const char *sep_pos = NULL;
+ size_t substr_len;
+ char **list = NULL;
+ int num_strings = 0;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ if (str == NULL || *str == '\0' || _list == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ do {
+ substr_len = 0;
+
+ /* If this is not the first substring, then move from the separator. */
+ if (sep_pos != NULL) {
+ substr_end = sep_pos + 1;
+ substr_begin = sep_pos + 1;
+ }
+
+ /* Find end of the first substring */
+ while (*substr_end != sep && *substr_end != '\0') {
+ substr_end++;
+ substr_len++;
+ }
+
+ sep_pos = substr_end;
+
+ if (trim) {
+ /* Trim leading whitespace */
+ while (isspace(*substr_begin) && substr_begin < substr_end) {
+ substr_begin++;
+ substr_len--;
+ }
+
+ /* Trim trailing whitespace */
+ while (substr_end - 1 > substr_begin && isspace(*(substr_end-1))) {
+ substr_end--;
+ substr_len--;
+ }
+ }
+
+ /* Copy the substring to the output list of strings */
+ if (skip_empty == false || substr_len > 0) {
+ list = talloc_realloc(tmp_ctx, list, char*, num_strings + 2);
+ if (list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* empty string is stored for substr_len == 0 */
+ list[num_strings] = talloc_strndup(list, substr_begin, substr_len);
+ if (list[num_strings] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ num_strings++;
+ }
+
+ } while (*sep_pos != '\0');
+
+ if (list == NULL) {
+ /* No allocations were done, make space for the NULL */
+ list = talloc(tmp_ctx, char *);
+ if (list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ list[num_strings] = NULL;
+
+ if (size) {
+ *size = num_strings;
+ }
+
+ *_list = talloc_steal(mem_ctx, list);
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+bool string_in_list(const char *string, char **list, bool case_sensitive)
+{
+ size_t c;
+ int(*compare)(const char *s1, const char *s2);
+
+ if (string == NULL || list == NULL || *list == NULL) {
+ return false;
+ }
+
+ compare = case_sensitive ? strcmp : strcasecmp;
+
+ for (c = 0; list[c] != NULL; c++) {
+ if (compare(string, list[c]) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool string_in_list_size(const char *string, const char **list, size_t size,
+ bool case_sensitive)
+{
+ size_t c;
+ int(*compare)(const char *s1, const char *s2);
+
+ if (string == NULL || list == NULL || size == 0) {
+ return false;
+ }
+
+ compare = case_sensitive ? strcmp : strcasecmp;
+
+ for (c = 0; c < size; c++) {
+ if (compare(string, list[c]) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+errno_t sss_filter_sanitize_ex(TALLOC_CTX *mem_ctx,
+ const char *input,
+ char **sanitized,
+ const char *ignore)
+{
+ char *output;
+ size_t i = 0;
+ size_t j = 0;
+ char *allowed;
+
+ /* Assume the worst-case. We'll resize it later, once */
+ output = talloc_array(mem_ctx, char, strlen(input) * 3 + 1);
+ if (!output) {
+ return ENOMEM;
+ }
+
+ while (input[i]) {
+ /* Even though this character might have a special meaning, if it's
+ * explicitly allowed, just copy it and move on
+ */
+ if (ignore == NULL) {
+ allowed = NULL;
+ } else {
+ allowed = strchr(ignore, input[i]);
+ }
+ if (allowed) {
+ output[j++] = input[i++];
+ continue;
+ }
+
+ switch(input[i]) {
+ case '\t':
+ output[j++] = '\\';
+ output[j++] = '0';
+ output[j++] = '9';
+ break;
+ case ' ':
+ output[j++] = '\\';
+ output[j++] = '2';
+ output[j++] = '0';
+ break;
+ case '*':
+ output[j++] = '\\';
+ output[j++] = '2';
+ output[j++] = 'a';
+ break;
+ case '(':
+ output[j++] = '\\';
+ output[j++] = '2';
+ output[j++] = '8';
+ break;
+ case ')':
+ output[j++] = '\\';
+ output[j++] = '2';
+ output[j++] = '9';
+ break;
+ case '\\':
+ output[j++] = '\\';
+ output[j++] = '5';
+ output[j++] = 'c';
+ break;
+ case '\r':
+ output[j++] = '\\';
+ output[j++] = '0';
+ output[j++] = 'd';
+ break;
+ case '\n':
+ output[j++] = '\\';
+ output[j++] = '0';
+ output[j++] = 'a';
+ break;
+ default:
+ output[j++] = input[i];
+ }
+
+ i++;
+ }
+ output[j] = '\0';
+ *sanitized = talloc_realloc(mem_ctx, output, char, j+1);
+ if (!*sanitized) {
+ talloc_free(output);
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+errno_t sss_filter_sanitize(TALLOC_CTX *mem_ctx,
+ const char *input,
+ char **sanitized)
+{
+ return sss_filter_sanitize_ex(mem_ctx, input, sanitized, NULL);
+}
+
+/* There is similar function ldap_dn_normalize in openldap.
+ * To avoid dependecies across project we have this own func.
+ * Also ldb can do this but doesn't handle all the cases
+ */
+static errno_t sss_trim_dn(TALLOC_CTX *mem_ctx,
+ const char *input,
+ char **trimmed)
+{
+ int i = 0;
+ int o = 0;
+ int s;
+ char *output;
+ enum sss_trim_dn_state {
+ SSS_TRIM_DN_STATE_READING_NAME,
+ SSS_TRIM_DN_STATE_READING_VALUE
+ } state = SSS_TRIM_DN_STATE_READING_NAME;
+
+ *trimmed = NULL;
+
+ output = talloc_array(mem_ctx, char, strlen(input) + 1);
+ if (!output) {
+ return ENOMEM;
+ }
+
+ /* skip leading spaces */
+ while(isspace(input[i])) {
+ ++i;
+ }
+
+ while(input[i] != '\0') {
+ if (!isspace(input[i])) {
+ switch (input[i]) {
+ case '=':
+ output[o++] = input[i++];
+ if (state == SSS_TRIM_DN_STATE_READING_NAME) {
+ while (isspace(input[i])) {
+ ++i;
+ }
+ state = SSS_TRIM_DN_STATE_READING_VALUE;
+ }
+ break;
+ case ',':
+ output[o++] = input[i++];
+ if (state == SSS_TRIM_DN_STATE_READING_VALUE) {
+ while (isspace(input[i])) {
+ ++i;
+ }
+ state = SSS_TRIM_DN_STATE_READING_NAME;
+ }
+ break;
+ case '\\':
+ output[o++] = input[i++];
+ if (input[i] != '\0') {
+ output[o++] = input[i++];
+ }
+ break;
+ default:
+ if (input[i] != '\0') {
+ output[o++] = input[i++];
+ }
+ break;
+ }
+
+ continue;
+ }
+
+ /* non escaped space found */
+ s = 1;
+ while (isspace(input[i + s])) {
+ ++s;
+ }
+
+ switch (state) {
+ case SSS_TRIM_DN_STATE_READING_NAME:
+ if (input[i + s] != '=') {
+ /* this is not trailing space - should not be removed */
+ while (isspace(input[i])) {
+ output[o++] = input[i++];
+ }
+ } else {
+ i += s;
+ }
+ break;
+ case SSS_TRIM_DN_STATE_READING_VALUE:
+ if (input[i + s] != ',') {
+ /* this is not trailing space - should not be removed */
+ while (isspace(input[i])) {
+ output[o++] = input[i++];
+ }
+ } else {
+ i += s;
+ }
+ break;
+ }
+ }
+
+ output[o--] = '\0';
+
+ /* trim trailing space */
+ while (o >= 0 && isspace(output[o])) {
+ output[o--] = '\0';
+ }
+
+ *trimmed = output;
+ return EOK;
+}
+
+errno_t sss_filter_sanitize_dn(TALLOC_CTX *mem_ctx,
+ const char *input,
+ char **sanitized)
+{
+ errno_t ret;
+ char *trimmed_dn = NULL;
+
+ ret = sss_trim_dn(mem_ctx, input, &trimmed_dn);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sss_filter_sanitize_ex(mem_ctx, trimmed_dn, sanitized, NULL);
+
+ done:
+ talloc_free(trimmed_dn);
+ return ret;
+}
diff --git a/src/util/util_lock.c b/src/util/util_lock.c
new file mode 100644
index 0000000..9f28858
--- /dev/null
+++ b/src/util/util_lock.c
@@ -0,0 +1,91 @@
+/*
+ SSSD
+
+ util_lock.c
+
+ Authors:
+ Michal Zidek <mzidek@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 <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "util/util.h"
+
+errno_t sss_br_lock_file(int fd, size_t start, size_t len,
+ int num_tries, useconds_t wait)
+{
+ int ret = EAGAIN;
+ struct flock lock;
+ int retries_left;
+
+ if (num_tries <= 0) {
+ return EINVAL;
+ }
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = start;
+ lock.l_len = len;
+ lock.l_pid = 0;
+
+ for (retries_left = num_tries; retries_left > 0; retries_left--) {
+ ret = fcntl(fd, F_SETLK, &lock);
+ if (ret == -1) {
+ ret = errno;
+ if (ret == EACCES || ret == EAGAIN || ret == EINTR) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Failed to lock file. Retries left: %d\n",
+ retries_left - 1);
+
+ if ((ret == EACCES || ret == EAGAIN) && (retries_left <= 1)) {
+ /* File is locked by someone else. Return EACCESS
+ * if this is the last try. */
+ return EACCES;
+ }
+
+ if (retries_left - 1 > 0) {
+ ret = usleep(wait);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "usleep() failed with %d -> ignoring\n", ret);
+ }
+ }
+ } else {
+ /* Error occurred */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to lock file.\n");
+ return ret;
+ }
+ } else if (ret == 0) {
+ /* File successfully locked */
+ break;
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unexpected fcntl() return code: %d\n", ret);
+ }
+ }
+ if (retries_left == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lock file.\n");
+ return ret;
+ }
+
+ return EOK;
+}
diff --git a/src/util/util_preauth.c b/src/util/util_preauth.c
new file mode 100644
index 0000000..a2b0ac6
--- /dev/null
+++ b/src/util/util_preauth.c
@@ -0,0 +1,86 @@
+/*
+ SSSD
+
+ Calls to manage the preauth indicator file
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2018 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "util/util.h"
+#include "sss_client/sss_cli.h"
+
+static void cleanup_preauth_indicator(void)
+{
+ int ret;
+
+ ret = unlink(PAM_PREAUTH_INDICATOR);
+ if (ret != EOK && errno != ENOENT) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to remove preauth indicator file [%s] %d [%s].\n",
+ PAM_PREAUTH_INDICATOR, ret, sss_strerror(ret));
+ }
+}
+
+errno_t create_preauth_indicator(void)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ int fd;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ fd = open(PAM_PREAUTH_INDICATOR, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW,
+ 0644);
+ if (fd < 0) {
+ if (errno != EEXIST) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to create preauth indicator file [%s].\n",
+ PAM_PREAUTH_INDICATOR);
+ ret = EOK;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Preauth indicator file [%s] already exists. Continuing.\n",
+ PAM_PREAUTH_INDICATOR);
+ } else {
+ close(fd);
+ }
+
+ ret = atexit(cleanup_preauth_indicator);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "atexit failed. Continuing.\n");
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
diff --git a/src/util/util_sss_idmap.c b/src/util/util_sss_idmap.c
new file mode 100644
index 0000000..4ce4250
--- /dev/null
+++ b/src/util/util_sss_idmap.c
@@ -0,0 +1,32 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2013 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 <talloc.h>
+#include "util/util_sss_idmap.h"
+
+void *sss_idmap_talloc(size_t size, void *pvt)
+{
+ return talloc_size(pvt, size);
+}
+
+void sss_idmap_talloc_free(void *ptr, void *pvt)
+{
+ talloc_free(ptr);
+}
diff --git a/src/util/util_sss_idmap.h b/src/util/util_sss_idmap.h
new file mode 100644
index 0000000..bde4727
--- /dev/null
+++ b/src/util/util_sss_idmap.h
@@ -0,0 +1,28 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2013 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 __UTIL_SSS_IDMAP_H__
+#define __UTIL_SSS_IDMAP_H__
+
+void *sss_idmap_talloc(size_t size, void *pvt);
+
+void sss_idmap_talloc_free(void *ptr, void *pvt);
+
+#endif /* __UTIL_SSS_IDMAP_H__ */
diff --git a/src/util/util_watchdog.c b/src/util/util_watchdog.c
new file mode 100644
index 0000000..abafd94
--- /dev/null
+++ b/src/util/util_watchdog.c
@@ -0,0 +1,290 @@
+/*
+ SSSD
+
+ Timer Watchdog routines
+
+ Copyright (C) Simo Sorce 2016
+
+ 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 <signal.h>
+
+#include "util/util.h"
+
+#define WATCHDOG_DEF_INTERVAL 10
+#define WATCHDOG_MAX_TICKS 3
+#define DEFAULT_BUFFER_SIZE 4096
+
+/* this is intentionally a global variable */
+struct watchdog_ctx {
+ timer_t timerid;
+ struct timeval interval;
+ struct tevent_timer *te;
+ volatile int ticks;
+
+ /* To detect time shift. */
+ struct tevent_context *ev;
+ int input_interval;
+ time_t timestamp;
+ struct tevent_fd *tfd;
+ int pipefd[2];
+ bool armed; /* if 'true' ticks counter will not be reset */
+} watchdog_ctx;
+
+static void watchdog_detect_timeshift(void)
+{
+ time_t prev_time;
+ time_t cur_time;
+
+ prev_time = watchdog_ctx.timestamp;
+ cur_time = watchdog_ctx.timestamp = time(NULL);
+ if (cur_time < prev_time) {
+ /* Time shift detected. We need to restart watchdog. */
+ if (write(watchdog_ctx.pipefd[1], "1", 1) != 1) {
+ if (getpid() == getpgrp()) {
+ kill(-getpgrp(), SIGTERM);
+ }
+ _exit(1);
+ }
+ }
+}
+
+/* the watchdog is purposefully *not* handled by the tevent
+ * signal handler as it is meant to check if the daemon is
+ * still processing the event queue itself. A stuck process
+ * may not handle the event queue at all and thus not handle
+ * signals either */
+static void watchdog_handler(int sig)
+{
+
+ watchdog_detect_timeshift();
+
+ /* if a pre-defined number of ticks passed by kills itself */
+ if (__sync_add_and_fetch(&watchdog_ctx.ticks, 1) >= WATCHDOG_MAX_TICKS) {
+ if (getpid() == getpgrp()) {
+ kill(-getpgrp(), SIGTERM);
+ }
+ _exit(SSS_WATCHDOG_EXIT_CODE);
+ }
+}
+
+static void watchdog_reset(void)
+{
+ __sync_and_and_fetch(&watchdog_ctx.ticks, 0);
+}
+
+static void watchdog_event_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *private_data)
+{
+ if (!watchdog_ctx.armed) {
+ /* first thing reset the watchdog ticks */
+ watchdog_reset();
+ } else {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Watchdog armed, process might be terminated soon.\n");
+ }
+
+ /* then set a new watchodg event */
+ watchdog_ctx.te = tevent_add_timer(ev, ev,
+ tevent_timeval_current_ofs(watchdog_ctx.interval.tv_sec, 0),
+ watchdog_event_handler, NULL);
+ /* if the function fails the watchdog will kill the
+ * process soon enough, so we just warn */
+ if (!watchdog_ctx.te) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to create a watchdog timer event!\n");
+ }
+}
+
+static errno_t watchdog_fd_recv_data(int fd)
+{
+ ssize_t len;
+ char buffer[DEFAULT_BUFFER_SIZE];
+ errno_t ret;
+
+ errno = 0;
+ len = read(fd, buffer, DEFAULT_BUFFER_SIZE);
+ if (len == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+ return EAGAIN;
+ } else {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "write failed [%d]: %s\n", ret, strerror(ret));
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static void watchdog_fd_read_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *data)
+{
+ errno_t ret;
+
+ ret = watchdog_fd_recv_data(watchdog_ctx.pipefd[0]);
+ switch(ret) {
+ case EAGAIN:
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Interrupted before any data could be read, retry later.\n");
+ return;
+ case EOK:
+ /* all fine */
+ break;
+ default:
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to receive data [%d]: %s. "
+ "orderly_shutdown() will be called.\n", ret, strerror(ret));
+ orderly_shutdown(1);
+ }
+
+ DEBUG(SSSDBG_IMPORTANT_INFO, "Time shift detected, "
+ "restarting watchdog!\n");
+ teardown_watchdog();
+ ret = setup_watchdog(watchdog_ctx.ev, watchdog_ctx.input_interval);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to restart watchdog "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ orderly_shutdown(1);
+ }
+ if (strncmp(debug_prg_name, "be[", sizeof("be[") - 1) == 0) {
+ kill(getpid(), SIGUSR2);
+ DEBUG(SSSDBG_IMPORTANT_INFO, "SIGUSR2 sent to %s\n", debug_prg_name);
+ }
+}
+
+int setup_watchdog(struct tevent_context *ev, int interval)
+{
+ struct sigevent sev;
+ struct itimerspec its;
+ struct tevent_fd *tfd;
+ int signum = SIGRTMIN;
+ int ret;
+
+ memset(&sev, 0, sizeof(sev));
+ CatchSignal(signum, watchdog_handler);
+
+ sev.sigev_notify = SIGEV_SIGNAL;
+ sev.sigev_signo = signum;
+ sev.sigev_value.sival_ptr = &watchdog_ctx.timerid;
+ errno = 0;
+ ret = timer_create(CLOCK_MONOTONIC, &sev, &watchdog_ctx.timerid);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to create watchdog timer (%d) [%s]\n",
+ ret, strerror(ret));
+ return ret;
+ }
+
+ if (interval == 0) {
+ interval = WATCHDOG_DEF_INTERVAL;
+ }
+ watchdog_ctx.interval.tv_sec = interval;
+ watchdog_ctx.interval.tv_usec = 0;
+
+ watchdog_ctx.ev = ev;
+ watchdog_ctx.input_interval = interval;
+ watchdog_ctx.timestamp = time(NULL);
+ watchdog_ctx.armed = false;
+
+ ret = pipe(watchdog_ctx.pipefd);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "pipe failed [%d] [%s].\n", ret, strerror(ret));
+ return ret;
+ }
+
+ sss_fd_nonblocking(watchdog_ctx.pipefd[0]);
+ sss_fd_nonblocking(watchdog_ctx.pipefd[1]);
+
+ tfd = tevent_add_fd(ev, (TALLOC_CTX *)ev, watchdog_ctx.pipefd[0],
+ TEVENT_FD_READ, watchdog_fd_read_handler, NULL);
+ watchdog_ctx.tfd = tfd;
+
+ /* Start the timer */
+ /* we give 1 second head start to the watchdog event */
+ its.it_value.tv_sec = interval + 1;
+ its.it_value.tv_nsec = 0;
+ its.it_interval.tv_sec = interval;
+ its.it_interval.tv_nsec = 0;
+ errno = 0;
+ ret = timer_settime(watchdog_ctx.timerid, 0, &its, NULL);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to create watchdog timer (%d) [%s]\n",
+ ret, strerror(ret));
+ return ret;
+ }
+
+ /* Add the watchdog event and make it fire as fast as the timer */
+ watchdog_event_handler(ev, NULL, tevent_timeval_zero(), NULL);
+
+ return EOK;
+}
+
+void teardown_watchdog(void)
+{
+ int ret;
+
+ /* Disarm the timer */
+ errno = 0;
+ ret = timer_delete(watchdog_ctx.timerid);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to destroy watchdog timer (%d) [%s]\n",
+ ret, strerror(ret));
+ }
+
+ /* Free the tevent_fd */
+ talloc_zfree(watchdog_ctx.tfd);
+
+ /* Close the pipefds */
+ PIPE_FD_CLOSE(watchdog_ctx.pipefd[0]);
+ PIPE_FD_CLOSE(watchdog_ctx.pipefd[1]);
+
+ /* and kill the watchdog event */
+ talloc_free(watchdog_ctx.te);
+}
+
+int get_watchdog_ticks(void)
+{
+ return __sync_add_and_fetch(&watchdog_ctx.ticks, 0);
+}
+
+void arm_watchdog(void)
+{
+ if (watchdog_ctx.armed) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "arm_watchdog() is called although the watchdog is already armed. "
+ "This indicates a programming error and should be avoided because "
+ "it will most probably not work as expected.\n");
+ }
+
+ watchdog_ctx.armed = true;
+}
+
+void disarm_watchdog(void)
+{
+ watchdog_ctx.armed = false;
+}
diff --git a/src/util/well_known_sids.c b/src/util/well_known_sids.c
new file mode 100644
index 0000000..03fc261
--- /dev/null
+++ b/src/util/well_known_sids.c
@@ -0,0 +1,478 @@
+/*
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2013 Red Hat
+
+ Translate well-known SIDs to domains and names
+
+ 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/strtonum.h"
+
+/* Well-Known SIDs are documented in section 2.4.2.4 "Well-Known SID
+ * Structures" of the "[MS-DTYP]: Windows Data Types" document. */
+
+#define DOM_SID_PREFIX "S-1-5-21-"
+#define DOM_SID_PREFIX_LEN (sizeof(DOM_SID_PREFIX) - 1)
+
+#define BUILTIN_SID_PREFIX "S-1-5-32-"
+#define BUILTIN_SID_PREFIX_LEN (sizeof(BUILTIN_SID_PREFIX) - 1)
+#define BUILTIN_DOM_NAME "BUILTIN"
+
+#define NT_SID_PREFIX "S-1-5-"
+#define NT_SID_PREFIX_LEN (sizeof(NT_SID_PREFIX) - 1)
+#define NT_DOM_NAME "NT AUTHORITY"
+
+#define NT_AUTH_SID_PREFIX "S-1-5-64-"
+#define NT_AUTH_SID_PREFIX_LEN (sizeof(NT_AUTH_SID_PREFIX) - 1)
+#define NT_AUTH_DOM_NAME NT_DOM_NAME
+
+#define NT_THIS_SID_PREFIX "S-1-5-65-"
+#define NT_THIS_SID_PREFIX_LEN (sizeof(NT_THIS_SID_PREFIX) - 1)
+#define NT_THIS_DOM_NAME NT_DOM_NAME
+
+#define SPECIAL_SID_PREFIX "S-1-"
+#define SPECIAL_SID_PREFIX_LEN (sizeof(SPECIAL_SID_PREFIX) - 1)
+#define NULL_DOM_NAME "NULL AUTHORITY"
+#define WORLD_DOM_NAME "WORLD AUTHORITY"
+#define LOCAL_DOM_NAME "LOCAL AUTHORITY"
+#define CREATOR_DOM_NAME "CREATOR AUTHORITY"
+#define MANDATORY_DOM_NAME "MANDATORY LABEL AUTHORITY"
+#define AUTHENTICATION_DOM_NAME "AUTHENTICATION AUTHORITY"
+
+#define NT_MAP_ENTRY(rid, name) {rid, NT_SID_PREFIX #rid, name}
+#define NT_AUTH_MAP_ENTRY(rid, name) {rid, NT_AUTH_SID_PREFIX #rid, name}
+#define NT_THIS_MAP_ENTRY(rid, name) {rid, NT_THIS_SID_PREFIX #rid, name}
+#define BUILTIN_MAP_ENTRY(rid, name) {rid, BUILTIN_SID_PREFIX #rid, name}
+#define SPECIAL_MAP_ENTRY(id_auth, rid, dom, name) \
+ {id_auth, rid, SPECIAL_SID_PREFIX #id_auth "-" #rid, dom, name}
+
+struct rid_sid_name {
+ uint32_t rid;
+ const char *sid;
+ const char *name;
+};
+
+struct special_map {
+ uint32_t id_auth;
+ uint32_t rid;
+ const char *sid;
+ const char *dom;
+ const char *name;
+};
+
+struct rid_sid_name builtin_map[] = {
+ BUILTIN_MAP_ENTRY(544, "Administrators"),
+ BUILTIN_MAP_ENTRY(545, "Users"),
+ BUILTIN_MAP_ENTRY(546, "Guests"),
+ BUILTIN_MAP_ENTRY(547, "Power Users"),
+ BUILTIN_MAP_ENTRY(548, "Account Operators"),
+ BUILTIN_MAP_ENTRY(549, "Server Operators"),
+ BUILTIN_MAP_ENTRY(550, "Print Operators"),
+ BUILTIN_MAP_ENTRY(551, "Backup Operators"),
+ BUILTIN_MAP_ENTRY(552, "Replicator"),
+ BUILTIN_MAP_ENTRY(554, "Pre-Windows 2000 Compatible Access"),
+ BUILTIN_MAP_ENTRY(555, "Remote Desktop Users"),
+ BUILTIN_MAP_ENTRY(556, "Network Configuration Operators"),
+ BUILTIN_MAP_ENTRY(557, "Incoming Forest Trust Builders"),
+ BUILTIN_MAP_ENTRY(558, "Performance Monitor Users"),
+ BUILTIN_MAP_ENTRY(559, "Performance Log Users"),
+ BUILTIN_MAP_ENTRY(560, "Windows Authorization Access Group"),
+ BUILTIN_MAP_ENTRY(561, "Terminal Server License Servers"),
+ BUILTIN_MAP_ENTRY(562, "Distributed COM Users"),
+ BUILTIN_MAP_ENTRY(568, "IIS Users"),
+ BUILTIN_MAP_ENTRY(569, "Cryptographic Operators"),
+ BUILTIN_MAP_ENTRY(573, "Event Log Readers"),
+ BUILTIN_MAP_ENTRY(574, "Certificate Service DCOM Access"),
+ BUILTIN_MAP_ENTRY(575, "RDS Remote Access Servers"),
+ BUILTIN_MAP_ENTRY(576, "RDS Endpoint Servers"),
+ BUILTIN_MAP_ENTRY(577, "RDS Management Servers"),
+ BUILTIN_MAP_ENTRY(578, "Hyper-V Admins"),
+ BUILTIN_MAP_ENTRY(579, "Access Control Assistance OPS"),
+ BUILTIN_MAP_ENTRY(580, "Remote Management Users"),
+
+ {UINT32_MAX, NULL, NULL}
+};
+
+#define LOGON_ID_NAME "LOGON ID"
+#define LOGON_ID_MODEL "S-1-5-5-0-0"
+struct rid_sid_name nt_map[] = {
+ NT_MAP_ENTRY(1, "DIALUP"),
+ NT_MAP_ENTRY(2, "NETWORK"),
+ NT_MAP_ENTRY(3, "BATCH"),
+ NT_MAP_ENTRY(4, "INTERACTIVE"),
+ /* NT_MAP_ENTRY(5-x-y, "LOGON ID"), special case treated separately */
+ NT_MAP_ENTRY(6, "SERVICE"),
+ NT_MAP_ENTRY(7, "ANONYMOUS LOGON"),
+ NT_MAP_ENTRY(8, "PROXY"),
+ NT_MAP_ENTRY(9, "ENTERPRISE DOMAIN CONTROLLERS"),
+ NT_MAP_ENTRY(10, "SELF"),
+ NT_MAP_ENTRY(11, "Authenticated Users"),
+ NT_MAP_ENTRY(12, "RESTRICTED CODE"),
+ NT_MAP_ENTRY(13, "TERMINAL SERVER USER"),
+ NT_MAP_ENTRY(14, "REMOTE INTERACTIVE LOGON"),
+ NT_MAP_ENTRY(15, "This Organization"),
+ NT_MAP_ENTRY(17, "IUSR"),
+ NT_MAP_ENTRY(18, "LOCAL SYSTEM"),
+ NT_MAP_ENTRY(19, "LOCAL SERVICE"),
+ NT_MAP_ENTRY(20, "NETWORK SERVICE"),
+ NT_MAP_ENTRY(33, "WRITE_RESTRICTED_CODE"),
+ NT_MAP_ENTRY(80, "NT_SERVICE"),
+ NT_MAP_ENTRY(113, "LOCAL_ACCOUNT"),
+ NT_MAP_ENTRY(114, "LOCAL MEMBER OF ADMINISTRATORS GROUP"),
+ NT_MAP_ENTRY(1000, "OTHER_ORGANIZATIONS"),
+
+ {UINT32_MAX, NULL, NULL}
+};
+
+struct rid_sid_name nt_auth_map[] = {
+ NT_AUTH_MAP_ENTRY(10, "NTLM AUTHENTICATION"),
+ NT_AUTH_MAP_ENTRY(14, "SCHANNEL AUTHENTICATION"),
+ NT_AUTH_MAP_ENTRY(21, "DIGEST AUTHENTICATION"),
+
+ {UINT32_MAX, NULL, NULL}
+};
+
+struct rid_sid_name nt_this_map[] = {
+ NT_THIS_MAP_ENTRY(1, "THIS ORGANIZATION CERTIFICATE"),
+
+ {UINT32_MAX, NULL, NULL}
+};
+
+struct special_map sp_map[] = {
+ SPECIAL_MAP_ENTRY(0, 0, NULL_DOM_NAME, "NULL SID"),
+ SPECIAL_MAP_ENTRY(1, 0, WORLD_DOM_NAME, "Everyone"),
+ SPECIAL_MAP_ENTRY(2, 0, LOCAL_DOM_NAME, "LOCAL"),
+ SPECIAL_MAP_ENTRY(2, 1, LOCAL_DOM_NAME, "CONSOLE LOGON"),
+ SPECIAL_MAP_ENTRY(3, 0, CREATOR_DOM_NAME, "CREATOR OWNER"),
+ SPECIAL_MAP_ENTRY(3, 1, CREATOR_DOM_NAME, "CREATOR GROUP"),
+ SPECIAL_MAP_ENTRY(3, 2, CREATOR_DOM_NAME, "CREATOR OWNER SERVER"),
+ SPECIAL_MAP_ENTRY(3, 3, CREATOR_DOM_NAME, "CREATOR GROUP SERVER"),
+ SPECIAL_MAP_ENTRY(3, 4, CREATOR_DOM_NAME, "OWNER RIGHTS"),
+ SPECIAL_MAP_ENTRY(16, 0, MANDATORY_DOM_NAME, "UNTRUSTED"),
+ SPECIAL_MAP_ENTRY(16, 4096, MANDATORY_DOM_NAME, "LOW"),
+ SPECIAL_MAP_ENTRY(16, 8192, MANDATORY_DOM_NAME, "MEDIUM"),
+ SPECIAL_MAP_ENTRY(16, 8448, MANDATORY_DOM_NAME, "MEDIUM_PLUS"),
+ SPECIAL_MAP_ENTRY(16, 12288, MANDATORY_DOM_NAME, "HIGH"),
+ SPECIAL_MAP_ENTRY(16, 16384, MANDATORY_DOM_NAME, "SYSTEM"),
+ SPECIAL_MAP_ENTRY(16, 20480, MANDATORY_DOM_NAME, "PROTECTED_PROCESS"),
+ SPECIAL_MAP_ENTRY(16, 28672, MANDATORY_DOM_NAME, "SECURE_PROCESS"),
+ SPECIAL_MAP_ENTRY(18, 1, AUTHENTICATION_DOM_NAME, "AUTHENTICATION ASSERTION"),
+ SPECIAL_MAP_ENTRY(18, 2, AUTHENTICATION_DOM_NAME, "SERVICE ASSERTION"),
+ SPECIAL_MAP_ENTRY(18, 3, AUTHENTICATION_DOM_NAME, "FRESH_PUBLIC_KEY_IDENTITY"),
+ SPECIAL_MAP_ENTRY(18, 4, AUTHENTICATION_DOM_NAME, "KEY_TRUST_IDENTITY"),
+ SPECIAL_MAP_ENTRY(18, 5, AUTHENTICATION_DOM_NAME, "KEY_PROPERTY_MFA"),
+ SPECIAL_MAP_ENTRY(18, 6, AUTHENTICATION_DOM_NAME, "KEY_PROPERTY_ATTESTATION"),
+
+ {'\0', '\0', NULL, NULL, NULL}
+};
+
+static errno_t handle_special_sids(const char *sid, const char **dom,
+ const char **name)
+{
+ size_t c;
+ uint32_t rid;
+ uint32_t id_auth;
+ char *endptr;
+
+ id_auth = strtouint32(sid + SPECIAL_SID_PREFIX_LEN, &endptr, 10);
+ if (errno != 0 || *endptr != '-' || *(endptr + 1) == '\0') {
+ return EINVAL;
+ }
+
+ rid = strtouint32(endptr + 1, &endptr, 10);
+ if (errno != 0 || *endptr != '\0') {
+ return EINVAL;
+ }
+
+ for (c = 0; sp_map[c].name != NULL; c++) {
+ if (id_auth == sp_map[c].id_auth && rid == sp_map[c].rid) {
+ *name = sp_map[c].name;
+ *dom = sp_map[c].dom;
+ return EOK;
+ }
+ }
+
+ return EINVAL;
+}
+
+static errno_t handle_special_names(const char *dom, const char *name,
+ const char **sid)
+{
+ size_t c;
+
+ for (c = 0; sp_map[c].name != NULL; c++) {
+ if (strcmp(name, sp_map[c].name) == 0
+ && strcmp(dom, sp_map[c].dom) == 0) {
+ *sid = sp_map[c].sid;
+ return EOK;
+ }
+ }
+
+ return EINVAL;
+}
+
+static errno_t handle_rid_to_name_map(const char *sid, size_t prefix_len,
+ struct rid_sid_name *map,
+ const char* dom_name, const char **dom,
+ const char **name)
+{
+ uint32_t rid;
+ char *endptr;
+ size_t c;
+
+ rid = (uint32_t) strtouint32(sid + prefix_len, &endptr, 10);
+ if (errno != 0 || *endptr != '\0') {
+ return EINVAL;
+ }
+
+ for (c = 0; map[c].name != NULL; c++) {
+ if (rid == map[c].rid) {
+ *name = map[c].name;
+ *dom = dom_name;
+ return EOK;
+ }
+ }
+
+ return EINVAL;
+}
+
+static errno_t handle_name_to_sid_map(const char *name,
+ struct rid_sid_name *map,
+ const char **sid)
+{
+ size_t c;
+
+ for (c = 0; map[c].name != NULL; c++) {
+ if (strcmp(name, map[c].name) == 0) {
+ *sid = map[c].sid;
+ return EOK;
+ }
+ }
+
+ return EINVAL;
+}
+
+/* This function treats a particular case that does not fit in the normal table */
+static errno_t handle_nt_sids_particular_cases(const char *sid,
+ const char **dom,
+ const char **name)
+{
+ uint32_t rid;
+ char *endptr;
+ char *startptr;
+ int i;
+
+ rid = (uint32_t) strtouint32(sid + NT_SID_PREFIX_LEN, &endptr, 10);
+ if (errno != 0) {
+ return EINVAL;
+ }
+
+ if (rid == 5) {
+ /* S-1-5-5-x-y, we can ignore x and y, but they must be present.
+ * The x and y values for these SIDs are different for each logon
+ * session and are recycled when the operating system is restarted. */
+ for (i = 1; i <= 2; i++) {
+ if (*endptr != '-') {
+ return EINVAL;
+ }
+
+ startptr = endptr + 1;
+ (void) strtouint32(startptr, &endptr, 10);
+ if (errno != 0 || startptr == endptr) {
+ return EINVAL;
+ }
+ }
+ if (*endptr != '\0') {
+ return EINVAL;
+ }
+
+ *dom = NT_DOM_NAME;
+ *name = LOGON_ID_NAME;
+ return EOK;
+ }
+
+ return EINVAL;
+}
+
+static errno_t handle_nt_particular_cases(const char *name, const char **sid)
+{
+
+
+ if (strcmp(name, LOGON_ID_NAME) == 0) {
+ /* We return a model because the SID includes variable data */
+ *sid = LOGON_ID_MODEL;
+ return EOK;
+ }
+
+ return EINVAL;
+}
+
+static errno_t handle_nt_auth_sids(const char *sid, const char **dom,
+ const char **name)
+{
+ return handle_rid_to_name_map(sid, NT_AUTH_SID_PREFIX_LEN, nt_auth_map,
+ NT_AUTH_DOM_NAME, dom, name);
+}
+
+static errno_t handle_nt_auth_names(const char *name, const char **sid)
+{
+ return handle_name_to_sid_map(name, nt_auth_map, sid);
+}
+
+static errno_t handle_nt_this_sids(const char *sid, const char **dom,
+ const char **name)
+{
+ return handle_rid_to_name_map(sid, NT_THIS_SID_PREFIX_LEN, nt_this_map,
+ NT_THIS_DOM_NAME, dom, name);
+}
+
+static errno_t handle_nt_this_names(const char *name, const char **sid)
+{
+ return handle_name_to_sid_map(name, nt_this_map, sid);
+}
+
+static errno_t handle_nt_sids(const char *sid, const char **dom,
+ const char **name)
+{
+ errno_t ret;
+
+ ret = handle_rid_to_name_map(sid, NT_SID_PREFIX_LEN, nt_map, NT_DOM_NAME,
+ dom, name);
+ if (ret == EINVAL) {
+ // These are a particular cases that need to be treated separately
+ ret = handle_nt_sids_particular_cases(sid, dom, name);
+ }
+
+ return ret;
+}
+
+static errno_t handle_nt_names(const char *name, const char **sid)
+{
+ return handle_name_to_sid_map(name, nt_map, sid);
+}
+
+static errno_t handle_builtin_sids(const char *sid, const char **dom,
+ const char **name)
+{
+ return handle_rid_to_name_map(sid, BUILTIN_SID_PREFIX_LEN, builtin_map,
+ BUILTIN_DOM_NAME, dom, name);
+}
+
+static errno_t handle_builtin_names(const char *name, const char **sid)
+{
+ return handle_name_to_sid_map(name, builtin_map, sid);
+}
+
+errno_t well_known_sid_to_name(const char *sid, const char **dom,
+ const char **name)
+{
+ int ret;
+
+ if (sid == NULL || dom == NULL || name == NULL) {
+ return EINVAL;
+ }
+
+ if (strncmp(sid, DOM_SID_PREFIX, DOM_SID_PREFIX_LEN) == 0) {
+ ret = ENOENT;
+ } else if (strncmp(sid, BUILTIN_SID_PREFIX, BUILTIN_SID_PREFIX_LEN) == 0) {
+ ret = handle_builtin_sids(sid, dom, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "handle_builtin_sids failed for SID: %s\n", sid);
+ }
+ } else if (strncmp(sid, NT_AUTH_SID_PREFIX, NT_AUTH_SID_PREFIX_LEN) == 0) {
+ ret = handle_nt_auth_sids(sid, dom, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "handle_nt_auth_sids failed for SID: %s\n", sid);
+ }
+ } else if (strncmp(sid, NT_THIS_SID_PREFIX, NT_THIS_SID_PREFIX_LEN) == 0) {
+ ret = handle_nt_this_sids(sid, dom, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "handle_nt_this_sids failed for SID: %s\n", sid);
+ }
+ } else if (strncmp(sid, NT_SID_PREFIX, NT_SID_PREFIX_LEN) == 0) {
+ ret = handle_nt_sids(sid, dom, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "handle_nt_sids failed for SID: %s\n", sid);
+ }
+ } else if (strncmp(sid, SPECIAL_SID_PREFIX, SPECIAL_SID_PREFIX_LEN) == 0) {
+ ret = handle_special_sids(sid, dom, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "handle_special_sids failed for SID: %s\n", sid);
+ }
+ } else {
+ ret = EINVAL;
+ }
+
+ return ret;
+}
+
+errno_t name_to_well_known_sid(const char *dom, const char *name,
+ const char **sid)
+{
+ int ret;
+
+ if (sid == NULL || dom == NULL || name == NULL) {
+ return EINVAL;
+ }
+
+ if (strcmp(dom, NT_DOM_NAME) == 0) {
+ /* NT_DOM_NAME == NT_AUTH_DOM_NAME == NT_THIS_DOM_NAME */
+ ret = handle_nt_names(name, sid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "handle_nt_name failed.\n");
+ ret = handle_nt_auth_names(name, sid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "handle_nt_auth_names failed.\n");
+ ret = handle_nt_this_names(name, sid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "handle_nt_this_names failed.\n");
+ ret = handle_nt_particular_cases(name, sid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "handle_nt_particular_cases failed.\n");
+ }
+ }
+ }
+ }
+ } else if (strcmp(dom, BUILTIN_DOM_NAME) == 0) {
+ ret = handle_builtin_names(name, sid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "handle_builtin_name failed.\n");
+ }
+ } else if (strcmp(dom, NULL_DOM_NAME) == 0
+ || strcmp(dom, WORLD_DOM_NAME) == 0
+ || strcmp(dom, LOCAL_DOM_NAME) == 0
+ || strcmp(dom, CREATOR_DOM_NAME) == 0
+ || strcmp(dom, MANDATORY_DOM_NAME) == 0
+ || strcmp(dom, AUTHENTICATION_DOM_NAME) == 0) {
+ ret = handle_special_names(dom, name, sid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "handle_special_name failed.\n");
+ }
+ } else {
+ ret = ENOENT;
+ }
+
+ return ret;
+}