summaryrefslogtreecommitdiffstats
path: root/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/util')
-rw-r--r--src/util/backup_file.c122
-rw-r--r--src/util/check_and_open.c89
-rw-r--r--src/util/crypto_sha512crypt.c382
-rw-r--r--src/util/debug.c154
-rw-r--r--src/util/dlinklist.h116
-rw-r--r--src/util/find_uid.c296
-rw-r--r--src/util/find_uid.h36
-rw-r--r--src/util/memory.c67
-rw-r--r--src/util/nss_sha512crypt.c419
-rw-r--r--src/util/refcount.c88
-rw-r--r--src/util/refcount.h39
-rw-r--r--src/util/server.c433
-rw-r--r--src/util/sha512crypt.h4
-rw-r--r--src/util/signal.c146
-rw-r--r--src/util/signal.m41
-rw-r--r--src/util/sss_krb5.c196
-rw-r--r--src/util/sss_krb5.h50
-rw-r--r--src/util/sss_ldap.c70
-rw-r--r--src/util/sss_ldap.h30
-rw-r--r--src/util/strtonum.c65
-rw-r--r--src/util/strtonum.h32
-rw-r--r--src/util/user_info_msg.c51
-rw-r--r--src/util/user_info_msg.h33
-rw-r--r--src/util/usertools.c175
-rw-r--r--src/util/util.c138
-rw-r--r--src/util/util.h256
26 files changed, 3488 insertions, 0 deletions
diff --git a/src/util/backup_file.c b/src/util/backup_file.c
new file mode 100644
index 000000000..cf9ddf303
--- /dev/null
+++ b/src/util/backup_file.c
@@ -0,0 +1,122 @@
+/*
+ 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 count;
+ ssize_t num;
+ ssize_t pos;
+ 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) {
+ num = read(src_fd, buf, BUFFER_SIZE);
+ if (num < 0) {
+ if (errno == EINTR) continue;
+ ret = errno;
+ DEBUG(dbglvl, ("Error (%d [%s]) reading from source %s\n",
+ ret, strerror(ret), src_file));
+ goto done;
+ }
+ if (num == 0) break;
+
+ count = num;
+
+ while (count > 0) {
+ pos = 0;
+ errno = 0;
+ num = write(dst_fd, &buf[pos], count);
+ if (num < 0) {
+ if (errno == EINTR) continue;
+ ret = errno;
+ DEBUG(dbglvl, ("Error (%d [%s]) writing to destination %s\n",
+ ret, strerror(ret), dst_file));
+ goto done;
+ }
+ pos += num;
+ count -= num;
+ }
+ }
+
+ 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/check_and_open.c b/src/util/check_and_open.c
new file mode 100644
index 000000000..5d5b57993
--- /dev/null
+++ b/src/util/check_and_open.c
@@ -0,0 +1,89 @@
+/*
+ SSSD
+
+ Check file permissions and open file
+
+ 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 <fcntl.h>
+#include <unistd.h>
+
+#include "util/util.h"
+
+errno_t check_and_open_readonly(const char *filename, int *fd, const uid_t uid,
+ const gid_t gid, const mode_t mode)
+{
+ int ret;
+ struct stat stat_buf;
+ struct stat fd_stat_buf;
+
+ *fd = -1;
+
+ ret = lstat(filename, &stat_buf);
+ if (ret == -1) {
+ DEBUG(1, ("lstat for [%s] failed: [%d][%s].\n", filename, errno,
+ strerror(errno)));
+ return errno;
+ }
+
+ if (!S_ISREG(stat_buf.st_mode)) {
+ DEBUG(1, ("File [%s] is not a regular file.\n", filename));
+ return EINVAL;
+ }
+
+ if ((stat_buf.st_mode & ~S_IFMT) != mode) {
+ DEBUG(1, ("File [%s] has the wrong mode [%.7o], expected [%.7o].\n",
+ filename, (stat_buf.st_mode & ~S_IFMT), mode));
+ return EINVAL;
+ }
+
+ if (stat_buf.st_uid != uid || stat_buf.st_gid != gid) {
+ DEBUG(1, ("File [%s] must be owned by uid [%d] and gid [%d].\n",
+ filename, uid, gid));
+ return EINVAL;
+ }
+
+ *fd = open(filename, O_RDONLY);
+ if (*fd == -1) {
+ DEBUG(1, ("open [%s] failed: [%d][%s].\n", filename, errno,
+ strerror(errno)));
+ return errno;
+ }
+
+ ret = fstat(*fd, &fd_stat_buf);
+ if (ret == -1) {
+ DEBUG(1, ("fstat for [%s] failed: [%d][%s].\n", filename, errno,
+ strerror(errno)));
+ return errno;
+ }
+
+ if (stat_buf.st_dev != fd_stat_buf.st_dev ||
+ stat_buf.st_ino != fd_stat_buf.st_ino) {
+ DEBUG(1, ("File [%s] was modified between lstat and open.\n", filename));
+ close(*fd);
+ *fd = -1;
+ return EIO;
+ }
+
+ return EOK;
+}
+
diff --git a/src/util/crypto_sha512crypt.c b/src/util/crypto_sha512crypt.c
new file mode 100644
index 000000000..9cd03a1e1
--- /dev/null
+++ b/src/util/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>. */
+
+#define _GNU_SOURCE
+#include <endian.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 <openssl/evp.h>
+#include <openssl/rand.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;
+}
+
+#define PTR_2_INT(x) ((x) - ((__typeof__ (x)) NULL))
+#define ALIGN64 __alignof__(uint64_t)
+
+static int sha512_crypt_r(const char *key,
+ const char *salt,
+ char *buffer, size_t buflen)
+{
+ unsigned char temp_result[64] __attribute__((__aligned__(ALIGN64)));
+ unsigned char alt_result[64] __attribute__((__aligned__(ALIGN64)));
+ size_t rounds = ROUNDS_DEFAULT;
+ bool rounds_custom = false;
+ EVP_MD_CTX alt_ctx;
+ EVP_MD_CTX ctx;
+ size_t salt_len;
+ size_t key_len;
+ size_t cnt;
+ char *copied_salt = NULL;
+ char *copied_key = NULL;
+ char *p_bytes = NULL;
+ char *s_bytes = NULL;
+ int p1, p2, p3, pt, n;
+ unsigned int part;
+ char *cp, *tmp;
+ 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;
+ srounds = strtoul(num, &endp, 10);
+ if (*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);
+
+ if ((PTR_2_INT(key) % ALIGN64) != 0) {
+ tmp = (char *)alloca(key_len + ALIGN64);
+ key = copied_key = memcpy(tmp + ALIGN64 - PTR_2_INT(tmp) % ALIGN64, key, key_len);
+ }
+
+ if (PTR_2_INT(salt) % ALIGN64 != 0) {
+ tmp = (char *)alloca(salt_len + ALIGN64);
+ salt = copied_salt = memcpy(tmp + ALIGN64 - PTR_2_INT(tmp) % ALIGN64, salt, salt_len);
+ }
+
+ EVP_MD_CTX_init(&ctx);
+
+ EVP_MD_CTX_init(&alt_ctx);
+
+ /* 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 every character in the password add the entire salt. */
+ 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 = __stpncpy(buffer, sha512_salt_prefix, 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_cleanup(&ctx);
+ EVP_MD_CTX_cleanup(&alt_ctx);
+ if (p_bytes) memset(p_bytes, '\0', key_len);
+ if (s_bytes) memset(s_bytes, '\0', salt_len);
+ if (copied_key) memset(copied_key, '\0', key_len);
+ if (copied_salt) memset(copied_salt, '\0', salt_len);
+ memset(temp_result, '\0', sizeof(temp_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;
+
+ salt = talloc_size(memctx, SALT_LEN_MAX + 1);
+ if (!salt) {
+ return ENOMEM;
+ }
+
+ ret = RAND_bytes(rb, SALT_RAND_LEN);
+ if (ret == 0) {
+ return EIO;
+ }
+
+ 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/debug.c b/src/util/debug.c
new file mode 100644
index 000000000..d26d31c95
--- /dev/null
+++ b/src/util/debug.c
@@ -0,0 +1,154 @@
+/*
+ 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/>.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "util/util.h"
+
+const char *debug_prg_name = "sssd";
+int debug_level = 0;
+int debug_timestamps = 1;
+
+int debug_to_file = 0;
+const char *debug_log_file = "sssd";
+FILE *debug_file = NULL;
+
+
+errno_t set_debug_file_from_fd(const int fd)
+{
+ FILE *dummy;
+
+ dummy = fdopen(fd, "a");
+ if (dummy == NULL) {
+ DEBUG(1, ("fdopen failed [%d][%s].\n", errno, strerror(errno)));
+ return errno;
+ }
+
+ debug_file = dummy;
+
+ return EOK;
+}
+
+void debug_fn(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ vfprintf(debug_file ? debug_file : stderr, format, ap);
+ fflush(debug_file ? debug_file : stderr);
+
+ va_end(ap);
+}
+
+void ldb_debug_messages(void *context, enum ldb_debug_level level,
+ const char *fmt, va_list ap)
+{
+ int loglevel = -1;
+ int ret;
+ char * message = NULL;
+
+ switch(level) {
+ case LDB_DEBUG_FATAL:
+ loglevel = 0;
+ break;
+ case LDB_DEBUG_ERROR:
+ loglevel = 1;
+ break;
+ case LDB_DEBUG_WARNING:
+ loglevel = 6;
+ break;
+ case LDB_DEBUG_TRACE:
+ loglevel = 9;
+ break;
+ }
+
+ ret = vasprintf(&message, fmt, ap);
+ if (ret < 0) {
+ /* ENOMEM */
+ return;
+ }
+
+ if (loglevel <= debug_level) {
+ if (debug_timestamps) {
+ time_t rightnow = time(NULL);
+ char stamp[25];
+ memcpy(stamp, ctime(&rightnow), 24);
+ stamp[24] = '\0';
+ debug_fn("(%s) [%s] [ldb] (%d): %s\n",
+ stamp, debug_prg_name, loglevel, message);
+ } else {
+ debug_fn("[%s] [ldb] (%d): %s\n",
+ debug_prg_name, loglevel, message);
+ }
+ }
+ free(message);
+}
+
+int open_debug_file_ex(const char *filename, FILE **filep)
+{
+ FILE *f = NULL;
+ char *logpath;
+ const char *log_file;
+ mode_t old_umask;
+ int 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;
+ }
+
+ if (debug_file && !filep) fclose(debug_file);
+
+ old_umask = umask(0177);
+ f = fopen(logpath, "a");
+ if (f == NULL) {
+ free(logpath);
+ return EIO;
+ }
+ umask(old_umask);
+
+ if (filep == NULL) {
+ debug_file = f;
+ } else {
+ *filep = f;
+ }
+ free(logpath);
+ return EOK;
+}
+
+int open_debug_file(void)
+{
+ return open_debug_file_ex(NULL, NULL);
+}
diff --git a/src/util/dlinklist.h b/src/util/dlinklist.h
new file mode 100644
index 000000000..be5ff914b
--- /dev/null
+++ b/src/util/dlinklist.h
@@ -0,0 +1,116 @@
+/*
+ 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) ; \
+ 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) ; \
+ tmp->next = (list2); \
+ if (list2) { \
+ (list2)->prev = tmp; \
+ } \
+ } \
+} while (0)
+
+#define DLIST_FOR_EACH(p, list) \
+ for ((p) = (list); (p) != NULL; (p) = (p)->next)
+
+#endif /* _DLINKLIST_H */
diff --git a/src/util/find_uid.c b/src/util/find_uid.c
new file mode 100644
index 000000000..965966ef3
--- /dev/null
+++ b/src/util/find_uid.c
@@ -0,0 +1,296 @@
+/*
+ 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/util.h"
+
+#define INITIAL_TABLE_SIZE 64
+#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)
+{
+ int ret;
+ char path[PATHLEN];
+ struct stat stat_buf;
+ int fd;
+ char buf[BUFSIZE];
+ char *p;
+ char *e;
+ char *endptr;
+ long num=0;
+
+ ret = snprintf(path, PATHLEN, "/proc/%d/status", pid);
+ if (ret < 0) {
+ DEBUG(1, ("snprintf failed"));
+ return EINVAL;
+ } else if (ret >= PATHLEN) {
+ DEBUG(1, ("path too long?!?!\n"));
+ return EINVAL;
+ }
+
+ ret = lstat(path, &stat_buf);
+ if (ret == -1) {
+ if (errno == ENOENT) {
+ DEBUG(7, ("Proc file [%s] is not available anymore, continuing.\n",
+ path));
+ return EOK;
+ }
+ DEBUG(1, ("lstat failed [%d][%s].\n", errno, strerror(errno)));
+ return errno;
+ }
+
+ if (!S_ISREG(stat_buf.st_mode)) {
+ DEBUG(1, ("not a regular file\n"));
+ return EINVAL;
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ if (errno == ENOENT) {
+ DEBUG(7, ("Proc file [%s] is not available anymore, continuing.\n",
+ path));
+ return EOK;
+ }
+ DEBUG(1, ("open failed [%d][%s].\n", errno, strerror(errno)));
+ return errno;
+ }
+ ret = read(fd, buf, BUFSIZE);
+ if (ret == -1) {
+ DEBUG(1, ("read failed [%d][%s].\n", errno, strerror(errno)));
+ return errno;
+ }
+
+ ret = close(fd);
+ if (ret == -1) {
+ DEBUG(1, ("close failed [%d][%s].\n", errno, strerror(errno)));
+ }
+
+ p = strstr(buf, "\nUid:\t");
+ if (p != NULL) {
+ p += 6;
+ e = strchr(p,'\t');
+ if (e == NULL) {
+ DEBUG(1, ("missing delimiter.\n"));
+ return EINVAL;
+ } else {
+ *e = '\0';
+ }
+ num = strtol(p, &endptr, 10);
+ if(errno == ERANGE) {
+ DEBUG(1, ("strtol failed [%s].\n", strerror(errno)));
+ return errno;
+ }
+ if (*endptr != '\0') {
+ DEBUG(1, ("uid contains extra characters\n"));
+ return EINVAL;
+ }
+
+ if (num < 0 || num >= INT_MAX) {
+ DEBUG(1, ("uid out of range.\n"));
+ return ERANGE;
+ }
+
+ } else {
+ DEBUG(1, ("format error\n"));
+ return EINVAL;
+ }
+
+ *uid = num;
+
+ return EOK;
+}
+
+static errno_t name_to_pid(const char *name, pid_t *pid)
+{
+ long num;
+ char *endptr;
+
+ errno = 0;
+ num = strtol(name, &endptr, 10);
+ if(errno == ERANGE) {
+ perror("strtol");
+ return errno;
+ }
+
+ if (*endptr != '\0') {
+ DEBUG(1, ("pid string contains extra characters.\n"));
+ return EINVAL;
+ }
+
+ if (num <= 0 || num >= INT_MAX) {
+ DEBUG(1, ("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;
+ pid_t pid = -1;
+ uid_t uid;
+
+ hash_key_t key;
+ hash_value_t value;
+
+ proc_dir = opendir("/proc");
+ if (proc_dir == NULL) {
+ DEBUG(1, ("Cannot open proc dir.\n"));
+ ret = errno;
+ 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(1, ("name_to_pid failed.\n"));
+ goto done;
+ }
+
+ ret = get_uid_from_pid(pid, &uid);
+ if (ret != EOK) {
+ DEBUG(1, ("get_uid_from_pid failed.\n"));
+ goto done;
+ }
+
+ 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(1, ("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 && dirent == NULL) {
+ DEBUG(1 ,("readdir failed.\n"));
+ ret = errno;
+ goto done;
+ }
+
+ ret = closedir(proc_dir);
+ proc_dir = NULL;
+ if (ret == -1) {
+ DEBUG(1 ,("closedir failed, watch out.\n"));
+ }
+
+ if (table != NULL) {
+ ret = EOK;
+ } else {
+ ret = ENOENT;
+ }
+
+done:
+ if (proc_dir != NULL) closedir(proc_dir);
+ return ret;
+}
+
+errno_t get_uid_table(TALLOC_CTX *mem_ctx, hash_table_t **table)
+{
+#ifdef __linux__
+ int ret;
+
+ ret = hash_create_ex(INITIAL_TABLE_SIZE, table, 0, 0, 0, 0,
+ hash_talloc, hash_talloc_free, mem_ctx,
+ NULL, NULL);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(1, ("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;
+
+ ret = get_active_uid_linux(NULL, uid);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(1, ("get_uid_table 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 000000000..154a5f955
--- /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/memory.c b/src/util/memory.c
new file mode 100644
index 000000000..a2c8b54b8
--- /dev/null
+++ b/src/util/memory.c
@@ -0,0 +1,67 @@
+/*
+ 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"
+
+/*
+ * 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.
+ */
+
+int password_destructor(void *memctx)
+{
+ char *password = (char *)memctx;
+ int i;
+
+ /* zero out password */
+ for (i = 0; password[i]; i++) password[i] = '\0';
+
+ return 0;
+}
+
+static int mem_holder_destructor(void *ptr)
+{
+ struct mem_holder *h;
+
+ h = talloc_get_type(ptr, struct mem_holder);
+ return h->fn(h->mem);
+}
+
+void *sss_mem_attach(TALLOC_CTX *mem_ctx,
+ void *ptr,
+ void_destructor_fn_t *fn)
+{
+ struct mem_holder *h;
+
+ if (!ptr || !fn) return NULL;
+
+ h = talloc(mem_ctx, struct mem_holder);
+ if (!h) return NULL;
+
+ h->mem = ptr;
+ h->fn = fn;
+ talloc_set_destructor((TALLOC_CTX *)h, mem_holder_destructor);
+
+ return h;
+}
diff --git a/src/util/nss_sha512crypt.c b/src/util/nss_sha512crypt.c
new file mode 100644
index 000000000..8ba16d4aa
--- /dev/null
+++ b/src/util/nss_sha512crypt.c
@@ -0,0 +1,419 @@
+/* This file is based on the work of Ulrich Drepper
+ * (http://people.redhat.com/drepper/SHA-crypt.txt). I have replaced the
+ * included SHA512 implementation by calls to NSS
+ * (http://www.mozilla.org/projects/security/pki/nss/).
+ *
+ * Sumit Bose <sbose@redhat.com>
+ */
+/* SHA512-based Unix crypt implementation.
+ Released into the Public Domain by Ulrich Drepper <drepper@redhat.com>. */
+
+#define _GNU_SOURCE
+#include <endian.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 <prinit.h>
+#include <nss.h>
+#include <sechash.h>
+#include <pk11func.h>
+
+static int nspr_nss_init_done = 0;
+
+static int nspr_nss_init(void)
+{
+ int ret;
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ ret = NSS_NoDB_Init(NULL);
+ if (ret != SECSuccess) {
+ return ret;
+ }
+ nspr_nss_init_done = 1;
+ return 0;
+}
+
+/* added for completness, so far not used */
+#if 0
+static int nspr_nss_cleanup(void)
+{
+ int ret;
+ ret = NSS_Shutdown();
+ if (ret != SECSuccess) {
+ return ret;
+ }
+ PR_Cleanup();
+ nspr_nss_init_done = 0;
+ return 0;
+}
+#endif
+
+/* 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;
+}
+
+#define PTR_2_INT(x) ((x) - ((__typeof__ (x)) NULL))
+#define ALIGN64 __alignof__(uint64_t)
+
+static int sha512_crypt_r(const char *key,
+ const char *salt,
+ char *buffer, size_t buflen)
+{
+ unsigned char temp_result[64] __attribute__((__aligned__(ALIGN64)));
+ unsigned char alt_result[64] __attribute__((__aligned__(ALIGN64)));
+ size_t rounds = ROUNDS_DEFAULT;
+ bool rounds_custom = false;
+ HASHContext *alt_ctx = NULL;
+ HASHContext *ctx = NULL;
+ size_t salt_len;
+ size_t key_len;
+ size_t cnt;
+ char *copied_salt = NULL;
+ char *copied_key = NULL;
+ char *p_bytes = NULL;
+ char *s_bytes = NULL;
+ int p1, p2, p3, pt, n;
+ unsigned int part;
+ char *cp, *tmp;
+ 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;
+ srounds = strtoul(num, &endp, 10);
+ if (*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);
+
+ if ((PTR_2_INT(key) % ALIGN64) != 0) {
+ tmp = (char *)alloca(key_len + ALIGN64);
+ key = copied_key = memcpy(tmp + ALIGN64 - PTR_2_INT(tmp) % ALIGN64, key, key_len);
+ }
+
+ if (PTR_2_INT(salt) % ALIGN64 != 0) {
+ tmp = (char *)alloca(salt_len + ALIGN64);
+ salt = copied_salt = memcpy(tmp + ALIGN64 - PTR_2_INT(tmp) % ALIGN64, salt, salt_len);
+ }
+
+ if (!nspr_nss_init_done) {
+ ret = nspr_nss_init();
+ if (ret != SECSuccess) {
+ ret = EIO;
+ goto done;
+ }
+ }
+
+ ctx = HASH_Create(HASH_AlgSHA512);
+ if (!ctx) {
+ ret = EIO;
+ goto done;
+ }
+
+ alt_ctx = HASH_Create(HASH_AlgSHA512);
+ if (!alt_ctx) {
+ ret = EIO;
+ goto done;
+ }
+
+ /* Prepare for the real work. */
+ HASH_Begin(ctx);
+
+ /* Add the key string. */
+ HASH_Update(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). */
+ HASH_Update(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. */
+ HASH_Begin(alt_ctx);
+
+ /* Add key. */
+ HASH_Update(alt_ctx, (const unsigned char *)key, key_len);
+
+ /* Add salt. */
+ HASH_Update(alt_ctx, (const unsigned char *)salt, salt_len);
+
+ /* Add key again. */
+ HASH_Update(alt_ctx, (const unsigned char *)key, key_len);
+
+ /* Now get result of this (64 bytes) and add it to the other context. */
+ HASH_End(alt_ctx, alt_result, &part, HASH_ResultLenContext(alt_ctx));
+
+ /* Add for any character in the key one byte of the alternate sum. */
+ for (cnt = key_len; cnt > 64; cnt -= 64) {
+ HASH_Update(ctx, alt_result, 64);
+ }
+ HASH_Update(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) {
+ HASH_Update(ctx, alt_result, 64);
+ } else {
+ HASH_Update(ctx, (const unsigned char *)key, key_len);
+ }
+ }
+
+ /* Create intermediate result. */
+ HASH_End(ctx, alt_result, &part, HASH_ResultLenContext(ctx));
+
+ /* Start computation of P byte sequence. */
+ HASH_Begin(alt_ctx);
+
+ /* For every character in the password add the entire password. */
+ for (cnt = 0; cnt < key_len; cnt++) {
+ HASH_Update(alt_ctx, (const unsigned char *)key, key_len);
+ }
+
+ /* Finish the digest. */
+ HASH_End(alt_ctx, temp_result, &part, HASH_ResultLenContext(alt_ctx));
+
+ /* 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. */
+ HASH_Begin(alt_ctx);
+
+ /* For every character in the password add the entire salt. */
+ for (cnt = 0; cnt < 16 + alt_result[0]; cnt++) {
+ HASH_Update(alt_ctx, (const unsigned char *)salt, salt_len);
+ }
+
+ /* Finish the digest. */
+ HASH_End(alt_ctx, temp_result, &part, HASH_ResultLenContext(alt_ctx));
+
+ /* 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++) {
+
+ HASH_Begin(ctx);
+
+ /* Add key or last result. */
+ if ((cnt & 1) != 0) {
+ HASH_Update(ctx, (const unsigned char *)p_bytes, key_len);
+ } else {
+ HASH_Update(ctx, alt_result, 64);
+ }
+
+ /* Add salt for numbers not divisible by 3. */
+ if (cnt % 3 != 0) {
+ HASH_Update(ctx, (const unsigned char *)s_bytes, salt_len);
+ }
+
+ /* Add key for numbers not divisible by 7. */
+ if (cnt % 7 != 0) {
+ HASH_Update(ctx, (const unsigned char *)p_bytes, key_len);
+ }
+
+ /* Add key or last result. */
+ if ((cnt & 1) != 0) {
+ HASH_Update(ctx, alt_result, 64);
+ } else {
+ HASH_Update(ctx, (const unsigned char *)p_bytes, key_len);
+ }
+
+ /* Create intermediate result. */
+ HASH_End(ctx, alt_result, &part, HASH_ResultLenContext(ctx));
+ }
+
+ /* Now we can construct the result string.
+ * It consists of three parts. */
+ if (buflen <= SALT_PREF_SIZE) {
+ ret = ERANGE;
+ goto done;
+ }
+
+ cp = __stpncpy(buffer, sha512_salt_prefix, 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. */
+ if (ctx) HASH_Destroy(ctx);
+ if (alt_ctx) HASH_Destroy(alt_ctx);
+ if (p_bytes) memset(p_bytes, '\0', key_len);
+ if (s_bytes) memset(s_bytes, '\0', salt_len);
+ if (copied_key) memset(copied_key, '\0', key_len);
+ if (copied_salt) memset(copied_salt, '\0', salt_len);
+ memset(temp_result, '\0', sizeof(temp_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;
+
+ if (!nspr_nss_init_done) {
+ ret = nspr_nss_init();
+ if (ret != SECSuccess) {
+ return EIO;
+ }
+ }
+
+ salt = talloc_size(memctx, SALT_LEN_MAX + 1);
+ if (!salt) {
+ return ENOMEM;
+ }
+
+ ret = PK11_GenerateRandom(rb, SALT_RAND_LEN);
+ if (ret != SECSuccess) {
+ return EIO;
+ }
+
+ 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/refcount.c b/src/util/refcount.c
new file mode 100644
index 000000000..735170d1b
--- /dev/null
+++ b/src/util/refcount.c
@@ -0,0 +1,88 @@
+/*
+ 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;
+
+ 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;
+ };
+
+ wrapper->refcount = (int *)((char *)wrapper->ptr + refcount_offset);
+ *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;
+
+ wrapper = talloc(context, struct wrapper);
+ if (wrapper == NULL) {
+ return NULL;
+ }
+
+ wrapper->ptr = source;
+ wrapper->refcount = (int *)((char *)wrapper->ptr + refcount_offset);
+ (*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 000000000..cc99a1731
--- /dev/null
+++ b/src/util/refcount.h
@@ -0,0 +1,39 @@
+#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/server.c b/src/util/server.c
new file mode 100644
index 000000000..fd6c46531
--- /dev/null
+++ b/src/util/server.c
@@ -0,0 +1,433 @@
+/*
+ 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/>.
+*/
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "util/util.h"
+#include "ldb.h"
+#include "confdb/confdb.h"
+
+#ifdef HAVE_PRCTL
+#include <sys/prctl.h>
+#endif
+
+/*******************************************************************
+ Close the low 3 fd's and open dev/null in their place.
+********************************************************************/
+static void close_low_fds(void)
+{
+#ifndef VALGRIND
+ int fd;
+ int i;
+
+ close(0);
+ close(1);
+ close(2);
+
+ /* try and use up these file descriptors, so silly
+ library routines writing to stdout etc won't cause havoc */
+ for (i=0;i<3;i++) {
+ fd = open("/dev/null",O_RDWR,0);
+ if (fd < 0)
+ fd = open("/dev/null",O_WRONLY,0);
+ if (fd < 0) {
+ DEBUG(0,("Can't open /dev/null\n"));
+ return;
+ }
+ if (fd != i) {
+ DEBUG(0,("Didn't get file descriptor %d\n",i));
+ return;
+ }
+ }
+#endif
+}
+
+/**
+ Become a daemon, discarding the controlling terminal.
+**/
+
+void become_daemon(bool Fork)
+{
+ int ret;
+
+ if (Fork) {
+ if (fork()) {
+ _exit(0);
+ }
+ }
+
+ /* detach from the terminal */
+ setsid();
+
+ /* chdir to / to be sure we're not on a remote filesystem */
+ errno = 0;
+ if(chdir("/") == -1) {
+ ret = errno;
+ DEBUG(0, ("Cannot change directory (%d [%s])\n",
+ ret, strerror(ret)));
+ return;
+ }
+
+ /* Close fd's 0,1,2. Needed if started by rsh */
+ close_low_fds();
+}
+
+int pidfile(const char *path, const char *name)
+{
+ char pid_str[32];
+ pid_t pid;
+ char *file;
+ int fd;
+ int ret, err;
+
+ file = talloc_asprintf(NULL, "%s/%s.pid", path, name);
+ if (!file) {
+ return ENOMEM;
+ }
+
+ fd = open(file, O_RDONLY, 0644);
+ err = errno;
+ if (fd != -1) {
+
+ pid_str[sizeof(pid_str) -1] = '\0';
+ ret = read(fd, pid_str, sizeof(pid_str) -1);
+ if (ret > 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) {
+ close(fd);
+ talloc_free(file);
+ return EEXIST;
+ }
+ if (ret != 0 && errno != ESRCH) {
+ err = errno;
+ close(fd);
+ talloc_free(file);
+ return err;
+ }
+ }
+ }
+
+ /* nothing in the file or no process */
+ close(fd);
+ unlink(file);
+
+ } else {
+ if (err != ENOENT) {
+ talloc_free(file);
+ return err;
+ }
+ }
+
+ fd = open(file, O_CREAT | O_WRONLY | O_EXCL, 0644);
+ err = errno;
+ if (fd == -1) {
+ talloc_free(file);
+ return err;
+ }
+ talloc_free(file);
+
+ memset(pid_str, 0, sizeof(pid_str));
+ snprintf(pid_str, sizeof(pid_str) -1, "%u\n", (unsigned int) getpid());
+
+ ret = write(fd, pid_str, strlen(pid_str));
+ err = errno;
+ if (ret != strlen(pid_str)) {
+ close(fd);
+ return err;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+static void sig_hup(int sig)
+{
+ /* cycle log/debug files */
+ return;
+}
+
+static void sig_term(int sig)
+{
+#if HAVE_GETPGRP
+ static int done_sigterm;
+ if (done_sigterm == 0 && getpgrp() == getpid()) {
+ DEBUG(0,("SIGTERM: killing children\n"));
+ done_sigterm = 1;
+ kill(-getpgrp(), SIGTERM);
+ }
+#endif
+ exit(0);
+}
+
+#ifndef HAVE_PRCTL
+static void sig_segv_abrt(int sig)
+{
+#if HAVE_GETPGRP
+ static int done;
+ if (done == 0 && getpgrp() == getpid()) {
+ DEBUG(0,("%s: killing children\n", strsignal(sig)));
+ done = 1;
+ kill(-getpgrp(), SIGTERM);
+ }
+#endif /* HAVE_GETPGRP */
+ exit(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);
+
+#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 recieve them. */
+ BlockSignals(false, SIGHUP);
+ BlockSignals(false, SIGTERM);
+
+ CatchSignal(SIGHUP, sig_hup);
+ CatchSignal(SIGTERM, sig_term);
+
+#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;
+ if (read(0, &c, 1) == 0) {
+ DEBUG(0,("%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(2, ("prctl failed [%d]: %s", ret, strerror(ret)));
+ return ret;
+ }
+#endif
+ return EOK;
+}
+
+int server_setup(const char *name, int flags,
+ const char *conf_entry,
+ struct main_context **main_ctx)
+{
+ struct tevent_context *event_ctx;
+ struct main_context *ctx;
+ uint16_t stdin_event_flags;
+ char *conf_db;
+ int ret = EOK;
+ bool dt;
+ bool dl;
+
+ debug_prg_name = strdup(name);
+ if (!debug_prg_name) {
+ return ENOMEM;
+ }
+
+ setenv("_SSS_LOOPS", "NO", 0);
+
+ setup_signals();
+
+ /* we want default permissions on created files to be very strict,
+ so set our umask to 0177 */
+ umask(0177);
+
+ if (flags & FLAGS_DAEMON) {
+ DEBUG(3,("Becoming a daemon.\n"));
+ become_daemon(true);
+ }
+
+ if (flags & FLAGS_PID_FILE) {
+ ret = pidfile(PID_PATH, name);
+ if (ret != EOK) {
+ DEBUG(0, ("Error creating pidfile! (%d [%s])\n",
+ ret, strerror(ret)));
+ return ret;
+ }
+ }
+
+ /* Set up locale */
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ /* the event context is the top level structure.
+ * Everything else should hang off that */
+ event_ctx = tevent_context_init(talloc_autofree_context());
+ if (event_ctx == NULL) {
+ DEBUG(0,("The event context initialiaziton failed\n"));
+ return 1;
+ }
+
+ ctx = talloc(event_ctx, struct main_context);
+ if (ctx == NULL) {
+ DEBUG(0,("Out of memory, aborting!\n"));
+ return ENOMEM;
+ }
+
+ ctx->event_ctx = event_ctx;
+
+ conf_db = talloc_asprintf(ctx, "%s/%s", DB_PATH, CONFDB_FILE);
+ if (conf_db == NULL) {
+ DEBUG(0,("Out of memory, aborting!\n"));
+ return ENOMEM;
+ }
+
+ ret = confdb_init(ctx, &ctx->confdb_ctx, conf_db);
+ if (ret != EOK) {
+ DEBUG(0,("The confdb initialization failed\n"));
+ return ret;
+ }
+
+ /* set debug level if any in conf_entry */
+ ret = confdb_get_int(ctx->confdb_ctx, ctx, conf_entry,
+ CONFDB_SERVICE_DEBUG_LEVEL,
+ debug_level, &debug_level);
+ if (ret != EOK) {
+ DEBUG(0, ("Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret)));
+ return ret;
+ }
+
+ /* same for debug timestamps */
+ dt = (debug_timestamps != 0);
+ ret = confdb_get_bool(ctx->confdb_ctx, ctx, conf_entry,
+ CONFDB_SERVICE_DEBUG_TIMESTAMPS,
+ dt, &dt);
+ if (ret != EOK) {
+ DEBUG(0, ("Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret)));
+ return ret;
+ }
+ if (dt) debug_timestamps = 1;
+
+ /* same for debug to file */
+ dl = (debug_to_file != 0);
+ ret = confdb_get_bool(ctx->confdb_ctx, ctx, conf_entry,
+ CONFDB_SERVICE_DEBUG_TO_FILES,
+ dl, &dl);
+ if (ret != EOK) {
+ DEBUG(0, ("Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret)));
+ return ret;
+ }
+ if (dl) debug_to_file = 1;
+
+ /* open log file if told so */
+ if (debug_to_file) {
+ ret = open_debug_file();
+ if (ret != EOK) {
+ DEBUG(0, ("Error setting up logging (%d) [%s]\n",
+ ret, strerror(ret)));
+ return ret;
+ }
+ }
+
+ DEBUG(3, ("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, 0, 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/sha512crypt.h b/src/util/sha512crypt.h
new file mode 100644
index 000000000..5512c5d96
--- /dev/null
+++ b/src/util/sha512crypt.h
@@ -0,0 +1,4 @@
+
+int s3crypt_sha512(TALLOC_CTX *mmectx,
+ const char *key, const char *salt, char **_hash);
+int s3crypt_gen_salt(TALLOC_CTX *memctx, char **_salt);
diff --git a/src/util/signal.c b/src/util/signal.c
new file mode 100644
index 000000000..e4a782da9
--- /dev/null
+++ b/src/util/signal.c
@@ -0,0 +1,146 @@
+/*
+ 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>
+
+
+/**
+ * @file
+ * @brief Signal handling
+ */
+
+/****************************************************************************
+ Catch child exits and reap the child zombie status.
+****************************************************************************/
+
+static void sig_cld(int signum)
+{
+ while (waitpid((pid_t)-1,(int *)NULL, WNOHANG) > 0)
+ ;
+
+ /*
+ * Turns out it's *really* important not to
+ * restore the signal handler here if we have real POSIX
+ * signal handling. If we do, then we get the signal re-delivered
+ * immediately - hey presto - instant loop ! JRA.
+ */
+
+#if !defined(HAVE_SIGACTION)
+ CatchSignal(SIGCLD, sig_cld);
+#endif
+}
+
+/****************************************************************************
+catch child exits - leave status;
+****************************************************************************/
+
+static void sig_cld_leave_status(int signum)
+{
+ /*
+ * Turns out it's *really* important not to
+ * restore the signal handler here if we have real POSIX
+ * signal handling. If we do, then we get the signal re-delivered
+ * immediately - hey presto - instant loop ! JRA.
+ */
+
+#if !defined(HAVE_SIGACTION)
+ CatchSignal(SIGCLD, sig_cld_leave_status);
+#endif
+}
+
+/**
+ 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(0,("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;
+
+ ZERO_STRUCT(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
+}
+
+/**
+ Ignore SIGCLD via whatever means is necessary for this OS.
+**/
+
+void CatchChild(void)
+{
+ CatchSignal(SIGCLD, sig_cld);
+}
+
+/**
+ Catch SIGCLD but leave the child around so it's status can be reaped.
+**/
+
+void CatchChildLeaveStatus(void)
+{
+ CatchSignal(SIGCLD, sig_cld_leave_status);
+}
diff --git a/src/util/signal.m4 b/src/util/signal.m4
new file mode 100644
index 000000000..747c7dbf3
--- /dev/null
+++ b/src/util/signal.m4
@@ -0,0 +1 @@
+AC_CHECK_FUNCS(sigprocmask sigblock sigaction getpgrp prctl)
diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c
new file mode 100644
index 000000000..0bc25df17
--- /dev/null
+++ b/src/util/sss_krb5.c
@@ -0,0 +1,196 @@
+/*
+ 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 <errno.h>
+#include <talloc.h>
+
+#include "config.h"
+
+#include "util/util.h"
+#include "util/sss_krb5.h"
+
+
+
+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 ret;
+ char *s = NULL;
+ int size = sizeof("Kerberos error [XXXXXXXXXXXX]");
+
+ s = malloc(sizeof(char) * (size));
+ if (s == NULL) {
+ return NULL;
+ }
+
+ ret = snprintf(s, size, "Kerberos error [%12d]", ec);
+
+ if (ret < 0 || ret >= size) {
+ return NULL;
+ }
+
+ return s;
+#endif
+}
+
+void KRB5_CALLCONV sss_krb5_free_error_message(krb5_context ctx, const char *s)
+{
+#ifdef HAVE_KRB5_GET_ERROR_MESSAGE
+ 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
+ krb5_free_unparsed_name(context, name);
+#else
+ if (name != NULL) {
+ memset(name, 0, strlen(name));
+ free(name);
+ }
+#endif
+}
+
+
+krb5_error_code check_for_valid_tgt(const char *ccname, const char *realm,
+ const char *client_princ_str, bool *result)
+{
+ krb5_context context = NULL;
+ krb5_ccache ccache = NULL;
+ krb5_error_code krberr;
+ TALLOC_CTX *tmp_ctx = NULL;
+ krb5_creds mcred;
+ krb5_creds cred;
+ char *server_name = NULL;
+ krb5_principal client_principal = NULL;
+ krb5_principal server_principal = NULL;
+
+ *result = false;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(1, ("talloc_new failed.\n"));
+ return ENOMEM;
+ }
+
+ krberr = krb5_init_context(&context);
+ if (krberr) {
+ DEBUG(1, ("Failed to init kerberos context\n"));
+ goto done;
+ }
+
+ krberr = krb5_cc_resolve(context, ccname, &ccache);
+ if (krberr != 0) {
+ DEBUG(1, ("krb5_cc_resolve failed.\n"));
+ goto done;
+ }
+
+ server_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm);
+ if (server_name == NULL) {
+ DEBUG(1, ("talloc_asprintf failed.\n"));
+ goto done;
+ }
+
+ krberr = krb5_parse_name(context, server_name, &server_principal);
+ if (krberr != 0) {
+ DEBUG(1, ("krb5_parse_name failed.\n"));
+ goto done;
+ }
+
+ krberr = krb5_parse_name(context, client_princ_str, &client_principal);
+ if (krberr != 0) {
+ DEBUG(1, ("krb5_parse_name failed.\n"));
+ goto done;
+ }
+
+ memset(&mcred, 0, sizeof(mcred));
+ memset(&cred, 0, sizeof(mcred));
+ mcred.client = client_principal;
+ mcred.server = server_principal;
+
+ krberr = krb5_cc_retrieve_cred(context, ccache, 0, &mcred, &cred);
+ if (krberr != 0) {
+ DEBUG(1, ("krb5_cc_retrieve_cred failed.\n"));
+ krberr = 0;
+ goto done;
+ }
+
+ DEBUG(7, ("TGT end time [%d].\n", cred.times.endtime));
+
+ if (cred.times.endtime > time(NULL)) {
+ DEBUG(3, ("TGT is valid.\n"));
+ *result = true;
+ }
+ krb5_free_cred_contents(context, &cred);
+
+ krberr = 0;
+
+done:
+ if (client_principal != NULL) {
+ krb5_free_principal(context, client_principal);
+ }
+ if (server_principal != NULL) {
+ krb5_free_principal(context, server_principal);
+ }
+ if (ccache != NULL) {
+ krb5_cc_close(context, ccache);
+ }
+ if (context != NULL) krb5_free_context(context);
+ talloc_free(tmp_ctx);
+ return krberr;
+}
+
diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h
new file mode 100644
index 000000000..60994e123
--- /dev/null
+++ b/src/util/sss_krb5.h
@@ -0,0 +1,50 @@
+/*
+ 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_KRB5_H__
+#define __SSS_KRB5_H__
+
+#include "config.h"
+
+#include <stdbool.h>
+
+#ifdef HAVE_KRB5_KRB5_H
+#include <krb5/krb5.h>
+#else
+#include <krb5.h>
+#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 *);
+
+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 check_for_valid_tgt(const char *ccname, const char *realm,
+ const char *client_princ_str, bool *result);
+#endif /* __SSS_KRB5_H__ */
diff --git a/src/util/sss_ldap.c b/src/util/sss_ldap.c
new file mode 100644
index 000000000..f098e7d6d
--- /dev/null
+++ b/src/util/sss_ldap.c
@@ -0,0 +1,70 @@
+/*
+ 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 <stdlib.h>
+
+#include "config.h"
+
+#include "util/sss_ldap.h"
+
+
+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
+}
diff --git a/src/util/sss_ldap.h b/src/util/sss_ldap.h
new file mode 100644
index 000000000..14747dffc
--- /dev/null
+++ b/src/util/sss_ldap.h
@@ -0,0 +1,30 @@
+/*
+ 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 <ldap.h>
+
+int sss_ldap_control_create(const char *oid, int iscritical,
+ struct berval *value, int dupval,
+ LDAPControl **ctrlp);
+
+#endif /* __SSS_LDAP_H__ */
diff --git a/src/util/strtonum.c b/src/util/strtonum.c
new file mode 100644
index 000000000..744e0f71a
--- /dev/null
+++ b/src/util/strtonum.c
@@ -0,0 +1,65 @@
+/*
+ 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 <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "config.h"
+#include "util/util.h"
+#include "util/strtonum.h"
+
+/* strtoint32 */
+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;
+}
+
+
+/* strtouint32 */
+uint32_t strtouint32(const char *nptr, char **endptr, int base)
+{
+ 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;
+}
diff --git a/src/util/strtonum.h b/src/util/strtonum.h
new file mode 100644
index 000000000..450959621
--- /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 <ctype.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+int32_t strtoint32(const char *nptr, char **endptr, int base);
+uint32_t strtouint32(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 000000000..547e3bb74
--- /dev/null
+++ b/src/util/user_info_msg.c
@@ -0,0 +1,51 @@
+/*
+ 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 "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;
+
+ 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(1, ("talloc_size failed.\n"));
+ return ENOMEM;
+ }
+
+ memcpy(resp, &resp_type, sizeof(uint32_t));
+ memcpy(resp + sizeof(uint32_t), &err_len, sizeof(uint32_t));
+ memcpy(resp + 2 * sizeof(uint32_t), user_error_message, err_len);
+
+ *_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 000000000..c68d538c2
--- /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 000000000..738ac62d7
--- /dev/null
+++ b/src/util/usertools.c
@@ -0,0 +1,175 @@
+/*
+ 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 <pcre.h>
+#include <errno.h>
+#include <talloc.h>
+
+#include "confdb/confdb.h"
+#include "util/util.h"
+
+#ifdef HAVE_LIBPCRE_LESSER_THAN_7
+#define NAME_DOMAIN_PATTERN_OPTIONS (PCRE_EXTENDED)
+#else
+#define NAME_DOMAIN_PATTERN_OPTIONS (PCRE_DUPNAMES | PCRE_EXTENDED)
+#endif
+
+char *get_username_from_uid(TALLOC_CTX *mem_ctx, uid_t uid)
+{
+ char *username;
+ struct passwd *pwd;
+
+ pwd = getpwuid(uid);
+ if (!pwd) return NULL;
+
+ username = talloc_strdup(mem_ctx, pwd->pw_name);
+ return username;
+}
+
+static int sss_names_ctx_destructor(struct sss_names_ctx *snctx)
+{
+ if (snctx->re) {
+ pcre_free(snctx->re);
+ snctx->re = NULL;
+ }
+ return 0;
+}
+
+int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, struct sss_names_ctx **out)
+{
+ struct sss_names_ctx *ctx;
+ const char *errstr;
+ int errval;
+ int errpos;
+ int ret;
+
+ ctx = talloc_zero(mem_ctx, struct sss_names_ctx);
+ if (!ctx) return ENOMEM;
+ talloc_set_destructor(ctx, sss_names_ctx_destructor);
+
+ ret = confdb_get_string(cdb, ctx, CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_NAME_REGEX, NULL, &ctx->re_pattern);
+ if (ret != EOK) goto done;
+
+ if (!ctx->re_pattern) {
+ ctx->re_pattern = talloc_strdup(ctx,
+ "(?P<name>[^@]+)@?(?P<domain>[^@]*$)");
+ if (!ctx->re_pattern) {
+ ret = ENOMEM;
+ goto done;
+ }
+#ifdef HAVE_LIBPCRE_LESSER_THAN_7
+ } else {
+ DEBUG(2, ("This binary was build with a version of libpcre that does "
+ "not support non-unique named subpatterns.\n"));
+ DEBUG(2, ("Please make sure that your pattern [%s] only contains "
+ "subpatterns with a unique name and uses "
+ "the Python syntax (?P<name>).\n", ctx->re_pattern));
+#endif
+ }
+
+ ret = confdb_get_string(cdb, ctx, CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_FULL_NAME_FORMAT, NULL, &ctx->fq_fmt);
+ if (ret != EOK) goto done;
+
+ if (!ctx->fq_fmt) {
+ ctx->fq_fmt = talloc_strdup(ctx, "%1$s@%2$s");
+ if (!ctx->fq_fmt) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ctx->re = pcre_compile2(ctx->re_pattern,
+ NAME_DOMAIN_PATTERN_OPTIONS,
+ &errval, &errstr, &errpos, NULL);
+ if (!ctx->re) {
+ DEBUG(1, ("Invalid Regular Expression pattern at position %d."
+ " (Error: %d [%s])\n", errpos, errval, errstr));
+ ret = EFAULT;
+ goto done;
+ }
+
+ *out = ctx;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(ctx);
+ }
+ return ret;
+}
+
+int sss_parse_name(TALLOC_CTX *memctx,
+ struct sss_names_ctx *snctx,
+ const char *orig, char **domain, char **name)
+{
+ pcre *re = snctx->re;
+ const char *result;
+ int ovec[30];
+ int origlen;
+ int ret, strnum;
+
+ origlen = strlen(orig);
+
+ ret = pcre_exec(re, NULL, orig, origlen, 0, PCRE_NOTEMPTY, ovec, 30);
+ if (ret < 0) {
+ DEBUG(2, ("PCRE Matching error, %d\n", ret));
+ return EINVAL;
+ }
+
+ if (ret == 0) {
+ DEBUG(1, ("Too many matches, the pattern is invalid.\n"));
+ }
+
+ strnum = ret;
+
+ result = NULL;
+ ret = pcre_get_named_substring(re, orig, ovec, strnum, "name", &result);
+ if (ret < 0 || !result) {
+ DEBUG(2, ("Name not found!\n"));
+ return EINVAL;
+ }
+ *name = talloc_strdup(memctx, result);
+ pcre_free_substring(result);
+ if (!*name) return ENOMEM;
+
+
+ result = NULL;
+ ret = pcre_get_named_substring(re, orig, ovec, strnum, "domain", &result);
+ if (ret < 0 || !result) {
+ DEBUG(4, ("Domain not provided!\n"));
+ *domain = NULL;
+ } else {
+ /* ignore "" string */
+ if (*result) {
+ *domain = talloc_strdup(memctx, result);
+ pcre_free_substring(result);
+ if (!*domain) return ENOMEM;
+ } else {
+ pcre_free_substring(result);
+ *domain = NULL;
+ }
+ }
+
+ return EOK;
+}
diff --git a/src/util/util.c b/src/util/util.c
new file mode 100644
index 000000000..57ceb597f
--- /dev/null
+++ b/src/util/util.c
@@ -0,0 +1,138 @@
+/*
+ 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 <ctype.h>
+
+#include "talloc.h"
+#include "util/util.h"
+
+/* split a string into an allocated array of strings.
+ * the separator is a string, and is case-sensitive.
+ * optionally single values can be trimmed of of spaces and tabs */
+int split_on_separator(TALLOC_CTX *mem_ctx, const char *str,
+ const char sep, bool trim, char ***_list, int *size)
+{
+ const char *t, *p, *n;
+ size_t l, len;
+ char **list, **r;
+ const char sep_str[2] = { sep, '\0'};
+
+ if (!str || !*str || !_list) return EINVAL;
+
+ t = str;
+
+ list = NULL;
+ l = 0;
+
+ /* trim leading whitespace */
+ if (trim)
+ while (isspace(*t)) t++;
+
+ /* find substrings separated by the separator */
+ while (t && (p = strpbrk(t, sep_str))) {
+ len = p - t;
+ n = p + 1; /* save next string starting point */
+ if (trim) {
+ /* strip whitespace after the separator
+ * so it's not in the next token */
+ while (isspace(*t)) {
+ t++;
+ len--;
+ if (len == 0) break;
+ }
+ p--;
+ /* strip whitespace before the separator
+ * so it's not in the current token */
+ while (len > 0 && (isspace(*p))) {
+ len--;
+ p--;
+ }
+ }
+
+ /* Add the token to the array, +2 b/c of the trailing NULL */
+ r = talloc_realloc(mem_ctx, list, char *, l + 2);
+ if (!r) {
+ talloc_free(list);
+ return ENOMEM;
+ } else {
+ list = r;
+ }
+
+ if (len == 0) {
+ list[l] = talloc_strdup(list, "");
+ } else {
+ list[l] = talloc_strndup(list, t, len);
+ }
+ if (!list[l]) {
+ talloc_free(list);
+ return ENOMEM;
+ }
+ l++;
+
+ t = n; /* move to next string */
+ }
+
+ /* Handle the last remaining token */
+ if (t) {
+ r = talloc_realloc(mem_ctx, list, char *, l + 2);
+ if (!r) {
+ talloc_free(list);
+ return ENOMEM;
+ } else {
+ list = r;
+ }
+
+ if (trim) {
+ /* trim leading whitespace */
+ len = strlen(t);
+ while (isspace(*t)) {
+ t++;
+ len--;
+ if (len == 0) break;
+ }
+ /* trim trailing whitespace */
+ p = t + len - 1;
+ while (len > 0 && (isspace(*p))) {
+ len--;
+ p--;
+ }
+
+ if (len == 0) {
+ list[l] = talloc_strdup(list, "");
+ } else {
+ list[l] = talloc_strndup(list, t, len);
+ }
+ } else {
+ list[l] = talloc_strdup(list, t);
+ }
+ if (!list[l]) {
+ talloc_free(list);
+ return ENOMEM;
+ }
+ l++;
+ }
+
+ list[l] = NULL; /* terminate list */
+
+ if (size) *size = l + 1;
+ *_list = list;
+
+ return EOK;
+}
diff --git a/src/util/util.h b/src/util/util.h
new file mode 100644
index 000000000..945e20d00
--- /dev/null
+++ b/src/util/util.h
@@ -0,0 +1,256 @@
+/*
+ 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 <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <libintl.h>
+#include <limits.h>
+#include <locale.h>
+#include <time.h>
+#include <pcre.h>
+#include <sys/types.h>
+
+#include "config.h"
+
+#include <talloc.h>
+#include <tevent.h>
+#include <ldb.h>
+
+#ifndef HAVE_ERRNO_T
+#define HAVE_ERRNO_T
+typedef int errno_t;
+#endif
+
+#define _(STRING) gettext (STRING)
+
+extern const char *debug_prg_name;
+extern int debug_level;
+extern int debug_timestamps;
+extern int debug_to_file;
+extern const char *debug_log_file;
+void debug_fn(const char *format, ...);
+errno_t set_debug_file_from_fd(const int fd);
+
+#define SSSD_DEBUG_OPTS \
+ {"debug-level", 'd', POPT_ARG_INT, &debug_level, 0, \
+ _("Debug level"), NULL}, \
+ {"debug-to-files", 'f', POPT_ARG_NONE, &debug_to_file, 0, \
+ _("Send the debug output to files instead of stderr"), NULL }, \
+ {"debug-timestamps", 0, POPT_ARG_INT, &debug_timestamps, 0, \
+ _("Add debug timestamps"), NULL},
+
+/** \def DEBUG(level, body)
+ \brief macro to generate debug messages
+
+ \param level the debug level, please respect the following guidelines:
+ - 1 is for critical errors users may find it difficult to understand but
+ are still quite clear
+ - 2-4 is for stuff developers are interested in in general, but
+ shouldn't fill the screen with useless low level verbose stuff
+ - 5-6 is for errors you may want to track, but only if you explicitly
+ looking for additional clues
+ - 7-10 is for informational stuff
+
+ \param body the debug message you want to send, should end with \n
+*/
+#define DEBUG(level, body) do { \
+ if (level <= debug_level) { \
+ if (debug_timestamps) { \
+ time_t rightnow = time(NULL); \
+ char stamp[25]; \
+ memcpy(stamp, ctime(&rightnow), 24); \
+ stamp[24] = '\0'; \
+ debug_fn("(%s) [%s] [%s] (%d): ", \
+ stamp, debug_prg_name, __FUNCTION__, level); \
+ } else { \
+ debug_fn("[%s] [%s] (%d): ", \
+ debug_prg_name, __FUNCTION__, level); \
+ } \
+ debug_fn body; \
+ } \
+} while(0);
+
+#define PRINT(fmt, ...) fprintf(stdout, gettext(fmt), ##__VA_ARGS__)
+#define ERROR(fmt, ...) fprintf(stderr, gettext(fmt), ##__VA_ARGS__)
+
+#ifndef discard_const
+#define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
+
+#define EOK 0
+
+#define SSSD_MAIN_OPTS SSSD_DEBUG_OPTS
+
+#define FLAGS_NONE 0x0000
+#define FLAGS_DAEMON 0x0001
+#define FLAGS_INTERACTIVE 0x0002
+#define FLAGS_PID_FILE 0x0004
+
+#ifndef talloc_zfree
+#define talloc_zfree(ptr) do { talloc_free(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
+
+/* TODO: remove later
+ * These functions are available in the latest tevent and are the ones that
+ * should be used as tevent_req is rightfully opaque there */
+#ifndef tevent_req_data
+#define tevent_req_data(req, type) ((type *)req->private_state)
+#define tevent_req_set_callback(req, func, data) \
+ do { req->async.fn = func; req->async.private_data = data; } while(0)
+#define tevent_req_callback_data(req, type) ((type *)req->async.private_data)
+#define tevent_req_notify_callback(req) \
+ do { \
+ if (req->async.fn != NULL) { \
+ req->async.fn(req); \
+ } \
+ } while(0)
+
+/* noop */
+#define tevent_loop_allow_nesting(x)
+#endif
+
+#define TEVENT_REQ_RETURN_ON_ERROR(req) do { \
+ enum tevent_req_state TRROEstate; \
+ uint64_t TRROEerr; \
+ \
+ if (tevent_req_is_error(req, &TRROEstate, &TRROEerr)) { \
+ if (TRROEstate == TEVENT_REQ_USER_ERROR) { \
+ return TRROEerr; \
+ } \
+ return EIO; \
+ } \
+} while (0)
+
+#define OUT_OF_ID_RANGE(id, min, max) \
+ (id == 0 || (min && (id < min)) || (max && (id > max)))
+
+#define COPY_MEM(to, from, ptr, size) do { \
+ memcpy(to, from, size); \
+ ptr += size; \
+} while(0)
+
+#define COPY_TYPE(to, from, ptr, type) COPY_MEM(to, from, ptr, sizeof(type))
+
+#define COPY_VALUE(to, value, ptr, type) do { \
+ type CV_MACRO_val = (type) value; \
+ COPY_TYPE(to, &CV_MACRO_val, ptr, type); \
+} while(0)
+
+#define COPY_UINT32(to, from, ptr) COPY_TYPE(to, from, ptr, uint32_t)
+#define COPY_UINT32_VALUE(to, value, ptr) COPY_VALUE(to, value, ptr, uint32_t)
+#define COPY_INT32(to, from, ptr) COPY_TYPE(to, from, ptr, int32_t)
+#define COPY_INT32_VALUE(to, value, ptr) COPY_VALUE(to, value, ptr, int32_t)
+
+#define COPY_UINT32_CHECK(to, from, ptr, size) do { \
+ if ((ptr + sizeof(uint32_t)) > size) return EINVAL; \
+ COPY_UINT32(to, from, ptr); \
+} while(0)
+
+#include "util/dlinklist.h"
+
+/* From debug.c */
+void ldb_debug_messages(void *context, enum ldb_debug_level level,
+ const char *fmt, va_list ap);
+int open_debug_file_ex(const char *filename, FILE **filep);
+int open_debug_file(void);
+
+/* from server.c */
+struct main_context {
+ struct tevent_context *event_ctx;
+ struct confdb_ctx *confdb_ctx;
+};
+
+int die_if_parent_died(void);
+int server_setup(const char *name, int flags,
+ const char *conf_entry,
+ struct main_context **main_ctx);
+void server_loop(struct main_context *main_ctx);
+
+/* from signal.c */
+#include <signal.h>
+void BlockSignals(bool block, int signum);
+void (*CatchSignal(int signum,void (*handler)(int )))(int);
+void CatchChild(void);
+void CatchChildLeaveStatus(void);
+
+/* from memory.c */
+typedef int (void_destructor_fn_t)(void *);
+
+struct mem_holder {
+ void *mem;
+ void_destructor_fn_t *fn;
+};
+
+void *sss_mem_attach(TALLOC_CTX *mem_ctx,
+ void *ptr,
+ void_destructor_fn_t *fn);
+
+int password_destructor(void *memctx);
+
+/* from usertools.c */
+char *get_username_from_uid(TALLOC_CTX *mem_ctx, uid_t uid);
+
+struct sss_names_ctx {
+ char *re_pattern;
+ char *fq_fmt;
+
+ pcre *re;
+};
+
+int sss_names_init(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *cdb,
+ struct sss_names_ctx **out);
+
+int sss_parse_name(TALLOC_CTX *memctx,
+ struct sss_names_ctx *snctx,
+ const char *orig, char **domain, char **name);
+
+/* from backup-file.c */
+int backup_file(const char *src, int dbglvl);
+
+/* from check_and_open.c */
+errno_t check_and_open_readonly(const char *filename, int *fd, const uid_t uid,
+ const gid_t gid, const mode_t mode);
+
+/* from util.c */
+int split_on_separator(TALLOC_CTX *mem_ctx, const char *str,
+ const char sep, bool trim, char ***_list, int *size);
+#endif /* __SSSD_UTIL_H__ */