I was starting to benchmark how much performance we'd lose by using jCIFS instead of requiring our users to map drives manually themselves, but then I found that I couldn't even pass the first basic test of reading data from a file using jCIFS.
I boiled down my issue to a test problem:
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import com.google.common.io.ByteStreams;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbFile;
public class TestCifs
{
public static void main(String[] args) throws Exception
{
OutputStream output = ByteStreams.nullOutputStream();
String domain = ""; // have tried the actual name too
String user = "<your_user_here>";
String pass = "<your_pass_here>";
String relativePath = "path/to/100MiB";
// Read via UNC path (Windows doing the SMB work.)
Path uncFile = Paths.get("\\\\192.168.1.66\\Shared", relativePath);
Files.copy(uncFile, output);
// Read via jCIFS
//TODO: Figure out if this is a real URL in which case we'd have to escape stuff.
SmbFile smbFile = new SmbFile("smb://192.168.1.66/Shared/" + relativePath,
new NtlmPasswordAuthentication(domain, user, pass));
ByteStreams.copy(smbFile.getInputStream(), output);
}
}
If I run this, it runs past the direct copy and then the jCIFS copy fails with:
Exception in thread "main" jcifs.smb.SmbAuthException: Logon failure: unknown user name or bad password.
at jcifs.smb.SmbTransport.checkStatus(SmbTransport.java:546)
at jcifs.smb.SmbTransport.send(SmbTransport.java:663)
at jcifs.smb.SmbSession.sessionSetup(SmbSession.java:390)
at jcifs.smb.SmbSession.send(SmbSession.java:218)
at jcifs.smb.SmbTree.treeConnect(SmbTree.java:176)
at jcifs.smb.SmbFile.doConnect(SmbFile.java:911)
at jcifs.smb.SmbFile.connect(SmbFile.java:954)
at jcifs.smb.SmbFile.connect0(SmbFile.java:880)
at jcifs.smb.SmbFile.open0(SmbFile.java:972)
at jcifs.smb.SmbFile.open(SmbFile.java:1006)
at jcifs.smb.SmbFileInputStream.<init>(SmbFileInputStream.java:73)
at jcifs.smb.SmbFileInputStream.<init>(SmbFileInputStream.java:65)
at jcifs.smb.SmbFile.getInputStream(SmbFile.java:2844)
at TestCifs.main(TestCifs.java:33)
The username and password I provided in user/pass are the same ones I used when authenticating with the server to mount its directory via Windows. I also tried using the name of the server as the domain to no effect. I'm really at a loss with this one and am about to look at something like Wireshark to figure out what is actually going wrong but maybe I'm doing something obviously wrong and someone here can point that out.
This is probably the relevant bit from jCIFS' own diagnostics for the failed login:
treeConnect: unc=\\192.168.1.66\SHARED,service=?????
sessionSetup: accountName=TESTER,primaryDomain=
NtlmContext[auth=TESTER,ntlmsspFlags=0x20080004,workstation=JCIFS1_183_42,isEstablished=false,state=1,serverChallenge=null,signingKey=null]
Type1Message[suppliedDomain=,suppliedWorkstation=JCIFS1_183_42,flags=0x20080205]
00000: 4E 54 4C 4D 53 53 50 00 01 00 00 00 05 22 08 20 |NTLMSSP......". |
00010: 00 00 00 00 00 00 00 00 0D 00 0D 00 20 00 00 00 |............ ...|
00020: 4A 43 49 46 53 31 5F 31 38 33 5F 34 32 |JCIFS1_183_42 |
SmbComSessionSetupAndX[command=SMB_COM_SESSION_SETUP_ANDX,received=false,errorCode=0,flags=0x0018,flags2=0xC803,signSeq=0,tid=0,pid=21886,uid=0,mid=2,wordCount=12,byteCount=77,andxCommand=0xFF,andxOffset=0,snd_buf_size=16644,maxMpxCount=10,VC_NUMBER=1,sessionKey=0,lmHash.length=0,ntHash.length=0,capabilities=-2147483564,accountName=null,primaryDomain=null,NATIVE_OS=Windows 8,NATIVE_LANMAN=jCIFS]
00000: FF 53 4D 42 73 00 00 00 00 18 03 C8 00 00 00 00 |ÿSMBs......È....|
00010: 00 00 00 00 00 00 00 00 00 00 7E 55 00 00 02 00 |..........~U....|
00020: 0C FF 00 DE DE 04 41 0A 00 01 00 00 00 00 00 2D |.ÿ.ÞÞ.A........-|
00030: 00 00 00 00 00 54 00 00 80 4D 00 4E 54 4C 4D 53 |.....T...M.NTLMS|
00040: 53 50 00 01 00 00 00 05 22 08 20 00 00 00 00 00 |SP......". .....|
00050: 00 00 00 0D 00 0D 00 20 00 00 00 4A 43 49 46 53 |....... ...JCIFS|
00060: 31 5F 31 38 33 5F 34 32 57 00 69 00 6E 00 64 00 |1_183_42W.i.n.d.|
00070: 6F 00 77 00 73 00 20 00 38 00 00 00 6A 00 43 00 |o.w.s. .8...j.C.|
00080: 49 00 46 00 53 00 00 00 |I.F.S... |
New data read: Transport1[0.0.0.0<00>/192.168.1.66:445]
00000: FF 53 4D 42 73 6D 00 00 C0 80 41 C8 00 00 00 00 |ÿSMBsm..À.AÈ....|
00010: 00 00 00 00 00 00 00 00 00 00 7E 55 00 00 02 00 |..........~U....|
SmbComSessionSetupAndXResponse[command=SMB_COM_SESSION_SETUP_ANDX,received=false,errorCode=Logon failure: unknown user name or bad password.,flags=0x0080,flags2=0xC841,signSeq=0,tid=0,pid=21886,uid=0,mid=2,wordCount=0,byteCount=0,andxCommand=0xFF,andxOffset=0,isLoggedInAsGuest=false,nativeOs=,nativeLanMan=,primaryDomain=]
00000: FF 53 4D 42 73 6D 00 00 C0 80 41 C8 00 00 00 00 |ÿSMBsm..À.AÈ....|
00010: 00 00 00 00 00 00 00 00 00 00 7E 55 00 00 02 00 |..........~U....|
00020: 00 00 00 |... |
It seems like it isn't putting the username into the message at all, so maybe I'm looking at a bug, but if it's really doing this, surely it would have been noticed by someone else before...
Workarounds already attempted:
- Putting authentication info into the URL instead of the auth object.
- Calling SmbSession.logon before trying to access the SmbFile. In this situation, you just get the error from logon instead.
- Setting a default authenticator using NtlmAuthenticator.setDefault(). In this situation, you see it call the authenticator to get the auth details but then seemingly throw away the info and make the request with no username.