diff --git a/kerberos_sspi.py b/kerberos_sspi.py index 7cca3da..62eefc3 100644 --- a/kerberos_sspi.py +++ b/kerberos_sspi.py @@ -46,9 +46,9 @@ class GSSError(KrbError): def _sspi_spn_from_nt_service_name(nt_service_name, realm=None): """ - create a service name consumable by sspi from the nt_service_name fromat used by krb, + create a service name consumable by sspi from the nt_service_name format used by krb, e.g. from http@somehost -> http/somehost[@REALM] - + """ global hostname, defaultrealm if "/" not in nt_service_name and "@" in nt_service_name: @@ -71,11 +71,11 @@ def checkPassword(user, pswd, service, default_realm): those normally used for Kerberos authentication. It does this by checking that the supplied user name and password can be used to get a ticket for the supplied service. If the user name does not contain a realm, then the default realm supplied is used. - + NB For this to work properly the Kerberos must be configured properly on this machine. That will likely mean ensuring that the edu.mit.Kerberos preference file has the correct realms and KDCs listed. - + @param user: a string containing the Kerberos user name. A realm may be included by appending an '@' followed by the realm string to the actual user id. If no realm is supplied, then the realm set in the default_realm argument will @@ -125,7 +125,7 @@ def getServerPrincipalDetails(service, hostname): """ This function returns the service principal for the server given a service type and hostname. Details are looked up via the /etc/keytab file. - + @param service: a string containing the Kerberos service type for the server. @param hostname: a string containing the hostname of the server. @return: a string containing the service principal. @@ -135,7 +135,7 @@ def getServerPrincipalDetails(service, hostname): """ GSSAPI Function Result Codes: - + -1 : Error 0 : GSSAPI step continuation (only returned by 'Step' function) 1 : GSSAPI step complete, or function return OK @@ -143,22 +143,22 @@ def getServerPrincipalDetails(service, hostname): """ # Some useful result codes -AUTH_GSS_CONTINUE = 0 -AUTH_GSS_COMPLETE = 1 - -# Some useful gss flags -GSS_C_DELEG_FLAG = sspicon.ISC_REQ_DELEGATE +AUTH_GSS_CONTINUE = 0 +AUTH_GSS_COMPLETE = 1 + +# Some useful gss flags +GSS_C_DELEG_FLAG = sspicon.ISC_REQ_DELEGATE GSS_C_MUTUAL_FLAG = sspicon.ISC_REQ_MUTUAL_AUTH GSS_C_REPLAY_FLAG = sspicon.ISC_REQ_REPLAY_DETECT GSS_C_SEQUENCE_FLAG = sspicon.ISC_REQ_SEQUENCE_DETECT -GSS_C_CONF_FLAG = sspicon.ISC_REQ_CONFIDENTIALITY -GSS_C_INTEG_FLAG = sspicon.ISC_REQ_INTEGRITY +GSS_C_CONF_FLAG = sspicon.ISC_REQ_CONFIDENTIALITY +GSS_C_INTEG_FLAG = sspicon.ISC_REQ_INTEGRITY # leave the following undefined, so if someone relies on them they know that this package # is not for them -#GSS_C_ANON_FLAG = 0 -#GSS_C_PROT_READY_FLAG = 0 -#GSS_C_TRANS_FLAG = 0 +#GSS_C_ANON_FLAG = 0 +#GSS_C_PROT_READY_FLAG = 0 +#GSS_C_TRANS_FLAG = 0 GSS_AUTH_P_NONE = 1 GSS_AUTH_P_INTEGRITY = 2 @@ -176,15 +176,15 @@ def authGSSClientInit(service, gssflags=GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG): @param service: a string containing the service principal in the form 'type@fqdn' (e.g. 'imap@mail.apple.com'). @param gssflags: optional integer used to set GSS flags. - (e.g. GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG will allow + (e.g. GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG will allow for forwarding credentials to the remote host) @return: a tuple of (result, context) where result is the result code (see above) and context is an opaque value that will need to be passed to subsequent functions. """ spn=_sspi_spn_from_nt_service_name(service) - ctx={"csa":sspi.ClientAuth("Kerberos", scflags=gssflags, targetspn=spn), - "service":service, + ctx={"csa":sspi.ClientAuth("Kerberos", scflags=gssflags, targetspn=spn), + "service":service, "gssflags":gssflags, "response":None } @@ -242,12 +242,12 @@ def authGSSClientUserName(context): return context["csa"].ctxt.QueryContextAttributes(sspicon.SECPKG_ATTR_NAMES) -def authGSSClientUnwrap(context, challenge): - """ - Perform the client side GSSAPI unwrap step - - @param challenge: a string containing the base64-encoded server data. - @return: a result code (see above) +def authGSSClientUnwrap(context, challenge): + """ + Perform the client side GSSAPI unwrap step + + @param challenge: a string containing the base64-encoded server data. + @return: a result code (see above) """ data = decodestring(challenge) if challenge else None @@ -259,23 +259,23 @@ def authGSSClientUnwrap(context, challenge): encbuf[1].Buffer=data ca.ctxt.DecryptMessage(encbuf,ca._get_next_seq_num()) context["response"]= encbuf[0].Buffer - + return AUTH_GSS_COMPLETE -def authGSSClientWrap(context, data, user=None): - """ - Perform the client side GSSAPI wrap step. - - @param data:the result of the authGSSClientResponse after the authGSSClientUnwrap - @param user: the user to authorize - @return: a result code (see above) - """ - +def authGSSClientWrap(context, data, user=None): + """ + Perform the client side GSSAPI wrap step. + + @param data:the result of the authGSSClientResponse after the authGSSClientUnwrap + @param user: the user to authorize + @return: a result code (see above) + """ + ca = context["csa"] 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'] @@ -299,7 +308,7 @@ def authGSSClientWrap(context, data, user=None): ca.ctxt.EncryptMessage(0,encbuf, ca._get_next_seq_num()) #ca.ctxt.EncryptMessage(0,encbuf, 0) - + context["response"] = encbuf[0].Buffer+encbuf[1].Buffer+encbuf[2].Buffer return AUTH_GSS_COMPLETE @@ -316,8 +325,8 @@ def authGSSServerInit(service): context is an opaque value that will need to be passed to subsequent functions. """ spn=_sspi_spn_from_nt_service_name(service) - ctx={"csa":sspi.ServerAuth("Kerberos", spn=spn), - "service":service, + ctx={"csa":sspi.ServerAuth("Kerberos", spn=spn), + "service":service, "response":None, } return AUTH_GSS_COMPLETE, ctx