Skip to content

Commit

Permalink
Add CRIU security provider
Browse files Browse the repository at this point in the history
Signed-off-by: Alon Shalev Housfater <alonsh@ca.ibm.com>
  • Loading branch information
alon-sh committed Apr 29, 2022
1 parent 2fae6f1 commit 1303674
Show file tree
Hide file tree
Showing 7 changed files with 965 additions and 6 deletions.
15 changes: 9 additions & 6 deletions closed/GensrcJ9JCL.gmk
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,15 @@ ifeq (true,$(OPENJ9_ENABLE_OPENJDK_METHODHANDLES))
JPP_TAGS += OPENJDK_METHODHANDLES
endif # OPENJ9_ENABLE_OPENJDK_METHODHANDLES

# OpenJ9 CRIU only supports Linux, so we only need to consider the unix sub-directory.
OPENJDK_SOURCE_PROCESS_ENVIRONMENT := $(TOPDIR)/src/java.base/unix/classes/java/lang/ProcessEnvironment.java
OPENJDK_STAGED_PROCESS_ENVIRONMENT := $(patsubst $(TOPDIR)/%,$(SUPPORT_OUTPUTDIR)/overlay/%,$(OPENJDK_SOURCE_PROCESS_ENVIRONMENT))

$(OPENJDK_STAGED_PROCESS_ENVIRONMENT) : $(OPENJDK_SOURCE_PROCESS_ENVIRONMENT)
$(call install-file)
# OpenJ9 CRIU only supports Linux, so we only need to consider the share and unix sub-directories.
$(eval $(call SetupCopyFiles,COPY_OVERLAY_FILES, \
SRC := $(TOPDIR), \
DEST := $(SUPPORT_OUTPUTDIR)/overlay, \
FILES := \
src/java.base/share/classes/java/security/Security.java \
src/java.base/unix/classes/java/lang/ProcessEnvironment.java \
))

# invoke JPP to preprocess java source files
# $1 - configuration
Expand All @@ -76,7 +79,7 @@ IncludeIfUnsure := -includeIfUnsure -noWarnIncludeIf

$(J9JCL_SOURCES_DONEFILE) : \
$(foreach dir, $(JppSourceDirs), $(call RecursiveWildcard,$(dir),*.java)) \
$(OPENJDK_STAGED_PROCESS_ENVIRONMENT)
$(COPY_OVERLAY_FILES)
@$(ECHO) Building OpenJ9 Java Preprocessor
@$(MKDIR) -p $(J9TOOLS_DIR)
$(MAKE) $(MAKE_ARGS) -C $(OPENJ9_TOPDIR)/sourcetools -f buildj9tools.mk \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*[INCLUDE-IF CRIU_SUPPORT]*/
/*
* ===========================================================================
* (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved
* ===========================================================================
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* IBM designates this particular file as subject to the "Classpath" exception
* as provided by IBM in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
*
* ===========================================================================
*/

package com.ibm.security.criu;

import java.security.Provider;

