@@ -390,6 +390,9 @@ def get_usage(self):
390390 elif isinstance (self .underlayer , KRB_AS_REP ):
391391 # AS-REP encrypted part
392392 return 3 , EncASRepPart
393+ elif isinstance (self .underlayer , KRB_KDC_REQ_BODY ):
394+ # KDC-REQ enc-authorization-data
395+ return 4 , AuthorizationData
393396 elif isinstance (self .underlayer , KRB_AP_REQ ) and isinstance (
394397 self .underlayer .underlayer , PADATA
395398 ):
@@ -982,9 +985,10 @@ class KERB_AD_RESTRICTION_ENTRY(ASN1_Packet):
982985class KERB_AUTH_DATA_AP_OPTIONS (Packet ):
983986 name = "KERB-AUTH-DATA-AP-OPTIONS"
984987 fields_desc = [
985- LEIntEnumField (
988+ FlagsField (
986989 "apOptions" ,
987990 0x4000 ,
991+ - 32 ,
988992 {
989993 0x4000 : "KERB_AP_OPTIONS_CBT" ,
990994 0x8000 : "KERB_AP_OPTIONS_UNVERIFIED_TARGET_NAME" ,
@@ -1809,6 +1813,12 @@ class KRB_AS_REP(ASN1_Packet):
18091813 implicit_tag = ASN1_Class_KRB .AS_REP ,
18101814 )
18111815
1816+ def getUPN (self ):
1817+ return "%s@%s" % (
1818+ self .cname .toString (),
1819+ self .crealm .val .decode (),
1820+ )
1821+
18121822
18131823class KRB_TGS_REP (ASN1_Packet ):
18141824 ASN1_codec = ASN1_Codecs .BER
@@ -2420,11 +2430,11 @@ class KRB_AuthenticatorChecksum(Packet):
24202430 },
24212431 ),
24222432 ConditionalField (
2423- LEShortField ("DlgOpt" , 0 ),
2433+ LEShortField ("DlgOpt" , 1 ),
24242434 lambda pkt : pkt .Flags .GSS_C_DELEG_FLAG ,
24252435 ),
24262436 ConditionalField (
2427- FieldLenField ("Dlgth" , None , length_of = "Deleg" ),
2437+ FieldLenField ("Dlgth" , None , length_of = "Deleg" , fmt = "<H" ),
24282438 lambda pkt : pkt .Flags .GSS_C_DELEG_FLAG ,
24292439 ),
24302440 ConditionalField (
@@ -3021,8 +3031,8 @@ class KerberosClient(Automaton):
30213031 :param dmsa: sets the 'unconditional delegation' mode for DMSA TGT retrieval
30223032 """
30233033
3024- RES_AS_MODE = namedtuple ("AS_Result" , ["asrep" , "sessionkey" , "kdcrep" ])
3025- RES_TGS_MODE = namedtuple ("TGS_Result" , ["tgsrep" , "sessionkey" , "kdcrep" ])
3034+ RES_AS_MODE = namedtuple ("AS_Result" , ["asrep" , "sessionkey" , "kdcrep" , "upn" ])
3035+ RES_TGS_MODE = namedtuple ("TGS_Result" , ["tgsrep" , "sessionkey" , "kdcrep" , "upn" ])
30263036
30273037 class MODE (IntEnum ):
30283038 AS_REQ = 0
@@ -3120,7 +3130,7 @@ def __init__(
31203130 x509 = Cert (x509 )
31213131 if not isinstance (x509key , PrivKey ):
31223132 x509key = PrivKey (x509key )
3123- if not isinstance (ca , CertList ):
3133+ if ca and not isinstance (ca , CertList ):
31243134 ca = CertList (ca )
31253135
31263136 if mode in [self .MODE .AS_REQ , self .MODE .GET_SALT ]:
@@ -4037,7 +4047,12 @@ def decrypt_as_rep(self, pkt):
40374047 # Decrypt AS-REP response
40384048 enc = pkt .root .encPart
40394049 res = enc .decrypt (self .replykey )
4040- self .result = self .RES_AS_MODE (pkt .root , res .key .toKey (), res )
4050+ self .result = self .RES_AS_MODE (
4051+ pkt .root ,
4052+ res .key .toKey (),
4053+ res ,
4054+ pkt .root .getUPN (),
4055+ )
40414056
40424057 @ATMT .receive_condition (SENT_TGS_REQ )
40434058 def receive_krb_error_tgs_req (self , pkt ):
@@ -4108,7 +4123,12 @@ def decrypt_tgs_rep(self, pkt):
41084123 res = enc .decrypt (self .replykey )
41094124
41104125 # Store result
4111- self .result = self .RES_TGS_MODE (pkt .root , res .key .toKey (), res )
4126+ self .result = self .RES_TGS_MODE (
4127+ pkt .root ,
4128+ res .key .toKey (),
4129+ res ,
4130+ self .upn ,
4131+ )
41124132
41134133 @ATMT .state (final = 1 )
41144134 def FINAL (self ):
@@ -4315,8 +4335,9 @@ def krb_as_and_tgs(upn, spn, ip=None, key=None, password=None, **kwargs):
43154335 res = krb_as_req (upn = upn , ip = ip , key = key , password = password , ** kwargs )
43164336 if not res :
43174337 return
4338+
43184339 return krb_tgs_req (
4319- upn = upn ,
4340+ upn = res . upn , # UPN might get canonicalized
43204341 spn = spn ,
43214342 sessionkey = res .sessionkey ,
43224343 ticket = res .asrep .ticket ,
@@ -4640,6 +4661,7 @@ def __init__(
46404661 self .KEY = KEY
46414662 self .SPN = SPN
46424663 self .TGT = TGT
4664+ self .TGTSessionKey = None
46434665 self .PASSWORD = PASSWORD
46444666 self .U2U = U2U
46454667 self .DC_IP = DC_IP
@@ -5043,12 +5065,14 @@ def GSS_Init_sec_context(
50435065 if Context .state in [self .STATE .INIT , self .STATE .CLI_SENT_TGTREQ ]:
50445066 if not self .UPN :
50455067 raise ValueError ("Missing UPN attribute" )
5068+
50465069 # Do we have a ST?
50475070 if self .ST is None :
50485071 # Client sends an AP-req
50495072 if not self .SPN and not target_name :
50505073 raise ValueError ("Missing SPN/target_name attribute" )
50515074 additional_tickets = []
5075+
50525076 if self .U2U :
50535077 try :
50545078 # GSSAPI / Kerberos
@@ -5063,39 +5087,54 @@ def GSS_Init_sec_context(
50635087 tgt_rep .show ()
50645088 raise ValueError ("KerberosSSP: Unexpected token !" )
50655089 additional_tickets = [tgt_rep .ticket ]
5066- if self .TGT is not None :
5067- if not self .KEY :
5068- raise ValueError ("Cannot use TGT without the KEY" )
5069- # Use TGT
5070- res = krb_tgs_req (
5071- upn = self .UPN ,
5072- spn = self .SPN or target_name ,
5073- ip = self .DC_IP ,
5074- sessionkey = self .KEY ,
5075- ticket = self .TGT ,
5076- additional_tickets = additional_tickets ,
5077- u2u = self .U2U ,
5078- debug = self .debug ,
5079- )
5080- else :
5081- # Ask for TGT then ST
5082- res = krb_as_and_tgs (
5090+
5091+ if self .TGT is None :
5092+ # Get TGT. We were passed a kerberos key
5093+ res = krb_as_req (
50835094 upn = self .UPN ,
5084- spn = self .SPN or target_name ,
50855095 ip = self .DC_IP ,
50865096 key = self .KEY ,
50875097 password = self .PASSWORD ,
5088- additional_tickets = additional_tickets ,
5089- u2u = self .U2U ,
50905098 debug = self .debug ,
50915099 )
5100+ # Update UPN (could have been canonicalized)
5101+ self .UPN = res .upn
5102+
5103+ # Store TGT,
5104+ self .TGT = res .asrep .ticket
5105+ self .TGTSessionKey = res .sessionkey
5106+ else :
5107+ # We have a TGT and were passed its key
5108+ self .TGTSessionKey = self .KEY
5109+
5110+ # Get ST
5111+ if not self .TGTSessionKey :
5112+ raise ValueError ("Cannot use TGT without the KEY" )
5113+
5114+ res = krb_tgs_req (
5115+ upn = self .UPN ,
5116+ spn = self .SPN or target_name ,
5117+ ip = self .DC_IP ,
5118+ sessionkey = self .TGTSessionKey ,
5119+ ticket = self .TGT ,
5120+ additional_tickets = additional_tickets ,
5121+ u2u = self .U2U ,
5122+ debug = self .debug ,
5123+ )
50925124 if not res :
50935125 # Failed to retrieve the ticket
50945126 return Context , None , GSS_S_FAILURE
5095- self .ST , self .KEY = res .tgsrep .ticket , res .sessionkey
5127+
5128+ # Store the service ticket and associated key
5129+ self .ST , Context .STSessionKey = res .tgsrep .ticket , res .sessionkey
50965130 elif not self .KEY :
50975131 raise ValueError ("Must provide KEY with ST" )
5098- Context .STSessionKey = self .KEY
5132+ else :
5133+ # We were passed a ST and its key
5134+ Context .STSessionKey = self .KEY
5135+
5136+ if Context .flags & GSS_C_FLAGS .GSS_C_DELEG_FLAG :
5137+ raise ValueError ("Cannot use GSS_C_DELEG_FLAG when passed a service ticket !" )
50995138
51005139 # Save ServerHostname
51015140 if len (self .ST .sname .nameString ) == 2 :
@@ -5127,25 +5166,49 @@ def GSS_Init_sec_context(
51275166 # Get the realm of the client
51285167 _ , crealm = _parse_upn (self .UPN )
51295168
5169+ # Build the RFC4121 authenticator checksum
5170+ authenticator_checksum = KRB_AuthenticatorChecksum (
5171+ # RFC 4121 sect 4.1.1.2
5172+ # "The Bnd field contains the MD5 hash of channel bindings"
5173+ Bnd = (
5174+ chan_bindings .digestMD5 ()
5175+ if chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
5176+ else (b"\x00 " * 16 )
5177+ ),
5178+ Flags = int (Context .flags ),
5179+ )
5180+
5181+ if Context .flags & GSS_C_FLAGS .GSS_C_DELEG_FLAG :
5182+ # Delegate TGT
5183+ raise NotImplementedError ("GSS_C_DELEG_FLAG is not implemented !" )
5184+ # authenticator_checksum.Deleg = KRB_CRED(
5185+ # tickets=[self.TGT],
5186+ # encPart=EncryptedData()
5187+ # )
5188+ # authenticator_checksum.encPart.encrypt(
5189+ # Context.STSessionKey,
5190+ # EncKrbCredPart(
5191+ # ticketInfo=KrbCredInfo(
5192+ # key=EncryptionKey.fromKey(self.TGTSessionKey),
5193+ # prealm=ASN1_GENERAL_STRING(crealm),
5194+ # pname=PrincipalName.fromUPN(self.UPN),
5195+ # # TODO: rework API to pass starttime... here.
5196+ # sreralm=self.TGT.realm,
5197+ # sname=self.TGT.sname,
5198+ # )
5199+ # )
5200+ # )
5201+
5202+
51305203 # Build and encrypt the full KRB_Authenticator
51315204 ap_req .authenticator .encrypt (
51325205 Context .STSessionKey ,
51335206 KRB_Authenticator (
51345207 crealm = crealm ,
51355208 cname = PrincipalName .fromUPN (self .UPN ),
5136- # RFC 4121 checksum
51375209 cksum = Checksum (
51385210 cksumtype = "KRB-AUTHENTICATOR" ,
5139- checksum = KRB_AuthenticatorChecksum (
5140- # RFC 4121 sect 4.1.1.2
5141- # "The Bnd field contains the MD5 hash of channel bindings"
5142- Bnd = (
5143- chan_bindings .digestMD5 ()
5144- if chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
5145- else (b"\x00 " * 16 )
5146- ),
5147- Flags = int (Context .flags ),
5148- ),
5211+ checksum = authenticator_checksum
51495212 ),
51505213 ctime = ASN1_GENERALIZED_TIME (now_time ),
51515214 cusec = ASN1_INTEGER (0 ),
@@ -5510,19 +5573,21 @@ def GSS_Passive(
55105573 Context .state = self .STATE .CLI_SENT_APREQ
55115574 else :
55125575 Context .state = self .STATE .FAILED
5513- return Context , status
55145576 elif Context .state == self .STATE .CLI_SENT_APREQ :
55155577 Context , _ , status = self .GSS_Init_sec_context (
55165578 Context , token , req_flags = req_flags
55175579 )
55185580 if status == GSS_S_COMPLETE :
5581+ if req_flags & GSS_C_FLAGS .GSS_C_DCE_STYLE :
5582+ status = GSS_S_CONTINUE_NEEDED
55195583 Context .state = self .STATE .SRV_SENT_APREP
55205584 else :
55215585 Context .state == self .STATE .FAILED
5522- return Context , status
5586+ else :
5587+ # Unknown state. Don't crash though.
5588+ status = GSS_S_FAILURE
55235589
5524- # Unknown state. Don't crash though.
5525- return Context , GSS_S_FAILURE
5590+ return Context , status
55265591
55275592 def GSS_Passive_set_Direction (self , Context : CONTEXT , IsAcceptor = False ):
55285593 if Context .IsAcceptor is not IsAcceptor :
0 commit comments