Skip to content

Commit 3026109

Browse files
dborowitzgitster
authored andcommitted
push: support signing pushes iff the server supports it
Add a new flag --sign=true (or --sign=false), which means the same thing as the original --signed (or --no-signed). Give it a third value --sign=if-asked to tell push and send-pack to send a push certificate if and only if the server advertised a push cert nonce. If not, warn the user that their push may not be as secure as they thought. Signed-off-by: Dave Borowitz <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 068c77a commit 3026109

File tree

10 files changed

+128
-49
lines changed

10 files changed

+128
-49
lines changed

Documentation/git-push.txt

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ SYNOPSIS
1111
[verse]
1212
'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
1313
[--repo=<repository>] [-f | --force] [--prune] [-v | --verbose]
14-
[-u | --set-upstream] [--signed]
14+
[-u | --set-upstream]
15+
[--[no-]signed|--sign=(true|false|if-asked)]
1516
[--force-with-lease[=<refname>[:<expect>]]]
1617
[--no-verify] [<repository> [<refspec>...]]
1718

@@ -132,14 +133,16 @@ already exists on the remote side.
132133
with configuration variable 'push.followTags'. For more
133134
information, see 'push.followTags' in linkgit:git-config[1].
134135

135-
136-
--signed::
136+
--[no-]signed::
137+
--sign=(true|false|if-asked)::
137138
GPG-sign the push request to update refs on the receiving
138139
side, to allow it to be checked by the hooks and/or be
139-
logged. See linkgit:git-receive-pack[1] for the details
140-
on the receiving end. If the attempt to sign with `gpg` fails,
141-
or if the server does not support signed pushes, the push will
142-
fail.
140+
logged. If `false` or `--no-signed`, no signing will be
141+
attempted. If `true` or `--signed`, the push will fail if the
142+
server does not support signed pushes. If set to `if-asked`,
143+
sign if and only if the server supports signed pushes. The push
144+
will also fail if the actual call to `gpg --sign` fails. See
145+
linkgit:git-receive-pack[1] for the details on the receiving end.
143146

144147
--[no-]atomic::
145148
Use an atomic transaction on the remote side if available.

Documentation/git-send-pack.txt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ SYNOPSIS
1010
--------
1111
[verse]
1212
'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>]
13-
[--verbose] [--thin] [--atomic] [--signed]
13+
[--verbose] [--thin] [--atomic]
14+
[--[no-]signed|--sign=(true|false|if-asked)]
1415
[<host>:]<directory> [<ref>...]
1516

1617
DESCRIPTION
@@ -69,13 +70,16 @@ be in a separate packet, and the list must end with a flush packet.
6970
fails to update then the entire push will fail without changing any
7071
refs.
7172

72-
--signed::
73+
--[no-]signed::
74+
--sign=(true|false|if-asked)::
7375
GPG-sign the push request to update refs on the receiving
7476
side, to allow it to be checked by the hooks and/or be
75-
logged. See linkgit:git-receive-pack[1] for the details
76-
on the receiving end. If the attempt to sign with `gpg` fails,
77-
or if the server does not support signed pushes, the push will
78-
fail.
77+
logged. If `false` or `--no-signed`, no signing will be
78+
attempted. If `true` or `--signed`, the push will fail if the
79+
server does not support signed pushes. If set to `if-asked`,
80+
sign if and only if the server supports signed pushes. The push
81+
will also fail if the actual call to `gpg --sign` fails. See
82+
linkgit:git-receive-pack[1] for the details on the receiving end.
7983

8084
<host>::
8185
A remote host to house the repository. When this

builtin/push.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "transport.h"
1010
#include "parse-options.h"
1111
#include "submodule.h"
12+
#include "send-pack.h"
1213

