diff options
author | Stephen Gallagher <sgallagh@redhat.com> | 2010-02-18 07:49:04 -0500 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2010-02-18 13:48:45 -0500 |
commit | 1c48b5a62f73234ed26bb20f0ab345ab61cda0ab (patch) | |
tree | 0b6cddd567a862e1a7b5df23764869782a62ca78 /src/util | |
parent | 8c56df3176f528fe0260974b3bf934173c4651ea (diff) | |
download | sssd-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.tar.gz sssd-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.tar.xz sssd-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.zip |
Rename server/ directory to src/
Also update BUILD.txt
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/backup_file.c | 122 | ||||
-rw-r--r-- | src/util/check_and_open.c | 89 | ||||
-rw-r--r-- | src/util/crypto_sha512crypt.c | 382 | ||||
-rw-r--r-- | src/util/debug.c | 154 | ||||
-rw-r--r-- | src/util/dlinklist.h | 116 | ||||
-rw-r--r-- | src/util/find_uid.c | 296 | ||||
-rw-r--r-- | src/util/find_uid.h | 36 | ||||
-rw-r--r-- | src/util/memory.c | 67 | ||||
-rw-r--r-- | src/util/nss_sha512crypt.c | 419 | ||||
-rw-r--r-- | src/util/refcount.c | 88 | ||||
-rw-r--r-- | src/util/refcount.h | 39 | ||||
-rw-r--r-- | src/util/server.c | 433 | ||||
-rw-r--r-- | src/util/sha512crypt.h | 4 | ||||
-rw-r--r-- | src/util/signal.c | 146 | ||||
-rw-r--r-- | src/util/signal.m4 | 1 | ||||
-rw-r--r-- | src/util/sss_krb5.c | 196 | ||||
-rw-r--r-- | src/util/sss_krb5.h | 50 | ||||
-rw-r--r-- | src/util/sss_ldap.c | 70 | ||||
-rw-r--r-- | src/util/sss_ldap.h | 30 | ||||
-rw-r--r-- | src/util/strtonum.c | 65 | ||||
-rw-r--r-- | src/util/strtonum.h | 32 | ||||
-rw-r--r-- | src/util/user_info_msg.c | 51 | ||||
-rw-r--r-- | src/util/user_info_msg.h | 33 | ||||
-rw-r--r-- | src/util/usertools.c | 175 | ||||
-rw-r--r-- | src/util/util.c | 138 | ||||
-rw-r--r-- | src/util/util.h | 256 |
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__ */ |