Skip to content

Commit 11ad30b

Browse files
committed
Merge branch 'hi/gpg-mintrustlevel'
gpg.minTrustLevel configuration variable has been introduced to tell various signature verification codepaths the required minimum trust level. * hi/gpg-mintrustlevel: gpg-interface: add minTrustLevel as a configuration option
2 parents 96aef8f + 54887b4 commit 11ad30b

13 files changed

+319
-23
lines changed

Documentation/config/gpg.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,18 @@ gpg.<format>.program::
1818
chose. (see `gpg.program` and `gpg.format`) `gpg.program` can still
1919
be used as a legacy synonym for `gpg.openpgp.program`. The default
2020
value for `gpg.x509.program` is "gpgsm".
21+
22+
gpg.minTrustLevel::
23+
Specifies a minimum trust level for signature verification. If
24+
this option is unset, then signature verification for merge
25+
operations require a key with at least `marginal` trust. Other
26+
operations that perform signature verification require a key
27+
with at least `undefined` trust. Setting this option overrides
28+
the required trust-level for all operations. Supported values,
29+
in increasing order of significance:
30+
+
31+
* `undefined`
32+
* `never`
33+
* `marginal`
34+
* `fully`
35+
* `ultimate`

Documentation/pretty-formats.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ endif::git-rev-list[]
226226
'%GF':: show the fingerprint of the key used to sign a signed commit
227227
'%GP':: show the fingerprint of the primary key whose subkey was used
228228
to sign a signed commit
229+
'%GT':: show the trust level for the key used to sign a signed commit
229230
'%gD':: reflog selector, e.g., `refs/stash@{1}` or `refs/stash@{2
230231
minutes ago}`; the format follows the rules described for the
231232
`-g` option. The portion before the `@` is the refname as

builtin/merge.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ static int show_diffstat = 1, shortlog_len = -1, squash;
6262
static int option_commit = -1;
6363
static int option_edit = -1;
6464
static int allow_trivial = 1, have_message, verify_signatures;
65+
static int check_trust_level = 1;
6566
static int overwrite_ignore = 1;
6667
static struct strbuf merge_msg = STRBUF_INIT;
6768
static struct strategy **use_strategies;
@@ -631,6 +632,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
631632
} else if (!strcmp(k, "commit.gpgsign")) {
632633
sign_commit = git_config_bool(k, v) ? "" : NULL;
633634
return 0;
635+
} else if (!strcmp(k, "gpg.mintrustlevel")) {
636+
check_trust_level = 0;
634637
}
635638

636639
status = fmt_merge_msg_config(k, v, cb);
@@ -1397,7 +1400,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
13971400
die(_("Can merge only exactly one commit into empty head"));
13981401

13991402
if (verify_signatures)
1400-
verify_merge_signature(remoteheads->item, verbosity);
1403+
verify_merge_signature(remoteheads->item, verbosity,
1404+
check_trust_level);
14011405

14021406
remote_head_oid = &remoteheads->item->object.oid;
14031407
read_empty(remote_head_oid, 0);
@@ -1420,7 +1424,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
14201424

14211425
if (verify_signatures) {
14221426
for (p = remoteheads; p; p = p->next) {
1423-
verify_merge_signature(p->item, verbosity);
1427+
verify_merge_signature(p->item, verbosity,
1428+
check_trust_level);
14241429
}
14251430
}
14261431