1314
static const char * const push_usage[] = {
1415
N_("git push [<options>] [<repository> [<refspec>...]]"),
@@ -495,6 +496,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
495496
{
496497
int flags = 0;
497498
int tags = 0;
499+
int push_cert = -1;
498500
int rc;
499501
const char *repo = NULL; /* default repository */
500502
struct option options[] = {
@@ -526,7 +528,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
526528
OPT_BIT(0, "no-verify", &flags, N_("bypass pre-push hook"), TRANSPORT_PUSH_NO_HOOK),
527529
OPT_BIT(0, "follow-tags", &flags, N_("push missing but relevant tags"),
528530
TRANSPORT_PUSH_FOLLOW_TAGS),
529-
OPT_BIT(0, "signed", &flags, N_("GPG sign the push"), TRANSPORT_PUSH_CERT),
531+
{ OPTION_CALLBACK,
532+
0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
533+
PARSE_OPT_OPTARG, option_parse_push_signed },
530534
OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
531535
OPT_END()
532536
};
@@ -548,6 +552,20 @@ int cmd_push(int argc, const char **argv, const char *prefix)
548552
set_refspecs(argv + 1, argc - 1, repo);
549553
}
550554

555+
switch (push_cert) {
556+
case SEND_PACK_PUSH_CERT_NEVER:
557+
flags &= ~(TRANSPORT_PUSH_CERT_ALWAYS | TRANSPORT_PUSH_CERT_IF_ASKED);
558+
break;
559+
case SEND_PACK_PUSH_CERT_ALWAYS:
560+
flags |= TRANSPORT_PUSH_CERT_ALWAYS;
561+
flags &= ~TRANSPORT_PUSH_CERT_IF_ASKED;
562+
break;
563+
case SEND_PACK_PUSH_CERT_IF_ASKED:
564+
flags |= TRANSPORT_PUSH_CERT_IF_ASKED;
565+
flags &= ~TRANSPORT_PUSH_CERT_ALWAYS;
566+
break;
567+
}
568+
551569
rc = do_push(repo, flags);
552570
if (rc == -1)
553571
usage_with_options(push_usage, options);

builtin/send-pack.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
118118
unsigned send_mirror = 0;
119119
unsigned force_update = 0;
120120
unsigned quiet = 0;
121-
unsigned push_cert = 0;
121+
int push_cert = 0;
122122
unsigned use_thin_pack = 0;
123123
unsigned atomic = 0;
124124
unsigned stateless_rpc = 0;
@@ -137,7 +137,9 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
137137
OPT_BOOL('n' , "dry-run", &dry_run, N_("dry run")),
138138
OPT_BOOL(0, "mirror", &send_mirror, N_("mirror all refs")),
139139
OPT_BOOL('f', "force", &force_update, N_("force updates")),
140-
OPT_BOOL(0, "signed", &push_cert, N_("GPG sign the push")),
140+
{ OPTION_CALLBACK,
141+
0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
142+
PARSE_OPT_OPTARG, option_parse_push_signed },
141143
OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
142144
OPT_BOOL(0, "thin", &use_thin_pack, N_("use thin pack")),
143145
OPT_BOOL(0, "atomic", &atomic, N_("request atomic transaction on remote side")),

remote-curl.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "argv-array.h"
1212
#include "credential.h"
1313
#include "sha1-array.h"
14+
#include "send-pack.h"
1415

1516
static struct remote *remote;
1617
/* always ends with a trailing slash */
@@ -26,7 +27,8 @@ struct options {
2627
followtags : 1,
2728
dry_run : 1,
2829
thin : 1,
29-
push_cert : 1;
30+
/* One of the SEND_PACK_PUSH_CERT_* constants. */
31+
push_cert : 2;
3032
};
3133
static struct options options;
3234
static struct string_list cas_options = STRING_LIST_INIT_DUP;
@@ -109,9 +111,11 @@ static int set_option(const char *name, const char *value)
109111
return 0;
110112
} else if (!strcmp(name, "pushcert")) {
111113
if (!strcmp(value, "true"))
112-
options.push_cert = 1;
114+
options.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
113115
else if (!strcmp(value, "false"))
114-
options.push_cert = 0;
116+
options.push_cert = SEND_PACK_PUSH_CERT_NEVER;
117+
else if (!strcmp(value, "if-asked"))
118+
options.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
115119
else
116120
return -1;
117121
return 0;
@@ -880,8 +884,10 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
880884
argv_array_push(&args, "--thin");
881885
if (options.dry_run)
882886
argv_array_push(&args, "--dry-run");
883-
if (options.push_cert)
884-
argv_array_push(&args, "--signed");
887+
if (options.push_cert == SEND_PACK_PUSH_CERT_ALWAYS)
888+
argv_array_push(&args, "--signed=yes");
889+
else if (options.push_cert == SEND_PACK_PUSH_CERT_IF_ASKED)
890+
argv_array_push(&args, "--signed=if-asked");
885891
if (options.verbosity == 0)
886892
argv_array_push(&args, "--quiet");
887893
else if (options.verbosity > 1)

send-pack.c

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,29 @@
1212
#include "version.h"
1313
#include "sha1-array.h"
1414
#include "gpg-interface.h"
15+
#include "cache.h"
16+
17+
int option_parse_push_signed(const struct option *opt,
18+
const char *arg, int unset)
19+
{
20+
if (unset) {
21+
*(int *)(opt->value) = SEND_PACK_PUSH_CERT_NEVER;
22+
return 0;
23+
}
24+
switch (git_parse_maybe_bool(arg)) {
25+
case 1:
26+
*(int *)(opt->value) = SEND_PACK_PUSH_CERT_ALWAYS;
27+
return 0;
28+
case 0:
29+
*(int *)(opt->value) = SEND_PACK_PUSH_CERT_NEVER;
30+
return 0;
31+
}
32+
if (!strcasecmp("if-asked", arg)) {
33+
*(int *)(opt->value) = SEND_PACK_PUSH_CERT_IF_ASKED;
34+
return 0;
35+
}
36+
die("bad %s argument: %s", opt->long_name, arg);
37+
}
1538

1639
static int feed_object(const unsigned char *sha1, int fd, int negative)
1740
{
@@ -370,14 +393,20 @@ int send_pack(struct send_pack_args *args,
370393
args->use_thin_pack = 0;
371394
if (server_supports("atomic"))
372395
atomic_supported = 1;
373-
if (args->push_cert) {
374-
int len;
375396

397+
if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) {
398+
int len;
376399
push_cert_nonce = server_feature_value("push-cert", &len);
377-
if (!push_cert_nonce)
400+
if (push_cert_nonce) {
401+
reject_invalid_nonce(push_cert_nonce, len);
402+
push_cert_nonce = xmemdupz(push_cert_nonce, len);
403+
} else if (args->push_cert == SEND_PACK_PUSH_CERT_ALWAYS) {
378404
die(_("the receiving end does not support --signed push"));
379-
reject_invalid_nonce(push_cert_nonce, len);
380-
push_cert_nonce = xmemdupz(push_cert_nonce, len);
405+
} else if (args->push_cert == SEND_PACK_PUSH_CERT_IF_ASKED) {
406+
warning(_("not sending a push certificate since the"
407+
" receiving end does not support --signed"
408+
" push"));
409+
}
381410
}
382411

383412
if (!remote_refs) {
@@ -413,7 +442,7 @@ int send_pack(struct send_pack_args *args,
413442
if (!args->dry_run)
414443
advertise_shallow_grafts_buf(&req_buf);
415444

416-
if (!args->dry_run && args->push_cert)
445+
if (!args->dry_run && push_cert_nonce)
417446
cmds_sent = generate_push_cert(&req_buf, remote_refs, args,
418447
cap_buf.buf, push_cert_nonce);
419448

@@ -452,7 +481,7 @@ int send_pack(struct send_pack_args *args,
452481
for (ref = remote_refs; ref; ref = ref->next) {
453482
char *old_hex, *new_hex;
454483

455-
if (args->dry_run || args->push_cert)
484+
if (args->dry_run || push_cert_nonce)
456485
continue;
457486

458487
if (check_to_send_update(ref, args) < 0)

send-pack.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
#ifndef SEND_PACK_H
22
#define SEND_PACK_H
33

4+
/* Possible values for push_cert field in send_pack_args. */
5+
#define SEND_PACK_PUSH_CERT_NEVER 0
6+
#define SEND_PACK_PUSH_CERT_IF_ASKED 1
7+
#define SEND_PACK_PUSH_CERT_ALWAYS 2
8+
49
struct send_pack_args {
510
const char *url;
611
unsigned verbose:1,
@@ -12,11 +17,16 @@ struct send_pack_args {
1217
use_thin_pack:1,
1318
use_ofs_delta:1,
1419
dry_run:1,
15-
push_cert:1,
20+
/* One of the SEND_PACK_PUSH_CERT_* constants. */
21+
push_cert:2,
1622
stateless_rpc:1,
1723
atomic:1;
1824
};
1925

26+
struct option;
27+
int option_parse_push_signed(const struct option *opt,
28+
const char *arg, int unset);
29+
2030
int send_pack(struct send_pack_args *args,
2131
int fd[], struct child_process *conn,
2232
struct ref *remote_refs, struct sha1_array *extra_have);

transport-helper.c

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,6 @@ static const char *boolean_options[] = {
257257
TRANS_OPT_THIN,
258258
TRANS_OPT_KEEP,
259259
TRANS_OPT_FOLLOWTAGS,
260-
TRANS_OPT_PUSH_CERT
261260
};
262261

263262
static int set_helper_option(struct transport *transport,
@@ -763,6 +762,21 @@ static int push_update_refs_status(struct helper_data *data,
763762
return ret;
764763
}
765764

765+
static void set_common_push_options(struct transport *transport,
766+
const char *name, int flags)
767+
{
768+
if (flags & TRANSPORT_PUSH_DRY_RUN) {
769+
if (set_helper_option(transport, "dry-run", "true") != 0)
770+
die("helper %s does not support dry-run", name);
771+
} else if (flags & TRANSPORT_PUSH_CERT_ALWAYS) {
772+
if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "true") != 0)
773+
die("helper %s does not support --signed", name);
774+
} else if (flags & TRANSPORT_PUSH_CERT_IF_ASKED) {
775+
if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "if-asked") != 0)
776+
die("helper %s does not support --signed=if-asked", name);
777+
}
778+
}
779+
766780
static int push_refs_with_push(struct transport *transport,
767781
struct ref *remote_refs, int flags)
768782
{
@@ -830,14 +844,7 @@ static int push_refs_with_push(struct transport *transport,
830844

831845
for_each_string_list_item(cas_option, &cas_options)
832846
set_helper_option(transport, "cas", cas_option->string);
833-
834-
if (flags & TRANSPORT_PUSH_DRY_RUN) {
835-
if (set_helper_option(transport, "dry-run", "true") != 0)
836-
die("helper %s does not support dry-run", data->name);
837-
} else if (flags & TRANSPORT_PUSH_CERT) {
838-
if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "true") != 0)
839-
die("helper %s does not support --signed", data->name);
840-
}
847+
set_common_push_options(transport, data->name, flags);
841848

842849
strbuf_addch(&buf, '\n');
843850
sendline(data, &buf);
@@ -858,14 +865,7 @@ static int push_refs_with_export(struct transport *transport,
858865
if (!data->refspecs)
859866
die("remote-helper doesn't support push; refspec needed");
860867

861-
if (flags & TRANSPORT_PUSH_DRY_RUN) {
862-
if (set_helper_option(transport, "dry-run", "true") != 0)
863-
die("helper %s does not support dry-run", data->name);
864-
} else if (flags & TRANSPORT_PUSH_CERT) {
865-
if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "true") != 0)
866-
die("helper %s does not support --signed", data->name);
867-
}
868-
868+
set_common_push_options(transport, data->name, flags);
869869
if (flags & TRANSPORT_PUSH_FORCE) {
870870
if (set_helper_option(transport, "force", "true") != 0)
871871
warning("helper %s does not support 'force'", data->name);

transport.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -828,10 +828,16 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
828828
args.progress = transport->progress;
829829
args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
830830
args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
831-
args.push_cert = !!(flags & TRANSPORT_PUSH_CERT);
832831
args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
833832
args.url = transport->url;
834833

834+
if (flags & TRANSPORT_PUSH_CERT_ALWAYS)
835+
args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
836+
else if (flags & TRANSPORT_PUSH_CERT_IF_ASKED)
837+
args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
838+
else
839+
args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
840+
835841
ret = send_pack(&args, data->fd, data->conn, remote_refs,
836842
&data->extra_have);
837843

transport.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,9 @@ struct transport {
123123
#define TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND 256
124124
#define TRANSPORT_PUSH_NO_HOOK 512
125125
#define TRANSPORT_PUSH_FOLLOW_TAGS 1024
126-
#define TRANSPORT_PUSH_CERT 2048
127-
#define TRANSPORT_PUSH_ATOMIC 4096
126+
#define TRANSPORT_PUSH_CERT_ALWAYS 2048
127+
#define TRANSPORT_PUSH_CERT_IF_ASKED 4096
128+
#define TRANSPORT_PUSH_ATOMIC 8192
128129

129130
#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
130131
#define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)

0 commit comments

Comments
 (0)