Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using gss-ntlmssp from Java #94

Open
nir2 opened this issue May 23, 2023 · 3 comments
Open

Using gss-ntlmssp from Java #94

nir2 opened this issue May 23, 2023 · 3 comments

Comments

@nir2
Copy link

nir2 commented May 23, 2023

I have a problem, maybe you can help me.
I am using the gss-ntlmssp GSS plugin from Java on Linux by setting the system property "-Dsun.security.jgss.native=true".

I am testing HTTP proxy authorization with Negotiate (Kerberos), NTLM and Basic. I have set up a test system with multiple docker containers, one with a Samba DC, one with a Squid proxy and one development container with which I test the authorization.

The test setup seems to be ok, I can succesfully do:

[CORP\user1@develop1 proxytest]$ curl -v --proxy-negotiate -u: -x squidserver.corp.example.com:3128 https://<destination>
[CORP\user1@develop1 proxytest]$ curl -v --proxy-ntlm --proxy-user CORP\\user1 -u: -x squidserver.corp.example.com:3128 https://<destination>

Though I must admit challenge/response authorization with wbinfo is only successful if I run it as root, not as CORP\user1 (maybe that's the problem?):

[CORP\user1@develop1 proxytest]$ wbinfo -a CORP\\user1
Enter CORP\user1's password:
plaintext password authentication succeeded
Enter CORP\user1's password:
challenge/response password authentication failed
Could not authenticate user CORP\user1 with challenge/response

In my Java program I create a GSS context with the NTLMSSP Oid and create a token with:

		Oid ntlmOid = new Oid("1.3.6.1.4.1.311.2.2.10");
		Oid oid = ntlmOid;
		String service = "[email protected]";
		GSSName serverName = manager.createName(service, GSSName.NT_HOSTBASED_SERVICE);
		GSSContext context = manager.createContext(serverName,
				oid,
				null,
				GSSContext.DEFAULT_LIFETIME);

This is successful, and I can create a token which I send Base64 encoded to Squid:

		byte[] token = new byte[0];
		token = context.initSecContext(token, 0, token.length);
		String encodedToken = Base64.getEncoder().encodeToString(token);

The Proxy-authenticate line I send to Squid looks like this:

Proxy-Authorization: NTLM TlRMTVNTUAABAAAAN4II4gAAAAAAAAAAAAAAAAAAAAAGAgAAAAAADw==

Then I get back the following token from Squid:

Proxy-Authenticate: NTLM TlRMTVNTUAACAAAACAAIADgAAAA1goniYTUVUwP3TmIAAAAAAAAAAJYAlgBAAAAABgEAAAAAAA9DAE8AUgBQAAIACABDAE8AUgBQAAEAFgBTAFEAVQBJAEQAUwBFAFIAVgBFAFIABAAgAGMAbwByAHAALgBlAHgAYQBtAHAAbABlAC4AYwBvAG0AAwA4AHMAcQB1AGkAZABzAGUAcgB2AGUAcgAuAGMAbwByAHAALgBlAHgAYQBtAHAAbABlAC4AYwBvAG0ABwAIAHAs/DxdjdkBAAAAAA==

But if I Base64 decode this token and feed it into the GSSContext I get an exception:

...
byte[] tokenBin = Base64.getDecoder().decode(token64);
byte[] token2 = context.initSecContext(tokenBin, 0, tokenBin.length);

Exception:

Exception in thread "main" GSSException: Defective token detected (Mechanism level: Failed to decode data)
        at java.security.jgss/sun.security.jgss.wrapper.GSSLibStub.initContext(Native Method)
        at java.security.jgss/sun.security.jgss.wrapper.NativeGSSContext.initSecContext(NativeGSSContext.java:277)
        at java.security.jgss/sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:266)
        at java.security.jgss/sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:196)
        at proxytest.HttpClient.main(HttpClient.java:136)

The gss-ntlmssp Log looks like this:

[CORP\user1@develop1 proxytest]$ cat gssntlm.log
[1684837520] ALLOK: gssntlm_import_name_by_mech() @ src/gss_names.c:355 [0:0]
[1684837520] ALLOK: gssntlm_acquire_cred_from() @ src/gss_creds.c:501 [0:0]
[1684837520] ALLOK: gssntlm_import_name_by_mech() @ src/gss_names.c:355 [0:0]
[1684837520] ALLOK: gssntlm_init_sec_context() @ src/gss_sec_ctx.c:246 [1:0]
[1684837520] ALLOK: gssntlm_release_name() @ src/gss_names.c:633 [0:0]
[1684837520] ALLOK: gssntlm_release_name() @ src/gss_names.c:633 [0:0]
[1684837520] ALLOK: gssntlm_import_name_by_mech() @ src/gss_names.c:355 [0:0]
[1684837520] ALLOK: gssntlm_acquire_cred_from() @ src/gss_creds.c:501 [0:0]
[1684837520] ERROR: gssntlm_init_sec_context() @ src/gss_sec_ctx.c:293 [589824:1314127873]
[1684837520] ALLOK: gssntlm_delete_sec_context() @ src/gss_sec_ctx.c:483 [0:0]
[1684837520] ALLOK: gssntlm_release_name() @ src/gss_names.c:633 [0:0]
[1684837520] ALLOK: gssntlm_release_name() @ src/gss_names.c:633 [0:0]
[1684837520] ALLOK: gssntlm_display_status() @ src/gss_err.c:139 [0:0]

