From 0f95d2d0462f8badd3cdb1fadb6dbb3fe84074b4 Mon Sep 17 00:00:00 2001 From: Nico Sallembien Date: Fri, 12 Mar 2010 16:07:19 -0800 Subject: [PATCH] Add a paranoid 'must be provided' option to ReceivePack By default a receive pack assumes that its user will only provide references to objects that the user already has access to on their local client. In certain cases, an additional check to verify the references point only to reachable objects is necessary. This additional checking is useful when the code doesn't trust the client not to provide a forged SHA-1 reference to an object, in an attempt to access parts of the DAG that they weren't allowed to see by the configured RefFilter. Change-Id: I3e4b8505cb2992e3e4be253abb14a1501e47b970 Signed-off-by: Shawn O. Pearce --- .../eclipse/jgit/transport/ReceivePack.java | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) 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 dae28ab27..6ba326c00 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java @@ -70,6 +70,7 @@ import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectIdSubclassMap; import org.eclipse.jgit.lib.PackLock; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; @@ -77,6 +78,7 @@ import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Config.SectionParser; import org.eclipse.jgit.revwalk.ObjectWalk; +import org.eclipse.jgit.revwalk.RevBlob; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevFlag; import org.eclipse.jgit.revwalk.RevObject; @@ -184,6 +186,8 @@ public class ReceivePack { private boolean needBaseObjectIds; + private boolean ensureObjectsProvidedVisible; + /** * Create a new pack receive for an open repository. * @@ -288,6 +292,26 @@ public class ReceivePack { return ip.getNewObjectIds(); } + /** + * Configure this receive pack instance to ensure that the provided + * objects are visible to the user. + *

+ * By default, a receive pack assumes that its user will only provide + * references to objects that it can see. Setting this flag to {@code true} + * will add an additional check that verifies that the objects that were + * provided are reachable by a tree or a commit that the user can see. + *

+ * This option is useful when the code doesn't trust the client not to + * provide a forged SHA-1 reference to an object in an attempt to access + * parts of the DAG that they aren't allowed to see, via the configured + * {@link RefFilter}. + * + * @param b {@code true} to enable the additional check. + */ + public void setEnsureProvidedObjectsVisible(boolean b) { + this.ensureObjectsProvidedVisible = b; + } + /** * @return true if this class expects a bi-directional pipe opened between * the client and itself. The default is true. @@ -777,8 +801,9 @@ public class ReceivePack { ip = IndexPack.create(db, rawIn); ip.setFixThin(true); - ip.setNeedNewObjectIds(needNewObjectIds); - ip.setNeedBaseObjectIds(needBaseObjectIds); + ip.setNeedNewObjectIds(needNewObjectIds || ensureObjectsProvidedVisible); + ip.setNeedBaseObjectIds(needBaseObjectIds + || ensureObjectsProvidedVisible); ip.setObjectChecking(isCheckReceivedObjects()); ip.index(NullProgressMonitor.INSTANCE); @@ -802,7 +827,34 @@ public class ReceivePack { } for (final Ref ref : refs.values()) ow.markUninteresting(ow.parseAny(ref.getObjectId())); - ow.checkConnectivity(); + + ObjectIdSubclassMap provided = + new ObjectIdSubclassMap(); + if (ensureObjectsProvidedVisible) { + for (ObjectId id : getBaseObjectIds()) { + RevObject b = ow.lookupAny(id, Constants.OBJ_BLOB); + if (!b.has(RevFlag.UNINTERESTING)) + throw new MissingObjectException(b, b.getType()); + } + for (ObjectId id : getNewObjectIds()) { + provided.add(id); + } + } + + RevCommit c; + while ((c = ow.next()) != null) { + if (ensureObjectsProvidedVisible && !provided.contains(c)) + throw new MissingObjectException(c, Constants.TYPE_COMMIT); + } + + RevObject o; + while ((o = ow.nextObject()) != null) { + if (o instanceof RevBlob && !db.hasObject(o)) + throw new MissingObjectException(o, Constants.TYPE_BLOB); + + if (ensureObjectsProvidedVisible && !provided.contains(o)) + throw new MissingObjectException(o, Constants.TYPE_BLOB); + } } private void validateCommands() {