diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java index 1308fab19..185c97e0a 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java @@ -56,7 +56,6 @@ import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.transport.BaseReceivePack.ReceiveConfig; import org.junit.Test; /** Test for push certificate parsing. */ @@ -92,7 +91,7 @@ public class PushCertificateParserTest { new DfsRepositoryDescription("repo")); PushCertificateParser parser = new PushCertificateParser( - db, new ReceiveConfig(cfg)); + db, new SignedPushConfig(cfg)); parser.receiveHeader(pckIn, false); parser.addCommand(pckIn.readStringRaw()); assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readStringRaw()); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java index 63b9bbbd3..b687bb2d7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java @@ -253,6 +253,7 @@ public abstract class BaseReceivePack { private Long packSize; private PushCertificateParser pushCertificateParser; + private SignedPushConfig signedPushConfig; /** * Get the push certificate used to verify the pusher's identity. @@ -264,7 +265,7 @@ public abstract class BaseReceivePack { * @since 4.1 */ public PushCertificate getPushCertificate() throws IOException { - return pushCertificateParser.build(); + return getPushCertificateParser().build(); } /** @@ -288,7 +289,7 @@ public abstract class BaseReceivePack { refFilter = RefFilter.DEFAULT; advertisedHaves = new HashSet(); clientShallowCommits = new HashSet(); - pushCertificateParser = new PushCertificateParser(db, cfg); + signedPushConfig = cfg.signedPush; } /** Configuration for receive operations. */ @@ -310,8 +311,7 @@ public abstract class BaseReceivePack { final boolean allowNonFastForwards; final boolean allowOfsDelta; - final String certNonceSeed; - final int certNonceSlopLimit; + final SignedPushConfig signedPush; ReceiveConfig(final Config config) { checkReceivedObjects = config.getBoolean( @@ -332,8 +332,7 @@ public abstract class BaseReceivePack { "denynonfastforwards", false); //$NON-NLS-1$ allowOfsDelta = config.getBoolean("repack", "usedeltabaseoffset", //$NON-NLS-1$ //$NON-NLS-2$ true); - certNonceSeed = config.getString("receive", null, "certnonceseed"); //$NON-NLS-1$ //$NON-NLS-2$ - certNonceSlopLimit = config.getInt("receive", "certnonceslop", 0); //$NON-NLS-1$ //$NON-NLS-2$ + signedPush = SignedPushConfig.KEY.parse(config); } ObjectChecker newObjectChecker() { @@ -789,6 +788,26 @@ public abstract class BaseReceivePack { return quiet; } + /** + * Set the configuration for push certificate verification. + * + * @param cfg + * new configuration; if this object is null or its {@link + * SignedPushConfig#getCertNonceSeed()} is null, push certificate + * verification will be disabled. + * @since 4.1 + */ + public void setSignedPushConfig(SignedPushConfig cfg) { + signedPushConfig = cfg; + } + + private PushCertificateParser getPushCertificateParser() { + if (pushCertificateParser == null) { + pushCertificateParser = new PushCertificateParser(db, signedPushConfig); + } + return pushCertificateParser; + } + /** * Get the user agent of the client. *

@@ -1020,7 +1039,7 @@ public abstract class BaseReceivePack { adv.advertiseCapability(CAPABILITY_REPORT_STATUS); if (allowQuiet) adv.advertiseCapability(CAPABILITY_QUIET); - String nonce = pushCertificateParser.getAdvertiseNonce(); + String nonce = getPushCertificateParser().getAdvertiseNonce(); if (nonce != null) { adv.advertiseCapability(nonce); } @@ -1043,6 +1062,7 @@ public abstract class BaseReceivePack { * @throws IOException */ protected void recvCommands() throws IOException { + PushCertificateParser certParser = getPushCertificateParser(); FirstLine firstLine = null; for (;;) { String rawLine; @@ -1069,14 +1089,13 @@ public abstract class BaseReceivePack { line = firstLine.getLine(); if (line.equals(GitProtocolConstants.OPTION_PUSH_CERT)) { - pushCertificateParser.receiveHeader(pckIn, - !isBiDirectionalPipe()); + certParser.receiveHeader(pckIn, !isBiDirectionalPipe()); continue; } } if (rawLine.equals(PushCertificateParser.BEGIN_SIGNATURE)) { - pushCertificateParser.receiveSignature(pckIn); + certParser.receiveSignature(pckIn); continue; } @@ -1093,10 +1112,10 @@ public abstract class BaseReceivePack { cmd.setRef(refs.get(cmd.getRefName())); } commands.add(cmd); - if (pushCertificateParser.enabled()) { + if (certParser.enabled()) { // Must use raw line with optional newline so signed payload can be // reconstructed. - pushCertificateParser.addCommand(cmd, rawLine); + certParser.addCommand(cmd, rawLine); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java index fc7c19bfa..ccc82da64 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java @@ -57,7 +57,6 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.transport.BaseReceivePack.ReceiveConfig; import org.eclipse.jgit.transport.PushCertificate.NonceStatus; import org.eclipse.jgit.util.RawParseUtils; @@ -115,11 +114,16 @@ public class PushCertificateParser { private final List commands; private final StringBuilder rawCommands; - PushCertificateParser(Repository into, ReceiveConfig cfg) { - nonceSlopLimit = cfg.certNonceSlopLimit; - nonceGenerator = cfg.certNonceSeed != null - ? new HMACSHA1NonceGenerator(cfg.certNonceSeed) - : null; + PushCertificateParser(Repository into, SignedPushConfig cfg) { + if (cfg != null) { + nonceSlopLimit = cfg.getCertNonceSlopLimit(); + nonceGenerator = cfg.getCertNonceSeed() != null + ? new HMACSHA1NonceGenerator(cfg.certNonceSeed) + : null; + } else { + nonceSlopLimit = 0; + nonceGenerator = null; + } db = into; commands = new ArrayList<>(); rawCommands = new StringBuilder(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SignedPushConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SignedPushConfig.java new file mode 100644 index 000000000..ad2a9969f --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SignedPushConfig.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2015, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.transport; + +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.Config.SectionParser; + +/** + * Configuration for server-side signed push verification. + * + * @since 4.1 + */ +public class SignedPushConfig { + /** + * Key for {@link Config#get(SectionParser)}. + * + * @since 4.1 + */ + public static final SectionParser KEY = + new SectionParser() { + public SignedPushConfig parse(Config cfg) { + return new SignedPushConfig(cfg); + } + }; + + String certNonceSeed; + int certNonceSlopLimit; + + /** + * Create a new config with default values disabling push verification. + * + * @since 4.1 + */ + public SignedPushConfig() { + } + + SignedPushConfig(Config cfg) { + certNonceSeed = cfg.getString("receive", null, "certnonceseed"); //$NON-NLS-1$ //$NON-NLS-2$ + certNonceSlopLimit = cfg.getInt("receive", "certnonceslop", 0); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Set the seed used by the nonce verifier. + *

+ * Setting this to a non-null value enables push certificate verification. + * + * @param seed + * new seed value. + * @since 4.1 + */ + public void setCertNonceSeed(String seed) { + certNonceSeed = seed; + } + + /** + * @return the configured seed used by the nonce verifier. + * @since 4.1 + */ + public String getCertNonceSeed() { + return certNonceSeed; + } + + /** + * Set the nonce slop limit. + *

+ * Old but valid nonces within this limit will be accepted. + * + * @param limit + * new limit in seconds. + * @since 4.1 + */ + public void setCertNonceSlopLimit(int limit) { + certNonceSlopLimit = limit; + } + + /** + * @return the configured nonce slop limit. + * @since 4.1 + */ + public int getCertNonceSlopLimit() { + return certNonceSlopLimit; + } +}