Any idea what I am doing wrong ? Isn't base64 decoding the token I get and feeding it into gss-ntlmssp enough, do I have to do something else ? Btw., if I use the Kerberos Oid, and do a Proxy-Authenticate: Negotiate instead of NTLM, my test program works, so I think principally the Java GSSAPI is working.

@simo5
Copy link
Collaborator

simo5 commented May 23, 2023

sounds to me the Java wrapper is doing something wrong.

For example it is very od to see gssntlm_acquire_cred_from(0 called twice, are you sure your code is performing a continuation and not trying to start a new ISC operation ?

Not knowing how the Java bindings works, I can't tell.

Decoding the base64 you posted shows a valid NTLMSSP challenge token.

As for access to windbind, asking it to decode tokens is indeed a privileged operation. You do not have to be root, but your user need to be given permission to access the privileged winbind pipe.

@nir2
Copy link
Author

nir2 commented May 25, 2023

Thanks, good to know the base64 encoded challenge is valid.

It seems, the Java GSS wrapper isn't the problem. I wrote a small test program in C/C++ based on the example in RFC 7546, and get exactly the same error (in src/gss_sec_ctx.c:277). I must be doing something fundamentally wrong, perhaps linking with the wrong libraries. The duplicate gssntlm_acquire_cred_from is caused, because I pass GSS_C_NO_CREDENTIAL into the gss_init_sec_context; if I pass a credential in, I get it only once but the error in gss_sec_ctx.c:277 persists.

I attached the source of my small test, maybe (if you have a moment of time), you at a first glance see what I am doing wrong or missing. I was developing and testing on Fedora 38.

proxytest.zip

Ps: Just saw a bug in my code, it should be "memcpy((char *)(tokenResult->value), token.data(), token.size());" instead of "strcpy(...",
This makes a change, the decoded token in the gss_buffer_desc looks like this now:

x tokenResult->value tokenResult->value+214
0x004689b0: 4e 54 4c 4d 53 53 50 00 02 00 00 00 08 00 08 00  NTLMSSP.........
0x004689c0: 38 00 00 00 35 82 89 e2 14 e6 aa a6 dd 66 7d 95  8...5........f}.
0x004689d0: 00 00 00 00 00 00 00 00 96 00 96 00 40 00 00 00  ............@...
0x004689e0: 06 01 00 00 00 00 00 0f 43 00 4f 00 52 00 50 00  ........C.O.R.P.
0x004689f0: 02 00 08 00 43 00 4f 00 52 00 50 00 01 00 16 00  ....C.O.R.P.....
0x00468a00: 53 00 51 00 55 00 49 00 44 00 53 00 45 00 52 00  S.Q.U.I.D.S.E.R.
0x00468a10: 56 00 45 00 52 00 04 00 00 63 00 6f 00 72 00 70  V.E.R....c.o.r.p
0x00468a20: 00 2e 00 65 00 78 00 61 00 6d 00 70 00 6c 00 65  ...e.x.a.m.p.l.e
0x00468a30: 00 2e 00 63 00 6f 00 6d 00 03 00 38 00 73 00 71  ...c.o.m...8.s.q
0x00468a40: 00 75 00 69 00 64 00 73 00 65 00 72 00 76 00 65  .u.i.d.s.e.r.v.e
0x00468a50: 00 72 00 2e 00 63 00 6f 00 72 00 70 00 2e 00 65  .r...c.o.r.p...e
0x00468a60: 00 78 00 61 00 6d 00 70 00 6c 00 65 00 2e 00 63  .x.a.m.p.l.e...c
0x00468a70: 00 6f 00 6d 00 07 00 08 00 5c c7 50 38 ee 8e d9  .o.m.....\.P8...
0x00468a80: 01 00 00 00 00 00

Now I get an error in line src/gss_sec_ctx.c:293 instead of src/gss_sec_ctx.c:277, like in Java.

@simo5
Copy link
Collaborator

simo5 commented May 25, 2023

The NTLMSSP protol is a multistep challenge response protocol. It mean you need to preserve the security context between calls to gss_init_sec_context and you need to provide input credentials.

The credentials either store internally the password used to authenticate the user, or provide the linkage to talk to winbindd.

Without credentials you can't complete authentication.
Was your gss-ntlmssp built with winbindd support?

That said, looking again at the error log your code seem to be failing in ntlm_decode_chal_msg(), if you could GDB the binary reproducer and step through that function and tell me how it fails, I can probably give you more hints on what is wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants