ronin-db-activerecord 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/ChangeLog.md +91 -0
  4. data/README.md +51 -2
  5. data/db/migrate/0037_add_created_at_column_to_ronin_ports_table.rb +40 -0
  6. data/db/migrate/0038_add_created_at_column_to_ronin_services_table.rb +40 -0
  7. data/db/migrate/0039_create_ronin_cert_names_table.rb +37 -0
  8. data/db/migrate/0040_create_ronin_cert_issuers_table.rb +52 -0
  9. data/db/migrate/0041_create_ronin_cert_subjects_table.rb +54 -0
  10. data/db/migrate/0042_create_ronin_cert_subject_alt_names_table.rb +42 -0
  11. data/db/migrate/0043_create_ronin_certs_table.rb +61 -0
  12. data/db/migrate/0044_add_cert_id_column_to_ronin_open_ports_table.rb +35 -0
  13. data/db/migrate/0045_create_ronin_notes_table.rb +120 -0
  14. data/db/migrate/0046_create_ronin_web_vulns_table.rb +61 -0
  15. data/db/migrate/0047_create_ronin_phone_numbers_table.rb +47 -0
  16. data/db/migrate/0048_create_ronin_street_addresses_table.rb +46 -0
  17. data/db/migrate/0049_create_ronin_people_table.rb +48 -0
  18. data/db/migrate/0050_create_ronin_personal_connections_table.rb +48 -0
  19. data/db/migrate/0051_create_ronin_personal_phone_numbers_table.rb +48 -0
  20. data/db/migrate/0052_create_ronin_personal_email_addresses_table.rb +45 -0
  21. data/db/migrate/0053_create_ronin_personal_street_addresses_table.rb +47 -0
  22. data/db/migrate/0054_add_type_column_to_ronin_organizations_table.rb +33 -0
  23. data/db/migrate/0055_add_parent_id_column_to_ronin_organizations_table.rb +43 -0
  24. data/db/migrate/0056_create_ronin_organization_departments_table.rb +59 -0
  25. data/db/migrate/0057_create_ronin_organization_members_table.rb +62 -0
  26. data/db/migrate/0058_create_ronin_organization_customers_table.rb +52 -0
  27. data/db/migrate/0059_create_ronin_organization_phone_numbers_table.rb +47 -0
  28. data/db/migrate/0060_create_ronin_organization_email_addresses_table.rb +45 -0
  29. data/db/migrate/0061_create_ronin_organization_street_addresses_table.rb +47 -0
  30. data/db/migrate/0062_add_source_ip_column_to_http_requests_table.rb +30 -0
  31. data/db/migrate/0063_create_ronin_dns_queries_table.rb +41 -0
  32. data/db/migrate/0064_create_ronin_dns_records_table.rb +42 -0
  33. data/db/migrate/0065_create_ronin_organization_host_names_table.rb +43 -0
  34. data/db/migrate/0066_create_ronin_organization_ip_addresses_table.rb +43 -0
  35. data/gemspec.yml +3 -2
  36. data/lib/ronin/db/address.rb +1 -1
  37. data/lib/ronin/db/advisory.rb +66 -1
  38. data/lib/ronin/db/arch.rb +1 -1
  39. data/lib/ronin/db/asn.rb +15 -1
  40. data/lib/ronin/db/cert.rb +501 -0
  41. data/lib/ronin/db/cert_issuer.rb +78 -0
  42. data/lib/ronin/db/cert_name.rb +107 -0
  43. data/lib/ronin/db/cert_organization.rb +127 -0
  44. data/lib/ronin/db/cert_subject.rb +81 -0
  45. data/lib/ronin/db/cert_subject_alt_name.rb +88 -0
  46. data/lib/ronin/db/credential.rb +10 -1
  47. data/lib/ronin/db/dns_query.rb +98 -0
  48. data/lib/ronin/db/dns_record.rb +76 -0
  49. data/lib/ronin/db/email_address.rb +139 -1
  50. data/lib/ronin/db/host_name.rb +45 -1
  51. data/lib/ronin/db/host_name_ip_address.rb +1 -1
  52. data/lib/ronin/db/http_header_name.rb +1 -1
  53. data/lib/ronin/db/http_query_param.rb +1 -1
  54. data/lib/ronin/db/http_query_param_name.rb +1 -1
  55. data/lib/ronin/db/http_request.rb +13 -1
  56. data/lib/ronin/db/http_request_header.rb +1 -1
  57. data/lib/ronin/db/http_response.rb +1 -1
  58. data/lib/ronin/db/http_response_header.rb +1 -1
  59. data/lib/ronin/db/ip_address.rb +46 -1
  60. data/lib/ronin/db/ip_address_mac_address.rb +1 -1
  61. data/lib/ronin/db/mac_address.rb +28 -1
  62. data/lib/ronin/db/migrations.rb +1 -1
  63. data/lib/ronin/db/model/has_name.rb +18 -1
  64. data/lib/ronin/db/model/has_unique_name.rb +1 -1
  65. data/lib/ronin/db/model/importable.rb +1 -1
  66. data/lib/ronin/db/model/last_scanned_at.rb +1 -1
  67. data/lib/ronin/db/model.rb +1 -1
  68. data/lib/ronin/db/models.rb +44 -2
  69. data/lib/ronin/db/note.rb +199 -0
  70. data/lib/ronin/db/open_port.rb +104 -3
  71. data/lib/ronin/db/organization.rb +237 -3
  72. data/lib/ronin/db/organization_customer.rb +73 -0
  73. data/lib/ronin/db/organization_department.rb +97 -0
  74. data/lib/ronin/db/organization_email_address.rb +66 -0
  75. data/lib/ronin/db/organization_host_name.rb +65 -0
  76. data/lib/ronin/db/organization_ip_address.rb +65 -0
  77. data/lib/ronin/db/organization_member.rb +158 -0
  78. data/lib/ronin/db/organization_phone_number.rb +66 -0
  79. data/lib/ronin/db/organization_street_address.rb +66 -0
  80. data/lib/ronin/db/os.rb +35 -1
  81. data/lib/ronin/db/os_guess.rb +1 -1
  82. data/lib/ronin/db/password.rb +84 -1
  83. data/lib/ronin/db/person.rb +455 -0
  84. data/lib/ronin/db/personal_connection.rb +110 -0
  85. data/lib/ronin/db/personal_email_address.rb +66 -0
  86. data/lib/ronin/db/personal_phone_number.rb +76 -0
  87. data/lib/ronin/db/personal_street_address.rb +66 -0
  88. data/lib/ronin/db/phone_number.rb +330 -0
  89. data/lib/ronin/db/port.rb +110 -1
  90. data/lib/ronin/db/service.rb +130 -1
  91. data/lib/ronin/db/service_credential.rb +1 -1
  92. data/lib/ronin/db/software.rb +37 -1
  93. data/lib/ronin/db/software_vendor.rb +1 -1
  94. data/lib/ronin/db/street_address.rb +340 -0
  95. data/lib/ronin/db/url.rb +37 -1
  96. data/lib/ronin/db/url_query_param.rb +1 -1
  97. data/lib/ronin/db/url_query_param_name.rb +9 -1
  98. data/lib/ronin/db/url_scheme.rb +1 -1
  99. data/lib/ronin/db/user_name.rb +58 -1
  100. data/lib/ronin/db/vulnerability.rb +1 -1
  101. data/lib/ronin/db/web_credential.rb +1 -1
  102. data/lib/ronin/db/web_vuln.rb +348 -0
  103. metadata +60 -4
@@ -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'