builtin/pull.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ static char *opt_ff;
107107
static char *opt_verify_signatures;
108108
static int opt_autostash = -1;
109109
static int config_autostash;
110+
static int check_trust_level = 1;
110111
static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
111112
static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
112113
static char *opt_gpg_sign;
@@ -355,14 +356,23 @@ static enum rebase_type config_get_rebase(void)
355356
*/
356357
static int git_pull_config(const char *var, const char *value, void *cb)
357358
{
359+
int status;
360+
358361
if (!strcmp(var, "rebase.autostash")) {
359362
config_autostash = git_config_bool(var, value);
360363
return 0;
361364
} else if (!strcmp(var, "submodule.recurse")) {
362365
recurse_submodules = git_config_bool(var, value) ?
363366
RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
364367
return 0;
368+
} else if (!strcmp(var, "gpg.mintrustlevel")) {
369+
check_trust_level = 0;
365370
}
371+
372+
status = git_gpg_config(var, value, cb);
373+
if (status)
374+
return status;
375+
366376
return git_default_config(var, value, cb);
367377
}
368378

@@ -587,7 +597,8 @@ static int pull_into_void(const struct object_id *merge_head,
587597
die(_("unable to access commit %s"),
588598
oid_to_hex(merge_head));
589599

590-
verify_merge_signature(commit, opt_verbosity);
600+
verify_merge_signature(commit, opt_verbosity,
601+
check_trust_level);
591602
}
592603

593604
/*

commit.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,21 +1136,23 @@ int check_commit_signature(const struct commit *commit, struct signature_check *
11361136
return ret;
11371137
}
11381138

1139-
void verify_merge_signature(struct commit *commit, int verbosity)
1139+
void verify_merge_signature(struct commit *commit, int verbosity,
1140+
int check_trust)
11401141
{
11411142
char hex[GIT_MAX_HEXSZ + 1];
11421143
struct signature_check signature_check;
1144+
int ret;
11431145
memset(&signature_check, 0, sizeof(signature_check));
11441146

1145-
check_commit_signature(commit, &signature_check);
1147+
ret = check_commit_signature(commit, &signature_check);
11461148

11471149
find_unique_abbrev_r(hex, &commit->object.oid, DEFAULT_ABBREV);
11481150
switch (signature_check.result) {
11491151
case 'G':
1152+
if (ret || (check_trust && signature_check.trust_level < TRUST_MARGINAL))
1153+
die(_("Commit %s has an untrusted GPG signature, "
1154+
"allegedly by %s."), hex, signature_check.signer);
11501155
break;
1151-
case 'U':
1152-
die(_("Commit %s has an untrusted GPG signature, "
1153-
"allegedly by %s."), hex, signature_check.signer);
11541156
case 'B':
11551157
die(_("Commit %s has a bad GPG signature "
11561158
"allegedly by %s."), hex, signature_check.signer);

commit.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,8 +383,18 @@ int compare_commits_by_author_date(const void *a_, const void *b_, void *unused)
383383
* Verify a single commit with check_commit_signature() and die() if it is not
384384
* a good signature. This isn't really suitable for general use, but is a
385385
* helper to implement consistent logic for pull/merge --verify-signatures.
386+
*
387+
* The check_trust parameter is meant for backward-compatibility. The GPG
388+
* interface verifies key trust with a default trust level that is below the
389+
* default trust level for merge operations. Its value should be non-zero if
390+
* the user hasn't set a minimum trust level explicitly in their configuration.
391+
*
392+
* If the user has set a minimum trust level, then that value should be obeyed
393+
* and check_trust should be zero, even if the configured trust level is below
394+
* the default trust level for merges.
386395
*/
387-
void verify_merge_signature(struct commit *commit, int verbose);
396+
void verify_merge_signature(struct commit *commit, int verbose,
397+
int check_trust);
388398

389399
int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
390400
int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused);

gpg-interface.c

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include "tempfile.h"
88

