]> The Tcpdump Group git mirrors - tcpdump/blobdiff - smbutil.c
Add some additional bounds checking.
[tcpdump] / smbutil.c
index e80b663628a5ecc8731f14c5cd1a20ee9a812ade..d3020085d3a808c7b5a4852a1db7c9cd13c38e89 100644 (file)
--- a/smbutil.c
+++ b/smbutil.c
@@ -11,8 +11,8 @@
 #endif
 
 #ifndef lint
-static const char rcsid[] =
-     "@(#) $Header: /tcpdump/master/tcpdump/smbutil.c,v 1.25 2002-09-05 00:00:25 guy Exp $";
+static const char rcsid[] _U_ =
+     "@(#) $Header: /tcpdump/master/tcpdump/smbutil.c,v 1.34 2004-12-29 05:27:27 guy Exp $";
 #endif
 
 #include <tcpdump-stdinc.h>
@@ -25,6 +25,7 @@ static const char rcsid[] =
 #include "extract.h"
 #include "smb.h"
 
+static u_int32_t stringlen;
 extern const u_char *startbuf;
 
 /*
@@ -106,8 +107,6 @@ interpret_long_date(const u_char *p)
     double d;
     time_t ret;
 
-    TCHECK2(p[4], 4);
-
     /* this gives us seconds since jan 1st 1601 (approx) */
     d = (EXTRACT_LE_32BITS(p + 4) * 256.0 + p[3]) * (1.0e-7 * (1 << 24));
 
@@ -123,8 +122,6 @@ interpret_long_date(const u_char *p)
     ret = (time_t)d;
 
     return(ret);
-trunc:
-    return(0);
 }
 
 /*
@@ -186,7 +183,12 @@ name_ptr(const u_char *buf, int ofs, const u_char *maxbuf)
 
     /* XXX - this should use the same code that the DNS dissector does */
     if ((c & 0xC0) == 0xC0) {
-       u_int16_t l = EXTRACT_16BITS(buf + ofs) & 0x3FFF;
+       u_int16_t l;
+
+       TCHECK2(*p, 2);
+       if ((p + 1) >= maxbuf)
+           return(NULL);       /* name goes past the end of the buffer */
+       l = EXTRACT_16BITS(p) & 0x3FFF;
        if (l == 0) {
            /* We have a pointer that points to itself. */
            return(NULL);
@@ -195,9 +197,8 @@ name_ptr(const u_char *buf, int ofs, const u_char *maxbuf)
        if (p >= maxbuf)
            return(NULL);       /* name goes past the end of the buffer */
        TCHECK2(*p, 1);
-       return(buf + l);
-    } else
-       return(buf + ofs);
+    }
+    return(p);
 
 trunc:
     return(NULL);      /* name goes past the end of the buffer */
@@ -328,47 +329,102 @@ write_bits(unsigned int val, const char *fmt)
 }
 
 /* convert a UCS2 string into iso-8859-1 string */
+#define MAX_UNISTR_SIZE        1000
 static const char *
