Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JENKINS-73334] make plugin FIPS-140 compliant by blocking PKCS#12 certificates when in FIPS mode #539

Merged
merged 3 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
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 @@ -404,7 +405,7 @@ private Object readResolve() {
}

/**
* 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 @@ -435,6 +436,7 @@ public static class UploadedKeyStoreSource extends KeyStoreSource implements Ser
@SuppressWarnings("unused") // by stapler
@Deprecated
public UploadedKeyStoreSource(String uploadedKeystore) {
ensureNotRunningInFIPSMode();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other plugins are using this code directly, so we can not just not register the extension.
Whilst this will break other plugins, those other plugins are not Compliant and are usign a byte[] that is a PKCS#12 cert - so they are not compliant anyway.

I am not intending to fix /adapt any of these plugins.

this.uploadedKeystoreBytes = StringUtils.isBlank(uploadedKeystore)
? null
: SecretBytes.fromBytes(DescriptorImpl.toByteArray(Secret.fromString(uploadedKeystore)));
Expand All @@ -449,6 +451,7 @@ public UploadedKeyStoreSource(String uploadedKeystore) {
@SuppressWarnings("unused") // by stapler
@Deprecated
public UploadedKeyStoreSource(@CheckForNull SecretBytes uploadedKeystore) {
ensureNotRunningInFIPSMode();
this.uploadedKeystoreBytes = uploadedKeystore;
}

Expand All @@ -461,6 +464,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 @@ -478,6 +482,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 @@ -526,13 +531,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>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a small UI warning as I intend to follow up with https://issues.jenkins.io/browse/JENKINS-73335 so this should not live for that long, hence no tests for this

</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
Loading