Skip to content

Commit 2f47eae

Browse files
committed
Split GPG interface into its own helper library
This mostly moves existing code from builtin/tag.c (for signing) and builtin/verify-tag.c (for verifying) to a new gpg-interface.c file to provide a more generic library interface. - sign_buffer() takes a payload strbuf, a signature strbuf, and a signing key, runs "gpg" to produce a detached signature for the payload, and appends it to the signature strbuf. The contents of a signed tag that concatenates the payload and the detached signature can be produced by giving the same strbuf as payload and signature strbuf. - verify_signed_buffer() takes a payload and a detached signature as <ptr, len> pairs, and runs "gpg --verify" to see if the payload matches the signature. It can optionally capture the output from GPG to allow the callers to pretty-print it in a way more suitable for their contexts. "verify-tag" (aka "tag -v") used to save the whole tag contents as if it is a detached signature, and fed gpg the payload part of the tag. It relied on gpg to fail when the given tag is not signed but just is annotated. The updated run_gpg_verify() function detects the lack of detached signature in the input, and errors out without bothering "gpg". Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3dfbe68 commit 2f47eae

File tree

6 files changed

+166
-100
lines changed

6 files changed

+166
-100
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ LIB_H += exec_cmd.h
528528
LIB_H += fsck.h
529529
LIB_H += gettext.h
530530
LIB_H += git-compat-util.h
531+
LIB_H += gpg-interface.h
531532
LIB_H += graph.h
532533
LIB_H += grep.h
533534
LIB_H += hash.h
@@ -621,6 +622,7 @@ LIB_OBJS += entry.o
621622
LIB_OBJS += environment.o
622623
LIB_OBJS += exec_cmd.o
623624
LIB_OBJS += fsck.o
625+
LIB_OBJS += gpg-interface.o
624626
LIB_OBJS += graph.o
625627
LIB_OBJS += grep.o
626628
LIB_OBJS += hash.o

builtin/tag.c

Lines changed: 6 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "parse-options.h"
1515
#include "diff.h"
1616
#include "revision.h"
17+
#include "gpg-interface.h"
1718

