inejge / ldap3

A pure-Rust LDAP library using the Tokio stack
Apache License 2.0
226 stars 39 forks source link

buffer too large for GSSAPI #97

Closed troelsarvin closed 1 year ago

troelsarvin commented 1 year ago

Hello,

I'm having trouble using ldap3 with a FreeIPA installation. The same code which works well agains at Samba DC LDAP cannot connect to a FreeIPA LDAP. This is the error message I get:

2023-02-14T15:55:01.862764Z  WARN ldap3::conn: socket send error: buffer too large for GSSAPI: 118 > 0    
2023-02-14T15:55:01.863363Z  WARN ldap3::sync: LDAP connection error: I/O error: buffer too large for GSSAPI: 118 > 0    
Error: result recv error: channel closed

Caused by:
    channel closed

When I run the "ldapsearch" utility from the same server (also using Kerberos), I experience no issues.

This is what the code looks like:

pub fn get_ldap_conn(cfg: &mut Cfg) -> Result<LdapConn> {
    let ldap_url = format!("ldap://{}", cfg.ldap.hostname);
    debug!("ldap_url: {:?}", &ldap_url);
    let mut ldap = LdapConn::new(&ldap_url)?;
    let _ = ldap.sasl_gssapi_bind(&cfg.ldap.hostname)?.success();
...

I'm going to attach output from strace, as well as a tcpdump, both scrambled with regards to IP address etc.

What could be wrong?

troelsarvin commented 1 year ago

Interestingly, if I comment out lines 190 through 199 in src/protocol.rs, then things work for me:

/*
        if out_buf.len() > sasl_send_max as usize {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                format!(
                    "buffer too large for GSSAPI: {} > {}",
                    out_buf.len(),
                    sasl_send_max
                ),
            ));
        }
*/

It's hardly a solution, but maybe a hint of something?

inejge commented 1 year ago

Note: I can't look into this before the middle of the next week.

troelsarvin commented 1 year ago

Note: If the cross-krb5 crate turns out being a problematic dependency, then may the rsasl crate can be used instead? It seems that crate was very recently released in version 2. It doesn't explicitly state whether nor not it is platform (in)dependent, though.

inejge commented 1 year ago

Let's first see if the bind actually succeeded. The sasl_gssapi_bind() call should look like:

let _ = ldap.sasl_gssapi_bind(&cfg.ldap.hostname)?.success()?;

(There is a ? at the end.) Then, patch src/ldap.rs, recompile, and tell me what the line with send_max in the output says:

--- a/src/ldap.rs
+++ b/src/ldap.rs
@@ -362,6 +362,7 @@ impl Ldap {
                 buf[0] = 0;
                 let send_max_size =
                     u32::from_be_bytes((&buf[..]).try_into().expect("send max size"));
+                eprintln!("send_max: {}", send_max_size);
                 let mut sasl_param = self.sasl_param.write().expect("sasl param");
                 sasl_param.0 = true;
                 sasl_param.1 = send_max_size;

Some background: I don't have a FreeIPA installation handy, so I used the demo sandbox to check connectivity. The FreeIPA Kerberos server is very particular about the case in the principal name; I had to kinit with admin@DEMO1.FREEIPA.ORG, since the lowercased domain name didn't work. Otherwise, I could do a GSSAPI bind and issue a Search without problems.

troelsarvin commented 1 year ago

Changing the "let _ = ..." line to include the final question mark does not change the picture. I added the eprintln line to ldap.rs, and it now results in the following:

send_max: 0
2023-02-21T08:51:16.202820Z  WARN ldap3::conn: socket send error: buffer too large for GSSAPI: 118 > 0    
2023-02-21T08:51:16.203053Z  WARN ldap3::sync: LDAP connection error: I/O error: buffer too large for GSSAPI: 118 > 0    
Error: result recv error: channel closed

Caused by:
    channel closed

I recognize the need to for case sensitive "kinit". I kinit like this:

kinit mylogin@DOMAIN.LOCAL

and it works, while if I kinit with the lowercase version of DOMAIN.LOCAL, then kinit fails.

Note that after a successful kinit, I can work with the "ldapsearch" tool without entering a password.

inejge commented 1 year ago

send_max: 0

Uhh, that's really wacky. Delete the eprintln!() and try the following patch:

--- a/src/ldap.rs
+++ b/src/ldap.rs
@@ -360,8 +360,12 @@ impl Ldap {
         if res.rc == 0 {
             if needed_layer == GSSAUTH_P_PRIVACY {
                 buf[0] = 0;
-                let send_max_size =
+                let mut send_max_size =
                     u32::from_be_bytes((&buf[..]).try_into().expect("send max size"));
+                if send_max_size < 262_144 {
+                    warn!("got bogus send_max_size {}, adjusting to 256 KiB", send_max_size);
+                    send_max_size = 262_144;
+                }
                 let mut sasl_param = self.sasl_param.write().expect("sasl param");
                 sasl_param.0 = true;
                 sasl_param.1 = send_max_size;

The sandboxed version at freeipa.org returned the reasonable 2097152 (2 MiB). What's the version of 389DS in your FreeIPA installation?

troelsarvin commented 1 year ago

I get the following output after the code adjustment:

2023-02-21T09:46:49.491920Z WARN ldap3::ldap: got bogus send_max_size 0, adjusting to 256 KiB

With regards to 389DS version: I believe it's version 1.3.10.something.

inejge commented 1 year ago

WARN ldap3::ldap: got bogus send_max_size 0...

Does it otherwise work?

troelsarvin commented 1 year ago

Does it otherwise work?

Ah, that's a fair question ;-) Yes: It works.

dequbed commented 1 year ago

send_max: 0

Uhh, that's really wacky. Delete the eprintln!() and try the following patch:

It's definitely an annoying edge-case. Maximum wrap size is specified to zero by the RFC if no security layer are negotiated, but not specified for the case that security layers are in use but the effective maximum size is 'infinite', which is theoretically the case here since LDAP runs over a stream transport. I'm all in all not surprised by some implementations using 0 as indicator for that.

Note: If the cross-krb5 crate turns out being a problematic dependency, then may the rsasl crate can be used instead? It seems that crate was very recently released in version 2. It doesn't explicitly state whether nor not it is platform (in)dependent, though.

Funny you should mention that :) Regardless, the GSSAPI mechanism implementation provided by rsasl itself uses libgssapi at the moment so it will not use SSPI on Windows but requires an GSSAPI library to be installed instead. I will probably change that up to use cross-krb5 instead in the future, so no, switching to rsasl would not fix that inherently — and cross-krb5 isn't the problem to begin with here.

troelsarvin commented 1 year ago

For the record, 389DS generation 2.1 seems to behave differently: With that version, the updated ldap3 code is not needed. But I think it's good that your adjustment is now part of the code, thank you.