Skip to content

Commit 27f9a8f

Browse files
committed
[compat] copy X509 (internal) collection arguments
1 parent 97fccf9 commit 27f9a8f

7 files changed

Lines changed: 177 additions & 25 deletions

File tree

‎src/main/java/org/jruby/ext/openssl/X509CRL.java‎

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
import org.jruby.exceptions.RaiseException;
8181
import org.jruby.ext.openssl.log.Logger;
8282
import org.jruby.ext.openssl.shim.ASN1Shim;
83+
import org.jruby.ext.openssl.util.RubySupport;
8384
import org.jruby.ext.openssl.x509store.PEMInputOutput;
8485
import org.jruby.runtime.Arity;
8586
import org.jruby.runtime.Block;
@@ -88,7 +89,6 @@
8889
import org.jruby.runtime.builtin.Variable;
8990
import org.jruby.runtime.builtin.IRubyObject;
9091
import org.jruby.util.ByteList;
91-
import org.jruby.util.TypeConverter;
9292

9393
import static org.jruby.ext.openssl.OpenSSL.*;
9494
import static org.jruby.ext.openssl.X509._X509;
@@ -250,7 +250,7 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
250250
final X509CRLEntry[] revokedSorted = revokedCRLs.toArray(new X509CRLEntry[revokedCRLs.size()]);
251251
Arrays.sort(revokedSorted, 0, revokedSorted.length, Comparator.comparing(X509CRLEntry::getRevocationDate));
252252
for (X509CRLEntry entry : revokedSorted) {
253-
revoked().append( X509Revoked.newInstance(context, entry) );
253+
revokedInternal().append( X509Revoked.newInstance(context, entry) );
254254
}
255255
}
256256

@@ -544,44 +544,56 @@ public IRubyObject set_next_update(final ThreadContext context, IRubyObject val)
544544
}
545545