1819
static const char * const git_tag_usage[] = {
1920
"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
@@ -23,8 +24,6 @@ static const char * const git_tag_usage[] = {
2324
NULL
2425
};
2526

26-
static char signingkey[1000];
27-
2827
struct tag_filter {
2928
const char **patterns;
3029
int lines;
@@ -208,60 +207,7 @@ static int verify_tag(const char *name, const char *ref,
208207

209208
static int do_sign(struct strbuf *buffer)
210209
{
211-
struct child_process gpg;
212-
const char *args[4];
213-
char *bracket;
214-
int len;
215-
int i, j;
216-
217-
if (!*signingkey) {
218-
if (strlcpy(signingkey, git_committer_info(IDENT_ERROR_ON_NO_NAME),
219-
sizeof(signingkey)) > sizeof(signingkey) - 1)
220-
return error(_("committer info too long."));
221-
bracket = strchr(signingkey, '>');
222-
if (bracket)
223-
bracket[1] = '\0';
224-
}
225-
226-
/* When the username signingkey is bad, program could be terminated
227-
* because gpg exits without reading and then write gets SIGPIPE. */
228-
signal(SIGPIPE, SIG_IGN);
229-
230-
memset(&gpg, 0, sizeof(gpg));
231-
gpg.argv = args;
232-
gpg.in = -1;
233-
gpg.out = -1;
234-
args[0] = "gpg";
235-
args[1] = "-bsau";
236-
args[2] = signingkey;
237-
args[3] = NULL;
238-
239-
if (start_command(&gpg))
240-
return error(_("could not run gpg."));
241-
242-
if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
243-
close(gpg.in);
244-
close(gpg.out);
245-
finish_command(&gpg);
246-
return error(_("gpg did not accept the tag data"));
247-
}
248-
close(gpg.in);
249-
len = strbuf_read(buffer, gpg.out, 1024);
250-
close(gpg.out);
251-
252-
if (finish_command(&gpg) || !len || len < 0)
253-
return error(_("gpg failed to sign the tag"));
254-
255-
/* Strip CR from the line endings, in case we are on Windows. */
256-
for (i = j = 0; i < buffer->len; i++)
257-
if (buffer->buf[i] != '\r') {
258-
if (i != j)
259-
buffer->buf[j] = buffer->buf[i];
260-
j++;
261-
}
262-
strbuf_setlen(buffer, j);
263-
264-
return 0;
210+
return sign_buffer(buffer, buffer, get_signing_key());
265211
}
266212

267213
static const char tag_template[] =
@@ -270,21 +216,11 @@ static const char tag_template[] =
270216
"# Write a tag message\n"
271217
"#\n");
272218

273-
static void set_signingkey(const char *value)
274-
{
275-
if (strlcpy(signingkey, value, sizeof(signingkey)) >= sizeof(signingkey))
276-
die(_("signing key value too long (%.10s...)"), value);
277-
}
278-
279219
static int git_tag_config(const char *var, const char *value, void *cb)
280220
{
281-
if (!strcmp(var, "user.signingkey")) {
282-
if (!value)
283-
return config_error_nonbool(var);
284-
set_signingkey(value);
285-
return 0;
286-
}
287-
221+
int status = git_gpg_config(var, value, cb);
222+
if (status)
223+
return status;
288224
return git_default_config(var, value, cb);
289225
}
290226

@@ -463,7 +399,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
463399

464400
if (keyid) {
465401
sign = 1;
466-
set_signingkey(keyid);
402+
set_signing_key(keyid);
467403
}
468404
if (sign)
469405
annotate = 1;

builtin/verify-tag.c

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "run-command.h"
1212
#include <signal.h>
1313
#include "parse-options.h"
14+
#include "gpg-interface.h"
1415

1516
static const char * const verify_tag_usage[] = {
1617
"git verify-tag [-v|--verbose] <tag>...",
@@ -19,42 +20,16 @@ static const char * const verify_tag_usage[] = {
1920

2021
static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
2122
{
22-
struct child_process gpg;
23-
const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL};
24-
char path[PATH_MAX];
25-
size_t len;
26-
int fd, ret;
23+
int len;
2724

28-
fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
29-
if (fd < 0)
30-
return error("could not create temporary file '%s': %s",
31-
path, strerror(errno));
32-
if (write_in_full(fd, buf, size) < 0)
33-
return error("failed writing temporary file '%s': %s",
34-
path, strerror(errno));
35-
close(fd);
36-
37-
/* find the length without signature */
3825
len = parse_signature(buf, size);
3926
if (verbose)
4027
write_in_full(1, buf, len);
4128

42-
memset(&gpg, 0, sizeof(gpg));
43-
gpg.argv = args_gpg;
44-
gpg.in = -1;
45-
args_gpg[2] = path;
46-
if (start_command(&gpg)) {
47-
unlink(path);
48-
return error("could not run gpg.");
49-
}
50-
51-
write_in_full(gpg.in, buf, len);
52-
close(gpg.in);
53-
ret = finish_command(&gpg);
29+
if (size == len)
30+
return error("no signature found");
5431

55-
unlink_or_warn(path);
56-
57-
return ret;
32+
return verify_signed_buffer(buf, len, buf + len, size - len, NULL);
5833
}
5934

6035
static int verify_tag(const char *name, int verbose)

gpg-interface.c

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#include "cache.h"
2+
#include "run-command.h"
3+
#include "strbuf.h"
4+
#include "gpg-interface.h"
5+
#include "sigchain.h"
6+
7+
static char *configured_signing_key;
8+
9+
void set_signing_key(const char *key)
10+
{
11+
free(configured_signing_key);
12+
configured_signing_key = xstrdup(key);
13+
}
14+
15+
int git_gpg_config(const char *var, const char *value, void *cb)
16+
{
17+
if (!strcmp(var, "user.signingkey")) {
18+
if (!value)
19+
return config_error_nonbool(var);
20+
set_signing_key(value);
21+
}
22+
return 0;
23+
}
24+
25+
const char *get_signing_key(void)
26+
{
27+
if (configured_signing_key)
28+
return configured_signing_key;
29+
return git_committer_info(IDENT_ERROR_ON_NO_NAME|IDENT_NO_DATE);
30+
}
31+
32+
/*
33+
* Create a detached signature for the contents of "buffer" and append
34+
* it after "signature"; "buffer" and "signature" can be the same
35+
* strbuf instance, which would cause the detached signature appended
36+
* at the end.
37+
*/
38+
int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
39+
{
40+
struct child_process gpg;
41+
const char *args[4];
42+
ssize_t len;
43+
size_t i, j, bottom;
44+
45+
memset(&gpg, 0, sizeof(gpg));
46+
gpg.argv = args;
47+
gpg.in = -1;
48+
gpg.out = -1;
49+
args[0] = "gpg";
50+
args[1] = "-bsau";
51+
args[2] = signing_key;
52+
args[3] = NULL;
53+
54+
if (start_command(&gpg))
55+
return error(_("could not run gpg."));
56+
57+
/*
58+
* When the username signingkey is bad, program could be terminated
59+
* because gpg exits without reading and then write gets SIGPIPE.
60+
*/
61+
sigchain_push(SIGPIPE, SIG_IGN);
62+
63+
if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
64+
close(gpg.in);
65+
close(gpg.out);
66+
finish_command(&gpg);
67+
return error(_("gpg did not accept the data"));
68+
}
69+
close(gpg.in);
70+
71+
bottom = signature->len;
72+
len = strbuf_read(signature, gpg.out, 1024);
73+
close(gpg.out);
74+
75+
sigchain_pop(SIGPIPE);
76+
77+
if (finish_command(&gpg) || !len || len < 0)
78+
return error(_("gpg failed to sign the data"));
79+
80+
/* Strip CR from the line endings, in case we are on Windows. */
81+
for (i = j = bottom; i < signature->len; i++)
82+
if (signature->buf[i] != '\r') {
83+
if (i != j)
84+
signature->buf[j] = signature->buf[i];
85+
j++;
86+
}
87+
strbuf_setlen(signature, j);
88+
89+
return 0;
90+
}
91+
92+
/*
93+
* Run "gpg" to see if the payload matches the detached signature.
94+
* gpg_output_to tells where the output from "gpg" should go:
95+
* < 0: /dev/null
96+
* = 0: standard error of the calling process
97+
* > 0: the specified file descriptor
98+
*/
99+
int verify_signed_buffer(const char *payload, size_t payload_size,
100+
const char *signature, size_t signature_size,
101+
struct strbuf *gpg_output)
102+
{
103+
struct child_process gpg;
104+
const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL};
105+
char path[PATH_MAX];
106+
int fd, ret;
107+
108+
fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
109+
if (fd < 0)
110+
return error("could not create temporary file '%s': %s",
111+
path, strerror(errno));
112+
if (write_in_full(fd, signature, signature_size) < 0)
113+
return error("failed writing detached signature to '%s': %s",
114+
path, strerror(errno));
115+
close(fd);
116+
117+
memset(&gpg, 0, sizeof(gpg));
118+
gpg.argv = args_gpg;
119+
gpg.in = -1;
120+
if (gpg_output)
121+
gpg.err = -1;
122+
args_gpg[2] = path;
123+
if (start_command(&gpg)) {
124+
unlink(path);
125+
return error("could not run gpg.");
126+
}
127+
128+
write_in_full(gpg.in, payload, payload_size);
129+
close(gpg.in);
130+
131+
if (gpg_output)
132+
strbuf_read(gpg_output, gpg.err, 0);
133+
ret = finish_command(&gpg);
134+
135+
unlink_or_warn(path);
136+
137+
return ret;
138+
}

gpg-interface.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef GPG_INTERFACE_H
2+
#define GPG_INTERFACE_H
3+
4+
extern int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key);
5+
extern int verify_signed_buffer(const char *payload, size_t payload_size, const char *signature, size_t signature_size, struct strbuf *gpg_output);
6+
extern int git_gpg_config(const char *, const char *, void *);
7+
extern void set_signing_key(const char *);
8+
extern const char *get_signing_key(void);
9+
10+
#endif

tag.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ int parse_tag(struct tag *item)
139139
return ret;
140140
}
141141

142+
/*
143+
* Look at a signed tag object, and return the offset where
144+
* the embedded detached signature begins, or the end of the
145+
* data when there is no such signature.
146+
*/
142147
size_t parse_signature(const char *buf, unsigned long size)
143148
{
144149
char *eol;

0 commit comments

Comments
 (0)