ronin-db-activerecord 0.1.6 → 0.2.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ChangeLog.md +91 -0
- data/README.md +49 -1
- data/db/migrate/0037_add_created_at_column_to_ronin_ports_table.rb +40 -0
- data/db/migrate/0038_add_created_at_column_to_ronin_services_table.rb +40 -0
- data/db/migrate/0039_create_ronin_cert_names_table.rb +37 -0
- data/db/migrate/0040_create_ronin_cert_issuers_table.rb +52 -0
- data/db/migrate/0041_create_ronin_cert_subjects_table.rb +54 -0
- data/db/migrate/0042_create_ronin_cert_subject_alt_names_table.rb +42 -0
- data/db/migrate/0043_create_ronin_certs_table.rb +61 -0
- data/db/migrate/0044_add_cert_id_column_to_ronin_open_ports_table.rb +35 -0
- data/db/migrate/0045_create_ronin_notes_table.rb +120 -0
- data/db/migrate/0046_create_ronin_web_vulns_table.rb +61 -0
- data/db/migrate/0047_create_ronin_phone_numbers_table.rb +47 -0
- data/db/migrate/0048_create_ronin_street_addresses_table.rb +46 -0
- data/db/migrate/0049_create_ronin_people_table.rb +48 -0
- data/db/migrate/0050_create_ronin_personal_connections_table.rb +48 -0
- data/db/migrate/0051_create_ronin_personal_phone_numbers_table.rb +48 -0
- data/db/migrate/0052_create_ronin_personal_email_addresses_table.rb +45 -0
- data/db/migrate/0053_create_ronin_personal_street_addresses_table.rb +47 -0
- data/db/migrate/0054_add_type_column_to_ronin_organizations_table.rb +33 -0
- data/db/migrate/0055_add_parent_id_column_to_ronin_organizations_table.rb +43 -0
- data/db/migrate/0056_create_ronin_organization_departments_table.rb +59 -0
- data/db/migrate/0057_create_ronin_organization_members_table.rb +62 -0
- data/db/migrate/0058_create_ronin_organization_customers_table.rb +52 -0
- data/db/migrate/0059_create_ronin_organization_phone_numbers_table.rb +47 -0
- data/db/migrate/0060_create_ronin_organization_email_addresses_table.rb +45 -0
- data/db/migrate/0061_create_ronin_organization_street_addresses_table.rb +47 -0
- data/db/migrate/0062_add_source_ip_column_to_http_requests_table.rb +30 -0
- data/db/migrate/0063_create_ronin_dns_queries_table.rb +41 -0
- data/db/migrate/0064_create_ronin_dns_records_table.rb +42 -0
- data/db/migrate/0065_create_ronin_organization_host_names_table.rb +43 -0
- data/db/migrate/0066_create_ronin_organization_ip_addresses_table.rb +43 -0
- data/gemspec.yml +1 -1
- data/lib/ronin/db/address.rb +1 -1
- data/lib/ronin/db/advisory.rb +66 -1
- data/lib/ronin/db/arch.rb +1 -1
- data/lib/ronin/db/asn.rb +15 -1
- data/lib/ronin/db/cert.rb +501 -0
- data/lib/ronin/db/cert_issuer.rb +78 -0
- data/lib/ronin/db/cert_name.rb +107 -0
- data/lib/ronin/db/cert_organization.rb +127 -0
- data/lib/ronin/db/cert_subject.rb +81 -0
- data/lib/ronin/db/cert_subject_alt_name.rb +88 -0
- data/lib/ronin/db/credential.rb +10 -1
- data/lib/ronin/db/dns_query.rb +98 -0
- data/lib/ronin/db/dns_record.rb +76 -0
- data/lib/ronin/db/email_address.rb +139 -1
- data/lib/ronin/db/host_name.rb +45 -1
- data/lib/ronin/db/host_name_ip_address.rb +1 -1
- data/lib/ronin/db/http_header_name.rb +1 -1
- data/lib/ronin/db/http_query_param.rb +1 -1
- data/lib/ronin/db/http_query_param_name.rb +1 -1
- data/lib/ronin/db/http_request.rb +13 -1
- data/lib/ronin/db/http_request_header.rb +1 -1
- data/lib/ronin/db/http_response.rb +1 -1
- data/lib/ronin/db/http_response_header.rb +1 -1
- data/lib/ronin/db/ip_address.rb +46 -1
- data/lib/ronin/db/ip_address_mac_address.rb +1 -1
- data/lib/ronin/db/mac_address.rb +28 -1
- data/lib/ronin/db/migrations.rb +1 -1
- data/lib/ronin/db/model/has_name.rb +18 -1
- data/lib/ronin/db/model/has_unique_name.rb +1 -1
- data/lib/ronin/db/model/importable.rb +1 -1
- data/lib/ronin/db/model/last_scanned_at.rb +1 -1
- data/lib/ronin/db/model.rb +1 -1
- data/lib/ronin/db/models.rb +44 -2
- data/lib/ronin/db/note.rb +199 -0
- data/lib/ronin/db/open_port.rb +104 -3
- data/lib/ronin/db/organization.rb +237 -3
- data/lib/ronin/db/organization_customer.rb +73 -0
- data/lib/ronin/db/organization_department.rb +97 -0
- data/lib/ronin/db/organization_email_address.rb +66 -0
- data/lib/ronin/db/organization_host_name.rb +65 -0
- data/lib/ronin/db/organization_ip_address.rb +65 -0
- data/lib/ronin/db/organization_member.rb +158 -0
- data/lib/ronin/db/organization_phone_number.rb +66 -0
- data/lib/ronin/db/organization_street_address.rb +66 -0
- data/lib/ronin/db/os.rb +35 -1
- data/lib/ronin/db/os_guess.rb +1 -1
- data/lib/ronin/db/password.rb +84 -1
- data/lib/ronin/db/person.rb +455 -0
- data/lib/ronin/db/personal_connection.rb +110 -0
- data/lib/ronin/db/personal_email_address.rb +66 -0
- data/lib/ronin/db/personal_phone_number.rb +76 -0
- data/lib/ronin/db/personal_street_address.rb +66 -0
- data/lib/ronin/db/phone_number.rb +330 -0
- data/lib/ronin/db/port.rb +110 -1
- data/lib/ronin/db/service.rb +130 -1
- data/lib/ronin/db/service_credential.rb +1 -1
- data/lib/ronin/db/software.rb +37 -1
- data/lib/ronin/db/software_vendor.rb +1 -1
- data/lib/ronin/db/street_address.rb +340 -0
- data/lib/ronin/db/url.rb +37 -1
- data/lib/ronin/db/url_query_param.rb +1 -1
- data/lib/ronin/db/url_query_param_name.rb +9 -1
- data/lib/ronin/db/url_scheme.rb +1 -1
- data/lib/ronin/db/user_name.rb +58 -1
- data/lib/ronin/db/vulnerability.rb +1 -1
- data/lib/ronin/db/web_credential.rb +1 -1
- data/lib/ronin/db/web_vuln.rb +348 -0
- metadata +57 -2
@@ -0,0 +1,501 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-db-activerecord - ActiveRecord backend for the Ronin Database.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2022-2024 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-db-activerecord is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-db-activerecord is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-db-activerecord. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/db/model'
|
22
|
+
require 'ronin/db/model/importable'
|
23
|
+
|
24
|
+
module Ronin
|
25
|
+
module DB
|
26
|
+
#
|
27
|
+
# Represents a SSL/TLS certificate.
|
28
|
+
#
|
29
|
+
# @since 0.2.0
|
30
|
+
#
|
31
|
+
class Cert < ActiveRecord::Base
|
32
|
+
|
33
|
+
include Model
|
34
|
+
include Model::Importable
|
35
|
+
|
36
|
+
# @!attribute [rw] id
|
37
|
+
# The primary ID of the certificate.
|
38
|
+
#
|
39
|
+
# @return [Integer]
|
40
|
+
attribute :id, :integer
|
41
|
+
|
42
|
+
# @!attribute [rw] serial
|
43
|
+
# The certificate's serial number.
|
44
|
+
#
|
45
|
+
# @return [String]
|
46
|
+
attribute :serial, :string
|
47
|
+
validates :serial, presence: true
|
48
|
+
|
49
|
+
# @!attribute [rw] version
|
50
|
+
# The certificate's version number.
|
51
|
+
#
|
52
|
+
# @return [Integer]
|
53
|
+
attribute :version, :integer
|
54
|
+
validates :version, presence: true
|
55
|
+
|
56
|
+
# @!attribute [rw] not_before
|
57
|
+
# When the certificate starts being valid.
|
58
|
+
#
|
59
|
+
# @return [Time]
|
60
|
+
attribute :not_before, :datetime
|
61
|
+
validates :not_before, presence: true
|
62
|
+
|
63
|
+
# @!attribute [rw] not_after
|
64
|
+
# When the certificate expires.
|
65
|
+
#
|
66
|
+
# @return [Time]
|
67
|
+
attribute :not_after, :datetime
|
68
|
+
validates :not_after, presence: true
|
69
|
+
|
70
|
+
# @!attribute [rw] issuer
|
71
|
+
# The certificate issuer information.
|
72
|
+
#
|
73
|
+
# @return [CertIssuer, nil]
|
74
|
+
#
|
75
|
+
# @note
|
76
|
+
# When the certificate is self-signed, {#issuer} will not be set.
|
77
|
+
belongs_to :issuer, class_name: 'CertIssuer',
|
78
|
+
optional: true
|
79
|
+
|
80
|
+
# @!attribute [rw] subject
|
81
|
+
# The certificate subject information.
|
82
|
+
#
|
83
|
+
# @return [CertSubject]
|
84
|
+
belongs_to :subject, class_name: 'CertSubject',
|
85
|
+
required: true
|
86
|
+
|
87
|
+
# @!attribute [rw] public_key_algorithm
|
88
|
+
# The public key algorithm.
|
89
|
+
#
|
90
|
+
# @return ["rsa", "dsa", "dh", "ec"]
|
91
|
+
enum :public_key_algorithm, {rsa: 'RSA', dsa: 'DSA', dh: 'DH', ec: 'EC'}
|
92
|
+
validates :public_key_algorithm, presence: true
|
93
|
+
|
94
|
+
# @!attribute [rw] public_key_size
|
95
|
+
# The public key size in bits.
|
96
|
+
#
|
97
|
+
# @return [Integer]
|
98
|
+
attribute :public_key_size, :integer
|
99
|
+
validates :public_key_size, presence: true
|
100
|
+
|
101
|
+
# @!attribute [rw] signing_algorithm
|
102
|
+
# The algorithm used to sign the certificate.
|
103
|
+
#
|
104
|
+
# @return [String]
|
105
|
+
attribute :signing_algorithm, :string
|
106
|
+
validates :signing_algorithm, presence: true
|
107
|
+
|
108
|
+
# @!attribute [rw] sha1_fingerprint
|
109
|
+
# The SHA1 fingerprint of the certificate.
|
110
|
+
#
|
111
|
+
# @return [String]
|
112
|
+
attribute :sha1_fingerprint
|
113
|
+
|
114
|
+
# @!attribute [rw] sha256_fingerprint
|
115
|
+
# The SHA256 fingerprint of the certificate.
|
116
|
+
#
|
117
|
+
# @return [String]
|
118
|
+
attribute :sha256_fingerprint
|
119
|
+
|
120
|
+
# @!attribute [rw] pem
|
121
|
+
# The PEM encoded version of the certificate.
|
122
|
+
#
|
123
|
+
# @return [String]
|
124
|
+
attribute :pem, :text
|
125
|
+
validates :pem, presence: true
|
126
|
+
|
127
|
+
# @!attribute [rw] created_at
|
128
|
+
# When the certificate was created.
|
129
|
+
#
|
130
|
+
# @return [Time]
|
131
|
+
attribute :created_at, :datetime
|
132
|
+
|
133
|
+
# @!attribute [rw] subject_alt_names
|
134
|
+
# The `subjectAltName`s of the certificate.
|
135
|
+
#
|
136
|
+
# @return [Array<CertSubjectAltName>]
|
137
|
+
has_many :subject_alt_names, class_name: 'CertSubjectAltName',
|
138
|
+
dependent: :destroy
|
139
|
+
|
140
|
+
# @!attribute [rw] open_ports
|
141
|
+
# The open ports that use this certificate.
|
142
|
+
#
|
143
|
+
# @return [Array<OpenPort>]
|
144
|
+
has_many :open_ports, dependent: :nullify
|
145
|
+
|
146
|
+
# @!attribute [rw] ip_addresses
|
147
|
+
# The IP addresses that use this certificate.
|
148
|
+
#
|
149
|
+
# @return [Array<IPAddress>]
|
150
|
+
has_many :ip_addresses, through: :open_ports
|
151
|
+
|
152
|
+
# @!attribute [rw] notes
|
153
|
+
# The associated notes.
|
154
|
+
#
|
155
|
+
# @return [Array<Note>]
|
156
|
+
#
|
157
|
+
# @since 0.2.0
|
158
|
+
has_many :notes
|
159
|
+
|
160
|
+
#
|
161
|
+
# Queries all active certificates.
|
162
|
+
#
|
163
|
+
# @return [Array<Cert>]
|
164
|
+
#
|
165
|
+
def self.active
|
166
|
+
now = DateTime.now
|
167
|
+
|
168
|
+
where(not_before: ..now, not_after: now...)
|
169
|
+
end
|
170
|
+
|
171
|
+
#
|
172
|
+
# Queries all expired certificates.
|
173
|
+
#
|
174
|
+
# @return [Array<Cert>]
|
175
|
+
#
|
176
|
+
def self.expired
|
177
|
+
where(not_after: ...Time.now)
|
178
|
+
end
|
179
|
+
|
180
|
+
#
|
181
|
+
# Queries all certificates with the issuer common name (`CN`).
|
182
|
+
#
|
183
|
+
# @param [String] name
|
184
|
+
# The issuer common name to search for.
|
185
|
+
#
|
186
|
+
# @return [Array<Cert>]
|
187
|
+
#
|
188
|
+
def self.with_issuer_common_name(name)
|
189
|
+
joins(:issuer).where(issuer: {common_name: name})
|
190
|
+
end
|
191
|
+
|
192
|
+
#
|
193
|
+
# Queries all certificates with the issuer common name (`O`).
|
194
|
+
#
|
195
|
+
# @param [String] name
|
196
|
+
# The issuer organization to search for.
|
197
|
+
#
|
198
|
+
# @return [Array<Cert>]
|
199
|
+
#
|
200
|
+
def self.with_issuer_organization(name)
|
201
|
+
joins(:issuer).where(issuer: {organization: name})
|
202
|
+
end
|
203
|
+
|
204
|
+
#
|
205
|
+
# Queries all certificates with the issuer common name (`OU`).
|
206
|
+
#
|
207
|
+
# @param [String] unit
|
208
|
+
# The issuer organizational unit name to search for.
|
209
|
+
#
|
210
|
+
# @return [Array<Cert>]
|
211
|
+
#
|
212
|
+
def self.with_issuer_organizational_unit(unit)
|
213
|
+
joins(:issuer).where(issuer: {organizational_unit: unit})
|
214
|
+
end
|
215
|
+
|
216
|
+
#
|
217
|
+
# Queries all certificates with the issuer common name (`L`).
|
218
|
+
#
|
219
|
+
# @param [String] locality
|
220
|
+
# The issuer locality to search for.
|
221
|
+
#
|
222
|
+
# @return [Array<Cert>]
|
223
|
+
#
|
224
|
+
def self.with_issuer_locality(locality)
|
225
|
+
joins(:issuer).where(issuer: {locality: locality})
|
226
|
+
end
|
227
|
+
|
228
|
+
#
|
229
|
+
# Queries all certificates with the issuer common name (`ST`).
|
230
|
+
#
|
231
|
+
# @param [String] state
|
232
|
+
# The issuer state name to search for.
|
233
|
+
#
|
234
|
+
# @return [Array<Cert>]
|
235
|
+
#
|
236
|
+
def self.with_issuer_state(state)
|
237
|
+
joins(:issuer).where(issuer: {state: state})
|
238
|
+
end
|
239
|
+
|
240
|
+
#
|
241
|
+
# Queries all certificates with the issuer common name (`C`).
|
242
|
+
#
|
243
|
+
# @param [String] country
|
244
|
+
# The issuer's two-letter country code to search for.
|
245
|
+
#
|
246
|
+
# @return [Array<Cert>]
|
247
|
+
#
|
248
|
+
def self.with_issuer_country(country)
|
249
|
+
joins(:issuer).where(issuer: {country: country})
|
250
|
+
end
|
251
|
+
|
252
|
+
#
|
253
|
+
# Queries all certificates with the subject state (`O`).
|
254
|
+
#
|
255
|
+
# @param [String] name
|
256
|
+
# The organization name to search for.
|
257
|
+
#
|
258
|
+
# @return [Array<Cert>]
|
259
|
+
#
|
260
|
+
def self.with_organization(name)
|
261
|
+
joins(:subject).where(subject: {organization: name})
|
262
|
+
end
|
263
|
+
|
264
|
+
#
|
265
|
+
# Queries all certificates with the subject state (`OU`).
|
266
|
+
#
|
267
|
+
# @param [String] unit
|
268
|
+
# The organizational unit name to search for.
|
269
|
+
#
|
270
|
+
# @return [Array<Cert>]
|
271
|
+
#
|
272
|
+
def self.with_organizational_unit(unit)
|
273
|
+
joins(:subject).where(subject: {organizational_unit: unit})
|
274
|
+
end
|
275
|
+
|
276
|
+
#
|
277
|
+
# Queries all certificates with the subject state (`L`).
|
278
|
+
#
|
279
|
+
# @param [String] locality
|
280
|
+
# The locality to search for.
|
281
|
+
#
|
282
|
+
# @return [Array<Cert>]
|
283
|
+
#
|
284
|
+
def self.with_locality(locality)
|
285
|
+
joins(:subject).where(subject: {locality: locality})
|
286
|
+
end
|
287
|
+
|
288
|
+
#
|
289
|
+
# Queries all certificates with the subject state (`ST`).
|
290
|
+
#
|
291
|
+
# @param [String] state
|
292
|
+
# The state name to search for.
|
293
|
+
#
|
294
|
+
# @return [Array<Cert>]
|
295
|
+
#
|
296
|
+
def self.with_state(state)
|
297
|
+
joins(:subject).where(subject: {state: state})
|
298
|
+
end
|
299
|
+
|
300
|
+
#
|
301
|
+
# Queries all certificates with the subject country (`C`).
|
302
|
+
#
|
303
|
+
# @param [String] country
|
304
|
+
# The two-letter country code to search for.
|
305
|
+
#
|
306
|
+
# @return [Array<Cert>]
|
307
|
+
#
|
308
|
+
def self.with_country(country)
|
309
|
+
joins(:subject).where(subject: {country: country})
|
310
|
+
end
|
311
|
+
|
312
|
+
#
|
313
|
+
# Queries all certificates with the common name (`CN`).
|
314
|
+
#
|
315
|
+
# @param [String] name
|
316
|
+
# The common name to search for.
|
317
|
+
#
|
318
|
+
# @return [Array<Cert>]
|
319
|
+
#
|
320
|
+
def self.with_common_name(name)
|
321
|
+
joins(subject: [:common_name]).where(
|
322
|
+
subject: {
|
323
|
+
ronin_cert_names: {
|
324
|
+
name: name
|
325
|
+
}
|
326
|
+
}
|
327
|
+
)
|
328
|
+
end
|
329
|
+
|
330
|
+
#
|
331
|
+
# Queries all certificates with the `subjectAltName` value.
|
332
|
+
#
|
333
|
+
# @param [String] name
|
334
|
+
# The host name or IP address to query.
|
335
|
+
#
|
336
|
+
# @return [Array<Cert>]
|
337
|
+
#
|
338
|
+
def self.with_subject_alt_name(name)
|
339
|
+
joins(subject_alt_names: [:name]).where(
|
340
|
+
subject_alt_names: {
|
341
|
+
ronin_cert_names: {
|
342
|
+
name: name
|
343
|
+
}
|
344
|
+
}
|
345
|
+
)
|
346
|
+
end
|
347
|
+
|
348
|
+
#
|
349
|
+
# Looks up the certificate.
|
350
|
+
#
|
351
|
+
# @param [OpenSSL::X509::Certificate] cert
|
352
|
+
# The X509 certificate object or PEM string.
|
353
|
+
#
|
354
|
+
# @return [Cert, nil]
|
355
|
+
# The matching certificate.
|
356
|
+
#
|
357
|
+
def self.lookup(cert)
|
358
|
+
find_by(sha256_fingerprint: Digest::SHA256.hexdigest(cert.to_der))
|
359
|
+
end
|
360
|
+
|
361
|
+
#
|
362
|
+
# Imports an SSL/TLS X509 certificate into the database.
|
363
|
+
#
|
364
|
+
# @param [OpenSSL::X509::Certificate] cert
|
365
|
+
# The certificate object to import.
|
366
|
+
#
|
367
|
+
# @return [Cert]
|
368
|
+
# The imported certificate.
|
369
|
+
#
|
370
|
+
def self.import(cert)
|
371
|
+
case (public_key = cert.public_key)
|
372
|
+
when OpenSSL::PKey::RSA
|
373
|
+
public_key_algorithm = :rsa
|
374
|
+
public_key_size = public_key.n.num_bits
|
375
|
+
when OpenSSL::PKey::DSA
|
376
|
+
public_key_algorithm = :dsa
|
377
|
+
public_key_size = public_key.p.num_bits
|
378
|
+
when OpenSSL::PKey::DH
|
379
|
+
public_key_algorithm = :dh
|
380
|
+
public_key_size = public_key.p.num_bits
|
381
|
+
when OpenSSL::PKey::EC
|
382
|
+
public_key_algorithm = :ec
|
383
|
+
|
384
|
+
public_key_text = public_key.to_text
|
385
|
+
public_key_size = if (match = public_key_text.match(/\((\d+) bit\)/))
|
386
|
+
match[1].to_i
|
387
|
+
end
|
388
|
+
else
|
389
|
+
raise(NotImplementedError,"unsupported public key type: #{public_key.inspect}")
|
390
|
+
end
|
391
|
+
|
392
|
+
der = cert.to_der
|
393
|
+
|
394
|
+
create(
|
395
|
+
serial: cert.serial.to_s(16),
|
396
|
+
version: cert.version,
|
397
|
+
|
398
|
+
not_before: cert.not_before,
|
399
|
+
not_after: cert.not_after,
|
400
|
+
|
401
|
+
# NOTE: set #issuer to nil if the cert is self-signed
|
402
|
+
issuer: unless cert.issuer == cert.subject
|
403
|
+
CertIssuer.import(cert.issuer)
|
404
|
+
end,
|
405
|
+
|
406
|
+
subject: CertSubject.import(cert.subject),
|
407
|
+
|
408
|
+
public_key_algorithm: public_key_algorithm,
|
409
|
+
public_key_size: public_key_size,
|
410
|
+
|
411
|
+
signing_algorithm: cert.signature_algorithm,
|
412
|
+
|
413
|
+
sha1_fingerprint: Digest::SHA1.hexdigest(der),
|
414
|
+
sha256_fingerprint: Digest::SHA256.hexdigest(der),
|
415
|
+
|
416
|
+
pem: cert.to_pem
|
417
|
+
) do |new_cert|
|
418
|
+
if (subject_alt_name = cert.find_extension('subjectAltName'))
|
419
|
+
CertSubjectAltName.parse(subject_alt_name.value).each do |name|
|
420
|
+
new_cert.subject_alt_names.new(
|
421
|
+
name: CertName.find_or_import(name)
|
422
|
+
)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
#
|
429
|
+
# The subject's common name (`CN`).
|
430
|
+
#
|
431
|
+
# @return [String]
|
432
|
+
#
|
433
|
+
def common_name
|
434
|
+
subject.common_name
|
435
|
+
end
|
436
|
+
|
437
|
+
#
|
438
|
+
# The subject's organization (`O`).
|
439
|
+
#
|
440
|
+
# @return [String]
|
441
|
+
#
|
442
|
+
def organization
|
443
|
+
subject.organization
|
444
|
+
end
|
445
|
+
|
446
|
+
#
|
447
|
+
# The subject's organizational unit (`OU`).
|
448
|
+
#
|
449
|
+
# @return [String]
|
450
|
+
#
|
451
|
+
def organizational_unit
|
452
|
+
subject.organizational_unit
|
453
|
+
end
|
454
|
+
|
455
|
+
#
|
456
|
+
# The subject's locality (`L`).
|
457
|
+
#
|
458
|
+
# @return [String]
|
459
|
+
#
|
460
|
+
def locality
|
461
|
+
subject.locality
|
462
|
+
end
|
463
|
+
|
464
|
+
#
|
465
|
+
# The subject's state (`ST`).
|
466
|
+
#
|
467
|
+
# @return [String]
|
468
|
+
#
|
469
|
+
def state
|
470
|
+
subject.state
|
471
|
+
end
|
472
|
+
|
473
|
+
#
|
474
|
+
# The subject's country (`C`).
|
475
|
+
#
|
476
|
+
# @return [String]
|
477
|
+
#
|
478
|
+
def country
|
479
|
+
subject.country
|
480
|
+
end
|
481
|
+
|
482
|
+
#
|
483
|
+
# Converts the certificate back into PEM format.
|
484
|
+
#
|
485
|
+
# @return [String]
|
486
|
+
#
|
487
|
+
def to_pem
|
488
|
+
pem
|
489
|
+
end
|
490
|
+
|
491
|
+
alias to_s to_pem
|
492
|
+
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
require 'ronin/db/cert_issuer'
|
498
|
+
require 'ronin/db/cert_subject'
|
499
|
+
require 'ronin/db/cert_subject_alt_name'
|
500
|
+
require 'ronin/db/open_port'
|
501
|
+
require 'ronin/db/note'
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-db-activerecord - ActiveRecord backend for the Ronin Database.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2022-2024 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-db-activerecord is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-db-activerecord is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-db-activerecord. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/db/cert_organization'
|
22
|
+
|
23
|
+
module Ronin
|
24
|
+
module DB
|
25
|
+
#
|
26
|
+
# Represents the issuer of a SSL/TLS certificate.
|
27
|
+
#
|
28
|
+
# @since 0.2.0
|
29
|
+
#
|
30
|
+
class CertIssuer < CertOrganization
|
31
|
+
|
32
|
+
# @!attribute [rw] common_name
|
33
|
+
# The issuer's common name (`CN`).
|
34
|
+
#
|
35
|
+
# @return [String, nil]
|
36
|
+
#
|
37
|
+
# @note
|
38
|
+
# Some Equifax certs do not set the issuer's common name (CN),
|
39
|
+
# so {#common_name} may sometimes return `nil`.
|
40
|
+
attribute :common_name, :string
|
41
|
+
validates :common_name, uniqueness: {
|
42
|
+
scope: [
|
43
|
+
:email_address,
|
44
|
+
:organization,
|
45
|
+
:organizational_unit,
|
46
|
+
:locality,
|
47
|
+
:state,
|
48
|
+
:country
|
49
|
+
]
|
50
|
+
}
|
51
|
+
|
52
|
+
# @!attribute [rw] certs
|
53
|
+
# The certificates that share this issuer information.
|
54
|
+
#
|
55
|
+
# @return [Array<Cert>]
|
56
|
+
has_many :certs, foreign_key: :issuer_id,
|
57
|
+
dependent: :destroy
|
58
|
+
|
59
|
+
#
|
60
|
+
# Imports the certificate issuer's X509 distinguished name.
|
61
|
+
#
|
62
|
+
# @param [OpenSSL::X509::Name, String] name
|
63
|
+
# The X509 name to parse and import.
|
64
|
+
#
|
65
|
+
# @return [CertIssuer]
|
66
|
+
# The imported or pre-existing certificate issuer.
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
#
|
70
|
+
def self.import(name)
|
71
|
+
find_or_create_by(parse(name))
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
require 'ronin/db/cert'
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-db-activerecord - ActiveRecord backend for the Ronin Database.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2022-2024 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-db-activerecord is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-db-activerecord is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-db-activerecord. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/db/model'
|
22
|
+
require 'ronin/db/model/has_unique_name'
|
23
|
+
require 'ronin/db/model/importable'
|
24
|
+
|
25
|
+
module Ronin
|
26
|
+
module DB
|
27
|
+
#
|
28
|
+
# Represents a certificate's common name (`CN`) or `subjectAltName`s.
|
29
|
+
#
|
30
|
+
# @since 0.2.0
|
31
|
+
#
|
32
|
+
class CertName < ActiveRecord::Base
|
33
|
+
|
34
|
+
include Model
|
35
|
+
include Model::HasUniqueName
|
36
|
+
include Model::Importable
|
37
|
+
|
38
|
+
# @!attribute [rw] id
|
39
|
+
# The primary key of the certificate name.
|
40
|
+
#
|
41
|
+
# @return [Integer]
|
42
|
+
attribute :id, :integer
|
43
|
+
|
44
|
+
# @!attribute [rw] created_at
|
45
|
+
# When the certificate name was created.
|
46
|
+
#
|
47
|
+
# @return [Time]
|
48
|
+
attribute :created_at, :datetime
|
49
|
+
|
50
|
+
# @!attribute [rw] certs
|
51
|
+
# The certificates that use this name as their common name (`CN`).
|
52
|
+
#
|
53
|
+
# @return [Array<CertSubject>]
|
54
|
+
has_many :subjects, class_name: 'CertSubject',
|
55
|
+
foreign_key: :common_name_id,
|
56
|
+
dependent: :destroy
|
57
|
+
|
58
|
+
# @!attribute [rw] subject_alt_names
|
59
|
+
# The certificates that use this name as one of their `subjectAltName`
|
60
|
+
# values.
|
61
|
+
#
|
62
|
+
# @return [Array<CertSubjectAltName>]
|
63
|
+
has_many :subject_alt_names, class_name: 'CertSubjectAltName',
|
64
|
+
foreign_key: :name_id,
|
65
|
+
dependent: :destroy
|
66
|
+
|
67
|
+
#
|
68
|
+
# Looks up the certificate name.
|
69
|
+
#
|
70
|
+
# @param [String] name
|
71
|
+
# The name to search by.
|
72
|
+
#
|
73
|
+
# @return [CertName, nil]
|
74
|
+
# The found certificate name with the matching name.
|
75
|
+
#
|
76
|
+
def self.lookup(name)
|
77
|
+
find_by(name: name)
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Imports the certificate name.
|
82
|
+
#
|
83
|
+
# @param [String] name
|
84
|
+
# The certificate name to import.
|
85
|
+
#
|
86
|
+
# @return [CertName]
|
87
|
+
# The newly created certificate name.
|
88
|
+
#
|
89
|
+
def self.import(name)
|
90
|
+
create(name: name)
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# Converts the certificate name to a String.
|
95
|
+
#
|
96
|
+
# @return [String]
|
97
|
+
#
|
98
|
+
def to_s
|
99
|
+
name
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
require 'ronin/db/cert_subject_alt_name'
|
107
|
+
require 'ronin/db/cert'
|