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 <dborowitz@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Dave Borowitz committed Aug 19, 2015 at 11:26 UTC 30261094b1f7fdcba3b7a1f396e43891cd998149
10 files changed +128 -49
Documentation/git-push.txt
+10 -7
@@ -11,7 +11,8 @@ SYNOPSIS
11 [verse]
12 'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
13 [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose]
14 - [-u | --set-upstream] [--signed]
14 + [-u | --set-upstream]
15 + [--[no-]signed|--sign=(true|false|if-asked)]
16 [--force-with-lease[=<refname>[:<expect>]]]
17 [--no-verify] [<repository> [<refspec>...]]
18
@@ -132,14 +133,16 @@ already exists on the remote side.
133 with configuration variable 'push.followTags'. For more
134 information, see 'push.followTags' in linkgit:git-config[1].
135
135 -
136 ---signed::
136 +--[no-]signed::
137 +--sign=(true|false|if-asked)::
138 GPG-sign the push request to update refs on the receiving
139 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.
146
147 --[no-]atomic::
148 Use an atomic transaction on the remote side if available.
Documentation/git-send-pack.txt
+10 -6
@@ -10,7 +10,8 @@ SYNOPSIS
10 --------
11 [verse]
12 '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)]
15 [<host>:]<directory> [<ref>...]
16
17 DESCRIPTION
@@ -69,13 +70,16 @@ be in a separate packet, and the list must end with a flush packet.
70 fails to update then the entire push will fail without changing any
71 refs.
72
72 ---signed::
73 +--[no-]signed::
74 +--sign=(true|false|if-asked)::
75 GPG-sign the push request to update refs on the receiving
76 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.
83
84 <host>::
85 A remote host to house the repository. When this
builtin/push.c
+19 -1
@@ -9,6 +9,7 @@
9 #include "transport.h"
10 #include "parse-options.h"
11 #include "submodule.h"
12 +#include "send-pack.h"
13
14 static const char * const push_usage[] = {
15 N_("git push [<options>] [<repository> [<refspec>...]]"),
@@ -495,6 +496,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
496 {
497 int flags = 0;
498 int tags = 0;
499 + int push_cert = -1;
500 int rc;
501 const char *repo = NULL; /* default repository */
502 struct option options[] = {
@@ -526,7 +528,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
528 OPT_BIT(0, "no-verify", &flags, N_("bypass pre-push hook"), TRANSPORT_PUSH_NO_HOOK),
529 OPT_BIT(0, "follow-tags", &flags, N_("push missing but relevant tags"),
530 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 },
534 OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
535 OPT_END()
536 };
@@ -548,6 +552,20 @@ int cmd_push(int argc, const char **argv, const char *prefix)
552 set_refspecs(argv + 1, argc - 1, repo);
553 }
554
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 +
569 rc = do_push(repo, flags);
570 if (rc == -1)
571 usage_with_options(push_usage, options);
builtin/send-pack.c
+4 -2
@@ -118,7 +118,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
118 unsigned send_mirror = 0;
119 unsigned force_update = 0;
120 unsigned quiet = 0;
121 - unsigned push_cert = 0;
121 + int push_cert = 0;
122 unsigned use_thin_pack = 0;
123 unsigned atomic = 0;
124 unsigned stateless_rpc = 0;
@@ -137,7 +137,9 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
137 OPT_BOOL('n' , "dry-run", &dry_run, N_("dry run")),
138 OPT_BOOL(0, "mirror", &send_mirror, N_("mirror all refs")),
139 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 },
143 OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
144 OPT_BOOL(0, "thin", &use_thin_pack, N_("use thin pack")),
145 OPT_BOOL(0, "atomic", &atomic, N_("request atomic transaction on remote side")),
remote-curl.c
+11 -5
@@ -11,6 +11,7 @@
11 #include "argv-array.h"
12 #include "credential.h"
13 #include "sha1-array.h"
14 +#include "send-pack.h"
15
16 static struct remote *remote;
17 /* always ends with a trailing slash */
@@ -26,7 +27,8 @@ struct options {
27 followtags : 1,
28 dry_run : 1,
29 thin : 1,
29 - push_cert : 1;
30 + /* One of the SEND_PACK_PUSH_CERT_* constants. */
31 + push_cert : 2;
32 };
33 static struct options options;
34 static struct string_list cas_options = STRING_LIST_INIT_DUP;
@@ -109,9 +111,11 @@ static int set_option(const char *name, const char *value)
111 return 0;
112 } else if (!strcmp(name, "pushcert")) {
113 if (!strcmp(value, "true"))
112 - options.push_cert = 1;
114 + options.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
115 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;
119 else
120 return -1;
121 return 0;
@@ -880,8 +884,10 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
884 argv_array_push(&args, "--thin");
885 if (options.dry_run)
886 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");
891 if (options.verbosity == 0)
892 argv_array_push(&args, "--quiet");
893 else if (options.verbosity > 1)
send-pack.c
+36 -7
@@ -12,6 +12,29 @@
12 #include "version.h"
13 #include "sha1-array.h"
14 #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 +}
38
39 static int feed_object(const unsigned char *sha1, int fd, int negative)
40 {
@@ -370,14 +393,20 @@ int send_pack(struct send_pack_args *args,
393 args->use_thin_pack = 0;
394 if (server_supports("atomic"))
395 atomic_supported = 1;
373 - if (args->push_cert) {
374 - int len;
396
397 + if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) {
398 + int len;
399 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) {
404 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 + }
410 }
411
412 if (!remote_refs) {
@@ -413,7 +442,7 @@ int send_pack(struct send_pack_args *args,
442 if (!args->dry_run)
443 advertise_shallow_grafts_buf(&req_buf);
444
416 - if (!args->dry_run && args->push_cert)
445 + if (!args->dry_run && push_cert_nonce)
446 cmds_sent = generate_push_cert(&req_buf, remote_refs, args,
447 cap_buf.buf, push_cert_nonce);
448
@@ -452,7 +481,7 @@ int send_pack(struct send_pack_args *args,
481 for (ref = remote_refs; ref; ref = ref->next) {
482 char *old_hex, *new_hex;
483
455 - if (args->dry_run || args->push_cert)
484 + if (args->dry_run || push_cert_nonce)
485 continue;
486
487 if (check_to_send_update(ref, args) < 0)
send-pack.h
+11 -1
@@ -1,6 +1,11 @@
1 #ifndef SEND_PACK_H
2 #define SEND_PACK_H
3
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 +
9 struct send_pack_args {
10 const char *url;
11 unsigned verbose:1,
@@ -12,11 +17,16 @@ struct send_pack_args {
17 use_thin_pack:1,
18 use_ofs_delta:1,
19 dry_run:1,
15 - push_cert:1,
20 + /* One of the SEND_PACK_PUSH_CERT_* constants. */
21 + push_cert:2,
22 stateless_rpc:1,
23 atomic:1;
24 };
25
26 +struct option;
27 +int option_parse_push_signed(const struct option *opt,
28 + const char *arg, int unset);
29 +
30 int send_pack(struct send_pack_args *args,
31 int fd[], struct child_process *conn,
32 struct ref *remote_refs, struct sha1_array *extra_have);
transport-helper.c
+17 -17
@@ -257,7 +257,6 @@ static const char *boolean_options[] = {
257 TRANS_OPT_THIN,
258 TRANS_OPT_KEEP,
259 TRANS_OPT_FOLLOWTAGS,
260 - TRANS_OPT_PUSH_CERT
260 };
261
262 static int set_helper_option(struct transport *transport,
@@ -763,6 +762,21 @@ static int push_update_refs_status(struct helper_data *data,
762 return ret;
763 }
764
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 +
780 static int push_refs_with_push(struct transport *transport,
781 struct ref *remote_refs, int flags)
782 {
@@ -830,14 +844,7 @@ static int push_refs_with_push(struct transport *transport,
844
845 for_each_string_list_item(cas_option, &cas_options)
846 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);
848
849 strbuf_addch(&buf, '\n');
850 sendline(data, &buf);
@@ -858,14 +865,7 @@ static int push_refs_with_export(struct transport *transport,
865 if (!data->refspecs)
866 die("remote-helper doesn't support push; refspec needed");
867
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);
869 if (flags & TRANSPORT_PUSH_FORCE) {
870 if (set_helper_option(transport, "force", "true") != 0)
871 warning("helper %s does not support 'force'", data->name);
transport.c
+7 -1
@@ -828,10 +828,16 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
828 args.progress = transport->progress;
829 args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
830 args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
831 - args.push_cert = !!(flags & TRANSPORT_PUSH_CERT);
831 args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
832 args.url = transport->url;
833
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 +
841 ret = send_pack(&args, data->fd, data->conn, remote_refs,
842 &data->extra_have);
843
transport.h
+3 -2
@@ -123,8 +123,9 @@ struct transport {
123 #define TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND 256
124 #define TRANSPORT_PUSH_NO_HOOK 512
125 #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
129
130 #define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
131 #define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)