From 19126f70e9e5645278b38239c01d990c32d44af2 Mon Sep 17 00:00:00 2001 From: Nico Sallembien Date: Tue, 9 Feb 2010 15:01:27 -0800 Subject: [PATCH] Allow users of ReceivePack access to the objects being sent When implementing branch read access, we need to prove that the newly created reference(s) point to objects that the user can see. There are two ways that an object is reachable: 1) It's reachable from a branch or change the user can see 2) It was uploaded as part of the pack file the user sent us This change adds additional methods in ReceivePack that will allow a server to check the above conditions, in order to ensure that a user is not trying to create a reference that they cannot see, or that a malicious user isn't attempting to forge the SHA-1 of an object that they cannot see in order to base a change off of it. Change-Id: Ieba75b4f0331e06a03417c37f4ae1ebca4fbee5a --- .../org/eclipse/jgit/transport/IndexPack.java | 77 ++++++++++++++++++- .../eclipse/jgit/transport/ReceivePack.java | 49 +++++++++++- 2 files changed, 121 insertions(+), 5 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java index 7c94767b4..23faa42a2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2009, Google Inc. + * Copyright (C) 2008-2010, Google Inc. * Copyright (C) 2007-2008, Robin Rosenberg * Copyright (C) 2008, Shawn O. Pearce * and other copyright owners as documented in the project's IP log. @@ -54,7 +54,10 @@ import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.zip.CRC32; import java.util.zip.DataFormatException; import java.util.zip.Deflater; @@ -158,6 +161,8 @@ public static IndexPack create(final Repository db, final InputStream is) private boolean keepEmpty; + private boolean needBaseObjectIds; + private int outputVersion; private final File dstPack; @@ -168,6 +173,8 @@ public static IndexPack create(final Repository db, final InputStream is) private PackedObjectInfo[] entries; + private Set newObjectIds; + private int deltaCount; private int entryCount; @@ -176,6 +183,8 @@ public static IndexPack create(final Repository db, final InputStream is) private ObjectIdSubclassMap baseById; + private Set baseIds; + private LongMap baseByPos; private byte[] objectData; @@ -267,6 +276,54 @@ public void setKeepEmpty(final boolean empty) { keepEmpty = empty; } + /** + * Configure this index pack instance to keep track of new objects. + *

+ * By default an index pack doesn't save the new objects that were created + * when it was instantiated. Setting this flag to {@code true} allows the + * caller to use {@link #getNewObjectIds()} to retrieve that list. + * + * @param b {@code true} to enable keeping track of new objects. + */ + public void setNeedNewObjectIds(boolean b) { + if (b) + newObjectIds = new HashSet(); + else + newObjectIds = null; + } + + private boolean needNewObjectIds() { + return newObjectIds != null; + } + + /** + * Configure this index pack instance to keep track of the objects assumed + * for delta bases. + *

+ * By default an index pack doesn't save the objects that were used as delta + * bases. Setting this flag to {@code true} will allow the caller to + * use {@link #getBaseObjectIds()} to retrieve that list. + * + * @param b {@code true} to enable keeping track of delta bases. + */ + public void setNeedBaseObjectIds(boolean b) { + this.needBaseObjectIds = b; + } + + /** @return the new objects that were sent by the user */ + public Set getNewObjectIds() { + return newObjectIds == null ? + Collections.emptySet() : newObjectIds; + } + + /** + * @return the set of objects the incoming pack assumed for delta purposes + */ + public Set getBaseObjectIds() { + return baseIds == null ? + Collections.emptySet() : baseIds; + } + /** * Configure the checker used to validate received objects. *

@@ -333,6 +390,12 @@ public void index(final ProgressMonitor progress) throws IOException { if (packOut == null) throw new IOException("need packOut"); resolveDeltas(progress); + if (needBaseObjectIds) { + baseIds = new HashSet(); + for (DeltaChain c : baseById) { + baseIds.add(c); + } + } if (entryCount < objectCount) { if (!fixThin) { throw new IOException("pack has " @@ -453,7 +516,7 @@ private void resolveDeltas(final long pos, final int oldCRC, int type, verifySafeObject(tempObjectId, type, data); oe = new PackedObjectInfo(pos, crc32, tempObjectId); - entries[entryCount++] = oe; + addObjectAndTrack(oe); } resolveChildDeltas(pos, type, data, oe); @@ -749,7 +812,7 @@ private void whole(final int type, final long pos, final long sz) verifySafeObject(tempObjectId, type, data); final int crc32 = (int) crc.getValue(); - entries[entryCount++] = new PackedObjectInfo(pos, crc32, tempObjectId); + addObjectAndTrack(new PackedObjectInfo(pos, crc32, tempObjectId)); } private void verifySafeObject(final AnyObjectId id, final int type, @@ -1112,4 +1175,10 @@ private void cleanupTemporaryFiles() { if (!dstPack.delete()) dstPack.deleteOnExit(); } -} + + private void addObjectAndTrack(PackedObjectInfo oe) { + entries[entryCount++] = oe; + if (needNewObjectIds()) + newObjectIds.add(oe); + } +} \ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java index 4d63ee68e..8d75f3cb9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java @@ -153,6 +153,8 @@ public class ReceivePack { private PrintWriter msgs; + private IndexPack ip; + /** The refs we advertised as existing at the start of the connection. */ private Map refs; @@ -171,6 +173,10 @@ public class ReceivePack { /** Lock around the received pack file, while updating refs. */ private PackLock packLock; + private boolean needNewObjectIds; + + private boolean needBaseObjectIds; + /** * Create a new pack receive for an open repository. * @@ -236,6 +242,45 @@ public final Map getAdvertisedRefs() { return refs; } + /** + * Configure this receive pack instance to keep track of the objects assumed + * for delta bases. + *

+ * By default a receive pack doesn't save the objects that were used as + * delta bases. Setting this flag to {@code true} will allow the caller to + * use {@link #getBaseObjectIds()} to retrieve that list. + * + * @param b {@code true} to enable keeping track of delta bases. + */ + public void setNeedBaseObjectIds(boolean b) { + this.needBaseObjectIds = b; + } + + /** + * @return the set of objects the incoming pack assumed for delta purposes + */ + public final Set getBaseObjectIds() { + return ip.getBaseObjectIds(); + } + + /** + * Configure this receive pack instance to keep track of new objects. + *

+ * By default a receive pack doesn't save the new objects that were created + * when it was instantiated. Setting this flag to {@code true} allows the + * caller to use {@link #getNewObjectIds()} to retrieve that list. + * + * @param b {@code true} to enable keeping track of new objects. + */ + public void setNeedNewObjectIds(boolean b) { + this.needNewObjectIds = b; + } + + /** @return the new objects that were sent by the user */ + public final Set getNewObjectIds() { + return ip.getNewObjectIds(); + } + /** * @return true if this class expects a bi-directional pipe opened between * the client and itself. The default is true. @@ -685,8 +730,10 @@ private void receivePack() throws IOException { if (timeoutIn != null) timeoutIn.setTimeout(10 * timeout * 1000); - final IndexPack ip = IndexPack.create(db, rawIn); + ip = IndexPack.create(db, rawIn); ip.setFixThin(true); + ip.setNeedNewObjectIds(needNewObjectIds); + ip.setNeedBaseObjectIds(needBaseObjectIds); ip.setObjectChecking(isCheckReceivedObjects()); ip.index(NullProgressMonitor.INSTANCE);