/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * References used throughout this code: * * [RFC1001] : PROTOCOL STANDARD FOR A NetBIOS SERVICE * ON A TCP/UDP TRANSPORT: * CONCEPTS AND METHODS * NetBIOS Working Group, March 1987 * * [RFC1002] : PROTOCOL STANDARD FOR A NetBIOS SERVICE * ON A TCP/UDP TRANSPORT: * DETAILED SPECIFICATIONS * NetBIOS Working Group, March 1987 */ #include #include "snoop.h" #include #include #include "snoop.h" extern char *dlc_header; char *show_type(); /* See snoop_smb.c */ extern void interpret_smb(int flags, uchar_t *data, int len); /* * NBT Session Packet Header * [RFC 1002, Sec. 4.3.1] */ struct nbt_ss { uchar_t type; uchar_t flags; ushort_t length; }; /* * NBT Session Request Packet trailer * [RFC 1002, Sec. 4.3.2] */ struct callnames { uchar_t space; /* padding */ uchar_t calledname[32]; uchar_t nullchar; /* padding */ uchar_t space2; /* padding */ uchar_t callingname[32]; uchar_t nullchar2; /* padding */ }; static void interpret_netbios_names(int flags, uchar_t *data, int len, char *xtra); static void netbiosname2ascii(char *asciiname, uchar_t *netbiosname); /* * Helpers to read network-order values, * with NO alignment assumed. */ static ushort_t getshort(uchar_t *p) { return (p[1] + (p[0]<<8)); } static uint_t getlong(uchar_t *p) { return (p[3] + (p[2]<<8) + (p[1]<<16) + (p[0]<<24)); } /* * NM_FLAGS fields in the NetBIOS Name Service Packet header. * [RFC 1002, Sec. 4.2.1.1] */ static void print_flag_details(int headerflags) { if (headerflags & 1<<4) sprintf(get_line(0, 0), " - Broadcast"); if (headerflags & 1<<7) sprintf(get_line(0, 0), " - Recursion Available"); if (headerflags & 1<<8) sprintf(get_line(0, 0), " - Recursion Desired"); if (headerflags & 1<<9) sprintf(get_line(0, 0), " - Truncation Flag"); if (headerflags & 1<<10) sprintf(get_line(0, 0), " - Authoritative Answer"); } /* * Possible errors in NetBIOS name service packets. * [RFC 1002, Sec. 4.2.6, 4.2.11, 4.2.14] */ static void getrcodeerr(int headerflags, char *errortype) { int error = (headerflags & 0xf); switch (error) { case 0: sprintf(errortype, "Success"); break; case 1: sprintf(errortype, "Format Error"); break; case 2: sprintf(errortype, "Server Failure"); break; case 3: sprintf(errortype, "Name Error"); break; case 4: sprintf(errortype, "Unsupported Request Error"); break; case 5: sprintf(errortype, "Refused Error"); break; case 6: sprintf(errortype, "Active Error"); break; case 7: sprintf(errortype, "Name in Conflict Error"); break; default: sprintf(errortype, "Unknown Error"); break; } } /* * OPCODE fields in the NetBIOS Name Service Packet header. * [RFC 1002, Sec. 4.2.1.1] */ static void print_ns_type(int flags, int headerflags, char *xtra) { int opcode = (headerflags & 0x7800)>>11; int response = (headerflags & 1<<15); char *resptype = response ? "Response" : "Request"; char *optype; switch (opcode) { case 0: optype = "Query"; break; case 5: optype = "Registration"; break; case 6: optype = "Release"; break; case 7: optype = "WACK"; break; case 8: optype = "Refresh"; break; default: optype = "Unknown"; break; } if (flags & F_DTAIL) sprintf(get_line(0, 0), "Type = %s %s", optype, resptype); else sprintf(xtra, "%s %s", optype, resptype); } /* * Interpret Datagram Packets * [RFC 1002, Sec. 4.4] */ void interpret_netbios_datagram(int flags, uchar_t *data, int len) { char name[24]; int packettype = data[0]; int packetlen; data++; if (packettype < 0x10 || packettype > 0x11) return; if (flags & F_SUM) { data += 14; netbiosname2ascii(name, data); sprintf(get_sum_line(), "NBT Datagram Service Type=%d Source=%s", packettype, name); } if (flags & F_DTAIL) { show_header("NBT: ", "Netbios Datagram Service Header", len); show_space(); sprintf(get_line(0, 0), "Datagram Packet Type = 0x%.2x", packettype); sprintf(get_line(0, 0), "Datagram Flags = 0x%.2x", data[0]); data++; sprintf(get_line(0, 0), "Datagram ID = 0x%.4x", getshort(data)); data += 2; sprintf(get_line(0, 0), "Source IP = %d.%d.%d.%d", data[0], data[1], data[2], data[3]); data += 4; sprintf(get_line(0, 0), "Source Port = %d", getshort(data)); data += 2; packetlen = getshort(data); sprintf(get_line(0, 0), "Datagram Length = 0x%.4x", packetlen); data += 2; sprintf(get_line(0, 0), "Packet Offset = 0x%.4x", getshort(data)); data += 3; netbiosname2ascii(name, data); sprintf(get_line(0, 0), "Source Name = %s", name); data += 34; netbiosname2ascii(name, data); sprintf(get_line(0, 0), "Destination Name = %s", name); sprintf(get_line(0, 0), "Number of data bytes remaining = %d", packetlen - 68); show_trailer(); } } /* * Interpret NetBIOS Name Service packets. * [RFC 1002, Sec. 4.2] */ void interpret_netbios_ns(int flags, uchar_t *data, int len) { int headerflags, qcount, acount, nscount, arcount; int transid; char name[24]; char extra[256]; char errortype[50]; int rdatalen; int rrflags; int nameptr; int nodecode; char *nodetype; uchar_t *data0 = data; transid = getshort(data); data += 2; headerflags = getshort(data); data += 2; qcount = getshort(data); data += 2; acount = getshort(data); data += 2; nscount = getshort(data); data += 2; arcount = getshort(data); data += 2; getrcodeerr(headerflags, errortype); if (flags & F_SUM) { print_ns_type(flags, headerflags, extra); data++; netbiosname2ascii(name, data); sprintf(get_sum_line(), "NBT NS %s for %s, %s", extra, name, errortype); } if (flags & F_DTAIL) { show_header("NBT: ", "Netbios Name Service Header", len); show_space(); print_ns_type(flags, headerflags, 0); sprintf(get_line(0, 0), "Status = %s", errortype); sprintf(get_line(0, 0), "Transaction ID = 0x%.4x", transid); sprintf(get_line(0, 0), "Flags Summary = 0x%.4x", headerflags); print_flag_details(headerflags); sprintf(get_line(0, 0), "Question count = %d", qcount); sprintf(get_line(0, 0), "Answer Count = %d", acount); sprintf(get_line(0, 0), "Name Service Count = %d", nscount); sprintf(get_line(0, 0), "Additional Record Count = %d", arcount); /* * Question Section Packet Description from * [RFC 1002, Sec. 4.2.1.2] */ if (qcount) { data++; netbiosname2ascii(name, data); sprintf(get_line(0, 0), "Question Name = %s", name); data += 33; sprintf(get_line(0, 0), "Question Type = 0x%.4x", getshort(data)); data += 2; sprintf(get_line(0, 0), "Question Class = 0x%.4x", getshort(data)); data += 2; } /* * Resrouce Record Packet Description from * [RFC 1002, Sec. 4.2.1.3] */ if ((acount || nscount || arcount) || (qcount+acount+nscount+arcount == 0)) { /* Second level encoding from RFC883 (p.31, 32) */ if (data[0] & 0xc0) { nameptr = getshort(data)&0x3fff; netbiosname2ascii(name, (data0+nameptr+1)); sprintf(get_line(0, 0), "Resource Record Name = %s", name); data += 2; } else { data++; netbiosname2ascii(name, data); sprintf(get_line(0, 0), "Resource Record Name = %s", name); data += 33; } sprintf(get_line(0, 0), "Resource Record Type = 0x%.4x", getshort(data)); data += 2; sprintf(get_line(0, 0), "Resource Record Class = 0x%.4x", getshort(data)); data += 2; sprintf(get_line(0, 0), "Time to Live (Milliseconds) = %d", getlong(data)); data += 4; rdatalen = getshort(data); sprintf(get_line(0, 0), "RDATA Length = 0x%.4x", rdatalen); data += 2; /* 15.4.2.1.3 */ if (rdatalen == 6) { rrflags = getshort(data); data += 2; sprintf(get_line(0, 0), "Resource Record Flags = 0x%.4x", rrflags); nodecode = (rrflags>>13)& 0x11; if (nodecode == 0) nodetype = "B"; if (nodecode == 1) nodetype = "P"; if (nodecode == 2) nodetype = "M"; sprintf(get_line(0, 0), " - %s, %s node", (rrflags & 1<<15) ? "Group NetBIOS Name": "Unique NetBIOS Name", nodetype); sprintf(get_line(0, 0), "Owner IP Address = %d.%d.%d.%d", data[0], data[1], data[2], data[3]); } } show_trailer(); } } /* * Interpret NetBIOS session packets. * [RFC 1002, Sec. 4.3] */ void interpret_netbios_ses(int flags, uchar_t *data, int len) { struct nbt_ss *ss; uchar_t *trailer; int length = len - 4; /* NBT packet length without header */ char *type; char extrainfo[300]; if (len < sizeof (struct nbt_ss)) return; /* * Packets that are fragments of a large NetBIOS session * message will have no NetBIOS header. (Only the first * TCP segment will have a NetBIOS header.) It turns out * that very often, such fragments start with SMB data, so * we should try to recognize and decode them. */ if (data[0] == 0xff && data[1] == 'S' && data[2] == 'M' && data[3] == 'B') { interpret_smb(flags, data, len); return; } /* LINTED PTRALIGN */ ss = (struct nbt_ss *)data; trailer = data + sizeof (*ss); extrainfo[0] = '\0'; if (flags & F_SUM) { switch (ss->type) { case 0x00: type = "SESSION MESSAGE"; break; case 0x81: type = "SESSION REQUEST"; interpret_netbios_names(flags, trailer, length, extrainfo); break; case 0x82: type = "POSITIVE SESSION RESPONSE"; break; case 0x83: type = "NEGATIVE SESSION RESPONSE"; break; case 0x84: type = "RETARGET SESSION RESPONSE"; break; case 0x85: type = "SESSION KEEP ALIVE"; break; default: type = "Unknown"; break; } (void) sprintf(get_sum_line(), "NBT Type=%s %sLength=%d", type, extrainfo, length); } if (flags & F_DTAIL) { show_header("NBT: ", "NBT Header", len); show_space(); switch (ss->type) { case 0x00: (void) sprintf(get_line(0, 0), "Type = SESSION MESSAGE"); break; case 0x81: (void) sprintf(get_line(0, 0), "Type = SESSION REQUEST"); interpret_netbios_names(flags, trailer, length, 0); break; case 0x82: (void) sprintf(get_line(0, 0), "Type = POSITIVE SESSION RESPONSE"); break; case 0x83: (void) sprintf(get_line(0, 0), "Type = NEGATIVE SESSION RESPONSE"); break; case 0x84: (void) sprintf(get_line(0, 0), "Type = RETARGET SESSION RESPONSE"); break; case 0x85: (void) sprintf(get_line(0, 0), "Type = SESSION KEEP ALIVE"); break; default: (void) sprintf(get_line(0, 0), "Type = Unknown"); break; } (void) sprintf(get_line(0, 0), "Length = %d bytes", length); show_trailer(); } /* * SMB packets have { 0xff, 'S', 'M', 'B' } * in the first four bytes. If we find that, * let snoop_smb.c have a look at it. */ if (ss->type == 0x00 && length > 0 && trailer[0] == 0xff && trailer[1] == 'S' && trailer[2] == 'M' && trailer[3] == 'B') interpret_smb(flags, trailer, length); } /* * NetBIOS name encoding (First Level Encoding) * [RFC 1001, Sec. 14.1] */ static void netbiosname2ascii(char *aname, uchar_t *nbname) { int c, i, j; i = j = 0; for (;;) { c = nbname[i++] - 'A'; c = (c << 4) + nbname[i++] - 'A'; /* 16th char is the "type" */ if (i >= 32) break; if (iscntrl(c)) c = '.'; if (c != ' ') aname[j++] = c; } c &= 0xff; sprintf(&aname[j], "[%x]", c); } /* * Interpret the names in a Session Request packet. * [RFC 1002, Sec. 4.3.2] */ static void interpret_netbios_names(int flags, uchar_t *data, int len, char *xtra) { char calledname[24]; char callingname[24]; struct callnames *names = (struct callnames *)data; if (len < sizeof (*names)) return; netbiosname2ascii(calledname, names->calledname); netbiosname2ascii(callingname, names->callingname); if (flags & F_SUM) { sprintf(xtra, "Dest=%s Source=%s ", calledname, callingname); } if (flags & F_DTAIL) { sprintf(get_line(0, 0), "Destination = %s", calledname); sprintf(get_line(0, 0), "Source = %s", callingname); } }