-unistr(const u_char *s, int *len)
+unistr(const u_char *s, u_int32_t *len, int use_unicode)
 {
-    static char buf[1000];
-    int l=0;
-    static int use_unicode = -1;
-
-    if (use_unicode == -1) {
-       char *p = getenv("USE_UNICODE");
-       if (p && (atoi(p) == 1))
-           use_unicode = 1;
-       else
-           use_unicode = 0;
-    }
-
-    /* maybe it isn't unicode - a cheap trick */
-    if (!use_unicode || (s[0] && s[1])) {
-       *len = strlen((const char *)s) + 1;
-       return (const char *)s;
+    static char buf[MAX_UNISTR_SIZE+1];
+    size_t l = 0;
+    u_int32_t strsize;
+    const u_char *sp;
+
+    if (use_unicode) {
+       /*
+        * Skip padding that puts the string on an even boundary.
+        */
+       if (((s - startbuf) % 2) != 0) {
+           TCHECK(s[0]);
+           s++;
+       }
     }
-
-    *len = 0;
-
-    if (s[0] == 0 && s[1] != 0) {
-       s++;
-       *len = 1;
+    if (*len == 0) {
+       /*
+        * Null-terminated string.
+        */
+       strsize = 0;
+       sp = s;
+       if (!use_unicode) {
+           for (;;) {
+               TCHECK(sp[0]);
+               *len += 1;
+               if (sp[0] == 0)
+                   break;
+               sp++;
+           }
+           strsize = *len - 1;
+       } else {
+           for (;;) {
+               TCHECK2(sp[0], 2);
+               *len += 2;
+               if (sp[0] == 0 && sp[1] == 0)
+                   break;
+               sp += 2;
+           }
+           strsize = *len - 2;
+       }
+    } else {
+       /*
+        * Counted string.
+        */
+       strsize = *len;
     }
-
-    while (l < (int)(sizeof(buf) - 1) && s[0] && s[1] == 0) {
-       buf[l] = s[0];
-       s += 2;
-       l++;
-       *len += 2;
+    if (!use_unicode) {
+       while (strsize != 0) {
+           TCHECK(s[0]);
+           if (l >= MAX_UNISTR_SIZE)
+               break;
+           if (isprint(s[0]))
+               buf[l] = s[0];
+           else {
+               if (s[0] == 0)
+                   break;
+               buf[l] = '.';
+           }
+           l++;
+           s++;
+           strsize--;
+       }
+    } else {
+       while (strsize != 0) {
+           TCHECK2(s[0], 2);
+           if (l >= MAX_UNISTR_SIZE)
+               break;
+           if (s[1] == 0 && isprint(s[0])) {
+               /* It's a printable ASCII character */
+               buf[l] = s[0];
+           } else {
+               /* It's a non-ASCII character or a non-printable ASCII character */
+               if (s[0] == 0 && s[1] == 0)
+                   break;
+               buf[l] = '.';
+           }
+           l++;
+           s += 2;
+           if (strsize == 1)
+               break;
+           strsize -= 2;
+       }
     }
     buf[l] = 0;
-    *len += 2;
     return buf;
+
+trunc:
+    return NULL;
 }
 
 static const u_char *
-smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
+smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf,
+    int unicodestr)
 {
     int reverse = 0;
     const char *attrib_fmt = "READONLY|HIDDEN|SYSTEM|VOLUME|DIR|ARCHIVE|";
@@ -377,12 +433,14 @@ smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
     while (*fmt && buf<maxbuf) {
        switch (*fmt) {
        case 'a':
+           TCHECK(buf[0]);
            write_bits(buf[0], attrib_fmt);
            buf++;
            fmt++;
            break;
 
        case 'A':
+           TCHECK2(buf[0], 2);
            write_bits(EXTRACT_LE_16BITS(buf), attrib_fmt);
            buf += 2;
            fmt++;
@@ -396,9 +454,14 @@ smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
 
            p = strchr(++fmt, '}');
            l = PTR_DIFF(p, fmt);
+
+           if ((unsigned int)l > sizeof(bitfmt) - 1)
+                   l = sizeof(bitfmt)-1;
+
            strncpy(bitfmt, fmt, l);
-           bitfmt[l] = 0;
+           bitfmt[l] = '\0';
            fmt = p + 1;
+           TCHECK(buf[0]);
            write_bits(buf[0], bitfmt);
            buf++;
            break;
@@ -407,6 +470,7 @@ smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
        case 'P':
          {
            int l = atoi(fmt + 1);
+           TCHECK2(buf[0], l);
            buf += l;
            fmt++;
            while (isdigit((unsigned char)*fmt))
@@ -417,31 +481,13 @@ smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
            reverse = !reverse;
            fmt++;
            break;
-       case 'D':
+       case 'b':
          {
            unsigned int x;
-
-           TCHECK2(buf[0], 4);
-           x = reverse ? EXTRACT_32BITS(buf) : EXTRACT_LE_32BITS(buf);
-           printf("%d (0x%x)", x, x);
-           buf += 4;
-           fmt++;
-           break;
-         }
-       case 'L':
-         {
-           unsigned int x1, x2;
-
-           TCHECK2(buf[4], 4);
-           x1 = reverse ? EXTRACT_32BITS(buf) :
-                          EXTRACT_LE_32BITS(buf);
-           x2 = reverse ? EXTRACT_32BITS(buf + 4) :
-                          EXTRACT_LE_32BITS(buf + 4);
-           if (x2)
-               printf("0x%08x:%08x", x2, x1);
-           else
-               printf("%d (0x%08x%08x)", x1, x2, x1);
-           buf += 8;
+           TCHECK(buf[0]);
+           x = buf[0];
+           printf("%u (0x%x)", x, x);
+           buf += 1;
            fmt++;
            break;
          }
@@ -456,17 +502,54 @@ smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
            fmt++;
            break;
          }
-       case 'W':
+       case 'D':
          {
            unsigned int x;
            TCHECK2(buf[0], 4);
            x = reverse ? EXTRACT_32BITS(buf) :
                          EXTRACT_LE_32BITS(buf);
-           printf("0x%X", x);
+           printf("%d (0x%x)", x, x);
            buf += 4;
            fmt++;
            break;
          }
+       case 'L':
+         {
+           u_int64_t x;
+           TCHECK2(buf[0], 8);
+           x = reverse ? EXTRACT_64BITS(buf) :
+                         EXTRACT_LE_64BITS(buf);
+           printf("%" PRIu64 " (0x%" PRIx64 ")", x, x);
+           buf += 8;
+           fmt++;
+           break;
+         }
+       case 'M':
+         {
+           /* Weird mixed-endian length values in 64-bit locks */
+           u_int32_t x1, x2;
+           u_int64_t x;
+           TCHECK2(buf[0], 8);
+           x1 = reverse ? EXTRACT_32BITS(buf) :
+                          EXTRACT_LE_32BITS(buf);
+           x2 = reverse ? EXTRACT_32BITS(buf + 4) :
+                          EXTRACT_LE_32BITS(buf + 4);
+           x = (((u_int64_t)x1) << 32) | x2;
+           printf("%" PRIu64 " (0x%" PRIx64 ")", x, x);
+           buf += 8;
+           fmt++;
+           break;
+         }
+       case 'B':
+         {
+           unsigned int x;
+           TCHECK(buf[0]);
+           x = buf[0];
+           printf("0x%X", x);
+           buf += 1;
+           fmt++;
+           break;
+         }
        case 'w':
          {
            unsigned int x;
@@ -478,41 +561,76 @@ smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
            fmt++;
            break;
          }
-       case 'B':
+       case 'W':
          {
            unsigned int x;
-           TCHECK(buf[0]);
-           x = buf[0];
+           TCHECK2(buf[0], 4);
+           x = reverse ? EXTRACT_32BITS(buf) :
+                         EXTRACT_LE_32BITS(buf);
            printf("0x%X", x);
-           buf += 1;
+           buf += 4;
            fmt++;
            break;
          }
-       case 'b':
+       case 'l':
          {
-           unsigned int x;
-           TCHECK(buf[0]);
-           x = buf[0];
-           printf("%u (0x%x)", x, x);
-           buf += 1;
+           fmt++;
+           switch (*fmt) {
+
+           case 'b':
+               TCHECK(buf[0]);
+               stringlen = buf[0];
+               printf("%u", stringlen);
+               buf += 1;
+               break;
+
+           case 'd':
+               TCHECK2(buf[0], 2);
+               stringlen = reverse ? EXTRACT_16BITS(buf) :
+                                     EXTRACT_LE_16BITS(buf);
+               printf("%u", stringlen);
+               buf += 2;
+               break;
+
+           case 'D':
+               TCHECK2(buf[0], 4);
+               stringlen = reverse ? EXTRACT_32BITS(buf) :
+                                     EXTRACT_LE_32BITS(buf);
+               printf("%u", stringlen);
+               buf += 4;
+               break;
+           }
            fmt++;
            break;
          }
        case 'S':
+       case 'R':       /* like 'S', but always ASCII */
          {
            /*XXX unistr() */
-           printf("%.*s", (int)PTR_DIFF(maxbuf, buf), unistr(buf, &len));
+           const char *s;
+           len = 0;
+           s = unistr(buf, &len, (*fmt == 'R') ? 0 : unicodestr);
+           if (s == NULL)
+               goto trunc;
+           printf("%s", s);
            buf += len;
            fmt++;
            break;
          }
        case 'Z':
+       case 'Y':       /* like 'Z', but always ASCII */
          {
-           if (*buf != 4 && *buf != 2)
-               printf("Error! ASCIIZ buffer of type %u (safety=%lu)\n", *buf,
-                   (unsigned long)PTR_DIFF(maxbuf, buf));
-           printf("%.*s", (int)PTR_DIFF(maxbuf, buf + 1),
-               unistr(buf + 1, &len));
+           const char *s;
+           TCHECK(*buf);
+           if (*buf != 4 && *buf != 2) {
+               printf("Error! ASCIIZ buffer of type %u", *buf);
+               return maxbuf;  /* give up */
+           }
+           len = 0;
+           s = unistr(buf + 1, &len, (*fmt == 'Y') ? 0 : unicodestr);
+           if (s == NULL)
+               goto trunc;
+           printf("%s", s);
            buf += len + 1;
            fmt++;
            break;
@@ -520,6 +638,7 @@ smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
        case 's':
          {
            int l = atoi(fmt + 1);
+           TCHECK2(*buf, l);
            printf("%-*.*s", l, l, buf);
            buf += l;
            fmt++;
@@ -527,9 +646,31 @@ smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
                fmt++;
            break;
          }
+       case 'c':
+         {
+           TCHECK2(*buf, stringlen);
+           printf("%-*.*s", stringlen, stringlen, buf);
+           buf += stringlen;
+           fmt++;
+           while (isdigit((unsigned char)*fmt))
+               fmt++;
+           break;
+         }
+       case 'C':
+         {
+           const char *s;
+           s = unistr(buf, &stringlen, unicodestr);
+           if (s == NULL)
+               goto trunc;
+           printf("%s", s);
+           buf += stringlen;
+           fmt++;
+           break;
+         }
        case 'h':
          {
            int l = atoi(fmt + 1);
+           TCHECK2(*buf, l);
            while (l--)
                printf("%02x", *buf++);
            fmt++;
@@ -558,6 +699,7 @@ smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
                    name_type_str(name_type));
                break;
            case 2:
+               TCHECK(buf[15]);
                name_type = buf[15];
                printf("%-15.15s NameType=0x%02X (%s)", buf, name_type,
                    name_type_str(name_type));
@@ -575,10 +717,11 @@ smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
            struct tm *lt;
            const char *tstring;
            u_int32_t x;
-           x = EXTRACT_LE_32BITS(buf);
 
            switch (atoi(fmt + 1)) {
            case 1:
+               TCHECK2(buf[0], 4);
+               x = EXTRACT_LE_32BITS(buf);
                if (x == 0 || x == 0xFFFFFFFF)
                    t = 0;
                else
@@ -586,6 +729,8 @@ smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
                buf += 4;
                break;
            case 2:
+               TCHECK2(buf[0], 4);
+               x = EXTRACT_LE_32BITS(buf);
                if (x == 0 || x == 0xFFFFFFFF)
                    t = 0;
                else
@@ -593,6 +738,7 @@ smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
                buf += 4;
                break;
            case 3:
+               TCHECK2(buf[0], 8);
                t = interpret_long_date(buf);
                buf += 8;
                break;
@@ -630,7 +776,8 @@ trunc:
 }
 
 const u_char *
-smb_fdata(const u_char *buf, const char *fmt, const u_char *maxbuf)
+smb_fdata(const u_char *buf, const char *fmt, const u_char *maxbuf,
+    int unicodestr)
 {
     static int depth = 0;
     char s[128];
@@ -643,7 +790,7 @@ smb_fdata(const u_char *buf, const char *fmt, const u_char *maxbuf)
            while (buf < maxbuf) {
                const u_char *buf2;
                depth++;
-               buf2 = smb_fdata(buf, fmt, maxbuf);
+               buf2 = smb_fdata(buf, fmt, maxbuf, unicodestr);
                depth--;
                if (buf2 == NULL)
                    return(NULL);
@@ -682,7 +829,7 @@ smb_fdata(const u_char *buf, const char *fmt, const u_char *maxbuf)
            strncpy(s, fmt, p - fmt);
            s[p - fmt] = '\0';
            fmt = p + 1;
-           buf = smb_fdata1(buf, s, maxbuf);
+           buf = smb_fdata1(buf, s, maxbuf, unicodestr);
            if (buf == NULL)
                return(NULL);
            break;
@@ -726,12 +873,12 @@ static err_code_struct dos_msgs[] = {
     { "ERRbaddata", 13, "Invalid data." },
     { "ERR", 14, "reserved." },
     { "ERRbaddrive", 15, "Invalid drive specified." },
-    { "ERRremcd", 16, "A Delete Directory request attempted  to  remove  the  server's  current directory." },
+    { "ERRremcd", 16, "A Delete Directory request attempted to remove the server's current directory." },
     { "ERRdiffdevice", 17, "Not same device." },
     { "ERRnofiles", 18, "A File Search command can find no more files matching the specified criteria." },
-    { "ERRbadshare", 32, "The sharing mode specified for an Open conflicts with existing  FIDs  on the file." },
-    { "ERRlock", 33, "A Lock request conflicted with an existing lock or specified an  invalid mode,   or an Unlock requested attempted to remove a lock held by another process." },
-    { "ERRfilexists", 80, "The file named in a Create Directory,  Make  New  File  or  Link  request already exists." },
+    { "ERRbadshare", 32, "The sharing mode specified for an Open conflicts with existing FIDs on the file." },
+    { "ERRlock", 33, "A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process." },
+    { "ERRfilexists", 80, "The file named in a Create Directory, Make New File or Link request already exists." },
     { "ERRbadpipe", 230, "Pipe invalid." },
     { "ERRpipebusy", 231, "All instances of the requested pipe are busy." },
     { "ERRpipeclosing", 232, "Pipe close in progress." },
@@ -745,17 +892,17 @@ err_code_struct server_msgs[] = {
     { "ERRerror", 1, "Non-specific error code." },
     { "ERRbadpw", 2, "Bad password - name/password pair in a Tree Connect or Session Setup are invalid." },
     { "ERRbadtype", 3, "reserved." },
-    { "ERRaccess", 4, "The requester does not have  the  necessary  access  rights  within  the specified  context for the requested function. The context is defined by the TID or the UID." },
+    { "ERRaccess", 4, "The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID." },
     { "ERRinvnid", 5, "The tree ID (TID) specified in a command was invalid." },
     { "ERRinvnetname", 6, "Invalid network name in tree connect." },
-    { "ERRinvdevice", 7, "Invalid device - printer request made to non-printer connection or  non-printer request made to printer connection." },
+    { "ERRinvdevice", 7, "Invalid device - printer request made to non-printer connection or non-printer request made to printer connection." },
     { "ERRqfull", 49, "Print queue full (files) -- returned by open print file." },
     { "ERRqtoobig", 50, "Print queue full -- no space." },
     { "ERRqeof", 51, "EOF on print queue dump." },
     { "ERRinvpfid", 52, "Invalid print file FID." },
     { "ERRsmbcmd", 64, "The server did not recognize the command received." },
-    { "ERRsrverror", 65, "The server encountered an internal error,  e.g.,  system file unavailable." },
-    { "ERRfilespecs", 67, "The file handle (FID) and pathname parameters contained an invalid  combination of values." },
+    { "ERRsrverror", 65, "The server encountered an internal error, e.g., system file unavailable." },
+    { "ERRfilespecs", 67, "The file handle (FID) and pathname parameters contained an invalid combination of values." },
     { "ERRreserved", 68, "reserved." },
     { "ERRbadpermits", 69, "The access permissions specified for a file or directory are not a valid combination.  The server cannot set the requested attribute." },
     { "ERRreserved", 70, "reserved." },
@@ -768,8 +915,8 @@ err_code_struct server_msgs[] = {
     { "ERRnoresource", 89, "No resources currently available for request." },
     { "ERRtoomanyuids", 90, "Too many UIDs active on this session." },
     { "ERRbaduid", 91, "The UID is not known as a valid ID on this session." },
-    { "ERRusempx", 250, "Temp unable to support Raw,  use MPX mode." },
-    { "ERRusestd", 251, "Temp unable to support Raw,  use standard read/write." },
+    { "ERRusempx", 250, "Temp unable to support Raw, use MPX mode." },
+    { "ERRusestd", 251, "Temp unable to support Raw, use standard read/write." },
     { "ERRcontmpx", 252, "Continue in MPX mode." },
     { "ERRreserved", 253, "reserved." },
     { "ERRreserved", 254, "reserved." },
@@ -793,7 +940,7 @@ err_code_struct hard_msgs[] = {
     { "ERRread", 30, "Read fault." },
     { "ERRgeneral", 31, "General failure." },
     { "ERRbadshare", 32, "A open conflicts with an existing open." },
-    { "ERRlock", 33, "A Lock request conflicted with an existing lock or specified an invalid mode,  or an Unlock requested attempted to remove a lock held by another process." },
+    { "ERRlock", 33, "A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process." },
     { "ERRwrongdisk", 34, "The wrong disk was found in a drive." },
     { "ERRFCBUnavail", 35, "No FCBs are available to process request." },
     { "ERRsharebufexc", 36, "A sharing buffer has been exceeded." },