/* $Id: fakesnprintf.h,v 1.1 1997/09/09 18:52:32 agulbra Exp $ */ /* * Not sure why the vendors supposedly selling robust Unix versions * have yet to implement the size-restricted variants of the stdio * formatting routines. Their lack is the single biggest cause of * buffer-overrun security problems. * * This code implements a secure replacement if your O/S: * * a. Doesn't have a real [v]snprintf() * b. Does have an mprotect(2) call that can be pointed * at arbitrary pages. * * It doesn't implement full [v]snprintf() semantics, just * sufficient to keep troll-ftpd happy. For example, if * an overrun occurs, the function will just return -1. * * The code is known to work on: * * Solaris 2.4 * Irix 5.3 * Ultrix 4.4 (with -DHAVE_GETPAGESIZE) * * The code is known NOT to work on: * * HP/UX 9.01 * AIX 3.2.5 */ #ifndef FAKESNPRINTF_H #define FAKESNPRINTF_H #include #include #include #include #include #include #include #include #include #include /* ** Insist on at least this much space for the format area. */ #define SPRINTF_MINAREA 1024 static int sys_pagesize(void) { #if defined(_SC_PAGESIZE) return (int)sysconf(_SC_PAGESIZE); #else #if defined(_SC_PAGE_SIZE) return (int)sysconf(_SC_PAGE_SIZE); #else #if defined(HAVE_GETPAGESIZE) return getpagesize(); #else #error Need to configure ftpd_getpagesize() in fake-snprintf.c #endif #endif #endif } static char *snprintf_area = 0; static int snprintf_areasize = 0; static void snprintf_init(void) { int pagesize, allocsize; char *area; unsigned long addr; snprintf_areasize = pagesize = sys_pagesize(); if(snprintf_areasize < SPRINTF_MINAREA) { if(SPRINTF_MINAREA % pagesize) snprintf_areasize = pagesize * (SPRINTF_MINAREA/pagesize + 1); else snprintf_areasize = SPRINTF_MINAREA; } allocsize = snprintf_areasize + pagesize*2; area = malloc(allocsize); if(!area) { printf("421 Out of memory in vsnprintf\r\n"); fflush(stdout); exit(6); } addr = (unsigned long)area; if(addr % pagesize) { int offset = pagesize - (addr % pagesize); addr += offset; area = (char *)addr; } if(mprotect((caddr_t)area + snprintf_areasize, pagesize, PROT_READ) < 0) { printf("421 Mprotect() failed -- %s\r\n", strerror(errno)); exit(6); } snprintf_area = area; } static void snprintf_catch(int sig) { printf("421 Buffer overrun detected\r\n"); exit(sig); } int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) { int len, lim; void (*disp)(int); if(size <= 0) return -1; buf[0] = 0; /* ** Minor optimization saves setting up signal handling */ if(!strchr(fmt, '%') && (len = strlen(fmt)) < (int)size-1) { strcpy(buf, fmt); return len; } if(!snprintf_area) snprintf_init(); disp = signal(SIGSEGV, snprintf_catch); len = vsprintf(snprintf_area, fmt, ap); signal(SIGSEGV, disp); if(len < 0) return len; lim = len; if(lim > (int)size - 1) lim = size - 1; snprintf_area[lim] = 0; strcpy(buf, snprintf_area); return len; } int snprintf(char *buf, size_t size, const char *fmt, ...) { int len; va_list ap; va_start(ap, fmt); len = vsnprintf(buf, size, fmt, ap); va_end(ap); return len; } #endif /* FAKESNPRINTF_H */