]> git.ipfire.org Git - thirdparty/git.git/blobdiff - commit.c
Merge branch 'bc/signed-objects-with-both-hashes'
[thirdparty/git.git] / commit.c
index 4694c4cf9bca07a4e86a9d9701053c9185df9ebd..6ccd774841c6939eb977696ac6f30c04dd514ce3 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -995,7 +995,7 @@ static const char *gpg_sig_headers[] = {
        "gpgsig-sha256",
 };
 
-static int do_sign_commit(struct strbuf *buf, const char *keyid)
+int sign_with_header(struct strbuf *buf, const char *keyid)
 {
        struct strbuf sig = STRBUF_INIT;
        int inspos, copypos;
@@ -1035,21 +1035,32 @@ static int do_sign_commit(struct strbuf *buf, const char *keyid)
        return 0;
 }
 
+
+
 int parse_signed_commit(const struct commit *commit,
-                       struct strbuf *payload, struct strbuf *signature)
+                       struct strbuf *payload, struct strbuf *signature,
+                       const struct git_hash_algo *algop)
 {
-
        unsigned long size;
        const char *buffer = get_commit_buffer(commit, &size);
-       int in_signature, saw_signature = -1;
-       const char *line, *tail;
-       const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
-       int gpg_sig_header_len = strlen(gpg_sig_header);
+       int ret = parse_buffer_signed_by_header(buffer, size, payload, signature, algop);
+
+       unuse_commit_buffer(commit, buffer);
+       return ret;
+}
+
+int parse_buffer_signed_by_header(const char *buffer,
+                                 unsigned long size,
+                                 struct strbuf *payload,
+                                 struct strbuf *signature,
+                                 const struct git_hash_algo *algop)
+{
+       int in_signature = 0, saw_signature = 0, other_signature = 0;
+       const char *line, *tail, *p;
+       const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(algop)];
 
        line = buffer;
        tail = buffer + size;
-       in_signature = 0;
-       saw_signature = 0;
        while (line < tail) {
                const char *sig = NULL;
                const char *next = memchr(line, '\n', tail - line);
@@ -1057,9 +1068,15 @@ int parse_signed_commit(const struct commit *commit,
                next = next ? next + 1 : tail;
                if (in_signature && line[0] == ' ')
                        sig = line + 1;
-               else if (starts_with(line, gpg_sig_header) &&
-                        line[gpg_sig_header_len] == ' ')
-                       sig = line + gpg_sig_header_len + 1;
+               else if (skip_prefix(line, gpg_sig_header, &p) &&
+                        *p == ' ') {
+                       sig = line + strlen(gpg_sig_header) + 1;
+                       other_signature = 0;
+               }
+               else if (starts_with(line, "gpgsig"))
+                       other_signature = 1;
+               else if (other_signature && line[0] != ' ')
+                       other_signature = 0;
                if (sig) {
                        strbuf_add(signature, sig, next - sig);
                        saw_signature = 1;
@@ -1068,12 +1085,12 @@ int parse_signed_commit(const struct commit *commit,
                        if (*line == '\n')
                                /* dump the whole remainder of the buffer */
                                next = tail;
-                       strbuf_add(payload, line, next - line);
+                       if (!other_signature)
+                               strbuf_add(payload, line, next - line);
                        in_signature = 0;
                }
                line = next;
        }
-       unuse_commit_buffer(commit, buffer);
        return saw_signature;
 }
 
@@ -1082,23 +1099,29 @@ int remove_signature(struct strbuf *buf)
        const char *line = buf->buf;
        const char *tail = buf->buf + buf->len;
        int in_signature = 0;
-       const char *sig_start = NULL;
-       const char *sig_end = NULL;
+       struct sigbuf {
+               const char *start;
+               const char *end;
+       } sigs[2], *sigp = &sigs[0];
+       int i;
+       const char *orig_buf = buf->buf;
+
+       memset(sigs, 0, sizeof(sigs));
 
        while (line < tail) {
                const char *next = memchr(line, '\n', tail - line);
                next = next ? next + 1 : tail;
 
                if (in_signature && line[0] == ' ')
-                       sig_end = next;
+                       sigp->end = next;
                else if (starts_with(line, "gpgsig")) {
                        int i;
                        for (i = 1; i < GIT_HASH_NALGOS; i++) {
                                const char *p;
                                if (skip_prefix(line, gpg_sig_headers[i], &p) &&
                                    *p == ' ') {
-                                       sig_start = line;
-                                       sig_end = next;
+                                       sigp->start = line;
+                                       sigp->end = next;
                                        in_signature = 1;
                                }
                        }
@@ -1106,15 +1129,18 @@ int remove_signature(struct strbuf *buf)
                        if (*line == '\n')
                                /* dump the whole remainder of the buffer */
                                next = tail;
+                       if (in_signature && sigp - sigs != ARRAY_SIZE(sigs))
+                               sigp++;
                        in_signature = 0;
                }
                line = next;
        }
 
-       if (sig_start)
-               strbuf_remove(buf, sig_start - buf->buf, sig_end - sig_start);
+       for (i = ARRAY_SIZE(sigs) - 1; i >= 0; i--)
+               if (sigs[i].start)
+                       strbuf_remove(buf, sigs[i].start - orig_buf, sigs[i].end - sigs[i].start);
 
-       return sig_start != NULL;
+       return sigs[0].start != NULL;
 }
 
 static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
@@ -1122,8 +1148,10 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header
        struct merge_remote_desc *desc;
        struct commit_extra_header *mergetag;
        char *buf;
-       unsigned long size, len;
+       unsigned long size;
        enum object_type type;
+       struct strbuf payload = STRBUF_INIT;
+       struct strbuf signature = STRBUF_INIT;
 
        desc = merge_remote_util(parent);
        if (!desc || !desc->obj)
@@ -1131,8 +1159,7 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header
        buf = read_object_file(&desc->obj->oid, &type, &size);
        if (!buf || type != OBJ_TAG)
                goto free_return;
-       len = parse_signature(buf, size);
-       if (size == len)
+       if (!parse_signature(buf, size, &payload, &signature))
                goto free_return;
        /*
         * We could verify this signature and either omit the tag when
@@ -1151,6 +1178,8 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header
 
        **tail = mergetag;
        *tail = &mergetag->next;
+       strbuf_release(&payload);
+       strbuf_release(&signature);
        return;
 
 free_return:
@@ -1165,7 +1194,7 @@ int check_commit_signature(const struct commit *commit, struct signature_check *
 
        sigc->result = 'N';
 
-       if (parse_signed_commit(commit, &payload, &signature) <= 0)
+       if (parse_signed_commit(commit, &payload, &signature, the_hash_algo) <= 0)
                goto out;
        ret = check_signature(payload.buf, payload.len, signature.buf,
                signature.len, sigc);
@@ -1515,7 +1544,7 @@ int commit_tree_extended(const char *msg, size_t msg_len,
        if (encoding_is_utf8 && !verify_utf8(&buffer))
                fprintf(stderr, _(commit_utf8_warn));
 
-       if (sign_commit && do_sign_commit(&buffer, sign_commit)) {
+       if (sign_commit && sign_with_header(&buffer, sign_commit)) {
                result = -1;
                goto out;
        }