Skip to content

Commit 410ab9a

Browse files
committed
Merge branch 'jt/fast-import-sign-again'
"git fast-import" learned to optionally replace signature on commits whose signatures get invalidated due to replaying by signing afresh. * jt/fast-import-sign-again: fast-import: add mode to sign commits with invalid signatures gpg-interface: allow sign_buffer() to use default signing key commit: remove unused forward declaration
2 parents 1080981 + ee66c79 commit 410ab9a

File tree

10 files changed

+231
-108
lines changed

10 files changed

+231
-108
lines changed

Documentation/git-fast-import.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ already trusted to run their own code.
8686
* `strip-if-invalid` will check signatures and, if they are invalid,
8787
will strip them and display a warning. The validation is performed
8888
in the same way as linkgit:git-verify-commit[1] does it.
89+
* `sign-if-invalid[=<keyid>]`, similar to `strip-if-invalid`, verifies
90+
commit signatures and replaces invalid signatures with newly created ones.
91+
Valid signatures are left unchanged. If `<keyid>` is provided, that key is
92+
used for signing; otherwise the configured default signing key is used.
8993

9094
Options for Frontends
9195
~~~~~~~~~~~~~~~~~~~~~

builtin/fast-export.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static int parse_opt_sign_mode(const struct option *opt,
6464
if (unset)
6565
return 0;
6666

67-
if (parse_sign_mode(arg, val))
67+
if (parse_sign_mode(arg, val, NULL))
6868
return error(_("unknown %s mode: %s"), opt->long_name, arg);
6969

7070
return 0;
@@ -825,6 +825,9 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
825825
case SIGN_STRIP_IF_INVALID:
826826
die(_("'strip-if-invalid' is not a valid mode for "
827827
"git fast-export with --signed-commits=<mode>"));
828+
case SIGN_SIGN_IF_INVALID:
829+
die(_("'sign-if-invalid' is not a valid mode for "
830+
"git fast-export with --signed-commits=<mode>"));
828831
default:
829832
BUG("invalid signed_commit_mode value %d", signed_commit_mode);
830833
}
@@ -970,6 +973,9 @@ static void handle_tag(const char *name, struct tag *tag)
970973
case SIGN_STRIP_IF_INVALID:
971974
die(_("'strip-if-invalid' is not a valid mode for "
972975
"git fast-export with --signed-tags=<mode>"));
976+
case SIGN_SIGN_IF_INVALID:
977+
die(_("'sign-if-invalid' is not a valid mode for "
978+
"git fast-export with --signed-tags=<mode>"));
973979
default:
974980
BUG("invalid signed_commit_mode value %d", signed_commit_mode);
975981
}

builtin/fast-import.c

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ static const char *global_prefix;
190190

191191
static enum sign_mode signed_tag_mode = SIGN_VERBATIM;
192192
static enum sign_mode signed_commit_mode = SIGN_VERBATIM;
193+
static const char *signed_commit_keyid;
193194

