X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/d58c39ce13befcab7da81eb0f61c6e3c2652e44a..00b189d27cd235c958497d9b1aca6d3addd55d43:/smbutil.c diff --git a/smbutil.c b/smbutil.c index e3a939a7..0ec88233 100644 --- a/smbutil.c +++ b/smbutil.c @@ -1,693 +1,1897 @@ -/* - Copyright (C) Andrew Tridgell 1995-1999 - - This software may be distributed either under the terms of the - BSD-style license that accompanies tcpdump or the GNU GPL version 2 - or later */ +/* + * Copyright (C) Andrew Tridgell 1995-1999 + * + * This software may be distributed either under the terms of the + * BSD-style license that accompanies tcpdump or the GNU GPL version 2 + * or later + */ #ifdef HAVE_CONFIG_H #include "config.h" #endif -#include -#include -#include -#include - -#include +#include -#include -#include - -#include #include #include #include +#include "netdissect.h" +#include "extract.h" #include "smb.h" -extern uchar *startbuf; +static uint32_t stringlen; +extern const u_char *startbuf; -/******************************************************************* - interpret a 32 bit dos packed date/time to some parameters -********************************************************************/ -static void interpret_dos_date(uint32 date,int *year,int *month,int *day,int *hour,int *minute,int *second) +/* + * interpret a 32 bit dos packed date/time to some parameters + */ +static void +interpret_dos_date(uint32_t date, struct tm *tp) { - uint32 p0,p1,p2,p3; - - p0=date&0xFF; p1=((date&0xFF00)>>8)&0xFF; - p2=((date&0xFF0000)>>16)&0xFF; p3=((date&0xFF000000)>>24)&0xFF; - - *second = 2*(p0 & 0x1F); - *minute = ((p0>>5)&0xFF) + ((p1&0x7)<<3); - *hour = (p1>>3)&0xFF; - *day = (p2&0x1F); - *month = ((p2>>5)&0xFF) + ((p3&0x1)<<3) - 1; - *year = ((p3>>1)&0xFF) + 80; + uint32_t p0, p1, p2, p3; + + p0 = date & 0xFF; + p1 = ((date & 0xFF00) >> 8) & 0xFF; + p2 = ((date & 0xFF0000) >> 16) & 0xFF; + p3 = ((date & 0xFF000000) >> 24) & 0xFF; + + tp->tm_sec = 2 * (p0 & 0x1F); + tp->tm_min = ((p0 >> 5) & 0xFF) + ((p1 & 0x7) << 3); + tp->tm_hour = (p1 >> 3) & 0xFF; + tp->tm_mday = (p2 & 0x1F); + tp->tm_mon = ((p2 >> 5) & 0xFF) + ((p3 & 0x1) << 3) - 1; + tp->tm_year = ((p3 >> 1) & 0xFF) + 80; } -/******************************************************************* - create a unix date from a dos date -********************************************************************/ -time_t make_unix_date(void *date_ptr) +/* + * common portion: + * create a unix date from a dos date + */ +static time_t +int_unix_date(uint32_t dos_date) { - uint32 dos_date=0; - struct tm t; + struct tm t; - dos_date = IVAL(date_ptr,0); + if (dos_date == 0) + return(0); - if (dos_date == 0) return(0); - - interpret_dos_date(dos_date,&t.tm_year,&t.tm_mon, - &t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec); - t.tm_wday = 1; - t.tm_yday = 1; - t.tm_isdst = 0; + interpret_dos_date(dos_date, &t); + t.tm_wday = 1; + t.tm_yday = 1; + t.tm_isdst = 0; - return (mktime(&t)); + return (mktime(&t)); } -/******************************************************************* - create a unix date from a dos date -********************************************************************/ -time_t make_unix_date2(void *date_ptr) +/* + * create a unix date from a dos date + * in network byte order + */ +static time_t +make_unix_date(const u_char *date_ptr) { - uint32 x,x2; + uint32_t dos_date = 0; - x = IVAL(date_ptr,0); - x2 = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16); - SIVAL(&x,0,x2); + dos_date = EXTRACT_LE_U_4(date_ptr); - return(make_unix_date((void *)&x)); + return int_unix_date(dos_date); } -/**************************************************************************** -interpret an 8 byte "filetime" structure to a time_t -It's originally in "100ns units since jan 1st 1601" -****************************************************************************/ -time_t interpret_long_date(char *p) +/* + * create a unix date from a dos date + * in halfword-swapped network byte order! + */ +static time_t +make_unix_date2(const u_char *date_ptr) { - double d; - time_t ret; + uint32_t x, x2; - /* this gives us seconds since jan 1st 1601 (approx) */ - d = (IVAL(p,4)*256.0 + CVAL(p,3)) * (1.0e-7 * (1<<24)); - - /* now adjust by 369 years to make the secs since 1970 */ - d -= 369.0*365.25*24*60*60; + x = EXTRACT_LE_U_4(date_ptr); + x2 = ((x & 0xFFFF) << 16) | ((x & 0xFFFF0000) >> 16); + return int_unix_date(x2); +} - /* and a fudge factor as we got it wrong by a few days */ - d += (3*24*60*60 + 6*60*60 + 2); +/* + * interpret an 8 byte "filetime" structure to a time_t + * It's originally in "100ns units since jan 1st 1601" + */ +static time_t +interpret_long_date(const u_char *p) +{ + double d; + time_t ret; - if (d<0) - return(0); + /* this gives us seconds since jan 1st 1601 (approx) */ + d = (EXTRACT_LE_U_4(p + 4) * 256.0 + p[3]) * (1.0e-7 * (1 << 24)); - ret = (time_t)d; + /* now adjust by 369 years to make the secs since 1970 */ + d -= 369.0 * 365.25 * 24 * 60 * 60; - return(ret); -} + /* and a fudge factor as we got it wrong by a few days */ + d += (3 * 24 * 60 * 60 + 6 * 60 * 60 + 2); + + if (d < 0) + return(0); + + ret = (time_t)d; + return(ret); +} -/**************************************************************************** -interpret the weird netbios "name". Return the name type -****************************************************************************/ -static int name_interpret(char *in,char *out) +/* + * interpret the weird netbios "name". Return the name type, or -1 if + * we run past the end of the buffer + */ +static int +name_interpret(netdissect_options *ndo, + const u_char *in, const u_char *maxbuf, char *out) { - int ret; - int len = (*in++) / 2; + int ret; + int len; - *out=0; + if (in >= maxbuf) + return(-1); /* name goes past the end of the buffer */ + ND_TCHECK_1(in); + len = EXTRACT_U_1(in) / 2; + in++; - if (len > 30 || len<1) return(0); + *out=0; - while (len--) - { - if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') { - *out = 0; + if (len > 30 || len < 1) return(0); - } - *out = ((in[0]-'A')<<4) + (in[1]-'A'); - in += 2; - out++; + + while (len--) { + ND_TCHECK_2(in); + if (in + 1 >= maxbuf) + return(-1); /* name goes past the end of the buffer */ + if (EXTRACT_U_1(in) < 'A' || EXTRACT_U_1(in) > 'P' || + EXTRACT_U_1(in + 1) < 'A' || EXTRACT_U_1(in + 1) > 'P') { + *out = 0; + return(0); + } + *out = ((in[0] - 'A') << 4) + (in[1] - 'A'); + in += 2; + out++; } - *out = 0; - ret = out[-1]; + *out = 0; + ret = out[-1]; - return(ret); + return(ret); + +trunc: + return(-1); } -/**************************************************************************** -find a pointer to a netbios name -****************************************************************************/ -static char *name_ptr(char *buf,int ofs) +/* + * find a pointer to a netbios name + */ +static const u_char * +name_ptr(netdissect_options *ndo, + const u_char *buf, int ofs, const u_char *maxbuf) { - unsigned char c = *(unsigned char *)(buf+ofs); - - if ((c & 0xC0) == 0xC0) - { - uint16 l = RSVAL(buf, ofs) & 0x3FFF; - return(buf + l); + const u_char *p; + u_char c; + + p = buf + ofs; + if (p >= maxbuf) + return(NULL); /* name goes past the end of the buffer */ + ND_TCHECK_1(p); + + c = *p; + + /* XXX - this should use the same code that the DNS dissector does */ + if ((c & 0xC0) == 0xC0) { + uint16_t l; + + ND_TCHECK_2(p); + if ((p + 1) >= maxbuf) + return(NULL); /* name goes past the end of the buffer */ + l = EXTRACT_BE_U_2(p) & 0x3FFF; + if (l == 0) { + /* We have a pointer that points to itself. */ + return(NULL); + } + p = buf + l; + if (p >= maxbuf) + return(NULL); /* name goes past the end of the buffer */ + ND_TCHECK_1(p); } - else - return(buf+ofs); -} - -/**************************************************************************** -extract a netbios name from a buf -****************************************************************************/ -static int name_extract(char *buf,int ofs,char *name) + return(p); + +trunc: + return(NULL); /* name goes past the end of the buffer */ +} + +/* + * extract a netbios name from a buf + */ +static int +name_extract(netdissect_options *ndo, + const u_char *buf, int ofs, const u_char *maxbuf, char *name) { - char *p = name_ptr(buf,ofs); - int d = PTR_DIFF(p,buf+ofs); - strcpy(name,""); - return(name_interpret(p,name)); -} - - -/**************************************************************************** -return the total storage length of a mangled name -****************************************************************************/ -static int name_len(unsigned char *s) + const u_char *p = name_ptr(ndo, buf, ofs, maxbuf); + if (p == NULL) + return(-1); /* error (probably name going past end of buffer) */ + name[0] = '\0'; + return(name_interpret(ndo, p, maxbuf, name)); +} + + +/* + * return the total storage length of a mangled name + */ +static int +name_len(netdissect_options *ndo, + const unsigned char *s, const unsigned char *maxbuf) { - char *s0 = s; - unsigned char c = *(unsigned char *)s; - if ((c & 0xC0) == 0xC0) - return(2); - while (*s) s += (*s)+1; - return(PTR_DIFF(s,s0)+1); + const unsigned char *s0 = s; + unsigned char c; + + if (s >= maxbuf) + return(-1); /* name goes past the end of the buffer */ + ND_TCHECK_1(s); + c = *s; + if ((c & 0xC0) == 0xC0) + return(2); + while (*s) { + if (s >= maxbuf) + return(-1); /* name goes past the end of the buffer */ + ND_TCHECK_1(s); + s += (*s) + 1; + ND_TCHECK_1(s); + } + return(PTR_DIFF(s, s0) + 1); + +trunc: + return(-1); /* name goes past the end of the buffer */ } -void print_asc(unsigned char *buf,int len) +static void +print_asc(netdissect_options *ndo, + const unsigned char *buf, int len) { - int i; - for (i=0;i8) printf(" "); - while (n--) printf(" "); - - n = MIN(8,i%16); - print_asc(&buf[i-(i%16)],n); printf(" "); - n = (i%16) - n; - if (n>0) print_asc(&buf[i-n],n); - printf("\n"); - } + if (i % 16) { + int n; + + n = 16 - (i % 16); + ND_PRINT((ndo, " ")); + if (n>8) + ND_PRINT((ndo, " ")); + while (n--) + ND_PRINT((ndo, " ")); + + n = min(8, i % 16); + print_asc(ndo, buf + i - (i % 16), n); + ND_PRINT((ndo, " ")); + n = (i % 16) - n; + if (n > 0) + print_asc(ndo, buf + i - n, n); + ND_PRINT((ndo, "\n")); + } + return; + +trunc: + ND_PRINT((ndo, "\n")); + ND_PRINT((ndo, "WARNING: Short packet. Try increasing the snap length\n")); } -static void write_bits(unsigned int val,char *fmt) +static void +write_bits(netdissect_options *ndo, + unsigned int val, const char *fmt) { - char *p = fmt; - int i=0; - - while ((p=strchr(fmt,'|'))) { - int l = PTR_DIFF(p,fmt); - if (l && (val & (1<= MAX_UNISTR_SIZE) + break; + if (ND_ISPRINT(EXTRACT_U_1(s))) + buf[l] = s[0]; + else { + if (EXTRACT_U_1(s) == 0) + break; + buf[l] = '.'; + } + l++; + s++; + strsize--; } - - while (l < (sizeof(buf)-1) && s[0] && s[1] == 0) { + } else { + while (strsize != 0) { + ND_TCHECK_2(s); + if (l >= MAX_UNISTR_SIZE) + break; + if (EXTRACT_U_1(s + 1) == 0 && ND_ISPRINT(EXTRACT_U_1(s))) { + /* It's a printable ASCII character */ buf[l] = s[0]; - s += 2; l++; - *len += 2; + } else { + /* It's a non-ASCII character or a non-printable ASCII character */ + if (EXTRACT_U_1(s) == 0 && EXTRACT_U_1(s + 1) == 0) + break; + buf[l] = '.'; + } + l++; + s += 2; + if (strsize == 1) + break; + strsize -= 2; } - buf[l] = 0; - *len += 2; - return buf; + } + buf[l] = 0; + return buf; + +trunc: + return NULL; } -uchar *fdata1(uchar *buf,char *fmt,uchar *maxbuf) +static const u_char * +smb_fdata1(netdissect_options *ndo, + const u_char *buf, const char *fmt, const u_char *maxbuf, + int unicodestr) { - int reverse=0; - char *attrib_fmt = "READONLY|HIDDEN|SYSTEM|VOLUME|DIR|ARCHIVE|"; - int len; - - while (*fmt && buf sizeof(bitfmt) - 1) + l = sizeof(bitfmt)-1; + + strncpy(bitfmt, fmt, l); + bitfmt[l] = '\0'; + fmt = p + 1; + ND_TCHECK_1(buf); + write_bits(ndo, EXTRACT_U_1(buf), bitfmt); + buf++; + break; + } + + case 'P': + { + int l = atoi(fmt + 1); + ND_TCHECK2(buf[0], l); + buf += l; + fmt++; + while (isdigit((unsigned char)*fmt)) + fmt++; + break; + } + case 'r': + reverse = !reverse; + fmt++; + break; + case 'b': + { + unsigned int x; + ND_TCHECK_1(buf); + x = buf[0]; + ND_PRINT((ndo, "%u (0x%x)", x, x)); + buf += 1; + fmt++; + break; + } + case 'd': + { + unsigned int x; + ND_TCHECK_2(buf); + x = reverse ? EXTRACT_BE_U_2(buf) : + EXTRACT_LE_U_2(buf); + ND_PRINT((ndo, "%d (0x%x)", x, x)); + buf += 2; + fmt++; + break; + } + case 'D': + { + unsigned int x; + ND_TCHECK_4(buf); + x = reverse ? EXTRACT_BE_U_4(buf) : + EXTRACT_LE_U_4(buf); + ND_PRINT((ndo, "%d (0x%x)", x, x)); + buf += 4; + fmt++; + break; + } + case 'L': + { + uint64_t x; + ND_TCHECK_8(buf); + x = reverse ? EXTRACT_BE_U_8(buf) : + EXTRACT_LE_U_8(buf); + ND_PRINT((ndo, "%" PRIu64 " (0x%" PRIx64 ")", x, x)); + buf += 8; + fmt++; + break; + } + case 'M': + { + /* Weird mixed-endian length values in 64-bit locks */ + uint32_t x1, x2; + uint64_t x; + ND_TCHECK_8(buf); + x1 = reverse ? EXTRACT_BE_U_4(buf) : + EXTRACT_LE_U_4(buf); + x2 = reverse ? EXTRACT_BE_U_4(buf + 4) : + EXTRACT_LE_U_4(buf + 4); + x = (((uint64_t)x1) << 32) | x2; + ND_PRINT((ndo, "%" PRIu64 " (0x%" PRIx64 ")", x, x)); + buf += 8; + fmt++; + break; + } + case 'B': + { + unsigned int x; + ND_TCHECK_1(buf); + x = buf[0]; + ND_PRINT((ndo, "0x%X", x)); + buf += 1; + fmt++; + break; + } + case 'w': + { + unsigned int x; + ND_TCHECK_2(buf); + x = reverse ? EXTRACT_BE_U_2(buf) : + EXTRACT_LE_U_2(buf); + ND_PRINT((ndo, "0x%X", x)); + buf += 2; + fmt++; + break; + } + case 'W': + { + unsigned int x; + ND_TCHECK_4(buf); + x = reverse ? EXTRACT_BE_U_4(buf) : + EXTRACT_LE_U_4(buf); + ND_PRINT((ndo, "0x%X", x)); + buf += 4; + fmt++; + break; + } + case 'l': + { + fmt++; + switch (*fmt) { + + case 'b': + ND_TCHECK_1(buf); + stringlen = buf[0]; + ND_PRINT((ndo, "%u", stringlen)); + buf += 1; + break; + + case 'd': + ND_TCHECK_2(buf); + stringlen = reverse ? EXTRACT_BE_U_2(buf) : + EXTRACT_LE_U_2(buf); + ND_PRINT((ndo, "%u", stringlen)); + buf += 2; + break; + + case 'D': + ND_TCHECK_4(buf); + stringlen = reverse ? EXTRACT_BE_U_4(buf) : + EXTRACT_LE_U_4(buf); + ND_PRINT((ndo, "%u", stringlen)); + buf += 4; + break; + } + fmt++; + break; + } + case 'S': + case 'R': /* like 'S', but always ASCII */ + { + /*XXX unistr() */ + const char *s; + uint32_t len; + + len = 0; + s = unistr(ndo, buf, &len, (*fmt == 'R') ? 0 : unicodestr); + if (s == NULL) + goto trunc; + ND_PRINT((ndo, "%s", s)); + buf += len; + fmt++; + break; + } + case 'Z': + case 'Y': /* like 'Z', but always ASCII */ + { + const char *s; + uint32_t len; + + ND_TCHECK(*buf); + if (*buf != 4 && *buf != 2) { + ND_PRINT((ndo, "Error! ASCIIZ buffer of type %u", EXTRACT_U_1(buf))); + return maxbuf; /* give up */ + } + len = 0; + s = unistr(ndo, buf + 1, &len, (*fmt == 'Y') ? 0 : unicodestr); + if (s == NULL) + goto trunc; + ND_PRINT((ndo, "%s", s)); + buf += len + 1; + fmt++; + break; + } + case 's': + { + int l = atoi(fmt + 1); + ND_TCHECK2(*buf, l); + ND_PRINT((ndo, "%-*.*s", l, l, buf)); + buf += l; + fmt++; + while (isdigit((unsigned char)*fmt)) + fmt++; + break; + } + case 'c': + { + ND_TCHECK2(*buf, stringlen); + ND_PRINT((ndo, "%-*.*s", (int)stringlen, (int)stringlen, buf)); + buf += stringlen; + fmt++; + while (isdigit((unsigned char)*fmt)) + fmt++; + break; + } + case 'C': + { + const char *s; + s = unistr(ndo, buf, &stringlen, unicodestr); + if (s == NULL) + goto trunc; + ND_PRINT((ndo, "%s", s)); + buf += stringlen; + fmt++; + break; + } + case 'h': + { + int l = atoi(fmt + 1); + ND_TCHECK2(*buf, l); + while (l--) { + ND_PRINT((ndo, "%02x", EXTRACT_U_1(buf))); + buf++; + } + fmt++; + while (isdigit((unsigned char)*fmt)) + fmt++; + break; + } + case 'n': + { + int t = atoi(fmt+1); + char nbuf[255]; + int name_type; + int len; + + switch (t) { + case 1: + name_type = name_extract(ndo, startbuf, PTR_DIFF(buf, startbuf), + maxbuf, nbuf); + if (name_type < 0) + goto trunc; + len = name_len(ndo, buf, maxbuf); + if (len < 0) + goto trunc; + buf += len; + ND_PRINT((ndo, "%-15.15s NameType=0x%02X (%s)", nbuf, name_type, + name_type_str(name_type))); + break; + case 2: + ND_TCHECK_1(buf + 15); + name_type = buf[15]; + ND_PRINT((ndo, "%-15.15s NameType=0x%02X (%s)", buf, name_type, + name_type_str(name_type))); + buf += 16; + break; + } + fmt++; + while (isdigit((unsigned char)*fmt)) + fmt++; + break; + } + case 'T': + { + time_t t; + struct tm *lt; + const char *tstring; + uint32_t x; + + switch (atoi(fmt + 1)) { + case 1: + ND_TCHECK_4(buf); + x = EXTRACT_LE_U_4(buf); + if (x == 0 || x == 0xFFFFFFFF) + t = 0; + else + t = make_unix_date(buf); + buf += 4; + break; + case 2: + ND_TCHECK_4(buf); + x = EXTRACT_LE_U_4(buf); + if (x == 0 || x == 0xFFFFFFFF) + t = 0; + else + t = make_unix_date2(buf); + buf += 4; + break; + case 3: + ND_TCHECK_8(buf); + t = interpret_long_date(buf); + buf += 8; + break; + default: + t = 0; + break; + } + if (t != 0) { + lt = localtime(&t); + if (lt != NULL) + tstring = asctime(lt); + else + tstring = "(Can't convert time)\n"; + } else + tstring = "NULL\n"; + ND_PRINT((ndo, "%s", tstring)); + fmt++; + while (isdigit((unsigned char)*fmt)) + fmt++; + break; + } + default: + ND_PRINT((ndo, "%c", *fmt)); + fmt++; + break; } - printf("%s",t?asctime(localtime(&t)):"NULL\n"); - fmt++; while (isdigit(*fmt)) fmt++; - break; - } - default: - putchar(*fmt); - fmt++; - break; } - } - if (buf>=maxbuf && *fmt) - printf("END OF BUFFER\n"); + if (buf >= maxbuf && *fmt) + ND_PRINT((ndo, "END OF BUFFER\n")); + + return(buf); - return(buf); +trunc: + ND_PRINT((ndo, "\n")); + ND_PRINT((ndo, "WARNING: Short packet. Try increasing the snap length\n")); + return(NULL); } -uchar *fdata(uchar *buf,char *fmt,uchar *maxbuf) +const u_char * +smb_fdata(netdissect_options *ndo, + const u_char *buf, const char *fmt, const u_char *maxbuf, + int unicodestr) { - static int depth=0; - char s[128]; - char *p; - - while (*fmt) { - switch (*fmt) { - case '*': - fmt++; - while (buf < maxbuf) { - uchar *buf2; - depth++; - buf2 = fdata(buf,fmt,maxbuf); - depth--; - if (buf2 == buf) return(buf); - buf = buf2; - } - break; - - case '|': - fmt++; - if (buf>=maxbuf) return(buf); - break; - - case '%': - fmt++; - buf=maxbuf; - break; - - case '#': - fmt++; - return(buf); - break; - - case '[': - fmt++; - if (buf>=maxbuf) return(buf); - bzero(s,sizeof(s)); - p = strchr(fmt,']'); - strncpy(s,fmt,p-fmt); - fmt = p+1; - buf = fdata1(buf,s,maxbuf); - break; - - default: - putchar(*fmt); fmt++; - fflush(stdout); - break; + static int depth = 0; + char s[128]; + char *p; + + while (*fmt) { + switch (*fmt) { + case '*': + fmt++; + while (buf < maxbuf) { + const u_char *buf2; + depth++; + buf2 = smb_fdata(ndo, buf, fmt, maxbuf, unicodestr); + depth--; + if (buf2 == NULL) + return(NULL); + if (buf2 == buf) + return(buf); + buf = buf2; + } + return(buf); + + case '|': + fmt++; + if (buf >= maxbuf) + return(buf); + break; + + case '%': + fmt++; + buf = maxbuf; + break; + + case '#': + fmt++; + return(buf); + break; + + case '[': + fmt++; + if (buf >= maxbuf) + return(buf); + memset(s, 0, sizeof(s)); + p = strchr(fmt, ']'); + if ((size_t)(p - fmt + 1) > sizeof(s)) { + /* overrun */ + return(buf); + } + strncpy(s, fmt, p - fmt); + s[p - fmt] = '\0'; + fmt = p + 1; + buf = smb_fdata1(ndo, buf, s, maxbuf, unicodestr); + if (buf == NULL) + return(NULL); + break; + + default: + ND_PRINT((ndo, "%c", *fmt)); + fmt++; + break; + } } - } - if (!depth && buf