From 4cd02b55709e4cec35f4c12a6a2e17825a2e8690 Mon Sep 17 00:00:00 2001 From: Michael Schlenker Date: Thu, 4 Feb 2016 15:19:21 +0100 Subject: [PATCH] Handle MIC/Wrap security layers correctly even if no user is given This is obviously cut&copied all over the net. (found nearly identical code including comments in the mutt email client, the upstream calendarserver/pykerberos code and a few other places). And it is wrong if 'user' is empty. RFC 4752 Section 3.1 states the responsibilities of the client side a SASL GSSAPI Authentication. We get the last package from the server in this step, and have to GSS_Unwrap() and inspect the security_layer offerings of the server. This is done and logged here (even if not perfect). Once the offered security layers are known, the client has to send the one used. In the code before the patch this was kind of ok (GSS_AUTH_P_NONE) for the case when there is a authorization ID given, but broken for the case when no user was given. The code just replied with anything the server offers, even if totally unable to handle that, which leads to errors if the established security context is used for further communication (e.g. in LDAP). In addition, these options should be synced with the gssflags of the context, e.g. fail if the server does not honour the requested security. (not that it makes any sense to request any higher security, as the client doesn't handle it.) --- kerberos_sspi.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/kerberos_sspi.py b/kerberos_sspi.py index 2721afa..62eefc3 100644 --- a/kerberos_sspi.py +++ b/kerberos_sspi.py @@ -275,7 +275,7 @@ def authGSSClientWrap(context, data, user=None): data = decodestring(data) if data else None # RFC 4752 Section 3.1 last 2 paragraphs - if user and data: + if data: import struct conf_and_size = data[:struct.calcsize("!L")] # network unsigned long conf = struct.unpack("B", conf_and_size[0])[0] # B .. unsigned char @@ -283,9 +283,18 @@ def authGSSClientWrap(context, data, user=None): logger.info("N" if conf & GSS_AUTH_P_NONE else "-") logger.info("I" if conf & GSS_AUTH_P_INTEGRITY else "-") logger.info("P" if conf & GSS_AUTH_P_PRIVACY else "-") - logger.info("Maximum GSS token size is %d", size) - conf_and_size=chr(GSS_AUTH_P_NONE) + conf_and_size[1:] - data = conf_and_size + user.encode("utf-8") + logger.info("Maximum GSS message size for server side is %d", size) + # Tell the truth, we do not handle any security layer + # (aka GSS_AUTH_P_NONE). RFC 4752 demands that the + # max client message size is zero in this case. + max_size_client_message = 0 + security_layer = GSS_AUTH_P_NONE + conf_and_size = struct.pack("!L", security_layer << 24 + + (max_size_client_message & 0x00ffffff)) + if user: + data = conf_and_size + user.encode("utf-8") + else: + data = conf_and_size pkg_size_info=ca.ctxt.QueryContextAttributes(sspicon.SECPKG_ATTR_SIZES) trailersize=pkg_size_info['SecurityTrailer']