194195
/* Memory pools */
195196
static struct mem_pool fi_mem_pool = {
@@ -2840,26 +2841,15 @@ static void finalize_commit_buffer(struct strbuf *new_data,
28402841
strbuf_addbuf(new_data, msg);
28412842
}
28422843

2843-
static void handle_strip_if_invalid(struct strbuf *new_data,
2844-
struct signature_data *sig_sha1,
2845-
struct signature_data *sig_sha256,
2846-
struct strbuf *msg)
2844+
static void warn_invalid_signature(struct signature_check *check,
2845+
const char *msg, enum sign_mode mode)
28472846
{
2848-
struct strbuf tmp_buf = STRBUF_INIT;
2849-
struct signature_check signature_check = { 0 };
2850-
int ret;
2851-
2852-
/* Check signature in a temporary commit buffer */
2853-
strbuf_addbuf(&tmp_buf, new_data);
2854-
finalize_commit_buffer(&tmp_buf, sig_sha1, sig_sha256, msg);
2855-
ret = verify_commit_buffer(tmp_buf.buf, tmp_buf.len, &signature_check);
2856-
2857-
if (ret) {
2858-
const char *signer = signature_check.signer ?
2859-
signature_check.signer : _("unknown");
2860-
const char *subject;
2861-
int subject_len = find_commit_subject(msg->buf, &subject);
2847+
const char *signer = check->signer ? check->signer : _("unknown");
2848+
const char *subject;
2849+
int subject_len = find_commit_subject(msg, &subject);
28622850

2851+
switch (mode) {
2852+
case SIGN_STRIP_IF_INVALID:
28632853
if (subject_len > 100)
28642854
warning(_("stripping invalid signature for commit '%.100s...'\n"
28652855
" allegedly by %s"), subject, signer);
@@ -2869,6 +2859,67 @@ static void handle_strip_if_invalid(struct strbuf *new_data,
28692859
else
28702860
warning(_("stripping invalid signature for commit\n"
28712861
" allegedly by %s"), signer);
2862+
break;
2863+
case SIGN_SIGN_IF_INVALID:
2864+
if (subject_len > 100)
2865+
warning(_("replacing invalid signature for commit '%.100s...'\n"
2866+
" allegedly by %s"), subject, signer);
2867+
else if (subject_len > 0)
2868+
warning(_("replacing invalid signature for commit '%.*s'\n"
2869+
" allegedly by %s"), subject_len, subject, signer);
2870+
else
2871+
warning(_("replacing invalid signature for commit\n"
2872+
" allegedly by %s"), signer);
2873+
break;
2874+
default:
2875+
BUG("unsupported signing mode");
2876+
}
2877+
}
2878+
2879+
static void handle_signature_if_invalid(struct strbuf *new_data,
2880+
struct signature_data *sig_sha1,
2881+
struct signature_data *sig_sha256,
2882+
struct strbuf *msg,
2883+
enum sign_mode mode)
2884+
{
2885+
struct strbuf tmp_buf = STRBUF_INIT;
2886+
struct signature_check signature_check = { 0 };
2887+
int ret;
2888+
2889+
/* Check signature in a temporary commit buffer */
2890+
strbuf_addbuf(&tmp_buf, new_data);
2891+
finalize_commit_buffer(&tmp_buf, sig_sha1, sig_sha256, msg);
2892+
ret = verify_commit_buffer(tmp_buf.buf, tmp_buf.len, &signature_check);
2893+
2894+
if (ret) {
2895+
warn_invalid_signature(&signature_check, msg->buf, mode);
2896+
2897+
if (mode == SIGN_SIGN_IF_INVALID) {
2898+
struct strbuf signature = STRBUF_INIT;
2899+
struct strbuf payload = STRBUF_INIT;
2900+
2901+
/*
2902+
* NEEDSWORK: To properly support interoperability mode
2903+
* when signing commit signatures, the commit buffer
2904+
* must be provided in both the repository and
2905+
* compatibility object formats. As currently
2906+
* implemented, only the repository object format is
2907+
* considered meaning compatibility signatures cannot be
2908+
* generated. Thus, attempting to sign commit signatures
2909+
* in interoperability mode is currently unsupported.
2910+
*/
2911+
if (the_repository->compat_hash_algo)
2912+
die(_("signing commits in interoperability mode is unsupported"));
2913+
2914+
strbuf_addstr(&payload, signature_check.payload);
2915+
if (sign_buffer(&payload, &signature, signed_commit_keyid,
2916+
SIGN_BUFFER_USE_DEFAULT_KEY))
2917+
die(_("failed to sign commit object"));
2918+
add_header_signature(new_data, &signature, the_hash_algo);
2919+
2920+
strbuf_release(&signature);
2921+
strbuf_release(&payload);
2922+
}
28722923

28732924
finalize_commit_buffer(new_data, NULL, NULL, msg);
28742925
} else {
@@ -2931,6 +2982,7 @@ static void parse_new_commit(const char *arg)
29312982
/* fallthru */
29322983
case SIGN_VERBATIM:
29332984
case SIGN_STRIP_IF_INVALID:
2985+
case SIGN_SIGN_IF_INVALID:
29342986
import_one_signature(&sig_sha1, &sig_sha256, v);
29352987
break;
29362988

@@ -3015,9 +3067,11 @@ static void parse_new_commit(const char *arg)
30153067
"encoding %s\n",
30163068
encoding);
30173069

3018-
if (signed_commit_mode == SIGN_STRIP_IF_INVALID &&
3070+
if ((signed_commit_mode == SIGN_STRIP_IF_INVALID ||
3071+
signed_commit_mode == SIGN_SIGN_IF_INVALID) &&
30193072
(sig_sha1.hash_algo || sig_sha256.hash_algo))
3020-
handle_strip_if_invalid(&new_data, &sig_sha1, &sig_sha256, &msg);
3073+
handle_signature_if_invalid(&new_data, &sig_sha1, &sig_sha256,
3074+
&msg, signed_commit_mode);
30213075
else
30223076
finalize_commit_buffer(&new_data, &sig_sha1, &sig_sha256, &msg);
30233077

@@ -3064,6 +3118,9 @@ static void handle_tag_signature(struct strbuf *msg, const char *name)
30643118
case SIGN_STRIP_IF_INVALID:
30653119
die(_("'strip-if-invalid' is not a valid mode for "
30663120
"git fast-import with --signed-tags=<mode>"));
3121+
case SIGN_SIGN_IF_INVALID:
3122+
die(_("'sign-if-invalid' is not a valid mode for "
3123+
"git fast-import with --signed-tags=<mode>"));
30673124
default:
30683125
BUG("invalid signed_tag_mode value %d from tag '%s'",
30693126
signed_tag_mode, name);
@@ -3653,10 +3710,10 @@ static int parse_one_option(const char *option)
36533710
} else if (skip_prefix(option, "export-pack-edges=", &option)) {
36543711
option_export_pack_edges(option);
36553712
} else if (skip_prefix(option, "signed-commits=", &option)) {
3656-
if (parse_sign_mode(option, &signed_commit_mode))
3713+
if (parse_sign_mode(option, &signed_commit_mode, &signed_commit_keyid))
36573714
usagef(_("unknown --signed-commits mode '%s'"), option);
36583715
} else if (skip_prefix(option, "signed-tags=", &option)) {
3659-
if (parse_sign_mode(option, &signed_tag_mode))
3716+
if (parse_sign_mode(option, &signed_tag_mode, NULL))
36603717
usagef(_("unknown --signed-tags mode '%s'"), option);
36613718
} else if (!strcmp(option, "quiet")) {
36623719
show_stats = 0;

builtin/tag.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ static int do_sign(struct strbuf *buffer, struct object_id **compat_oid,
167167
char *keyid = get_signing_key();
168168
int ret = -1;
169169

170-
if (sign_buffer(buffer, &sig, keyid))
170+
if (sign_buffer(buffer, &sig, keyid, 0))
171171
goto out;
172172

173173
if (compat) {
@@ -176,7 +176,7 @@ static int do_sign(struct strbuf *buffer, struct object_id **compat_oid,
176176
if (convert_object_file(the_repository ,&compat_buf, algo, compat,
177177
buffer->buf, buffer->len, OBJ_TAG, 1))
178178
goto out;
179-
if (sign_buffer(&compat_buf, &compat_sig, keyid))
179+
if (sign_buffer(&compat_buf, &compat_sig, keyid, 0))
180180
goto out;
181181
add_header_signature(&compat_buf, &sig, algo);
182182
strbuf_addbuf(&compat_buf, &compat_sig);

commit.c

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,18 +1170,6 @@ int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct gi
11701170
return 0;
11711171
}
11721172

1173-
static int sign_commit_to_strbuf(struct strbuf *sig, struct strbuf *buf, const char *keyid)
1174-
{
1175-
char *keyid_to_free = NULL;
1176-
int ret = 0;
1177-
if (!keyid || !*keyid)
1178-
keyid = keyid_to_free = get_signing_key();
1179-
if (sign_buffer(buf, sig, keyid))
1180-
ret = -1;
1181-
free(keyid_to_free);
1182-
return ret;
1183-
}
1184-
11851173
int parse_signed_commit(const struct commit *commit,
11861174
struct strbuf *payload, struct strbuf *signature,
11871175
const struct git_hash_algo *algop)
@@ -1759,7 +1747,8 @@ int commit_tree_extended(const char *msg, size_t msg_len,
17591747
oidcpy(&parent_buf[i++], &p->item->object.oid);
17601748

17611749
write_commit_tree(&buffer, msg, msg_len, tree, parent_buf, nparents, author, committer, extra);
1762-
if (sign_commit && sign_commit_to_strbuf(&sig, &buffer, sign_commit)) {
1750+
if (sign_commit && sign_buffer(&buffer, &sig, sign_commit,
1751+
SIGN_BUFFER_USE_DEFAULT_KEY)) {
17631752
result = -1;
17641753
goto out;
17651754
}
@@ -1791,7 +1780,9 @@ int commit_tree_extended(const char *msg, size_t msg_len,
17911780
free_commit_extra_headers(compat_extra);
17921781
free(mapped_parents);
17931782

1794-
if (sign_commit && sign_commit_to_strbuf(&compat_sig, &compat_buffer, sign_commit)) {
1783+
if (sign_commit && sign_buffer(&compat_buffer, &compat_sig,
1784+
sign_commit,
1785+
SIGN_BUFFER_USE_DEFAULT_KEY)) {
17951786
result = -1;
17961787
goto out;
17971788
}

commit.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,8 +400,6 @@ LAST_ARG_MUST_BE_NULL
400400
int run_commit_hook(int editor_is_used, const char *index_file,
401401
int *invoked_hook, const char *name, ...);
402402

403-
/* Sign a commit or tag buffer, storing the result in a header. */
404-
int sign_with_header(struct strbuf *buf, const char *keyid);
405403
/* Parse the signature out of a header. */
406404
int parse_buffer_signed_by_header(const char *buffer,
407405
unsigned long size,

gpg-interface.c

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -974,11 +974,20 @@ const char *gpg_trust_level_to_str(enum signature_trust_level level)
974974
return sigcheck_gpg_trust_level[level].display_key;
975975
}
976976

977-
int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
977+
int sign_buffer(struct strbuf *buffer, struct strbuf *signature,
978+
const char *signing_key, enum sign_buffer_flags flags)
978979
{
980+
char *keyid_to_free = NULL;
981+
int ret = 0;
982+
979983
gpg_interface_lazy_init();
980984

981-
return use_format->sign_buffer(buffer, signature, signing_key);
985+
if ((flags & SIGN_BUFFER_USE_DEFAULT_KEY) && (!signing_key || !*signing_key))
986+
signing_key = keyid_to_free = get_signing_key();
987+
988+
ret = use_format->sign_buffer(buffer, signature, signing_key);
989+
free(keyid_to_free);
990+
return ret;
982991
}
983992

984993
/*
@@ -1143,21 +1152,28 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
11431152
return ret;
11441153
}
11451154

1146-
int parse_sign_mode(const char *arg, enum sign_mode *mode)
1155+
int parse_sign_mode(const char *arg, enum sign_mode *mode, const char **keyid)
11471156
{
1148-
if (!strcmp(arg, "abort"))
1157+
if (!strcmp(arg, "abort")) {
11491158
*mode = SIGN_ABORT;
1150-
else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
1159+
} else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore")) {
11511160
*mode = SIGN_VERBATIM;
1152-
else if (!strcmp(arg, "warn-verbatim") || !strcmp(arg, "warn"))
1161+
} else if (!strcmp(arg, "warn-verbatim") || !strcmp(arg, "warn")) {
11531162
*mode = SIGN_WARN_VERBATIM;
1154-
else if (!strcmp(arg, "warn-strip"))
1163+
} else if (!strcmp(arg, "warn-strip")) {
11551164
*mode = SIGN_WARN_STRIP;
1156-
else if (!strcmp(arg, "strip"))
1165+
} else if (!strcmp(arg, "strip")) {
11571166
*mode = SIGN_STRIP;
1158-
else if (!strcmp(arg, "strip-if-invalid"))
1167+
} else if (!strcmp(arg, "strip-if-invalid")) {
11591168
*mode = SIGN_STRIP_IF_INVALID;
1160-
else
1169+
} else if (!strcmp(arg, "sign-if-invalid")) {
1170+
*mode = SIGN_SIGN_IF_INVALID;
1171+
} else if (skip_prefix(arg, "sign-if-invalid=", &arg)) {
1172+
*mode = SIGN_SIGN_IF_INVALID;
1173+
if (keyid)
1174+
*keyid = arg;
1175+
} else {
11611176
return -1;
1177+
}
11621178
return 0;
11631179
}

gpg-interface.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,23 @@ int parse_signature(const char *buf, size_t size, struct strbuf *payload, struct
7474
*/
7575
size_t parse_signed_buffer(const char *buf, size_t size);
7676

77+
/* Flags for sign_buffer(). */
78+
enum sign_buffer_flags {
79+
/*
80+
* Use the default configured signing key as returned by `get_signing_key()`
81+
* when the provided "signing_key" is NULL or empty.
82+
*/
83+
SIGN_BUFFER_USE_DEFAULT_KEY = (1 << 0),
84+
};
85+
7786
/*
7887
* Create a detached signature for the contents of "buffer" and append
7988
* it after "signature"; "buffer" and "signature" can be the same
8089
* strbuf instance, which would cause the detached signature appended
8190
* at the end. Returns 0 on success, non-zero on failure.
8291
*/
8392
int sign_buffer(struct strbuf *buffer, struct strbuf *signature,
84-
const char *signing_key);
85-
93+
const char *signing_key, enum sign_buffer_flags flags);
8694

8795
/*
8896
* Returns corresponding string in lowercase for a given member of
@@ -112,12 +120,15 @@ enum sign_mode {
112120
SIGN_WARN_STRIP,
113121
SIGN_STRIP,
114122
SIGN_STRIP_IF_INVALID,
123+
SIGN_SIGN_IF_INVALID,
115124
};
116125

117126
/*
118127
* Return 0 if `arg` can be parsed into an `enum sign_mode`. Return -1
119-
* otherwise.
128+
* otherwise. If the parsed mode is SIGN_SIGN_IF_INVALID and GPG key provided in
129+
* the arguments in the form `sign-if-invalid=<keyid>`, the key-ID is parsed
130+
* into `char **keyid`.
120131
*/
121-
int parse_sign_mode(const char *arg, enum sign_mode *mode);
132+
int parse_sign_mode(const char *arg, enum sign_mode *mode, const char **keyid);
122133

123134
#endif

send-pack.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ static int generate_push_cert(struct strbuf *req_buf,
391391
if (!update_seen)
392392
goto free_return;
393393

394-
if (sign_buffer(&cert, &cert, signing_key))
394+
if (sign_buffer(&cert, &cert, signing_key, 0))
395395
die(_("failed to sign the push certificate"));
396396

397397
packet_buf_write(req_buf, "push-cert%c%s", 0, cap_string);

0 commit comments

Comments
 (0)