99
static char *configured_signing_key;
10+
static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
11+
1012
struct gpg_format {
1113
const char *name;
1214
const char *program;
@@ -85,6 +87,8 @@ void signature_check_clear(struct signature_check *sigc)
8587
#define GPG_STATUS_UID (1<<2)
8688
/* The status includes key fingerprints */
8789
#define GPG_STATUS_FINGERPRINT (1<<3)
90+
/* The status includes trust level */
91+
#define GPG_STATUS_TRUST_LEVEL (1<<4)
8892

8993
/* Short-hand for standard exclusive *SIG status with keyid & UID */
9094
#define GPG_STATUS_STDSIG (GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID|GPG_STATUS_UID)
@@ -96,13 +100,23 @@ static struct {
96100
} sigcheck_gpg_status[] = {
97101
{ 'G', "GOODSIG ", GPG_STATUS_STDSIG },
98102
{ 'B', "BADSIG ", GPG_STATUS_STDSIG },
99-
{ 'U', "TRUST_NEVER", 0 },
100-
{ 'U', "TRUST_UNDEFINED", 0 },
101103
{ 'E', "ERRSIG ", GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID },
102104
{ 'X', "EXPSIG ", GPG_STATUS_STDSIG },
103105
{ 'Y', "EXPKEYSIG ", GPG_STATUS_STDSIG },
104106
{ 'R', "REVKEYSIG ", GPG_STATUS_STDSIG },
105107
{ 0, "VALIDSIG ", GPG_STATUS_FINGERPRINT },
108+
{ 0, "TRUST_", GPG_STATUS_TRUST_LEVEL },
109+
};
110+
111+
static struct {
112+
const char *key;
113+
enum signature_trust_level value;
114+
} sigcheck_gpg_trust_level[] = {
115+
{ "UNDEFINED", TRUST_UNDEFINED },
116+
{ "NEVER", TRUST_NEVER },
117+
{ "MARGINAL", TRUST_MARGINAL },
118+
{ "FULLY", TRUST_FULLY },
119+
{ "ULTIMATE", TRUST_ULTIMATE },
106120
};
107121

108122
static void replace_cstring(char **field, const char *line, const char *next)
@@ -115,6 +129,20 @@ static void replace_cstring(char **field, const char *line, const char *next)
115129
*field = NULL;
116130
}
117131

132+
static int parse_gpg_trust_level(const char *level,
133+
enum signature_trust_level *res)
134+
{
135+
size_t i;
136+
137+
for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_trust_level); i++) {
138+
if (!strcmp(sigcheck_gpg_trust_level[i].key, level)) {
139+
*res = sigcheck_gpg_trust_level[i].value;
140+
return 0;
141+
}
142+
}
143+
return 1;
144+
}
145+
118146
static void parse_gpg_output(struct signature_check *sigc)
119147
{
120148
const char *buf = sigc->gpg_status;
@@ -136,9 +164,18 @@ static void parse_gpg_output(struct signature_check *sigc)
136164
/* Iterate over all search strings */
137165
for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
138166
if (skip_prefix(line, sigcheck_gpg_status[i].check, &line)) {
167+
/*
168+
* GOODSIG, BADSIG etc. can occur only once for
169+
* each signature. Therefore, if we had more
170+
* than one then we're dealing with multiple
171+
* signatures. We don't support them
172+
* currently, and they're rather hard to
173+
* create, so something is likely fishy and we
174+
* should reject them altogether.
175+
*/
139176
if (sigcheck_gpg_status[i].flags & GPG_STATUS_EXCLUSIVE) {
140177
if (seen_exclusive_status++)
141-
goto found_duplicate_status;
178+
goto error;
142179
}
143180

144181
if (sigcheck_gpg_status[i].result)
@@ -154,6 +191,25 @@ static void parse_gpg_output(struct signature_check *sigc)
154191
replace_cstring(&sigc->signer, line, next);
155192
}
156193
}
194+
195+
/* Do we have trust level? */
196+
if (sigcheck_gpg_status[i].flags & GPG_STATUS_TRUST_LEVEL) {
197+
/*
198+
* GPG v1 and v2 differs in how the
199+
* TRUST_ lines are written. Some
200+
* trust lines contain no additional
201+
* space-separated information for v1.
202+
*/
203+
size_t trust_size = strcspn(line, " \n");
204+
char *trust = xmemdupz(line, trust_size);
205+
206+
if (parse_gpg_trust_level(trust, &sigc->trust_level)) {
207+
free(trust);
208+
goto error;
209+
}
210+
free(trust);
211+
}
212+
157213
/* Do we have fingerprint? */
158214
if (sigcheck_gpg_status[i].flags & GPG_STATUS_FINGERPRINT) {
159215
const char *limit;
@@ -191,14 +247,7 @@ static void parse_gpg_output(struct signature_check *sigc)
191247
}
192248
return;
193249

