@@ -157,8 +157,9 @@ def initialize(
157
157
credential = nil
158
158
if cache_file . present?
159
159
# the cache file is only used for loading credentials, it is *not* written to
160
- credential = load_credential_from_file ( cache_file , sname : nil , sname_hostname : @hostname )
161
- serviceclass = build_spn . name_string . first
160
+ load_sname_hostname_credential_result = load_credential_from_file ( cache_file , sname : nil , sname_hostname : @hostname )
161
+ credential = load_sname_hostname_credential_result [ :credential ]
162
+ serviceclass = build_spn &.name_string &.first
162
163
if credential && credential . server . components [ 0 ] != serviceclass
163
164
old_sname = credential . server . components . snapshot . join ( '/' )
164
165
credential . server . components [ 0 ] = serviceclass
@@ -168,9 +169,18 @@ def initialize(
168
169
ticket . sname . name_string [ 0 ] = serviceclass
169
170
credential . ticket = ticket . encode
170
171
elsif credential . nil? && hostname . present?
171
- credential = load_credential_from_file ( cache_file , sname : "krbtgt/#{ hostname . split ( '.' , 2 ) . last } " )
172
+ load_sname_krbtgt_hostname_credential_result = load_credential_from_file ( cache_file , sname : "krbtgt/#{ hostname . split ( '.' , 2 ) . last } " )
173
+ credential = load_sname_krbtgt_hostname_credential_result [ :credential ]
172
174
end
173
175
if credential . nil?
176
+ print_error ( "Failed to load a usable credential from ticket file: #{ cache_file } " )
177
+ print_error ( "Attempt failed to find a valid credential in #{ cache_file } for #{ load_sname_hostname_credential_result [ :filter ] . map { |k , v | "#{ k } =#{ v . inspect } " } . join ( ', ' ) } :" )
178
+ print_error ( load_sname_hostname_credential_result [ :filter_reasons ] . join ( "\n " ) . indent ( 2 ) )
179
+
180
+ if load_sname_krbtgt_hostname_credential_result
181
+ print_error ( "Attempt failed to find a valid credential in #{ cache_file } for #{ load_sname_krbtgt_hostname_credential_result [ :filter ] . map { |k , v | "#{ k } =#{ v . inspect } " } . join ( ', ' ) } " )
182
+ print_error ( load_sname_krbtgt_hostname_credential_result [ :filter_reasons ] . join ( "\n " ) . indent ( 2 ) )
183
+ end
174
184
raise ::Rex ::Proto ::Kerberos ::Model ::Error ::KerberosError . new ( "Failed to load a usable credential from ticket file: #{ cache_file } " )
175
185
end
176
186
print_status ( "Loaded a credential from ticket file: #{ cache_file } " )
@@ -362,7 +372,7 @@ def build_spn(options = {})
362
372
# @return [Rex::Proto::Kerberos::CredentialCache::Krb5CcacheCredential] The ccache credential
363
373
def request_tgt_only ( options = { } )
364
374
if options [ :cache_file ]
365
- credential = load_credential_from_file ( options [ :cache_file ] )
375
+ credential = load_credential_from_file ( options [ :cache_file ] ) &. fetch ( :credential , nil )
366
376
else
367
377
credential = get_cached_credential (
368
378
options . merge (
@@ -1058,64 +1068,72 @@ def get_cached_credential(options = {})
1058
1068
# Load a credential object from a file for authentication. Credentials in the file will be filtered by multiple
1059
1069
# attributes including their timestamps to ensure that the returned credential appears usable.
1060
1070
#
1061
- # @param [String] file_path The file path to load a credential object from
1062
- # @return [Rex::Proto::Kerberos::CredentialCache::Krb5CacheCredential] the credential object for authentication
1063
- def load_credential_from_file ( file_path , options = { } )
1064
- unless File . readable? ( file_path . to_s )
1065
- wlog ( "Failed to load ticket file ' #{ file_path } ' (file not readable)" )
1071
+ # @param [String] path The path to load a credential object from
1072
+ # @return [Hash] :credential [ Rex::Proto::Kerberos::CredentialCache::Krb5CacheCredential] the credential object for authentication
1073
+ # @return [Hash] :filter_reasons [Array<String>] the reasons for filtering tickets
1074
+ def load_credential_from_file ( path , options = { } )
1075
+ unless File . readable? ( path . to_s )
1066
1076
return nil
1067
1077
end
1068
1078
1069
1079
begin
1070
- cache = Rex ::Proto ::Kerberos ::CredentialCache ::Krb5Ccache . read ( File . binread ( file_path ) )
1080
+ cache = Rex ::Proto ::Kerberos ::CredentialCache ::Krb5Ccache . read ( File . binread ( path ) )
1071
1081
rescue StandardError => e
1072
- elog ( "Failed to load ticket file '#{ file_path } ' (parsing failed)" , error : e )
1082
+ elog ( "Failed to load ticket file '#{ path } ' (parsing failed)" , error : e )
1073
1083
return nil
1074
1084
end
1075
1085
1076
1086
sname = options . fetch ( :sname ) { build_spn &.to_s }
1077
1087
sname_hostname = options . fetch ( :sname_hostname , nil )
1078
1088
now = Time . now . utc
1079
1089
1090
+ filter = {
1091
+ realm : @realm ,
1092
+ sname : sname ,
1093
+ sname_hostname : sname_hostname
1094
+ } . merge ( options )
1095
+ filter_reasons = [ ]
1096
+
1080
1097
cache . credentials . to_ary . each . with_index ( 1 ) do |credential , index |
1081
1098
tkt_start = credential . starttime == Time . at ( 0 ) . utc ? credential . authtime : credential . starttime
1082
1099
tkt_end = credential . endtime
1100
+ filter_reason_prefix = "Filtered credential #{ path } ##{ index } reason: "
1083
1101
1084
1102
unless tkt_start < now
1085
- wlog ( "Filtered credential #{ file_path } # #{ index } reason: Ticket start time is before now (start: #{ tkt_start } )")
1103
+ filter_reasons << " #{ filter_reason_prefix } Ticket start time is before now (start: #{ tkt_start } )"
1086
1104
next
1087
1105
end
1088
1106
1089
1107
unless now < tkt_end
1090
- wlog ( "Filtered credential #{ file_path } # #{ index } reason: Ticket is expired (expiration: #{ tkt_end } )")
1108
+ filter_reasons << " #{ filter_reason_prefix } Ticket is expired (expiration: #{ tkt_end } )"
1091
1109
next
1092
1110
end
1093
1111
1094
1112
unless !@realm || @realm . casecmp? ( credential . server . realm . to_s )
1095
- wlog ( "Filtered credential #{ file_path } # #{ index } reason: Realm (#{ @realm } ) does not match (realm: #{ credential . server . realm } )")
1113
+ filter_reasons << " #{ filter_reason_prefix } Realm (#{ @realm } ) does not match (realm: #{ credential . server . realm } )"
1096
1114
next
1097
1115
end
1098
1116
1099
1117
unless !sname || sname . to_s . casecmp? ( credential . server . components . snapshot . join ( '/' ) )
1100
- wlog ( "Filtered credential #{ file_path } # #{ index } reason: SPN (#{ sname } ) does not match (spn: #{ credential . server . components . snapshot . join ( '/' ) } )")
1118
+ filter_reasons << " #{ filter_reason_prefix } SPN (#{ sname } ) does not match (spn: #{ credential . server . components . snapshot . join ( '/' ) } )"
1101
1119
next
1102
1120
end
1103
1121
1104
1122
unless !sname_hostname ||
1105
- sname_hostname . to_s . downcase == credential . server . components [ 1 ] . downcase ||
1106
- sname_hostname . to_s . downcase . ends_with? ( '.' + credential . server . components [ 1 ] . downcase )
1107
- wlog ( "Filtered credential #{ file_path } # #{ index } reason: SPN (#{ sname_hostname } ) hostname does not match (spn: #{ credential . server . components . snapshot . join ( '/' ) } )")
1123
+ sname_hostname . to_s . downcase == credential . server . components [ 1 ] . downcase ||
1124
+ sname_hostname . to_s . downcase . ends_with? ( '.' + credential . server . components [ 1 ] . downcase )
1125
+ filter_reasons << " #{ filter_reason_prefix } SPN (#{ sname_hostname } ) hostname does not match (spn: #{ credential . server . components . snapshot . join ( '/' ) } )"
1108
1126
next
1109
1127
end
1110
1128
1111
1129
unless !@username || @username . casecmp? ( credential . client . components . last . to_s )
1112
- wlog ( "Filtered credential #{ file_path } ##{ index } reason: Username (#{ @username } ) does not match (username: #{ credential . client . components . last } )" )
1130
+ filter_reasons << "Filtered credential #{ path } ##{ index } reason: Username (#{ @username } ) does not match (username: #{ credential . client . components . last } )"
1113
1131
next
1114
1132
end
1115
1133
1116
- return credential
1134
+ return { credential : credential , filter : filter , filter_reasons : filter_reasons }
1117
1135
end
1118
1136
1119
- nil
1137
+ { credential : nil , filter : filter , filter_reasons : filter_reasons }
1120
1138
end
1121
1139
end
0 commit comments