def get_gssapi_token(principal, host, domain): ''' Get the gssapi token for Kerberos connection principal The service principal host Host url where we would like to authenticate domain Kerberos user domain ''' if not HAS_GSSAPI: raise ImportError('The gssapi library is not imported.') service = '{0}/{1}@{2}'.format(principal, host, domain) log.debug('Retrieving gsspi token for service {0}'.format(service)) service_name = gssapi.Name(service, gssapi.C_NT_USER_NAME) ctx = gssapi.InitContext(service_name) in_token = None while not ctx.established: out_token = ctx.step(in_token) if out_token: encoded_token = base64.b64encode(out_token) return encoded_token if ctx.established: break if not in_token: raise salt.exceptions.CommandExecutionError( 'Can\'t receive token, no response from server') raise salt.exceptions.CommandExecutionError( 'Context established, but didn\'t receive token')
def sasl_gssapi(connection, controls): """ Performs a bind using the Kerberos v5 ("GSSAPI") SASL mechanism from RFC 4752. Does not support any security layers, only authentication! """ NO_SECURITY_LAYER = 1 INTEGRITY_PROTECTION = 2 CONFIDENTIALITY_PROTECTION = 4 authz_id = b'' krb5mech = gssapi.OID.mech_from_string() target_name = gssapi.Name('ldap@' + connection.server.host, gssapi.C_NT_HOSTBASED_SERVICE) ctx_kwargs = { 'mech_type': krb5mech, 'req_flags': (gssapi.C_INTEG_FLAG,) } if ( isinstance(connection.sasl_credentials, (list, tuple)) and len(connection.sasl_credentials) == 2 ): # connection.sasl_credentials can be a 2-tuple. The first element can be a # gssapi.Credential to use as the initiator credential. The second can be an authorization ID. # Either can be None if the default credential, or no authorization ID is to be used. if isinstance(connection.sasl_credentials[0], gssapi.Credential): ctx_kwargs['cred'] = connection.sasl_credentials if connection.sasl_credentials[1] is not None: authz_id = connection.sasl_credentials[1].encode('utf-8') ctx = gssapi.InitContext(target_name, **ctx_kwargs) in_token = None try: while not ctx.established: # Client calls init_sec_context... out_token = ctx.step(in_token) if out_token is None: out_token = b'' # and sends the server the result result = send_sasl_negotiation(connection, controls, out_token) in_token = result['saslCreds'] # once the Context is established, decode the server's token unwrapped_token = ctx.unwrap(in_token, conf_req=False) if len(unwrapped_token) != 4: raise ValueError("Incorrect response from server.") server_security_layers = ord(unwrapped_token[0]) if server_security_layers in (0, NO_SECURITY_LAYER): if unwrapped_token[1:] != b'\x00\x00\x00': raise ValueError("Server max buffer size must be 0 if no security layer.") if not (server_security_layers & NO_SECURITY_LAYER): raise ValueError("Server requires a security layer, but we don't support any.") client_security_layers = bytearray([NO_SECURITY_LAYER, 0, 0, 0]) out_token = ctx.wrap(bytes(client_security_layers) + authz_id, conf_req=False) return send_sasl_negotiation(connection, controls, out_token) except (gssapi.GSSException, ValueError) as exc: abort_sasl_negotiation(connection, controls) raise
def ssh_init_sec_context(self, target, desired_mech=None, username=None, recv_token=None): """ Initialize a GSS-API context. :param str username: The name of the user who attempts to login :param str target: The hostname of the target to connect to :param str desired_mech: The negotiated GSS-API mechanism ("pseudo negotiated" mechanism, because we support just the krb5 mechanism :-)) :param str recv_token: The GSS-API token received from the Server :raises: `.SSHException` -- Is raised if the desired mechanism of the client is not supported :return: A ``String`` if the GSS-API has returned a token or ``None`` if no token was returned """ from pyasn1.codec.der import decoder self._username = username self._gss_host = target targ_name = gssapi.Name("host@" + self._gss_host, gssapi.C_NT_HOSTBASED_SERVICE) ctx = gssapi.Context() ctx.flags = self._gss_flags if desired_mech is None: krb5_mech = gssapi.OID.mech_from_string(self._krb5_mech) else: mech, __ = decoder.decode(desired_mech) if mech.__str__() != self._krb5_mech: raise SSHException("Unsupported mechanism OID.") else: krb5_mech = gssapi.OID.mech_from_string(self._krb5_mech) token = None try: if recv_token is None: self._gss_ctxt = gssapi.InitContext( peer_name=targ_name, mech_type=krb5_mech, req_flags=ctx.flags, ) token = self._gss_ctxt.step(token) else: token = self._gss_ctxt.step(recv_token) except gssapi.GSSException: message = "{} Target: {}".format(sys.exc_info()[1], self._gss_host) raise gssapi.GSSException(message) self._gss_ctxt_status = self._gss_ctxt.established return token
def _gssapi_sspi_test(self): """ Test the used methods of python-gssapi or sspi, sspicon from pywin32. """ try: import gssapi if ( hasattr(gssapi, "__title__") and gssapi.__title__ == "python-gssapi" ): _API = "PYTHON-GSSAPI-OLD" else: _API = "PYTHON-GSSAPI-NEW" except ImportError: import sspicon import sspi _API = "SSPI" c_token = None gss_ctxt_status = False mic_msg = b"G'day Mate!" if _API == "PYTHON-GSSAPI-OLD": if self.server_mode: gss_flags = ( gssapi.C_PROT_READY_FLAG, gssapi.C_INTEG_FLAG, gssapi.C_MUTUAL_FLAG, gssapi.C_DELEG_FLAG, ) else: gss_flags = ( gssapi.C_PROT_READY_FLAG, gssapi.C_INTEG_FLAG, gssapi.C_DELEG_FLAG, ) # Initialize a GSS-API context. ctx = gssapi.Context() ctx.flags = gss_flags krb5_oid = gssapi.OID.mech_from_string(self.krb5_mech) target_name = gssapi.Name( "host@" + self.targ_name, gssapi.C_NT_HOSTBASED_SERVICE ) gss_ctxt = gssapi.InitContext( peer_name=target_name, mech_type=krb5_oid, req_flags=ctx.flags ) if self.server_mode: c_token = gss_ctxt.step(c_token) gss_ctxt_status = gss_ctxt.established self.assertEquals(False, gss_ctxt_status) # Accept a GSS-API context. gss_srv_ctxt = gssapi.AcceptContext() s_token = gss_srv_ctxt.step(c_token) gss_ctxt_status = gss_srv_ctxt.established self.assertNotEquals(None, s_token) self.assertEquals(True, gss_ctxt_status) # Establish the client context c_token = gss_ctxt.step(s_token) self.assertEquals(None, c_token) else: while not gss_ctxt.established: c_token = gss_ctxt.step(c_token) self.assertNotEquals(None, c_token) # Build MIC mic_token = gss_ctxt.get_mic(mic_msg) if self.server_mode: # Check MIC status = gss_srv_ctxt.verify_mic(mic_msg, mic_token) self.assertEquals(0, status) elif _API == "PYTHON-GSSAPI-NEW": if self.server_mode: gss_flags = ( gssapi.RequirementFlag.protection_ready, gssapi.RequirementFlag.integrity, gssapi.RequirementFlag.mutual_authentication, gssapi.RequirementFlag.delegate_to_peer, ) else: gss_flags = ( gssapi.RequirementFlag.protection_ready, gssapi.RequirementFlag.integrity, gssapi.RequirementFlag.delegate_to_peer, ) # Initialize a GSS-API context. krb5_oid = gssapi.MechType.kerberos target_name = gssapi.Name( "host@" + self.targ_name, name_type=gssapi.NameType.hostbased_service, ) gss_ctxt = gssapi.SecurityContext( name=target_name, flags=gss_flags, mech=krb5_oid, usage="initiate", ) if self.server_mode: c_token = gss_ctxt.step(c_token) gss_ctxt_status = gss_ctxt.complete self.assertEquals(False, gss_ctxt_status) # Accept a GSS-API context. gss_srv_ctxt = gssapi.SecurityContext(usage="accept") s_token = gss_srv_ctxt.step(c_token) gss_ctxt_status = gss_srv_ctxt.complete self.assertNotEquals(None, s_token) self.assertEquals(True, gss_ctxt_status) # Establish the client context c_token = gss_ctxt.step(s_token) self.assertEquals(None, c_token) else: while not gss_ctxt.complete: c_token = gss_ctxt.step(c_token) self.assertNotEquals(None, c_token) # Build MIC mic_token = gss_ctxt.get_signature(mic_msg) if self.server_mode: # Check MIC status = gss_srv_ctxt.verify_signature(mic_msg, mic_token) self.assertEquals(0, status) else: gss_flags = ( sspicon.ISC_REQ_INTEGRITY | sspicon.ISC_REQ_MUTUAL_AUTH | sspicon.ISC_REQ_DELEGATE ) # Initialize a GSS-API context. target_name = "host/" + socket.getfqdn(self.targ_name) gss_ctxt = sspi.ClientAuth( "Kerberos", scflags=gss_flags, targetspn=target_name ) if self.server_mode: error, token = gss_ctxt.authorize(c_token) c_token = token[0].Buffer self.assertEquals(0, error) # Accept a GSS-API context. gss_srv_ctxt = sspi.ServerAuth("Kerberos", spn=target_name) error, token = gss_srv_ctxt.authorize(c_token) s_token = token[0].Buffer # Establish the context. error, token = gss_ctxt.authorize(s_token) c_token = token[0].Buffer self.assertEquals(None, c_token) self.assertEquals(0, error) # Build MIC mic_token = gss_ctxt.sign(mic_msg) # Check MIC gss_srv_ctxt.verify(mic_msg, mic_token) else: error, token = gss_ctxt.authorize(c_token) c_token = token[0].Buffer self.assertNotEquals(0, error)
def test_2_gssapi_sspi(self): """ Test the used methods of python-gssapi or sspi, sspicon from pywin32. """ _API = "MIT" try: import gssapi except ImportError: import sspicon import sspi _API = "SSPI" c_token = None gss_ctxt_status = False mic_msg = b"G'day Mate!" if _API == "MIT": if server_mode: gss_flags = (gssapi.C_PROT_READY_FLAG, gssapi.C_INTEG_FLAG, gssapi.C_MUTUAL_FLAG, gssapi.C_DELEG_FLAG) else: gss_flags = (gssapi.C_PROT_READY_FLAG, gssapi.C_INTEG_FLAG, gssapi.C_DELEG_FLAG) # Initialize a GSS-API context. ctx = gssapi.Context() ctx.flags = gss_flags krb5_oid = gssapi.OID.mech_from_string(krb5_mech) target_name = gssapi.Name("host@" + targ_name, gssapi.C_NT_HOSTBASED_SERVICE) gss_ctxt = gssapi.InitContext(peer_name=target_name, mech_type=krb5_oid, req_flags=ctx.flags) if server_mode: c_token = gss_ctxt.step(c_token) gss_ctxt_status = gss_ctxt.established self.assertEquals(False, gss_ctxt_status) # Accept a GSS-API context. gss_srv_ctxt = gssapi.AcceptContext() s_token = gss_srv_ctxt.step(c_token) gss_ctxt_status = gss_srv_ctxt.established self.assertNotEquals(None, s_token) self.assertEquals(True, gss_ctxt_status) # Establish the client context c_token = gss_ctxt.step(s_token) self.assertEquals(None, c_token) else: while not gss_ctxt.established: c_token = gss_ctxt.step(c_token) self.assertNotEquals(None, c_token) # Build MIC mic_token = gss_ctxt.get_mic(mic_msg) if server_mode: # Check MIC status = gss_srv_ctxt.verify_mic(mic_msg, mic_token) self.assertEquals(0, status) else: gss_flags = (sspicon.ISC_REQ_INTEGRITY | sspicon.ISC_REQ_MUTUAL_AUTH | sspicon.ISC_REQ_DELEGATE) # Initialize a GSS-API context. target_name = "host/" + socket.getfqdn(targ_name) gss_ctxt = sspi.ClientAuth("Kerberos", scflags=gss_flags, targetspn=target_name) if server_mode: error, token = gss_ctxt.authorize(c_token) c_token = token[0].Buffer self.assertEquals(0, error) # Accept a GSS-API context. gss_srv_ctxt = sspi.ServerAuth("Kerberos", spn=target_name) error, token = gss_srv_ctxt.authorize(c_token) s_token = token[0].Buffer # Establish the context. error, token = gss_ctxt.authorize(s_token) c_token = token[0].Buffer self.assertEquals(None, c_token) self.assertEquals(0, error) # Build MIC mic_token = gss_ctxt.sign(mic_msg) # Check MIC gss_srv_ctxt.verify(mic_msg, mic_token) else: error, token = gss_ctxt.authorize(c_token) c_token = token[0].Buffer self.assertNotEquals(0, error)
def get_context(self, host): service_name = gssapi.Name('{0}@{1}'.format(self.service, host), gssapi.C_NT_HOSTBASED_SERVICE) logging.debug("get_context(): service name={0}".format(service_name)) return gssapi.InitContext(service_name)
def open(self, host, port=4373, principal=None): if principal is None: gss_name = gssapi.Name('host@' + host, gssapi.C_NT_HOSTBASED_SERVICE) elif isinstance(principal, gssapi.Name): gss_name = principal else: gss_name = gssapi.Name(principal, gssapi.C_NT_HOSTBASED_SERVICE) args = [(host, port)] if self.timeout != 0: args.append(self.timeout) if self.source is not None: args.append((self.source, 0)) sock = socket.create_connection(*args) sock.sendall(self._build_pkt( flags=(TOKEN_NOOP | TOKEN_CONTEXT_NEXT | TOKEN_PROTOCOL), data='', wrap=False )) ctx_args = [gss_name] if self.credential is not None: ctx_args.append(self.credential) ctx = gssapi.InitContext(*ctx_args, req_flags=( gssapi.C_MUTUAL_FLAG, gssapi.C_CONF_FLAG, gssapi.C_INTEG_FLAG, gssapi.C_REPLAY_FLAG, gssapi.C_SEQUENCE_FLAG )) in_token = None receiver = _packet_generator(sock) out_token = ctx.step() while not ctx.established: sock.sendall(self._build_pkt( flags=(TOKEN_CONTEXT | TOKEN_PROTOCOL), data=out_token, wrap=False )) try: flags, in_token = next(receiver) except StopIteration: sock.close() raise RemctlError("Network error: Server closed connection.") if not (flags & TOKEN_PROTOCOL): sock.close() raise RemctlError("Server is using remctl protocol version 1 which is unsupported.") if not (flags & TOKEN_CONTEXT): sock.close() raise RemctlError("Server failed to set TOKEN_CONTEXT flag on context packet.") out_token = ctx.step(in_token) if out_token: sock.sendall(self._build_pkt( flags=(TOKEN_CONTEXT | TOKEN_PROTOCOL), data=out_token, wrap=False )) if not ctx.mutual_auth_negotiated: sock.close() raise RemctlError("Could not negotiate mutual authentication") if not ctx.integrity_negotiated: sock.close() raise RemctlError("Could not negotiate integrity protection") if not ctx.confidentiality_negotiated: sock.close() raise RemctlError("Could not negotiate confidentiality protection") # otherwise, everything is fine, continue: self.last_error = None self.commands = 0 self.sock = sock self.receiver = receiver self.ctx = ctx
# sudo yum install -y epel-release # sudo yum install -y python-pip krb5-devel python-devel libffi-devel # sudo pip install python-gssapi HOSTNAME = socket.gethostname() PORT = 8765 URL = "/" SHOW_TOKENS = False SHOW_HEADERS = False # Create a Name identifying the target service service_name = gssapi.Name('HTTP@%s' % HOSTNAME, gssapi.C_NT_HOSTBASED_SERVICE) #service_name = gssapi.Name('HTTP@%s' % HOSTNAME) # Create an InitContext targeting the demo service ctx = gssapi.InitContext( service_name, mech_type=gssapi.oids.OID.mech_from_string("1.3.6.1.5.5.2")) # Create the connection conn = httplib.HTTPConnection("%s:%d" % (HOSTNAME, PORT)) def random_string(len): hex_characters = "0123456789abcdef" return ''.join(random.choice(hex_characters) for _ in range(len)) lengths = [8, 4, 4, 4, 12] connection_id = "-".join([random_string(x) for x in lengths]) request = { "request": "openConnection", "connectionId": connection_id,