标签:birt hot includes display vendor art form weight label
Like NetBIOS, the Server Message Block protocol originated a long time ago at IBM. Microsoft embraced it, extended it, and in 1996 gave it a marketing upgrade by renaming it "CIFS".
Over the years there have been several attempts to document and
standardize the SMB/CIFS protocol:
Change is the essential process
of all existence.
-- Spock (Leonard Nimoy)
Let That Be Your Last
Battlefield,
stardate 5730.2
Without a current and authoritative protocol specification, there
is no external reference against which to measure the
"correctness" of an implementation, and no way to hold anyone
accountable. Since Microsoft is the market leader, with a proven
monopoly on the desktop, the behavior of their clients and servers is
the standard against which all other implementations are measured.
You knew the job was
dangerous when you took it.
-- Super Chicken
Jay Ward and Bill Scott,
ABC TV, 1967-1968
Jeremy Allison, the Samba Team‘s First Officer3, has stated that "The level of detail required to interoperate successfully is simply not documentable". One reason that this is true is that Microsoft can "enhance" SMB behavior at will. Combined with the dearth of authoritative references, this means that the only criteria for a well-behaved SMB implementation is that it works with Microsoft products. As a result, subtle inconsistencies and variations have crept into the protocol. They are discovered in much the same way that a dog-owner discovers poop in the yard in springtime when the snow melts4.
Many people dread spring chores, but spring also brings the
flowers. The children play, the dog chases a butterfly, the birds
sing...and it all seems suddenly worthwhile. Likewise with the work
we have ahead. Things are not really too bad, once you‘ve gotten
started.
This part of the book will cover the basics of SMB, enumerate and describe some of the SMB message types (commands), discuss protocol dialects, give some details on authentication, and provide a few examples. That should be enough to help you develop a working knowledge of the protocol, a working SMB client, and possibly a simple server.
Bear in mind, though, that SMB is more complex and less well defined than NBT. In the NBT section it was possible to describe every message type and provide a comprehensive review of the entire NBT protocol. It is not practical to cover all of SMB in the same way. Instead, the goal here is to explain the basics of SMB, provide details that are missing from other sources, and describe how to go about exploring SMB on your own. In other words, the goal is to develop understanding rather than simply providing knowledge.
The textbook for this class is the latest version of the SNIA CIFS Technical Reference. Additional sources are listed in the References section near the end of this book. The most important tool, however, is probably the protocol analyzer. Warm up your copy of Ethereal or NetMon, and get ready to do some packet shoveling.
Before we actually start, there is one more thing to mention: The SMB protocol is supposed to be "transport independent". That is, SMB should work over any reliable transport that meets a few basic criteria. NBT is one such transport, but SMB does not really require the NetBIOS API. It can, for instance, be run directly over TCP/IP.
Just for fun, we will refer to SMB over TCP/IP without NBT as "naked" or "raw". When running naked, SMB defaults to using TCP port 445 instead of the NBT Session Service port (TCP/139). Windows2000, WindowsXP, and Samba all support raw transport, but the large number of "legacy" Windows clients still in use suggest that NBT will not go away any time soon.
Other than the new port number, there are only two notable changes between NBT and naked transport. The first is that naked transport does not make use of the NBT SESSION REQUEST and POSITIVE SESSION RESPONSE messages. The second is that the two transports interpret the SESSION MESSAGE header a bit differently.
Recall (from section 1.6) that the NBT Session Service prepends a four-byte header to each SESSION MESSAGE, like so:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 0 |
1 1 |
1 2 |
1 3 |
1 4 |
1 5 |
1 6 |
1 7 |
1 8 |
1 9 |
2 0 |
2 1 |
2 2 |
2 3 |
2 4 |
2 5 |
2 6 |
2 7 |
2 8 |
2 9 |
3 0 |
3 1 |
0 (zero) | <reserved> | LENGTH (17 bits) |
The LENGTH field, as shown, is 17 bits wide5. Raw TCP transport also prepends a four-byte header, but the full 24 bits are available for the LENGTH:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 0 |
1 1 |
1 2 |
1 3 |
1 4 |
1 5 |
1 6 |
1 7 |
1 8 |
1 9 |
2 0 |
2 1 |
2 2 |
2 3 |
2 4 |
2 5 |
2 6 |
2 7 |
2 8 |
2 9 |
3 0 |
3 1 |
0 (zero) | LENGTH (24 bits) |
Your mileage may vary.
-- advertiser‘s disclaimer
Appendix B of the SNIA CIFS Technical Reference is the only source that was found which clearly shows the naked transport LENGTH field as being 24 bits wide. 24 bits translates to 16 megabytes, though, and that‘s a bigbunch--more than is typically practical. Fortunately, the actual maximum message size is something that is negotiated when the client and server establish the session.
When we discuss the SMB messages themselves we will ignore the SESSION MESSAGE headers, since they are part of the transport, not the SMB protocol.
We will start with a quick museum tour of SMB. Our guide will be the venerable Universal Naming Convention (UNC). You may remember UNC from the brief introduction way back in section 1.1. UNC will provide directions and point out highlights along the tour.
Please stay together, everyone.
The UNC directions are presented in terms of a path, much like the Uniform Resource Identifier (URI) paths that are used on the World Wide Web. To explain UNC, let us first consider something more modern and familiar:
http://ubiqx.org/cifs/SMB.html
That string is in URI syntax, as used by web browsers, and it breaks down to provide these landmarks:
http == | The protocol to use. |
ubiqx.org == | The name of the server. |
cifs == | The directory path. |
SMB.html == | The file name. |
The landmarks guide us along a path which eventually leads us to the file we wanted to access.
The SMB protocol pre-dates the use of URIs and was originally designed for use on LANs, not internetworks, so it naturally has a different (though surprisingly similar) way of specifying paths. A Universal Naming Convention (UNC) path comparable to the URI path above might look something like this:
\\ubiqx\cifs\SMB.html
...and would parse out like this:
ubiqx == | The name of the server. |
cifs == | The directory path. |
SMB.html == | The file name. |
Very similar indeed.
The devil is in the details.
-- Popular saying
One obvious difference between the two formats is that UNC doesn‘t provide a protocol specification. That‘s not because it always assumes SMB. The UNC format can support all sorts of filesharing protocols, but it is up to the underlying operating system or application to try to figure out which one to use. Protocol and transport discovery are handled by trial-and-error, with each possibility tested until something works. As you might imagine, a system with AppleTalk, NetWare, and SMB all enabled may have a lot of work to do.
The UNC format is handled natively by Microsoft & IBM‘s extended family of operating systems: DOS, OS/2, and Windows6. Samba‘s smbclient utility can also parse UNC names, but it does so at the application level rather than within the OS and it only ever tries to deal with SMB. Even so, smbclient must handle both NBT and naked transport, which can be tricky.
The first stop on our UNC tour of SMB is the server name field, which is really a server identifier field because it will accept addresses in addition to names. This book concerns itself with only two transports--NBT and naked TCP transport--so the only identifiers we care about are:
NetBIOS and DNS names both resolve to IP addresses, so all three are equivalent.
Sort of...
Recall that the NBT SESSION REQUEST packet requires a CALLED NAME in order to set up an NBT session with the server. Without a correct CALLED NAME, the NBT SESSION REQUEST may be rejected (different implementations behave differently). So...
...then we‘re in a bit of a pickle. How do we find the correct NetBIOS name to put into the CALLED NAME field? There really is no "right" way to reverse-map an IP address to a particular NetBIOS service name. The solution to this problem involves some guessing, and it‘s not pretty. We will go into detail when we discuss the interface between SMB and the transport layer.
Of course, if SMB is running over raw transport then there is no NBT SESSION REQUEST message and, therefore, no CALLED NAME. In that case, the NetBIOS name isn‘t needed at all, which saves a lot of fuss and bother.
A path! A path!
-- The Knights Who Say Ni
Monty Python And The
Holy Grail, Monty Python‘s
Flying Circus
The directory path looks just like a directory path, but there is one small thing that makes it different. That thing is called the "share name".
Whenever a resource is made available (shared) via SMB it is given a share name. The share name doesn‘t need to be the same as the actual name of the object being shared as it exists on the server. For example, consider the directory path below:
/dogs/corgi/stories/jolyon/
Suppose we just want to share the /stories subdirectory. If we simply call it "stories" no one will know what kind of stories it contains, so we should give it a more descriptive name. We might, for example, call it "dogbytes".
The share name takes the place of the actual directory name when the share is accessed via SMB. If the server is named "petserver", then the UNC path to the same directory would be:
\\petserver\dogbytes\jolyon\
As shown in figure 2.1, there can be more than one share name pointing to the same directory and access rules may be applied on a per-share basis. The idea is similar, in some ways, to that of symbolic links (symlinks) in Unix, or shortcuts in Windows. The share is a named pointer--with its own set of attributes--to the object being made available by the server.
This is the last stop on our quick UNC tour of SMB.
Files, like directories, should be fairly familiar and fairly straight-forward. As has been continually demonstrated, however, things in the CIFS world are not always as simple as they ought to be. Our point of interest on this part of the tour is the distinction between server filesystem syntax and semantics and client expectations...a very gnarled knot for CIFS implementors.
Consider, for example, a bunch of Windows clients connecting to an SMB server running on Linux. On the Linux system the filenames Corgi, corgi, and CORGI would all be distinct because Linux filesystems are typically case-sensitive. Windows, however, expects filenames to be case-insensitive, so all three names are the same from the Windows point of view. Thus, we have a conflict. How does a Linux server make all three files available to the Windows client?
Other difficult issues include:
These are complex problems, not easily solved. The CIFS protocol suite is not designed to be agnostic with regard to such things. In fact, CIFS goes out of its way at times to support features that are specific to DOS, OS/2, and Windows.
...and that concludes our tour. It‘s time to visit the gift shoppe.
The UNC format is specific to one family of operating systems. Earlier on, though, we compared UNC with the more portable and modern URI format. That‘s called foreshadowing. It‘s a literary trick used to build suspense and anticipation.
There is, in fact, such a thing as an SMB URL. It fits into the general URI syntax7 and can be used to specify files, directories, and other SMB-shared stuff. It is intended as a more portable, and more complete way to specify SMB paths at the application level.
As of this writing, the SMB URL is only documented in an IETF Internet Draft, and is not yet any kind of standard. That hasn‘t stopped folks from implementing it, though. The SMB URL is supported in a wide variety of products including the KDE and GNOME desktop GUI environments, web browsers such as Galeon and Konqueror, and Open Source CIFS projects like jCIFS and libsmbclient (the latter is included with Samba). Thursby Software and Apple Computer also make use of the SMB URL in their commercial CIFS implementations.
That‘s good news for CIFS implementors because it means that there is an accepted, cross-platform way to identify SMB-shared resources, both within LANs and across the Internet.
Our quick UNC tour provided an introduction to some of the basic concepts, and annoyances, of SMB. We will expand upon those ideas as we dig more deeply into the protocol. The UNC format itself is also important for a variety of reasons, both historical and practical. Not least among these is that UNC strings are used within some of the SMB messages that cross the wire.
The SMB URL format is equally significant. It is portable, flexible, and gaining in popularity. It will also form the basis for examples given later in the text. If you are implementing an SMB client, you will most likely want to have some convention for identifying resources. You could invent your own, or use UNC, but the SMB URL is probably your best option.
Getting there is half the fun.
-- Unknown
We are approaching this thing in layers. A little history, a quick introductory tour...and now this. It may seem like a bit of a diversion, but the goal in this section is to figure out how a client finds the server and initiates a connection. No, we‘re not dealing with SMB protocol yet, but we can‘t send SMB messages until we can talk to a server.
Think of a telephone call. If you want to call your cousin in New York the first thing you need to know is the telephone number. You could ask your uncle for the number or look it up in the telephone book, or perhaps you have it written on a scrap of paper somewhere in the kitchen with your favorite tofu recipes. If you dial the wrong number you will annoy some guy in a gas station in Brooklyn. When you dial the correct number, the underlying system will go through a complex process to set up the connection so that you can start talking to your cousin (or, more likely, to the answering machine).
...and if you want to connect with an SMB server you might need to resolve a NetBIOS or DNS name to an IP address. Once you have the address, you can attempt to open a session with the server.
Consider this simple SMB URL:
smb://server/
From the user‘s perspective, that should be enough to build an initial connection to an SMB server named "server".
From an implementation point of view, the first thing to do with this example is to parse out the "server" substring. In URI parlance, the field we are looking for is called the "host non-terminal"8, and it contains the name or address of the server to which we are trying to connect. Our term for the parsed-out string is "Server Identifier". Once we have extracted it, the next thing we need to know how to do is interpret it so that we can use the information to create the session.
The SMB URL format supports the use of three different identifier
types in the host field. We went over them briefly before. They are
the IP address, DNS name, or NetBIOS name of the destination. Our
next task is to figure out which is which.
If you want something done right
you have to do it yourself.
-- Well-known axiom
Presentation is everything, and it turns out that the code for interpreting the Server Identifier is verbose and tedious. Most of the busywork for handling NetBIOS names was covered in section 1, and there are plenty of tools for dealing with IP addresses and DNS names, so to save time we will describe how to interpret and resolve the address (and let you write the code yourself9).
Check the syntax of the input to determine whether it is a valid representation of an IP address. Do this test first. It is quick, and does not involve sending any queries out over the network. The inet_aton() function, common on Unix-like operating systems, does the job nicely for the four-byte IPv4 addresses used today.
IP version 6 (IPv6) addresses are different. They are longer, harder for a human to read, and potentially more complicated to parse out. Fortunately, when used in URLs they are always contained within square brackets, as in the following example:
smb://[fe80::240:f4ff:fe1f:8243]/
The square brackets are reserved characters, used specifically for this purpose10. They make it easy to identify an IPv6 IP address. Once identified, the IPv6 address can be converted into its internal format by the inet_pton() function, which is now supported by many systems.
You are likely to be eaten
by a grue.
-- Zork, Marc Blank and
David Lebling, InfoCom
If the Server Identifier isn‘t an IP address, it could be a NetBIOS name. To see if this is the case, the first step is to look for a dot (‘.‘). The SMB URL format does not allow un-escaped dots to appear in the NetBIOS name itself, so if there is a dot character in the raw string then consider the rest of the string to be a Scope ID. For example:
smb://my%2Enode.scope/
is made up of the NetBIOS name "MY.NODE" and the Scope ID "SCOPE". (The URL escape sequence for encoding a dot is %2E.)
Once the string has been parsed into its NetBIOS Name and Scope ID components, the next thing to do is to send an NBT Name Query. Always use a suffix value of 0x20, which is the prescribed suffix for SMB services. The handling of the query depends, of course, on whether the client is a B, M, P, or H node. For anything other than a B node, the IP address of the NBNS is required. Most client implementations keep such information in some form of configuration file or database.
If a positive response is received, keep track of the NetBIOS name and returned IP address. You will need them in order to connect to the server.
If the Server Identifier is neither an IP address nor a NetBIOS name, try DNS name resolution. The gethostbyname() function is commonly used to resolve DNS names to IP addresses, but be warned that this is a blocking function. It may take quite a while for it to do its job, and your program will do nothing in the mean time11. That is one reason that it is typically the last thing to try.
That is how to go about determining which kind of Server Identifier you‘ve been given. Isn‘t overloading fun? Now you see why the code for handling all of this is tedious and verbose. It really is not very difficult, though, it‘s just that it takes a bit of work to get it all coded up.
Port 139 is for NBT, and port 445 is for raw TCP--good rules of thumb. Recall, though, that the NBT Session Service provides a mechanism for redirection. In addition, some security protocols use high-numbered ports to tunnel SMB connections through firewalls. That means that the use of non-standard ports should be supported on the client side.
The SMB URL allows the specification of a destination port number, like so:
smb://server:1928/
Once again, that fits into standard URI syntax. If you spend any time using a web browser, the port field should be familiar.
What this all means, however, is that the port number does not always indicate which transport should be used. Rather the opposite; if the port number is not specified, the default port depends upon the transport. Knowing which transport to choose is, once again, something that requires some figuring out.
As has been stated previously, we are only considering the NBT and naked TCP transports. Both of these are IP-based and the behavior of SMB over these two is nearly identical, so it does not seem as though separating them would be very important...but this is CIFS we‘re talking about.
The crux of the problem is whether or not the NBT SESSION REQUEST message is required. If the server is expecting correct NBT semantics, then we will need to find a valid NetBIOS name to place into the CALLED NAME field. This is a complicated process, involving a lot of trial-and-error. The recipe presented below is only one way to go about it. A good chef knows how to adjust the ingredients and choose seasonings to get the desired result. This is as much an art as it is a science.
Running naked is probably the easiest transport test to try first. The procedure is tasteful and dignified: simply assume that the server is expecting raw TCP transport. Open a TCP connection to port 445 on the server, but do not send an NBT SESSION REQUEST--just start sending SMB messages and see if that works. There are four possible results from this test:
All of the above applies if the user did not specify a non-standard port number. If the input looks more like this:
smb://server:2891/
...then the option of falling back to NBT on port 139 is excluded. In addition, there is no way to guess which transport type should be used if a port number other than 139 or 445 is specified. (In theory, it is also possible to run NBT transport on port 445 and naked transport on port 139. If you catch anyone doing such a twisted thing you should probably notify the authorities.)
Fortunately, Windows systems (Windows95, ‘98, and W2K were tested)
return an NBT NEGATIVE SESSION RESPONSE if they get naked
semantics on an NBT service port. This makes sense, because it lets
the client know that NBT semantics are required. Samba‘s
smbd goes one better and simply ignores the lack of a
SESSION REQUEST message. Samba‘s behavior effectively merges
the two transport types and makes the distinction between them
irrelevant, which simplifies things on the server side and makes life
easier for the client.
Real Programmers
don‘t draw flowcharts.
--Unknown
The transport discovery process is illustrated using the anachronistic flowchart presented in figure 2.2.
If running naked didn‘t work, then you will probably need to try NBT transport. Also, back in section 2.3.1 we talked about the different types of Server Identifiers that most implementations support. One of those is the NetBIOS name, and it seems logical to assume that if the Server Identifier is a NetBIOS name then the transport will be NBT.
That‘s two good reasons to give NBT transport a whirl.
As stated earlier, the critical difference between the raw TCP and NBT transports is that NBT requires the SESSION REQUEST/POSITIVE SESSION RESPONSE exchange before the SMB messages can start flowing. The SESSION REQUEST, in turn, must contain a valid CALLED NAME. If the CALLED NAME is not correct, then some server implementations will reject the connection. (Windows seems to be quite picky, but Samba ignores the CALLED NAME field.)
Finding a valid CALLED NAME is easy if the Server Identifier is a NetBIOS name because, well... because there you are. The NetBIOS name is the correct CALLED NAME. Also, since the Server Identifier was resolved via an NBT Name Query, the server‘s IP address is known. That‘s everything you need.
There is one small problem with this scenario that could cause a little trouble: some NBNS servers can be configured to pass NetBIOS name queries through to the DNS system, which means that the DNS--not the NBNS--may have resolved the name to an IP address. That would mean that we have a false-positive and the Server Identifier is not, in fact, a NetBIOS name. If that happens, you could wind up trying to make an NBT connection to a system that isn‘t running NBT services. (The opposite of the "run naked" test described above.)
Detecting an SMB service that wants naked transport is not as clean and easy as detecting one that wants NBT. In testing, a Windows2000 system running naked TCP transport did not respond at all to an NBT SESSION REQUEST, and the client timed out waiting for the reply. This problem is neatly avoided if naked transport is attempted before NBT transport. Since Samba considers the SESSION REQUEST optional, this kind of transport confusion is not an issue when talking to a Samba server.
Reverse-mapping is the last, desperate means for finding a workable NetBIOS CALLED NAME so that a valid SESSION REQUEST can be sent. Reverse-mapping is also quite common. Your code will need to try this technique if naked transport didn‘t work and the Server Identifier was a DNS name or IP address--a situation which is not unusual.
As stated before, there is no right way to do reverse-mapping. Fortunately, there are a few almost-right ways to go about it. Here they are:
Send an NBT NODE STATUS QUERY to the server. If it responds, run through the list of returned names looking for a unique name with a suffix byte value of 0x20. Try using that name as the CALLED NAME when setting up the session. If there are multiple names with a suffix value of 0x20, try them in series until you get a POSITIVE SESSION RESPONSE (or until they all fail).
Stop laughing. It gets better.
This kludge was introduced in Windows NT4 and has been adopted by many other implementations. It is fairly common, but not universal.
The generic CALLED NAME is *SMBSERVER<20> (that is, "*SMBSERVER" with a suffix byte value of 0x20). Think of it as an alias, allowing you to connect to the SMB server without knowing its "real", registered NetBIOS name. The *SMBSERVER<20> name starts with an asterisk, which is against the rules, so it is never registered with the NBT Name Service. If you send a unicast Name Query for this name, the destination node should always send a NEGATIVE NAME QUERY RESPONSE in reply (assuming that it is actually running NBT).
A bit awkward but it does work...sometimes. Now for the coup de gras.
"Guess," said Marvin.
-- Restaurant at the End
of the Universe,
Douglas Adams
Try using the first label of the DNS name (the hostname of the server) as the CALLED NAME. If you were given an IP address you will need to do a reverse DNS lookup to get a name to play with (we suggested earlier that the DNS name might come in handy). As always, use a suffix byte value of 0x20.
If the first label doesn‘t work, try the first two labels (retaining the dot) and so on until you have a string that is longer than 15 bytes, at which point you give up.
Yes, there are implementations which actually do this.
If none of those options worked, then it is finally time to send an error message back to the user explaining that the Server Identifier is no good.
|
We are still dealing with the transport layer and haven‘t actually seen any SMBs yet. It is, however, finally time for some code. Listing 2.1 handles the basics of opening the connection with an SMB server. It is example code so, of course, it takes a few shortcuts. For instance, it completely side-steps Server Identifier interpretation and transport discovery (that is, everything we just covered).
The code in listing 2.1 provides an outline for setting up the session via NBT or raw TCP. With that step behind us, we won‘t have to deal with the details of the transport layer any longer. Let‘s run through some code highlights quickly and put all that transport stuff behind us.
The name entered on the command line will be used as the CALLED NAME. If the input string begins with an asterisk, the generic *SMBSERVER<20> name will be used instead.
In a correct implementation, the name should be the client‘s NetBIOS Machine Name (which is typically the same as the client‘s DNS hostname) with a suffix byte value if 0x00.
The contents of the CALLING NAME field are not particularly significant. According to the expired Leach/Naik CIFS Internet Draft, the same name from the same IP address is supposed to represent the same client...but you knew that. Samba can make use of the CALLING NAME via a macro in the smb.conf configuration file. The macro is used for all sorts of things, including generating per-client log files.
We leave this as an
exercise for the reader.
-- Unknown
/* ** Do real work here. ** */
That‘s where the SMB stuff is supposed to happen. At that point in the code, the session has been established on top of the transport layer and it is time to start moving those Server Message Blocks.
Use the program above as a starting point for building your own SMB client utility. Add a parser capable of dissecting the UNC or SMB URL format, and then code up Server Identifier resolution and transport discovery, as described above. When you have all of that put together, you will have completed the foundation of your SMB client.
Are we there yet?
-- Kids in the back seat.
We have spent a lot of time and effort preparing for this expedition, and we are finally ready to venture into SMB territory. It can be a treacherous journey, though, so before we push ahead we should re-check our equipment.
If you are going to start testing, you have to have something at which to fling packets. When choosing a test server, keep in mind that SMB has grown and changed and evolved and adapted and mutated over the years. You want a server that can be configured to meet your testing needs. Samba, of course, is highly configurable. If you know your way around the Windows Registry, you may have luck with those systems as well. In particular, you probably want to avoid strong password encryption during the initial stages. Handling authentication is a big chunk of work, and it is best to try and reduce the number of simultaneous problems to a manageable few.
|
The next thing you will want is a packet flinger. That is, a working client. You need this for testing, and to compare behavior when debugging your own client. Samba offers the smbclient utility, and jCIFS comes with a variety of example programs. Windows systems all have SMB support built-in. That‘s quite a selection from which to choose.
Always your best friend. A good packet analyzer--one with a lot of built-in knowledge of SMB--will be your trusted guide through the SMB jungle.
When exploring NBT we relied upon RFC 1001 and RFC 1002 as if they were ancient maps, drawn on cracked and drying parchment, handed down to us by those who had gone before. In the wilds of SMB territory, we will count on the SNIA CIFS Technical Reference as our primary resource. The old X/Open SMB specification and the SMB/CIFS documentation available from Microsoft‘s FTP server will also come in handy. For the sake of efficiency, from here on out we will be a bit less formal and refer to the SNIA doc as "the SNIA doc", and the X/Open doc as "the X/Open doc".
|
Keep in mind that the goal of our first trip into the wilds of SMB-land is to become familiar with the terrain and to study SMBs in their natural habitat, so we can learn about their anatomy and behavior. We are not ready yet for a detailed study of SMB innards. That will come later.
We need to capture a few SMBs to see what they look like up close. That means it‘s time to take a look at the wire and see what‘s there to be seen. Fire up your protocol analyzer, and then your SMB client. If you can configure your test server to allow anonymous connections (no username, no password) it will simplify things at this stage. If you can‘t, then things won‘t run quite as they are shown below. Don‘t worry, it will be close enough.
For this example, we will use the Exists.java program that comes with jCIFS. It is a very simple utility that does nothing more than verify the existence of the object specified by the given SMB URL string, like so:
shell |
$ java Exists smb://smedley/home smb://smedley/home exists $ |
The above shows that we were able to access the HOME share on node SMEDLEY. A similar test can be performed using Samba‘s smbclient, or with the NET USE command under Windows12:
DOS Prompt |
C:\> net use \\smedley\home The command was completed successfully. C:\> net use /d \\smedley\home The command was completed successfully. C:\> |
Those simple commands will generate the packets we want to capture and study. Stop your sniffer and take a look at the trace. You should see a chain of events similar to the following:
No. Source Destination Protocol Info --- -------- ---------------- -------- ------------------------------ 1 Marika 255.255.255.255 NBNS Name query 2 Smedley Marika NBNS Name query response 3 Marika Smedley TCP 34102 > netbios-ssn [SYN] 4 Smedley Marika TCP netbios-ssn > 34102 [SYN, ACK] 5 Marika Smedley TCP 34102 > netbios-ssn [ACK] 6 Marika Smedley NBSS Session request 7 Smedley Marika NBSS Positive session response 8 Marika Smedley TCP 34102 > netbios-ssn [ACK] 9 Marika Smedley SMB Negotiate Protocol Request 10 Smedley Marika SMB Negotiate Protocol Response 11 Marika Smedley SMB Session Setup AndX Request 12 Smedley Marika SMB Session Setup AndX Response 13 Marika Smedley TCP 34102 > netbios-ssn [FIN, ACK] 14 Smedley Marika TCP netbios-ssn > 34102 [FIN, ACK] 15 Marika Smedley TCP 34102 > netbios-ssn [ACK] |
The above is edited output from an Ethereal capture13. The packets were generated using the jCIFS Exists utility, as described above. In this case jCIFS was talking to an old Windows95 system, but any SMB server should produce the same or similar results.
The trace is reasonably simple. The first thing that node MARIKA does is send a broadcast NBT Name query to find node SMEDLEY, and SMEDLEY responds. Packets 3, 4, & 5 show the TCP session being created. (Note that netbios-ssn is the descriptive name given to port 139.) Packets 6 and 7 are the NBT SESSION REQUEST/SESSION RESPONSE exchange, and packet #8 is an ACK message, which is just TCP taking care of its business.
Packets 9 and 10 are what we want. These are our first SMBs.
I never metaphor I couldn‘t mix.
-- Me
Figure 2.3 provides an overview of SMB gross anatomy. It shows that SMBs are composed of three basic parts:
Either or both of the latter two segments may be vestigial (size == 0) in some specimens.
Starting at the top, the SMB header is arranged like so:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 0 |
1 1 |
1 2 |
1 3 |
1 4 |
1 5 |
1 6 |
1 7 |
1 8 |
1 9 |
2 0 |
2 1 |
2 2 |
2 3 |
2 4 |
2 5 |
2 6 |
2 7 |
2 8 |
2 9 |
3 0 |
3 1 |
0xff | ‘S‘ | ‘M‘ | ‘B‘ | ||||||||||||||||||||||||||||
COMMAND | STATUS... | ||||||||||||||||||||||||||||||
...STATUS | FLAGS | FLAGS2 | |||||||||||||||||||||||||||||
EXTRA ... ... |
|||||||||||||||||||||||||||||||
TID | PID | ||||||||||||||||||||||||||||||
UID | MID |
We can also dissect the header using the simple syntax presented previously:
SMB_HEADER { PROTOCOL = "\xffSMB" COMMAND = <SMB Command code (one byte)> STATUS = <Status code> FLAGS = <Old flags> FLAGS2 = <New flags> EXTRA = <Sometimes used for additional data> TID = <Tree ID> PID = <Process ID> UID = <User ID> MID = <Multiplex ID> }
We now have a pair of perspectives on the header structure. Time for some good, old-fashioned descriptive text.
The next byte is the COMMAND field, which tells us what kind of SMB we are looking at. In the NEGOTIATE PROTOCOL messages captured above, the COMMAND field has a value of 0x72 (aka. SMB_COM_NEGOTIATE). The SNIA doc has a list of the available command codes. That list is probably complete, but this is SMB we are talking about so you never know...
DOS and OS/2 use 16-bit error codes, grouped into classes. To accommodate these codes, the STATUS field is subdivided like so:
0 1 2 3 4 5 6 7 8 9 1
01
11
21
31
41
51
61
71
81
92
02
12
22
32
42
52
62
72
82
93
03
1ErrorClass <reserved> ErrorCode
WindowsNT introduced a new set of 32-bit error codes, known as NT_STATUS codes. These use the entire status field to hold the NT_Status value:
0 1 2 3 4 5 6 7 8 9 1
01
11
21
31
41
51
61
71
81
92
02
12
22
32
42
52
62
72
82
93
03
1NT_Status
Be afraid. Be very afraid.
-- Veronica Quaife
(Geena Davis)
The Fly (1986)
The first formerly-reserved byte is now known as the FLAGS field. The bits of the FLAGS field are used to modify the interpretation of the SMB. For example, the highest-order bit is used to indicate whether the SMB is a request (0) or a response (1).
Following the FLAGS field is the two-byte FLAGS2 field. This set of bits is used to indicate the use of newer features, such as the 32-bit NT_STATUS error codes.
0 1 2 3 4 5 6 7 8 9 1
01
11
21
31
41
51
61
71
81
92
02
12
22
32
42
52
62
72
82
93
03
1PidHigh Signature... ...Signature... ...Signature <unused>
The PidHigh subfield is used to accommodate systems that have 32-bit Process IDs. The original SMB header format only had room for 16-bit PIDs (in the PID field, described further on).
The 8-byte Signature subfield is for SMB message signing, which uses cryptography to protect against a variety of attacks that might be tried by badguys hoping to gain unauthorized access to SMB shares.
When not in use, these fields must be filled with zeros.
TID: |
The "Tree ID".
In SMB, a share name typically represents a directory or subdirectory tree on the server. The SMB used to open a share is called a "Tree Connect" because it allows the client to connect to the shared [sub]directory tree. That‘s where the name comes from. The TID field is used to identify connections to shares once they have been established. |
PID: |
The "Process ID".
This value is set by the client, and is intended as an identifier for the process sending the SMB request. The most important thing to note regarding the PID is that file locking and access modes are maintained relative to the value in this field. The PID is 16 bits wide, but it can be extended to
32 bits using the EXTRA.PidHigh field described earlier.
|
UID: |
The "User ID"
This is also known as a VUID (Virtual User ID). It is assigned by the server after the user has authenticated and is valid until the user logs off. It does not need to be the user‘s actual User ID on the server system. Think of it as a session token assigned to a successful logon. |
MID: |
The "Multiplex ID".
This is used by the client to keep track of multiple outstanding requests. The server must echo back the MID and the PID provided in the client request. The client can use those values to make sure that the reply is matched up to the correct request. |
The TID and [V]UID are assigned and managed by the server, while the PID and MID are assigned by the client. It is important to note that the values in these fields do not necessarily have any meaning outside of the SMB connection. The PID, for example, does not need to be the actual ID of the client process. The client and server assign values to these fields in order to keep track of context, and that‘s all.
In the middle of the SMB message are two fields labeled WordCount and Words[]. For our purposes, we will identify these two fields as being the SMB_PARAMETERS block, which looks like this:
0 1 2 3 4 5 6 7 8 9 1
01
11
21
31
41
51
61
71
81
92
0. . . WordCount Words...
SMB_PARAMETERS { WordCount = <Number of words in the Words array> Words[WordCount] = <SMB parameters; varies with SMB command> }
The Words field is simply a block of data that is 2 × WordCount bytes in length. Perhaps at one time the intention was that it would contain only two-byte values (a quick look at COREP.TXT suggests that this is the case). In practice, all sorts of stuff is thrown in there.
Each SMB message type (species?) has a different record structure that is carried in the Words block. Think of that structure as representing the parameters passed to a function (the function identified by the SMB command code listed in the header).
Following the SMB_PARAMETERS is another block of data, the content of which also varies in structure on a per-SMB basis:
0 1 2 3 4 5 6 7 8 9 1
01
11
21
31
41
51
61
71
81
92
0. . . ByteCount Bytes...
SMB_DATA { ByteCount = <Number of bytes in the Bytes field> Bytes[ByteCount] = <Contents varies with SMB command> }
The Bytes field holds the data to be manipulated. For example, it may contain the data retrieved in response to a READ operation, or the data to be written by a WRITE operation. In many cases, though, the SMB_DATA block is just another record structure with several subfields. Through time, SMB has evolved lazily and any functional distinction that may have separated the Parameter and Data blocks has been blurred.
Note that the SMB_DATA.ByteCount field is an unsigned short, while the SMB_PARAMETERS.WordCount field is an unsigned byte. That means that the SMB_PARAMETERS.Words block is limited in length to 510 bytes (2 × 255), while SMB_DATA.Bytes may be as much as 65535 bytes in length. If you add all that up, and then add in the SMB_PARAMETERS.WordCount field, the SMB_DATA.ByteCount field, and the size of the header, you will find that the whole thing fits easily into the 217-1 bytes made available in the NBT SESSION MESSAGE header.
Now that we have an overview of the structure of SMB messages, we can take a closer look at our live specimen. Remember packets 9 and 10 from the capture we made earlier? They show a NEGOTIATE PROTOCOL exchange. Let‘s get out the tweezers, the pocket knife, & dad‘s hammer and see what‘s inside.
NEGOTIATE_PROTOCOL_REQUEST { SMB_HEADER { PROTOCOL = "\xffSMB" COMMAND = SMB_COM_NEGOTIATE (0x72) STATUS { ErrorClass = 0x00 (Success) ErrorCode = 0x0000 (No Error) } FLAGS = 0x18 (Pathnames are case-insensitive) FLAGS2 = 0x8001 (Unicode and long filename support) EXTRA { PidHigh = 0x0000 Signature = 0 (all bytes zero filled) } TID = 0 (Not yet known) PID = <Client Process ID> UID = 0 (Not yet known) MID = 2 (often 0 or 1, but varies per OS) } SMB_PARAMETERS { WordCount = 0 Words = <empty> } SMB_DATA { ByteCount = 12 Bytes { BufferFormat = 0x02 (Dialect) Name = "NT LM 0.12" (nul terminated) } } }
The breakdown of packet 9 shows the SMB NEGOTIATE PROTOCOL REQUEST as sent by the jCIFS Exists utility. Other clients will use slightly different values, but they are all variations on the same theme. Some features worth noting:
The Data block in the request contains the list of protocols that the client is able to speak. jCIFS only knows one dialect, so only one name is listed in the message above. As you can see, jCIFS implements the "NT LM 0.12" dialect (the most recent and widely supported as of this writing). Other clients, such as Samba‘s smbclient, support a longer list of dialects.
In the trace given above, Ethereal has identified packets 11 and 12 as being a SESSION SETUP ANDX exchange16. The term "ANDX" at the end of the names indicates that these messages belong to a curious class of creatures known as "AndX messages". SMB AndX messages are actually several SMBs combined into a single symbiotic packet as shown in figure 2.4. It is an efficient mutation.
<tpot> shouldn‘t that be an AntX?
-- Tim Potter on IRC
AndX messages work something like a linked list. Each Parameter block in an AndX message begins with the following structure:
0 1 2 3 4 5 6 7 8 9 1
01
11
21
31
41
51
61
71
81
92
02
12
22
32
42
52
62
72
82
93
03
1AndXCommand <reserved> AndXOffset
The AndXCommand field provides the SMB command code for the next AndX block in the list (not the current one). The AndXOffset contains the byte index, relative to the start of the SMB header, of that next AndX block--think of it as a pointer. Since the AndXOffset value is independent of the SMB_PARAMETERS.WordCount and SMB_DATA.ByteCount values, it is possible to provide padding between the AndX blocks as shown in figure 2.5.
Now that we have a general idea of what an SMB AndX message looks like we are ready to dissect packet 11. It looks like this:
SESSION_SETUP_ANDX_REQUEST { SMB_HEADER { PROTOCOL = "\xffSMB" COMMAND = SMB_COM_SESSION_SETUP_ANDX (0x73) STATUS { ErrorClass = 0x00 (Success) ErrorCode = 0x0000 (No Error) } FLAGS = 0x18 (Pathnames are case-insensitive) FLAGS2 = 0x0001 (Long filename support) EXTRA { PidHigh = 0x0000 Signature = 0 (all bytes zero filled) } TID = 0 (Not yet known) PID = <Client Process ID> UID = 0 (Not yet known) MID = 2 (often 0 or 1, but varies per OS) } ANDX_BLOCK[0] (Session Setup AndX Request) { SMB_PARAMETERS { WordCount = 13 AndXCommand = SMB_COM_TREE_CONNECT_ANDX (0x75) AndXOffset = 79 MaxBufferSize = 1300 MaxMpxCount = 2 VcNumber = 1 SessionKey = 0 CaseInsensitivePasswordLength = 0 CaseSensitivePasswordLength = 0 Capabilities = 0x00000014 } SMB_DATA { ByteCount = 20 AccountName = "GUEST" PrimaryDomain = "?" NativeOS = "Linux" NativeLanMan = "jCIFS" } } ANDX_BLOCK[1] (Tree Connect AndX Request) { SMB_PARAMETERS { WordCount = 4 AndXCommand = SMB_COM_NONE (0xFF) AndXOffset = 0 Flags = 0x0000 PasswordLength = 1 } SMB_DATA { ByteCount = 22 Password = "" Path = "\\SMEDLEY\HOME" Service = "?????" (yes, really) } } }
There is a lot of information in that message, but we are not yet ready to dig into the details. There is just too much to cover all of it at once. Our goals right now are simply to highlight the workings of the AndX blocks, and to provide a glimpse inside the SESSION SETUP ANDX & TREE CONNECT ANDX sub-messages so that we will have something to talk about later on.
The block labeled ANDX_BLOCK[0] is the body of the SESSION SETUP REQUEST, and ANDX_BLOCK[1] contains the TREE CONNECT REQUEST. Note that the AndXCommand field in the final AndX block is given a value of 0xFF. This, in addition to the zero offset in the AndXOffset field, indicates the end of the AndX list.
SMB conversations start after the session has been established via the transport layer. As a rule, the client always speaks first. Clients send requests, servers respond, and that‘s the way SMB is supposed to work. This is a hard-and-fast rule which means, of course, that there is an exception. Fortunately, we can (and will) put off talking about that exception until we talk about Opportunistic Locks (OpLocks).
The NEGOTIATE PROTOCOL REQUEST/RESPONSE is always the first SMB exchange in the conversation. The client and server need to know what language to speak before they can say anything else. This is also a hard-and-fast rule, but there are no exceptions (which is an exception to the rule that all hard-and-fast rules have exceptions).
Once the dialect has been selected, the next formality is to establish an SMB session using the SMB SESSION SETUP REQUEST message. We keep running into terminology twists, and here we have yet another. The SMB SESSION SETUP exchange sets up an SMB session within the NBT or naked TCP session.
Huh?
Well, yes, that‘s confusing. The problem is that we are talking about two different kinds of sessions here.
Ah, there‘s a clue! The SESSION SETUP is used to perform authentication and establish a user session with the server17. A quick look at the SESSION SETUP ANDX REQUEST block in the packet above shows that the Exists utility did in fact send a username--the name "GUEST", passed via the AccountName field--to the server.
Once the user session is established, the client may try to connect to a share using a TREE CONNECT SMB. It is a hard-and-fast rule that TREE CONNECT SMBs must follow the SESSION SETUP. There is an exception to this as well, which we will cover when we get to share-mode vs. user-mode authentication.
Figure 2.6 shows the right way to start an SMB conversation. Combining the SESSION SETUP ANDX and TREE CONNECT ANDX SMBs into a single AndX message is optional (jCIFS‘ Exists does, but Samba‘s smbclient doesn‘t). Once the conversation has been initiated using the above sequence, the client is free to improvise.
There is another small detail you may have noticed while studying the captured SMB packets--or perhaps you remember this from one of the !Alert boxes in the NBT section: SMBs are written using little-endian byte order. If your target platform is big-endian, or if you want your code to be portable to big-endian systems, you will need to be able to handle the conversion between host and SMB byte order.
The htonl(), htons(), ntohl(), and ntohs() functions won‘t help us here. They convert between host and network order. We need to be able to convert between host and SMB order (and SMB order is definitely not the same as network order).
So, to solve the problem, we need a little bit of code, which is presented here mostly to get it out of the way so that we won‘t have to bother with it when we are dealing with more complex issues. The functions in Listing 2.2 read short and long integer values directly from incoming message buffers and write them directly to outgoing message buffers.
Our field trip into SMB territory is now over. We have covered a lot of ground, collected samples, and taken a look at SMBs in the wild. Our next step will be doing the lab work, studying our specimens under a microscope. It is time to take a break, relax, and reflect on what we have learned so far.
Time for a cup of tea.
In the next section we will go back over the SMB header in a lot more detail with the goal of explaining some of the key concepts that we have only touched on so far. You will probably want to be well rested and in a good mood for that.
During that first expedition into SMB territory we continually deferred studying the finer details of the SMB header, among other things. We were trying to cover the general concepts, but now we need to dig into the guts of SMB to see how things really work. Latex gloves and lab coats required.
Let‘s start by revisiting the header layout. Just for review, here‘s what it looks like:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 0 |
1 1 |
1 2 |
1 3 |
1 4 |
1 5 |
1 6 |
1 7 |
1 8 |
1 9 |
2 0 |
2 1 |
2 2 |
2 3 |
2 4 |
2 5 |
2 6 |
2 7 |
2 8 |
2 9 |
3 0 |
3 1 |
0xff | ‘S‘ | ‘M‘ | ‘B‘ | ||||||||||||||||||||||||||||
COMMAND | STATUS... | ||||||||||||||||||||||||||||||
...STATUS | FLAGS | FLAGS2 | |||||||||||||||||||||||||||||
EXTRA ... ... |
|||||||||||||||||||||||||||||||
TID | PID | ||||||||||||||||||||||||||||||
UID | MID |
The first four bytes are constant, so we won‘t worry about those. The COMMAND field is fairly straight-forward too; it‘s just a one byte field containing an SMB command code. The list of available codes is given in section 5.1 of the SNIA doc. The rest of the header is where the fun lies....
Things get interesting starting at the STATUS field. It wouldn‘t be so bad except for the fact that there are two possible error code formats to consider. There is the DOS & OS/2 format, and then there is the NT_STATUS format. In C-language terms, the STATUS field looks something like this:
typedef union { ulong NT_Status; struct { uchar ErrorClass; uchar reserved; ushort ErrorCode; } DosError; } Status;
From the client side, one way to deal with the split personality problem is to use the DOS codes exclusively18. These are fairly well documented (by SMB standards), and should be supported by all SMB servers. Using DOS codes is probably a good choice, but there is a catch... There are some advanced features which simply don‘t work unless the client negotiates NT_STATUS codes.
Rats!
-- Charlie Brown
Peanuts, by Charles Schultz
|
Another reason to support NT_STATUS codes is that they provide finer-grained diagnostics, simply because there are more of them defined than there are DOS codes. Samba has a fairly complete list of the known NT_STATUS codes, which can be found in the samba/source/include/nterr.h file in the Samba distribution. The list of DOS codes is in doserr.h in the same directory.
We have already described the structure of the DOS error codes. NT_STATUS codes also have a structure, and it looks like this:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 0 |
1 1 |
1 2 |
1 3 |
1 4 |
1 5 |
1 6 |
1 7 |
1 8 |
1 9 |
2 0 |
2 1 |
2 2 |
2 3 |
2 4 |
2 5 |
2 6 |
2 7 |
2 8 |
2 9 |
3 0 |
3 1 |
Level | <reserved> | Facility | ErrorCode |
In testing, it appears as though the Facility field is always set to zero (FACILITY_NULL) for SMB errors. That leaves us with the Level and ErrorCode fields to provide variety ... and, as we have suggested, there is quite a bit of variety. Samba‘s nterr.h file lists over 500 NT_STATUS codes, while doserr.h lists only 99 (and some of those are repeats).
Level is one of the following:
00 == Success
01 == Information
10 == Warning
11 == Error
Since the next two bits (the <reserved> bits) are always zero, the highest-order nibble will have one of the following values: 0x0, 0x4, 0x8, or 0xC. At the other end of the longword, the ErrorCode is read as an unsigned short (just like the DOS ErrorCode field).
The availability of Samba‘s list of NT_STATUS codes makes things easy. It took a bit of doing to generate that list, however, as most of the codes are not documented in an accessible form. Andrew Tridgell described the method below, which he used to generate a list of valid NT_STATUS codes. His results were used to create the nterr.h file used in Samba.
|
Okay, now for the next conundrum...
Servers have it tougher than clients. Consider a server that needs to respond to one client using DOS error codes, and to another client using NT_STATUS codes. That‘s bad enough, but consider what happens when that server needs to query yet another server in order to complete some operation. For example, a file server might need to contact a Domain Controller in order to authenticate the user.
The problem is that, no matter which STATUS format the Domain Controller uses when responding to the file server, it will be the wrong format for one of the clients. To solve this problem the server needs to provide a consistent mapping between DOS and NT_STATUS codes.
WindowsNT and Windows2000 both have such mappings built-in but, of course, the details are not published (a partial list is given in section 6 of the SNIA doc). Andrew Bartlett used a trick similar to Tridge‘s in order to generate the required mappings. His setup uses a Samba server running as a Primary Domain Controller (PDC), and a Windows2000 system providing SMB file services. A third system, running Samba‘s smbtorture testing utility, acts as the client. When the client system tries to log on to the Windows server, Windows passes the login request to the Samba PDC.
The test works like this:
The test configuration is shown in figure 2.7.
|
Andrew‘s test must be rerun periodically. The mappings have been known to change when Windows service packs are installed. See the file samba/source/libsmb/errormap.c in the Samba distribution for more fun and adventure19.
Most (but not all) of the bits in the older FLAGS field
are of interest only to older servers. They represent features that
have been superseded by newer features in newer servers. It would be
nice if all of the old stuff would just go away so that we wouldn‘t
have to worry about it. It does seem, in fact, as though this is
slowly happening. (Maybe it would be better if the old stuff stayed
and the new stuff had never happened. Hmmm...)
Duh... dat sounds logical!
-- Baby Huey
Harvey Entertainment
In any case, this next table presents the FLAGS bits in order of descending significance--the opposite of the order used in the SNIA doc. English speaking people tend to read from left to right and from top to bottom, so it seems logical (as this book is, more or less, written in English20) to transpose left-to-right order into a top-to-bottom table.
SMB_HEADER.FLAGS | ||
---|---|---|
Bit # | Name / Bitmask / Values | Description |
7 |
SMB_FLAGS_SERVER_TO_REDIR 0x80
0: request |
What an awful name!
|
6 |
SMB_FLAGS_REQUEST_BATCH_OPLOCK 0x40
0: Exclusive |
Obsolete. If bit 5 is set, then bit 6 is the "batch OpLock" (aka. OPBATCH) bit. Bit 6 should be clear if bit 5 is clear. In a request from the client, this bit is used to indicate whether the client wants an exclusive OpLock (0) or a batch OpLock (1). In a response, this bit indicates that the server has granted the batch OpLock. OpLocks (opportunistic locks) will be covered later. This bit is only used in the deprecated SMB_COM_OPEN, SMB_COM_CREATE, and SMB_COM_CREATE_NEW SMBs. It should be zero in all other SMBs. The SMB_COM_OPEN_ANDX SMB has a separate set of flags that handle OpLock requests, as does the SMB_COM_NT_CREATE_ANDX SMB. |
5 |
SMB_FLAGS_REQUEST_OPLOCK 0x20
0: no OpLock |
Obsolete. This is the "OpLock" bit. If this bit is set in a request, it indicates that the client wants to obtain an OpLock. If set in the reply, it indicates that the server has granted the OpLock. OpLocks (opportunistic locks) will be covered later. This bit is only used in the deprecated SMB_COM_OPEN, SMB_COM_CREATE, and SMB_COM_CREATE_NEW SMBs. It should be zero in all other SMBs. The SMB_COM_OPEN_ANDX SMB has a separate set of flags that handle OpLock requests, as does the SMB_COM_NT_CREATE_ANDX SMB. (Sigh.) |
4 |
SMB_FLAGS_CANONICAL_PATHNAMES 0x10
0: Host format |
Obsolete. This was supposed to be used to indicate whether or not pathnames in SMB messages were mapped to their "canonical" form. Thing is, it doesn‘t do much good to write a client or server that doesn‘t map names to the canonical form (which is basically DOS, OS/2, or Windows compatible). This bit should always be set (1). |
3 |
SMB_FLAGS_CASELESS_PATHNAMES 0x08
0: case-sensitive |
When this bit is clear (0), pathnames should be treated as
case-sensitive. When the bit is set, pathnames are considered
caseless.
All good in theory. The trouble is that some systems assume caseless pathnames no matter what the state of this bit. Best practice on the client side is to leave this bit set (1) and always assume caseless pathnames. |
2 | 0x04 |
<Reserved> (must be zero) ...well, sort of. This bit is clearly listed as "Reserved (must be zero)" in both the SNIA and the X/Open docs, yet the latter contains some odd references to optionally using this bit in conjunction with OpLocks. It‘s probably a typo. Best bet is to clear it (0) and leave it alone. |
1 |
SMB_FLAGS_CLIENT_BUF_AVAIL 0x02
0: Not posted |
Obsolete. This was probably useful with other transports, such as NetBEUI. If the client sets this bit, it is telling the server that it has already posted a buffer to receive the server‘s response. The expired Leach/Naik Internet Draft says that this allows a "send without acknowledgment" from the server. This bit should be Clear (0) for use with NBT and naked TCP transports. |
0 |
SMB_FLAGS_SUPPORT_LOCKREAD 0x01
0: Not supported |
Obsolete. If this bit is set in the SMB NEGOTIATE PROTOCOL RESPONSE, then the server supports the deprecated SMB_COM_LOCK_AND_READ and SMB_COM_WRITE_AND_UNLOCK SMBs. Unless you are implementing outdated dialects, this bit should be clear (0). |
The NEGOTIATE PROTOCOL REQUEST that we dissected back in section 2.4.3 shows only the SMB_FLAGS_CANONICAL_PATHNAMES and SMB_FLAGS_CASELESS_PATHNAMES bits set, which is probably the best thing for new implementations to do. Testing with other clients may reveal other workable combinations.
Now let‘s take a look at the newer flags in the FLAGS2 field.
SMB_HEADER.FLAGS2 | ||
---|---|---|
Bit # | Name / Bitmask / Values | Description |
15 |
SMB_FLAGS2_UNICODE_STRINGS 0x8000
0: ASCII |
If set (1), this bit indicates that string fields within the SMB
message are encoded using a two-byte, little endian Unicode
format. The SNIA doc says that the format is UTF-16LE but some
folks on the Samba Team say it‘s really UCS-2LE. The latter is
probably correct, but it may not matter as both formats are
probably the same for the Basic Multilingual Plane. Doesn‘t
Unicode sound like fun21?
If clear (0), all strings are in 8-bit ASCII format (by which we actually mean 8-bit OEM character set format). |
14 |
SMB_FLAGS2_32BIT_STATUS 0x4000
0: DOS error code |
Indicates whether the STATUS field is in DOS or NT_STATUS format. This may also be used to help the server guess which format the client prefers before it has actually been negotiated. |
13 |
SMB_FLAGS2_READ_IF_EXECUTE 0x2000
0: Execute != Read |
A quirky little bit this. If set (1), it indicates that execute permission on a file also grants read permission. It is only useful in read operations. |
12 |
SMB_FLAGS2_DFS_PATHNAME 0x1000
0: Normal pathname |
This is used with the Distributed File System (DFS), which we haven‘t covered yet. If this bit is set (1), it indicates that the client knows about DFS, and that the server should resolve any UNC names in the SMB message by looking in the DFS namespace. If this bit is clear (0), the server should not check the DFS namespace. |
11 |
SMB_FLAGS2_EXTENDED_SECURITY 0x0800
0: Normal security |
If set (1), this bit indicates that the sending node understands Extended Security. We‘ll touch on this again when we discuss authentication. |
10 | 0x0400 | <Reserved> (must be zero) |
9 | 0x0200 | <Reserved> (must be zero) |
8 | 0x0100 | <Reserved> (must be zero) |
7 | 0x0080 | <Reserved> (must be zero) |
6 |
SMB_FLAGS2_IS_LONG_NAME 0x0040
0: 8.3 format |
If set (1), then any pathnames that the SMB contains are long pathnames, else the pathnames are in 8.3 format. Any new CIFS implementation really should support long names. |
5 | 0x0020 | <Reserved> (must be zero) |
4 | 0x0010 | <Reserved> (must be zero) |
3 | 0x0008 | <Reserved> (must be zero) |
2 |
SMB_FLAGS2_SECURITY_SIGNATURE 0x0004
0: No signature |
If set, the SMB contains a Message Authentication Code (MAC). The MAC is used to authenticate each packet in a session, to prevent various attacks. |
1 |
SMB_FLAGS2_EAS 0x0002
0: No EAs |
Indicates that the client understands Extended Attributes.
Note that the SNIA doc talks about "Extended Attributes" and about "Extended File Attributes". These are two completely different concepts. Extended Attributes are a feature of OS/2. They are mentioned in section 1.1.6 (page 2) of the SNIA doc and explained in better detail on page 87. Extended File Attributes are described in section 3.13 (page 30) of the SNIA doc. The SMB_FLAGS2_EAS bit deals with Extended Attribute support. |
0 |
SMB_FLAGS2_KNOWS_LONG_NAMES 0x0001
0: Client wants 8.3 |
Set by the client to let the server know that long names are acceptable in the response. |
Some of the flags are used to modify the interpretation of the SMB message, while others are used to negotiate features. Some do both. It may take some experimentation to find the safest way to handle these bits. Implementations are not consistent, so new code must be fine-tuned.
You may need to refer back to these tables as we dig further into the details. Note that the constant names listed above may not match those in the SNIA doc, or those in other docs or available source code. There doesn‘t seem to be a lot of agreement on the names.
Um, actually we are going to delay covering the EXTRA field yet again. EXTRA.PidHigh will be thrown in with the PID field, and EXTRA.Signature will be handled as part of authentication.
It would seem logical that the [V]UID and TID fields would be somehow related. Both are assigned and managed by the server, and we said before that the SESSION SETUP (where the logon occurs) is supposed to happen before the TREE CONNECT.
Well, put all that aside and pay attention to this little story...
Once upon a time there were many, many magic kingdoms taking up office space in cities and towns around the world. In each of these magic kingdoms there were lots of overpaid advisors called VeePees. The VeePees were all jealous of one another, but they were more jealous of the underpaid wizards in the IT department who had power over the data and could work spells and make the numbers come out all right. Then, one day, evil marketing magicians appeared and convinced the VeePees that they could steal all of the power away from the wizards of IT and have it for themselves. To do this, the only thing the VeePees would need was a magic box called a PeeCee (the name appealed to the VeePees). PeeCees, of course, were not cheap but the lure of power was great and the marketing magicians knew that the VeePees had control of the budget. Soon, the wizards of IT discovered that their supplies of mag-tapes and 8-inch floppies were dwindling, and that no one had bothered to update the service contracts on their VAXes. Worse, the VeePees started taunting them, saying "We don‘t need you any more. We have spreadsheets". The wise wizards of IT smiled quietly, went back to their darkened cubicles, and entertained themselves by implementing EMACS in TECO macro language. They did not seem at all surprised when the VeePees showed up asking questions like "What happens if I format C-colon?" and "Should I Abort, Retry, or--um--Fail?". The wizards understood what the VeePees did not: With power there must be equal measures of knowledge and understanding, otherwise the power will consume the data--and the user. The marketing magicians, seeing that their golden goose was molting, came up with a bold plan. They conjured up a LAN system and connected it to a shiny new fileserver, which they gave to the IT wizards. At first, the wizards were delighted by the wonderful new server and the beautiful strands of network cable running all over the kingdom. They quickly realized, however, that they had been tricked. The client/server architecture had effectively separated authority from responsibility, and the wizards were left with only the latter. ...and so it is unto this very day. The VeePees and their minions
have their PeeCees and hold the power of the data, but they remain
under the influence of the sinister marketing magicians. The wizards
of IT are still underpaid, have little or no say when decisions are
made, and are held responsible and told to clean up the mess whenever
anything goes wrong. A wholly dysfunctional arrangement.
|
So what the purplebananafish does this have to do with TIDs and UIDs? Well, see, it‘s like this...
Early corporate LANs, such as those in our story, were small and self-contained. The driving goal was to make sure that the data were available to everyone in the office who could legitimately claim to need access. Security was not considered a top priority, so PC OSes (eg. DOS) did not support complicated minicomputer features like user-based authentication. Given the environment, it is not surprising that the authentication system originally built into SMB was (by today‘s standards) quite primitive. Passwords, if they were used at all, were assigned to shares--not users--and everyone who wanted to access the same share would use the same password.
This early form of SMB authentication is now known as "Share Level" security. It does not include the concept of user accounts, so the UID field is always zero. The password is included in the TREE CONNECT message, and a valid TID indicates a successfully authenticated connection. In fact, though the UID field is listed in the SMB message format layout described in the ancient COREP.TXT scrolls, it is not mentioned again anywhere else in that document. There is no mention of a SESSION SETUP message either.
There are some interesting tricks that add a bit of flexibility to Share Level security. For example, a single share may have multiple passwords assigned, each granting different access rights. It is fairly common, for instance, to assign separate passwords for read-only vs. read/write access to a share.
Another interesting fudge is often used to provide access to user home directories. The server (which, in this case, understands user-based authentication even if the protocol and/or client do not) simply offers usernames as share names. When a user connects to the share matching their username, they give their own login password. The server then checks the username/password pair using its normal account validation routines. Thus, user-based authentication can be mapped to Share Level security. (See figure 2.8.)
Share Level security, though still used, is considered deprecated.
It has been replaced with "User Level" security which, of
course, makes use of username/password instead of sharename/password
pairs.
What a difference
15 years can make
-- John Lewkowicz,
The Complete MUMPS
Under User Level security, the SESSION SETUP is performed as the authentication step before any TREE CONNECT requests may be sent. If the logon succeeds, the server will assign a valid (non-zero) UID. Subsequent TREE CONNECT attempts can use the UID as an authentication token when requesting access to a share. If User Level security is in use, the password field in the TREE CONNECT message will be blank.
So, with User Level security, the client must authenticate to get a valid UID, and then present the UID to gain access to shares. Thing is, more than one UID may be generated within a single connection, and the UID used to connect to the share does not need to be the same as the one used to access files within the share.
Simply put:
That‘s the idea, anyway. The client provides values for these fields when it sends a request to the server, and the server is supposed to echo the values back in the response. That way, the client can match the reply to the original request.
Some systems (such as Windows and OS/2) multiplex all of the SMB traffic between a client and a server over a single TCP connection. If the client OS is multi-tasking there may be several active SMB sessions running concurrently, so there may be several requests outstanding at any given time. The SMB conversations are all intertwined, so the client needs a way to sort out the replies and hand them off to the correct thread within the correct process. (See figure 2.9.)
The PID field is also used to maintain the semantics of local file I/O. Think about a simple program, like the one in listing 2.3 which opens a file in read-only mode and dumps the contents. Consider, in particular, the call to the open() function, which returns a file descriptor. File descriptors are maintained on a per-process basis--that is, each process has its own private set. The descriptor is an integer used by the operating system to identify an internal record that keeps track of lots of information about the open file, such as:
Now take all of that and stretch it out across a network. The files physically reside on the server and information about locks, offsets, etc. must be kept on the server side. The process that has opened the files, however, resides on the client and all of the file status information is relevant within the context of that process. That brings us back to what we said before: The PID identifies a client process. It lets the server keep track of client context, and associate it correctly with the right customer when the requests come rolling in.
Further complicating things, some clients support multiple threads running within a process. Threads share context (memory, file descriptors, etc.) with their sister threads within the same process, but each thread may generate SMB traffic all on its own. The MID field is used to make sure that server replies get back to the thread that sent the request. The server really doesn‘t do much with the MID. It just echoes it back to the client so, in fact, the client could make whatever use it wanted of the MID field. Using it as a thread identifier is probably the most practical thing to do.
There is an important rule which the client should obey with regard to the MID and PID fields: Only one SMB request should ever be outstanding per [PID, MID] pair per connection. The reason for this rule is that the client will generally need to know the result of a request before sending the next request, especially if an error occurred. The problems which might result should this rule be broken probably depend upon the server, but defensive programming practices would suggest avoiding trouble.
Earlier on we promised to cover the EXTRA.PidHigh field. Well, a promise is a promise...
The PidHigh field is supposed to be a PID extension, allowing the use of 32-bit rather than 16-bit values as process identifiers. As with all extensions, however, there is the basic problem of backward compatibility.
In this case, trouble shows up if (and only if) the client supports 32-bit process IDs but the server does not. In that situation, the client must have a mechanism for mapping 32-bit process IDs to 16-bit values that can fit into the PID field. It doesn‘t need to be an elaborate mapping scheme, and it is unlikely that there will be 64K client processes talking to the same server at the same time, so it should be a simple problem to solve.
Since that mapping mechanism needs to be in place in order for the client to work with servers that don‘t support the PidHigh field, there‘s no reason to use 32-bit process IDs at all. In testing, it appears as though the PidHigh field is, in fact, always zero (except in some obscure security negotiations that are still not completely understood). Best bet, leave it zero.
Code...
The next two listings (2.4a and 2.4b) provide support for reading and writing SMB message headers. Most of the header fields are simple integer values, so we can use the smb_Set*() and smb_Get*() functions from listing 2.2 to move the data in and out of the header buffer. To make subsequent code easier to read, we provide a set of macros with nice clear names to front-end the function calls and assignments that are actually used.
The smb_hdrInit() and smb_hdrCheck() functions are there primarily to ensure that the SMB headers are reasonably sane. They check for things like the buffer size, and ensure that the "\xffSMB" string is included correctly in the header buffer.
Note that none of these functions or macros handle the reading and writing of the four-byte session header, though that would be trivial. The SESSION MESSAGE header is part of the transport layer, not SMB. It is handled as a simple network-byte-order longword; something from the NBT Session Service that has been carried over into naked transport. (We covered all this back in sections 1.6 and 2.1.2.)
This one goes to eleven.
-- Nigel Tufnel
(Christopher Guest),
This Is Spinal Tap
CIFS is a very rich and varied protocol suite, a fact that is evident in the number of SMB dialects that exist. Five are listed in the X/Open SMB protocol specification, and the SNIA doc--published ten years later--lists eleven. That‘s a bigbunch, and they probably missed a few. Each new dialect may add new SMBs, deprecate old ones, or extend existing ones. As if that were not enough, implementations introduce subtle variations within dialects.
All that in mind, our goal in this section will be to provide an overview of the available dialects, cover the workings of the NEGOTIATE PROTOCOL SMB exchange, and take a preliminary peek at some of the concepts that we have yet to consider (things like virtual circuits and authentication). For the most part, the examples and discussion will be based on the "NT LM 0.12" dialect. The majority of the servers currently available support some variation of NT LM 0.12, and at least one client implementation (jCIFS) has managed to get by without supporting any others. Server writers should be warned, however, that there really are a lot of clients still around that use older calls. Even new clients will use older calls, simply because of the difficulty of acquiring reliable documentation on the newer stuff.
In keeping with tradition, the list of dialects is presented as a table with the dialect name in the left-hand column and a short description in the right, ordered from oldest to newest. Most of the references to these dialects seem to do it this way. Our list is not quite as complete as you might find elsewhere. The aim here is to highlight some of the better-known examples in order to provide a bit of context for the examination of the SMB_COM_NEGOTIATE message.
Where relevant, important differences between dialects will be
noted. It would be very difficult, however, to try to document all of
the features of each dialect and all of the changes between them. If
you really, really need to know more (which is likely, if you are
working on server code) see the SNIA doc, the X/Open doc, the expired
IETF drafts, and the other old Microsoft documentation that is still
freely available from their FTP server22.
A language is a dialect
with an army and a navy.
-- Uriel Weinreich
SMB Dialects | |
---|---|
Dialect Identifier | Notes |
PC NETWORK PROGRAM 1.0 | Also known as the Core Protocol. This is the original stuff, as documented in COREP.TXT. According to ancient lore, this dialect is sometimes also identified by the string "PCLAN1.0". |
MICROSOFT NETWORKS 1.03 | This is the Core Plus Protocol. It extends a few Core Protocol SMB commands, and adds a few new ones. |
MICROSOFT NETWORKS 3.0 | Known as the Extended 1.0 Protocol or LAN Manager 1.0. This dialect was created when IBM and Microsoft were working together on OS/2. This particular variant was designed for DOS clients, which understood a narrower set of error codes than OS/2. |
LANMAN1.0 | Identical to the MICROSOFT NETWORKS 3.0 dialect except that it was intended for use with OS/2 clients, so a larger set of error codes was available. OS/2 and DOS both expect that the STATUS field will be in the DOS-style ErrorClass / ErrorCode format. Again, this dialect is also known as LAN Manager 1.0 or as the Extended 1.0 Protocol. |
LM1.2X002 | Called the Extended 2.0 Protocol; also known as LAN Manager 2.0. This dialect represents OS/2 LANMAN version 2.0, and it introduces a few new SMBs. The identifier for the DOS version of this dialect is "DOS LM1.2X002". As before, the key difference between the DOS and OS/2 dialects is simply that the OS/2 version provides a larger set of error codes. |
LANMAN2.1 | Called the LAN Manager 2.1 dialect (no surprise there), this version is documented in a paper titled Microsoft Networks SMB File Sharing Protocol Extensions, Document Version 3.4. You can find it by searching the web for a file named "SMB-LM21.DOC". You will likely need a conversion tool of some sort in order to read the file, as it is encoded in an outdated form of a proprietary Microsoft format (it‘s a word-processing file). The cool thing about the SMB-LM21.DOC document is that instead of explaining how LANMAN2.1 works it describes how LANMAN2.1 differs from its predecessor, LANMAN2.0. That‘s useful for people who want to know how the protocol has evolved. |
Samba | You may see this dialect listed in the protocol negotiation request coming from a Samba-based client such as smbclient, KDE Konqueror (which uses Samba‘s libsmbclient library), or the Linux SMBFS implementation. No one from the Samba Team seems to remember when, or why, this was added. It doesn‘t appear to be used any more (if, indeed, it ever was). |
NT LM 0.12 | This dialect, sometimes called NT LANMAN, was developed for use with WindowsNT. All of the Windows9x clients also claim to speak it as do Windows2000 and XP. As mentioned above, this is currently the most widely supported. It is, quite possibly, also the sloppiest with all sorts of variations and differing implementations. |
CIFS | Following the release of the IETF CIFS protocol drafts, many people thought that Microsoft would produce a "CIFS" dialect, and many documents refer to it. No such beast has actually materialized, however. Maybe that‘s a good thing. |
Section 3.16 of the SNIA CIFS Technical Reference, V1.0 provides a list of of SMB message types categorized by the dialect in which they were introduced. There is also a slightly more complete list of dialects in section 5.4 of the SNIA doc.
We have already provided a detailed breakdown of a NEGOTIATE PROTOCOL REQUEST SMB (back in section section 2.4.3), so we don‘t need to go to the trouble of fully dissecting it again. The interesting part of the request is the data section (the parameter section is empty). If we were to write a client that supported all of the dialects in our chart, the NEGOTIATE_PROTOCOL_REQUEST.SMB_DATA field would break out something like this:
SMB_DATA { ByteCount = 131 Bytes { Dialect[0] = "\x02PC NETWORK PROGRAM 1.0" Dialect[1] = "\x02MICROSOFT NETWORKS 1.03" Dialect[2] = "\x02MICROSOFT NETWORKS 3.0" Dialect[3] = "\x02LANMAN1.0" Dialect[4] = "\x02LM1.2X002" Dialect[5] = "\x02LANMAN2.1" Dialect[6] = "\x02Samba" Dialect[7] = "\x02NT LM 0.12" Dialect[8] = "\x02CIFS" } }
Each dialect string is preceded by a byte containing the value 0x02. This, perhaps, was originally intended to make it easier to parse the buffer. In addition to the 0x02 prefix the dialect strings are nul-terminated, so if you go to the trouble of counting up the bytes to see if the ByteCount value is correct in this example don‘t forget to add 1 to each string length.
Listing 2.5 provides code for creating a NEGOTIATE PROTOCOL REQUEST message. It also takes care of writing an NBT Session Message header for us--something we must not forget to do.
The NEGOTIATE PROTOCOL RESPONSE SMB is more complex than the request. In addition to the dialect selection, it also contains a variety of other parameters that let the client know the capabilities, limitations, and expectations of the server. Most of these values are stuffed into the SMB_PARAMETERS block, but there are a few fields defined in the SMB_DATA block as well.
The NEGOTIATE_PROTOCOL_RESPONSE.SMB_PARAMETERS.Words block for the NT LM 0.12 dialect is 17 words (34 bytes) in size, and is structured as shown below. Earlier dialects use a different structure and, of course, the server should always match the reply to the dialect it selects.
typedef struct { uchar WordCount; /* Always 17 for this struct */ struct { ushort DialectIndex; /* Selected dialect index */ uchar SecurityMode; /* Server security flags */ ushort MaxMpxCount; /* Maximum Multiplex Count */ ushort MaxNumberVCs; /* Maximum Virtual Circuits */ ulong MaxBufferSize; /* Maximum SMB message size */ ulong MaxRawSize; /* Obsolete */ ulong SessionKey; /* Unique session ID */ ulong Capabilities; /* Server capabilities flags */ ulong SystemTimeLow; /* Server time; low bytes */ ulong SystemTimeHigh; /* Server time; high bytes */ short ServerTimeZone; /* Minutes from UTC; signed */ uchar EncryptionKeyLength; /* 0 or 8 */ } Words; } smb_NegProt_Rsp_Params;
She‘s so dull,
come on rip her to shreds.
-- Rip Her To Shreds
Blondie
That requires a lot of discussion. Let‘s tear it up and take a close look at the tiny pieces.
SecurityMode | ||
---|---|---|
Bit # | Name / Bitmask / Values | Description |
7-4 | 0xF0 | <Reserved> (must be zero) |
3 |
NEGOTIATE_SECURITY_SIGNATURES_REQUIRED 0x08
0: Message signing is optional |
If set, this bit indicates that the server is requiring the use of a Message Authentication Code (MAC) in each packet. If the bit is clear then message signing is optional. This bit should be zero if the next bit (mask 0x04) is zero. |
2 |
NEGOTIATE_SECURITY_SIGNATURES_ENABLED 0x04
0: Message signing not supported |
If set, the server is indicating that it is capable of performing Message Authentication Code (MAC) message signing. This bit should be zero if the next bit (mask 0x02) is zero. |
1 |
NEGOTIATE_SECURITY_CHALLENGE_RESPONSE 0x02
0: Plaintext Passwords |
This bit indicates whether or not the server supports Challenge/Response authentication (which will be covered further on). If the bit is clear, then plaintext passwords must be used. If set, the server may (optionally) reject plaintext authentication. If this bit is clear and the client rejects the use of plaintext, then there is no way to perform the logon and the client will be unable to connect to the server. |
0 |
NEGOTIATE_SECURITY_USER_LEVEL 0x01
0: Share Level |
Ah! Finally something we‘ve already covered! This bit indicates whether the server, as a whole, is operating under Share Level or User Level security. Share and User Level security were explained along with the TID and UID header fields, back in section 2.5.4. |
The MaxMpxCount field lets the server tell the client how many requests, in total, it can handle concurrently. It is the client‘s responsibility to ensure that there are no more than MaxMpxCount outstanding requests in the pipe at any time. That may mean that client processes will block, waiting for their turn to send an SMB.
Keep in mind that the transport layer will fragment and defragment packets as necessary. It is, therefore, possible to send very large SMBs and let the lower layers worry about ensuring safe, fast, reliable delivery.
How big can an SMB message be?
In the NT LM 0.12 dialect, the MaxBufferSize field is an unsigned longword. As described much earlier on, however, the Length field in the NBT SESSION MESSAGE is 17 bits wide and the naked transport header has a 24-bit Length field. So the session headers place slightly more reasonable limits on the maximum size of a single SMB message.
The X/Open doc describes the READ RAW and WRITE RAW SMBs, which were introduced with the Extended 1.0 version of SMB (the MICROSOFT NETWORKS 3.0 and LANMAN1.0 dialects). These were a speed hack. For a large read or write operation, the first message would be a proper SMB, but subsequent messages would be sent in "raw" mode, with no SMB or session header. The raw blocks could be as large as MaxRawSize bytes in length. Once again, the transport layer was expected to take care of fragmentation/defragmentation and the re-sending of any lost packets.
Raw mode is not used much any more. Among other things, it conflicts with message signing because the raw messages have no header in which to put the MAC Signature. Thus, the MaxRawSize field is considered obsolete23.
In theory, the SessionKey value should be echoed back to the server whenever the client sends a SESSION SETUP request. Samba‘s smbclient does this, but some versions of jCIFS always reply with zero, and they don‘t seem to have any trouble with it. In testing, it also appears that Windows2000 servers do not generate a session key. They send zero in NEGOTIATE PROTOCOL RESPONSE messages. Hmmm...
It would seem that the use of this field was never clearly defined--anywhere by anyone--and that most servers really don‘t care what goes there. It is probably safest if the client echoes back the value sent by the server.
We are already building up a backlog of unexplained features. We will also postpone the discussion of the Capabilities field until we get some of the other stuff out of the way.
typedef struct { ulong timeLow; ulong timeHigh; } smb_Time;
Keeping byte-order in mind, the completed time value should be read as two little-endian 32-bit integers. The result, however, should be handled as a 64-bit signed value representing the number of tenths of a microsecond since January 1, 1601, 00:00:00.0 UTC.
WHAT?!?!
Yes, you read that right folks. The time value is based on that unwieldy little formula. Read it again five times and see if you don‘t get a headache. Looks as though we need to get out the protractor, the astrolabe, and the didgeridoo and try a little calculating. Let‘s start with some complex scientific equations:
In other words, the server time is given in units of 10-7seconds24. Many CIFS implementations handle these units by converting them into Unix-style measurements. Unix, of course, bases its time measurements on an equally obscure date: January 1, 1970, 00:00:00.0 UTC25. Converting between the two schemes requires knowing the difference (in seconds) between the two base times.
Proof by Faith: I believe
that this has been proven
...somewhere.
-- Jonathan Young, PhD.
email
> (what‘s the number of seconds between 1601 and 1970 again?) Between Jan 1, 1601 and Jan 1, 1970, you have 369
complete years, of which 89 are leap years (1700, 1800, and 1900
were not leap years). That gives you a total of 134774 days or
11644473600 seconds.
|
So, if you want to convert the SystemTime to a Unix time_t value, you need to do something like this:
unix_time = (time_t)(((smb_time)/10000000) - 11644473600);
Which gives you the server‘s system time in seconds since January 1, 1970, 00:00:00.0 UTC.
The available documentation (the SNIA doc and the Leach/Naik IETF draft) states that this field is an unsigned short integer. They‘re wrong. The field is a signed value which is subtracted from the SystemTime to give local time.
If, for example, your server is located in the beautiful city of Saint Paul, Minnesota, it would be in the US Central timezone26 which is six hours west of UTC. The value in the ServerTimeZone field would, therefore be 360 minutes. (Except, of course, during the summer when Daylight Savings Time is in effect in which case it would be 300 minutes.) On the other hand if your server is in Moscow in the winter, the ServerTimeZone value would be -180.
The basic rule of thumb:
LocalTime = SystemTime - ( ServerTimeZone × 600000000 )
...which returns local time in units of 10-7seconds, based on January 1601 as described above.
If you found all of that to be complicated, you will be relieved to know that this is only one of many different time formats used in SMB. Time And Date Encoding is covered in section 3.7 of the SNIA doc.
The name of this field is probably a hold-over from some previous enhancement to the protocol--still in use for "historical reasons".
The job‘s not done
until the paperwork‘s finished.
-- Lavatory Axiom
Wow... a lot of stuff there. No time to sit and chat about it right now, though. We still need to finish out the of the NEGOTIATE_PROTOCOL_RESPONSE.SMB_DATA block.
SMB_DATA, of course, is handed to us as an array of bytes with the length provided in the ByteCount field. The parsing of those bytes depends upon the values in the SMB_PARAMETER block that we just examined. The structure is completely different depending upon whether Extended Security has been negotiated.
Here is what it looks like, more or less, in the NT LM 0.12 dialect:
typedef struct { ushort ByteCount; /* Number of bytes to follow. */ union { struct { uchar GUID[16]; /* 16-byte Globally Unique ID */ uchar SecurityBlob[]; /* Auth-system dependent */ } ext_sec; /* Extended Security */ struct { uchar EncryptionKey[]; /* 0 or 8 bytes long */ uchar DomainName[]; /* nul-terminated string */ } non_ext_sec; /* Non-Extended Security */ } Bytes; } smb_NegProt_Rsp_Data;
The first thing to note is that this SMB_DATA.Bytes block structure is the union of two smaller structures:
The second thing to note is that this is pseudo-code, not valid C code. Some of the array lengths are unspecified because we don‘t know the byte-length of the fields ahead of time. In real code, you will probably need to use pointers or some other mechanism to extract the variable-length data from the buffer.
Okay, let‘s chop that structure into little bits...
As of this writing, research by Samba Team members shows that this value is probably the same as the GUID identifier used by Active Directory to keep track of servers in the database. Stand-alone servers (which are not listed in any Active Directory) also generate and use a GUID. Go figure.
Though this field is only present when Extended Security is enabled, it is not, strictly speaking, a security field. The value is well known and easily forged. It is not clear (yet) why this field is even sent to the client. In testing, a Samba server was configured to fill the GUID field with its own 16-byte Server Service NetBIOS name...and that worked just fine.
The SecurityBlob is variable in length. Fortunately, the GUID field is always 16 bytes, so the length of the SecurityBlob is (ByteCount - 16) bytes.
The SNIA doc calls this field the OEMDomainName and claims that the characters will be eight-bit values using the OEM character set of the server (that‘s the 7-bit ASCII character set augmented by an extended DOS code page which defines characters for the upper 128 octet values). In fact, this field may contain either a string of 8-bit OEM characters or a Unicode string with 16-bit characters. The value of SMB_HEADER.FLAGS2.SMB_FLAGS2_UNICODE_STRINGS will let you know how to read the DomainName field.
Okay, let‘s be honest... Ripping apart that NEGOTIATE PROTOCOL RESPONSE SMB was about as exciting as the epic saga of undercooked toast. It doesn‘t get any better than that, though, and there‘s a lot more of it. Implementing SMB is a game of patience and persistence. It also helps if you get a cheap thrill from fiddly little details. (Just don‘t go parsing your packets in public or people will look at you funny.)
It seems, too, that our overview of the SMB Header and the NEGOTIATE PROTOCOL exchange has left a bit of a mess on the floor. We have pulled a lot of concepts off of the shelves and out of the closets, and we will need to do some sorting and organizing before we can put them back. Let‘s see what we‘ve got:
The only way to approach all of these topics is one-at-a-time. ...but first, take another break. Every now and then, it is a good idea to stop and think about what has been covered so far. This is one of those times. We have finished tearing apart SMB headers and the body of a NEGOTIATE PROTOCOL message. That should provide some familiarity with the overall structure of SMBs. Try doing some packet captures, or skim through the SNIA CIFS Technical Reference. It should all begin to make a little more sense now than it did when we started.
Originally, the SESSION SETUP was not required by--or even defined as part of--the SMB protocol. It was introduced in the LANMAN days in order to handle User Level authentication and could be skipped if the server was in Share Level security mode. These days, however, the SESSION SETUP takes care of a lot of unfinished business, like cleaning up some of the debris left by the NEGOTIATE PROTOCOL RESPONSE. In the NT LM 0.12 dialect there must be a SESSION SETUP exchange before a TREE CONNECT may be sent, even if the server is operating in Share Level security mode.
The SESSION SETUP SMB is actually a SESSION SETUP ANDX, which simply means that there‘s an AndX block in the parameter section. In the NT LM 0.12 dialect, the Parameter block is formatted as shown below:
typedef struct { uchar WordCount; /* 12 or 13 words */ struct { struct { uchar Command; uchar Reserved; ushort Offset; } AndX; ushort MaxBufferSize; ushort MaxMpxCount; ushort VcNumber; ulong SessionKey; ushort Lengths[]; /* 1 or 2 elements */ ulong Reserved; ulong Capabilities; } Words; } smb_SessSetupAndX_Req_Params;
When looking at these C-like structures, keep in mind that they are intended as descriptions rather than specifications. On the wire, the parameters are packed tightly into the SMB messages, and they are not aligned. Though the structures show the type and on-the-wire ordering of the fields, the C programming language does not guarantee that the layout will be retained in memory. That‘s why our example code includes all of those functions and macros for packing and unpacking the packets28.
Many of the fields in the SESSION_SETUP_ANDX.SMB_PARAMETERS block should be familiar from the NEGOTIATE PROTOCOL RESPONSE SMB. This time, though, it‘s the client‘s turn to set the limits.
Basically, though, if Extended Security has been negotiated then the Lengths field is a single ushort, known as SecurityBlobLength in the SNIA doc. (We touched on the concept of security blobs briefly back in section 2.6.3.2.) If Extended Security is not in use then there will be two ushort fields identified by the excessively long names:
Obviously, all of this stuff falls into the general category of authentication, and will be covered in more detail when we finally focus on that topic.
You might notice, upon careful examination, that the client does not send back a MaxRawSize value. That‘s because it can specify raw read/write sizes in the SMB_COM_RAW_READ and SMB_COM_RAW_WRITE requests, if it sends them. These SMBs are considered obsolete, so newer clients really shouldn‘t be using them.
There are a couple of fields in the SESSION SETUP REQUEST which touch on esoteric concepts that we have been promising to explain for quite a while now--specifically virtual circuits and capabilities--so let‘s get it over with...
It does seem as though there‘s a good deal of cruft in the SMB protocol. The SessionKey, for example, appears to be a vestigial organ, the purpose of which has been mostly forgotten. Originally, such fields may have been intended to compensate for a limitation in a specific transport or an older implementation, or to solve some other problem that isn‘t a problem any more.
Consider virtual circuits...
The LAN Manager documentation available from Microsoft‘s ftp site provides the best clues regarding virtual circuits (see SMB-LM1X.PS, for instance). According to those docs a virtual circuit (VC) represents a single transport layer connection, and the VcNumber is a tag used to identify a specific transport link between a specific client/server pair.
That concept probably needs to be considered in context.
The LANMAN dialects were developed in conjunction with OS/2 (an honest-to-goodness, really-truly, multitasking OS). OS/2 clients pass SMB traffic through a redirector--just like DOS and Windows--and it seems as though there was some concern that multiplexing the SMB traffic from several processes across a single connection might cause a bit of a bottleneck. So, to avoid congestion, the redirector could create additional connections to facilitate faster transfers for individual processes29. Under this scheme, all of the transport level connections from a client to a server were considered part of a single logical "session" (we now, officially, have way too many meanings for that term). Within that logical session there could, conversely, be multiple transport level connections--aka. virtual circuits--up to the limit set in the NEGOTIATE PROTOCOL RESPONSE.
Figure 2.10 illustrates the point, and here‘s how it‘s supposed to work:
Ah-Hahhh! The mystery of the SessionKey field is finally revealed. Kind of a let-down, isn‘t it?
Whenever a new transport-layer connection is created, the client is supposed to assign a new VC number. Note that the VcNumber on the initial connection is expected to be zero to indicate that the client is starting from scratch and is creating a new logical session. If an additional VC is given a VcNumber of zero, the server may assume that any existing connections with that same client are now bogus, and shut them down.
Why do such a thing?
There is a finite amount of clue
in the Universe...
and the Universe is expanding.
-- Unknown
(thanks to John Ladwig
and Marcus Ranum)
The explanation given in the LANMAN documentation, the Leach/Naik IETF draft, and the SNIA doc is that clients may crash and reboot without first closing their connections. The zero VcNumber is the client‘s signal to the server to clean up old connections. Reasonable or not, that‘s the logic behind it. Unfortunately, it turns out that there are some annoying side-effects that result from this behavior. It is possible, for example, for one rogue application to completely disrupt SMB filesharing on a system simply by sending Session Setup requests with a zero VcNumber. Connecting to a server through a NAT (Network Address Translation) gateway is also problematic, since the NAT makes multiple clients appear to be a single client by placing them all behind the same IP address30.
The biggest problem with virtual circuits, however, is that they are not really needed any more (if, in fact, they ever were). As a result, they are handled inconsistently by various implementations and are not entirely to be trusted. On the client-side, the best thing to do is to ignore the concept and view each transport connection as a separate logical session, one VC per session. Oh! ...and contrary to the specs the client should always use a VcNumber of one, never zero.
On the server side, it is important to keep in mind that the TID, UID, PID, and MID are all supposed to be relative to the VC. In particular, TID and UID values negotiated on one VC have no meaning (and no authority) on another VC, even if both VCs appear to be from the same client. Another important note is that the server should not disconnect existing VCs upon receipt of a new VC with a zero VcNumber. As described above, doing so is impractical and may break things. The server should let the transport layer detect and report session disconnects. At most, a zero VcNumber might be a good excuse to send a keep-alive packet.
The whole VC thing probably seemed like a good idea at the time.
Remember a little while back when we said that there were subtle variations within SMB dialects? Well, some of them are not all that subtle once you get to know them. The Capabilities bits formalize several such variations by letting the client and server negotiate which special features will be supported. The server sends its Capabilities field in the NEGOTIATE PROTOCOL RESPONSE, and the client returns its own set of capabilities in the SESSION SETUP ANDX REQUEST.
The table below provides a listing of the capabilities defined for
servers. The client set is smaller.
* <---- Tribble
. <---- Tribble.gz
-- Karen Swanberg
Server Capabilities | ||
---|---|---|
Bit # | Name / Bitmask | Description |
31 |
CAP_EXTENDED_SECURITY 0x80000000 |
Set to indicate that Extended Security exchanges are supported. |
30 |
CAP_COMPRESSED_DATA 0x40000000 |
If set, it indicates that the server can compress Data blocks before sending them31. This might be useful to improve throughput of large file transfers over low-bandwidth links. This capability requires that the CAP_BULK_TRANSFER capability also be set. Currently, however, there are no known implementations that support bulk transfer. |
29 |
CAP_BULK_TRANSFER 0x20000000 |
If set, the server supports the SMB_COM_READ_BULK and
SMB_COM_WRITE_BULK SMBs.
There are no known implementations which support CAP_BULK_TRANSFER and/or CAP_COMPRESSED_DATA. Samba does not even bother to define constants for these capabilities. |
23 |
CAP_UNIX 0x00800000 |
Microsoft reserved this bit based on a proposal (by Byron Deadwiler at Hewlett-Packard) for a small set of Unix extensions. The SNIA doc describes these extensions in an appendix. Note, however, that the proposal was made and the appendix written before the extensions were widely implemented. Samba supports the SMB Unix extensions, but probably not exactly as specified in the SNIA doc. |
15 |
CAP_LARGE_WRITEX 0x00008000 |
If set, the server supports a special mode of the SMB_COM_WRITE_ANDX SMB which allows the client to send more data than would normally fit into the server‘s receive buffers, up to a maximum of 64 Kbytes. |
14 |
CAP_LARGE_READX 0x00004000 |
Similar to the CAP_LARGE_WRITEX, this bit indicates whether the server can handle SMB_COM_READ_ANDX requests for blocks of data larger than the reported maximum buffer size. The theoretical maximum is 64 Kbytes, but the client should never request more data than it can receive. |
13 |
CAP_INFOLEVEL_PASSTHROUGH 0x00002000 |
Samba calls this the CAP_W2K_SMBS bit. In testing NT4 systems did not set this bit, but W2K systems did. Basically, it indicates support for some advanced requests. |
12 |
CAP_DFS 0x00001000 |
If set, this bit indicates that the server supports Microsoft‘s Distributed File System. |
9 |
CAP_NT_FIND 0x00000200 |
This is a mystery bit. There is very little documentation about
it and what does exist is not particularly helpful. The SNIA doc
simply says that this bit is "Reserved", but the notes
regarding the CAP_NT_SMBS bit state that the latter
implies the former. (Counter-examples have been found in some
references, but not on the wire during testing. Your mileage may
vary.)
Basically, though, if this bit is set it indicates that the server supports an extended set of function calls belonging to a class of calls known as "transactions". |
8 |
CAP_LOCK_AND_READ 0x00000100 |
If set, the server is reporting that it supports the obsolete
SMB_COM_LOCK_AND_READ SMB.
...but go back and look at the SMB_HEADER.FLAGS bits described earlier. The lowest order FLAGS bit is SMB_FLAGS_SUPPORT_LOCKREAD, and it is also supposed to indicate whether or not the server supports SMB_COM_LOCK_AND_READ (as well as the complimentary SMB_COM_WRITE_AND_UNLOCK). The thing is, traces from WindowsNT and Windows2000 systems show the CAP_LOCK_AND_READ bit set while the SMB_FLAGS_SUPPORT_LOCKREAD is clear. That doesn‘t make a lot of sense. Well... it may be that the server is indicating that it supports the SMB_COM_LOCK_AND_READ SMB but not the SMB_COM_WRITE_AND_UNLOCK SMB, or it may be that the server may be using the Capabilities field in preference to the FLAGS field. Avoid the use of the SMB_COM_LOCK_AND_READ and SMB_COM_WRITE_AND_UNLOCK SMBs and everything should turn out alright. |
7 |
CAP_LEVEL_II_OPLOCKS 0x00000080 |
If set, Level II OpLocks are supported in addition to Exclusive and Batch OpLocks. |
6 |
CAP_STATUS32 0x00000040 |
If set, indicates that the server supports the 32-bit NT_STATUS error codes. |
5 |
CAP_RPC_REMOTE_APIS 0x00000020 |
If set, this bit indicates that the server permits remote management via Remote Procedure Call (RPC) requests. RPC is way beyond the scope of this book. |
4 |
CAP_NT_SMBS 0x00000010 |
If set, this bit indicates that the server supports some advanced
SMBs that were designed for use with WindowsNT and above. These
are, essentially, an extension to the NT LM 0.12
dialect.
According to the SNIA doc, the CAP_NT_SMBS implies CAP_NT_FIND. |
3 |
CAP_LARGE_FILES 0x00000008 |
If set, this bit indicates that the server can handle 64-bit file sizes. With 32-bit file sizes, files are limited to 4GB in size. |
2 |
CAP_UNICODE 0x00000004 |
Set to indicate that the server supports Unicode. |
1 |
CAP_MPX_MODE 0x00000002 |
If set, the server supports the (obsolete) SMB_COM_READ_MPX and SMB_COM_WRITE_MPX SMBs. |
0 |
CAP_RAW_MODE 0x00000001 |
If set, the server supports the (obsolete) SMB_COM_READ_RAW and SMB_COM_WRITE_RAW SMBs. |
On the server side, the implementor‘s rule of thumb regarding capabilities is to start by supporting as few as possible and add new ones one at a time. Each bit is a cornucopia--or Pandora‘s box--of new features and requirements, and most represent a very large development effort. As usual, if there is documentation it is generally either scarce or encumbered.
Things are not quite so bad if you are implementing a client, though the client also has a list of capabilities that it can declare. The client list is as follows:
Client Capabilities | ||
---|---|---|
Bit # | Name / Bitmask | Description |
31 |
CAP_EXTENDED_SECURITY 0x80000000 |
Set to indicate that Extended Security exchanges are supported.
The SNIA doc and the older IETF Draft do not list this as a capability set by the client. On the wire, however, it is clearly used as such by Windows, Samba, and by Steve French‘s CIFS VFS for Linux. If the server indicates Extended Security support in its Capabilities field, then the client may set this bit to indicate that it also supports Extended Security. |
9 |
CAP_NT_FIND 0x00000200 |
If set, it indicates that the client is capable of utilizing the CAP_NT_FIND capability of the server. |
7 |
CAP_LEVEL_II_OPLOCKS 0x00000080 |
If set, this bit indicates that the client understands Level II OpLocks. |
6 |
CAP_STATUS32 0x00000040 |
Indicates that the client understands 32-bit NT_STATUS error codes. |
4 |
CAP_NT_SMBS 0x00000010 |
Likewise, I‘m sure.
As with the CAP_NT_FIND bit, the client will set this to let the server know that it, too, understands the extended set of SMBs and function calls that are available if the server has set the CAP_NT_SMBS bit. |
3 |
CAP_LARGE_FILES 0x00000008 |
The client sets this to let the server know that it can handle 64-bit file sizes and offsets. |
2 |
CAP_UNICODE 0x00000004 |
Set to indicate that the client understands Unicode. |
The client should not set any bits that were not also set by the server. That is, the Capabilities bits sent to the server should be the intersection (bitwise AND) of the client‘s actual capabilities and the set sent by the server.
The Capabilities bits are like the razor-sharp barbs on a government fence. Attempting to hurdle any one of them can shred your implementation. Consider adding Unicode support to a system that doesn‘t already have it. Ooof! That‘s going to be a lot of work32.
Some Capabilities bits indicate support for sets of function calls that can be made via SMB. These function calls, which are sometimes referred to as "sub-protocols", fall into two separate (but similar) categories:
Of the two, the RAP sub-protocol is older and (relatively speaking) simpler. Depending upon the SMB dialect, server support for some RAP calls is assumed rather than negotiated. Fortunately, much of RAP is documented...if you know where to look33.
Microsoft‘s RPC system--known as MS-RPC--is newer, and has a lot in common with the better-known DCE/RPC system. MS-RPC over SMB allows the client to make calls to certain Windows DLL library functions on the server side which, in turn, allows the client to do all sorts of interesting things. Of course, if you are building a server and you want to support the MS-RPC calls you have to implement all of the required functions in addition to SMB itself. Unfortunately, much of MS-RPC is undocumented34.
The MS-RPC function call APIs are defined using a language called Microsoft Interface Definition Language (MIDL). There is a fair amount of information about MIDL available on the web and some of the function interface definitions have been published. CIFS implementors have repeatedly asked Microsoft for open access to all of the CIFS-relevant MIDL source files. Unencumbered access to the MIDL source would go a long way towards opening up the CIFS protocol suite. Since MIDL provides only the interface specifications and not the function internals, Microsoft could release them without exposing their proprietary DLL source code.
Both the RAP and MS-RPC sub-protocols provide access to a large set of features, and both are too big to be covered in detail here. Complete documentation of all of the nooks and crannies of CIFS would probably require a set of books large enough to cause an encyclopedia to cringe in awe, so it would seem that our attempt to clean up the mess we made with the NEGOTIATE PROTOCOL exchange has instead created an even bigger mess and left some permanent stains on the carpet. Ah, well. Such is the nature of CIFS.
The dissection of the SMB_PARAMETERS portion of the
SESSION SETUP ANDX REQUEST cleared up a few issues and
exposed a few others. Now we get to look at the SMB_DATA
block and see what further mysteries may lie uncovered.
...just another piece of
useless information.
-- Rain on the Hills,
Judie Tzuke
Fortunately, the Data block is much less daunting. It contains a few fields used for authentication and the rest is just useful bits of information about the client‘s operating environment. The structure looks like this:
typedef struct { ushort ByteCount; struct { union { uchar SecurityBlob[]; struct { uchar CaseInsensitivePassword[]; uchar CaseSensitivePassword[]; uchar Pad[]; uchar AccountName[]; uchar PrimaryDomain[]; } non_ext_sec; } auth_stuff; uchar NativeOS[]; uchar NativeLanMan[]; uchar Pad2[]; } Bytes; } smb_SessSetupAndx_Req_Data;
If Extended Security has not been negotiated, the structure will contain the following fields:
As you know, the Parameter block is made up of a single byte followed by an array of zero or more words. It starts on a word boundary, but the WordCount byte knocks it off balance, so it never ends on a word boundary. That means that the Data block always starts misaligned35. Typically, that‘s not considered a problem for data in SMB messages. It is not clear why, but it seems that when Unicode support was added to SMB it was decided that Unicode strings should be word-aligned within the SMB message (even though they are likely to be copied out of the message before they‘re fiddled). That‘s why the Pad byte is there.
Note that if Unicode support is enabled the password fields will always contain an even number of bytes. Strange but true. Here‘s why:
Note the subtle glitch here. If Samba is configured to send Unicode plaintext passwords, the CaseSensitivePassword field will not be word-aligned because the Pad byte comes afterward. It seems that the designers of the NT LM 0.12 dialect did not consider the possibility of plaintext Unicode passwords.
Who is your user?
-- Tron
Whenever possible, this field should contain the NetBIOS name of the NT Domain to which the user belongs. Basically, it allows the client to specify the NT Domain in which the username and password are valid--the Authentication Domain. A correct value is not always needed, however. If the server is not a member of an NT Domain, then it will have its own authentication database, and no Domain Controller need be consulted.
Some testing was done with WindowsNT4 and Windows2000 systems that were not members of an NT Domain. As clients, these systems sent their own NetBIOS machine name in the PrimaryDomain field. The smbclient utility sent the workgroup name, as specified in the smb.conf file. jCIFS just sent a question mark. All of these variations seem to work, as long as the server maintains its own authentication database. The PrimaryDomain field is really only useful when authenticating against a Domain Controller.
...and that‘s the end of the auth_stuff block. On to the rest of it.
email
Note that NT4 misaligns the NativeLanMan string by one byte
(see Ethereal for details). Also note that Samba uses this
string to distinguish between W2K/XP/2K3 for the %a smb.conf
variable. So it is used by the server in some cases.
|
We have done a lot of work ripping apart packet structures and studying the internal organs. Don‘t worry, that‘s the last of it. You should be familiar enough with this stuff by now, so from here on out we will rely on the SNIA doc and packet traces to provide the gory details.
|
The SESSION SETUP ANDX RESPONSE SMB structure is described in section 4.1.2 of the SNIA doc.
In the NT LM 0.12 dialect, there are two versions of the SESSION SETUP ANDX RESPONSE message. They differ, of course, based on whether or not Extended Security is in use. In the Extended Security version the Parameter block has a SecurityBlobLength field, and there is an associated SecurityBlob within the Data block. These two fields are missing from the non-Extended Security version. Other than that, the two are the same.
The SESSION SETUP ANDX RESPONSE message also has an interesting little bitfield called SMB_PARAMETERS.Action. Only the low-order bit (bit 0) of this field is defined. If set, it indicates that the username was not recognized by the server (that is, authentication failed--no such user) but the logon is being allowed to succeed anyway.
That‘s rather odd, eh?
What it means is this: If the username (in the AccountName field) is not recognized, the server may choose to grant anonymous or guest authorization instead. Anonymous access typically provides only very limited access to the server. For example, it may allow the use of a limited set of RAP function calls such as those used for querying the Browse Service.
So, the Action bit is used to indicate that the logon attempt failed, but anonymous access was granted instead. No error code will be returned in this case, so the Action bit is the only indication to the client that the rules have changed. Server-side support for this behavior is optional.
Now for the big one...
If you are familiar with authentication schemes then this section
should be comfortable for you. If not, then perhaps it‘s time for a
fresh pot of tea. Some people find their first experience with the
innards of password security to be a bit intimidating, possibly
because the encryption formulae are sometimes made to look a lot like
mathematics. Authentication itself isn‘t really that complex, though.
The basic idea is that the would-be user needs to prove that they are
who they say they are in order to get what they want. The proof is
usually in the form of something private or secret--something that
only the user has or knows.
Car locks are there
to keep the honest people honest.
-- Something my brother Robert
once told me. (He sells cars.)
Consider, for example, the key to an automobile (something you have). With the key in hand, you are able to unlock the door, turn the ignition switch, and start the engine. As far as the car is concerned, you have proven that you have the right to drive. Likewise with the password you use to access your computer (something you know). If you enter a valid username/password pair at the login prompt, then you can access the system. Unfortunately passwords, like keys, can be stolen or forged or copied. Just as locks can be picked, so passwords can be cracked36.
In the early days of SMB, when the LANs were small and sheltered, there was very little concern for the safety of the password itself. It was sent in plaintext (un-encrypted) over the wire from the client to the server. Eventually, though, corporate networks got bigger, modems were installed to provide access from home and on the road, the "disgruntled employee" boogeyman learned how to use a keyboard, and everything got connected to the Internet. These were hard times for plaintext passwords, so a series of schemes was developed to keep the passwords safe--each more complex than its predecessor.
For SMB, the initial attempt was called LAN Manager Challenge/Response authentication, often simply abbreviated "LM". The LM scheme turned out to be too simple and too easy to crack, and was replaced with something stronger called WindowsNT Challenge/Response (known as "NTLM"). NTLM was superseded by NTLMv2 which has, in turn, been replaced with a modified version of MIT‘s Kerberos system.
Got that?
We‘ll go through them all in various degrees of detail. The LM algorithm is fairly simple, so we can provide a thorough description. At the other extreme, Kerberos is an entire system unto itself and anything more than an overview would be overkill.
Gather and study piles of SMB packet captures and you will notice that some SESSION SETUP requests contain no username and password at all. These are anonymous logins, and they are used to access special-purpose SMB shares such as the hidden "IPC$" share (the Inter-Process Communications share). You can learn more about IPC$ in the Browsing section. Put simply, though, this share allows one system to query another using RAP function calls.
Anonymous login may be a design artifact; something created in the days of Share Level security when it seemed safe to leave a share unprotected, and still with us today because it cannot easily be removed. Maybe not. One guess is as good as another.
"GUEST" account logons are also often sent sans password. The guest login is sometimes used in the same way as the anonymous login, but there are additional permissions which a guest account may have. Guest accounts are maintained like other "normal" accounts, so they can be a security problem and are commonly disabled. When SMB is doing its housekeeping, the anonymous login is generally preferred over the guest login.
This is the easiest SMB authentication mechanism to implement--and the least secure. It‘s roughly equivalent to leaving your keys in the door lock after you‘ve parked the car. Sure, the car is locked, but...
Plaintext passwords may still be sufficient for use in small, isolated networks, such as home networks or small office environments (assuming no disgruntled employees and a well-configured firewall on the uplink--or no Internet connection at all). Plaintext passwords also provide us with a nice opportunity to get our feet wet in the mired pool of authentication. We can look at the packets and clearly see what is happening on the wire. Note, however, that many newer clients are configured to prevent the use of plaintext. Windows clients have registry entries that must be twiddled in order to permit plaintext passwords, and jCIFS did not support them at all until version 0.7.
In order to set up a workable test environment you will need a server that does not expect encrypted passwords, and a client that doesn‘t mind sending the passwords in the clear. That is not an easy combination to come by. Most contemporary SMB clients and servers disable plaintext by default. It is easy, however, to configure Samba so that it requests unencrypted passwords. Just change the encrypt passwords parameter to no in the smb.conf file, like so:
; Disable encrypted passwords. encrypt passwords = no
Don‘t forget to signal smbd to reload the configuration file after making this change.
On the client side we will, once again, use the jCIFS Exists utility in our examples. If you would rather use a Windows client for your own tests, you can find a collection of helpful registry settings in the docs/Registry/ subdirectory of the Samba distribution. You will probably need to change the registry settings to permit the Windows client to send plaintext passwords. Another option as a testing tool is Samba‘s smbclient utility, which does not seem to argue if the server tells it not to encrypt the passwords.
This is what our updated Exists test looks like:
shell |
$ java -DdisablePlainTextPasswords=false Exists \ > smb://pat:p%40ssw0rd@smedley/home smb://pat:p%40ssw0rd@smedley/home exists $ |
A few things to note:
smb://[[user[:password]@]host[:port]]
The username in our example is pat.
User and Share Level security were described back in section 2.5.4, along with the TID and [V]UID header fields. The SecurityMode field of the NEGOTIATE PROTOCOL RESPONSE SMB will indicate the authentication expectations of the server. For User Level plaintext passwords, the value of the SecurityMode field will be 0x01.
Below is an example SESSION_SETUP_ANDX.SMB_DATA block such as would be generated by the jCIFS Exists tool. Note, once again, that the discussion is focused on the NT LM 0.12 dialect.
SMB_DATA { ByteCount = 27 Bytes { CaseInsensitivePassword = "p@ssw0rd" CaseSensitivePassword = <NULL> Pad = <NULL> AccountName = "PAT" PrimaryDomain = "?" NativeOS = "Linux" NativeLanMan = "jCIFS" } }
There are always fiddly little details to consider when working with SMB. In this case, we need to talk about upper- and lower-case. (bLeCH.) The example above shows that the AccountName field has been converted to upper-case. This is common practice, but it is not really necessary and some implementations don‘t bother. It is a holdover from the early days of SMB when lots of things (filenames, passwords, share names, NetBIOS names, bagels, and pop singers) were converted to upper-case as a matter of course. Some older servers (pre-NT LM 0.12) may require upper-case usernames, but newer servers shouldn‘t care. Converting to upper-case is probably the safest option, just in case...
Although the AccountName in the example is upper-case, the CaseInsensitivePassword is not. Hmmm... Odd, eh? The situation here is that some server operating systems (eg. most Unixy OSes) use case-sensitive password verification algorithms. If the password is sent all upper-case it probably won‘t match what the OS expects, resulting in a login failure even though the user entered the correct password. The field may be labeled case-insensitive (and that really is what it is intended to be) but some server OSes prefer to have the original password, case preserved, just as the user entered it.
A tradition is a mistake
that you have made
more than once.
-- Stephanie Cohen
This is a sticky problem, though, because some clients insist on converting passwords to upper-case before sending them to the server. Windows95 and ‘98 may do this, for example. As you might have come to expect by now, the reason for this odd behavior is backward compatibility. There are older (pre-NT LM 0.12) servers still running that will reject passwords that are not all upper-case. Windows9x systems solve the problem by forcing all passwords to upper-case even when the NT LM 0.12 dialect has been selected. Samba‘s smbd server, which generally runs on case-sensitive platforms, must go through a variety of contortions to get upper-case plaintext passwords to be accepted39.
Another annoyance is that Windows98 will pad the plaintext password string to 24 bytes, filling the empty space with semi-random garbage. This behavior was noted in testing, but there wasn‘t time to investigate the problem in-depth so it may or may not be wide-spread. Still, it‘s the odd case that will break things. Server implementors should be careful to both check the field length and look for the first terminating nul byte when reading the plaintext password.
In short, client-side handling of the plaintext CaseInsensitivePassword is inconsistent and problematic--and the server has to compensate. That‘s why you need piles of SMB packet captures and lots of different clients to test against when writing a server implementation. It can be done, but it takes a bit of perseverance. When writing a new client, ensure that the client sends the password as the user intended. If that fails, and the dialect is pre-NT LM 0.12, then convert to upper-case and try again. Believe it or not, the use of challenge/response authentication bypasses much of this trouble.
...but that‘s only half the story. In addition to the CaseInsensitivePassword field there is also a CaseSensitivePassword field in the data block, and we haven‘t even touched on that yet. This latter field is only used if Unicode has been negotiated, and it is rare that both Unicode and plaintext will be used simultaneously. It can happen, though. As mentioned earlier, Samba can be easily configured to provide support for Unicode plaintext passwords40. In theory, this should be a simple switch from ASCII to Unicode. In practice, no client really supports it yet--and weird things have been seen on the wire. For example:
Empirically, it would seem that Unicode plaintext passwords were never meant to be.
An interesting fact-ette that can be gleaned from this discussion is that there is a linkage between the password fields and the negotiation of Unicode. Simply put:
ASCII (OEM character set) | <==> | CaseInsensitivePassword |
Unicode | <==> | CaseSensitivePassword |
That is, ASCII plaintext passwords are stored in the CaseInsensitivePassword field, and Unicode plaintext passwords should be placed into the CaseSensitivePassword field. Indeed, Ethereal names these two fields, respectively, as "ANSI Password" and "Unicode Password" instead of using the longer names shown above. This relationship carries over to the challenge/response passwords as well, as we shall soon see.
We won‘t spend too much time on this. It is easy to see by looking at packet captures. Basically, in Share Level security mode the plaintext password is passed to the server in the TREE CONNECT ANDX request instead of the SESSION SETUP ANDX. In the NT LM 0.12 dialect, however, a valid username should also be placed into the SESSION SETUP AccountName field if at all possible. Doing so allows the server to map Share Level security to its own user-based authentication system.
|
In plaintext mode, the client proves that it knows the password by sending the password itself to the server. In challenge/response mode, the goal is to prove that the password is known without risking any exposure. It‘s a bit of a magic trick. Here‘s how it‘s done:
That‘s a rough, general overview of challenge/response. The details of its use in LAN Manager authentication are a bit more involved, but are fairly easy to explain. As we dig deeper, keep in mind that the goal is to protect the password while still allowing authentication to occur. Also remember that LM challenge/response was the first attempt to add encrypted password support to SMB.
The formula used to generate the LM response makes use of the U.S. Department of Commerce Data Encryption Standard (DES) function, in block-mode. DES has been around a long time. There are a lot of references which describe it and a good number of implementations available, so we will not spend a whole lot of time studying DES itself41. For our purposes, the important thing to know is that the DES function--as used with SMB--takes two input parameters and returns a result, like so:
result = DES( key, source );
The source and result are both eight-byte blocks of data, the result being the DES encryption of the source. In the SNIA doc, as in the Leach/Naik draft, the key is described as being seven bytes (56 bits) long. Documentation on DES itself gives the length of the key as eight bytes (64 bits), but each byte contains a parity bit so there really are only 56 bits worth of "key" in the 64-bit key. As shown in figure 2.12, there is a simple formula for mapping 56 bits into the required 64-bit format. The seven byte string is simply copied, seven bits at a time, into an eight byte array. A parity bit (odd parity) is inserted following each set of seven bits (but some existing DES implementations use zero and ignore the parity bit).
The key is used by the DES algorithm to encrypt the source. Given the same key and source, DES will always return the same result.
The challenge needs to be very random, otherwise the logon process could be made vulnerable to "replay" attacks.
A replay attack is fairly straight-forward. The attacker captures the exchange between the server and the client and keeps track of the challenge, the response, and the username. The attacker then tries to log on, hoping that the challenge will be repeated (this step is easier if the challenge is at all predictable). If the server sends a challenge that is in the stored list, the attacker can use the recorded username and response to fake a logon. No password cracking required.
Given that the challenge is eight bytes (64 bits) long, and that random number generators are pretty good these days, it is probably best to create the challenge using a random number function. The better the random number generator, the lower the likelihood (approaching 1 in 264) that a particular challenge will be repeated.
The X/Open doc (which was written a long time ago) briefly describes a different approach to creating the challenge. According to that document, a seven-byte pseudo-random number is generated using an internal counter and the system time. That value is then used as the key in a call to DES(), like so:
Ckey = fn( time( NULL ), counter++ ); challenge = DES( Ckey, "????????" );
(...the source string is honest-to-goodnessly given as eight question marks.)
That formula actually makes a bit of sense, though it‘s probably overkill. The pseudo-random Ckey is non-repeating (because it‘s based on the time), so the resulting challenge is likely to be non-repeating as well. Also note that the pseudo-random value is passed as the key, not the source, in the call to DES(). That makes it much more difficult to reverse and, since it changes all the time, reversing it is probably not useful anyway.
Anybody remotely interesting
is mad, in some way or another.
--Doctor Who
As Andrew Bartlett42 points out, however, the time and counter inputs are easily guessed so the challenge is predictable, which is a potential weakness. Adding a byte or two of truly random "salt" to the Ckey in the recipe above would prevent such predictability.
email
Actually, given comments I‘ve read on some SMB cracking sites, it would not surprise me if MS still does (or at least did) use exactly this for the challenges. I still think you should address the X/Open function not as
‘overkill‘ but as ‘flawed‘.
|
Using a plain random number generator is probably faster, easier, and safer.
LM challenge/response authentication prevents password theft by ensuring that the plaintext password is never transmitted across a network or stored on disk. Instead, a separate value known as the "LM Hash" is generated. It is the LM Hash that is stored on the server side for use in authentication, and used on the client side to create the response from the challenge.
The LM Hash is a sixteen byte string, created as follows:
That outline would make a lot more sense as code, wouldn‘t it? Well, you‘re in luck. Listing 2.6 shows how the steps given above might be implemented.
Now we get to the actual logon. When a NEGOTIATE PROTOCOL REQUEST arrives from the client, the server generates a new challenge on the fly and hands it back in the NEGOTIATE PROTOCOL RESPONSE.
On the client side, the user is prompted for the password. The client generates the LM Hash from the password, and then uses the hash to DES-encrypt the challenge. Of course, it‘s not a straight-forward DES operation. As you may have noticed, the LM Hash is 16 bytes but the DES() function requires 7-byte keys. Ah, well... Looks as though there‘s a bit more padding and chopping to do.
Once again, we provide demonstrative code. Listing 2.7 shows how the LM Response would be generated.
The server, which has the username and associated LM Hash tucked away safely in its authentication database, also generates the 24-byte response string. When the client‘s response arrives, the server compares its own value against the client‘s. If they match, then the client has authenticated.
Under User Level security, the client sends its LM Response in the SESSION_SETUP_ANDX.CaseInsensitivePassword field of the SESSION SETUP request (yes, the LM response is in the SESSION SETUP REQUEST). With Share Level security, the LM Response is placed in the TREE_CONNECT_ANDX.Password field.
The details sometimes obfuscate the concepts, and vice versa. We have presented a general overview of the challenge/response mechanism, as well as the particular formulae of the LAN Manager scheme. Let‘s go through it once again, quickly, just to put the pieces together and cover anything that we may have missed.
On the down side, the LM Hash is password equivalent. Because of the design in the LM challenge/response mechanism, a cracker45 can use the LM Hash to break into a system. The password itself is not, in fact, needed. Thus, the LM Hash must be protected as if it were the password.
Well, that‘s a lot of work and it certainly goes a long way towards
looking complicated. Unfortunately, looking complicated isn‘t enough
to truly protect a password. LM challenge/response is an improvement
over plaintext, but there are some problems with the formula and it
turns out that it is not, in fact, a very big improvement.
...and we‘ll have fun, fun, fun
‘till somebody takes the
keyboard away.
-- Not quite as The
Beach Boys intended.
Let‘s consider what an attacker might do to try and break into a system. We‘ve already explained the replay attack. Other common garden varieties include the "dictionary" and the "brute force" attack, both of which simply try pushing possible passwords through the algorithm until one of them returns the same response seen on the wire. The dictionary attack is typically faster because it uses a database of likely passwords, so tools tend to try this first. The brute force method tries all (remaining) possible combinations of bytes, which is usually a longer process. Unfortunately, all of the upper-casing, nul-padding, chopping, and concatenating used in the LM algorithm makes LM challenge/response very susceptible to these attacks. Here‘s why:
The LM Hash formula pads the original password with nul bytes. If the password is short enough (seven or fewer characters) then, when the 14-byte padded password is split into two seven-byte DES keys, the second key will always be a string of seven nuls. Given the same input, DES produces the same output...
0xAAD3B435B51404EE = DES( "\0\0\0\0\0\0\0", "KGS!@#$%" )
...which results in an LM Hash in which the second set of eight bytes are known:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
result 0 | result 1 | ||||||||||||||
?? | ?? | ?? | ?? | ?? | ?? | ?? | ?? | AA | D3 | B4 | 35 | B5 | 14 | 04 | EE |
To create the LM Response, the LM Hash is padded with nuls to 21 bytes, and then split again into three DES keys:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
key 0 | key 1 | key 2 | ||||||||||||||||||
?? | ?? | ?? | ?? | ?? | ?? | ?? | ?? | AA | D3 | B4 | 35 | B5 | 14 | 04 | EE | 00 | 00 | 00 | 00 | 00 |
...and now the problem is obvious. If the original password was seven bytes or less, then almost two-thirds of the encryption key used to generate the LM Response will be a known, constant value. The password cracking tools leverage this information to reduce the size of the keyspace (the set of possible passwords) that needs to be tested to find the password. Less obvious, but clear enough if you study the LM Response algorithm closely, is that short passwords are only part of the problem. Because the hash is created in pieces, it is possible to attack the password in 7-byte chunks even if it is longer than 7 bytes.
Converting to upper-case also diminishes the keyspace, because lower-case characters do not need to be tested at all. The smaller the keyspace, the faster a dictionary or brute-force attack can run through the possible options and discover the original password46.
At some point in the evolution of WindowsNT a new, improved challenge/response formula was introduced. It was similar to the LAN Manager version, with the following changes:
...and that‘s basically it. The rest of the formula is the same.
So what does it buy us?
The first advantage of NTLM is that the passwords are more complex. They‘re mixed case and in Unicode, which means that the keyspace is much larger. The second advantage over LM is that the MD4() function doesn‘t require fixed length input. That means no padding bytes and no chopping to over-simplify the keys. The NTLM Hash itself is more robust than the LM Hash, so the NTLM Response is much more difficult to reverse.
Unfortunately, the NTLM Response is still created using the same algorithm as is used with LM, which provides only 56-bit encryption. Worse, clients often include both the NTLM Response and the LM Response (derived from the weaker LM Hash) in the SESSION SETUP ANDX REQUEST. They do this to maintain backward-compatibility with older servers. Even if the server refuses to accept the LM Response, the client has sent it. Ouch.
|
There is a theory which states
that if ever anyone discovers
exactly what the Universe is for
and why it is here, it will
instantly disappear
and be replaced by something
even more bizarre
and inexplicable.
There is another which states
that this has already happened.
-- Douglas Adams
The Restaurant at the End of the Universe
NTLMv2, as it‘s called, has some additional safeguards thrown into the recipe that make it more complex--and hopefully more secure--than its predecessors. There are, however, two small problems with NTLMv2:
Regarding the first point, Appendix B of Luke K. C. Leighton‘s book DCE/RPC over SMB: Samba and Windows NT Domain Internals provides a recipe for NTLMv2 authentication. We‘ll do our best to expand on Luke‘s description. The other option, of course, is to look at available Open Source code.
The second point is really conjecture, based in part on the fact that it took a very long time to get NTLMv2 implemented in Samba and few seemed to care. Indeed, NTLMv2 support had already been added to Samba-TNG by Luke and crew, and needed only to be copied over. It seems that the delay in adding it to Samba was not a question of know-how, but of priorities.
Another factor is that NTLMv2 is not required by default on most Windows systems. When challenge/response is negotiated, even newer Windows versions will default to using the LM/NTLM combination unless they are specifically configured not to.
We have already fussed with the DES algorithm and toyed with the MD4 algorithm. Now we get to use the HMAC-MD5 Message Authentication Code hash. This one‘s a power tool with razor-sharp keys and swivel-action hashing. The kind of thing your Dad would never let you play with when you were a kid. Like all good tools, though, it‘s neither complex nor dangerous once you learn how it works.
HMAC-MD5 is actually a combination of two different algorithms: HMAC and MD5. HMAC is a Message Authentication Code (MAC) algorithm that takes a hashing function (such as MD5) and adds a secret key to the works so that the resulting hash can be used to verify the authenticity of the data. The MD5 algorithm is basically an industrial-strength version of MD4. Put them together and you get HMAC-MD5.
HMAC-MD5 is quite well documented48, and there are a lot of implementations available. It‘s also much less complicated than it appears in figure 2.13, so we won‘t need to go into any of the details. For our purposes, what you need to know is that the HMAC_MD5() function takes a key and some source data as inputs, and returns a 16-byte (128-bit) output.
Hmmm... Well, it‘s not actually quite that simple. See, MD4, MD5, and HMAC-MD5 all work with variable-length input, so they also need to know how big their input parameters are. The function call winds up looking something like this:
hash16 = HMAC_MD5( Key, KeySize, Data, DataSize );
There is, as it turns out, more than one way to skin an HMAC-MD5. Some implementations use a whole set of functions to compute the result:
Conceptually, though, the multi-function approach is the same as the simpler example shown above. That is: Key and Data in, 16-byte hash out.
|
Another important tool is the older NTLM hash algorithm. It was described earlier but it is simple enough that we can present it again, this time in pseudo-code:
uchar *NTLMhash( uchar *password ) { UniPasswd = UCS2LE( password ); KeySize = 2 * strlen( password ); return( MD4( UniPasswd, KeySize ) ); }
The ASCII password is converted to Unicode UCS-2LE format, which requires two bytes per character. The KeySize is simply the length of that (Unicode) password string, which we calculate here by doubling the ASCII string length (which is probably cheating). Finally, we generate the MD4 hash (that‘s MD4, not MD5) of the password, and that‘s all there is to it.
Note that the string terminator is not counted in the KeySize. That is common behavior for NTLM and NTLMv2 challenge/response when working with Unicode strings.
The NTLM Hash is of interest because the SMB/CIFS designers at Microsoft (if indeed such people truly exist any more, except in legend) used it to cleverly avoid upgrade problems. With LM and NTLM, the hash is created from the password. Under NTLMv2, however, the older NTLM (v1) Hash is used instead of the password to generate the new hash. A server or Domain Controller being upgraded to use NTLMv2 may already have the older NTLM hash values in its authentication database. The stored values can be used to generate the new hashes--no password required. That avoids the nasty chicken-and-egg problem of trying to upgrade to NTLMv2 Hashes on a system that only allows NTLMv2 authentication.
The NTLMv2 Hash is created from:
The process works as shown in the following pseudo-code example:
v1hash = NTLMhash( password ); UniUser = UCS2LE( upcase( user ) ); UniDest = UCS2LE( upcase( destination ) ); data = uni_strcat( UniUser, UniDest ); datalen = 2 * (strlen( user ) + strlen( destination )); v2hash = HMAC_MD5( v1hash, 16, data, datalen );
Let‘s clarify that, shall we?
v1hash | = | The NTLM Hash, calculated as described previously. |
UniUser | = |
The username, converted to upper-case UCS-2LE Unicode.
|
UniDest | = |
The NetBIOS name of either the SMB server or NT Domain against
which the user is trying to authenticate.
|
data | = |
The two Unicode strings are concatenated and passed as the
Data parameter to the HMAC_MD5() function.
|
datalen | = |
The length of the concatenated Unicode strings, excluding the nul
termination. Once again, doubling the ASCII string lengths is
probably cheating.
|
v2hash | = | The NTLM Version 2 Hash. |
A bit more explanation is required regarding the destination value (which gets converted to UniDest).
In theory, the client can use NTLMv2 challenge/response to log into a stand-alone server or to log into an NT Domain. In the former case, the server will have an authentication database of its very own, but an NT Domain logon requires authentication against the central database maintained by the Domain Controllers.
So, in theory, the destination name could be either the NetBIOS name of the stand-alone server or the NetBIOS name of the NT Domain (no NetBIOS suffix byte in either case). In practice, however, the server logon doesn‘t seem to work reliably. The Windows systems used in testing were unable to use NTLMv2 authentication with one another when they were in stand-alone mode, but once they joined the NT Domain NTLMv2 logons worked just fine49.
The NTLMv2 Response is calculated using the NTLMv2 Hash as the Key. The Data parameter is composed of the challenge plus a blob of data which we will refer to as "the blob". The blob will be explained shortly. For now, just think of it as a mostly-random bunch of garblement. The formula is shown in this pseudo-code example:
blob = RandomBytes( blobsize ); data = concat( ServerChallenge, 8, blob, blobsize ); hmac = HMAC_MD5( v2hash, 16, data, (8 + blobsize) ); v2resp = concat( hmac, 16, blob, blobsize );
Okay, let‘s take a closer look at that and see if we can force it to make some sense.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 0 |
1 1 |
1 2 |
. | . | . |
challenge | blob... |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 0 |
1 1 |
1 2 |
1 3 |
1 4 |
1 5 |
1 6 |
1 7 |
1 8 |
1 9 |
2 0 |
. | . | . |
hmac | blob... |
If the client sends the NTLMv2 Response, it will take the place of the NTLM Response in the SESSION_SETUP_ANDX.CaseSensitivePassword field. Note that, unlike the older NTLM Response, the NTLMv2 Response algorithm uses 128-bit encryption all the way through.
If you have ever taken a college-level Invertebrate Zoology course, you may find the dissection of the blob to be nauseatingly familiar. The rest of you... try not to be squeamish. One more warning before we cut into this: The blob‘s structure may not matter at all. We‘ll explain why a little later on.
Okay, now that the disclaimers are out of the way, we can get back to work. The blob does have a structure, which is more or less as follows:
4 bytes: |
The value seen in testing is consistently 0x01010000. (Note that
those are nibbles, not bits.) The field is broken out as follows:
|
||||||
4 bytes: |
The value seen in testing is always 0x00000000. This field may,
however, be reserved for some purpose.
|
||||||
8 bytes: |
A timestamp, in the same 64-bit format described back in section 2.6.3.1.
|
||||||
8 bytes: |
The "blip": An eight-byte random value, sometimes referred
to as the "Client Challenge". More on this later, when we
talk about LMv2 challenge/response.
|
||||||
4 bytes: |
Unknown.
Comments in the Samba-TNG code and other sources suggest that this is meant to be either a 4-byte field or a pair of 2-byte fields. These fields should contain offsets to other data. That interpretation is probably based on empirical observation, but in the testing done for this book there was no pattern to the data in these fields. It may be that some implementations provide offsets and others just fill this space with left-over buffer garbage. Variety is the spice of life. |
||||||
variable length: |
A list of structures containing NetBIOS names in Unicode.
|
||||||
4 bytes: | Unknown. (Appears to be more buffer garbage.) |
The list of names near the end of the blob may contain the NT Domain and/or the server name. As with the names used to generate the NTLMv2 Hash, these are NetBIOS names in upper-case UCS-2LE Unicode with no string termination and no suffix byte. The name list also has a structure:
2 bytes: |
Name type.
|
||||||||||
2 bytes: |
The length, in bytes, of the name. If the name type is
0x0000, then this field will also be 0x0000.
|
||||||||||
variable length: | The name, in upper-case UCS-2LE Unicode format. |
The blob structure is probably related to (the same as?) data formats used in the more advanced security systems available under Extended Security50.
...they have weapons
of mass confusion
and aren‘t afraid to use them.
-- iomud
on Slashdot
Now that we have the formula worked out, let‘s take a closer look at the NTLMv2 challenge/response algorithm and see how much better it is than NTLM.
With the exception of the password itself, all of the inputs to NTLMv2 are known or knowable from a packet capture. Even the blob can be read off the wire, since it is sent as part of the response. That means that the problem is still a not-so-simple case of solving for a single variable: the password.
The NTLMv2 Hash is derived directly from the NTLM (v1) Hash. Since there is no change to the initial input (the password) the keyspace is exactly the same. The only change is that the increased complexity of the algorithm means that there are more encryption hoops through which to jump than in the simpler NTLM process. It takes more computer time to generate a v2 response, which doesn‘t impact a normal login but will slow down dictionary and brute force attacks against NTLMv2 (though Moore‘s Law may compensate). Weak passwords (those that are near the beginning of the password dictionary) are still vulnerable.
Another thing to consider is the blob. If the blob were zero length (empty), the NTLMv2 Response formula would reduce to:
v2resp = HMAC_MD5( v2hash, ServerChallenge );
...which would still be pretty darn secure. So the question is this: Does the inclusion of the blob improve the NTLMv2 algorithm and, if so, how?
Well, see, it‘s like this... Instead of being produced by the key and challenge alone, the NTLMv2 Response involves the hash of a chunk of semi-random data. As a result, the same challenge will not always generate the same response. That‘s good, because it prevents replay attacks...in theory.
In practice, the randomness of the challenge should be enough to prevent replay attacks. Even if that were not the case, the only way that the blob could help would be if it, too, were non-repeating and if the server could somehow verify that the blob was not a repeat. That, quite possibly, is why the timestamp is included.
The timestamp could be used to let the server know that the blob is "fresh". That is, that it was created a reasonably short amount of time before it was received. Fresh packets can‘t easily be forged because the response is HMAC-signed using the v2hash as the key (and that‘s based on the password which is the very thing the cracker doesn‘t know). Of course, the timestamp test won‘t work unless the client and server clocks are synchronized, which is not always the case.
In all likelihood the contents of the blob are never tested at all. There is code and commentary in the Samba-TNG source that shows that they have done some testing, and that their results indicate that a completely random blob of bytes works just fine. If that‘s true, then the blob does little to improve the security of the algorithm except perhaps by adding a few more CPU cycles to the processing time.
Bottom line: | NTLMv2 challenge/response provides only a minimal improvement over its predecessor. |
...it is a tale
Told by an idiot, full of sound
and fury, signifying nothing.
-- Macbeth, Act V, Scene v,
William Shakespeare
This isn‘t the first time that we have put a lot of effort into figuring out some complex piece of the protocol only to discover that it‘s almost pointless, and it probably won‘t be the last time either.
email
In section 2.8.5.5 you talk about the "client challenge" a bit, but miss the point of it: the client nonce (as it should really more correctly be called) is there to prevent precomputed dictionary attacks by the server, and has nothing to do with replay attacks against the server (which, as you correctly state, is what the server challenge is for). If there‘s no client nonce, then a rogue server can pick a fixed server nonce (server challenge), take dictionary, and precompute all the responses. Then any time a client connects to it it sends the fixed challenge, and upon receipt of the client‘s response it can do a simple database lookup to find the password (assuming the password was in the dictionary). However, if the client adds its own bit of random stuff to the response computation, then this attack (by the server) is not possible. Hence the client nonce. Even with client nonces a rogue server can still try to use a
dictionary to figure out your password, but the server has to run the
complete dictionary on each response, instead of being able to
precompute and use the results for all responses.
|
There is yet one more small problem with the NTLMv2 Response, and that problem is known as pass-through authentication. Simply put, a server can pass the authentication process through to an NT Domain Controller. The trouble is that some servers that use pass-through assume that the response string is only 24 bytes long.
You may recall that both the LM and NTLM responses are, in fact, 24 bytes long. Because of the blob, however, the NTLMv2 response is much longer. If a server truncates the response to 24 bytes before forwarding it to the NT Domain Controller almost all of the blob will be lost. Without the blob, the Domain Controller will have no way to verify the response so authentication will fail.
To compensate, a simpler response--known as the LMv2 response--is also calculated and returned alongside the NTLMv2 response. The formula is identical to that of NTLMv2, except that the blob is really small.
blip = RandomBytes( 8 ); data = concat( ServerChallenge, 8, blip, 8 ); hmac = HMAC_MD5( v2hash, 16, data, 16 ); LMv2resp = concat( hmac, 16, blip, 8 );
The "blip", as we‘ve chosen to call it, is sometimes referred to as the "Client Challenge". If you go back and look, you‘ll find that the blip value is also included in the blob, just after the timestamp. It is fairly easy to spot in packet captures. The blip is 8 bytes long so that the resulting LMv2 Response will be 24 bytes, exactly the number needed for pass-through authentication.
If it is true that the contents of the blob are not checked, then the LMv2 Response isn‘t really any less secure than the NTLMv2 Response--even though the latter is bigger.
The LMv2 Response takes the place of the LM Response in the SESSION_SETUP_ANDX.CaseInsensitivePassword field.
The use of NTLMv2 is not negotiated between the client and server. There is nothing in the protocol to determine which challenge/response algorithms should be used.
So, um... how does the client know what to send, and how does the server know what to expect?
The default behavior for Windows clients is to send the LM and NTLM responses, and the default for Windows servers is to accept them. Changing these defaults requires fiddling in the Windows registry. Fortunately, the fiddles are well known and documented so we can go through them quickly and get them out of the way51.
The registry path to look at is:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\LSA
On Win9x the variable is called LMCompatibility, but on WindowsNT and 2000 it is LMCompatibilityLevel. That variable may not be present in the registry, so you might have to add it. In general, it‘s best to follow Microsoft‘s instructions when editing the registry52.
The settings for LMCompatibilityLevel are as follows:
Level | Description | Client Implications | Server Implications |
---|---|---|---|
0 | The Default | LM and NTLM responses are sent by the client. | The server or Domain Controller will compare the client‘s responses against the LM, NTLM, LMv2, and NTLMv2 responses. Any valid response is acceptable. |
1 | NTLMv2 Session Security | This level does nothing to change the algorithm used to generate the response. Instead, at this level and higher a feature called NTLMv2 Session Security is supported. Session Security is only used with Extended Security, and must be negotiated between the client and the server. Session Security is an advanced topic, and won‘t be covered here. | |
2 | NTLM Authentication | The LM Response is not sent by the client. Instead, the NTLM Response is sent in both password fields. Replacing the LM Response with the NTLM Response facilitates pass-through authentication. Servers need only hand the 24-byte contents of the SESSION_SETUP_ANDX.CaseInsensitivePassword field along to the Domain Controller. | The server or Domain Controller will accept a valid LM, NTLM, LMv2, or NTLMv2 response. |
3 | NTLMv2 Authentication | The client sends the LMv2 and NTLMv2 responses in place of the older LM and NTLM values. | The server or Domain Controller will accept a valid LM, NTLM, LMv2, or NTLMv2 response. |
4 | NTLM Required | The client sends the LMv2 and NTLMv2 responses. | At this level, the server (or Domain Controller) will not check LM Responses. It will compare responses using the NTLM, LMv2, and/or NTLMv2 algorithms. |
5 | NTLMv2 Required | The client sends the LMv2 and NTLMv2 responses. | The server (or Domain Controller) will compare the client‘s responses using the LMv2 and NTLMv2 algorithms only. |
That‘s just a quick overview of the settings and their meanings. The important points are these:
Our discussion of SMB authentication mechanisms is winding down now. There are a few more topics to be covered and a few others that will be carefully, but purposefully, avoided. Extended Security falls somewhere in between. We will dip our toes into its troubled waters, but we won‘t wade in too deep (or the monsters might get us).
One reason for trepidation is that--as of this writing--Extended Security is still an area of active research and development for the Samba Team and others. Though much has been learned, and much has been implemented, the dark pools are still being explored and the fine points are still being examined. Another deterrent is that Extended Security represents a full set of sub-protocols--a whole, vast world of possibilities to be explored ...some other day. As with MS-RPC (which we touched on just long enough to get our fingers burned), the topic is simply too large to cover here.
As suggested in figure 2.14, Extended Security makes use of nested protocols. Go back to section 2.6.3.2 and take a look at the NEGOTIATE_PROTOCOL_RESPONSE.SMB_DATA structure. Note that the ext_sec.SecurityBlob field is nothing more than a block of bytes--and it‘s what‘s inside that block that matters. If the client and server agree to use Extended Security, then the whole NEGOTIATE PROTOCOL RESPONSE / SESSION SETUP REQUEST business becomes a transport for the authentication protocol.
In some cases the security exchange may require several packets and a few round trips to complete. When that happens, a single NEGOTIATE PROTOCOL RESPONSE / SESSION SETUP REQUEST pair will not be sufficient to handle it all. The solution to this dilemma is fairly simple: The server sends an error message to force the client to send another SESSION SETUP REQUEST containing the next chunk of data.
The only spec I trust
is written in C.
-- Andrew Bartlett,
Samba Team
The process is briefly (and incompletely) described in section 4.1.2 of the SNIA doc as part of the discussion of the SESSION SETUP RESPONSE. Simply put, as long as there are more Extended Security packets required, the server will reply to the SESSION SETUP REQUEST by sending a NEGATIVE SESSION SETUP RESPONSE with an NT_STATUS value of 0xC0000016 (which is known as STATUS_MORE_PROCESSING_REQUIRED). The client then sends another SESSION SETUP REQUEST containing the additional data. This continues until the authentication protocol has completed.
There is no DOS error code equivalent for STATUS_MORE_PROCESSING_REQUIRED, something we have already whined about in the "!Strange Behavior Alert" box back in section 2.5.1. It seems that Extended Security expects that the client can handle NT_STATUS codes, which may be a significant issue for anyone trying to implement an SMB client53.
There are several different authentication protocols which may
be carried within the SecurityBlob. Those protocols, in
turn, are built on top of a whole pile of different languages and APIs
and data transfer formats. The result is an alphabet soup of
acronyms. Here‘s a taste:
...mit Schlag.
ASN.1: |
Abstract Syntax Notation One
ASN.1 is a language used to define the structure and content of objects such as data records and protocol messages. If you are not familiar with ASN.1, you might think of it as a super-duper-hyper version of the typedef in C--only a lot more powerful. ASN.1 was developed as part of the Open Systems Interconnection (OSI) environment, and was originally used for writing specifications. More recently, though, tools have been developed that will generate software from ASN.1. The development and promotion of the ASN.1 language is managed by
the ASN.1 Consortium.
|
---|---|
DER: |
Distinguished Encoding Rules of ASN.1
DER is a set of rules for encoding and decoding ASN.1 data.
It provides a standard format for transport of data over a network
so that the receiving end can convert the data back into its correct
ASN.1 format. DER is a specialized form of a more general encoding
known as BER (Basic Encoding Rules). DER is
designed to work well with security protocols, and is used for
encoding Kerberos and LDAP exchanges.
|
GSS-API: |
Generic Security Service Application Program Interface
As the name suggests, GSS-API is a generic interface to a set of
security services. It makes it possible to write software that does
not care what the underlying security mechanisms actually are.
GSS-API is documented in RFC 2078.
|
Kerberos: |
"Kerberos" is a name, not an acronym.
Kerberos is the preferred authentication system for SMB
over naked TCP/IP transport. The use of Kerberos with CIFS is also
tied in with GSS-API and SPNEGO.
|
LDAP: |
Lightweight Directory Access Protocol
Some folks at the University of Michigan realized that the DAP
protocol (which was designed as part of the the Open Systems
Interconnection (OSI) environment for use with the X.500 directory
system) was just too big and hairy for general-purpose use, so they
came up with a "lightweight" version, known as LDAP. LDAP
was popularized in the mid-1990‘s, and support was included with
directory service implementations such as Novell‘s NDS
(Novell Directory Service). When Microsoft
created their Active Directory system they followed Novell‘s lead
and added LDAP support as well.
|
MIDL: |
Microsoft Interface Definition Language
MIDL is Microsoft‘s version of the Interface
Definition Language (IDL). It is used to specify the
parameters to function calls, particularly function calls made
across a network--things like Remote Procedure Call (RPC). MIDL is
also used to define the interfaces to Microsoft DLL library
functions.
|
MS-RPC: |
Microsoft Remote Procedure Call
Paul Leach was one of the founders of Apollo Computer. At
Apollo, he worked on a system for distributed computing that
eventually became the DCE/RPC system. When Hewlett-Packard
purchased Apollo, Leach went to Microsoft. That‘s probably why the
MS-RPC system is so remarkably similar to the DCE/RPC system.
|
NDR: |
Network Data Representation
NDR is to DCE/RPC as DER (or BER) is to ASN.1. That is, NDR is
an on-the-wire encoding for parameters passed via RPC. When an
MS-RPC call is made on the client side, the parameters are converted
into NDR format (marshalled) for transmission over the
network. On the server side, the NDR formatted data is
unmarshalled and passed into the called function. The process
is then reversed to return the results.
|
NTLMSSP: |
NTLM Security Support Provider
It seems there are a few variations on the interpretation of the acronym. A quick web search for "NTLMSSP" turns up NTLM Security Service Provider and NTLM Secure Service Provider in addition to NTLM Security Support Provider. No matter. They all amount to the same thing. NTLMSSP is a Windows authentication service that is accessed in
much the same way as MS-RPC services. NTLMSSP authentication
requests are formatted into a record structure and converted to NDR
format for transport to the NTLMSSP authentication service provider.
In addition to Extended Security, NTLMSSP authentication shows up in
lots of odd places. It is even used by Microsoft Internet Explorer
to authenticate HTTP (web) connections.
|
SPNEGO: |
Simple, Protected Negotiation
Also known as "The Simple and Protected GSS-API Negotiation Mechanism", SPNEGO is a protocol that underlies GSS-API. It is used to negotiate the security mechanism to be used between two systems. SPNEGO is documented in RFC 2478. |
Quite a list, eh?
I am friend to the undertow
I take you in, I don‘t let go
-- Undertow,
Suzanne Vega
As you can see, there is a lot going on below the surface of Extended Security. We could try diving into a few of the above topics, but the waters are deep and the currents are strong and we would quickly be swept away. Out of necessity, we will spend a little time talking about Kerberos, but we won‘t swim out too far and we will be wearing a PFD (Personal Floatation Device--don‘cha just love acronyms?).
As already stated, we won‘t be going into depth about Kerberos. There is a lot of documentation available on the Internet and in print, so the wiser course is to suggest some starting points for research. There are, of course, several starting points presented in the References section of this very book. A good place to get your feet wet is Bruce Schneier‘s Applied Cryptography, Second Edition.
Kerberos version 5 is specified in RFC 1510, but this is CIFS we‘re talking about. Microsoft has made a few "enhancements" to the standard. The best known is probably the inclusion of a proprietary Privilege Access Certificate (PAC) which carries Windows-specific authorization information. Microsoft heard a lot of grumbling about the PAC, and in the end they did publish the information required by third-party implementors. They even did so under acceptable licensing terms (and the CIFS community sighed a collective sigh of relief). The PAC information is available in a Microsoft Developer Network (MSDN) document entitled Windows 2000 Authorization Data in Kerberos Tickets.
There are a lot of Kerberos-related RFCs. The interesting ones for our purposes are:
There is also (as of this writing) a set of Internet Drafts that cover Microsoft Kerberos features, including a draft for Kerberos authentication over HTTP.
Finally, a web search for "Microsoft" and "Kerberos" will toss up an abundant salad of opinions and references, both historical and contemporary. Where CIFS is concerned, it seems that there is always either too little or too much information. Microsoft-compatible Kerberos falls under the latter curse. There is a lot of stuff out there, and it is easy to get overwhelmed. If you plan to dive in, find a buddy. Don‘t swim alone.
We have been delicately dancing around the role of the Domain Controller in authentication. It‘s time to face the music.
The concept is fairly simple: Take the password database that is normally kept locally by a stand-alone server and move it to a central authority so that it can be shared by multiple servers, then call the whole thing a "Domain". The central authority that stores the shared database is, of course, the Domain Controller. As shown in figure 2.15, the result is that the SMB fileserver must now consult the Domain Controller when a user tries to access SMB services.
That general description applies to both NT and W2K Domains, even though the two are implemented in very different ways. Windows2000 Domains are based on Active Directory and Kerberos, while WindowsNT Domains make use of a Security Accounts Manager (SAM) Database and MS-RPC.
Let‘s see what bits of wisdom we can pull out of the hat regarding these two Domain systems...
As with Microsoft‘s Kerberos implementation, there is probably too much information available on this topic. A full description would also be very much beyond the stated scope of this book. So, as briefly as possible, here are some notes about W2K Domains and Domain Controllers:
...and that barely begins to scratch the surface. CIFS client and server participation in a W2K Domain requires Kerberos support, but does not require a detailed understanding of Active Directory architecture. The points above are of interest here primarily for comparison with the NT Domain system notes, presented below.
In contrast to W2K Domains, NT Domains have the following features:
There are two mechanisms that an SMB Server can use to ask a Domain Controller to validate a client logon attempt. These are known as pass-through and NetLogon authentication. The NetLogon mechanism uses MS-RPC, so we won‘t cover it here except to say that it provides a more intimate relationship between the SMB server and the Domain Controller than does the pass-through mechanism. There are several good sources for further reading listed in the References section. In particular:
Pass-through, in contrast to NetLogon, is really quite simple. It is also documented in (yet) an(other) expired Leach/Naik IETF draft, titled CIFS Domain Logon and Pass Through Authentication, which can be found on Microsoft‘s CIFS FTP site (under the name cifslog.txt).
Basically, pass-through authentication is a man-in-the-middle mechanism. It goes like this:
It should be easy to capture an example of pass-through authentication using your network sniffer. Windows9x systems (and possibly other Windows varieties) do not support NetLogon so they always use the pass-through method if they are part of an NT Domain. Samba can be configured to use either method.
|
In the NT Domain system, there is a single Domain Controller that is primarily responsible for the maintenance of the domain‘s SAM database. This Domain Controller is known as the (surprise) Primary Domain Controller (PDC).
The domain may also have zero or more Backup Domain Controllers (BDCs). The BDCs keep read-only replicas of the PDC‘s SAM database. BDCs can be used for authentication just as the PDC can, and if the PDC is accidentally thrown out of a twelfth-story window into an active volcano, a BDC can be "promoted" to fill the role of the dearly departed PDC.
Windows2000 Domains do things differently. They do not distinguish between Primary and Backup DCs. Instead, Active Directory makes use of something called "multimaster replication". Updates to any replica are propagated to all of the other replicas, so there is no longer any need to specify one copy of the database as the primary.
This is one of those concepts that we have to cover because--unless you‘re already familiar with it--you‘ll read about it somewhere else and think to yourself "What the heck is that all about?".
Somewhere back a few paragraphs it was stated that NT Domains are, conceptually, stand-alone entities ...and so they are, but it is possible to introduce them to one another and get them to cooperate. The agreements forged between the domains are known as "Inter-Domain Trust Relationships".
Let‘s use an example to explain what this is all about.
Consider a large corporate organization with several divisions, departments, committees, consultants, and such-like. In this corporation, the Business Units Reassignment Planning Division runs the BURP_DIV domain, and the Displacement Entry Department calls theirs the DISENTRY domain.
Now, let‘s say that the BURP_DIV folks need access to files stored on DISENTRY servers (so they can move the files around a bit). One way to handle this would be to create accounts for the BURP_DIV users in the DISENTRY domain. That would cause a bit of a problem, however, because the BURP_DIV users would need two accounts, one per domain. That is likely to result in things like passwords, preferences, and web browser bookmarks getting a bit out of sync. Also, the Benefits Reduction Committee will want to know why all of the BURP_DIV employees are moonlighting in the DISENTRY department and how they could possibly be doing two jobs at once. It could become quite a mess, resulting in the hiring of dozens of consultants to ensure that the problem is properly ignored.
The better way to handle this situation is to create a trust relationship between the DISENTRY and BURP_DIV domains. With inter-domain trust established, the BURP_DIV folks can log on to DISENTRY servers using their BURP_DIV credentials. As shown in figure 2.16, the DISENTRY Domain Controller will ask the BURP_DIV Domain Controllers to validate the logon.
Note that, in the non-extended-security version of the SESSION SETUP REQUEST message, there is a field called PrimaryDomain. This field identifies the NT domain against which the client wishes to authenticate. That is, the PrimaryDomain field should contain the name of the NT Domain to which the user belongs.
Windows2000 domains also support trust relationships. This is useful for creating trust between two separate W2K Domain trees, or between W2K Domains and NT Domains.
The mechanisms used to support inter-domain trust are very advanced topics, and won‘t be covered here.
Message Authentication Codes (MACs) are used to prevent "pickle-in-the-middle" attacks (more commonly known as "man-in-the-middle" attacks 54). This form of attack is simple to describe, but it can be difficult to pull off in practice (though wireless LAN technology has the potential to make it much easier). Figure 2.17 provides some visuals.
Generally speaking, in the pickle-in-the-middle attack an evil interloper allows the "real" client to authenticate with the server and then assumes ownership of the TCP/IP connection, thus bypassing the whole problem of needing to know the password.
There are a number of ways to hijack the TCP session, but with SMB
that step isn‘t necessary. Instead, the evil interloper can simply
impersonate the server to fool the client. For instance, if the evil
interloper is on the same IP subnet as both the client and server (a
B-mode network) then it can usurp the server‘s name by responding to
broadcast name queries sent by the client faster than the server does.
Server identity theft can also be accomplished by "poisoning"
the NBNS database (or, possibly, the DNS). That is, by somehow
forcing it to swallow false information. A simple way to do that is
to register the server‘s name--with the interloper‘s IP address--in
the NBNS before the server does (perhaps by registering the name while
the server is down for maintenance or something).
Proof by Familiarity:
Well, that looks familiar!
-- Jonathan Young, PhD.
In any case, when the client tries to open an SMB session with the server it may wind up talking to the evil interloper instead. The evil interloper will pass the authentication request through to the real server, then pass the challenge back to the client, and then pass the client‘s response to the server... and that... um... um... um... that looks exactly like pass-through authentication. In fact, the basic difference between pass-through authentication and this type of attack is the ownership of the box that is relaying the authentication. If the crackers control the box, consider it an attack.
This authentication stuff is fun, isn‘t it?
So, given a situation in which you are concerned about evil interlopers gaining access to your network, you need a mechanism that allows the client and server to prove to one another on an ongoing basis that they are the real client and server. That‘s what the MACs are supposed to do.
|
The server and client each generate a special key, known as the Session Key. There are several potential uses for the Session Key, but we will only be looking at its use in MAC signing.
The Session Key is derived from the password hash--something that only the client and server should know. There are several hash types available: LM, NTLM, LMv2, and NTLMv2. The hash chosen is probably the most advanced hash that the two systems know they share. So, if the client sent an LM Response--but did not send an NTLM Response--then the Session Key will be based on the LM Hash. The LM Session Key is calculated as follows:
char eightnuls[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; LM_Session_Key = concat( LM_Hash, 8, eightnuls, 8 );
That is, take the first eight bytes of the LM Hash and add eight nul bytes to the end for a total of 16 bytes. Note that the resulting Session Key is not the same as the LM Hash itself. As stated earlier, the password hashes can be used to perform all of the authentication functions we have covered so far, so they must be protected as if they were the actual password. Overwriting the last eight bytes of the hash with zeros serves to obfuscate the hash (though this method is rather weak).
A different formula is used if the client did send an NTLM Response. The NTLM Session Key is calculated like so:
NTLM_Session_Key = MD4( NTLM_Hash );
Which means that the NTLM Session Key is the MD4 of the MD4 of the Unicode password. The SNIA doc says there‘s only one MD4, but that would make the NTLM Session Key the same as the NTLM Hash. Andrew Bartlett of the Samba Team says there are two MD4s; the second does a fine job of protecting the password-equivalent NTLM Hash from exposure.
Moving along to LMv2 and NTLMv2, we find that the Session Key recipe is slightly more complex, but it‘s all stuff we have seen before. We need the following ingredients:
v2hash | = | The NTLM Version 2 Hash, which we calculated back in section 2.8.5.2. |
hmac | = | The result of the HMAC_MD5() function using the v2hash as the key and the server challenge plus the blob (or blip) as the input data. The NTLMv2 hmac was calculated in section 2.8.5.3, and sent as the first 16 bytes of the response. The LMv2 hmac was calculated in section 2.8.5.6. |
The LMv2 and NTLMv2 session keys are computed as follows:
LMv2_Session_Key = HMAC_MD5( v2hash, 16, lmv2_hmac, 16 ); NTLMv2_Session_Key = HMAC_MD5( v2hash, 16, ntlmv2_hmac, 16 );
The client is able to generate the Session Key because it knows the password and other required information (because the user entered the required information at the logon prompt). If the server is stand-alone, it will have the password hash and other required information in its local SAM database, and can generate the Session Key as well. On the other hand, if the server relies upon a Domain Controller for authentication then it won‘t have the password hash and won‘t be able to generate the Session Key.
What‘s a server to do?
As we have already pointed out, the MAC protocol is designed to prevent a situation that looks exactly like pass-through authentication, so a pass-through server simply cannot do MAC signing. A NetLogon-capable server, however, has a special relationship with the Domain Controller. The NetLogon protocol is secured, so the Domain Controller can generate the Session Key and send it to the server. That‘s how an NT Domain member server gets hold of the Session Key without ever knowing the user‘s password or password hash.
Both the client and server maintain an integer counter which they initialize to zero. This counter is used as a message sequence number, and it gets incremented for every message such that requests always have an even sequence number and replies always have an odd.
The zero-eth message is always a SESSION SETUP ANDX message, but it may not be the first SESSION SETUP ANDX of the session. Recall, from near the beginning of the Authentication section, that the client sometimes uses an anonymous or guest logon to access server information. Watch enough packet captures and you will see that MAC signing doesn‘t really start until after a real user logon occurs.
Also, it appears from testing that the MAC Signature in the zero-eth message is never checked (and that existing clients send a bogus MAC Signature in the zero-eth packet). That‘s okay, since the authenticity of the zero-eth message can be verified by the fact that it contains a valid response to the server challenge.
Once the MAC signing has been initialized within a session, all messages are numbered using the same counters and signed using the same Session Key. This is true even if additional SESSION SETUP ANDX exchanges occur.
The MAC itself is calculated using the MD5 function. That‘s the plain MD5, not HMAC-MD5 and not MD4. The input to the MD5 function consists of three concatenated blocks of data:
We start by combining the Session Key and the response into a single value known as the MAC Key. For LM, NTLM, and LMv2 the MAC Key is created like so:
MAC_Key = concat( Session_Key, 16, Response, 24 );
The thing to note here is that all of the responses, with the exception of the NTLMv2 Response, are 24 bytes long. So, except for NTLMv2, all auth mechanisms produce a MAC Key that is 40 bytes long. (16 + 24 = 40). Unfortunately, the formula for creating the NTLMv2 MAC Key is not yet known. It is probably similar to the above, however. Possibly identical to the calculation of the LMv2 MAC Key, or possibly the concatenation of the Session Key with the first 28 bytes of the blob.
Okay, now you need to pay careful attention. The last few steps of MAC Signature calculation are a bit fiddly.
data = concat( MAC_Key, MAC_Key_Len, SMB_Msg, SMB_Msg_Len ); hash = MD5( data ); MAC = head( hash, 8 );
In words: the MAC Signature is the first eight bytes of the MD5 of the MAC_Key plus the entire SMB message.
...and that, to the best of our knowledge, is how it‘s done.
Windows NT systems offer four registry keys to control the use of SMB MAC signing. The first two manage server behavior, and the second pair represent client settings.
Study those closely and you may detect some small amount of similarity between the client and server parameter settings. (Well, okay, they are mirror images of one another.) Keep in mind that the client and server must have compatible settings or the SESSION SETUP will fail.
These options are also available under Windows2000, but are managed using security policy settings55.
A mathematician, a physicist, and an engineer were sitting together in a teashop, sharing a pot of Lapsang Souchong and discussing the relationship between theory and practice. The mathematician said "One of my students asked me today whether all odd numbers greater than one were prime numbers, so I provided this simple proof:
"Stated: All odd numbers greater than one are prime.
3 is prime,
5 is prime,
7 is prime,
9 is divisible by three, so it is odd but not prime.
Contradiction; the statement is false."
"Interesting.", replied the physicist. "Perhaps I have the same student. I was asked the same question today. I solved the problem using a thought-experiment, as Galileo might have done. Our experiment was as follows:
"By observation we can see that:
3 is prime,
5 is prime,
7 is prime,
9 is experimental error,
11 is prime,
13 is prime..."
Proof by Assertion:
This has got to be true!
-- Jonathan Young, PhD.
The engineer interrupted before the physicist could draw a conclusion, and said "Out in the field we don‘t have time to mess with theory. We just define all odd numbers as prime and work from there. It‘s simpler that way."
Consider this as you contemplate what you have learned about SMB authentication.
You should now have all you need to create an SMB session with an SMB server. As you become more comfortable with the system you will likely become curious about the vast uncharted jungle of Extended Security. Don‘t be afraid to go exploring. With the background provided here, and the guidebooks listed in the References section, you are well prepared. If you get it all mapped out, do us all a favor: write it up so that everyone can share what you‘ve learned.
A few more bits of advice before we move along...
This is one of Andrew Bartlett‘s rules of thumb. If you are trying to figure out how an encrypted token or key or somesuch is derived, consider the available functions and inputs. Existing tools and values are often re-used. Just look through the calculation of the NTLMv2 Response and you‘ll see what we mean.
Read the available documentation and make notes, but don‘t assume that the documentation is always right. The truth is on the wire. In some cases implementations stray from the specifications, and in other cases (eg., this book) the documentation is a best-effort attempt at presenting what has been learned. There are few truly definitive sources. Another factor, as you are by now aware, is that there is a tremendous amount of variation in the CIFS world. Something may work correctly in one instance only to surprise you in another.
Don‘t go looking for weirdness in CIFS, but don‘t be surprised when you find it. If you expect bad behavior, you may miss the sane and obvious. A lot of CIFS does, in fact, make some sort of sense when you think about it. There are gotchas, though, so be prepared.
These guidelines are quite general, but they apply particularly well to the study of SMB security and authentication.
Looking back over our shoulders, we see that we have performed only two SMB exchanges so far: the NEGOTIATE PROTOCOL and the SESSION SETUP. There may be a TREE CONNECT shoved into the packet with the SESSION SETUP as an AndX, but we haven‘t really described the TREE CONNECT in detail.
So, although we have covered a tremendous amount of material, our progress seems rather pathetic doesn‘t it? What if the rest of SMB is just as tedious, verbose, and difficult?
Relax. It‘s not.
Certainly there are other difficulties lying in wait, but the biggest ones have already been identified and we are carefully avoiding them. If you pursue your dream of creating a complete and competitive CIFS implementation then you may, some day, need to know how things like MS-RPC and Extended Security really work inside. Fortunately, you can do without them for now.
Let‘s just be clear on this before we move along:
There is a lot you can do with CIFS without implementing any of the extended sub-protocols that SMB supports, but if you want to build a complete and competitive CIFS client/server implementation you will need to go well beyond the SMB protocol itself.
Everything should be made
as simple as possible,
but not simpler.
-- Albert Einstein
That‘s why it has taken the Samba Team (with help from hundreds if not thousands of people across the Internet) more than ten years to make Samba the industrial-strength server system it is today. Tridge worked out the basics of NBT and SMB in a couple of weeks back in 1991, but new things keep getting tacked on to the system.
When implementing CIFS, the rule of thumb is this: Implement as little as possible to do the job you need to do.
The minute you cross the border into uncharted territory you open up a whole new world to explore and discover. Sometimes, you just don‘t want to go there. Other times, you must.
Anyway, in the spirit of keeping things simple we will cover only a few more SMB messages, and those in much less depth than we have done so far. There really is no need to study every message, longword, bit, and string. If you‘ve come this far, you should know how to read packet captures and interpret the message definitions in the SNIA doc. It is time to take the training wheels off and learn to ride.
We have talked a lot about the TREE CONNECT ANDX REQUEST SMB. There was even an example way back in section 2.4.4. The example looked like this:
SMB_PARAMETERS { WordCount = 4 AndXCommand = SMB_COM_NONE (0xFF) AndXOffset = 0 Flags = 0x0000 PasswordLength = 1 } SMB_DATA { ByteCount = 22 Password = "" Path = "\\SMEDLEY\HOME" Service = "?????" (yes, really) }
Notice that the TREE CONNECT includes a Password field, but that in this example the Password field is almost empty (it contains a nul byte). If the server negotiates Share Level security, then the password that would otherwise be in the SESSION_SETUP_ANDX.CaseInsensitivePassword field will show up in the TREE_CONNECT_ANDX.Password field instead. The password may be plaintext, or it may be one of the response values we calculated earlier.
The TREE_CONNECT_ANDX.Path field is also worth mentioning. It contains the UNC pathname of the share to which the client is trying to connect. In this example, the client is attempting to access the HOME share on node SMEDLEY. Note that the Path will be in Unicode if negotiated.
Finally there is that weird quintuple question mark string in the TREE_CONNECT_ANDX.Service field. There are, as it turns out, five possible values for that field:
String | Meaning |
---|---|
A: | A filesystem share |
LPT1: | A shared printer |
IPC | An interprocess communications named pipe |
COMM | A serial or other communications device |
????? | Wildcard |
It‘s annoying for the client to need to know the kind of share to which it is connecting, which is probably why the wildcard option is available. The server will return the service type in the Service field of the Response. Note that the Service strings are always in 8-bit ASCII characters--never Unicode.
The response (for LANMAN2.1 and above) looks like this:
SMB_PARAMETERS { WordCount = 3 AndXCommand = <Next ANDX command> AndXOffset = <Next ANDX block offset> OptionalSupport = <A bitfield> } SMB_DATA { ByteCount = <variable> Service = <"A:" | "LPT1:" | "IPC" | "COMM"> NativeFileSystem = <"" | "FAT" | "NTFS"> }
The example above shows the empty string, "FAT", or "NTFS" as the valid values for the NativeFileSystem field. Other values are possible. (Samba, for instance, has a configuration option that allows you to put in anything you like.) The empty string is used for the hidden IPC$ share.
There are two bits defined in the OptionalSupport bitfield:
Bit | Meaning |
---|---|
SMB_SUPPORT_SEARCH_BITS 0x0001 |
The meaning of this bit is explained in the LANMAN2.1 documentation. Basically, it indicates that the server knows how to perform directory searches that filter out some entries based on specific file attributes. For example, whether the DOS archive bit is set, whether the name represents a directory, etc. This is old stuff and all current implementations should support it. |
SMB_SHARE_IS_IN_DFS
0x0002 |
This bit, if set, indicates that the UNC name is in the Distributed File System (DFS) namespace. DFS is yet to be covered. |
There is a note in the SNIA doc that states that some servers will leave out the OptionalSupport field even if the LANMAN2.1 or later dialect is negotiated. It does not say whether SMB_SUPPORT_SEARCH_BITS should be assumed in such cases.
Here‘s a toy we can play with.
ECHO is really as simple as it sounds. It‘s sort of the SMB equivalent of ping. The client sends a packet with a data block full of bytes, and the server echoes the block back. Simple.
...but this is CIFS we‘re talking about.
Although the ECHO itself is simple, there are many quirks to be found in existing implementations. We will dig into this just a tiny bit to give you a taste of the kinds of problems you are likely to encounter. Let‘s start with a quick look at the ECHO REQUEST structure:
SMB_PARAMETERS { WordCount = 1 EchoCount = <In theory, anything from 0 to 65535> } SMB_DATA { ByteCount = <Number of data bytes to follow> Bytes = <Your favorite soup recipe?> }
Neun und neunzig luftballons
Auf ihrem Weg zum Horizont.
-- 99 Luftballoons, Nena
The EchoCount field is a multiplier. It tells the server to respond EchoCount times. If EchoCount is zero, you shouldn‘t get any reply at all. If EchoCount is 9,999, then you are likely to get nine thousand, nine hundred, and ninety-nine replies. We say likely because of the wide variety of weirdity that can be seen in testing.
One bit of weirdation is that all of the systems that were tested would respond to an ECHO REQUEST even if no SESSION SETUP had been sent and no authentication performed. This behavior is, in fact, per design, but it means that any client that can talk to your server from anywhere can ask for EchoCount replies to a single request. (It would probably be safer for the server to send a ERRSRV/ERRnosupport error message in response to an un-authenticated ECHO REQUEST.)
Other strangisms of note:
email
I saw the same "packet drop" with an overlong
WRITE_ANDX. The maximum buffer size an NT SP6 claims on the
NEGOTIATE response is 0x1104 (4356). This limit is not on the
data, the limit includes the SMB header (32 bytes) and the SMB
command. Based upon the size of an ECHO command I‘d expect you
could send 4319 bytes, not 4323, so on this topic you‘ll have to
have the last word... sorry.
|
No apologies. This is CIFS we‘re talking about.
The ECHO SMB may be one of those things that gets coded up just because it‘s in the documentation and it seems easy. It also appears as though ECHO hasn‘t been tested much. Certainly, the more it is stressed the more variation that can be seen. There is, however, something to note in the last example in the list above and in the message from Conrad: once you know what you‘re looking at, you will find common themes that appear and reappear across a given implementation. These common themes are derived from common internals, and they can provide many clues about the inner workings of the implementation.
Another fine point highlighted by our quick look at the ECHO SMB is that TCP is designed to carry streams of data--not discrete packets. This can be seen in the results of the tests against Samba, in which multiple replies were contained in a single TCP packet. At the other extreme, several TCP packets are needed to transfer a single ECHO if it has a very large data payload. As a result, a single read operation may or may not return one and only one complete SMB message.
|
Here is a quick run-down on some of the basic essentials of SMB.
It‘s times like these that the earlier documentation comes in really handy.
The OPEN_ANDX SMB is used to gain access to a file for further processing (reading, writing, that sort of thing). The open file is identified by a FID (File ID). The FID, of course, is returned by a successful OPEN_ANDX call.
An oddity of the READ_ANDX is the MaxCountHigh field, which is only used if the CAP_LARGE_READX capability has been set. MaxCountHigh is an unsigned long (four bytes) that is supposed to hold the upper 16 bits (two bytes) of the unsigned short (two byte) MaxCount field. Two problems with this:
Play with it and see what happens. Should be interesting.
email
SMB_COM_SEEK is a useless SMB, since all of the read and write functions require a file relative address. It‘s not surprising it wasn‘t used; it would have been a waste of network bandwidth if it had been sent. - Chuck
|
See also the SNIA doc‘s comments regarding this SMB.
One problem with complex calls such as this is that the number of permutations gets to be very high, and it quickly becomes very difficult to test them all 56. There are various reports describing combinations of values that can cause a WindowsNT client or server to go BSOD (Blue Screen Of Death). Have fun with your testing.
There is yet another version of this SMB known as the NT_TRANSACT_CREATE, which is implemented as a sub-command of the SMB_COM_NT_TRANSACTION SMB. It is used to apply Extended Attributes (EAs) or Security Descriptors (SDs) to a file or directory.
Remember earlier when we talked about SMB messages as if we were dissecting some strange, new species of multi-legged critter? Well, we‘ve moved beyond Entomology, Invertebrate Zoology, Taxonomy, and such. We‘re now studying really complex stuff like Sociology, Psychology, and Numismatics, and we get to put the little critters into Skinner boxes and see how they react to various stimuli. It‘s important research, and there are all sorts of interesting things to discover.
Consider, for example, the SMB_COM_COPY command. It‘s supposed to allow you to copy a file from one location on the server to another location. That saves the client from having to read the data over the wire and write it back again. A good idea, eh? Unfortunately, no one seems to be able to get it to work--at least, not against Windows servers. There has been some limited success in the laboratory...
email
Chris, I found that smb_copy will in fact copy a file iff:
The resulting destination file will be named like the source file, minus the first character. It will NOT be named as specified in the dest parameter. Hence "smb_copy wanda -> fred" results in a second file "anda" in the root of the share. This works on the .NET server RC1 and Windows 2000 servers that
I‘ve tried. Hope it helps.
|
SMB is an old protocol, and it has gotten sloppy over the years. As you work your way through the SMB messages, implementing first the easy ones and then the more difficult ones, keep this thought in mind: It‘s not your fault.
Say it to yourself now: "It‘s not my fault."
Very good.
That will prevent you from getting frustrated and doubting your own skill. It‘s really not your fault.
We are going to blast through this, so you‘d better get your running shoes on.
The purpose of the Transaction SMBs is to carry specialized sub-protocols. Examples include the Remote Administration Protocol (RAP) and Microsoft‘s implementation of DCE/RPC (MS-RPC). There are other, more esoteric sets of calls as well. We will play with some of them when we get to the Browse Service.
Think of these sub-protocols as sets of function calls that are stretched across the network. As suggested in figure 2.18, a function call is made on the client side and the parameters and data are packed up and shoved across the network. The call is then completed at the remote end and the results (if any) are packed up and shoved back. In CIFS jargon, that‘s called a transaction.
Transactions are designed to be able to transfer more data than the limit imposed by the negotiated buffer size. They do so by fragmenting the payload. The protocol for sending large Protocol Data Units (PDUs) is described in a variety of documents, but here is a quick run-down:
There are three primary Transaction SMBs:
SMB_COM_TRANSACTION == 0x25 SMB_COM_TRANSACTION2 == 0x32 SMB_COM_NT_TRANSACT == 0xA0
Those are really long names, so folks on the various mailing lists tend to shorten them to "SMBtrans", "Trans2", and "NTtrans", respectively. Each of these also has a matching secondary:
SMB_COM_TRANSACTION_SECONDARY == 0x26 SMB_COM_TRANSACTION2_SECONDARY == 0x33 SMB_COM_NT_TRANSACT_SECONDARY == 0xA1
There is very little difference between these three transaction types, except that the NTtrans SMB has 32-bit fields where the other two have 16-bit fields. That means that NTtrans can handle a lot more data (that is, much larger transactions). Besides that, the real difference between these three is the set of functions that are traditionally carried over each.
The SNIA doc and the Leach/Naik CIFS draft provide examples of transactions that use Trans2 and NTtrans. Calls that use SMBtrans are documented elsewhere. Places to look include Luke‘s book (DCE/RPC over SMB), the Leach/Naik Browser and RAP Internet Drafts, and the X/Open documentation (particularly IPC Mechanisms for SMB). These (as you already know) are listed in the References section.
Just to simplify things even further, SMBtrans supports yet another layer of abstraction.
Mailslots and Named Pipes are used to access specific sets of remote functions. For example, the "LANMAN" pipe (which is identified as \PIPE\LANMAN) is always used for RAP calls.
Named Pipes are two-way inter-process communications channels. Once opened, they can be read from or written to as if they were files. In contrast, Mailslots are used for one-way, connectionless communications.
...and this is where something unexpected happens. Mailslot messages are sent using SMBs transported via the NBT Datagram Service. You‘ll have to see it to believe it, but that is easily arranged. All you need to do is grab a packet capture of port 138 on an active LAN, one with a few local servers that announce themselves to the working Network Neighborhood. If you don‘t like to wait, reboot something. A Windows/9x system that offers shares will do nicely.
This topic will be revisited in the Browse Service section. If you want to do some extra-curricular reading, the X/Open IPC Mechanisms for SMB document is recommended.
Promises were made, and promises should be kept.
Remember that closet full of concepts that burst open and spilled out all over the floor? Well, we have managed to clean up a good deal of the mess, but there are still a few things that we said we would put away--and we will. We can provide a brief explanation of each of these as we shove them back into the closet, just so you are not surprised when you stumble across them in the literature.
OpLocks are a caching mechanism.
A client may request an OpLock from an SMB server when it opens a file. If the server grants the request, then the client knows that it can safely cache large chunks of the file and not tell the server what it is doing with those cached chunks until it is finished. That saves a lot of network I/O round-trip time and is a very big boost to performance.
The problem, of course, is that other clients may want to access the same file at the same time. As long as everyone is just reading the file things are okay, but if even one client makes a change then all of the cached copies held by the other clients will be out of sync. That‘s why OpLock handling is a bit tricky.
There are two types of OpLocks that a client may request:
We came across these two when digging into the SMB_HEADER.FLAGS field way back in section 2.5.2. In olden times, the client would request an OpLock by setting the SMB_FLAGS_REQUEST_OPLOCK bit and, optionally, the SMB_FLAGS_REQUEST_BATCH_OPLOCK bit in the FLAGS field when opening a file. Now-a-days the FLAGS bits are (supposedly) ignored and fields within newer-style SMBs are used instead.
Anyway, an Exclusive OpLock can be granted if no other client or application is accessing the file at all. The client may then read, write, lock, and unlock the cached portions of the file without informing the server. As long as the client holds the Exclusive OpLock, it knows that it won‘t cause any conflicts. It‘s sort of like a kid sitting in a corner of the kitchen with a spoon and a big ol‘ carton of ice cream. As long as no one else is looking, that kid‘s world is just the spoon and the ice cream.
Batch OpLocks are similar to Exclusive OpLocks except that they cause the client to delay sending a CLOSE SMB to the server. This is done specifically to bypass a weirdity in the way that DOS handles batch files (batch files are the DOS equivalent of shell scripts). The problem is that DOS executes these scripts in the following way:
Yes, you‘ve read that correctly. The batch file is opened and closed for every line. It‘s ugly, but that‘s what DOS reportedly does and that‘s why there are Batch OpLocks.
To make Batch OpLocks effective, the client‘s SMB layer simply delays sending the CLOSE message. If the file is opened again, the CLOSE and OPEN simply cancel each other out and nothing needs to be sent over the wire at all. That also means that the client can keep hold of the cached copy of the batch file so that it doesn‘t have to re-read it for every line of the script.
There is also a third type of OpLock, known as a Level II OpLock, which the client cannot request but the server may grant. Level II OpLocks are, essentially, "read-only" OpLocks. They permit the client to cache data for reading only. All operations which would change the file or meta-data must still be sent to the server.
Level II OpLocks may be granted when the server cannot grant an Exclusive or Batch OpLock. They allow multiple clients to cache the same file at the same time so, unlike the other two, Level II OpLocks are not exclusive. As long as all of the clients are just reading their cached copies there is no chance of conflict. If one client makes a change, however, then all of the other clients need to be notified that their cached copies are no longer valid. That‘s called an OpLock Break.
It‘s called an "OpLock Break" because it involves breaking an existing OpLock. The more formal term is "revocation", but no one actually says that when they get together after hours to sit around, drink tea, and whine about CIFS.
OpLock Breaks are sent from the server to the client. This is unusual, because SMB request/response pairs are always initiated by the client. The OpLock Break is sent out-of-band by the server, which is against the rules...but this is CIFS we‘re talking about. Who needs rules?
The OpLock Break is sent in the form of a SMB_COM_LOCKING_ANDX message. The server may send this to reduce an Exclusive or Batch OpLock to a Level II OpLock, or to revoke an existing OpLock entirely. In either case, the client‘s immediate responsibility is to flush its cache to comply with the new OpLock status. If the client held an Exclusive or Batch OpLock, it must send all writes to the server and request any byte-range locks that it needs in order to continue processing. If the OpLock has been reduced to a Level II OpLock, the client may keep its local cache for read-only purposes.
Note that there is a big difference between OpLocks and the more traditional types of locks. With a traditional file or byte-range lock, the client is in charge once it has obtained a lock. It can maintain it as long as needed, relinquishing it only when it is finished using it. In contrast, an OpLock is like borrowing your neighbor‘s lawnmower. You have to give it back when your neighbor asks for it.
Support for OpLocks is optional on both the client and the server side, but implementing them provides a hefty performance boost. More information on OpLocks may be found in the Paul Leach/Dan Perry article CIFS: A Common Internet File System (listed in the References), as well as the usual sources.
The CIFS Distributed File System (DFS) is not nearly as fancy as it sounds. It is simply a way to collect separate shares into a single, virtual tree structure. It also has some limited ability to provide fileserver redundancy and load balancing.
The key feature of DFS is that it can create links from within a shared tree on one server to shares and directories on another, thus providing a single point of entry to a virtual SMB tree. From the user‘s perspective, the whole thing looks like a single share, even though the resources are scattered across separate SMB servers.
Clear as mud? Perhaps an illustration will help...
In figure 2.19, the client is shown attempting to access a file on server PETSERVER. Well, that‘s where the client thinks the file resides. On the server side, the name CORGIS in the DOGS directory is actually a link to another UNC pathname: \\DATADOG\CORGIS. Following that link leads us to a different share on a different server.
The server offering the DFS share (PETSERVER, in our example) does not act as a proxy for the client. That is, it won‘t follow the DFS links itself. Instead, the server sends an error code to the client indicating that there is some additional work to be done. The error code is either a DOS code of ERRSRV/ERRbadtype (0x02/0x0003), or an NT_STATUS code of STATUS_DFS_PATH_NOT_COVERED (0xC0000257).
The client‘s task, at this point, is to query the server to resolve the link. The client sends a TRANS2_GET_DFS_REFERRAL which is passed to the server via the Trans2 transaction mechanism, briefly described earlier. The client will use the information provided in the query response to create a new UNC path. It must then establish an SMB session with the new server. This whole mess is known as a "DFS referral".
It was mentioned above that DFS can provide a certain amount of redundancy. This is possible because the links in the DFS tree may contain multiple references. If the client fails to connect to the first server listed in the referral it can try the second, and so on. DFS can also provide a simple form of load balancing by reshuffling the order in which the list of links is presented each time it is queried. Of course, load balancing and redundancy are only workable if all of the linked copies are in sync.
A quick search of the web will turn up a lot of articles and papers that do a better job of describing the behavior of DFS that the blurb provided here. If you are planning on implementing DFS it is worthwhile to read up on the subject a bit, just to get a complete sense of how it is supposed to work from the user or network administrator‘s perspective. The SNIA doc provides enough information to get you started building a working client implementation. The server side is more complex because doing it right involves implementing a set of management functions as well.
These all present the same problem.
The CIFS protocol suite is designed, in its heart and soul, to work with DOS, OS/2, and Windows systems. As a result, the protocols that make up the CIFS suite have a tendency to reflect the behavior of those operating systems.
DOS, of course, is the oldest and simplest of the IBM/Microsoft family of PC OSes. The filesystem used with DOS is the venerable File Allocation Table (FAT) filesystem which, according to legend, was originally coded up by Bill Gates himself. The characteristics of the FAT filesystem should be familiar to anyone who has spent any time working with DOS. Consider, for example, the following FAT features:
It‘s a fairly spartan system.
There are improvements and extensions that have appeared over the years. The FAT32 filesystem, for example, is a modified version of FAT that uses disk space more efficiently and also supports much larger disk sizes than the original. There is also VFAT, which keeps track of both 8.3 format filenames and longer secondary filenames that may contain a wider variety of characters than the 8.3 format allows. VFAT long filenames are case-preserving (but not case sensitive) so, overall, VFAT allows a lot more creativity with file and directory names57.
Even with these extensions, the semantics of the FAT filesystem are not sufficient to meet the needs of more powerful OSes such as OS/2 and WindowsNT. These OSes have newer, more complex filesystems which they support in addition to FAT. Specifically, OS/2 has HPFS (High Performance File System) and WindowsNT & W2K can make use of NTFS (New Technology File System). These newer filesystems have lots and lots of features which, in turn, have to be supported by CIFS.
Problems arise when the server semantics (made available via CIFS) do not match those expected by the client. Consider, for instance, Samba running on a Unix system. Unix filesystems typically have these general characteristics:
Now consider a Windows application that requires the old 8.3 name format. (Such applications do exist. They make calls to older, 16-bit OS functions that assume 8.3 format.) Unlike VFAT, Unix filesystems do not normally keep track of both long and short names. That causes a problem, and Samba has to compensate by generating 8.3 format names on the fly. The process is called "Name Mangling".
There are other gotchas too. Indeed, name mangling is just the tip of the proverbial iceberg.
One solution that some CIFS vendors have been able to implement is to develop a whole new filesystem for their server platform, one that maintains all of the required attributes and maps between them as necessary. This is a pain, but it works in situations in which the server vendor has control over the deployment of their product. One such filesystem is Microsoft‘s NTFS, which can handle a very wide variety of attributes and map them to the semantics required by Apple Macintosh clients, Unix clients, DOS clients, OS/2 clients...
You‘ve got the basic idea. Let‘s run through some of the trouble spots to give you a sense of what you‘re up against.
The timestamps stored in the FAT filesystem may also be different from those used by other systems.
The term "Extended File Attributes" is also sometimes misused when discussing NTFS permissions. Permissions are different. Permissions are associated with Access Control Entries (ACEs), and ACEs are gathered together into Access Control Lists. There‘s a whole bigbunch of stuff there that could be explored--and would be, if this were a book about implementing NTFS.
EAs are a feature of HPFS and, therefore, are supported by NTFS. Basically, they are a separate data space associated with a file into which applications may store additional data or metadata specific to the application (things like author name or a file comment)58.
CIFS offers facilities to support all of these features and more. That‘s good news if you are writing client code, because you can pick and choose the sets of attributes you want to support. It‘s bad for server systems, which may need to offer various levels of compatibility in order to contend with client expectations.
If the Internet has proven anything it‘s that a very large number
of primates banging randomly on keyboards over a long enough period of
time can and will produce some amazingly useful software. On the
other hand, if you gather some of those primates together, place them
into cubicles, and train them to perform like circus animals...
Let‘s just get rid of these
horrible protocols.
-- Andrew Tridgell,
Samba Team Leader
...well, we‘ve just put a lot of effort into cleaning up the mess that was made in those cubicles. A shame, really. It was a nice little protocol when it started out.
Although the SNIA gave it their best shot, there are currently no industry committees or standards groups writing bona fide specifications to be reviewed and voted on, and no standard test suites to verify conformity. That‘s not to say that specifications and test suites don‘t exist--quite the contrary. The problem is that they have no teeth. With no real standards and no real enforcement, the only measure of correctness for an SMB implementation is whether or not it works most of the time. Since most of the clients out there are Windows clients, the formula simplifies down to whether or not an implementation works with Windows. An additional problem is that SMB itself is not enough for true interoperability with Windows systems--particularly if you want to write a workable server.
In San Jose, California, there is a mansion known as the Winchester
Mystery House. It started out as a simple farmhouse, but it was
expanded over a period of thirty-eight years by a millionaire widow
with an obsessive compulsion to keep on adding new rooms. It has
stairways that rise directly into the ceiling, windows in the floor,
doors that open to solid walls...and that‘s just for starters. The
building covers four and a half acres and has an estimated 160 rooms.
CIFS is like that.
The original SMB protocol was simple and well suited to its environment. Over the years, however, it has been greatly expanded. Several sub-protocols have been added on as well. These subprotocols (which include such things as the Extended Security protocols, RAP, MS-RPC, etc.) are implemented by Windows so, if you want to build something truly compatible, SMB alone just isn‘t enough.
...but don‘t go away feeling that it is all just a hopeless mess. It is really a question of how much effort you are willing to put into solving the problems you will encounter. Take it one step at a time, because the individual pieces are much less daunting than the whole.
1 The X/Open SMB documentation is out of print, but electronic copies are now available on-line (free registration required). See: http://www.opengroup.org/products/publications/catalog/, and look for documents #C195 and #C209.
2 I must rely on anecdotal evidence to support this claim. Due to the licensing restrictions, I have not read these documents, which were released in March of 2002.
3 ...and Tactical Officer. He‘s the one with the prosthetic forehead.
4 I live in Minnesota, where it most definitely snows in winter. I share my home with a Pembrokeshire Welsh Corgi and a Golden Retriever, so the springtime scenario described above is vividly real and meaningful to me. Some of my Australian Samba Team friends have suggested that people in other parts of the world may find it less familiar. Use your imagination.
5 There are some old, archived conversations on Microsoft‘s CIFS mailing list which suggest that some implementors were--and possibly still are--only allowing for a 16 bit LENGTH field in the NBT SESSION MESSAGE.
6 Steve French says that OS/2 may have been the first OS to fully support the UNC scheme.
7 The distinction between a URL and a URI is subtle, and confuses me to no end. Fortunately, it is not something we need to worry about.
8 The "host" field is not really a field, but the name of a non-terminal in the BNF grammar presented in RFC 2396. That grammar has been amended to support IP version 6 (IPv6) addressing in RFC 2732. The SMB URL format adds support for the use of NetBIOS names and Scope IDs, so it is a further extension of the syntax.
9 Additional source code is available at http://ubiqx.org/libcifs/.
10 See RFC 2732 for information on the use of IPv6 addresses in URLs.
11 Samba‘s nmbd daemon spawns a separate process to handle DNS queries, just to get around this very problem.
12 When working with the NET USE command, it is important to remember to close the connection to the server using the /d command-line option. Type NET HELP at the DOS prompt for more information.
13 The original was much more detailed and interesting. It had to be edited so that it would fit on the page, and because all those details can be distracting.
15 The first place to look is Microsoft‘s CIFS FTP site: ftp://ftp.microsoft.com/developr/drg/CIFS/. The COREP.TXT file is formatted for printing on an old-style dot-matrix printer, which makes it look a little goofy in places (eg. bold font is accomplished by typing a character, then backspacing, then re-typing the same character). The same content is available in an alternate format in the file SMB-CORE.PS. See the References section.
16 Ethereal version 0.9.3 will report the name of the last AndX Command in the chain, rather than the first. This was fixed somewhere between 0.9.3 and 0.9.6. The trick with Ethereal is to update early and often.
17 We are dealing with a vague definition here. According to the SNIA doc, the SESSION SETUP is meant to "set up" the session created by the NEGOTIATE PROTOCOL, which also makes some sort of sense. Thing is, there may be multiple SESSION SETUP exchanges following the NEGOTIATE PROTOCOL, meaning multiple SMB user sessions per NBT or naked TCP transport session. The waters are muddy.
18 This is exactly what jCIFS does (up through release 0.6.6 and the 0.7.0beta series). There has been a small amount of discussion about supporting the NT_STATUS codes, but it‘s not clear whether there is any need to change.
19 After all that work... Sometime around August of 2002, Microsoft posted a bit of documentation listing the DOS error codes that they have defined. Not all are used in CIFS, but it‘s a nice list to have. In addition, they have documented an NTDLL.DLL function that converts DOS error codes into NT_STATUS codes. [Thanks to Jeremy for finding these.]
20 The English language is Copyright (C) 1597 by William Shakespeare & Co., used by permission, all rights deserved.
21 One of the reasons that the jCIFS project was started is that Java has built-in Unicode support, which solves a lot of problems. That, plus the native threading model and a few other features, made an SMB implementation in Java very tempting. Support for Unicode in a CIFS implementation is not really optional any more except, perhaps, in the simplest of client systems. Unfortunately, Unicode is way beyond the scope of this book. See the References section for some web links to get you started with Unicode.
22 ...or was, last time I checked. Once again, that URL is: ftp://ftp.microsoft.com/developr/drg/CIFS/. See the References section for links to specific documents.
23 There may be a further problem with raw mode. Microsoft has made some obtuse references to obscure patents which may or may not be related to READ RAW and WRITE RAW. The patents in question have been around for quite some time, and were not mentioned in any of the SMB/CIFS documentation that Microsoft released up until March of 2002. Still, the best bet is to avoid ReadRAW and WriteRAW (since they are not particularly useful anyway) and/or check with a patent lawyer. The Samba Team released a statement regarding this issue. See: http://us1.samba.org/samba/ms_license.html.
24 There is no name for 10-7seconds. Other fractions of seconds have names with prefixes like deci, centi, milli, micro, nano, pico, even zepto, but there is no prefix that applies to 10-7. In honor of the fact that this rare measure of time is used in the CIFS protocol suite, I propose that it be called a bozosecond.
25 January 1, 1970, 00:00:00.0 UTC, known as "the Epoch", is sometimes excused as being the approximate birthdate of Unix.
26 This is probably because Saint Paul is at the center of the universe. The biomagnetic center of the universe used to be located across the river in Minneapolis until they closed it down. It was a little out of whack in the same way that the magnetic poles are not quite where they should be. The magnetic north pole, for instance, is on or near an island in northern Canada instead of at the center of the Arctic Ocean where it belongs.
27 A lot of time was wasted trying to figure out which configuration options would change the behavior. The results were inconclusive. At first it seemed as though the DomainName was included if the Windows98 system running in User Level security mode, and passing logins through to an NT Domain Controller. Further testing, however, showed that this was not a hard-and-fast rule. It should also be mentioned that if the systems are running naked transport there may not be an NT Domain or Workgroup name. SMB can be mightily inconsistent--but not all the time.
28 To be pedantic, the correct terms are "marshalling" and "unmarshalling". "Marshalling" means collecting data in system-internal format and re-organizing it into a linear format for transport to another system (virtual, physical, or otherwise). "Unmarshalling", of course, is the reverse process. These terms are commonly associated with Remote Procedure Call (RPC) protocols, but some have argued (not unreasonably) that SMB is a simple form of RPC.
29 If you enjoy digging into odd details, this is a great one. See the SMB-LM1X.PS file, also known as Microsoft Networks/SMB File Sharing Protocol Extensions, Version 2.0, Document Version 3.3. In particular, see the definition of a VC on page 2, and the description of the "Virtual Circuit Environment" in section 4.a on page 10.
30 See Microsoft Knowledge Base article #301673 for more information.
31 There are a few small notes scattered about the SNIA doc that suggest that the prescribed compression algorithm is something called LZNT. I haven‘t been able to find a definitive reference that explains what LZNT is, but it appears from the name that it is a form of Lempel-Ziv compression.
32 It was, in fact, a lot of work for the Samba Team. Those involved did a tremendous job, and they deserve several rounds of applause. Things were much easier for jCIFS because Java natively supports Unicode.
33 Information on RAP calls is scattered among several sources, including the archives of Microsoft‘s CIFS mailing list. The SNIA doc has enough to get you started with the basics of RAP, but see also the file cifsrap2.txt which can be found on Microsoft‘s aforementioned FTP site.
34 Luke Kenneth Casson Leighton‘s book DCE/RPC over SMB: Samba and Windows NT Domain Internals is an essential reference for CIFS developers who need to know more about MS-RPC.
35 I vaguely remember a conversation with Tridge in which he indicated that there was an obscure exception to the misalignment of the Data block. I‘m not sure which SMB, or which dialect, but if I recall correctly there‘s one SMB that has an extra byte just before the ByteCount field. Keep your eyes open.
36 In addition to "something you have " and "something you know " there is another class of access token that is sometimes described as "something you are". This latter class, also known as "biometrics", includes such things as your fingerprints, your DNA pattern, your brainwaves, and your karmic aura. Some folks have argued that these features are simply "something you have" that is a little harder (or more painful) to steal. There was great hope that biometrics would offer improvements over the other authentication tokens, but it seems that they may be just as easy to crack. For example, a group of researchers in Japan was able to fool fingerprint scanners using fake fingertips created from gelatin and other common ingredients.
37 ...sort of. Support for inclusion of a password within a URL is considered very dangerous. The recommendation from the authors of RFC 2396 is that new applications should not recognize the password field and that the application should instead prompt for both the username and password.
38 Yet again we seek the wisdom of the RFCs. See Appendix A of RFC 2396 for the full generic syntax of URLs, and RFC 2732 for the IPv6 update.
39 See the discussion of the password level parameter in Samba‘s smb.conf(5) documentation for more information about these problems.
40 I don‘t know whether a Windows server can be configured to support Unicode plaintext passwords. To test against Samba, however, you need to use Samba version 3.0 or above. On the client side, Microsoft has a Knowledge Base article--and a patch--that addresses some of the message formatting problems in Windows2000. See: Microsoft Knowledge Base Article #257292. Thanks to Nir Soffer for finding this article.
41 If you are interested in the workings of DES, Bruce Schneier‘s Applied Cryptography, Second Edition provides a very complete discussion. See the References section.
42 ...without whom the Authentication section would never have been written.
43 Both the the X/Open doc and the expired Leach/Naik draft state that the padding character is a space, not a nul. They are incorrect. It really is a nul.
44 The magic string was considered secret, and was not listed in the Leach/Naik draft. The story of Tridge and Jeremy‘s (pre-DMCA) successful effort to reverse-engineer this value is quite entertaining.
45 A "cracker", not a "hacker". The former is someone who cracks passwords or authentication schemes with the goal of cracking into a system (naughty). The latter is one who studies and fiddles with software and systems to see how they work and, possibly, to make them work better (nice). The popular media has mangled the distinction. Don‘t make the same mistake. If you are reading this book, you most likely are a hacker (and that‘s good).
46 Jeremy Allison proved it could be done with a little tool called PWdump. Mudge and other folks at the L0pht then expanded on the idea and built the now semi-infamous L0phtCrack tool. In July of 1997, Mudge posted a long and detailed description of the decomposition of LM challenge/response, a copy of which can be found at: http://www.insecure.org/sploits/l0phtcrack.lanman.problems.html. For a curious counterpoint, see Microsoft Knowledge Base Article #147706.
47 Andrew Bartlett prefers to call this the "NT Hash", stating that the NT Hash is passed through the LM response algorithm to produce the NTLM (NT+LM) response.
48 MD4 is explained in RFC 1320 and MD5 is in RFC 1321. HMAC in general, and HMAC-MD5 in particular, is written up in RFC 2104. ...an embarrassment of riches. As usual with this sort of thing, a deeper understanding can be gained by reading about it in Bruce Schneier‘s Applied Cryptography, Second Edition. See the References section.
49 The lab in the basement is somewhat limited which, in turn, limits my ability to do rigorous testing of esoteric CIFS nuances. You should probably verify these results yourself. Andrew Bartlett (him again!) turned up an interesting quirk regarding the NTLMv2 Response calculation when authenticating against a stand-alone server. It seems that the NT Domain name is left blank in the v2hash calculation. That is: destination = "";
50 Luke Kenneth Casson Leighton‘s book DCE/RPC over SMB: Samba and Windows NT Domain Internals gives an outline of the structure of the data blob used in NTLMv2 Response creation. Using Luke‘s book as a starting point, the details presented above were worked out during a late-night IRC session. My thanks to Andrew Bartlett, Richard Sharpe, and Vance Lankhaar for their patience, commitment, and sudden flashes of insight. Thanks also to Luke Howard for later clarifying some of the finer points.
51 A quick web search for "LMCompatibility" will turn up a lot of references, Microsoft Knowledge Base Article #147706 among them.
52 ...so that if something goes wrong you can blame them, and not me.
53 It might be worth doing some testing if you really want to use DOS codes in your implementation, but also want Extended Security. It may be possible to use the NT_STATUS codes for this exchange only, or you might try interpreting any unrecognized DOS error code as if it were STATUS_MORE_PROCESSING_REQUIRED.
54 The latter name--though decidedly less Freudian--is somewhat gender-biased.
55 Jean-Baptiste Marchand has done some digging and reports that starting with Windows2000 the SMB redirector (rdr) has been redesigned, which may impact which registry keys are fiddled. The preferred way to configure SMB MAC signing in Windows 2000 is to use the Local Security Settings/Group Policy Management Console (whatever that is). Basically, this means that Windows2000 and WindowsXP have MAC signing settings comparable to those in WindowsNT, but they are handled in a different way.
56 I vaguely remember a presentation given by David Korn, author of the Korn Shell (ksh), regarding AT&T‘s UWIN project. At the end of the presentation there was some discussion regarding the differences between standard Posix APIs and Win32 APIs. It was pointed out that there were hundreds or possibly thousands of permutations of parameter values that could be passed to the Posix open() function. The permutations for the equivalent Win32 function, it was reported, was on the order of millions. How the heck do you test all those possibilities?
57 Digging through the documentation, it appears that the FAT family consists of FAT12, FAT16, FAT32, and VFAT. There is documentation on the web that provides implementation details, if you are so inclined.
58 NTFS is a complex filesystem based on some simple concepts. One such is that each "file" is actually a set of "attributes" (records). Many of these attributes are pre-defined to contain such things as the short name, the long name, file creation and access times, etc. The actual content of the file is stored in a specific, pre-defined "stream", where a stream is a particular kind of attribute. NTFS supports OS/2-style Extended Attributes in another type of NTFS attribute... and it just gets more confusing from there. There is a lot of documentation on the web about the workings of NTFS, and there is a project aimed at implementing NTFS for Linux.
Copyright © 1999-2004 Christopher R. Hertel All rights reserved. $Revision: 1.289 $ |
标签:birt hot includes display vendor art form weight label
原文地址:http://www.cnblogs.com/merlindu/p/7641424.html