194-
found_duplicate_status:
195-
/*
196-
* GOODSIG, BADSIG etc. can occur only once for each signature.
197-
* Therefore, if we had more than one then we're dealing with multiple
198-
* signatures. We don't support them currently, and they're rather
199-
* hard to create, so something is likely fishy and we should reject
200-
* them altogether.
201-
*/
250+
error:
202251
sigc->result = 'E';
203252
/* Clear partial data to avoid confusion */
204253
FREE_AND_NULL(sigc->primary_key_fingerprint);
@@ -264,6 +313,7 @@ int check_signature(const char *payload, size_t plen, const char *signature,
264313
int status;
265314

266315
sigc->result = 'N';
316+
sigc->trust_level = -1;
267317

268318
status = verify_signed_buffer(payload, plen, signature, slen,
269319
&gpg_output, &gpg_status);
@@ -273,7 +323,8 @@ int check_signature(const char *payload, size_t plen, const char *signature,
273323
sigc->gpg_output = strbuf_detach(&gpg_output, NULL);
274324
sigc->gpg_status = strbuf_detach(&gpg_status, NULL);
275325
parse_gpg_output(sigc);
276-
status |= sigc->result != 'G' && sigc->result != 'U';
326+
status |= sigc->result != 'G';
327+
status |= sigc->trust_level < configured_min_trust_level;
277328

278329
out:
279330
strbuf_release(&gpg_status);
@@ -320,6 +371,8 @@ int git_gpg_config(const char *var, const char *value, void *cb)
320371
{
321372
struct gpg_format *fmt = NULL;
322373
char *fmtname = NULL;
374+
char *trust;
375+
int ret;
323376

324377
if (!strcmp(var, "user.signingkey")) {
325378
if (!value)
@@ -339,6 +392,20 @@ int git_gpg_config(const char *var, const char *value, void *cb)
339392
return 0;
340393
}
341394

395+
if (!strcmp(var, "gpg.mintrustlevel")) {
396+
if (!value)
397+
return config_error_nonbool(var);
398+
399+
trust = xstrdup_toupper(value);
400+
ret = parse_gpg_trust_level(trust, &configured_min_trust_level);
401+
free(trust);
402+
403+
if (ret)
404+
return error("unsupported value for %s: %s", var,
405+
value);
406+
return 0;
407+
}
408+
342409
if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
343410
fmtname = "openpgp";
344411

gpg-interface.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ struct strbuf;
77
#define GPG_VERIFY_RAW 2
88
#define GPG_VERIFY_OMIT_STATUS 4
99

10+
enum signature_trust_level {
11+
TRUST_UNDEFINED,
12+
TRUST_NEVER,
13+
TRUST_MARGINAL,
14+
TRUST_FULLY,
15+
TRUST_ULTIMATE,
16+
};
17+
1018
struct signature_check {
1119
char *payload;
1220
char *gpg_output;
@@ -16,7 +24,6 @@ struct signature_check {
1624
* possible "result":
1725
* 0 (not checked)
1826
* N (checked but no further result)
19-
* U (untrusted good)
2027
* G (good)
2128
* B (bad)
2229
*/
@@ -25,6 +32,7 @@ struct signature_check {
2532
char *key;
2633
char *fingerprint;
2734
char *primary_key_fingerprint;
35+
enum signature_trust_level trust_level;
2836
};
2937

3038
void signature_check_clear(struct signature_check *sigc);

0 commit comments

Comments
 (0)