/**
* The CRIUSECProvider is a security provider that is used as follows when CRIU
* is enabled. During the checkpoint phase, all other security providers are
* removed, except CRIUSECProvider, and the digests are cleared, to ensure that
* no state is saved during checkpoint that is then restored during the restore
* phase. During the resore phase, CRIUSECProvider is removed and the other
* security providers are added back.
*/
public final class CRIUSECProvider extends Provider {

private static final long serialVersionUID = -3240458633432287743L;

public CRIUSECProvider() {
super("CRIUSEC", "1", "CRIUSEC Provider for IBM Semeru");

String packageName = CRIUSECProvider.class.getPackage().getName() + ".";

String[] aliases = new String[] { "SHA",
"SHA1",
"OID.1.3.14.3.2.26",
"1.3.14.3.2.26" };

// SHA1PRNG is the default name needed by the jdk, but SHA1 is not used, rather it reads directly from /dev/random.
putService(new Service(this, "MessageDigest", "SHA-1", packageName + "SHA", java.util.Arrays.asList(aliases), null));
putService(new Service(this, "SecureRandom", "SHA1PRNG", packageName + "NativePRNG", null, null));
}

/**
* Resets the security digests.
*/
public static void resetDigests() {
DigestBase.resetDigests();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
/*[INCLUDE-IF CRIU_SUPPORT]*/
/*
* Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

/*
* ===========================================================================
* (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved
* ===========================================================================
*/

package com.ibm.security.criu;

import java.security.MessageDigestSpi;
import java.security.DigestException;
import java.security.ProviderException;
import java.util.Arrays;
import java.util.Objects;
import java.util.ArrayList;

import sun.security.action.GetPropertyAction;

/**
* Common base message digest implementation for the Sun provider.
* It implements all the JCA methods as suitable for a Java message digest
* implementation of an algorithm based on a compression function (as all
* commonly used algorithms are). The individual digest subclasses only need to
* implement the following methods:
*
* . abstract void implCompress(byte[] b, int ofs);
* . abstract void implDigest(byte[] out, int ofs);
* . abstract void implReset();
*
* See the inline documentation for details.
*
* @since 1.5
* @author Andreas Sterbenz
*/
abstract class DigestBase extends MessageDigestSpi implements Cloneable {

// one element byte array, temporary storage for update(byte)
private byte[] oneByte;

// algorithm name to use in the exception message
private final String algorithm;
// length of the message digest in bytes
private final int digestLength;

// size of the input to the compression function in bytes
private final int blockSize;
// buffer to store partial blocks, blockSize bytes large
// Subclasses should not access this array directly except possibly in their
// implDigest() method. See MD5.java as an example.
byte[] buffer;
// offset into buffer
private int bufferOffset;

// number of bytes processed so far
// subclasses should not modify this value
// also used as a flag to indicate reset status
// -1: need to call engineReset() before next call to update()
// 0: is already reset
long bytesProcessed;

private static final ArrayList<DigestBase> digestList = new ArrayList<>();

// tracing for CRIUSEC
private static final boolean debug = Boolean.parseBoolean(GetPropertyAction.privilegedGetProperty
("enable.j9internal.checkpoint.security.api.debug", "false"));

/**
* This method is required to ensure that no state is maintained when checkpointing.
*/
static void resetDigests() {
if (debug) {
System.out.println("Clearing all digests ...");
}
synchronized (digestList) {
for (DigestBase digest : digestList) {
digest.engineReset();
}
digestList.clear();
}
}

/**
* Main constructor.
*/
DigestBase(String algorithm, int digestLength, int blockSize) {
super();
this.algorithm = algorithm;
this.digestLength = digestLength;
this.blockSize = blockSize;
this.buffer = new byte[blockSize];

// adding digest object to digest list to be reset
// during post-checkpoint hook
synchronized (digestList) {
digestList.add(this);
}
}

// return digest length. See JCA doc.
protected final int engineGetDigestLength() {
return digestLength;
}

// single byte update. See JCA doc.
protected final void engineUpdate(byte b) {
if (oneByte == null) {
oneByte = new byte[1];
}
oneByte[0] = b;
engineUpdate(oneByte, 0, 1);
}

// array update. See JCA doc.
protected final void engineUpdate(byte[] b, int ofs, int len) {
if (len == 0) {
return;
}
if ((ofs < 0) || (len < 0) || (ofs > b.length - len)) {
throw new ArrayIndexOutOfBoundsException();
}
if (bytesProcessed < 0) {
engineReset();
}
bytesProcessed += len;
// if buffer is not empty, we need to fill it before proceeding
if (bufferOffset != 0) {
int n = Math.min(len, blockSize - bufferOffset);
System.arraycopy(b, ofs, buffer, bufferOffset, n);
bufferOffset += n;
ofs += n;
len -= n;
if (bufferOffset >= blockSize) {
// compress completed block now
implCompress(buffer, 0);
bufferOffset = 0;
}
}
// compress complete blocks
if (len >= blockSize) {
int limit = ofs + len;
ofs = implCompressMultiBlock(b, ofs, limit - blockSize);
len = limit - ofs;
}
// copy remainder to buffer
if (len > 0) {
System.arraycopy(b, ofs, buffer, 0, len);
bufferOffset = len;
}
}

// compress complete blocks
private int implCompressMultiBlock(byte[] b, int ofs, int limit) {
implCompressMultiBlockCheck(b, ofs, limit);
return implCompressMultiBlock0(b, ofs, limit);
}

private int implCompressMultiBlock0(byte[] b, int ofs, int limit) {
for (; ofs <= limit; ofs += blockSize) {
implCompress(b, ofs);
}
return ofs;
}

private void implCompressMultiBlockCheck(byte[] b, int ofs, int limit) {
if (limit < 0) {
return; // not an error because implCompressMultiBlockImpl won't execute if limit < 0
// and an exception is thrown if ofs < 0.
}

Objects.requireNonNull(b);

if ((ofs < 0) || (ofs >= b.length)) {
throw new ArrayIndexOutOfBoundsException(ofs);
}

int endIndex = (limit / blockSize) * blockSize + blockSize - 1;
if (endIndex >= b.length) {
throw new ArrayIndexOutOfBoundsException(endIndex);
}
}

// reset this object. See JCA doc.
protected final void engineReset() {
if (bytesProcessed == 0) {
// already reset, ignore
return;
}
implReset();
bufferOffset = 0;
bytesProcessed = 0;
Arrays.fill(buffer, (byte) 0x00);
}

// return the digest. See JCA doc.
protected final byte[] engineDigest() {
byte[] b = new byte[digestLength];
try {
engineDigest(b, 0, b.length);
} catch (DigestException e) {
throw (ProviderException)
new ProviderException("Internal error").initCause(e);
}
return b;
}

// return the digest in the specified array. See JCA doc.
protected final int engineDigest(byte[] out, int ofs, int len)
throws DigestException {
if (len < digestLength) {
throw new DigestException("Length must be at least "
+ digestLength + " for " + algorithm + "digests");
}
if ((ofs < 0) || (len < 0) || (ofs > (out.length - len))) {
throw new DigestException("Buffer too short to store digest");
}
if (bytesProcessed < 0) {
engineReset();
}
implDigest(out, ofs);
bytesProcessed = -1;
return digestLength;
}

/**
* Core compression function. Processes blockSize bytes at a time
* and updates the state of this object.
*/
abstract void implCompress(byte[] b, int ofs);

/**
* Return the digest. Subclasses do not need to reset() themselves,
* DigestBase calls implReset() when necessary.
*/
abstract void implDigest(byte[] out, int ofs);

/**
* Reset subclass specific state to their initial values. DigestBase
* calls this method when necessary.
*/
abstract void implReset();

@Override
public Object clone() throws CloneNotSupportedException {
DigestBase copy = (DigestBase) super.clone();
copy.buffer = copy.buffer.clone();
return copy;
}

// padding used for the MD5, and SHA-* message digests
static final byte[] padding;

static {
// we need 128 byte padding for SHA-384/512
// and an additional 8 bytes for the high 8 bytes of the 16
// byte bit counter in SHA-384/512
padding = new byte[136];
padding[0] = (byte) 0x80;
}
}
Loading

0 comments on commit 1303674

Please sign in to comment.