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
This commit is contained in:
Nico Sallembien 2010-02-09 15:01:27 -08:00
parent e54d33b687
commit 19126f70e9
2 changed files with 121 additions and 5 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2008-2009, Google Inc.
* Copyright (C) 2008-2010, Google Inc.
* Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
* 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<ObjectId> newObjectIds;
private int deltaCount;
private int entryCount;
@ -176,6 +183,8 @@ public static IndexPack create(final Repository db, final InputStream is)
private ObjectIdSubclassMap<DeltaChain> baseById;
private Set<ObjectId> baseIds;
private LongMap<UnresolvedDelta> 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.
* <p>
* 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<ObjectId>();
else
newObjectIds = null;
}
private boolean needNewObjectIds() {
return newObjectIds != null;
}
/**
* Configure this index pack instance to keep track of the objects assumed
* for delta bases.
* <p>
* 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<ObjectId> getNewObjectIds() {
return newObjectIds == null ?
Collections.<ObjectId>emptySet() : newObjectIds;
}
/**
* @return the set of objects the incoming pack assumed for delta purposes
*/
public Set<ObjectId> getBaseObjectIds() {
return baseIds == null ?
Collections.<ObjectId>emptySet() : baseIds;
}
/**
* Configure the checker used to validate received objects.
* <p>
@ -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<ObjectId>();
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);
}
}

View File

@ -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<String, Ref> 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<String, Ref> getAdvertisedRefs() {
return refs;
}
/**
* Configure this receive pack instance to keep track of the objects assumed
* for delta bases.
* <p>
* 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<ObjectId> getBaseObjectIds() {
return ip.getBaseObjectIds();
}
/**
* Configure this receive pack instance to keep track of new objects.
* <p>
* 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<ObjectId> 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);