#include #include #include #include #include #include #include #include #include #include "pdbg.h" #include "pmem.h" #include "putil.h" static char normalize_table[256]; const char putil_base64_table[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}; static const char base64_reverse_table[256] = { -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -1, -2, -2, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, 63, -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2}; __attribute((constructor)) static void init() { unsigned long i; for (i = 0; i < 256; i++) { normalize_table[i] = i; } normalize_table[':'] = '_'; normalize_table['/'] = '_'; normalize_table['\\'] = '_'; } // putil_wipe wipes a segment of memory mem of size sz using a DoD 5220.22-M // compliant 3-pass wipe. void putil_wipe(void *mem, size_t sz) { if (!mem || sz == 0) { return; } volatile unsigned char *p = (volatile unsigned char *)mem; memset((void*)p, 0x00, sz); memset((void*)p, 0xFF, sz); ssize_t result = getrandom((void*)p, sz, 0); if (result != (ssize_t)sz) { if (result == -1) { pdbg_logf(D_WARNING, "getrandom() failed: %s.", strerror(errno)); } else { pdbg_logf(D_WARNING, "getrandom() returned partial data."); } pdbg_logf(D_WARNING, "falling back to less secure third pass. This may occur due to insufficient entropy, early boot state, or kernel incompatibility."); srand((unsigned int)(time(NULL) ^ (uintptr_t)&srand)); for (size_t i = 0; i < sz; i++) { p[i] = (unsigned char)rand(); } } } void putil_time_format(time_t tm, unsigned long ns, char *result) { static const char month_names[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; static const char day_names[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; struct tm dt; unsigned long y; ns /= 1000000; gmtime_r(&tm, &dt); memcpy(result, day_names[dt.tm_wday], 3); result += 3; *result++ = ','; *result++ = ' '; *result++ = dt.tm_mday / 10 + '0'; *result++ = dt.tm_mday % 10 + '0'; *result++ = ' '; memcpy(result, month_names[dt.tm_mon], 3); result += 3; *result++ = ' '; y = dt.tm_year + 1900; *result++ = '0' + y / 1000; y = y % 1000; *result++ = '0' + y / 100; y = y % 100; *result++ = '0' + y / 10; y = y % 10; *result++ = '0' + y; *result++ = ' '; *result++ = dt.tm_hour / 10 + '0'; *result++ = dt.tm_hour % 10 + '0'; *result++ = ':'; *result++ = dt.tm_min / 10 + '0'; *result++ = dt.tm_min % 10 + '0'; *result++ = ':'; *result++ = dt.tm_sec / 10 + '0'; *result++ = dt.tm_sec % 10 + '0'; *result++ = '.'; *result++ = ns / 100 + '0'; *result++ = (ns / 10) % 10 + '0'; *result++ = ns % 10 + '0'; memcpy(result, " +0000", 7); // copies the null byte } char *putil_strdup(const char *str) { size_t len; char *ptr; len = strlen(str) + 1; ptr = (char *)pmem_malloc_array(PMEM_SUBSYS_OTHER, len, sizeof(char)); if (!ptr) return NULL; return (char *)memcpy(ptr, str, len); } char *putil_strnormalize_filename(const char *str) { size_t len, i; char *ptr; len = strlen(str) + 1; ptr = pmem_malloc_array(PMEM_SUBSYS_OTHER, len, sizeof(char)); for (i = 0; i < len; i++) ptr[i] = normalize_table[(unsigned char)str[i]]; return ptr; } char *putil_strndup(const char *str, size_t len) { char *ptr; ptr = (char *)memcpy(pmem_malloc_array(PMEM_SUBSYS_OTHER, len + 1, sizeof(char)), str, len); ptr[len] = 0; return ptr; } char *putil_strcat(const char *str, ...) { size_t i, size, len; const char *strs[64]; size_t lengths[64]; const char *ptr; char *ptr2, *ptr3; va_list ap; va_start(ap, str); strs[0] = str; len = strlen(str); lengths[0] = len; size = len + 1; i = 1; while ((ptr = va_arg(ap, const char *))) { pdbg_assert(i < ARRAY_SIZE(strs)); len = strlen(ptr); lengths[i] = len; strs[i++] = ptr; size += len; } va_end(ap); ptr2 = ptr3 = (char *)pmem_malloc(PMEM_SUBSYS_OTHER, size); for (size = 0; size < i; size++) { memcpy(ptr2, strs[size], lengths[size]); ptr2 += lengths[size]; } *ptr2 = 0; return ptr3; } int putil_slprintf(char *str, size_t size, const char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = vsnprintf(str, size, format, ap); va_end(ap); if (pdbg_unlikely(ret >= size)) str[size - 1] = 0; return ret; } unsigned char *putil_base32_encode(const unsigned char *str, size_t length, size_t *ret_length) { static const unsigned char *table = (const unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; unsigned char *result; unsigned char *p; uint32_t bits, buff; size_t temp, alloc_size; temp = (length + 4) / 5; if (temp != 0 && 8 > SIZE_MAX / temp) { return NULL; } alloc_size = temp * 8 + 1; result = (unsigned char *)pmem_malloc(PMEM_SUBSYS_OTHER, alloc_size); p = result; bits = 0; buff = 0; // don't really have to initialize this one, but a compiler that // will detect that this is safe is yet to be born while (length) { if (bits < 5) { buff = (buff << 8) | (*str++); length--; bits += 8; } bits -= 5; *p++ = table[0x1f & (buff >> bits)]; } while (bits) { if (bits < 5) { buff <<= (5 - bits); bits = 5; } bits -= 5; *p++ = table[0x1f & (buff >> bits)]; } *ret_length = p - result; *p = 0; return result; } unsigned char *putil_base32_decode(const unsigned char *str, size_t length, size_t *ret_length) { unsigned char *result, *p; uint32_t bits, buff; unsigned char ch; size_t temp, alloc_size; temp = (length + 7) / 8; if (temp != 0 && 5 > SIZE_MAX / temp) { return NULL; } alloc_size = temp * 5 + 1; result = (unsigned char *)pmem_malloc(PMEM_SUBSYS_OTHER, alloc_size); p = result; bits = 0; buff = 0; while (length) { ch = *str++; length--; if (ch >= 'A' && ch <= 'Z') ch = (ch & 0x1f) - 1; else if (ch >= '2' && ch <= '7') ch -= '2' - 26; else { pmem_free(PMEM_SUBSYS_OTHER, result); return NULL; } buff = (buff << 5) + ch; bits += 5; if (bits >= 8) { bits -= 8; *p++ = buff >> bits; } } *p = 0; *ret_length = p - result; return result; } unsigned char *putil_base64_encode(const unsigned char *str, size_t length, size_t *ret_length) { const unsigned char *current = str; unsigned char *p; unsigned char *result; size_t temp, alloc_size; temp = (length + 2) / 3; if (temp != 0 && 4 > SIZE_MAX / temp) { return NULL; } alloc_size = temp * 4 + 1; result = (unsigned char *)pmem_malloc(PMEM_SUBSYS_OTHER, alloc_size); p = result; while (length > 2) { *p++ = putil_base64_table[current[0] >> 2]; *p++ = putil_base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)]; *p++ = putil_base64_table[((current[1] & 0x0f) << 2) + (current[2] >> 6)]; *p++ = putil_base64_table[current[2] & 0x3f]; current += 3; length -= 3; } if (length != 0) { *p++ = putil_base64_table[current[0] >> 2]; if (length > 1) { *p++ = putil_base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)]; *p++ = putil_base64_table[(current[1] & 0x0f) << 2]; *p++ = '='; /* Add padding */ } else { *p++ = putil_base64_table[(current[0] & 0x03) << 4]; *p++ = '='; /* Add padding */ *p++ = '='; /* Add padding */ } } *ret_length = p - result; *p = 0; return result; } unsigned char *putil_base64_decode(const unsigned char *str, size_t length, size_t *ret_length) { const unsigned char *current = str; unsigned char *result; size_t i = 0, j = 0; ssize_t ch; size_t temp, alloc_size; temp = (length + 3) / 4; if (temp != 0 && 3 > SIZE_MAX / temp) { return NULL; } alloc_size = temp * 3 + 1; result = (unsigned char *)pmem_malloc(PMEM_SUBSYS_OTHER, alloc_size); while (length-- > 0) { ch = base64_reverse_table[*current++]; if (ch == -1) continue; else if (ch == -2) { pmem_free(PMEM_SUBSYS_OTHER, result); return NULL; } switch (i % 4) { case 0: result[j] = ch << 2; break; case 1: result[j++] |= ch >> 4; result[j] = (ch & 0x0f) << 4; break; case 2: result[j++] |= ch >> 2; result[j] = (ch & 0x03) << 6; break; case 3: result[j++] |= ch; break; } i++; } *ret_length = j; result[j] = 0; return result; } int putil_is_valid_utf8(const char *str) { static const int8_t trailing[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, -1, -1, -1, -1, -1, -1, -1, -1}; int8_t t; while (*str) { t = trailing[(unsigned char)*str++]; if (unlikely(t)) { if (t < 0) return 0; while (t--) if ((((unsigned char)*str++) & 0xc0) != 0x80) return 0; } } return 1; } uint64_t putil_ato64(const char *str) { uint64_t n = 0; while (*str >= '0' && *str <= '9') n = n * 10 + (*str++) - '0'; return n; } uint32_t putil_ato32(const char *str) { uint32_t n = 0; while (*str >= '0' && *str <= '9') n = n * 10 + (*str++) - '0'; return n; }