546546
@JRubyMethod
547-
public RubyArray revoked() {
547+
public RubyArray revoked(ThreadContext context) {
548+
return revoked == null ? context.runtime.newEmptyArray() : RubySupport.copyArray(context.runtime, revoked);
549+
}
550+
551+
private RubyArray revokedInternal() {
548552
return revoked == null ? revoked = getRuntime().newArray(4) : revoked;
549553
}
550554

551555
@JRubyMethod(name="revoked=")
552556
public IRubyObject set_revoked(final IRubyObject revoked) {
553557
if (!(revoked instanceof RubyArray)) throw getRuntime().newTypeError(revoked, getRuntime().getArray());
558+
this.revoked = X509Revoked.copyRevoked(getRuntime(), (RubyArray) revoked);
554559
this.changed = true;
555-
return this.revoked = (RubyArray) revoked;
560+
return revoked;
556561
}
557562

558563
@JRubyMethod
559564
public IRubyObject add_revoked(final ThreadContext context, IRubyObject val) {
560-
final X509Revoked revoked = asRevoked(context.runtime, val);
565+
final X509Revoked revoked = X509Revoked.asRevoked(context.runtime, val);
561566
if (revoked.getTime() == null) throw X509Revoked.newRevokedError(context.runtime, "revocation time not set");
567+
revokedInternal().append(X509Revoked.copy(context.runtime, revoked));
562568
this.changed = true;
563-
revoked().callMethod(context, "<<", val); return val;
569+
return val;
564570
}
565571

566572
@JRubyMethod
567-
public RubyArray extensions() {
568-
return this.extensions;
573+
public RubyArray extensions(ThreadContext context) {
574+
return extensions == null ? context.runtime.newEmptyArray() : RubySupport.copyArray(context.runtime, extensions);
575+
}
576+
577+
private RubyArray extensionsInternal() {
578+
return extensions == null ? extensions = getRuntime().newArray(4) : extensions;
569579
}
570580

571581
@SuppressWarnings("unchecked")
572582
@JRubyMethod(name="extensions=")
573583
public IRubyObject set_extensions(final IRubyObject extensions) {
574584
if (!(extensions instanceof RubyArray)) throw getRuntime().newTypeError(extensions, getRuntime().getArray());
585+
this.extensions = X509Extension.copyExtensions(getRuntime(), (RubyArray) extensions);
575586
this.changed = true;
576-
return this.extensions = (RubyArray) extensions;
587+
return extensions;
577588
}
578589

579590
@JRubyMethod
580591
public IRubyObject add_extension(final IRubyObject extension) {
581592
if (!(extension instanceof X509Extension)) throw getRuntime().newTypeError(
582593
extension, X509Extension._Extension(getRuntime()));
594+
extensionsInternal().append(X509Extension.copy(getRuntime(), (X509Extension) extension));
583595
this.changed = true;
584-
extensions().append(extension); return extension;
596+
return extension;
585597
}
586598

587599
@JRubyMethod
@@ -598,13 +610,13 @@ public IRubyObject sign(final ThreadContext context, final IRubyObject key, IRub
598610

599611
if ( revoked != null ) {
600612
for ( int i = 0; i < revoked.size(); i++ ) {
601-
final X509Revoked rev = asRevoked(runtime, revoked.eltInternal(i));
613+
final X509Revoked rev = X509Revoked.asRevoked(runtime, revoked.eltInternal(i));
602614
final DateTime revTime = rev.getTime();
603615
if (revTime == null) throw X509Revoked.newRevokedError(runtime, "revocation time not set");
604616

605617
final Extensions revExts;
606618
if ( rev.hasExtensions() ) {
607-
final RubyArray exts = rev.extensions();
619+
final RubyArray exts = rev.extensions(context);
608620
final ASN1Encodable[] array = new ASN1Encodable[ exts.size() ];
609621
for ( int j = 0; j < exts.size(); j++ ) {
610622
final X509Extension ext = (X509Extension) exts.entry(j);
@@ -754,7 +766,7 @@ private ASN1Sequence toASN1Sequence(final Ruby runtime) {
754766
final ASN1EncodableVector revokedEntries = new ASN1EncodableVector(revoked.size());
755767
try {
756768
for (int i = 0; i < revoked.size(); i++) {
757-
revokedEntries.add(asRevoked(runtime, revoked.eltInternal(i)).toASN1Sequence());
769+
revokedEntries.add(X509Revoked.asRevoked(runtime, revoked.eltInternal(i)).toASN1Sequence());
758770
}
759771
} catch (IOException e) {
760772
throw newCRLError(runtime, e);
@@ -782,11 +794,6 @@ private ASN1Sequence toASN1Sequence(final Ruby runtime) {
782794
});
783795
}
784796

785-
private static X509Revoked asRevoked(final Ruby runtime, final IRubyObject value) {
786-
if (!(value instanceof X509Revoked)) throw runtime.newTypeError(value, X509Revoked._Revoked(runtime));
787-
return (X509Revoked) value;
788-
}
789-
790797
private ASN1Primitive readCRL(final Ruby runtime) {
791798
try {
792799
return ASN1.readObject(getEncodedOriginal());

‎src/main/java/org/jruby/ext/openssl/X509Extension.java‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,31 @@ static X509Extension newExtension(final Ruby runtime, ASN1ObjectIdentifier objec
164164
return ext;
165165
}
166166

167+
static X509Extension copy(final Ruby runtime, final X509Extension extension) {
168+
final X509Extension copy = new X509Extension(runtime, _Extension(runtime));
169+
copy.setRealObjectID(extension.getRealObjectID());
170+
copy.setRealCritical(extension.isRealCritical());
171+
try {
172+
copy.value = extension.getRealValueEncoded().clone();
173+
} catch (IOException e) {
174+
throw newExtensionError(runtime, e);
175+
}
176+
return copy;
177+
}
178+
179+
static RubyArray copyExtensions(final Ruby runtime, final RubyArray array) {
180+
final int length = array.size();
181+
final IRubyObject[] copy = new IRubyObject[length];
182+
for (int i = 0; i < length; i++) {
183+
final IRubyObject extension = array.eltInternal(i);
184+
if (!(extension instanceof X509Extension)) {
185+
throw runtime.newTypeError(extension, X509Extension._Extension(runtime));
186+
}
187+
copy[i] = copy(runtime, (X509Extension) extension);
188+
}
189+
return RubyArray.newArrayNoCopy(runtime, copy);
190+
}
191+
167192
ASN1ObjectIdentifier getRealObjectID() {
168193
return objectID;
169194
}

‎src/main/java/org/jruby/ext/openssl/X509Revoked.java‎

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import org.jruby.RubyTime;
5454
import org.jruby.anno.JRubyMethod;
5555
import org.jruby.exceptions.RaiseException;
56+
import org.jruby.ext.openssl.util.RubySupport;
5657
import org.jruby.runtime.ThreadContext;
5758
import org.jruby.runtime.builtin.IRubyObject;
5859
import org.jruby.runtime.Visibility;
@@ -124,6 +125,32 @@ public X509Revoked(Ruby runtime, RubyClass type) {
124125
super(runtime,type);
125126
}
126127

128+
static X509Revoked copy(final Ruby runtime, final X509Revoked revoked) {
129+
final X509Revoked copy = new X509Revoked(runtime, _Revoked(runtime));
130+
copy.serial = BN.newInstance(runtime, revoked.getSerial().getValue());
131+
if (revoked.time != null) {
132+
copy.time = RubyTime.newTime(runtime, revoked.time.getJavaDate().getTime());
133+
}
134+
if (revoked.extensions != null) {
135+
copy.extensions = X509Extension.copyExtensions(runtime, revoked.extensions);
136+
}
137+
return copy;
138+
}
139+
140+
static RubyArray copyRevoked(final Ruby runtime, final RubyArray array) {
141+
final int length = array.size();
142+
final IRubyObject[] copy = new IRubyObject[length];
143+
for (int i = 0; i < length; i++) {
144+
copy[i] = X509Revoked.copy(runtime, X509Revoked.asRevoked(runtime, array.eltInternal(i)));
145+
}
146+
return RubyArray.newArrayNoCopy(runtime, copy);
147+
}
148+
149+
static X509Revoked asRevoked(final Ruby runtime, final IRubyObject value) {
150+
if (!(value instanceof X509Revoked)) throw runtime.newTypeError(value, _Revoked(runtime));
151+
return (X509Revoked) value;
152+
}
153+
127154
@JRubyMethod(name = "initialize", rest = true, visibility = Visibility.PRIVATE)
128155
public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) {
129156
serial = BN.newInstance(context.runtime, BigInteger.ZERO);
@@ -171,24 +198,30 @@ boolean hasExtensions() {
171198
}
172199

173200
@JRubyMethod
174-
public RubyArray extensions() {
201+
public RubyArray extensions(ThreadContext context) {
202+
return extensions == null ? context.runtime.newEmptyArray() : RubySupport.copyArray(context.runtime, extensions);
203+
}
204+
205+
private RubyArray extensionsInternal() {
175206
return extensions == null ? extensions = RubyArray.newArray(getRuntime(), 4) : extensions;
176207
}
177208

178209
@JRubyMethod(name = "extensions=")
179-
public IRubyObject set_extensions(final IRubyObject extensions) {
210+
public IRubyObject set_extensions(ThreadContext context, final IRubyObject extensions) {
180211
if (!(extensions instanceof RubyArray)) {
181-
throw getRuntime().newTypeError(extensions, getRuntime().getArray());
212+
throw context.runtime.newTypeError(extensions, context.runtime.getArray());
182213
}
183-
return this.extensions = (RubyArray) extensions;
214+
this.extensions = X509Extension.copyExtensions(context.runtime, (RubyArray) extensions);
215+
return extensions;
184216
}
185217

186218
@JRubyMethod
187-
public IRubyObject add_extension(final ThreadContext context, final IRubyObject ext) {
219+
public IRubyObject add_extension(ThreadContext context, final IRubyObject ext) {
188220
if (!(ext instanceof X509Extension)) {
189221
throw context.runtime.newTypeError(ext, X509Extension._Extension(context.runtime));
190222
}
191-
return extensions().append(ext);
223+
extensionsInternal().append(X509Extension.copy(context.runtime, (X509Extension) ext));
224+
return ext;
192225
}
193226

194227
@Override
@@ -216,7 +249,7 @@ ASN1Sequence toASN1Sequence() throws IOException {
216249
vec.add(new ASN1Integer(getSerial().getValue()));
217250
vec.add(new Time(revokedTime.toDate()));
218251
if (hasExtensions()) {
219-
final RubyArray extensions = this.extensions();
252+
final RubyArray extensions = this.extensions;
220253
final ASN1Encodable[] entries = new ASN1Encodable[extensions.size()];
221254
for (int i = 0; i < extensions.size(); i++) {
222255
entries[i] = ((X509Extension) extensions.eltInternal(i)).toASN1Sequence();

‎src/main/java/org/jruby/ext/openssl/util/RubySupport.java‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.jcodings.specific.ASCIIEncoding;
3232
import org.jcodings.specific.UTF8Encoding;
3333
import org.jruby.Ruby;
34+
import org.jruby.RubyArray;
3435
import org.jruby.RubyBasicObject;
3536
import org.jruby.RubyClass;
3637
import org.jruby.RubyEncoding;
@@ -90,6 +91,13 @@ public static Date timeToJavaDate(final ThreadContext context, final IRubyObject
9091
return ((RubyTime) value).getJavaDate();
9192
}
9293

94+
public static RubyArray copyArray(final Ruby runtime, final RubyArray array) {
95+
final int length = array.size();
96+
final IRubyObject[] copy = new IRubyObject[length];
97+
for (int i = 0; i < length; i++) copy[i] = array.eltInternal(i);
98+
return RubyArray.newArrayNoCopy(runtime, copy);
99+
}
100+
93101
// error/exception factory helpers
94102

95103
public static RaiseException newIOError(Ruby runtime, IOException e) {

‎test/x509/test_x509crl.rb‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,50 @@ def test_encoded_crl_tracks_changes
228228
assert_equal 1, crl.to_java.getRevokedCertificates.size
229229
end
230230

231+
def test_assigned_revoked_and_extensions_are_owned_by_crl
232+
crl = OpenSSL::X509::CRL.new
233+
revoked = OpenSSL::X509::Revoked.new
234+
revoked.serial = 1
235+
revoked.time = Time.at(1)
236+
extension = OpenSSL::X509::Extension.new('crlNumber', '1')
237+
revoked_entries = [revoked]
238+
extensions = [extension]
239+
crl.revoked = revoked_entries
240+
crl.extensions = extensions
241+
242+
revoked_entries.clear
243+
extensions.clear
244+
revoked.serial = 2
245+
extension.oid = 'keyUsage'
246+
247+
assert_equal 1, crl.revoked.size
248+
assert_equal 1, crl.revoked.first.serial
249+
assert_equal 1, crl.extensions.size
250+
assert_equal 'crlNumber', crl.extensions.first.oid
251+
252+
crl.revoked.clear
253+
crl.extensions.clear
254+
assert_equal 1, crl.revoked.size
255+
assert_equal 1, crl.extensions.size
256+
257+
assert_raises(TypeError) { crl.revoked = [Object.new] }
258+
assert_raises(TypeError) { crl.extensions = [Object.new] }
259+
assert_equal 1, crl.revoked.size
260+
assert_equal 1, crl.extensions.size
261+
262+
crl = OpenSSL::X509::CRL.new
263+
revoked.serial = 1
264+
extension.oid = 'crlNumber'
265+
266+
crl.add_revoked(revoked)
267+
crl.add_extension(extension)
268+
269+
revoked.serial = 2
270+
extension.oid = 'keyUsage'
271+
assert_equal 1, crl.revoked.first.serial
272+
assert_equal 'crlNumber', crl.extensions.first.oid
273+
end
274+
231275
def test_revoked_requires_time
232276
now = Time.now
233277
key = OpenSSL::PKey::RSA.new TEST_KEY_RSA2048

‎test/x509/test_x509ext.rb‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ def test_subject_alt_name_invalid_ip
7878
'(name=subjectAltName, value=IP:not.a.valid.ip.address)', error.message
7979
end
8080

81+
def test_extension_factory_accepts_shifted_array_arguments
82+
args = [:discard, 'basicConstraints', 'CA:FALSE']
83+
args.shift
84+
85+
ext = OpenSSL::X509::ExtensionFactory.new.create_extension(args)
86+
assert_equal 'basicConstraints', ext.oid
87+
assert_equal 'CA:FALSE', ext.value
88+
end
89+
8190
def test_to_a
8291
ext = OpenSSL::X509::Extension.new('1.1.1.1.1.1', 'foo')
8392
assert_equal [ '1.1.1.1.1.1', 'foo', false ], ext.to_a

‎test/x509/test_x509revoked.rb‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,32 @@ def test_serial=
3737
assert_equal '4242', rev.serial.to_s
3838
end
3939

40+
def test_assigned_extensions_are_owned_by_revoked
41+
rev = OpenSSL::X509::Revoked.new
42+
extension = OpenSSL::X509::Extension.new('CRLReason', 'keyCompromise')
43+
extensions = [extension]
44+
rev.extensions = extensions
45+
46+
extensions.clear
47+
extension.oid = 'keyUsage'
48+
assert_equal 1, rev.extensions.size
49+
assert_equal 'CRLReason', rev.extensions.first.oid
50+
51+
rev.extensions.clear
52+
assert_equal 1, rev.extensions.size
53+
54+
assert_raises(TypeError) { rev.extensions = [Object.new] }
55+
assert_equal 1, rev.extensions.size
56+
57+
rev = OpenSSL::X509::Revoked.new
58+
extension.oid = 'CRLReason'
59+
60+
rev.add_extension(extension)
61+
62+
extension.oid = 'keyUsage'
63+
assert_equal 'CRLReason', rev.extensions.first.oid
64+
end
65+
4066
def test_to_der
4167
rev = OpenSSL::X509::Revoked.new
4268
rev.serial = 42

0 commit comments

Comments
 (0)