summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2017-05-01 16:17:48 +0100
committerGitHub <noreply@github.com>2017-05-01 16:17:48 +0100
commit13c1bf0718363960c1867f35c9ce3ebc7bf74729 (patch)
treef3d0aee020f8d45ce2a0c0d9e0bf03501b0a8822 /src
parentd87028438621240406b73ddff5a9e488734cbda7 (diff)
parente0973bc0fc2b04b6bb27e4ce4db2b37e9577a75d (diff)
downloadlibgit2-13c1bf0718363960c1867f35c9ce3ebc7bf74729.tar.gz
Merge pull request #4197 from pks-t/pks/verify-object-hashes
Verify object hashes
Diffstat (limited to 'src')
-rw-r--r--src/odb.c76
-rw-r--r--src/odb.h8
-rw-r--r--src/settings.c5
3 files changed, 79 insertions, 10 deletions
diff --git a/src/odb.c b/src/odb.c
index cf321f51e..3090ccaac 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -31,6 +31,8 @@
#define GIT_ALTERNATES_MAX_DEPTH 5
+bool git_odb__strict_hash_verification = true;
+
typedef struct
{
git_odb_backend *backend;
@@ -998,7 +1000,9 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
size_t i;
git_rawobj raw;
git_odb_object *object;
+ git_oid hashed;
bool found = false;
+ int error;
if (!only_refreshed && odb_read_hardcoded(&raw, id) == 0)
found = true;
@@ -1011,7 +1015,7 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
continue;
if (b->read != NULL) {
- int error = b->read(&raw.data, &raw.len, &raw.type, b, id);
+ error = b->read(&raw.data, &raw.len, &raw.type, b, id);
if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND)
continue;
@@ -1025,12 +1029,26 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
if (!found)
return GIT_ENOTFOUND;
+ if (git_odb__strict_hash_verification) {
+ if ((error = git_odb_hash(&hashed, raw.data, raw.len, raw.type)) < 0)
+ goto out;
+
+ if (!git_oid_equal(id, &hashed)) {
+ error = git_odb__error_mismatch(id, &hashed);
+ goto out;
+ }
+ }
+
giterr_clear();
if ((object = odb_object__alloc(id, &raw)) == NULL)
- return -1;
+ goto out;
*out = git_cache_store_raw(odb_cache(db), object);
- return 0;
+
+out:
+ if (error)
+ git__free(raw.data);
+ return error;
}
int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
@@ -1081,9 +1099,9 @@ static int read_prefix_1(git_odb_object **out, git_odb *db,
const git_oid *key, size_t len, bool only_refreshed)
{
size_t i;
- int error = GIT_ENOTFOUND;
+ int error;
git_oid found_full_oid = {{0}};
- git_rawobj raw;
+ git_rawobj raw = {0};
void *data = NULL;
bool found = false;
git_odb_object *object;
@@ -1102,14 +1120,22 @@ static int read_prefix_1(git_odb_object **out, git_odb *db,
continue;
if (error)
- return error;
+ goto out;
git__free(data);
data = raw.data;
if (found && git_oid__cmp(&full_oid, &found_full_oid)) {
- git__free(raw.data);
- return git_odb__error_ambiguous("multiple matches for prefix");
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_printf(&buf, "multiple matches for prefix: %s",
+ git_oid_tostr_s(&full_oid));
+ git_buf_printf(&buf, " %s",
+ git_oid_tostr_s(&found_full_oid));
+
+ error = git_odb__error_ambiguous(buf.ptr);
+ git_buf_free(&buf);
+ goto out;
}
found_full_oid = full_oid;
@@ -1120,11 +1146,28 @@ static int read_prefix_1(git_odb_object **out, git_odb *db,
if (!found)
return GIT_ENOTFOUND;
+ if (git_odb__strict_hash_verification) {
+ git_oid hash;
+
+ if ((error = git_odb_hash(&hash, raw.data, raw.len, raw.type)) < 0)
+ goto out;
+
+ if (!git_oid_equal(&found_full_oid, &hash)) {
+ error = git_odb__error_mismatch(&found_full_oid, &hash);
+ goto out;
+ }
+ }
+
if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL)
- return -1;
+ goto out;
*out = git_cache_store_raw(odb_cache(db), object);
- return 0;
+
+out:
+ if (error)
+ git__free(raw.data);
+
+ return error;
}
int git_odb_read_prefix(
@@ -1411,6 +1454,19 @@ int git_odb_refresh(struct git_odb *db)
return 0;
}
+int git_odb__error_mismatch(const git_oid *expected, const git_oid *actual)
+{
+ char expected_oid[GIT_OID_HEXSZ + 1], actual_oid[GIT_OID_HEXSZ + 1];
+
+ git_oid_tostr(expected_oid, sizeof(expected_oid), expected);
+ git_oid_tostr(actual_oid, sizeof(actual_oid), actual);
+
+ giterr_set(GITERR_ODB, "object hash mismatch - expected %s but got %s",
+ expected_oid, actual_oid);
+
+ return GIT_EMISMATCH;
+}
+
int git_odb__error_notfound(
const char *message, const git_oid *oid, size_t oid_len)
{
diff --git a/src/odb.h b/src/odb.h
index 4f548bbba..61d687abf 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -20,6 +20,8 @@
#define GIT_OBJECT_DIR_MODE 0777
#define GIT_OBJECT_FILE_MODE 0444
+extern bool git_odb__strict_hash_verification;
+
/* DO NOT EXPORT */
typedef struct {
void *data; /**< Raw, decompressed object data. */
@@ -96,6 +98,12 @@ int git_odb__hashfd_filtered(
*/
int git_odb__hashlink(git_oid *out, const char *path);
+/**
+ * Generate a GIT_EMISMATCH error for the ODB.
+ */
+int git_odb__error_mismatch(
+ const git_oid *expected, const git_oid *actual);
+
/*
* Generate a GIT_ENOTFOUND error for the ODB.
*/
diff --git a/src/settings.c b/src/settings.c
index 42f247aae..25c5aae6d 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -15,6 +15,7 @@
#include "cache.h"
#include "global.h"
#include "object.h"
+#include "odb.h"
#include "refs.h"
#include "transports/smart.h"
@@ -243,6 +244,10 @@ int git_libgit2_opts(int key, ...)
#endif
break;
+ case GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION:
+ git_odb__strict_hash_verification = (va_arg(ap, int) != 0);
+ break;
+
default:
giterr_set(GITERR_INVALID, "invalid option key");
error = -1;