Skip to content

Commit

Permalink
Merge pull request #539 from jtnord/JENKINS-73334
Browse files Browse the repository at this point in the history
[JENKINS-73334] make plugin FIPS-140 compliant by blocking PKCS#12 certificates when in FIPS mode
  • Loading branch information
jtnord committed Jul 5, 2024
2 parents 15ba4cd + 800d92c commit 56f5ca3
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import jenkins.model.Jenkins;
import jenkins.security.FIPS140;
import net.jcip.annotations.GuardedBy;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.lang.StringUtils;
Expand Down Expand Up @@ -331,7 +332,7 @@ protected static FormValidation validateCertificateKeystore(String type, byte[]
}

/**
* Let the user reference an uploaded file.
* Let the user reference an uploaded PKCS12 file.
*/
public static class UploadedKeyStoreSource extends KeyStoreSource implements Serializable {
/**
Expand Down Expand Up @@ -362,6 +363,7 @@ public static class UploadedKeyStoreSource extends KeyStoreSource implements Ser
@SuppressWarnings("unused") // by stapler
@Deprecated
public UploadedKeyStoreSource(String uploadedKeystore) {
ensureNotRunningInFIPSMode();
this.uploadedKeystoreBytes = StringUtils.isBlank(uploadedKeystore)
? null
: SecretBytes.fromBytes(DescriptorImpl.toByteArray(Secret.fromString(uploadedKeystore)));
Expand All @@ -376,6 +378,7 @@ public UploadedKeyStoreSource(String uploadedKeystore) {
@SuppressWarnings("unused") // by stapler
@Deprecated
public UploadedKeyStoreSource(@CheckForNull SecretBytes uploadedKeystore) {
ensureNotRunningInFIPSMode();
this.uploadedKeystoreBytes = uploadedKeystore;
}

Expand All @@ -388,6 +391,7 @@ public UploadedKeyStoreSource(@CheckForNull SecretBytes uploadedKeystore) {
@SuppressWarnings("unused") // by stapler
@DataBoundConstructor
public UploadedKeyStoreSource(FileItem uploadedCertFile, @CheckForNull SecretBytes uploadedKeystore) {
ensureNotRunningInFIPSMode();
if (uploadedCertFile != null) {
byte[] fileBytes = uploadedCertFile.get();
if (fileBytes.length != 0) {
Expand All @@ -405,6 +409,7 @@ public UploadedKeyStoreSource(FileItem uploadedCertFile, @CheckForNull SecretByt
* @since 2.1.5
*/
private Object readResolve() throws ObjectStreamException {
ensureNotRunningInFIPSMode();
if (uploadedKeystore != null && uploadedKeystoreBytes == null) {
return new UploadedKeyStoreSource(SecretBytes.fromBytes(DescriptorImpl.toByteArray(uploadedKeystore)));
}
Expand Down Expand Up @@ -453,13 +458,31 @@ public String toString() {
return "UploadedKeyStoreSource{uploadedKeystoreBytes=******}";
}

/*
* Prevents the use of any direct usage of the class when running in FIPS mode as PKCS12 is not compliant.
*/
private static void ensureNotRunningInFIPSMode() {
if (FIPS140.useCompliantAlgorithms()) {
throw new IllegalStateException("UploadedKeyStoreSource is not compliant with FIPS-140 and can not be used when Jenkins is in FIPS mode. " +
"This is an error in the calling code and an issue should be filed against the plugin that is calling to adapt to become FIPS compliant.");
}
}

/**
* {@inheritDoc}
*/
@Extension
public static class DescriptorImpl extends KeyStoreSourceDescriptor {
public static final String DEFAULT_VALUE = UploadedKeyStoreSource.class.getName() + ".default-value";

/**
* Creates the extension if we are not in FIPS mode, do <em>NOT</em> call this directly!
*/
@Restricted(NoExternalUse.class)
@Extension
public static DescriptorImpl extension() {
return FIPS140.useCompliantAlgorithms() ? null : new DescriptorImpl();
}

/**
* Decode the {@link Base64} keystore wrapped in a {@link Secret}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,15 @@

<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form" xmlns:st="jelly:stapler">
<f:entry title="${%Certificate}" field="keyStoreSource">
<f:hetero-radio field="keyStoreSource" descriptors="${descriptor.getPropertyType('keyStoreSource').applicableDescriptors}"/>
<j:set var="keyStoreSourceDescriptors" value="${descriptor.getPropertyType('keyStoreSource').applicableDescriptors}"/>
<j:choose>
<j:when test="${keyStoreSourceDescriptors.isEmpty()}">
<div class="warning">Jenkins has no <code>KeyStoreSources</code> available, Certificate credentials will not be able to be created.</div>
</j:when>
<j:otherwise>
<f:hetero-radio field="keyStoreSource" descriptors="${keyStoreSourceDescriptors}"/>
</j:otherwise>
</j:choose>
</f:entry>
<f:entry title="${%Password}" field="password">
<f:password value="${instance==null || instance.passwordEmpty ? '' : instance.password}"/>
Expand Down

0 comments on commit 56f5ca3

Please sign in to comment.