26
26
import com .cloudbees .jenkins .plugins .sshcredentials .SSHUserPrivateKey ;
27
27
import com .cloudbees .plugins .credentials .CredentialsProvider ;
28
28
import com .cloudbees .plugins .credentials .CredentialsScope ;
29
- import com .cloudbees .plugins .credentials .CredentialsSnapshotTaker ;
30
29
import edu .umd .cs .findbugs .annotations .CheckForNull ;
31
30
import edu .umd .cs .findbugs .annotations .NonNull ;
32
31
import hudson .DescriptorExtensionList ;
33
32
import hudson .Extension ;
34
33
import hudson .model .AbstractDescribableImpl ;
35
34
import hudson .model .Descriptor ;
35
+ import hudson .model .Items ;
36
36
import hudson .remoting .Channel ;
37
37
import hudson .util .Secret ;
38
38
import java .io .File ;
39
39
import java .io .IOException ;
40
40
import java .io .ObjectStreamException ;
41
41
import java .io .Serializable ;
42
42
import java .io .StringReader ;
43
+ import java .lang .reflect .InvocationTargetException ;
44
+ import java .lang .reflect .Method ;
43
45
import java .util .ArrayList ;
44
46
import java .util .Arrays ;
45
47
import java .util .Collections ;
46
48
import java .util .List ;
47
49
import java .util .concurrent .TimeUnit ;
48
50
import java .util .logging .Level ;
49
51
import java .util .logging .Logger ;
52
+
53
+ import hudson .util .XStream2 ;
50
54
import jenkins .model .Jenkins ;
51
55
import net .jcip .annotations .GuardedBy ;
52
56
import org .apache .commons .io .FileUtils ;
@@ -148,16 +152,6 @@ private synchronized Object readResolve() throws ObjectStreamException {
148
152
return this ;
149
153
}
150
154
151
- private Object writeReplace () {
152
- if (/* XStream */ Channel .current () == null ) {
153
- return this ;
154
- }
155
- if (privateKeySource == null || privateKeySource .isSnapshotSource ()) {
156
- return this ;
157
- }
158
- return CredentialsProvider .snapshot (this );
159
- }
160
-
161
155
/**
162
156
* {@inheritDoc}
163
157
*/
@@ -290,7 +284,9 @@ public long getPrivateKeysLastModified() {
290
284
*
291
285
* @return {@code true} if and only if the source is self contained.
292
286
* @since 1.7
287
+ * @deprecated no more used since FileOnMaster- and Users- PrivateKeySource are deprecated too
293
288
*/
289
+ @ Deprecated
294
290
public boolean isSnapshotSource () {
295
291
return false ;
296
292
}
@@ -371,7 +367,9 @@ public String getDisplayName() {
371
367
372
368
/**
373
369
* Let the user reference a file on the disk.
370
+ * @deprecated This approach has security vulnerability and should be migrated to {@link DirectEntryPrivateKeySource}
374
371
*/
372
+ @ Deprecated
375
373
public static class FileOnMasterPrivateKeySource extends PrivateKeySource {
376
374
377
375
/**
@@ -394,8 +392,6 @@ public static class FileOnMasterPrivateKeySource extends PrivateKeySource {
394
392
*/
395
393
private transient volatile long nextCheckLastModified ;
396
394
397
-
398
- @ DataBoundConstructor
399
395
public FileOnMasterPrivateKeySource (String privateKeyFile ) {
400
396
this .privateKeyFile = privateKeyFile ;
401
397
}
@@ -436,7 +432,12 @@ private Object readResolve() {
436
432
// this is a borked upgrade, not actually the file name but is actually the key contents
437
433
return new DirectEntryPrivateKeySource (privateKeyFile );
438
434
}
439
- return this ;
435
+
436
+ Jenkins .getActiveInstance ().checkPermission (Jenkins .RUN_SCRIPTS );
437
+
438
+ LOGGER .log (Level .INFO , "SECURITY-440: Migrating FileOnMasterPrivateKeySource to DirectEntryPrivateKeySource" );
439
+ // read the content of the file and then migrate to Direct
440
+ return new DirectEntryPrivateKeySource (getPrivateKeys ());
440
441
}
441
442
442
443
@ Override
@@ -453,26 +454,13 @@ public long getPrivateKeysLastModified() {
453
454
}
454
455
return lastModified ;
455
456
}
456
-
457
- /**
458
- * {@inheritDoc}
459
- */
460
- @ Extension
461
- public static class DescriptorImpl extends PrivateKeySourceDescriptor {
462
-
463
- /**
464
- * {@inheritDoc}
465
- */
466
- @ Override
467
- public String getDisplayName () {
468
- return Messages .BasicSSHUserPrivateKey_FileOnMasterPrivateKeySourceDisplayName ();
469
- }
470
- }
471
457
}
472
458
473
459
/**
474
460
* Let the user
461
+ * @deprecated This approach has security vulnerability and should be migrated to {@link DirectEntryPrivateKeySource}
475
462
*/
463
+ @ Deprecated
476
464
public static class UsersPrivateKeySource extends PrivateKeySource {
477
465
478
466
/**
@@ -490,10 +478,6 @@ public static class UsersPrivateKeySource extends PrivateKeySource {
490
478
*/
491
479
private transient volatile long nextCheckLastModified ;
492
480
493
- @ DataBoundConstructor
494
- public UsersPrivateKeySource () {
495
- }
496
-
497
481
private List <File > files () {
498
482
List <File > files = new ArrayList <File >();
499
483
File sshHome = new File (new File (System .getProperty ("user.home" )), ".ssh" );
@@ -535,51 +519,27 @@ public long getPrivateKeysLastModified() {
535
519
return lastModified ;
536
520
}
537
521
538
- /**
539
- * {@inheritDoc}
540
- */
541
- @ Extension
542
- public static class DescriptorImpl extends PrivateKeySourceDescriptor {
522
+ private Object readResolve () {
523
+ Jenkins .getActiveInstance ().checkPermission (Jenkins .RUN_SCRIPTS );
543
524
544
- /**
545
- * {@inheritDoc}
546
- */
547
- @ Override
548
- public String getDisplayName () {
549
- return Messages .BasicSSHUserPrivateKey_UsersPrivateKeySourceDisplayName ();
550
- }
525
+ LOGGER .log (Level .INFO , "SECURITY-440: Migrating UsersPrivateKeySource to DirectEntryPrivateKeySource" );
526
+ // read the content of the file and then migrate to Direct
527
+ return new DirectEntryPrivateKeySource (getPrivateKeys ());
551
528
}
552
529
}
553
530
554
- /**
555
- * @since 1.7
556
- */
557
- @ Extension
558
- public static class CredentialsSnapshotTakerImpl extends CredentialsSnapshotTaker <SSHUserPrivateKey > {
559
-
560
- /**
561
- * {@inheritDoc}
562
- */
563
- @ Override
564
- public Class <SSHUserPrivateKey > type () {
565
- return SSHUserPrivateKey .class ;
566
- }
567
-
568
- /**
569
- * {@inheritDoc}
570
- */
571
- @ Override
572
- public SSHUserPrivateKey snapshot (SSHUserPrivateKey credentials ) {
573
- if (credentials instanceof BasicSSHUserPrivateKey ) {
574
- final PrivateKeySource keySource = ((BasicSSHUserPrivateKey ) credentials ).getPrivateKeySource ();
575
- if (keySource .isSnapshotSource ()) {
576
- return credentials ;
577
- }
578
- }
579
- final Secret passphrase = credentials .getPassphrase ();
580
- return new BasicSSHUserPrivateKey (credentials .getScope (), credentials .getId (), credentials .getUsername (),
581
- new DirectEntryPrivateKeySource (credentials .getPrivateKeys ()),
582
- passphrase == null ? null : passphrase .getEncryptedValue (), credentials .getDescription ());
531
+ static {
532
+ try {
533
+ // the critical field allow the permission check to make the XML read to fail completely in case of violation
534
+ // TODO: Remove reflection once baseline is updated past 2.85.
535
+ Method m = XStream2 .class .getMethod ("addCriticalField" , Class .class , String .class );
536
+ m .invoke (Items .XSTREAM2 , BasicSSHUserPrivateKey .class , "privateKeySource" );
537
+ } catch (IllegalAccessException e ) {
538
+ throw new ExceptionInInitializerError (e );
539
+ } catch (InvocationTargetException e ) {
540
+ throw new ExceptionInInitializerError (e );
541
+ } catch (NoSuchMethodException e ) {
542
+ throw new ExceptionInInitializerError (e );
583
543
}
584
544
}
585
545
}
0 commit comments