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