Skip to content

Commit

Permalink
Merge pull request #3 from schlenk/claim_no_mic_options
Browse files Browse the repository at this point in the history
Claim no message integrity options
  • Loading branch information
may-day committed Feb 5, 2016
2 parents 21d00f2 + 4cd02b5 commit c40a280
Showing 1 changed file with 51 additions and 42 deletions.
93 changes: 51 additions & 42 deletions kerberos_sspi.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -135,30 +135,30 @@ 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
"""

# 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
Expand All @@ -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. '[email protected]').
@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
}
Expand Down Expand Up @@ -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

Expand All @@ -259,33 +259,42 @@ 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
size = struct.unpack("!L", conf_and_size)[0] & 0x00ffffff
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']
Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit c40a280

Please sign in to comment.