diff options
Diffstat (limited to '')
-rw-r--r-- | lib/util/util.c | 1335 |
1 files changed, 1335 insertions, 0 deletions
diff --git a/lib/util/util.c b/lib/util/util.c new file mode 100644 index 0000000..5f9310e --- /dev/null +++ b/lib/util/util.c @@ -0,0 +1,1335 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 2001-2002 + Copyright (C) Simo Sorce 2001-2011 + Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003. + Copyright (C) James J Myers 2003 + Copyright (C) Volker Lendecke 2010 + Copyright (C) Swen Schillig 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include <talloc.h> +#include <tevent.h> +#include "system/network.h" +#include "system/filesys.h" +#include "system/locale.h" +#include "system/shmem.h" +#include "system/passwd.h" +#include "system/time.h" +#include "system/wait.h" +#include "debug.h" +#include "samba_util.h" +#include "lib/util/select.h" +#include <libgen.h> +#include <gnutls/gnutls.h> + +#ifdef HAVE_SYS_PRCTL_H +#include <sys/prctl.h> +#endif + +#undef malloc +#undef strcasecmp +#undef strncasecmp +#undef strdup +#undef realloc +#undef calloc + +/** + * @file + * @brief Misc utility functions + */ + +/** + Find a suitable temporary directory. The result should be copied immediately + as it may be overwritten by a subsequent call. +**/ +_PUBLIC_ const char *tmpdir(void) +{ + char *p; + if ((p = getenv("TMPDIR"))) + return p; + return "/tmp"; +} + + +/** + Create a tmp file, open it and immediately unlink it. + If dir is NULL uses tmpdir() + Returns the file descriptor or -1 on error. +**/ +int create_unlink_tmp(const char *dir) +{ + size_t len = strlen(dir ? dir : (dir = tmpdir())); + char fname[len+25]; + int fd; + mode_t mask; + + len = snprintf(fname, sizeof(fname), "%s/listenerlock_XXXXXX", dir); + if (len >= sizeof(fname)) { + errno = ENOMEM; + return -1; + } + mask = umask(S_IRWXO | S_IRWXG); + fd = mkstemp(fname); + umask(mask); + if (fd == -1) { + return -1; + } + if (unlink(fname) == -1) { + int sys_errno = errno; + close(fd); + errno = sys_errno; + return -1; + } + return fd; +} + + +/** + Check if a file exists - call vfs_file_exist for samba files. +**/ +_PUBLIC_ bool file_exist(const char *fname) +{ + struct stat st; + + if (stat(fname, &st) != 0) { + return false; + } + + return ((S_ISREG(st.st_mode)) || (S_ISFIFO(st.st_mode))); +} + +/** + * @brief Return a files modification time. + * + * @param fname The name of the file. + * + * @param mt A pointer to store the modification time. + * + * @return 0 on success, errno otherwise. + */ +_PUBLIC_ int file_modtime(const char *fname, struct timespec *mt) +{ + struct stat st = {0}; + + if (stat(fname, &st) != 0) { + return errno; + } + + *mt = get_mtimespec(&st); + return 0; +} + +/** + Check file permissions. +**/ + +_PUBLIC_ bool file_check_permissions(const char *fname, + uid_t uid, + mode_t file_perms, + struct stat *pst) +{ + int ret; + struct stat st; + + if (pst == NULL) { + pst = &st; + } + + ZERO_STRUCTP(pst); + + ret = stat(fname, pst); + if (ret != 0) { + DEBUG(0, ("stat failed on file '%s': %s\n", + fname, strerror(errno))); + return false; + } + + if (pst->st_uid != uid && !uid_wrapper_enabled()) { + DEBUG(0, ("invalid ownership of file '%s': " + "owned by uid %u, should be %u\n", + fname, (unsigned int)pst->st_uid, + (unsigned int)uid)); + return false; + } + + if ((pst->st_mode & 0777) != file_perms) { + DEBUG(0, ("invalid permissions on file " + "'%s': has 0%o should be 0%o\n", fname, + (unsigned int)(pst->st_mode & 0777), + (unsigned int)file_perms)); + return false; + } + + return true; +} + +/** + Check if a directory exists. +**/ + +_PUBLIC_ bool directory_exist(const char *dname) +{ + struct stat st; + bool ret; + + if (stat(dname,&st) != 0) { + return false; + } + + ret = S_ISDIR(st.st_mode); + if(!ret) + errno = ENOTDIR; + return ret; +} + +/** + * Try to create the specified directory if it didn't exist. + * A symlink to a directory is also accepted as a valid existing directory. + * + * @retval true if the directory already existed + * or was successfully created. + */ +_PUBLIC_ bool directory_create_or_exist(const char *dname, + mode_t dir_perms) +{ + int ret; + mode_t old_umask; + + /* Create directory */ + old_umask = umask(0); + ret = mkdir(dname, dir_perms); + if (ret == -1 && errno != EEXIST) { + int dbg_level = geteuid() == 0 ? DBGLVL_ERR : DBGLVL_NOTICE; + + DBG_PREFIX(dbg_level, + ("mkdir failed on directory %s: %s\n", + dname, + strerror(errno))); + umask(old_umask); + return false; + } + umask(old_umask); + + if (ret != 0 && errno == EEXIST) { + struct stat sbuf; + + ret = lstat(dname, &sbuf); + if (ret != 0) { + return false; + } + + if (S_ISDIR(sbuf.st_mode)) { + return true; + } + + if (S_ISLNK(sbuf.st_mode)) { + ret = stat(dname, &sbuf); + if (ret != 0) { + return false; + } + + if (S_ISDIR(sbuf.st_mode)) { + return true; + } + } + + return false; + } + + return true; +} + +_PUBLIC_ bool directory_create_or_exists_recursive( + const char *dname, + mode_t dir_perms) +{ + bool ok; + + ok = directory_create_or_exist(dname, dir_perms); + if (!ok) { + if (!directory_exist(dname)) { + char tmp[PATH_MAX] = {0}; + char *parent = NULL; + size_t n; + + /* Use the null context */ + n = strlcpy(tmp, dname, sizeof(tmp)); + if (n < strlen(dname)) { + DBG_ERR("Path too long!\n"); + return false; + } + + parent = dirname(tmp); + if (parent == NULL) { + DBG_ERR("Failed to create dirname!\n"); + return false; + } + + ok = directory_create_or_exists_recursive(parent, + dir_perms); + if (!ok) { + return false; + } + + ok = directory_create_or_exist(dname, dir_perms); + } + } + + return ok; +} + +/** + * @brief Try to create a specified directory if it doesn't exist. + * + * The function creates a directory with the given uid and permissions if it + * doesn't exist. If it exists it makes sure the uid and permissions are + * correct and it will fail if they are different. + * + * @param[in] dname The directory to create. + * + * @param[in] uid The uid the directory needs to belong too. + * + * @param[in] dir_perms The expected permissions of the directory. + * + * @return True on success, false on error. + */ +_PUBLIC_ bool directory_create_or_exist_strict(const char *dname, + uid_t uid, + mode_t dir_perms) +{ + struct stat st; + bool ok; + int rc; + + ok = directory_create_or_exist(dname, dir_perms); + if (!ok) { + return false; + } + + rc = lstat(dname, &st); + if (rc == -1) { + DEBUG(0, ("lstat failed on created directory %s: %s\n", + dname, strerror(errno))); + return false; + } + + /* Check ownership and permission on existing directory */ + if (!S_ISDIR(st.st_mode)) { + DEBUG(0, ("directory %s isn't a directory\n", + dname)); + return false; + } + if (st.st_uid != uid && !uid_wrapper_enabled()) { + DBG_NOTICE("invalid ownership on directory " + "%s\n", dname); + return false; + } + if ((st.st_mode & 0777) != dir_perms) { + DEBUG(0, ("invalid permissions on directory " + "'%s': has 0%o should be 0%o\n", dname, + (unsigned int)(st.st_mode & 0777), (unsigned int)dir_perms)); + return false; + } + + return true; +} + + +/** + Sleep for a specified number of milliseconds. +**/ + +_PUBLIC_ void smb_msleep(unsigned int t) +{ + sys_poll_intr(NULL, 0, t); +} + +/** + Get my own name, return in talloc'ed storage. +**/ + +_PUBLIC_ char *get_myname(TALLOC_CTX *ctx) +{ + char *p; + char hostname[HOST_NAME_MAX]; + + /* get my host name */ + if (gethostname(hostname, sizeof(hostname)) == -1) { + DEBUG(0,("gethostname failed\n")); + return NULL; + } + + /* Ensure null termination. */ + hostname[sizeof(hostname)-1] = '\0'; + + /* split off any parts after an initial . */ + p = strchr_m(hostname, '.'); + if (p) { + *p = 0; + } + + return talloc_strdup(ctx, hostname); +} + +/** + Check if a process exists. Does this work on all unixes? +**/ + +_PUBLIC_ bool process_exists_by_pid(pid_t pid) +{ + /* Doing kill with a non-positive pid causes messages to be + * sent to places we don't want. */ + if (pid <= 0) { + return false; + } + return(kill(pid,0) == 0 || errno != ESRCH); +} + +/** + Simple routine to do POSIX file locking. Cruft in NFS and 64->32 bit mapping + is dealt with in posix.c +**/ + +_PUBLIC_ bool fcntl_lock(int fd, int op, off_t offset, off_t count, int type) +{ + struct flock lock; + int ret; + + DEBUG(8,("fcntl_lock %d %d %.0f %.0f %d\n",fd,op,(double)offset,(double)count,type)); + + lock.l_type = type; + lock.l_whence = SEEK_SET; + lock.l_start = offset; + lock.l_len = count; + lock.l_pid = 0; + + ret = fcntl(fd,op,&lock); + + if (ret == -1 && errno != 0) + DEBUG(3,("fcntl_lock: fcntl lock gave errno %d (%s)\n",errno,strerror(errno))); + + /* a lock query */ + if (op == F_GETLK) { + if ((ret != -1) && + (lock.l_type != F_UNLCK) && + (lock.l_pid != 0) && + (lock.l_pid != tevent_cached_getpid())) { + DEBUG(3,("fcntl_lock: fd %d is locked by pid %d\n",fd,(int)lock.l_pid)); + return true; + } + + /* it must be not locked or locked by me */ + return false; + } + + /* a lock set or unset */ + if (ret == -1) { + DEBUG(3,("fcntl_lock: lock failed at offset %.0f count %.0f op %d type %d (%s)\n", + (double)offset,(double)count,op,type,strerror(errno))); + return false; + } + + /* everything went OK */ + DEBUG(8,("fcntl_lock: Lock call successful\n")); + + return true; +} + +struct debug_channel_level { + int channel; + int level; +}; + +static void debugadd_channel_cb(const char *buf, void *private_data) +{ + struct debug_channel_level *dcl = + (struct debug_channel_level *)private_data; + + DEBUGADDC(dcl->channel, dcl->level,("%s", buf)); +} + +static void debugadd_cb(const char *buf, void *private_data) +{ + int *plevel = (int *)private_data; + DEBUGADD(*plevel, ("%s", buf)); +} + +void print_asc_cb(const uint8_t *buf, int len, + void (*cb)(const char *buf, void *private_data), + void *private_data) +{ + int i; + char s[2]; + s[1] = 0; + + for (i=0; i<len; i++) { + s[0] = isprint(buf[i]) ? buf[i] : '.'; + cb(s, private_data); + } +} + +void print_asc(int level, const uint8_t *buf,int len) +{ + print_asc_cb(buf, len, debugadd_cb, &level); +} + +static void dump_data_block16(const char *prefix, size_t idx, + const uint8_t *buf, size_t len, + void (*cb)(const char *buf, void *private_data), + void *private_data) +{ + char tmp[16]; + size_t i; + + SMB_ASSERT(len <= 16); + + snprintf(tmp, sizeof(tmp), "%s[%04zX]", prefix, idx); + cb(tmp, private_data); + + for (i=0; i<16; i++) { + if (i == 8) { + cb(" ", private_data); + } + if (i < len) { + snprintf(tmp, sizeof(tmp), " %02X", (int)buf[i]); + } else { + snprintf(tmp, sizeof(tmp), " "); + } + cb(tmp, private_data); + } + + cb(" ", private_data); + + if (len == 0) { + cb("EMPTY BLOCK\n", private_data); + return; + } + + for (i=0; i<len; i++) { + if (i == 8) { + cb(" ", private_data); + } + print_asc_cb(&buf[i], 1, cb, private_data); + } + + cb("\n", private_data); +} + +/** + * Write dump of binary data to a callback + */ +void dump_data_cb(const uint8_t *buf, int len, + bool omit_zero_bytes, + void (*cb)(const char *buf, void *private_data), + void *private_data) +{ + int i=0; + bool skipped = false; + + if (len<=0) return; + + for (i=0;i<len;i+=16) { + size_t remaining_len = len - i; + size_t this_len = MIN(remaining_len, 16); + const uint8_t *this_buf = &buf[i]; + + if ((omit_zero_bytes == true) && + (i > 0) && (remaining_len > 16) && + (this_len == 16) && all_zero(this_buf, 16)) + { + if (!skipped) { + cb("skipping zero buffer bytes\n", + private_data); + skipped = true; + } + continue; + } + + skipped = false; + dump_data_block16("", i, this_buf, this_len, + cb, private_data); + } +} + +/** + * Write dump of binary data to the log file. + * + * The data is only written if the log level is at least level. + */ +_PUBLIC_ void dump_data(int level, const uint8_t *buf, int len) +{ + if (!DEBUGLVL(level)) { + return; + } + dump_data_cb(buf, len, false, debugadd_cb, &level); +} + +/** + * Write dump of binary data to the log file. + * + * The data is only written if the log level is at least level for + * debug class dbgc_class. + */ +_PUBLIC_ void dump_data_dbgc(int dbgc_class, int level, const uint8_t *buf, int len) +{ + struct debug_channel_level dcl = { dbgc_class, level }; + + if (!DEBUGLVLC(dbgc_class, level)) { + return; + } + dump_data_cb(buf, len, false, debugadd_channel_cb, &dcl); +} + +/** + * Write dump of binary data to the log file. + * + * The data is only written if the log level is at least level. + * 16 zero bytes in a row are omitted + */ +_PUBLIC_ void dump_data_skip_zeros(int level, const uint8_t *buf, int len) +{ + if (!DEBUGLVL(level)) { + return; + } + dump_data_cb(buf, len, true, debugadd_cb, &level); +} + +static void fprintf_cb(const char *buf, void *private_data) +{ + FILE *f = (FILE *)private_data; + fprintf(f, "%s", buf); +} + +void dump_data_file(const uint8_t *buf, int len, bool omit_zero_bytes, + FILE *f) +{ + dump_data_cb(buf, len, omit_zero_bytes, fprintf_cb, f); +} + +/** + * Write dump of compared binary data to a callback + */ +void dump_data_diff_cb(const uint8_t *buf1, size_t len1, + const uint8_t *buf2, size_t len2, + bool omit_zero_bytes, + void (*cb)(const char *buf, void *private_data), + void *private_data) +{ + size_t len = MAX(len1, len2); + size_t i; + bool skipped = false; + + for (i=0; i<len; i+=16) { + size_t remaining_len = len - i; + size_t remaining_len1 = 0; + size_t this_len1 = 0; + const uint8_t *this_buf1 = NULL; + size_t remaining_len2 = 0; + size_t this_len2 = 0; + const uint8_t *this_buf2 = NULL; + + if (i < len1) { + remaining_len1 = len1 - i; + this_len1 = MIN(remaining_len1, 16); + this_buf1 = &buf1[i]; + } + if (i < len2) { + remaining_len2 = len2 - i; + this_len2 = MIN(remaining_len2, 16); + this_buf2 = &buf2[i]; + } + + if ((omit_zero_bytes == true) && + (i > 0) && (remaining_len > 16) && + (this_len1 == 16) && all_zero(this_buf1, 16) && + (this_len2 == 16) && all_zero(this_buf2, 16)) + { + if (!skipped) { + cb("skipping zero buffer bytes\n", + private_data); + skipped = true; + } + continue; + } + + skipped = false; + + if ((this_len1 == this_len2) && + (memcmp(this_buf1, this_buf2, this_len1) == 0)) + { + dump_data_block16(" ", i, this_buf1, this_len1, + cb, private_data); + continue; + } + + dump_data_block16("-", i, this_buf1, this_len1, + cb, private_data); + dump_data_block16("+", i, this_buf2, this_len2, + cb, private_data); + } +} + +_PUBLIC_ void dump_data_diff(int dbgc_class, int level, + bool omit_zero_bytes, + const uint8_t *buf1, size_t len1, + const uint8_t *buf2, size_t len2) +{ + struct debug_channel_level dcl = { dbgc_class, level }; + + if (!DEBUGLVLC(dbgc_class, level)) { + return; + } + dump_data_diff_cb(buf1, len1, buf2, len2, true, debugadd_channel_cb, &dcl); +} + +_PUBLIC_ void dump_data_file_diff(FILE *f, + bool omit_zero_bytes, + const uint8_t *buf1, size_t len1, + const uint8_t *buf2, size_t len2) +{ + dump_data_diff_cb(buf1, len1, buf2, len2, omit_zero_bytes, fprintf_cb, f); +} + +/** + malloc that aborts with smb_panic on fail or zero size. +**/ + +_PUBLIC_ void *smb_xmalloc(size_t size) +{ + void *p; + if (size == 0) + smb_panic("smb_xmalloc: called with zero size.\n"); + if ((p = malloc(size)) == NULL) + smb_panic("smb_xmalloc: malloc fail.\n"); + return p; +} + +/** + Memdup with smb_panic on fail. +**/ + +_PUBLIC_ void *smb_xmemdup(const void *p, size_t size) +{ + void *p2; + p2 = smb_xmalloc(size); + memcpy(p2, p, size); + return p2; +} + +/** + strdup that aborts on malloc fail. +**/ + +char *smb_xstrdup(const char *s) +{ +#if defined(PARANOID_MALLOC_CHECKER) +#ifdef strdup +#undef strdup +#endif +#endif + +#ifndef HAVE_STRDUP +#define strdup rep_strdup +#endif + + char *s1 = strdup(s); +#if defined(PARANOID_MALLOC_CHECKER) +#ifdef strdup +#undef strdup +#endif +#define strdup(s) __ERROR_DONT_USE_STRDUP_DIRECTLY +#endif + if (!s1) { + smb_panic("smb_xstrdup: malloc failed"); + } + return s1; + +} + +/** + strndup that aborts on malloc fail. +**/ + +char *smb_xstrndup(const char *s, size_t n) +{ +#if defined(PARANOID_MALLOC_CHECKER) +#ifdef strndup +#undef strndup +#endif +#endif + +#if (defined(BROKEN_STRNDUP) || !defined(HAVE_STRNDUP)) +#undef HAVE_STRNDUP +#define strndup rep_strndup +#endif + + char *s1 = strndup(s, n); +#if defined(PARANOID_MALLOC_CHECKER) +#ifdef strndup +#undef strndup +#endif +#define strndup(s,n) __ERROR_DONT_USE_STRNDUP_DIRECTLY +#endif + if (!s1) { + smb_panic("smb_xstrndup: malloc failed"); + } + return s1; +} + + + +/** + Like strdup but for memory. +**/ + +_PUBLIC_ void *smb_memdup(const void *p, size_t size) +{ + void *p2; + if (size == 0) + return NULL; + p2 = malloc(size); + if (!p2) + return NULL; + memcpy(p2, p, size); + return p2; +} + +/** + * Write a password to the log file. + * + * @note Only actually does something if DEBUG_PASSWORD was defined during + * compile-time. + */ +_PUBLIC_ void dump_data_pw(const char *msg, const uint8_t * data, size_t len) +{ +#ifdef DEBUG_PASSWORD + DEBUG(11, ("%s", msg)); + if (data != NULL && len > 0) + { + dump_data(11, data, len); + } +#endif +} + +static void dump_data_addbuf_cb(const char *buf, void *private_data) +{ + char **str = private_data; + talloc_asprintf_addbuf(str, "%s", buf); +} + +_PUBLIC_ void dump_data_addbuf(const uint8_t *buf, size_t buflen, char **str) +{ + dump_data_cb(buf, buflen, false, dump_data_addbuf_cb, str); +} + + +/** + * see if a range of memory is all zero. A NULL pointer is considered + * to be all zero + */ +_PUBLIC_ bool all_zero(const uint8_t *ptr, size_t size) +{ + size_t i; + if (!ptr) return true; + for (i=0;i<size;i++) { + if (ptr[i]) return false; + } + return true; +} + +/** + realloc an array, checking for integer overflow in the array size +*/ +_PUBLIC_ void *realloc_array(void *ptr, size_t el_size, unsigned count, bool free_on_fail) +{ +#define MAX_MALLOC_SIZE 0x7fffffff + if (count == 0 || + count >= MAX_MALLOC_SIZE/el_size) { + if (free_on_fail) + SAFE_FREE(ptr); + return NULL; + } + if (!ptr) { + return malloc(el_size * count); + } + return realloc(ptr, el_size * count); +} + +/**************************************************************************** + Type-safe malloc. +****************************************************************************/ + +void *malloc_array(size_t el_size, unsigned int count) +{ + return realloc_array(NULL, el_size, count, false); +} + +/**************************************************************************** + Type-safe memalign +****************************************************************************/ + +void *memalign_array(size_t el_size, size_t align, unsigned int count) +{ + if (el_size == 0 || count >= MAX_MALLOC_SIZE/el_size) { + return NULL; + } + + return memalign(align, el_size*count); +} + +/**************************************************************************** + Type-safe calloc. +****************************************************************************/ + +void *calloc_array(size_t size, size_t nmemb) +{ + if (nmemb >= MAX_MALLOC_SIZE/size) { + return NULL; + } + if (size == 0 || nmemb == 0) { + return NULL; + } + return calloc(nmemb, size); +} + +/** + Trim the specified elements off the front and back of a string. +**/ +_PUBLIC_ bool trim_string(char *s, const char *front, const char *back) +{ + bool ret = false; + size_t front_len; + size_t back_len; + size_t len; + + /* Ignore null or empty strings. */ + if (!s || (s[0] == '\0')) { + return false; + } + len = strlen(s); + + front_len = front? strlen(front) : 0; + back_len = back? strlen(back) : 0; + + if (front_len) { + size_t front_trim = 0; + + while (strncmp(s+front_trim, front, front_len)==0) { + front_trim += front_len; + } + if (front_trim > 0) { + /* Must use memmove here as src & dest can + * easily overlap. Found by valgrind. JRA. */ + memmove(s, s+front_trim, (len-front_trim)+1); + len -= front_trim; + ret=true; + } + } + + if (back_len) { + while ((len >= back_len) && strncmp(s+len-back_len,back,back_len)==0) { + s[len-back_len]='\0'; + len -= back_len; + ret=true; + } + } + return ret; +} + +/** + Find the number of 'c' chars in a string +**/ +_PUBLIC_ _PURE_ size_t count_chars(const char *s, char c) +{ + size_t count = 0; + + while (*s) { + if (*s == c) count++; + s ++; + } + + return count; +} + +/** + * Routine to get hex characters and turn them into a byte array. + * the array can be variable length. + * - "0xnn" or "0Xnn" is specially catered for. + * - The first non-hex-digit character (apart from possibly leading "0x" + * finishes the conversion and skips the rest of the input. + * - A single hex-digit character at the end of the string is skipped. + * + * valid examples: "0A5D15"; "0x123456" + */ +_PUBLIC_ size_t strhex_to_str(char *p, size_t p_len, const char *strhex, size_t strhex_len) +{ + size_t i = 0; + size_t num_chars = 0; + + /* skip leading 0x prefix */ + if (strncasecmp(strhex, "0x", 2) == 0) { + i += 2; /* skip two chars */ + } + + while ((i < strhex_len) && (num_chars < p_len)) { + bool ok = hex_byte(&strhex[i], (uint8_t *)&p[num_chars]); + if (!ok) { + break; + } + i += 2; + num_chars += 1; + } + + return num_chars; +} + +/** + * Parse a hex string and return a data blob. + */ +_PUBLIC_ DATA_BLOB strhex_to_data_blob(TALLOC_CTX *mem_ctx, const char *strhex) +{ + DATA_BLOB ret_blob = data_blob_talloc(mem_ctx, NULL, strlen(strhex)/2+1); + if (ret_blob.data == NULL) { + /* ret_blob.length is already 0 */ + return ret_blob; + } + ret_blob.length = strhex_to_str((char *)ret_blob.data, ret_blob.length, + strhex, + strlen(strhex)); + + return ret_blob; +} + +/** + * Parse a hex dump and return a data blob. Hex dump is structured as + * is generated from dump_data_cb() elsewhere in this file + * + */ +_PUBLIC_ DATA_BLOB hexdump_to_data_blob(TALLOC_CTX *mem_ctx, const char *hexdump, size_t hexdump_len) +{ + DATA_BLOB ret_blob = { 0 }; + size_t i = 0; + size_t char_count = 0; + /* hexdump line length is 77 chars long. We then use the ASCII representation of the bytes + * at the end of the final line to calculate how many are in that line, minus the extra space + * and newline. */ + size_t hexdump_byte_count = (16 * (hexdump_len / 77)); + if (hexdump_len % 77) { + hexdump_byte_count += ((hexdump_len % 77) - 59 - 2); + } + + ret_blob = data_blob_talloc(mem_ctx, NULL, hexdump_byte_count+1); + for (; i+1 < hexdump_len && hexdump[i] != 0 && hexdump[i+1] != 0; i++) { + if ((i%77) == 0) + i += 7; /* Skip the offset at the start of the line */ + if ((i%77) < 56) { /* position 56 is after both hex chunks */ + if (hexdump[i] != ' ') { + char_count += strhex_to_str((char *)&ret_blob.data[char_count], + hexdump_byte_count - char_count, + &hexdump[i], 2); + i += 2; + } else { + i++; + } + } else { + i++; + } + } + ret_blob.length = char_count; + + return ret_blob; +} + +/** + * Print a buf in hex. Assumes dst is at least (srclen*2)+1 large. + */ +_PUBLIC_ void hex_encode_buf(char *dst, const uint8_t *src, size_t srclen) +{ + size_t i; + for (i=0; i<srclen; i++) { + snprintf(dst + i*2, 3, "%02X", src[i]); + } + /* + * Ensure 0-termination for 0-length buffers + */ + dst[srclen*2] = '\0'; +} + +/** + * talloc version of hex_encode_buf() + */ +_PUBLIC_ char *hex_encode_talloc(TALLOC_CTX *mem_ctx, const unsigned char *buff_in, size_t len) +{ + char *hex_buffer; + + hex_buffer = talloc_array(mem_ctx, char, (len*2)+1); + if (!hex_buffer) { + return NULL; + } + hex_encode_buf(hex_buffer, buff_in, len); + talloc_set_name_const(hex_buffer, hex_buffer); + return hex_buffer; +} + +/** + variant of strcmp() that handles NULL ptrs +**/ +_PUBLIC_ int strcmp_safe(const char *s1, const char *s2) +{ + if (s1 == s2) { + return 0; + } + if (s1 == NULL || s2 == NULL) { + return s1?-1:1; + } + return strcmp(s1, s2); +} + + +/** +return the number of bytes occupied by a buffer in ASCII format +the result includes the null termination +limited by 'n' bytes +**/ +_PUBLIC_ size_t ascii_len_n(const char *src, size_t n) +{ + size_t len; + + len = strnlen(src, n); + if (len+1 <= n) { + len += 1; + } + + return len; +} + +_PUBLIC_ bool mem_equal_const_time(const void *s1, const void *s2, size_t n) +{ + /* Ensure we won't overflow the unsigned index used by gnutls. */ + SMB_ASSERT(n <= UINT_MAX); + + return gnutls_memcmp(s1, s2, n) == 0; +} + +struct anonymous_shared_header { + union { + size_t length; + uint8_t pad[16]; + } u; +}; + +/* Map a shared memory buffer of at least nelem counters. */ +void *anonymous_shared_allocate(size_t orig_bufsz) +{ + void *ptr; + void *buf; + size_t pagesz = getpagesize(); + size_t pagecnt; + size_t bufsz = orig_bufsz; + struct anonymous_shared_header *hdr; + + bufsz += sizeof(*hdr); + + /* round up to full pages */ + pagecnt = bufsz / pagesz; + if (bufsz % pagesz) { + pagecnt += 1; + } + bufsz = pagesz * pagecnt; + + if (orig_bufsz >= bufsz) { + /* integer wrap */ + errno = ENOMEM; + return NULL; + } + +#ifdef MAP_ANON + /* BSD */ + buf = mmap(NULL, bufsz, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, + -1 /* fd */, 0 /* offset */); +#else +{ + int saved_errno; + int fd; + + fd = open("/dev/zero", O_RDWR); + if (fd == -1) { + return NULL; + } + + buf = mmap(NULL, bufsz, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, + fd, 0 /* offset */); + saved_errno = errno; + close(fd); + errno = saved_errno; +} +#endif + + if (buf == MAP_FAILED) { + return NULL; + } + + hdr = (struct anonymous_shared_header *)buf; + hdr->u.length = bufsz; + + ptr = (void *)(&hdr[1]); + + return ptr; +} + +void *anonymous_shared_resize(void *ptr, size_t new_size, bool maymove) +{ +#ifdef HAVE_MREMAP + void *buf; + size_t pagesz = getpagesize(); + size_t pagecnt; + size_t bufsz; + struct anonymous_shared_header *hdr; + int flags = 0; + + if (ptr == NULL) { + errno = EINVAL; + return NULL; + } + + hdr = (struct anonymous_shared_header *)ptr; + hdr--; + if (hdr->u.length > (new_size + sizeof(*hdr))) { + errno = EINVAL; + return NULL; + } + + bufsz = new_size + sizeof(*hdr); + + /* round up to full pages */ + pagecnt = bufsz / pagesz; + if (bufsz % pagesz) { + pagecnt += 1; + } + bufsz = pagesz * pagecnt; + + if (new_size >= bufsz) { + /* integer wrap */ + errno = ENOSPC; + return NULL; + } + + if (bufsz <= hdr->u.length) { + return ptr; + } + + if (maymove) { + flags = MREMAP_MAYMOVE; + } + + buf = mremap(hdr, hdr->u.length, bufsz, flags); + + if (buf == MAP_FAILED) { + errno = ENOSPC; + return NULL; + } + + hdr = (struct anonymous_shared_header *)buf; + hdr->u.length = bufsz; + + ptr = (void *)(&hdr[1]); + + return ptr; +#else + errno = ENOSPC; + return NULL; +#endif +} + +void anonymous_shared_free(void *ptr) +{ + struct anonymous_shared_header *hdr; + + if (ptr == NULL) { + return; + } + + hdr = (struct anonymous_shared_header *)ptr; + + hdr--; + + munmap(hdr, hdr->u.length); +} + +#ifdef DEVELOPER +/* used when you want a debugger started at a particular point in the + code. Mostly useful in code that runs as a child process, where + normal gdb attach is harder to organise. +*/ +void samba_start_debugger(void) +{ + int ready_pipe[2]; + char c; + int ret; + pid_t pid; + + ret = pipe(ready_pipe); + SMB_ASSERT(ret == 0); + + pid = fork(); + SMB_ASSERT(pid >= 0); + + if (pid) { + c = 0; + + ret = close(ready_pipe[0]); + SMB_ASSERT(ret == 0); +#if defined(HAVE_PRCTL) && defined(PR_SET_PTRACER) + /* + * Make sure the child process can attach a debugger. + * + * We don't check the error code as the debugger + * will tell us if it can't attach. + */ + (void)prctl(PR_SET_PTRACER, pid, 0, 0, 0); +#endif + ret = write(ready_pipe[1], &c, 1); + SMB_ASSERT(ret == 1); + + ret = close(ready_pipe[1]); + SMB_ASSERT(ret == 0); + + /* Wait for gdb to attach. */ + sleep(2); + } else { + char *cmd = NULL; + + ret = close(ready_pipe[1]); + SMB_ASSERT(ret == 0); + + ret = read(ready_pipe[0], &c, 1); + SMB_ASSERT(ret == 1); + + ret = close(ready_pipe[0]); + SMB_ASSERT(ret == 0); + + ret = asprintf(&cmd, "gdb --pid %u", getppid()); + SMB_ASSERT(ret != -1); + + execlp("xterm", "xterm", "-e", cmd, (char *) NULL); + smb_panic("execlp() failed"); + } +} +#endif |