/* ** (c) Copyright 2003-2005 Matt Messier and John Viega ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** ** Redistributions of source code must retain the above copyright notice, ** this list of conditions and the following disclaimer. ** ** Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** ** Neither the name of the primary nor the names of the contributors may ** be used to endorse or promote products derived from this software ** without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "isafestr.h" #ifdef HAVE_TERMIOS_H #include #endif #ifdef HAVE_SIGNAL_H #include #endif #ifdef HAVE_PATHS_H #include #endif #ifdef WIN32 #include #define _PATH_TTY "CONIN$" #else #ifdef HAVE_PTHREAD #include #endif #endif #ifndef _PATH_TTY #define _PATH_TTY "/dev/tty" #endif #define SAFESTR_READLINE_SIZE (128 - (sizeof(small_isafestr_t) + 1)) typedef union safestr_integer_t { int32_t s32; int64_t s64; u_int32_t u32; u_int64_t u64; } safestr_integer_t; static char ascii_values[64] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, }; static int alpha_bytes[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 - 47 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 48 - 63 */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64 - 79 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 80 - 95 */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96 - 111 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112 - 127 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128 - 143 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144 - 159 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 160 - 175 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 176 - 191 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192 - 207 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 208 - 223 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 224 - 239 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 240 - 255 */ }; static int space_bytes[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0 - 15 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 16 - 31 */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 - 47 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 48 - 63 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64 - 79 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 95 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 - 111 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 112 - 127 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128 - 143 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144 - 159 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 160 - 175 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 176 - 191 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192 - 207 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 208 - 223 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 224 - 239 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 240 - 255 */ }; static void cleanup_safestr(safestr_t, void *); static void parse_integer(isafestr_t, safestr_integer_t *, int, int, int); #ifndef WIN32 static void restore_signals(sigset_t *, void *); static void restore_terminal(struct termios *, FILE *); #endif static void cleanup_safestr(safestr_t str, void *arg) { safestr_free(str); } static void parse_integer(isafestr_t istr, safestr_integer_t *result, int base, int size, int sign) { int negative = 0, value; char *cptr; unsigned char c; if (size != 32 && size != 64) XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_PARAMETER, NULL); if (base && (base < 2 || base > 36)) XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_PARAMETER, NULL); if (size == 32 && sign) result->s32 = 0; else if (size == 32) result->u32 = 0; else if (size == 64 && sign) result->s64 = 0; else if (size == 64) result->u64 = 0; cptr = istr->str; while (space_bytes[(int)(unsigned char)*cptr]) cptr++; if (*cptr == '+') cptr++; else if (*cptr == '-' && sign) { cptr++; negative = 1; } if (*cptr == '0' && (*(cptr + 1) == 'b' || *(cptr + 1) == 'B')) { if (base && base != 2) XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, NULL); base = 2; cptr += 2; } else if (*cptr == '0' && *(cptr + 1) >= '0' && *(cptr + 1) <= '7') { if (base && base != 8) XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, NULL); base = 8; cptr++; } else if (*cptr == '0' && (*(cptr + 1) == 'x' || *(cptr + 1) == 'X')) { if (base && base != 16) XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, NULL); base = 16; cptr += 2; } else { base = 10; } for (; *cptr; cptr++) { c = safestr_casemap_upper[(int)*cptr & 0xff]; if (c < 32 || c > sizeof(ascii_values) + 32) XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, NULL); if ((value = ascii_values[c - 32]) == -1) { if (space_bytes[(int)c]) break; XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, NULL); } if (value >= base) XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, NULL); if (size == 32 && sign) result->s32 = (result->s32 * base) + value; else if (size == 32) result->u32 = (result->u32 * base) + value; else if (size == 64 && sign) result->s64 = (result->s64 * base) + value; else if (size == 64) result->u64 = (result->u64 * base) + value; } if (sign && negative) { if (size == 32) result->s32 *= -1; else if (size == 64) result->s64 *= -1; } } #ifndef WIN32 static void restore_signals(sigset_t *saved_signals, void *arg) { #ifdef HAVE_PTHREAD pthread_sigmask(SIG_SETMASK, saved_signals, NULL); #endif } static void restore_terminal(struct termios *saved_term, FILE *term) { tcsetattr(fileno(term), TCSAFLUSH, saved_term); } #endif void safestr_convert(safestr_t str, u_int32_t flags) { char *cptr; u_int32_t i = 0; isafestr_t istr; XXL_ASSET_BLOCK_BEGIN { istr = safestr_get(str, SAFESTR_GET_WRITABLE); if (flags & SAFESTR_CONVERT_UPPERCASE) { for (cptr = istr->str; i < istr->hdr.length; i++, cptr++) *cptr = (char)safestr_casemap_upper[(int)(unsigned char)*cptr]; } else if (flags & SAFESTR_CONVERT_LOWERCASE) { for (cptr = istr->str; i < istr->hdr.length; i++, cptr++) *cptr = (char)safestr_casemap_lower[(int)(unsigned char)*cptr]; } else if (flags & SAFESTR_CONVERT_TITLECASE) { for (cptr = istr->str; i < istr->hdr.length; i++, cptr++) { if (i + 2 < istr->hdr.length && safestr_casemap_lower[(int)(unsigned char)*cptr] == 'm' && safestr_casemap_lower[(int)(unsigned char)*(cptr + 1)] == 'c') { *cptr = (char)safestr_casemap_upper[(int)(unsigned char)*cptr]; i++; cptr++; *cptr = (char)safestr_casemap_lower[(int)(unsigned char)*cptr]; i++; cptr++; *cptr = (char)safestr_casemap_upper[(int)(unsigned char)*cptr]; } else if (cptr == istr->str) { *cptr = (char)safestr_casemap_upper[(int)(unsigned char)*cptr]; } else if (i + 1 < istr->hdr.length && !alpha_bytes[(int)(unsigned char)*cptr]) { i++, cptr++; *cptr = (char)safestr_casemap_upper[(int)(unsigned char)*cptr]; } else { /* Last resort ... lowercase it */ *cptr = (char)safestr_casemap_lower[(int)(unsigned char)*cptr]; } } } } XXL_ASSET_BLOCK_END; } safestr_t safestr_do_getpassword(FILE *term, safestr_t prompt, const char *file, unsigned int lineno) { int done = 0, termfd; char ch; #ifndef WIN32 sigset_t saved_signals, set_signals; #endif safestr_t str; u_int32_t actual_length; isafestr_t iorig, iprompt, istr; #ifndef WIN32 struct termios saved_term, set_term; #endif XXL_ASSET_BLOCK_BEGIN { if (!term && !(term = xxl_fopen(_PATH_TTY, "r+", XXL_ASSET_TEMPORARY))) XXL_THROW_ERROR(errno, 0); termfd = fileno(term); iprompt = safestr_get(prompt, SAFESTR_GET_READONLY); fprintf(term, "%s", iprompt->str); fflush(term); #ifndef WIN32 /* Defer interrupt when echo is turned off */ sigemptyset(&set_signals); sigaddset(&set_signals, SIGINT); sigaddset(&set_signals, SIGTSTP); #ifdef HAVE_PTHREAD pthread_sigmask(SIG_BLOCK, &set_signals, &saved_signals); #endif XXL_ASSET_SAVE(&saved_signals, restore_signals, NULL, XXL_ASSET_TEMPORARY); /* Set the terminal to not echo */ tcgetattr(termfd, &saved_term); set_term = saved_term; set_term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); tcsetattr(termfd, TCSAFLUSH, &set_term); XXL_ASSET_SAVE(&saved_term, restore_terminal, term, XXL_ASSET_TEMPORARY); #endif /* Allocate a string to hold the user input */ str = safestr_do_alloc(SAFESTR_READLINE_SIZE, 0, file, lineno); XXL_ASSET_SAVE(str, cleanup_safestr, NULL, XXL_ASSET_PROMOTE); istr = iorig = safestr_get(str, SAFESTR_GET_WRITABLE); /* Read the password from the terminal user */ while (!done) { switch (read(termfd, &ch, 1)) { case 1: if (ch != '\n') break; /* FALL THROUGH */ case 0: istr->str[istr->hdr.length] = '\0'; done = 1; break; case -1: XXL_THROW_ERROR(errno, NULL); break; } istr->str[istr->hdr.length++] = ch; if (istr->hdr.length == istr->hdr.size) { actual_length = istr->hdr.length; istr = safestr_resize(istr, actual_length + SAFESTR_READLINE_SIZE); istr->hdr.length = actual_length; } } if (istr->str[istr->hdr.length - 1] == '\n') istr->str[--istr->hdr.length] = '\0'; putc('\n', term); str = safestr_complete(iorig, istr); } XXL_ASSET_BLOCK_END; return str; } safestr_t safestr_do_readline(FILE *f, const char *file, unsigned int lineno) { int eof_reached = 0; safestr_t str; u_int32_t actual_length; isafestr_t iorig, istr; XXL_ASSET_BLOCK_BEGIN { str = safestr_do_alloc(SAFESTR_READLINE_SIZE, 0, file, lineno); XXL_ASSET_SAVE(str, cleanup_safestr, NULL, XXL_ASSET_PROMOTE); istr = iorig = safestr_get(str, SAFESTR_GET_WRITABLE); while (!eof_reached) { if (!fgets(istr->str + istr->hdr.length, istr->hdr.size - istr->hdr.length + 1, f)) { if (!feof(f)) XXL_THROW_ERROR(errno, NULL); eof_reached = 1; break; } /* we have to play some games with string length here, because * safestr_resize() sets the length to the requested length, but * we don't actually want that. */ actual_length = istr->hdr.length + strlen(istr->str + istr->hdr.length); if (istr->str[actual_length - 1] == '\n') { istr->str[--actual_length] = '\0'; if (istr->str[actual_length - 1] == '\r') istr->str[--actual_length] = '\0'; istr->hdr.length = actual_length; break; } istr->hdr.length = actual_length; istr = safestr_resize(istr, actual_length + SAFESTR_READLINE_SIZE); istr->hdr.length = actual_length; } str = safestr_complete(iorig, istr); } XXL_ASSET_BLOCK_END; if (eof_reached) { safestr_free(str); return NULL; } return str; } double safestr_todouble(safestr_t str) { char *end; double result; isafestr_t istr; XXL_ASSET_BLOCK_BEGIN { istr = safestr_get(str, SAFESTR_GET_READONLY); result = strtod(istr->str, &end); if (end && (*end || end == istr->str)) XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, end); } XXL_ASSET_BLOCK_END; return result; } int32_t safestr_toint32(safestr_t str, int base) { safestr_integer_t result; XXL_ASSET_BLOCK_BEGIN { parse_integer(safestr_get(str, SAFESTR_GET_READONLY), &result, base, 32, 1); } XXL_ASSET_BLOCK_END; return result.s32; } int64_t safestr_toint64(safestr_t str, int base) { safestr_integer_t result; XXL_ASSET_BLOCK_BEGIN { parse_integer(safestr_get(str, SAFESTR_GET_READONLY), &result, base, 64, 1); } XXL_ASSET_BLOCK_END; return result.s64; } u_int32_t safestr_touint32(safestr_t str, int base) { safestr_integer_t result; XXL_ASSET_BLOCK_BEGIN { parse_integer(safestr_get(str, SAFESTR_GET_READONLY), &result, base, 32, 0); } XXL_ASSET_BLOCK_END; return result.u32; } u_int64_t safestr_touint64(safestr_t str, int base) { safestr_integer_t result; XXL_ASSET_BLOCK_BEGIN { parse_integer(safestr_get(str, SAFESTR_GET_READONLY), &result, base, 64, 0); } XXL_ASSET_BLOCK_END; return result.u64; } void safestr_trim(safestr_t str, u_int32_t flags) { char *cptr; u_int32_t length; isafestr_t istr; XXL_ASSET_BLOCK_BEGIN { istr = safestr_get(str, SAFESTR_GET_WRITABLE); length = istr->hdr.length; if (!(flags & (SAFESTR_TRIM_BOTH))) flags = SAFESTR_TRIM_BOTH; if (flags & SAFESTR_TRIM_LEADING) { for (cptr = istr->str; length > 0 && space_bytes[(int)(unsigned char)*cptr]; cptr++) { length--; } memmove(istr->str, cptr, length + 1); } if (flags & SAFESTR_TRIM_TRAILING) { while (length > 0 && space_bytes[(int)(unsigned char)istr->str[length - 1]]) length--; istr->str[length] = '\0'; } istr->hdr.length = length; } XXL_ASSET_BLOCK_END; }