diff options
| author | Edward Thomson <ethomson@edwardthomson.com> | 2017-05-01 16:17:48 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-05-01 16:17:48 +0100 |
| commit | 13c1bf0718363960c1867f35c9ce3ebc7bf74729 (patch) | |
| tree | f3d0aee020f8d45ce2a0c0d9e0bf03501b0a8822 /src | |
| parent | d87028438621240406b73ddff5a9e488734cbda7 (diff) | |
| parent | e0973bc0fc2b04b6bb27e4ce4db2b37e9577a75d (diff) | |
| download | libgit2-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.c | 76 | ||||
| -rw-r--r-- | src/odb.h | 8 | ||||
| -rw-r--r-- | src/settings.c | 5 |
3 files changed, 79 insertions, 10 deletions
@@ -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) { @@ -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; |
